129 lines
3.7 KiB
JavaScript
129 lines
3.7 KiB
JavaScript
const pathLib = require('path');
|
|
const t = require('@babel/types');
|
|
const { default: traverse } = require('@babel/traverse');
|
|
const { Analyzer } = require('./helpers/Analyzer.js');
|
|
const { trackDownIdentifierFromScope } = require('./helpers/track-down-identifier.js');
|
|
const { aForEach } = require('../utils/async-array-utils.js');
|
|
|
|
function cleanup(transformedEntry) {
|
|
transformedEntry.forEach(definitionObj => {
|
|
if (definitionObj.__tmp) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
delete definitionObj.__tmp;
|
|
}
|
|
});
|
|
return transformedEntry;
|
|
}
|
|
|
|
async function trackdownRoot(transformedEntry, relativePath, projectPath) {
|
|
const fullCurrentFilePath = pathLib.resolve(projectPath, relativePath);
|
|
|
|
await aForEach(transformedEntry, async definitionObj => {
|
|
const rootFile = await trackDownIdentifierFromScope(
|
|
definitionObj.__tmp.path,
|
|
definitionObj.constructorIdentifier,
|
|
fullCurrentFilePath,
|
|
projectPath,
|
|
);
|
|
// eslint-disable-next-line no-param-reassign
|
|
definitionObj.rootFile = rootFile;
|
|
});
|
|
return transformedEntry;
|
|
}
|
|
|
|
/**
|
|
* @desc Finds import specifiers and sources
|
|
* @param {BabelAst} ast
|
|
*/
|
|
function findCustomElementsPerAstEntry(ast) {
|
|
const definitions = [];
|
|
traverse(ast, {
|
|
CallExpression(path) {
|
|
let found = false;
|
|
// Doing it like this we detect 'customElements.define()',
|
|
// but also 'window.customElements.define()'
|
|
path.traverse({
|
|
MemberExpression(memberPath) {
|
|
if (memberPath.parentPath !== path) {
|
|
return;
|
|
}
|
|
const { node } = memberPath;
|
|
if (node.object.name === 'customElements' && node.property.name === 'define') {
|
|
found = true;
|
|
}
|
|
if (
|
|
node.object.object &&
|
|
node.object.object.name === 'window' &&
|
|
node.object.property.name === 'customElements' &&
|
|
node.property.name === 'define'
|
|
) {
|
|
found = true;
|
|
}
|
|
},
|
|
});
|
|
if (found) {
|
|
let tagName;
|
|
let constructorIdentifier;
|
|
|
|
if (t.isLiteral(path.node.arguments[0])) {
|
|
tagName = path.node.arguments[0].value;
|
|
} else {
|
|
// No Literal found. For now, we only mark them as '[variable]'
|
|
tagName = '[variable]';
|
|
}
|
|
if (path.node.arguments[1].type === 'Identifier') {
|
|
constructorIdentifier = path.node.arguments[1].name;
|
|
} else {
|
|
// We assume customElements.define('my-el', class extends HTMLElement {...})
|
|
constructorIdentifier = '[inline]';
|
|
}
|
|
definitions.push({ tagName, constructorIdentifier, __tmp: { path } });
|
|
}
|
|
},
|
|
});
|
|
return definitions;
|
|
}
|
|
|
|
class FindCustomelementsAnalyzer extends Analyzer {
|
|
constructor() {
|
|
super();
|
|
this.name = 'find-customelements';
|
|
}
|
|
|
|
/**
|
|
* @desc Finds export specifiers and sources
|
|
* @param {FindCustomelementsConfig} customConfig
|
|
*/
|
|
async execute(customConfig = {}) {
|
|
const cfg = {
|
|
targetProjectPath: null,
|
|
...customConfig,
|
|
};
|
|
|
|
/**
|
|
* Prepare
|
|
*/
|
|
const analyzerResult = this._prepare(cfg);
|
|
if (analyzerResult) {
|
|
return analyzerResult;
|
|
}
|
|
|
|
/**
|
|
* Traverse
|
|
*/
|
|
const projectPath = cfg.targetProjectPath;
|
|
const queryOutput = await this._traverse(async (ast, { relativePath }) => {
|
|
let transformedEntry = findCustomElementsPerAstEntry(ast);
|
|
transformedEntry = await trackdownRoot(transformedEntry, relativePath, projectPath);
|
|
transformedEntry = cleanup(transformedEntry);
|
|
return { result: transformedEntry };
|
|
});
|
|
|
|
/**
|
|
* Finalize
|
|
*/
|
|
return this._finalize(queryOutput, cfg);
|
|
}
|
|
}
|
|
|
|
module.exports = FindCustomelementsAnalyzer;
|