Skip to content

Commit

Permalink
Convert UMD to use new implementation of module logic.
Browse files Browse the repository at this point in the history
  • Loading branch information
loganfsmyth committed Sep 13, 2017
1 parent 47a2540 commit 95e08b6
Show file tree
Hide file tree
Showing 60 changed files with 1,512 additions and 284 deletions.
2 changes: 1 addition & 1 deletion babel.sublime-project
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"settings": {
"rulers": [
110
80
],

// Set to false to disable detection of tabs vs. spaces on load
Expand Down
3 changes: 3 additions & 0 deletions packages/babel-helper-modules/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
src
test
*.log
5 changes: 5 additions & 0 deletions packages/babel-helper-modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# babel-helper-modules

## Usage

TODO
15 changes: 15 additions & 0 deletions packages/babel-helper-modules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "babel-helper-modules",
"version": "7.0.0-beta.0",
"description": "Babel helper functions for implementing ES6 module transformations",
"author": "Logan Smyth <[email protected]>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-modules",
"main": "lib/index.js",
"dependencies": {
"babel-template": "7.0.0-beta.0",
"babel-types": "7.0.0-beta.0",
"lodash": "^4.2.0"
}
}
302 changes: 302 additions & 0 deletions packages/babel-helper-modules/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
import * as t from "babel-types";
import template from "babel-template";
import chunk from "lodash/chunk";

import rewriteThis from "./rewrite-this";
import rewriteLiveReferences from "./rewrite-live-references";
import normalizeAndLoadModuleMetadata, {
hasExports,
isSideEffectImport,
} from "./normalize-and-load-metadata";

export { hasExports };

/**
* Perform all of the generic ES6 module rewriting needed to handle initial
* module processing. This function will rewrite the majority of the given
* program to reference the modules described by the returned metadata,
* and returns a list of statements for use when initializing the module.
*/
export function rewriteModuleStatementsAndPrepareHeader(
path: NodePath,
{ exportName, strict, allowTopLevelThis, strictMode, loose, noInterop },
) {
const meta = normalizeAndLoadModuleMetadata(path, exportName, {
strict,
noInterop,
});

if (!allowTopLevelThis) {
rewriteThis(path);
}

rewriteLiveReferences(path, meta);

if (strictMode !== false && strict !== false) {
const hasStrict = path.node.directives.some(directive => {
return directive.value.value === "use strict";
});
if (!hasStrict) {
path.unshiftContainer(
"directives",
t.directive(t.directiveLiteral("use strict")),
);
}
}

const headers = [];
if (hasExports(meta) && !strict) {
headers.push(buildESModuleHeader(meta, loose /* enumerable */));
}

// Create all of the statically known named exports.
headers.push(...buildExportInitializationStatements(path, meta));

return { meta, headers };
}

/**
* Break down the module metadata into a simple array that contains the
* fields generally needed for compiling ES6 module support.
*/
export function getSourceMetadataArray(meta: ModuleMetadata) {
const lastNonSideEffectBlock = Array.from(
meta.source,
).reduceRight((acc, [source, metadata]) => {
if (acc !== null) return acc;

if (isSideEffectImport(metadata)) return null;
return source;
}, null);

let inSideEffectBlock = lastNonSideEffectBlock === null;

const items = [];
for (const [source, metadata] of meta.source) {
const isSideEffect = isSideEffectImport(metadata);

items.push([source, metadata, isSideEffect, inSideEffectBlock]);

if (source === lastNonSideEffectBlock) inSideEffectBlock = true;
}

return items;
}

/**
* Flag a set of statements as hoisted above all else so that module init
* statements all run before user code.
*/
export function ensureStatementsHoisted(statements) {
// Force all of the header fields to be at the top of the file.
statements.forEach(header => {
header._blockHoist = 3;
});
}

/**
* Given an expression for a standard import object, like "require('foo')",
* wrap it in a call to the interop helpers based on the type.
*/
export function wrapInterop(
programPath: NodePath,
expr: Node,
type: InteropType,
): Node {
if (type === "none") {
return null;
}

let helper;
if (type === "default") {
helper = "interopRequireDefault";
} else if (type === "namespace") {
helper = "interopRequireWildcard";
} else {
throw new Error(`Unknown interop: ${type}`);
}

return t.callExpression(programPath.hub.file.addHelper(helper), [expr]);
}

const buildNamespaceInit = template(`
var NAME = SOURCE;
`);

const buildReexportNamespace = template(`
EXPORTS.NAME = NAMESPACE;
`);

/**
* Create the runtime initialization statements for a given requested source.
* These will initialize all of the runtime import/export logic that
* can't be handled statically by the statements created by
* buildExportInitializationStatements().
*/
export function buildNamespaceInitStatements(
metadata: ModuleMetadata,
sourceMetadata: SourceModuleMetadata,
) {
const statements = [];

for (const localName of sourceMetadata.importsNamespace) {
if (localName === sourceMetadata.name) continue;

// Create and assign binding to namespace object
statements.push(
buildNamespaceInit({
NAME: t.identifier(localName),
SOURCE: t.identifier(sourceMetadata.name),
}),
);
}
for (const exportName of sourceMetadata.reexportNamespace) {
// Assign export to namespace object.
statements.push(
buildReexportNamespace({
EXPORTS: t.identifier(metadata.exportName),
NAME: t.identifier(exportName),
NAMESPACE: t.identifier(sourceMetadata.name),
}),
);
}
if (sourceMetadata.reexportAll) {
const statement = buildNamespaceReexport(metadata, sourceMetadata.name);
statement.loc = sourceMetadata.reexportAll.loc;

// Iterate props creating getter for each prop.
statements.push(statement);
}
return statements;
}

const moduleHeader = template(`
Object.defineProperty(EXPORTS, "__esModule", {
value: true,
})
`);

const moduleHeaderLoose = template(`
EXPORTS.__esModule = true;
`);

/**
* Build an "__esModule" header statement setting the property on a given object.
*/
function buildESModuleHeader(
metadata: ModuleMetadata,
enumerable: boolean = false,
) {
if (enumerable) {
return moduleHeaderLoose({
EXPORTS: t.identifier(metadata.exportName),
});
}

return moduleHeader({
EXPORTS: t.identifier(metadata.exportName),
});
}

const namespaceReexport = template(`
Object.keys(NAMESPACE).forEach(function(key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(EXPORTS, key, {
enumerable: true,
get: function() {
return NAMESPACE[key];
},
});
});
`);

/**
* Create a re-export initialization loop for a specific imported namespace.
*/
function buildNamespaceReexport(metadata, namespace) {
// TODO: This should skip exporting a prop that is already exported.
return namespaceReexport({
NAMESPACE: t.identifier(namespace),
EXPORTS: t.identifier(metadata.exportName),
});
}

const reexportGetter = template(`
Object.defineProperty(EXPORTS, EXPORT_NAME, {
enumerable: true,
get: function() {
return NAMESPACE.IMPORT_NAME;
},
});
`);

/**
* Create a set of statements that will initialize all of the statically-known
* export names with their expected values.
*/
function buildExportInitializationStatements(
programPath: NodePath,
metadata: ModuleMetadata,
) {
const initStatements = [];

const exportNames = [];
for (const [localName, data] of metadata.local) {
if (data.kind === "import") {
// No-open since these are explicitly set with the "reexports" block.
} else if (data.kind === "hoisted") {
initStatements.push(
buildInitStatement(metadata, data.names, t.identifier(localName)),
);
} else {
exportNames.push(...data.names);
}
}
for (const [, data] of metadata.source) {
for (const [exportName, importName] of data.reexports) {
initStatements.push(
reexportGetter({
EXPORTS: t.identifier(metadata.exportName),
EXPORT_NAME: t.stringLiteral(exportName),
NAMESPACE: t.identifier(data.name),
IMPORT_NAME: t.identifier(importName),
}),
);
}
for (const exportName of data.reexportNamespace) {
exportNames.push(exportName);
}
}

initStatements.push(
...chunk(exportNames, 100).map(members => {
return buildInitStatement(
metadata,
members,
programPath.scope.buildUndefinedNode(),
);
}),
);

return initStatements;
}

const initStatement = template(`
EXPORTS.NAME = VALUE;
`);

/**
* Given a set of export names, create a set of nested assignments to
* initialize them all to a given expression.
*/
function buildInitStatement(metadata, exportNames, initExpr) {
return t.expressionStatement(
exportNames.reduce((acc, exportName) => {
return initStatement({
EXPORTS: t.identifier(metadata.exportName),
NAME: t.identifier(exportName),
VALUE: acc,
}).expression;
}, initExpr),
);
}
Loading

0 comments on commit 95e08b6

Please sign in to comment.