From 71992cc0fb4afb8ebde8763ec0fb6edd9a73d83f Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Tue, 14 Jan 2025 17:30:06 +0100 Subject: [PATCH] feat(providence): update version of oxc; cleanup; include .ts(x) and jsx by default --- .changeset/proud-pugs-think.md | 5 ++ .../providence-analytics/overview.md | 10 +-- package-lock.json | 31 +++++++- .../providence-analytics/package.json | 4 +- .../providence-analytics/src/cli/cli.js | 3 +- .../src/program/analyzers/find-classes.js | 37 ++-------- .../program/analyzers/find-customelements.js | 45 ++++-------- .../src/program/analyzers/find-exports.js | 7 ++ .../src/program/analyzers/find-imports.js | 73 ++++++------------- .../src/program/core/Analyzer.js | 54 +++++++------- .../src/program/core/InputDataService.js | 30 ++++---- .../test-node/program/utils/memoize.test.js | 2 - 12 files changed, 132 insertions(+), 169 deletions(-) create mode 100644 .changeset/proud-pugs-think.md diff --git a/.changeset/proud-pugs-think.md b/.changeset/proud-pugs-think.md new file mode 100644 index 000000000..301a88d69 --- /dev/null +++ b/.changeset/proud-pugs-think.md @@ -0,0 +1,5 @@ +--- +'providence-analytics': patch +--- + +update version of oxc; cleanup; include .ts(x) and jsx by default diff --git a/docs/fundamentals/node-tools/providence-analytics/overview.md b/docs/fundamentals/node-tools/providence-analytics/overview.md index 2f48cd1c7..2ff65adfe 100644 --- a/docs/fundamentals/node-tools/providence-analytics/overview.md +++ b/docs/fundamentals/node-tools/providence-analytics/overview.md @@ -28,7 +28,7 @@ It does this via the [oxc parser](https://oxc.rs/docs/guide/usage/parser.html), Providence expects an analyzer name that tells it what type of analysis to run: ```bash -npx providence analyze +npx providence-analytics analyze ``` By default Providence ships these analyzers: @@ -42,7 +42,7 @@ By default Providence ships these analyzers: Let's say we run `find-imports`: ```bash -npx providence analyze find-imports +npx providence-analytics analyze find-imports ``` Now it retrieves all relevant data about es module imports. @@ -68,14 +68,14 @@ For a "find" analyzer, there is one project involved (the target project). We can specify it like this (we override the default current working directory): ```bash -npx providence analyze find-imports -t /importing/project +npx providence-analytics analyze find-imports -t /importing/project ``` For a "match" analyzer, there is also a reference project. Here we match the exports of the reference project (-r) against the imports of the target project (-t). ```bash -npx providence analyze match-imports -t /importing/project -r /exporting/project +npx providence-analytics analyze match-imports -t /importing/project -r /exporting/project ``` ## Utils @@ -91,5 +91,5 @@ For a better understanding, check out the utils folders (tests and code). For more options, see: ```bash -npx providence --help +npx providence-analytics --help ``` diff --git a/package-lock.json b/package-lock.json index ad2d8e82e..ea2f3d390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5306,6 +5306,7 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", + "dev": true, "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -28331,9 +28332,9 @@ "version": "0.17.3", "license": "MIT", "dependencies": { - "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-node-resolve": "^16.0.0", "commander": "^2.20.3", - "oxc-parser": "^0.39.0", + "oxc-parser": "^0.46.0", "parse5": "^7.2.1", "semver": "^7.6.3" }, @@ -28598,6 +28599,30 @@ "win32" ] }, + "packages-node/providence-analytics/node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.0.tgz", + "integrity": "sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "packages-node/providence-analytics/node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", @@ -28823,7 +28848,7 @@ }, "packages/ui": { "name": "@lion/ui", - "version": "0.9.0", + "version": "0.9.1", "license": "MIT", "dependencies": { "@bundled-es-modules/message-format": "^6.2.4", diff --git a/packages-node/providence-analytics/package.json b/packages-node/providence-analytics/package.json index 2d605d025..2d6200b8f 100644 --- a/packages-node/providence-analytics/package.json +++ b/packages-node/providence-analytics/package.json @@ -37,9 +37,9 @@ "test:node:unit": "mocha './{test-node,src}/**/*.test.js'" }, "dependencies": { - "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-node-resolve": "^16.0.0", "commander": "^2.20.3", - "oxc-parser": "^0.39.0", + "oxc-parser": "^0.46.0", "parse5": "^7.2.1", "semver": "^7.6.3" }, diff --git a/packages-node/providence-analytics/src/cli/cli.js b/packages-node/providence-analytics/src/cli/cli.js index 230bde378..bd5b85475 100755 --- a/packages-node/providence-analytics/src/cli/cli.js +++ b/packages-node/providence-analytics/src/cli/cli.js @@ -1,6 +1,5 @@ -import path from 'path'; - import commander from 'commander'; +import path from 'path'; import { InputDataService } from '../program/core/InputDataService.js'; import { getCurrentDir } from '../program/utils/get-current-dir.js'; diff --git a/packages-node/providence-analytics/src/program/analyzers/find-classes.js b/packages-node/providence-analytics/src/program/analyzers/find-classes.js index 3afe22fe1..59474b2fd 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-classes.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-classes.js @@ -234,37 +234,10 @@ export default class FindClassesAnalyzer extends Analyzer { /** @type {AnalyzerAst} */ static requiredAst = 'oxc'; - /** - * Will find all public members (properties (incl. getter/setters)/functions) of a class and - * will make a distinction between private, public and protected methods - * @param {Partial} customConfig - */ - async execute(customConfig) { - const cfg = customConfig; - - /** - * Prepare - */ - const analyzerResult = await this._prepare(cfg); - if (analyzerResult) { - return analyzerResult; - } - - /** - * Traverse - */ - /** @type {FindClassesAnalyzerOutput} */ - const queryOutput = await this._traverse(async (ast, { relativePath }) => { - const projectPath = cfg.targetProjectPath; - const fullPath = path.resolve(projectPath, relativePath); - const transformedEntry = await findMembersPerAstEntry(ast, fullPath, projectPath); - return { result: transformedEntry }; - }); - // _flattenedFormsPostProcessor(); - - /** - * Finalize - */ - return this._finalize(queryOutput, cfg); + static async analyzeFile(oxcAst, context) { + const projectPath = context.analyzerCfg.targetProjectPath; + const fullPath = path.resolve(projectPath, context.relativePath); + const transformedEntry = await findMembersPerAstEntry(oxcAst, fullPath, projectPath); + return { result: transformedEntry }; } } diff --git a/packages-node/providence-analytics/src/program/analyzers/find-customelements.js b/packages-node/providence-analytics/src/program/analyzers/find-customelements.js index 9f3e9a2a0..63e822ba9 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-customelements.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-customelements.js @@ -7,7 +7,7 @@ import { trackDownIdentifierFromScope } from '../utils/track-down-identifier.js' import { Analyzer } from '../core/Analyzer.js'; /** - * @typedef {import('../../../types/index.js').FindCustomelementsConfig} FindCustomelementsConfig + * @typedef {import('../../../types/index.js').AnalyzerAst} AnalyzerAst * @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName * @typedef {import('@babel/types').File} File */ @@ -100,38 +100,21 @@ export default class FindCustomelementsAnalyzer extends Analyzer { /** @type {AnalyzerAst} */ static requiredAst = 'oxc'; - /** - * Finds export specifiers and sources - * @param {FindCustomelementsConfig} customConfig - */ - async execute(customConfig = {}) { - const cfg = { + get config() { + return { targetProjectPath: null, - ...customConfig, + ...this._customConfig, }; + } - /** - * Prepare - */ - const cachedAnalyzerResult = await this._prepare(cfg); - if (cachedAnalyzerResult) { - return cachedAnalyzerResult; - } - - /** - * Traverse - */ - const projectPath = cfg.targetProjectPath; - const queryOutput = await this._traverse(async (ast, context) => { - let transformedEntry = findCustomElementsPerAstFile(ast); - transformedEntry = await trackdownRoot(transformedEntry, context.relativePath, projectPath); - transformedEntry = cleanup(transformedEntry); - return { result: transformedEntry }; - }); - - /** - * Finalize - */ - return this._finalize(queryOutput, cfg); + static async analyzeFile(oxcAst, context) { + let transformedEntry = findCustomElementsPerAstFile(oxcAst); + transformedEntry = await trackdownRoot( + transformedEntry, + context.relativePath, + context.projectData.project.path, + ); + transformedEntry = cleanup(transformedEntry); + return { result: transformedEntry }; } } diff --git a/packages-node/providence-analytics/src/program/analyzers/find-exports.js b/packages-node/providence-analytics/src/program/analyzers/find-exports.js index c4cae7f31..27b08b411 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-exports.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-exports.js @@ -1,6 +1,7 @@ /* eslint-disable no-shadow, no-param-reassign */ import path from 'path'; +// import { transformIntoIterableFindExportsOutput } from './helpers/transform-into-iterable-find-exports-output.js'; import { getReferencedDeclaration } from '../utils/get-source-code-fragment-of-declaration.js'; import { normalizeSourcePaths } from './helpers/normalize-source-paths.js'; import { trackDownIdentifier } from '../utils/track-down-identifier.js'; @@ -274,4 +275,10 @@ export default class FindExportsAnalyzer extends Analyzer { return { result: transformedFile }; } + + static async analyzeProject(...args) { + const totalResult = await super.analyzeProject(...args); + // return transformIntoIterableFindExportsOutput({ queryOutput: totalResult }); + return totalResult; + } } diff --git a/packages-node/providence-analytics/src/program/analyzers/find-imports.js b/packages-node/providence-analytics/src/program/analyzers/find-imports.js index c4a2af9cd..3377b34cd 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-imports.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-imports.js @@ -23,16 +23,9 @@ import { Analyzer } from '../core/Analyzer.js'; /** * Intends to work for oxc, swc, and babel asts + * @param {SwcNode} s */ 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 || @@ -164,57 +157,35 @@ export default class FindImportsSwcAnalyzer extends Analyzer { static requiredAst = /** @type {AnalyzerAst} */ ('oxc'); /** - * Finds import specifiers and sources - * @param {FindImportsConfig} customConfig + * @typedef FindImportsConfig + * @property {boolean} [keepInternalSources=false] by default, relative paths like '../x.js' are + * filtered out. This option keeps them. + * means that 'external-dep/file' will be resolved to 'external-dep/file.js' will both be stored + * as the latter */ - async execute(customConfig = {}) { - /** - * @typedef FindImportsConfig - * @property {boolean} [keepInternalSources=false] by default, relative paths like '../x.js' are - * filtered out. This option keeps them. - * means that 'external-dep/file' will be resolved to 'external-dep/file.js' will both be stored - * as the latter - */ - const cfg = { + get config() { + return { targetProjectPath: null, // post process file keepInternalSources: false, - ...customConfig, + ...this._customConfig, }; + } - /** - * Prepare - */ - const cachedAnalyzerResult = await this._prepare(cfg); - if (cachedAnalyzerResult) { - return cachedAnalyzerResult; - } + static async analyzeFile(oxcAst, context) { + let transformedFile = findImportsPerAstFile(oxcAst); + // Post processing based on configuration... + transformedFile = await normalizeSourcePaths( + transformedFile, + context.relativePath, + context.analyzerCfg.targetProjectPath, + ); - /** - * Traverse - */ - const queryOutput = await this._traverse(async (oxcAst, context) => { + if (!context.analyzerCfg.keepInternalSources) { // @ts-expect-error - let transformedFile = findImportsPerAstFile(oxcAst); - // Post processing based on configuration... - transformedFile = await normalizeSourcePaths( - transformedFile, - context.relativePath, - // @ts-expect-error - cfg.targetProjectPath, - ); - - if (!cfg.keepInternalSources) { - // @ts-expect-error - transformedFile = transformedFile.filter(entry => !isRelativeSourcePath(entry.source)); - } - - return { result: transformedFile }; - }); + transformedFile = transformedFile.filter(entry => !isRelativeSourcePath(entry.source)); + } - /** - * Finalize - */ - return this._finalize(queryOutput, cfg); + return { result: transformedFile }; } } diff --git a/packages-node/providence-analytics/src/program/core/Analyzer.js b/packages-node/providence-analytics/src/program/core/Analyzer.js index 181b37d76..f208287ca 100644 --- a/packages-node/providence-analytics/src/program/core/Analyzer.js +++ b/packages-node/providence-analytics/src/program/core/Analyzer.js @@ -348,36 +348,28 @@ export class Analyzer { } /** - * @param {FileAstTraverseFn|{traverseEntryFn: FileAstTraverseFn; filePaths:string[]; projectPath: string}} traverseEntryOrConfig + * @param {FileAstTraverseFn|{traverseEntryFn: FileAstTraverseFn; filePaths:string[]; projectPath: string; targetData: ProjectInputDataWithMeta}} analyzeFileCfg */ - async _traverse(traverseEntryOrConfig) { + static async analyzeProject(analyzeFileCfg) { LogService.debug(`Analyzer "${this.name}": started _traverse method`); - let traverseEntryFn; let finalTargetData; - - if (typeof traverseEntryOrConfig === 'function') { - traverseEntryFn = traverseEntryOrConfig; - finalTargetData = this.targetData; + if (!analyzeFileCfg.filePaths) { + finalTargetData = analyzeFileCfg.targetData; } else { - traverseEntryFn = traverseEntryOrConfig.traverseEntryFn; - if (!traverseEntryOrConfig.filePaths) { - finalTargetData = this.targetData; - } else { - const { projectPath, projectName } = traverseEntryOrConfig; - if (!projectPath) { - LogService.error(`[Analyzer._traverse]: you must provide a projectPath`); - } - finalTargetData = await InputDataService.createDataObject([ - { - project: { - name: projectName || '[n/a]', - path: projectPath, - }, - entries: traverseEntryOrConfig.filePaths, - }, - ]); + const { projectPath, projectName } = analyzeFileCfg; + if (!projectPath) { + LogService.error(`[Analyzer._traverse]: you must provide a projectPath`); } + finalTargetData = await InputDataService.createDataObject([ + { + project: { + name: projectName || '[n/a]', + path: projectPath, + }, + entries: analyzeFileCfg.filePaths, + }, + ]); } /** @@ -385,9 +377,13 @@ export class Analyzer { */ const astDataProjects = await QueryService.addAstToProjectsData( finalTargetData, - this.constructor.requiredAst, + this.requiredAst, + ); + return analyzePerAstFile( + astDataProjects[0], + analyzeFileCfg.traverseEntryFn, + analyzeFileCfg.config, ); - return analyzePerAstFile(astDataProjects[0], traverseEntryFn, this.config); } /** @@ -409,11 +405,13 @@ export class Analyzer { /** * Traverse */ - const queryOutput = await this._traverse({ + const queryOutput = await /** @type {typeof Analyzer} */ (this.constructor).analyzeProject({ // @ts-ignore traverseEntryFn: this.constructor.analyzeFile, - filePaths: cfg.targetFilePaths, projectPath: cfg.targetProjectPath, + filePaths: cfg.targetFilePaths, + targetData: this.targetData, + config: this.config, }); /** diff --git a/packages-node/providence-analytics/src/program/core/InputDataService.js b/packages-node/providence-analytics/src/program/core/InputDataService.js index 5a1446713..b4d9993aa 100644 --- a/packages-node/providence-analytics/src/program/core/InputDataService.js +++ b/packages-node/providence-analytics/src/program/core/InputDataService.js @@ -34,6 +34,9 @@ import { AstService } from './AstService.js'; * @typedef {import('../../../types/index.js').Feature} Feature */ +/** @type {`.${string}`[]} */ +const defaultExtensions = ['.js', '.ts', '.jsx', '.tsx', '.mjs']; + /** * @typedef {(rootPath:PathFromSystemRoot) => PackageJson|undefined} GetPackageJsonFn * @type {GetPackageJsonFn} @@ -179,8 +182,9 @@ const getNpmPackagePaths = memoize((/** @type {PathFromSystemRoot} */ rootPath) }); /** - * @param {any|any[]} v - * @returns {any[]} + * @template T + * @param {T|T[]} v + * @returns {T[]} */ function ensureArray(v) { return Array.isArray(v) ? v : [v]; @@ -429,7 +433,7 @@ export class InputDataService { static get defaultGatherFilesConfig() { return { allowlist: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'], - extensions: ['.js'], + extensions: defaultExtensions, depth: Infinity, }; } @@ -440,7 +444,7 @@ export class InputDataService { * @param {string[]} extensions * @returns {string} */ - static _getDefaultGlobDepthPattern(depth = Infinity, extensions = ['.js']) { + static _getDefaultGlobDepthPattern(depth = Infinity, extensions = defaultExtensions) { // `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`; const extensionsGlobPart = `.{${extensions.map(extension => extension.slice(1)).join(',')},}`; if (depth === Infinity) { @@ -472,6 +476,15 @@ export class InputDataService { * @returns {Promise} result list of file paths */ static async gatherFilesFromDir(startPath, customConfig = {}) { + const allowlistModes = ['npm', 'git', 'all', 'export-map']; + if (customConfig.allowlistMode && !allowlistModes.includes(customConfig.allowlistMode)) { + throw new Error( + `[gatherFilesConfig] Please provide a valid allowListMode like "${allowlistModes.join( + '|', + )}". Found: "${customConfig.allowlistMode}"`, + ); + } + const cfg = { ...this.defaultGatherFilesConfig, ...customConfig, @@ -483,15 +496,6 @@ export class InputDataService { ]; } - const allowlistModes = ['npm', 'git', 'all', 'export-map']; - if (customConfig.allowlistMode && !allowlistModes.includes(customConfig.allowlistMode)) { - throw new Error( - `[gatherFilesConfig] Please provide a valid allowListMode like "${allowlistModes.join( - '|', - )}". Found: "${customConfig.allowlistMode}"`, - ); - } - if (cfg.allowlistMode === 'export-map') { const pkgJson = getPackageJson(startPath); if (!pkgJson?.exports) { diff --git a/packages-node/providence-analytics/test-node/program/utils/memoize.test.js b/packages-node/providence-analytics/test-node/program/utils/memoize.test.js index b039f75e9..b1e1b28a6 100644 --- a/packages-node/providence-analytics/test-node/program/utils/memoize.test.js +++ b/packages-node/providence-analytics/test-node/program/utils/memoize.test.js @@ -424,9 +424,7 @@ describe('Memoize', () => { { fn: spy2Memoized, count: 4 }, ]); - console.debug('spy3Memoized'); spy3Memoized(); - console.debug(memoize.cacheStrategyItems); expect(memoize.cacheStrategyItems).to.deep.equal([ { fn: spy2Memoized, count: 4 }, { fn: spy3Memoized, count: 1 }, // we start over