fix: support export maps for match-* analyzers
This commit is contained in:
parent
c59dda0c93
commit
1e8839f2fd
12 changed files with 703 additions and 340 deletions
5
.changeset/afraid-peaches-type.md
Normal file
5
.changeset/afraid-peaches-type.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'providence-analytics': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Support export maps for match-\* analyzers
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"@babel/register": "^7.5.5",
|
"@babel/register": "^7.5.5",
|
||||||
"@babel/traverse": "^7.5.5",
|
"@babel/traverse": "^7.5.5",
|
||||||
"@babel/types": "^7.9.0",
|
"@babel/types": "^7.9.0",
|
||||||
"@rollup/plugin-node-resolve": "^7.1.1",
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||||
"@typescript-eslint/typescript-estree": "^3.0.0",
|
"@typescript-eslint/typescript-estree": "^3.0.0",
|
||||||
"anymatch": "^3.1.1",
|
"anymatch": "^3.1.1",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
"inquirer": "^7.0.0",
|
"inquirer": "^7.0.0",
|
||||||
"is-negated-glob": "^1.0.0",
|
"is-negated-glob": "^1.0.0",
|
||||||
"lit-element": "~2.4.0",
|
"lit-element": "~2.4.0",
|
||||||
|
"mock-require": "^3.0.3",
|
||||||
"ora": "^3.4.0",
|
"ora": "^3.4.0",
|
||||||
"parse5": "^5.1.1",
|
"parse5": "^5.1.1",
|
||||||
"read-package-tree": "5.3.1",
|
"read-package-tree": "5.3.1",
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,51 @@
|
||||||
const fs = require('fs');
|
|
||||||
const pathLib = require('path');
|
|
||||||
const { isRelativeSourcePath } = require('../../utils/relative-source-path.js');
|
const { isRelativeSourcePath } = require('../../utils/relative-source-path.js');
|
||||||
const { LogService } = require('../../services/LogService.js');
|
const { LogService } = require('../../services/LogService.js');
|
||||||
|
const { resolveImportPath } = require('../../utils/resolve-import-path.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Use utils/resolve-import-path for 100% accuracy
|
* @param {string} importee like '@lion/core/myFile.js'
|
||||||
*
|
* @returns {string} project name ('@lion/core')
|
||||||
* - from: 'reference-project/foo.js'
|
|
||||||
* - to: './foo.js'
|
|
||||||
* When we need to resolve to the main entry:
|
|
||||||
* - from: 'reference-project'
|
|
||||||
* - to: './index.js' (or other file specified in package.json 'main')
|
|
||||||
* @param {object} config
|
|
||||||
* @param {string} config.requestedExternalSource
|
|
||||||
* @param {{name, mainEntry}} config.externalProjectMeta
|
|
||||||
* @param {string} config.externalRootPath
|
|
||||||
* @returns {string|null}
|
|
||||||
*/
|
*/
|
||||||
function fromImportToExportPerspective({
|
function getProjectFromImportee(importee) {
|
||||||
requestedExternalSource,
|
const scopedProject = importee[0] === '@';
|
||||||
externalProjectMeta,
|
|
||||||
externalRootPath,
|
|
||||||
}) {
|
|
||||||
if (isRelativeSourcePath(requestedExternalSource)) {
|
|
||||||
LogService.warn('[fromImportToExportPerspective] Please only provide external import paths');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scopedProject = requestedExternalSource[0] === '@';
|
|
||||||
// 'external-project/src/file.js' -> ['external-project', 'src', file.js']
|
// 'external-project/src/file.js' -> ['external-project', 'src', file.js']
|
||||||
let splitSource = requestedExternalSource.split('/');
|
let splitSource = importee.split('/');
|
||||||
if (scopedProject) {
|
if (scopedProject) {
|
||||||
// '@external/project'
|
// '@external/project'
|
||||||
splitSource = [splitSource.slice(0, 2).join('/'), ...splitSource.slice(2)];
|
splitSource = [splitSource.slice(0, 2).join('/'), ...splitSource.slice(2)];
|
||||||
}
|
}
|
||||||
// ['external-project', 'src', 'file.js'] -> 'external-project'
|
// ['external-project', 'src', 'file.js'] -> 'external-project'
|
||||||
const project = splitSource.slice(0, 1).join('/');
|
const project = splitSource.slice(0, 1).join('/');
|
||||||
// ['external-project', 'src', 'file.js'] -> 'src/file.js'
|
|
||||||
const localPath = splitSource.slice(1).join('/');
|
|
||||||
|
|
||||||
if (externalProjectMeta.name !== project) {
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets local path from reference project
|
||||||
|
*
|
||||||
|
* - from: 'reference-project/foo'
|
||||||
|
* - to: './foo.js'
|
||||||
|
* When we need to resolve to the main entry:
|
||||||
|
* - from: 'reference-project'
|
||||||
|
* - to: './index.js' (or other file specified in package.json 'main')
|
||||||
|
* @param {object} config
|
||||||
|
* @param {string} config.importee 'reference-project/foo.js'
|
||||||
|
* @param {string} config.importer '/my/project/importing-file.js'
|
||||||
|
* @returns {Promise<string|null>} './foo.js'
|
||||||
|
*/
|
||||||
|
async function fromImportToExportPerspective({ importee, importer }) {
|
||||||
|
if (isRelativeSourcePath(importee)) {
|
||||||
|
LogService.warn('[fromImportToExportPerspective] Please only provide external import paths');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localPath) {
|
const absolutePath = await resolveImportPath(importee, importer);
|
||||||
// like '@open-wc/x/y.js'
|
const projectName = getProjectFromImportee(importee);
|
||||||
// Now, we need to resolve to a file or path. Even though a path can contain '.',
|
|
||||||
// we still need to check if we're not dealing with a folder.
|
|
||||||
// - '@open-wc/x/y.js' -> '@open-wc/x/y.js' or... '@open-wc/x/y.js/index.js' ?
|
|
||||||
// - or 'lion-based-ui/test' -> 'lion-based-ui/test/index.js' or 'lion-based-ui/test' ?
|
|
||||||
if (externalRootPath) {
|
|
||||||
const pathToCheck = pathLib.resolve(externalRootPath, `./${localPath}`);
|
|
||||||
|
|
||||||
if (fs.existsSync(pathToCheck)) {
|
// from /my/reference/project/packages/foo/index.js to './packages/foo/index.js'
|
||||||
const stat = fs.statSync(pathToCheck);
|
return absolutePath
|
||||||
if (stat && stat.isFile()) {
|
? absolutePath.replace(new RegExp(`^.*/${projectName}/?(.*)$`), './$1')
|
||||||
return `./${localPath}`; // '/path/to/lion-based-ui/fol.der' is a file
|
: null;
|
||||||
}
|
|
||||||
return `./${localPath}/index.js`; // '/path/to/lion-based-ui/fol.der' is a folder
|
|
||||||
// eslint-disable-next-line no-else-return
|
|
||||||
} else if (fs.existsSync(`${pathToCheck}.js`)) {
|
|
||||||
return `./${localPath}.js`; // '/path/to/lion-based-ui/fol.der' is file '/path/to/lion-based-ui/fol.der.js'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return `./${localPath}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// like '@lion/core'
|
|
||||||
let mainEntry = externalProjectMeta.mainEntry || 'index.js';
|
|
||||||
if (!mainEntry.startsWith('./')) {
|
|
||||||
mainEntry = `./${mainEntry}`;
|
|
||||||
}
|
|
||||||
return mainEntry;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { fromImportToExportPerspective };
|
module.exports = { fromImportToExportPerspective };
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable no-continue */
|
||||||
|
const pathLib = require('path');
|
||||||
/* eslint-disable no-shadow, no-param-reassign */
|
/* eslint-disable no-shadow, no-param-reassign */
|
||||||
const FindImportsAnalyzer = require('./find-imports.js');
|
const FindImportsAnalyzer = require('./find-imports.js');
|
||||||
const FindExportsAnalyzer = require('./find-exports.js');
|
const FindExportsAnalyzer = require('./find-exports.js');
|
||||||
|
|
@ -5,19 +7,13 @@ const { Analyzer } = require('./helpers/Analyzer.js');
|
||||||
const { fromImportToExportPerspective } = require('./helpers/from-import-to-export-perspective.js');
|
const { fromImportToExportPerspective } = require('./helpers/from-import-to-export-perspective.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Helper method for matchImportsPostprocess. Modifies its resultsObj
|
* @typedef {import('../types/find-imports').FindImportsAnalyzerResult} FindImportsAnalyzerResult
|
||||||
* @param {object} resultsObj
|
* @typedef {import('../types/find-exports').FindExportsAnalyzerResult} FindExportsAnalyzerResult
|
||||||
* @param {string} exportId like 'myExport::./reference-project/my/export.js::my-project'
|
* @typedef {import('../types/find-exports').IterableFindExportsAnalyzerEntry} IterableFindExportsAnalyzerEntry
|
||||||
* @param {Set<string>} filteredList
|
* @typedef {import('../types/find-imports').IterableFindImportsAnalyzerEntry} IterableFindImportsAnalyzerEntry
|
||||||
|
* @typedef {import('../types/match-imports').ConciseMatchImportsAnalyzerResult} ConciseMatchImportsAnalyzerResult
|
||||||
|
* @typedef {import('../types/core').PathRelativeFromRoot} PathRelativeFromRoot
|
||||||
*/
|
*/
|
||||||
function storeResult(resultsObj, exportId, filteredList, meta) {
|
|
||||||
if (!resultsObj[exportId]) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
resultsObj[exportId] = { meta };
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
resultsObj[exportId].files = [...(resultsObj[exportId].files || []), ...Array.from(filteredList)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Needed in case fromImportToExportPerspective does not have a
|
* Needed in case fromImportToExportPerspective does not have a
|
||||||
|
|
@ -33,166 +29,227 @@ function compareImportAndExportPaths(exportPath, translatedImportPath) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to more easily iterable object
|
||||||
|
*
|
||||||
|
* From:
|
||||||
|
* ```js
|
||||||
|
* [
|
||||||
|
* "file": "./file-1.js",
|
||||||
|
* "result": [{
|
||||||
|
* "exportSpecifiers": [ "a", "b"],
|
||||||
|
* "localMap": [{...},{...}],
|
||||||
|
* "source": null,
|
||||||
|
* "rootFileMap": [{"currentFileSpecifier": "a", "rootFile": { "file": "[current]", "specifier": "a" }}]
|
||||||
|
* }, ...],
|
||||||
|
* ```
|
||||||
|
* To:
|
||||||
|
* ```js
|
||||||
|
* [{
|
||||||
|
* "file": ""./file-1.js",
|
||||||
|
* "exportSpecifier": "a",
|
||||||
|
* "localMap": {...},
|
||||||
|
* "source": null,
|
||||||
|
* "rootFileMap": {...}
|
||||||
|
* },
|
||||||
|
* {{
|
||||||
|
* "file": ""./file-1.js",
|
||||||
|
* "exportSpecifier": "b",
|
||||||
|
* "localMap": {...},
|
||||||
|
* "source": null,
|
||||||
|
* "rootFileMap": {...}
|
||||||
|
* }}],
|
||||||
|
*
|
||||||
|
* @param {FindExportsAnalyzerResult} exportsAnalyzerResult
|
||||||
|
*/
|
||||||
|
function transformIntoIterableFindExportsOutput(exportsAnalyzerResult) {
|
||||||
|
/** @type {IterableFindExportsAnalyzerEntry[]} */
|
||||||
|
const iterableEntries = [];
|
||||||
|
|
||||||
|
for (const { file, result } of exportsAnalyzerResult.queryOutput) {
|
||||||
|
for (const { exportSpecifiers, source, rootFileMap, localMap, meta } of result) {
|
||||||
|
if (!exportSpecifiers) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (const exportSpecifier of exportSpecifiers) {
|
||||||
|
const i = exportSpecifiers.indexOf(exportSpecifier);
|
||||||
|
/** @type {IterableFindExportsAnalyzerEntry} */
|
||||||
|
const resultEntry = {
|
||||||
|
file,
|
||||||
|
specifier: exportSpecifier,
|
||||||
|
source,
|
||||||
|
rootFile: rootFileMap ? rootFileMap[i] : undefined,
|
||||||
|
localSpecifier: localMap ? localMap[i] : undefined,
|
||||||
|
meta,
|
||||||
|
};
|
||||||
|
iterableEntries.push(resultEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to more easily iterable object
|
||||||
|
*
|
||||||
|
* From:
|
||||||
|
* ```js
|
||||||
|
* [
|
||||||
|
* "file": "./file-1.js",
|
||||||
|
* "result": [{
|
||||||
|
* "importSpecifiers": [ "a", "b" ],
|
||||||
|
* "source": "exporting-ref-project",
|
||||||
|
* "normalizedSource": "exporting-ref-project"
|
||||||
|
* }], ,
|
||||||
|
* ```
|
||||||
|
* To:
|
||||||
|
* ```js
|
||||||
|
* [{
|
||||||
|
* "file": ""./file-1.js",
|
||||||
|
* "importSpecifier": "a",,
|
||||||
|
* "source": "exporting-ref-project",
|
||||||
|
* "normalizedSource": "exporting-ref-project"
|
||||||
|
* },
|
||||||
|
* {{
|
||||||
|
* "file": ""./file-1.js",
|
||||||
|
* "importSpecifier": "b",,
|
||||||
|
* "source": "exporting-ref-project",
|
||||||
|
* "normalizedSource": "exporting-ref-project"
|
||||||
|
* }}],
|
||||||
|
*
|
||||||
|
* @param {FindImportsAnalyzerResult} importsAnalyzerResult
|
||||||
|
*/
|
||||||
|
function transformIntoIterableFindImportsOutput(importsAnalyzerResult) {
|
||||||
|
/** @type {IterableFindImportsAnalyzerEntry[]} */
|
||||||
|
const iterableEntries = [];
|
||||||
|
|
||||||
|
for (const { file, result } of importsAnalyzerResult.queryOutput) {
|
||||||
|
for (const { importSpecifiers, source, normalizedSource } of result) {
|
||||||
|
if (!importSpecifiers) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (const importSpecifier of importSpecifiers) {
|
||||||
|
/** @type {IterableFindImportsAnalyzerEntry} */
|
||||||
|
const resultEntry = {
|
||||||
|
file,
|
||||||
|
specifier: importSpecifier,
|
||||||
|
source,
|
||||||
|
normalizedSource,
|
||||||
|
};
|
||||||
|
iterableEntries.push(resultEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a concise results array a 'compatible resultsArray' (compatible with dashbaord + tests + ...?)
|
||||||
|
* @param {object[]} conciseResultsArray
|
||||||
|
* @param {string} importProject
|
||||||
|
*/
|
||||||
|
function createCompatibleMatchImportsResult(conciseResultsArray, importProject) {
|
||||||
|
const compatibleResult = [];
|
||||||
|
for (const matchedExportEntry of conciseResultsArray) {
|
||||||
|
const [name, filePath, project] = matchedExportEntry.exportSpecifier.id.split('::');
|
||||||
|
const exportSpecifier = {
|
||||||
|
...matchedExportEntry.exportSpecifier,
|
||||||
|
name,
|
||||||
|
filePath,
|
||||||
|
project,
|
||||||
|
};
|
||||||
|
compatibleResult.push({
|
||||||
|
exportSpecifier,
|
||||||
|
matchesPerProject: [{ project: importProject, files: matchedExportEntry.importProjectFiles }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return compatibleResult;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {FindExportsAnalyzerResult} exportsAnalyzerResult
|
* @param {FindExportsAnalyzerResult} exportsAnalyzerResult
|
||||||
* @param {FindImportsAnalyzerResult} importsAnalyzerResult
|
* @param {FindImportsAnalyzerResult} importsAnalyzerResult
|
||||||
* @param {matchImportsConfig} customConfig
|
* @param {matchImportsConfig} customConfig
|
||||||
* @returns {AnalyzerResult}
|
* @returns {Promise<AnalyzerResult>}
|
||||||
*/
|
*/
|
||||||
function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, customConfig) {
|
async function matchImportsPostprocess(exportsAnalyzerResult, importsAnalyzerResult, customConfig) {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
...customConfig,
|
...customConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// TODO: What if this info is retrieved from cached importProject/target project?
|
||||||
* Step 1: a 'flat' data structure
|
const importProjectPath = cfg.targetProjectPath;
|
||||||
* @desc Create a key value storage map for exports/imports matches
|
|
||||||
* - key: `${exportSpecifier}::${normalizedSource}::${project}` from reference project
|
|
||||||
* - value: an array of import file matches like `${targetProject}::${normalizedSource}`
|
|
||||||
* @example
|
|
||||||
* {
|
|
||||||
* 'myExport::./reference-project/my/export.js::my-project' : {
|
|
||||||
* meta: {...},
|
|
||||||
* files: [
|
|
||||||
* 'target-project-a::./import/file.js',
|
|
||||||
* 'target-project-b::./another/import/file.js'
|
|
||||||
* ],
|
|
||||||
* ]}
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const resultsObj = {};
|
|
||||||
|
|
||||||
exportsAnalyzerResult.queryOutput.forEach(exportEntry => {
|
const iterableFindExportsOutput = transformIntoIterableFindExportsOutput(exportsAnalyzerResult);
|
||||||
const exportsProjectObj = exportsAnalyzerResult.analyzerMeta.targetProject;
|
const iterableFindImportsOutput = transformIntoIterableFindImportsOutput(importsAnalyzerResult);
|
||||||
|
|
||||||
// Look for all specifiers that are exported, like [import {specifier} 'lion-based-ui/foo.js']
|
/** @type {ConciseMatchImportsAnalyzerResult} */
|
||||||
exportEntry.result.forEach(exportEntryResult => {
|
const conciseResultsArray = [];
|
||||||
if (!exportEntryResult.exportSpecifiers) {
|
|
||||||
return;
|
for (const exportEntry of iterableFindExportsOutput) {
|
||||||
|
for (const importEntry of iterableFindImportsOutput) {
|
||||||
|
/**
|
||||||
|
* 1. Does target import ref specifier?
|
||||||
|
*
|
||||||
|
* Example context (read by 'find-imports'/'find-exports' analyzers)
|
||||||
|
* - export (/folder/exporting-file.js):
|
||||||
|
* `export const x = 'foo'`
|
||||||
|
* - import (target-project-a/importing-file.js):
|
||||||
|
* `import { x, y } from '@reference-repo/folder/exporting-file.js'`
|
||||||
|
* Example variables (extracted by 'find-imports'/'find-exports' analyzers)
|
||||||
|
* - exportSpecifier: 'x'
|
||||||
|
* - importSpecifiers: ['x', 'y']
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
const hasExportSpecifierImported =
|
||||||
|
exportEntry.specifier === importEntry.specifier || importEntry.specifier === '[*]';
|
||||||
|
if (!hasExportSpecifierImported) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportEntryResult.exportSpecifiers.forEach(exportSpecifier => {
|
/**
|
||||||
// Get all unique imports (name::source::project combinations) that match current exportSpecifier
|
* 2. Are we from the same source?
|
||||||
const filteredImportsList = new Set();
|
* A.k.a. is source required by target the same as the one found in target.
|
||||||
const exportId = `${exportSpecifier}::${exportEntry.file}::${exportsProjectObj.name}`;
|
* (we know the specifier name is tha same, now we need to check the file as well.)
|
||||||
|
*
|
||||||
// eslint-disable-next-line no-shadow
|
* Example:
|
||||||
// importsAnalyzerResult.queryOutput.forEach(({ entries, project }) => {
|
* exportFile './foo.js'
|
||||||
const importProject = importsAnalyzerResult.analyzerMeta.targetProject.name;
|
* => export const z = 'bar'
|
||||||
importsAnalyzerResult.queryOutput.forEach(({ result, file }) =>
|
* importFile 'importing-target-project/file.js'
|
||||||
result.forEach(importEntryResult => {
|
* => import { z } from '@reference/foo.js'
|
||||||
/**
|
* @type {PathRelativeFromRoot}
|
||||||
* @example
|
*/
|
||||||
* Example context (read by 'find-imports'/'find-exports' analyzers)
|
const fromImportToExport = await fromImportToExportPerspective({
|
||||||
* - export (/folder/exporting-file.js):
|
importee: importEntry.normalizedSource,
|
||||||
* `export const x = 'foo'`
|
importer: pathLib.resolve(importProjectPath, importEntry.file),
|
||||||
* - import (target-project-a/importing-file.js):
|
|
||||||
* `import { x, y } from '@reference-repo/folder/exporting-file.js'`
|
|
||||||
* Example variables (extracted by 'find-imports'/'find-exports' analyzers)
|
|
||||||
* - exportSpecifier: 'x'
|
|
||||||
* - importSpecifiers: ['x', 'y']
|
|
||||||
*/
|
|
||||||
const hasExportSpecifierImported =
|
|
||||||
// ['x', 'y'].includes('x')
|
|
||||||
importEntryResult.importSpecifiers.includes(exportSpecifier) ||
|
|
||||||
importEntryResult.importSpecifiers.includes('[*]');
|
|
||||||
|
|
||||||
if (!hasExportSpecifierImported) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* exportFile './foo.js'
|
|
||||||
* => export const z = 'bar'
|
|
||||||
* importFile 'importing-target-project/file.js'
|
|
||||||
* => import { z } from '@reference/foo.js'
|
|
||||||
*/
|
|
||||||
const fromImportToExport = fromImportToExportPerspective({
|
|
||||||
requestedExternalSource: importEntryResult.normalizedSource,
|
|
||||||
externalProjectMeta: exportsProjectObj,
|
|
||||||
externalRootPath: cfg.referenceProjectResult ? null : cfg.referenceProjectPath,
|
|
||||||
});
|
|
||||||
const isFromSameSource = compareImportAndExportPaths(
|
|
||||||
exportEntry.file,
|
|
||||||
fromImportToExport,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isFromSameSource) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: transitive deps recognition? Could also be distinct post processor
|
|
||||||
|
|
||||||
filteredImportsList.add(`${importProject}::${file}`);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
|
||||||
});
|
});
|
||||||
});
|
const isFromSameSource = compareImportAndExportPaths(exportEntry.file, fromImportToExport);
|
||||||
});
|
if (!isFromSameSource) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step 2: a rich data structure
|
* 3. When above checks pass, we have a match.
|
||||||
* @desc Transform resultObj from step 1 into an array of objects
|
* Add it to the results array
|
||||||
* @example
|
*/
|
||||||
* [{
|
const id = `${exportEntry.specifier}::${exportEntry.file}::${exportsAnalyzerResult.analyzerMeta.targetProject.name}`;
|
||||||
* exportSpecifier: {
|
const resultForCurrentExport = conciseResultsArray.find(entry => entry.id === id);
|
||||||
* // name under which it is registered in npm ("name" attr in package.json)
|
if (resultForCurrentExport) {
|
||||||
* name: 'RefClass',
|
resultForCurrentExport.importProjectFiles.push(importEntry.file);
|
||||||
* project: 'exporting-ref-project',
|
} else {
|
||||||
* filePath: './ref-src/core.js',
|
conciseResultsArray.push({
|
||||||
* id: 'RefClass::ref-src/core.js::exporting-ref-project',
|
exportSpecifier: { id, ...(exportEntry.meta ? { meta: exportEntry.meta } : {}) },
|
||||||
* meta: {...},
|
importProjectFiles: [importEntry.file],
|
||||||
*
|
});
|
||||||
* // most likely via post processor
|
}
|
||||||
* },
|
}
|
||||||
* // All the matched targets (files importing the specifier), ordered per project
|
}
|
||||||
* matchesPerProject: [
|
|
||||||
* {
|
|
||||||
* project: 'importing-target-project',
|
|
||||||
* files: [
|
|
||||||
* './target-src/indirect-imports.js',
|
|
||||||
* ...
|
|
||||||
* ],
|
|
||||||
* },
|
|
||||||
* ...
|
|
||||||
* ],
|
|
||||||
* }]
|
|
||||||
*/
|
|
||||||
const resultsArray = Object.entries(resultsObj)
|
|
||||||
.map(([id, flatResult]) => {
|
|
||||||
const [exportSpecifierName, filePath, project] = id.split('::');
|
|
||||||
const { meta } = flatResult;
|
|
||||||
|
|
||||||
const exportSpecifier = {
|
const importProject = importsAnalyzerResult.analyzerMeta.targetProject.name;
|
||||||
name: exportSpecifierName,
|
return /** @type {AnalyzerResult} */ createCompatibleMatchImportsResult(
|
||||||
project,
|
conciseResultsArray,
|
||||||
filePath,
|
importProject,
|
||||||
id,
|
);
|
||||||
...(meta || {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const matchesPerProject = [];
|
|
||||||
flatResult.files.forEach(projectFile => {
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
const [project, file] = projectFile.split('::');
|
|
||||||
let projectEntry = matchesPerProject.find(m => m.project === project);
|
|
||||||
if (!projectEntry) {
|
|
||||||
matchesPerProject.push({ project, files: [] });
|
|
||||||
projectEntry = matchesPerProject[matchesPerProject.length - 1];
|
|
||||||
}
|
|
||||||
projectEntry.files.push(file);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
exportSpecifier,
|
|
||||||
matchesPerProject,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(r => Object.keys(r.matchesPerProject).length);
|
|
||||||
|
|
||||||
return /** @type {AnalyzerResult} */ resultsArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MatchImportsAnalyzer extends Analyzer {
|
class MatchImportsAnalyzer extends Analyzer {
|
||||||
|
|
@ -236,6 +293,7 @@ class MatchImportsAnalyzer extends Analyzer {
|
||||||
* Prepare
|
* Prepare
|
||||||
*/
|
*/
|
||||||
const analyzerResult = this._prepare(cfg);
|
const analyzerResult = this._prepare(cfg);
|
||||||
|
|
||||||
if (analyzerResult) {
|
if (analyzerResult) {
|
||||||
return analyzerResult;
|
return analyzerResult;
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +321,11 @@ class MatchImportsAnalyzer extends Analyzer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryOutput = matchImportsPostprocess(referenceProjectResult, targetProjectResult, cfg);
|
const queryOutput = await matchImportsPostprocess(
|
||||||
|
referenceProjectResult,
|
||||||
|
targetProjectResult,
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalize
|
* Finalize
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable no-continue */
|
||||||
|
const pathLib = require('path');
|
||||||
/* eslint-disable no-shadow, no-param-reassign */
|
/* eslint-disable no-shadow, no-param-reassign */
|
||||||
const FindClassesAnalyzer = require('./find-classes.js');
|
const FindClassesAnalyzer = require('./find-classes.js');
|
||||||
const FindExportsAnalyzer = require('./find-exports.js');
|
const FindExportsAnalyzer = require('./find-exports.js');
|
||||||
|
|
@ -63,12 +65,13 @@ function storeResult(resultsObj, exportId, filteredList, meta) {
|
||||||
* @param {MatchSubclassesConfig} customConfig
|
* @param {MatchSubclassesConfig} customConfig
|
||||||
* @returns {AnalyzerResult}
|
* @returns {AnalyzerResult}
|
||||||
*/
|
*/
|
||||||
function matchSubclassesPostprocess(
|
async function matchSubclassesPostprocess(
|
||||||
exportsAnalyzerResult,
|
exportsAnalyzerResult,
|
||||||
targetClassesAnalyzerResult,
|
targetClassesAnalyzerResult,
|
||||||
refClassesAResult,
|
refClassesAResult,
|
||||||
customConfig,
|
customConfig,
|
||||||
) {
|
) {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const cfg = {
|
const cfg = {
|
||||||
...customConfig,
|
...customConfig,
|
||||||
};
|
};
|
||||||
|
|
@ -91,17 +94,17 @@ function matchSubclassesPostprocess(
|
||||||
*/
|
*/
|
||||||
const resultsObj = {};
|
const resultsObj = {};
|
||||||
|
|
||||||
exportsAnalyzerResult.queryOutput.forEach(exportEntry => {
|
for (const exportEntry of exportsAnalyzerResult.queryOutput) {
|
||||||
const exportsProjectObj = exportsAnalyzerResult.analyzerMeta.targetProject;
|
const exportsProjectObj = exportsAnalyzerResult.analyzerMeta.targetProject;
|
||||||
const exportsProjectName = exportsProjectObj.name;
|
const exportsProjectName = exportsProjectObj.name;
|
||||||
|
|
||||||
// Look for all specifiers that are exported, like [import {specifier} 'lion-based-ui/foo.js']
|
// Look for all specifiers that are exported, like [import {specifier} 'lion-based-ui/foo.js']
|
||||||
exportEntry.result.forEach(exportEntryResult => {
|
for (const exportEntryResult of exportEntry.result) {
|
||||||
if (!exportEntryResult.exportSpecifiers) {
|
if (!exportEntryResult.exportSpecifiers) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportEntryResult.exportSpecifiers.forEach(exportSpecifier => {
|
for (const exportSpecifier of exportEntryResult.exportSpecifiers) {
|
||||||
// Get all unique imports (name::source::project combinations) that match current
|
// Get all unique imports (name::source::project combinations) that match current
|
||||||
// exportSpecifier
|
// exportSpecifier
|
||||||
const filteredImportsList = new Set();
|
const filteredImportsList = new Set();
|
||||||
|
|
@ -109,8 +112,13 @@ function matchSubclassesPostprocess(
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
const importProject = targetClassesAnalyzerResult.analyzerMeta.targetProject.name;
|
const importProject = targetClassesAnalyzerResult.analyzerMeta.targetProject.name;
|
||||||
targetClassesAnalyzerResult.queryOutput.forEach(({ result, file }) =>
|
|
||||||
result.forEach(classEntryResult => {
|
// TODO: What if this info is retrieved from cached importProject/target project?
|
||||||
|
const importProjectPath = cfg.targetProjectPath;
|
||||||
|
for (const { result, file } of targetClassesAnalyzerResult.queryOutput) {
|
||||||
|
// targetClassesAnalyzerResult.queryOutput.forEach(({ result, file }) =>
|
||||||
|
for (const classEntryResult of result) {
|
||||||
|
// result.forEach(classEntryResult => {
|
||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
* Example context (read by 'find-classes'/'find-exports' analyzers)
|
* Example context (read by 'find-classes'/'find-exports' analyzers)
|
||||||
|
|
@ -133,7 +141,7 @@ function matchSubclassesPostprocess(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!classMatch) {
|
if (!classMatch) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -147,11 +155,10 @@ function matchSubclassesPostprocess(
|
||||||
*/
|
*/
|
||||||
const isFromSameSource =
|
const isFromSameSource =
|
||||||
exportEntry.file ===
|
exportEntry.file ===
|
||||||
fromImportToExportPerspective({
|
(await fromImportToExportPerspective({
|
||||||
requestedExternalSource: classMatch.rootFile.file,
|
importee: classMatch.rootFile.file,
|
||||||
externalProjectMeta: exportsProjectObj,
|
importer: pathLib.resolve(importProjectPath, file),
|
||||||
externalRootPath: cfg.referenceProjectPath,
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
if (classMatch && isFromSameSource) {
|
if (classMatch && isFromSameSource) {
|
||||||
const memberOverrides = getMemberOverrides(
|
const memberOverrides = getMemberOverrides(
|
||||||
|
|
@ -166,12 +173,12 @@ function matchSubclassesPostprocess(
|
||||||
memberOverrides,
|
memberOverrides,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}
|
||||||
);
|
}
|
||||||
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
storeResult(resultsObj, exportId, filteredImportsList, exportEntry.meta);
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step 2: a rich data structure
|
* Step 2: a rich data structure
|
||||||
|
|
@ -313,7 +320,7 @@ class MatchSubclassesAnalyzer extends Analyzer {
|
||||||
skipCheckMatchCompatibility: cfg.skipCheckMatchCompatibility,
|
skipCheckMatchCompatibility: cfg.skipCheckMatchCompatibility,
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryOutput = matchSubclassesPostprocess(
|
const queryOutput = await matchSubclassesPostprocess(
|
||||||
exportsAnalyzerResult,
|
exportsAnalyzerResult,
|
||||||
targetClassesAnalyzerResult,
|
targetClassesAnalyzerResult,
|
||||||
refClassesAnalyzerResult,
|
refClassesAnalyzerResult,
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const pathLib = require('path');
|
const pathLib = require('path');
|
||||||
const nodeResolvePackageJson = require('@rollup/plugin-node-resolve/package.json');
|
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
||||||
const createRollupResolve = require('@rollup/plugin-node-resolve');
|
|
||||||
const { LogService } = require('../services/LogService.js');
|
const { LogService } = require('../services/LogService.js');
|
||||||
|
|
||||||
const fakePluginContext = {
|
const fakePluginContext = {
|
||||||
meta: {
|
meta: {
|
||||||
rollupVersion: nodeResolvePackageJson.peerDependencies.rollup,
|
rollupVersion: '^2.42.0', // nodeResolvePackageJson.peerDependencies.rollup,
|
||||||
},
|
},
|
||||||
|
resolve: () => {},
|
||||||
warn(...msg) {
|
warn(...msg) {
|
||||||
LogService.warn('[resolve-import-path]: ', ...msg);
|
LogService.warn('[resolve-import-path]: ', ...msg);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc based on importee (in a statement "import {x} from '@lion/core'", "@lion/core" is an
|
* Based on importee (in a statement "import {x} from '@lion/core'", "@lion/core" is an
|
||||||
* importee), which can be a bare module specifier, a filename without extension, or a folder
|
* importee), which can be a bare module specifier, a filename without extension, or a folder
|
||||||
* name without an extension.
|
* name without an extension.
|
||||||
* @param {string} importee source like '@lion/core'
|
* @param {string} importee source like '@lion/core'
|
||||||
* @param {string} importer importing file, like '/my/project/importing-file.js'
|
* @param {string} importer importing file, like '/my/project/importing-file.js'
|
||||||
* @returns {string} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js'
|
* @returns {string} the resolved file system path, like '/my/project/node_modules/@lion/core/index.js'
|
||||||
*/
|
*/
|
||||||
async function resolveImportPath(importee, importer, opts = {}) {
|
async function resolveImportPath(importee, importer, opts = {}) {
|
||||||
const rollupResolve = createRollupResolve({
|
const rollupResolve = nodeResolve({
|
||||||
rootDir: pathLib.dirname(importer),
|
rootDir: pathLib.dirname(importer),
|
||||||
// allow resolving polyfills for nodejs libs
|
// allow resolving polyfills for nodejs libs
|
||||||
preferBuiltins: false,
|
preferBuiltins: false,
|
||||||
|
|
@ -38,7 +38,7 @@ async function resolveImportPath(importee, importer, opts = {}) {
|
||||||
(opts && opts.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false;
|
(opts && opts.customResolveOptions && opts.customResolveOptions.preserveSymlinks) || false;
|
||||||
rollupResolve.buildStart.call(fakePluginContext, { preserveSymlinks });
|
rollupResolve.buildStart.call(fakePluginContext, { preserveSymlinks });
|
||||||
|
|
||||||
const result = await rollupResolve.resolveId.call(fakePluginContext, importee, importer);
|
const result = await rollupResolve.resolveId.call(fakePluginContext, importee, importer, {});
|
||||||
if (!result || !result.id) {
|
if (!result || !result.id) {
|
||||||
// throw new Error(`importee ${importee} not found in filesystem.`);
|
// throw new Error(`importee ${importee} not found in filesystem.`);
|
||||||
LogService.warn(`importee ${importee} not found in filesystem for importer '${importer}'.`);
|
LogService.warn(`importee ${importee} not found in filesystem for importer '${importer}'.`);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,27 @@
|
||||||
|
const path = require('path');
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
const mockFs = require('mock-fs');
|
const mockFs = require('mock-fs');
|
||||||
const path = require('path');
|
const mockRequire = require('mock-require');
|
||||||
|
|
||||||
|
function mock(obj) {
|
||||||
|
mockFs(obj);
|
||||||
|
|
||||||
|
Object.entries(obj).forEach(([key, value]) => {
|
||||||
|
if (key.endsWith('.json')) {
|
||||||
|
mockRequire(key, JSON.parse(value));
|
||||||
|
} else {
|
||||||
|
mockRequire(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.restore = () => {
|
||||||
|
mockFs.restore();
|
||||||
|
mockRequire.stopAll();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Makes sure that, whenever the main program (providence) calls
|
* Makes sure that, whenever the main program (providence) calls
|
||||||
* "InputDataService.createDataObject", it gives back a mocked response.
|
* "InputDataService.createDataObject", it gives back a mocked response.
|
||||||
* @param {string[]|object} files all the code that will be run trhough AST
|
* @param {string[]|object} files all the code that will be run trhough AST
|
||||||
* @param {object} [cfg]
|
* @param {object} [cfg]
|
||||||
|
|
@ -13,7 +31,7 @@ const path = require('path');
|
||||||
* paths match with the indexes of the files
|
* paths match with the indexes of the files
|
||||||
* @param {object} existingMock config for mock-fs, so the previous config is not overridden
|
* @param {object} existingMock config for mock-fs, so the previous config is not overridden
|
||||||
*/
|
*/
|
||||||
function mockProject(files, cfg = {}, existingMock = {}) {
|
function getMockObjectForProject(files, cfg = {}, existingMock = {}) {
|
||||||
const projName = cfg.projectName || 'fictional-project';
|
const projName = cfg.projectName || 'fictional-project';
|
||||||
const projPath = cfg.projectPath || '/fictional/project';
|
const projPath = cfg.projectPath || '/fictional/project';
|
||||||
|
|
||||||
|
|
@ -50,17 +68,32 @@ function mockProject(files, cfg = {}, existingMock = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalMock = {
|
const totalMock = {
|
||||||
...existingMock, // can only add to mock-fs, not expand existing config?
|
|
||||||
...optionalPackageJson,
|
...optionalPackageJson,
|
||||||
|
...existingMock, // can only add to mock-fs, not expand existing config?
|
||||||
...createFilesObjForFolder(files),
|
...createFilesObjForFolder(files),
|
||||||
};
|
};
|
||||||
|
|
||||||
mockFs(totalMock);
|
|
||||||
return totalMock;
|
return totalMock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure that, whenever the main program (providence) calls
|
||||||
|
* "InputDataService.createDataObject", it gives back a mocked response.
|
||||||
|
* @param {string[]|object} files all the code that will be run trhough AST
|
||||||
|
* @param {object} [cfg]
|
||||||
|
* @param {string} [cfg.projectName='fictional-project']
|
||||||
|
* @param {string} [cfg.projectPath='/fictional/project']
|
||||||
|
* @param {string[]} [cfg.filePaths=`[/fictional/project/test-file-${i}.js]`] The indexes of the file
|
||||||
|
* paths match with the indexes of the files
|
||||||
|
* @param {object} existingMock config for mock-fs, so the previous config is not overridden
|
||||||
|
*/
|
||||||
|
function mockProject(files, cfg = {}, existingMock = {}) {
|
||||||
|
const obj = getMockObjectForProject(files, cfg, existingMock);
|
||||||
|
mockFs(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
function restoreMockedProjects() {
|
function restoreMockedProjects() {
|
||||||
mockFs.restore();
|
mock.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEntry(queryResult, index = 0) {
|
function getEntry(queryResult, index = 0) {
|
||||||
|
|
@ -71,6 +104,23 @@ function getEntries(queryResult) {
|
||||||
return queryResult.queryOutput;
|
return queryResult.queryOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createPackageJson({ filePaths, codeSnippets, projectName, refProjectName, refVersion }) {
|
||||||
|
const targetHasPackageJson = filePaths.includes('./package.json');
|
||||||
|
// Make target depend on ref
|
||||||
|
if (targetHasPackageJson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pkgJson = {
|
||||||
|
name: projectName,
|
||||||
|
version: '1.0.0',
|
||||||
|
};
|
||||||
|
if (refProjectName && refVersion) {
|
||||||
|
pkgJson.dependencies = { [refProjectName]: refVersion };
|
||||||
|
}
|
||||||
|
codeSnippets.push(JSON.stringify(pkgJson));
|
||||||
|
filePaths.push('./package.json');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires two config objects (see match-imports and match-subclasses tests)
|
* Requires two config objects (see match-imports and match-subclasses tests)
|
||||||
* and based on those, will use mock-fs package to mock them in the file system.
|
* and based on those, will use mock-fs package to mock them in the file system.
|
||||||
|
|
@ -86,22 +136,25 @@ function mockTargetAndReferenceProject(searchTargetProject, referenceProject) {
|
||||||
const targetcodeSnippets = searchTargetProject.files.map(f => f.code);
|
const targetcodeSnippets = searchTargetProject.files.map(f => f.code);
|
||||||
const targetFilePaths = searchTargetProject.files.map(f => f.file);
|
const targetFilePaths = searchTargetProject.files.map(f => f.file);
|
||||||
const refVersion = referenceProject.version || '1.0.0';
|
const refVersion = referenceProject.version || '1.0.0';
|
||||||
|
const refcodeSnippets = referenceProject.files.map(f => f.code);
|
||||||
|
const refFilePaths = referenceProject.files.map(f => f.file);
|
||||||
|
|
||||||
const targetHasPackageJson = targetFilePaths.includes('./package.json');
|
createPackageJson({
|
||||||
// Make target depend on ref
|
filePaths: targetFilePaths,
|
||||||
if (!targetHasPackageJson) {
|
codeSnippets: targetcodeSnippets,
|
||||||
targetcodeSnippets.push(`{
|
projectName: targetProjectName,
|
||||||
"name": "${targetProjectName}" ,
|
refProjectName,
|
||||||
"version": "1.0.0",
|
refVersion,
|
||||||
"dependencies": {
|
});
|
||||||
"${refProjectName}": "${refVersion}"
|
|
||||||
}
|
createPackageJson({
|
||||||
}`);
|
filePaths: refFilePaths,
|
||||||
targetFilePaths.push('./package.json');
|
codeSnippets: refcodeSnippets,
|
||||||
}
|
projectName: refProjectName,
|
||||||
|
});
|
||||||
|
|
||||||
// Create target mock
|
// Create target mock
|
||||||
const targetMock = mockProject(targetcodeSnippets, {
|
const targetMock = getMockObjectForProject(targetcodeSnippets, {
|
||||||
filePaths: targetFilePaths,
|
filePaths: targetFilePaths,
|
||||||
projectName: targetProjectName,
|
projectName: targetProjectName,
|
||||||
projectPath: searchTargetProject.path || 'fictional/target/project',
|
projectPath: searchTargetProject.path || 'fictional/target/project',
|
||||||
|
|
|
||||||
|
|
@ -568,7 +568,7 @@ describe('CLI helpers', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const theirProject = {
|
const theirProject = {
|
||||||
path: '/their-components',
|
path: '/my-components/node_modules/their-components',
|
||||||
name: 'their-components',
|
name: 'their-components',
|
||||||
files: Object.entries(theirProjectFiles).map(([file, code]) => ({ file, code })),
|
files: Object.entries(theirProjectFiles).map(([file, code]) => ({ file, code })),
|
||||||
};
|
};
|
||||||
|
|
@ -582,7 +582,7 @@ describe('CLI helpers', () => {
|
||||||
mockTargetAndReferenceProject(theirProject, myProject);
|
mockTargetAndReferenceProject(theirProject, myProject);
|
||||||
|
|
||||||
const result = await getExtendDocsResults({
|
const result = await getExtendDocsResults({
|
||||||
referenceProjectPaths: ['/their-components'],
|
referenceProjectPaths: [theirProject.path],
|
||||||
prefixCfg: { from: 'their', to: 'my' },
|
prefixCfg: { from: 'their', to: 'my' },
|
||||||
extensions: ['.js'],
|
extensions: ['.js'],
|
||||||
cwd: '/my-components',
|
cwd: '/my-components',
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ const {
|
||||||
const matchImportsQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-imports');
|
const matchImportsQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-imports');
|
||||||
const _providenceCfg = {
|
const _providenceCfg = {
|
||||||
targetProjectPaths: ['/importing/target/project'],
|
targetProjectPaths: ['/importing/target/project'],
|
||||||
referenceProjectPaths: ['/exporting/ref/project'],
|
referenceProjectPaths: ['/importing/target/project/node_modules/exporting-ref-project'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Reference input data
|
// 1. Reference input data
|
||||||
const referenceProject = {
|
const referenceProject = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/exporting-ref-project',
|
||||||
name: 'exporting-ref-project',
|
name: 'exporting-ref-project',
|
||||||
files: [
|
files: [
|
||||||
// This file contains all 'original' exported definitions
|
// This file contains all 'original' exported definitions
|
||||||
|
|
@ -253,58 +253,206 @@ describe('Analyzer "match-imports"', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Extracting exports', () => {
|
describe('Extracting exports', () => {
|
||||||
it(`identifies all direct export specifiers consumed by "importing-target-project"`, async () => {
|
it(`identifies all direct export specifiers consumed by target`, async () => {
|
||||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
const refProject = {
|
||||||
await providence(matchImportsQueryConfig, _providenceCfg);
|
path: '/target/node_modules/ref',
|
||||||
const queryResult = queryResults[0];
|
name: 'ref',
|
||||||
expectedExportIdsDirect.forEach(directId => {
|
files: [{ file: './direct.js', code: `export default function x() {};` }],
|
||||||
expect(
|
};
|
||||||
queryResult.queryOutput.find(
|
const targetProject = {
|
||||||
exportMatchResult => exportMatchResult.exportSpecifier.id === directId,
|
path: '/target',
|
||||||
),
|
name: 'target',
|
||||||
).not.to.equal(undefined, `id '${directId}' not found`);
|
files: [{ file: './index.js', code: `import myFn from 'ref/direct.js';` }],
|
||||||
|
};
|
||||||
|
mockTargetAndReferenceProject(targetProject, refProject);
|
||||||
|
await providence(matchImportsQueryConfig, {
|
||||||
|
targetProjectPaths: [targetProject.path],
|
||||||
|
referenceProjectPaths: [refProject.path],
|
||||||
});
|
});
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
expect(queryResult.queryOutput).eql([
|
||||||
|
{
|
||||||
|
exportSpecifier: {
|
||||||
|
filePath: './direct.js',
|
||||||
|
id: '[default]::./direct.js::ref',
|
||||||
|
name: '[default]',
|
||||||
|
project: 'ref',
|
||||||
|
},
|
||||||
|
matchesPerProject: [{ files: ['./index.js'], project: 'target' }],
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`identifies all indirect export specifiers consumed by "importing-target-project"`, async () => {
|
it(`identifies all indirect (transitive) export specifiers consumed by target`, async () => {
|
||||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
const refProject = {
|
||||||
await providence(matchImportsQueryConfig, _providenceCfg);
|
path: '/target/node_modules/ref',
|
||||||
const queryResult = queryResults[0];
|
name: 'ref',
|
||||||
expectedExportIdsIndirect.forEach(indirectId => {
|
files: [
|
||||||
expect(
|
{ file: './direct.js', code: `export function x() {};` },
|
||||||
queryResult.queryOutput.find(
|
{ file: './indirect.js', code: `export { x } from './direct.js';` },
|
||||||
exportMatchResult => exportMatchResult.exportSpecifier.id === indirectId,
|
],
|
||||||
),
|
};
|
||||||
).not.to.equal(undefined, `id '${indirectId}' not found`);
|
const targetProject = {
|
||||||
|
path: '/target',
|
||||||
|
name: 'target',
|
||||||
|
files: [{ file: './index.js', code: `import { x } from 'ref/indirect.js';` }],
|
||||||
|
};
|
||||||
|
mockTargetAndReferenceProject(targetProject, refProject);
|
||||||
|
await providence(matchImportsQueryConfig, {
|
||||||
|
targetProjectPaths: [targetProject.path],
|
||||||
|
referenceProjectPaths: [refProject.path],
|
||||||
});
|
});
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
expect(queryResult.queryOutput).eql([
|
||||||
|
{
|
||||||
|
exportSpecifier: {
|
||||||
|
filePath: './indirect.js',
|
||||||
|
id: 'x::./indirect.js::ref',
|
||||||
|
name: 'x',
|
||||||
|
project: 'ref',
|
||||||
|
},
|
||||||
|
matchesPerProject: [{ files: ['./index.js'], project: 'target' }],
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`matches namespaced specifiers consumed by "importing-target-project"`, async () => {
|
it(`matches namespaced specifiers consumed by target`, async () => {
|
||||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
const refProject = {
|
||||||
await providence(matchImportsQueryConfig, _providenceCfg);
|
path: '/target/node_modules/ref',
|
||||||
|
name: 'ref',
|
||||||
|
files: [
|
||||||
|
{ file: './namespaced.js', code: `export function x() {}; export function y() {};` },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const targetProject = {
|
||||||
|
path: '/target',
|
||||||
|
name: 'target',
|
||||||
|
files: [{ file: './index.js', code: `import * as xy from 'ref/namespaced.js';` }],
|
||||||
|
};
|
||||||
|
mockTargetAndReferenceProject(targetProject, refProject);
|
||||||
|
await providence(matchImportsQueryConfig, {
|
||||||
|
targetProjectPaths: [targetProject.path],
|
||||||
|
referenceProjectPaths: [refProject.path],
|
||||||
|
});
|
||||||
const queryResult = queryResults[0];
|
const queryResult = queryResults[0];
|
||||||
expectedExportIdsNamespaced.forEach(exportedSpecifierId => {
|
expect(queryResult.queryOutput).eql([
|
||||||
expect(
|
{
|
||||||
queryResult.queryOutput.find(
|
exportSpecifier: {
|
||||||
exportMatchResult => exportMatchResult.exportSpecifier.id === exportedSpecifierId,
|
filePath: './namespaced.js',
|
||||||
),
|
id: 'x::./namespaced.js::ref',
|
||||||
).not.to.equal(undefined, `id '${exportedSpecifierId}' not found`);
|
name: 'x',
|
||||||
|
project: 'ref',
|
||||||
|
},
|
||||||
|
matchesPerProject: [{ files: ['./index.js'], project: 'target' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exportSpecifier: {
|
||||||
|
filePath: './namespaced.js',
|
||||||
|
id: 'y::./namespaced.js::ref',
|
||||||
|
name: 'y',
|
||||||
|
project: 'ref',
|
||||||
|
},
|
||||||
|
matchesPerProject: [{ files: ['./index.js'], project: 'target' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
exportSpecifier: {
|
||||||
|
filePath: './namespaced.js',
|
||||||
|
id: '[file]::./namespaced.js::ref',
|
||||||
|
name: '[file]',
|
||||||
|
project: 'ref',
|
||||||
|
},
|
||||||
|
matchesPerProject: [{ files: ['./index.js'], project: 'target' }],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inside small example project', () => {
|
||||||
|
it(`identifies all direct export specifiers consumed by "importing-target-project"`, async () => {
|
||||||
|
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||||
|
await providence(matchImportsQueryConfig, _providenceCfg);
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
// console.log(JSON.stringify(queryResult.queryOutput, null, 2));
|
||||||
|
expectedExportIdsDirect.forEach(directId => {
|
||||||
|
expect(
|
||||||
|
queryResult.queryOutput.find(
|
||||||
|
exportMatchResult => exportMatchResult.exportSpecifier.id === directId,
|
||||||
|
),
|
||||||
|
).not.to.equal(undefined, `id '${directId}' not found`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`identifies all indirect export specifiers consumed by "importing-target-project"`, async () => {
|
||||||
|
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||||
|
await providence(matchImportsQueryConfig, _providenceCfg);
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
expectedExportIdsIndirect.forEach(indirectId => {
|
||||||
|
expect(
|
||||||
|
queryResult.queryOutput.find(
|
||||||
|
exportMatchResult => exportMatchResult.exportSpecifier.id === indirectId,
|
||||||
|
),
|
||||||
|
).not.to.equal(undefined, `id '${indirectId}' not found`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`matches namespaced specifiers consumed by "importing-target-project"`, async () => {
|
||||||
|
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||||
|
await providence(matchImportsQueryConfig, _providenceCfg);
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
expectedExportIdsNamespaced.forEach(exportedSpecifierId => {
|
||||||
|
expect(
|
||||||
|
queryResult.queryOutput.find(
|
||||||
|
exportMatchResult => exportMatchResult.exportSpecifier.id === exportedSpecifierId,
|
||||||
|
),
|
||||||
|
).not.to.equal(undefined, `id '${exportedSpecifierId}' not found`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Matching', () => {
|
describe('Matching', () => {
|
||||||
it(`produces a list of all matches, sorted by project`, async () => {
|
it(`produces a list of all matches, sorted by project`, async () => {
|
||||||
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
/**
|
||||||
await providence(matchImportsQueryConfig, _providenceCfg);
|
* N.B. output structure could be simplified, since there is
|
||||||
const queryResult = queryResults[0];
|
* For now, we keep it, so integration with dashboard stays intact.
|
||||||
|
* TODO:
|
||||||
expectedExportIdsDirect.forEach(targetId => {
|
* - write tests for dashboard transform logic
|
||||||
testMatchedEntry(targetId, queryResult, ['./target-src/direct-imports.js']);
|
* - simplify output for match-* analyzers
|
||||||
|
* - adjust dashboard transfrom logic
|
||||||
|
*/
|
||||||
|
const refProject = {
|
||||||
|
path: '/target/node_modules/ref',
|
||||||
|
name: 'ref',
|
||||||
|
files: [{ file: './direct.js', code: `export default function x() {};` }],
|
||||||
|
};
|
||||||
|
const targetProject = {
|
||||||
|
path: '/target',
|
||||||
|
name: 'target',
|
||||||
|
files: [{ file: './index.js', code: `import myFn from 'ref/direct.js';` }],
|
||||||
|
};
|
||||||
|
mockTargetAndReferenceProject(targetProject, refProject);
|
||||||
|
await providence(matchImportsQueryConfig, {
|
||||||
|
targetProjectPaths: [targetProject.path],
|
||||||
|
referenceProjectPaths: [refProject.path],
|
||||||
});
|
});
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
expect(queryResult.queryOutput[0].matchesPerProject).eql([
|
||||||
|
{ files: ['./index.js'], project: 'target' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
expectedExportIdsIndirect.forEach(targetId => {
|
describe('Inside small example project', () => {
|
||||||
testMatchedEntry(targetId, queryResult, ['./target-src/indirect-imports.js']);
|
it(`produces a list of all matches, sorted by project`, async () => {
|
||||||
|
mockTargetAndReferenceProject(searchTargetProject, referenceProject);
|
||||||
|
await providence(matchImportsQueryConfig, _providenceCfg);
|
||||||
|
const queryResult = queryResults[0];
|
||||||
|
|
||||||
|
expectedExportIdsDirect.forEach(targetId => {
|
||||||
|
testMatchedEntry(targetId, queryResult, ['./target-src/direct-imports.js']);
|
||||||
|
});
|
||||||
|
|
||||||
|
expectedExportIdsIndirect.forEach(targetId => {
|
||||||
|
testMatchedEntry(targetId, queryResult, ['./target-src/indirect-imports.js']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,6 @@ const {
|
||||||
restoreSuppressNonCriticalLogs,
|
restoreSuppressNonCriticalLogs,
|
||||||
} = require('../../../test-helpers/mock-log-service-helpers.js');
|
} = require('../../../test-helpers/mock-log-service-helpers.js');
|
||||||
|
|
||||||
const matchPathsQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-paths');
|
|
||||||
const _providenceCfg = {
|
|
||||||
targetProjectPaths: ['/importing/target/project'],
|
|
||||||
referenceProjectPaths: ['/exporting/ref/project'],
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Analyzer "match-paths"', () => {
|
describe('Analyzer "match-paths"', () => {
|
||||||
const originalReferenceProjectPaths = InputDataService.referenceProjectPaths;
|
const originalReferenceProjectPaths = InputDataService.referenceProjectPaths;
|
||||||
const queryResults = [];
|
const queryResults = [];
|
||||||
|
|
@ -48,8 +42,8 @@ describe('Analyzer "match-paths"', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const referenceProject = {
|
const referenceProject = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/reference-project',
|
||||||
name: 'exporting-ref-project',
|
name: 'reference-project',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
file: './ref-src/core.js',
|
file: './ref-src/core.js',
|
||||||
|
|
@ -90,8 +84,8 @@ describe('Analyzer "match-paths"', () => {
|
||||||
file: './target-src/ExtendRefRenamedClass.js',
|
file: './target-src/ExtendRefRenamedClass.js',
|
||||||
code: `
|
code: `
|
||||||
// renamed import (indirect, needs transitivity check)
|
// renamed import (indirect, needs transitivity check)
|
||||||
import { RefRenamedClass } from 'exporting-ref-project/reexport.js';
|
import { RefRenamedClass } from 'reference-project/reexport.js';
|
||||||
import defaultExport from 'exporting-ref-project/reexport.js';
|
import defaultExport from 'reference-project/reexport.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should result in:
|
* This should result in:
|
||||||
|
|
@ -110,10 +104,10 @@ describe('Analyzer "match-paths"', () => {
|
||||||
file: './target-src/direct-imports.js',
|
file: './target-src/direct-imports.js',
|
||||||
code: `
|
code: `
|
||||||
// a direct named import
|
// a direct named import
|
||||||
import { RefClass } from 'exporting-ref-project/ref-src/core.js';
|
import { RefClass } from 'reference-project/ref-src/core.js';
|
||||||
|
|
||||||
// a direct default import
|
// a direct default import
|
||||||
import RefDefault from 'exporting-ref-project/reexport.js';
|
import RefDefault from 'reference-project/reexport.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should result in:
|
* This should result in:
|
||||||
|
|
@ -148,6 +142,12 @@ describe('Analyzer "match-paths"', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const matchPathsQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-paths');
|
||||||
|
const _providenceCfg = {
|
||||||
|
targetProjectPaths: [searchTargetProject.path],
|
||||||
|
referenceProjectPaths: [referenceProject.path],
|
||||||
|
};
|
||||||
|
|
||||||
describe('Variables', () => {
|
describe('Variables', () => {
|
||||||
const expectedMatches = [
|
const expectedMatches = [
|
||||||
{
|
{
|
||||||
|
|
@ -161,7 +161,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './target-src/ExtendRefRenamedClass.js',
|
to: './target-src/ExtendRefRenamedClass.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/reexport.js',
|
from: 'reference-project/reexport.js',
|
||||||
to: './target-src/ExtendRefRenamedClass.js',
|
to: './target-src/ExtendRefRenamedClass.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -182,11 +182,11 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/reexport.js',
|
from: 'reference-project/reexport.js',
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/ref-src/core.js',
|
from: 'reference-project/ref-src/core.js',
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -203,7 +203,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './target-src/direct-imports.js',
|
to: './target-src/direct-imports.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/ref-src/core.js',
|
from: 'reference-project/ref-src/core.js',
|
||||||
to: './target-src/direct-imports.js',
|
to: './target-src/direct-imports.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -220,7 +220,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
|
|
||||||
describe('Features', () => {
|
describe('Features', () => {
|
||||||
const refProj = {
|
const refProj = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/reference-project',
|
||||||
name: 'reference-project',
|
name: 'reference-project',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
|
|
@ -376,7 +376,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
|
|
||||||
describe('Options', () => {
|
describe('Options', () => {
|
||||||
const refProj = {
|
const refProj = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/reference-project',
|
||||||
name: 'reference-project',
|
name: 'reference-project',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
|
|
@ -446,8 +446,8 @@ describe('Analyzer "match-paths"', () => {
|
||||||
describe('Tags', () => {
|
describe('Tags', () => {
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
const referenceProject = {
|
const referenceProject = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/reference-project',
|
||||||
name: 'exporting-ref-project',
|
name: 'reference-project',
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
file: './customelementDefinitions.js',
|
file: './customelementDefinitions.js',
|
||||||
|
|
@ -493,7 +493,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
{
|
{
|
||||||
file: './extendedClassDefinitions.js',
|
file: './extendedClassDefinitions.js',
|
||||||
code: `
|
code: `
|
||||||
export { El1, El2 } from 'exporting-ref-project/classDefinitions.js';
|
export { El1, El2 } from 'reference-project/classDefinitions.js';
|
||||||
|
|
||||||
export class ExtendedEl1 extends El1 {}
|
export class ExtendedEl1 extends El1 {}
|
||||||
`,
|
`,
|
||||||
|
|
@ -517,7 +517,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
paths: [
|
paths: [
|
||||||
{ from: './customelementDefinitions.js', to: './extendedCustomelementDefinitions.js' },
|
{ from: './customelementDefinitions.js', to: './extendedCustomelementDefinitions.js' },
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/customelementDefinitions.js',
|
from: 'reference-project/customelementDefinitions.js',
|
||||||
to: './extendedCustomelementDefinitions.js',
|
to: './extendedCustomelementDefinitions.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -528,7 +528,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
paths: [
|
paths: [
|
||||||
{ from: './customelementDefinitions.js', to: './extendedCustomelementDefinitions.js' },
|
{ from: './customelementDefinitions.js', to: './extendedCustomelementDefinitions.js' },
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/customelementDefinitions.js',
|
from: 'reference-project/customelementDefinitions.js',
|
||||||
to: './extendedCustomelementDefinitions.js',
|
to: './extendedCustomelementDefinitions.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -642,7 +642,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
await providence(matchPathsQueryConfig, _providenceCfg);
|
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.eql({
|
||||||
from: 'exporting-ref-project/customelementDefinitions.js',
|
from: 'reference-project/customelementDefinitions.js',
|
||||||
to: './extendedCustomelementDefinitions.js',
|
to: './extendedCustomelementDefinitions.js',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -692,7 +692,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './target-src/ExtendRefRenamedClass.js',
|
to: './target-src/ExtendRefRenamedClass.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/reexport.js',
|
from: 'reference-project/reexport.js',
|
||||||
to: './target-src/ExtendRefRenamedClass.js',
|
to: './target-src/ExtendRefRenamedClass.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -713,11 +713,11 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/reexport.js',
|
from: 'reference-project/reexport.js',
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/ref-src/core.js',
|
from: 'reference-project/ref-src/core.js',
|
||||||
to: './index.js',
|
to: './index.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -734,7 +734,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './target-src/direct-imports.js',
|
to: './target-src/direct-imports.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/ref-src/core.js',
|
from: 'reference-project/ref-src/core.js',
|
||||||
to: './target-src/direct-imports.js',
|
to: './target-src/direct-imports.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -748,7 +748,7 @@ describe('Analyzer "match-paths"', () => {
|
||||||
to: './tag-extended.js',
|
to: './tag-extended.js',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 'exporting-ref-project/tag.js',
|
from: 'reference-project/tag.js',
|
||||||
to: './tag-extended.js',
|
to: './tag-extended.js',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,9 @@ const {
|
||||||
restoreSuppressNonCriticalLogs,
|
restoreSuppressNonCriticalLogs,
|
||||||
} = require('../../../test-helpers/mock-log-service-helpers.js');
|
} = require('../../../test-helpers/mock-log-service-helpers.js');
|
||||||
|
|
||||||
const matchSubclassesQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-subclasses');
|
|
||||||
const _providenceCfg = {
|
|
||||||
targetProjectPaths: ['/importing/target/project'],
|
|
||||||
referenceProjectPaths: ['/exporting/ref/project'],
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. Reference input data
|
// 1. Reference input data
|
||||||
const referenceProject = {
|
const referenceProject = {
|
||||||
path: '/exporting/ref/project',
|
path: '/importing/target/project/node_modules/exporting-ref-project',
|
||||||
name: 'exporting-ref-project',
|
name: 'exporting-ref-project',
|
||||||
files: [
|
files: [
|
||||||
// This file contains all 'original' exported definitions
|
// This file contains all 'original' exported definitions
|
||||||
|
|
@ -92,6 +86,12 @@ const searchTargetProject = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const matchSubclassesQueryConfig = QueryService.getQueryConfigFromAnalyzer('match-subclasses');
|
||||||
|
const _providenceCfg = {
|
||||||
|
targetProjectPaths: [searchTargetProject.path],
|
||||||
|
referenceProjectPaths: [referenceProject.path],
|
||||||
|
};
|
||||||
|
|
||||||
// 2. Extracted specifiers (by find-exports analyzer)
|
// 2. Extracted specifiers (by find-exports analyzer)
|
||||||
const expectedExportIdsIndirect = ['RefRenamedClass::./index.js::exporting-ref-project'];
|
const expectedExportIdsIndirect = ['RefRenamedClass::./index.js::exporting-ref-project'];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
const { expect } = require('chai');
|
||||||
|
const {
|
||||||
|
mockProject,
|
||||||
|
restoreMockedProjects,
|
||||||
|
mockTargetAndReferenceProject,
|
||||||
|
} = require('../../../test-helpers/mock-project-helpers.js');
|
||||||
|
const { resolveImportPath } = require('../../../src/program/utils/resolve-import-path.js');
|
||||||
|
|
||||||
|
describe('resolveImportPath', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
restoreMockedProjects();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`resolves file in same project`, async () => {
|
||||||
|
mockProject(
|
||||||
|
{
|
||||||
|
'./src/declarationOfMyClass.js': `
|
||||||
|
export class MyClass extends HTMLElement {}
|
||||||
|
`,
|
||||||
|
'./currentFile.js': `
|
||||||
|
import { MyClass } from './src/declarationOfMyClass';
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectName: 'my-project',
|
||||||
|
projectPath: '/my/project',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const foundPath = await resolveImportPath(
|
||||||
|
'./src/declarationOfMyClass',
|
||||||
|
'/my/project/currentFile.js',
|
||||||
|
);
|
||||||
|
expect(foundPath).to.equal('/my/project/src/declarationOfMyClass.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`resolves file in different projects`, async () => {
|
||||||
|
const targetProject = {
|
||||||
|
path: '/target/node_modules/ref',
|
||||||
|
name: 'ref',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
file: './index.js',
|
||||||
|
code: `
|
||||||
|
export const x = 10;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const referenceProject = {
|
||||||
|
path: '/target',
|
||||||
|
name: 'target',
|
||||||
|
files: [
|
||||||
|
// This file contains all 'original' exported definitions
|
||||||
|
{
|
||||||
|
file: './a.js',
|
||||||
|
code: `
|
||||||
|
import { x } from 'ref';
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
mockTargetAndReferenceProject(targetProject, referenceProject);
|
||||||
|
|
||||||
|
const foundPath = await resolveImportPath('ref', '/target/a.js');
|
||||||
|
expect(foundPath).to.equal('/target/node_modules/ref/index.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`resolves export maps`, async () => {
|
||||||
|
const targetProject = {
|
||||||
|
path: '/target/node_modules/ref',
|
||||||
|
name: 'ref',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
file: './packages/x/index.js',
|
||||||
|
code: `
|
||||||
|
export const x = 10;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: './package.json',
|
||||||
|
code: JSON.stringify({
|
||||||
|
name: 'ref',
|
||||||
|
exports: {
|
||||||
|
'./x': './packages/x/index.js',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const referenceProject = {
|
||||||
|
path: '/target',
|
||||||
|
name: 'target',
|
||||||
|
files: [
|
||||||
|
// This file contains all 'original' exported definitions
|
||||||
|
{
|
||||||
|
file: './a.js',
|
||||||
|
code: `
|
||||||
|
import { x } from 'ref/x';
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
mockTargetAndReferenceProject(targetProject, referenceProject);
|
||||||
|
|
||||||
|
const foundPath = await resolveImportPath('ref/x', '/target/a.js');
|
||||||
|
expect(foundPath).to.equal('/target/node_modules/ref/packages/x/index.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All edge cases are covered by https://github.com/rollup/plugins/tree/master/packages/node-resolve/test
|
||||||
|
*/
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue