feat(providence): traverseHtml improvement + node builtin support
This commit is contained in:
parent
0315b33acd
commit
f4448394b9
21 changed files with 290 additions and 101 deletions
|
|
@ -29,32 +29,32 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dashboard": "node ./dashboard/server.js --run-server --serve-from-package-root",
|
"dashboard": "node ./dashboard/server.js --run-server --serve-from-package-root",
|
||||||
"postinstall": "npx patch-package",
|
"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",
|
"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 ../../",
|
"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",
|
"prepublishOnly": "npm run publish-docs",
|
||||||
"test:node": "npm run test:node:unit && npm run test:node:e2e",
|
"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:e2e": "mocha './test-node/**/*.e2e.js' --timeout 60000",
|
||||||
"test:node:unit": "mocha './test-node/**/*.test.{j,mj}s'"
|
"test:node:unit": "mocha './test-node/**/*.test.js'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.21.3",
|
"@babel/core": "^7.21.4",
|
||||||
"@babel/parser": "^7.21.3",
|
"@babel/parser": "^7.21.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-syntax-export-default-from": "^7.18.6",
|
"@babel/plugin-syntax-export-default-from": "^7.18.6",
|
||||||
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
||||||
"@babel/register": "^7.21.0",
|
"@babel/register": "^7.21.0",
|
||||||
"@babel/traverse": "^7.21.3",
|
"@babel/traverse": "^7.21.4",
|
||||||
"@babel/types": "^7.21.3",
|
"@babel/types": "^7.21.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.2",
|
||||||
"@swc/core": "^1.3.42",
|
"@swc/core": "^1.3.46",
|
||||||
"@web/dev-server": "^0.1.37",
|
"@web/dev-server": "^0.1.38",
|
||||||
"anymatch": "^3.1.3",
|
"anymatch": "^3.1.3",
|
||||||
"commander": "^2.20.3",
|
"commander": "^2.20.3",
|
||||||
"glob": "^7.2.3",
|
"glob": "^8.1.0",
|
||||||
"inquirer": "^9.1.5",
|
"inquirer": "^9.1.5",
|
||||||
"is-negated-glob": "^1.0.0",
|
"is-negated-glob": "^1.0.0",
|
||||||
"lit-element": "~2.4.0",
|
"lit-element": "~3.3.1",
|
||||||
"parse5": "^7.1.2",
|
"parse5": "^7.1.2",
|
||||||
"read-package-tree": "5.3.1",
|
"read-package-tree": "5.3.1",
|
||||||
"semver": "^7.3.8",
|
"semver": "^7.3.8",
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,6 @@ export default {
|
||||||
// Usually the references are different from the targets.
|
// Usually the references are different from the targets.
|
||||||
// In this demo file, we test @lion usage amongst itself
|
// In this demo file, we test @lion usage amongst itself
|
||||||
// Select via " providence analyze --reference-collection 'exampleCollection' "
|
// Select via " providence analyze --reference-collection 'exampleCollection' "
|
||||||
'@lion-references': ['../../packages/ui/'],
|
'@lion-references': lionScopedPackagePaths,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable no-shadow, no-param-reassign */
|
/* eslint-disable no-shadow, no-param-reassign */
|
||||||
import pathLib from 'path';
|
import path from 'path';
|
||||||
import t from '@babel/types';
|
import t from '@babel/types';
|
||||||
import babelTraverse from '@babel/traverse';
|
import babelTraverse from '@babel/traverse';
|
||||||
import { Analyzer } from '../core/Analyzer.js';
|
import { Analyzer } from '../core/Analyzer.js';
|
||||||
|
|
@ -251,7 +251,7 @@ export default class FindClassesAnalyzer extends Analyzer {
|
||||||
/** @type {FindClassesAnalyzerOutput} */
|
/** @type {FindClassesAnalyzerOutput} */
|
||||||
const queryOutput = await this._traverse(async (ast, { relativePath }) => {
|
const queryOutput = await this._traverse(async (ast, { relativePath }) => {
|
||||||
const projectPath = cfg.targetProjectPath;
|
const projectPath = cfg.targetProjectPath;
|
||||||
const fullPath = pathLib.resolve(projectPath, relativePath);
|
const fullPath = path.resolve(projectPath, relativePath);
|
||||||
const transformedEntry = await findMembersPerAstEntry(ast, fullPath, projectPath);
|
const transformedEntry = await findMembersPerAstEntry(ast, fullPath, projectPath);
|
||||||
return { result: transformedEntry };
|
return { result: transformedEntry };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -109,9 +109,9 @@ export default class FindCustomelementsAnalyzer extends Analyzer {
|
||||||
/**
|
/**
|
||||||
* Prepare
|
* Prepare
|
||||||
*/
|
*/
|
||||||
const analyzerResult = this._prepare(cfg);
|
const cachedAnalyzerResult = this._prepare(cfg);
|
||||||
if (analyzerResult) {
|
if (cachedAnalyzerResult) {
|
||||||
return analyzerResult;
|
return cachedAnalyzerResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -170,13 +170,13 @@ function findExportsPerAstFile(babelAst, { skipFileImports }) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExportNamedDeclaration(path) {
|
ExportNamedDeclaration(astPath) {
|
||||||
const exportSpecifiers = getExportSpecifiers(path.node);
|
const exportSpecifiers = getExportSpecifiers(astPath.node);
|
||||||
const localMap = getLocalNameSpecifiers(path.node);
|
const localMap = getLocalNameSpecifiers(astPath.node);
|
||||||
const source = path.node.source?.value;
|
const source = astPath.node.source?.value;
|
||||||
const entry = { exportSpecifiers, localMap, source, __tmp: { path } };
|
const entry = { exportSpecifiers, localMap, source, __tmp: { astPath } };
|
||||||
if (path.node.assertions?.length) {
|
if (astPath.node.assertions?.length) {
|
||||||
entry.assertionType = path.node.assertions[0].value?.value;
|
entry.assertionType = astPath.node.assertions[0].value?.value;
|
||||||
}
|
}
|
||||||
transformedFile.push(entry);
|
transformedFile.push(entry);
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +194,7 @@ function findExportsPerAstFile(babelAst, { skipFileImports }) {
|
||||||
source = importOrDeclPath.parentPath.node.source.value;
|
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
|
* Prepare
|
||||||
*/
|
*/
|
||||||
const analyzerResult = this._prepare(cfg);
|
const cachedAnalyzerResult = this._prepare(cfg);
|
||||||
if (analyzerResult) {
|
if (cachedAnalyzerResult) {
|
||||||
return analyzerResult;
|
return cachedAnalyzerResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,13 @@ import { toPosixPath } from '../../utils/to-posix-path.js';
|
||||||
*/
|
*/
|
||||||
export async function fromImportToExportPerspective({ importee, importer, importeeProjectPath }) {
|
export async function fromImportToExportPerspective({ importee, importer, importeeProjectPath }) {
|
||||||
if (isRelativeSourcePath(importee)) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
import pathLib from 'path';
|
import path from 'path';
|
||||||
import { isRelativeSourcePath } from '../../utils/relative-source-path.js';
|
import { isRelativeSourcePath } from '../../utils/relative-source-path.js';
|
||||||
import { resolveImportPath } from '../../utils/resolve-import-path.js';
|
import { resolveImportPath } from '../../utils/resolve-import-path.js';
|
||||||
import { toPosixPath } from '../../utils/to-posix-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').PathRelative} PathRelative
|
||||||
* @typedef {import('../../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
* @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}
|
* @returns {PathRelative}
|
||||||
*/
|
*/
|
||||||
function toLocalPath(currentDirPath, resolvedPath) {
|
function toLocalPath(currentDirPath, resolvedPath) {
|
||||||
let relativeSourcePath = pathLib.relative(currentDirPath, resolvedPath);
|
let relativeSourcePath = path.relative(currentDirPath, resolvedPath);
|
||||||
if (!relativeSourcePath.startsWith('.')) {
|
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.
|
// (from import perspective) external modules.
|
||||||
// so 'my-local-files.js' -> './my-local-files.js'
|
// so 'my-local-files.js' -> './my-local-files.js'
|
||||||
relativeSourcePath = `./${relativeSourcePath}`;
|
relativeSourcePath = `./${relativeSourcePath}`;
|
||||||
|
|
@ -28,36 +28,39 @@ function toLocalPath(currentDirPath, resolvedPath) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves and converts to normalized local/absolute path, based on file-system information.
|
* Resolves and converts to normalized local/absolute path, based on file-system information.
|
||||||
* - from: { source: '../../relative/file' }
|
* - from: '../../relative/file'
|
||||||
* - to: {
|
* - to: './src/relative/file.js'
|
||||||
* fullPath: './absolute/path/from/root/to/relative/file.js',
|
* @param {string} oldSource
|
||||||
* normalizedPath: '../../relative/file.js'
|
* @param {string} relativePath
|
||||||
* }
|
* @param {string} rootPath
|
||||||
* @param {QueryOutput} queryOutput
|
*/
|
||||||
|
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<FindImportsAnalyzerEntry>[]} queryOutput
|
||||||
* @param {string} relativePath
|
* @param {string} relativePath
|
||||||
* @param {string} rootPath
|
* @param {string} rootPath
|
||||||
*/
|
*/
|
||||||
export async function normalizeSourcePaths(queryOutput, relativePath, rootPath = process.cwd()) {
|
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 = [];
|
const normalizedQueryOutput = [];
|
||||||
for (const specifierResObj of queryOutput) {
|
for (const specifierResObj of queryOutput) {
|
||||||
if (specifierResObj.source) {
|
if (specifierResObj.source) {
|
||||||
if (isRelativeSourcePath(specifierResObj.source) && relativePath) {
|
const x = await normalizeSourcePath(specifierResObj.source, relativePath, rootPath);
|
||||||
// This will be a source like '../my/file.js' or './file.js'
|
if (x) {
|
||||||
const resolvedPath = /** @type {PathFromSystemRoot} */ (
|
specifierResObj.normalizedSource = x;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
normalizedQueryOutput.push(specifierResObj);
|
normalizedQueryOutput.push(specifierResObj);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../../../../types/index.js').FindExportsAnalyzerResult} FindExportsAnalyzerResult
|
* @typedef {import('../../../../types/index.js').FindExportsAnalyzerResult} FindExportsAnalyzerResult
|
||||||
|
* @typedef {import('../../../../types/index.js').IterableFindExportsAnalyzerEntry} IterableFindExportsAnalyzerEntry
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../../../../types/index.js').FindImportsAnalyzerResult} FindImportsAnalyzerResult
|
* @typedef {import('../../../../types/index.js').FindImportsAnalyzerResult} FindImportsAnalyzerResult
|
||||||
|
* @typedef {import('../../../../types/index.js').IterableFindImportsAnalyzerEntry} IterableFindImportsAnalyzerEntry
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,13 +44,12 @@ export function transformIntoIterableFindImportsOutput(importsAnalyzerResult) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (const importSpecifier of importSpecifiers) {
|
for (const importSpecifier of importSpecifiers) {
|
||||||
/** @type {IterableFindImportsAnalyzerEntry} */
|
const resultEntry = /** @type {IterableFindImportsAnalyzerEntry} */ ({
|
||||||
const resultEntry = {
|
|
||||||
file,
|
file,
|
||||||
specifier: importSpecifier,
|
specifier: importSpecifier,
|
||||||
source,
|
source,
|
||||||
normalizedSource,
|
normalizedSource,
|
||||||
};
|
});
|
||||||
iterableEntries.push(resultEntry);
|
iterableEntries.push(resultEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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').ProjectInputDataWithMeta} ProjectInputDataWithMeta
|
||||||
* @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult
|
* @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult
|
||||||
* @typedef {import('../../../types/index.js').MatchAnalyzerConfig} MatchAnalyzerConfig
|
* @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':
|
* Before running the analyzer, we need two conditions for a 'compatible match':
|
||||||
* - 1. referenceProject is imported by targetProject at all
|
* - 1. referenceProject is imported by targetProject at all
|
||||||
* - 2. referenceProject and targetProject have compatible major versions
|
* - 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}
|
* @type {CheckForMatchCompatibilityFn}
|
||||||
*/
|
*/
|
||||||
const checkForMatchCompatibility = memoize(
|
const checkForMatchCompatibility = memoize(
|
||||||
|
|
@ -172,7 +174,7 @@ export class Analyzer {
|
||||||
|
|
||||||
name = /** @type {typeof Analyzer} */ (this.constructor).analyzerName;
|
name = /** @type {typeof Analyzer} */ (this.constructor).analyzerName;
|
||||||
|
|
||||||
/** @type {'babel'|'swc-to-babel'|'swc-to-babel'} */
|
/** @type {'babel'|'swc-to-babel'} */
|
||||||
requiredAst = 'babel';
|
requiredAst = 'babel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -183,10 +185,10 @@ export class Analyzer {
|
||||||
* @param {MatchAnalyzerConfig} cfg
|
* @param {MatchAnalyzerConfig} cfg
|
||||||
*/
|
*/
|
||||||
static __unwindProvidedResults(cfg) {
|
static __unwindProvidedResults(cfg) {
|
||||||
if (cfg.targetProjectResult && !cfg.targetProjectResult.analyzerMeta) {
|
if (cfg.targetProjectResult && !cfg.targetProjectResult?.analyzerMeta) {
|
||||||
cfg.targetProjectResult = unwindJsonResult(cfg.targetProjectResult);
|
cfg.targetProjectResult = unwindJsonResult(cfg.targetProjectResult);
|
||||||
}
|
}
|
||||||
if (cfg.referenceProjectResult && !cfg.referenceProjectResult.analyzerMeta) {
|
if (cfg.referenceProjectResult && !cfg.referenceProjectResult?.analyzerMeta) {
|
||||||
cfg.referenceProjectResult = unwindJsonResult(cfg.referenceProjectResult);
|
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) {
|
async _traverse(traverseEntryOrConfig) {
|
||||||
LogService.debug(`Analyzer "${this.name}": started _traverse method`);
|
LogService.debug(`Analyzer "${this.name}": started _traverse method`);
|
||||||
|
|
|
||||||
|
|
@ -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
|
* @param {{ filePath: PathFromSystemRoot; exportedIdentifier: string; projectRootPath: PathFromSystemRoot }} opts
|
||||||
* @returns {Promise<{ sourceNodePath: string; sourceFragment: string|null; externalImportSource: string; }>}
|
* @returns {Promise<{ sourceNodePath: string; sourceFragment: string|null; externalImportSource: string; }>}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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 { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||||
import { LogService } from '../core/LogService.js';
|
import { LogService } from '../core/LogService.js';
|
||||||
import { memoize } from './memoize.js';
|
import { memoize } from './memoize.js';
|
||||||
|
|
@ -36,11 +37,15 @@ const fakePluginContext = {
|
||||||
* @param {SpecifierSource} importee source like '@lion/core' or '../helpers/index.js'
|
* @param {SpecifierSource} importee source like '@lion/core' or '../helpers/index.js'
|
||||||
* @param {PathFromSystemRoot} importer importing file, like '/my/project/importing-file.js'
|
* @param {PathFromSystemRoot} importer importing file, like '/my/project/importing-file.js'
|
||||||
* @param {{customResolveOptions?: {preserveSymlinks:boolean}}} [opts] nodeResolve options
|
* @param {{customResolveOptions?: {preserveSymlinks:boolean}}} [opts] nodeResolve options
|
||||||
* @returns {Promise<PathFromSystemRoot|null>} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js'
|
* @returns {Promise<PathFromSystemRoot|null|'[node-builtin]'>} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js'
|
||||||
*/
|
*/
|
||||||
async function resolveImportPathFn(importee, importer, opts) {
|
async function resolveImportPathFn(importee, importer, opts) {
|
||||||
|
if (isBuiltin(importee)) {
|
||||||
|
return '[node-builtin]';
|
||||||
|
}
|
||||||
|
|
||||||
const rollupResolve = nodeResolve({
|
const rollupResolve = nodeResolve({
|
||||||
rootDir: pathLib.dirname(importer),
|
rootDir: path.dirname(importer),
|
||||||
// allow resolving polyfills for nodejs libs
|
// allow resolving polyfills for nodejs libs
|
||||||
preferBuiltins: false,
|
preferBuiltins: false,
|
||||||
// extensions: ['.mjs', '.js', '.json', '.node'],
|
// extensions: ['.mjs', '.js', '.json', '.node'],
|
||||||
|
|
@ -48,23 +53,24 @@ async function resolveImportPathFn(importee, importer, opts) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const preserveSymlinks =
|
const preserveSymlinks =
|
||||||
(opts && opts.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false;
|
(opts?.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false;
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
rollupResolve.buildStart.call(fakePluginContext, { preserveSymlinks });
|
rollupResolve.buildStart.call(fakePluginContext, { preserveSymlinks });
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
const result = await rollupResolve.resolveId.handler.call(
|
const result = await rollupResolve.resolveId.handler.call(
|
||||||
fakePluginContext,
|
fakePluginContext,
|
||||||
importee,
|
importee,
|
||||||
importer,
|
importer,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
// @ts-ignore
|
|
||||||
if (!result?.id) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
|
||||||
return toPosixPath(result.id);
|
return toPosixPath(result.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
* @param {object} processObject Will be executed for every node
|
||||||
*/
|
*/
|
||||||
export function traverseHtml(curNode, processObject) {
|
export function traverseHtml(curNode, processObject, config = {}) {
|
||||||
function pathify(node) {
|
function pathify(node) {
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
traverse(obj) {
|
traverseHtml(obj) {
|
||||||
traverseHtml(node, 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]) {
|
if (processObject[curNode.nodeName]) {
|
||||||
processObject[curNode.nodeName](pathify(curNode));
|
processObject[curNode.nodeName](pathify(curNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curNode.childNodes) {
|
let { childNodes } = curNode;
|
||||||
curNode.childNodes.forEach(childNode => {
|
if (curNode.nodeName === 'template') {
|
||||||
traverseHtml(childNode, processObject, curNode);
|
childNodes = curNode.content.childNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.stopped && childNodes) {
|
||||||
|
childNodes.forEach(childNode => {
|
||||||
|
if (!config.stopped) {
|
||||||
|
traverseHtml(childNode, processObject, config);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const options = {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
function getResultPerAstFile(ast) {
|
function getResultPerAstFile(ast) {
|
||||||
console.debug('myAnalyzerPerAstEntry');
|
|
||||||
// Visit AST...
|
// Visit AST...
|
||||||
const transformedEntryResult = [];
|
const transformedEntryResult = [];
|
||||||
// Do the traverse: https://babeljs.io/docs/en/babel-traverse
|
// Do the traverse: https://babeljs.io/docs/en/babel-traverse
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
mockProject,
|
mockProject,
|
||||||
mock,
|
mock,
|
||||||
} from '../../../test-helpers/mock-project-helpers.js';
|
} from '../../../test-helpers/mock-project-helpers.js';
|
||||||
// import { setupAnalyzerTest } from '../../../test-helpers/setup-analyzer-test.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,25 @@ describe('resolveImportPath', () => {
|
||||||
expect(foundPath).to.equal('/target/node_modules/ref/packages/x/index.js');
|
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
|
* All edge cases are covered by https://github.com/rollup/plugins/tree/master/packages/node-resolve/test
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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 = `
|
||||||
|
<div id="a-lvl1">
|
||||||
|
<span id="a-lvl2">
|
||||||
|
<my-tag id="a-lvl3">
|
||||||
|
<not-found></notfound>
|
||||||
|
</my-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="b"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div id="a-lvl1">
|
||||||
|
<span id="a-lvl2">
|
||||||
|
<my-tag id="a-lvl3">
|
||||||
|
<not-found></notfound>
|
||||||
|
</my-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="b"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div id="a-lvl1">
|
||||||
|
<span id="a-lvl2">
|
||||||
|
<my-tag id="a-lvl3">
|
||||||
|
<not-found></notfound>
|
||||||
|
</my-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="b"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = `
|
||||||
|
<div id="a-lvl1">
|
||||||
|
<span id="a-lvl2">
|
||||||
|
<my-tag id="a-lvl3">
|
||||||
|
<not-found id="a-lvl4"></notfound>
|
||||||
|
</my-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="b"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -4,9 +4,11 @@ export type TargetDep = `${PkgName}#${PkgVersion}`;
|
||||||
export type TargetDepsObj = {
|
export type TargetDepsObj = {
|
||||||
[key: TargetDep]: TargetDep[];
|
[key: TargetDep]: TargetDep[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TargetOrRefCollectionsObj = {
|
export type TargetOrRefCollectionsObj = {
|
||||||
[key: PkgName]: PkgName[];
|
[key: PkgName]: PkgName[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProvidenceCliConf = {
|
export type ProvidenceCliConf = {
|
||||||
metaConfig: {
|
metaConfig: {
|
||||||
categoryConfig: {
|
categoryConfig: {
|
||||||
|
|
@ -35,3 +37,5 @@ export type ProvidenceCliConf = {
|
||||||
[referenceCollection: string]: string[];
|
[referenceCollection: string]: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export { Node as Parse5Node } from 'parse5/dist/tree-adapters/default/index.js';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue