/**
|
* @param {{ defer?: boolean, source?: boolean }} options
|
* @param {typeof import("acorn").Parser} Parser
|
* @param {typeof import("acorn").tokTypes} acorn
|
*/
|
exports.plugin = function acornImportPhase(options, Parser, tt) {
|
return class extends Parser {
|
parseImport(node) {
|
this._phase = null;
|
const result = super.parseImport(node);
|
if (this._phase) {
|
node.phase = this._phase;
|
}
|
return result;
|
}
|
|
parseImportSpecifiers() {
|
let phase =
|
options.defer !== false && this.isContextual("defer")
|
? "defer"
|
: options.source !== false && this.isContextual("source")
|
? "source"
|
: null;
|
if (!phase) return super.parseImportSpecifiers();
|
|
const phaseId = this.parseIdent();
|
if (this.isContextual("from") || this.type === tt.comma) {
|
const defaultSpecifier = this.startNodeAt(phaseId.start, phaseId.loc && phaseId.loc.start);
|
defaultSpecifier.local = phaseId;
|
this.checkLValSimple(phaseId, /* BIND_LEXICAL */ 2);
|
|
const nodes = [this.finishNode(defaultSpecifier, "ImportDefaultSpecifier")];
|
if (this.eat(tt.comma)) {
|
if (this.type !== tt.star && this.type !== tt.braceL) {
|
this.unexpected();
|
}
|
nodes.push(...super.parseImportSpecifiers());
|
}
|
return nodes;
|
}
|
|
this._phase = phase;
|
|
if (phase === "defer") {
|
if (this.type !== tt.star) {
|
this.raiseRecoverable(
|
phaseId.start,
|
"'import defer' can only be used with namespace imports ('import defer * as identifierName from ...')."
|
);
|
}
|
} else if (phase === "source") {
|
if (this.type !== tt.name) {
|
this.raiseRecoverable(
|
phaseId.start,
|
"'import source' can only be used with direct identifier specifier imports."
|
);
|
}
|
}
|
|
const specifiers = super.parseImportSpecifiers();
|
|
if (phase === "source" && specifiers.some(s => s.type !== "ImportDefaultSpecifier")) {
|
this.raiseRecoverable(
|
phaseId.start,
|
`'import source' can only be used with direct identifier specifier imports ('import source identifierName from ...').`
|
);
|
}
|
|
return specifiers;
|
}
|
|
parseExprImport(forNew) {
|
const node = super.parseExprImport(forNew);
|
|
if (node.type === "MetaProperty" && (node.property.name === "defer" || node.property.name === "source")) {
|
if (this.type === tt.parenL) {
|
const dynImport = this.parseDynamicImport(this.startNodeAt(node.start, node.loc && node.loc.start));
|
dynImport.phase = node.property.name;
|
return dynImport;
|
} else {
|
this.raiseRecoverable(
|
node.start,
|
`'import.${node.property.name}' can only be used in a dynamic import.`
|
);
|
}
|
}
|
|
return node;
|
}
|
|
parseImportMeta(node) {
|
this.next();
|
|
var containsEsc = this.containsEsc;
|
node.property = this.parseIdent(true);
|
|
const { name } = node.property;
|
|
if (name !== "meta" && name !== "defer" && name !== "source") {
|
this.raiseRecoverable(
|
node.property.start,
|
"The only valid meta property for import is 'import.meta'"
|
);
|
}
|
if (containsEsc) {
|
this.raiseRecoverable(
|
node.start,
|
`'import.${name}' must not contain escaped characters`
|
);
|
}
|
if (
|
name === "meta" &&
|
this.options.sourceType !== "module" &&
|
!this.options.allowImportExportEverywhere
|
) {
|
this.raiseRecoverable(
|
node.start,
|
"Cannot use 'import.meta' outside a module"
|
);
|
}
|
|
return this.finishNode(node, "MetaProperty");
|
}
|
};
|
};
|