Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
node_modules
.idea/
66 changes: 49 additions & 17 deletions playground.cjs
Original file line number Diff line number Diff line change
@@ -1,22 +1,54 @@
const { transformAsync } = require("@babel/core")

// Change the following code and and run `pnpm run playground`
const javascriptSrc =/*javascript*/`
import { Component } from 'solid-js'
import { component } from 'undestructure-macros'

component(({ a, b, c, ...other }) => <div>{a} {b} {c} {other.d}</div>)

const ArrowComp = ({ a, b, c, ...other }) => <div>{a} {b} {c} {other.d}</div>

const FunctionExpressionComp = function ({ a, b, c, ...other }) { return <div>{a} {b} {c} {other.d}</div>; }

const NamedExpressionComp = function Comp({ a, b, c, ...other }) { return <div>{a} {b} {c} {other.d}</div>; }

function FunctionComp({ a, b, c, ...other }) { return <div>{a} {b} {c} {other.d}</div>; }

function notAComponent({ a, b, c, ...other }) { return 2 + 2; }
`;

// Change the following code and and run `pnpm run playground`
const src=/*javascript*/`
const typeScriptSrc =/*typescript*/`
import { Component } from 'solid-js'
import x from 'undestructure-macros'
const MyComp: Component<T> = ({ a, b, c, ...other }) => {a; b; c; other;}
`


;(async () => {
const res = await transformAsync(
src,
{ plugins: [
["@babel/plugin-syntax-typescript", { isTSX: true }],
"./src/index.cjs"
] }
)

console.log(res.code)
})()

const ArrowComp: Component<T> = ({ a, b, c, ...other }) => <div>{a} {b} {c} {other.d}</div>

const FunctionExpressionComp: Component<T> = function ({ a, b, c, ...other }) { return <div>{a} {b} {c} {other.d}</div>; }
`;

(async () => {
const resTS = await transformAsync(
typeScriptSrc,
{
plugins: [
["@babel/plugin-syntax-typescript", { isTSX: true }],
["./src/index.cjs", { uppercaseFuncNames: true }],
]
}
)

console.log(resTS.code);

const resJS = await transformAsync(
javascriptSrc,
{
plugins: [
["@babel/plugin-syntax-typescript", { isTSX: true }],
["./src/index.cjs", { uppercaseFuncNames: true }],
]
}
);

console.log(resJS.code);
})();
15 changes: 15 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ const MyComponent = c(/* your component goes here. */)

In this last example, `MyComponent` won't be transformed.

## uppercaseFuncNames option

By setting this option to `true`, the plugin will assume function with names that start with an uppercase letter are components and will transform them accordingly.

This option is disabled by default.

Example:

```javascript
const MyComponent = ({ a, b, c }) => { ... }
```

```javascript
function MyComponent({ a, b, c }) { ... }
```

## Installation and Configuring Vite

Expand Down
44 changes: 40 additions & 4 deletions src/check-func/check-func.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
const { checkFuncAnnotation } = require("./check-func-annotation.cjs")


/**
* Checks if a function node has a name that starts with an uppercase character
*
* @param {Object} opts
* @param {NodePath} path
*
* @returns {boolean}
*/
const checkFuncStartsWithUppercase = (opts, path) => {
if (!opts.uppercaseFuncNames) { return false }

let funcName;
switch (path.type) {
case "FunctionDeclaration":
funcName = path.node.id.name
break;
case "FunctionExpression":
funcName = path.parent.id?.name;
break;
case "ArrowFunctionExpression":
funcName = path.parent.id?.name
break;
default:
return false;
}

if (!funcName) { return false }

return (funcName[0] === funcName[0].toUpperCase());
}


/**
* Check that the first param ( `props` ) exists and is an object pattern.
*
Expand All @@ -25,21 +57,25 @@ const checkFirstParam = path => {

/**
* Checks if the visited function needs to be transformed (if the function is annotated
* and and the first argument is destructured).
* and the first argument is destructured).
* If the function is annotated with a CTF, mark it in the state.
*/
const checkFunc = (funcPath, opts, state) => {
// Make sure the function wasn't already transformed.
if (funcPath.transformed) return false

if (!checkFuncAnnotation(opts, funcPath, state)) return { funcAnnotation: false }
if (!checkFuncAnnotation(opts, funcPath, state)) {
if (!checkFuncStartsWithUppercase(opts, funcPath, state)) {
return { funcAnnotation: false }
}
}

if (!checkFirstParam(funcPath)) return {
if (!checkFirstParam(funcPath)) return {
funcAnnotation: true,
propDestructuring: false
}

return {
return {
funcAnnotation: true,
propDestructuring: true
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { types } = require("@babel/core")
module.exports.getDestructredProperties = funcPath => {
const firstParam = funcPath.node.params[0]

const objectPattern = firstParam.type == "AssignmentPattern"
const objectPattern = firstParam.type === "AssignmentPattern"
? firstParam.left
: firstParam

Expand Down
78 changes: 78 additions & 0 deletions tests/check-func-capital-letter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { test } from 'uvu'
import * as assert from 'uvu/assert'
import babelParser from '@babel/parser'
import babelTraverse from '@babel/traverse'
import { checkFunc } from '../src/check-func/check-func.cjs'

const traverse = babelTraverse.default


test('checkFunc', () => {
const assertCheckFunc = (code, expects, msg, uppercaseFuncNames = true) => {
const ast = babelParser.parse(code, { sourceType: "module" })

traverse(ast, {
Function: path => {
assert.equal(checkFunc(path, { uppercaseFuncNames }, {}), expects, msg)
path.transformed = true
}
})
}

const code7 = /*javascript*/`
const ArrowComp = ({ someProp }) => {}
`
assertCheckFunc(
code7,
{ funcAnnotation: false },
"Returns false when uppercaseFuncNames is false even though the function starts with a capital letter.",
false,
)

const code1 = /*javascript*/`
const ArrowComp = ({ someProp }) => {}
`
assertCheckFunc(
code1,
{ funcAnnotation: true, propDestructuring: true },
"Returns true for an arrow function that starts with a capital letter."
)

const code5 = /*javascript*/`
function FuncComp({ someProp }) {}
`
assertCheckFunc(
code5,
{ funcAnnotation: true, propDestructuring: true },
"Returns true for a function that starts with a capital letter."
)

const code6 = /*javascript*/`
const ExprComp = function({ someProp }) {}
`
assertCheckFunc(
code6,
{ funcAnnotation: true, propDestructuring: true },
"Returns true for a function expression that starts with a capital letter."
)

const code2 = /*javascript*/`
const notAComp = ({ someProp }) => {}
`
assertCheckFunc(
code2,
{ funcAnnotation: false },
"Returns false for a camelCase function."
)

const code4 = /*javascript*/`
const ArrowComp = (props => {});
`
assertCheckFunc(
code4,
{ funcAnnotation: true, propDestructuring: false },
"Returns false for a function with no prop destructuring."
)
})

test.run()