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 { 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
|
||||
* @param {AstDataProject[]} astDataProjects
|
||||
|
|
@ -86,6 +66,14 @@ function ensureAnalyzerResultFormat(queryOutput, configuration, analyzer) {
|
|||
delete aResult.analyzerMeta.configuration.referenceProjectPath;
|
||||
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)) {
|
||||
aResult.queryOutput.forEach(projectOutput => {
|
||||
if (projectOutput.project) {
|
||||
|
|
@ -123,6 +111,16 @@ function checkForMatchCompatibility(referencePath, targetPath) {
|
|||
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 {
|
||||
constructor() {
|
||||
this.requiredAst = 'babel';
|
||||
|
|
@ -132,15 +130,39 @@ class Analyzer {
|
|||
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
|
||||
* @returns {CachedAnalyzerResult|undefined}
|
||||
*/
|
||||
_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);
|
||||
} else if (cfg.referenceProjectResult) {
|
||||
this.referenceProjectMeta = cfg.referenceProjectResult.analyzerMeta.targetProject;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -152,9 +174,9 @@ class Analyzer {
|
|||
analyzerConfig: cfg,
|
||||
});
|
||||
|
||||
// If we have a provided result cfg.referenceProjectResult, we assume the providing
|
||||
// party provides compatible results for now...
|
||||
if (cfg.referenceProjectPath) {
|
||||
this.referenceProjectMeta = InputDataService.getProjectMeta(cfg.referenceProjectPath, true);
|
||||
|
||||
const { compatible, reason } = checkForMatchCompatibility(
|
||||
cfg.referenceProjectPath,
|
||||
cfg.targetProjectPath,
|
||||
|
|
@ -176,7 +198,7 @@ class Analyzer {
|
|||
/**
|
||||
* See if we maybe already have our result in cache in the file-system.
|
||||
*/
|
||||
const cachedResult = getCachedAnalyzerResult({
|
||||
const cachedResult = Analyzer._getCachedAnalyzerResult({
|
||||
analyzerName: this.name,
|
||||
identifier: this.identifier,
|
||||
});
|
||||
|
|
@ -186,13 +208,16 @@ class Analyzer {
|
|||
}
|
||||
|
||||
LogService.info(`starting ${LogService.pad(this.name, 16)} for ${this.identifier}`);
|
||||
|
||||
/**
|
||||
* Get reference and search-target data
|
||||
*/
|
||||
this.targetData = InputDataService.createDataObject(
|
||||
[cfg.targetProjectPath],
|
||||
cfg.gatherFilesConfig,
|
||||
);
|
||||
if (!cfg.targetProjectResult) {
|
||||
this.targetData = InputDataService.createDataObject(
|
||||
[cfg.targetProjectPath],
|
||||
cfg.gatherFilesConfig,
|
||||
);
|
||||
}
|
||||
|
||||
if (cfg.referenceProjectPath) {
|
||||
this.referenceData = InputDataService.createDataObject(
|
||||
|
|
@ -251,6 +276,27 @@ class Analyzer {
|
|||
*/
|
||||
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 };
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, c
|
|||
importEntryResult.importSpecifiers.includes(exportSpecifier) ||
|
||||
importEntryResult.importSpecifiers.includes('[*]');
|
||||
|
||||
if (!hasExportSpecifierImported) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @example
|
||||
* exportFile './foo.js'
|
||||
|
|
@ -98,14 +102,13 @@ function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, c
|
|||
externalRootPath: cfg.referenceProjectPath,
|
||||
});
|
||||
|
||||
// TODO: transitive deps recognition. Could also be distinct post processor
|
||||
// // export { z } from '../foo.js'
|
||||
// // import { z } from '@reference/foo.js'
|
||||
// (exportEntryResult.normalizedSource === importEntryResult.normalizedSource)
|
||||
|
||||
if (hasExportSpecifierImported && isFromSameSource) {
|
||||
filteredImportsList.add(`${importProject}::${file}`);
|
||||
if (!isFromSameSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: transitive deps recognition? Could also be distinct post processor
|
||||
|
||||
filteredImportsList.add(`${importProject}::${file}`);
|
||||
}),
|
||||
);
|
||||
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
||||
|
|
@ -201,15 +204,15 @@ class MatchImportsAnalyzer extends Analyzer {
|
|||
* @property {GatherFilesConfig} [gatherFilesConfig]
|
||||
* @property {array} [referenceProjectPath] reference paths
|
||||
* @property {array} [targetProjectPath] search target paths
|
||||
* @property {FindExportsAnalyzerResult} [exportsAnalyzerResult]
|
||||
* @property {FindImportsAnalyzerResult} [importsAnalyzerResult]
|
||||
* @property {FindImportsAnalyzerResult} [targetProjectResult]
|
||||
* @property {FindExportsAnalyzerResult} [referenceProjectResult]
|
||||
*/
|
||||
const cfg = {
|
||||
gatherFilesConfig: {},
|
||||
referenceProjectPath: null,
|
||||
targetProjectPath: null,
|
||||
exportsAnalyzerResult: null,
|
||||
importsAnalyzerResult: null,
|
||||
targetProjectResult: null,
|
||||
referenceProjectResult: null,
|
||||
...customConfig,
|
||||
};
|
||||
|
||||
|
|
@ -224,25 +227,25 @@ class MatchImportsAnalyzer extends Analyzer {
|
|||
/**
|
||||
* Traverse
|
||||
*/
|
||||
let { exportsAnalyzerResult } = cfg;
|
||||
if (!exportsAnalyzerResult) {
|
||||
let { referenceProjectResult } = cfg;
|
||||
if (!referenceProjectResult) {
|
||||
const findExportsAnalyzer = new FindExportsAnalyzer();
|
||||
exportsAnalyzerResult = await findExportsAnalyzer.execute({
|
||||
referenceProjectResult = await findExportsAnalyzer.execute({
|
||||
metaConfig: cfg.metaConfig,
|
||||
targetProjectPath: cfg.referenceProjectPath,
|
||||
});
|
||||
}
|
||||
|
||||
let { importsAnalyzerResult } = cfg;
|
||||
if (!importsAnalyzerResult) {
|
||||
let { targetProjectResult } = cfg;
|
||||
if (!targetProjectResult) {
|
||||
const findImportsAnalyzer = new FindImportsAnalyzer();
|
||||
importsAnalyzerResult = await findImportsAnalyzer.execute({
|
||||
targetProjectResult = await findImportsAnalyzer.execute({
|
||||
metaConfig: cfg.metaConfig,
|
||||
targetProjectPath: cfg.targetProjectPath,
|
||||
});
|
||||
}
|
||||
|
||||
const queryOutput = matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, cfg);
|
||||
const queryOutput = matchImportsPostprocess(referenceProjectResult, targetProjectResult, cfg);
|
||||
|
||||
/**
|
||||
* Finalize
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ const {
|
|||
appendProjectDependencyPaths,
|
||||
} = cliHelpersModule;
|
||||
|
||||
// Prevent (node:2860) MaxListenersExceededWarning
|
||||
commander.setMaxListeners(100);
|
||||
|
||||
const queryResults = [];
|
||||
const rootDir = pathLib.resolve(__dirname, '../../');
|
||||
|
||||
|
|
@ -63,6 +60,9 @@ describe('Providence CLI', () => {
|
|||
let qConfStub;
|
||||
|
||||
before(() => {
|
||||
// Prevent MaxListenersExceededWarning
|
||||
commander.setMaxListeners(100);
|
||||
|
||||
mockWriteToJson(queryResults);
|
||||
suppressNonCriticalLogs();
|
||||
|
||||
|
|
@ -105,6 +105,8 @@ describe('Providence CLI', () => {
|
|||
});
|
||||
|
||||
after(() => {
|
||||
commander.setMaxListeners(10);
|
||||
|
||||
restoreSuppressNonCriticalLogs();
|
||||
restoreMockedProjects();
|
||||
restoreWriteToJson();
|
||||
|
|
|
|||
|
|
@ -312,17 +312,18 @@ describe('Analyzer "match-imports"', () => {
|
|||
describe('Configuration', () => {
|
||||
it(`allows to provide results of FindExportsAnalyzer and FindImportsAnalyzer`, async () => {
|
||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||
const importsAnalyzerResult = await new FindImportsAnalyzer().execute({
|
||||
const findImportsResult = await new FindImportsAnalyzer().execute({
|
||||
targetProjectPath: searchTargetProject.path,
|
||||
});
|
||||
const exportsAnalyzerResult = await new FindExportsAnalyzer().execute({
|
||||
const findExportsResult = await new FindExportsAnalyzer().execute({
|
||||
targetProjectPath: referenceProject.path,
|
||||
});
|
||||
await providence(matchImportsQueryConfig, {
|
||||
..._providenceCfg,
|
||||
importsAnalyzerResult,
|
||||
exportsAnalyzerResult,
|
||||
|
||||
const matchImportsQueryConfigExt = QueryService.getQueryConfigFromAnalyzer('match-imports', {
|
||||
targetProjectResult: findImportsResult,
|
||||
referenceProjectResult: findExportsResult,
|
||||
});
|
||||
await providence(matchImportsQueryConfigExt, _providenceCfg);
|
||||
const queryResult = queryResults[0];
|
||||
|
||||
expectedExportIdsDirect.forEach(targetId => {
|
||||
|
|
@ -333,5 +334,8 @@ describe('Analyzer "match-imports"', () => {
|
|||
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