feat(providence): oxc for main analyzers and traversal; parsers as peerDeps
This commit is contained in:
parent
6f3137c963
commit
615472cc0a
41 changed files with 870 additions and 1818 deletions
12
.changeset/sour-balloons-smoke.md
Normal file
12
.changeset/sour-balloons-smoke.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
'providence-analytics': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
- use oxc for all analyzers (oxc is way smaller and more performant than swc, let alone babel)
|
||||||
|
- make swcTraverse compatible with oxc
|
||||||
|
- expand scope functionality of swcTraverse
|
||||||
|
|
||||||
|
BREAKING:
|
||||||
|
|
||||||
|
- make parsers peerDependencies (babel or swc should be loaded by external analyzers)
|
||||||
|
- rename `swcTraverse` to `oxcTraverse`
|
||||||
163
package-lock.json
generated
163
package-lock.json
generated
|
|
@ -4198,15 +4198,6 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@putout/babel": {
|
|
||||||
"version": "2.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@putout/babel/-/babel-2.8.0.tgz",
|
|
||||||
"integrity": "sha512-Vq4DgAR6Zfc0VXyspQndmgT4T7sTgJBm8kwigN2zPxtyTtz8R199qjxSrypY1P2d+iAGatG2imksrzlPOlombg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rocket/blog": {
|
"node_modules/@rocket/blog": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rocket/blog/-/blog-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rocket/blog/-/blog-0.4.0.tgz",
|
||||||
|
|
@ -5525,6 +5516,7 @@
|
||||||
"version": "1.7.36",
|
"version": "1.7.36",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.36.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.36.tgz",
|
||||||
"integrity": "sha512-bu7ymMX+LCJOSSrKank25Jaq66ymLVA9fOUuy4ck3/6rbXdLw+pIJPnIDKQ9uNcxww8KDxOuJk9Ui9pqR+aGFw==",
|
"integrity": "sha512-bu7ymMX+LCJOSSrKank25Jaq66ymLVA9fOUuy4ck3/6rbXdLw+pIJPnIDKQ9uNcxww8KDxOuJk9Ui9pqR+aGFw==",
|
||||||
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -5566,6 +5558,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5582,6 +5575,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5598,6 +5592,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5614,6 +5609,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5630,6 +5626,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5646,6 +5643,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5662,6 +5660,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5678,6 +5677,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5694,6 +5694,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5710,6 +5711,7 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0 AND MIT",
|
"license": "Apache-2.0 AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5723,12 +5725,14 @@
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/types": {
|
"node_modules/@swc/types": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz",
|
||||||
"integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==",
|
"integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/counter": "^0.1.3"
|
"@swc/counter": "^0.1.3"
|
||||||
|
|
@ -28032,17 +28036,13 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"packages-node/providence-analytics": {
|
"packages-node/providence-analytics": {
|
||||||
"version": "0.16.5",
|
"version": "0.16.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.25.8",
|
|
||||||
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
|
||||||
"@babel/traverse": "^7.25.7",
|
"@babel/traverse": "^7.25.7",
|
||||||
"@babel/types": "^7.25.8",
|
|
||||||
"@putout/babel": "^2.8.0",
|
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
"@swc/core": "^1.7.36",
|
|
||||||
"commander": "^2.20.3",
|
"commander": "^2.20.3",
|
||||||
|
"oxc-parser": "^0.34.0",
|
||||||
"parse5": "^7.2.0",
|
"parse5": "^7.2.0",
|
||||||
"semver": "^7.6.3"
|
"semver": "^7.6.3"
|
||||||
},
|
},
|
||||||
|
|
@ -28050,6 +28050,9 @@
|
||||||
"providence": "src/cli/index.js"
|
"providence": "src/cli/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/parser": "^7.25.8",
|
||||||
|
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
||||||
|
"@swc/core": "^1.7.36",
|
||||||
"@types/inquirer": "^9.0.7",
|
"@types/inquirer": "^9.0.7",
|
||||||
"@types/mocha": "^10.0.9",
|
"@types/mocha": "^10.0.9",
|
||||||
"@web/dev-server": "^0.4.6",
|
"@web/dev-server": "^0.4.6",
|
||||||
|
|
@ -28060,6 +28063,134 @@
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/parser": "^7.25.8",
|
||||||
|
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
||||||
|
"@swc/core": "^1.7.36"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-darwin-arm64": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-1G99sWa40ylI1bX8VxUnvl3eEnT7C045t6HJ5UkYg+B6XoJouUgo/wrhFn53+kRBm9gT65YHBGs4gCLpk8b8dw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-darwin-x64": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-7+EcaPjG7PlnM/Oj2Cpf1tYFCXmABlO7P9uYNa/aiNVHv4oZklL2q1HT3fSsU/7nNa09IDKsl8yvQHhwfkNbWw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-linux-arm64-gnu": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-uiQfQESB5WLxVUwWe+/wiaujoT0we5tDm7fz3EwpgqKDsqA3Y/IobLsymIPp5/dfOmOUElB9dMUZUaDTrQeWtA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-linux-arm64-musl": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-08ChBq0X4U60B6ervmNDdSIjxleeCLrztbhul/cFFRcqUNj10F7ZLgz7/rucfxD80hE8Cf2PsbVK5e89qY7Y/Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-linux-x64-gnu": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-OVXEcQu9/FxUeSK1RGgvBzHNKQYjiYL536GahOzuptCyYjYUhrz+eopu7P0wIB/1irrMv33gCNt211inVnWaZQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-linux-x64-musl": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-/9WFKdTDKVRs2JJh4oxF1gEbnlJcZtoIAH6yToTqftghal7NFLoHBGdp1Jo8b2m0Vn4L3yiDE/sAbrB0XgIAsw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-win32-arm64-msvc": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-eKO8BmgDSWl47SoKBtxj+XQjvn8SqXbpZ/NuZbcYkZVzpwWui4Ycqo76GRLrEhp+ykN3Nj9gl29nka3oenrk7A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/@oxc-parser/binding-win32-x64-msvc": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-7KWqCm7DmkFVd8MRMp14/HjxpYgWaj9k2E2pAQGOnExPCuzzF2Wji0qHcNzuMQMjTDKswjRkAOg6gk5b45JQRQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packages-node/providence-analytics/node_modules/oxc-parser": {
|
||||||
|
"version": "0.34.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.34.0.tgz",
|
||||||
|
"integrity": "sha512-k9PJKDD4+U3VzLic2Pup+N4YNIlBhzUkEWZP6yVkXtwEVZn1gIp1ixAt1e9+9EagzzAiY/Kx6EPEsZxNb3d1fg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/Boshen"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@oxc-parser/binding-darwin-arm64": "0.34.0",
|
||||||
|
"@oxc-parser/binding-darwin-x64": "0.34.0",
|
||||||
|
"@oxc-parser/binding-linux-arm64-gnu": "0.34.0",
|
||||||
|
"@oxc-parser/binding-linux-arm64-musl": "0.34.0",
|
||||||
|
"@oxc-parser/binding-linux-x64-gnu": "0.34.0",
|
||||||
|
"@oxc-parser/binding-linux-x64-musl": "0.34.0",
|
||||||
|
"@oxc-parser/binding-win32-arm64-msvc": "0.34.0",
|
||||||
|
"@oxc-parser/binding-win32-x64-msvc": "0.34.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages-node/publish-docs": {
|
"packages-node/publish-docs": {
|
||||||
|
|
@ -28164,7 +28295,7 @@
|
||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@lion/ui",
|
"name": "@lion/ui",
|
||||||
"version": "0.7.9",
|
"version": "0.8.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bundled-es-modules/message-format": "^6.2.4",
|
"@bundled-es-modules/message-format": "^6.2.4",
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
- bdb038e1: Many improvements:
|
- bdb038e1: Many improvements:
|
||||||
|
|
||||||
- rewritten from babel to swc
|
- rewritten from babel to swc
|
||||||
- swcTraverse tool, compatible with babel traverse api
|
- oxcTraverse tool, compatible with babel traverse api
|
||||||
- increased performance
|
- increased performance
|
||||||
- better windows compatibility
|
- better windows compatibility
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,266 +0,0 @@
|
||||||
2023.10.18, v3.0.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- d37e664 types
|
|
||||||
|
|
||||||
2023.10.17, v3.0.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- 7aefd72 types
|
|
||||||
- aa0ef7b babel v8
|
|
||||||
- be3c7a6 ImportAttributes
|
|
||||||
- f927c37 package: putout v32.2.2
|
|
||||||
- 4a8b9e3 package: eslint-plugin-putout v20.0.0
|
|
||||||
|
|
||||||
2023.07.18, v2.2.0
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- ce8a51a incorrect line number calculation and fix bug with JSX elements not having loc keys (#20)
|
|
||||||
|
|
||||||
2023.07.12, v2.1.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- 68dfe2d package: eslint-plugin-putout v18.1.0
|
|
||||||
- a303550 package: c8 v8.0.0
|
|
||||||
- 8e70e4c package: putout v30.4.0
|
|
||||||
- 8f5df0a package: eslint-plugin-n v16.0.1
|
|
||||||
- 19700e5 package: nodemon v3.0.1
|
|
||||||
|
|
||||||
2023.04.26, v2.0.0
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- 606fd45 handle null element in holey arrays (#18)
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- 6a1e3a3 swc-to-babel: drop support of node < 16
|
|
||||||
- ab3263e swc-to-babel: use @putout/printer
|
|
||||||
- d21f30e package: eslint-plugin-putout v17.5.1
|
|
||||||
- e14d18c package: check-dts v0.7.1
|
|
||||||
- a3cabd8 package: typescript v5.0.4
|
|
||||||
- 48a0b6c package: putout v29.3.0
|
|
||||||
|
|
||||||
2022.10.12, v1.26.0
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babel: parenthesized const assertion error (#15)
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- package: supertape v8.1.0
|
|
||||||
|
|
||||||
2022.08.24, v1.25.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- getters/setters: make getters work properly and fix tests (#12)
|
|
||||||
|
|
||||||
2022.08.23, v1.25.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- make source an optional parameter (#10)
|
|
||||||
|
|
||||||
2022.08.23, v1.24.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- add type definitions (#8)
|
|
||||||
|
|
||||||
2022.08.23, v1.23.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- add support for GetterProperty and SetterProperty (#7)
|
|
||||||
|
|
||||||
2022.08.21, v1.22.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- add support of KeyValueProperty nodes
|
|
||||||
|
|
||||||
2022.08.21, v1.21.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- package: eslint-plugin-putout v16.0.1
|
|
||||||
- package: putout v27.1.0
|
|
||||||
- add support of TSKeywordType (close #1)
|
|
||||||
|
|
||||||
2022.06.12, v1.20.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babel: add directives
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.12, v1.20.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ExportDefaultDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.19.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: ObjectProperty instead of AssignmentPatternProperty
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.18.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ClassExpression and ExportSpecifier
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.17.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babl: ImportSpecifier
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.17.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ObjectProperty
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.16.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babel: ArrowFunctionExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.16.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: NewExpression: arguments field should always present
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.15.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ArrayExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.11, v1.14.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of FunctionDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.13.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: TSAliasDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.12.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: handle typeParameters
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.11.2
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: improve SpreadElement support
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.11.1
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: improve support of SpreadElement
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.11.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of NewExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.10.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: improve support of MemberExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.9.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ClassDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.8.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ParenthesisExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.7.1
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: improve support of typeAnnotation in Identifiers
|
|
||||||
- swc-to-babel: add support of ExportNamedDeclaration and ExportDefaultDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.10, v1.7.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of ExportNamedDeclaration and ExportDefaultDeclaration
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.6.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: CallExpression has no typeArguments
|
|
||||||
- swc-to-babel: TemplateElement
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.5.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: TemplateElement
|
|
||||||
- package: eslint-plugin-putout v15.6.0
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.4.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of typescript
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.3.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babel: position
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.3.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: add support of BlockStatement
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.2.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: CallExpression
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.1.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- swc-to-babel: no type
|
|
||||||
|
|
||||||
|
|
||||||
2022.06.09, v1.1.0
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- (package) supertape v7.3.0
|
|
||||||
- (package) putout v26.13.0
|
|
||||||
- (package) madrun v9.0.4
|
|
||||||
- swc-to-babel: add support of Identifier
|
|
||||||
|
|
||||||
|
|
||||||
2022.02.06, v1.0.2
|
|
||||||
|
|
||||||
feature:
|
|
||||||
- swc-to-babel: rm unused
|
|
||||||
|
|
||||||
|
|
||||||
2022.02.05, v1.0.1
|
|
||||||
|
|
||||||
fix:
|
|
||||||
- lint
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) coderaiser
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
Temp inline version of swc-to-babel, as we need to change a few things for 100% compatibility with our analyzers.
|
|
||||||
"version": "3.0.1", [swc-to-babel](http://github.com/coderaiser/swc-to-babel)
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = ({tokens, ...program}) => {
|
|
||||||
const ast = {
|
|
||||||
type: 'File',
|
|
||||||
|
|
||||||
program: {
|
|
||||||
...program,
|
|
||||||
directives: [],
|
|
||||||
},
|
|
||||||
|
|
||||||
comments: [],
|
|
||||||
tokens,
|
|
||||||
};
|
|
||||||
|
|
||||||
return ast;
|
|
||||||
};
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { types, traverse } = require('@putout/babel');
|
|
||||||
|
|
||||||
const {
|
|
||||||
convertModuleToProgram,
|
|
||||||
convertSpanToPosition,
|
|
||||||
convertVariableDeclarator,
|
|
||||||
convertStringLiteral,
|
|
||||||
convertIdentifier,
|
|
||||||
convertCallExpression,
|
|
||||||
convertObjectProperty,
|
|
||||||
BlockStatement,
|
|
||||||
TemplateElement,
|
|
||||||
convertTSTypeParameter,
|
|
||||||
convertExportDeclaration,
|
|
||||||
convertExportDefaultExpression,
|
|
||||||
convertParenthesisExpression,
|
|
||||||
convertGetterSetter,
|
|
||||||
ClassMethod,
|
|
||||||
ClassDeclaration,
|
|
||||||
ArrayExpression,
|
|
||||||
MemberExpression,
|
|
||||||
NewExpression,
|
|
||||||
Function,
|
|
||||||
ImportDeclaration,
|
|
||||||
ImportSpecifier,
|
|
||||||
ExportNamedDeclaration,
|
|
||||||
ExportDefaultDeclaration,
|
|
||||||
ExportSpecifier,
|
|
||||||
TSTypeAliasDeclaration,
|
|
||||||
TSMappedType,
|
|
||||||
TSTypeReference,
|
|
||||||
TSTypeOperator,
|
|
||||||
TSTypeParameter,
|
|
||||||
TSIndexedAccessType,
|
|
||||||
TSAsExpression,
|
|
||||||
JSXElement,
|
|
||||||
JSXFragment,
|
|
||||||
} = require('./swc/index.cjs');
|
|
||||||
|
|
||||||
const getAST = require('./get-ast.cjs');
|
|
||||||
const { isIdentifier } = types;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an SWC ast to a babel ast
|
|
||||||
* @param ast {Module} SWC ast
|
|
||||||
* @param {string} [src=""] Source code
|
|
||||||
* @returns {ParseResult<File>} Babel ast
|
|
||||||
*/
|
|
||||||
function toBabel(node, source = '') {
|
|
||||||
const ast = getAST(node);
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
noScope: true,
|
|
||||||
|
|
||||||
BlockStatement,
|
|
||||||
TemplateElement,
|
|
||||||
ClassMethod,
|
|
||||||
ClassDeclaration,
|
|
||||||
ClassExpression: ClassDeclaration,
|
|
||||||
ArrayExpression,
|
|
||||||
MemberExpression,
|
|
||||||
NewExpression,
|
|
||||||
Function,
|
|
||||||
ImportDeclaration,
|
|
||||||
ImportSpecifier,
|
|
||||||
ExportNamedDeclaration,
|
|
||||||
ExportSpecifier,
|
|
||||||
ExportDefaultDeclaration,
|
|
||||||
|
|
||||||
TSTypeAliasDeclaration,
|
|
||||||
TSMappedType,
|
|
||||||
TSTypeReference,
|
|
||||||
TSTypeOperator,
|
|
||||||
TSTypeParameter,
|
|
||||||
TSIndexedAccessType,
|
|
||||||
TSAsExpression,
|
|
||||||
|
|
||||||
JSXElement,
|
|
||||||
JSXFragment,
|
|
||||||
|
|
||||||
enter(path) {
|
|
||||||
const { node } = path;
|
|
||||||
const { type } = node;
|
|
||||||
|
|
||||||
if ('span' in path.node) convertSpanToPosition(path, source);
|
|
||||||
|
|
||||||
delete node.start;
|
|
||||||
delete node.end;
|
|
||||||
|
|
||||||
if (type?.startsWith('Ts')) node.type = type.replace('Ts', 'TS');
|
|
||||||
|
|
||||||
if (type?.endsWith('Literal')) setEsprimaRaw(node);
|
|
||||||
|
|
||||||
if (isIdentifier(path)) return convertIdentifier(path);
|
|
||||||
|
|
||||||
if (path.isStringLiteral()) return convertStringLiteral(path);
|
|
||||||
|
|
||||||
if (type === 'Module') return convertModuleToProgram(path);
|
|
||||||
|
|
||||||
if (path.isVariableDeclarator()) return convertVariableDeclarator(path);
|
|
||||||
|
|
||||||
if (path.isCallExpression()) return convertCallExpression(path);
|
|
||||||
|
|
||||||
if (path.isTSTypeParameter()) return convertTSTypeParameter(path);
|
|
||||||
|
|
||||||
if (path.type === 'ExportDeclaration') return convertExportDeclaration(path);
|
|
||||||
|
|
||||||
if (path.type === 'ExportDefaultExpression') return convertExportDefaultExpression(path);
|
|
||||||
|
|
||||||
if (path.type === 'ParenthesisExpression') return convertParenthesisExpression(path);
|
|
||||||
|
|
||||||
if (/^(KeyValue|KeyValuePattern|AssignmentPattern)Property$/.test(path.type))
|
|
||||||
return convertObjectProperty(path);
|
|
||||||
|
|
||||||
if (path.type === 'GetterProperty' || path.type === 'SetterProperty')
|
|
||||||
return convertGetterSetter(path);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = toBabel;
|
|
||||||
|
|
||||||
function setEsprimaRaw(node) {
|
|
||||||
const { raw } = node;
|
|
||||||
|
|
||||||
node.raw = raw || node.extra?.raw;
|
|
||||||
node.extra = node.extra || {
|
|
||||||
raw,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports.getPositionByOffset = (offset, source) => {
|
|
||||||
let line = 1;
|
|
||||||
let column = 0;
|
|
||||||
|
|
||||||
if (offset > source.length)
|
|
||||||
throw Error('end cannot be more then length ' + offset + ', ' + source.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < offset; i++) {
|
|
||||||
if (source[i] === '\n' && i !== offset - 1) {
|
|
||||||
line++;
|
|
||||||
column = 0;
|
|
||||||
} else {
|
|
||||||
column++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
line,
|
|
||||||
column,
|
|
||||||
index: offset - 1,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,390 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { getPositionByOffset } = require('./get-position-by-offset.cjs');
|
|
||||||
|
|
||||||
const isNull = a => !a && typeof a === 'object';
|
|
||||||
const { assign } = Object;
|
|
||||||
|
|
||||||
module.exports.convertModuleToProgram = path => {
|
|
||||||
path.node.type = 'Program';
|
|
||||||
path.node.sourceType = 'module';
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertSpanToPosition = (path, source) => {
|
|
||||||
const { start, end } = path.node.span;
|
|
||||||
|
|
||||||
delete path.node.span;
|
|
||||||
|
|
||||||
if (end > source.length)
|
|
||||||
return assign(path.node, {
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startPosition = getPositionByOffset(start, source);
|
|
||||||
const endPosition = getPositionByOffset(end, source);
|
|
||||||
|
|
||||||
assign(path.node, {
|
|
||||||
start: startPosition.index,
|
|
||||||
end: endPosition.index,
|
|
||||||
loc: {
|
|
||||||
start: startPosition,
|
|
||||||
end: endPosition,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertVariableDeclarator = path => {
|
|
||||||
delete path.parentPath.node.declare;
|
|
||||||
delete path.node.optional;
|
|
||||||
delete path.node.definite;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertStringLiteral = path => {
|
|
||||||
delete path.node.hasEscape;
|
|
||||||
delete path.node.kind;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertIdentifier = ({ node }) => {
|
|
||||||
convertIdentifier(node);
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertIdentifier(node) {
|
|
||||||
const { typeAnnotation } = node;
|
|
||||||
|
|
||||||
node.name = node.value;
|
|
||||||
|
|
||||||
if (isNull(typeAnnotation)) {
|
|
||||||
delete node.typeAnnotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete node.value;
|
|
||||||
delete node.optional;
|
|
||||||
delete node.span;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.convertCallExpression = path => {
|
|
||||||
const newArgs = [];
|
|
||||||
|
|
||||||
for (const arg of path.node.arguments) {
|
|
||||||
newArgs.push(arg.expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete path.node.typeArguments;
|
|
||||||
path.node.arguments = newArgs;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.BlockStatement = path => {
|
|
||||||
path.node.body = path.node.stmts;
|
|
||||||
delete path.node.stmts;
|
|
||||||
path.node.directives = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSMappedType = path => {
|
|
||||||
path.node.typeParameter = path.node.typeParam;
|
|
||||||
|
|
||||||
if (!path.node.nameType) path.node.nameType = null;
|
|
||||||
|
|
||||||
if (!path.node.readonly) delete path.node.readonly;
|
|
||||||
|
|
||||||
if (!path.node.optional) delete path.node.optional;
|
|
||||||
|
|
||||||
delete path.node.typeParam;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertTSTypeParameter = path => {
|
|
||||||
convertIdentifier(path.node.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TemplateElement = path => {
|
|
||||||
const { cooked, raw } = path.node;
|
|
||||||
|
|
||||||
path.node.value = {
|
|
||||||
cooked,
|
|
||||||
raw,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete path.node.cooked;
|
|
||||||
delete path.node.raw;
|
|
||||||
delete path.node.tail;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertExportDeclaration = path => {
|
|
||||||
path.node.type = 'ExportNamedDeclaration';
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertExportDefaultExpression = path => {
|
|
||||||
path.node.type = 'ExportDefaultDeclaration';
|
|
||||||
path.node.declaration = path.node.expression;
|
|
||||||
|
|
||||||
delete path.node.expression;
|
|
||||||
delete path.node.declare;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertParenthesisExpression = path => {
|
|
||||||
const expressionPath = path.get('expression');
|
|
||||||
|
|
||||||
if (expressionPath.type === 'TsAsExpression') convertTSAsExpression(expressionPath);
|
|
||||||
else if (expressionPath.type === 'TsConstAssertion') convertTSConstAssertion(expressionPath);
|
|
||||||
|
|
||||||
path.replaceWith(expressionPath.node);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ClassMethod = path => {
|
|
||||||
const { node } = path;
|
|
||||||
const { key } = path.node;
|
|
||||||
|
|
||||||
Object.assign(node, {
|
|
||||||
...path.node.function,
|
|
||||||
key,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (node.kind === 'getter') {
|
|
||||||
node.kind = 'get';
|
|
||||||
}
|
|
||||||
if (node.kind === 'setter') {
|
|
||||||
node.kind = 'set';
|
|
||||||
}
|
|
||||||
|
|
||||||
node.static = node.isStatic;
|
|
||||||
|
|
||||||
delete path.node.isStatic;
|
|
||||||
delete path.node.accessibility;
|
|
||||||
delete path.node.isAbstract;
|
|
||||||
delete path.node.isOptional;
|
|
||||||
delete path.node.isOverride;
|
|
||||||
delete path.node.optional;
|
|
||||||
delete path.node.function;
|
|
||||||
delete path.node.decorators;
|
|
||||||
delete path.node.typeParameters;
|
|
||||||
delete path.node.returnType;
|
|
||||||
delete path.node.span;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ClassDeclaration = path => {
|
|
||||||
path.node.id = path.node.identifier;
|
|
||||||
path.node.body = {
|
|
||||||
type: 'ClassBody',
|
|
||||||
body: path.node.body,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete path.node.identifier;
|
|
||||||
delete path.node.declare;
|
|
||||||
delete path.node.decorators;
|
|
||||||
delete path.node.isAbstract;
|
|
||||||
delete path.node.typeParams;
|
|
||||||
delete path.node.superTypeParams;
|
|
||||||
delete path.node.implements;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.MemberExpression = ({ node }) => {
|
|
||||||
node.computed = node.property.type === 'Computed';
|
|
||||||
|
|
||||||
if (node.computed) node.property = node.property.expression;
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertSpreadElement(node) {
|
|
||||||
const { expression } = node;
|
|
||||||
|
|
||||||
assign(node, {
|
|
||||||
type: 'SpreadElement',
|
|
||||||
argument: expression,
|
|
||||||
});
|
|
||||||
|
|
||||||
delete node.spread;
|
|
||||||
delete node.expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeConvertSpread(arg) {
|
|
||||||
if (arg === null) return;
|
|
||||||
|
|
||||||
const { spread } = arg;
|
|
||||||
|
|
||||||
if (spread) {
|
|
||||||
convertSpreadElement(arg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(arg, arg.expression);
|
|
||||||
|
|
||||||
delete arg.spread;
|
|
||||||
delete arg.expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.NewExpression = path => {
|
|
||||||
path.node.arguments = path.node.arguments || [];
|
|
||||||
path.node.arguments.forEach(maybeConvertSpread);
|
|
||||||
|
|
||||||
delete path.node.typeArguments;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ArrayExpression = path => {
|
|
||||||
path.node.elements.forEach(maybeConvertSpread);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Function = path => {
|
|
||||||
const { node } = path;
|
|
||||||
|
|
||||||
if (path.parentPath.isExportDefaultDeclaration()) path.node.type = 'FunctionDeclaration';
|
|
||||||
|
|
||||||
const { params, typeParameters } = node;
|
|
||||||
|
|
||||||
node.id = node.identifier || null;
|
|
||||||
|
|
||||||
delete node.identifier;
|
|
||||||
delete node.decorators;
|
|
||||||
|
|
||||||
if (!node.returnType) delete node.returnType;
|
|
||||||
|
|
||||||
for (const [index, param] of params.entries()) {
|
|
||||||
if (param.type === 'Parameter') params[index] = param.pat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNull(typeParameters)) delete node.typeParameters;
|
|
||||||
|
|
||||||
delete node.declare;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSTypeAliasDeclaration = path => {
|
|
||||||
delete path.node.declare;
|
|
||||||
delete path.node.typeParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSAsExpression = convertTSAsExpression;
|
|
||||||
function convertTSAsExpression({ node }) {
|
|
||||||
node.type = 'TSAsExpression';
|
|
||||||
|
|
||||||
if (node.typeAnnotation.kind === 'any')
|
|
||||||
assign(node.typeAnnotation, {
|
|
||||||
type: 'TSAnyKeyword',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.TSConstAssertion = convertTSConstAssertion;
|
|
||||||
function convertTSConstAssertion({ node }) {
|
|
||||||
assign(node, {
|
|
||||||
type: 'TSAsExpression',
|
|
||||||
extra: {
|
|
||||||
parenthesized: true,
|
|
||||||
parenStart: 0,
|
|
||||||
},
|
|
||||||
typeAnnotation: {
|
|
||||||
type: 'TSTypeReference',
|
|
||||||
typeName: {
|
|
||||||
type: 'Identifier',
|
|
||||||
name: 'const',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.TSTypeReference = path => {
|
|
||||||
delete path.node.typeParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSTypeOperator = path => {
|
|
||||||
path.node.operator = path.node.op;
|
|
||||||
|
|
||||||
delete path.node.op;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSTypeParameter = path => {
|
|
||||||
path.node.name = path.node.name.name;
|
|
||||||
|
|
||||||
delete path.node.in;
|
|
||||||
delete path.node.out;
|
|
||||||
delete path.node.default;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.TSIndexedAccessType = path => {
|
|
||||||
delete path.node.readonly;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ImportDeclaration = ({ node }) => {
|
|
||||||
const { typeOnly } = node;
|
|
||||||
|
|
||||||
node.assertions = node.asserts?.properties || [];
|
|
||||||
node.importKind = typeOnly ? 'type' : 'value';
|
|
||||||
|
|
||||||
delete node.asserts;
|
|
||||||
delete node.typeOnly;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ImportSpecifier = ({ node }) => {
|
|
||||||
if (!node.imported)
|
|
||||||
node.imported = {
|
|
||||||
...node.local,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete node.isTypeOnly;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertObjectProperty = path => {
|
|
||||||
const { node } = path;
|
|
||||||
|
|
||||||
node.type = 'ObjectProperty';
|
|
||||||
node.shorthand = !node.value;
|
|
||||||
|
|
||||||
if (!node.value)
|
|
||||||
node.value = {
|
|
||||||
...node.key,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete path.parentPath.node.optional;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convertGetterSetter = ({ node }) => {
|
|
||||||
node.kind = node.type === 'GetterProperty' ? 'get' : 'set';
|
|
||||||
node.type = 'ObjectMethod';
|
|
||||||
node.params = node.param ? [node.param] : [];
|
|
||||||
|
|
||||||
delete node.param;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ExportDefaultDeclaration = ({ node }) => {
|
|
||||||
// node.declaration may have been already provided by convertExportDefaultExpression
|
|
||||||
node.declaration = node.declaration || node.decl;
|
|
||||||
node.exportKind = 'value';
|
|
||||||
node.assertions = node.asserts?.properties || [];
|
|
||||||
|
|
||||||
delete node.decl;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ExportNamedDeclaration = ({ node }) => {
|
|
||||||
const { typeOnly } = node;
|
|
||||||
|
|
||||||
node.assertions = node.asserts?.properties || [];
|
|
||||||
// node.source = null;
|
|
||||||
node.specifiers = node.specifiers || [];
|
|
||||||
|
|
||||||
node.exportKind = typeOnly ? 'type' : 'value';
|
|
||||||
|
|
||||||
delete node.asserts;
|
|
||||||
delete node.typeOnly;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.ExportSpecifier = ({ node }) => {
|
|
||||||
const { orig, exported } = node;
|
|
||||||
|
|
||||||
node.local = orig;
|
|
||||||
node.exported = exported || {
|
|
||||||
...orig,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete node.isTypeOnly;
|
|
||||||
delete node.orig;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.JSXElement = path => {
|
|
||||||
path.node.openingElement = path.node.opening;
|
|
||||||
delete path.node.opening;
|
|
||||||
path.node.closingElement = path.node.closing;
|
|
||||||
delete path.node.closing;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.JSXFragment = path => {
|
|
||||||
path.node.openingFragment = path.node.opening;
|
|
||||||
delete path.node.opening;
|
|
||||||
path.node.closingFragment = path.node.closing;
|
|
||||||
delete path.node.closing;
|
|
||||||
};
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
"providence": "./src/cli/index.js"
|
"providence": "./src/cli/index.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"inlined-swc-to-babel",
|
|
||||||
"src",
|
"src",
|
||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
|
|
@ -38,18 +37,22 @@
|
||||||
"test:node:unit": "mocha './{test-node,src}/**/*.test.js'"
|
"test:node:unit": "mocha './{test-node,src}/**/*.test.js'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.25.8",
|
|
||||||
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
|
||||||
"@babel/traverse": "^7.25.7",
|
"@babel/traverse": "^7.25.7",
|
||||||
"@babel/types": "^7.25.8",
|
|
||||||
"@putout/babel": "^2.8.0",
|
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
"@swc/core": "^1.7.36",
|
|
||||||
"commander": "^2.20.3",
|
"commander": "^2.20.3",
|
||||||
|
"oxc-parser": "^0.34.0",
|
||||||
"parse5": "^7.2.0",
|
"parse5": "^7.2.0",
|
||||||
"semver": "^7.6.3"
|
"semver": "^7.6.3"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/parser": "^7.25.8",
|
||||||
|
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
||||||
|
"@swc/core": "^1.7.36"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/parser": "^7.25.8",
|
||||||
|
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
||||||
|
"@swc/core": "^1.7.36",
|
||||||
"@types/inquirer": "^9.0.7",
|
"@types/inquirer": "^9.0.7",
|
||||||
"@types/mocha": "^10.0.9",
|
"@types/mocha": "^10.0.9",
|
||||||
"@web/dev-server": "^0.4.6",
|
"@web/dev-server": "^0.4.6",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
/* eslint-disable no-shadow, no-param-reassign */
|
/* eslint-disable no-shadow, no-param-reassign */
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import babelTraverse from '@babel/traverse';
|
import { oxcTraverse, isProperty } from '../utils/oxc-traverse.js';
|
||||||
import t from '@babel/types';
|
|
||||||
|
|
||||||
import { trackDownIdentifierFromScope } from '../utils/track-down-identifier--legacy.js';
|
import { trackDownIdentifierFromScope } from '../utils/track-down-identifier.js';
|
||||||
import { Analyzer } from '../core/Analyzer.js';
|
import { Analyzer } from '../core/Analyzer.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,6 +15,7 @@ import { Analyzer } from '../core/Analyzer.js';
|
||||||
* @typedef {import('../../../types/index.js').FindClassesAnalyzerOutputFile} FindClassesAnalyzerOutputFile
|
* @typedef {import('../../../types/index.js').FindClassesAnalyzerOutputFile} FindClassesAnalyzerOutputFile
|
||||||
* @typedef {import('../../../types/index.js').FindClassesAnalyzerEntry} FindClassesAnalyzerEntry
|
* @typedef {import('../../../types/index.js').FindClassesAnalyzerEntry} FindClassesAnalyzerEntry
|
||||||
* @typedef {import('../../../types/index.js').FindClassesConfig} FindClassesConfig
|
* @typedef {import('../../../types/index.js').FindClassesConfig} FindClassesConfig
|
||||||
|
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -132,8 +132,7 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
||||||
methods: [],
|
methods: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
astPath.traverse({
|
const handleMethodDefinitionOrClassMethod = astPath => {
|
||||||
ClassMethod(astPath) {
|
|
||||||
// if (isBlacklisted(astPath)) {
|
// if (isBlacklisted(astPath)) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
@ -144,7 +143,7 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
||||||
if (hasFoundTopLvlObjExpr) return;
|
if (hasFoundTopLvlObjExpr) return;
|
||||||
hasFoundTopLvlObjExpr = true;
|
hasFoundTopLvlObjExpr = true;
|
||||||
astPath.node.properties.forEach(objectProperty => {
|
astPath.node.properties.forEach(objectProperty => {
|
||||||
if (!t.isProperty(objectProperty)) {
|
if (!isProperty(objectProperty)) {
|
||||||
// we can also have a SpreadElement
|
// we can also have a SpreadElement
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +179,11 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
||||||
} else {
|
} else {
|
||||||
classRes.members.methods.push(methodRes);
|
classRes.members.methods.push(methodRes);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
astPath.traverse({
|
||||||
|
ClassMethod: handleMethodDefinitionOrClassMethod,
|
||||||
|
MethodDefinition: handleMethodDefinitionOrClassMethod,
|
||||||
});
|
});
|
||||||
|
|
||||||
classesFound.push(classRes);
|
classesFound.push(classRes);
|
||||||
|
|
@ -188,7 +191,7 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
||||||
|
|
||||||
const classesToTraverse = [];
|
const classesToTraverse = [];
|
||||||
|
|
||||||
babelTraverse.default(babelAst, {
|
oxcTraverse(babelAst, {
|
||||||
ClassDeclaration(astPath) {
|
ClassDeclaration(astPath) {
|
||||||
classesToTraverse.push({ astPath, isMixin: false });
|
classesToTraverse.push({ astPath, isMixin: false });
|
||||||
},
|
},
|
||||||
|
|
@ -228,8 +231,8 @@ export default class FindClassesAnalyzer extends Analyzer {
|
||||||
/** @type {AnalyzerName} */
|
/** @type {AnalyzerName} */
|
||||||
static analyzerName = 'find-classes';
|
static analyzerName = 'find-classes';
|
||||||
|
|
||||||
/** @type {'babel'|'swc-to-babel'} */
|
/** @type {AnalyzerAst} */
|
||||||
static requiredAst = 'babel';
|
static requiredAst = 'oxc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will find all public members (properties (incl. getter/setters)/functions) of a class and
|
* Will find all public members (properties (incl. getter/setters)/functions) of a class and
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import babelTraverse from '@babel/traverse';
|
// import babelTraverse from '@babel/traverse';
|
||||||
import t from '@babel/types';
|
import { oxcTraverse } from '../utils/oxc-traverse.js';
|
||||||
|
|
||||||
import { trackDownIdentifierFromScope } from '../utils/track-down-identifier--legacy.js';
|
import { trackDownIdentifierFromScope } from '../utils/track-down-identifier.js';
|
||||||
import { Analyzer } from '../core/Analyzer.js';
|
import { Analyzer } from '../core/Analyzer.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,27 +40,29 @@ async function trackdownRoot(transformedEntry, relativePath, projectPath) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds import specifiers and sources
|
* Finds import specifiers and sources
|
||||||
* @param {File} babelAst
|
* @param {File} oxcAst
|
||||||
*/
|
*/
|
||||||
function findCustomElementsPerAstFile(babelAst) {
|
function findCustomElementsPerAstFile(oxcAst) {
|
||||||
const definitions = [];
|
const definitions = [];
|
||||||
babelTraverse.default(babelAst, {
|
oxcTraverse(oxcAst, {
|
||||||
CallExpression(astPath) {
|
CallExpression(astPath) {
|
||||||
let found = false;
|
let found = false;
|
||||||
// Doing it like this we detect 'customElements.define()',
|
// Doing it like this we detect 'customElements.define()',
|
||||||
// but also 'window.customElements.define()'
|
// but also 'window.customElements.define()'
|
||||||
astPath.traverse({
|
astPath.traverse({
|
||||||
MemberExpression(memberPath) {
|
// MemberExpression in babel
|
||||||
if (memberPath.parentPath !== astPath) {
|
StaticMemberExpression(memberPath) {
|
||||||
|
if (memberPath.node !== astPath.node.callee) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { node } = memberPath;
|
const { node } = memberPath;
|
||||||
|
|
||||||
if (node.object.name === 'customElements' && node.property.name === 'define') {
|
if (node.object.name === 'customElements' && node.property.name === 'define') {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
node.object.object &&
|
node.object.object?.name === 'window' &&
|
||||||
node.object.object.name === 'window' &&
|
|
||||||
node.object.property.name === 'customElements' &&
|
node.object.property.name === 'customElements' &&
|
||||||
node.property.name === 'define'
|
node.property.name === 'define'
|
||||||
) {
|
) {
|
||||||
|
|
@ -72,7 +74,7 @@ function findCustomElementsPerAstFile(babelAst) {
|
||||||
let tagName;
|
let tagName;
|
||||||
let constructorIdentifier;
|
let constructorIdentifier;
|
||||||
|
|
||||||
if (t.isLiteral(astPath.node.arguments[0])) {
|
if (astPath.node.arguments[0].type === 'StringLiteral') {
|
||||||
tagName = astPath.node.arguments[0].value;
|
tagName = astPath.node.arguments[0].value;
|
||||||
} else {
|
} else {
|
||||||
// No Literal found. For now, we only mark them as '[variable]'
|
// No Literal found. For now, we only mark them as '[variable]'
|
||||||
|
|
@ -95,8 +97,8 @@ export default class FindCustomelementsAnalyzer extends Analyzer {
|
||||||
/** @type {AnalyzerName} */
|
/** @type {AnalyzerName} */
|
||||||
static analyzerName = 'find-customelements';
|
static analyzerName = 'find-customelements';
|
||||||
|
|
||||||
/** @type {'babel'|'swc-to-babel'} */
|
/** @type {AnalyzerAst} */
|
||||||
static requiredAst = 'swc-to-babel';
|
static requiredAst = 'oxc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds export specifiers and sources
|
* Finds export specifiers and sources
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { getReferencedDeclaration } from '../utils/get-source-code-fragment-of-d
|
||||||
import { normalizeSourcePaths } from './helpers/normalize-source-paths.js';
|
import { normalizeSourcePaths } from './helpers/normalize-source-paths.js';
|
||||||
import { trackDownIdentifier } from '../utils/track-down-identifier.js';
|
import { trackDownIdentifier } from '../utils/track-down-identifier.js';
|
||||||
import { getAssertionType } from '../utils/get-assertion-type.js';
|
import { getAssertionType } from '../utils/get-assertion-type.js';
|
||||||
import { swcTraverse } from '../utils/swc-traverse.js';
|
import { oxcTraverse } from '../utils/oxc-traverse.js';
|
||||||
import { LogService } from '../core/LogService.js';
|
import { LogService } from '../core/LogService.js';
|
||||||
import { Analyzer } from '../core/Analyzer.js';
|
import { Analyzer } from '../core/Analyzer.js';
|
||||||
|
|
||||||
|
|
@ -110,20 +110,25 @@ function cleanup(transformedFile) {
|
||||||
function getExportSpecifiers(node) {
|
function getExportSpecifiers(node) {
|
||||||
// handles default [export const g = 4];
|
// handles default [export const g = 4];
|
||||||
if (node.declaration?.declarations) {
|
if (node.declaration?.declarations) {
|
||||||
return [node.declaration.declarations[0].id.value];
|
return [node.declaration.declarations[0].id.value || node.declaration.declarations[0].id.name];
|
||||||
}
|
}
|
||||||
if (node.declaration?.identifier) {
|
if (node.declaration?.identifier) {
|
||||||
return [node.declaration.identifier.value];
|
return [node.declaration.identifier.value || node.declaration.identifier.name];
|
||||||
|
}
|
||||||
|
if (node.declaration?.id) {
|
||||||
|
return [node.declaration.id.value || node.declaration.id.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles (re)named specifiers [export { x (as y)} from 'y'];
|
// handles (re)named specifiers [export { x (as y)} from 'y'];
|
||||||
return (node.specifiers || []).map(s => {
|
return (node.specifiers || []).map(s => {
|
||||||
if (s.exported) {
|
if (s.exported) {
|
||||||
// { x as y }
|
// { x as y }
|
||||||
return s.exported.value === 'default' ? '[default]' : s.exported.value;
|
return (s.exported.value || s.exported.name) === 'default'
|
||||||
|
? '[default]'
|
||||||
|
: s.exported.value || s.exported.name;
|
||||||
}
|
}
|
||||||
// { x }
|
// { x }
|
||||||
return s.orig.value;
|
return s.orig.value || s.local.name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,11 +138,18 @@ function getExportSpecifiers(node) {
|
||||||
function getLocalNameSpecifiers(node) {
|
function getLocalNameSpecifiers(node) {
|
||||||
return (node.declaration?.declarations || node.specifiers || [])
|
return (node.declaration?.declarations || node.specifiers || [])
|
||||||
.map(s => {
|
.map(s => {
|
||||||
if (s.exported && s.orig && s.exported.value !== s.orig.value) {
|
if (
|
||||||
|
s.exported &&
|
||||||
|
(s.orig || s.local) &&
|
||||||
|
(s.exported.value || s.exported.name) !== (s.orig?.value || s.local?.name)
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
// if reserved keyword 'default' is used, translate it into 'providence keyword'
|
// if reserved keyword 'default' is used, translate it into 'providence keyword'
|
||||||
local: s.orig.value === 'default' ? '[default]' : s.orig.value,
|
local:
|
||||||
exported: s.exported.value,
|
(s.orig?.value || s.local?.name) === 'default'
|
||||||
|
? '[default]'
|
||||||
|
: s.orig?.value || s.local?.name,
|
||||||
|
exported: s.exported.value || s.exported.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
@ -150,10 +162,10 @@ const isImportingSpecifier = pathOrNode =>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds import specifiers and sources for a given ast result
|
* Finds import specifiers and sources for a given ast result
|
||||||
* @param {SwcAstModule} swcAst
|
* @param {SwcAstModule} oxcAst
|
||||||
* @param {FindExportsConfig} config
|
* @param {FindExportsConfig} config
|
||||||
*/
|
*/
|
||||||
function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
function findExportsPerAstFile(oxcAst, { skipFileImports }) {
|
||||||
LogService.debug(`Analyzer "find-exports": started findExportsPerAstFile method`);
|
LogService.debug(`Analyzer "find-exports": started findExportsPerAstFile method`);
|
||||||
|
|
||||||
// Visit AST...
|
// Visit AST...
|
||||||
|
|
@ -169,7 +181,7 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
||||||
const exportHandler = (/** @type {SwcPath} */ astPath) => {
|
const exportHandler = (/** @type {SwcPath} */ astPath) => {
|
||||||
const exportSpecifiers = getExportSpecifiers(astPath.node);
|
const exportSpecifiers = getExportSpecifiers(astPath.node);
|
||||||
const localMap = getLocalNameSpecifiers(astPath.node);
|
const localMap = getLocalNameSpecifiers(astPath.node);
|
||||||
const source = astPath.node.source?.value;
|
const source = astPath.node.source?.value || astPath.node.source?.name;
|
||||||
const entry = { exportSpecifiers, localMap, source, __tmp: { astPath } };
|
const entry = { exportSpecifiers, localMap, source, __tmp: { astPath } };
|
||||||
const assertionType = getAssertionType(astPath.node);
|
const assertionType = getAssertionType(astPath.node);
|
||||||
if (assertionType) {
|
if (assertionType) {
|
||||||
|
|
@ -180,15 +192,18 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
||||||
|
|
||||||
const exportDefaultHandler = (/** @type {SwcPath} */ astPath) => {
|
const exportDefaultHandler = (/** @type {SwcPath} */ astPath) => {
|
||||||
const exportSpecifiers = ['[default]'];
|
const exportSpecifiers = ['[default]'];
|
||||||
|
const { node } = astPath;
|
||||||
let source;
|
let source;
|
||||||
// Is it an inline declaration like "export default class X {};" ?
|
// Is it an inline declaration like "export default class X {};" ?
|
||||||
if (
|
if (
|
||||||
astPath.node.decl?.type === 'Identifier' ||
|
node.decl?.type === 'Identifier' ||
|
||||||
astPath.node.expression?.type === 'Identifier'
|
node.expression?.type === 'Identifier' ||
|
||||||
|
node.declaration?.type === 'Identifier'
|
||||||
) {
|
) {
|
||||||
// It is a reference to an identifier like "export { x } from 'y';"
|
// It is a reference to an identifier like "export { x } from 'y';"
|
||||||
const importOrDeclPath = getReferencedDeclaration({
|
const importOrDeclPath = getReferencedDeclaration({
|
||||||
referencedIdentifierName: astPath.node.decl?.value || astPath.node.expression.value,
|
referencedIdentifierName:
|
||||||
|
node.decl?.value || node.expression?.value || node.declaration?.name,
|
||||||
globalScopeBindings,
|
globalScopeBindings,
|
||||||
});
|
});
|
||||||
if (isImportingSpecifier(importOrDeclPath)) {
|
if (isImportingSpecifier(importOrDeclPath)) {
|
||||||
|
|
@ -198,18 +213,23 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
||||||
transformedFile.push({ exportSpecifiers, source, __tmp: { astPath } });
|
transformedFile.push({ exportSpecifiers, source, __tmp: { astPath } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const globalScopeHandler = ({ scope }) => {
|
||||||
|
globalScopeBindings = scope.bindings;
|
||||||
|
};
|
||||||
|
|
||||||
/** @type {SwcVisitor} */
|
/** @type {SwcVisitor} */
|
||||||
const visitor = {
|
const visitor = {
|
||||||
Module({ scope }) {
|
// for swc
|
||||||
globalScopeBindings = scope.bindings;
|
Module: globalScopeHandler,
|
||||||
},
|
// for oxc and babel
|
||||||
|
Program: globalScopeHandler,
|
||||||
ExportDeclaration: exportHandler,
|
ExportDeclaration: exportHandler,
|
||||||
ExportNamedDeclaration: exportHandler,
|
ExportNamedDeclaration: exportHandler,
|
||||||
ExportDefaultDeclaration: exportDefaultHandler,
|
ExportDefaultDeclaration: exportDefaultHandler,
|
||||||
ExportDefaultExpression: exportDefaultHandler,
|
ExportDefaultExpression: exportDefaultHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
if (!skipFileImports) {
|
if (!skipFileImports) {
|
||||||
// Always add an entry for just the file 'relativePath'
|
// Always add an entry for just the file 'relativePath'
|
||||||
|
|
@ -226,7 +246,7 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
||||||
export default class FindExportsAnalyzer extends Analyzer {
|
export default class FindExportsAnalyzer extends Analyzer {
|
||||||
static analyzerName = /** @type {AnalyzerName} */ ('find-exports');
|
static analyzerName = /** @type {AnalyzerName} */ ('find-exports');
|
||||||
|
|
||||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef FindExportsConfig
|
* @typedef FindExportsConfig
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { normalizeSourcePaths } from './helpers/normalize-source-paths.js';
|
import { normalizeSourcePaths } from './helpers/normalize-source-paths.js';
|
||||||
import { isRelativeSourcePath } from '../utils/relative-source-path.js';
|
import { isRelativeSourcePath } from '../utils/relative-source-path.js';
|
||||||
import { getAssertionType } from '../utils/get-assertion-type.js';
|
import { getAssertionType } from '../utils/get-assertion-type.js';
|
||||||
import { swcTraverse } from '../utils/swc-traverse.js';
|
import { oxcTraverse } from '../utils/oxc-traverse.js';
|
||||||
import { LogService } from '../core/LogService.js';
|
import { LogService } from '../core/LogService.js';
|
||||||
import { Analyzer } from '../core/Analyzer.js';
|
import { Analyzer } from '../core/Analyzer.js';
|
||||||
|
|
||||||
|
|
@ -13,49 +13,80 @@ import { Analyzer } from '../core/Analyzer.js';
|
||||||
* @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig
|
* @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig
|
||||||
* @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName
|
* @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName
|
||||||
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||||
* @typedef {import("@swc/core").Module} SwcAstModule
|
* @typedef {import("@swc/core").Module} oxcAstModule
|
||||||
* @typedef {import("@swc/core").Node} SwcNode
|
* @typedef {import("@swc/core").Node} SwcNode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// const exportedNames = ['exported'];
|
||||||
|
// const importedNames = ['orig', 'imported', 'local'];
|
||||||
|
// const valueNames = ['name', 'value'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intends to work for oxc, swc, and babel asts
|
||||||
|
*/
|
||||||
|
function getSpecifierValue(s) {
|
||||||
|
// for (const exportedorImportedName of [...exportedNames, ...importedNames]) {
|
||||||
|
// for (const valueName of valueNames) {
|
||||||
|
// const result = s[exportedorImportedName][valueName];
|
||||||
|
// if (result) return result;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
// These are regular import values and must be checked first
|
||||||
|
s.imported?.value ||
|
||||||
|
s.imported?.name ||
|
||||||
|
s.orig?.value ||
|
||||||
|
s.orig?.name ||
|
||||||
|
s.local?.value ||
|
||||||
|
s.local?.name ||
|
||||||
|
// Re-export
|
||||||
|
s.exported?.value ||
|
||||||
|
s.exported?.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SwcNode} node
|
* @param {SwcNode} node
|
||||||
*/
|
*/
|
||||||
function getImportOrReexportsSpecifiers(node) {
|
function getImportOrReexportsSpecifiers(node) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
return node.specifiers.map(s => {
|
return (node.specifiers || []).map(s => {
|
||||||
if (
|
if (
|
||||||
s.type === 'ImportDefaultSpecifier' ||
|
s.type === 'ImportDefaultSpecifier' ||
|
||||||
s.type === 'ExportDefaultSpecifier' ||
|
s.type === 'ExportDefaultSpecifier' ||
|
||||||
(s.type === 'ExportSpecifier' && s.exported?.value === 'default')
|
(s.type === 'ExportSpecifier' &&
|
||||||
|
(s.exported?.value === 'default' || s.exported?.name === 'default'))
|
||||||
) {
|
) {
|
||||||
return '[default]';
|
return '[default]';
|
||||||
}
|
}
|
||||||
if (s.type === 'ImportNamespaceSpecifier' || s.type === 'ExportNamespaceSpecifier') {
|
if (s.type === 'ImportNamespaceSpecifier' || s.type === 'ExportNamespaceSpecifier') {
|
||||||
return '[*]';
|
return '[*]';
|
||||||
}
|
}
|
||||||
const importedValue = s.imported?.value || s.orig?.value || s.exported?.value || s.local?.value;
|
const importedValue = getSpecifierValue(s);
|
||||||
return importedValue;
|
return importedValue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds import specifiers and sources
|
* Finds import specifiers and sources
|
||||||
* @param {SwcAstModule} swcAst
|
* @param {oxcAstModule} oxcAst
|
||||||
*/
|
*/
|
||||||
function findImportsPerAstFile(swcAst) {
|
function findImportsPerAstFile(oxcAst) {
|
||||||
LogService.debug(`Analyzer "find-imports": started findImportsPerAstFile method`);
|
LogService.debug(`Analyzer "find-imports": started findImportsPerAstFile method`);
|
||||||
|
|
||||||
// https://github.com/babel/babel/blob/672a58660f0b15691c44582f1f3fdcdac0fa0d2f/packages/babel-core/src/transformation/index.ts#L110
|
// https://github.com/babel/babel/blob/672a58660f0b15691c44582f1f3fdcdac0fa0d2f/packages/babel-core/src/transformation/index.ts#L110
|
||||||
// Visit AST...
|
// Visit AST...
|
||||||
/** @type {Partial<FindImportsAnalyzerEntry>[]} */
|
/** @type {Partial<FindImportsAnalyzerEntry>[]} */
|
||||||
const transformedFile = [];
|
const transformedFile = [];
|
||||||
|
oxcTraverse(oxcAst, {
|
||||||
swcTraverse(swcAst, {
|
|
||||||
ImportDeclaration({ node }) {
|
ImportDeclaration({ node }) {
|
||||||
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
||||||
if (!importSpecifiers.length) {
|
if (!importSpecifiers.length) {
|
||||||
importSpecifiers.push('[file]'); // apparently, there was just a file import
|
importSpecifiers.push('[file]'); // apparently, there was just a file import
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = node.source.value;
|
const source = node.source.value;
|
||||||
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
||||||
const assertionType = getAssertionType(node);
|
const assertionType = getAssertionType(node);
|
||||||
|
|
@ -65,9 +96,9 @@ function findImportsPerAstFile(swcAst) {
|
||||||
transformedFile.push(entry);
|
transformedFile.push(entry);
|
||||||
},
|
},
|
||||||
ExportNamedDeclaration({ node }) {
|
ExportNamedDeclaration({ node }) {
|
||||||
if (!node.source) {
|
// Are we dealing with a regular export, not a re-export?
|
||||||
return; // we are dealing with a regular export, not a reexport
|
if (!node.source) return;
|
||||||
}
|
|
||||||
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
||||||
const source = node.source.value;
|
const source = node.source.value;
|
||||||
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
||||||
|
|
@ -77,7 +108,22 @@ function findImportsPerAstFile(swcAst) {
|
||||||
}
|
}
|
||||||
transformedFile.push(entry);
|
transformedFile.push(entry);
|
||||||
},
|
},
|
||||||
// Dynamic imports
|
ExportAllDeclaration({ node }) {
|
||||||
|
// Are we dealing with a regular export, not a re-export?
|
||||||
|
if (!node.source) return;
|
||||||
|
|
||||||
|
const importSpecifiers = ['[*]'];
|
||||||
|
|
||||||
|
const source = node.source.value;
|
||||||
|
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
||||||
|
const assertionType = getAssertionType(node);
|
||||||
|
if (assertionType) {
|
||||||
|
entry.assertionType = assertionType;
|
||||||
|
}
|
||||||
|
transformedFile.push(entry);
|
||||||
|
},
|
||||||
|
// Dynamic imports for swc
|
||||||
|
// TODO: remove if swc is completely phased out
|
||||||
CallExpression({ node }) {
|
CallExpression({ node }) {
|
||||||
if (node.callee?.type !== 'Import') {
|
if (node.callee?.type !== 'Import') {
|
||||||
return;
|
return;
|
||||||
|
|
@ -92,6 +138,21 @@ function findImportsPerAstFile(swcAst) {
|
||||||
: '[variable]';
|
: '[variable]';
|
||||||
transformedFile.push({ importSpecifiers, source });
|
transformedFile.push({ importSpecifiers, source });
|
||||||
},
|
},
|
||||||
|
// Dynamic imports for oxc
|
||||||
|
|
||||||
|
ExpressionStatement({ node }) {
|
||||||
|
if (node.expression.type !== 'ImportExpression') return;
|
||||||
|
|
||||||
|
// TODO: check for specifiers catched via obj destructuring?
|
||||||
|
// TODO: also check for ['file']
|
||||||
|
const importSpecifiers = ['[default]'];
|
||||||
|
const dynamicImportExpression = node.expression;
|
||||||
|
const source =
|
||||||
|
dynamicImportExpression.source?.type === 'StringLiteral'
|
||||||
|
? dynamicImportExpression.source.value
|
||||||
|
: '[variable]';
|
||||||
|
transformedFile.push({ importSpecifiers, source });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return transformedFile;
|
return transformedFile;
|
||||||
|
|
@ -100,7 +161,7 @@ function findImportsPerAstFile(swcAst) {
|
||||||
export default class FindImportsSwcAnalyzer extends Analyzer {
|
export default class FindImportsSwcAnalyzer extends Analyzer {
|
||||||
static analyzerName = /** @type {AnalyzerName} */ ('find-imports');
|
static analyzerName = /** @type {AnalyzerName} */ ('find-imports');
|
||||||
|
|
||||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds import specifiers and sources
|
* Finds import specifiers and sources
|
||||||
|
|
@ -132,9 +193,9 @@ export default class FindImportsSwcAnalyzer extends Analyzer {
|
||||||
/**
|
/**
|
||||||
* Traverse
|
* Traverse
|
||||||
*/
|
*/
|
||||||
const queryOutput = await this._traverse(async (swcAst, context) => {
|
const queryOutput = await this._traverse(async (oxcAst, context) => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
let transformedFile = findImportsPerAstFile(swcAst);
|
let transformedFile = findImportsPerAstFile(oxcAst);
|
||||||
// Post processing based on configuration...
|
// Post processing based on configuration...
|
||||||
transformedFile = await normalizeSourcePaths(
|
transformedFile = await normalizeSourcePaths(
|
||||||
transformedFile,
|
transformedFile,
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,8 @@ async function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerRes
|
||||||
export default class MatchImportsAnalyzer extends Analyzer {
|
export default class MatchImportsAnalyzer extends Analyzer {
|
||||||
static analyzerName = /** @type {AnalyzerName} */ ('match-imports');
|
static analyzerName = /** @type {AnalyzerName} */ ('match-imports');
|
||||||
|
|
||||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
// N.B. implicit
|
||||||
|
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||||
|
|
||||||
static requiresReference = true;
|
static requiresReference = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ function storeResult(resultsObj, exportId, filteredList, meta) {
|
||||||
* @param {FindClassesAnalyzerResult} targetClassesAnalyzerResult
|
* @param {FindClassesAnalyzerResult} targetClassesAnalyzerResult
|
||||||
* @param {FindClassesAnalyzerResult} refClassesAResult
|
* @param {FindClassesAnalyzerResult} refClassesAResult
|
||||||
* @param {MatchSubclassesConfig} customConfig
|
* @param {MatchSubclassesConfig} customConfig
|
||||||
* @returns {AnalyzerQueryResult}
|
* @returns {Promise<AnalyzerQueryResult>}
|
||||||
*/
|
*/
|
||||||
async function matchSubclassesPostprocess(
|
async function matchSubclassesPostprocess(
|
||||||
refExportsAnalyzerResult,
|
refExportsAnalyzerResult,
|
||||||
|
|
@ -281,7 +281,7 @@ export default class MatchSubclassesAnalyzer extends Analyzer {
|
||||||
/** @type {AnalyzerName} */
|
/** @type {AnalyzerName} */
|
||||||
static analyzerName = 'match-subclasses';
|
static analyzerName = 'match-subclasses';
|
||||||
|
|
||||||
static requiredAst = /** @type {AnalyzerAst} */ ('babel');
|
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||||
|
|
||||||
static requiresReference = true;
|
static requiresReference = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
import babelParser from '@babel/parser';
|
|
||||||
import * as parse5 from 'parse5';
|
import * as parse5 from 'parse5';
|
||||||
import swc from '@swc/core';
|
|
||||||
import { traverseHtml } from '../utils/traverse-html.js';
|
import { traverseHtml } from '../utils/traverse-html.js';
|
||||||
import { LogService } from './LogService.js';
|
import { LogService } from './LogService.js';
|
||||||
import { guardedSwcToBabel } from '../utils/guarded-swc-to-babel.js';
|
|
||||||
|
/** @type {import('@babel/parser')} */
|
||||||
|
let babelParser;
|
||||||
|
/** @type {import('@swc/core')} */
|
||||||
|
let swcParser;
|
||||||
|
/** @type {import('oxc-parser')} */
|
||||||
|
let oxcParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("@babel/types").File} File
|
|
||||||
* @typedef {import("@swc/core").Module} SwcAstModule
|
|
||||||
* @typedef {import("@babel/parser").ParserOptions} ParserOptions
|
|
||||||
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
||||||
|
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||||
|
* @typedef {import("oxc-parser").ParseResult} OxcParseResult
|
||||||
|
* @typedef {import("@babel/parser").ParserOptions} ParserOptions
|
||||||
|
* @typedef {import("@swc/core").Module} SwcAstModule
|
||||||
|
* @typedef {import("@babel/types").File} File
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class AstService {
|
export class AstService {
|
||||||
|
|
@ -17,9 +23,13 @@ export class AstService {
|
||||||
* Compiles an array of file paths using Babel.
|
* Compiles an array of file paths using Babel.
|
||||||
* @param {string} code
|
* @param {string} code
|
||||||
* @param {ParserOptions} parserOptions
|
* @param {ParserOptions} parserOptions
|
||||||
* @returns {File}
|
* @returns {Promise<File>}
|
||||||
*/
|
*/
|
||||||
static _getBabelAst(code, parserOptions = {}) {
|
static async _getBabelAst(code, parserOptions = {}) {
|
||||||
|
if (!babelParser) {
|
||||||
|
babelParser = (await import('@babel/parser')).default;
|
||||||
|
}
|
||||||
|
|
||||||
const ast = babelParser.parse(code, {
|
const ast = babelParser.parse(code, {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
@ -34,32 +44,18 @@ export class AstService {
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiles an array of file paths using Babel.
|
|
||||||
* @param {string} code
|
|
||||||
* @param {ParserOptions} parserOptions
|
|
||||||
* @returns {File}
|
|
||||||
*/
|
|
||||||
static _getSwcToBabelAst(code, parserOptions = {}) {
|
|
||||||
if (this.fallbackToBabel) {
|
|
||||||
return this._getBabelAst(code, parserOptions);
|
|
||||||
}
|
|
||||||
const ast = swc.parseSync(code, {
|
|
||||||
syntax: 'typescript',
|
|
||||||
// importAssertions: true,
|
|
||||||
...parserOptions,
|
|
||||||
});
|
|
||||||
return guardedSwcToBabel(ast, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles an array of file paths using swc.
|
* Compiles an array of file paths using swc.
|
||||||
* @param {string} code
|
* @param {string} code
|
||||||
* @param {ParserOptions} parserOptions
|
* @param {ParserOptions} parserOptions
|
||||||
* @returns {SwcAstModule}
|
* @returns {Promise<SwcAstModule>}
|
||||||
*/
|
*/
|
||||||
static _getSwcAst(code, parserOptions = {}) {
|
static async _getSwcAst(code, parserOptions = {}) {
|
||||||
const ast = swc.parseSync(code, {
|
if (!swcParser) {
|
||||||
|
swcParser = (await import('@swc/core')).default;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ast = swcParser.parseSync(code, {
|
||||||
syntax: 'typescript',
|
syntax: 'typescript',
|
||||||
target: 'es2022',
|
target: 'es2022',
|
||||||
...parserOptions,
|
...parserOptions,
|
||||||
|
|
@ -72,7 +68,22 @@ export class AstService {
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
static _getSwcOffset() {
|
static _getSwcOffset() {
|
||||||
return swc.parseSync('').span.end;
|
return swcParser.parseSync('').span.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles an array of file paths using swc.
|
||||||
|
* @param {string} code
|
||||||
|
* @param {ParserOptions} parserOptions
|
||||||
|
* @returns {Promise<OxcParseResult>}
|
||||||
|
*/
|
||||||
|
static async _getOxcAst(code, parserOptions = {}) {
|
||||||
|
if (!oxcParser) {
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
oxcParser = (await import('oxc-parser')).default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oxcParser.parseSync(code, parserOptions).program;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -100,22 +111,22 @@ export class AstService {
|
||||||
/**
|
/**
|
||||||
* Returns the Babel AST
|
* Returns the Babel AST
|
||||||
* @param { string } code
|
* @param { string } code
|
||||||
* @param { 'babel'|'swc-to-babel'|'swc'} astType
|
* @param {AnalyzerAst} astType
|
||||||
* @param { {filePath?: PathFromSystemRoot} } options
|
* @param { {filePath?: PathFromSystemRoot} } options
|
||||||
* @returns {File|undefined|SwcAstModule}
|
* @returns {Promise<File|undefined|SwcAstModule|OxcParseResult>}
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
static getAst(code, astType, { filePath } = {}) {
|
static async getAst(code, astType, { filePath } = {}) {
|
||||||
// eslint-disable-next-line default-case
|
// eslint-disable-next-line default-case
|
||||||
try {
|
try {
|
||||||
if (astType === 'babel') {
|
if (astType === 'babel') {
|
||||||
return this._getBabelAst(code);
|
return await this._getBabelAst(code);
|
||||||
}
|
|
||||||
if (astType === 'swc-to-babel') {
|
|
||||||
return this._getSwcToBabelAst(code);
|
|
||||||
}
|
}
|
||||||
if (astType === 'swc') {
|
if (astType === 'swc') {
|
||||||
return this._getSwcAst(code);
|
return await this._getSwcAst(code);
|
||||||
|
}
|
||||||
|
if (astType === 'oxc') {
|
||||||
|
return await this._getOxcAst(code);
|
||||||
}
|
}
|
||||||
throw new Error(`astType "${astType}" not supported.`);
|
throw new Error(`astType "${astType}" not supported.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -107,20 +107,27 @@ export class QueryService {
|
||||||
* @param {AnalyzerAst} requiredAst
|
* @param {AnalyzerAst} requiredAst
|
||||||
*/
|
*/
|
||||||
static async addAstToProjectsData(projectsData, requiredAst) {
|
static async addAstToProjectsData(projectsData, requiredAst) {
|
||||||
return projectsData.map(projectData => {
|
const resultWithAsts = [];
|
||||||
|
|
||||||
|
for (const projectData of projectsData) {
|
||||||
const cachedData = astProjectsDataCache.get(projectData.project.path);
|
const cachedData = astProjectsDataCache.get(projectData.project.path);
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
return cachedData;
|
return cachedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultEntries = projectData.entries.map(entry => {
|
const resultEntries = [];
|
||||||
const ast = AstService.getAst(entry.context.code, requiredAst, { filePath: entry.file });
|
for (const entry of projectData.entries) {
|
||||||
return { ...entry, ast };
|
const ast = await AstService.getAst(entry.context.code, requiredAst, {
|
||||||
|
filePath: entry.file,
|
||||||
});
|
});
|
||||||
|
resultEntries.push({ ...entry, ast });
|
||||||
|
}
|
||||||
const astData = { ...projectData, entries: resultEntries };
|
const astData = { ...projectData, entries: resultEntries };
|
||||||
this._addToProjectsDataCache(`${projectData.project.path}#${requiredAst}`, astData);
|
this._addToProjectsDataCache(`${projectData.project.path}#${requiredAst}`, astData);
|
||||||
return astData;
|
resultWithAsts.push(astData);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return resultWithAsts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,9 @@ export function getAssertionType(node) {
|
||||||
if (node.assertions) {
|
if (node.assertions) {
|
||||||
return node.assertions.properties[0].value?.value;
|
return node.assertions.properties[0].value?.value;
|
||||||
}
|
}
|
||||||
|
// oxc
|
||||||
|
if (node.withClause) {
|
||||||
|
return node.withClause.withEntries[0].value?.name;
|
||||||
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import babelTraversePkg from '@babel/traverse';
|
|
||||||
|
|
||||||
import { trackDownIdentifier } from './track-down-identifier.js';
|
|
||||||
import { AstService } from '../core/AstService.js';
|
|
||||||
import { toPosixPath } from './to-posix-path.js';
|
|
||||||
import { fsAdapter } from './fs-adapter.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot
|
|
||||||
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
|
||||||
* @typedef {import('@babel/traverse').NodePath} NodePath
|
|
||||||
* @typedef {import('@babel/types').Node} Node
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {{rootPath:PathFromSystemRoot; localPath:PathRelativeFromProjectRoot}} opts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getFilePathOrExternalSource({ rootPath, localPath }) {
|
|
||||||
if (!localPath.startsWith('.')) {
|
|
||||||
// We are not resolving external files like '@lion/input-amount/x.js',
|
|
||||||
// but we give a 100% score if from and to are same here..
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
return toPosixPath(path.resolve(rootPath, localPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assume we had:
|
|
||||||
* ```js
|
|
||||||
* const x = 88;
|
|
||||||
* const y = x;
|
|
||||||
* export const myIdentifier = y;
|
|
||||||
* ```
|
|
||||||
* - We started in getSourceCodeFragmentOfDeclaration (looking for 'myIdentifier'), which found VariableDeclarator of export myIdentifier
|
|
||||||
* - getReferencedDeclaration is called with { referencedIdentifierName: 'y', ... }
|
|
||||||
* - now we will look in globalScopeBindings, till we find declaration of 'y'
|
|
||||||
* - Is it a ref? Call ourselves with referencedIdentifierName ('x' in example above)
|
|
||||||
* - is it a non ref declaration? Return the path of the node
|
|
||||||
* @param {{ referencedIdentifierName:string, globalScopeBindings:BabelBinding; }} opts
|
|
||||||
* @returns {NodePath}
|
|
||||||
*/
|
|
||||||
export function getReferencedDeclaration({ referencedIdentifierName, globalScopeBindings }) {
|
|
||||||
const [, refDeclaratorBinding] = Object.entries(globalScopeBindings).find(
|
|
||||||
([key]) => key === referencedIdentifierName,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
refDeclaratorBinding.path.type === 'ImportSpecifier' ||
|
|
||||||
refDeclaratorBinding.path.type === 'ImportDefaultSpecifier'
|
|
||||||
) {
|
|
||||||
return refDeclaratorBinding.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refDeclaratorBinding.path.node.init.type === 'Identifier') {
|
|
||||||
return getReferencedDeclaration({
|
|
||||||
referencedIdentifierName: refDeclaratorBinding.path.node.init.name,
|
|
||||||
globalScopeBindings,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return refDeclaratorBinding.path.get('init');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* // ------ input file --------
|
|
||||||
* const x = 88;
|
|
||||||
* const y = x;
|
|
||||||
* export const myIdentifier = y;
|
|
||||||
* // --------------------------
|
|
||||||
*
|
|
||||||
* await getSourceCodeFragmentOfDeclaration(code) // finds "88"
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param {{ filePath: PathFromSystemRoot; exportedIdentifier: string; projectRootPath: PathFromSystemRoot }} opts
|
|
||||||
* @returns {Promise<{ sourceNodePath: string; sourceFragment: string|null; externalImportSource: string; }>}
|
|
||||||
*/
|
|
||||||
export async function getSourceCodeFragmentOfDeclaration({
|
|
||||||
filePath,
|
|
||||||
exportedIdentifier,
|
|
||||||
projectRootPath,
|
|
||||||
}) {
|
|
||||||
const code = fsAdapter.fs.readFileSync(filePath, 'utf8');
|
|
||||||
// TODO: fix swc-to-babel lib to make this compatible with 'swc-to-babel' mode of getAst
|
|
||||||
const babelAst = AstService.getAst(code, 'babel', { filePath });
|
|
||||||
|
|
||||||
/** @type {NodePath} */
|
|
||||||
let finalNodePath;
|
|
||||||
|
|
||||||
babelTraversePkg.default(babelAst, {
|
|
||||||
Program(astPath) {
|
|
||||||
astPath.stop();
|
|
||||||
|
|
||||||
// Situations
|
|
||||||
// - Identifier is part of default export (in this case 'exportedIdentifier' is '[default]' )
|
|
||||||
// - declared right away (for instance a class)
|
|
||||||
// - referenced (possibly recursively) by other declaration
|
|
||||||
// - Identifier is part of a named export
|
|
||||||
// - declared right away
|
|
||||||
// - referenced (possibly recursively) by other declaration
|
|
||||||
|
|
||||||
const globalScopeBindings = astPath.get('body')[0].scope.bindings;
|
|
||||||
|
|
||||||
if (exportedIdentifier === '[default]') {
|
|
||||||
const defaultExportPath = astPath
|
|
||||||
.get('body')
|
|
||||||
.find(child => child.node.type === 'ExportDefaultDeclaration');
|
|
||||||
// @ts-expect-error
|
|
||||||
const isReferenced = defaultExportPath?.node.declaration?.type === 'Identifier';
|
|
||||||
|
|
||||||
if (!isReferenced) {
|
|
||||||
finalNodePath = defaultExportPath.get('declaration');
|
|
||||||
} else {
|
|
||||||
finalNodePath = getReferencedDeclaration({
|
|
||||||
referencedIdentifierName: defaultExportPath.node.declaration.name,
|
|
||||||
globalScopeBindings,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const variableDeclaratorPath = astPath.scope.getBinding(exportedIdentifier).path;
|
|
||||||
const varDeclNode = variableDeclaratorPath.node;
|
|
||||||
const isReferenced = varDeclNode.init?.type === 'Identifier';
|
|
||||||
const contentPath = varDeclNode.init
|
|
||||||
? variableDeclaratorPath.get('init')
|
|
||||||
: variableDeclaratorPath;
|
|
||||||
|
|
||||||
const name = varDeclNode.init
|
|
||||||
? varDeclNode.init.name
|
|
||||||
: varDeclNode.id?.name || varDeclNode.imported.name;
|
|
||||||
|
|
||||||
if (!isReferenced) {
|
|
||||||
// it must be an exported declaration
|
|
||||||
finalNodePath = contentPath;
|
|
||||||
} else {
|
|
||||||
finalNodePath = getReferencedDeclaration({
|
|
||||||
referencedIdentifierName: name,
|
|
||||||
globalScopeBindings,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (finalNodePath.type === 'ImportSpecifier') {
|
|
||||||
const importDeclNode = finalNodePath.parentPath.node;
|
|
||||||
const source = importDeclNode.source.value;
|
|
||||||
const identifierName = finalNodePath.node.imported.name;
|
|
||||||
const currentFilePath = filePath;
|
|
||||||
|
|
||||||
const rootFile = await trackDownIdentifier(
|
|
||||||
source,
|
|
||||||
identifierName,
|
|
||||||
currentFilePath,
|
|
||||||
projectRootPath,
|
|
||||||
);
|
|
||||||
const filePathOrSrc = getFilePathOrExternalSource({
|
|
||||||
rootPath: projectRootPath,
|
|
||||||
localPath: rootFile.file,
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: allow resolving external project file paths
|
|
||||||
if (!filePathOrSrc.startsWith('/')) {
|
|
||||||
// So we have external project; smth like '@lion/input/x.js'
|
|
||||||
return {
|
|
||||||
sourceNodePath: finalNodePath,
|
|
||||||
sourceFragment: null,
|
|
||||||
externalImportSource: filePathOrSrc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return getSourceCodeFragmentOfDeclaration({
|
|
||||||
filePath: filePathOrSrc,
|
|
||||||
exportedIdentifier: rootFile.specifier,
|
|
||||||
projectRootPath,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sourceNodePath: finalNodePath,
|
|
||||||
sourceFragment: code.slice(
|
|
||||||
finalNodePath.node?.loc?.start.index,
|
|
||||||
finalNodePath.node?.loc?.end.index,
|
|
||||||
),
|
|
||||||
externalImportSource: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import { oxcTraverse, getPathFromNode, nameOf } from './oxc-traverse.js';
|
||||||
import { trackDownIdentifier } from './track-down-identifier.js';
|
import { trackDownIdentifier } from './track-down-identifier.js';
|
||||||
import { swcTraverse, getPathFromNode } from './swc-traverse.js';
|
|
||||||
import { AstService } from '../core/AstService.js';
|
import { AstService } from '../core/AstService.js';
|
||||||
import { toPosixPath } from './to-posix-path.js';
|
import { toPosixPath } from './to-posix-path.js';
|
||||||
import { fsAdapter } from './fs-adapter.js';
|
import { fsAdapter } from './fs-adapter.js';
|
||||||
|
|
@ -9,6 +9,7 @@ import { fsAdapter } from './fs-adapter.js';
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot
|
* @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot
|
||||||
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
||||||
|
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||||
* @typedef {import('../../../types/index.js').SwcBinding} SwcBinding
|
* @typedef {import('../../../types/index.js').SwcBinding} SwcBinding
|
||||||
* @typedef {import('../../../types/index.js').SwcPath} SwcPath
|
* @typedef {import('../../../types/index.js').SwcPath} SwcPath
|
||||||
* @typedef {import('@swc/core').Node} SwcNode
|
* @typedef {import('@swc/core').Node} SwcNode
|
||||||
|
|
@ -57,9 +58,9 @@ export function getReferencedDeclaration({ referencedIdentifierName, globalScope
|
||||||
return refDeclaratorBinding.path;
|
return refDeclaratorBinding.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refDeclaratorBinding.identifier.init.type === 'Identifier') {
|
if (refDeclaratorBinding.path.node.init.type === 'Identifier') {
|
||||||
return getReferencedDeclaration({
|
return getReferencedDeclaration({
|
||||||
referencedIdentifierName: refDeclaratorBinding.identifier.init.value,
|
referencedIdentifierName: nameOf(refDeclaratorBinding.path.node.init),
|
||||||
globalScopeBindings,
|
globalScopeBindings,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -79,28 +80,25 @@ export function getReferencedDeclaration({ referencedIdentifierName, globalScope
|
||||||
* await getSourceCodeFragmentOfDeclaration(code) // finds "88"
|
* await getSourceCodeFragmentOfDeclaration(code) // finds "88"
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param {{ filePath: PathFromSystemRoot; exportedIdentifier: string; projectRootPath: PathFromSystemRoot }} opts
|
* @param {{ filePath: PathFromSystemRoot; exportedIdentifier: string; projectRootPath: PathFromSystemRoot; parser: AnalyzerAst }} opts
|
||||||
* @returns {Promise<{ sourceNodePath: SwcPath; sourceFragment: string|null; externalImportSource: string|null; }>}
|
* @returns {Promise<{ sourceNodePath: SwcPath; sourceFragment: string|null; externalImportSource: string|null; }>}
|
||||||
*/
|
*/
|
||||||
export async function getSourceCodeFragmentOfDeclaration({
|
export async function getSourceCodeFragmentOfDeclaration({
|
||||||
exportedIdentifier,
|
exportedIdentifier,
|
||||||
projectRootPath,
|
projectRootPath,
|
||||||
|
parser = 'oxc',
|
||||||
filePath,
|
filePath,
|
||||||
}) {
|
}) {
|
||||||
const code = fsAdapter.fs.readFileSync(filePath, 'utf8');
|
const code = await fsAdapter.fs.promises.readFile(filePath, 'utf8');
|
||||||
|
|
||||||
// compensate for swc span bug: https://github.com/swc-project/swc/issues/1366#issuecomment-1516539812
|
// compensate for swc span bug: https://github.com/swc-project/swc/issues/1366#issuecomment-1516539812
|
||||||
const offset = AstService._getSwcOffset();
|
const offset = parser === 'swc' ? await AstService._getSwcOffset() : -1;
|
||||||
// TODO: fix swc-to-babel lib to make this compatible with 'swc-to-babel' mode of getAst
|
const ast = await AstService.getAst(code, parser);
|
||||||
const swcAst = AstService._getSwcAst(code);
|
|
||||||
|
|
||||||
/** @type {SwcPath} */
|
/** @type {SwcPath} */
|
||||||
let finalNodePath;
|
let finalNodePath;
|
||||||
|
|
||||||
swcTraverse(
|
const moduleOrProgramHandler = astPath => {
|
||||||
swcAst,
|
|
||||||
{
|
|
||||||
Module(astPath) {
|
|
||||||
astPath.stop();
|
astPath.stop();
|
||||||
|
|
||||||
// Situations
|
// Situations
|
||||||
|
|
@ -121,14 +119,22 @@ export async function getSourceCodeFragmentOfDeclaration({
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const isReferenced = defaultExportPath?.node.expression?.type === 'Identifier';
|
|
||||||
|
const isReferenced =
|
||||||
|
(defaultExportPath?.node.declaration?.type || defaultExportPath?.node.expression?.type) ===
|
||||||
|
'Identifier';
|
||||||
|
|
||||||
if (!isReferenced) {
|
if (!isReferenced) {
|
||||||
finalNodePath = defaultExportPath.get('decl') || defaultExportPath.get('expression');
|
finalNodePath =
|
||||||
|
defaultExportPath.get('declaration') ||
|
||||||
|
defaultExportPath.get('decl') ||
|
||||||
|
defaultExportPath.get('expression');
|
||||||
} else {
|
} else {
|
||||||
finalNodePath = /** @type {SwcPath} */ (
|
finalNodePath = /** @type {SwcPath} */ (
|
||||||
getReferencedDeclaration({
|
getReferencedDeclaration({
|
||||||
referencedIdentifierName: defaultExportPath.node.expression.value,
|
referencedIdentifierName: nameOf(
|
||||||
|
defaultExportPath.node.declaration || defaultExportPath.node.expression,
|
||||||
|
),
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
globalScopeBindings,
|
globalScopeBindings,
|
||||||
})
|
})
|
||||||
|
|
@ -143,8 +149,8 @@ export async function getSourceCodeFragmentOfDeclaration({
|
||||||
: variableDeclaratorPath;
|
: variableDeclaratorPath;
|
||||||
|
|
||||||
const name = varDeclNode.init
|
const name = varDeclNode.init
|
||||||
? varDeclNode.init.value
|
? nameOf(varDeclNode.init)
|
||||||
: varDeclNode.id?.value || varDeclNode.imported?.value || varDeclNode.orig?.value;
|
: nameOf(varDeclNode.id) || nameOf(varDeclNode.imported) || nameOf(varDeclNode.orig);
|
||||||
|
|
||||||
if (!isReferenced) {
|
if (!isReferenced) {
|
||||||
// it must be an exported declaration
|
// it must be an exported declaration
|
||||||
|
|
@ -159,7 +165,13 @@ export async function getSourceCodeFragmentOfDeclaration({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
oxcTraverse(
|
||||||
|
ast,
|
||||||
|
{
|
||||||
|
Module: moduleOrProgramHandler,
|
||||||
|
Program: moduleOrProgramHandler,
|
||||||
},
|
},
|
||||||
{ needsAdvancedPaths: true },
|
{ needsAdvancedPaths: true },
|
||||||
);
|
);
|
||||||
|
|
@ -168,9 +180,9 @@ export async function getSourceCodeFragmentOfDeclaration({
|
||||||
if (finalNodePath.type === 'ImportSpecifier') {
|
if (finalNodePath.type === 'ImportSpecifier') {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const importDeclNode = finalNodePath.parentPath.node;
|
const importDeclNode = finalNodePath.parentPath.node;
|
||||||
const source = importDeclNode.source.value;
|
const source = nameOf(importDeclNode.source);
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const identifierName = finalNodePath.node.imported?.value || finalNodePath.node.local?.value;
|
const identifierName = nameOf(finalNodePath.node.imported) || nameOf(finalNodePath.node.local);
|
||||||
const currentFilePath = filePath;
|
const currentFilePath = filePath;
|
||||||
|
|
||||||
const rootFile = await trackDownIdentifier(
|
const rootFile = await trackDownIdentifier(
|
||||||
|
|
@ -199,17 +211,20 @@ export async function getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: /** @type {PathFromSystemRoot} */ (filePathOrSrc),
|
filePath: /** @type {PathFromSystemRoot} */ (filePathOrSrc),
|
||||||
exportedIdentifier: rootFile.specifier,
|
exportedIdentifier: rootFile.specifier,
|
||||||
projectRootPath,
|
projectRootPath,
|
||||||
|
parser,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startOf = node => node.start || node.span.start;
|
||||||
|
const endOf = node => node.end || node.span.end;
|
||||||
return {
|
return {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
sourceNodePath: finalNodePath,
|
sourceNodePath: finalNodePath,
|
||||||
sourceFragment: code.slice(
|
sourceFragment: code.slice(
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
finalNodePath.node.span.start - 1 - offset,
|
startOf(finalNodePath.node) - 1 - offset,
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
finalNodePath.node.span.end - 1 - offset,
|
endOf(finalNodePath.node) - 1 - offset,
|
||||||
),
|
),
|
||||||
// sourceFragment: finalNodePath.node?.raw || finalNodePath.node?.value,
|
// sourceFragment: finalNodePath.node?.raw || finalNodePath.node?.value,
|
||||||
externalImportSource: null,
|
externalImportSource: null,
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import toBabel from '../../../inlined-swc-to-babel/lib/swc-to-babel.cjs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {import('@babel/types').File} File
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal wrapper around swc-to-babel...
|
|
||||||
* Allows to easily switch all swc based analyzers to Babel in case
|
|
||||||
* they turn out to be not stable yet (for instance printing a transformed ast with @babel/generator)
|
|
||||||
* Checks first whether it gets a Babel ast provided or not...
|
|
||||||
* @param {*} swcOrBabelAst
|
|
||||||
* @param {string} source
|
|
||||||
* @returns {File}
|
|
||||||
*/
|
|
||||||
export function guardedSwcToBabel(swcOrBabelAst, source) {
|
|
||||||
const isSwcAst = swcOrBabelAst.type === 'Module';
|
|
||||||
if (isSwcAst) {
|
|
||||||
// @ts-ignore
|
|
||||||
return toBabel(swcOrBabelAst, source);
|
|
||||||
}
|
|
||||||
return swcOrBabelAst;
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,7 @@ export {
|
||||||
getFilePathOrExternalSource,
|
getFilePathOrExternalSource,
|
||||||
} from './get-source-code-fragment-of-declaration.js';
|
} from './get-source-code-fragment-of-declaration.js';
|
||||||
export { optimisedGlob } from './optimised-glob.js';
|
export { optimisedGlob } from './optimised-glob.js';
|
||||||
export { swcTraverse } from './swc-traverse.js';
|
export { oxcTraverse } from './oxc-traverse.js';
|
||||||
export { fsAdapter } from './fs-adapter.js';
|
export { fsAdapter } from './fs-adapter.js';
|
||||||
export { memoize } from './memoize.js';
|
export { memoize } from './memoize.js';
|
||||||
export { hash } from './hash.js';
|
export { hash } from './hash.js';
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@swc/core').Module} SwcAstModule
|
* @typedef {import('../../../types/index.js').SwcTraversalContext} SwcTraversalContext
|
||||||
* @typedef {import('@swc/core').Node} SwcNode
|
|
||||||
* @typedef {import('@swc/core').VariableDeclarator} SwcVariableDeclarator
|
* @typedef {import('@swc/core').VariableDeclarator} SwcVariableDeclarator
|
||||||
* @typedef {import('@swc/core').Identifier} SwcIdentifierNode
|
* @typedef {import('@swc/core').Identifier} SwcIdentifierNode
|
||||||
|
* @typedef {import('@swc/core').Module} SwcAstModule
|
||||||
|
* @typedef {import('@swc/core').Node} SwcNode
|
||||||
* @typedef {import('../../../types/index.js').SwcPath} SwcPath
|
* @typedef {import('../../../types/index.js').SwcPath} SwcPath
|
||||||
* @typedef {import('../../../types/index.js').SwcScope} SwcScope
|
* @typedef {import('../../../types/index.js').SwcScope} SwcScope
|
||||||
* @typedef {import('../../../types/index.js').SwcVisitor} SwcVisitor
|
* @typedef {import('../../../types/index.js').SwcVisitor} SwcVisitor
|
||||||
* @typedef {import('../../../types/index.js').SwcBinding} SwcBinding
|
* @typedef {import('../../../types/index.js').SwcBinding} SwcBinding
|
||||||
* @typedef {import('../../../types/index.js').SwcTraversalContext} SwcTraversalContext
|
* @typedef {import('oxc-parser').ParseResult} OxcNode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,6 +27,42 @@ const fnTypes = [
|
||||||
|
|
||||||
const nonBlockParentTypes = [...fnTypes, 'SwitchStatement', 'ClassDeclaration'];
|
const nonBlockParentTypes = [...fnTypes, 'SwitchStatement', 'ClassDeclaration'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SwcNode|OxcNode} node
|
||||||
|
*/
|
||||||
|
export function nameOf(node) {
|
||||||
|
// @ts-expect-error
|
||||||
|
return node.value || node.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SwcNode|OxcNode} node
|
||||||
|
*/
|
||||||
|
export function importedOf(node) {
|
||||||
|
// @ts-expect-error
|
||||||
|
// babel/oxc vs swc
|
||||||
|
return node?.imported || node?.orig || node?.local;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SwcNode|OxcNode} node
|
||||||
|
*/
|
||||||
|
export function isProperty(node) {
|
||||||
|
if (!node) return false;
|
||||||
|
|
||||||
|
switch (node.type) {
|
||||||
|
case 'ObjectProperty':
|
||||||
|
case 'ClassProperty':
|
||||||
|
case 'ClassAccessorProperty':
|
||||||
|
case 'ClassPrivateProperty':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SwcPath} swcPath
|
* @param {SwcPath} swcPath
|
||||||
* @param {SwcScope} currentScope
|
* @param {SwcScope} currentScope
|
||||||
|
|
@ -48,6 +85,15 @@ function getNewScope(swcPath, currentScope, traversalContext) {
|
||||||
parentScope: currentScope,
|
parentScope: currentScope,
|
||||||
path: swcPath,
|
path: swcPath,
|
||||||
bindings: {},
|
bindings: {},
|
||||||
|
getBinding(identifierName) {
|
||||||
|
let parentScope = currentScope;
|
||||||
|
let foundBinding;
|
||||||
|
while (!foundBinding && parentScope) {
|
||||||
|
foundBinding = parentScope.bindings[identifierName];
|
||||||
|
parentScope = parentScope.parentScope;
|
||||||
|
}
|
||||||
|
return foundBinding;
|
||||||
|
},
|
||||||
_pendingRefsWithoutBinding: [],
|
_pendingRefsWithoutBinding: [],
|
||||||
_isIsolatedBlockStatement: isIsolatedBlockStatement,
|
_isIsolatedBlockStatement: isIsolatedBlockStatement,
|
||||||
};
|
};
|
||||||
|
|
@ -83,7 +129,7 @@ function createSwcPath(node, parent, stop, scope) {
|
||||||
const swcPathForNode = getPathFromNode(node[id]);
|
const swcPathForNode = getPathFromNode(node[id]);
|
||||||
if (node[id] && !swcPathForNode) {
|
if (node[id] && !swcPathForNode) {
|
||||||
// throw new Error(
|
// throw new Error(
|
||||||
// `[swcTraverse]: Use {needsAdvancedPaths: true} to find path for node: ${node[name]}`,
|
// `[oxcTraverse]: Use {needsAdvancedPaths: true} to find path for node: ${node[name]}`,
|
||||||
// );
|
// );
|
||||||
// TODO: "pre-traverse" the missing path parts instead
|
// TODO: "pre-traverse" the missing path parts instead
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +138,10 @@ function createSwcPath(node, parent, stop, scope) {
|
||||||
get type() {
|
get type() {
|
||||||
return node.type;
|
return node.type;
|
||||||
},
|
},
|
||||||
|
traverse(visitor) {
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
return oxcTraverse(node, visitor);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
swcPathCache.set(node, swcPath);
|
swcPathCache.set(node, swcPath);
|
||||||
return swcPath;
|
return swcPath;
|
||||||
|
|
@ -103,19 +153,18 @@ function createSwcPath(node, parent, stop, scope) {
|
||||||
* - an import specifier (like "import { a } from 'b'")?
|
* - an import specifier (like "import { a } from 'b'")?
|
||||||
* Handy to know if the parents of Identifiers mark a binding
|
* Handy to know if the parents of Identifiers mark a binding
|
||||||
* @param {SwcNode} parent
|
* @param {SwcNode} parent
|
||||||
* @param {string} identifierValue
|
* @param {string} identifierName
|
||||||
*/
|
*/
|
||||||
function isBindingNode(parent, identifierValue) {
|
function isBindingNode(parent, identifierName) {
|
||||||
if (parent.type === 'VariableDeclarator') {
|
if (['VariableDeclarator', 'ClassDeclaration'].includes(parent.type)) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
return parent.id.value === identifierValue;
|
return nameOf(parent.id) === identifierName;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
'ClassDeclaration',
|
|
||||||
'FunctionDeclaration',
|
|
||||||
'ArrowFunctionExpression',
|
'ArrowFunctionExpression',
|
||||||
'ImportSpecifier',
|
|
||||||
'ImportDefaultSpecifier',
|
'ImportDefaultSpecifier',
|
||||||
|
'FunctionDeclaration',
|
||||||
|
'ImportSpecifier',
|
||||||
].includes(parent.type);
|
].includes(parent.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,15 +190,13 @@ function isBindingRefNode(parent) {
|
||||||
function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
||||||
const { node, parent, scope, parentPath } = swcPathForIdentifier;
|
const { node, parent, scope, parentPath } = swcPathForIdentifier;
|
||||||
|
|
||||||
if (node.type !== 'Identifier') {
|
if (node.type !== 'Identifier') return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const parentPath = getPathFromNode(parent);
|
// const parentPath = getPathFromNode(parent);
|
||||||
if (isBindingNode(parent, node.value)) {
|
if (isBindingNode(parent, nameOf(node))) {
|
||||||
/** @type {SwcBinding} */
|
/** @type {SwcBinding} */
|
||||||
const binding = {
|
const binding = {
|
||||||
identifier: parent,
|
identifier: parent?.id || parent?.identifier,
|
||||||
// kind: 'var',
|
// kind: 'var',
|
||||||
refs: [],
|
refs: [],
|
||||||
path: swcPathForIdentifier.parentPath,
|
path: swcPathForIdentifier.parentPath,
|
||||||
|
|
@ -170,19 +217,20 @@ function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const idName = node.value || node.local?.value || node.orig?.value;
|
const idName = nameOf(node) || nameOf(node.local) || nameOf(node.orig || node.imported);
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
scopeBindingBelongsTo.bindings[idName] = binding;
|
scopeBindingBelongsTo.bindings[idName] = binding;
|
||||||
|
|
||||||
// Align with Babel... => in example `class Q {}`, Q has binding to root scope and ClassDeclaration scope
|
// Align with Babel... => in example `class Q {}`, Q has binding to root scope and ClassDeclaration scope
|
||||||
if (parent.type === 'ClassDeclaration') {
|
if (parent.type === 'ClassDeclaration' && (parent.id || parent.identifier) === node) {
|
||||||
scope.bindings[idName] = binding;
|
scope.bindings[idName] = binding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// In other cases, we are dealing with a reference that must be bound to a binding
|
// In other cases, we are dealing with a reference that must be bound to a binding
|
||||||
else if (isBindingRefNode(parent)) {
|
else if (isBindingRefNode(parent)) {
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
const binding = scope.bindings.hasOwnProperty(node.value) && scope.bindings[node.value];
|
const binding = scope.bindings.hasOwnProperty(nameOf(node)) && scope.bindings[nameOf(node)];
|
||||||
if (binding) {
|
if (binding) {
|
||||||
binding.refs.push(parentPath);
|
binding.refs.push(parentPath);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -200,7 +248,7 @@ function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function isRootNode(node) {
|
function isRootNode(node) {
|
||||||
return node.type === 'Module' || node.type === 'Script';
|
return node.type === 'Program' || node.type === 'Module' || node.type === 'Script';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -260,12 +308,12 @@ function visit(swcPath, visitor, traversalContext) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple traversal for swc ast.
|
* Simple traversal for swc ast.
|
||||||
* @param {SwcAstModule} swcAst
|
* @param {SwcAstModule|SwcNode} oxcAst
|
||||||
* @param {SwcVisitor} visitor
|
* @param {SwcVisitor} visitor
|
||||||
* @param {object} config
|
* @param {object} config
|
||||||
* @param {boolean} [config.needsAdvancedPaths] needs a full traversal before starting the visitor, which is less performant. Only enable when path.get() is used
|
* @param {boolean} [config.needsAdvancedPaths] needs a full traversal before starting the visitor, which is less performant. Only enable when path.get() is used
|
||||||
*/
|
*/
|
||||||
export function swcTraverse(swcAst, visitor, { needsAdvancedPaths = false } = {}) {
|
export function oxcTraverse(oxcAst, visitor, { needsAdvancedPaths = false } = {}) {
|
||||||
/**
|
/**
|
||||||
* For performance, the author of a visitor can call this to stop further traversal
|
* For performance, the author of a visitor can call this to stop further traversal
|
||||||
*/
|
*/
|
||||||
|
|
@ -344,15 +392,18 @@ export function swcTraverse(swcAst, visitor, { needsAdvancedPaths = false } = {}
|
||||||
id: traversalContext.scopeId,
|
id: traversalContext.scopeId,
|
||||||
bindings: {},
|
bindings: {},
|
||||||
path: null,
|
path: null,
|
||||||
|
getBinding(/** @type {string} */ identifierName) {
|
||||||
|
return initialScope.bindings[identifierName];
|
||||||
|
},
|
||||||
_pendingRefsWithoutBinding: [],
|
_pendingRefsWithoutBinding: [],
|
||||||
_isIsolatedBlockStatement: false,
|
_isIsolatedBlockStatement: false,
|
||||||
};
|
};
|
||||||
if (needsAdvancedPaths) {
|
if (needsAdvancedPaths) {
|
||||||
// Do one full traversal to prepare advanced path functionality like path.get() and path.scope.bindings
|
// Do one full traversal to prepare advanced path functionality like path.get() and path.scope.bindings
|
||||||
// TODO: improve with on the fly, partial tree traversal for best performance
|
// TODO: improve with on the fly, partial tree traversal for best performance
|
||||||
prepareTree(swcAst, null, initialScope, traversalContext);
|
prepareTree(oxcAst, null, initialScope, traversalContext);
|
||||||
}
|
}
|
||||||
visitTree(swcAst, null, initialScope, { hasPreparedTree: needsAdvancedPaths }, traversalContext);
|
visitTree(oxcAst, null, initialScope, { hasPreparedTree: needsAdvancedPaths }, traversalContext);
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
traversalContext.visitOnExitFns.reverse().forEach(fn => fn());
|
traversalContext.visitOnExitFns.reverse().forEach(fn => fn());
|
||||||
}
|
}
|
||||||
|
|
@ -1,334 +0,0 @@
|
||||||
/* eslint-disable no-shadow */
|
|
||||||
// @ts-nocheck
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import babelTraverse from '@babel/traverse';
|
|
||||||
|
|
||||||
import { isRelativeSourcePath, toRelativeSourcePath } from './relative-source-path.js';
|
|
||||||
import { InputDataService } from '../core/InputDataService.js';
|
|
||||||
import { resolveImportPath } from './resolve-import-path.js';
|
|
||||||
import { AstService } from '../core/AstService.js';
|
|
||||||
import { LogService } from '../core/LogService.js';
|
|
||||||
import { fsAdapter } from './fs-adapter.js';
|
|
||||||
import { memoize } from './memoize.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {import('../../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
|
||||||
* @typedef {import('../../../../types/index.js').SpecifierSource} SpecifierSource
|
|
||||||
* @typedef {import('../../../../types/index.js').IdentifierName} IdentifierName
|
|
||||||
* @typedef {import('../../../../types/index.js').RootFile} RootFile
|
|
||||||
* @typedef {import('@babel/traverse').NodePath} NodePath
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} source
|
|
||||||
* @param {string} projectName
|
|
||||||
*/
|
|
||||||
function isSelfReferencingProject(source, projectName) {
|
|
||||||
return source.split('/')[0] === projectName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} source
|
|
||||||
* @param {string} projectName
|
|
||||||
*/
|
|
||||||
function isExternalProject(source, projectName) {
|
|
||||||
return (
|
|
||||||
!source.startsWith('#') &&
|
|
||||||
!isRelativeSourcePath(source) &&
|
|
||||||
!isSelfReferencingProject(source, projectName)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Other than with import, no binding is created for MyClass by Babel(?)
|
|
||||||
* This means 'path.scope.getBinding('MyClass')' returns undefined
|
|
||||||
* and we have to find a different way to retrieve this value.
|
|
||||||
* @param {NodePath} astPath Babel ast traversal path
|
|
||||||
* @param {IdentifierName} identifierName the name that should be tracked (and that exists inside scope of astPath)
|
|
||||||
*/
|
|
||||||
function getBindingAndSourceReexports(astPath, identifierName) {
|
|
||||||
// Get to root node of file and look for exports like `export { identifierName } from 'src';`
|
|
||||||
let source;
|
|
||||||
let bindingType;
|
|
||||||
let bindingPath;
|
|
||||||
|
|
||||||
let curPath = astPath;
|
|
||||||
while (curPath.parentPath) {
|
|
||||||
curPath = curPath.parentPath;
|
|
||||||
}
|
|
||||||
const rootPath = curPath;
|
|
||||||
rootPath.traverse({
|
|
||||||
ExportSpecifier(astPath) {
|
|
||||||
// eslint-disable-next-line arrow-body-style
|
|
||||||
const found =
|
|
||||||
astPath.node.exported.name === identifierName || astPath.node.local.name === identifierName;
|
|
||||||
if (found) {
|
|
||||||
bindingPath = astPath;
|
|
||||||
bindingType = 'ExportSpecifier';
|
|
||||||
source = astPath.parentPath.node.source
|
|
||||||
? astPath.parentPath.node.source.value
|
|
||||||
: '[current]';
|
|
||||||
astPath.stop();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return [source, bindingType, bindingPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves source (like '@lion/core') and importedIdentifierName (like 'lit') from ast for
|
|
||||||
* current file.
|
|
||||||
* We might be an import that was locally renamed.
|
|
||||||
* Since we are traversing, we are interested in the imported name. Or in case of a re-export,
|
|
||||||
* the local name.
|
|
||||||
* @param {NodePath} astPath Babel ast traversal path
|
|
||||||
* @param {string} identifierName the name that should be tracked (and that exists inside scope of astPath)
|
|
||||||
* @returns {{ source:string, importedIdentifierName:string }}
|
|
||||||
*/
|
|
||||||
export function getImportSourceFromAst(astPath, identifierName) {
|
|
||||||
let source;
|
|
||||||
let importedIdentifierName;
|
|
||||||
|
|
||||||
const binding = astPath.scope.getBinding(identifierName);
|
|
||||||
let bindingType = binding?.path.type;
|
|
||||||
let bindingPath = binding?.path;
|
|
||||||
const matchingTypes = ['ImportSpecifier', 'ImportDefaultSpecifier', 'ExportSpecifier'];
|
|
||||||
|
|
||||||
if (bindingType && matchingTypes.includes(bindingType)) {
|
|
||||||
source = binding?.path?.parentPath?.node?.source?.value;
|
|
||||||
} else {
|
|
||||||
// no binding
|
|
||||||
[source, bindingType, bindingPath] = getBindingAndSourceReexports(astPath, identifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldLookForDefaultExport = bindingType === 'ImportDefaultSpecifier';
|
|
||||||
if (shouldLookForDefaultExport) {
|
|
||||||
importedIdentifierName = '[default]';
|
|
||||||
} else if (source) {
|
|
||||||
const { node } = bindingPath;
|
|
||||||
importedIdentifierName = (node.imported && node.imported.name) || node.local.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { source, importedIdentifierName };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {(source:SpecifierSource,identifierName:IdentifierName,currentFilePath:PathFromSystemRoot,rootPath:PathFromSystemRoot,projectName?: string,depth?:number) => Promise<RootFile>} TrackDownIdentifierFn
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Follows the full path of an Identifier until its declaration ('root file') is found.
|
|
||||||
* @example
|
|
||||||
*```js
|
|
||||||
* // 1. Starting point
|
|
||||||
* // target-proj/my-comp-import.js
|
|
||||||
* import { MyComp as TargetComp } from 'ref-proj';
|
|
||||||
*
|
|
||||||
* // 2. Intermediate stop: a re-export
|
|
||||||
* // ref-proj/exportsIndex.js (package.json has main: './exportsIndex.js')
|
|
||||||
* export { RefComp as MyComp } from './src/RefComp.js';
|
|
||||||
*
|
|
||||||
* // 3. End point: our declaration
|
|
||||||
* // ref-proj/src/RefComp.js
|
|
||||||
* export class RefComp extends LitElement {...}
|
|
||||||
*```
|
|
||||||
*
|
|
||||||
* -param {SpecifierSource} source an importSpecifier source, like 'ref-proj' or '../file'
|
|
||||||
* -param {IdentifierName} identifierName imported reference/Identifier name, like 'MyComp'
|
|
||||||
* -param {PathFromSystemRoot} currentFilePath file path, like '/path/to/target-proj/my-comp-import.js'
|
|
||||||
* -param {PathFromSystemRoot} rootPath dir path, like '/path/to/target-proj'
|
|
||||||
* -param {string} [projectName] like 'target-proj' or '@lion/input'
|
|
||||||
* -returns {Promise<RootFile>} file: path of file containing the binding (exported declaration),
|
|
||||||
* like '/path/to/ref-proj/src/RefComp.js'
|
|
||||||
*/
|
|
||||||
/** @type {TrackDownIdentifierFn} */
|
|
||||||
// eslint-disable-next-line import/no-mutable-exports
|
|
||||||
export let trackDownIdentifier;
|
|
||||||
|
|
||||||
/** @type {TrackDownIdentifierFn} */
|
|
||||||
async function trackDownIdentifierFn(
|
|
||||||
source,
|
|
||||||
identifierName,
|
|
||||||
currentFilePath,
|
|
||||||
rootPath,
|
|
||||||
projectName,
|
|
||||||
depth = 0,
|
|
||||||
) {
|
|
||||||
let rootFilePath; // our result path
|
|
||||||
let rootSpecifier; // the name under which it was imported
|
|
||||||
|
|
||||||
if (!projectName) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
projectName = InputDataService.getPackageJson(rootPath)?.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (projectName && isExternalProject(source, projectName)) {
|
|
||||||
// So, it is an external ref like '@lion/core' or '@open-wc/scoped-elements/index.js'
|
|
||||||
// At this moment in time, we don't know if we have file system access to this particular
|
|
||||||
// project. Therefore, we limit ourselves to tracking down local references.
|
|
||||||
// In case this helper is used inside an analyzer like 'match-subclasses', the external
|
|
||||||
// (search-target) project can be accessed and paths can be resolved to local ones,
|
|
||||||
// just like in 'match-imports' analyzer.
|
|
||||||
/** @type {RootFile} */
|
|
||||||
const result = { file: source, specifier: identifierName };
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvedSourcePath = await resolveImportPath(source, currentFilePath);
|
|
||||||
|
|
||||||
LogService.debug(`[trackDownIdentifier] ${resolvedSourcePath}`);
|
|
||||||
const allowedJsModuleExtensions = ['.mjs', '.js'];
|
|
||||||
if (!allowedJsModuleExtensions.includes(path.extname(resolvedSourcePath))) {
|
|
||||||
// We have an import assertion
|
|
||||||
return /** @type { RootFile } */ {
|
|
||||||
file: toRelativeSourcePath(resolvedSourcePath, rootPath),
|
|
||||||
specifier: '[default]',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const code = fsAdapter.fs.readFileSync(resolvedSourcePath, 'utf8');
|
|
||||||
const babelAst = AstService.getAst(code, 'swc-to-babel', { filePath: resolvedSourcePath });
|
|
||||||
|
|
||||||
const shouldLookForDefaultExport = identifierName === '[default]';
|
|
||||||
|
|
||||||
let reexportMatch = false; // named specifier declaration
|
|
||||||
let exportMatch;
|
|
||||||
let pendingTrackDownPromise;
|
|
||||||
|
|
||||||
babelTraverse.default(babelAst, {
|
|
||||||
ExportDefaultDeclaration(astPath) {
|
|
||||||
if (!shouldLookForDefaultExport) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newSource;
|
|
||||||
if (astPath.node.declaration.type === 'Identifier') {
|
|
||||||
newSource = getImportSourceFromAst(astPath, astPath.node.declaration.name).source;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSource) {
|
|
||||||
pendingTrackDownPromise = trackDownIdentifier(
|
|
||||||
newSource,
|
|
||||||
'[default]',
|
|
||||||
resolvedSourcePath,
|
|
||||||
rootPath,
|
|
||||||
projectName,
|
|
||||||
depth + 1,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// We found our file!
|
|
||||||
rootSpecifier = identifierName;
|
|
||||||
rootFilePath = toRelativeSourcePath(resolvedSourcePath, rootPath);
|
|
||||||
}
|
|
||||||
astPath.stop();
|
|
||||||
},
|
|
||||||
ExportNamedDeclaration: {
|
|
||||||
enter(astPath) {
|
|
||||||
if (reexportMatch || shouldLookForDefaultExport) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Are we dealing with a re-export ?
|
|
||||||
if (astPath.node.specifiers?.length) {
|
|
||||||
exportMatch = astPath.node.specifiers.find(s => s.exported.name === identifierName);
|
|
||||||
|
|
||||||
if (exportMatch) {
|
|
||||||
const localName = exportMatch.local.name;
|
|
||||||
let newSource;
|
|
||||||
if (astPath.node.source) {
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* export { x } from 'y'
|
|
||||||
*/
|
|
||||||
newSource = astPath.node.source.value;
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* import { x } from 'y'
|
|
||||||
* export { x }
|
|
||||||
*/
|
|
||||||
newSource = getImportSourceFromAst(astPath, identifierName).source;
|
|
||||||
|
|
||||||
if (!newSource || newSource === '[current]') {
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* const x = 12;
|
|
||||||
* export { x }
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reexportMatch = true;
|
|
||||||
pendingTrackDownPromise = trackDownIdentifier(
|
|
||||||
newSource,
|
|
||||||
localName,
|
|
||||||
resolvedSourcePath,
|
|
||||||
rootPath,
|
|
||||||
projectName,
|
|
||||||
depth + 1,
|
|
||||||
);
|
|
||||||
astPath.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit(astPath) {
|
|
||||||
if (!reexportMatch) {
|
|
||||||
// We didn't find a re-exported Identifier, that means the reference is declared
|
|
||||||
// in current file...
|
|
||||||
rootSpecifier = identifierName;
|
|
||||||
rootFilePath = toRelativeSourcePath(resolvedSourcePath, rootPath);
|
|
||||||
|
|
||||||
if (exportMatch) {
|
|
||||||
astPath.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pendingTrackDownPromise) {
|
|
||||||
// We can't handle promises inside Babel traverse, so we do it here...
|
|
||||||
const resObj = await pendingTrackDownPromise;
|
|
||||||
rootFilePath = resObj.file;
|
|
||||||
rootSpecifier = resObj.specifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
return /** @type { RootFile } */ { file: rootFilePath, specifier: rootSpecifier };
|
|
||||||
}
|
|
||||||
|
|
||||||
trackDownIdentifier = memoize(trackDownIdentifierFn);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {NodePath} astPath
|
|
||||||
* @param {string} identifierNameInScope
|
|
||||||
* @param {PathFromSystemRoot} fullCurrentFilePath
|
|
||||||
* @param {PathFromSystemRoot} projectPath
|
|
||||||
* @param {string} [projectName]
|
|
||||||
*/
|
|
||||||
async function trackDownIdentifierFromScopeFn(
|
|
||||||
astPath,
|
|
||||||
identifierNameInScope,
|
|
||||||
fullCurrentFilePath,
|
|
||||||
projectPath,
|
|
||||||
projectName,
|
|
||||||
) {
|
|
||||||
const sourceObj = getImportSourceFromAst(astPath, identifierNameInScope);
|
|
||||||
|
|
||||||
/** @type {RootFile} */
|
|
||||||
let rootFile;
|
|
||||||
if (sourceObj.source) {
|
|
||||||
rootFile = await trackDownIdentifier(
|
|
||||||
sourceObj.source,
|
|
||||||
sourceObj.importedIdentifierName,
|
|
||||||
fullCurrentFilePath,
|
|
||||||
projectPath,
|
|
||||||
projectName,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const specifier = sourceObj.importedIdentifierName || identifierNameInScope;
|
|
||||||
rootFile = { file: '[current]', specifier };
|
|
||||||
}
|
|
||||||
return rootFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const trackDownIdentifierFromScope = memoize(trackDownIdentifierFromScopeFn);
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { isRelativeSourcePath, toRelativeSourcePath } from './relative-source-pa
|
||||||
import { InputDataService } from '../core/InputDataService.js';
|
import { InputDataService } from '../core/InputDataService.js';
|
||||||
import { resolveImportPath } from './resolve-import-path.js';
|
import { resolveImportPath } from './resolve-import-path.js';
|
||||||
import { AstService } from '../core/AstService.js';
|
import { AstService } from '../core/AstService.js';
|
||||||
import { swcTraverse } from './swc-traverse.js';
|
import { oxcTraverse, nameOf, importedOf } from './oxc-traverse.js';
|
||||||
import { fsAdapter } from './fs-adapter.js';
|
import { fsAdapter } from './fs-adapter.js';
|
||||||
import { memoize } from './memoize.js';
|
import { memoize } from './memoize.js';
|
||||||
|
|
||||||
|
|
@ -55,18 +55,19 @@ function getBindingAndSourceReexports(swcPath, identifierName) {
|
||||||
}
|
}
|
||||||
const rootPath = curPath;
|
const rootPath = curPath;
|
||||||
|
|
||||||
swcTraverse(rootPath.node, {
|
oxcTraverse(rootPath.node, {
|
||||||
ExportSpecifier(astPath) {
|
ExportSpecifier(astPath) {
|
||||||
|
const { node } = astPath;
|
||||||
// eslint-disable-next-line arrow-body-style
|
// eslint-disable-next-line arrow-body-style
|
||||||
const found =
|
const found =
|
||||||
astPath.node.orig?.value === identifierName ||
|
nameOf(importedOf(node)) === identifierName ||
|
||||||
astPath.node.exported?.value === identifierName ||
|
nameOf(node.exported) === identifierName ||
|
||||||
astPath.node.local?.value === identifierName;
|
nameOf(node.local) === identifierName;
|
||||||
if (found) {
|
if (found) {
|
||||||
bindingPath = astPath;
|
bindingPath = astPath;
|
||||||
bindingType = 'ExportSpecifier';
|
bindingType = 'ExportSpecifier';
|
||||||
source = astPath.parentPath.node.source
|
source = astPath.parentPath.node.source
|
||||||
? astPath.parentPath.node.source.value
|
? nameOf(astPath.parentPath.node.source)
|
||||||
: '[current]';
|
: '[current]';
|
||||||
astPath.stop();
|
astPath.stop();
|
||||||
}
|
}
|
||||||
|
|
@ -89,13 +90,15 @@ export function getImportSourceFromAst(astPath, identifierName) {
|
||||||
let source;
|
let source;
|
||||||
let importedIdentifierName;
|
let importedIdentifierName;
|
||||||
|
|
||||||
const binding = astPath.scope.bindings[identifierName];
|
// TODO: use (smth like) getReferencedDeclaration if we want to catch renamed variables
|
||||||
|
const binding = astPath.scope.getBinding(identifierName);
|
||||||
|
|
||||||
let bindingType = binding?.path.type;
|
let bindingType = binding?.path.type;
|
||||||
let bindingPath = binding?.path;
|
let bindingPath = binding?.path;
|
||||||
const matchingTypes = ['ImportSpecifier', 'ImportDefaultSpecifier', 'ExportSpecifier'];
|
const matchingTypes = ['ImportSpecifier', 'ImportDefaultSpecifier', 'ExportSpecifier'];
|
||||||
|
|
||||||
if (bindingType && matchingTypes.includes(bindingType)) {
|
if (bindingType && matchingTypes.includes(bindingType)) {
|
||||||
source = binding?.path?.parentPath?.node?.source?.value;
|
source = nameOf(binding?.path?.parentPath?.node?.source);
|
||||||
} else {
|
} else {
|
||||||
// no binding
|
// no binding
|
||||||
[source, bindingType, bindingPath] = getBindingAndSourceReexports(astPath, identifierName);
|
[source, bindingType, bindingPath] = getBindingAndSourceReexports(astPath, identifierName);
|
||||||
|
|
@ -106,7 +109,7 @@ export function getImportSourceFromAst(astPath, identifierName) {
|
||||||
importedIdentifierName = '[default]';
|
importedIdentifierName = '[default]';
|
||||||
} else if (source) {
|
} else if (source) {
|
||||||
const { node } = bindingPath;
|
const { node } = bindingPath;
|
||||||
importedIdentifierName = node.orig?.value || node.imported?.value || node.local?.value;
|
importedIdentifierName = nameOf(importedOf(node)) || nameOf(node.local);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { source, importedIdentifierName };
|
return { source, importedIdentifierName };
|
||||||
|
|
@ -194,8 +197,11 @@ async function trackDownIdentifierFn(
|
||||||
specifier: '[default]',
|
specifier: '[default]',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const code = fsAdapter.fs.readFileSync(/** @type {string} */ (resolvedSourcePath), 'utf8');
|
const code = await fsAdapter.fs.promises.readFile(
|
||||||
const swcAst = AstService._getSwcAst(code);
|
/** @type {string} */ (resolvedSourcePath),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
const shouldLookForDefaultExport = identifierName === '[default]';
|
const shouldLookForDefaultExport = identifierName === '[default]';
|
||||||
|
|
||||||
|
|
@ -204,16 +210,16 @@ async function trackDownIdentifierFn(
|
||||||
let pendingTrackDownPromise;
|
let pendingTrackDownPromise;
|
||||||
|
|
||||||
const handleExportDefaultDeclOrExpr = astPath => {
|
const handleExportDefaultDeclOrExpr = astPath => {
|
||||||
if (!shouldLookForDefaultExport) {
|
if (!shouldLookForDefaultExport) return;
|
||||||
return;
|
|
||||||
}
|
const { node } = astPath;
|
||||||
|
|
||||||
let newSource;
|
let newSource;
|
||||||
if (
|
if (node.expression?.type === 'Identifier' || node.declaration?.type === 'Identifier') {
|
||||||
astPath.node.expression?.type === 'Identifier' ||
|
newSource = getImportSourceFromAst(
|
||||||
astPath.node.declaration?.type === 'Identifier'
|
astPath,
|
||||||
) {
|
nameOf(node.expression || node.declaration),
|
||||||
newSource = getImportSourceFromAst(astPath, astPath.node.expression.value).source;
|
).source;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newSource) {
|
if (newSource) {
|
||||||
|
|
@ -237,25 +243,27 @@ async function trackDownIdentifierFn(
|
||||||
};
|
};
|
||||||
const handleExportDeclOrNamedDecl = {
|
const handleExportDeclOrNamedDecl = {
|
||||||
enter(astPath) {
|
enter(astPath) {
|
||||||
if (reexportMatch || shouldLookForDefaultExport) {
|
if (reexportMatch || shouldLookForDefaultExport) return;
|
||||||
return;
|
|
||||||
}
|
const { node } = astPath;
|
||||||
|
|
||||||
// Are we dealing with a re-export ?
|
// Are we dealing with a re-export ?
|
||||||
if (astPath.node.specifiers?.length) {
|
if (!node.specifiers?.length) return;
|
||||||
exportMatch = astPath.node.specifiers.find(
|
|
||||||
s => s.orig?.value === identifierName || s.exported?.value === identifierName,
|
exportMatch = node.specifiers.find(
|
||||||
|
s => nameOf(importedOf(s)) === identifierName || nameOf(s.exported) === identifierName,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (exportMatch) {
|
if (!exportMatch) return;
|
||||||
const localName = exportMatch.orig.value;
|
|
||||||
|
const localName = nameOf(importedOf(exportMatch));
|
||||||
let newSource;
|
let newSource;
|
||||||
if (astPath.node.source) {
|
if (node.source) {
|
||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
* export { x } from 'y'
|
* export { x } from 'y'
|
||||||
*/
|
*/
|
||||||
newSource = astPath.node.source.value;
|
newSource = nameOf(node.source);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -283,8 +291,6 @@ async function trackDownIdentifierFn(
|
||||||
depth + 1,
|
depth + 1,
|
||||||
);
|
);
|
||||||
astPath.stop();
|
astPath.stop();
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
exit(astPath) {
|
exit(astPath) {
|
||||||
if (!reexportMatch) {
|
if (!reexportMatch) {
|
||||||
|
|
@ -307,7 +313,7 @@ async function trackDownIdentifierFn(
|
||||||
ExportDeclaration: handleExportDeclOrNamedDecl,
|
ExportDeclaration: handleExportDeclOrNamedDecl,
|
||||||
};
|
};
|
||||||
|
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
if (pendingTrackDownPromise) {
|
if (pendingTrackDownPromise) {
|
||||||
// We can't handle promises inside Babel traverse, so we do it here...
|
// We can't handle promises inside Babel traverse, so we do it here...
|
||||||
|
|
@ -351,6 +357,7 @@ async function trackDownIdentifierFromScopeFn(
|
||||||
const specifier = sourceObj.importedIdentifierName || identifierNameInScope;
|
const specifier = sourceObj.importedIdentifierName || identifierNameInScope;
|
||||||
rootFile = { file: '[current]', specifier };
|
rootFile = { file: '[current]', specifier };
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootFile;
|
return rootFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import module from 'module';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import mockFs from 'mock-fs';
|
import mockFs from 'mock-fs';
|
||||||
|
|
@ -59,6 +60,46 @@ function getMockObjectForProject(files, cfg = {}, existingMock = {}) {
|
||||||
return totalMock;
|
return totalMock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} resolvedPath
|
||||||
|
* @param {string} dynamicImport
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getPackageRootFromNodeModulesPath(resolvedPath, dynamicImport) {
|
||||||
|
const scope = dynamicImport.startsWith('@') ? dynamicImport.split('/')[0] : dynamicImport;
|
||||||
|
const tailOfRootPath = `${path.sep}node_modules${path.sep}${scope}`;
|
||||||
|
const lio = resolvedPath.lastIndexOf(tailOfRootPath);
|
||||||
|
return resolvedPath.slice(0, lio + tailOfRootPath.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function incorporateDynamicImports(
|
||||||
|
dynamicImports = [
|
||||||
|
{
|
||||||
|
name: 'oxc-parser',
|
||||||
|
siblings: [
|
||||||
|
'@oxc-parser',
|
||||||
|
// '@oxc-resolver'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ name: '@babel/parser' },
|
||||||
|
{ name: '@swc/core' },
|
||||||
|
],
|
||||||
|
) {
|
||||||
|
const require = module.createRequire(import.meta.url);
|
||||||
|
const importablePaths = [];
|
||||||
|
for (const dynamicImport of dynamicImports) {
|
||||||
|
const resolvedPath = require.resolve(dynamicImport.name);
|
||||||
|
const rootPath = getPackageRootFromNodeModulesPath(resolvedPath, dynamicImport.name);
|
||||||
|
importablePaths.push(rootPath);
|
||||||
|
for (const sibling of dynamicImport.siblings || []) {
|
||||||
|
const siblingPath = `${rootPath.split(path.sep).slice(0, -1).join(path.sep)}${path.sep}${sibling}`;
|
||||||
|
importablePaths.push(siblingPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return importablePaths;
|
||||||
|
}
|
||||||
|
const importablePaths = incorporateDynamicImports();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sure that, whenever the main program (providence) calls
|
* Makes sure that, whenever the main program (providence) calls
|
||||||
* "InputDataService.createDataObject", it gives back a mocked response.
|
* "InputDataService.createDataObject", it gives back a mocked response.
|
||||||
|
|
@ -72,7 +113,7 @@ function getMockObjectForProject(files, cfg = {}, existingMock = {}) {
|
||||||
*/
|
*/
|
||||||
export function mockProject(files, cfg = {}, existingMock = {}) {
|
export function mockProject(files, cfg = {}, existingMock = {}) {
|
||||||
const obj = getMockObjectForProject(files, cfg, existingMock);
|
const obj = getMockObjectForProject(files, cfg, existingMock);
|
||||||
mockFs(obj);
|
mockFs({ ...obj, ...Object.fromEntries(importablePaths.map(p => [p, mockFs.load(p)])) });
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "find-classes",
|
"name": "find-classes",
|
||||||
"requiredAst": "babel",
|
"requiredAst": "oxc",
|
||||||
"identifier": "importing-target-project_0.0.2-target-mock__-905964591",
|
"identifier": "importing-target-project_0.0.2-target-mock__-905964591",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "find-customelements",
|
"name": "find-customelements",
|
||||||
"requiredAst": "swc-to-babel",
|
"requiredAst": "oxc",
|
||||||
"identifier": "importing-target-project_0.0.2-target-mock__61665553",
|
"identifier": "importing-target-project_0.0.2-target-mock__61665553",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "find-exports",
|
"name": "find-exports",
|
||||||
"requiredAst": "swc",
|
"requiredAst": "oxc",
|
||||||
"identifier": "exporting-ref-project_1.0.0__-42206859",
|
"identifier": "exporting-ref-project_1.0.0__-42206859",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./index.js",
|
"mainEntry": "./index.js",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "find-imports",
|
"name": "find-imports",
|
||||||
"requiredAst": "swc",
|
"requiredAst": "oxc",
|
||||||
"identifier": "importing-target-project_0.0.2-target-mock__349742630",
|
"identifier": "importing-target-project_0.0.2-target-mock__349742630",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "match-imports",
|
"name": "match-imports",
|
||||||
"requiredAst": "swc",
|
"requiredAst": "oxc",
|
||||||
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1789378150",
|
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1789378150",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"searchType": "ast-analyzer",
|
"searchType": "ast-analyzer",
|
||||||
"analyzerMeta": {
|
"analyzerMeta": {
|
||||||
"name": "match-subclasses",
|
"name": "match-subclasses",
|
||||||
"requiredAst": "babel",
|
"requiredAst": "oxc",
|
||||||
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1982316146",
|
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1982316146",
|
||||||
"targetProject": {
|
"targetProject": {
|
||||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ describe('Analyzer "find-imports"', async () => {
|
||||||
expect(firstEntry.result[0].importSpecifiers[0]).to.equal('[*]');
|
expect(firstEntry.result[0].importSpecifiers[0]).to.equal('[*]');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reexports', () => {
|
describe('Re-exports', () => {
|
||||||
it(`supports [export { x } from 'imported/source'] (re-exported named specifiers)`, async () => {
|
it(`supports [export { x } from 'imported/source'] (re-exported named specifiers)`, async () => {
|
||||||
mockProject([`export { x } from 'imported/source'`]);
|
mockProject([`export { x } from 'imported/source'`]);
|
||||||
const queryResults = await providence(findImportsQueryConfig, _providenceCfg);
|
const queryResults = await providence(findImportsQueryConfig, _providenceCfg);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ import { expect } from 'chai';
|
||||||
import { it } from 'mocha';
|
import { it } from 'mocha';
|
||||||
|
|
||||||
import { getSourceCodeFragmentOfDeclaration } from '../../../src/program/utils/index.js';
|
import { getSourceCodeFragmentOfDeclaration } from '../../../src/program/utils/index.js';
|
||||||
import { mock } from '../../../test-helpers/mock-project-helpers.js';
|
import { mockProject } from '../../../test-helpers/mock-project-helpers.js';
|
||||||
import { memoize } from '../../../src/program/utils/memoize.js';
|
import { memoize } from '../../../src/program/utils/memoize.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
||||||
|
*/
|
||||||
|
|
||||||
describe('getSourceCodeFragmentOfDeclaration', () => {
|
describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
const initialMemoizeCacheEnabled = memoize.isCacheEnabled;
|
const initialMemoizeCacheEnabled = memoize.isCacheEnabled;
|
||||||
before(() => {
|
before(() => {
|
||||||
|
|
@ -19,10 +23,10 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
const fakeFs = {
|
const fakeFs = {
|
||||||
'/my/proj/exports/file.js': 'export const x = 0;',
|
'/my/proj/exports/file.js': 'export const x = 0;',
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: /** @type {PathFromSystemRoot} */ ('/my/proj/exports/file.js'),
|
||||||
exportedIdentifier: 'x',
|
exportedIdentifier: 'x',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -36,7 +40,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export const x = y;
|
export const x = y;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: '/my/proj/exports/file.js',
|
||||||
|
|
@ -54,7 +58,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export const myIdentifier = y;
|
export const myIdentifier = y;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: '/my/proj/exports/file.js',
|
||||||
|
|
@ -74,7 +78,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export const black67 = black59;
|
export const black67 = black59;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file-2.js',
|
filePath: '/my/proj/exports/file-2.js',
|
||||||
|
|
@ -93,7 +97,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export class AjaxClass extends LionAjaxClass {}
|
export class AjaxClass extends LionAjaxClass {}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/ajax.js',
|
filePath: '/my/proj/exports/ajax.js',
|
||||||
|
|
@ -109,7 +113,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export function myFn() {}
|
export function myFn() {}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/myFn.js',
|
filePath: '/my/proj/exports/myFn.js',
|
||||||
|
|
@ -126,7 +130,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
const fakeFs = {
|
const fakeFs = {
|
||||||
'/my/proj/exports/file.js': 'export default class {};',
|
'/my/proj/exports/file.js': 'export default class {};',
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: '/my/proj/exports/file.js',
|
||||||
|
|
@ -143,7 +147,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export default myIdentifier;
|
export default myIdentifier;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: '/my/proj/exports/file.js',
|
||||||
|
|
@ -161,7 +165,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export default myIdentifier;
|
export default myIdentifier;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/file.js',
|
filePath: '/my/proj/exports/file.js',
|
||||||
|
|
@ -180,7 +184,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export default class AjaxClass extends LionAjaxClass {}
|
export default class AjaxClass extends LionAjaxClass {}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/ajax.js',
|
filePath: '/my/proj/exports/ajax.js',
|
||||||
|
|
@ -196,7 +200,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||||
export default function myFn() {}
|
export default function myFn() {}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
mock(fakeFs);
|
mockProject(fakeFs);
|
||||||
|
|
||||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||||
filePath: '/my/proj/exports/myFn.js',
|
filePath: '/my/proj/exports/myFn.js',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { expect } from 'chai';
|
||||||
import { it } from 'mocha';
|
import { it } from 'mocha';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import babelTraversePkg from '@babel/traverse';
|
import babelTraversePkg from '@babel/traverse';
|
||||||
import { swcTraverse } from '../../../src/program/utils/swc-traverse.js';
|
import { oxcTraverse } from '../../../src/program/utils/oxc-traverse.js';
|
||||||
import { AstService } from '../../../src/program/core/AstService.js';
|
import { AstService } from '../../../src/program/core/AstService.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -12,12 +12,12 @@ import { AstService } from '../../../src/program/core/AstService.js';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SwcAstModule} swcAst
|
* @param {SwcAstModule} oxcAst
|
||||||
*/
|
*/
|
||||||
function gatherAllScopes(swcAst) {
|
function gatherAllScopes(oxcAst) {
|
||||||
/** @type {SwcScope[]} */
|
/** @type {SwcScope[]} */
|
||||||
const swcScopes = [];
|
const swcScopes = [];
|
||||||
swcTraverse(swcAst, {
|
oxcTraverse(oxcAst, {
|
||||||
enter({ scope }) {
|
enter({ scope }) {
|
||||||
if (!swcScopes.includes(scope)) {
|
if (!swcScopes.includes(scope)) {
|
||||||
swcScopes.push(scope);
|
swcScopes.push(scope);
|
||||||
|
|
@ -27,11 +27,18 @@ function gatherAllScopes(swcAst) {
|
||||||
return swcScopes;
|
return swcScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('swcTraverse', () => {
|
/**
|
||||||
|
* @param {*} node
|
||||||
|
*/
|
||||||
|
function nameOf(node) {
|
||||||
|
return node.value || node.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('oxcTraverse', () => {
|
||||||
describe('Visitor', () => {
|
describe('Visitor', () => {
|
||||||
it('traverses an swc AST based on <Node.type> visitor', async () => {
|
it('traverses an swc AST based on <Node.type> visitor', async () => {
|
||||||
const code = `import x from 'y';`;
|
const code = `import x from 'y';`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
let foundImportDeclarationPath;
|
let foundImportDeclarationPath;
|
||||||
const visitor = {
|
const visitor = {
|
||||||
|
|
@ -39,14 +46,14 @@ describe('swcTraverse', () => {
|
||||||
foundImportDeclarationPath = path;
|
foundImportDeclarationPath = path;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor);
|
oxcTraverse(oxcAst, visitor);
|
||||||
|
|
||||||
expect(foundImportDeclarationPath).to.not.be.undefined;
|
expect(foundImportDeclarationPath).to.not.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports "enter" as a generic arrival handler', async () => {
|
it('supports "enter" as a generic arrival handler', async () => {
|
||||||
const code = `import x from 'y';`;
|
const code = `import x from 'y';`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const foundTypes = [];
|
const foundTypes = [];
|
||||||
|
|
@ -58,10 +65,11 @@ describe('swcTraverse', () => {
|
||||||
foundTypes.push(path.node.type);
|
foundTypes.push(path.node.type);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor);
|
oxcTraverse(oxcAst, visitor);
|
||||||
|
|
||||||
expect(foundTypes).to.deep.equal([
|
expect(foundTypes).to.deep.equal([
|
||||||
'Module',
|
// 'Module',
|
||||||
|
'Program',
|
||||||
'ImportDeclaration',
|
'ImportDeclaration',
|
||||||
'ImportDefaultSpecifier',
|
'ImportDefaultSpecifier',
|
||||||
'Identifier',
|
'Identifier',
|
||||||
|
|
@ -71,7 +79,7 @@ describe('swcTraverse', () => {
|
||||||
|
|
||||||
it('supports "enter" and "exit" as generic handlers inside <Node.type> handlers', async () => {
|
it('supports "enter" and "exit" as generic handlers inside <Node.type> handlers', async () => {
|
||||||
const code = `import x from 'y';`;
|
const code = `import x from 'y';`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const visitedPaths = [];
|
const visitedPaths = [];
|
||||||
|
|
@ -88,7 +96,7 @@ describe('swcTraverse', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor);
|
oxcTraverse(oxcAst, visitor);
|
||||||
|
|
||||||
expect(visitedPaths[0].path).to.equal(visitedPaths[1].path);
|
expect(visitedPaths[0].path).to.equal(visitedPaths[1].path);
|
||||||
expect(visitedPaths[0].phase).to.equal('enter');
|
expect(visitedPaths[0].phase).to.equal('enter');
|
||||||
|
|
@ -97,7 +105,7 @@ describe('swcTraverse', () => {
|
||||||
|
|
||||||
it('supports "root" as alternative for Program', async () => {
|
it('supports "root" as alternative for Program', async () => {
|
||||||
const code = `import x from 'y';`;
|
const code = `import x from 'y';`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
let rootPath;
|
let rootPath;
|
||||||
const visitor = {
|
const visitor = {
|
||||||
|
|
@ -108,17 +116,17 @@ describe('swcTraverse', () => {
|
||||||
rootPath = path;
|
rootPath = path;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor);
|
oxcTraverse(oxcAst, visitor);
|
||||||
|
|
||||||
// TODO: also add case for Script
|
// TODO: also add case for Script
|
||||||
expect(rootPath.node.type).to.equal('Module');
|
expect(rootPath.node.type).to.equal('Program');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not fail on object prototype built-ins (like "toString")', async () => {
|
it('does not fail on object prototype built-ins (like "toString")', async () => {
|
||||||
const code = `const { toString } = x;`;
|
const code = `const { toString } = x;`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
expect(swcTraverse(swcAst, {})).to.not.throw;
|
expect(oxcTraverse(oxcAst, {})).to.not.throw;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -147,7 +155,7 @@ describe('swcTraverse', () => {
|
||||||
}
|
}
|
||||||
const alsoGlobalScope = 3;
|
const alsoGlobalScope = 3;
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -159,13 +167,13 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||||
expect(declaratorPaths[2].scope.id).to.equal(2);
|
expect(declaratorPaths[2].scope.id).to.equal(2);
|
||||||
|
|
||||||
expect(declaratorPaths[0].node.id.value).to.equal('globalScope');
|
expect(nameOf(declaratorPaths[0].node.id)).to.equal('globalScope');
|
||||||
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
||||||
'globalScope',
|
'globalScope',
|
||||||
'alsoGlobalScope',
|
'alsoGlobalScope',
|
||||||
|
|
@ -173,10 +181,10 @@ describe('swcTraverse', () => {
|
||||||
// 0 and 3 are the same scope
|
// 0 and 3 are the same scope
|
||||||
expect(declaratorPaths[0].scope).to.equal(declaratorPaths[3].scope);
|
expect(declaratorPaths[0].scope).to.equal(declaratorPaths[3].scope);
|
||||||
// Scope bindings refer to Declarator nodes
|
// Scope bindings refer to Declarator nodes
|
||||||
expect(declaratorPaths[0].scope.bindings.globalScope.identifier).to.equal(
|
expect(declaratorPaths[0].scope.bindings.globalScope.path.node).to.equal(
|
||||||
declaratorPaths[0].node,
|
declaratorPaths[0].node,
|
||||||
);
|
);
|
||||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.identifier).to.equal(
|
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.path.node).to.equal(
|
||||||
declaratorPaths[3].node,
|
declaratorPaths[3].node,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -194,7 +202,7 @@ describe('swcTraverse', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -203,8 +211,8 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
const scopes = gatherAllScopes(swcAst);
|
const scopes = gatherAllScopes(oxcAst);
|
||||||
|
|
||||||
expect(scopes[1].path?.node).to.equal(declaratorPaths[0].node);
|
expect(scopes[1].path?.node).to.equal(declaratorPaths[0].node);
|
||||||
expect(scopes[2].path?.node).to.equal(declaratorPaths[1].node);
|
expect(scopes[2].path?.node).to.equal(declaratorPaths[1].node);
|
||||||
|
|
@ -223,7 +231,7 @@ describe('swcTraverse', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -232,7 +240,7 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(declaratorPaths[0].scope.id).to.equal(2);
|
expect(declaratorPaths[0].scope.id).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
@ -246,7 +254,7 @@ describe('swcTraverse', () => {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}`;
|
}`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -255,10 +263,10 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(declaratorPaths[0].node.id.value).to.equal('myCases');
|
expect(nameOf(declaratorPaths[0].node.id)).to.equal('myCases');
|
||||||
expect(declaratorPaths[1].node.id.value).to.equal('x');
|
expect(nameOf(declaratorPaths[1].node.id)).to.equal('x');
|
||||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
@ -269,18 +277,19 @@ describe('swcTraverse', () => {
|
||||||
toString(dateObj, opt = {}) {},
|
toString(dateObj, opt = {}) {},
|
||||||
};
|
};
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const results = [];
|
const results = [];
|
||||||
const visitor = {
|
const visitor = {
|
||||||
MethodProperty(/** @type {any} */ path) {
|
// MethodProperty for swc...
|
||||||
|
ObjectProperty(/** @type {any} */ path) {
|
||||||
results.push(path);
|
results.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(results[0].node.key.value).to.equal('toString');
|
expect(nameOf(results[0].node.key)).to.equal('toString');
|
||||||
expect(results[0].scope.id).to.equal(0);
|
expect(results[0].scope.id).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -292,7 +301,7 @@ describe('swcTraverse', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -301,10 +310,10 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(declaratorPaths[0].node.id.value).to.equal('x');
|
expect(nameOf(declaratorPaths[0].node.id)).to.equal('x');
|
||||||
expect(declaratorPaths[1].node.id.value).to.equal('z');
|
expect(nameOf(declaratorPaths[1].node.id)).to.equal('z');
|
||||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
@ -322,7 +331,7 @@ describe('swcTraverse', () => {
|
||||||
}
|
}
|
||||||
let alsoGlobalScope = 3;
|
let alsoGlobalScope = 3;
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -334,17 +343,17 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
||||||
'globalScope',
|
'globalScope',
|
||||||
'alsoGlobalScope',
|
'alsoGlobalScope',
|
||||||
]);
|
]);
|
||||||
// Scope bindings refer to Declarator nodes
|
// Scope bindings refer to Declarator nodes
|
||||||
expect(declaratorPaths[0].scope.bindings.globalScope.identifier).to.equal(
|
expect(declaratorPaths[0].scope.bindings.globalScope.path.node).to.equal(
|
||||||
declaratorPaths[0].node,
|
declaratorPaths[0].node,
|
||||||
);
|
);
|
||||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.identifier).to.equal(
|
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.path.node).to.equal(
|
||||||
declaratorPaths[3].node,
|
declaratorPaths[3].node,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -359,7 +368,7 @@ describe('swcTraverse', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
|
|
||||||
/** @type {SwcPath[]} */
|
/** @type {SwcPath[]} */
|
||||||
const declaratorPaths = [];
|
const declaratorPaths = [];
|
||||||
|
|
@ -368,7 +377,7 @@ describe('swcTraverse', () => {
|
||||||
declaratorPaths.push(path);
|
declaratorPaths.push(path);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||||
|
|
||||||
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
||||||
'globalScope',
|
'globalScope',
|
||||||
|
|
@ -389,7 +398,7 @@ describe('swcTraverse', () => {
|
||||||
* @param {string} code
|
* @param {string} code
|
||||||
*/
|
*/
|
||||||
async function compareScopeResultsWithBabel(code) {
|
async function compareScopeResultsWithBabel(code) {
|
||||||
const swcAst = await AstService._getSwcAst(code);
|
const oxcAst = await AstService._getOxcAst(code);
|
||||||
const babelAst = await AstService._getBabelAst(code);
|
const babelAst = await AstService._getBabelAst(code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -407,7 +416,7 @@ describe('swcTraverse', () => {
|
||||||
|
|
||||||
/** @type {SwcScope[]} */
|
/** @type {SwcScope[]} */
|
||||||
const swcScopes = [];
|
const swcScopes = [];
|
||||||
swcTraverse(swcAst, {
|
oxcTraverse(oxcAst, {
|
||||||
enter({ scope }) {
|
enter({ scope }) {
|
||||||
if (!swcScopes.includes(scope)) {
|
if (!swcScopes.includes(scope)) {
|
||||||
swcScopes.push(scope);
|
swcScopes.push(scope);
|
||||||
|
|
@ -420,9 +429,17 @@ describe('swcTraverse', () => {
|
||||||
expect(babelScopes.length).to.equal(swcScopes.length);
|
expect(babelScopes.length).to.equal(swcScopes.length);
|
||||||
for (let i = 0; i < babelScopes.length; i += 1) {
|
for (let i = 0; i < babelScopes.length; i += 1) {
|
||||||
expect(babelScopes[i].uid - babelRootScopeIdOffset).to.equal(swcScopes[i].id);
|
expect(babelScopes[i].uid - babelRootScopeIdOffset).to.equal(swcScopes[i].id);
|
||||||
expect(Object.keys(babelScopes[i].bindings)).to.deep.equal(
|
|
||||||
Object.keys(swcScopes[i].bindings),
|
const babelEntries = Object.entries(babelScopes[i].bindings);
|
||||||
);
|
const swcEntries = Object.entries(swcScopes[i].bindings);
|
||||||
|
for (const [j, [bindingKey, binding]] of babelEntries.entries()) {
|
||||||
|
expect(bindingKey).to.equal(swcEntries[j][0]);
|
||||||
|
expect(binding.path.node.type).to.equal(swcEntries[j][1]?.path?.node.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect(Object.keys(babelScopes[i].bindings)).to.deep.equal(
|
||||||
|
// Object.keys(swcScopes[i].bindings),
|
||||||
|
// );
|
||||||
// expect(babelScopes[i].references).to.deep.equal(swcResults[i].references);
|
// expect(babelScopes[i].references).to.deep.equal(swcResults[i].references);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -459,5 +476,15 @@ describe('swcTraverse', () => {
|
||||||
|
|
||||||
await compareScopeResultsWithBabel(code);
|
await compareScopeResultsWithBabel(code);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles all kinds of lexical scopes and bindings in a similar way 2', async () => {
|
||||||
|
const code = `
|
||||||
|
import { LionComp } from 'ref/LionComp.js';
|
||||||
|
|
||||||
|
export class WolfComp extends LionComp {}
|
||||||
|
`;
|
||||||
|
|
||||||
|
await compareScopeResultsWithBabel(code);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { it } from 'mocha';
|
import { it } from 'mocha';
|
||||||
|
|
||||||
import { setupAnalyzerTest } from '../../../../test-helpers/setup-analyzer-test.js';
|
import { setupAnalyzerTest } from '../../../test-helpers/setup-analyzer-test.js';
|
||||||
import { mockProject } from '../../../../test-helpers/mock-project-helpers.js';
|
import { mockProject } from '../../../test-helpers/mock-project-helpers.js';
|
||||||
import { swcTraverse } from '../../../../src/program/utils/swc-traverse.js';
|
import { oxcTraverse } from '../../../src/program/utils/oxc-traverse.js';
|
||||||
import { AstService } from '../../../../src/program/core/AstService.js';
|
import { AstService } from '../../../src/program/core/AstService.js';
|
||||||
import {
|
import {
|
||||||
trackDownIdentifier,
|
trackDownIdentifier,
|
||||||
trackDownIdentifierFromScope,
|
trackDownIdentifierFromScope,
|
||||||
} from '../../../../src/program/utils/track-down-identifier.js';
|
} from '../../../src/program/utils/track-down-identifier.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@babel/traverse').NodePath} NodePath
|
* @typedef {import('@babel/traverse').NodePath} NodePath
|
||||||
|
|
@ -295,7 +295,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
|
|
||||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||||
// const ast = AstService._getBabelAst(projectFiles['./src/declarationOfMyClass.js']);
|
// const ast = AstService._getBabelAst(projectFiles['./src/declarationOfMyClass.js']);
|
||||||
const ast = AstService._getSwcAst(projectFiles['./src/declarationOfMyClass.js']);
|
const ast = await AstService._getOxcAst(projectFiles['./src/declarationOfMyClass.js']);
|
||||||
|
|
||||||
// Let's say we want to track down 'MyClass' in the code above
|
// Let's say we want to track down 'MyClass' in the code above
|
||||||
const identifierNameInScope = 'MyClass';
|
const identifierNameInScope = 'MyClass';
|
||||||
|
|
@ -309,7 +309,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
// astPath = path;
|
// astPath = path;
|
||||||
// },
|
// },
|
||||||
// });
|
// });
|
||||||
swcTraverse(ast, {
|
oxcTraverse(ast, {
|
||||||
ClassDeclaration(path) {
|
ClassDeclaration(path) {
|
||||||
astPath = path;
|
astPath = path;
|
||||||
},
|
},
|
||||||
|
|
@ -346,7 +346,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
|
|
||||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||||
// const ast = AstService._getBabelAst(projectFiles['./imported.js']);
|
// const ast = AstService._getBabelAst(projectFiles['./imported.js']);
|
||||||
const ast = AstService._getSwcAst(projectFiles['./imported.js']);
|
const ast = await AstService._getOxcAst(projectFiles['./imported.js']);
|
||||||
|
|
||||||
// Let's say we want to track down 'MyClass' in the code above
|
// Let's say we want to track down 'MyClass' in the code above
|
||||||
const identifierNameInScope = 'MyClass';
|
const identifierNameInScope = 'MyClass';
|
||||||
|
|
@ -360,7 +360,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
// astPath = path;
|
// astPath = path;
|
||||||
// },
|
// },
|
||||||
// });
|
// });
|
||||||
swcTraverse(ast, {
|
oxcTraverse(ast, {
|
||||||
ImportDeclaration(path) {
|
ImportDeclaration(path) {
|
||||||
astPath = path;
|
astPath = path;
|
||||||
},
|
},
|
||||||
|
|
@ -394,7 +394,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
|
|
||||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||||
// const ast = AstService._getBabelAst(projectFiles['./imported.js']);
|
// const ast = AstService._getBabelAst(projectFiles['./imported.js']);
|
||||||
const ast = AstService._getSwcAst(projectFiles['./imported.js']);
|
const ast = await AstService._getOxcAst(projectFiles['./imported.js']);
|
||||||
|
|
||||||
// Let's say we want to track down 'MyClass' in the code above
|
// Let's say we want to track down 'MyClass' in the code above
|
||||||
const identifierNameInScope = 'El1';
|
const identifierNameInScope = 'El1';
|
||||||
|
|
@ -408,7 +408,7 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
// astPath = path;
|
// astPath = path;
|
||||||
// },
|
// },
|
||||||
// });
|
// });
|
||||||
swcTraverse(ast, {
|
oxcTraverse(ast, {
|
||||||
ClassDeclaration(path) {
|
ClassDeclaration(path) {
|
||||||
astPath = path;
|
astPath = path;
|
||||||
},
|
},
|
||||||
|
|
@ -426,4 +426,54 @@ describe('trackDownIdentifierFromScope', () => {
|
||||||
specifier: 'El1',
|
specifier: 'El1',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`handles edge cases`, async () => {
|
||||||
|
const projectName = 'my-project';
|
||||||
|
const projectPath = '/my/project';
|
||||||
|
|
||||||
|
const targetProject = {
|
||||||
|
path: projectPath,
|
||||||
|
name: projectName,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
file: './WolfComp.js',
|
||||||
|
code: `
|
||||||
|
import { LionComp } from 'ref/LionComp.js';
|
||||||
|
|
||||||
|
export class WolfComp extends LionComp {}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: './node_modules/ref/LionComp.js',
|
||||||
|
code: `export class LionComp extends HTMLElement {};`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
mockProject(targetProject, { projectName, projectPath });
|
||||||
|
const ast = await AstService._getOxcAst(targetProject.files[0].code);
|
||||||
|
|
||||||
|
// Let's say we want to track down 'LionComp' in the code above
|
||||||
|
const identifierNameInScope = 'LionComp';
|
||||||
|
const fullCurrentFilePath = '/my/project/WolfComp.js';
|
||||||
|
|
||||||
|
/** @type {NodePath} */
|
||||||
|
let astPath;
|
||||||
|
|
||||||
|
oxcTraverse(ast, {
|
||||||
|
ClassDeclaration(path) {
|
||||||
|
astPath = path;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootFile = await trackDownIdentifierFromScope(
|
||||||
|
// @ts-ignore
|
||||||
|
astPath,
|
||||||
|
identifierNameInScope,
|
||||||
|
fullCurrentFilePath,
|
||||||
|
projectPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(rootFile).to.deep.equal({ file: 'ref/LionComp.js', specifier: 'LionComp' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
*/
|
*/
|
||||||
export type AnalyzerName = `${'find' | 'match'}-${string}` | '';
|
export type AnalyzerName = `${'find' | 'match'}-${string}` | '';
|
||||||
|
|
||||||
export type AnalyzerAst = 'babel' | 'swc-to-babel' | 'swc';
|
export type AnalyzerAst = 'babel' | 'swc' | 'oxc';
|
||||||
|
|
||||||
// TODO: make sure that data structures of JSON output (generated in ReportService)
|
// TODO: make sure that data structures of JSON output (generated in ReportService)
|
||||||
// and data structure generated in Analyzer.prototype._finalize match exactly (move logic from ReportSerivce to _finalize)
|
// and data structure generated in Analyzer.prototype._finalize match exactly (move logic from ReportSerivce to _finalize)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import { IdentifierName } from "../index.js";
|
||||||
|
|
||||||
export type SwcScope = {
|
export type SwcScope = {
|
||||||
id: number;
|
id: number;
|
||||||
parentScope?: Scope;
|
parentScope?: Scope;
|
||||||
bindings: { [key: string]: SwcBinding };
|
bindings: { [key: string]: SwcBinding };
|
||||||
path: SwcPath | null;
|
path: SwcPath | null;
|
||||||
|
getBinding: (IdentifierName:string) => SwcBinding;
|
||||||
_pendingRefsWithoutBinding: SwcNode[];
|
_pendingRefsWithoutBinding: SwcNode[];
|
||||||
_isIsolatedBlockStatement: boolean;
|
_isIsolatedBlockStatement: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -19,7 +22,7 @@ export type SwcPath = {
|
||||||
node: SwcNode;
|
node: SwcNode;
|
||||||
parent: SwcNode;
|
parent: SwcNode;
|
||||||
stop: function;
|
stop: function;
|
||||||
scope: SwcScope | undefined;
|
scope: SwcScope;
|
||||||
parentPath: SwcPath | null | undefined;
|
parentPath: SwcPath | null | undefined;
|
||||||
get: (id: string) => SwcPath | undefined;
|
get: (id: string) => SwcPath | undefined;
|
||||||
type: string;
|
type: string;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"lib": ["es2017", "dom"],
|
"lib": ["es2022", "dom"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue