fix(providence-analytics): provide target/reference result match
This commit is contained in:
parent
cc251ed46c
commit
d7f0807903
4 changed files with 111 additions and 56 deletions
|
|
@ -9,26 +9,6 @@ const { InputDataService } = require('../../services/InputDataService.js');
|
||||||
const { aForEach } = require('../../utils/async-array-utils.js');
|
const { aForEach } = require('../../utils/async-array-utils.js');
|
||||||
const { getFilePathRelativeFromRoot } = require('../../utils/get-file-path-relative-from-root.js');
|
const { getFilePathRelativeFromRoot } = require('../../utils/get-file-path-relative-from-root.js');
|
||||||
|
|
||||||
/**
|
|
||||||
* @desc Gets a cached result from ReportService. Since ReportService slightly modifies analyzer
|
|
||||||
* output, we 'unwind' before we return...
|
|
||||||
* @param {object} config
|
|
||||||
* @param {string} config.analyzerName
|
|
||||||
* @param {string} config.identifier
|
|
||||||
*/
|
|
||||||
function getCachedAnalyzerResult({ analyzerName, identifier }) {
|
|
||||||
const cachedResult = ReportService.getCachedResult({ analyzerName, identifier });
|
|
||||||
if (!cachedResult) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogService.success(`cached version found for ${identifier}`);
|
|
||||||
|
|
||||||
const { queryOutput } = cachedResult;
|
|
||||||
const { analyzerMeta } = cachedResult.meta;
|
|
||||||
analyzerMeta.__fromCache = true;
|
|
||||||
return { analyzerMeta, queryOutput }; // eslint-disable-line consistent-return
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc analyzes one entry: the callback can traverse a given ast for each entry
|
* @desc analyzes one entry: the callback can traverse a given ast for each entry
|
||||||
* @param {AstDataProject[]} astDataProjects
|
* @param {AstDataProject[]} astDataProjects
|
||||||
|
|
@ -86,6 +66,14 @@ function ensureAnalyzerResultFormat(queryOutput, configuration, analyzer) {
|
||||||
delete aResult.analyzerMeta.configuration.referenceProjectPath;
|
delete aResult.analyzerMeta.configuration.referenceProjectPath;
|
||||||
delete aResult.analyzerMeta.configuration.targetProjectPath;
|
delete aResult.analyzerMeta.configuration.targetProjectPath;
|
||||||
|
|
||||||
|
const { referenceProjectResult, targetProjectResult } = aResult.analyzerMeta.configuration;
|
||||||
|
|
||||||
|
if (referenceProjectResult) {
|
||||||
|
delete aResult.analyzerMeta.configuration.referenceProjectResult;
|
||||||
|
} else if (targetProjectResult) {
|
||||||
|
delete aResult.analyzerMeta.configuration.targetProjectResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(aResult.queryOutput)) {
|
if (Array.isArray(aResult.queryOutput)) {
|
||||||
aResult.queryOutput.forEach(projectOutput => {
|
aResult.queryOutput.forEach(projectOutput => {
|
||||||
if (projectOutput.project) {
|
if (projectOutput.project) {
|
||||||
|
|
@ -123,6 +111,16 @@ function checkForMatchCompatibility(referencePath, targetPath) {
|
||||||
return { compatible: true };
|
return { compatible: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If in json format, 'unwind' to be compatible for analysis...
|
||||||
|
* @param {AnalyzerResult} targetOrReferenceProjectResult
|
||||||
|
*/
|
||||||
|
function unwindJsonResult(targetOrReferenceProjectResult) {
|
||||||
|
const { queryOutput } = targetOrReferenceProjectResult;
|
||||||
|
const { analyzerMeta } = targetOrReferenceProjectResult.meta;
|
||||||
|
return { queryOutput, analyzerMeta };
|
||||||
|
}
|
||||||
|
|
||||||
class Analyzer {
|
class Analyzer {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.requiredAst = 'babel';
|
this.requiredAst = 'babel';
|
||||||
|
|
@ -132,15 +130,39 @@ class Analyzer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a MatchAnalyzer, two Analyzers (a reference and targer) are run.
|
||||||
|
* For instance, in a MatchImportsAnalyzer, a FindExportsAnalyzer and FinImportsAnalyzer are run.
|
||||||
|
* Their results can be provided as config params.
|
||||||
|
* If they are stored in json format, 'unwind' them to be compatible for analysis...
|
||||||
|
* @param {MatchAnalyzerConfig} cfg
|
||||||
|
*/
|
||||||
|
static __unwindProvidedResults(cfg) {
|
||||||
|
if (cfg.targetProjectResult && !cfg.targetProjectResult.analyzerMeta) {
|
||||||
|
cfg.targetProjectResult = unwindJsonResult(cfg.targetProjectResult);
|
||||||
|
}
|
||||||
|
if (cfg.referenceProjectResult && !cfg.referenceProjectResult.analyzerMeta) {
|
||||||
|
cfg.referenceProjectResult = unwindJsonResult(cfg.referenceProjectResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AnalyzerConfig} cfg
|
* @param {AnalyzerConfig} cfg
|
||||||
* @returns {CachedAnalyzerResult|undefined}
|
* @returns {CachedAnalyzerResult|undefined}
|
||||||
*/
|
*/
|
||||||
_prepare(cfg) {
|
_prepare(cfg) {
|
||||||
this.targetProjectMeta = InputDataService.getProjectMeta(cfg.targetProjectPath, true);
|
this.constructor.__unwindProvidedResults(cfg);
|
||||||
|
|
||||||
if (cfg.referenceProjectPath) {
|
if (!cfg.targetProjectResult) {
|
||||||
|
this.targetProjectMeta = InputDataService.getProjectMeta(cfg.targetProjectPath, true);
|
||||||
|
} else {
|
||||||
|
this.targetProjectMeta = cfg.targetProjectResult.analyzerMeta.targetProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.referenceProjectPath && !cfg.referenceProjectResult) {
|
||||||
this.referenceProjectMeta = InputDataService.getProjectMeta(cfg.referenceProjectPath, true);
|
this.referenceProjectMeta = InputDataService.getProjectMeta(cfg.referenceProjectPath, true);
|
||||||
|
} else if (cfg.referenceProjectResult) {
|
||||||
|
this.referenceProjectMeta = cfg.referenceProjectResult.analyzerMeta.targetProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -152,9 +174,9 @@ class Analyzer {
|
||||||
analyzerConfig: cfg,
|
analyzerConfig: cfg,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If we have a provided result cfg.referenceProjectResult, we assume the providing
|
||||||
|
// party provides compatible results for now...
|
||||||
if (cfg.referenceProjectPath) {
|
if (cfg.referenceProjectPath) {
|
||||||
this.referenceProjectMeta = InputDataService.getProjectMeta(cfg.referenceProjectPath, true);
|
|
||||||
|
|
||||||
const { compatible, reason } = checkForMatchCompatibility(
|
const { compatible, reason } = checkForMatchCompatibility(
|
||||||
cfg.referenceProjectPath,
|
cfg.referenceProjectPath,
|
||||||
cfg.targetProjectPath,
|
cfg.targetProjectPath,
|
||||||
|
|
@ -176,7 +198,7 @@ class Analyzer {
|
||||||
/**
|
/**
|
||||||
* See if we maybe already have our result in cache in the file-system.
|
* See if we maybe already have our result in cache in the file-system.
|
||||||
*/
|
*/
|
||||||
const cachedResult = getCachedAnalyzerResult({
|
const cachedResult = Analyzer._getCachedAnalyzerResult({
|
||||||
analyzerName: this.name,
|
analyzerName: this.name,
|
||||||
identifier: this.identifier,
|
identifier: this.identifier,
|
||||||
});
|
});
|
||||||
|
|
@ -186,13 +208,16 @@ class Analyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
LogService.info(`starting ${LogService.pad(this.name, 16)} for ${this.identifier}`);
|
LogService.info(`starting ${LogService.pad(this.name, 16)} for ${this.identifier}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get reference and search-target data
|
* Get reference and search-target data
|
||||||
*/
|
*/
|
||||||
|
if (!cfg.targetProjectResult) {
|
||||||
this.targetData = InputDataService.createDataObject(
|
this.targetData = InputDataService.createDataObject(
|
||||||
[cfg.targetProjectPath],
|
[cfg.targetProjectPath],
|
||||||
cfg.gatherFilesConfig,
|
cfg.gatherFilesConfig,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (cfg.referenceProjectPath) {
|
if (cfg.referenceProjectPath) {
|
||||||
this.referenceData = InputDataService.createDataObject(
|
this.referenceData = InputDataService.createDataObject(
|
||||||
|
|
@ -251,6 +276,27 @@ class Analyzer {
|
||||||
*/
|
*/
|
||||||
return this._finalize(queryOutput, cfg);
|
return this._finalize(queryOutput, cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Gets a cached result from ReportService. Since ReportService slightly modifies analyzer
|
||||||
|
* output, we 'unwind' before we return...
|
||||||
|
* @param {object} config
|
||||||
|
* @param {string} config.analyzerName
|
||||||
|
* @param {string} config.identifier
|
||||||
|
* @returns {AnalyzerResult|undefined}
|
||||||
|
*/
|
||||||
|
static _getCachedAnalyzerResult({ analyzerName, identifier }) {
|
||||||
|
const cachedResult = ReportService.getCachedResult({ analyzerName, identifier });
|
||||||
|
if (!cachedResult) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
LogService.success(`cached version found for ${identifier}`);
|
||||||
|
|
||||||
|
/** @type {AnalyzerResult} */
|
||||||
|
const result = unwindJsonResult(cachedResult);
|
||||||
|
result.analyzerMeta.__fromCache = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { Analyzer };
|
module.exports = { Analyzer };
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@ function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, c
|
||||||
importEntryResult.importSpecifiers.includes(exportSpecifier) ||
|
importEntryResult.importSpecifiers.includes(exportSpecifier) ||
|
||||||
importEntryResult.importSpecifiers.includes('[*]');
|
importEntryResult.importSpecifiers.includes('[*]');
|
||||||
|
|
||||||
|
if (!hasExportSpecifierImported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
* exportFile './foo.js'
|
* exportFile './foo.js'
|
||||||
|
|
@ -98,14 +102,13 @@ function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, c
|
||||||
externalRootPath: cfg.referenceProjectPath,
|
externalRootPath: cfg.referenceProjectPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: transitive deps recognition. Could also be distinct post processor
|
if (!isFromSameSource) {
|
||||||
// // export { z } from '../foo.js'
|
return;
|
||||||
// // import { z } from '@reference/foo.js'
|
|
||||||
// (exportEntryResult.normalizedSource === importEntryResult.normalizedSource)
|
|
||||||
|
|
||||||
if (hasExportSpecifierImported && isFromSameSource) {
|
|
||||||
filteredImportsList.add(`${importProject}::${file}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: transitive deps recognition? Could also be distinct post processor
|
||||||
|
|
||||||
|
filteredImportsList.add(`${importProject}::${file}`);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
||||||
|
|
@ -201,15 +204,15 @@ class MatchImportsAnalyzer extends Analyzer {
|
||||||
* @property {GatherFilesConfig} [gatherFilesConfig]
|
* @property {GatherFilesConfig} [gatherFilesConfig]
|
||||||
* @property {array} [referenceProjectPath] reference paths
|
* @property {array} [referenceProjectPath] reference paths
|
||||||
* @property {array} [targetProjectPath] search target paths
|
* @property {array} [targetProjectPath] search target paths
|
||||||
* @property {FindExportsAnalyzerResult} [exportsAnalyzerResult]
|
* @property {FindImportsAnalyzerResult} [targetProjectResult]
|
||||||
* @property {FindImportsAnalyzerResult} [importsAnalyzerResult]
|
* @property {FindExportsAnalyzerResult} [referenceProjectResult]
|
||||||
*/
|
*/
|
||||||
const cfg = {
|
const cfg = {
|
||||||
gatherFilesConfig: {},
|
gatherFilesConfig: {},
|
||||||
referenceProjectPath: null,
|
referenceProjectPath: null,
|
||||||
targetProjectPath: null,
|
targetProjectPath: null,
|
||||||
exportsAnalyzerResult: null,
|
targetProjectResult: null,
|
||||||
importsAnalyzerResult: null,
|
referenceProjectResult: null,
|
||||||
...customConfig,
|
...customConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -224,25 +227,25 @@ class MatchImportsAnalyzer extends Analyzer {
|
||||||
/**
|
/**
|
||||||
* Traverse
|
* Traverse
|
||||||
*/
|
*/
|
||||||
let { exportsAnalyzerResult } = cfg;
|
let { referenceProjectResult } = cfg;
|
||||||
if (!exportsAnalyzerResult) {
|
if (!referenceProjectResult) {
|
||||||
const findExportsAnalyzer = new FindExportsAnalyzer();
|
const findExportsAnalyzer = new FindExportsAnalyzer();
|
||||||
exportsAnalyzerResult = await findExportsAnalyzer.execute({
|
referenceProjectResult = await findExportsAnalyzer.execute({
|
||||||
metaConfig: cfg.metaConfig,
|
metaConfig: cfg.metaConfig,
|
||||||
targetProjectPath: cfg.referenceProjectPath,
|
targetProjectPath: cfg.referenceProjectPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let { importsAnalyzerResult } = cfg;
|
let { targetProjectResult } = cfg;
|
||||||
if (!importsAnalyzerResult) {
|
if (!targetProjectResult) {
|
||||||
const findImportsAnalyzer = new FindImportsAnalyzer();
|
const findImportsAnalyzer = new FindImportsAnalyzer();
|
||||||
importsAnalyzerResult = await findImportsAnalyzer.execute({
|
targetProjectResult = await findImportsAnalyzer.execute({
|
||||||
metaConfig: cfg.metaConfig,
|
metaConfig: cfg.metaConfig,
|
||||||
targetProjectPath: cfg.targetProjectPath,
|
targetProjectPath: cfg.targetProjectPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryOutput = matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, cfg);
|
const queryOutput = matchImportsPostprocess(referenceProjectResult, targetProjectResult, cfg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalize
|
* Finalize
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@ const {
|
||||||
appendProjectDependencyPaths,
|
appendProjectDependencyPaths,
|
||||||
} = cliHelpersModule;
|
} = cliHelpersModule;
|
||||||
|
|
||||||
// Prevent (node:2860) MaxListenersExceededWarning
|
|
||||||
commander.setMaxListeners(100);
|
|
||||||
|
|
||||||
const queryResults = [];
|
const queryResults = [];
|
||||||
const rootDir = pathLib.resolve(__dirname, '../../');
|
const rootDir = pathLib.resolve(__dirname, '../../');
|
||||||
|
|
||||||
|
|
@ -63,6 +60,9 @@ describe('Providence CLI', () => {
|
||||||
let qConfStub;
|
let qConfStub;
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
// Prevent MaxListenersExceededWarning
|
||||||
|
commander.setMaxListeners(100);
|
||||||
|
|
||||||
mockWriteToJson(queryResults);
|
mockWriteToJson(queryResults);
|
||||||
suppressNonCriticalLogs();
|
suppressNonCriticalLogs();
|
||||||
|
|
||||||
|
|
@ -105,6 +105,8 @@ describe('Providence CLI', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
|
commander.setMaxListeners(10);
|
||||||
|
|
||||||
restoreSuppressNonCriticalLogs();
|
restoreSuppressNonCriticalLogs();
|
||||||
restoreMockedProjects();
|
restoreMockedProjects();
|
||||||
restoreWriteToJson();
|
restoreWriteToJson();
|
||||||
|
|
|
||||||
|
|
@ -312,17 +312,18 @@ describe('Analyzer "match-imports"', () => {
|
||||||
describe('Configuration', () => {
|
describe('Configuration', () => {
|
||||||
it(`allows to provide results of FindExportsAnalyzer and FindImportsAnalyzer`, async () => {
|
it(`allows to provide results of FindExportsAnalyzer and FindImportsAnalyzer`, async () => {
|
||||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||||
const importsAnalyzerResult = await new FindImportsAnalyzer().execute({
|
const findImportsResult = await new FindImportsAnalyzer().execute({
|
||||||
targetProjectPath: searchTargetProject.path,
|
targetProjectPath: searchTargetProject.path,
|
||||||
});
|
});
|
||||||
const exportsAnalyzerResult = await new FindExportsAnalyzer().execute({
|
const findExportsResult = await new FindExportsAnalyzer().execute({
|
||||||
targetProjectPath: referenceProject.path,
|
targetProjectPath: referenceProject.path,
|
||||||
});
|
});
|
||||||
await providence(matchImportsQueryConfig, {
|
|
||||||
..._providenceCfg,
|
const matchImportsQueryConfigExt = QueryService.getQueryConfigFromAnalyzer('match-imports', {
|
||||||
importsAnalyzerResult,
|
targetProjectResult: findImportsResult,
|
||||||
exportsAnalyzerResult,
|
referenceProjectResult: findExportsResult,
|
||||||
});
|
});
|
||||||
|
await providence(matchImportsQueryConfigExt, _providenceCfg);
|
||||||
const queryResult = queryResults[0];
|
const queryResult = queryResults[0];
|
||||||
|
|
||||||
expectedExportIdsDirect.forEach(targetId => {
|
expectedExportIdsDirect.forEach(targetId => {
|
||||||
|
|
@ -333,5 +334,8 @@ describe('Analyzer "match-imports"', () => {
|
||||||
testMatchedEntry(targetId, queryResult, ['./target-src/indirect-imports.js']);
|
testMatchedEntry(targetId, queryResult, ['./target-src/indirect-imports.js']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: Test this unwind functionality in a generic MatchAnalyzer test
|
||||||
|
it.skip(`allows to provide results of FindExportsAnalyzer and FindImportsAnalyzer from external jsons`, async () => {});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue