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_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": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@rocket/blog/-/blog-0.4.0.tgz",
|
||||
|
|
@ -5525,6 +5516,7 @@
|
|||
"version": "1.7.36",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.36.tgz",
|
||||
"integrity": "sha512-bu7ymMX+LCJOSSrKank25Jaq66ymLVA9fOUuy4ck3/6rbXdLw+pIJPnIDKQ9uNcxww8KDxOuJk9Ui9pqR+aGFw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -5566,6 +5558,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5582,6 +5575,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5598,6 +5592,7 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5614,6 +5609,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5630,6 +5626,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5646,6 +5643,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5662,6 +5660,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5678,6 +5677,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5694,6 +5694,7 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5710,6 +5711,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -5723,12 +5725,14 @@
|
|||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@swc/types": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz",
|
||||
"integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3"
|
||||
|
|
@ -28032,17 +28036,13 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"packages-node/providence-analytics": {
|
||||
"version": "0.16.5",
|
||||
"version": "0.16.8",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.8",
|
||||
"@babel/plugin-syntax-import-assertions": "^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",
|
||||
"@swc/core": "^1.7.36",
|
||||
"commander": "^2.20.3",
|
||||
"oxc-parser": "^0.34.0",
|
||||
"parse5": "^7.2.0",
|
||||
"semver": "^7.6.3"
|
||||
},
|
||||
|
|
@ -28050,6 +28050,9 @@
|
|||
"providence": "src/cli/index.js"
|
||||
},
|
||||
"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/mocha": "^10.0.9",
|
||||
"@web/dev-server": "^0.4.6",
|
||||
|
|
@ -28060,6 +28063,134 @@
|
|||
},
|
||||
"engines": {
|
||||
"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": {
|
||||
|
|
@ -28164,7 +28295,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@lion/ui",
|
||||
"version": "0.7.9",
|
||||
"version": "0.8.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bundled-es-modules/message-format": "^6.2.4",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@
|
|||
- bdb038e1: Many improvements:
|
||||
|
||||
- rewritten from babel to swc
|
||||
- swcTraverse tool, compatible with babel traverse api
|
||||
- oxcTraverse tool, compatible with babel traverse api
|
||||
- increased performance
|
||||
- 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"
|
||||
},
|
||||
"files": [
|
||||
"inlined-swc-to-babel",
|
||||
"src",
|
||||
"types"
|
||||
],
|
||||
|
|
@ -38,18 +37,22 @@
|
|||
"test:node:unit": "mocha './{test-node,src}/**/*.test.js'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.8",
|
||||
"@babel/plugin-syntax-import-assertions": "^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",
|
||||
"@swc/core": "^1.7.36",
|
||||
"commander": "^2.20.3",
|
||||
"oxc-parser": "^0.34.0",
|
||||
"parse5": "^7.2.0",
|
||||
"semver": "^7.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/parser": "^7.25.8",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
||||
"@swc/core": "^1.7.36"
|
||||
},
|
||||
"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/mocha": "^10.0.9",
|
||||
"@web/dev-server": "^0.4.6",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
/* eslint-disable no-shadow, no-param-reassign */
|
||||
import path from 'path';
|
||||
|
||||
import babelTraverse from '@babel/traverse';
|
||||
import t from '@babel/types';
|
||||
import { oxcTraverse, isProperty } 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';
|
||||
|
||||
/**
|
||||
|
|
@ -16,6 +15,7 @@ import { Analyzer } from '../core/Analyzer.js';
|
|||
* @typedef {import('../../../types/index.js').FindClassesAnalyzerOutputFile} FindClassesAnalyzerOutputFile
|
||||
* @typedef {import('../../../types/index.js').FindClassesAnalyzerEntry} FindClassesAnalyzerEntry
|
||||
* @typedef {import('../../../types/index.js').FindClassesConfig} FindClassesConfig
|
||||
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -132,55 +132,58 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
|||
methods: [],
|
||||
};
|
||||
|
||||
astPath.traverse({
|
||||
ClassMethod(astPath) {
|
||||
// if (isBlacklisted(astPath)) {
|
||||
// return;
|
||||
// }
|
||||
if (isStaticProperties(astPath)) {
|
||||
let hasFoundTopLvlObjExpr = false;
|
||||
astPath.traverse({
|
||||
ObjectExpression(astPath) {
|
||||
if (hasFoundTopLvlObjExpr) return;
|
||||
hasFoundTopLvlObjExpr = true;
|
||||
astPath.node.properties.forEach(objectProperty => {
|
||||
if (!t.isProperty(objectProperty)) {
|
||||
// we can also have a SpreadElement
|
||||
return;
|
||||
}
|
||||
const propRes = {};
|
||||
const { name } = objectProperty.key;
|
||||
propRes.name = name;
|
||||
propRes.accessType = computeAccessType(name);
|
||||
propRes.kind = [...(propRes.kind || []), objectProperty.kind];
|
||||
classRes.members.props.push(propRes);
|
||||
});
|
||||
},
|
||||
});
|
||||
return;
|
||||
const handleMethodDefinitionOrClassMethod = astPath => {
|
||||
// if (isBlacklisted(astPath)) {
|
||||
// return;
|
||||
// }
|
||||
if (isStaticProperties(astPath)) {
|
||||
let hasFoundTopLvlObjExpr = false;
|
||||
astPath.traverse({
|
||||
ObjectExpression(astPath) {
|
||||
if (hasFoundTopLvlObjExpr) return;
|
||||
hasFoundTopLvlObjExpr = true;
|
||||
astPath.node.properties.forEach(objectProperty => {
|
||||
if (!isProperty(objectProperty)) {
|
||||
// we can also have a SpreadElement
|
||||
return;
|
||||
}
|
||||
const propRes = {};
|
||||
const { name } = objectProperty.key;
|
||||
propRes.name = name;
|
||||
propRes.accessType = computeAccessType(name);
|
||||
propRes.kind = [...(propRes.kind || []), objectProperty.kind];
|
||||
classRes.members.props.push(propRes);
|
||||
});
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const methodRes = {};
|
||||
const { name } = astPath.node.key;
|
||||
methodRes.name = name;
|
||||
methodRes.accessType = computeAccessType(name);
|
||||
|
||||
if (astPath.node.kind === 'set' || astPath.node.kind === 'get') {
|
||||
if (astPath.node.static) {
|
||||
methodRes.static = true;
|
||||
}
|
||||
|
||||
const methodRes = {};
|
||||
const { name } = astPath.node.key;
|
||||
methodRes.name = name;
|
||||
methodRes.accessType = computeAccessType(name);
|
||||
|
||||
if (astPath.node.kind === 'set' || astPath.node.kind === 'get') {
|
||||
if (astPath.node.static) {
|
||||
methodRes.static = true;
|
||||
}
|
||||
methodRes.kind = [...(methodRes.kind || []), astPath.node.kind];
|
||||
// Merge getter/setters into one
|
||||
const found = classRes.members.props.find(p => p.name === name);
|
||||
if (found) {
|
||||
found.kind = [...(found.kind || []), astPath.node.kind];
|
||||
} else {
|
||||
classRes.members.props.push(methodRes);
|
||||
}
|
||||
methodRes.kind = [...(methodRes.kind || []), astPath.node.kind];
|
||||
// Merge getter/setters into one
|
||||
const found = classRes.members.props.find(p => p.name === name);
|
||||
if (found) {
|
||||
found.kind = [...(found.kind || []), astPath.node.kind];
|
||||
} else {
|
||||
classRes.members.methods.push(methodRes);
|
||||
classRes.members.props.push(methodRes);
|
||||
}
|
||||
},
|
||||
} else {
|
||||
classRes.members.methods.push(methodRes);
|
||||
}
|
||||
};
|
||||
|
||||
astPath.traverse({
|
||||
ClassMethod: handleMethodDefinitionOrClassMethod,
|
||||
MethodDefinition: handleMethodDefinitionOrClassMethod,
|
||||
});
|
||||
|
||||
classesFound.push(classRes);
|
||||
|
|
@ -188,7 +191,7 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
|
|||
|
||||
const classesToTraverse = [];
|
||||
|
||||
babelTraverse.default(babelAst, {
|
||||
oxcTraverse(babelAst, {
|
||||
ClassDeclaration(astPath) {
|
||||
classesToTraverse.push({ astPath, isMixin: false });
|
||||
},
|
||||
|
|
@ -228,8 +231,8 @@ export default class FindClassesAnalyzer extends Analyzer {
|
|||
/** @type {AnalyzerName} */
|
||||
static analyzerName = 'find-classes';
|
||||
|
||||
/** @type {'babel'|'swc-to-babel'} */
|
||||
static requiredAst = 'babel';
|
||||
/** @type {AnalyzerAst} */
|
||||
static requiredAst = 'oxc';
|
||||
|
||||
/**
|
||||
* Will find all public members (properties (incl. getter/setters)/functions) of a class and
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import path from 'path';
|
||||
|
||||
import babelTraverse from '@babel/traverse';
|
||||
import t from '@babel/types';
|
||||
// import babelTraverse from '@babel/traverse';
|
||||
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';
|
||||
|
||||
/**
|
||||
|
|
@ -40,27 +40,29 @@ async function trackdownRoot(transformedEntry, relativePath, projectPath) {
|
|||
|
||||
/**
|
||||
* Finds import specifiers and sources
|
||||
* @param {File} babelAst
|
||||
* @param {File} oxcAst
|
||||
*/
|
||||
function findCustomElementsPerAstFile(babelAst) {
|
||||
function findCustomElementsPerAstFile(oxcAst) {
|
||||
const definitions = [];
|
||||
babelTraverse.default(babelAst, {
|
||||
oxcTraverse(oxcAst, {
|
||||
CallExpression(astPath) {
|
||||
let found = false;
|
||||
// Doing it like this we detect 'customElements.define()',
|
||||
// but also 'window.customElements.define()'
|
||||
astPath.traverse({
|
||||
MemberExpression(memberPath) {
|
||||
if (memberPath.parentPath !== astPath) {
|
||||
// MemberExpression in babel
|
||||
StaticMemberExpression(memberPath) {
|
||||
if (memberPath.node !== astPath.node.callee) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { node } = memberPath;
|
||||
|
||||
if (node.object.name === 'customElements' && node.property.name === 'define') {
|
||||
found = true;
|
||||
}
|
||||
if (
|
||||
node.object.object &&
|
||||
node.object.object.name === 'window' &&
|
||||
node.object.object?.name === 'window' &&
|
||||
node.object.property.name === 'customElements' &&
|
||||
node.property.name === 'define'
|
||||
) {
|
||||
|
|
@ -72,7 +74,7 @@ function findCustomElementsPerAstFile(babelAst) {
|
|||
let tagName;
|
||||
let constructorIdentifier;
|
||||
|
||||
if (t.isLiteral(astPath.node.arguments[0])) {
|
||||
if (astPath.node.arguments[0].type === 'StringLiteral') {
|
||||
tagName = astPath.node.arguments[0].value;
|
||||
} else {
|
||||
// No Literal found. For now, we only mark them as '[variable]'
|
||||
|
|
@ -95,8 +97,8 @@ export default class FindCustomelementsAnalyzer extends Analyzer {
|
|||
/** @type {AnalyzerName} */
|
||||
static analyzerName = 'find-customelements';
|
||||
|
||||
/** @type {'babel'|'swc-to-babel'} */
|
||||
static requiredAst = 'swc-to-babel';
|
||||
/** @type {AnalyzerAst} */
|
||||
static requiredAst = 'oxc';
|
||||
|
||||
/**
|
||||
* 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 { trackDownIdentifier } from '../utils/track-down-identifier.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 { Analyzer } from '../core/Analyzer.js';
|
||||
|
||||
|
|
@ -110,20 +110,25 @@ function cleanup(transformedFile) {
|
|||
function getExportSpecifiers(node) {
|
||||
// handles default [export const g = 4];
|
||||
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) {
|
||||
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'];
|
||||
return (node.specifiers || []).map(s => {
|
||||
if (s.exported) {
|
||||
// { 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 }
|
||||
return s.orig.value;
|
||||
return s.orig.value || s.local.name;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -133,11 +138,18 @@ function getExportSpecifiers(node) {
|
|||
function getLocalNameSpecifiers(node) {
|
||||
return (node.declaration?.declarations || node.specifiers || [])
|
||||
.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 {
|
||||
// if reserved keyword 'default' is used, translate it into 'providence keyword'
|
||||
local: s.orig.value === 'default' ? '[default]' : s.orig.value,
|
||||
exported: s.exported.value,
|
||||
local:
|
||||
(s.orig?.value || s.local?.name) === 'default'
|
||||
? '[default]'
|
||||
: s.orig?.value || s.local?.name,
|
||||
exported: s.exported.value || s.exported.name,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
|
@ -150,10 +162,10 @@ const isImportingSpecifier = pathOrNode =>
|
|||
|
||||
/**
|
||||
* Finds import specifiers and sources for a given ast result
|
||||
* @param {SwcAstModule} swcAst
|
||||
* @param {SwcAstModule} oxcAst
|
||||
* @param {FindExportsConfig} config
|
||||
*/
|
||||
function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
||||
function findExportsPerAstFile(oxcAst, { skipFileImports }) {
|
||||
LogService.debug(`Analyzer "find-exports": started findExportsPerAstFile method`);
|
||||
|
||||
// Visit AST...
|
||||
|
|
@ -169,7 +181,7 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
|||
const exportHandler = (/** @type {SwcPath} */ astPath) => {
|
||||
const exportSpecifiers = getExportSpecifiers(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 assertionType = getAssertionType(astPath.node);
|
||||
if (assertionType) {
|
||||
|
|
@ -180,15 +192,18 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
|||
|
||||
const exportDefaultHandler = (/** @type {SwcPath} */ astPath) => {
|
||||
const exportSpecifiers = ['[default]'];
|
||||
const { node } = astPath;
|
||||
let source;
|
||||
// Is it an inline declaration like "export default class X {};" ?
|
||||
if (
|
||||
astPath.node.decl?.type === 'Identifier' ||
|
||||
astPath.node.expression?.type === 'Identifier'
|
||||
node.decl?.type === 'Identifier' ||
|
||||
node.expression?.type === 'Identifier' ||
|
||||
node.declaration?.type === 'Identifier'
|
||||
) {
|
||||
// It is a reference to an identifier like "export { x } from 'y';"
|
||||
const importOrDeclPath = getReferencedDeclaration({
|
||||
referencedIdentifierName: astPath.node.decl?.value || astPath.node.expression.value,
|
||||
referencedIdentifierName:
|
||||
node.decl?.value || node.expression?.value || node.declaration?.name,
|
||||
globalScopeBindings,
|
||||
});
|
||||
if (isImportingSpecifier(importOrDeclPath)) {
|
||||
|
|
@ -198,18 +213,23 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
|||
transformedFile.push({ exportSpecifiers, source, __tmp: { astPath } });
|
||||
};
|
||||
|
||||
const globalScopeHandler = ({ scope }) => {
|
||||
globalScopeBindings = scope.bindings;
|
||||
};
|
||||
|
||||
/** @type {SwcVisitor} */
|
||||
const visitor = {
|
||||
Module({ scope }) {
|
||||
globalScopeBindings = scope.bindings;
|
||||
},
|
||||
// for swc
|
||||
Module: globalScopeHandler,
|
||||
// for oxc and babel
|
||||
Program: globalScopeHandler,
|
||||
ExportDeclaration: exportHandler,
|
||||
ExportNamedDeclaration: exportHandler,
|
||||
ExportDefaultDeclaration: exportDefaultHandler,
|
||||
ExportDefaultExpression: exportDefaultHandler,
|
||||
};
|
||||
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
if (!skipFileImports) {
|
||||
// Always add an entry for just the file 'relativePath'
|
||||
|
|
@ -226,7 +246,7 @@ function findExportsPerAstFile(swcAst, { skipFileImports }) {
|
|||
export default class FindExportsAnalyzer extends Analyzer {
|
||||
static analyzerName = /** @type {AnalyzerName} */ ('find-exports');
|
||||
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||
|
||||
/**
|
||||
* @typedef FindExportsConfig
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { normalizeSourcePaths } from './helpers/normalize-source-paths.js';
|
||||
import { isRelativeSourcePath } from '../utils/relative-source-path.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 { 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').AnalyzerName} AnalyzerName
|
||||
* @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
|
||||
*/
|
||||
|
||||
// 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
|
||||
*/
|
||||
function getImportOrReexportsSpecifiers(node) {
|
||||
// @ts-expect-error
|
||||
return node.specifiers.map(s => {
|
||||
return (node.specifiers || []).map(s => {
|
||||
if (
|
||||
s.type === 'ImportDefaultSpecifier' ||
|
||||
s.type === 'ExportDefaultSpecifier' ||
|
||||
(s.type === 'ExportSpecifier' && s.exported?.value === 'default')
|
||||
(s.type === 'ExportSpecifier' &&
|
||||
(s.exported?.value === 'default' || s.exported?.name === 'default'))
|
||||
) {
|
||||
return '[default]';
|
||||
}
|
||||
if (s.type === 'ImportNamespaceSpecifier' || s.type === 'ExportNamespaceSpecifier') {
|
||||
return '[*]';
|
||||
}
|
||||
const importedValue = s.imported?.value || s.orig?.value || s.exported?.value || s.local?.value;
|
||||
const importedValue = getSpecifierValue(s);
|
||||
return importedValue;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`);
|
||||
|
||||
// https://github.com/babel/babel/blob/672a58660f0b15691c44582f1f3fdcdac0fa0d2f/packages/babel-core/src/transformation/index.ts#L110
|
||||
// Visit AST...
|
||||
/** @type {Partial<FindImportsAnalyzerEntry>[]} */
|
||||
const transformedFile = [];
|
||||
|
||||
swcTraverse(swcAst, {
|
||||
oxcTraverse(oxcAst, {
|
||||
ImportDeclaration({ node }) {
|
||||
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
||||
if (!importSpecifiers.length) {
|
||||
importSpecifiers.push('[file]'); // apparently, there was just a file import
|
||||
}
|
||||
|
||||
const source = node.source.value;
|
||||
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
||||
const assertionType = getAssertionType(node);
|
||||
|
|
@ -65,9 +96,9 @@ function findImportsPerAstFile(swcAst) {
|
|||
transformedFile.push(entry);
|
||||
},
|
||||
ExportNamedDeclaration({ node }) {
|
||||
if (!node.source) {
|
||||
return; // we are dealing with a regular export, not a reexport
|
||||
}
|
||||
// Are we dealing with a regular export, not a re-export?
|
||||
if (!node.source) return;
|
||||
|
||||
const importSpecifiers = getImportOrReexportsSpecifiers(node);
|
||||
const source = node.source.value;
|
||||
const entry = /** @type {Partial<FindImportsAnalyzerEntry>} */ ({ importSpecifiers, source });
|
||||
|
|
@ -77,7 +108,22 @@ function findImportsPerAstFile(swcAst) {
|
|||
}
|
||||
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 }) {
|
||||
if (node.callee?.type !== 'Import') {
|
||||
return;
|
||||
|
|
@ -92,6 +138,21 @@ function findImportsPerAstFile(swcAst) {
|
|||
: '[variable]';
|
||||
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;
|
||||
|
|
@ -100,7 +161,7 @@ function findImportsPerAstFile(swcAst) {
|
|||
export default class FindImportsSwcAnalyzer extends Analyzer {
|
||||
static analyzerName = /** @type {AnalyzerName} */ ('find-imports');
|
||||
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||
|
||||
/**
|
||||
* Finds import specifiers and sources
|
||||
|
|
@ -132,9 +193,9 @@ export default class FindImportsSwcAnalyzer extends Analyzer {
|
|||
/**
|
||||
* Traverse
|
||||
*/
|
||||
const queryOutput = await this._traverse(async (swcAst, context) => {
|
||||
const queryOutput = await this._traverse(async (oxcAst, context) => {
|
||||
// @ts-expect-error
|
||||
let transformedFile = findImportsPerAstFile(swcAst);
|
||||
let transformedFile = findImportsPerAstFile(oxcAst);
|
||||
// Post processing based on configuration...
|
||||
transformedFile = await normalizeSourcePaths(
|
||||
transformedFile,
|
||||
|
|
|
|||
|
|
@ -159,7 +159,8 @@ async function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerRes
|
|||
export default class MatchImportsAnalyzer extends Analyzer {
|
||||
static analyzerName = /** @type {AnalyzerName} */ ('match-imports');
|
||||
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('swc');
|
||||
// N.B. implicit
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||
|
||||
static requiresReference = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ function storeResult(resultsObj, exportId, filteredList, meta) {
|
|||
* @param {FindClassesAnalyzerResult} targetClassesAnalyzerResult
|
||||
* @param {FindClassesAnalyzerResult} refClassesAResult
|
||||
* @param {MatchSubclassesConfig} customConfig
|
||||
* @returns {AnalyzerQueryResult}
|
||||
* @returns {Promise<AnalyzerQueryResult>}
|
||||
*/
|
||||
async function matchSubclassesPostprocess(
|
||||
refExportsAnalyzerResult,
|
||||
|
|
@ -281,7 +281,7 @@ export default class MatchSubclassesAnalyzer extends Analyzer {
|
|||
/** @type {AnalyzerName} */
|
||||
static analyzerName = 'match-subclasses';
|
||||
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('babel');
|
||||
static requiredAst = /** @type {AnalyzerAst} */ ('oxc');
|
||||
|
||||
static requiresReference = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
import babelParser from '@babel/parser';
|
||||
import * as parse5 from 'parse5';
|
||||
import swc from '@swc/core';
|
||||
import { traverseHtml } from '../utils/traverse-html.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').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 {
|
||||
|
|
@ -17,9 +23,13 @@ export class AstService {
|
|||
* Compiles an array of file paths using Babel.
|
||||
* @param {string} code
|
||||
* @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, {
|
||||
sourceType: 'module',
|
||||
plugins: [
|
||||
|
|
@ -34,32 +44,18 @@ export class AstService {
|
|||
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.
|
||||
* @param {string} code
|
||||
* @param {ParserOptions} parserOptions
|
||||
* @returns {SwcAstModule}
|
||||
* @returns {Promise<SwcAstModule>}
|
||||
*/
|
||||
static _getSwcAst(code, parserOptions = {}) {
|
||||
const ast = swc.parseSync(code, {
|
||||
static async _getSwcAst(code, parserOptions = {}) {
|
||||
if (!swcParser) {
|
||||
swcParser = (await import('@swc/core')).default;
|
||||
}
|
||||
|
||||
const ast = swcParser.parseSync(code, {
|
||||
syntax: 'typescript',
|
||||
target: 'es2022',
|
||||
...parserOptions,
|
||||
|
|
@ -72,7 +68,22 @@ export class AstService {
|
|||
* @returns {number}
|
||||
*/
|
||||
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
|
||||
* @param { string } code
|
||||
* @param { 'babel'|'swc-to-babel'|'swc'} astType
|
||||
* @param {AnalyzerAst} astType
|
||||
* @param { {filePath?: PathFromSystemRoot} } options
|
||||
* @returns {File|undefined|SwcAstModule}
|
||||
* @returns {Promise<File|undefined|SwcAstModule|OxcParseResult>}
|
||||
*/
|
||||
// eslint-disable-next-line consistent-return
|
||||
static getAst(code, astType, { filePath } = {}) {
|
||||
static async getAst(code, astType, { filePath } = {}) {
|
||||
// eslint-disable-next-line default-case
|
||||
try {
|
||||
if (astType === 'babel') {
|
||||
return this._getBabelAst(code);
|
||||
}
|
||||
if (astType === 'swc-to-babel') {
|
||||
return this._getSwcToBabelAst(code);
|
||||
return await this._getBabelAst(code);
|
||||
}
|
||||
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.`);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -107,20 +107,27 @@ export class QueryService {
|
|||
* @param {AnalyzerAst} requiredAst
|
||||
*/
|
||||
static async addAstToProjectsData(projectsData, requiredAst) {
|
||||
return projectsData.map(projectData => {
|
||||
const resultWithAsts = [];
|
||||
|
||||
for (const projectData of projectsData) {
|
||||
const cachedData = astProjectsDataCache.get(projectData.project.path);
|
||||
if (cachedData) {
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
const resultEntries = projectData.entries.map(entry => {
|
||||
const ast = AstService.getAst(entry.context.code, requiredAst, { filePath: entry.file });
|
||||
return { ...entry, ast };
|
||||
});
|
||||
const resultEntries = [];
|
||||
for (const entry of projectData.entries) {
|
||||
const ast = await AstService.getAst(entry.context.code, requiredAst, {
|
||||
filePath: entry.file,
|
||||
});
|
||||
resultEntries.push({ ...entry, ast });
|
||||
}
|
||||
const astData = { ...projectData, entries: resultEntries };
|
||||
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) {
|
||||
return node.assertions.properties[0].value?.value;
|
||||
}
|
||||
// oxc
|
||||
if (node.withClause) {
|
||||
return node.withClause.withEntries[0].value?.name;
|
||||
}
|
||||
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 { oxcTraverse, getPathFromNode, nameOf } from './oxc-traverse.js';
|
||||
import { trackDownIdentifier } from './track-down-identifier.js';
|
||||
import { swcTraverse, getPathFromNode } from './swc-traverse.js';
|
||||
import { AstService } from '../core/AstService.js';
|
||||
import { toPosixPath } from './to-posix-path.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').PathFromSystemRoot} PathFromSystemRoot
|
||||
* @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst
|
||||
* @typedef {import('../../../types/index.js').SwcBinding} SwcBinding
|
||||
* @typedef {import('../../../types/index.js').SwcPath} SwcPath
|
||||
* @typedef {import('@swc/core').Node} SwcNode
|
||||
|
|
@ -57,9 +58,9 @@ export function getReferencedDeclaration({ referencedIdentifierName, globalScope
|
|||
return refDeclaratorBinding.path;
|
||||
}
|
||||
|
||||
if (refDeclaratorBinding.identifier.init.type === 'Identifier') {
|
||||
if (refDeclaratorBinding.path.node.init.type === 'Identifier') {
|
||||
return getReferencedDeclaration({
|
||||
referencedIdentifierName: refDeclaratorBinding.identifier.init.value,
|
||||
referencedIdentifierName: nameOf(refDeclaratorBinding.path.node.init),
|
||||
globalScopeBindings,
|
||||
});
|
||||
}
|
||||
|
|
@ -79,87 +80,98 @@ export function getReferencedDeclaration({ referencedIdentifierName, globalScope
|
|||
* 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; }>}
|
||||
*/
|
||||
export async function getSourceCodeFragmentOfDeclaration({
|
||||
exportedIdentifier,
|
||||
projectRootPath,
|
||||
parser = 'oxc',
|
||||
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
|
||||
const offset = AstService._getSwcOffset();
|
||||
// TODO: fix swc-to-babel lib to make this compatible with 'swc-to-babel' mode of getAst
|
||||
const swcAst = AstService._getSwcAst(code);
|
||||
const offset = parser === 'swc' ? await AstService._getSwcOffset() : -1;
|
||||
const ast = await AstService.getAst(code, parser);
|
||||
|
||||
/** @type {SwcPath} */
|
||||
let finalNodePath;
|
||||
|
||||
swcTraverse(
|
||||
swcAst,
|
||||
const moduleOrProgramHandler = 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 = getPathFromNode(astPath.node.body?.[0])?.scope.bindings;
|
||||
|
||||
if (exportedIdentifier === '[default]') {
|
||||
const defaultExportPath = /** @type {SwcPath} */ (
|
||||
getPathFromNode(
|
||||
astPath.node.body.find((/** @type {{ type: string; }} */ child) =>
|
||||
['ExportDefaultDeclaration', 'ExportDefaultExpression'].includes(child.type),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
const isReferenced =
|
||||
(defaultExportPath?.node.declaration?.type || defaultExportPath?.node.expression?.type) ===
|
||||
'Identifier';
|
||||
|
||||
if (!isReferenced) {
|
||||
finalNodePath =
|
||||
defaultExportPath.get('declaration') ||
|
||||
defaultExportPath.get('decl') ||
|
||||
defaultExportPath.get('expression');
|
||||
} else {
|
||||
finalNodePath = /** @type {SwcPath} */ (
|
||||
getReferencedDeclaration({
|
||||
referencedIdentifierName: nameOf(
|
||||
defaultExportPath.node.declaration || defaultExportPath.node.expression,
|
||||
),
|
||||
// @ts-expect-error
|
||||
globalScopeBindings,
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const variableDeclaratorPath = astPath.scope.bindings[exportedIdentifier].path;
|
||||
const varDeclNode = variableDeclaratorPath.node;
|
||||
const isReferenced = varDeclNode.init?.type === 'Identifier';
|
||||
const contentPath = varDeclNode.init
|
||||
? variableDeclaratorPath.get('init')
|
||||
: variableDeclaratorPath;
|
||||
|
||||
const name = varDeclNode.init
|
||||
? nameOf(varDeclNode.init)
|
||||
: nameOf(varDeclNode.id) || nameOf(varDeclNode.imported) || nameOf(varDeclNode.orig);
|
||||
|
||||
if (!isReferenced) {
|
||||
// it must be an exported declaration
|
||||
finalNodePath = contentPath;
|
||||
} else {
|
||||
finalNodePath = /** @type {SwcPath} */ (
|
||||
getReferencedDeclaration({
|
||||
referencedIdentifierName: name,
|
||||
// @ts-expect-error
|
||||
globalScopeBindings,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
oxcTraverse(
|
||||
ast,
|
||||
{
|
||||
Module(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 = getPathFromNode(astPath.node.body?.[0])?.scope.bindings;
|
||||
|
||||
if (exportedIdentifier === '[default]') {
|
||||
const defaultExportPath = /** @type {SwcPath} */ (
|
||||
getPathFromNode(
|
||||
astPath.node.body.find((/** @type {{ type: string; }} */ child) =>
|
||||
['ExportDefaultDeclaration', 'ExportDefaultExpression'].includes(child.type),
|
||||
),
|
||||
)
|
||||
);
|
||||
const isReferenced = defaultExportPath?.node.expression?.type === 'Identifier';
|
||||
|
||||
if (!isReferenced) {
|
||||
finalNodePath = defaultExportPath.get('decl') || defaultExportPath.get('expression');
|
||||
} else {
|
||||
finalNodePath = /** @type {SwcPath} */ (
|
||||
getReferencedDeclaration({
|
||||
referencedIdentifierName: defaultExportPath.node.expression.value,
|
||||
// @ts-expect-error
|
||||
globalScopeBindings,
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const variableDeclaratorPath = astPath.scope.bindings[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.value
|
||||
: varDeclNode.id?.value || varDeclNode.imported?.value || varDeclNode.orig?.value;
|
||||
|
||||
if (!isReferenced) {
|
||||
// it must be an exported declaration
|
||||
finalNodePath = contentPath;
|
||||
} else {
|
||||
finalNodePath = /** @type {SwcPath} */ (
|
||||
getReferencedDeclaration({
|
||||
referencedIdentifierName: name,
|
||||
// @ts-expect-error
|
||||
globalScopeBindings,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Module: moduleOrProgramHandler,
|
||||
Program: moduleOrProgramHandler,
|
||||
},
|
||||
{ needsAdvancedPaths: true },
|
||||
);
|
||||
|
|
@ -168,9 +180,9 @@ export async function getSourceCodeFragmentOfDeclaration({
|
|||
if (finalNodePath.type === 'ImportSpecifier') {
|
||||
// @ts-expect-error
|
||||
const importDeclNode = finalNodePath.parentPath.node;
|
||||
const source = importDeclNode.source.value;
|
||||
const source = nameOf(importDeclNode.source);
|
||||
// @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 rootFile = await trackDownIdentifier(
|
||||
|
|
@ -199,17 +211,20 @@ export async function getSourceCodeFragmentOfDeclaration({
|
|||
filePath: /** @type {PathFromSystemRoot} */ (filePathOrSrc),
|
||||
exportedIdentifier: rootFile.specifier,
|
||||
projectRootPath,
|
||||
parser,
|
||||
});
|
||||
}
|
||||
|
||||
const startOf = node => node.start || node.span.start;
|
||||
const endOf = node => node.end || node.span.end;
|
||||
return {
|
||||
// @ts-expect-error
|
||||
sourceNodePath: finalNodePath,
|
||||
sourceFragment: code.slice(
|
||||
// @ts-expect-error
|
||||
finalNodePath.node.span.start - 1 - offset,
|
||||
startOf(finalNodePath.node) - 1 - offset,
|
||||
// @ts-expect-error
|
||||
finalNodePath.node.span.end - 1 - offset,
|
||||
endOf(finalNodePath.node) - 1 - offset,
|
||||
),
|
||||
// sourceFragment: finalNodePath.node?.raw || finalNodePath.node?.value,
|
||||
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,
|
||||
} from './get-source-code-fragment-of-declaration.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 { memoize } from './memoize.js';
|
||||
export { hash } from './hash.js';
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
/**
|
||||
* @typedef {import('@swc/core').Module} SwcAstModule
|
||||
* @typedef {import('@swc/core').Node} SwcNode
|
||||
* @typedef {import('../../../types/index.js').SwcTraversalContext} SwcTraversalContext
|
||||
* @typedef {import('@swc/core').VariableDeclarator} SwcVariableDeclarator
|
||||
* @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').SwcScope} SwcScope
|
||||
* @typedef {import('../../../types/index.js').SwcVisitor} SwcVisitor
|
||||
* @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'];
|
||||
|
||||
/**
|
||||
* @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 {SwcScope} currentScope
|
||||
|
|
@ -48,6 +85,15 @@ function getNewScope(swcPath, currentScope, traversalContext) {
|
|||
parentScope: currentScope,
|
||||
path: swcPath,
|
||||
bindings: {},
|
||||
getBinding(identifierName) {
|
||||
let parentScope = currentScope;
|
||||
let foundBinding;
|
||||
while (!foundBinding && parentScope) {
|
||||
foundBinding = parentScope.bindings[identifierName];
|
||||
parentScope = parentScope.parentScope;
|
||||
}
|
||||
return foundBinding;
|
||||
},
|
||||
_pendingRefsWithoutBinding: [],
|
||||
_isIsolatedBlockStatement: isIsolatedBlockStatement,
|
||||
};
|
||||
|
|
@ -83,7 +129,7 @@ function createSwcPath(node, parent, stop, scope) {
|
|||
const swcPathForNode = getPathFromNode(node[id]);
|
||||
if (node[id] && !swcPathForNode) {
|
||||
// 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
|
||||
}
|
||||
|
|
@ -92,6 +138,10 @@ function createSwcPath(node, parent, stop, scope) {
|
|||
get type() {
|
||||
return node.type;
|
||||
},
|
||||
traverse(visitor) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
return oxcTraverse(node, visitor);
|
||||
},
|
||||
};
|
||||
swcPathCache.set(node, swcPath);
|
||||
return swcPath;
|
||||
|
|
@ -103,19 +153,18 @@ function createSwcPath(node, parent, stop, scope) {
|
|||
* - an import specifier (like "import { a } from 'b'")?
|
||||
* Handy to know if the parents of Identifiers mark a binding
|
||||
* @param {SwcNode} parent
|
||||
* @param {string} identifierValue
|
||||
* @param {string} identifierName
|
||||
*/
|
||||
function isBindingNode(parent, identifierValue) {
|
||||
if (parent.type === 'VariableDeclarator') {
|
||||
function isBindingNode(parent, identifierName) {
|
||||
if (['VariableDeclarator', 'ClassDeclaration'].includes(parent.type)) {
|
||||
// @ts-expect-error
|
||||
return parent.id.value === identifierValue;
|
||||
return nameOf(parent.id) === identifierName;
|
||||
}
|
||||
return [
|
||||
'ClassDeclaration',
|
||||
'FunctionDeclaration',
|
||||
'ArrowFunctionExpression',
|
||||
'ImportSpecifier',
|
||||
'ImportDefaultSpecifier',
|
||||
'FunctionDeclaration',
|
||||
'ImportSpecifier',
|
||||
].includes(parent.type);
|
||||
}
|
||||
|
||||
|
|
@ -141,15 +190,13 @@ function isBindingRefNode(parent) {
|
|||
function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
||||
const { node, parent, scope, parentPath } = swcPathForIdentifier;
|
||||
|
||||
if (node.type !== 'Identifier') {
|
||||
return;
|
||||
}
|
||||
if (node.type !== 'Identifier') return;
|
||||
|
||||
// const parentPath = getPathFromNode(parent);
|
||||
if (isBindingNode(parent, node.value)) {
|
||||
if (isBindingNode(parent, nameOf(node))) {
|
||||
/** @type {SwcBinding} */
|
||||
const binding = {
|
||||
identifier: parent,
|
||||
identifier: parent?.id || parent?.identifier,
|
||||
// kind: 'var',
|
||||
refs: [],
|
||||
path: swcPathForIdentifier.parentPath,
|
||||
|
|
@ -170,19 +217,20 @@ function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
|||
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
|
||||
scopeBindingBelongsTo.bindings[idName] = binding;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// In other cases, we are dealing with a reference that must be bound to a binding
|
||||
else if (isBindingRefNode(parent)) {
|
||||
// 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) {
|
||||
binding.refs.push(parentPath);
|
||||
} else {
|
||||
|
|
@ -200,7 +248,7 @@ function addPotentialBindingOrRefToScope(swcPathForIdentifier) {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
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.
|
||||
* @param {SwcAstModule} swcAst
|
||||
* @param {SwcAstModule|SwcNode} oxcAst
|
||||
* @param {SwcVisitor} visitor
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
|
@ -344,15 +392,18 @@ export function swcTraverse(swcAst, visitor, { needsAdvancedPaths = false } = {}
|
|||
id: traversalContext.scopeId,
|
||||
bindings: {},
|
||||
path: null,
|
||||
getBinding(/** @type {string} */ identifierName) {
|
||||
return initialScope.bindings[identifierName];
|
||||
},
|
||||
_pendingRefsWithoutBinding: [],
|
||||
_isIsolatedBlockStatement: false,
|
||||
};
|
||||
if (needsAdvancedPaths) {
|
||||
// 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
|
||||
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
|
||||
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 { resolveImportPath } from './resolve-import-path.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 { memoize } from './memoize.js';
|
||||
|
||||
|
|
@ -55,18 +55,19 @@ function getBindingAndSourceReexports(swcPath, identifierName) {
|
|||
}
|
||||
const rootPath = curPath;
|
||||
|
||||
swcTraverse(rootPath.node, {
|
||||
oxcTraverse(rootPath.node, {
|
||||
ExportSpecifier(astPath) {
|
||||
const { node } = astPath;
|
||||
// eslint-disable-next-line arrow-body-style
|
||||
const found =
|
||||
astPath.node.orig?.value === identifierName ||
|
||||
astPath.node.exported?.value === identifierName ||
|
||||
astPath.node.local?.value === identifierName;
|
||||
nameOf(importedOf(node)) === identifierName ||
|
||||
nameOf(node.exported) === identifierName ||
|
||||
nameOf(node.local) === identifierName;
|
||||
if (found) {
|
||||
bindingPath = astPath;
|
||||
bindingType = 'ExportSpecifier';
|
||||
source = astPath.parentPath.node.source
|
||||
? astPath.parentPath.node.source.value
|
||||
? nameOf(astPath.parentPath.node.source)
|
||||
: '[current]';
|
||||
astPath.stop();
|
||||
}
|
||||
|
|
@ -89,13 +90,15 @@ export function getImportSourceFromAst(astPath, identifierName) {
|
|||
let source;
|
||||
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 bindingPath = binding?.path;
|
||||
const matchingTypes = ['ImportSpecifier', 'ImportDefaultSpecifier', 'ExportSpecifier'];
|
||||
|
||||
if (bindingType && matchingTypes.includes(bindingType)) {
|
||||
source = binding?.path?.parentPath?.node?.source?.value;
|
||||
source = nameOf(binding?.path?.parentPath?.node?.source);
|
||||
} else {
|
||||
// no binding
|
||||
[source, bindingType, bindingPath] = getBindingAndSourceReexports(astPath, identifierName);
|
||||
|
|
@ -106,7 +109,7 @@ export function getImportSourceFromAst(astPath, identifierName) {
|
|||
importedIdentifierName = '[default]';
|
||||
} else if (source) {
|
||||
const { node } = bindingPath;
|
||||
importedIdentifierName = node.orig?.value || node.imported?.value || node.local?.value;
|
||||
importedIdentifierName = nameOf(importedOf(node)) || nameOf(node.local);
|
||||
}
|
||||
|
||||
return { source, importedIdentifierName };
|
||||
|
|
@ -194,8 +197,11 @@ async function trackDownIdentifierFn(
|
|||
specifier: '[default]',
|
||||
};
|
||||
}
|
||||
const code = fsAdapter.fs.readFileSync(/** @type {string} */ (resolvedSourcePath), 'utf8');
|
||||
const swcAst = AstService._getSwcAst(code);
|
||||
const code = await fsAdapter.fs.promises.readFile(
|
||||
/** @type {string} */ (resolvedSourcePath),
|
||||
'utf8',
|
||||
);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
const shouldLookForDefaultExport = identifierName === '[default]';
|
||||
|
||||
|
|
@ -204,16 +210,16 @@ async function trackDownIdentifierFn(
|
|||
let pendingTrackDownPromise;
|
||||
|
||||
const handleExportDefaultDeclOrExpr = astPath => {
|
||||
if (!shouldLookForDefaultExport) {
|
||||
return;
|
||||
}
|
||||
if (!shouldLookForDefaultExport) return;
|
||||
|
||||
const { node } = astPath;
|
||||
|
||||
let newSource;
|
||||
if (
|
||||
astPath.node.expression?.type === 'Identifier' ||
|
||||
astPath.node.declaration?.type === 'Identifier'
|
||||
) {
|
||||
newSource = getImportSourceFromAst(astPath, astPath.node.expression.value).source;
|
||||
if (node.expression?.type === 'Identifier' || node.declaration?.type === 'Identifier') {
|
||||
newSource = getImportSourceFromAst(
|
||||
astPath,
|
||||
nameOf(node.expression || node.declaration),
|
||||
).source;
|
||||
}
|
||||
|
||||
if (newSource) {
|
||||
|
|
@ -237,54 +243,54 @@ async function trackDownIdentifierFn(
|
|||
};
|
||||
const handleExportDeclOrNamedDecl = {
|
||||
enter(astPath) {
|
||||
if (reexportMatch || shouldLookForDefaultExport) {
|
||||
return;
|
||||
}
|
||||
if (reexportMatch || shouldLookForDefaultExport) return;
|
||||
|
||||
const { node } = astPath;
|
||||
|
||||
// Are we dealing with a re-export ?
|
||||
if (astPath.node.specifiers?.length) {
|
||||
exportMatch = astPath.node.specifiers.find(
|
||||
s => s.orig?.value === identifierName || s.exported?.value === identifierName,
|
||||
);
|
||||
if (!node.specifiers?.length) return;
|
||||
|
||||
if (exportMatch) {
|
||||
const localName = exportMatch.orig.value;
|
||||
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;
|
||||
exportMatch = node.specifiers.find(
|
||||
s => nameOf(importedOf(s)) === identifierName || nameOf(s.exported) === identifierName,
|
||||
);
|
||||
|
||||
if (!newSource || newSource === '[current]') {
|
||||
/**
|
||||
* @example
|
||||
* const x = 12;
|
||||
* export { x }
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
reexportMatch = true;
|
||||
pendingTrackDownPromise = trackDownIdentifier(
|
||||
newSource,
|
||||
localName,
|
||||
resolvedSourcePath,
|
||||
rootPath,
|
||||
projectName,
|
||||
depth + 1,
|
||||
);
|
||||
astPath.stop();
|
||||
if (!exportMatch) return;
|
||||
|
||||
const localName = nameOf(importedOf(exportMatch));
|
||||
let newSource;
|
||||
if (node.source) {
|
||||
/**
|
||||
* @example
|
||||
* export { x } from 'y'
|
||||
*/
|
||||
newSource = nameOf(node.source);
|
||||
} 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) {
|
||||
|
|
@ -307,7 +313,7 @@ async function trackDownIdentifierFn(
|
|||
ExportDeclaration: handleExportDeclOrNamedDecl,
|
||||
};
|
||||
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
if (pendingTrackDownPromise) {
|
||||
// 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;
|
||||
rootFile = { file: '[current]', specifier };
|
||||
}
|
||||
|
||||
return rootFile;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import module from 'module';
|
||||
import path from 'path';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import mockFs from 'mock-fs';
|
||||
|
|
@ -59,6 +60,46 @@ function getMockObjectForProject(files, cfg = {}, existingMock = {}) {
|
|||
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
|
||||
* "InputDataService.createDataObject", it gives back a mocked response.
|
||||
|
|
@ -72,7 +113,7 @@ function getMockObjectForProject(files, cfg = {}, existingMock = {}) {
|
|||
*/
|
||||
export function mockProject(files, cfg = {}, existingMock = {}) {
|
||||
const obj = getMockObjectForProject(files, cfg, existingMock);
|
||||
mockFs(obj);
|
||||
mockFs({ ...obj, ...Object.fromEntries(importablePaths.map(p => [p, mockFs.load(p)])) });
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "find-classes",
|
||||
"requiredAst": "babel",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "importing-target-project_0.0.2-target-mock__-905964591",
|
||||
"targetProject": {
|
||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "find-customelements",
|
||||
"requiredAst": "swc-to-babel",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "importing-target-project_0.0.2-target-mock__61665553",
|
||||
"targetProject": {
|
||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "find-exports",
|
||||
"requiredAst": "swc",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "exporting-ref-project_1.0.0__-42206859",
|
||||
"targetProject": {
|
||||
"mainEntry": "./index.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "find-imports",
|
||||
"requiredAst": "swc",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "importing-target-project_0.0.2-target-mock__349742630",
|
||||
"targetProject": {
|
||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "match-imports",
|
||||
"requiredAst": "swc",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1789378150",
|
||||
"targetProject": {
|
||||
"mainEntry": "./target-src/match-imports/root-level-imports.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"searchType": "ast-analyzer",
|
||||
"analyzerMeta": {
|
||||
"name": "match-subclasses",
|
||||
"requiredAst": "babel",
|
||||
"requiredAst": "oxc",
|
||||
"identifier": "importing-target-project_0.0.2-target-mock_+_exporting-ref-project_1.0.0__1982316146",
|
||||
"targetProject": {
|
||||
"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('[*]');
|
||||
});
|
||||
|
||||
describe('Reexports', () => {
|
||||
describe('Re-exports', () => {
|
||||
it(`supports [export { x } from 'imported/source'] (re-exported named specifiers)`, async () => {
|
||||
mockProject([`export { x } from 'imported/source'`]);
|
||||
const queryResults = await providence(findImportsQueryConfig, _providenceCfg);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@ import { expect } from 'chai';
|
|||
import { it } from 'mocha';
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
||||
*/
|
||||
|
||||
describe('getSourceCodeFragmentOfDeclaration', () => {
|
||||
const initialMemoizeCacheEnabled = memoize.isCacheEnabled;
|
||||
before(() => {
|
||||
|
|
@ -19,10 +23,10 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
const fakeFs = {
|
||||
'/my/proj/exports/file.js': 'export const x = 0;',
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
filePath: /** @type {PathFromSystemRoot} */ ('/my/proj/exports/file.js'),
|
||||
exportedIdentifier: 'x',
|
||||
});
|
||||
|
||||
|
|
@ -36,7 +40,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export const x = y;
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
|
|
@ -54,7 +58,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export const myIdentifier = y;
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
|
|
@ -74,7 +78,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export const black67 = black59;
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file-2.js',
|
||||
|
|
@ -93,7 +97,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export class AjaxClass extends LionAjaxClass {}
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/ajax.js',
|
||||
|
|
@ -109,7 +113,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export function myFn() {}
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/myFn.js',
|
||||
|
|
@ -126,7 +130,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
const fakeFs = {
|
||||
'/my/proj/exports/file.js': 'export default class {};',
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
|
|
@ -143,7 +147,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export default myIdentifier;
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
|
|
@ -161,7 +165,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export default myIdentifier;
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/file.js',
|
||||
|
|
@ -180,7 +184,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export default class AjaxClass extends LionAjaxClass {}
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/ajax.js',
|
||||
|
|
@ -196,7 +200,7 @@ describe('getSourceCodeFragmentOfDeclaration', () => {
|
|||
export default function myFn() {}
|
||||
`,
|
||||
};
|
||||
mock(fakeFs);
|
||||
mockProject(fakeFs);
|
||||
|
||||
const { sourceFragment } = await getSourceCodeFragmentOfDeclaration({
|
||||
filePath: '/my/proj/exports/myFn.js',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { expect } from 'chai';
|
|||
import { it } from 'mocha';
|
||||
// @ts-ignore
|
||||
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';
|
||||
|
||||
/**
|
||||
|
|
@ -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[]} */
|
||||
const swcScopes = [];
|
||||
swcTraverse(swcAst, {
|
||||
oxcTraverse(oxcAst, {
|
||||
enter({ scope }) {
|
||||
if (!swcScopes.includes(scope)) {
|
||||
swcScopes.push(scope);
|
||||
|
|
@ -27,11 +27,18 @@ function gatherAllScopes(swcAst) {
|
|||
return swcScopes;
|
||||
}
|
||||
|
||||
describe('swcTraverse', () => {
|
||||
/**
|
||||
* @param {*} node
|
||||
*/
|
||||
function nameOf(node) {
|
||||
return node.value || node.name;
|
||||
}
|
||||
|
||||
describe('oxcTraverse', () => {
|
||||
describe('Visitor', () => {
|
||||
it('traverses an swc AST based on <Node.type> visitor', async () => {
|
||||
const code = `import x from 'y';`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
let foundImportDeclarationPath;
|
||||
const visitor = {
|
||||
|
|
@ -39,14 +46,14 @@ describe('swcTraverse', () => {
|
|||
foundImportDeclarationPath = path;
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor);
|
||||
oxcTraverse(oxcAst, visitor);
|
||||
|
||||
expect(foundImportDeclarationPath).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('supports "enter" as a generic arrival handler', async () => {
|
||||
const code = `import x from 'y';`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {string[]} */
|
||||
const foundTypes = [];
|
||||
|
|
@ -58,10 +65,11 @@ describe('swcTraverse', () => {
|
|||
foundTypes.push(path.node.type);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor);
|
||||
oxcTraverse(oxcAst, visitor);
|
||||
|
||||
expect(foundTypes).to.deep.equal([
|
||||
'Module',
|
||||
// 'Module',
|
||||
'Program',
|
||||
'ImportDeclaration',
|
||||
'ImportDefaultSpecifier',
|
||||
'Identifier',
|
||||
|
|
@ -71,7 +79,7 @@ describe('swcTraverse', () => {
|
|||
|
||||
it('supports "enter" and "exit" as generic handlers inside <Node.type> handlers', async () => {
|
||||
const code = `import x from 'y';`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {string[]} */
|
||||
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].phase).to.equal('enter');
|
||||
|
|
@ -97,7 +105,7 @@ describe('swcTraverse', () => {
|
|||
|
||||
it('supports "root" as alternative for Program', async () => {
|
||||
const code = `import x from 'y';`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
let rootPath;
|
||||
const visitor = {
|
||||
|
|
@ -108,17 +116,17 @@ describe('swcTraverse', () => {
|
|||
rootPath = path;
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor);
|
||||
oxcTraverse(oxcAst, visitor);
|
||||
|
||||
// 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 () => {
|
||||
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 swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -159,13 +167,13 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||
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([
|
||||
'globalScope',
|
||||
'alsoGlobalScope',
|
||||
|
|
@ -173,10 +181,10 @@ describe('swcTraverse', () => {
|
|||
// 0 and 3 are the same scope
|
||||
expect(declaratorPaths[0].scope).to.equal(declaratorPaths[3].scope);
|
||||
// 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,
|
||||
);
|
||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.identifier).to.equal(
|
||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.path.node).to.equal(
|
||||
declaratorPaths[3].node,
|
||||
);
|
||||
|
||||
|
|
@ -194,7 +202,7 @@ describe('swcTraverse', () => {
|
|||
}
|
||||
}
|
||||
`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -203,8 +211,8 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
const scopes = gatherAllScopes(swcAst);
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
const scopes = gatherAllScopes(oxcAst);
|
||||
|
||||
expect(scopes[1].path?.node).to.equal(declaratorPaths[0].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[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -232,7 +240,7 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(declaratorPaths[0].scope.id).to.equal(2);
|
||||
});
|
||||
|
|
@ -246,7 +254,7 @@ describe('swcTraverse', () => {
|
|||
break;
|
||||
default:
|
||||
}`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -255,10 +263,10 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(declaratorPaths[0].node.id.value).to.equal('myCases');
|
||||
expect(declaratorPaths[1].node.id.value).to.equal('x');
|
||||
expect(nameOf(declaratorPaths[0].node.id)).to.equal('myCases');
|
||||
expect(nameOf(declaratorPaths[1].node.id)).to.equal('x');
|
||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||
});
|
||||
|
|
@ -269,18 +277,19 @@ describe('swcTraverse', () => {
|
|||
toString(dateObj, opt = {}) {},
|
||||
};
|
||||
`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const results = [];
|
||||
const visitor = {
|
||||
MethodProperty(/** @type {any} */ path) {
|
||||
// MethodProperty for swc...
|
||||
ObjectProperty(/** @type {any} */ 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);
|
||||
});
|
||||
|
||||
|
|
@ -292,7 +301,7 @@ describe('swcTraverse', () => {
|
|||
},
|
||||
};
|
||||
`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -301,10 +310,10 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(declaratorPaths[0].node.id.value).to.equal('x');
|
||||
expect(declaratorPaths[1].node.id.value).to.equal('z');
|
||||
expect(nameOf(declaratorPaths[0].node.id)).to.equal('x');
|
||||
expect(nameOf(declaratorPaths[1].node.id)).to.equal('z');
|
||||
expect(declaratorPaths[0].scope.id).to.equal(0);
|
||||
expect(declaratorPaths[1].scope.id).to.equal(1);
|
||||
});
|
||||
|
|
@ -322,7 +331,7 @@ describe('swcTraverse', () => {
|
|||
}
|
||||
let alsoGlobalScope = 3;
|
||||
`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -334,17 +343,17 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
||||
'globalScope',
|
||||
'alsoGlobalScope',
|
||||
]);
|
||||
// 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,
|
||||
);
|
||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.identifier).to.equal(
|
||||
expect(declaratorPaths[0].scope.bindings.alsoGlobalScope.path.node).to.equal(
|
||||
declaratorPaths[3].node,
|
||||
);
|
||||
});
|
||||
|
|
@ -359,7 +368,7 @@ describe('swcTraverse', () => {
|
|||
}
|
||||
}
|
||||
`;
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
|
||||
/** @type {SwcPath[]} */
|
||||
const declaratorPaths = [];
|
||||
|
|
@ -368,7 +377,7 @@ describe('swcTraverse', () => {
|
|||
declaratorPaths.push(path);
|
||||
},
|
||||
};
|
||||
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true });
|
||||
oxcTraverse(oxcAst, visitor, { needsAdvancedPaths: true });
|
||||
|
||||
expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([
|
||||
'globalScope',
|
||||
|
|
@ -389,7 +398,7 @@ describe('swcTraverse', () => {
|
|||
* @param {string} code
|
||||
*/
|
||||
async function compareScopeResultsWithBabel(code) {
|
||||
const swcAst = await AstService._getSwcAst(code);
|
||||
const oxcAst = await AstService._getOxcAst(code);
|
||||
const babelAst = await AstService._getBabelAst(code);
|
||||
|
||||
/**
|
||||
|
|
@ -407,7 +416,7 @@ describe('swcTraverse', () => {
|
|||
|
||||
/** @type {SwcScope[]} */
|
||||
const swcScopes = [];
|
||||
swcTraverse(swcAst, {
|
||||
oxcTraverse(oxcAst, {
|
||||
enter({ scope }) {
|
||||
if (!swcScopes.includes(scope)) {
|
||||
swcScopes.push(scope);
|
||||
|
|
@ -420,9 +429,17 @@ describe('swcTraverse', () => {
|
|||
expect(babelScopes.length).to.equal(swcScopes.length);
|
||||
for (let i = 0; i < babelScopes.length; i += 1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -459,5 +476,15 @@ describe('swcTraverse', () => {
|
|||
|
||||
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 { it } from 'mocha';
|
||||
|
||||
import { setupAnalyzerTest } from '../../../../test-helpers/setup-analyzer-test.js';
|
||||
import { mockProject } from '../../../../test-helpers/mock-project-helpers.js';
|
||||
import { swcTraverse } from '../../../../src/program/utils/swc-traverse.js';
|
||||
import { AstService } from '../../../../src/program/core/AstService.js';
|
||||
import { setupAnalyzerTest } from '../../../test-helpers/setup-analyzer-test.js';
|
||||
import { mockProject } from '../../../test-helpers/mock-project-helpers.js';
|
||||
import { oxcTraverse } from '../../../src/program/utils/oxc-traverse.js';
|
||||
import { AstService } from '../../../src/program/core/AstService.js';
|
||||
import {
|
||||
trackDownIdentifier,
|
||||
trackDownIdentifierFromScope,
|
||||
} from '../../../../src/program/utils/track-down-identifier.js';
|
||||
} from '../../../src/program/utils/track-down-identifier.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('@babel/traverse').NodePath} NodePath
|
||||
|
|
@ -295,7 +295,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
|
||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||
// 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
|
||||
const identifierNameInScope = 'MyClass';
|
||||
|
|
@ -309,7 +309,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
// astPath = path;
|
||||
// },
|
||||
// });
|
||||
swcTraverse(ast, {
|
||||
oxcTraverse(ast, {
|
||||
ClassDeclaration(path) {
|
||||
astPath = path;
|
||||
},
|
||||
|
|
@ -346,7 +346,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
|
||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||
// 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
|
||||
const identifierNameInScope = 'MyClass';
|
||||
|
|
@ -360,7 +360,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
// astPath = path;
|
||||
// },
|
||||
// });
|
||||
swcTraverse(ast, {
|
||||
oxcTraverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
astPath = path;
|
||||
},
|
||||
|
|
@ -394,7 +394,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
|
||||
mockProject(projectFiles, { projectName: 'my-project', projectPath: '/my/project' });
|
||||
// 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
|
||||
const identifierNameInScope = 'El1';
|
||||
|
|
@ -408,7 +408,7 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
// astPath = path;
|
||||
// },
|
||||
// });
|
||||
swcTraverse(ast, {
|
||||
oxcTraverse(ast, {
|
||||
ClassDeclaration(path) {
|
||||
astPath = path;
|
||||
},
|
||||
|
|
@ -426,4 +426,54 @@ describe('trackDownIdentifierFromScope', () => {
|
|||
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 AnalyzerAst = 'babel' | 'swc-to-babel' | 'swc';
|
||||
export type AnalyzerAst = 'babel' | 'swc' | 'oxc';
|
||||
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { IdentifierName } from "../index.js";
|
||||
|
||||
export type SwcScope = {
|
||||
id: number;
|
||||
parentScope?: Scope;
|
||||
bindings: { [key: string]: SwcBinding };
|
||||
path: SwcPath | null;
|
||||
getBinding: (IdentifierName:string) => SwcBinding;
|
||||
_pendingRefsWithoutBinding: SwcNode[];
|
||||
_isIsolatedBlockStatement: boolean;
|
||||
};
|
||||
|
|
@ -19,7 +22,7 @@ export type SwcPath = {
|
|||
node: SwcNode;
|
||||
parent: SwcNode;
|
||||
stop: function;
|
||||
scope: SwcScope | undefined;
|
||||
scope: SwcScope;
|
||||
parentPath: SwcPath | null | undefined;
|
||||
get: (id: string) => SwcPath | undefined;
|
||||
type: string;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["es2017", "dom"],
|
||||
"lib": ["es2022", "dom"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue