Given a names like: "some.module.js" and "some.other.module.js" the current regex in DefaultFormatter.prototype.getModuleName will overmatch and only return "some" as the module name in both cases. Changing the . character class to \w will make sure it does not pick up additional sections and returns "some.module" and "some.other.module" for the names
303 lines
8.1 KiB
JavaScript
303 lines
8.1 KiB
JavaScript
"use strict";
|
|
|
|
module.exports = DefaultFormatter;
|
|
|
|
var traverse = require("../../traverse");
|
|
var object = require("../../helpers/object");
|
|
var util = require("../../util");
|
|
var t = require("../../types");
|
|
var extend = require("lodash/object/extend");
|
|
|
|
function DefaultFormatter(file) {
|
|
this.file = file;
|
|
this.ids = object();
|
|
|
|
this.hasNonDefaultExports = false;
|
|
|
|
this.hasLocalExports = false;
|
|
this.hasLocalImports = false;
|
|
|
|
this.localImportOccurences = object();
|
|
this.localExports = object();
|
|
this.localImports = object();
|
|
|
|
this.getLocalExports();
|
|
this.getLocalImports();
|
|
|
|
this.remapAssignments();
|
|
}
|
|
|
|
DefaultFormatter.prototype.doDefaultExportInterop = function (node) {
|
|
return node.default && !this.noInteropRequire && !this.hasNonDefaultExports;
|
|
};
|
|
|
|
DefaultFormatter.prototype.bumpImportOccurences = function (node) {
|
|
var source = node.source.value;
|
|
var occurs = this.localImportOccurences;
|
|
occurs[source] = occurs[source] || 0;
|
|
occurs[source] += node.specifiers.length;
|
|
};
|
|
|
|
var exportsVisitor = {
|
|
enter: function (node, parent, scope, context, formatter) {
|
|
var declar = node && node.declaration;
|
|
if (t.isExportDeclaration(node)) {
|
|
formatter.hasLocalImports = true;
|
|
|
|
if (declar && t.isStatement(declar)) {
|
|
extend(formatter.localExports, t.getBindingIdentifiers(declar));
|
|
}
|
|
|
|
if (!node.default) {
|
|
formatter.hasNonDefaultExports = true;
|
|
}
|
|
|
|
if (node.source) {
|
|
formatter.bumpImportOccurences(node);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.getLocalExports = function () {
|
|
traverse(this.file.ast, exportsVisitor, this.file.scope, this);
|
|
};
|
|
|
|
var importsVisitor = {
|
|
enter: function (node, parent, scope, context, formatter) {
|
|
if (t.isImportDeclaration(node)) {
|
|
formatter.hasLocalImports = true;
|
|
extend(formatter.localImports, t.getBindingIdentifiers(node));
|
|
formatter.bumpImportOccurences(node);
|
|
}
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.getLocalImports = function () {
|
|
traverse(this.file.ast, importsVisitor, this.file.scope, this);
|
|
};
|
|
|
|
var remapVisitor = {
|
|
enter: function (node, parent, scope, context, formatter) {
|
|
if (t.isUpdateExpression(node) && formatter.isLocalReference(node.argument, scope)) {
|
|
context.skip();
|
|
|
|
// expand to long file assignment expression
|
|
var assign = t.assignmentExpression(node.operator[0] + "=", node.argument, t.literal(1));
|
|
|
|
// remap this assignment expression
|
|
var remapped = formatter.remapExportAssignment(assign);
|
|
|
|
// we don't need to change the result
|
|
if (t.isExpressionStatement(parent) || node.prefix) {
|
|
return remapped;
|
|
}
|
|
|
|
var nodes = [];
|
|
nodes.push(remapped);
|
|
|
|
var operator;
|
|
if (node.operator === "--") {
|
|
operator = "+";
|
|
} else { // "++"
|
|
operator = "-";
|
|
}
|
|
nodes.push(t.binaryExpression(operator, node.argument, t.literal(1)));
|
|
|
|
return t.sequenceExpression(nodes);
|
|
}
|
|
|
|
if (t.isAssignmentExpression(node) && formatter.isLocalReference(node.left, scope)) {
|
|
context.skip();
|
|
return formatter.remapExportAssignment(node);
|
|
}
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.remapAssignments = function () {
|
|
if (this.hasLocalImports) {
|
|
traverse(this.file.ast, remapVisitor, this.file.scope, this);
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.isLocalReference = function (node) {
|
|
var localImports = this.localImports;
|
|
return t.isIdentifier(node) && localImports[node.name] && localImports[node.name] !== node;
|
|
};
|
|
|
|
DefaultFormatter.prototype.checkLocalReference = function (node) {
|
|
var file = this.file;
|
|
if (this.isLocalReference(node)) {
|
|
throw file.errorWithNode(node, "Illegal assignment of module import");
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.remapExportAssignment = function (node) {
|
|
return t.assignmentExpression(
|
|
"=",
|
|
node.left,
|
|
t.assignmentExpression(
|
|
node.operator,
|
|
t.memberExpression(t.identifier("exports"), node.left),
|
|
node.right
|
|
)
|
|
);
|
|
};
|
|
|
|
DefaultFormatter.prototype.isLocalReference = function (node, scope) {
|
|
var localExports = this.localExports;
|
|
var name = node.name;
|
|
return t.isIdentifier(node) && localExports[name] && localExports[name] === scope.get(name, true);
|
|
};
|
|
|
|
DefaultFormatter.prototype.getModuleName = function () {
|
|
var opts = this.file.opts;
|
|
var filenameRelative = opts.filenameRelative;
|
|
var moduleName = "";
|
|
|
|
if (opts.moduleRoot) {
|
|
moduleName = opts.moduleRoot + "/";
|
|
}
|
|
|
|
if (!opts.filenameRelative) {
|
|
return moduleName + opts.filename.replace(/^\//, "");
|
|
}
|
|
|
|
if (opts.sourceRoot) {
|
|
// remove sourceRoot from filename
|
|
var sourceRootRegEx = new RegExp("^" + opts.sourceRoot + "\/?");
|
|
filenameRelative = filenameRelative.replace(sourceRootRegEx, "");
|
|
}
|
|
|
|
if (!opts.keepModuleIdExtensions) {
|
|
// remove extension
|
|
filenameRelative = filenameRelative.replace(/\.(\w*?)$/, "");
|
|
}
|
|
|
|
moduleName += filenameRelative;
|
|
|
|
// normalise path separators
|
|
moduleName = moduleName.replace(/\\/g, "/");
|
|
|
|
return moduleName;
|
|
};
|
|
|
|
DefaultFormatter.prototype._pushStatement = function (ref, nodes) {
|
|
if (t.isClass(ref) || t.isFunction(ref)) {
|
|
if (ref.id) {
|
|
nodes.push(t.toStatement(ref));
|
|
ref = ref.id;
|
|
}
|
|
}
|
|
|
|
return ref;
|
|
};
|
|
|
|
DefaultFormatter.prototype._hoistExport = function (declar, assign, priority) {
|
|
if (t.isFunctionDeclaration(declar)) {
|
|
assign._blockHoist = priority || 2;
|
|
}
|
|
|
|
return assign;
|
|
};
|
|
|
|
DefaultFormatter.prototype.getExternalReference = function (node, nodes) {
|
|
var ids = this.ids;
|
|
var id = node.source.value;
|
|
|
|
if (ids[id]) {
|
|
return ids[id];
|
|
} else {
|
|
return this.ids[id] = this._getExternalReference(node, nodes);
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.checkExportIdentifier = function (node) {
|
|
if (t.isIdentifier(node, { name: "__esModule" })) {
|
|
throw this.file.errorWithNode(node, "Illegal export __esModule - this is used internally for CommonJS interop");
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.exportSpecifier = function (specifier, node, nodes) {
|
|
var inherits = false;
|
|
if (node.specifiers.length === 1) inherits = node;
|
|
|
|
if (node.source) {
|
|
var ref = this.getExternalReference(node, nodes);
|
|
|
|
if (t.isExportBatchSpecifier(specifier)) {
|
|
// export * from "foo";
|
|
nodes.push(this.buildExportsWildcard(ref, node));
|
|
} else {
|
|
if (t.isSpecifierDefault(specifier) && !this.noInteropRequire) {
|
|
// importing a default so we need to normalise it
|
|
ref = t.callExpression(this.file.addHelper("interop-require"), [ref]);
|
|
} else {
|
|
ref = t.memberExpression(ref, t.getSpecifierId(specifier));
|
|
}
|
|
|
|
// export { foo } from "test";
|
|
nodes.push(this.buildExportsAssignment(
|
|
t.getSpecifierName(specifier),
|
|
ref,
|
|
node
|
|
));
|
|
}
|
|
} else {
|
|
// export { foo };
|
|
nodes.push(this.buildExportsAssignment(t.getSpecifierName(specifier), specifier.id, node));
|
|
}
|
|
};
|
|
|
|
DefaultFormatter.prototype.buildExportsWildcard = function (objectIdentifier) {
|
|
return t.expressionStatement(t.callExpression(this.file.addHelper("defaults"), [
|
|
t.identifier("exports"),
|
|
t.callExpression(this.file.addHelper("interop-require-wildcard"), [objectIdentifier])
|
|
]));
|
|
};
|
|
|
|
DefaultFormatter.prototype.buildExportsAssignment = function (id, init) {
|
|
this.checkExportIdentifier(id);
|
|
return util.template("exports-assign", {
|
|
VALUE: init,
|
|
KEY: id
|
|
}, true);
|
|
};
|
|
|
|
DefaultFormatter.prototype.exportDeclaration = function (node, nodes) {
|
|
var declar = node.declaration;
|
|
|
|
var id = declar.id;
|
|
|
|
if (node.default) {
|
|
id = t.identifier("default");
|
|
}
|
|
|
|
var assign;
|
|
|
|
if (t.isVariableDeclaration(declar)) {
|
|
for (var i = 0; i < declar.declarations.length; i++) {
|
|
var decl = declar.declarations[i];
|
|
|
|
decl.init = this.buildExportsAssignment(decl.id, decl.init, node).expression;
|
|
|
|
var newDeclar = t.variableDeclaration(declar.kind, [decl]);
|
|
if (i === 0) t.inherits(newDeclar, declar);
|
|
nodes.push(newDeclar);
|
|
}
|
|
} else {
|
|
var ref = declar;
|
|
|
|
if (t.isFunctionDeclaration(declar) || t.isClassDeclaration(declar)) {
|
|
ref = declar.id;
|
|
nodes.push(declar);
|
|
}
|
|
|
|
assign = this.buildExportsAssignment(id, ref, node);
|
|
|
|
nodes.push(assign);
|
|
|
|
this._hoistExport(declar, assign);
|
|
}
|
|
};
|