diff --git a/packages-node/providence-analytics/package.json b/packages-node/providence-analytics/package.json index b82d88781..9ba0db0ca 100644 --- a/packages-node/providence-analytics/package.json +++ b/packages-node/providence-analytics/package.json @@ -29,32 +29,32 @@ "scripts": { "dashboard": "node ./dashboard/server.js --run-server --serve-from-package-root", "postinstall": "npx patch-package", - "match-lion-imports": "npm run providence -- analyze match-subclasses --search-target-collection @lion-targets --reference-collection @lion-references --measure-perf --add-system-paths", + "match-lion-imports": "npm run providence -- analyze match-imports --search-target-collection @lion-targets --reference-collection @lion-references --measure-perf --skip-check-match-compatibility", "providence": "node --max-old-space-size=8192 ./src/cli/index.js", "publish-docs": "node ../../packages-node/publish-docs/src/cli.js --github-url https://github.com/ing-bank/lion/ --git-root-dir ../../", "prepublishOnly": "npm run publish-docs", "test:node": "npm run test:node:unit && npm run test:node:e2e", - "test:node:e2e": "mocha './test-node/**/*.e2e.{j,mj}s' --timeout 60000", - "test:node:unit": "mocha './test-node/**/*.test.{j,mj}s'" + "test:node:e2e": "mocha './test-node/**/*.e2e.js' --timeout 60000", + "test:node:unit": "mocha './test-node/**/*.test.js'" }, "dependencies": { - "@babel/core": "^7.21.3", - "@babel/parser": "^7.21.3", + "@babel/core": "^7.21.4", + "@babel/parser": "^7.21.4", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-syntax-export-default-from": "^7.18.6", "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/register": "^7.21.0", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", - "@rollup/plugin-node-resolve": "^15.0.1", - "@swc/core": "^1.3.42", - "@web/dev-server": "^0.1.37", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", + "@rollup/plugin-node-resolve": "^15.0.2", + "@swc/core": "^1.3.46", + "@web/dev-server": "^0.1.38", "anymatch": "^3.1.3", "commander": "^2.20.3", - "glob": "^7.2.3", + "glob": "^8.1.0", "inquirer": "^9.1.5", "is-negated-glob": "^1.0.0", - "lit-element": "~2.4.0", + "lit-element": "~3.3.1", "parse5": "^7.1.2", "read-package-tree": "5.3.1", "semver": "^7.3.8", diff --git a/packages-node/providence-analytics/providence.conf.mjs b/packages-node/providence-analytics/providence.conf.mjs index 01cd3dd5e..60bf62d22 100644 --- a/packages-node/providence-analytics/providence.conf.mjs +++ b/packages-node/providence-analytics/providence.conf.mjs @@ -34,6 +34,6 @@ export default { // Usually the references are different from the targets. // In this demo file, we test @lion usage amongst itself // Select via " providence analyze --reference-collection 'exampleCollection' " - '@lion-references': ['../../packages/ui/'], + '@lion-references': lionScopedPackagePaths, }, }; diff --git a/packages-node/providence-analytics/rm-submodule.sh b/packages-node/providence-analytics/rm-submodule.sh deleted file mode 100644 index 7c18164a0..000000000 --- a/packages-node/providence-analytics/rm-submodule.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -# See https://gist.github.com/myusuf3/7f645819ded92bda6677 - -if [ -z "$1" ]; then - echo "Please define 'path/to/submodule'"; - exit; -fi - -# Remove the submodule entry from .git/config -git submodule deinit -f $1 - -# Remove the submodule directory from the superproject's .git/modules directory -rm -rf .git/modules/$1 - -# Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule -git rm -rf $1 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 fca5f3c8e..f9c9290d7 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-classes.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-classes.js @@ -1,5 +1,5 @@ /* eslint-disable no-shadow, no-param-reassign */ -import pathLib from 'path'; +import path from 'path'; import t from '@babel/types'; import babelTraverse from '@babel/traverse'; import { Analyzer } from '../core/Analyzer.js'; @@ -251,7 +251,7 @@ export default class FindClassesAnalyzer extends Analyzer { /** @type {FindClassesAnalyzerOutput} */ const queryOutput = await this._traverse(async (ast, { relativePath }) => { const projectPath = cfg.targetProjectPath; - const fullPath = pathLib.resolve(projectPath, relativePath); + const fullPath = path.resolve(projectPath, relativePath); const transformedEntry = await findMembersPerAstEntry(ast, 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 54c4cc4cd..cc3eb0293 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-customelements.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-customelements.js @@ -109,9 +109,9 @@ export default class FindCustomelementsAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); - if (analyzerResult) { - return analyzerResult; + const cachedAnalyzerResult = this._prepare(cfg); + if (cachedAnalyzerResult) { + return cachedAnalyzerResult; } /** 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 c531e2433..22a1e4ab1 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-exports.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-exports.js @@ -170,13 +170,13 @@ function findExportsPerAstFile(babelAst, { skipFileImports }) { } }, }, - ExportNamedDeclaration(path) { - const exportSpecifiers = getExportSpecifiers(path.node); - const localMap = getLocalNameSpecifiers(path.node); - const source = path.node.source?.value; - const entry = { exportSpecifiers, localMap, source, __tmp: { path } }; - if (path.node.assertions?.length) { - entry.assertionType = path.node.assertions[0].value?.value; + ExportNamedDeclaration(astPath) { + const exportSpecifiers = getExportSpecifiers(astPath.node); + const localMap = getLocalNameSpecifiers(astPath.node); + const source = astPath.node.source?.value; + const entry = { exportSpecifiers, localMap, source, __tmp: { astPath } }; + if (astPath.node.assertions?.length) { + entry.assertionType = astPath.node.assertions[0].value?.value; } transformedFile.push(entry); }, @@ -194,7 +194,7 @@ function findExportsPerAstFile(babelAst, { skipFileImports }) { source = importOrDeclPath.parentPath.node.source.value; } } - transformedFile.push({ exportSpecifiers, source, __tmp: { path: defaultExportPath } }); + transformedFile.push({ exportSpecifiers, source, __tmp: { astPath: defaultExportPath } }); }, }); @@ -238,9 +238,9 @@ export default class FindExportsAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); - if (analyzerResult) { - return analyzerResult; + const cachedAnalyzerResult = this._prepare(cfg); + if (cachedAnalyzerResult) { + return cachedAnalyzerResult; } /** diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/from-import-to-export-perspective.js b/packages-node/providence-analytics/src/program/analyzers/helpers/from-import-to-export-perspective.js index d03b79ff0..0cea11874 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/from-import-to-export-perspective.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/from-import-to-export-perspective.js @@ -25,7 +25,13 @@ import { toPosixPath } from '../../utils/to-posix-path.js'; */ export async function fromImportToExportPerspective({ importee, importer, importeeProjectPath }) { if (isRelativeSourcePath(importee)) { - LogService.warn('[fromImportToExportPerspective] Please only provide external import paths'); + LogService.warn( + `[fromImportToExportPerspective] Please only provide external import paths for ${{ + importee, + importer, + importeeProjectPath, + }}`, + ); return null; } diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/normalize-source-paths.js b/packages-node/providence-analytics/src/program/analyzers/helpers/normalize-source-paths.js index 65132f8fe..751fae4ae 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/normalize-source-paths.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/normalize-source-paths.js @@ -1,5 +1,5 @@ /* eslint-disable no-param-reassign */ -import pathLib from 'path'; +import path from 'path'; import { isRelativeSourcePath } from '../../utils/relative-source-path.js'; import { resolveImportPath } from '../../utils/resolve-import-path.js'; import { toPosixPath } from '../../utils/to-posix-path.js'; @@ -7,7 +7,7 @@ import { toPosixPath } from '../../utils/to-posix-path.js'; /** * @typedef {import('../../../../types/index.js').PathRelative} PathRelative * @typedef {import('../../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot - * @typedef {import('../../../../types/index.js').QueryOutput} QueryOutput + * @typedef {import('../../../../types/index.js').FindImportsAnalyzerEntry} FindImportsAnalyzerEntry */ /** @@ -16,9 +16,9 @@ import { toPosixPath } from '../../utils/to-posix-path.js'; * @returns {PathRelative} */ function toLocalPath(currentDirPath, resolvedPath) { - let relativeSourcePath = pathLib.relative(currentDirPath, resolvedPath); + let relativeSourcePath = path.relative(currentDirPath, resolvedPath); if (!relativeSourcePath.startsWith('.')) { - // correction on top of pathLib.resolve, which resolves local paths like + // correction on top of path.resolve, which resolves local paths like // (from import perspective) external modules. // so 'my-local-files.js' -> './my-local-files.js' relativeSourcePath = `./${relativeSourcePath}`; @@ -28,36 +28,39 @@ function toLocalPath(currentDirPath, resolvedPath) { /** * Resolves and converts to normalized local/absolute path, based on file-system information. - * - from: { source: '../../relative/file' } - * - to: { - * fullPath: './absolute/path/from/root/to/relative/file.js', - * normalizedPath: '../../relative/file.js' - * } - * @param {QueryOutput} queryOutput + * - from: '../../relative/file' + * - to: './src/relative/file.js' + * @param {string} oldSource + * @param {string} relativePath + * @param {string} rootPath + */ +export async function normalizeSourcePath(oldSource, relativePath, rootPath = process.cwd()) { + const currentFilePath = /** @type {PathFromSystemRoot} */ (path.resolve(rootPath, relativePath)); + const currentDirPath = /** @type {PathFromSystemRoot} */ (path.dirname(currentFilePath)); + + if (isRelativeSourcePath(oldSource) && relativePath) { + // This will be a source like '../my/file.js' or './file.js' + const resolvedPath = /** @type {PathFromSystemRoot} */ ( + await resolveImportPath(oldSource, currentFilePath) + ); + return resolvedPath && toLocalPath(currentDirPath, resolvedPath); + } + // This will be a source from a project, like 'lion-based-ui/x.js' or '@open-wc/testing/y.js' + return oldSource; +} + +/** + * @param {Partial[]} queryOutput * @param {string} relativePath * @param {string} rootPath */ export async function normalizeSourcePaths(queryOutput, relativePath, rootPath = process.cwd()) { - const currentFilePath = /** @type {PathFromSystemRoot} */ ( - pathLib.resolve(rootPath, relativePath) - ); - const currentDirPath = /** @type {PathFromSystemRoot} */ (pathLib.dirname(currentFilePath)); - const normalizedQueryOutput = []; for (const specifierResObj of queryOutput) { if (specifierResObj.source) { - if (isRelativeSourcePath(specifierResObj.source) && relativePath) { - // This will be a source like '../my/file.js' or './file.js' - const resolvedPath = /** @type {PathFromSystemRoot} */ ( - await resolveImportPath(specifierResObj.source, currentFilePath) - ); - specifierResObj.normalizedSource = - resolvedPath && toLocalPath(currentDirPath, resolvedPath); - // specifierResObj.fullSource = resolvedPath && toRelativeSourcePath(resolvedPath, rootPath); - } else { - // This will be a source from a project, like 'lion-based-ui/x.js' or '@open-wc/testing/y.js' - specifierResObj.normalizedSource = specifierResObj.source; - // specifierResObj.fullSource = specifierResObj.source; + const x = await normalizeSourcePath(specifierResObj.source, relativePath, rootPath); + if (x) { + specifierResObj.normalizedSource = x; } } normalizedQueryOutput.push(specifierResObj); diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-exports-output.js b/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-exports-output.js index 5812567e8..f11edeb68 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-exports-output.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-exports-output.js @@ -1,5 +1,6 @@ /** * @typedef {import('../../../../types/index.js').FindExportsAnalyzerResult} FindExportsAnalyzerResult + * @typedef {import('../../../../types/index.js').IterableFindExportsAnalyzerEntry} IterableFindExportsAnalyzerEntry */ /** diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-imports-output.js b/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-imports-output.js index a9a92b7d3..23ac85a43 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-imports-output.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/transform-into-iterable-find-imports-output.js @@ -1,5 +1,6 @@ /** * @typedef {import('../../../../types/index.js').FindImportsAnalyzerResult} FindImportsAnalyzerResult + * @typedef {import('../../../../types/index.js').IterableFindImportsAnalyzerEntry} IterableFindImportsAnalyzerEntry */ /** @@ -43,13 +44,12 @@ export function transformIntoIterableFindImportsOutput(importsAnalyzerResult) { continue; } for (const importSpecifier of importSpecifiers) { - /** @type {IterableFindImportsAnalyzerEntry} */ - const resultEntry = { + const resultEntry = /** @type {IterableFindImportsAnalyzerEntry} */ ({ file, specifier: importSpecifier, source, normalizedSource, - }; + }); iterableEntries.push(resultEntry); } } diff --git a/packages-node/providence-analytics/src/program/core/Analyzer.js b/packages-node/providence-analytics/src/program/core/Analyzer.js index 477544655..61e446fe9 100644 --- a/packages-node/providence-analytics/src/program/core/Analyzer.js +++ b/packages-node/providence-analytics/src/program/core/Analyzer.js @@ -17,6 +17,8 @@ import { getFilePathRelativeFromRoot } from '../utils/get-file-path-relative-fro * @typedef {import('../../../types/index.js').ProjectInputDataWithMeta} ProjectInputDataWithMeta * @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult * @typedef {import('../../../types/index.js').MatchAnalyzerConfig} MatchAnalyzerConfig + * @typedef {import('@babel/types').File} File + * @typedef {(ast: File, astContext: {code:string; relativePath:string; projectData: ProjectInputDataWithMeta}) => object} FileAstTraverseFn */ /** @@ -126,7 +128,7 @@ function ensureAnalyzerResultFormat(queryOutput, cfg, analyzer) { * Before running the analyzer, we need two conditions for a 'compatible match': * - 1. referenceProject is imported by targetProject at all * - 2. referenceProject and targetProject have compatible major versions - * @typedef {(referencePath:PathFromSystemRoot,targetPath:PathFromSystemRoot) => {compatible:boolean}} CheckForMatchCompatibilityFn + * @typedef {(referencePath:PathFromSystemRoot,targetPath:PathFromSystemRoot) => {compatible:boolean; reason?:string}} CheckForMatchCompatibilityFn * @type {CheckForMatchCompatibilityFn} */ const checkForMatchCompatibility = memoize( @@ -172,7 +174,7 @@ export class Analyzer { name = /** @type {typeof Analyzer} */ (this.constructor).analyzerName; - /** @type {'babel'|'swc-to-babel'|'swc-to-babel'} */ + /** @type {'babel'|'swc-to-babel'} */ requiredAst = 'babel'; /** @@ -183,10 +185,10 @@ export class Analyzer { * @param {MatchAnalyzerConfig} cfg */ static __unwindProvidedResults(cfg) { - if (cfg.targetProjectResult && !cfg.targetProjectResult.analyzerMeta) { + if (cfg.targetProjectResult && !cfg.targetProjectResult?.analyzerMeta) { cfg.targetProjectResult = unwindJsonResult(cfg.targetProjectResult); } - if (cfg.referenceProjectResult && !cfg.referenceProjectResult.analyzerMeta) { + if (cfg.referenceProjectResult && !cfg.referenceProjectResult?.analyzerMeta) { cfg.referenceProjectResult = unwindJsonResult(cfg.referenceProjectResult); } } @@ -296,7 +298,7 @@ export class Analyzer { } /** - * @param {function|{traverseEntryFn: function; filePaths:string[]; projectPath: string}} traverseEntryOrConfig + * @param {FileAstTraverseFn|{traverseEntryFn: FileAstTraverseFn; filePaths:string[]; projectPath: string}} traverseEntryOrConfig */ async _traverse(traverseEntryOrConfig) { LogService.debug(`Analyzer "${this.name}": started _traverse method`); diff --git a/packages-node/providence-analytics/src/program/core/AstService.js b/packages-node/providence-analytics/src/program/core/AstService.js index dc5995e88..a911e04b1 100644 --- a/packages-node/providence-analytics/src/program/core/AstService.js +++ b/packages-node/providence-analytics/src/program/core/AstService.js @@ -77,7 +77,7 @@ export class AstService { /** * Returns the Babel AST * @param { string } code - * @param { 'babel'|'swc-to-babel' } astType + * @param { 'babel'|'swc-to-babel'} astType * @param { {filePath?: PathFromSystemRoot} } options * @returns {File|undefined} */ diff --git a/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration.js b/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration.js index 2ed02c1a1..d11e76374 100644 --- a/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration.js +++ b/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration.js @@ -63,6 +63,17 @@ export function getReferencedDeclaration({ referencedIdentifierName, globalScope } /** + * @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; }>} */ diff --git a/packages-node/providence-analytics/src/program/utils/resolve-import-path.js b/packages-node/providence-analytics/src/program/utils/resolve-import-path.js index 8e529a16d..b130d070a 100644 --- a/packages-node/providence-analytics/src/program/utils/resolve-import-path.js +++ b/packages-node/providence-analytics/src/program/utils/resolve-import-path.js @@ -1,4 +1,5 @@ -import pathLib from 'path'; +import { isBuiltin } from 'node:module'; +import path from 'path'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import { LogService } from '../core/LogService.js'; import { memoize } from './memoize.js'; @@ -36,11 +37,15 @@ const fakePluginContext = { * @param {SpecifierSource} importee source like '@lion/core' or '../helpers/index.js' * @param {PathFromSystemRoot} importer importing file, like '/my/project/importing-file.js' * @param {{customResolveOptions?: {preserveSymlinks:boolean}}} [opts] nodeResolve options - * @returns {Promise} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js' + * @returns {Promise} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js' */ async function resolveImportPathFn(importee, importer, opts) { + if (isBuiltin(importee)) { + return '[node-builtin]'; + } + const rollupResolve = nodeResolve({ - rootDir: pathLib.dirname(importer), + rootDir: path.dirname(importer), // allow resolving polyfills for nodejs libs preferBuiltins: false, // extensions: ['.mjs', '.js', '.json', '.node'], @@ -48,23 +53,24 @@ async function resolveImportPathFn(importee, importer, opts) { }); const preserveSymlinks = - (opts && opts.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false; - // @ts-ignore + (opts?.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false; + // @ts-expect-error rollupResolve.buildStart.call(fakePluginContext, { preserveSymlinks }); - // @ts-ignore + // @ts-expect-error const result = await rollupResolve.resolveId.handler.call( fakePluginContext, importee, importer, {}, ); - // @ts-ignore + if (!result?.id) { - LogService.warn(`importee ${importee} not found in filesystem for importer '${importer}'.`); + LogService.warn( + `[resolveImportPath] importee ${importee} not found in filesystem for importer '${importer}'.`, + ); return null; } - // @ts-ignore return toPosixPath(result.id); } diff --git a/packages-node/providence-analytics/src/program/utils/traverse-html.js b/packages-node/providence-analytics/src/program/utils/traverse-html.js index eab95a1ce..2c304b74f 100644 --- a/packages-node/providence-analytics/src/program/utils/traverse-html.js +++ b/packages-node/providence-analytics/src/program/utils/traverse-html.js @@ -1,25 +1,41 @@ /** - * @param {Node} curNode Node to start from. Will loop over its children + * @typedef {import('parse5/dist/tree-adapters/default.js').Node} Node + */ + +/** + * Creates an api similar to Babel traverse for parse5 trees + * @param {Parse5AstNode} curNode Node to start from. Will loop over its children * @param {object} processObject Will be executed for every node */ -export function traverseHtml(curNode, processObject) { +export function traverseHtml(curNode, processObject, config = {}) { function pathify(node) { return { node, - traverse(obj) { + traverseHtml(obj) { traverseHtml(node, obj); }, + stop() { + // eslint-disable-next-line no-param-reassign + config.stopped = true; + }, }; } - // let done = processFn(curNode, parentNode); + // Match... if (processObject[curNode.nodeName]) { processObject[curNode.nodeName](pathify(curNode)); } - if (curNode.childNodes) { - curNode.childNodes.forEach(childNode => { - traverseHtml(childNode, processObject, curNode); + let { childNodes } = curNode; + if (curNode.nodeName === 'template') { + childNodes = curNode.content.childNodes; + } + + if (!config.stopped && childNodes) { + childNodes.forEach(childNode => { + if (!config.stopped) { + traverseHtml(childNode, processObject, config); + } }); } } diff --git a/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js b/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js index b3ddf51d4..4dce2d150 100644 --- a/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js +++ b/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js @@ -33,7 +33,6 @@ const options = { */ // eslint-disable-next-line no-unused-vars function getResultPerAstFile(ast) { - console.debug('myAnalyzerPerAstEntry'); // Visit AST... const transformedEntryResult = []; // Do the traverse: https://babeljs.io/docs/en/babel-traverse diff --git a/packages-node/providence-analytics/test-node/program/core/InputDataService.test.js b/packages-node/providence-analytics/test-node/program/core/InputDataService.test.js index 0abff66cb..dd06fbb5e 100644 --- a/packages-node/providence-analytics/test-node/program/core/InputDataService.test.js +++ b/packages-node/providence-analytics/test-node/program/core/InputDataService.test.js @@ -9,7 +9,6 @@ import { mockProject, mock, } from '../../../test-helpers/mock-project-helpers.js'; -// import { setupAnalyzerTest } from '../../../test-helpers/setup-analyzer-test.js'; /** * @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot diff --git a/packages-node/providence-analytics/test-node/program/utils/getSourceCodeFragmentOfDeclaration.test.js b/packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js similarity index 100% rename from packages-node/providence-analytics/test-node/program/utils/getSourceCodeFragmentOfDeclaration.test.js rename to packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js diff --git a/packages-node/providence-analytics/test-node/program/utils/resolve-import-path.test.js b/packages-node/providence-analytics/test-node/program/utils/resolve-import-path.test.js index 77ef6b4d9..404e58bac 100644 --- a/packages-node/providence-analytics/test-node/program/utils/resolve-import-path.test.js +++ b/packages-node/providence-analytics/test-node/program/utils/resolve-import-path.test.js @@ -115,6 +115,25 @@ describe('resolveImportPath', () => { expect(foundPath).to.equal('/target/node_modules/ref/packages/x/index.js'); }); + it(`resolves native node modules`, async () => { + mockProject( + { + './src/someFile.js': ` + import { readFile } from 'fs'; + + export const x = readFile('/path/to/someOtherFile.js'); + `, + }, + { + projectName: 'my-project', + projectPath: '/my/project', + }, + ); + + const foundPath = await resolveImportPath('fs', '/my/project/someFile.js'); + expect(foundPath).to.equal('[node-builtin]'); + }); + /** * All edge cases are covered by https://github.com/rollup/plugins/tree/master/packages/node-resolve/test */ diff --git a/packages-node/providence-analytics/test-node/program/utils/traverse-html.test.js b/packages-node/providence-analytics/test-node/program/utils/traverse-html.test.js new file mode 100644 index 000000000..46ba6d8a5 --- /dev/null +++ b/packages-node/providence-analytics/test-node/program/utils/traverse-html.test.js @@ -0,0 +1,140 @@ +import { expect } from 'chai'; +import { it } from 'mocha'; +import * as parse5 from 'parse5'; +import { traverseHtml } from '../../../src/program/utils/traverse-html.js'; + +function getId(p5Path) { + return p5Path.node.attrs.find(a => a.name === 'id').value; +} + +describe('traverseHtml', () => { + it('finds different tag names', async () => { + const htmlCode = ` +
+ + + + + +
+
+ `; + + const ast = parse5.parseFragment(htmlCode); + const foundDivs = []; + const foundSpans = []; + const foundMyTags = []; + + traverseHtml(ast, { + div(p5Path) { + foundDivs.push(getId(p5Path)); + }, + span(p5Path) { + foundSpans.push(getId(p5Path)); + }, + // eslint-disable-next-line object-shorthand + 'my-tag'(p5Path) { + foundMyTags.push(getId(p5Path)); + }, + }); + + expect(foundDivs).to.eql(['a-lvl1', 'b']); + expect(foundSpans).to.eql(['a-lvl2']); + expect(foundMyTags).to.eql(['a-lvl3']); + }); + + it('traverses different levels in DOM order', async () => { + const htmlCode = ` +
+ + + + + +
+
+ `; + + const ast = parse5.parseFragment(htmlCode); + const callOrder = []; + const processObj = { + span(p5Path) { + callOrder.push(`span#${getId(p5Path)}`); + }, + div(p5Path) { + callOrder.push(`div#${getId(p5Path)}`); + }, + // eslint-disable-next-line object-shorthand + 'my-tag'(p5Path) { + callOrder.push(`my-tag#${getId(p5Path)}`); + }, + }; + traverseHtml(ast, processObj); + + // call order based on dom tree + expect(callOrder).to.eql(['div#a-lvl1', 'span#a-lvl2', 'my-tag#a-lvl3', 'div#b']); + }); + + it('allows to stop traversal (for performance)', async () => { + const htmlCode = ` +
+ + + + + +
+
+ `; + + const ast = parse5.parseFragment(htmlCode); + const callOrder = []; + const processObj = { + div(p5Path) { + callOrder.push(`div#${getId(p5Path)}`); + p5Path.stop(); + }, + span(p5Path) { + callOrder.push(`span#${getId(p5Path)}`); + }, + // eslint-disable-next-line object-shorthand + 'my-tag'(p5Path) { + callOrder.push(`my-tag#${getId(p5Path)}`); + }, + }; + traverseHtml(ast, processObj); + + expect(callOrder).to.eql(['div#a-lvl1']); + }); + + it('allows to traverse within a path', async () => { + const htmlCode = ` +
+ + + + + +
+
+ `; + + const ast = parse5.parseFragment(htmlCode); + const callOrder = []; + const processObj = { + // eslint-disable-next-line object-shorthand + 'my-tag'(p5Path) { + callOrder.push(`my-tag#${getId(p5Path)}`); + p5Path.traverseHtml({ + // eslint-disable-next-line object-shorthand, no-shadow + 'not-found'(p5Path) { + callOrder.push(`not-found#${getId(p5Path)}`); + }, + }); + }, + }; + traverseHtml(ast, processObj); + + expect(callOrder).to.eql(['my-tag#a-lvl3', 'not-found#a-lvl4']); + }); +}); diff --git a/packages-node/providence-analytics/types/misc.d.ts b/packages-node/providence-analytics/types/misc.d.ts index 52c870fbe..28832fa0d 100644 --- a/packages-node/providence-analytics/types/misc.d.ts +++ b/packages-node/providence-analytics/types/misc.d.ts @@ -4,9 +4,11 @@ export type TargetDep = `${PkgName}#${PkgVersion}`; export type TargetDepsObj = { [key: TargetDep]: TargetDep[]; }; + export type TargetOrRefCollectionsObj = { [key: PkgName]: PkgName[]; }; + export type ProvidenceCliConf = { metaConfig: { categoryConfig: { @@ -35,3 +37,5 @@ export type ProvidenceCliConf = { [referenceCollection: string]: string[]; }; }; + +// export { Node as Parse5Node } from 'parse5/dist/tree-adapters/default/index.js';