@amplitude/ampli
Version:
Amplitude CLI
105 lines (104 loc) • 4.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const babelParser = require("@babel/parser");
const traverse_1 = require("@babel/traverse");
const base_1 = require("../base");
const BABEL_PARSER_OPTIONS = {
sourceType: 'unambiguous',
plugins: [
`flow`,
`jsx`,
`asyncGenerators`,
`bigInt`,
`classProperties`,
`classPrivateProperties`,
`classPrivateMethods`,
[`decorators`, { decoratorsBeforeExport: true }],
`doExpressions`,
`dynamicImport`,
`exportDefaultFrom`,
`exportNamespaceFrom`,
`functionBind`,
`functionSent`,
`importMeta`,
`nullishCoalescingOperator`,
`numericSeparator`,
`objectRestSpread`,
`optionalCatchBinding`,
`optionalChaining`,
[`pipelineOperator`, { proposal: 'minimal' }],
`throwExpressions`,
],
};
class JavaScriptVerifier extends base_1.Verifier {
constructor(ampliInstanceNames = ['itly'], fileExtensionPattern = 'js{x,}', fileFilter = null) {
super(fileExtensionPattern, ampliInstanceNames.map(name => `${name}.`), fileFilter, ['node_modules']);
this.ampliInstanceNames = ampliInstanceNames;
}
async doVerify(eventNames, workingDirs) {
return this.verifyEach(this.verifyFile.bind(this), workingDirs);
}
verifyFile(filePath, fileContents, addReference) {
if (!fileContents) {
return;
}
const ast = babelParser.parse(fileContents, BABEL_PARSER_OPTIONS);
traverse_1.default(ast, {
Identifier: path => {
if (!path.parent || path.parent.type !== 'NewExpression')
return;
const { node } = path;
if (node) {
const { name, loc } = node;
if (name && loc) {
addReference({
filePath,
name: this.getReferenceName(name),
row: loc.start.line,
column: loc.start.column,
});
}
}
},
});
this.ampliInstanceNames.forEach(instanceName => {
const nameParts = instanceName.split('.');
traverse_1.default(ast, {
MemberExpression: path => JavaScriptVerifier.visitMemberExpression(path, filePath, nameParts, addReference),
OptionalMemberExpression: path => JavaScriptVerifier.visitMemberExpression(path, filePath, nameParts, addReference),
});
});
}
static visitMemberExpression(path, filePath, nameParts, addReference) {
let node = path.node;
if (!path.parent || (path.parent.type !== 'CallExpression' && path.parent.type !== 'OptionalCallExpression'))
return;
const { name, loc } = node.property;
let optional = path.parent.type === 'OptionalCallExpression';
node = node.object;
for (let i = nameParts.length - 1; node && i >= 0; i -= 1) {
if (node.type === 'Identifier') {
if (`${node.name}${optional ? '?' : ''}` !== nameParts[i]) {
return;
}
optional = false;
}
else if (node.type === 'MemberExpression') {
if (`${node.property.name}${optional ? '?' : ''}` !== nameParts[i]) {
return;
}
optional = false;
node = node.object;
}
else if (node.type === 'OptionalMemberExpression') {
if (`${node.property.name}${optional ? '?' : ''}` !== nameParts[i]) {
return;
}
optional = true;
node = node.object;
}
}
addReference({ filePath, name, row: loc.start.line, column: loc.start.column });
}
}
exports.default = JavaScriptVerifier;