-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(web-components): fix no-direct default export
- Loading branch information
Showing
5 changed files
with
241 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
src/utils/transform-jsx-to-reactive/transform-to-direct-export/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { describe, expect, it } from "bun:test"; | ||
import AST from "../../ast"; | ||
import transformToDirectExport from "."; | ||
|
||
const { parseCodeToAST, generateCodeFromAST } = AST(); | ||
const toInline = (s: string) => s.replace(/\s*\n\s*/g, "").replaceAll("'", '"'); | ||
|
||
describe("utils", () => { | ||
describe("transform-jsx-to-reactive", () => { | ||
describe("transform-to-direct-export", () => { | ||
it("should transform the web-component to a direct export if the component is a variable declaration", () => { | ||
const ast = parseCodeToAST(` | ||
const MyComponent = (props) => <div>{props.foo}</div>; | ||
export default MyComponent; | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default props => jsxDEV("div", {children: props.foo}, undefined, false, undefined, this); | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
|
||
it("should transform the web-component to a direct export if the component is a function declaration", () => { | ||
const ast = parseCodeToAST(` | ||
function MyComponent(props) { | ||
return <div>{props.foo}</div>; | ||
} | ||
export default MyComponent; | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default function (props) {return jsxDEV("div", {children: props.foo}, undefined, false, undefined, this);} | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
|
||
it("should transform the web-component to a direct export if the component is an arrow function with block statement declaration", () => { | ||
const ast = parseCodeToAST(` | ||
const MyComponent = (props) => { | ||
return <div>{props.foo}</div>; | ||
} | ||
export default MyComponent; | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default props => {return jsxDEV("div", {children: props.foo}, undefined, false, undefined, this);}; | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
|
||
it("should not transform the web-component to a direct export if the component is a direct export", () => { | ||
const ast = parseCodeToAST(` | ||
export default (props) => <div>{props.foo}</div>; | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default props => jsxDEV("div", {children: props.foo}, undefined, false, undefined, this); | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
|
||
it("should not transform the web-component to a direct export if the component is a direct export with block statement", () => { | ||
const ast = parseCodeToAST(` | ||
export default (props) => { | ||
return <div>{props.foo}</div>; | ||
} | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default props => {return jsxDEV("div", {children: props.foo}, undefined, false, undefined, this);}; | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
|
||
it("should not transform the web-component to a direct export if the component is a direct export with a function declaration", () => { | ||
const ast = parseCodeToAST(` | ||
export default function MyComponent(props) { | ||
return <div>{props.foo}</div>; | ||
} | ||
`); | ||
const outputAst = transformToDirectExport(ast); | ||
const outputCode = toInline(generateCodeFromAST(outputAst)); | ||
const expectedCode = toInline(` | ||
export default function MyComponent(props) {return jsxDEV("div", {children: props.foo}, undefined, false, undefined, this);} | ||
`); | ||
|
||
expect(outputCode).toBe(expectedCode); | ||
}); | ||
}); | ||
}); | ||
}); |
72 changes: 72 additions & 0 deletions
72
src/utils/transform-jsx-to-reactive/transform-to-direct-export/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { ESTree } from "meriyah"; | ||
|
||
const DIRECT_TYPES = new Set([ | ||
"ArrowFunctionExpression", | ||
"FunctionExpression", | ||
"VariableDeclaration", | ||
]); | ||
|
||
/** | ||
* transformToDirectExport | ||
* | ||
* @description Transform no-direct default export to a direct default export | ||
* @example | ||
* Input: | ||
* const MyComponent = (props) => <div>{props.foo}</div>; | ||
* export default MyComponent; | ||
* | ||
* Output: | ||
* export default (props) => <div>{props.foo}</div>; | ||
* | ||
* @param {ESTree.Program} ast | ||
* @returns {ESTree.Program} | ||
*/ | ||
export default function transformToDirectExport( | ||
ast: ESTree.Program, | ||
): ESTree.Program { | ||
const defaultExportIndex = ast.body.findIndex( | ||
(node) => node.type === "ExportDefaultDeclaration", | ||
); | ||
|
||
if (defaultExportIndex === -1) return ast; | ||
|
||
const defaultExportNode = ast.body[defaultExportIndex] as any; | ||
|
||
if (DIRECT_TYPES.has(defaultExportNode.declaration.type)) return ast; | ||
|
||
const astWithoutDefaultExport = { | ||
...ast, | ||
body: ast.body.filter((node, index) => index !== defaultExportIndex), | ||
}; | ||
|
||
const componentDeclarationIndex = ast.body.findIndex((node: any) => { | ||
const name = | ||
node.id?.name ?? | ||
node.declaration?.name ?? | ||
node?.declarations?.[0]?.id?.name; | ||
return ( | ||
DIRECT_TYPES.has(node.type) && name === defaultExportNode.declaration.name | ||
); | ||
}); | ||
|
||
if (componentDeclarationIndex === -1) return ast; | ||
|
||
const componentDeclaration = ast.body[componentDeclarationIndex]; | ||
|
||
if (componentDeclaration.type === "VariableDeclaration") { | ||
const updatedBody = astWithoutDefaultExport.body.map((node, index) => { | ||
if (index === componentDeclarationIndex) { | ||
return { | ||
...node, | ||
type: "ExportDefaultDeclaration", | ||
declaration: componentDeclaration.declarations[0].init, | ||
}; | ||
} | ||
return node; | ||
}); | ||
|
||
return { ...astWithoutDefaultExport, body: updatedBody } as ESTree.Program; | ||
} | ||
|
||
return ast; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters