Skip to content

Commit

Permalink
Create chunks for virtual modules when preserving modules (#2511)
Browse files Browse the repository at this point in the history
* Create test

* Enable functional tests with code-splitting and add red code splitting module order test

* Sort external and inter-chunk imports according to execution order

* Extract execution order sorting, improve some names and clean up code

* Extract module execution order analysis from Graph

* Create chunks for virtual modules when preserving modules

* Automatically deconflict virtual modules from existing ones
  • Loading branch information
lukastaegert committed Oct 30, 2018
1 parent fa341ab commit 7746e0f
Show file tree
Hide file tree
Showing 23 changed files with 195 additions and 22 deletions.
18 changes: 9 additions & 9 deletions browser/path.ts
@@ -1,23 +1,23 @@
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/;
export const relativePath = /^\.?\.\//;

export function isAbsolute (path: string) {
export function isAbsolute(path: string) {
return absolutePath.test(path);
}

export function isRelative (path: string) {
export function isRelative(path: string) {
return relativePath.test(path);
}

export function normalize (path: string) {
export function normalize(path: string) {
return path.replace(/\\/g, '/');
}

export function basename (path: string) {
export function basename(path: string) {
return path.split(/(\/|\\)/).pop();
}

export function dirname (path: string) {
export function dirname(path: string) {
const match = /(\/|\\)[^/\\]*$/.exec(path);
if (!match) return '.';

Expand All @@ -27,13 +27,13 @@ export function dirname (path: string) {
return dir ? dir : '/';
}

export function extname (path: string) {
export function extname(path: string) {
const match = /\.[^.]+$/.exec(basename(path));
if (!match) return '';
return match[0];
}

export function relative (from: string, to: string) {
export function relative(from: string, to: string) {
const fromParts = from.split(/[/\\]/).filter(Boolean);
const toParts = to.split(/[/\\]/).filter(Boolean);

Expand All @@ -56,7 +56,7 @@ export function relative (from: string, to: string) {
return toParts.join('/');
}

export function resolve (...paths: string[]) {
export function resolve(...paths: string[]) {
let resolvedParts = paths.shift().split(/[/\\]/);

paths.forEach(path => {
Expand All @@ -76,5 +76,5 @@ export function resolve (...paths: string[]) {
}
});

return resolvedParts.join('/'); // TODO windows...
return resolvedParts.join('/');
}
19 changes: 15 additions & 4 deletions src/Chunk.ts
Expand Up @@ -26,7 +26,7 @@ import error from './utils/error';
import { sortByExecutionOrder } from './utils/execution-order';
import getIndentString from './utils/getIndentString';
import { makeLegal } from './utils/identifierHelpers';
import { basename, dirname, normalize, relative, resolve } from './utils/path';
import { basename, dirname, isAbsolute, normalize, relative, resolve } from './utils/path';
import renderChunk from './utils/renderChunk';
import { RenderOptions } from './utils/renderHelpers';
import { makeUnique, renderNamePattern } from './utils/renderNamePattern';
Expand Down Expand Up @@ -1010,16 +1010,27 @@ export default class Chunk {
this.preRender(options, inputBase);
}

generateIdPreserveModules(preserveModulesRelativeDir: string) {
return (this.id = normalize(relative(preserveModulesRelativeDir, this.entryModule.id)));
generateIdPreserveModules(
preserveModulesRelativeDir: string,
existingNames: Record<string, true>
) {
const sanitizedId = this.entryModule.id.replace('\0', '_');
this.id = makeUnique(
normalize(
isAbsolute(this.entryModule.id)
? relative(preserveModulesRelativeDir, sanitizedId)
: '_virtual/' + basename(sanitizedId)
),
existingNames
);
}

generateId(
pattern: string,
patternName: string,
addons: Addons,
options: OutputOptions,
existingNames: { [name: string]: any }
existingNames: Record<string, true>
) {
this.id = makeUnique(
renderNamePattern(pattern, patternName, type => {
Expand Down
11 changes: 6 additions & 5 deletions src/rollup/index.ts
Expand Up @@ -9,7 +9,7 @@ import error from '../utils/error';
import { writeFile } from '../utils/fs';
import getExportMode from '../utils/getExportMode';
import mergeOptions, { GenericConfigObject } from '../utils/mergeOptions';
import { basename, dirname, resolve } from '../utils/path';
import { basename, dirname, isAbsolute, resolve } from '../utils/path';
import { SOURCEMAPPING_URL } from '../utils/sourceMappingURL';
import { getTimings, initialiseTimers, timeEnd, timeStart } from '../utils/timers';
import { Watcher } from '../watch';
Expand Down Expand Up @@ -262,9 +262,10 @@ export default function rollup(
// populate asset files into output
const assetFileNames = outputOptions.assetFileNames || 'assets/[name]-[hash][extname]';
const outputBundle: OutputBundle = graph.finaliseAssets(assetFileNames);

const inputBase = commondir(
chunks.filter(chunk => chunk.entryModule).map(chunk => chunk.entryModule.id)
chunks
.filter(chunk => chunk.entryModule && isAbsolute(chunk.entryModule.id))
.map(chunk => chunk.entryModule.id)
);

return graph.pluginDriver
Expand Down Expand Up @@ -299,7 +300,7 @@ export default function rollup(
: <string>inputOptions.input)
);
} else if (inputOptions.experimentalPreserveModules) {
chunk.generateIdPreserveModules(inputBase);
chunk.generateIdPreserveModules(inputBase, usedIds);
} else {
let pattern, patternName;
if (chunk.isEntryModuleFacade) {
Expand All @@ -310,8 +311,8 @@ export default function rollup(
patternName = 'output.chunkFileNames';
}
chunk.generateId(pattern, patternName, addons, outputOptions, usedIds);
usedIds[chunk.id] = true;
}
usedIds[chunk.id] = true;
}

// assign to outputBundle
Expand Down
1 change: 1 addition & 0 deletions src/utils/commondir.ts
Expand Up @@ -2,6 +2,7 @@ import * as path from './path';

// ported from https://github.com/substack/node-commondir
export default function commondir(files: string[]) {
if (files.length === 0) return '/';
if (files.length === 1) return path.dirname(files[0]);
const commonSegments = files.slice(1).reduce((commonSegments, file) => {
const pathSegements = file.split(/\/+|\\+/);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/path.ts
@@ -1,5 +1,5 @@
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/;
export const relativePath = /^\.?\.\//;
const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/;
const relativePath = /^\.?\.\//;

export function isAbsolute(path: string) {
return absolutePath.test(path);
Expand Down
@@ -0,0 +1,23 @@
module.exports = {
solo: true,
description: 'Generates actual files for virtual modules when preserving modules',
options: {
input: 'main.js',
experimentalPreserveModules: true,
plugins: [
{
resolveId(id) {
if (id === '\0virtualModule') return id;
},
load(id) {
if (id !== '\0virtualModule') return null;
return 'export const virtual = "Virtual!";\n';
},
transform(code, id) {
if (id === '\0virtualModule') return null;
return 'import {virtual} from "\0virtualModule";\n' + code;
}
}
]
}
};
@@ -0,0 +1,7 @@
define(['exports'], function (exports) { 'use strict';

const virtual = "Virtual!";

exports.virtual = virtual;

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

assert.equal(__chunk_1.virtual, 'Virtual!');

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

const virtual = "Virtual!";

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

var __chunk_1 = require('./_virtual/_virtualModule');

assert.equal(__chunk_1.virtual, 'Virtual!');
@@ -0,0 +1,3 @@
const virtual = "Virtual!";

export { virtual };
@@ -0,0 +1,3 @@
import { virtual } from './_virtual/_virtualModule';

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

const virtual = exports('virtual', "Virtual!");

}
};
});
@@ -0,0 +1,14 @@
System.register(['./_virtual/_virtualModule'], function (exports, module) {
'use strict';
var virtual;
return {
setters: [function (module) {
virtual = module.virtual;
}],
execute: function () {

assert.equal(virtual, 'Virtual!');

}
};
});
@@ -0,0 +1 @@
assert.equal(virtual, 'Virtual!');
2 changes: 1 addition & 1 deletion test/function/index.js
Expand Up @@ -31,7 +31,7 @@ function runSingleFileTest(code, configContext) {

function runCodeSplitTest(output, configContext) {
const requireFromOutputVia = importer => importee => {
const outputId = path.join(path.dirname(importer), importee);
const outputId = path.posix.join(path.posix.dirname(importer), importee);
const chunk = output[outputId];
if (chunk) {
return requireWithContext(
Expand Down
@@ -0,0 +1,36 @@
const assert = require('assert');

module.exports = {
description: 'Generates actual files for virtual modules when preserving modules',
options: {
input: ['main.js'],
experimentalCodeSplitting: true,
experimentalPreserveModules: true,
plugins: [
{
resolveId(id) {
if (id === '\0virtualModule.js') return id;
},
load(id) {
if (id !== '\0virtualModule.js') return null;
return 'export const virtual = "Virtual!";\n';
},
transform(code, id) {
if (id === '\0virtualModule.js') return null;
return 'import {virtual} from "\0virtualModule.js";\n' + code;
}
}
]
},
bundle(bundle) {
return bundle
.generate({ format: 'esm' })
.then(generated =>
assert.deepEqual(Object.keys(generated.output), [
'_virtual/_virtualModule.js',
'_virtual/_virtualModule2.js',
'main.js'
])
);
}
};
@@ -0,0 +1,3 @@
export const notSoVirtual = 'real';

assert.equal(virtual, 'Virtual!');
@@ -0,0 +1,4 @@
import { notSoVirtual } from './_virtual/_virtualModule';

assert.equal(virtual, 'Virtual!');
assert.equal(notSoVirtual, 'real');
36 changes: 36 additions & 0 deletions test/function/samples/preserve-modules-virtual-modules/_config.js
@@ -0,0 +1,36 @@
const assert = require('assert');

module.exports = {
description: 'Generates actual files for virtual modules when preserving modules',
options: {
input: ['main.js'],
experimentalCodeSplitting: true,
experimentalPreserveModules: true,
plugins: [
{
resolveId(id) {
if (id === '\0virtualModule') return id;
},
load(id) {
if (id !== '\0virtualModule') return null;
return 'export const virtual = "Virtual!";\n';
},
transform(code, id) {
if (id === '\0virtualModule') return null;
return 'import {virtual} from "\0virtualModule";\n' + code;
}
}
]
},
bundle(bundle) {
return bundle
.generate({ format: 'esm' })
.then(generated =>
assert.deepEqual(Object.keys(generated.output), [
'_virtual/_virtualModule',
'lib/lib.js',
'main.js'
])
);
}
};
@@ -0,0 +1 @@
assert.equal(virtual, 'Virtual!');
@@ -0,0 +1,3 @@
import './lib/lib.js';

assert.equal(virtual, 'Virtual!');
3 changes: 2 additions & 1 deletion test/utils.js
Expand Up @@ -160,8 +160,10 @@ function runTestsInDir(dir, runTest) {
const fileNames = sander.readdirSync(dir);

if (fileNames.indexOf('_config.js') >= 0) {
removeOldOutput(dir);
loadConfigAndRunTest(dir, runTest);
} else if (fileNames.indexOf('_actual') >= 0 || fileNames.indexOf('_actual.js') >= 0) {
removeOldOutput(dir);
removeOldTest(dir);
} else {
describe(path.basename(dir), () => {
Expand All @@ -174,7 +176,6 @@ function runTestsInDir(dir, runTest) {
}

function loadConfigAndRunTest(dir, runTest) {
removeOldOutput(dir);
const config = loadConfig(dir + '/_config.js');
if (
config &&
Expand Down

0 comments on commit 7746e0f

Please sign in to comment.