diff --git a/.babelrc.json b/.babelrc.json index 5b50df38..68ca053f 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -1,6 +1,7 @@ { "plugins": [ - "add-module-exports" + "add-module-exports", + "babel-plugin-transform-import-meta" ], "presets": [ [ diff --git a/package.json b/package.json index e482610f..f9d788fb 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,10 @@ "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", - "parse-imports": "^2.0.0", + "parse-imports": "^2.1.0", "semver": "^7.6.2", - "spdx-expression-parse": "^4.0.0" + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.0" }, "description": "JSDoc linting rules for ESLint.", "devDependencies": { @@ -44,6 +45,7 @@ "@typescript-eslint/parser": "^7.11.0", "babel-plugin-add-module-exports": "^1.0.4", "babel-plugin-istanbul": "^6.1.1", + "babel-plugin-transform-import-meta": "^2.2.1", "c8": "^9.1.0", "camelcase": "^6.3.0", "chai": "^4.3.10", @@ -126,7 +128,7 @@ "scripts": { "tsc": "tsc", "tsc-build": "tsc -p tsconfig-prod.json", - "build": "rimraf ./dist && cross-env NODE_ENV=production babel ./src --out-file-extension .cjs --out-dir ./dist --copy-files --source-maps --ignore ./src/bin/*.js --no-copy-ignored && replace 'require\\(\"\\.(.*?)\\.[^.]*?\"\\)' 'require(\".$1.cjs\")' 'dist' -r --include=\"*.cjs\" && pnpm tsc-build", + "build": "rimraf ./dist && cross-env NODE_ENV=production babel ./src --out-file-extension .cjs --out-dir ./dist --copy-files --source-maps --ignore ./src/bin/*.js --no-copy-ignored && replace 'require\\(\"\\.(.*?)\\.[^.]*?\"\\)' 'require(\".$1.cjs\")' 'dist' -r --include=\"*.cjs\" && cp src/import-worker.mjs dist/import-worker.mjs && pnpm tsc-build", "check-docs": "babel-node ./src/bin/generateDocs.js --check", "create-docs": "npm run create-options && babel-node ./src/bin/generateDocs.js", "create-rule": "babel-node ./src/bin/generateRule.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2462ab7c..a5a8c20c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,14 +27,17 @@ importers: specifier: ^1.5.0 version: 1.5.0 parse-imports: - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^2.1.0 + version: 2.1.0 semver: specifier: ^7.6.2 version: 7.6.2 spdx-expression-parse: specifier: ^4.0.0 version: 4.0.0 + synckit: + specifier: ^0.9.0 + version: 0.9.0 devDependencies: '@babel/cli': specifier: ^7.24.6 @@ -117,6 +120,9 @@ importers: babel-plugin-istanbul: specifier: ^6.1.1 version: 6.1.1 + babel-plugin-transform-import-meta: + specifier: ^2.2.1 + version: 2.2.1(@babel/core@7.24.6) c8: specifier: ^9.1.0 version: 9.1.0 @@ -894,10 +900,6 @@ packages: resolution: {integrity: sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.0': - resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} - engines: {node: '>=6.9.0'} - '@babel/template@7.24.6': resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==} engines: {node: '>=6.9.0'} @@ -1776,6 +1778,11 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-transform-import-meta@2.2.1: + resolution: {integrity: sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==} + peerDependencies: + '@babel/core': ^7.10.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4418,8 +4425,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-imports@2.0.0: - resolution: {integrity: sha512-3Dh6W2SPvs4k8neQG6fluUnVgt/WLDheepyD+FP1GlmOijKRBqr7Vhij0Cko6wyAp3tm/OHDDAniwFS0VCA0qQ==} + parse-imports@2.1.0: + resolution: {integrity: sha512-JQWgmK2o4w8leUkZeZPatWdAny6vXGU/3siIUvMF6J2rDCud9aTt8h/px9oZJ6U3EcfhngBJ635uPFI0q0VAeA==} engines: {node: '>= 18'} parse-json@4.0.0: @@ -5230,6 +5237,10 @@ packages: resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} engines: {node: ^14.18.0 || >=16.0.0} + synckit@0.9.0: + resolution: {integrity: sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==} + engines: {node: ^14.18.0 || >=16.0.0} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -5897,7 +5908,7 @@ snapshots: '@babel/helper-function-name@7.23.0': dependencies: - '@babel/template': 7.24.0 + '@babel/template': 7.24.6 '@babel/types': 7.24.5 '@babel/helper-function-name@7.24.6': @@ -6581,12 +6592,6 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.24.0': - dependencies: - '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 - '@babel/template@7.24.6': dependencies: '@babel/code-frame': 7.24.6 @@ -7830,6 +7835,12 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-transform-import-meta@2.2.1(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/template': 7.24.6 + tslib: 2.6.2 + balanced-match@1.0.2: {} base@0.11.2: @@ -10893,7 +10904,7 @@ snapshots: dependencies: callsites: 3.1.0 - parse-imports@2.0.0: + parse-imports@2.1.0: dependencies: es-module-lexer: 1.5.3 slashes: 3.0.12 @@ -11772,6 +11783,11 @@ snapshots: '@pkgr/core': 0.1.0 tslib: 2.6.2 + synckit@0.9.0: + dependencies: + '@pkgr/core': 0.1.0 + tslib: 2.6.2 + tapable@2.2.1: {} tar@6.2.0: diff --git a/src/import-worker.mjs b/src/import-worker.mjs new file mode 100644 index 00000000..cc54de76 --- /dev/null +++ b/src/import-worker.mjs @@ -0,0 +1,11 @@ +import { runAsWorker } from 'synckit' + +runAsWorker(async (imprt) => { + const { parseImports } = await import('parse-imports'); + try { + // ESLint doesn't support async rules + return [...await parseImports(imprt)]; + } catch (err) { + return false; + } +}) diff --git a/src/rules/checkValues.js b/src/rules/checkValues.js index 97a8ce14..dcb50f6a 100644 --- a/src/rules/checkValues.js +++ b/src/rules/checkValues.js @@ -1,8 +1,14 @@ -import { parseImportsSync } from 'parse-imports'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { createSyncFn } from 'synckit'; import semver from 'semver'; import spdxExpressionParse from 'spdx-expression-parse'; import iterateJsdoc from '../iterateJsdoc.js'; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const pathName = join(__dirname, '../import-worker.mjs'); + const allowedKinds = new Set([ 'class', 'constant', @@ -169,11 +175,8 @@ export default iterateJsdoc(({ ? `${typePart}${name} ${description}` : `${typePart}${name}`); - try { - // Should technically await non-sync, but ESLint doesn't support async rules; - // thankfully, the Wasm load time is safely fast - parseImportsSync(imprt); - } catch (err) { + const getImports = createSyncFn(pathName); + if (!getImports(imprt)) { report( `Bad @import tag`, null, diff --git a/src/rules/noUndefinedTypes.js b/src/rules/noUndefinedTypes.js index c84ed43b..3642f127 100644 --- a/src/rules/noUndefinedTypes.js +++ b/src/rules/noUndefinedTypes.js @@ -1,4 +1,7 @@ -import { parseImportsSync } from 'parse-imports'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +import { createSyncFn } from 'synckit'; import { getJSDocComment, parse as parseType, @@ -9,6 +12,9 @@ import iterateJsdoc, { parseComment, } from '../iterateJsdoc.js'; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const pathName = join(__dirname, '../import-worker.mjs'); + const extraTypes = [ 'null', 'undefined', 'void', 'string', 'boolean', 'object', 'function', 'symbol', @@ -146,16 +152,13 @@ export default iterateJsdoc(({ ? `${typePart}${name} ${description}` : `${typePart}${name}`); - let imports; - try { - // Should technically await non-sync, but ESLint doesn't support async rules; - // thankfully, the Wasm load time is safely fast - imports = parseImportsSync(imprt); - } catch (err) { + const getImports = createSyncFn(pathName); + const imports = /** @type {import('parse-imports').Import[]} */ (getImports(imprt)); + if (!imports) { return null; } - return [...imports].flatMap(({importClause}) => { + return imports.flatMap(({importClause}) => { /* c8 ignore next */ const {default: dflt, named, namespace} = importClause || {}; const types = []; diff --git a/tsconfig.json b/tsconfig.json index 510f1fde..74387d48 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,8 @@ "src/**/*.js", "test/**/*.js", "typings/gitdown.d.ts", - "typings/babel__eslint-parser.d.ts" + "typings/babel__eslint-parser.d.ts", + "src/import-worker.mjs" ], "exclude": ["node_modules"] }