diff --git a/package.json b/package.json index f8e7463..899031d 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,9 @@ "@babel/types": "^7.6.3", "@favoloso/conventional-changelog-emoji": "^0.10.0", "@release-it/conventional-changelog": "^1.1.0", + "@types/babel-plugin-macros": "^2.8.5", "@types/jest": "^27.4.1", + "babel-plugin-macros": "^3.1.0", "babel-test": "^0.2.3", "conventional-changelog-cli": "^2.0.25", "husky": "^4.2.3", @@ -80,7 +82,8 @@ }, "peerDependencies": { "@babel/core": "^7", - "@babel/traverse": "^7" + "@babel/traverse": "^7", + "babel-plugin-macros": "^3.1.0" }, "peerDependenciesMeta": { "@babel/core": { @@ -88,6 +91,9 @@ }, "@babel/traverse": { "optional": true + }, + "babel-plugin-macros": { + "optional": true } } -} \ No newline at end of file +} diff --git a/src/macro.ts b/src/macro.ts new file mode 100644 index 0000000..086d52f --- /dev/null +++ b/src/macro.ts @@ -0,0 +1,24 @@ +import { createMacro, MacroHandler } from 'babel-plugin-macros'; +import { transform } from './transform'; + +const metadataMacro: MacroHandler = ({ references }) => { + references.default.forEach(reference => { + const decorator = reference.findParent(parent => parent.isDecorator()); + if (!decorator) { + throw new Error("Metadata macro should be used as class decorator"); + } + const classDeclaration = decorator.findParent(parent => parent.isClassDeclaration()); + if (!classDeclaration) { + throw new Error("Metadata macro should be used as class decorator"); + } + if (classDeclaration.isClassDeclaration()) { + if (!classDeclaration.node.decorators) { + classDeclaration.node.decorators = []; + } + transform(classDeclaration); + classDeclaration.node.decorators = classDeclaration.node.decorators.filter(it => it !== decorator.node); + } + }) +}; + +export default createMacro(metadataMacro); diff --git a/src/plugin.ts b/src/plugin.ts index fb0fab2..b6e74c0 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,7 +1,6 @@ import { PluginObj } from '@babel/core'; import { declare } from '@babel/helper-plugin-utils'; -import { parameterVisitor } from './parameter/parameterVisitor'; -import { metadataVisitor } from './metadata/metadataVisitor'; +import { transform } from './transform'; export default declare( (api: any): PluginObj => { @@ -20,23 +19,7 @@ export default declare( */ programPath.traverse({ ClassDeclaration(path) { - for (const field of path.get('body').get('body')) { - if ( - field.type !== 'ClassMethod' && - field.type !== 'ClassProperty' - ) - continue; - - parameterVisitor(path, field as any); - metadataVisitor(path, field as any); - } - - /** - * We need to keep binding in order to let babel know where imports - * are used as a Value (and not just as a type), so that - * `babel-transform-typescript` do not strip the import. - */ - (path.parentPath.scope as any).crawl(); + transform(path); } }); } diff --git a/src/transform.ts b/src/transform.ts new file mode 100644 index 0000000..fa433b2 --- /dev/null +++ b/src/transform.ts @@ -0,0 +1,24 @@ +import { types } from '@babel/core'; +import { NodePath } from '@babel/traverse'; +import { metadataVisitor } from './metadata/metadataVisitor'; +import { parameterVisitor } from './parameter/parameterVisitor'; + +export function transform(path: NodePath) { + for (const field of path.get('body').get('body')) { + if ( + field.type !== 'ClassMethod' && + field.type !== 'ClassProperty' + ) + continue; + + parameterVisitor(path, field as any); + metadataVisitor(path, field as any); + } + + /** + * We need to keep binding in order to let babel know where imports + * are used as a Value (and not just as a type), so that + * `babel-transform-typescript` do not strip the import. + */ + (path.parentPath.scope as any).crawl(); +} diff --git a/yarn.lock b/yarn.lock index 8f27f3e..61215c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -963,7 +963,7 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" -"@babel/runtime@^7.8.4": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": version "7.17.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== @@ -1424,9 +1424,16 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel-plugin-macros@^2.8.5": + version "2.8.5" + resolved "https://registry.npmjs.org/@types/babel-plugin-macros/-/babel-plugin-macros-2.8.5.tgz#04474f9898aa9112afc22fa0e7e53a898fcaba4c" + integrity sha512-+NcIm/VBaSb4xaycov9f4Vmk4hMVPrgISoHEk+kalgVK6BlkwDZbXkW9kt1jCXVczTKXldL5RHIacExaWzxAkA== + dependencies: + "@types/babel__core" "*" + +"@types/babel__core@*", "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== dependencies: "@babel/parser" "^7.1.0" @@ -1766,6 +1773,15 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + babel-plugin-polyfill-corejs2@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" @@ -3451,7 +3467,7 @@ is-ci@2.0.0, is-ci@^2.0.0: is-core-module@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -4874,7 +4890,7 @@ path-key@^3.0.0, path-key@^3.1.0: path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^1.0.0: @@ -5336,7 +5352,7 @@ resolve@^1.1.6, resolve@^1.10.0: dependencies: path-parse "^1.0.6" -resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -5752,7 +5768,7 @@ supports-hyperlinks@^2.0.0: supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== symbol-tree@^3.2.4: