Merge pull request #6952 from loganfsmyth/commonjs-lazy

Add a 'lazy' options to modules-commonjs
This commit is contained in:
Logan Smyth
2017-12-17 19:32:51 -08:00
committed by GitHub
56 changed files with 557 additions and 23 deletions

View File

@@ -127,3 +127,37 @@ function _interopRequireDefault(obj) {
In cases where the auto-unwrapping of `default` is not needed, you can set the
`noInterop` option to `true` to avoid the usage of the `interopRequireDefault`
helper (shown in inline form above).
### `lazy`
`boolean`, `Array<string>`, or `(string) => boolean`, defaults to `false`
Changes Babel's compiled `import` statements to be lazily evaluated when their
imported bindings are used for the first time.
This can improve initial load time of your module because evaluating
dependencies up front is sometimes entirely un-necessary. This is especially
the case when implementing a library module.
The value of `lazy` has a few possible effects:
* `false` - No lazy initialization of any imported module.
* `true` - Do not lazy-initialize local `./foo` imports, but lazy-init `foo` dependencies.
Local paths are much more likely to have circular dependencies, which may break if loaded lazily,
so they are not lazy by default, whereas dependencies between independent modules are rarely cyclical.
* `Array<string>` - Lazy-initialize all imports with source matching one of the given strings.
* `(string) => boolean` - Pass a callback that will be called to decide if a given source string should be lazy-loaded.
The two cases where imports can never be lazy are:
* `import "foo";`
Side-effect imports are automatically non-lazy since their very existence means
that there is no binding to later kick off initialization.
* `export * from "foo"`
Re-exporting all names requires up-front execution because otherwise there is no
way to know what names need to be exported.

View File

@@ -16,9 +16,19 @@ export default function(api, options) {
strict,
strictMode,
noInterop,
lazy = false,
// Defaulting to 'true' for now. May change before 7.x major.
allowCommonJSExports = true,
} = options;
if (
typeof lazy !== "boolean" &&
typeof lazy !== "function" &&
(!Array.isArray(lazy) || !lazy.every(item => typeof item === "string"))
) {
throw new Error(`.lazy must be a boolean, array of strings, or a function`);
}
const getAssertion = localName => template.expression.ast`
(function(){
throw new Error("The CommonJS '" + "${localName}" + "' variable is not available in ES6 modules.");
@@ -122,6 +132,7 @@ export default function(api, options) {
strictMode,
allowTopLevelThis,
noInterop,
lazy,
},
);
@@ -132,14 +143,26 @@ export default function(api, options) {
let header;
if (isSideEffectImport(metadata)) {
if (metadata.lazy) throw new Error("Assertion failure");
header = t.expressionStatement(loadExpr);
} else {
header = t.variableDeclaration("var", [
t.variableDeclarator(
t.identifier(metadata.name),
wrapInterop(path, loadExpr, metadata.interop) || loadExpr,
),
]);
const init =
wrapInterop(path, loadExpr, metadata.interop) || loadExpr;
if (metadata.lazy) {
header = template.ast`
function ${metadata.name}() {
const data = ${init};
${metadata.name} = function(){ return data; };
return data;
}
`;
} else {
header = template.ast`
var ${metadata.name} = ${init};
`;
}
}
header.loc = metadata.loc;

View File

@@ -0,0 +1,3 @@
import foo from "foo";
console.log(foo);

View File

@@ -0,0 +1,13 @@
"use strict";
function _foo() {
const data = babelHelpers.interopRequireDefault(require("foo"));
_foo = function () {
return data;
};
return data;
}
console.log(_foo().default);

View File

@@ -0,0 +1,3 @@
import { foo } from "foo";
console.log(foo);

View File

@@ -0,0 +1,13 @@
"use strict";
function _foo() {
const data = require("foo");
_foo = function () {
return data;
};
return data;
}
console.log(_foo().foo);

View File

@@ -0,0 +1,3 @@
import * as foo from "foo";
console.log(foo);

View File

@@ -0,0 +1,13 @@
"use strict";
function foo() {
const data = babelHelpers.interopRequireWildcard(require("foo"));
foo = function () {
return data;
};
return data;
}
console.log(foo());

View File

@@ -0,0 +1,6 @@
{
"plugins": [
"external-helpers",
["transform-modules-commonjs", { "lazy": true }]
]
}

View File

@@ -0,0 +1 @@
export * from "foo";

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _foo = require("foo");
Object.keys(_foo).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _foo[key];
}
});
});

View File

@@ -0,0 +1,2 @@
import foo from "foo";
export { foo as default };

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function () {
return _foo().default;
}
});
function _foo() {
const data = babelHelpers.interopRequireDefault(require("foo"));
_foo = function () {
return data;
};
return data;
}

View File

@@ -0,0 +1,2 @@
import { named } from "foo";
export { named };

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "named", {
enumerable: true,
get: function () {
return _foo().named;
}
});
function _foo() {
const data = require("foo");
_foo = function () {
return data;
};
return data;
}

View File

@@ -0,0 +1,2 @@
import * as namespace from "foo";
export { namespace };

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.namespace = void 0;
function namespace() {
const data = babelHelpers.interopRequireWildcard(require("foo"));
namespace = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "namespace", {
enumerable: true,
get: function () {
return namespace();
}
});

View File

@@ -0,0 +1 @@
import "foo";

View File

@@ -0,0 +1,3 @@
"use strict";
require("foo");

View File

@@ -0,0 +1,3 @@
import foo from "./foo";
console.log(foo);

View File

@@ -0,0 +1,5 @@
"use strict";
var _foo = babelHelpers.interopRequireDefault(require("./foo"));
console.log(_foo.default);

View File

@@ -0,0 +1,3 @@
import { foo } from "./foo";
console.log(foo);

View File

@@ -0,0 +1,5 @@
"use strict";
var _foo = require("./foo");
console.log(_foo.foo);

View File

@@ -0,0 +1,3 @@
import * as foo from "./foo";
console.log(foo);

View File

@@ -0,0 +1,4 @@
"use strict";
var foo = babelHelpers.interopRequireWildcard(require("./foo"));
console.log(foo);

View File

@@ -0,0 +1,6 @@
{
"plugins": [
"external-helpers",
["transform-modules-commonjs", { "lazy": true }]
]
}

View File

@@ -0,0 +1 @@
export * from "./foo";

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _foo = require("./foo");
Object.keys(_foo).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _foo[key];
}
});
});

View File

@@ -0,0 +1,2 @@
import foo from "./foo";
export { foo as default };

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function () {
return _foo.default;
}
});
var _foo = babelHelpers.interopRequireDefault(require("./foo"));

View File

@@ -0,0 +1,2 @@
import { named } from "./foo";
export { named };

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "named", {
enumerable: true,
get: function () {
return _foo.named;
}
});
var _foo = require("./foo");

View File

@@ -0,0 +1,2 @@
import * as namespace from "./foo";
export { namespace };

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.namespace = void 0;
var namespace = babelHelpers.interopRequireWildcard(require("./foo"));
exports.namespace = namespace;

View File

@@ -0,0 +1 @@
import "./a";

View File

@@ -0,0 +1,3 @@
"use strict";
require("./a");

View File

@@ -0,0 +1,7 @@
import foo1 from "white";
console.log(foo1);
import foo2 from "black";
console.log(foo2);

View File

@@ -0,0 +1,16 @@
"use strict";
var _white = babelHelpers.interopRequireDefault(require("white"));
function _black() {
const data = babelHelpers.interopRequireDefault(require("black"));
_black = function () {
return data;
};
return data;
}
console.log(_white.default);
console.log(_black().default);

View File

@@ -0,0 +1,7 @@
import { foo1 } from "white";
console.log(foo1);
import { foo2 } from "black";
console.log(foo2);

View File

@@ -0,0 +1,16 @@
"use strict";
var _white = require("white");
function _black() {
const data = require("black");
_black = function () {
return data;
};
return data;
}
console.log(_white.foo1);
console.log(_black().foo2);

View File

@@ -0,0 +1,7 @@
import * as foo1 from "white";
console.log(foo1);
import * as foo2 from "black";
console.log(foo2);

View File

@@ -0,0 +1,16 @@
"use strict";
var foo1 = babelHelpers.interopRequireWildcard(require("white"));
function foo2() {
const data = babelHelpers.interopRequireWildcard(require("black"));
foo2 = function () {
return data;
};
return data;
}
console.log(foo1);
console.log(foo2());

View File

@@ -0,0 +1,6 @@
{
"plugins": [
"external-helpers",
["transform-modules-commonjs", { "lazy": ["white"] }]
]
}

View File

@@ -0,0 +1,3 @@
export * from "white";
export * from "black";

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _white = require("white");
Object.keys(_white).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _white[key];
}
});
});
var _black = require("black");
Object.keys(_black).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _black[key];
}
});
});

View File

@@ -0,0 +1,2 @@
import foo from "white";
export { foo as default };

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function () {
return _white.default;
}
});
var _white = babelHelpers.interopRequireDefault(require("white"));

View File

@@ -0,0 +1,5 @@
import { named1 } from "white";
export { named1 };
import { named2 } from "black";
export { named2 };

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "named1", {
enumerable: true,
get: function () {
return _white.named1;
}
});
Object.defineProperty(exports, "named2", {
enumerable: true,
get: function () {
return _black().named2;
}
});
var _white = require("white");
function _black() {
const data = require("black");
_black = function () {
return data;
};
return data;
}

View File

@@ -0,0 +1,5 @@
import * as namespace1 from "white";
export { namespace1 };
import * as namespace2 from "black";
export { namespace2 };

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.namespace2 = exports.namespace1 = void 0;
var namespace1 = babelHelpers.interopRequireWildcard(require("white"));
exports.namespace1 = namespace1;
function namespace2() {
const data = babelHelpers.interopRequireWildcard(require("black"));
namespace2 = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "namespace2", {
enumerable: true,
get: function () {
return namespace2();
}
});

View File

@@ -0,0 +1,2 @@
import "white";
import "black";

View File

@@ -0,0 +1,5 @@
"use strict";
require("white");
require("black");