diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 4c3d684f10..d571b92b7c 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -36,9 +36,18 @@ export default class File { this.declarations = {}; this.usedHelpers = {}; this.dynamicData = {}; - this.metadata = {}; this.data = {}; + this.metadata = { + modules: { + imports: [], + exports: { + exported: [], + specifiers: [] + } + } + }; + this.pipeline = pipeline; this.log = new Logger(this, opts.filename || "unknown"); this.ast = {}; @@ -493,15 +502,9 @@ export default class File { if (modFormatter.init && this.transformers["es6.modules"].canTransform()) { modFormatter.init(); } - this.populateModuleMetadata(); this.log.debug("End module formatter init"); } - populateModuleMetadata() { - var modules = {}; - this.metadata.modules = modules; - } - transform() { this.call("pre"); for (var pass of (this.transformerStack: Array)) { diff --git a/src/babel/transformation/modules/_default.js b/src/babel/transformation/modules/_default.js index 07bbef9797..39012123c3 100644 --- a/src/babel/transformation/modules/_default.js +++ b/src/babel/transformation/modules/_default.js @@ -77,31 +77,134 @@ var metadataVisitor = { ImportDeclaration(node, parent, scope, formatter) { formatter.hasLocalImports = true; - extend(formatter.localImports, this.getBindingIdentifiers()); + + var specifiers = []; + var imported = []; + formatter.metadata.imports.push({ + source: node.source.value, + imported, + specifiers + }); + + for (var specifier of (this.get("specifiers"): Array)) { + var ids = specifier.getBindingIdentifiers(); + extend(formatter.localImports, ids); + + var local = specifier.node.local.name; + + if (specifier.isImportDefaultSpecifier()) { + imported.push("default"); + specifiers.push({ + kind: "named", + imported: "default", + local + }); + } + + if (specifier.isImportSpecifier()) { + var importedName = specifier.node.imported.name; + imported.push(importedName); + specifiers.push({ + kind: "named", + imported: importedName, + local + }); + } + + if (specifier.isImportNamespaceSpecifier()) { + imported.push("*"); + specifiers.push({ + kind: "namespace", + local + }); + } + } }, ExportDeclaration(node, parent, scope, formatter) { formatter.hasLocalExports = true; + var source = node.source ? node.source.value : null; + var exports = formatter.metadata.exports; + + // export function foo() {} + // export var foo = "bar"; var declar = this.get("declaration"); if (declar.isStatement()) { var bindings = declar.getBindingIdentifiers(); for (var name in bindings) { var binding = bindings[name]; formatter._addExport(name, binding); + + exports.exported.push(name); + exports.specifiers.push({ + kind: "local", + local: name, + exported: this.isExportDefaultDeclaration() ? "default" : name + }); } } if (this.isExportNamedDeclaration() && node.specifiers) { - for (var i = 0; i < node.specifiers.length; i++) { - var specifier = node.specifiers[i]; + for (var specifier of (node.specifiers: Array)) { + var exported = specifier.exported.name; + exports.exported.push(exported); + + // export foo from "bar"; + if (t.isExportDefaultSpecifier(specifier)) { + exports.specifiers.push({ + kind: "external", + local: exported, + exported, + source + }); + } + + // export * as foo from "bar"; + if (t.isExportNamespaceSpecifier(specifier)) { + exports.specifiers.push({ + kind: "external-namespace", + exported, + source + }); + } + var local = specifier.local; if (!local) continue; formatter._addExport(local.name, specifier.exported); + + // export { foo } from "bar"; + // export { foo as bar } from "bar"; + if (source) { + exports.specifiers.push({ + kind: "external", + local: local.name, + exported, + source + }); + } + + // export { foo }; + // export { foo as bar }; + if (!source) { + exports.specifiers.push({ + kind: "local", + local: local.name, + exported + }); + } } } + // export * from "bar"; + if (this.isExportAllDeclaration()) { + exports.specifiers.push({ + kind: "external-all", + source + }); + } + if (!t.isExportDefaultDeclaration(node)) { var onlyDefault = node.specifiers && node.specifiers.length === 1 && t.isSpecifierDefault(node.specifiers[0]); if (!onlyDefault) { @@ -131,6 +234,7 @@ export default class DefaultFormatter { this.localExports = object(); this.localImports = object(); + this.metadata = file.metadata.modules; this.getMetadata(); } diff --git a/test/core/api.js b/test/core/api.js index 9ccd448b1f..10ab1c3047 100644 --- a/test/core/api.js +++ b/test/core/api.js @@ -8,17 +8,17 @@ var assert = require("assert"); var File = require("../../lib/babel/transformation/file"); suite("api", function () { - test("{ code: false }", function () { + test("code option false", function () { var result = transform("foo('bar');", { code: false }); assert.ok(!result.code); }); - test("{ ast: false }", function () { + test("ast option false", function () { var result = transform("foo('bar');", { ast: false }); assert.ok(!result.ast); }); - test("{ auxiliaryComment }", function () { + test("auxiliaryComment option", function () { assert.ok(transform("class Foo {}", { auxiliaryComment: "foobar" }).code.indexOf("foobar") >= 0); @@ -28,7 +28,115 @@ suite("api", function () { }).code.indexOf("foobar") >= 0); }); - test("ignore", function () { + test("modules metadata", function () { + assert.deepEqual(transform('import { externalName as localName } from "external";').metadata.modules.imports[0], { + source: "external", + imported: ["externalName"], + specifiers: [{ + kind: "named", + imported: "externalName", + local: "localName" + }] + }); + + assert.deepEqual(transform('import * as localName2 from "external";').metadata.modules.imports[0], { + source: "external", + imported: ["*"], + specifiers: [{ + kind: "namespace", + local: "localName2" + }] + }); + + assert.deepEqual(transform('import localName3 from "external";').metadata.modules.imports[0], { + source: 'external', + imported: ['default'], + specifiers: [{ + kind: 'named', + imported: 'default', + local: 'localName3' + }] + }); + + assert.deepEqual(transform('export * as externalName1 from "external";', { + stage: 0 + }).metadata.modules.exports, { + exported: ['externalName1'], + specifiers: [{ + kind: 'external-namespace', + exported: 'externalName1', + source: "external", + }] + }); + + assert.deepEqual(transform('export externalName2 from "external";', { + stage: 0 + }).metadata.modules.exports, { + exported: ["externalName2"], + specifiers: [{ + kind: "external", + local: "externalName2", + exported: "externalName2", + source: "external" + }] + }); + + assert.deepEqual(transform('export function namedFunction() {}').metadata.modules.exports, { + exported: ["namedFunction"], + specifiers: [{ + kind: "local", + local: "namedFunction", + exported: "namedFunction" + }] + }); + + assert.deepEqual(transform('export var foo = "bar";').metadata.modules.exports, { + "exported": ["foo"], + specifiers: [{ + kind: "local", + local: "foo", + exported: "foo" + }] + }); + + assert.deepEqual(transform("export { localName as externalName3 };").metadata.modules.exports, { + exported: ["externalName3"], + specifiers: [{ + kind: "local", + local: "localName", + exported: "externalName3" + }] + }); + + assert.deepEqual(transform('export { externalName4 } from "external";').metadata.modules.exports, { + exported: ["externalName4"], + specifiers: [{ + kind: "external", + local: "externalName4", + exported: "externalName4", + source: "external" + }] + }); + + assert.deepEqual(transform('export * from "external";').metadata.modules.exports, { + exported: [], + specifiers: [{ + kind: "external-all", + source: "external" + }] + }); + + assert.deepEqual(transform("export default function defaultFunction() {}").metadata.modules.exports, { + exported: ["defaultFunction"], + specifiers: [{ + kind: "local", + local: "defaultFunction", + exported: "default" + }] + }); + }); + + test("ignore option", function () { assert.ok(transform("", { ignore: "node_modules", filename: "/foo/node_modules/bar" @@ -45,7 +153,7 @@ suite("api", function () { }).ignored); }); - test("only", function () { + test("only option", function () { assert.ok(!transform("", { only: "node_modules", filename: "/foo/node_modules/bar" @@ -77,7 +185,7 @@ suite("api", function () { }).ignored); }); - suite("getModuleId() {} option", function () { + suite("getModuleId option", function () { // As of this commit, `getModuleId` is the only option that isn't JSON // compatible which is why it's not inside /test/core/fixtures/transformation @@ -94,7 +202,7 @@ suite("api", function () { assert.equal(result.code, expected); } - test("{ modules: \"amd\" }", function () { + test("amd", function () { var expected = [ "define('foo/bar', ['exports'], function (exports) {", " 'use strict';", @@ -106,7 +214,7 @@ suite("api", function () { getModuleNameTest("amd", expected); }); - test("{ modules: \"umd\" }", function () { + test("umd", function () { var expected = [ "(function (global, factory) {", " if (typeof define === 'function' && define.amd) {", @@ -130,7 +238,7 @@ suite("api", function () { getModuleNameTest("umd", expected); }); - test("{ modules: \"system\" }", function () { + test("system", function () { var expected = [ "System.register('foo/bar', [], function (_export) {", " 'use strict';", @@ -196,7 +304,7 @@ suite("api", function () { }, /Unknown helper foob/); }); - test("resolveModuleSource", function () { + test("resolveModuleSource option", function () { var actual = 'import foo from "foo-import-default";\nimport "foo-import-bare";\nexport { foo } from "foo-export-named";'; var expected = 'import foo from "resolved/foo-import-default";\nimport "resolved/foo-import-bare";\nexport { foo } from "resolved/foo-export-named";';