feat(providence-analytics): support import assertions
This commit is contained in:
parent
1f87043e82
commit
8890cc0dea
8 changed files with 101 additions and 14 deletions
|
|
@ -33,6 +33,7 @@
|
|||
"@babel/parser": "^7.5.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/plugin-syntax-export-default-from": "^7.18.6",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.18.6",
|
||||
"@babel/register": "^7.5.5",
|
||||
"@babel/traverse": "^7.5.5",
|
||||
"@babel/types": "^7.9.0",
|
||||
|
|
|
|||
|
|
@ -170,7 +170,11 @@ function findExportsPerAstEntry(ast, { skipFileImports }) {
|
|||
const exportSpecifiers = getExportSpecifiers(path.node);
|
||||
const localMap = getLocalNameSpecifiers(path.node);
|
||||
const source = path.node.source?.value;
|
||||
transformedEntry.push({ exportSpecifiers, localMap, source, __tmp: { path } });
|
||||
const entry = { exportSpecifiers, localMap, source, __tmp: { path } };
|
||||
if (path.node.assertions?.length) {
|
||||
entry.assertionType = path.node.assertions[0].value?.value;
|
||||
}
|
||||
transformedEntry.push(entry);
|
||||
},
|
||||
ExportDefaultDeclaration(defaultExportPath) {
|
||||
const exportSpecifiers = ['[default]'];
|
||||
|
|
|
|||
|
|
@ -60,11 +60,15 @@ function findImportsPerAstEntry(ast) {
|
|||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const importSpecifiers = getImportOrReexportsSpecifiers(path.node);
|
||||
if (importSpecifiers.length === 0) {
|
||||
if (!importSpecifiers.length) {
|
||||
importSpecifiers.push('[file]'); // apparently, there was just a file import
|
||||
}
|
||||
const source = path.node.source.value;
|
||||
transformedEntry.push({ importSpecifiers, source });
|
||||
const entry = { importSpecifiers, source };
|
||||
if (path.node.assertions?.length) {
|
||||
entry.assertionType = path.node.assertions[0].value?.value;
|
||||
}
|
||||
transformedEntry.push(entry);
|
||||
},
|
||||
// Dynamic imports
|
||||
CallExpression(path) {
|
||||
|
|
@ -86,7 +90,11 @@ function findImportsPerAstEntry(ast) {
|
|||
}
|
||||
const importSpecifiers = getImportOrReexportsSpecifiers(path.node);
|
||||
const source = path.node.source.value;
|
||||
transformedEntry.push({ importSpecifiers, source });
|
||||
const entry = { importSpecifiers, source };
|
||||
if (path.node.assertions?.length) {
|
||||
entry.assertionType = path.node.assertions[0].value?.value;
|
||||
}
|
||||
transformedEntry.push(entry);
|
||||
},
|
||||
// ExportAllDeclaration(path) {
|
||||
// if (!path.node.source) {
|
||||
|
|
@ -97,6 +105,7 @@ function findImportsPerAstEntry(ast) {
|
|||
// transformedEntry.push({ importSpecifiers, source });
|
||||
// },
|
||||
});
|
||||
|
||||
return transformedEntry;
|
||||
}
|
||||
|
||||
|
|
@ -140,15 +149,16 @@ class FindImportsAnalyzer extends Analyzer {
|
|||
const queryOutput = await this._traverse(async (ast, { relativePath }) => {
|
||||
let transformedEntry = findImportsPerAstEntry(ast);
|
||||
// Post processing based on configuration...
|
||||
|
||||
transformedEntry = await normalizeSourcePaths(
|
||||
transformedEntry,
|
||||
relativePath,
|
||||
cfg.targetProjectPath,
|
||||
);
|
||||
|
||||
if (!cfg.keepInternalSources) {
|
||||
transformedEntry = options.onlyExternalSources(transformedEntry);
|
||||
}
|
||||
|
||||
return { result: transformedEntry };
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const fs = require('fs');
|
||||
const pathLib = require('path');
|
||||
const { default: traverse } = require('@babel/traverse');
|
||||
const {
|
||||
isRelativeSourcePath,
|
||||
|
|
@ -157,7 +158,16 @@ async function trackDownIdentifierFn(
|
|||
* @type {PathFromSystemRoot}
|
||||
*/
|
||||
const resolvedSourcePath = await resolveImportPath(source, currentFilePath);
|
||||
|
||||
LogService.debug(`[trackDownIdentifier] ${resolvedSourcePath}`);
|
||||
const allowedJsModuleExtensions = ['.mjs', '.js'];
|
||||
if (!allowedJsModuleExtensions.includes(pathLib.extname(resolvedSourcePath))) {
|
||||
// We have an import assertion
|
||||
return /** @type { RootFile } */ {
|
||||
file: toRelativeSourcePath(resolvedSourcePath, rootPath),
|
||||
specifier: '[default]',
|
||||
};
|
||||
}
|
||||
const code = fs.readFileSync(resolvedSourcePath, 'utf8');
|
||||
const ast = AstService.getAst(code, 'babel', { filePath: resolvedSourcePath });
|
||||
const shouldLookForDefaultExport = identifierName === '[default]';
|
||||
|
|
|
|||
|
|
@ -61,7 +61,13 @@ class AstService {
|
|||
static _getBabelAst(code) {
|
||||
const ast = babelParser.parse(code, {
|
||||
sourceType: 'module',
|
||||
plugins: ['importMeta', 'dynamicImport', 'classProperties', 'exportDefaultFrom'],
|
||||
plugins: [
|
||||
'importMeta',
|
||||
'dynamicImport',
|
||||
'classProperties',
|
||||
'exportDefaultFrom',
|
||||
'importAssertions',
|
||||
],
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,46 @@ describe('Analyzer "find-exports"', () => {
|
|||
expect(firstEntry.result[0].source).to.equal('my/source');
|
||||
});
|
||||
|
||||
it(`supports [export styles from './styles.css' assert { type: "css" }] (import assertions)`, async () => {
|
||||
mockProject({
|
||||
'./styles.css': '.block { display:block; };',
|
||||
'./x.js': `export styles from './styles.css' assert { type: "css" };`,
|
||||
});
|
||||
await providence(findExportsQueryConfig, _providenceCfg);
|
||||
const queryResult = queryResults[0];
|
||||
const firstEntry = getEntry(queryResult);
|
||||
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
|
||||
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('styles');
|
||||
expect(firstEntry.result[0].source).to.equal('./styles.css');
|
||||
expect(firstEntry.result[0].rootFileMap[0]).to.eql({
|
||||
currentFileSpecifier: 'styles',
|
||||
rootFile: {
|
||||
file: './styles.css',
|
||||
specifier: '[default]',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`supports [import styles from './styles.css' assert { type: "css" }; export default styles;] (import assertions)`, async () => {
|
||||
mockProject({
|
||||
'./styles.css': '.block { display:block; };',
|
||||
'./x.js': `import styles from './styles.css' assert { type: "css" }; export default styles;`,
|
||||
});
|
||||
await providence(findExportsQueryConfig, _providenceCfg);
|
||||
const queryResult = queryResults[0];
|
||||
const firstEntry = getEntry(queryResult);
|
||||
expect(firstEntry.result[0].exportSpecifiers.length).to.equal(1);
|
||||
expect(firstEntry.result[0].exportSpecifiers[0]).to.equal('[default]');
|
||||
expect(firstEntry.result[0].source).to.equal('./styles.css');
|
||||
expect(firstEntry.result[0].rootFileMap[0]).to.eql({
|
||||
currentFileSpecifier: '[default]',
|
||||
rootFile: {
|
||||
file: './styles.css',
|
||||
specifier: '[default]',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`stores meta info(local name) of renamed specifiers`, async () => {
|
||||
mockProject([`export { x as y } from 'my/source'`]);
|
||||
await providence(findExportsQueryConfig, _providenceCfg);
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ describe('Analyzer "find-imports"', () => {
|
|||
expect(firstEntry.result[0].source).to.equal('my/source');
|
||||
});
|
||||
|
||||
// TODO: we can track [variable] down via trackdownId + getSourceCodeFragmentOfDeclaration
|
||||
it(`supports [import(pathReference)] (dynamic imports with variable source)`, async () => {
|
||||
mockProject([
|
||||
`
|
||||
|
|
@ -183,6 +184,29 @@ describe('Analyzer "find-imports"', () => {
|
|||
expect(firstEntry.result[0].source).to.equal('[variable]');
|
||||
});
|
||||
|
||||
// import styles from "./styles.css" assert { type: "css" };
|
||||
it(`supports [import styles from "@css/lib/styles.css" assert { type: "css" }] (import assertions)`, async () => {
|
||||
mockProject([`import styles from "@css/lib/styles.css" assert { type: "css" };`]);
|
||||
await providence(findImportsQueryConfig, _providenceCfg);
|
||||
const queryResult = queryResults[0];
|
||||
const firstEntry = getEntry(queryResult);
|
||||
console.log({ firstEntry });
|
||||
expect(firstEntry.result[0].importSpecifiers[0]).to.equal('[default]');
|
||||
expect(firstEntry.result[0].source).to.equal('@css/lib/styles.css');
|
||||
expect(firstEntry.result[0].assertionType).to.equal('css');
|
||||
});
|
||||
|
||||
it(`supports [export styles from "@css/lib/styles.css" assert { type: "css" }] (import assertions)`, async () => {
|
||||
mockProject([`export styles from "@css/lib/styles.css" assert { type: "css" };`]);
|
||||
await providence(findImportsQueryConfig, _providenceCfg);
|
||||
const queryResult = queryResults[0];
|
||||
const firstEntry = getEntry(queryResult);
|
||||
console.log({ firstEntry });
|
||||
expect(firstEntry.result[0].importSpecifiers[0]).to.equal('[default]');
|
||||
expect(firstEntry.result[0].source).to.equal('@css/lib/styles.css');
|
||||
expect(firstEntry.result[0].assertionType).to.equal('css');
|
||||
});
|
||||
|
||||
describe('Filter out false positives', () => {
|
||||
it(`doesn't support [object.import('my/source')] (import method members)`, async () => {
|
||||
mockProject([`object.import('my/source')`]);
|
||||
|
|
@ -336,11 +360,4 @@ describe('Analyzer "find-imports"', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: put this in the generic providence/analyzer part
|
||||
describe.skip('With <script type="module"> inside .html', () => {
|
||||
it('gets the source from script tags', async () => {});
|
||||
|
||||
it('gets the content from script tags', async () => {});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -137,7 +137,6 @@ describe('trackdownIdentifier', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO: support import maps
|
||||
it(`identifies import map entries as internal sources`, async () => {
|
||||
mockProject(
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue