fix: consume optimised-glob and fs-adapter internally; optimize code; optimize dependencies

This commit is contained in:
Thijs Louisse 2024-05-08 17:46:19 +02:00
parent c0cf85de70
commit f6693b3bc4
50 changed files with 688 additions and 904 deletions

View file

@ -34,36 +34,29 @@
"prepublishOnly": "npm run publish-docs", "prepublishOnly": "npm run publish-docs",
"test:node": "npm run test:node:unit && npm run test:node:e2e", "test:node": "npm run test:node:unit && npm run test:node:e2e",
"test:node:e2e": "mocha './test-node/**/*.e2e.js' --timeout 60000", "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": { "dependencies": {
"@babel/core": "^7.21.4", "@babel/parser": "^7.24.5",
"@babel/parser": "^7.21.4", "@babel/plugin-syntax-import-assertions": "^7.24.1",
"@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/traverse": "^7.24.5",
"@babel/plugin-syntax-export-default-from": "^7.18.6", "@babel/types": "^7.24.5",
"@babel/plugin-syntax-import-assertions": "^7.20.0", "@rollup/plugin-node-resolve": "^15.2.3",
"@babel/register": "^7.21.0", "@swc/core": "^1.5.5",
"@babel/traverse": "^7.21.4", "@web/dev-server": "^0.4.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",
"commander": "^2.20.3", "commander": "^2.20.3",
"glob": "^8.1.0", "inquirer": "^9.2.20",
"inquirer": "^9.1.5", "lit-element": "^4.0.5",
"is-negated-glob": "^1.0.0",
"lit-element": "~3.3.1",
"parse5": "^7.1.2", "parse5": "^7.1.2",
"read-package-tree": "5.3.1", "semver": "^7.6.1",
"semver": "^7.3.8",
"swc-to-babel": "^1.26.0" "swc-to-babel": "^1.26.0"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^4.3.4", "@types/chai": "^4.3.16",
"@types/inquirer": "^9.0.3", "@types/inquirer": "^9.0.7",
"@types/mocha": "^10.0.1", "@types/mocha": "^10.0.6",
"@web/dev-server-core": "^0.4.0", "@web/dev-server-core": "^0.7.1",
"globby": "^14.0.1",
"mock-fs": "^5.2.0", "mock-fs": "^5.2.0",
"mock-require": "^3.0.3" "mock-require": "^3.0.3"
}, },

View file

@ -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,

View file

@ -1,10 +1,11 @@
/* eslint-disable no-shadow */ /* eslint-disable no-shadow */
import pathLib from 'path'; import path from 'path';
import child_process from 'child_process'; // eslint-disable-line camelcase import child_process from 'child_process'; // eslint-disable-line camelcase
import glob from 'glob'; import { globbySync } from 'globby'; // eslint-disable-line import/no-extraneous-dependencies
import readPackageTree from '../program/utils/read-package-tree-with-bower-support.js'; import { optimisedGlob } from '../program/utils/optimised-glob.js';
import { LogService } from '../program/core/LogService.js'; import { LogService } from '../program/core/LogService.js';
import { toPosixPath } from '../program/utils/to-posix-path.js'; import { toPosixPath } from '../program/utils/to-posix-path.js';
import { fsAdapter } from '../program/utils/fs-adapter.js';
/** /**
* @param {any[]} arr * @param {any[]} arr
@ -59,13 +60,9 @@ export function pathsArrayFromCs(t, cwd = process.cwd()) {
return t; return t;
} }
if (t.includes('*')) { if (t.includes('*')) {
if (!t.endsWith('/')) { return globbySync(t, { cwd, absolute: true, onlyFiles: false }).map(toPosixPath);
// eslint-disable-next-line no-param-reassign
t = `${t}/`;
}
return glob.sync(t, { cwd, absolute: true }).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)]; 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 * Returns all sub projects matching condition supplied in matchFn
* @param {string[]} rootPaths all search-target project paths * @param {string[]} rootPaths all search-target project paths
@ -143,44 +182,15 @@ export async function appendProjectDependencyPaths(
matchPattern, matchPattern,
modes = ['npm', 'bower'], modes = ['npm', 'bower'],
) { ) {
let matchFn; const matcher = getMatcher(matchPattern);
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}`,
);
}
}
/** @type {string[]} */ /** @type {string[]} */
const depProjectPaths = []; const depProjectPaths = [];
for (const targetPath of rootPaths) { for (const targetPath of rootPaths) {
for (const mode of modes) { for (const mode of modes) {
await readPackageTree( depProjectPaths.push(...(await readPackageTree(targetPath, matcher, mode)));
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,
);
} }
} }
// Write all data to {outputPath}/projectDeps.json
// const projectDeps = {};
// rootPaths.forEach(rootP => {
// depProjectPaths.filter(depP => depP.startsWith(rootP)).;
// });
return depProjectPaths.concat(rootPaths).map(toPosixPath); return depProjectPaths.concat(rootPaths).map(toPosixPath);
} }
@ -192,7 +202,7 @@ export async function appendProjectDependencyPaths(
*/ */
export async function installDeps(searchTargetPaths) { export async function installDeps(searchTargetPaths) {
for (const targetPath of 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 { try {
await spawnProcess('npm i --no-progress', { cwd: targetPath }); await spawnProcess('npm i --no-progress', { cwd: targetPath });
} catch (e) { } catch (e) {
@ -200,7 +210,7 @@ export async function installDeps(searchTargetPaths) {
LogService.error(e); LogService.error(e);
} }
LogService.info(`Installing bower dependencies for ${pathLib.basename(targetPath)}`); LogService.info(`Installing bower dependencies for ${path.basename(targetPath)}`);
try { try {
await spawnProcess(`bower i --production --force-latest`, { cwd: targetPath }); await spawnProcess(`bower i --production --force-latest`, { cwd: targetPath });
} catch (e) { } catch (e) {
@ -211,14 +221,14 @@ export async function installDeps(searchTargetPaths) {
} }
export const _cliHelpersModule = { export const _cliHelpersModule = {
csToArray,
extensionsFromCs,
setQueryMethod,
pathsArrayFromCs,
targetDefault,
appendProjectDependencyPaths, appendProjectDependencyPaths,
pathsArrayFromCollectionName,
extensionsFromCs,
pathsArrayFromCs,
setQueryMethod,
targetDefault,
spawnProcess, spawnProcess,
installDeps, installDeps,
pathsArrayFromCollectionName, csToArray,
flatten, flatten,
}; };

View file

@ -1,7 +1,8 @@
import child_process from 'child_process'; // eslint-disable-line camelcase import child_process from 'child_process'; // eslint-disable-line camelcase
import path from 'path'; import path from 'path';
import fs from 'fs';
import commander from 'commander'; import commander from 'commander';
import { LogService } from '../program/core/LogService.js'; import { LogService } from '../program/core/LogService.js';
import { QueryService } from '../program/core/QueryService.js'; import { QueryService } from '../program/core/QueryService.js';
import { InputDataService } from '../program/core/InputDataService.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 { _cliHelpersModule } from './cli-helpers.js';
import { _extendDocsModule } from './launch-providence-with-extend-docs.js'; import { _extendDocsModule } from './launch-providence-with-extend-docs.js';
import { _promptAnalyzerMenuModule } from './prompt-analyzer-menu.js'; import { _promptAnalyzerMenuModule } from './prompt-analyzer-menu.js';
import { fsAdapter } from '../program/utils/fs-adapter.js';
/** /**
* @typedef {import('../../types/index.js').AnalyzerName} AnalyzerName * @typedef {import('../../types/index.js').AnalyzerName} AnalyzerName
@ -19,7 +21,10 @@ import { _promptAnalyzerMenuModule } from './prompt-analyzer-menu.js';
*/ */
const { version } = JSON.parse( 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; const { extensionsFromCs, setQueryMethod, targetDefault, installDeps } = _cliHelpersModule;

View file

@ -1,6 +1,5 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import fs from 'fs'; import path from 'path';
import pathLib from 'path';
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
import { _providenceModule } from '../program/providence.js'; import { _providenceModule } from '../program/providence.js';
import { QueryService } from '../program/core/QueryService.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 { LogService } from '../program/core/LogService.js';
import { flatten } from './cli-helpers.js'; import { flatten } from './cli-helpers.js';
import MatchPathsAnalyzer from '../program/analyzers/match-paths.js'; import MatchPathsAnalyzer from '../program/analyzers/match-paths.js';
import { fsAdapter } from '../program/utils/fs-adapter.js';
/** /**
* @typedef {import('../../types/index.js').PathFromSystemRoot} PathFromSystemRoot * @typedef {import('../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
@ -33,7 +33,7 @@ export async function getExtendDocsResults({
allowlistReference, allowlistReference,
cwd, cwd,
}) { }) {
const monoPkgs = InputDataService.getMonoRepoPackages(cwd); const monoPkgs = await InputDataService.getMonoRepoPackages(cwd);
const results = await _providenceModule.providence( const results = await _providenceModule.providence(
await QueryService.getQueryConfigFromAnalyzer(MatchPathsAnalyzer, { prefix: prefixCfg }), await QueryService.getQueryConfigFromAnalyzer(MatchPathsAnalyzer, { prefix: prefixCfg }),
@ -71,7 +71,7 @@ export async function getExtendDocsResults({
const normalizedP = `./${p}`; const normalizedP = `./${p}`;
if (pathStr.startsWith(normalizedP)) { if (pathStr.startsWith(normalizedP)) {
const localPath = pathStr.replace(normalizedP, ''); // 'lea-tabs.js' 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 true;
} }
return false; return false;
@ -124,12 +124,12 @@ export async function launchProvidenceWithExtendDocs({
}); });
// Write results // 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)) { if (fsAdapter.fs.existsSync(outputFilePath)) {
fs.unlinkSync(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) { if (err) {
throw err; throw err;
} }

View file

@ -1,4 +1,3 @@
import fs from 'fs';
import path from 'path'; import path from 'path';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import traverse from '@babel/traverse'; import traverse from '@babel/traverse';
@ -7,6 +6,7 @@ import { AstService } from '../program/core/AstService.js';
import { LogService } from '../program/core/LogService.js'; import { LogService } from '../program/core/LogService.js';
import JsdocCommentParser from '../program/utils/jsdoc-comment-parser.js'; import JsdocCommentParser from '../program/utils/jsdoc-comment-parser.js';
import { getCurrentDir } from '../program/utils/get-current-dir.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 * @typedef {import('../../types/index.js').TargetDepsObj} TargetDepsObj
@ -42,7 +42,7 @@ function getPropsFromParsedJsDoc(jsdoc) {
* @param {PathFromSystemRoot} file * @param {PathFromSystemRoot} file
*/ */
function getAnalyzerOptions(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 }); const babelAst = AstService.getAst(code, 'swc-to-babel', { filePath: file });
let commentNode; let commentNode;
@ -74,7 +74,7 @@ function getAnalyzerOptions(file) {
* @param {PathFromSystemRoot} dir * @param {PathFromSystemRoot} dir
* @param {boolean} [shouldGetOptions] * @param {boolean} [shouldGetOptions]
*/ */
function gatherAnalyzers(dir, shouldGetOptions) { async function gatherAnalyzers(dir, shouldGetOptions) {
return InputDataService.gatherFilesFromDir(dir, { depth: 0 }).map(file => { return InputDataService.gatherFilesFromDir(dir, { depth: 0 }).map(file => {
const analyzerObj = { file, name: path.basename(file, '.js') }; const analyzerObj = { file, name: path.basename(file, '.js') };
if (shouldGetOptions) { if (shouldGetOptions) {
@ -98,7 +98,7 @@ export async function promptAnalyzerConfigMenu(
path.resolve(getCurrentDir(import.meta.url), '../program/analyzers') 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); const analyzer = menuOptions.find(o => o.name === analyzerName);
if (!analyzer) { if (!analyzer) {
LogService.error(`[promptAnalyzerConfigMenu] analyzer "${analyzerName}" not found.`); LogService.error(`[promptAnalyzerConfigMenu] analyzer "${analyzerName}" not found.`);

View file

@ -1,9 +1,9 @@
import fs from 'fs'; import path from 'path';
import pathLib from 'path';
import { startDevServer } from '@web/dev-server'; import { startDevServer } from '@web/dev-server';
import { ReportService } from '../program/core/ReportService.js'; import { ReportService } from '../program/core/ReportService.js';
import { providenceConfUtil } from '../program/utils/providence-conf-util.js'; import { providenceConfUtil } from '../program/utils/providence-conf-util.js';
import { getCurrentDir } from '../program/utils/get-current-dir.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 * @typedef {import('../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
@ -25,7 +25,7 @@ async function getCachedProvidenceResults({
*/ */
let outputFilePaths; let outputFilePaths;
try { try {
outputFilePaths = fs.readdirSync(resultsPath); outputFilePaths = fsAdapter.fs.readdirSync(resultsPath);
} catch (_) { } catch (_) {
throw new Error(`Please make sure providence results can be found in ${resultsPath}`); throw new Error(`Please make sure providence results can be found in ${resultsPath}`);
} }
@ -33,7 +33,9 @@ async function getCachedProvidenceResults({
const resultFiles = {}; const resultFiles = {};
let searchTargetDeps; let searchTargetDeps;
outputFilePaths.forEach(fileName => { 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') { if (fileName === 'search-target-deps-file.json') {
searchTargetDeps = content; searchTargetDeps = content;
} else { } else {
@ -62,8 +64,8 @@ function createMiddleWares({ providenceConf, providenceConfRaw, searchTargetDeps
*/ */
function getPackageJson(projectPath) { function getPackageJson(projectPath) {
try { try {
const file = pathLib.resolve(projectPath, 'package.json'); const file = path.resolve(projectPath, 'package.json');
return JSON.parse(fs.readFileSync(file, 'utf8')); return JSON.parse(fsAdapter.fs.readFileSync(file, 'utf8'));
} catch (_) { } catch (_) {
return null; return null;
} }
@ -85,7 +87,7 @@ function createMiddleWares({ providenceConf, providenceConfRaw, searchTargetDeps
return res; return res;
} }
const pathFromServerRootToHere = `/${pathLib.relative( const pathFromServerRootToHere = `/${path.relative(
process.cwd(), process.cwd(),
getCurrentDir(import.meta.url), 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 ./) // Needed for dev purposes (we call it from ./packages-node/providence-analytics/ instead of ./)
// Allows es-dev-server to find the right moduleDirs // Allows es-dev-server to find the right moduleDirs
const fromPackageRoot = process.argv.includes('--serve-from-package-root'); 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 { return {
appIndex: pathLib.resolve(getCurrentDir(import.meta.url), 'index.html'), appIndex: path.resolve(getCurrentDir(import.meta.url), 'index.html'),
rootDir: moduleRoot, rootDir: moduleRoot,
nodeResolve: true, nodeResolve: true,
moduleDirs: pathLib.resolve(moduleRoot, 'node_modules'), moduleDirs: path.resolve(moduleRoot, 'node_modules'),
watch: false, watch: false,
open: true, open: true,
middleware: createMiddleWares({ middleware: createMiddleWares({

View file

@ -241,7 +241,7 @@ export default class FindClassesAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const analyzerResult = this._prepare(cfg); const analyzerResult = await this._prepare(cfg);
if (analyzerResult) { if (analyzerResult) {
return analyzerResult; return analyzerResult;
} }

View file

@ -109,7 +109,7 @@ export default class FindCustomelementsAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const cachedAnalyzerResult = this._prepare(cfg); const cachedAnalyzerResult = await this._prepare(cfg);
if (cachedAnalyzerResult) { if (cachedAnalyzerResult) {
return cachedAnalyzerResult; return cachedAnalyzerResult;
} }

View file

@ -124,7 +124,7 @@ export default class FindImportsSwcAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const cachedAnalyzerResult = this._prepare(cfg); const cachedAnalyzerResult = await this._prepare(cfg);
if (cachedAnalyzerResult) { if (cachedAnalyzerResult) {
return cachedAnalyzerResult; return cachedAnalyzerResult;
} }

View file

@ -1,6 +1,5 @@
/* eslint-disable no-shadow */ /* eslint-disable no-shadow */
// @ts-nocheck // @ts-nocheck
import fs from 'fs';
import pathLib from 'path'; import pathLib from 'path';
import babelTraverse from '@babel/traverse'; import babelTraverse from '@babel/traverse';
import { isRelativeSourcePath, toRelativeSourcePath } from '../../utils/relative-source-path.js'; 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 { AstService } from '../../core/AstService.js';
import { LogService } from '../../core/LogService.js'; import { LogService } from '../../core/LogService.js';
import { memoize } from '../../utils/memoize.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').RootFile} RootFile
@ -23,7 +23,7 @@ import { memoize } from '../../utils/memoize.js';
* @param {string} projectName * @param {string} projectName
*/ */
function isSelfReferencingProject(source, projectName) { function isSelfReferencingProject(source, projectName) {
return source.startsWith(`${projectName}`); return source.split('/')[0] === projectName;
} }
/** /**
@ -184,7 +184,7 @@ async function trackDownIdentifierFn(
specifier: '[default]', 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 babelAst = AstService.getAst(code, 'swc-to-babel', { filePath: resolvedSourcePath });
const shouldLookForDefaultExport = identifierName === '[default]'; const shouldLookForDefaultExport = identifierName === '[default]';

View file

@ -1,4 +1,3 @@
import fs from 'fs';
import path from 'path'; import path from 'path';
import { swcTraverse } from '../../utils/swc-traverse.js'; import { swcTraverse } from '../../utils/swc-traverse.js';
import { isRelativeSourcePath, toRelativeSourcePath } from '../../utils/relative-source-path.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 { resolveImportPath } from '../../utils/resolve-import-path.js';
import { AstService } from '../../core/AstService.js'; import { AstService } from '../../core/AstService.js';
import { memoize } from '../../utils/memoize.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').SpecifierSource} SpecifierSource
* @typedef {import('../../../../types/index.js').IdentifierName} IdentifierName * @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 * @typedef {import('../../../../types/index.js').SwcPath} SwcPath
*/ */
@ -20,7 +20,7 @@ import { memoize } from '../../utils/memoize.js';
* @param {string} projectName * @param {string} projectName
*/ */
function isSelfReferencingProject(source, projectName) { function isSelfReferencingProject(source, projectName) {
return source.startsWith(`${projectName}`); return source.split('/')[0] === projectName;
} }
/** /**
@ -193,7 +193,7 @@ async function trackDownIdentifierFn(
specifier: '[default]', 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 swcAst = AstService._getSwcAst(code);
const shouldLookForDefaultExport = identifierName === '[default]'; const shouldLookForDefaultExport = identifierName === '[default]';

View file

@ -192,14 +192,11 @@ export default class MatchImportsAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const cachedAnalyzerResult = this._prepare(cfg); const cachedAnalyzerResult = await this._prepare(cfg);
if (cachedAnalyzerResult) { if (cachedAnalyzerResult) {
return cachedAnalyzerResult; return cachedAnalyzerResult;
} }
/**
* Traverse
*/
let { referenceProjectResult } = cfg; let { referenceProjectResult } = cfg;
if (!referenceProjectResult) { if (!referenceProjectResult) {
const findExportsAnalyzer = new FindExportsAnalyzer(); const findExportsAnalyzer = new FindExportsAnalyzer();
@ -222,6 +219,9 @@ export default class MatchImportsAnalyzer extends Analyzer {
}); });
} }
/**
* Traverse
*/
const queryOutput = await matchImportsPostprocess( const queryOutput = await matchImportsPostprocess(
referenceProjectResult, referenceProjectResult,
targetProjectResult, targetProjectResult,

View file

@ -198,7 +198,6 @@ function getVariablePaths(
} }
/** /**
*
* @param {FindCustomelementsAnalyzerResult} targetFindCustomelementsResult * @param {FindCustomelementsAnalyzerResult} targetFindCustomelementsResult
* @param {FindCustomelementsAnalyzerResult} refFindCustomelementsResult * @param {FindCustomelementsAnalyzerResult} refFindCustomelementsResult
* @param {FindExportsAnalyzerResult} refFindExportsResult * @param {FindExportsAnalyzerResult} refFindExportsResult
@ -240,8 +239,10 @@ function getTagPaths(
if (!matchSubclassSpecifierRootFile) { if (!matchSubclassSpecifierRootFile) {
return false; return false;
} }
const sameRoot = entry.rootFile.file === matchSubclassSpecifierRootFile.file; const sameRoot = entry.rootFile.file === matchSubclassSpecifierRootFile.file;
const sameIdentifier = entry.rootFile.specifier === matchSubclassEntry.exportSpecifier.name; const sameIdentifier = entry.rootFile.specifier === matchSubclassEntry.exportSpecifier.name;
return sameRoot && sameIdentifier; return sameRoot && sameIdentifier;
}); });
if (refPathMatch) { if (refPathMatch) {
@ -274,11 +275,11 @@ function matchPathsPostprocess(
/** @type {AnalyzerQueryResult} */ /** @type {AnalyzerQueryResult} */
const resultsArray = []; const resultsArray = [];
targetMatchSubclassesResult.queryOutput.forEach(matchSubclassEntry => { for (const matchSubclassEntry of targetMatchSubclassesResult.queryOutput) {
const fromClass = matchSubclassEntry.exportSpecifier.name; const fromClass = matchSubclassEntry.exportSpecifier.name;
matchSubclassEntry.matchesPerProject.forEach(projectMatch => { for (const projectMatch of matchSubclassEntry.matchesPerProject) {
projectMatch.files.forEach(({ identifier: toClass, file: targetMatchedFile }) => { for (const { identifier: toClass, file: targetMatchedFile } of projectMatch.files) {
const resultEntry = { const resultEntry = {
name: fromClass, name: fromClass,
}; };
@ -293,7 +294,7 @@ function matchPathsPostprocess(
refProjectName, refProjectName,
); );
if (paths && paths.length) { if (paths?.length) {
resultEntry.variable = { resultEntry.variable = {
from: fromClass, from: fromClass,
to: toClass, to: toClass,
@ -324,9 +325,9 @@ function matchPathsPostprocess(
if (resultEntry.variable || resultEntry.tag) { if (resultEntry.variable || resultEntry.tag) {
resultsArray.push(resultEntry); resultsArray.push(resultEntry);
} }
}); }
}); }
}); }
return resultsArray; return resultsArray;
} }
@ -394,7 +395,7 @@ export default class MatchPathsAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const analyzerResult = this._prepare(cfg); const analyzerResult = await this._prepare(cfg);
if (analyzerResult) { if (analyzerResult) {
return analyzerResult; return analyzerResult;
} }

View file

@ -312,7 +312,7 @@ export default class MatchSubclassesAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const analyzerResult = this._prepare(cfg); const analyzerResult = await this._prepare(cfg);
if (analyzerResult) { if (analyzerResult) {
return analyzerResult; return analyzerResult;
} }

View file

@ -54,9 +54,8 @@ async function analyzePerAstFile(projectData, astAnalysis, analyzerCfg) {
* @param {object[]|object} data * @param {object[]|object} data
*/ */
function posixify(data) { function posixify(data) {
if (!data) { if (!data) return;
return;
}
if (Array.isArray(data)) { if (Array.isArray(data)) {
data.forEach(posixify); data.forEach(posixify);
} else if (typeof data === 'object') { } else if (typeof data === 'object') {
@ -212,7 +211,7 @@ export class Analyzer {
* @param {AnalyzerConfig} cfg * @param {AnalyzerConfig} cfg
* @returns {CachedAnalyzerResult|undefined} * @returns {CachedAnalyzerResult|undefined}
*/ */
_prepare(cfg) { async _prepare(cfg) {
LogService.debug(`Analyzer "${this.name}": started _prepare method`); LogService.debug(`Analyzer "${this.name}": started _prepare method`);
/** @type {typeof Analyzer} */ (this.constructor).__unwindProvidedResults(cfg); /** @type {typeof Analyzer} */ (this.constructor).__unwindProvidedResults(cfg);
@ -281,14 +280,14 @@ export class Analyzer {
* Get reference and search-target data * Get reference and search-target data
*/ */
if (!cfg.targetProjectResult) { if (!cfg.targetProjectResult) {
this.targetData = InputDataService.createDataObject( this.targetData = await InputDataService.createDataObject(
[cfg.targetProjectPath], [cfg.targetProjectPath],
cfg.gatherFilesConfig, cfg.gatherFilesConfig,
); );
} }
if (cfg.referenceProjectPath) { if (cfg.referenceProjectPath) {
this.referenceData = InputDataService.createDataObject( this.referenceData = await InputDataService.createDataObject(
[cfg.referenceProjectPath], [cfg.referenceProjectPath],
cfg.gatherFilesConfigReference || cfg.gatherFilesConfig, cfg.gatherFilesConfigReference || cfg.gatherFilesConfig,
); );
@ -333,7 +332,7 @@ export class Analyzer {
if (!projectPath) { if (!projectPath) {
LogService.error(`[Analyzer._traverse]: you must provide a projectPath`); LogService.error(`[Analyzer._traverse]: you must provide a projectPath`);
} }
finalTargetData = InputDataService.createDataObject([ finalTargetData = await InputDataService.createDataObject([
{ {
project: { project: {
name: projectName || '[n/a]', name: projectName || '[n/a]',
@ -366,7 +365,7 @@ export class Analyzer {
/** /**
* Prepare * Prepare
*/ */
const cachedAnalyzerResult = this._prepare(cfg); const cachedAnalyzerResult = await this._prepare(cfg);
if (cachedAnalyzerResult) { if (cachedAnalyzerResult) {
return cachedAnalyzerResult; return cachedAnalyzerResult;
} }

View file

@ -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 child_process from 'child_process'; // eslint-disable-line camelcase
import glob from 'glob'; import path from 'path';
import anymatch from 'anymatch';
// @ts-expect-error import { getFilePathRelativeFromRoot } from '../utils/get-file-path-relative-from-root.js';
import isNegatedGlob from 'is-negated-glob'; 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 { LogService } from './LogService.js';
import { AstService } from './AstService.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').FindImportsAnalyzerResult} FindImportsAnalyzerResult
* @typedef {import('../../../types/index.js').FindImportsAnalyzerEntry} FindImportsAnalyzerEntry * @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').PathRelative} PathRelative
* @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName
* @typedef {import('../../../types/index.js').QueryConfig} QueryConfig * @typedef {import('../../../types/index.js').QueryConfig} QueryConfig
* @typedef {import('../../../types/index.js').QueryResult} QueryResult * @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').ProjectName} ProjectName
* @typedef {import('../../../types/index.js').PackageJson} PackageJson * @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) => { const getPackageJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => {
try { try {
const fileContent = fs.readFileSync(`${rootPath}/package.json`, 'utf8'); const fileContent = fsAdapter.fs.readFileSync(`${rootPath}/package.json`, 'utf8');
return JSON.parse(fileContent); return JSON.parse(fileContent);
} catch (_) { } catch (_) {
try { try {
// For testing purposes, we allow to have a package.mock.json that contains 'fictional' // For testing purposes, we allow to have a package.mock.json that contains 'fictional'
// packages (like 'exporting-ref-project') not on npm registry // 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); return JSON.parse(fileContent);
} catch (__) { } catch (__) {
return undefined; return undefined;
@ -65,7 +60,7 @@ const getPackageJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => {
*/ */
const getLernaJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { const getLernaJson = memoize((/** @type {PathFromSystemRoot} */ rootPath) => {
try { try {
const fileContent = fs.readFileSync(`${rootPath}/lerna.json`, 'utf8'); const fileContent = fsAdapter.fs.readFileSync(`${rootPath}/lerna.json`, 'utf8');
return JSON.parse(fileContent); return JSON.parse(fileContent);
} catch (_) { } catch (_) {
return undefined; 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<ProjectNameAndPath[]>} GetPathsFromGlobListFn
* @type {GetPathsFromGlobListFn} * @type {GetPathsFromGlobListFn}
*/ */
const getPathsFromGlobList = memoize( const getPathsFromGlobList = memoize(
( async (
/** @type {PathFromSystemRoot[]|string[]} */ list, /** @type {PathFromSystemRoot[]|string[]} */ list,
/** @type {PathFromSystemRoot} */ rootPath, /** @type {PathFromSystemRoot} */ rootPath,
) => { ) => {
/** @type {string[]} */ /** @type {string[]} */
const results = []; const results = [];
list.forEach(pathOrGlob => { for (const pathOrGlob of list) {
if (!pathOrGlob.endsWith('/')) {
// eslint-disable-next-line no-param-reassign
pathOrGlob = `${pathOrGlob}/`;
}
if (pathOrGlob.includes('*')) { if (pathOrGlob.includes('*')) {
const globResults = glob.sync(pathOrGlob, { cwd: rootPath, absolute: false }); const globResults = await optimisedGlob(pathOrGlob, {
globResults.forEach(r => { cwd: rootPath,
results.push(r); absolute: false,
onlyFiles: false,
}); });
results.push(...globResults);
} else { } else {
results.push(pathOrGlob); results.push(pathOrGlob);
} }
}); }
return results.map(pkgPath => { return results.map(pkgPath => {
const packageRoot = pathLib.resolve(rootPath, pkgPath); const packageRoot = path.resolve(rootPath, pkgPath);
const basename = pathLib.basename(pkgPath); const basename = path.basename(pkgPath);
const pkgJson = getPackageJson(/** @type {PathFromSystemRoot} */ (packageRoot)); const pkgJson = getPackageJson(/** @type {PathFromSystemRoot} */ (packageRoot));
const name = /** @type {ProjectName} */ ((pkgJson && pkgJson.name) || basename); const name = /** @type {ProjectName} */ ((pkgJson && pkgJson.name) || basename);
return { name, path: /** @type {PathFromSystemRoot} */ (pkgPath) }; return { name, path: /** @type {PathFromSystemRoot} */ (pkgPath) };
@ -114,7 +106,7 @@ const getPathsFromGlobList = memoize(
*/ */
const getGitignoreFile = memoize((/** @type {PathFromSystemRoot} */ rootPath) => { const getGitignoreFile = memoize((/** @type {PathFromSystemRoot} */ rootPath) => {
try { try {
return fs.readFileSync(`${rootPath}/.gitignore`, 'utf8'); return fsAdapter.fs.readFileSync(`${rootPath}/.gitignore`, 'utf8');
} catch (_) { } catch (_) {
return undefined; return undefined;
} }
@ -131,6 +123,7 @@ const getGitIgnorePaths = memoize((/** @type {PathFromSystemRoot} */ rootPath) =
} }
const entries = fileContent.split('\n').filter(entry => { const entries = fileContent.split('\n').filter(entry => {
// eslint-disable-next-line no-param-reassign
entry = entry.trim(); entry = entry.trim();
if (entry.startsWith('#')) { if (entry.startsWith('#')) {
return false; return false;
@ -143,15 +136,19 @@ const getGitIgnorePaths = memoize((/** @type {PathFromSystemRoot} */ rootPath) =
// normalize entries to be compatible with anymatch // normalize entries to be compatible with anymatch
const normalizedEntries = entries.map(entry => { const normalizedEntries = entries.map(entry => {
// eslint-disable-next-line no-param-reassign
entry = toPosixPath(entry); entry = toPosixPath(entry);
if (entry.startsWith('/')) { if (entry.startsWith('/')) {
// eslint-disable-next-line no-param-reassign
entry = entry.slice(1); entry = entry.slice(1);
} }
const isFile = entry.indexOf('.') > 0; // index of 0 means hidden file. const isFile = entry.indexOf('.') > 0; // index of 0 means hidden file.
if (entry.endsWith('/')) { if (entry.endsWith('/')) {
// eslint-disable-next-line no-param-reassign
entry += '**'; entry += '**';
} else if (!isFile) { } else if (!isFile) {
// eslint-disable-next-line no-param-reassign
entry += '/**'; entry += '/**';
} }
return entry; return entry;
@ -189,30 +186,6 @@ function ensureArray(v) {
return Array.isArray(v) ? v : [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 * @param {string} localPathWithDotSlash
* @returns {string} * @returns {string}
@ -261,26 +234,28 @@ export class InputDataService {
* @param {Partial<GatherFilesConfig>} gatherFilesConfig * @param {Partial<GatherFilesConfig>} gatherFilesConfig
* @returns {ProjectInputDataWithMeta[]} * @returns {ProjectInputDataWithMeta[]}
*/ */
static createDataObject(projectPaths, gatherFilesConfig = {}) { static async createDataObject(projectPaths, gatherFilesConfig = {}) {
/** @type {ProjectInputData[]} */ /** @type {ProjectInputData[]} */
const inputData = projectPaths.map(projectPathOrObj => { const inputData = [];
for (const projectPathOrObj of projectPaths) {
if (typeof projectPathOrObj === 'object') { if (typeof projectPathOrObj === 'object') {
// ProjectInputData was provided already manually // ProjectInputData was provided already manually
return projectPathOrObj; inputData.push(projectPathOrObj);
continue; // eslint-disable-line no-continue
} }
const projectPath = projectPathOrObj; const projectPath = projectPathOrObj;
return { inputData.push({
project: /** @type {Project} */ ({ project: /** @type {Project} */ ({
name: pathLib.basename(projectPath), name: path.basename(projectPath),
path: projectPath, path: projectPath,
}), }),
entries: this.gatherFilesFromDir(projectPath, { entries: await this.gatherFilesFromDir(projectPath, {
...this.defaultGatherFilesConfig, ...this.defaultGatherFilesConfig,
...gatherFilesConfig, ...gatherFilesConfig,
}), }),
}; });
}); }
// @ts-ignore // @ts-ignore
return this._addMetaToProjectsData(inputData); return this._addMetaToProjectsData(inputData);
} }
@ -333,7 +308,7 @@ export class InputDataService {
let commitHash; let commitHash;
let isGitRepo; let isGitRepo;
try { try {
isGitRepo = fs.lstatSync(pathLib.resolve(projectPath, '.git')).isDirectory(); isGitRepo = fsAdapter.fs.lstatSync(path.resolve(projectPath, '.git')).isDirectory();
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} catch (_) {} } catch (_) {}
@ -372,7 +347,7 @@ export class InputDataService {
projectObj.entries.forEach(entry => { projectObj.entries.forEach(entry => {
let code; let code;
try { try {
code = fs.readFileSync(entry, 'utf8'); code = fsAdapter.fs.readFileSync(entry, 'utf8');
} catch (e) { } catch (e) {
LogService.error(`Could not find "${entry}"`); LogService.error(`Could not find "${entry}"`);
} }
@ -380,7 +355,7 @@ export class InputDataService {
toPosixPath(entry), toPosixPath(entry),
toPosixPath(projectObj.project.path), toPosixPath(projectObj.project.path),
); );
if (pathLib.extname(file) === '.html') { if (path.extname(file) === '.html') {
const extractedScripts = AstService.getScriptsFromHtml(/** @type {string} */ (code)); const extractedScripts = AstService.getScriptsFromHtml(/** @type {string} */ (code));
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
extractedScripts.forEach((code, i) => { extractedScripts.forEach((code, i) => {
@ -409,19 +384,16 @@ export class InputDataService {
if (this.__targetProjectPaths) { if (this.__targetProjectPaths) {
return this.__targetProjectPaths; return this.__targetProjectPaths;
} }
const submoduleDir = pathLib.resolve( const submoduleDir = path.resolve(__dirname, '../../../providence-input-data/search-targets');
__dirname,
'../../../providence-input-data/search-targets',
);
let dirs; let dirs;
try { try {
dirs = fs.readdirSync(submoduleDir); dirs = fsAdapter.fs.readdirSync(submoduleDir);
} catch (_) { } catch (_) {
return []; return [];
} }
return dirs return dirs
.map(dir => /** @type {PathFromSystemRoot} */ (pathLib.join(submoduleDir, dir))) .map(dir => /** @type {PathFromSystemRoot} */ (path.join(submoduleDir, dir)))
.filter(dirPath => fs.lstatSync(dirPath).isDirectory()); .filter(dirPath => fsAdapter.fs.lstatSync(dirPath).isDirectory());
} }
static set targetProjectPaths(v) { static set targetProjectPaths(v) {
@ -438,11 +410,11 @@ export class InputDataService {
let dirs; let dirs;
try { try {
const referencesDir = pathLib.resolve(__dirname, '../../../providence-input-data/references'); const referencesDir = path.resolve(__dirname, '../../../providence-input-data/references');
dirs = fs.readdirSync(referencesDir); dirs = fsAdapter.fs.readdirSync(referencesDir);
dirs = dirs dirs = dirs
.map(dir => pathLib.join(referencesDir, dir)) .map(dir => path.join(referencesDir, dir))
.filter(dirPath => fs.lstatSync(dirPath).isDirectory()); .filter(dirPath => fsAdapter.fs.lstatSync(dirPath).isDirectory());
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} catch (_) {} } catch (_) {}
return /** @type {PathFromSystemRoot[]} */ (dirs); return /** @type {PathFromSystemRoot[]} */ (dirs);
@ -457,31 +429,31 @@ export class InputDataService {
*/ */
static get defaultGatherFilesConfig() { static get defaultGatherFilesConfig() {
return { return {
extensions: ['.js'],
allowlist: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'], allowlist: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'],
extensions: ['.js'],
depth: Infinity, depth: Infinity,
}; };
} }
/** /**
* @param {PathFromSystemRoot} startPath * @protected
* @param {GatherFilesConfig} cfg * @param {number} depth
* @param {boolean} withoutDepth * @param {string[]} extensions
* @returns {string}
*/ */
static getGlobPattern(startPath, cfg, withoutDepth = false) { static _getDefaultGlobDepthPattern(depth = Infinity, extensions = ['.js']) {
// if startPath ends with '/', remove // `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`;
let globPattern = startPath.replace(/\/$/, ''); const extensionsGlobPart = `.{${extensions.map(extension => extension.slice(1)).join(',')},}`;
if (process.platform === 'win32') { if (depth === Infinity) {
globPattern = globPattern.replace(/^.:/, '').replace(/\\/g, '/'); return `**/*${extensionsGlobPart}`;
} }
if (!withoutDepth) { if (depth > 1) {
if (typeof cfg.depth === 'number' && cfg.depth !== Infinity) { return `${`/*`.repeat(depth + 1)}${extensionsGlobPart}`;
globPattern += `/*`.repeat(cfg.depth + 1);
} else {
globPattern += `/**/*`;
}
} }
return { globPattern }; if (depth === 0) {
return `*${extensionsGlobPart}`;
}
return '';
} }
/** /**
@ -498,9 +470,9 @@ export class InputDataService {
* Gets an array of files for given extension * Gets an array of files for given extension
* @param {PathFromSystemRoot} startPath - local filesystem path * @param {PathFromSystemRoot} startPath - local filesystem path
* @param {Partial<GatherFilesConfig>} customConfig - configuration object * @param {Partial<GatherFilesConfig>} customConfig - configuration object
* @returns {PathFromSystemRoot[]} result list of file paths * @returns {Promise<PathFromSystemRoot[]>} result list of file paths
*/ */
static gatherFilesFromDir(startPath, customConfig = {}) { static async gatherFilesFromDir(startPath, customConfig = {}) {
const cfg = { const cfg = {
...this.defaultGatherFilesConfig, ...this.defaultGatherFilesConfig,
...customConfig, ...customConfig,
@ -523,84 +495,79 @@ export class InputDataService {
if (cfg.allowlistMode === 'export-map') { if (cfg.allowlistMode === 'export-map') {
const pkgJson = getPackageJson(startPath); const pkgJson = getPackageJson(startPath);
if (!pkgJson.exports) { if (!pkgJson?.exports) {
LogService.error(`No exports found in package.json of ${startPath}`); 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, packageRootPath: startPath,
}); });
return exposedAndInternalPaths return exposedAndInternalPaths
.map(p => p.internal) .map(p => p.internal)
.filter(p => cfg.extensions.includes(`${pathLib.extname(p)}`)); .filter(p => cfg.extensions.includes(`${path.extname(p)}`));
} }
/** @type {string[]} */ /** @type {string[]} */
let gitIgnorePaths = []; const negativeGitGlobs = [];
/** @type {string[]} */ /** @type {string[]} */
let npmPackagePaths = []; const npmGlobs = [];
const allowlistMode = cfg.allowlistMode || this._determineAllowListMode(startPath); const allowlistMode = cfg.allowlistMode || this._determineAllowListMode(startPath);
if (allowlistMode === 'git') { if (allowlistMode === 'git') {
gitIgnorePaths = getGitIgnorePaths(startPath); negativeGitGlobs.push(
...getGitIgnorePaths(startPath).map(gitIgnorePath => `!${gitIgnorePath}`),
);
} else if (allowlistMode === 'npm') { } else if (allowlistMode === 'npm') {
npmPackagePaths = getNpmPackagePaths(startPath); npmGlobs.push(...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;
});
} }
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}'`); LogService.warn(`No files found for path '${startPath}'`);
return []; return [];
} }
// reappend startPath
// const res = filteredGlobRes.map(f => pathLib.resolve(startPath, f));
return /** @type {PathFromSystemRoot[]} */ (filteredGlobRes.map(toPosixPath)); return /** @type {PathFromSystemRoot[]} */ (filteredGlobRes.map(toPosixPath));
} }
@ -617,9 +584,9 @@ export class InputDataService {
/** /**
* Gives back all monorepo package paths * Gives back all monorepo package paths
* @param {PathFromSystemRoot} rootPath * @param {PathFromSystemRoot} rootPath
* @returns {ProjectNameAndPath[]|undefined} * @ returns {ProjectNameAndPath[]|undefined}
*/ */
static getMonoRepoPackages(rootPath) { static async getMonoRepoPackages(rootPath) {
// [1] Look for npm/yarn workspaces // [1] Look for npm/yarn workspaces
const pkgJson = getPackageJson(rootPath); const pkgJson = getPackageJson(rootPath);
if (pkgJson?.workspaces) { if (pkgJson?.workspaces) {
@ -641,7 +608,7 @@ export class InputDataService {
* @param {string} opts.packageRootPath * @param {string} opts.packageRootPath
* @returns {Promise<{internalExportMapPaths:string[]; exposedExportMapPaths:string[]}>} * @returns {Promise<{internalExportMapPaths:string[]; exposedExportMapPaths:string[]}>}
*/ */
static getPathsFromExportMap(exports, { nodeResolveMode = 'default', packageRootPath }) { static async getPathsFromExportMap(exports, { nodeResolveMode = 'default', packageRootPath }) {
const exportMapPaths = []; const exportMapPaths = [];
for (const [key, valObjOrStr] of Object.entries(exports)) { for (const [key, valObjOrStr] of Object.entries(exports)) {
@ -672,25 +639,24 @@ export class InputDataService {
const valueToUseForGlob = stripDotSlashFromLocalPath(resolvedVal).replace('*', '**/*'); const valueToUseForGlob = stripDotSlashFromLocalPath(resolvedVal).replace('*', '**/*');
// Generate all possible entries via glob, first strip './' // Generate all possible entries via glob, first strip './'
const internalExportMapPathsForKeyRaw = glob.sync(valueToUseForGlob, { const internalExportMapPathsForKeyRaw = await optimisedGlob(valueToUseForGlob, {
cwd: packageRootPath, cwd: packageRootPath,
nodir: true, onlyFiles: true,
}); });
const exposedExportMapPathsForKeyRaw = internalExportMapPathsForKeyRaw.map(pathInside => { const exposedExportMapPathsForKeyRaw = internalExportMapPathsForKeyRaw.map(pathInside => {
// Say we have "exports": { "./*.js": "./src/*.js" } // Say we have "exports": { "./*.js": "./src/*.js" }
// => internalExportMapPathsForKey: ['./src/a.js', './src/b.js'] // => internalExportMapPathsForKey: ['./src/a.js', './src/b.js']
// => exposedExportMapPathsForKey: ['./a.js', './b.js'] // => exposedExportMapPathsForKey: ['./a.js', './b.js']
const [, variablePart] = pathInside.match( const [, variablePart] =
new RegExp(valueToUseForGlob.replace('*', '(.*)')), pathInside.match(new RegExp(valueToUseForGlob.replace('*', '(.*)'))) || [];
);
return resolvedKey.replace('*', variablePart); return resolvedKey.replace('*', variablePart);
}); });
const internalExportMapPathsForKey = internalExportMapPathsForKeyRaw.map(filePath => const internalExportMapPathsForKey = internalExportMapPathsForKeyRaw.map(
normalizeLocalPathWithDotSlash(filePath), normalizeLocalPathWithDotSlash,
); );
const exposedExportMapPathsForKey = exposedExportMapPathsForKeyRaw.map(filePath => const exposedExportMapPathsForKey = exposedExportMapPathsForKeyRaw.map(
normalizeLocalPathWithDotSlash(filePath), normalizeLocalPathWithDotSlash,
); );
exportMapPaths.push( exportMapPaths.push(
@ -704,9 +670,6 @@ export class InputDataService {
return exportMapPaths; 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.getProjectMeta = memoize(InputDataService.getProjectMeta);
InputDataService.gatherFilesFromDir = memoize(InputDataService.gatherFilesFromDir); InputDataService.gatherFilesFromDir = memoize(InputDataService.gatherFilesFromDir);

View file

@ -1,5 +1,5 @@
import pathLib from 'path'; import path from 'path';
import fs from 'fs'; import { fsAdapter } from '../utils/fs-adapter.js';
const { log } = console; const { log } = console;
@ -111,14 +111,14 @@ export class LogService {
} }
static writeLogFile() { static writeLogFile() {
const filePath = pathLib.join(process.cwd(), 'providence.log'); const filePath = path.join(process.cwd(), 'providence.log');
let file = `[log ${new Date()}]\n`; let file = `[log ${new Date()}]\n`;
// @ts-ignore // @ts-ignore
this._logHistory.forEach(l => { this._logHistory.forEach(l => {
file += `${l}\n`; file += `${l}\n`;
}); });
file += `[/log ${new Date()}]\n\n`; file += `[/log ${new Date()}]\n\n`;
fs.writeFileSync(filePath, file, { flag: 'a' }); fsAdapter.fs.writeFileSync(filePath, file, { flag: 'a' });
// @ts-ignore // @ts-ignore
this._logHistory = []; this._logHistory = [];
} }

View file

@ -1,17 +1,18 @@
import fs from 'fs'; import path from 'path';
import pathLib from 'path';
import { getHash } from '../utils/get-hash.js'; import { getHash } from '../utils/get-hash.js';
// import { memoize } from '../utils/memoize.js'; import { fsAdapter } from '../utils/fs-adapter.js';
const memoize = fn => fn;
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').AnalyzerQueryResult} AnalyzerQueryResult
* @typedef {import('../../../types/index.js').PathFromSystemRoot} PathFromSystemRoot
* @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig * @typedef {import('../../../types/index.js').AnalyzerConfig} AnalyzerConfig
* @typedef {import('../../../types/index.js').AnalyzerName} AnalyzerName * @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').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, outputPath = this.outputPath,
) { ) {
const output = JSON.stringify(queryResult, null, 2); const output = JSON.stringify(queryResult, null, 2);
if (!fs.existsSync(outputPath)) { if (!fsAdapter.fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath); fsAdapter.fs.mkdirSync(outputPath);
} }
const { name } = queryResult.meta.analyzerMeta; const { name } = queryResult.meta.analyzerMeta;
const filePath = this._getResultFileNameAndPath(name, identifier); const filePath = this._getResultFileNameAndPath(name, identifier);
fs.writeFileSync(filePath, output, { flag: 'w' }); fsAdapter.fs.writeFileSync(filePath, output, { flag: 'w' });
} }
/** /**
* @type {string} * @type {string}
*/ */
static get outputPath() { 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) { static set outputPath(p) {
@ -93,7 +94,10 @@ export class ReportService {
let cachedResult; let cachedResult;
try { try {
cachedResult = JSON.parse( 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 // eslint-disable-next-line no-empty
} catch (_) {} } catch (_) {}
@ -107,7 +111,7 @@ export class ReportService {
*/ */
static _getResultFileNameAndPath(name, identifier) { static _getResultFileNameAndPath(name, identifier) {
return /** @type {PathFromSystemRoot} */ ( 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) { static writeEntryToSearchTargetDepsFile(depProj, rootProjectMeta) {
const rootProj = `${rootProjectMeta.name}#${rootProjectMeta.version}`; 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 = {}; let file = {};
try { try {
file = JSON.parse(fs.readFileSync(filePath, 'utf-8')); file = JSON.parse(fsAdapter.fs.readFileSync(filePath, 'utf-8'));
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} catch (_) {} } catch (_) {}
const deps = [...(file[rootProj] || []), depProj]; const deps = [...(file[rootProj] || []), depProj];
file[rootProj] = [...new Set(deps)]; 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); ReportService.createIdentifier = memoize(ReportService.createIdentifier);

View file

@ -1,8 +1,11 @@
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
import { ReportService } from './core/ReportService.js'; import nodeFs from 'fs';
import { InputDataService } from './core/InputDataService.js'; 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 { QueryService } from './core/QueryService.js';
import { fsAdapter } from './utils/fs-adapter.js';
import { LogService } from './core/LogService.js';
import { AstService } from './core/AstService.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 */ /** Allows to navigate to source file in code editor */
addSystemPathsInResult: false, addSystemPathsInResult: false,
fallbackToBabel: false, fallbackToBabel: false,
fs: nodeFs,
...customConfig, ...customConfig,
}); });
if (cfg.fs) {
// Allow to mock fs for testing
fsAdapter.setFs(cfg.fs);
}
if (cfg.debugEnabled) { if (cfg.debugEnabled) {
LogService.debugEnabled = true; LogService.debugEnabled = true;
} }
@ -215,7 +224,7 @@ export async function providence(queryConfig, customConfig) {
if (queryConfig.type === 'ast-analyzer') { if (queryConfig.type === 'ast-analyzer') {
queryResults = await handleAnalyzer(queryConfig, cfg); queryResults = await handleAnalyzer(queryConfig, cfg);
} else { } else {
const inputData = InputDataService.createDataObject( const inputData = await InputDataService.createDataObject(
cfg.targetProjectPaths, cfg.targetProjectPaths,
cfg.gatherFilesConfig, cfg.gatherFilesConfig,
); );

View file

@ -1,9 +1,9 @@
import fs from 'fs';
import path from 'path'; import path from 'path';
import babelTraversePkg from '@babel/traverse'; import babelTraversePkg from '@babel/traverse';
import { AstService } from '../core/AstService.js'; import { AstService } from '../core/AstService.js';
import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js'; import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js';
import { toPosixPath } from './to-posix-path.js'; import { toPosixPath } from './to-posix-path.js';
import { fsAdapter } from './fs-adapter.js';
/** /**
* @typedef {import('@babel/types').Node} Node * @typedef {import('@babel/types').Node} Node
@ -82,7 +82,7 @@ export async function getSourceCodeFragmentOfDeclaration({
exportedIdentifier, exportedIdentifier,
projectRootPath, 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 // TODO: fix swc-to-babel lib to make this compatible with 'swc-to-babel' mode of getAst
const babelAst = AstService.getAst(code, 'babel', { filePath }); const babelAst = AstService.getAst(code, 'babel', { filePath });

View file

@ -1,9 +1,9 @@
import fs from 'fs';
import path from 'path'; import path from 'path';
import { swcTraverse, getPathFromNode } from './swc-traverse.js'; import { swcTraverse, getPathFromNode } from './swc-traverse.js';
import { AstService } from '../core/AstService.js'; import { AstService } from '../core/AstService.js';
import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js'; import { trackDownIdentifier } from '../analyzers/helpers/track-down-identifier.js';
import { toPosixPath } from './to-posix-path.js'; import { toPosixPath } from './to-posix-path.js';
import { fsAdapter } from './fs-adapter.js';
/** /**
* @typedef {import('@swc/core').Node} SwcNode * @typedef {import('@swc/core').Node} SwcNode
@ -84,7 +84,7 @@ export async function getSourceCodeFragmentOfDeclaration({
exportedIdentifier, exportedIdentifier,
projectRootPath, 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 // compensate for swc span bug: https://github.com/swc-project/swc/issues/1366#issuecomment-1516539812
const offset = AstService._getSwcOffset(); const offset = AstService._getSwcOffset();

View file

@ -1,6 +1,6 @@
import pathLib from 'path'; import path from 'path';
import fs from 'fs';
import { pathToFileURL } from 'url'; import { pathToFileURL } from 'url';
import { fsAdapter } from './fs-adapter.js';
/** /**
* @typedef {import('../../../types/index.js').ProvidenceCliConf} ProvidenceCliConf * @typedef {import('../../../types/index.js').ProvidenceCliConf} ProvidenceCliConf
@ -10,12 +10,12 @@ import { pathToFileURL } from 'url';
* @returns {Promise<{providenceConf:Partial<ProvidenceCliConf>;providenceConfRaw:string}|null>} * @returns {Promise<{providenceConf:Partial<ProvidenceCliConf>;providenceConfRaw:string}|null>}
*/ */
async function getConf() { async function getConf() {
const confPathWithoutExtension = `${pathLib.join(process.cwd(), 'providence.conf')}`; const confPathWithoutExtension = `${path.join(process.cwd(), 'providence.conf')}`;
let confPathFound; let confPathFound;
try { try {
if (fs.existsSync(`${confPathWithoutExtension}.js`)) { if (fsAdapter.fs.existsSync(`${confPathWithoutExtension}.js`)) {
confPathFound = `${confPathWithoutExtension}.js`; confPathFound = `${confPathWithoutExtension}.js`;
} else if (fs.existsSync(`${confPathWithoutExtension}.mjs`)) { } else if (fsAdapter.fs.existsSync(`${confPathWithoutExtension}.mjs`)) {
confPathFound = `${confPathWithoutExtension}.mjs`; confPathFound = `${confPathWithoutExtension}.mjs`;
} }
} catch (_) { } catch (_) {
@ -36,7 +36,7 @@ async function getConf() {
); );
} }
const providenceConfRaw = fs.readFileSync(confPathFound, 'utf8'); const providenceConfRaw = fsAdapter.fs.readFileSync(confPathFound, 'utf8');
return { providenceConf, providenceConfRaw }; return { providenceConf, providenceConfRaw };
} }

View file

@ -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;

View file

@ -1,10 +1,12 @@
import { builtinModules } from 'module'; import { builtinModules } from 'module';
import path from 'path'; import path from 'path';
import { nodeResolve } from '@rollup/plugin-node-resolve'; import { nodeResolve } from '@rollup/plugin-node-resolve';
import { LogService } from '../core/LogService.js';
import { memoize } from './memoize.js';
import { toPosixPath } from './to-posix-path.js';
import { isRelativeSourcePath } from './relative-source-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 * @typedef {import('../../../types/index.js').PathRelativeFromProjectRoot} PathRelativeFromProjectRoot

View file

@ -5,7 +5,7 @@ import {
suppressNonCriticalLogs, suppressNonCriticalLogs,
restoreSuppressNonCriticalLogs, restoreSuppressNonCriticalLogs,
} from './mock-log-service-helpers.js'; } 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 * @typedef {import('../types/index.js').QueryResult} QueryResult
@ -20,17 +20,17 @@ export function setupAnalyzerTest() {
const originalReferenceProjectPaths = InputDataService.referenceProjectPaths; const originalReferenceProjectPaths = InputDataService.referenceProjectPaths;
const cacheDisabledQInitialValue = QueryService.cacheDisabled; const cacheDisabledQInitialValue = QueryService.cacheDisabled;
const cacheDisabledIInitialValue = memoizeConfig.isCacheDisabled; const cacheEnabledIInitialValue = memoize.isCacheEnabled;
before(() => { before(() => {
QueryService.cacheDisabled = true; QueryService.cacheDisabled = true;
memoizeConfig.isCacheDisabled = true; memoize.disableCaching();
suppressNonCriticalLogs(); suppressNonCriticalLogs();
}); });
after(() => { after(() => {
QueryService.cacheDisabled = cacheDisabledQInitialValue; QueryService.cacheDisabled = cacheDisabledQInitialValue;
memoizeConfig.isCacheDisabled = cacheDisabledIInitialValue; memoize.restoreCaching(cacheEnabledIInitialValue);
restoreSuppressNonCriticalLogs(); restoreSuppressNonCriticalLogs();
}); });

View file

@ -61,7 +61,7 @@ export class DummyAnalyzer extends Analyzer {
/** /**
* Prepare * Prepare
*/ */
const analyzerResult = this._prepare(cfg); const analyzerResult = await this._prepare(cfg);
if (analyzerResult) { if (analyzerResult) {
return analyzerResult; return analyzerResult;
} }

View file

@ -1,22 +1,24 @@
/* eslint-disable no-unused-expressions */ /* eslint-disable no-unused-expressions */
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import sinon from 'sinon';
import pathLib from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import pathLib from 'path';
import { expect } from 'chai'; import { expect } from 'chai';
import sinon from 'sinon';
import { it } from 'mocha'; 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 { 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 { 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 * @typedef {import('../../types/index.js').QueryResult} QueryResult
@ -50,7 +52,7 @@ describe('CLI helpers', () => {
describe('pathsArrayFromCs', () => { describe('pathsArrayFromCs', () => {
it('allows absolute paths', async () => { 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', '/mocked/path/example-project',
]); ]);
}); });
@ -58,14 +60,14 @@ describe('CLI helpers', () => {
it('allows relative paths', async () => { it('allows relative paths', async () => {
expect( expect(
pathsArrayFromCs('./test-helpers/project-mocks/importing-target-project', rootDir), 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( expect(
pathsArrayFromCs('test-helpers/project-mocks/importing-target-project', rootDir), 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 () => { 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`,
`${rootDir}/test-helpers/project-mocks-analyzer-outputs`, `${rootDir}/test-helpers/project-mocks-analyzer-outputs`,
]); ]);
@ -74,7 +76,7 @@ describe('CLI helpers', () => {
it('allows multiple comma separated paths', async () => { it('allows multiple comma separated paths', async () => {
const paths = const paths =
'test-helpers/project-mocks*, ./test-helpers/project-mocks/importing-target-project,/mocked/path/example-project'; '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`,
`${rootDir}/test-helpers/project-mocks-analyzer-outputs`, `${rootDir}/test-helpers/project-mocks-analyzer-outputs`,
`${rootDir}/test-helpers/project-mocks/importing-target-project`, `${rootDir}/test-helpers/project-mocks/importing-target-project`,
@ -87,7 +89,7 @@ describe('CLI helpers', () => {
it('gets collections from external target config', async () => { it('gets collections from external target config', async () => {
expect( expect(
pathsArrayFromCollectionName('lion-collection', 'search-target', externalCfgMock, rootDir), pathsArrayFromCollectionName('lion-collection', 'search-target', externalCfgMock, rootDir),
).to.eql( ).to.deep.equal(
externalCfgMock.searchTargetCollections['lion-collection'].map(p => externalCfgMock.searchTargetCollections['lion-collection'].map(p =>
toPosixPath(pathLib.join(rootDir, p)), toPosixPath(pathLib.join(rootDir, p)),
), ),
@ -102,7 +104,7 @@ describe('CLI helpers', () => {
externalCfgMock, externalCfgMock,
rootDir, rootDir,
), ),
).to.eql( ).to.deep.equal(
externalCfgMock.referenceCollections['lion-based-ui-collection'].map(p => externalCfgMock.referenceCollections['lion-based-ui-collection'].map(p =>
toPosixPath(pathLib.join(rootDir, p)), toPosixPath(pathLib.join(rootDir, p)),
), ),
@ -130,7 +132,7 @@ describe('CLI helpers', () => {
it('adds bower and node dependencies', async () => { it('adds bower and node dependencies', async () => {
const result = await appendProjectDependencyPaths(['/mocked/path/example-project']); 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/dependency-a',
'/mocked/path/example-project/node_modules/my-dependency', '/mocked/path/example-project/node_modules/my-dependency',
'/mocked/path/example-project/bower_components/dependency-b', '/mocked/path/example-project/bower_components/dependency-b',
@ -143,7 +145,7 @@ describe('CLI helpers', () => {
['/mocked/path/example-project'], ['/mocked/path/example-project'],
'/^dependency-/', '/^dependency-/',
); );
expect(result).to.eql([ expect(result).to.deep.equal([
'/mocked/path/example-project/node_modules/dependency-a', '/mocked/path/example-project/node_modules/dependency-a',
// in windows, it should not add '/mocked/path/example-project/node_modules/my-dependency', // in windows, it should not add '/mocked/path/example-project/node_modules/my-dependency',
'/mocked/path/example-project/bower_components/dependency-b', '/mocked/path/example-project/bower_components/dependency-b',
@ -151,7 +153,7 @@ describe('CLI helpers', () => {
]); ]);
const result2 = await appendProjectDependencyPaths(['/mocked/path/example-project'], '/b$/'); 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/bower_components/dependency-b',
'/mocked/path/example-project', '/mocked/path/example-project',
]); ]);
@ -163,7 +165,7 @@ describe('CLI helpers', () => {
undefined, undefined,
['npm'], ['npm'],
); );
expect(result).to.eql([ expect(result).to.deep.equal([
'/mocked/path/example-project/node_modules/dependency-a', '/mocked/path/example-project/node_modules/dependency-a',
'/mocked/path/example-project/node_modules/my-dependency', '/mocked/path/example-project/node_modules/my-dependency',
'/mocked/path/example-project', '/mocked/path/example-project',
@ -174,7 +176,7 @@ describe('CLI helpers', () => {
undefined, undefined,
['bower'], ['bower'],
); );
expect(result2).to.eql([ expect(result2).to.deep.equal([
'/mocked/path/example-project/bower_components/dependency-b', '/mocked/path/example-project/bower_components/dependency-b',
'/mocked/path/example-project', '/mocked/path/example-project',
]); ]);
@ -189,7 +191,7 @@ describe('CLI helpers', () => {
it('rewrites monorepo package paths when analysis is run from monorepo root', async () => { it('rewrites monorepo package paths when analysis is run from monorepo root', async () => {
// This fails after InputDataService.addAstToProjectsData is memoized // This fails after InputDataService.addAstToProjectsData is memoized
// (it does pass when run in isolation however, as a quick fix we disable memoization cache here...) // (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 // Since we use the print method here, we need to force Babel, bc swc-to-babel output is not compatible
// with @babel/generate // with @babel/generate
const initialAstServiceFallbackToBabel = AstService.fallbackToBabel; const initialAstServiceFallbackToBabel = AstService.fallbackToBabel;
@ -268,7 +270,7 @@ describe('CLI helpers', () => {
cwd: '/my-components', cwd: '/my-components',
}); });
expect(result).to.eql([ expect(result).to.deep.equal([
{ {
name: 'TheirButton', name: 'TheirButton',
variable: { variable: {

View file

@ -1,5 +1,5 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import pathLib from 'path'; import path from 'path';
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; import { it } from 'mocha';
import { appendProjectDependencyPaths } from '../../src/cli/cli-helpers.js'; import { appendProjectDependencyPaths } from '../../src/cli/cli-helpers.js';
@ -15,13 +15,13 @@ describe('CLI helpers against filesystem', () => {
describe('appendProjectDependencyPaths', () => { describe('appendProjectDependencyPaths', () => {
it('allows a regex filter', async () => { it('allows a regex filter', async () => {
const targetFilePath = toPosixPath( const targetFilePath = toPosixPath(
pathLib.resolve( path.resolve(
getCurrentDir(import.meta.url), getCurrentDir(import.meta.url),
'../../test-helpers/project-mocks/importing-target-project', '../../test-helpers/project-mocks/importing-target-project',
), ),
); );
const result = await appendProjectDependencyPaths([targetFilePath], '/^dep-/'); const result = await appendProjectDependencyPaths([targetFilePath], '/^dep-/');
expect(result).to.eql([ expect(result).to.deep.equal([
`${targetFilePath}/node_modules/dep-a`, `${targetFilePath}/node_modules/dep-a`,
// in windows, it should not add `${targetFilePath}/node_modules/my-dep-b`, // in windows, it should not add `${targetFilePath}/node_modules/my-dep-b`,
targetFilePath, targetFilePath,

View file

@ -11,7 +11,7 @@ import { _providenceModule } from '../../src/program/providence.js';
import { _cliHelpersModule } from '../../src/cli/cli-helpers.js'; import { _cliHelpersModule } from '../../src/cli/cli-helpers.js';
import { cli } from '../../src/cli/cli.js'; import { cli } from '../../src/cli/cli.js';
import { _promptAnalyzerMenuModule } from '../../src/cli/prompt-analyzer-menu.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 { _extendDocsModule } from '../../src/cli/launch-providence-with-extend-docs.js';
import { dashboardServer } from '../../src/dashboard/server.js'; import { dashboardServer } from '../../src/dashboard/server.js';
import { setupAnalyzerTest } from '../../test-helpers/setup-analyzer-test.js'; import { setupAnalyzerTest } from '../../test-helpers/setup-analyzer-test.js';
@ -120,7 +120,8 @@ describe('Providence CLI', () => {
projectPath: '/mocked/path/example-project', projectPath: '/mocked/path/example-project',
}, },
); );
memoizeConfig.isCacheDisabled = true; // memoizeConfig.isCacheDisabled = true;
memoize.disableCaching();
}); });
afterEach(() => { afterEach(() => {
@ -186,18 +187,26 @@ describe('Providence CLI', () => {
it('"-e --extensions"', async () => { it('"-e --extensions"', async () => {
await runCli(`${anyCmdThatAcceptsGlobalOpts} -e bla,blu`, rootDir); 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(); providenceStub.resetHistory();
await runCli(`${anyCmdThatAcceptsGlobalOpts} --extensions bla,blu`, rootDir); 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 () => { it('"-t --search-target-paths"', async () => {
await runCli(`${anyCmdThatAcceptsGlobalOpts} -t /mocked/path/example-project`, rootDir); await runCli(`${anyCmdThatAcceptsGlobalOpts} -t /mocked/path/example-project`, rootDir);
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); 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(); pathsArrayFromCsStub.resetHistory();
providenceStub.resetHistory(); providenceStub.resetHistory();
@ -207,13 +216,15 @@ describe('Providence CLI', () => {
rootDir, rootDir,
); );
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); 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 () => { it('"-r --reference-paths"', async () => {
await runCli(`${anyCmdThatAcceptsGlobalOpts} -r /mocked/path/example-project`, rootDir); await runCli(`${anyCmdThatAcceptsGlobalOpts} -r /mocked/path/example-project`, rootDir);
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); 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', '/mocked/path/example-project',
]); ]);
@ -225,7 +236,7 @@ describe('Providence CLI', () => {
rootDir, rootDir,
); );
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); 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', '/mocked/path/example-project',
]); ]);
}); });
@ -236,7 +247,9 @@ describe('Providence CLI', () => {
rootDir, rootDir,
); );
expect(pathsArrayFromCollectionStub.args[0][0]).to.equal('lion-collection'); 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 () => { it('"--reference-collection"', async () => {
@ -245,14 +258,14 @@ describe('Providence CLI', () => {
rootDir, rootDir,
); );
expect(pathsArrayFromCollectionStub.args[0][0]).to.equal('lion-based-ui-collection'); 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', '/mocked/path/example-project',
]); ]);
}); });
it('"-a --allowlist"', async () => { it('"-a --allowlist"', async () => {
await runCli(`${anyCmdThatAcceptsGlobalOpts} -a mocked/**/*,rocked/*`, rootDir); 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/**/*', 'mocked/**/*',
'rocked/*', 'rocked/*',
]); ]);
@ -260,7 +273,7 @@ describe('Providence CLI', () => {
providenceStub.resetHistory(); providenceStub.resetHistory();
await runCli(`${anyCmdThatAcceptsGlobalOpts} --allowlist mocked/**/*,rocked/*`, rootDir); 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/**/*', 'mocked/**/*',
'rocked/*', 'rocked/*',
]); ]);
@ -271,7 +284,7 @@ describe('Providence CLI', () => {
`${anyCmdThatAcceptsGlobalOpts} --allowlist-reference mocked/**/*,rocked/*`, `${anyCmdThatAcceptsGlobalOpts} --allowlist-reference mocked/**/*,rocked/*`,
rootDir, rootDir,
); );
expect(providenceStub.args[0][1].gatherFilesConfigReference.allowlist).to.eql([ expect(providenceStub.args[0][1].gatherFilesConfigReference.allowlist).to.deep.equal([
'mocked/**/*', 'mocked/**/*',
'rocked/*', 'rocked/*',
]); ]);
@ -311,7 +324,7 @@ describe('Providence CLI', () => {
await runCli(`${anyCmdThatAcceptsGlobalOpts} --target-dependencies`, rootDir); await runCli(`${anyCmdThatAcceptsGlobalOpts} --target-dependencies`, rootDir);
expect(appendProjectDependencyPathsStub.called).to.be.true; 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',
'/mocked/path/example-project/node_modules/mock-dep-a', '/mocked/path/example-project/node_modules/mock-dep-a',
'/mocked/path/example-project/bower_components/mock-dep-b', '/mocked/path/example-project/bower_components/mock-dep-b',
@ -355,13 +368,13 @@ describe('Providence CLI', () => {
it('"-c --config"', async () => { it('"-c --config"', async () => {
await runCli(`analyze match-analyzer-mock -c {"a":"2"}`, rootDir); await runCli(`analyze match-analyzer-mock -c {"a":"2"}`, rootDir);
expect(qConfStub.args[0][0]).to.equal('match-analyzer-mock'); 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(); qConfStub.resetHistory();
await runCli(`analyze match-analyzer-mock --config {"a":"2"}`, rootDir); await runCli(`analyze match-analyzer-mock --config {"a":"2"}`, rootDir);
expect(qConfStub.args[0][0]).to.equal('match-analyzer-mock'); 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 () => { it('calls "promptAnalyzerConfigMenu" without config given', async () => {
@ -417,7 +430,7 @@ describe('Providence CLI', () => {
rootDir, rootDir,
); );
expect(extendDocsStub.called).to.be.true; expect(extendDocsStub.called).to.be.true;
expect(extendDocsStub.args[0][0]).to.eql({ expect(extendDocsStub.args[0][0]).to.deep.equal({
referenceProjectPaths: ['/xyz/x'], referenceProjectPaths: ['/xyz/x'],
prefixCfg: { prefixCfg: {
from: 'pfrom', from: 'pfrom',

View file

@ -1,15 +1,16 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import fs from 'fs'; import { fileURLToPath, pathToFileURL } from 'url';
import pathLib from 'path'; import pathLib from 'path';
import sinon from 'sinon'; import sinon from 'sinon';
import { fileURLToPath, pathToFileURL } from 'url';
import { createTestServer } from '@web/dev-server-core/test-helpers';
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; 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 { createDashboardServerConfig } from '../../src/dashboard/server.js';
import { ReportService } from '../../src/program/core/ReportService.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 * @typedef {import('@web/dev-server-core').DevServer} DevServer
@ -27,7 +28,7 @@ const mockedOutputPath = pathLib.join(__dirname, 'fixtures/providence-output');
async function getConf(url) { async function getConf(url) {
const { href } = pathToFileURL(url); const { href } = pathToFileURL(url);
const { default: providenceConf } = await import(href); const { default: providenceConf } = await import(href);
const providenceConfRaw = fs.readFileSync(url, 'utf8'); const providenceConfRaw = fsAdapter.fs.readFileSync(url, 'utf8');
return { providenceConf, providenceConfRaw }; return { providenceConf, providenceConfRaw };
} }
@ -40,7 +41,7 @@ describe('Dashboard Server', () => {
let providenceConfStub; let providenceConfStub;
before(() => { 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; ReportService.outputPath = mockedOutputPath;
}); });
@ -81,8 +82,11 @@ describe('Dashboard Server', () => {
const response = await fetch(`${host}/menu-data.json`); const response = await fetch(`${host}/menu-data.json`);
expect(response.status).to.equal(200); expect(response.status).to.equal(200);
const responseJSON = await response.json(); const responseJSON = await response.json();
const expectedResult = fs.readFileSync(`${mockedResponsesPath}/menu-data.json`, 'utf8'); const expectedResult = fsAdapter.fs.readFileSync(
expect(responseJSON).to.eql(JSON.parse(expectedResult)); `${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`); const response = await fetch(`${host}/results.json`);
expect(response.status).to.equal(200); expect(response.status).to.equal(200);
const responseJson = await response.json(); const responseJson = await response.json();
const expectedResult = fs.readFileSync(`${mockedResponsesPath}/results.json`, 'utf8'); const expectedResult = fsAdapter.fs.readFileSync(
expect(responseJson).to.eql(JSON.parse(expectedResult)); `${mockedResponsesPath}/results.json`,
'utf8',
);
expect(responseJson).to.deep.equal(JSON.parse(expectedResult));
}); });
}); });

View file

@ -1,13 +1,12 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import pathLib, { dirname } from 'path'; import pathLib, { dirname } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import fs from 'fs';
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; import { it } from 'mocha';
import { providence } from '../../../../src/program/providence.js'; import { providence } from '../../../../src/program/providence.js';
import { QueryService } from '../../../../src/program/core/QueryService.js'; import { QueryService } from '../../../../src/program/core/QueryService.js';
import { ReportService } from '../../../../src/program/core/ReportService.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 { setupAnalyzerTest } from '../../../../test-helpers/setup-analyzer-test.js';
import { import {
FindExportsAnalyzer, FindExportsAnalyzer,
@ -18,6 +17,7 @@ import MatchSubclassesAnalyzer from '../../../../src/program/analyzers/match-sub
import MatchPathsAnalyzer from '../../../../src/program/analyzers/match-paths.js'; import MatchPathsAnalyzer from '../../../../src/program/analyzers/match-paths.js';
import FindCustomelementsAnalyzer from '../../../../src/program/analyzers/find-customelements.js'; import FindCustomelementsAnalyzer from '../../../../src/program/analyzers/find-customelements.js';
import FindClassesAnalyzer from '../../../../src/program/analyzers/find-classes.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 * @typedef {import('../../../../types/index.js').ProvidenceConfig} ProvidenceConfig
@ -48,13 +48,13 @@ describe('Analyzers file-system integration', () => {
const originalGetResultFileNameAndPath = ReportService._getResultFileNameAndPath; const originalGetResultFileNameAndPath = ReportService._getResultFileNameAndPath;
const originalOutputPath = ReportService.outputPath; const originalOutputPath = ReportService.outputPath;
const memoizeCacheDisabledInitial = memoizeConfig.isCacheDisabled; const memoizeCacheEnabledInitial = memoize.isCacheEnabled;
memoizeConfig.isCacheDisabled = true; memoize.disableCaching();
after(() => { after(() => {
ReportService._getResultFileNameAndPath = originalGetResultFileNameAndPath; ReportService._getResultFileNameAndPath = originalGetResultFileNameAndPath;
ReportService.outputPath = originalOutputPath; ReportService.outputPath = originalOutputPath;
memoizeConfig.isCacheDisabled = memoizeCacheDisabledInitial; memoize.restoreCaching(memoizeCacheEnabledInitial);
}); });
if (generateE2eMode) { if (generateE2eMode) {
@ -132,7 +132,7 @@ describe('Analyzers file-system integration', () => {
return; return;
} }
const expectedOutput = JSON.parse( const expectedOutput = JSON.parse(
fs.readFileSync( fsAdapter.fs.readFileSync(
pathLib.resolve( pathLib.resolve(
__dirname, __dirname,
`../../../../test-helpers/project-mocks-analyzer-outputs/${ctor.analyzerName}.json`, `../../../../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])); const { queryOutput } = JSON.parse(JSON.stringify(queryResults[0]));
expect(queryOutput).not.to.eql([]); expect(queryOutput).not.to.deep.equal([]);
expect(queryOutput).to.eql(expectedOutput.queryOutput); expect(queryOutput).to.deep.equal(expectedOutput.queryOutput);
}); });
} }
}); });

View file

@ -25,7 +25,7 @@ describe('Analyzer "find-classes"', async () => {
mockProject([`class EmptyClass {}`]); mockProject([`class EmptyClass {}`]);
const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const queryResults = await providence(findClassesQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result).to.eql([ expect(firstEntry.result).to.deep.equal([
{ {
name: 'EmptyClass', name: 'EmptyClass',
isMixin: false, isMixin: false,
@ -41,7 +41,7 @@ describe('Analyzer "find-classes"', async () => {
mockProject([`const m = superclass => class MyMixin extends superclass {}`]); mockProject([`const m = superclass => class MyMixin extends superclass {}`]);
const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const queryResults = await providence(findClassesQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result).to.eql([ expect(firstEntry.result).to.deep.equal([
{ {
name: 'MyMixin', name: 'MyMixin',
superClasses: [ superClasses: [
@ -72,7 +72,7 @@ describe('Analyzer "find-classes"', async () => {
}); });
const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const queryResults = await providence(findClassesQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result[1].superClasses).to.eql([ expect(firstEntry.result[1].superClasses).to.deep.equal([
{ {
isMixin: true, isMixin: true,
name: 'Mixin', name: 'Mixin',
@ -109,7 +109,7 @@ describe('Analyzer "find-classes"', async () => {
]); ]);
const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const queryResults = await providence(findClassesQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result[0].members.methods).to.eql([ expect(firstEntry.result[0].members.methods).to.deep.equal([
{ {
accessType: 'public', accessType: 'public',
name: 'method', name: 'method',
@ -145,7 +145,7 @@ describe('Analyzer "find-classes"', async () => {
]); ]);
const queryResults = await providence(findClassesQueryConfig, _providenceCfg); const queryResults = await providence(findClassesQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result[0].members.props).to.eql([ expect(firstEntry.result[0].members.props).to.deep.equal([
{ {
accessType: 'public', accessType: 'public',
kind: ['get', 'set'], kind: ['get', 'set'],

View file

@ -58,7 +58,7 @@ describe('Analyzer "find-customelements"', async () => {
const queryResults = await providence(findCustomelementsQueryConfig, _providenceCfg); const queryResults = await providence(findCustomelementsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult); const firstEntry = getEntry(queryResult);
expect(firstEntry.result[0].rootFile).to.eql({ expect(firstEntry.result[0].rootFile).to.deep.equal({
file: './src/CustomEl.js', file: './src/CustomEl.js',
specifier: 'CustomEl', specifier: 'CustomEl',
}); });

View file

@ -26,7 +26,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstResult = getEntry(queryResults[0]).result[0]; 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; expect(firstResult.source).to.be.undefined;
}); });
@ -34,7 +34,7 @@ describe('Analyzer "find-exports"', async () => {
mockProject([`export default class X {}`]); mockProject([`export default class X {}`]);
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstResult = getEntry(queryResults[0]).result[0]; 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; expect(firstResult.source).to.be.undefined;
}); });
@ -43,7 +43,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstResult = getEntry(queryResults[0]).result[0]; 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; expect(firstResult.source).to.be.undefined;
}); });
@ -56,7 +56,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstResult = getEntry(queryResults[0]).result[0]; const firstResult = getEntry(queryResults[0]).result[0];
expect(firstResult).to.eql({ expect(firstResult).to.deep.equal({
exportSpecifiers: ['[default]'], exportSpecifiers: ['[default]'],
source: undefined, source: undefined,
rootFileMap: [ rootFileMap: [
@ -68,7 +68,7 @@ describe('Analyzer "find-exports"', async () => {
}); });
const secondEntry = getEntry(queryResults[0], 1); const secondEntry = getEntry(queryResults[0], 1);
expect(secondEntry.result[0]).to.eql({ expect(secondEntry.result[0]).to.deep.equal({
exportSpecifiers: ['namedExport'], exportSpecifiers: ['namedExport'],
source: './file-with-default-export.js', source: './file-with-default-export.js',
localMap: [{ exported: 'namedExport', local: '[default]' }], 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.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]'); expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]');
expect(firstEntry.result[0].source).to.equal('./styles.css'); 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]', currentFileSpecifier: '[default]',
rootFile: { rootFile: {
file: './styles.css', 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.length).to.equal(1);
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]'); expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]');
expect(firstEntry.result[0].source).to.equal('./styles.css'); 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]', currentFileSpecifier: '[default]',
rootFile: { rootFile: {
file: './styles.css', file: './styles.css',
@ -161,7 +161,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
// This info will be relevant later to identify 'transitive' relations // 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', local: 'x',
exported: 'y', exported: 'y',
@ -174,7 +174,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(2); 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'); 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 secondEntry = getEntry(queryResults[0], 1);
const thirdEntry = getEntry(queryResults[0], 2); 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 currentFileSpecifier: 'MyComp', // this is the local name in the file we track from
rootFile: { rootFile: {
@ -199,16 +199,7 @@ describe('Analyzer "find-exports"', async () => {
}, },
}, },
]); ]);
expect(secondEntry.result[0].rootFileMap).to.eql([ expect(secondEntry.result[0].rootFileMap).to.deep.equal([
{
currentFileSpecifier: 'InBetweenComp',
rootFile: {
file: './src/OriginalComp.js',
specifier: 'OriginalComp',
},
},
]);
expect(thirdEntry.result[0].rootFileMap).to.eql([
{ {
currentFileSpecifier: 'OriginalComp', currentFileSpecifier: 'OriginalComp',
rootFile: { 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 () => { it(`stores rootFileMap of an exported Identifier`, async () => {
@ -236,7 +236,7 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); const firstEntry = getEntry(queryResults[0]);
expect(firstEntry.result[0].rootFileMap).to.eql([ expect(firstEntry.result[0].rootFileMap).to.deep.equal([
{ {
currentFileSpecifier: '[default]', currentFileSpecifier: '[default]',
rootFile: { rootFile: {
@ -252,7 +252,7 @@ describe('Analyzer "find-exports"', async () => {
mockProject([`// some comment here...`]); mockProject([`// some comment here...`]);
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const firstEntry = getEntry(queryResults[0]); 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); expect(firstEntry.result[0].source).to.equal(undefined);
}); });
}); });
@ -322,10 +322,10 @@ describe('Analyzer "find-exports"', async () => {
const queryResults = await providence(findExportsQueryConfig, _providenceCfg); const queryResults = await providence(findExportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const [firstEntry, secondEntry, thirdEntry] = getEntries(queryResult); const [firstEntry, secondEntry, thirdEntry] = getEntries(queryResult);
expect(firstEntry.meta.categories).to.eql(['fooCategory']); expect(firstEntry.meta.categories).to.deep.equal(['fooCategory']);
// not mutually exclusive... // not mutually exclusive...
expect(secondEntry.meta.categories).to.eql(['barCategory', 'testCategory']); expect(secondEntry.meta.categories).to.deep.equal(['barCategory', 'testCategory']);
expect(thirdEntry.meta.categories).to.eql([]); expect(thirdEntry.meta.categories).to.deep.equal([]);
}); });
}); });
}); });

View file

@ -25,7 +25,7 @@ describe('Analyzer "find-imports"', async () => {
const queryResults = await providence(findImportsQueryConfig, _providenceCfg); const queryResults = await providence(findImportsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult); 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'); expect(firstEntry.result[0].source).to.equal('imported/source');
}); });
@ -128,7 +128,7 @@ describe('Analyzer "find-imports"', async () => {
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const firstEntry = getEntry(queryResult); const firstEntry = getEntry(queryResult);
// This info will be relevant later to identify transitive relations // 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', local: 'y',
imported: 'x', imported: 'x',
}); });
@ -332,7 +332,7 @@ describe('Analyzer "find-imports"', async () => {
// Should be normalized source...? // Should be normalized source...?
expect(queryResult.queryOutput[0].source).to.equal('@external/source.js'); 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].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/file1.js',
'fictional-project/file2.js', 'fictional-project/file2.js',
]); ]);

View file

@ -39,7 +39,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/declarationOfMyClass.js', file: './src/declarationOfMyClass.js',
specifier: 'MyClass', specifier: 'MyClass',
}); });
@ -71,7 +71,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/declarationOfMyClass.js', file: './src/declarationOfMyClass.js',
specifier: 'MyClass', specifier: 'MyClass',
}); });
@ -105,7 +105,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/declarationOfMyClass.js', file: './src/declarationOfMyClass.js',
specifier: '[default]', specifier: '[default]',
}); });
@ -131,7 +131,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: '@external/source', file: '@external/source',
specifier: '[default]', specifier: '[default]',
}); });
@ -162,7 +162,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './MyClass.js', file: './MyClass.js',
specifier: '[default]', specifier: '[default]',
}); });
@ -201,7 +201,7 @@ describe('trackdownIdentifier', () => {
rootPath, rootPath,
projectName, projectName,
); );
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './MyClass.js', file: './MyClass.js',
specifier: '[default]', specifier: '[default]',
}); });
@ -232,7 +232,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/declarationOfMyNumber.js', file: './src/declarationOfMyNumber.js',
specifier: 'myNumber', specifier: 'myNumber',
}); });
@ -260,7 +260,7 @@ describe('trackdownIdentifier', () => {
const rootPath = '/my/project'; const rootPath = '/my/project';
const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath); const rootFile = await trackDownIdentifier(source, identifierName, currentFilePath, rootPath);
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './packages/accordion/IngAccordionContent.js', file: './packages/accordion/IngAccordionContent.js',
specifier: 'IngAccordionContent', specifier: 'IngAccordionContent',
}); });
@ -277,7 +277,7 @@ describe('trackdownIdentifier', () => {
currentFilePath2, currentFilePath2,
rootPath2, rootPath2,
); );
expect(rootFile2).to.eql({ expect(rootFile2).to.deep.equal({
file: './packages/accordion/IngAccordionInvokerButton.js', file: './packages/accordion/IngAccordionInvokerButton.js',
specifier: 'IngAccordionInvokerButton', specifier: 'IngAccordionInvokerButton',
}); });
@ -321,7 +321,7 @@ describe('trackDownIdentifierFromScope', () => {
fullCurrentFilePath, fullCurrentFilePath,
projectPath, projectPath,
); );
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: '[current]', file: '[current]',
specifier: 'MyClass', specifier: 'MyClass',
}); });
@ -372,7 +372,7 @@ describe('trackDownIdentifierFromScope', () => {
fullCurrentFilePath, fullCurrentFilePath,
projectPath, projectPath,
); );
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/declarationOfMyClass.js', file: './src/declarationOfMyClass.js',
specifier: 'MyClass', specifier: 'MyClass',
}); });
@ -420,7 +420,7 @@ describe('trackDownIdentifierFromScope', () => {
fullCurrentFilePath, fullCurrentFilePath,
projectPath, projectPath,
); );
expect(rootFile).to.eql({ expect(rootFile).to.deep.equal({
file: './src/classes.js', file: './src/classes.js',
specifier: 'El1', specifier: 'El1',
}); });

View file

@ -215,14 +215,14 @@ describe('Analyzer "match-imports"', async () => {
); );
const [name, filePath, project] = targetExportedId.split('::'); const [name, filePath, project] = targetExportedId.split('::');
expect(matchedEntry.exportSpecifier).to.eql({ expect(matchedEntry.exportSpecifier).to.deep.equal({
name, name,
filePath, filePath,
project, project,
id: targetExportedId, id: targetExportedId,
}); });
expect(matchedEntry.matchesPerProject[0].project).to.equal('importing-target-project'); 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', () => { describe('Extracting exports', () => {
@ -435,7 +435,7 @@ describe('Analyzer "match-imports"', async () => {
}); });
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput[0].exportSpecifier.name).to.equal('[default]'); 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' }, { files: ['./importDefault1.js', './importDefault2.js'], project: 'target' },
]); ]);
}); });
@ -476,11 +476,11 @@ describe('Analyzer "match-imports"', async () => {
}); });
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput[0].exportSpecifier.name).to.equal('[default]'); 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' }, { files: ['./deep-imports.js'], project: 'target' },
]); ]);
expect(queryResult.queryOutput[1].exportSpecifier.name).to.equal('RefClass'); 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' }, { files: ['./deep-imports.js'], project: 'target' },
]); ]);
}); });

View file

@ -188,7 +188,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(searchTargetProject, referenceProject); mockTargetAndReferenceProject(searchTargetProject, referenceProject);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput).to.eql(expectedMatches); expect(queryResult.queryOutput).to.deep.equal(expectedMatches);
}); });
describe('Features', () => { describe('Features', () => {
@ -239,7 +239,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(targetProj, refProj); mockTargetAndReferenceProject(targetProj, refProj);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './index.js',
to: './target-src/TargetClass.js', to: './target-src/TargetClass.js',
}); });
@ -263,7 +263,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(targetProjWithMultipleExports, refProj); mockTargetAndReferenceProject(targetProjWithMultipleExports, refProj);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './index.js',
to: './reexportFromRoot.js', to: './reexportFromRoot.js',
}); });
@ -296,7 +296,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(targetProjWithMultipleExportsAndMainEntry, refProj); mockTargetAndReferenceProject(targetProjWithMultipleExportsAndMainEntry, refProj);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './index.js',
to: './target-src/mainEntry.js', to: './target-src/mainEntry.js',
}); });
@ -308,8 +308,11 @@ describe('Analyzer "match-paths"', async () => {
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const unprefixedPaths = queryResult.queryOutput[0].variable.paths[0]; const unprefixedPaths = queryResult.queryOutput[0].variable.paths[0];
expect(unprefixedPaths).to.eql({ from: './index.js', to: './target-src/TargetClass.js' }); expect(unprefixedPaths).to.deep.equal({
expect(queryResult.queryOutput[0].variable.paths[1]).to.eql({ 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)}`, from: `${refProj.name}/${unprefixedPaths.from.slice(2)}`,
to: unprefixedPaths.to, to: unprefixedPaths.to,
}); });
@ -336,11 +339,11 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(targetProjMultipleTargetExtensions, refProj); mockTargetAndReferenceProject(targetProjMultipleTargetExtensions, refProj);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './index.js',
to: './target-src/TargetClass.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', from: './index.js',
to: './target-src/TargetSomething.js', to: './target-src/TargetSomething.js',
}); });
@ -410,7 +413,7 @@ describe('Analyzer "match-paths"', async () => {
); );
const queryResults = await providence(matchPathsQueryConfigFilter, _providenceCfg); const queryResults = await providence(matchPathsQueryConfigFilter, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './index.js',
to: './target-src/TargetClass.js', to: './target-src/TargetClass.js',
}); });
@ -515,8 +518,8 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(searchTargetProject, referenceProject); mockTargetAndReferenceProject(searchTargetProject, referenceProject);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput[0].tag).to.eql(expectedMatches[0]); expect(queryResult.queryOutput[0].tag).to.deep.equal(expectedMatches[0]);
expect(queryResult.queryOutput[1].tag).to.eql(expectedMatches[1]); expect(queryResult.queryOutput[1].tag).to.deep.equal(expectedMatches[1]);
}); });
// TODO: test works in isolation, but some side effects occur when run in suite // TODO: test works in isolation, but some side effects occur when run in suite
@ -578,7 +581,7 @@ describe('Analyzer "match-paths"', async () => {
providenceCfg, providenceCfg,
); );
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput[0].tag).to.eql({ expect(queryResult.queryOutput[0].tag).to.deep.equal({
from: 'their-button', from: 'their-button',
to: 'my-button', to: 'my-button',
paths: [ paths: [
@ -607,7 +610,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(searchTargetProject, referenceProject); mockTargetAndReferenceProject(searchTargetProject, referenceProject);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: './customelementDefinitions.js',
to: './extendedCustomelementDefinitions.js', to: './extendedCustomelementDefinitions.js',
}); });
@ -617,7 +620,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(searchTargetProject, referenceProject); mockTargetAndReferenceProject(searchTargetProject, referenceProject);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; 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', from: 'reference-project/customelementDefinitions.js',
to: './extendedCustomelementDefinitions.js', to: './extendedCustomelementDefinitions.js',
}); });
@ -736,7 +739,7 @@ describe('Analyzer "match-paths"', async () => {
mockTargetAndReferenceProject(searchTargetProjectFull, referenceProjectFull); mockTargetAndReferenceProject(searchTargetProjectFull, referenceProjectFull);
const queryResults = await providence(matchPathsQueryConfig, _providenceCfg); const queryResults = await providence(matchPathsQueryConfig, _providenceCfg);
const queryResult = queryResults[0]; const queryResult = queryResults[0];
expect(queryResult.queryOutput).to.eql(expectedMatchesFull); expect(queryResult.queryOutput).to.deep.equal(expectedMatchesFull);
}); });
}); });
}); });

View file

@ -332,14 +332,14 @@ describe('Analyzer "match-subclasses"', async () => {
); );
const [name, filePath, project] = targetExportedId.split('::'); const [name, filePath, project] = targetExportedId.split('::');
expect(matchedEntry.exportSpecifier).to.eql({ expect(matchedEntry.exportSpecifier).to.deep.equal({
name, name,
filePath, filePath,
project, project,
id: targetExportedId, id: targetExportedId,
}); });
expect(matchedEntry.matchesPerProject[0].project).to.equal('importing-target-project'); 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); mockTargetAndReferenceProject(searchTargetProject, referenceProject);

View file

@ -81,19 +81,19 @@ describe('Analyzer', async () => {
const queryResult = queryResults[0]; const queryResult = queryResults[0];
const { queryOutput, meta } = queryResult; const { queryOutput, meta } = queryResult;
expect(queryOutput[0]).to.eql({ expect(queryOutput[0]).to.deep.equal({
file: './test-file-0.js', file: './test-file-0.js',
meta: {}, meta: {},
result: [{ matched: 'entry' }], result: [{ matched: 'entry' }],
}); });
expect(queryOutput[1]).to.eql({ expect(queryOutput[1]).to.deep.equal({
file: './test-file2.js', file: './test-file2.js',
meta: {}, meta: {},
result: [{ matched: 'entry' }], result: [{ matched: 'entry' }],
}); });
// Local machine info needs to be deleted, so that results are always 'machine agnostic' // Local machine info needs to be deleted, so that results are always 'machine agnostic'
// (which is needed to share cached json results via git) // (which is needed to share cached json results via git)
expect(meta).to.eql({ expect(meta).to.deep.equal({
searchType: 'ast-analyzer', searchType: 'ast-analyzer',
analyzerMeta: { analyzerMeta: {
name: 'my-analyzer', name: 'my-analyzer',

View file

@ -1,9 +1,7 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; import { it } from 'mocha';
import pathLib from 'path';
import { InputDataService } from '../../../src/program/core/InputDataService.js'; import { InputDataService } from '../../../src/program/core/InputDataService.js';
import { memoizeConfig } from '../../../src/program/utils/memoize.js'; import { memoize } from '../../../src/program/utils/memoize.js';
import { getCurrentDir } from '../../../src/program/utils/get-current-dir.js';
import { import {
restoreMockedProjects, restoreMockedProjects,
mockProject, mockProject,
@ -24,13 +22,13 @@ describe('InputDataService', () => {
} }
beforeEach(() => { beforeEach(() => {
memoizeConfig.isCacheDisabled = true; memoize.disableCaching();
}); });
afterEach(() => { afterEach(() => {
restoreOriginalInputDataPaths(); restoreOriginalInputDataPaths();
restoreMockedProjects(); restoreMockedProjects();
memoizeConfig.isCacheDisabled = false; memoize.restoreCaching();
}); });
describe('Configuration', () => { describe('Configuration', () => {
@ -50,36 +48,43 @@ describe('InputDataService', () => {
}); });
describe('Methods', () => { describe('Methods', () => {
// TODO: mock file system...
it('"createDataObject"', async () => { it('"createDataObject"', async () => {
/** @type {* & PathFromSystemRoot} */ mockProject({
const projectPath = pathLib.resolve( './package.json': JSON.stringify({
getCurrentDir(import.meta.url), name: 'fictional-project',
'../../../test-helpers/project-mocks/importing-target-project', main: 'my/index.js',
); version: '1.0.0',
}),
'./src/file.js': '// bla',
'./src/file2.js': '// bla',
});
const inputDataPerProject = InputDataService.createDataObject([projectPath]); const inputDataPerProject = await InputDataService.createDataObject(['/fictional/project']);
expect(Object.keys(inputDataPerProject[0].project)).to.eql([ expect(inputDataPerProject).to.deep.equal([
'path', {
'mainEntry', project: {
'name', path: '/fictional/project',
'version', mainEntry: './my/index.js',
'commitHash', 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 () => {}); it('"targetProjectPaths"', async () => {});
@ -99,11 +104,11 @@ describe('InputDataService', () => {
'{ "name": "@another-scope/another-package" }', '{ "name": "@another-scope/another-package" }',
}); });
expect(InputDataService.getMonoRepoPackages('/fictional/project')).to.eql([ expect(await InputDataService.getMonoRepoPackages('/fictional/project')).to.deep.equal([
{ path: 'packages/pkg1/', name: 'package1' }, { path: 'packages/pkg1', name: 'package1' },
{ path: 'packages/pkg2/', name: 'pkg2' }, // fallback when no package.json { path: 'packages/pkg2', name: 'pkg2' }, // fallback when no package.json
{ path: 'packages/pkg3/', name: '@scope/pkg3' }, { path: 'packages/pkg3', name: '@scope/pkg3' },
{ path: 'another-folder/another-package/', name: '@another-scope/another-package' }, { path: 'another-folder/another-package', name: '@another-scope/another-package' },
]); ]);
}); });
@ -120,11 +125,11 @@ describe('InputDataService', () => {
'{ "name": "@another-scope/another-package" }', '{ "name": "@another-scope/another-package" }',
}); });
expect(InputDataService.getMonoRepoPackages('/fictional/project')).to.eql([ expect(await InputDataService.getMonoRepoPackages('/fictional/project')).to.deep.equal([
{ path: 'packages/pkg1/', name: 'package1' }, { path: 'packages/pkg1', name: 'package1' },
{ path: 'packages/pkg2/', name: 'pkg2' }, // fallback when no package.json { path: 'packages/pkg2', name: 'pkg2' }, // fallback when no package.json
{ path: 'packages/pkg3/', name: '@scope/pkg3' }, { path: 'packages/pkg3', name: '@scope/pkg3' },
{ path: 'another-folder/another-package/', name: '@another-scope/another-package' }, { path: 'another-folder/another-package', name: '@another-scope/another-package' },
]); ]);
}); });
}); });
@ -143,19 +148,21 @@ describe('InputDataService', () => {
}); });
it('gathers a list of files', async () => { it('gathers a list of files', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/internal.js', '/fictional/project/internal.js',
'/fictional/project/something.test.js',
'/fictional/project/nested/index.js', '/fictional/project/nested/index.js',
'/fictional/project/nested/nested-two/index.test.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 () => { it('allows passing a depth which stops at nested depth', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { depth: 0 }); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
expect(globOutput).to.eql([ depth: 0,
});
expect(globOutput).to.deep.equal([
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/internal.js', '/fictional/project/internal.js',
'/fictional/project/something.test.js', '/fictional/project/something.test.js',
@ -163,26 +170,26 @@ describe('InputDataService', () => {
}); });
it('allows passing extensions', async () => { it('allows passing extensions', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.html', '/fictional/project/index.html',
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/internal.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.html',
'/fictional/project/something.test.js', '/fictional/project/something.test.js',
'/fictional/project/nested/index.js',
'/fictional/project/nested/nested-two/index.test.js',
]); ]);
}); });
it('allows passing excluded folders', async () => { it('allows passing excluded folders', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
allowlist: ['!nested/**'], allowlist: ['!nested/**'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.html', '/fictional/project/index.html',
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/internal.js', '/fictional/project/internal.js',
@ -192,25 +199,25 @@ describe('InputDataService', () => {
}); });
it('allows passing excluded files', async () => { it('allows passing excluded files', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
allowlist: ['!index.js', '!**/*/index.js'], allowlist: ['!index.js', '!**/*/index.js'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.html', '/fictional/project/index.html',
'/fictional/project/internal.js', '/fictional/project/internal.js',
'/fictional/project/nested/nested-two/index.test.js',
'/fictional/project/something.test.html', '/fictional/project/something.test.html',
'/fictional/project/something.test.js', '/fictional/project/something.test.js',
'/fictional/project/nested/nested-two/index.test.js',
]); ]);
}); });
it('allows passing exclude globs', async () => { it('allows passing exclude globs', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
allowlist: ['!**/*.test.{html,js}'], allowlist: ['!**/*.test.{html,js}'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.html', '/fictional/project/index.html',
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/internal.js', '/fictional/project/internal.js',
@ -219,11 +226,11 @@ describe('InputDataService', () => {
}); });
it('does not support non globs in "allowlist"', async () => { 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'], extensions: ['.html', '.js'],
allowlist: ['nested'], allowlist: ['nested'],
}); });
expect(globOutput).to.eql([]); expect(globOutput).to.deep.equal([]);
}); });
it('omits node_modules and bower_components at root level by default', async () => { 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': '', './nested/bower_components/pkg/y.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/index.js', '/fictional/project/index.js',
'/fictional/project/nested/bower_components/pkg/y.js',
'/fictional/project/nested/node_modules/pkg/x.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': '', './omitted/file.js': '',
'./added/file.js': '', './added/file.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlist: ['*', 'added/**/*'], allowlist: ['*', 'added/**/*'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/added/file.js',
'/fictional/project/root-lvl.js', '/fictional/project/root-lvl.js',
'/fictional/project/added/file.js',
]); ]);
}); });
@ -265,10 +272,10 @@ describe('InputDataService', () => {
'./deeper/glob/file.js': '', './deeper/glob/file.js': '',
'./deeper/file.js': '', './deeper/file.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlist: ['deeper/**/*'], allowlist: ['deeper/**/*'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/deeper/file.js', '/fictional/project/deeper/file.js',
'/fictional/project/deeper/glob/file.js', '/fictional/project/deeper/glob/file.js',
'/fictional/project/deeper/glob/structure/file.js', '/fictional/project/deeper/glob/structure/file.js',
@ -285,8 +292,8 @@ describe('InputDataService', () => {
'./some-other-pkg/commitlint.conf.js': '', './some-other-pkg/commitlint.conf.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput).to.eql(['/fictional/project/index.js']); expect(globOutput).to.deep.equal(['/fictional/project/index.js']);
}); });
it('omits hidden files by default', async () => { it('omits hidden files by default', async () => {
@ -295,8 +302,8 @@ describe('InputDataService', () => {
'./index.js': '', './index.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput).to.eql(['/fictional/project/index.js']); expect(globOutput).to.deep.equal(['/fictional/project/index.js']);
}); });
describe('AllowlistMode', () => { describe('AllowlistMode', () => {
@ -308,8 +315,8 @@ describe('InputDataService', () => {
}), }),
'.gitignore': '/dist', '.gitignore': '/dist',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
// This means allowlistMode is 'git' // This means allowlistMode is 'git'
]); ]);
@ -322,8 +329,8 @@ describe('InputDataService', () => {
files: ['dist'], files: ['dist'],
}), }),
}); });
const globOutput2 = InputDataService.gatherFilesFromDir('/fictional/project'); const globOutput2 = await InputDataService.gatherFilesFromDir('/fictional/project');
expect(globOutput2).to.eql([ expect(globOutput2).to.deep.equal([
// This means allowlistMode is 'npm' // This means allowlistMode is 'npm'
'/fictional/project/dist/bundle.js', '/fictional/project/dist/bundle.js',
]); ]);
@ -335,10 +342,10 @@ describe('InputDataService', () => {
projectPath: '/inside/proj/with/node_modules/detect-as-npm', 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', '/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) // This means allowlistMode is 'npm' (even though we found .gitignore)
'/inside/proj/with/node_modules/detect-as-npm/dist/bundle.js', '/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', 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', '/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) // This means allowlistMode is 'npm' (even though we found .gitignore)
'/inside/proj/with/node_modules/@scoped/detect-as-npm/dist/bundle.js', '/inside/proj/with/node_modules/@scoped/detect-as-npm/dist/bundle.js',
]); ]);
@ -369,13 +376,13 @@ describe('InputDataService', () => {
files: ['*.add.js', 'docs', 'src'], files: ['*.add.js', 'docs', 'src'],
}), }),
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlistMode: 'npm', allowlistMode: 'npm',
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/docs/x.js',
'/fictional/project/file.add.js', '/fictional/project/file.add.js',
'/fictional/project/src/y.js', '/fictional/project/src/y.js',
'/fictional/project/docs/x.js',
]); ]);
}); });
@ -395,12 +402,12 @@ build/
!keep/ !keep/
`, `,
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlistMode: 'git', allowlistMode: 'git',
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/keep/it.js',
'/fictional/project/shall/pass.js', '/fictional/project/shall/pass.js',
'/fictional/project/keep/it.js',
]); ]);
}); });
@ -415,12 +422,12 @@ build/
/dist /dist
`, `,
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlistMode: 'all', allowlistMode: 'all',
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/dist/bundle.js',
'/fictional/project/src/file.js', '/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', 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 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/**'], allowlist: ['dist/**'],
allowlistMode: 'git', // for clarity, (would also be autodetected if not provided) 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', () => { describe('Default allowlist', () => {
@ -466,10 +473,10 @@ build/
'./added.js': '', './added.js': '',
'./omit.js': '', './omit.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlist: ['added*'], 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 () => { it('allows to omit default config filter', async () => {
@ -481,16 +488,16 @@ build/
'./added.js': '', './added.js': '',
'./omit.js': '', './omit.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = await InputDataService.gatherFilesFromDir('/fictional/project', {
allowlist: ['!omit*'], allowlist: ['!omit*'],
omitDefaultAllowlist: true, omitDefaultAllowlist: true,
}); });
expect(globOutput).to.eql([ expect(globOutput).to.deep.equal([
'/fictional/project/abc.config.js', '/fictional/project/abc.config.js',
'/fictional/project/added.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/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', packageRootPath: '/my/proj',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './internal-path.js', exposed: './exposed-path.js' }, { 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-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, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './internal-path.js', exposed: './exposed-path.js' }, { internal: './internal-path.js', exposed: './exposed-path.js' },
]); ]);
}); });
@ -550,7 +557,7 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', 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-a.js', exposed: './file-a.js' },
{ internal: './internal-exports-folder/file-b.js', exposed: './file-b.js' }, { internal: './internal-exports-folder/file-b.js', exposed: './file-b.js' },
{ internal: './internal-exports-folder/file-c.js', exposed: './file-c.js' }, { internal: './internal-exports-folder/file-c.js', exposed: './file-c.js' },
@ -569,12 +576,12 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', 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', internal: './internal-folder/another-folder/file-b.js',
exposed: './exposed-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, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './folder-a/file.js', exposed: './exposed-folder/folder-a/file.js' }, { exposed: './exposed-folder/folder-b/file.js', internal: './folder-b/file.js' },
{ internal: './folder-b/file.js', exposed: './exposed-folder/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, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', 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-a.js', exposed: './exposed-folder/file-a.js' },
]); ]);
}); });
@ -631,7 +638,7 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './esm-exports/file.js', exposed: './file.js' }, { internal: './esm-exports/file.js', exposed: './file.js' },
]); ]);
}); });
@ -650,7 +657,7 @@ build/
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
nodeResolveMode: 'require', nodeResolveMode: 'require',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './cjs-exports/file.cjs', exposed: './file.cjs' }, { internal: './cjs-exports/file.cjs', exposed: './file.cjs' },
]); ]);
}); });
@ -669,7 +676,7 @@ build/
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
nodeResolveMode: 'develop', nodeResolveMode: 'develop',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './develop-exports/file.js', exposed: './file.js' }, { internal: './develop-exports/file.js', exposed: './file.js' },
]); ]);
}); });
@ -693,7 +700,7 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', packageRootPath: '/my/proj',
}); });
expect(exportMapPaths).to.eql([ expect(exportMapPaths).to.deep.equal([
{ internal: './index.js', exposed: '.' }, { internal: './index.js', exposed: '.' },
{ internal: './file.js', exposed: './exposed-file.js' }, { internal: './file.js', exposed: './exposed-file.js' },
]); ]);
@ -714,7 +721,7 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', 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-a.js', exposed: './exposed-folder/file-a.js' },
{ internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' }, { internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' },
]); ]);
@ -733,7 +740,7 @@ build/
const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, { const exportMapPaths = await InputDataService.getPathsFromExportMap(exports, {
packageRootPath: '/my/proj', 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-a.js', exposed: './exposed-folder/file-a.js' },
{ internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' }, { internal: './internal-folder/file-b.js', exposed: './exposed-folder/file-b.js' },
]); ]);

View file

@ -14,7 +14,7 @@ describe('QueryService', () => {
describe('Retrieving QueryConfig', () => { describe('Retrieving QueryConfig', () => {
it('"getQueryConfigFromRegexSearchString"', async () => { it('"getQueryConfigFromRegexSearchString"', async () => {
const result = QueryService.getQueryConfigFromRegexSearchString('x'); const result = QueryService.getQueryConfigFromRegexSearchString('x');
expect(result).to.eql({ type: 'search', regexString: 'x' }); expect(result).to.deep.equal({ type: 'search', regexString: 'x' });
expect(() => { expect(() => {
// @ts-expect-error // @ts-expect-error
@ -25,7 +25,7 @@ describe('QueryService', () => {
describe('"getQueryConfigFromFeatureString"', () => { describe('"getQueryConfigFromFeatureString"', () => {
it('with tag, attr-key and attr-value', async () => { it('with tag, attr-key and attr-value', async () => {
const result = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]'); const result = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'size', name: 'size',
@ -41,7 +41,7 @@ describe('QueryService', () => {
it('with only tag', async () => { it('with only tag', async () => {
const result = QueryService.getQueryConfigFromFeatureString('tg-icon'); const result = QueryService.getQueryConfigFromFeatureString('tg-icon');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
tag: 'tg-icon', tag: 'tg-icon',
@ -52,7 +52,7 @@ describe('QueryService', () => {
it('with only attr-key', async () => { it('with only attr-key', async () => {
const result = QueryService.getQueryConfigFromFeatureString('[attr]'); const result = QueryService.getQueryConfigFromFeatureString('[attr]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'attr', name: 'attr',
@ -68,7 +68,7 @@ describe('QueryService', () => {
it('with only attr-key and attr-value', async () => { it('with only attr-key and attr-value', async () => {
const result = QueryService.getQueryConfigFromFeatureString('[attr=x]'); const result = QueryService.getQueryConfigFromFeatureString('[attr=x]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'attr', name: 'attr',
@ -85,7 +85,7 @@ describe('QueryService', () => {
describe('With partial value', async () => { describe('With partial value', async () => {
it('with tag, attr-key and attr-value', async () => { it('with tag, attr-key and attr-value', async () => {
const result = QueryService.getQueryConfigFromFeatureString('tg-icon*[size*=xs*]'); const result = QueryService.getQueryConfigFromFeatureString('tg-icon*[size*=xs*]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'size', name: 'size',
@ -101,7 +101,7 @@ describe('QueryService', () => {
it('with only tag', async () => { it('with only tag', async () => {
const result = QueryService.getQueryConfigFromFeatureString('tg-icon*'); const result = QueryService.getQueryConfigFromFeatureString('tg-icon*');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
tag: 'tg-icon', tag: 'tg-icon',
@ -112,7 +112,7 @@ describe('QueryService', () => {
it('with only attr-key', async () => { it('with only attr-key', async () => {
const result = QueryService.getQueryConfigFromFeatureString('[attr*]'); const result = QueryService.getQueryConfigFromFeatureString('[attr*]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'attr', name: 'attr',
@ -128,7 +128,7 @@ describe('QueryService', () => {
it('with only attr-key and attr-value', async () => { it('with only attr-key and attr-value', async () => {
const result = QueryService.getQueryConfigFromFeatureString('[attr*=x*]'); const result = QueryService.getQueryConfigFromFeatureString('[attr*=x*]');
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'feature', type: 'feature',
feature: { feature: {
name: 'attr', name: 'attr',
@ -158,7 +158,7 @@ describe('QueryService', () => {
'find-imports', 'find-imports',
myAnalyzerCfg, myAnalyzerCfg,
); );
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'ast-analyzer', type: 'ast-analyzer',
analyzerName: 'find-imports', analyzerName: 'find-imports',
analyzerConfig: myAnalyzerCfg, analyzerConfig: myAnalyzerCfg,
@ -171,7 +171,7 @@ describe('QueryService', () => {
/** @type {* & Analyzer} */ (DummyAnalyzer), /** @type {* & Analyzer} */ (DummyAnalyzer),
myAnalyzerCfg, myAnalyzerCfg,
); );
expect(result).to.eql({ expect(result).to.deep.equal({
type: 'ast-analyzer', type: 'ast-analyzer',
analyzerName: 'find-dummy-analyzer', analyzerName: 'find-dummy-analyzer',
analyzerConfig: myAnalyzerCfg, analyzerConfig: myAnalyzerCfg,
@ -186,7 +186,7 @@ describe('QueryService', () => {
// it('with FeatureConfig', async () => { // it('with FeatureConfig', async () => {
// const featureCfg = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]'); // const featureCfg = QueryService.getQueryConfigFromFeatureString('tg-icon[size=xs]');
// const result = QueryService.grepSearch(featureCfg); // const result = QueryService.grepSearch(featureCfg);
// expect(result).to.eql({ // expect(result).to.deep.equal({
// type: 'ast-analyzer', // type: 'ast-analyzer',
// analyzerName: 'find-imports', // analyzerName: 'find-imports',
// analyzerConfig: { x: 'y' }, // analyzerConfig: { x: 'y' },

View file

@ -1,16 +1,17 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; import { it } from 'mocha';
import { mock } from '../../../test-helpers/mock-project-helpers.js';
import { getSourceCodeFragmentOfDeclaration } from '../../../src/program/utils/index.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', () => { describe('getSourceCodeFragmentOfDeclaration', () => {
const initialMemoizeSsCacheDisabled = memoizeConfig.isCacheDisabled; const initialMemoizeCacheEnabled = memoize.isCacheEnabled;
before(() => { before(() => {
memoizeConfig.isCacheDisabled = true; memoize.disableCaching();
}); });
after(() => { after(() => {
memoizeConfig.isCacheDisabled = initialMemoizeSsCacheDisabled; memoize.restoreCaching(initialMemoizeCacheEnabled);
}); });
describe('Named specifiers', () => { describe('Named specifiers', () => {

View file

@ -1,17 +1,11 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { it } from 'mocha'; import { it } from 'mocha';
import { memoize, memoizeConfig } from '../../../src/program/utils/memoize.js'; import { memoize } from '../../../src/program/utils/memoize.js';
const cacheDisabledInitialValue = memoizeConfig.isCacheDisabled;
describe('Memoize', () => { describe('Memoize', () => {
beforeEach(() => { // This is important, since memoization only works when cache is disabled.
// This is important, since memoization only works // We want to prevent that another test unintentionally disabled caching.
memoizeConfig.isCacheDisabled = false; memoize.restoreCaching();
});
afterEach(() => {
memoizeConfig.isCacheDisabled = cacheDisabledInitialValue;
});
describe('With primitives', () => { describe('With primitives', () => {
describe('Numbers', () => { describe('Numbers', () => {
@ -136,15 +130,15 @@ describe('Memoize', () => {
const sumMemoized = memoize(sum); const sumMemoized = memoize(sum);
// Put in cache for args combination // 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); expect(sumCalled).to.equal(1);
// Return from cache // 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); expect(sumCalled).to.equal(1);
// Put in cache for args combination // 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); expect(sumCalled).to.equal(2);
}); });
@ -162,17 +156,17 @@ describe('Memoize', () => {
} }
const sum2Memoized = memoize(sum2); 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(sumCalled).to.equal(1);
expect(sum2Called).to.equal(0); 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(sumCalled).to.equal(1);
expect(sum2Called).to.equal(1); expect(sum2Called).to.equal(1);
// Both cached // Both cached
expect(sumMemoized([1], [2])).to.eql([1, 2]); expect(sumMemoized([1], [2])).to.deep.equal([1, 2]);
expect(sum2Memoized([1], [2])).to.eql([1, 2]); expect(sum2Memoized([1], [2])).to.deep.equal([1, 2]);
expect(sumCalled).to.equal(1); expect(sumCalled).to.equal(1);
expect(sum2Called).to.equal(1); expect(sum2Called).to.equal(1);
}); });
@ -188,15 +182,15 @@ describe('Memoize', () => {
const sumMemoized = memoize(sum, { serializeObjects: true }); const sumMemoized = memoize(sum, { serializeObjects: true });
// Put in cache for args combination // 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); expect(sumCalled).to.equal(1);
// Return from cache // 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); expect(sumCalled).to.equal(1);
// Put in cache for args combination // 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); expect(sumCalled).to.equal(2);
}); });
@ -214,17 +208,17 @@ describe('Memoize', () => {
} }
const sum2Memoized = memoize(sum2, { serializeObjects: true }); 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(sumCalled).to.equal(1);
expect(sum2Called).to.equal(0); 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(sumCalled).to.equal(1);
expect(sum2Called).to.equal(1); expect(sum2Called).to.equal(1);
// Both cached // Both cached
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(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(sumCalled).to.equal(1);
expect(sum2Called).to.equal(1); expect(sum2Called).to.equal(1);
}); });
@ -242,13 +236,13 @@ describe('Memoize', () => {
// Put in cache for args combination // Put in cache for args combination
const result = sumMemoized({ x: 1 }, { y: 2 }); 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); expect(sumCalled).to.equal(1);
// Return from cache // Return from cache
const resultCached = sumMemoized({ x: 1 }, { y: 2 }); const resultCached = sumMemoized({ x: 1 }, { y: 2 });
expect(resultCached).to.equal(result); 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); expect(sumCalled).to.equal(1);
// Outside world can edit returned reference // Outside world can edit returned reference

View file

@ -6,14 +6,14 @@ import {
mockTargetAndReferenceProject, mockTargetAndReferenceProject,
} from '../../../test-helpers/mock-project-helpers.js'; } from '../../../test-helpers/mock-project-helpers.js';
import { resolveImportPath } from '../../../src/program/utils/resolve-import-path.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', () => { describe('resolveImportPath', () => {
beforeEach(() => { beforeEach(() => {
memoizeConfig.isCacheDisabled = true; memoize.disableCaching();
}); });
afterEach(() => { afterEach(() => {
memoizeConfig.isCacheDisabled = false; memoize.restoreCaching();
restoreMockedProjects(); restoreMockedProjects();
}); });

View file

@ -60,7 +60,7 @@ describe('swcTraverse', () => {
}; };
swcTraverse(swcAst, visitor); swcTraverse(swcAst, visitor);
expect(foundTypes).to.eql([ expect(foundTypes).to.deep.equal([
'Module', 'Module',
'ImportDeclaration', 'ImportDeclaration',
'ImportDefaultSpecifier', 'ImportDefaultSpecifier',
@ -166,7 +166,7 @@ describe('swcTraverse', () => {
expect(declaratorPaths[2].scope.id).to.equal(2); expect(declaratorPaths[2].scope.id).to.equal(2);
expect(declaratorPaths[0].node.id.value).to.equal('globalScope'); 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', 'globalScope',
'alsoGlobalScope', 'alsoGlobalScope',
]); ]);
@ -180,8 +180,8 @@ describe('swcTraverse', () => {
declaratorPaths[3].node, declaratorPaths[3].node,
); );
expect(Object.keys(declaratorPaths[1].scope.bindings)).to.eql(['middleScope']); expect(Object.keys(declaratorPaths[1].scope.bindings)).to.deep.equal(['middleScope']);
expect(Object.keys(declaratorPaths[2].scope.bindings)).to.eql(['deepestScope']); expect(Object.keys(declaratorPaths[2].scope.bindings)).to.deep.equal(['deepestScope']);
}); });
it('creates scopes for nested FunctionDeclaration', async () => { it('creates scopes for nested FunctionDeclaration', async () => {
@ -336,7 +336,7 @@ describe('swcTraverse', () => {
}; };
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true }); 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', 'globalScope',
'alsoGlobalScope', 'alsoGlobalScope',
]); ]);
@ -370,12 +370,12 @@ describe('swcTraverse', () => {
}; };
swcTraverse(swcAst, visitor, { needsAdvancedPaths: true }); 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', 'globalScope',
'stillGlobalScope', 'stillGlobalScope',
]); ]);
expect(Object.keys(declaratorPaths[1].scope.bindings)).to.eql(['middleScope']); expect(Object.keys(declaratorPaths[1].scope.bindings)).to.deep.equal(['middleScope']);
expect(Object.keys(declaratorPaths[2].scope.bindings)).to.eql(['insideFnScope']); expect(Object.keys(declaratorPaths[2].scope.bindings)).to.deep.equal(['insideFnScope']);
}); });
}); });
@ -420,8 +420,10 @@ describe('swcTraverse', () => {
expect(babelScopes.length).to.equal(swcScopes.length); expect(babelScopes.length).to.equal(swcScopes.length);
for (let i = 0; i < babelScopes.length; i += 1) { for (let i = 0; i < babelScopes.length; i += 1) {
expect(babelScopes[i].uid - babelRootScopeIdOffset).to.equal(swcScopes[i].id); expect(babelScopes[i].uid - babelRootScopeIdOffset).to.equal(swcScopes[i].id);
expect(Object.keys(babelScopes[i].bindings)).to.eql(Object.keys(swcScopes[i].bindings)); expect(Object.keys(babelScopes[i].bindings)).to.deep.equal(
// expect(babelScopes[i].references).to.eql(swcResults[i].references); Object.keys(swcScopes[i].bindings),
);
// expect(babelScopes[i].references).to.deep.equal(swcResults[i].references);
} }
} }

View file

@ -38,9 +38,9 @@ describe('traverseHtml', () => {
}, },
}); });
expect(foundDivs).to.eql(['a-lvl1', 'b']); expect(foundDivs).to.deep.equal(['a-lvl1', 'b']);
expect(foundSpans).to.eql(['a-lvl2']); expect(foundSpans).to.deep.equal(['a-lvl2']);
expect(foundMyTags).to.eql(['a-lvl3']); expect(foundMyTags).to.deep.equal(['a-lvl3']);
}); });
it('traverses different levels in DOM order', async () => { it('traverses different levels in DOM order', async () => {
@ -72,7 +72,7 @@ describe('traverseHtml', () => {
traverseHtml(ast, processObj); traverseHtml(ast, processObj);
// call order based on dom tree // 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 () => { it('allows to stop traversal (for performance)', async () => {
@ -104,7 +104,7 @@ describe('traverseHtml', () => {
}; };
traverseHtml(ast, processObj); 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 () => { it('allows to traverse within a path', async () => {
@ -135,6 +135,6 @@ describe('traverseHtml', () => {
}; };
traverseHtml(ast, processObj); 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']);
}); });
}); });

View file

@ -4,6 +4,6 @@
"outDir": "./dist-types", "outDir": "./dist-types",
"rootDir": "." "rootDir": "."
}, },
"include": ["types"], "include": ["types", "src", "test-node"],
"exclude": ["dist-types"] "exclude": ["dist-types"]
} }

View file

@ -1,4 +1,5 @@
import { File } from '@babel/types'; import { File } from '@babel/types';
import Vol from 'memfs';
/** /**
* The name of a variable in a local context. Examples: * 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 AnyMatchString = string;
export type FsAdapter = Vol;
export type ProvidenceConfig = { export type ProvidenceConfig = {
/* Whether analyzer should be run or a grep should be performed */ /* Whether analyzer should be run or a grep should be performed */
queryMethod: 'ast' | 'grep'; queryMethod: 'ast' | 'grep';
@ -169,6 +172,7 @@ export type ProvidenceConfig = {
writeLogFile: boolean; writeLogFile: boolean;
skipCheckMatchCompatibility: boolean; skipCheckMatchCompatibility: boolean;
fallbackToBabel: boolean; fallbackToBabel: boolean;
fs: FsAdapter;
}; };
/** /**
@ -182,6 +186,7 @@ export type PackageJson = {
devDependencies?: { [dependency: string]: string }; devDependencies?: { [dependency: string]: string };
workspaces?: string[]; workspaces?: string[];
main?: string; main?: string;
exports?: { [key: string]: string };
}; };
export type LernaJson = { export type LernaJson = {