diff --git a/packages-node/providence-analytics/package.json b/packages-node/providence-analytics/package.json index ea33c8fb4..366f232e6 100644 --- a/packages-node/providence-analytics/package.json +++ b/packages-node/providence-analytics/package.json @@ -34,36 +34,29 @@ "prepublishOnly": "npm run publish-docs", "test:node": "npm run test:node:unit && npm run test:node:e2e", "test:node:e2e": "mocha './test-node/**/*.e2e.js' --timeout 60000", - "test:node:unit": "mocha './test-node/**/*.test.js'" + "test:node:unit": "mocha './{test-node,src}/**/*.test.js'" }, "dependencies": { - "@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.4", - "@babel/types": "^7.21.4", - "@rollup/plugin-node-resolve": "^15.0.2", - "@swc/core": "^1.3.46", - "@web/dev-server": "^0.4.2", - "anymatch": "^3.1.3", + "@babel/parser": "^7.24.5", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "@rollup/plugin-node-resolve": "^15.2.3", + "@swc/core": "^1.5.5", + "@web/dev-server": "^0.4.4", "commander": "^2.20.3", - "glob": "^8.1.0", - "inquirer": "^9.1.5", - "is-negated-glob": "^1.0.0", - "lit-element": "~3.3.1", + "inquirer": "^9.2.20", + "lit-element": "^4.0.5", "parse5": "^7.1.2", - "read-package-tree": "5.3.1", - "semver": "^7.3.8", + "semver": "^7.6.1", "swc-to-babel": "^1.26.0" }, "devDependencies": { - "@types/chai": "^4.3.4", - "@types/inquirer": "^9.0.3", - "@types/mocha": "^10.0.1", - "@web/dev-server-core": "^0.4.0", + "@types/chai": "^4.3.16", + "@types/inquirer": "^9.0.7", + "@types/mocha": "^10.0.6", + "@web/dev-server-core": "^0.7.1", + "globby": "^14.0.1", "mock-fs": "^5.2.0", "mock-require": "^3.0.3" }, diff --git a/packages-node/providence-analytics/patches/@web+dev-server-core+0.3.17.patch b/packages-node/providence-analytics/patches/@web+dev-server-core+0.3.17.patch deleted file mode 100644 index 964b16886..000000000 --- a/packages-node/providence-analytics/patches/@web+dev-server-core+0.3.17.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/node_modules/@web/dev-server-core/test-helpers.mjs b/node_modules/@web/dev-server-core/test-helpers.mjs -index 1a4d604..9c0d714 100644 ---- a/node_modules/@web/dev-server-core/test-helpers.mjs -+++ b/node_modules/@web/dev-server-core/test-helpers.mjs -@@ -1,5 +1,5 @@ - // this file is autogenerated with the generate-mjs-dts-entrypoints script --import cjsEntrypoint from './dist/index.js'; -+import cjsEntrypoint from './dist/test-helpers.js'; - - const { - virtualFilesPlugin, diff --git a/packages-node/providence-analytics/src/cli/cli-helpers.js b/packages-node/providence-analytics/src/cli/cli-helpers.js index 01c09052c..1e221da0b 100644 --- a/packages-node/providence-analytics/src/cli/cli-helpers.js +++ b/packages-node/providence-analytics/src/cli/cli-helpers.js @@ -1,10 +1,11 @@ /* eslint-disable no-shadow */ -import pathLib from 'path'; +import path from 'path'; import child_process from 'child_process'; // eslint-disable-line camelcase -import glob from 'glob'; -import readPackageTree from '../program/utils/read-package-tree-with-bower-support.js'; +import { globbySync } from 'globby'; // eslint-disable-line import/no-extraneous-dependencies +import { optimisedGlob } from '../program/utils/optimised-glob.js'; import { LogService } from '../program/core/LogService.js'; import { toPosixPath } from '../program/utils/to-posix-path.js'; +import { fsAdapter } from '../program/utils/fs-adapter.js'; /** * @param {any[]} arr @@ -59,13 +60,9 @@ export function pathsArrayFromCs(t, cwd = process.cwd()) { return t; } if (t.includes('*')) { - if (!t.endsWith('/')) { - // eslint-disable-next-line no-param-reassign - t = `${t}/`; - } - return glob.sync(t, { cwd, absolute: true }).map(toPosixPath); + return globbySync(t, { cwd, absolute: true, onlyFiles: false }).map(toPosixPath); } - return toPosixPath(pathLib.resolve(cwd, t.trim())); + return toPosixPath(path.resolve(cwd, t.trim())); }), ); } @@ -132,6 +129,48 @@ export function targetDefault(cwd) { return [toPosixPath(cwd)]; } +/** + * @param {string} targetPath + * @param {((s:string) => boolean)|null} matcher + * @param {'npm'|'bower'} [mode] + */ +async function readPackageTree(targetPath, matcher, mode) { + const folderName = mode === 'npm' ? 'node_modules' : 'bower_components'; + const potentialPaths = await optimisedGlob(`${folderName}/**/*`, { + onlyDirectories: true, + cwd: targetPath, + absolute: true, + fs: fsAdapter.fs, + }); + const matchingPaths = potentialPaths.filter(potentialPath => { + // only dirs that are direct children of node_modules. So '**/node_modules/a' will match, but '**/node_modules/a/b' won't + const [, projectName] = potentialPath.match(new RegExp(`^.*/${folderName}/([^/]*)$`)) || []; + return matcher ? matcher(projectName) : true; + }); + return matchingPaths; +} + +/** + * @param {string|undefined} matchPattern + */ +function getMatcher(matchPattern) { + if (!matchPattern) return null; + + const isValidMatchPattern = matchPattern.startsWith('/') && matchPattern.endsWith('/'); + if (!isValidMatchPattern) { + LogService.error( + `[appendProjectDependencyPaths] Please provide a matchPattern enclosed by '/'. Found: ${matchPattern}`, + ); + return null; + } + + return (/** @type {string} */ d) => { + const reString = matchPattern.slice(1, -1); + const result = new RegExp(reString).test(d); + LogService.debug(`[appendProjectDependencyPaths]: /${reString}/.test(${d} => ${result})`); + return result; + }; +} /** * Returns all sub projects matching condition supplied in matchFn * @param {string[]} rootPaths all search-target project paths @@ -143,44 +182,15 @@ export async function appendProjectDependencyPaths( matchPattern, modes = ['npm', 'bower'], ) { - let matchFn; - if (matchPattern) { - if (matchPattern.startsWith('/') && matchPattern.endsWith('/')) { - matchFn = (/** @type {any} */ _, /** @type {string} */ d) => { - const reString = matchPattern.slice(1, -1); - const result = new RegExp(reString).test(d); - LogService.debug(`[appendProjectDependencyPaths]: /${reString}/.test(${d} => ${result})`); - return result; - }; - } else { - LogService.error( - `[appendProjectDependencyPaths] Please provide a matchPattern enclosed by '/'. Found: ${matchPattern}`, - ); - } - } + const matcher = getMatcher(matchPattern); + /** @type {string[]} */ const depProjectPaths = []; for (const targetPath of rootPaths) { for (const mode of modes) { - await readPackageTree( - targetPath, - matchFn, - (/** @type {string | undefined} */ err, /** @type {{ children: any[]; }} */ tree) => { - if (err) { - throw new Error(err); - } - const paths = tree.children.map(child => child.realpath); - depProjectPaths.push(...paths); - }, - mode, - ); + depProjectPaths.push(...(await readPackageTree(targetPath, matcher, mode))); } } - // Write all data to {outputPath}/projectDeps.json - // const projectDeps = {}; - // rootPaths.forEach(rootP => { - // depProjectPaths.filter(depP => depP.startsWith(rootP)).; - // }); return depProjectPaths.concat(rootPaths).map(toPosixPath); } @@ -192,7 +202,7 @@ export async function appendProjectDependencyPaths( */ export async function installDeps(searchTargetPaths) { for (const targetPath of searchTargetPaths) { - LogService.info(`Installing npm dependencies for ${pathLib.basename(targetPath)}`); + LogService.info(`Installing npm dependencies for ${path.basename(targetPath)}`); try { await spawnProcess('npm i --no-progress', { cwd: targetPath }); } catch (e) { @@ -200,7 +210,7 @@ export async function installDeps(searchTargetPaths) { LogService.error(e); } - LogService.info(`Installing bower dependencies for ${pathLib.basename(targetPath)}`); + LogService.info(`Installing bower dependencies for ${path.basename(targetPath)}`); try { await spawnProcess(`bower i --production --force-latest`, { cwd: targetPath }); } catch (e) { @@ -211,14 +221,14 @@ export async function installDeps(searchTargetPaths) { } export const _cliHelpersModule = { - csToArray, - extensionsFromCs, - setQueryMethod, - pathsArrayFromCs, - targetDefault, appendProjectDependencyPaths, + pathsArrayFromCollectionName, + extensionsFromCs, + pathsArrayFromCs, + setQueryMethod, + targetDefault, spawnProcess, installDeps, - pathsArrayFromCollectionName, + csToArray, flatten, }; diff --git a/packages-node/providence-analytics/src/cli/cli.js b/packages-node/providence-analytics/src/cli/cli.js index 641d98262..fc619529e 100755 --- a/packages-node/providence-analytics/src/cli/cli.js +++ b/packages-node/providence-analytics/src/cli/cli.js @@ -1,7 +1,8 @@ import child_process from 'child_process'; // eslint-disable-line camelcase import path from 'path'; -import fs from 'fs'; + import commander from 'commander'; + import { LogService } from '../program/core/LogService.js'; import { QueryService } from '../program/core/QueryService.js'; import { InputDataService } from '../program/core/InputDataService.js'; @@ -12,6 +13,7 @@ import { _providenceModule } from '../program/providence.js'; import { _cliHelpersModule } from './cli-helpers.js'; import { _extendDocsModule } from './launch-providence-with-extend-docs.js'; import { _promptAnalyzerMenuModule } from './prompt-analyzer-menu.js'; +import { fsAdapter } from '../program/utils/fs-adapter.js'; /** * @typedef {import('../../types/index.js').AnalyzerName} AnalyzerName @@ -19,7 +21,10 @@ import { _promptAnalyzerMenuModule } from './prompt-analyzer-menu.js'; */ const { version } = JSON.parse( - fs.readFileSync(path.resolve(getCurrentDir(import.meta.url), '../../package.json'), 'utf8'), + fsAdapter.fs.readFileSync( + path.resolve(getCurrentDir(import.meta.url), '../../package.json'), + 'utf8', + ), ); const { extensionsFromCs, setQueryMethod, targetDefault, installDeps } = _cliHelpersModule; diff --git a/packages-node/providence-analytics/src/cli/launch-providence-with-extend-docs.js b/packages-node/providence-analytics/src/cli/launch-providence-with-extend-docs.js index a8e902747..0ab4faa9a 100644 --- a/packages-node/providence-analytics/src/cli/launch-providence-with-extend-docs.js +++ b/packages-node/providence-analytics/src/cli/launch-providence-with-extend-docs.js @@ -1,6 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import fs from 'fs'; -import pathLib from 'path'; +import path from 'path'; import { performance } from 'perf_hooks'; import { _providenceModule } from '../program/providence.js'; import { QueryService } from '../program/core/QueryService.js'; @@ -8,6 +7,7 @@ import { InputDataService } from '../program/core/InputDataService.js'; import { LogService } from '../program/core/LogService.js'; import { flatten } from './cli-helpers.js'; import MatchPathsAnalyzer from '../program/analyzers/match-paths.js'; +import { fsAdapter } from '../program/utils/fs-adapter.js'; /** * @typedef {import('../../types/index.js').PathFromSystemRoot} PathFromSystemRoot @@ -33,7 +33,7 @@ export async function getExtendDocsResults({ allowlistReference, cwd, }) { - const monoPkgs = InputDataService.getMonoRepoPackages(cwd); + const monoPkgs = await InputDataService.getMonoRepoPackages(cwd); const results = await _providenceModule.providence( await QueryService.getQueryConfigFromAnalyzer(MatchPathsAnalyzer, { prefix: prefixCfg }), @@ -71,7 +71,7 @@ export async function getExtendDocsResults({ const normalizedP = `./${p}`; if (pathStr.startsWith(normalizedP)) { const localPath = pathStr.replace(normalizedP, ''); // 'lea-tabs.js' - result = `${name}/${localPath}`; // 'lea-tabs/lea-tabs.js' + result = path.join(name, localPath); // 'lea-tabs/lea-tabs.js' return true; } return false; @@ -124,12 +124,12 @@ export async function launchProvidenceWithExtendDocs({ }); // Write results - const outputFilePath = pathLib.join(outputFolder, 'providence-extend-docs-data.json'); + const outputFilePath = path.join(outputFolder, 'providence-extend-docs-data.json'); - if (fs.existsSync(outputFilePath)) { - fs.unlinkSync(outputFilePath); + if (fsAdapter.fs.existsSync(outputFilePath)) { + fsAdapter.fs.unlinkSync(outputFilePath); } - fs.writeFile(outputFilePath, JSON.stringify(queryOutputs, null, 2), err => { + fsAdapter.fs.writeFile(outputFilePath, JSON.stringify(queryOutputs, null, 2), err => { if (err) { throw err; } diff --git a/packages-node/providence-analytics/src/cli/prompt-analyzer-menu.js b/packages-node/providence-analytics/src/cli/prompt-analyzer-menu.js index 078989b73..cea0d75d6 100644 --- a/packages-node/providence-analytics/src/cli/prompt-analyzer-menu.js +++ b/packages-node/providence-analytics/src/cli/prompt-analyzer-menu.js @@ -1,4 +1,3 @@ -import fs from 'fs'; import path from 'path'; import inquirer from 'inquirer'; import traverse from '@babel/traverse'; @@ -7,6 +6,7 @@ import { AstService } from '../program/core/AstService.js'; import { LogService } from '../program/core/LogService.js'; import JsdocCommentParser from '../program/utils/jsdoc-comment-parser.js'; import { getCurrentDir } from '../program/utils/get-current-dir.js'; +import { fsAdapter } from '../program/utils/fs-adapter.js'; /** * @typedef {import('../../types/index.js').TargetDepsObj} TargetDepsObj @@ -42,7 +42,7 @@ function getPropsFromParsedJsDoc(jsdoc) { * @param {PathFromSystemRoot} file */ function getAnalyzerOptions(file) { - const code = fs.readFileSync(file, 'utf8'); + const code = fsAdapter.fs.readFileSync(file, 'utf8'); const babelAst = AstService.getAst(code, 'swc-to-babel', { filePath: file }); let commentNode; @@ -74,7 +74,7 @@ function getAnalyzerOptions(file) { * @param {PathFromSystemRoot} dir * @param {boolean} [shouldGetOptions] */ -function gatherAnalyzers(dir, shouldGetOptions) { +async function gatherAnalyzers(dir, shouldGetOptions) { return InputDataService.gatherFilesFromDir(dir, { depth: 0 }).map(file => { const analyzerObj = { file, name: path.basename(file, '.js') }; if (shouldGetOptions) { @@ -98,7 +98,7 @@ export async function promptAnalyzerConfigMenu( path.resolve(getCurrentDir(import.meta.url), '../program/analyzers') ), ) { - const menuOptions = gatherAnalyzers(dir, true); + const menuOptions = await gatherAnalyzers(dir, true); const analyzer = menuOptions.find(o => o.name === analyzerName); if (!analyzer) { LogService.error(`[promptAnalyzerConfigMenu] analyzer "${analyzerName}" not found.`); diff --git a/packages-node/providence-analytics/src/dashboard/server.js b/packages-node/providence-analytics/src/dashboard/server.js index 1c69d216c..fa789753b 100644 --- a/packages-node/providence-analytics/src/dashboard/server.js +++ b/packages-node/providence-analytics/src/dashboard/server.js @@ -1,9 +1,9 @@ -import fs from 'fs'; -import pathLib from 'path'; +import path from 'path'; import { startDevServer } from '@web/dev-server'; import { ReportService } from '../program/core/ReportService.js'; import { providenceConfUtil } from '../program/utils/providence-conf-util.js'; import { getCurrentDir } from '../program/utils/get-current-dir.js'; +import { fsAdapter } from '../program/utils/fs-adapter.js'; /** * @typedef {import('../../types/index.js').PathFromSystemRoot} PathFromSystemRoot @@ -25,7 +25,7 @@ async function getCachedProvidenceResults({ */ let outputFilePaths; try { - outputFilePaths = fs.readdirSync(resultsPath); + outputFilePaths = fsAdapter.fs.readdirSync(resultsPath); } catch (_) { throw new Error(`Please make sure providence results can be found in ${resultsPath}`); } @@ -33,7 +33,9 @@ async function getCachedProvidenceResults({ const resultFiles = {}; let searchTargetDeps; outputFilePaths.forEach(fileName => { - const content = JSON.parse(fs.readFileSync(pathLib.join(resultsPath, fileName), 'utf-8')); + const content = JSON.parse( + fsAdapter.fs.readFileSync(path.join(resultsPath, fileName), 'utf-8'), + ); if (fileName === 'search-target-deps-file.json') { searchTargetDeps = content; } else { @@ -62,8 +64,8 @@ function createMiddleWares({ providenceConf, providenceConfRaw, searchTargetDeps */ function getPackageJson(projectPath) { try { - const file = pathLib.resolve(projectPath, 'package.json'); - return JSON.parse(fs.readFileSync(file, 'utf8')); + const file = path.resolve(projectPath, 'package.json'); + return JSON.parse(fsAdapter.fs.readFileSync(file, 'utf8')); } catch (_) { return null; } @@ -85,7 +87,7 @@ function createMiddleWares({ providenceConf, providenceConfRaw, searchTargetDeps return res; } - const pathFromServerRootToHere = `/${pathLib.relative( + const pathFromServerRootToHere = `/${path.relative( process.cwd(), getCurrentDir(import.meta.url), )}`; @@ -148,13 +150,13 @@ export async function createDashboardServerConfig() { // Needed for dev purposes (we call it from ./packages-node/providence-analytics/ instead of ./) // Allows es-dev-server to find the right moduleDirs const fromPackageRoot = process.argv.includes('--serve-from-package-root'); - const moduleRoot = fromPackageRoot ? pathLib.resolve(process.cwd(), '../../') : process.cwd(); + const moduleRoot = fromPackageRoot ? path.resolve(process.cwd(), '../../') : process.cwd(); return { - appIndex: pathLib.resolve(getCurrentDir(import.meta.url), 'index.html'), + appIndex: path.resolve(getCurrentDir(import.meta.url), 'index.html'), rootDir: moduleRoot, nodeResolve: true, - moduleDirs: pathLib.resolve(moduleRoot, 'node_modules'), + moduleDirs: path.resolve(moduleRoot, 'node_modules'), watch: false, open: true, middleware: createMiddleWares({ 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 3ab8ee7c9..751268e65 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-classes.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-classes.js @@ -241,7 +241,7 @@ export default class FindClassesAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); + const analyzerResult = await this._prepare(cfg); if (analyzerResult) { return analyzerResult; } 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 5d6d3a348..9a981c17f 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-customelements.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-customelements.js @@ -109,7 +109,7 @@ export default class FindCustomelementsAnalyzer extends Analyzer { /** * Prepare */ - const cachedAnalyzerResult = this._prepare(cfg); + const cachedAnalyzerResult = await this._prepare(cfg); if (cachedAnalyzerResult) { return cachedAnalyzerResult; } 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 d3b811a00..71a65e720 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-imports.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-imports.js @@ -124,7 +124,7 @@ export default class FindImportsSwcAnalyzer extends Analyzer { /** * Prepare */ - const cachedAnalyzerResult = this._prepare(cfg); + const cachedAnalyzerResult = await this._prepare(cfg); if (cachedAnalyzerResult) { return cachedAnalyzerResult; } diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier--legacy.js b/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier--legacy.js index 504a5cf3f..cc135af8b 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier--legacy.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier--legacy.js @@ -1,6 +1,5 @@ /* eslint-disable no-shadow */ // @ts-nocheck -import fs from 'fs'; import pathLib from 'path'; import babelTraverse from '@babel/traverse'; import { isRelativeSourcePath, toRelativeSourcePath } from '../../utils/relative-source-path.js'; @@ -9,6 +8,7 @@ import { resolveImportPath } from '../../utils/resolve-import-path.js'; import { AstService } from '../../core/AstService.js'; import { LogService } from '../../core/LogService.js'; import { memoize } from '../../utils/memoize.js'; +import { fsAdapter } from '../../utils/fs-adapter.js'; /** * @typedef {import('../../../../types/index.js').RootFile} RootFile @@ -23,7 +23,7 @@ import { memoize } from '../../utils/memoize.js'; * @param {string} projectName */ function isSelfReferencingProject(source, projectName) { - return source.startsWith(`${projectName}`); + return source.split('/')[0] === projectName; } /** @@ -184,7 +184,7 @@ async function trackDownIdentifierFn( specifier: '[default]', }; } - const code = fs.readFileSync(resolvedSourcePath, 'utf8'); + const code = fsAdapter.fs.readFileSync(resolvedSourcePath, 'utf8'); const babelAst = AstService.getAst(code, 'swc-to-babel', { filePath: resolvedSourcePath }); const shouldLookForDefaultExport = identifierName === '[default]'; diff --git a/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier.js b/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier.js index 77384a91b..40a59546f 100644 --- a/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier.js +++ b/packages-node/providence-analytics/src/program/analyzers/helpers/track-down-identifier.js @@ -1,4 +1,3 @@ -import fs from 'fs'; import path from 'path'; import { swcTraverse } from '../../utils/swc-traverse.js'; import { isRelativeSourcePath, toRelativeSourcePath } from '../../utils/relative-source-path.js'; @@ -6,12 +5,13 @@ import { InputDataService } from '../../core/InputDataService.js'; import { resolveImportPath } from '../../utils/resolve-import-path.js'; import { AstService } from '../../core/AstService.js'; import { memoize } from '../../utils/memoize.js'; +import { fsAdapter } from '../../utils/fs-adapter.js'; /** - * @typedef {import('../../../../types/index.js').RootFile} RootFile + * @typedef {import('../../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot * @typedef {import('../../../../types/index.js').SpecifierSource} SpecifierSource * @typedef {import('../../../../types/index.js').IdentifierName} IdentifierName - * @typedef {import('../../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot + * @typedef {import('../../../../types/index.js').RootFile} RootFile * @typedef {import('../../../../types/index.js').SwcPath} SwcPath */ @@ -20,7 +20,7 @@ import { memoize } from '../../utils/memoize.js'; * @param {string} projectName */ function isSelfReferencingProject(source, projectName) { - return source.startsWith(`${projectName}`); + return source.split('/')[0] === projectName; } /** @@ -193,7 +193,7 @@ async function trackDownIdentifierFn( specifier: '[default]', }; } - const code = fs.readFileSync(/** @type {string} */ (resolvedSourcePath), 'utf8'); + const code = fsAdapter.fs.readFileSync(/** @type {string} */ (resolvedSourcePath), 'utf8'); const swcAst = AstService._getSwcAst(code); const shouldLookForDefaultExport = identifierName === '[default]'; diff --git a/packages-node/providence-analytics/src/program/analyzers/match-imports.js b/packages-node/providence-analytics/src/program/analyzers/match-imports.js index 3a2bd2cba..bff89747b 100644 --- a/packages-node/providence-analytics/src/program/analyzers/match-imports.js +++ b/packages-node/providence-analytics/src/program/analyzers/match-imports.js @@ -192,14 +192,11 @@ export default class MatchImportsAnalyzer extends Analyzer { /** * Prepare */ - const cachedAnalyzerResult = this._prepare(cfg); + const cachedAnalyzerResult = await this._prepare(cfg); if (cachedAnalyzerResult) { return cachedAnalyzerResult; } - /** - * Traverse - */ let { referenceProjectResult } = cfg; if (!referenceProjectResult) { const findExportsAnalyzer = new FindExportsAnalyzer(); @@ -222,6 +219,9 @@ export default class MatchImportsAnalyzer extends Analyzer { }); } + /** + * Traverse + */ const queryOutput = await matchImportsPostprocess( referenceProjectResult, targetProjectResult, diff --git a/packages-node/providence-analytics/src/program/analyzers/match-paths.js b/packages-node/providence-analytics/src/program/analyzers/match-paths.js index 54119a1ad..6adb5824f 100644 --- a/packages-node/providence-analytics/src/program/analyzers/match-paths.js +++ b/packages-node/providence-analytics/src/program/analyzers/match-paths.js @@ -198,7 +198,6 @@ function getVariablePaths( } /** - * * @param {FindCustomelementsAnalyzerResult} targetFindCustomelementsResult * @param {FindCustomelementsAnalyzerResult} refFindCustomelementsResult * @param {FindExportsAnalyzerResult} refFindExportsResult @@ -240,8 +239,10 @@ function getTagPaths( if (!matchSubclassSpecifierRootFile) { return false; } + const sameRoot = entry.rootFile.file === matchSubclassSpecifierRootFile.file; const sameIdentifier = entry.rootFile.specifier === matchSubclassEntry.exportSpecifier.name; + return sameRoot && sameIdentifier; }); if (refPathMatch) { @@ -274,11 +275,11 @@ function matchPathsPostprocess( /** @type {AnalyzerQueryResult} */ const resultsArray = []; - targetMatchSubclassesResult.queryOutput.forEach(matchSubclassEntry => { + for (const matchSubclassEntry of targetMatchSubclassesResult.queryOutput) { const fromClass = matchSubclassEntry.exportSpecifier.name; - matchSubclassEntry.matchesPerProject.forEach(projectMatch => { - projectMatch.files.forEach(({ identifier: toClass, file: targetMatchedFile }) => { + for (const projectMatch of matchSubclassEntry.matchesPerProject) { + for (const { identifier: toClass, file: targetMatchedFile } of projectMatch.files) { const resultEntry = { name: fromClass, }; @@ -293,7 +294,7 @@ function matchPathsPostprocess( refProjectName, ); - if (paths && paths.length) { + if (paths?.length) { resultEntry.variable = { from: fromClass, to: toClass, @@ -324,9 +325,9 @@ function matchPathsPostprocess( if (resultEntry.variable || resultEntry.tag) { resultsArray.push(resultEntry); } - }); - }); - }); + } + } + } return resultsArray; } @@ -394,7 +395,7 @@ export default class MatchPathsAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); + const analyzerResult = await this._prepare(cfg); if (analyzerResult) { return analyzerResult; } diff --git a/packages-node/providence-analytics/src/program/analyzers/match-subclasses.js b/packages-node/providence-analytics/src/program/analyzers/match-subclasses.js index 6a1b407a8..4a7ac2b67 100644 --- a/packages-node/providence-analytics/src/program/analyzers/match-subclasses.js +++ b/packages-node/providence-analytics/src/program/analyzers/match-subclasses.js @@ -312,7 +312,7 @@ export default class MatchSubclassesAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); + const analyzerResult = await this._prepare(cfg); if (analyzerResult) { return analyzerResult; } diff --git a/packages-node/providence-analytics/src/program/core/Analyzer.js b/packages-node/providence-analytics/src/program/core/Analyzer.js index c5d9989b1..72be87ba8 100644 --- a/packages-node/providence-analytics/src/program/core/Analyzer.js +++ b/packages-node/providence-analytics/src/program/core/Analyzer.js @@ -54,9 +54,8 @@ async function analyzePerAstFile(projectData, astAnalysis, analyzerCfg) { * @param {object[]|object} data */ function posixify(data) { - if (!data) { - return; - } + if (!data) return; + if (Array.isArray(data)) { data.forEach(posixify); } else if (typeof data === 'object') { @@ -212,7 +211,7 @@ export class Analyzer { * @param {AnalyzerConfig} cfg * @returns {CachedAnalyzerResult|undefined} */ - _prepare(cfg) { + async _prepare(cfg) { LogService.debug(`Analyzer "${this.name}": started _prepare method`); /** @type {typeof Analyzer} */ (this.constructor).__unwindProvidedResults(cfg); @@ -281,14 +280,14 @@ export class Analyzer { * Get reference and search-target data */ if (!cfg.targetProjectResult) { - this.targetData = InputDataService.createDataObject( + this.targetData = await InputDataService.createDataObject( [cfg.targetProjectPath], cfg.gatherFilesConfig, ); } if (cfg.referenceProjectPath) { - this.referenceData = InputDataService.createDataObject( + this.referenceData = await InputDataService.createDataObject( [cfg.referenceProjectPath], cfg.gatherFilesConfigReference || cfg.gatherFilesConfig, ); @@ -333,7 +332,7 @@ export class Analyzer { if (!projectPath) { LogService.error(`[Analyzer._traverse]: you must provide a projectPath`); } - finalTargetData = InputDataService.createDataObject([ + finalTargetData = await InputDataService.createDataObject([ { project: { name: projectName || '[n/a]', @@ -366,7 +365,7 @@ export class Analyzer { /** * Prepare */ - const cachedAnalyzerResult = this._prepare(cfg); + const cachedAnalyzerResult = await this._prepare(cfg); if (cachedAnalyzerResult) { return cachedAnalyzerResult; } diff --git a/packages-node/providence-analytics/src/program/core/InputDataService.js b/packages-node/providence-analytics/src/program/core/InputDataService.js index d41f2e3f9..e86716a02 100644 --- a/packages-node/providence-analytics/src/program/core/InputDataService.js +++ b/packages-node/providence-analytics/src/program/core/InputDataService.js @@ -1,42 +1,37 @@ -/* eslint-disable no-param-reassign */ -import fs from 'fs'; -import pathLib from 'path'; import child_process from 'child_process'; // eslint-disable-line camelcase -import glob from 'glob'; -import anymatch from 'anymatch'; -// @ts-expect-error -import isNegatedGlob from 'is-negated-glob'; +import path from 'path'; + +import { getFilePathRelativeFromRoot } from '../utils/get-file-path-relative-from-root.js'; +import { optimisedGlob } from '../utils/optimised-glob.js'; +import { toPosixPath } from '../utils/to-posix-path.js'; +import { fsAdapter } from '../utils/fs-adapter.js'; +import { memoize } from '../utils/memoize.js'; import { LogService } from './LogService.js'; import { AstService } from './AstService.js'; -import { getFilePathRelativeFromRoot } from '../utils/get-file-path-relative-from-root.js'; -import { toPosixPath } from '../utils/to-posix-path.js'; -import { memoize } from '../utils/memoize.js'; - -// const memoize = fn => fn; /** + * @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot * @typedef {import('../../../types/index.js').FindImportsAnalyzerResult} FindImportsAnalyzerResult * @typedef {import('../../../types/index.js').FindImportsAnalyzerEntry} FindImportsAnalyzerEntry - * @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot + * @typedef {import('../../../types/index.js').ProjectInputDataWithMeta} ProjectInputDataWithMeta + * @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult + * @typedef {import('../../../types/index.js').AnalyzerQueryConfig} AnalyzerQueryConfig + * @typedef {import('../../../types/index.js').FeatureQueryConfig} FeatureQueryConfig + * @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot + * @typedef {import('../../../types/index.js').SearchQueryConfig} SearchQueryConfig + * @typedef {import('../../../types/index.js').GatherFilesConfig} GatherFilesConfig + * @typedef {import('../../../types/index.js').ProjectInputData} ProjectInputData + * @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig + * @typedef {{path:PathFromSystemRoot; name:ProjectName}} ProjectNameAndPath * @typedef {import('../../../types/index.js').PathRelative} PathRelative + * @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName * @typedef {import('../../../types/index.js').QueryConfig} QueryConfig * @typedef {import('../../../types/index.js').QueryResult} QueryResult - * @typedef {import('../../../types/index.js').FeatureQueryConfig} FeatureQueryConfig - * @typedef {import('../../../types/index.js').SearchQueryConfig} SearchQueryConfig - * @typedef {import('../../../types/index.js').AnalyzerQueryConfig} AnalyzerQueryConfig - * @typedef {import('../../../types/index.js').Feature} Feature - * @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig - * @typedef {import('../../../types/index.js').Analyzer} Analyzer - * @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName - * @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot - * @typedef {import('../../../types/index.js').GatherFilesConfig} GatherFilesConfig - * @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult - * @typedef {import('../../../types/index.js').ProjectInputData} ProjectInputData - * @typedef {import('../../../types/index.js').ProjectInputDataWithMeta} ProjectInputDataWithMeta - * @typedef {import('../../../types/index.js').Project} Project * @typedef {import('../../../types/index.js').ProjectName} ProjectName * @typedef {import('../../../types/index.js').PackageJson} PackageJson - * @typedef {{path:PathFromSystemRoot; name:ProjectName}} ProjectNameAndPath + * @typedef {import('../../../types/index.js').Analyzer} Analyzer + * @typedef {import('../../../types/index.js').Project} Project + * @typedef {import('../../../types/index.js').Feature} Feature */ /** @@ -45,13 +40,13 @@ import { memoize } from '../utils/memoize.js'; */ const getPackageJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { try { - const fileContent = fs.readFileSync(`${rootPath}/package.json`, 'utf8'); + const fileContent = fsAdapter.fs.readFileSync(`${rootPath}/package.json`, 'utf8'); return JSON.parse(fileContent); } catch (_) { try { // For testing purposes, we allow to have a package.mock.json that contains 'fictional' // packages (like 'exporting-ref-project') not on npm registry - const fileContent = fs.readFileSync(`${rootPath}/package.mock.json`, 'utf8'); + const fileContent = fsAdapter.fs.readFileSync(`${rootPath}/package.mock.json`, 'utf8'); return JSON.parse(fileContent); } catch (__) { return undefined; @@ -65,7 +60,7 @@ const getPackageJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { */ const getLernaJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { try { - const fileContent = fs.readFileSync(`${rootPath}/lerna.json`, 'utf8'); + const fileContent = fsAdapter.fs.readFileSync(`${rootPath}/lerna.json`, 'utf8'); return JSON.parse(fileContent); } catch (_) { return undefined; @@ -73,34 +68,31 @@ const getLernaJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { }); /** - * @typedef {(list:PathFromSystemRoot[]|string[], rootPath:PathFromSystemRoot) => ProjectNameAndPath[]} GetPathsFromGlobListFn + * @typedef {(list:PathFromSystemRoot[]|string[], rootPath:PathFromSystemRoot) => Promise} GetPathsFromGlobListFn * @type {GetPathsFromGlobListFn} */ const getPathsFromGlobList = memoize( - ( + async ( /** @type {PathFromSystemRoot[]|string[]} */ list, /** @type {PathFromSystemRoot} */ rootPath, ) => { /** @type {string[]} */ const results = []; - list.forEach(pathOrGlob => { - if (!pathOrGlob.endsWith('/')) { - // eslint-disable-next-line no-param-reassign - pathOrGlob = `${pathOrGlob}/`; - } - + for (const pathOrGlob of list) { if (pathOrGlob.includes('*')) { - const globResults = glob.sync(pathOrGlob, { cwd: rootPath, absolute: false }); - globResults.forEach(r => { - results.push(r); + const globResults = await optimisedGlob(pathOrGlob, { + cwd: rootPath, + absolute: false, + onlyFiles: false, }); + results.push(...globResults); } else { results.push(pathOrGlob); } - }); + } return results.map(pkgPath => { - const packageRoot = pathLib.resolve(rootPath, pkgPath); - const basename = pathLib.basename(pkgPath); + const packageRoot = path.resolve(rootPath, pkgPath); + const basename = path.basename(pkgPath); const pkgJson = getPackageJson(/** @type {PathFromSystemRoot} */ (packageRoot)); const name = /** @type {ProjectName} */ ((pkgJson && pkgJson.name) || basename); return { name, path: /** @type {PathFromSystemRoot} */ (pkgPath) }; @@ -114,7 +106,7 @@ const getPathsFromGlobList = memoize( */ const getGitignoreFile = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { try { - return fs.readFileSync(`${rootPath}/.gitignore`, 'utf8'); + return fsAdapter.fs.readFileSync(`${rootPath}/.gitignore`, 'utf8'); } catch (_) { return undefined; } @@ -131,6 +123,7 @@ const getGitIgnorePaths = memoize((/** @type {PathFromSystemRoot} */ rootPath) = } const entries = fileContent.split('\n').filter(entry => { + // eslint-disable-next-line no-param-reassign entry = entry.trim(); if (entry.startsWith('#')) { return false; @@ -143,15 +136,19 @@ const getGitIgnorePaths = memoize((/** @type {PathFromSystemRoot} */ rootPath) = // normalize entries to be compatible with anymatch const normalizedEntries = entries.map(entry => { + // eslint-disable-next-line no-param-reassign entry = toPosixPath(entry); if (entry.startsWith('/')) { + // eslint-disable-next-line no-param-reassign entry = entry.slice(1); } const isFile = entry.indexOf('.') > 0; // index of 0 means hidden file. if (entry.endsWith('/')) { + // eslint-disable-next-line no-param-reassign entry += '**'; } else if (!isFile) { + // eslint-disable-next-line no-param-reassign entry += '/**'; } return entry; @@ -189,30 +186,6 @@ function ensureArray(v) { return Array.isArray(v) ? v : [v]; } -/** - * @param {string|string[]} patterns - * @param {Partial<{keepDirs:boolean;root:string}>} [options] - * - * @typedef {(patterns:string|string[], opts: {keepDirs?:boolean;root:string}) => string[]} MultiGlobSyncFn - * @type {MultiGlobSyncFn} - */ -const multiGlobSync = memoize( - (/** @type {string|string[]} */ patterns, { keepDirs = false, root } = {}) => { - patterns = ensureArray(patterns); - const res = new Set(); - patterns.forEach(pattern => { - const files = glob.sync(pattern, { root }); - files.forEach(filePath => { - if (fs.lstatSync(filePath).isDirectory() && !keepDirs) { - return; - } - res.add(filePath); - }); - }); - return Array.from(res); - }, -); - /** * @param {string} localPathWithDotSlash * @returns {string} @@ -261,26 +234,28 @@ export class InputDataService { * @param {Partial} gatherFilesConfig * @returns {ProjectInputDataWithMeta[]} */ - static createDataObject(projectPaths, gatherFilesConfig = {}) { + static async createDataObject(projectPaths, gatherFilesConfig = {}) { /** @type {ProjectInputData[]} */ - const inputData = projectPaths.map(projectPathOrObj => { + const inputData = []; + for (const projectPathOrObj of projectPaths) { if (typeof projectPathOrObj === 'object') { // ProjectInputData was provided already manually - return projectPathOrObj; + inputData.push(projectPathOrObj); + continue; // eslint-disable-line no-continue } const projectPath = projectPathOrObj; - return { + inputData.push({ project: /** @type {Project} */ ({ - name: pathLib.basename(projectPath), + name: path.basename(projectPath), path: projectPath, }), - entries: this.gatherFilesFromDir(projectPath, { + entries: await this.gatherFilesFromDir(projectPath, { ...this.defaultGatherFilesConfig, ...gatherFilesConfig, }), - }; - }); + }); + } // @ts-ignore return this._addMetaToProjectsData(inputData); } @@ -333,7 +308,7 @@ export class InputDataService { let commitHash; let isGitRepo; try { - isGitRepo = fs.lstatSync(pathLib.resolve(projectPath, '.git')).isDirectory(); + isGitRepo = fsAdapter.fs.lstatSync(path.resolve(projectPath, '.git')).isDirectory(); // eslint-disable-next-line no-empty } catch (_) {} @@ -372,7 +347,7 @@ export class InputDataService { projectObj.entries.forEach(entry => { let code; try { - code = fs.readFileSync(entry, 'utf8'); + code = fsAdapter.fs.readFileSync(entry, 'utf8'); } catch (e) { LogService.error(`Could not find "${entry}"`); } @@ -380,7 +355,7 @@ export class InputDataService { toPosixPath(entry), toPosixPath(projectObj.project.path), ); - if (pathLib.extname(file) === '.html') { + if (path.extname(file) === '.html') { const extractedScripts = AstService.getScriptsFromHtml(/** @type {string} */ (code)); // eslint-disable-next-line no-shadow extractedScripts.forEach((code, i) => { @@ -409,19 +384,16 @@ export class InputDataService { if (this.__targetProjectPaths) { return this.__targetProjectPaths; } - const submoduleDir = pathLib.resolve( - __dirname, - '../../../providence-input-data/search-targets', - ); + const submoduleDir = path.resolve(__dirname, '../../../providence-input-data/search-targets'); let dirs; try { - dirs = fs.readdirSync(submoduleDir); + dirs = fsAdapter.fs.readdirSync(submoduleDir); } catch (_) { return []; } return dirs - .map(dir => /** @type {PathFromSystemRoot} */ (pathLib.join(submoduleDir, dir))) - .filter(dirPath => fs.lstatSync(dirPath).isDirectory()); + .map(dir => /** @type {PathFromSystemRoot} */ (path.join(submoduleDir, dir))) + .filter(dirPath => fsAdapter.fs.lstatSync(dirPath).isDirectory()); } static set targetProjectPaths(v) { @@ -438,11 +410,11 @@ export class InputDataService { let dirs; try { - const referencesDir = pathLib.resolve(__dirname, '../../../providence-input-data/references'); - dirs = fs.readdirSync(referencesDir); + const referencesDir = path.resolve(__dirname, '../../../providence-input-data/references'); + dirs = fsAdapter.fs.readdirSync(referencesDir); dirs = dirs - .map(dir => pathLib.join(referencesDir, dir)) - .filter(dirPath => fs.lstatSync(dirPath).isDirectory()); + .map(dir => path.join(referencesDir, dir)) + .filter(dirPath => fsAdapter.fs.lstatSync(dirPath).isDirectory()); // eslint-disable-next-line no-empty } catch (_) {} return /** @type {PathFromSystemRoot[]} */ (dirs); @@ -457,31 +429,31 @@ export class InputDataService { */ static get defaultGatherFilesConfig() { return { - extensions: ['.js'], allowlist: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'], + extensions: ['.js'], depth: Infinity, }; } /** - * @param {PathFromSystemRoot} startPath - * @param {GatherFilesConfig} cfg - * @param {boolean} withoutDepth + * @protected + * @param {number} depth + * @param {string[]} extensions + * @returns {string} */ - static getGlobPattern(startPath, cfg, withoutDepth = false) { - // if startPath ends with '/', remove - let globPattern = startPath.replace(/\/$/, ''); - if (process.platform === 'win32') { - globPattern = globPattern.replace(/^.:/, '').replace(/\\/g, '/'); + static _getDefaultGlobDepthPattern(depth = Infinity, extensions = ['.js']) { + // `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`; + const extensionsGlobPart = `.{${extensions.map(extension => extension.slice(1)).join(',')},}`; + if (depth === Infinity) { + return `**/*${extensionsGlobPart}`; } - if (!withoutDepth) { - if (typeof cfg.depth === 'number' && cfg.depth !== Infinity) { - globPattern += `/*`.repeat(cfg.depth + 1); - } else { - globPattern += `/**/*`; - } + if (depth > 1) { + return `${`/*`.repeat(depth + 1)}${extensionsGlobPart}`; } - return { globPattern }; + if (depth === 0) { + return `*${extensionsGlobPart}`; + } + return ''; } /** @@ -498,9 +470,9 @@ export class InputDataService { * Gets an array of files for given extension * @param {PathFromSystemRoot} startPath - local filesystem path * @param {Partial} customConfig - configuration object - * @returns {PathFromSystemRoot[]} result list of file paths + * @returns {Promise} result list of file paths */ - static gatherFilesFromDir(startPath, customConfig = {}) { + static async gatherFilesFromDir(startPath, customConfig = {}) { const cfg = { ...this.defaultGatherFilesConfig, ...customConfig, @@ -523,84 +495,79 @@ export class InputDataService { if (cfg.allowlistMode === 'export-map') { const pkgJson = getPackageJson(startPath); - if (!pkgJson.exports) { + if (!pkgJson?.exports) { LogService.error(`No exports found in package.json of ${startPath}`); } - const exposedAndInternalPaths = this.getPathsFromExportMap(pkgJson.exports, { + const exposedAndInternalPaths = await this.getPathsFromExportMap(pkgJson?.exports, { packageRootPath: startPath, }); return exposedAndInternalPaths .map(p => p.internal) - .filter(p => cfg.extensions.includes(`${pathLib.extname(p)}`)); + .filter(p => cfg.extensions.includes(`${path.extname(p)}`)); } /** @type {string[]} */ - let gitIgnorePaths = []; + const negativeGitGlobs = []; /** @type {string[]} */ - let npmPackagePaths = []; + const npmGlobs = []; const allowlistMode = cfg.allowlistMode || this._determineAllowListMode(startPath); if (allowlistMode === 'git') { - gitIgnorePaths = getGitIgnorePaths(startPath); + negativeGitGlobs.push( + ...getGitIgnorePaths(startPath).map(gitIgnorePath => `!${gitIgnorePath}`), + ); } else if (allowlistMode === 'npm') { - npmPackagePaths = getNpmPackagePaths(startPath); - } - const removeFilter = gitIgnorePaths; - const keepFilter = npmPackagePaths; - - cfg.allowlist.forEach(allowEntry => { - const { negated, pattern } = isNegatedGlob(allowEntry); - if (negated) { - removeFilter.push(pattern); - } else { - keepFilter.push(allowEntry); - } - }); - - let { globPattern } = this.getGlobPattern(startPath, cfg); - globPattern += `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`; - const globRes = multiGlobSync(globPattern); - - let filteredGlobRes; - if (removeFilter.length || keepFilter.length) { - filteredGlobRes = globRes.filter(filePath => { - const localFilePath = toPosixPath(filePath).replace(`${toPosixPath(startPath)}/`, ''); - // @ts-expect-error - let shouldRemove = removeFilter.length && anymatch(removeFilter, localFilePath); - // @ts-expect-error - let shouldKeep = keepFilter.length && anymatch(keepFilter, localFilePath); - - if (shouldRemove && shouldKeep) { - // Contradicting configs: the one defined by end user takes highest precedence - // If the match came from allowListMode, it loses. - // @ts-expect-error - if (allowlistMode === 'git' && anymatch(gitIgnorePaths, localFilePath)) { - // shouldRemove was caused by .gitignore, shouldKeep by custom allowlist - shouldRemove = false; - // @ts-expect-error - } else if (allowlistMode === 'npm' && anymatch(npmPackagePaths, localFilePath)) { - // shouldKeep was caused by npm "files", shouldRemove by custom allowlist - shouldKeep = false; - } - } - - if (removeFilter.length && shouldRemove) { - return false; - } - if (!keepFilter.length) { - return true; - } - return shouldKeep; - }); + npmGlobs.push(...getNpmPackagePaths(startPath)); } - if (!filteredGlobRes || !filteredGlobRes.length) { + const combinedGlobs = [...cfg.allowlist, ...npmGlobs, ...negativeGitGlobs]; + const hasProvidedPositiveGlob = cfg.allowlist.some(glob => !glob.startsWith('!')); + + // We need to expand + const shouldLookForAllFilesInProject = + allowlistMode === 'all' || (!npmGlobs.length && !hasProvidedPositiveGlob); + if (shouldLookForAllFilesInProject) { + combinedGlobs.push(this._getDefaultGlobDepthPattern(cfg.depth, cfg.extensions)); + } + + const globbyCfg = { + expandDirectories: false, + onlyFiles: true, + absolute: true, + cwd: startPath, + }; + let filteredGlobRes = await optimisedGlob(combinedGlobs, globbyCfg); + + // Unfortunatly, globby does not correctly remove the negated globs, + // so we have to do it manually + const negatedGlobs = combinedGlobs.filter(p => p.startsWith('!')); + if (negatedGlobs.length) { + const subtract = await optimisedGlob( + negatedGlobs.map(p => p.slice(1)), + globbyCfg, + ); + // eslint-disable-next-line no-shadow + filteredGlobRes = filteredGlobRes.filter(file => !subtract.includes(file)); + } + + // Make sure we don't delete to much by giving customConfig.allowlist priority + if (customConfig.allowlist?.length) { + const customResults = await optimisedGlob(customConfig.allowlist, globbyCfg); + filteredGlobRes = Array.from(new Set([...filteredGlobRes, ...customResults])); + } + + // Filter by extension (in the future: use globs exclusively for this?) + if (cfg.extensions.length) { + filteredGlobRes = filteredGlobRes.filter(glob => + cfg.extensions.some(ext => glob.endsWith(ext)), + ); + } + + if (!filteredGlobRes?.length) { LogService.warn(`No files found for path '${startPath}'`); return []; } - // reappend startPath - // const res = filteredGlobRes.map(f => pathLib.resolve(startPath, f)); return /** @type {PathFromSystemRoot[]} */ (filteredGlobRes.map(toPosixPath)); } @@ -617,9 +584,9 @@ export class InputDataService { /** * Gives back all monorepo package paths * @param {PathFromSystemRoot} rootPath - * @returns {ProjectNameAndPath[]|undefined} + * @ returns {ProjectNameAndPath[]|undefined} */ - static getMonoRepoPackages(rootPath) { + static async getMonoRepoPackages(rootPath) { // [1] Look for npm/yarn workspaces const pkgJson = getPackageJson(rootPath); if (pkgJson?.workspaces) { @@ -641,7 +608,7 @@ export class InputDataService { * @param {string} opts.packageRootPath * @returns {Promise<{internalExportMapPaths:string[]; exposedExportMapPaths:string[]}>} */ - static getPathsFromExportMap(exports, { nodeResolveMode = 'default', packageRootPath }) { + static async getPathsFromExportMap(exports, { nodeResolveMode = 'default', packageRootPath }) { const exportMapPaths = []; for (const [key, valObjOrStr] of Object.entries(exports)) { @@ -672,25 +639,24 @@ export class InputDataService { const valueToUseForGlob = stripDotSlashFromLocalPath(resolvedVal).replace('*', '**/*'); // Generate all possible entries via glob, first strip './' - const internalExportMapPathsForKeyRaw = glob.sync(valueToUseForGlob, { + const internalExportMapPathsForKeyRaw = await optimisedGlob(valueToUseForGlob, { cwd: packageRootPath, - nodir: true, + onlyFiles: true, }); const exposedExportMapPathsForKeyRaw = internalExportMapPathsForKeyRaw.map(pathInside => { // Say we have "exports": { "./*.js": "./src/*.js" } // => internalExportMapPathsForKey: ['./src/a.js', './src/b.js'] // => exposedExportMapPathsForKey: ['./a.js', './b.js'] - const [, variablePart] = pathInside.match( - new RegExp(valueToUseForGlob.replace('*', '(.*)')), - ); + const [, variablePart] = + pathInside.match(new RegExp(valueToUseForGlob.replace('*', '(.*)'))) || []; return resolvedKey.replace('*', variablePart); }); - const internalExportMapPathsForKey = internalExportMapPathsForKeyRaw.map(filePath => - normalizeLocalPathWithDotSlash(filePath), + const internalExportMapPathsForKey = internalExportMapPathsForKeyRaw.map( + normalizeLocalPathWithDotSlash, ); - const exposedExportMapPathsForKey = exposedExportMapPathsForKeyRaw.map(filePath => - normalizeLocalPathWithDotSlash(filePath), + const exposedExportMapPathsForKey = exposedExportMapPathsForKeyRaw.map( + normalizeLocalPathWithDotSlash, ); exportMapPaths.push( @@ -704,9 +670,6 @@ export class InputDataService { return exportMapPaths; } } -// TODO: Remove memoizeConfig.isCacheDisabled this once whole providence uses cacheConfig instead of -// memoizeConfig.isCacheDisabled -// InputDataService.cacheDisabled = memoizeConfig.isCacheDisabled; InputDataService.getProjectMeta = memoize(InputDataService.getProjectMeta); InputDataService.gatherFilesFromDir = memoize(InputDataService.gatherFilesFromDir); diff --git a/packages-node/providence-analytics/src/program/core/LogService.js b/packages-node/providence-analytics/src/program/core/LogService.js index 618994120..6c924555a 100644 --- a/packages-node/providence-analytics/src/program/core/LogService.js +++ b/packages-node/providence-analytics/src/program/core/LogService.js @@ -1,5 +1,5 @@ -import pathLib from 'path'; -import fs from 'fs'; +import path from 'path'; +import { fsAdapter } from '../utils/fs-adapter.js'; const { log } = console; @@ -111,14 +111,14 @@ export class LogService { } static writeLogFile() { - const filePath = pathLib.join(process.cwd(), 'providence.log'); + const filePath = path.join(process.cwd(), 'providence.log'); let file = `[log ${new Date()}]\n`; // @ts-ignore this._logHistory.forEach(l => { file += `${l}\n`; }); file += `[/log ${new Date()}]\n\n`; - fs.writeFileSync(filePath, file, { flag: 'a' }); + fsAdapter.fs.writeFileSync(filePath, file, { flag: 'a' }); // @ts-ignore this._logHistory = []; } diff --git a/packages-node/providence-analytics/src/program/core/ReportService.js b/packages-node/providence-analytics/src/program/core/ReportService.js index 19c0b80dc..796cd133b 100644 --- a/packages-node/providence-analytics/src/program/core/ReportService.js +++ b/packages-node/providence-analytics/src/program/core/ReportService.js @@ -1,17 +1,18 @@ -import fs from 'fs'; -import pathLib from 'path'; +import path from 'path'; import { getHash } from '../utils/get-hash.js'; -// import { memoize } from '../utils/memoize.js'; -const memoize = fn => fn; +import { fsAdapter } from '../utils/fs-adapter.js'; + +import { memoize } from '../utils/memoize.js'; +// const memoize = fn => fn; /** - * @typedef {import('../../../types/index.js').Project} Project - * @typedef {import('../../../types/index.js').ProjectName} ProjectName * @typedef {import('../../../types/index.js').AnalyzerQueryResult} AnalyzerQueryResult + * @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot * @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig * @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName + * @typedef {import('../../../types/index.js').ProjectName} ProjectName * @typedef {import('../../../types/index.js').QueryResult} QueryResult - * @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot + * @typedef {import('../../../types/index.js').Project} Project */ /** @@ -57,20 +58,20 @@ export class ReportService { outputPath = this.outputPath, ) { const output = JSON.stringify(queryResult, null, 2); - if (!fs.existsSync(outputPath)) { - fs.mkdirSync(outputPath); + if (!fsAdapter.fs.existsSync(outputPath)) { + fsAdapter.fs.mkdirSync(outputPath); } const { name } = queryResult.meta.analyzerMeta; const filePath = this._getResultFileNameAndPath(name, identifier); - fs.writeFileSync(filePath, output, { flag: 'w' }); + fsAdapter.fs.writeFileSync(filePath, output, { flag: 'w' }); } /** * @type {string} */ static get outputPath() { - return this.__outputPath || pathLib.join(process.cwd(), '/providence-output'); + return this.__outputPath || path.join(process.cwd(), '/providence-output'); } static set outputPath(p) { @@ -93,7 +94,10 @@ export class ReportService { let cachedResult; try { cachedResult = JSON.parse( - fs.readFileSync(this._getResultFileNameAndPath(analyzerName, identifier), 'utf-8'), + fsAdapter.fs.readFileSync( + this._getResultFileNameAndPath(analyzerName, identifier), + 'utf-8', + ), ); // eslint-disable-next-line no-empty } catch (_) {} @@ -107,7 +111,7 @@ export class ReportService { */ static _getResultFileNameAndPath(name, identifier) { return /** @type {PathFromSystemRoot} */ ( - pathLib.join(this.outputPath, `${name || 'query'}_-_${identifier}.json`) + path.join(this.outputPath, `${name || 'query'}_-_${identifier}.json`) ); } @@ -117,15 +121,15 @@ export class ReportService { */ static writeEntryToSearchTargetDepsFile(depProj, rootProjectMeta) { const rootProj = `${rootProjectMeta.name}#${rootProjectMeta.version}`; - const filePath = pathLib.join(this.outputPath, 'search-target-deps-file.json'); + const filePath = path.join(this.outputPath, 'search-target-deps-file.json'); let file = {}; try { - file = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + file = JSON.parse(fsAdapter.fs.readFileSync(filePath, 'utf-8')); // eslint-disable-next-line no-empty } catch (_) {} const deps = [...(file[rootProj] || []), depProj]; file[rootProj] = [...new Set(deps)]; - fs.writeFileSync(filePath, JSON.stringify(file, null, 2), { flag: 'w' }); + fsAdapter.fs.writeFileSync(filePath, JSON.stringify(file, null, 2), { flag: 'w' }); } } ReportService.createIdentifier = memoize(ReportService.createIdentifier); diff --git a/packages-node/providence-analytics/src/program/providence.js b/packages-node/providence-analytics/src/program/providence.js index 272e530cc..971a836b3 100644 --- a/packages-node/providence-analytics/src/program/providence.js +++ b/packages-node/providence-analytics/src/program/providence.js @@ -1,8 +1,11 @@ import { performance } from 'perf_hooks'; -import { ReportService } from './core/ReportService.js'; +import nodeFs from 'fs'; + import { InputDataService } from './core/InputDataService.js'; -import { LogService } from './core/LogService.js'; +import { ReportService } from './core/ReportService.js'; import { QueryService } from './core/QueryService.js'; +import { fsAdapter } from './utils/fs-adapter.js'; +import { LogService } from './core/LogService.js'; import { AstService } from './core/AstService.js'; /** @@ -196,9 +199,15 @@ export async function providence(queryConfig, customConfig) { /** Allows to navigate to source file in code editor */ addSystemPathsInResult: false, fallbackToBabel: false, + fs: nodeFs, ...customConfig, }); + if (cfg.fs) { + // Allow to mock fs for testing + fsAdapter.setFs(cfg.fs); + } + if (cfg.debugEnabled) { LogService.debugEnabled = true; } @@ -215,7 +224,7 @@ export async function providence(queryConfig, customConfig) { if (queryConfig.type === 'ast-analyzer') { queryResults = await handleAnalyzer(queryConfig, cfg); } else { - const inputData = InputDataService.createDataObject( + const inputData = await InputDataService.createDataObject( cfg.targetProjectPaths, cfg.gatherFilesConfig, ); diff --git a/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration--legacy.js b/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration--legacy.js index d11e76374..31a446d87 100644 --- a/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration--legacy.js +++ b/packages-node/providence-analytics/src/program/utils/get-source-code-fragment-of-declaration--legacy.js @@ -1,9 +1,9 @@ -import fs from 'fs'; import path from 'path'; import babelTraversePkg from '@babel/traverse'; import { AstService } from '../core/AstService.js'; import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js'; import { toPosixPath } from './to-posix-path.js'; +import { fsAdapter } from './fs-adapter.js'; /** * @typedef {import('@babel/types').Node} Node @@ -82,7 +82,7 @@ export async function getSourceCodeFragmentOfDeclaration({ exportedIdentifier, projectRootPath, }) { - const code = fs.readFileSync(filePath, 'utf8'); + const code = fsAdapter.fs.readFileSync(filePath, 'utf8'); // TODO: fix swc-to-babel lib to make this compatible with 'swc-to-babel' mode of getAst const babelAst = AstService.getAst(code, 'babel', { filePath }); 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 d3f3c24ca..05a48cae0 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 @@ -1,9 +1,9 @@ -import fs from 'fs'; import path from 'path'; import { swcTraverse, getPathFromNode } from './swc-traverse.js'; import { AstService } from '../core/AstService.js'; import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js'; import { toPosixPath } from './to-posix-path.js'; +import { fsAdapter } from './fs-adapter.js'; /** * @typedef {import('@swc/core').Node} SwcNode @@ -84,7 +84,7 @@ export async function getSourceCodeFragmentOfDeclaration({ exportedIdentifier, projectRootPath, }) { - const code = fs.readFileSync(filePath, 'utf8'); + const code = fsAdapter.fs.readFileSync(filePath, 'utf8'); // compensate for swc span bug: https://github.com/swc-project/swc/issues/1366#issuecomment-1516539812 const offset = AstService._getSwcOffset(); diff --git a/packages-node/providence-analytics/src/program/utils/providence-conf-util.js b/packages-node/providence-analytics/src/program/utils/providence-conf-util.js index 6746e4315..c1121b0ae 100644 --- a/packages-node/providence-analytics/src/program/utils/providence-conf-util.js +++ b/packages-node/providence-analytics/src/program/utils/providence-conf-util.js @@ -1,6 +1,6 @@ -import pathLib from 'path'; -import fs from 'fs'; +import path from 'path'; import { pathToFileURL } from 'url'; +import { fsAdapter } from './fs-adapter.js'; /** * @typedef {import('../../../types/index.js').ProvidenceCliConf} ProvidenceCliConf @@ -10,12 +10,12 @@ import { pathToFileURL } from 'url'; * @returns {Promise<{providenceConf:Partial;providenceConfRaw:string}|null>} */ async function getConf() { - const confPathWithoutExtension = `${pathLib.join(process.cwd(), 'providence.conf')}`; + const confPathWithoutExtension = `${path.join(process.cwd(), 'providence.conf')}`; let confPathFound; try { - if (fs.existsSync(`${confPathWithoutExtension}.js`)) { + if (fsAdapter.fs.existsSync(`${confPathWithoutExtension}.js`)) { confPathFound = `${confPathWithoutExtension}.js`; - } else if (fs.existsSync(`${confPathWithoutExtension}.mjs`)) { + } else if (fsAdapter.fs.existsSync(`${confPathWithoutExtension}.mjs`)) { confPathFound = `${confPathWithoutExtension}.mjs`; } } catch (_) { @@ -36,7 +36,7 @@ async function getConf() { ); } - const providenceConfRaw = fs.readFileSync(confPathFound, 'utf8'); + const providenceConfRaw = fsAdapter.fs.readFileSync(confPathFound, 'utf8'); return { providenceConf, providenceConfRaw }; } diff --git a/packages-node/providence-analytics/src/program/utils/read-package-tree-with-bower-support.js b/packages-node/providence-analytics/src/program/utils/read-package-tree-with-bower-support.js deleted file mode 100644 index 57bcf3ecd..000000000 --- a/packages-node/providence-analytics/src/program/utils/read-package-tree-with-bower-support.js +++ /dev/null @@ -1,227 +0,0 @@ -// @ts-nocheck -/* eslint-disable */ -/** - * This is a modified version of https://github.com/npm/read-package-tree/blob/master/rpt.js - * The original is meant for npm dependencies only. In our (rare) case, we have a hybrid landscape - * where we also want to look for npm dependencies inside bower dependencies (bower_components folder). - * - * Original: https://github.com/npm/read-package-tree - * - * The ISC License - * - * Copyright (c) Isaac Z. Schlueter and Contributors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -import fs from 'fs'; -/* istanbul ignore next */ -import { promisify } from 'util'; -import { basename, dirname, join } from 'path'; -import rpjSync from 'read-package-json'; -import readdirSync from 'readdir-scoped-modules'; -import realpath from 'read-package-tree/realpath.js'; - -const rpj = promisify(rpjSync); -const readdir = promisify(readdirSync); - -let ID = 0; -class Node { - constructor(pkg, logical, physical, er, cache) { - // should be impossible. - const cached = cache.get(physical); - /* istanbul ignore next */ - if (cached && !cached.then) throw new Error('re-creating already instantiated node'); - - cache.set(physical, this); - - const parent = basename(dirname(logical)); - if (parent.charAt(0) === '@') this.name = `${parent}/${basename(logical)}`; - else this.name = basename(logical); - this.path = logical; - this.realpath = physical; - this.error = er; - this.id = ID++; - this.package = pkg || {}; - this.parent = null; - this.isLink = false; - this.children = []; - } -} - -class Link extends Node { - constructor(pkg, logical, physical, realpath, er, cache) { - super(pkg, logical, physical, er, cache); - - // if the target has started, but not completed, then - // a Promise will be in the cache to indicate this. - const cachedTarget = cache.get(realpath); - if (cachedTarget && cachedTarget.then) - cachedTarget.then(node => { - this.target = node; - this.children = node.children; - }); - - this.target = cachedTarget || new Node(pkg, logical, realpath, er, cache); - this.realpath = realpath; - this.isLink = true; - this.error = er; - this.children = this.target.children; - } -} - -// this is the way it is to expose a timing issue which is difficult to -// test otherwise. The creation of a Node may take slightly longer than -// the creation of a Link that targets it. If the Node has _begun_ its -// creation phase (and put a Promise in the cache) then the Link will -// get a Promise as its cachedTarget instead of an actual Node object. -// This is not a problem, because it gets resolved prior to returning -// the tree or attempting to load children. However, it IS remarkably -// difficult to get to happen in a test environment to verify reliably. -// Hence this kludge. -const newNode = (pkg, logical, physical, er, cache) => - process.env._TEST_RPT_SLOW_LINK_TARGET_ === '1' - ? new Promise(res => setTimeout(() => res(new Node(pkg, logical, physical, er, cache)), 10)) - : new Node(pkg, logical, physical, er, cache); - -const loadNode = (logical, physical, cache, rpcache, stcache) => { - // cache temporarily holds a promise placeholder so we - // don't try to create the same node multiple times. - // this is very rare to encounter, given the aggressive - // caching on fs.realpath and fs.lstat calls, but - // it can happen in theory. - const cached = cache.get(physical); - /* istanbul ignore next */ - if (cached) return Promise.resolve(cached); - - const p = realpath(physical, rpcache, stcache, 0).then( - real => - rpj(join(real, 'package.json')) - .then( - pkg => [pkg, null], - er => [null, er], - ) - .then(([pkg, er]) => - physical === real - ? newNode(pkg, logical, physical, er, cache) - : new Link(pkg, logical, physical, real, er, cache), - ), - // if the realpath fails, don't bother with the rest - er => new Node(null, logical, physical, er, cache), - ); - - cache.set(physical, p); - return p; -}; - -const loadChildren = (node, cache, filterWith, rpcache, stcache, mode) => { - // if a Link target has started, but not completed, then - // a Promise will be in the cache to indicate this. - // - // XXX When we can one day loadChildren on the link *target* instead of - // the link itself, to match real dep resolution, then we may end up with - // a node target in the cache that isn't yet done resolving when we get - // here. For now, though, this line will never be reached, so it's hidden - // - // if (node.then) - // return node.then(node => loadChildren(node, cache, filterWith, rpcache, stcache)) - - let depFolder = 'node_modules'; - if (mode === 'bower') { - // TODO: if people rename their bower_components folder to smth like "lib", please handle - depFolder = 'bower_components'; - try { - const bowerrc = JSON.parse(fs.readFileSync(join(node.path, '.bowerrc'))); - if (bowerrc && bowerrc.directory) { - depFolder = bowerrc.directory; - } - } catch (_) {} - } - const nm = join(node.path, depFolder); - // const nm = join(node.path, 'bower_components') - return realpath(nm, rpcache, stcache, 0) - .then(rm => readdir(rm).then(kids => [rm, kids])) - .then(([rm, kids]) => - Promise.all( - kids - .filter(kid => kid.charAt(0) !== '.' && (!filterWith || filterWith(node, kid))) - .map(kid => loadNode(join(nm, kid), join(rm, kid), cache, rpcache, stcache)), - ), - ) - .then(kidNodes => { - kidNodes.forEach(k => (k.parent = node)); - node.children.push.apply( - node.children, - kidNodes.sort((a, b) => - (a.package.name ? a.package.name.toLowerCase() : a.path).localeCompare( - b.package.name ? b.package.name.toLowerCase() : b.path, - ), - ), - ); - return node; - }) - .catch(() => node); -}; - -const loadTree = (node, did, cache, filterWith, rpcache, stcache, mode) => { - // impossible except in pathological ELOOP cases - /* istanbul ignore next */ - if (did.has(node.realpath)) return Promise.resolve(node); - - did.add(node.realpath); - - // load children on the target, not the link - return loadChildren(node, cache, filterWith, rpcache, stcache, mode) - .then(node => - Promise.all( - node.children - .filter(kid => !did.has(kid.realpath)) - .map(kid => loadTree(kid, did, cache, filterWith, rpcache, stcache, mode)), - ), - ) - .then(() => node); -}; - -// XXX Drop filterWith and/or cb in next semver major bump -/** - * - * @param {*} root - * @param {*} filterWith - * @param {*} cb - * @param {'npm'|'bower'} [mode='npm'] if mode is 'bower', will look in 'bower_components' instead - * of 'node_modules' - */ -const rpt = (root, filterWith, cb, mode = 'npm') => { - if (!cb && typeof filterWith === 'function') { - cb = filterWith; - filterWith = null; - } - - const cache = new Map(); - // we can assume that the cwd is real enough - const cwd = process.cwd(); - const rpcache = new Map([[cwd, cwd]]); - const stcache = new Map(); - const p = realpath(root, rpcache, stcache, 0) - .then(realRoot => loadNode(root, realRoot, cache, rpcache, stcache)) - .then(node => loadTree(node, new Set(), cache, filterWith, rpcache, stcache, mode)); - - if (typeof cb === 'function') p.then(tree => cb(null, tree), cb); - - return p; -}; - -rpt.Node = Node; -rpt.Link = Link; - -export default rpt; 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 266e02817..11f5e755f 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,10 +1,12 @@ import { builtinModules } from 'module'; import path from 'path'; + import { nodeResolve } from '@rollup/plugin-node-resolve'; -import { LogService } from '../core/LogService.js'; -import { memoize } from './memoize.js'; -import { toPosixPath } from './to-posix-path.js'; + import { isRelativeSourcePath } from './relative-source-path.js'; +import { LogService } from '../core/LogService.js'; +import { toPosixPath } from './to-posix-path.js'; +import { memoize } from './memoize.js'; /** * @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot diff --git a/packages-node/providence-analytics/test-helpers/setup-analyzer-test.js b/packages-node/providence-analytics/test-helpers/setup-analyzer-test.js index d7edf30cc..b14eb9d4b 100644 --- a/packages-node/providence-analytics/test-helpers/setup-analyzer-test.js +++ b/packages-node/providence-analytics/test-helpers/setup-analyzer-test.js @@ -5,7 +5,7 @@ import { suppressNonCriticalLogs, restoreSuppressNonCriticalLogs, } from './mock-log-service-helpers.js'; -import { memoizeConfig } from '../src/program/utils/memoize.js'; +import { memoize } from '../src/program/utils/memoize.js'; /** * @typedef {import('../types/index.js').QueryResult} QueryResult @@ -20,17 +20,17 @@ export function setupAnalyzerTest() { const originalReferenceProjectPaths = InputDataService.referenceProjectPaths; const cacheDisabledQInitialValue = QueryService.cacheDisabled; - const cacheDisabledIInitialValue = memoizeConfig.isCacheDisabled; + const cacheEnabledIInitialValue = memoize.isCacheEnabled; before(() => { QueryService.cacheDisabled = true; - memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); suppressNonCriticalLogs(); }); after(() => { QueryService.cacheDisabled = cacheDisabledQInitialValue; - memoizeConfig.isCacheDisabled = cacheDisabledIInitialValue; + memoize.restoreCaching(cacheEnabledIInitialValue); restoreSuppressNonCriticalLogs(); }); diff --git a/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js b/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js index 4dce2d150..01658e027 100644 --- a/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js +++ b/packages-node/providence-analytics/test-helpers/templates/DummyAnalyzer.js @@ -61,7 +61,7 @@ export class DummyAnalyzer extends Analyzer { /** * Prepare */ - const analyzerResult = this._prepare(cfg); + const analyzerResult = await this._prepare(cfg); if (analyzerResult) { return analyzerResult; } diff --git a/packages-node/providence-analytics/test-node/cli/cli-helpers.test.js b/packages-node/providence-analytics/test-node/cli/cli-helpers.test.js index 8015d9528..de337796e 100644 --- a/packages-node/providence-analytics/test-node/cli/cli-helpers.test.js +++ b/packages-node/providence-analytics/test-node/cli/cli-helpers.test.js @@ -1,22 +1,24 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable import/no-extraneous-dependencies */ -import sinon from 'sinon'; -import pathLib from 'path'; import { fileURLToPath } from 'url'; +import pathLib from 'path'; + import { expect } from 'chai'; +import sinon from 'sinon'; import { it } from 'mocha'; -import { - mockProject, - restoreMockedProjects, - mockTargetAndReferenceProject, -} from '../../test-helpers/mock-project-helpers.js'; -import { _providenceModule } from '../../src/program/providence.js'; -import { _cliHelpersModule } from '../../src/cli/cli-helpers.js'; -import { toPosixPath } from '../../src/program/utils/to-posix-path.js'; -import { memoizeConfig } from '../../src/program/utils/memoize.js'; + import { getExtendDocsResults } from '../../src/cli/launch-providence-with-extend-docs.js'; -import { AstService } from '../../src/index.js'; import { setupAnalyzerTest } from '../../test-helpers/setup-analyzer-test.js'; +import { toPosixPath } from '../../src/program/utils/to-posix-path.js'; +import { _providenceModule } from '../../src/program/providence.js'; +import { memoize } from '../../src/program/utils/memoize.js'; +import { _cliHelpersModule } from '../../src/cli/cli-helpers.js'; +import { + mockTargetAndReferenceProject, + restoreMockedProjects, + mockProject, +} from '../../test-helpers/mock-project-helpers.js'; +import { AstService } from '../../src/index.js'; /** * @typedef {import('../../types/index.js').QueryResult} QueryResult @@ -50,7 +52,7 @@ describe('CLI helpers', () => { describe('pathsArrayFromCs', () => { it('allows absolute paths', async () => { - expect(pathsArrayFromCs('/mocked/path/example-project', rootDir)).to.eql([ + expect(pathsArrayFromCs('/mocked/path/example-project', rootDir)).to.deep.equal([ '/mocked/path/example-project', ]); }); @@ -58,14 +60,14 @@ describe('CLI helpers', () => { it('allows relative paths', async () => { expect( pathsArrayFromCs('./test-helpers/project-mocks/importing-target-project', rootDir), - ).to.eql([`${rootDir}/test-helpers/project-mocks/importing-target-project`]); + ).to.deep.equal([`${rootDir}/test-helpers/project-mocks/importing-target-project`]); expect( pathsArrayFromCs('test-helpers/project-mocks/importing-target-project', rootDir), - ).to.eql([`${rootDir}/test-helpers/project-mocks/importing-target-project`]); + ).to.deep.equal([`${rootDir}/test-helpers/project-mocks/importing-target-project`]); }); it('allows globs', async () => { - expect(pathsArrayFromCs('test-helpers/project-mocks*', rootDir)).to.eql([ + expect(pathsArrayFromCs('test-helpers/project-mocks*', rootDir)).to.deep.equal([ `${rootDir}/test-helpers/project-mocks`, `${rootDir}/test-helpers/project-mocks-analyzer-outputs`, ]); @@ -74,7 +76,7 @@ describe('CLI helpers', () => { it('allows multiple comma separated paths', async () => { const paths = 'test-helpers/project-mocks*, ./test-helpers/project-mocks/importing-target-project,/mocked/path/example-project'; - expect(pathsArrayFromCs(paths, rootDir)).to.eql([ + expect(pathsArrayFromCs(paths, rootDir)).to.deep.equal([ `${rootDir}/test-helpers/project-mocks`, `${rootDir}/test-helpers/project-mocks-analyzer-outputs`, `${rootDir}/test-helpers/project-mocks/importing-target-project`, @@ -87,7 +89,7 @@ describe('CLI helpers', () => { it('gets collections from external target config', async () => { expect( pathsArrayFromCollectionName('lion-collection', 'search-target', externalCfgMock, rootDir), - ).to.eql( + ).to.deep.equal( externalCfgMock.searchTargetCollections['lion-collection'].map(p => toPosixPath(pathLib.join(rootDir, p)), ), @@ -102,7 +104,7 @@ describe('CLI helpers', () => { externalCfgMock, rootDir, ), - ).to.eql( + ).to.deep.equal( externalCfgMock.referenceCollections['lion-based-ui-collection'].map(p => toPosixPath(pathLib.join(rootDir, p)), ), @@ -130,7 +132,7 @@ describe('CLI helpers', () => { it('adds bower and node dependencies', async () => { const result = await appendProjectDependencyPaths(['/mocked/path/example-project']); - expect(result).to.eql([ + expect(result).to.deep.equal([ '/mocked/path/example-project/node_modules/dependency-a', '/mocked/path/example-project/node_modules/my-dependency', '/mocked/path/example-project/bower_components/dependency-b', @@ -143,7 +145,7 @@ describe('CLI helpers', () => { ['/mocked/path/example-project'], '/^dependency-/', ); - expect(result).to.eql([ + expect(result).to.deep.equal([ '/mocked/path/example-project/node_modules/dependency-a', // in windows, it should not add '/mocked/path/example-project/node_modules/my-dependency', '/mocked/path/example-project/bower_components/dependency-b', @@ -151,7 +153,7 @@ describe('CLI helpers', () => { ]); const result2 = await appendProjectDependencyPaths(['/mocked/path/example-project'], '/b$/'); - expect(result2).to.eql([ + expect(result2).to.deep.equal([ '/mocked/path/example-project/bower_components/dependency-b', '/mocked/path/example-project', ]); @@ -163,7 +165,7 @@ describe('CLI helpers', () => { undefined, ['npm'], ); - expect(result).to.eql([ + expect(result).to.deep.equal([ '/mocked/path/example-project/node_modules/dependency-a', '/mocked/path/example-project/node_modules/my-dependency', '/mocked/path/example-project', @@ -174,7 +176,7 @@ describe('CLI helpers', () => { undefined, ['bower'], ); - expect(result2).to.eql([ + expect(result2).to.deep.equal([ '/mocked/path/example-project/bower_components/dependency-b', '/mocked/path/example-project', ]); @@ -189,7 +191,7 @@ describe('CLI helpers', () => { it('rewrites monorepo package paths when analysis is run from monorepo root', async () => { // This fails after InputDataService.addAstToProjectsData is memoized // (it does pass when run in isolation however, as a quick fix we disable memoization cache here...) - memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); // Since we use the print method here, we need to force Babel, bc swc-to-babel output is not compatible // with @babel/generate const initialAstServiceFallbackToBabel = AstService.fallbackToBabel; @@ -268,7 +270,7 @@ describe('CLI helpers', () => { cwd: '/my-components', }); - expect(result).to.eql([ + expect(result).to.deep.equal([ { name: 'TheirButton', variable: { diff --git a/packages-node/providence-analytics/test-node/cli/cli.e2e.js b/packages-node/providence-analytics/test-node/cli/cli.e2e.js index e74d53ba7..917b7a09e 100644 --- a/packages-node/providence-analytics/test-node/cli/cli.e2e.js +++ b/packages-node/providence-analytics/test-node/cli/cli.e2e.js @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import pathLib from 'path'; +import path from 'path'; import { expect } from 'chai'; import { it } from 'mocha'; import { appendProjectDependencyPaths } from '../../src/cli/cli-helpers.js'; @@ -15,13 +15,13 @@ describe('CLI helpers against filesystem', () => { describe('appendProjectDependencyPaths', () => { it('allows a regex filter', async () => { const targetFilePath = toPosixPath( - pathLib.resolve( + path.resolve( getCurrentDir(import.meta.url), '../../test-helpers/project-mocks/importing-target-project', ), ); const result = await appendProjectDependencyPaths([targetFilePath], '/^dep-/'); - expect(result).to.eql([ + expect(result).to.deep.equal([ `${targetFilePath}/node_modules/dep-a`, // in windows, it should not add `${targetFilePath}/node_modules/my-dep-b`, targetFilePath, diff --git a/packages-node/providence-analytics/test-node/cli/cli.test.js b/packages-node/providence-analytics/test-node/cli/cli.test.js index c33173680..15eb9058c 100644 --- a/packages-node/providence-analytics/test-node/cli/cli.test.js +++ b/packages-node/providence-analytics/test-node/cli/cli.test.js @@ -11,7 +11,7 @@ import { _providenceModule } from '../../src/program/providence.js'; import { _cliHelpersModule } from '../../src/cli/cli-helpers.js'; import { cli } from '../../src/cli/cli.js'; import { _promptAnalyzerMenuModule } from '../../src/cli/prompt-analyzer-menu.js'; -import { memoizeConfig } from '../../src/program/utils/memoize.js'; +import { memoize } from '../../src/program/utils/memoize.js'; import { _extendDocsModule } from '../../src/cli/launch-providence-with-extend-docs.js'; import { dashboardServer } from '../../src/dashboard/server.js'; import { setupAnalyzerTest } from '../../test-helpers/setup-analyzer-test.js'; @@ -120,7 +120,8 @@ describe('Providence CLI', () => { projectPath: '/mocked/path/example-project', }, ); - memoizeConfig.isCacheDisabled = true; + // memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); }); afterEach(() => { @@ -186,18 +187,26 @@ describe('Providence CLI', () => { it('"-e --extensions"', async () => { await runCli(`${anyCmdThatAcceptsGlobalOpts} -e bla,blu`, rootDir); - expect(providenceStub.args[0][1].gatherFilesConfig.extensions).to.eql(['.bla', '.blu']); + expect(providenceStub.args[0][1].gatherFilesConfig.extensions).to.deep.equal([ + '.bla', + '.blu', + ]); providenceStub.resetHistory(); await runCli(`${anyCmdThatAcceptsGlobalOpts} --extensions bla,blu`, rootDir); - expect(providenceStub.args[0][1].gatherFilesConfig.extensions).to.eql(['.bla', '.blu']); + expect(providenceStub.args[0][1].gatherFilesConfig.extensions).to.deep.equal([ + '.bla', + '.blu', + ]); }); it('"-t --search-target-paths"', async () => { await runCli(`${anyCmdThatAcceptsGlobalOpts} -t /mocked/path/example-project`, rootDir); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); - expect(providenceStub.args[0][1].targetProjectPaths).to.eql(['/mocked/path/example-project']); + expect(providenceStub.args[0][1].targetProjectPaths).to.deep.equal([ + '/mocked/path/example-project', + ]); pathsArrayFromCsStub.resetHistory(); providenceStub.resetHistory(); @@ -207,13 +216,15 @@ describe('Providence CLI', () => { rootDir, ); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); - expect(providenceStub.args[0][1].targetProjectPaths).to.eql(['/mocked/path/example-project']); + expect(providenceStub.args[0][1].targetProjectPaths).to.deep.equal([ + '/mocked/path/example-project', + ]); }); it('"-r --reference-paths"', async () => { await runCli(`${anyCmdThatAcceptsGlobalOpts} -r /mocked/path/example-project`, rootDir); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); - expect(providenceStub.args[0][1].referenceProjectPaths).to.eql([ + expect(providenceStub.args[0][1].referenceProjectPaths).to.deep.equal([ '/mocked/path/example-project', ]); @@ -225,7 +236,7 @@ describe('Providence CLI', () => { rootDir, ); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); - expect(providenceStub.args[0][1].referenceProjectPaths).to.eql([ + expect(providenceStub.args[0][1].referenceProjectPaths).to.deep.equal([ '/mocked/path/example-project', ]); }); @@ -236,7 +247,9 @@ describe('Providence CLI', () => { rootDir, ); expect(pathsArrayFromCollectionStub.args[0][0]).to.equal('lion-collection'); - expect(providenceStub.args[0][1].targetProjectPaths).to.eql(['/mocked/path/example-project']); + expect(providenceStub.args[0][1].targetProjectPaths).to.deep.equal([ + '/mocked/path/example-project', + ]); }); it('"--reference-collection"', async () => { @@ -245,14 +258,14 @@ describe('Providence CLI', () => { rootDir, ); expect(pathsArrayFromCollectionStub.args[0][0]).to.equal('lion-based-ui-collection'); - expect(providenceStub.args[0][1].referenceProjectPaths).to.eql([ + expect(providenceStub.args[0][1].referenceProjectPaths).to.deep.equal([ '/mocked/path/example-project', ]); }); it('"-a --allowlist"', async () => { await runCli(`${anyCmdThatAcceptsGlobalOpts} -a mocked/**/*,rocked/*`, rootDir); - expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.eql([ + expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.deep.equal([ 'mocked/**/*', 'rocked/*', ]); @@ -260,7 +273,7 @@ describe('Providence CLI', () => { providenceStub.resetHistory(); await runCli(`${anyCmdThatAcceptsGlobalOpts} --allowlist mocked/**/*,rocked/*`, rootDir); - expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.eql([ + expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.deep.equal([ 'mocked/**/*', 'rocked/*', ]); @@ -271,7 +284,7 @@ describe('Providence CLI', () => { `${anyCmdThatAcceptsGlobalOpts} --allowlist-reference mocked/**/*,rocked/*`, rootDir, ); - expect(providenceStub.args[0][1].gatherFilesConfigReference.allowlist).to.eql([ + expect(providenceStub.args[0][1].gatherFilesConfigReference.allowlist).to.deep.equal([ 'mocked/**/*', 'rocked/*', ]); @@ -311,7 +324,7 @@ describe('Providence CLI', () => { await runCli(`${anyCmdThatAcceptsGlobalOpts} --target-dependencies`, rootDir); expect(appendProjectDependencyPathsStub.called).to.be.true; - expect(providenceStub.args[0][1].targetProjectPaths).to.eql([ + expect(providenceStub.args[0][1].targetProjectPaths).to.deep.equal([ '/mocked/path/example-project', '/mocked/path/example-project/node_modules/mock-dep-a', '/mocked/path/example-project/bower_components/mock-dep-b', @@ -355,13 +368,13 @@ describe('Providence CLI', () => { it('"-c --config"', async () => { await runCli(`analyze match-analyzer-mock -c {"a":"2"}`, rootDir); expect(qConfStub.args[0][0]).to.equal('match-analyzer-mock'); - expect(qConfStub.args[0][1]).to.eql({ a: '2', metaConfig: {} }); + expect(qConfStub.args[0][1]).to.deep.equal({ a: '2', metaConfig: {} }); qConfStub.resetHistory(); await runCli(`analyze match-analyzer-mock --config {"a":"2"}`, rootDir); expect(qConfStub.args[0][0]).to.equal('match-analyzer-mock'); - expect(qConfStub.args[0][1]).to.eql({ a: '2', metaConfig: {} }); + expect(qConfStub.args[0][1]).to.deep.equal({ a: '2', metaConfig: {} }); }); it('calls "promptAnalyzerConfigMenu" without config given', async () => { @@ -417,7 +430,7 @@ describe('Providence CLI', () => { rootDir, ); expect(extendDocsStub.called).to.be.true; - expect(extendDocsStub.args[0][0]).to.eql({ + expect(extendDocsStub.args[0][0]).to.deep.equal({ referenceProjectPaths: ['/xyz/x'], prefixCfg: { from: 'pfrom', diff --git a/packages-node/providence-analytics/test-node/dashboard/dashboard-server.test.js b/packages-node/providence-analytics/test-node/dashboard/dashboard-server.test.js index fb495f0be..74cb19821 100644 --- a/packages-node/providence-analytics/test-node/dashboard/dashboard-server.test.js +++ b/packages-node/providence-analytics/test-node/dashboard/dashboard-server.test.js @@ -1,15 +1,16 @@ /* eslint-disable import/no-extraneous-dependencies */ -import fs from 'fs'; +import { fileURLToPath, pathToFileURL } from 'url'; import pathLib from 'path'; import sinon from 'sinon'; -import { fileURLToPath, pathToFileURL } from 'url'; + +import { createTestServer } from '@web/dev-server-core/test-helpers'; import { expect } from 'chai'; import { it } from 'mocha'; -import fetch from 'node-fetch'; -import { createTestServer } from '@web/dev-server-core/test-helpers'; + +import { providenceConfUtil } from '../../src/program/utils/providence-conf-util.js'; import { createDashboardServerConfig } from '../../src/dashboard/server.js'; import { ReportService } from '../../src/program/core/ReportService.js'; -import { providenceConfUtil } from '../../src/program/utils/providence-conf-util.js'; +import { fsAdapter } from '../../src/program/utils/fs-adapter.js'; /** * @typedef {import('@web/dev-server-core').DevServer} DevServer @@ -27,7 +28,7 @@ const mockedOutputPath = pathLib.join(__dirname, 'fixtures/providence-output'); async function getConf(url) { const { href } = pathToFileURL(url); const { default: providenceConf } = await import(href); - const providenceConfRaw = fs.readFileSync(url, 'utf8'); + const providenceConfRaw = fsAdapter.fs.readFileSync(url, 'utf8'); return { providenceConf, providenceConfRaw }; } @@ -40,7 +41,7 @@ describe('Dashboard Server', () => { let providenceConfStub; before(() => { - // N.B. don't use mock-fs, since it doesn't correctly handle dynamic imports and fs.promises + // N.B. don't use mock-fs, since it doesn't correctly handle dynamic imports and fsAdapter.fs.promises ReportService.outputPath = mockedOutputPath; }); @@ -81,8 +82,11 @@ describe('Dashboard Server', () => { const response = await fetch(`${host}/menu-data.json`); expect(response.status).to.equal(200); const responseJSON = await response.json(); - const expectedResult = fs.readFileSync(`${mockedResponsesPath}/menu-data.json`, 'utf8'); - expect(responseJSON).to.eql(JSON.parse(expectedResult)); + const expectedResult = fsAdapter.fs.readFileSync( + `${mockedResponsesPath}/menu-data.json`, + 'utf8', + ); + expect(responseJSON).to.deep.equal(JSON.parse(expectedResult)); }); }); @@ -91,8 +95,11 @@ describe('Dashboard Server', () => { const response = await fetch(`${host}/results.json`); expect(response.status).to.equal(200); const responseJson = await response.json(); - const expectedResult = fs.readFileSync(`${mockedResponsesPath}/results.json`, 'utf8'); - expect(responseJson).to.eql(JSON.parse(expectedResult)); + const expectedResult = fsAdapter.fs.readFileSync( + `${mockedResponsesPath}/results.json`, + 'utf8', + ); + expect(responseJson).to.deep.equal(JSON.parse(expectedResult)); }); }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/e2e/all-analyzers.e2e.js b/packages-node/providence-analytics/test-node/program/analyzers/e2e/all-analyzers.e2e.js index 3b75b6d1b..9990f3e21 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/e2e/all-analyzers.e2e.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/e2e/all-analyzers.e2e.js @@ -1,13 +1,12 @@ /* eslint-disable import/no-extraneous-dependencies */ import pathLib, { dirname } from 'path'; import { fileURLToPath } from 'url'; -import fs from 'fs'; import { expect } from 'chai'; import { it } from 'mocha'; import { providence } from '../../../../src/program/providence.js'; import { QueryService } from '../../../../src/program/core/QueryService.js'; import { ReportService } from '../../../../src/program/core/ReportService.js'; -import { memoizeConfig } from '../../../../src/program/utils/memoize.js'; +import { memoize } from '../../../../src/program/utils/memoize.js'; import { setupAnalyzerTest } from '../../../../test-helpers/setup-analyzer-test.js'; import { FindExportsAnalyzer, @@ -18,6 +17,7 @@ import MatchSubclassesAnalyzer from '../../../../src/program/analyzers/match-sub import MatchPathsAnalyzer from '../../../../src/program/analyzers/match-paths.js'; import FindCustomelementsAnalyzer from '../../../../src/program/analyzers/find-customelements.js'; import FindClassesAnalyzer from '../../../../src/program/analyzers/find-classes.js'; +import { fsAdapter } from '../../../../src/program/utils/fs-adapter.js'; /** * @typedef {import('../../../../types/index.js').ProvidenceConfig} ProvidenceConfig @@ -48,13 +48,13 @@ describe('Analyzers file-system integration', () => { const originalGetResultFileNameAndPath = ReportService._getResultFileNameAndPath; const originalOutputPath = ReportService.outputPath; - const memoizeCacheDisabledInitial = memoizeConfig.isCacheDisabled; - memoizeConfig.isCacheDisabled = true; + const memoizeCacheEnabledInitial = memoize.isCacheEnabled; + memoize.disableCaching(); after(() => { ReportService._getResultFileNameAndPath = originalGetResultFileNameAndPath; ReportService.outputPath = originalOutputPath; - memoizeConfig.isCacheDisabled = memoizeCacheDisabledInitial; + memoize.restoreCaching(memoizeCacheEnabledInitial); }); if (generateE2eMode) { @@ -132,7 +132,7 @@ describe('Analyzers file-system integration', () => { return; } const expectedOutput = JSON.parse( - fs.readFileSync( + fsAdapter.fs.readFileSync( pathLib.resolve( __dirname, `../../../../test-helpers/project-mocks-analyzer-outputs/${ctor.analyzerName}.json`, @@ -141,8 +141,8 @@ describe('Analyzers file-system integration', () => { ), ); const { queryOutput } = JSON.parse(JSON.stringify(queryResults[0])); - expect(queryOutput).not.to.eql([]); - expect(queryOutput).to.eql(expectedOutput.queryOutput); + expect(queryOutput).not.to.deep.equal([]); + expect(queryOutput).to.deep.equal(expectedOutput.queryOutput); }); } }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js b/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js index a08269e8d..2501ca083 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js @@ -25,7 +25,7 @@ describe('Analyzer "find-classes"', async () => { mockProject([`class EmptyClass {}`]); const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result).to.eql([ + expect(firstEntry.result).to.deep.equal([ { name: 'EmptyClass', isMixin: false, @@ -41,7 +41,7 @@ describe('Analyzer "find-classes"', async () => { mockProject([`const m = superclass => class MyMixin extends superclass {}`]); const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result).to.eql([ + expect(firstEntry.result).to.deep.equal([ { name: 'MyMixin', superClasses: [ @@ -72,7 +72,7 @@ describe('Analyzer "find-classes"', async () => { }); const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result[1].superClasses).to.eql([ + expect(firstEntry.result[1].superClasses).to.deep.equal([ { isMixin: true, name: 'Mixin', @@ -109,7 +109,7 @@ describe('Analyzer "find-classes"', async () => { ]); const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result[0].members.methods).to.eql([ + expect(firstEntry.result[0].members.methods).to.deep.equal([ { accessType: 'public', name: 'method', @@ -145,7 +145,7 @@ describe('Analyzer "find-classes"', async () => { ]); const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result[0].members.props).to.eql([ + expect(firstEntry.result[0].members.props).to.deep.equal([ { accessType: 'public', kind: ['get', 'set'], diff --git a/packages-node/providence-analytics/test-node/program/analyzers/find-customelements.test.js b/packages-node/providence-analytics/test-node/program/analyzers/find-customelements.test.js index 3af9191de..0a1db5546 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/find-customelements.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/find-customelements.test.js @@ -58,7 +58,7 @@ describe('Analyzer "find-customelements"', async () => { const queryResults = await providence(findCustomelementsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; const firstEntry = getEntry(queryResult); - expect(firstEntry.result[0].rootFile).to.eql({ + expect(firstEntry.result[0].rootFile).to.deep.equal({ file: './src/CustomEl.js', specifier: 'CustomEl', }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/find-exports.test.js b/packages-node/providence-analytics/test-node/program/analyzers/find-exports.test.js index cf525d208..9f79b5a19 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/find-exports.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/find-exports.test.js @@ -26,7 +26,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstResult = getEntry(queryResults[0]).result[0]; - expect(firstResult.exportSpecifiers).to.eql(['x']); + expect(firstResult.exportSpecifiers).to.deep.equal(['x']); expect(firstResult.source).to.be.undefined; }); @@ -34,7 +34,7 @@ describe('Analyzer "find-exports"', async () => { mockProject([`export default class X {}`]); const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstResult = getEntry(queryResults[0]).result[0]; - expect(firstResult.exportSpecifiers).to.eql(['[default]']); + expect(firstResult.exportSpecifiers).to.deep.equal(['[default]']); expect(firstResult.source).to.be.undefined; }); @@ -43,7 +43,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstResult = getEntry(queryResults[0]).result[0]; - expect(firstResult.exportSpecifiers).to.eql(['[default]']); + expect(firstResult.exportSpecifiers).to.deep.equal(['[default]']); expect(firstResult.source).to.be.undefined; }); @@ -56,7 +56,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstResult = getEntry(queryResults[0]).result[0]; - expect(firstResult).to.eql({ + expect(firstResult).to.deep.equal({ exportSpecifiers: ['[default]'], source: undefined, rootFileMap: [ @@ -68,7 +68,7 @@ describe('Analyzer "find-exports"', async () => { }); const secondEntry = getEntry(queryResults[0], 1); - expect(secondEntry.result[0]).to.eql({ + expect(secondEntry.result[0]).to.deep.equal({ exportSpecifiers: ['namedExport'], source: './file-with-default-export.js', localMap: [{ exported: 'namedExport', local: '[default]' }], @@ -128,7 +128,7 @@ describe('Analyzer "find-exports"', async () => { expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1); expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]'); expect(firstEntry.result[0].source).to.equal('./styles.css'); - expect(firstEntry.result[0].rootFileMap[0]).to.eql({ + expect(firstEntry.result[0].rootFileMap[0]).to.deep.equal({ currentFileSpecifier: '[default]', rootFile: { file: './styles.css', @@ -147,7 +147,7 @@ describe('Analyzer "find-exports"', async () => { expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1); expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]'); expect(firstEntry.result[0].source).to.equal('./styles.css'); - expect(firstEntry.result[0].rootFileMap[0]).to.eql({ + expect(firstEntry.result[0].rootFileMap[0]).to.deep.equal({ currentFileSpecifier: '[default]', rootFile: { file: './styles.css', @@ -161,7 +161,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); // This info will be relevant later to identify 'transitive' relations - expect(firstEntry.result[0].localMap).to.eql([ + expect(firstEntry.result[0].localMap).to.deep.equal([ { local: 'x', exported: 'y', @@ -174,7 +174,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); expect(firstEntry.result[0].exportSpecifiers.length).to.equal(2); - expect(firstEntry.result[0].exportSpecifiers).to.eql(['x', 'y']); + expect(firstEntry.result[0].exportSpecifiers).to.deep.equal(['x', 'y']); expect(firstEntry.result[0].source).to.equal('my/source'); }); @@ -190,7 +190,7 @@ describe('Analyzer "find-exports"', async () => { const secondEntry = getEntry(queryResults[0], 1); const thirdEntry = getEntry(queryResults[0], 2); - expect(firstEntry.result[0].rootFileMap).to.eql([ + expect(firstEntry.result[0].rootFileMap).to.deep.equal([ { currentFileSpecifier: 'MyComp', // this is the local name in the file we track from rootFile: { @@ -199,16 +199,7 @@ describe('Analyzer "find-exports"', async () => { }, }, ]); - expect(secondEntry.result[0].rootFileMap).to.eql([ - { - currentFileSpecifier: 'InBetweenComp', - rootFile: { - file: './src/OriginalComp.js', - specifier: 'OriginalComp', - }, - }, - ]); - expect(thirdEntry.result[0].rootFileMap).to.eql([ + expect(secondEntry.result[0].rootFileMap).to.deep.equal([ { currentFileSpecifier: 'OriginalComp', rootFile: { @@ -217,6 +208,15 @@ describe('Analyzer "find-exports"', async () => { }, }, ]); + expect(thirdEntry.result[0].rootFileMap).to.deep.equal([ + { + currentFileSpecifier: 'InBetweenComp', + rootFile: { + file: './src/OriginalComp.js', + specifier: 'OriginalComp', + }, + }, + ]); }); it(`stores rootFileMap of an exported Identifier`, async () => { @@ -236,7 +236,7 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result[0].rootFileMap).to.eql([ + expect(firstEntry.result[0].rootFileMap).to.deep.equal([ { currentFileSpecifier: '[default]', rootFile: { @@ -252,7 +252,7 @@ describe('Analyzer "find-exports"', async () => { mockProject([`// some comment here...`]); const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const firstEntry = getEntry(queryResults[0]); - expect(firstEntry.result[0].exportSpecifiers).to.eql(['[file]']); + expect(firstEntry.result[0].exportSpecifiers).to.deep.equal(['[file]']); expect(firstEntry.result[0].source).to.equal(undefined); }); }); @@ -322,10 +322,10 @@ describe('Analyzer "find-exports"', async () => { const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; const [firstEntry, secondEntry, thirdEntry] = getEntries(queryResult); - expect(firstEntry.meta.categories).to.eql(['fooCategory']); + expect(firstEntry.meta.categories).to.deep.equal(['fooCategory']); // not mutually exclusive... - expect(secondEntry.meta.categories).to.eql(['barCategory', 'testCategory']); - expect(thirdEntry.meta.categories).to.eql([]); + expect(secondEntry.meta.categories).to.deep.equal(['barCategory', 'testCategory']); + expect(thirdEntry.meta.categories).to.deep.equal([]); }); }); }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/find-imports.test.js b/packages-node/providence-analytics/test-node/program/analyzers/find-imports.test.js index cc3477d7f..fc1bb3a9f 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/find-imports.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/find-imports.test.js @@ -25,7 +25,7 @@ describe('Analyzer "find-imports"', async () => { const queryResults = await providence(findImportsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; const firstEntry = getEntry(queryResult); - expect(firstEntry.result[0].importSpecifiers).to.eql(['[file]']); + expect(firstEntry.result[0].importSpecifiers).to.deep.equal(['[file]']); expect(firstEntry.result[0].source).to.equal('imported/source'); }); @@ -128,7 +128,7 @@ describe('Analyzer "find-imports"', async () => { const queryResult = queryResults[0]; const firstEntry = getEntry(queryResult); // This info will be relevant later to identify transitive relations - expect(firstEntry.result[0].localMap[0]).to.eql({ + expect(firstEntry.result[0].localMap[0]).to.deep.equal({ local: 'y', imported: 'x', }); @@ -332,7 +332,7 @@ describe('Analyzer "find-imports"', async () => { // Should be normalized source...? expect(queryResult.queryOutput[0].source).to.equal('@external/source.js'); expect(queryResult.queryOutput[0].id).to.equal('x::@external/source.js'); - expect(queryResult.queryOutput[0].dependents).to.eql([ + expect(queryResult.queryOutput[0].dependents).to.deep.equal([ 'fictional-project/file1.js', 'fictional-project/file2.js', ]); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/helpers/track-down-identifier.test.js b/packages-node/providence-analytics/test-node/program/analyzers/helpers/track-down-identifier.test.js index 2edfbe1ff..372572763 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/helpers/track-down-identifier.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/helpers/track-down-identifier.test.js @@ -39,7 +39,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/declarationOfMyClass.js', specifier: 'MyClass', }); @@ -71,7 +71,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/declarationOfMyClass.js', specifier: 'MyClass', }); @@ -105,7 +105,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/declarationOfMyClass.js', specifier: '[default]', }); @@ -131,7 +131,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: '@external/source', specifier: '[default]', }); @@ -162,7 +162,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './MyClass.js', specifier: '[default]', }); @@ -201,7 +201,7 @@ describe('trackdownIdentifier', () => { rootPath, projectName, ); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './MyClass.js', specifier: '[default]', }); @@ -232,7 +232,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/declarationOfMyNumber.js', specifier: 'myNumber', }); @@ -260,7 +260,7 @@ describe('trackdownIdentifier', () => { const rootPath = '/my/project'; const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './packages/accordion/IngAccordionContent.js', specifier: 'IngAccordionContent', }); @@ -277,7 +277,7 @@ describe('trackdownIdentifier', () => { currentFilePath2, rootPath2, ); - expect(rootFile2).to.eql({ + expect(rootFile2).to.deep.equal({ file: './packages/accordion/IngAccordionInvokerButton.js', specifier: 'IngAccordionInvokerButton', }); @@ -321,7 +321,7 @@ describe('trackDownIdentifierFromScope', () => { fullCurrentFilePath, projectPath, ); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: '[current]', specifier: 'MyClass', }); @@ -372,7 +372,7 @@ describe('trackDownIdentifierFromScope', () => { fullCurrentFilePath, projectPath, ); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/declarationOfMyClass.js', specifier: 'MyClass', }); @@ -420,7 +420,7 @@ describe('trackDownIdentifierFromScope', () => { fullCurrentFilePath, projectPath, ); - expect(rootFile).to.eql({ + expect(rootFile).to.deep.equal({ file: './src/classes.js', specifier: 'El1', }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/match-imports.test.js b/packages-node/providence-analytics/test-node/program/analyzers/match-imports.test.js index 32488e99a..672f93c5e 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/match-imports.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/match-imports.test.js @@ -215,14 +215,14 @@ describe('Analyzer "match-imports"', async () => { ); const [name, filePath, project] = targetExportedId.split('::'); - expect(matchedEntry.exportSpecifier).to.eql({ + expect(matchedEntry.exportSpecifier).to.deep.equal({ name, filePath, project, id: targetExportedId, }); expect(matchedEntry.matchesPerProject[0].project).to.equal('importing-target-project'); - expect(matchedEntry.matchesPerProject[0].files).to.eql(importedByFiles); + expect(matchedEntry.matchesPerProject[0].files).to.deep.equal(importedByFiles); } describe('Extracting exports', () => { @@ -435,7 +435,7 @@ describe('Analyzer "match-imports"', async () => { }); const queryResult = queryResults[0]; expect(queryResult.queryOutput[0].exportSpecifier.name).to.equal('[default]'); - expect(queryResult.queryOutput[0].matchesPerProject).to.eql([ + expect(queryResult.queryOutput[0].matchesPerProject).to.deep.equal([ { files: ['./importDefault1.js', './importDefault2.js'], project: 'target' }, ]); }); @@ -476,11 +476,11 @@ describe('Analyzer "match-imports"', async () => { }); const queryResult = queryResults[0]; expect(queryResult.queryOutput[0].exportSpecifier.name).to.equal('[default]'); - expect(queryResult.queryOutput[0].matchesPerProject).to.eql([ + expect(queryResult.queryOutput[0].matchesPerProject).to.deep.equal([ { files: ['./deep-imports.js'], project: 'target' }, ]); expect(queryResult.queryOutput[1].exportSpecifier.name).to.equal('RefClass'); - expect(queryResult.queryOutput[1].matchesPerProject).to.eql([ + expect(queryResult.queryOutput[1].matchesPerProject).to.deep.equal([ { files: ['./deep-imports.js'], project: 'target' }, ]); }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/match-paths.test.js b/packages-node/providence-analytics/test-node/program/analyzers/match-paths.test.js index 1c4ce7081..a74db2aa3 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/match-paths.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/match-paths.test.js @@ -188,7 +188,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(searchTargetProject, referenceProject); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput).to.eql(expectedMatches); + expect(queryResult.queryOutput).to.deep.equal(expectedMatches); }); describe('Features', () => { @@ -239,7 +239,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(targetProj, refProj); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].variable.paths[0]).to.deep.equal({ from: './index.js', to: './target-src/TargetClass.js', }); @@ -263,7 +263,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(targetProjWithMultipleExports, refProj); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].variable.paths[0]).to.deep.equal({ from: './index.js', to: './reexportFromRoot.js', }); @@ -296,7 +296,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(targetProjWithMultipleExportsAndMainEntry, refProj); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].variable.paths[0]).to.deep.equal({ from: './index.js', to: './target-src/mainEntry.js', }); @@ -308,8 +308,11 @@ describe('Analyzer "match-paths"', async () => { const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; const unprefixedPaths = queryResult.queryOutput[0].variable.paths[0]; - expect(unprefixedPaths).to.eql({ from: './index.js', to: './target-src/TargetClass.js' }); - expect(queryResult.queryOutput[0].variable.paths[1]).to.eql({ + expect(unprefixedPaths).to.deep.equal({ + from: './index.js', + to: './target-src/TargetClass.js', + }); + expect(queryResult.queryOutput[0].variable.paths[1]).to.deep.equal({ from: `${refProj.name}/${unprefixedPaths.from.slice(2)}`, to: unprefixedPaths.to, }); @@ -336,11 +339,11 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(targetProjMultipleTargetExtensions, refProj); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].variable.paths[0]).to.deep.equal({ from: './index.js', to: './target-src/TargetClass.js', }); - expect(queryResult.queryOutput[1].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[1].variable.paths[0]).to.deep.equal({ from: './index.js', to: './target-src/TargetSomething.js', }); @@ -410,7 +413,7 @@ describe('Analyzer "match-paths"', async () => { ); const queryResults = await providence(matchPathsQueryConfigFilter, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].variable.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].variable.paths[0]).to.deep.equal({ from: './index.js', to: './target-src/TargetClass.js', }); @@ -515,8 +518,8 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(searchTargetProject, referenceProject); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].tag).to.eql(expectedMatches[0]); - expect(queryResult.queryOutput[1].tag).to.eql(expectedMatches[1]); + expect(queryResult.queryOutput[0].tag).to.deep.equal(expectedMatches[0]); + expect(queryResult.queryOutput[1].tag).to.deep.equal(expectedMatches[1]); }); // TODO: test works in isolation, but some side effects occur when run in suite @@ -578,7 +581,7 @@ describe('Analyzer "match-paths"', async () => { providenceCfg, ); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].tag).to.eql({ + expect(queryResult.queryOutput[0].tag).to.deep.equal({ from: 'their-button', to: 'my-button', paths: [ @@ -607,7 +610,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(searchTargetProject, referenceProject); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].tag.paths[0]).to.eql({ + expect(queryResult.queryOutput[0].tag.paths[0]).to.deep.equal({ from: './customelementDefinitions.js', to: './extendedCustomelementDefinitions.js', }); @@ -617,7 +620,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(searchTargetProject, referenceProject); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput[0].tag.paths[1]).to.eql({ + expect(queryResult.queryOutput[0].tag.paths[1]).to.deep.equal({ from: 'reference-project/customelementDefinitions.js', to: './extendedCustomelementDefinitions.js', }); @@ -736,7 +739,7 @@ describe('Analyzer "match-paths"', async () => { mockTargetAndReferenceProject(searchTargetProjectFull, referenceProjectFull); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResult = queryResults[0]; - expect(queryResult.queryOutput).to.eql(expectedMatchesFull); + expect(queryResult.queryOutput).to.deep.equal(expectedMatchesFull); }); }); }); diff --git a/packages-node/providence-analytics/test-node/program/analyzers/match-subclasses.test.js b/packages-node/providence-analytics/test-node/program/analyzers/match-subclasses.test.js index eafae0b56..47b5ae3b6 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/match-subclasses.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/match-subclasses.test.js @@ -332,14 +332,14 @@ describe('Analyzer "match-subclasses"', async () => { ); const [name, filePath, project] = targetExportedId.split('::'); - expect(matchedEntry.exportSpecifier).to.eql({ + expect(matchedEntry.exportSpecifier).to.deep.equal({ name, filePath, project, id: targetExportedId, }); expect(matchedEntry.matchesPerProject[0].project).to.equal('importing-target-project'); - expect(matchedEntry.matchesPerProject[0].files).to.eql(importedByFiles); + expect(matchedEntry.matchesPerProject[0].files).to.deep.equal(importedByFiles); } mockTargetAndReferenceProject(searchTargetProject, referenceProject); diff --git a/packages-node/providence-analytics/test-node/program/core/Analyzer.test.js b/packages-node/providence-analytics/test-node/program/core/Analyzer.test.js index 4d00d6580..a3f0369b7 100644 --- a/packages-node/providence-analytics/test-node/program/core/Analyzer.test.js +++ b/packages-node/providence-analytics/test-node/program/core/Analyzer.test.js @@ -81,19 +81,19 @@ describe('Analyzer', async () => { const queryResult = queryResults[0]; const { queryOutput, meta } = queryResult; - expect(queryOutput[0]).to.eql({ + expect(queryOutput[0]).to.deep.equal({ file: './test-file-0.js', meta: {}, result: [{ matched: 'entry' }], }); - expect(queryOutput[1]).to.eql({ + expect(queryOutput[1]).to.deep.equal({ file: './test-file2.js', meta: {}, result: [{ matched: 'entry' }], }); // Local machine info needs to be deleted, so that results are always 'machine agnostic' // (which is needed to share cached json results via git) - expect(meta).to.eql({ + expect(meta).to.deep.equal({ searchType: 'ast-analyzer', analyzerMeta: { name: 'my-analyzer', 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 dd06fbb5e..f62fae309 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 @@ -1,9 +1,7 @@ import { expect } from 'chai'; import { it } from 'mocha'; -import pathLib from 'path'; import { InputDataService } from '../../../src/program/core/InputDataService.js'; -import { memoizeConfig } from '../../../src/program/utils/memoize.js'; -import { getCurrentDir } from '../../../src/program/utils/get-current-dir.js'; +import { memoize } from '../../../src/program/utils/memoize.js'; import { restoreMockedProjects, mockProject, @@ -24,13 +22,13 @@ describe('InputDataService', () => { } beforeEach(() => { - memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); }); afterEach(() => { restoreOriginalInputDataPaths(); restoreMockedProjects(); - memoizeConfig.isCacheDisabled = false; + memoize.restoreCaching(); }); describe('Configuration', () => { @@ -50,36 +48,43 @@ describe('InputDataService', () => { }); describe('Methods', () => { - // TODO: mock file system... it('"createDataObject"', async () => { - /** @type {* & PathFromSystemRoot} */ - const projectPath = pathLib.resolve( - getCurrentDir(import.meta.url), - '../../../test-helpers/project-mocks/importing-target-project', - ); + mockProject({ + './package.json': JSON.stringify({ + name: 'fictional-project', + main: 'my/index.js', + version: '1.0.0', + }), + './src/file.js': '// bla', + './src/file2.js': '// bla', + }); - const inputDataPerProject = InputDataService.createDataObject([projectPath]); - expect(Object.keys(inputDataPerProject[0].project)).to.eql([ - 'path', - 'mainEntry', - 'name', - 'version', - 'commitHash', + const inputDataPerProject = await InputDataService.createDataObject(['/fictional/project']); + expect(inputDataPerProject).to.deep.equal([ + { + project: { + path: '/fictional/project', + mainEntry: './my/index.js', + name: 'fictional-project', + version: '1.0.0', + commitHash: '[not-a-git-root]', + }, + entries: [ + { + file: './src/file.js', + context: { + code: '// bla', + }, + }, + { + file: './src/file2.js', + context: { + code: '// bla', + }, + }, + ], + }, ]); - expect(inputDataPerProject[0].project.name).to.equal('importing-target-project'); - expect(inputDataPerProject[0].project.mainEntry).to.equal( - './target-src/match-imports/root-level-imports.js', - ); - expect( - inputDataPerProject[0].project.path.endsWith( - '/test-helpers/project-mocks/importing-target-project', - ), - ).to.equal(true); - expect(inputDataPerProject[0].entries.length).to.equal(6); - expect(inputDataPerProject[0].entries[0].context.code).to.not.be.undefined; - expect(inputDataPerProject[0].entries[0].file).to.equal( - './target-src/find-customelements/multiple.js', - ); }); it('"targetProjectPaths"', async () => {}); @@ -99,11 +104,11 @@ describe('InputDataService', () => { '{ "name": "@another-scope/another-package" }', }); - expect(InputDataService.getMonoRepoPackages('/fictional/project')).to.eql([ - { path: 'packages/pkg1/', name: 'package1' }, - { path: 'packages/pkg2/', name: 'pkg2' }, // fallback when no package.json - { path: 'packages/pkg3/', name: '@scope/pkg3' }, - { path: 'another-folder/another-package/', name: '@another-scope/another-package' }, + expect(await InputDataService.getMonoRepoPackages('/fictional/project')).to.deep.equal([ + { path: 'packages/pkg1', name: 'package1' }, + { path: 'packages/pkg2', name: 'pkg2' }, // fallback when no package.json + { path: 'packages/pkg3', name: '@scope/pkg3' }, + { path: 'another-folder/another-package', name: '@another-scope/another-package' }, ]); }); @@ -120,11 +125,11 @@ describe('InputDataService', () => { '{ "name": "@another-scope/another-package" }', }); - expect(InputDataService.getMonoRepoPackages('/fictional/project')).to.eql([ - { path: 'packages/pkg1/', name: 'package1' }, - { path: 'packages/pkg2/', name: 'pkg2' }, // fallback when no package.json - { path: 'packages/pkg3/', name: '@scope/pkg3' }, - { path: 'another-folder/another-package/', name: '@another-scope/another-package' }, + expect(await InputDataService.getMonoRepoPackages('/fictional/project')).to.deep.equal([ + { path: 'packages/pkg1', name: 'package1' }, + { path: 'packages/pkg2', name: 'pkg2' }, // fallback when no package.json + { path: 'packages/pkg3', name: '@scope/pkg3' }, + { path: 'another-folder/another-package', name: '@another-scope/another-package' }, ]); }); }); @@ -143,19 +148,21 @@ describe('InputDataService', () => { }); it('gathers a list of files', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput).to.eql([ + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput).to.deep.equal([ '/fictional/project/index.js', '/fictional/project/internal.js', + '/fictional/project/something.test.js', '/fictional/project/nested/index.js', '/fictional/project/nested/nested-two/index.test.js', - '/fictional/project/something.test.js', ]); }); it('allows passing a depth which stops at nested depth', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { depth: 0 }); - expect(globOutput).to.eql([ + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { + depth: 0, + }); + expect(globOutput).to.deep.equal([ '/fictional/project/index.js', '/fictional/project/internal.js', '/fictional/project/something.test.js', @@ -163,26 +170,26 @@ describe('InputDataService', () => { }); it('allows passing extensions', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { extensions: ['.html', '.js'], }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/index.html', '/fictional/project/index.js', '/fictional/project/internal.js', - '/fictional/project/nested/index.js', - '/fictional/project/nested/nested-two/index.test.js', '/fictional/project/something.test.html', '/fictional/project/something.test.js', + '/fictional/project/nested/index.js', + '/fictional/project/nested/nested-two/index.test.js', ]); }); it('allows passing excluded folders', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { extensions: ['.html', '.js'], allowlist: ['!nested/**'], }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/index.html', '/fictional/project/index.js', '/fictional/project/internal.js', @@ -192,25 +199,25 @@ describe('InputDataService', () => { }); it('allows passing excluded files', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { extensions: ['.html', '.js'], allowlist: ['!index.js', '!**/*/index.js'], }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/index.html', '/fictional/project/internal.js', - '/fictional/project/nested/nested-two/index.test.js', '/fictional/project/something.test.html', '/fictional/project/something.test.js', + '/fictional/project/nested/nested-two/index.test.js', ]); }); it('allows passing exclude globs', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { extensions: ['.html', '.js'], allowlist: ['!**/*.test.{html,js}'], }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/index.html', '/fictional/project/index.js', '/fictional/project/internal.js', @@ -219,11 +226,11 @@ describe('InputDataService', () => { }); it('does not support non globs in "allowlist"', async () => { - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { extensions: ['.html', '.js'], allowlist: ['nested'], }); - expect(globOutput).to.eql([]); + expect(globOutput).to.deep.equal([]); }); it('omits node_modules and bower_components at root level by default', async () => { @@ -235,11 +242,11 @@ describe('InputDataService', () => { './nested/bower_components/pkg/y.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput).to.eql([ + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput).to.deep.equal([ '/fictional/project/index.js', - '/fictional/project/nested/bower_components/pkg/y.js', '/fictional/project/nested/node_modules/pkg/x.js', + '/fictional/project/nested/bower_components/pkg/y.js', ]); }); @@ -249,12 +256,12 @@ describe('InputDataService', () => { './omitted/file.js': '', './added/file.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlist: ['*', 'added/**/*'], }); - expect(globOutput).to.eql([ - '/fictional/project/added/file.js', + expect(globOutput).to.deep.equal([ '/fictional/project/root-lvl.js', + '/fictional/project/added/file.js', ]); }); @@ -265,10 +272,10 @@ describe('InputDataService', () => { './deeper/glob/file.js': '', './deeper/file.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlist: ['deeper/**/*'], }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/deeper/file.js', '/fictional/project/deeper/glob/file.js', '/fictional/project/deeper/glob/structure/file.js', @@ -285,8 +292,8 @@ describe('InputDataService', () => { './some-other-pkg/commitlint.conf.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput).to.eql(['/fictional/project/index.js']); + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput).to.deep.equal(['/fictional/project/index.js']); }); it('omits hidden files by default', async () => { @@ -295,8 +302,8 @@ describe('InputDataService', () => { './index.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput).to.eql(['/fictional/project/index.js']); + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput).to.deep.equal(['/fictional/project/index.js']); }); describe('AllowlistMode', () => { @@ -308,8 +315,8 @@ describe('InputDataService', () => { }), '.gitignore': '/dist', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput).to.eql([ + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput).to.deep.equal([ // This means allowlistMode is 'git' ]); @@ -322,8 +329,8 @@ describe('InputDataService', () => { files: ['dist'], }), }); - const globOutput2 = InputDataService.gatherFilesFromDir('/fictional/project'); - expect(globOutput2).to.eql([ + const globOutput2 = await InputDataService.gatherFilesFromDir('/fictional/project'); + expect(globOutput2).to.deep.equal([ // This means allowlistMode is 'npm' '/fictional/project/dist/bundle.js', ]); @@ -335,10 +342,10 @@ describe('InputDataService', () => { projectPath: '/inside/proj/with/node_modules/detect-as-npm', }, ); - const globOutput3 = InputDataService.gatherFilesFromDir( + const globOutput3 = await InputDataService.gatherFilesFromDir( '/inside/proj/with/node_modules/detect-as-npm', ); - expect(globOutput3).to.eql([ + expect(globOutput3).to.deep.equal([ // This means allowlistMode is 'npm' (even though we found .gitignore) '/inside/proj/with/node_modules/detect-as-npm/dist/bundle.js', ]); @@ -350,10 +357,10 @@ describe('InputDataService', () => { projectPath: '/inside/proj/with/node_modules/@scoped/detect-as-npm', }, ); - const globOutput4 = InputDataService.gatherFilesFromDir( + const globOutput4 = await InputDataService.gatherFilesFromDir( '/inside/proj/with/node_modules/@scoped/detect-as-npm', ); - expect(globOutput4).to.eql([ + expect(globOutput4).to.deep.equal([ // This means allowlistMode is 'npm' (even though we found .gitignore) '/inside/proj/with/node_modules/@scoped/detect-as-npm/dist/bundle.js', ]); @@ -369,13 +376,13 @@ describe('InputDataService', () => { files: ['*.add.js', 'docs', 'src'], }), }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlistMode: 'npm', }); - expect(globOutput).to.eql([ - '/fictional/project/docs/x.js', + expect(globOutput).to.deep.equal([ '/fictional/project/file.add.js', '/fictional/project/src/y.js', + '/fictional/project/docs/x.js', ]); }); @@ -395,12 +402,12 @@ build/ !keep/ `, }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlistMode: 'git', }); - expect(globOutput).to.eql([ - '/fictional/project/keep/it.js', + expect(globOutput).to.deep.equal([ '/fictional/project/shall/pass.js', + '/fictional/project/keep/it.js', ]); }); @@ -415,12 +422,12 @@ build/ /dist `, }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlistMode: 'all', }); - expect(globOutput).to.eql([ - '/fictional/project/dist/bundle.js', + expect(globOutput).to.deep.equal([ '/fictional/project/src/file.js', + '/fictional/project/dist/bundle.js', ]); }); @@ -434,10 +441,10 @@ build/ }, }), }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlistMode: 'export-map', }); - expect(globOutput).to.eql(['./internal/file.js']); + expect(globOutput).to.deep.equal(['./internal/file.js']); }); }); @@ -451,11 +458,11 @@ build/ files: ['dist'], // This will not be considered by default, unless explicitly configured in allowlist }), }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlist: ['dist/**'], allowlistMode: 'git', // for clarity, (would also be autodetected if not provided) }); - expect(globOutput).to.eql(['/fictional/project/dist/bundle.js']); + expect(globOutput).to.deep.equal(['/fictional/project/dist/bundle.js']); }); describe('Default allowlist', () => { @@ -466,10 +473,10 @@ build/ './added.js': '', './omit.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlist: ['added*'], }); - expect(globOutput).to.eql(['/fictional/project/added.js']); + expect(globOutput).to.deep.equal(['/fictional/project/added.js']); }); it('allows to omit default config filter', async () => { @@ -481,16 +488,16 @@ build/ './added.js': '', './omit.js': '', }); - const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { + const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', { allowlist: ['!omit*'], omitDefaultAllowlist: true, }); - expect(globOutput).to.eql([ + expect(globOutput).to.deep.equal([ '/fictional/project/abc.config.js', '/fictional/project/added.js', - '/fictional/project/bower_components/omitted/file.js', - '/fictional/project/node_modules/root-lvl.js', '/fictional/project/xyz.conf.js', + '/fictional/project/node_modules/root-lvl.js', + '/fictional/project/bower_components/omitted/file.js', ]); }); }); @@ -514,10 +521,10 @@ build/ packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-path.js', exposed: './exposed-path.js' }, - { internal: './internal/folder-a/path.js', exposed: './external/folder-a/path.js' }, { internal: './internal/folder-b/path.js', exposed: './external/folder-b/path.js' }, + { internal: './internal/folder-a/path.js', exposed: './external/folder-a/path.js' }, ]); }); @@ -532,7 +539,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-path.js', exposed: './exposed-path.js' }, ]); }); @@ -550,7 +557,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-exports-folder/file-a.js', exposed: './file-a.js' }, { internal: './internal-exports-folder/file-b.js', exposed: './file-b.js' }, { internal: './internal-exports-folder/file-c.js', exposed: './file-c.js' }, @@ -569,12 +576,12 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ + { internal: './internal-folder/file-a.js', exposed: './exposed-folder/file-a.js' }, { internal: './internal-folder/another-folder/file-b.js', exposed: './exposed-folder/another-folder/file-b.js', }, - { internal: './internal-folder/file-a.js', exposed: './exposed-folder/file-a.js' }, ]); }); @@ -591,9 +598,9 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ - { internal: './folder-a/file.js', exposed: './exposed-folder/folder-a/file.js' }, - { internal: './folder-b/file.js', exposed: './exposed-folder/folder-b/file.js' }, + expect(exportMapPaths).to.deep.equal([ + { exposed: './exposed-folder/folder-b/file.js', internal: './folder-b/file.js' }, + { exposed: './exposed-folder/folder-a/file.js', internal: './folder-a/file.js' }, ]); }); @@ -611,7 +618,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-folder/file-a.js', exposed: './exposed-folder/file-a.js' }, ]); }); @@ -631,7 +638,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './esm-exports/file.js', exposed: './file.js' }, ]); }); @@ -650,7 +657,7 @@ build/ packageRootPath: '/my/proj', nodeResolveMode: 'require', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './cjs-exports/file.cjs', exposed: './file.cjs' }, ]); }); @@ -669,7 +676,7 @@ build/ packageRootPath: '/my/proj', nodeResolveMode: 'develop', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './develop-exports/file.js', exposed: './file.js' }, ]); }); @@ -693,7 +700,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './index.js', exposed: '.' }, { internal: './file.js', exposed: './exposed-file.js' }, ]); @@ -714,7 +721,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-folder/file-a.js', exposed: './exposed-folder/file-a.js' }, { internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' }, ]); @@ -733,7 +740,7 @@ build/ const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { packageRootPath: '/my/proj', }); - expect(exportMapPaths).to.eql([ + expect(exportMapPaths).to.deep.equal([ { internal: './internal-folder/file-a.js', exposed: './exposed-folder/file-a.js' }, { internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' }, ]); diff --git a/packages-node/providence-analytics/test-node/program/core/QueryService.test.js b/packages-node/providence-analytics/test-node/program/core/QueryService.test.js index 5ac660d60..1f15e64f8 100644 --- a/packages-node/providence-analytics/test-node/program/core/QueryService.test.js +++ b/packages-node/providence-analytics/test-node/program/core/QueryService.test.js @@ -14,7 +14,7 @@ describe('QueryService', () => { describe('Retrieving QueryConfig', () => { it('"getQueryConfigFromRegexSearchString"', async () => { const result = QueryService.getQueryConfigFromRegexSearchString('x'); - expect(result).to.eql({ type: 'search', regexString: 'x' }); + expect(result).to.deep.equal({ type: 'search', regexString: 'x' }); expect(() => { // @ts-expect-error @@ -25,7 +25,7 @@ describe('QueryService', () => { describe('"getQueryConfigFromFeatureString"', () => { it('with tag, attr-key and attr-value', async () => { const result = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'size', @@ -41,7 +41,7 @@ describe('QueryService', () => { it('with only tag', async () => { const result = QueryService.getQueryConfigFromFeatureString('tg-icon'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { tag: 'tg-icon', @@ -52,7 +52,7 @@ describe('QueryService', () => { it('with only attr-key', async () => { const result = QueryService.getQueryConfigFromFeatureString('[attr]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'attr', @@ -68,7 +68,7 @@ describe('QueryService', () => { it('with only attr-key and attr-value', async () => { const result = QueryService.getQueryConfigFromFeatureString('[attr=x]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'attr', @@ -85,7 +85,7 @@ describe('QueryService', () => { describe('With partial value', async () => { it('with tag, attr-key and attr-value', async () => { const result = QueryService.getQueryConfigFromFeatureString('tg-icon*[size*=xs*]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'size', @@ -101,7 +101,7 @@ describe('QueryService', () => { it('with only tag', async () => { const result = QueryService.getQueryConfigFromFeatureString('tg-icon*'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { tag: 'tg-icon', @@ -112,7 +112,7 @@ describe('QueryService', () => { it('with only attr-key', async () => { const result = QueryService.getQueryConfigFromFeatureString('[attr*]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'attr', @@ -128,7 +128,7 @@ describe('QueryService', () => { it('with only attr-key and attr-value', async () => { const result = QueryService.getQueryConfigFromFeatureString('[attr*=x*]'); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'feature', feature: { name: 'attr', @@ -158,7 +158,7 @@ describe('QueryService', () => { 'find-imports', myAnalyzerCfg, ); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'ast-analyzer', analyzerName: 'find-imports', analyzerConfig: myAnalyzerCfg, @@ -171,7 +171,7 @@ describe('QueryService', () => { /** @type {* & Analyzer} */ (DummyAnalyzer), myAnalyzerCfg, ); - expect(result).to.eql({ + expect(result).to.deep.equal({ type: 'ast-analyzer', analyzerName: 'find-dummy-analyzer', analyzerConfig: myAnalyzerCfg, @@ -186,7 +186,7 @@ describe('QueryService', () => { // it('with FeatureConfig', async () => { // const featureCfg = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]'); // const result = QueryService.grepSearch(featureCfg); - // expect(result).to.eql({ + // expect(result).to.deep.equal({ // type: 'ast-analyzer', // analyzerName: 'find-imports', // analyzerConfig: { x: 'y' }, diff --git a/packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js b/packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js index 7aa38d3e0..01405bc29 100644 --- a/packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js +++ b/packages-node/providence-analytics/test-node/program/utils/get-source-code-fragment-of-declaration.test.js @@ -1,16 +1,17 @@ import { expect } from 'chai'; import { it } from 'mocha'; -import { mock } from '../../../test-helpers/mock-project-helpers.js'; + import { getSourceCodeFragmentOfDeclaration } from '../../../src/program/utils/index.js'; -import { memoizeConfig } from '../../../src/program/utils/memoize.js'; +import { mock } from '../../../test-helpers/mock-project-helpers.js'; +import { memoize } from '../../../src/program/utils/memoize.js'; describe('getSourceCodeFragmentOfDeclaration', () => { - const initialMemoizeSsCacheDisabled = memoizeConfig.isCacheDisabled; + const initialMemoizeCacheEnabled = memoize.isCacheEnabled; before(() => { - memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); }); after(() => { - memoizeConfig.isCacheDisabled = initialMemoizeSsCacheDisabled; + memoize.restoreCaching(initialMemoizeCacheEnabled); }); describe('Named specifiers', () => { 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 165aebd07..1e70c1fa0 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 @@ -1,17 +1,11 @@ import { expect } from 'chai'; import { it } from 'mocha'; -import { memoize, memoizeConfig } from '../../../src/program/utils/memoize.js'; - -const cacheDisabledInitialValue = memoizeConfig.isCacheDisabled; +import { memoize } from '../../../src/program/utils/memoize.js'; describe('Memoize', () => { - beforeEach(() => { - // This is important, since memoization only works - memoizeConfig.isCacheDisabled = false; - }); - afterEach(() => { - memoizeConfig.isCacheDisabled = cacheDisabledInitialValue; - }); + // This is important, since memoization only works when cache is disabled. + // We want to prevent that another test unintentionally disabled caching. + memoize.restoreCaching(); describe('With primitives', () => { describe('Numbers', () => { @@ -136,15 +130,15 @@ describe('Memoize', () => { const sumMemoized = memoize(sum); // Put in cache for args combination - expect(sumMemoized([1], [2])).to.eql([1, 2]); + expect(sumMemoized([1], [2])).to.deep.equal([1, 2]); expect(sumCalled).to.equal(1); // Return from cache - expect(sumMemoized([1], [2])).to.eql([1, 2]); + expect(sumMemoized([1], [2])).to.deep.equal([1, 2]); expect(sumCalled).to.equal(1); // Put in cache for args combination - expect(sumMemoized([1], [3])).to.eql([1, 3]); + expect(sumMemoized([1], [3])).to.deep.equal([1, 3]); expect(sumCalled).to.equal(2); }); @@ -162,17 +156,17 @@ describe('Memoize', () => { } const sum2Memoized = memoize(sum2); - expect(sumMemoized([1], [2])).to.eql([1, 2]); + expect(sumMemoized([1], [2])).to.deep.equal([1, 2]); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(0); - expect(sum2Memoized([1], [2])).to.eql([1, 2]); + expect(sum2Memoized([1], [2])).to.deep.equal([1, 2]); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(1); // Both cached - expect(sumMemoized([1], [2])).to.eql([1, 2]); - expect(sum2Memoized([1], [2])).to.eql([1, 2]); + expect(sumMemoized([1], [2])).to.deep.equal([1, 2]); + expect(sum2Memoized([1], [2])).to.deep.equal([1, 2]); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(1); }); @@ -188,15 +182,15 @@ describe('Memoize', () => { const sumMemoized = memoize(sum, { serializeObjects: true }); // Put in cache for args combination - expect(sumMemoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); + expect(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); // Return from cache - expect(sumMemoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); + expect(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); // Put in cache for args combination - expect(sumMemoized({ x: 1 }, { y: 3 })).to.eql({ x: 1, y: 3 }); + expect(sumMemoized({ x: 1 }, { y: 3 })).to.deep.equal({ x: 1, y: 3 }); expect(sumCalled).to.equal(2); }); @@ -214,17 +208,17 @@ describe('Memoize', () => { } const sum2Memoized = memoize(sum2, { serializeObjects: true }); - expect(sumMemoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); + expect(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(0); - expect(sum2Memoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); + expect(sum2Memoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(1); // Both cached - expect(sumMemoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); - expect(sum2Memoized({ x: 1 }, { y: 2 })).to.eql({ x: 1, y: 2 }); + expect(sumMemoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); + expect(sum2Memoized({ x: 1 }, { y: 2 })).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); expect(sum2Called).to.equal(1); }); @@ -242,13 +236,13 @@ describe('Memoize', () => { // Put in cache for args combination const result = sumMemoized({ x: 1 }, { y: 2 }); - expect(result).to.eql({ x: 1, y: 2 }); + expect(result).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); // Return from cache const resultCached = sumMemoized({ x: 1 }, { y: 2 }); expect(resultCached).to.equal(result); - expect(resultCached).to.eql({ x: 1, y: 2 }); + expect(resultCached).to.deep.equal({ x: 1, y: 2 }); expect(sumCalled).to.equal(1); // Outside world can edit returned reference 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 404e58bac..11f946499 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 @@ -6,14 +6,14 @@ import { mockTargetAndReferenceProject, } from '../../../test-helpers/mock-project-helpers.js'; import { resolveImportPath } from '../../../src/program/utils/resolve-import-path.js'; -import { memoizeConfig } from '../../../src/program/utils/memoize.js'; +import { memoize } from '../../../src/program/utils/memoize.js'; describe('resolveImportPath', () => { beforeEach(() => { - memoizeConfig.isCacheDisabled = true; + memoize.disableCaching(); }); afterEach(() => { - memoizeConfig.isCacheDisabled = false; + memoize.restoreCaching(); restoreMockedProjects(); }); diff --git a/packages-node/providence-analytics/test-node/program/utils/swc-traverse.test.js b/packages-node/providence-analytics/test-node/program/utils/swc-traverse.test.js index 9143bf891..566a89421 100644 --- a/packages-node/providence-analytics/test-node/program/utils/swc-traverse.test.js +++ b/packages-node/providence-analytics/test-node/program/utils/swc-traverse.test.js @@ -60,7 +60,7 @@ describe('swcTraverse', () => { }; swcTraverse(swcAst, visitor); - expect(foundTypes).to.eql([ + expect(foundTypes).to.deep.equal([ 'Module', 'ImportDeclaration', 'ImportDefaultSpecifier', @@ -166,7 +166,7 @@ describe('swcTraverse', () => { expect(declaratorPaths[2].scope.id).to.equal(2); expect(declaratorPaths[0].node.id.value).to.equal('globalScope'); - expect(Object.keys(declaratorPaths[0].scope.bindings)).to.eql([ + expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([ 'globalScope', 'alsoGlobalScope', ]); @@ -180,8 +180,8 @@ describe('swcTraverse', () => { declaratorPaths[3].node, ); - expect(Object.keys(declaratorPaths[1].scope.bindings)).to.eql(['middleScope']); - expect(Object.keys(declaratorPaths[2].scope.bindings)).to.eql(['deepestScope']); + expect(Object.keys(declaratorPaths[1].scope.bindings)).to.deep.equal(['middleScope']); + expect(Object.keys(declaratorPaths[2].scope.bindings)).to.deep.equal(['deepestScope']); }); it('creates scopes for nested FunctionDeclaration', async () => { @@ -336,7 +336,7 @@ describe('swcTraverse', () => { }; swcTraverse(swcAst, visitor, { needsAdvancedPaths: true }); - expect(Object.keys(declaratorPaths[0].scope.bindings)).to.eql([ + expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([ 'globalScope', 'alsoGlobalScope', ]); @@ -370,12 +370,12 @@ describe('swcTraverse', () => { }; swcTraverse(swcAst, visitor, { needsAdvancedPaths: true }); - expect(Object.keys(declaratorPaths[0].scope.bindings)).to.eql([ + expect(Object.keys(declaratorPaths[0].scope.bindings)).to.deep.equal([ 'globalScope', 'stillGlobalScope', ]); - expect(Object.keys(declaratorPaths[1].scope.bindings)).to.eql(['middleScope']); - expect(Object.keys(declaratorPaths[2].scope.bindings)).to.eql(['insideFnScope']); + expect(Object.keys(declaratorPaths[1].scope.bindings)).to.deep.equal(['middleScope']); + expect(Object.keys(declaratorPaths[2].scope.bindings)).to.deep.equal(['insideFnScope']); }); }); @@ -420,8 +420,10 @@ describe('swcTraverse', () => { expect(babelScopes.length).to.equal(swcScopes.length); for (let i = 0; i < babelScopes.length; i += 1) { expect(babelScopes[i].uid - babelRootScopeIdOffset).to.equal(swcScopes[i].id); - expect(Object.keys(babelScopes[i].bindings)).to.eql(Object.keys(swcScopes[i].bindings)); - // expect(babelScopes[i].references).to.eql(swcResults[i].references); + expect(Object.keys(babelScopes[i].bindings)).to.deep.equal( + Object.keys(swcScopes[i].bindings), + ); + // expect(babelScopes[i].references).to.deep.equal(swcResults[i].references); } } 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 index 46ba6d8a5..3122bde5e 100644 --- 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 @@ -38,9 +38,9 @@ describe('traverseHtml', () => { }, }); - expect(foundDivs).to.eql(['a-lvl1', 'b']); - expect(foundSpans).to.eql(['a-lvl2']); - expect(foundMyTags).to.eql(['a-lvl3']); + expect(foundDivs).to.deep.equal(['a-lvl1', 'b']); + expect(foundSpans).to.deep.equal(['a-lvl2']); + expect(foundMyTags).to.deep.equal(['a-lvl3']); }); it('traverses different levels in DOM order', async () => { @@ -72,7 +72,7 @@ describe('traverseHtml', () => { 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']); + expect(callOrder).to.deep.equal(['div#a-lvl1', 'span#a-lvl2', 'my-tag#a-lvl3', 'div#b']); }); it('allows to stop traversal (for performance)', async () => { @@ -104,7 +104,7 @@ describe('traverseHtml', () => { }; traverseHtml(ast, processObj); - expect(callOrder).to.eql(['div#a-lvl1']); + expect(callOrder).to.deep.equal(['div#a-lvl1']); }); it('allows to traverse within a path', async () => { @@ -135,6 +135,6 @@ describe('traverseHtml', () => { }; traverseHtml(ast, processObj); - expect(callOrder).to.eql(['my-tag#a-lvl3', 'not-found#a-lvl4']); + expect(callOrder).to.deep.equal(['my-tag#a-lvl3', 'not-found#a-lvl4']); }); }); diff --git a/packages-node/providence-analytics/tsconfig.json b/packages-node/providence-analytics/tsconfig.json index d9551e1d4..b24742e39 100644 --- a/packages-node/providence-analytics/tsconfig.json +++ b/packages-node/providence-analytics/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "./dist-types", "rootDir": "." }, - "include": ["types"], + "include": ["types", "src", "test-node"], "exclude": ["dist-types"] } diff --git a/packages-node/providence-analytics/types/core/core.d.ts b/packages-node/providence-analytics/types/core/core.d.ts index 7823604cd..4644022e9 100644 --- a/packages-node/providence-analytics/types/core/core.d.ts +++ b/packages-node/providence-analytics/types/core/core.d.ts @@ -1,4 +1,5 @@ import { File } from '@babel/types'; +import Vol from 'memfs'; /** * The name of a variable in a local context. Examples: @@ -152,6 +153,8 @@ export interface ProjectInputDataWithAstMeta extends ProjectInputDataWithMeta { */ export type AnyMatchString = string; +export type FsAdapter = Vol; + export type ProvidenceConfig = { /* Whether analyzer should be run or a grep should be performed */ queryMethod: 'ast' | 'grep'; @@ -169,6 +172,7 @@ export type ProvidenceConfig = { writeLogFile: boolean; skipCheckMatchCompatibility: boolean; fallbackToBabel: boolean; + fs: FsAdapter; }; /** @@ -182,6 +186,7 @@ export type PackageJson = { devDependencies?: { [dependency: string]: string }; workspaces?: string[]; main?: string; + exports?: { [key: string]: string }; }; export type LernaJson = {