Skip to content

Commit

Permalink
Prevent final resolution and facade creation for inlined dynamic impo…
Browse files Browse the repository at this point in the history
…rts (#2677)

* failing test for #2676

* skip final resolution if dynamic import has already been rendered

* add expected test output

* only mark dynamic import as replaced if it was completely overwritten

* * only render final resolutions for cross-chunk dynamic imports
* do not create facades for inlined dynamic imports

* Get rid of AppVeyor Node 8 test to speed up CI
  • Loading branch information
Rich-Harris authored and lukastaegert committed Feb 17, 2019
1 parent 4d082b0 commit db42a04
Show file tree
Hide file tree
Showing 39 changed files with 320 additions and 12 deletions.
1 change: 0 additions & 1 deletion appveyor.yml
Expand Up @@ -11,7 +11,6 @@ environment:
matrix:
# node.js
- nodejs_version: 6
- nodejs_version: 8
- nodejs_version: 10

install:
Expand Down
11 changes: 7 additions & 4 deletions src/Chunk.ts
Expand Up @@ -135,7 +135,7 @@ export default class Chunk {
private renderedSource: MagicStringBundle | null = null;
private renderedSourceLength: number = undefined;

constructor(graph: Graph, orderedModules: Module[], inlineDynamicImports: boolean) {
constructor(graph: Graph, orderedModules: Module[]) {
this.graph = graph;
this.orderedModules = orderedModules;
this.execIndex = orderedModules.length > 0 ? orderedModules[0].execIndex : Infinity;
Expand All @@ -149,7 +149,10 @@ export default class Chunk {
this.isManualChunk = true;
}
module.chunk = this;
if (module.isEntryPoint || (module.isDynamicEntryPoint && !inlineDynamicImports)) {
if (
module.isEntryPoint ||
module.dynamicallyImportedBy.some(module => orderedModules.indexOf(module) === -1)
) {
this.entryModules.push(module);
}
}
Expand Down Expand Up @@ -378,8 +381,8 @@ export default class Chunk {
for (const { node, resolution } of module.dynamicImports) {
if (!resolution) continue;
if (resolution instanceof Module) {
const resolutionChunk = resolution.facadeChunk || resolution.chunk;
if (resolutionChunk && resolutionChunk !== this && resolutionChunk.id) {
if (resolution.isIncluded() && resolution.chunk !== this) {
const resolutionChunk = resolution.facadeChunk || resolution.chunk;
let relPath = normalize(relative(dirname(this.id), resolutionChunk.id));
if (!relPath.startsWith('../')) relPath = './' + relPath;
node.renderFinalResolution(code, `'${relPath}'`);
Expand Down
6 changes: 3 additions & 3 deletions src/Graph.ts
Expand Up @@ -413,7 +413,7 @@ export default class Graph {
let chunks: Chunk[] = [];
if (this.preserveModules) {
for (const module of orderedModules) {
const chunk = new Chunk(this, [module], inlineDynamicImports);
const chunk = new Chunk(this, [module]);
if (module.isEntryPoint || !chunk.isEmpty) {
chunk.entryModules = [module];
}
Expand All @@ -434,7 +434,7 @@ export default class Graph {
for (const entryHashSum in chunkModules) {
const chunkModulesOrdered = chunkModules[entryHashSum];
sortByExecutionOrder(chunkModulesOrdered);
const chunk = new Chunk(this, chunkModulesOrdered, inlineDynamicImports);
const chunk = new Chunk(this, chunkModulesOrdered);
chunks.push(chunk);
}
}
Expand Down Expand Up @@ -463,7 +463,7 @@ export default class Graph {
for (const chunk of chunks) {
for (const entryModule of chunk.entryModules) {
if (chunk.facadeModule !== entryModule) {
const entryPointFacade = new Chunk(this, [], inlineDynamicImports);
const entryPointFacade = new Chunk(this, []);
entryPointFacade.turnIntoFacade(entryModule);
facades.push(entryPointFacade);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Module.ts
Expand Up @@ -160,6 +160,7 @@ export default class Module {
comments: CommentDescription[] = [];
customTransformCache: boolean;
dependencies: (Module | ExternalModule)[] = [];
dynamicallyImportedBy: Module[] = [];
dynamicDependencies: (Module | ExternalModule)[] = [];
dynamicImports: {
node: Import;
Expand All @@ -179,7 +180,6 @@ export default class Module {
importDescriptions: { [name: string]: ImportDescription } = Object.create(null);
importMetas: MetaProperty[] = [];
imports = new Set<Variable>();
isDynamicEntryPoint: boolean = false;
isEntryPoint: boolean = false;
isExecuted: boolean = false;
isExternal: false;
Expand Down Expand Up @@ -621,7 +621,7 @@ export default class Module {
const resolution = this.dynamicImports.find(dynamicImport => dynamicImport.node === node)
.resolution;
if (resolution instanceof Module) {
resolution.isDynamicEntryPoint = true;
resolution.dynamicallyImportedBy.push(this);
resolution.includeAllExports();
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/rollup/index.ts
Expand Up @@ -215,7 +215,8 @@ export default function rollup(rawInputOptions: GenericConfigObject): Promise<Ro
facadeModuleId: facadeModule && facadeModule.id,
fileName: chunk.id,
imports: chunk.getImportIds(),
isDynamicEntry: facadeModule !== null && facadeModule.isDynamicEntryPoint,
isDynamicEntry:
facadeModule !== null && facadeModule.dynamicallyImportedBy.length > 0,
isEntry: facadeModule !== null && facadeModule.isEntryPoint,
map: undefined,
modules: chunk.renderedModules,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/chunkColouring.ts
Expand Up @@ -33,7 +33,7 @@ export function assignChunkColouringHashes(
for (const { resolution } of module.dynamicImports) {
if (
resolution instanceof Module &&
resolution.isDynamicEntryPoint &&
resolution.dynamicallyImportedBy.length > 0 &&
!resolution.chunkAlias
) {
dynamicImports.push(resolution);
Expand Down
@@ -0,0 +1,7 @@
module.exports = {
description:
'handles dynamic imports of previously statically imported chunks that are also dynamically imported by other chunks',
options: {
input: ['main.js', 'main2.js']
}
};
@@ -0,0 +1,20 @@
define(['require', 'exports'], function (require, exports) { 'use strict';

function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

exports.foo = foo;
exports.bar = bar;

});
@@ -0,0 +1,7 @@
define(['exports', './generated-chunk.js'], function (exports, dep1_js) { 'use strict';



exports.bar = dep1_js.bar;

});
@@ -0,0 +1,5 @@
define(['./generated-chunk.js'], function (dep1_js) { 'use strict';

console.log(dep1_js.foo(), dep1_js.bar());

});
@@ -0,0 +1,5 @@
define(['require'], function (require) { 'use strict';

new Promise(function (resolve, reject) { require(['./generated-chunk2.js'], resolve, reject) }).then(({ bar }) => console.log(bar()));

});
@@ -0,0 +1,18 @@
'use strict';

function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

exports.foo = foo;
exports.bar = bar;
@@ -0,0 +1,7 @@
'use strict';

var dep1_js = require('./generated-chunk.js');



exports.bar = dep1_js.bar;
@@ -0,0 +1,5 @@
'use strict';

var dep1_js = require('./generated-chunk.js');

console.log(dep1_js.foo(), dep1_js.bar());
@@ -0,0 +1,3 @@
'use strict';

Promise.resolve(require('./generated-chunk2.js')).then(({ bar }) => console.log(bar()));
@@ -0,0 +1,15 @@
function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

export { foo as a, bar as b };
@@ -0,0 +1 @@
export { b as bar } from './generated-chunk.js';
@@ -0,0 +1,3 @@
import { a as foo, b as bar } from './generated-chunk.js';

console.log(foo(), bar());
@@ -0,0 +1 @@
import('./generated-chunk2.js').then(({ bar }) => console.log(bar()));
@@ -0,0 +1,27 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: function () {

exports({
a: foo,
b: bar
});

function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

}
};
});
@@ -0,0 +1,13 @@
System.register(['./generated-chunk.js'], function (exports, module) {
'use strict';
return {
setters: [function (module) {
exports('bar', module.b);
}],
execute: function () {



}
};
});
@@ -0,0 +1,15 @@
System.register(['./generated-chunk.js'], function (exports, module) {
'use strict';
var foo, bar;
return {
setters: [function (module) {
foo = module.a;
bar = module.b;
}],
execute: function () {

console.log(foo(), bar());

}
};
});
@@ -0,0 +1,10 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: function () {

module.import('./generated-chunk2.js').then(({ bar }) => console.log(bar()));

}
};
});
@@ -0,0 +1,5 @@
import {foo} from './dep2.js';

export function bar() {
return foo();
}
@@ -0,0 +1,5 @@
export function foo() {
return 'dep2';
}

import('./dep1.js').then(({ bar }) => console.log(bar()));
@@ -0,0 +1,4 @@
import { bar } from './dep1.js';
import { foo } from './dep2.js';

console.log(foo(), bar());
@@ -0,0 +1 @@
import('./dep1.js').then(({ bar }) => console.log(bar()));
@@ -0,0 +1,6 @@
module.exports = {
description: 'handles dynamic imports of previously statically imported chunks',
options: {
input: ['main.js']
}
};
@@ -0,0 +1,20 @@
define(['require', 'exports'], function (require, exports) { 'use strict';

function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

exports.foo = foo;
exports.bar = bar;

});
@@ -0,0 +1,5 @@
define(['./generated-chunk.js'], function (__chunk_1) { 'use strict';

console.log(__chunk_1.foo(), __chunk_1.bar());

});
@@ -0,0 +1,18 @@
'use strict';

function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

exports.foo = foo;
exports.bar = bar;
@@ -0,0 +1,5 @@
'use strict';

var __chunk_1 = require('./generated-chunk.js');

console.log(__chunk_1.foo(), __chunk_1.bar());
@@ -0,0 +1,15 @@
function foo() {
return 'dep2';
}

Promise.resolve().then(function () { return dep1; }).then(({ bar }) => console.log(bar()));

function bar() {
return foo();
}

var dep1 = /*#__PURE__*/Object.freeze({
bar: bar
});

export { foo as a, bar as b };
@@ -0,0 +1,3 @@
import { a as foo, b as bar } from './generated-chunk.js';

console.log(foo(), bar());

0 comments on commit db42a04

Please sign in to comment.