Skip to content

Commit 3851939

Browse files
committed
feat: be smart on onRequire stub's module space
1 parent 33838f8 commit 3851939

2 files changed

Lines changed: 101 additions & 25 deletions

File tree

spec/bundler.spec.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ test('Bundler replaces deps when onRequire returns array', t => {
427427
.then(t.end);
428428
});
429429

430-
test('Bundler supports implementation returned by onRequire', t => {
430+
test('Bundler supports user space implementation returned by onRequire', t => {
431431
const fakeFs = {
432432
'node_modules/dumber-module-loader/dist/index.js': 'dumber-module-loader',
433433
'node_modules/loo/package.json': '{"name":"loo","main":"loo"}',
@@ -469,6 +469,51 @@ test('Bundler supports implementation returned by onRequire', t => {
469469
.then(t.end);
470470
});
471471

472+
test('Bundler supports package space implementation returned by onRequire', t => {
473+
const fakeFs = {
474+
'node_modules/dumber-module-loader/dist/index.js': 'dumber-module-loader',
475+
'node_modules/loo/package.json': '{"name":"loo","main":"loo"}',
476+
'node_modules/loo/loo.js': '',
477+
'node_modules/bar/package.json': '{"name":"bar","main":"bar"}',
478+
'node_modules/bar/bar.js': 'foo',
479+
};
480+
const bundler = createBundler(fakeFs, {
481+
onRequire(moduleId) {
482+
// onRequire can return a Promise to resolve to false, array, or string.
483+
if (moduleId === 'foo') return Promise.resolve("loo"); // "loo" will be processed by mockTrace
484+
}
485+
});
486+
487+
Promise.resolve()
488+
.then(() => bundler.capture({path: 'src/app.js', contents: 'bar', moduleId: 'app'}))
489+
.then(() => bundler.resolve())
490+
.then(() => bundler.bundle())
491+
.then(
492+
bundleMap => {
493+
t.deepEqual(bundleMap, {
494+
'entry-bundle': {
495+
files: [
496+
{contents: 'dumber-module-loader;'},
497+
{contents: 'define.switchToUserSpace();'},
498+
{path: 'src/app.js', contents: "define('app',[\"bar\"],1);", sourceMap: undefined},
499+
{contents: 'define.switchToPackageSpace();'},
500+
{path: 'node_modules/bar/bar.js', contents: "define('bar/bar',[\"foo\"],1);define('bar',['bar/bar'],function(m){return m;});", sourceMap: undefined},
501+
{path: '__on_require__/foo.js', contents: "define('foo',[\"loo\"],1);", sourceMap: undefined},
502+
{path: 'node_modules/loo/loo.js', contents: "define('loo/loo',[],1);define('loo',['loo/loo'],function(m){return m;});", sourceMap: undefined},
503+
{contents: 'define.switchToUserSpace();'},
504+
],
505+
config: {
506+
baseUrl: '/dist',
507+
bundles: {}
508+
}
509+
}
510+
})
511+
},
512+
err => t.fail(err.stack)
513+
)
514+
.then(t.end);
515+
});
516+
472517
test('Bundler swallows onRequire exception', t => {
473518
const fakeFs = {
474519
'node_modules/dumber-module-loader/dist/index.js': 'dumber-module-loader',

src/index.js

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ export default class Bundler {
4242
// turn on injection of css (inject onto html head)
4343
if (opts.injectCss || opts.injectCSS) this._injectCss = true;
4444

45-
this._unitsMap = {};
46-
this._moduleId_done = new Set();
47-
this._moduleIds_todo = new Set();
48-
this._readersMap = {};
45+
this._unitsMap = Object.create(null);
46+
this._user_modules_done = new Set();
47+
this._package_modules_done = new Set();
48+
this._moduleIds_todo = Object.create(null);
49+
this._readersMap = Object.create(null);
4950
this._locator = opts.packageLocator || defaultPackageLocator;
5051

5152
// baseUrl default to "dist"
@@ -79,7 +80,7 @@ export default class Bundler {
7980
}
8081

8182
packageReaderFor(packageConfig) {
82-
if (this._readersMap.hasOwnProperty(packageConfig.name)) {
83+
if (this._readersMap[packageConfig.name]) {
8384
return Promise.resolve(this._readersMap[packageConfig.name]);
8485
}
8586

@@ -119,26 +120,45 @@ export default class Bundler {
119120
);
120121
}
121122

122-
_addToDone(id) {
123+
_didUserModule(id) {
123124
if (typeof id === 'string') {
124-
this._moduleId_done.add(id);
125+
this._user_modules_done.add(id);
125126
} else if (Array.isArray(id)) {
126-
id.forEach(d => this._moduleId_done.add(d));
127+
id.forEach(d => this._user_modules_done.add(d));
128+
}
129+
}
130+
131+
_didPackageModule(id) {
132+
if (typeof id === 'string') {
133+
this._package_modules_done.add(id);
134+
} else if (Array.isArray(id)) {
135+
id.forEach(d => this._package_modules_done.add(d));
136+
}
137+
}
138+
139+
_addTodo(d, isPackageSpace) {
140+
if (this._moduleIds_todo[d]) {
141+
if (isPackageSpace) this._moduleIds_todo[d] = 2;
142+
} else {
143+
this._moduleIds_todo[d] = isPackageSpace ? 2 : 1
127144
}
128145
}
129146

130147
_capture(tracedUnit) {
131148
this._unitsMap[tracedUnit.path] = tracedUnit;
132149

133150
// mark as done.
134-
this._addToDone(tracedUnit.moduleId);
135-
this._addToDone(tracedUnit.defined);
151+
if (tracedUnit.packageName) {
152+
this._didPackageModule(tracedUnit.moduleId);
153+
this._didPackageModule(tracedUnit.defined);
154+
} else {
155+
this._didUserModule(tracedUnit.moduleId);
156+
this._didUserModule(tracedUnit.defined);
157+
}
136158

137-
// mark todo. beware we didn't check whether the id is in _moduleId_done.
159+
// mark todo. beware we didn't check whether the id is in _user/package_modules_done.
138160
// they will be checked during resolve phase.
139-
140-
// console.log('_capture ' + tracedUnit.moduleId + ' deps ' + tracedUnit.deps);
141-
tracedUnit.deps.forEach(d => this._moduleIds_todo.add(d));
161+
tracedUnit.deps.forEach(d => this._addTodo(d, tracedUnit.packageName));
142162

143163
const bundle = this.bundleOf(tracedUnit);
144164
// mark related bundle dirty
@@ -193,7 +213,7 @@ export default class Bundler {
193213
// to some browser replacement.
194214
// e.g. readable-stream/readable -> readable-stream/readable-browser
195215
_ensureNpmAlias(tracedUnit, id) {
196-
if (this._moduleId_done.has(id)) return;
216+
if (this._package_modules_done.has(id)) return;
197217

198218
const defined = tracedUnit.defined;
199219
let toId;
@@ -212,7 +232,7 @@ export default class Bundler {
212232
if (toId !== id && toId !== tracedUnit.packageName) {
213233
const aliasResult = alias(id, toId);
214234
mergeTransformed(tracedUnit, aliasResult);
215-
this._addToDone(aliasResult.defined);
235+
this._didPackageModule(aliasResult.defined);
216236
}
217237
}
218238

@@ -228,21 +248,31 @@ export default class Bundler {
228248
}
229249

230250
resolve() {
251+
// todo is always to be resolved in package space.
231252
let todo = [];
253+
232254
return this._supportInjectCssIfNeeded()
233255
.then(() => this._resolvePrependsAndAppends())
234256
.then(() => this._resolveExplicitDepsIfNeeded())
235257
.then(() => {
236258
const consults = [];
237-
const rawTodo = Array.from(this._moduleIds_todo);
238-
this._moduleIds_todo.clear();
239-
259+
const rawTodo = JSON.parse(JSON.stringify(this._moduleIds_todo));
260+
this._moduleIds_todo = Object.create(null);
240261

241-
// console.log('_moduleIds_todo', this._moduleIds_todo);
242-
rawTodo.forEach(id => {
262+
Object.keys(rawTodo).forEach(id => {
263+
const isPackageSpace = rawTodo[id] === 2;
243264
const parsedId = parse(id);
244265
const possibleIds = nodejsIds(parsedId.bareId);
245-
if (possibleIds.some(id => this._moduleId_done.has(id))) return;
266+
267+
const isAvailabe = possibleIds.some(id => {
268+
if (isPackageSpace) {
269+
return this._package_modules_done.has(id);
270+
} else {
271+
return this._user_modules_done.has(id) || this._package_modules_done.has(id);
272+
}
273+
});
274+
275+
if (isAvailabe) return;
246276

247277
const j = new Promise(resolve => {
248278
resolve(this._onRequire && this._onRequire(parsedId.bareId, parsedId));
@@ -262,7 +292,8 @@ export default class Bundler {
262292
return this.capture({
263293
path: '__on_require__/' + parsedId.bareId + (parsedId.ext ? '' : '.js'),
264294
contents: result,
265-
moduleId: parsedId.bareId
295+
moduleId: parsedId.bareId,
296+
packageName: isPackageSpace ? parsedId.parts[0] : ''
266297
});
267298
}
268299

@@ -318,7 +349,7 @@ export default class Bundler {
318349
return p;
319350
})
320351
.then(() => {
321-
if (this._moduleIds_todo.size) {
352+
if (Object.keys(this._moduleIds_todo).length) {
322353
return this.resolve();
323354
}
324355
});

0 commit comments

Comments
 (0)