Skip to content

Commit 6309efe

Browse files
committed
feat: support duplicated module name in both user and package spaces
closes #5
1 parent de6811d commit 6309efe

32 files changed

Lines changed: 577 additions & 126 deletions

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
"dist"
1313
],
1414
"scripts": {
15-
"lint": "eslint src spec",
15+
"lint": "eslint src test",
1616
"preversion": "npm test",
1717
"version": "standard-changelog && git add CHANGELOG.md",
1818
"postversion": "git push && git push --tags && npm publish",
1919
"pretest": "npm run lint",
2020
"prepare": "del-cli dist && babel src -d dist",
21-
"nodejs-test": "tape -r @babel/register \"spec/**/*.spec.js\" | tap-dot",
22-
"coverage": "nyc --reporter=lcov tape -r @babel/register \"spec/**/*.spec.js\" | tap-dot",
23-
"browser-test": "browserify -t babelify spec/all-browser-spec.js | tape-run | tap-dot",
21+
"nodejs-test": "tape -r @babel/register \"test/**/*.spec.js\" | tap-dot",
22+
"coverage": "nyc --reporter=lcov tape -r @babel/register \"test/**/*.spec.js\" | tap-dot",
23+
"browser-test": "browserify -t babelify test/all-browser-spec.js | tape-run | tap-dot",
2424
"test": "npm run nodejs-test && npm run browser-test"
2525
},
2626
"repository": {

src/index.js

Lines changed: 100 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import trace from './trace';
2-
import {cleanPath, parse, nodejsIds, mapId, resolveModuleId} from 'dumber-module-loader/dist/id-utils';
2+
import {cleanPath, parse, mapId} from 'dumber-module-loader/dist/id-utils';
33
import alias from './transformers/alias';
44
import defaultPackageFileReader from './package-file-reader/default';
55
import PackageReader from './package-reader';
@@ -10,6 +10,8 @@ import {generateHash, stripJsExtension, resolvePackagePath, contentOrFile} from
1010
import * as cache from './cache/default';
1111
import path from 'path';
1212
import mergeTransformed from './transformers/merge';
13+
import ModulesDone from './modules-done';
14+
import ModulesTodo from './modules-todo';
1315

1416
// Bundler does
1517
// 1. capture: capture units (unit is a file like object plus meta data)
@@ -57,8 +59,10 @@ export default class Bundler {
5759
this._paths = _paths;
5860

5961
this._unitsMap = {};
60-
this._moduleId_done = new Set();
61-
this._moduleIds_todo = new Set();
62+
63+
this._modules_done = new ModulesDone();
64+
this._modules_todo = new ModulesTodo(this._modules_done);
65+
6266
this._readersMap = {};
6367
this._fileReader = opts.packageFileReader || defaultPackageFileReader;
6468

@@ -86,6 +90,10 @@ export default class Bundler {
8690
// persist bundles in watch mode
8791
this._bundles = {};
8892
this._inWatchMode = false;
93+
94+
this._onAcquire = this._onAcquire.bind(this);
95+
this._supportInjectCssIfNeeded = this._supportInjectCssIfNeeded.bind(this);
96+
this._resolveExplicitDepsIfNeeded = this._resolveExplicitDepsIfNeeded.bind(this);
8997
}
9098

9199
clearCache() {
@@ -158,32 +166,13 @@ export default class Bundler {
158166
);
159167
}
160168

161-
_addToDone(id) {
162-
if (typeof id === 'string') {
163-
this._moduleId_done.add(id);
164-
} else if (Array.isArray(id)) {
165-
id.forEach(d => this._moduleId_done.add(d));
166-
}
167-
}
168-
169169
_capture(tracedUnit) {
170170
this._unitsMap[tracedUnit.path] = tracedUnit;
171171

172172
// mark as done.
173-
this._addToDone(tracedUnit.moduleId);
174-
this._addToDone(tracedUnit.defined);
175-
176-
// mark todo. beware we didn't check whether the id is in _moduleId_done.
177-
// they will be checked during resolve phase.
178-
tracedUnit.deps.forEach(d => {
179-
const parsedId = parse(resolveModuleId(tracedUnit.moduleId, d));
180-
if (!parsedId.prefix && parsedId.ext === '.css') {
181-
this._needCssInjection = true;
182-
}
183-
// ignore relative dep on local source
184-
if (!tracedUnit.packageName && d.startsWith('.')) return;
185-
this._moduleIds_todo.add(parsedId.cleanId);
186-
});
173+
this._modules_done.addUnit(tracedUnit);
174+
// process deps.
175+
this._modules_todo.process(tracedUnit);
187176

188177
const bundle = this.bundleOf(tracedUnit);
189178
// mark related bundle dirty
@@ -237,8 +226,6 @@ export default class Bundler {
237226
// to some browser replacement.
238227
// e.g. readable-stream/readable -> readable-stream/readable-browser
239228
_ensureNpmAlias(tracedUnit, id) {
240-
if (this._moduleId_done.has(id)) return;
241-
242229
const defined = tracedUnit.defined;
243230
let toId;
244231
if (Array.isArray(defined)) {
@@ -256,12 +243,14 @@ export default class Bundler {
256243
if (toId !== id && toId !== tracedUnit.packageName) {
257244
const aliasResult = alias(id, toId);
258245
mergeTransformed(tracedUnit, aliasResult);
259-
this._addToDone(aliasResult.defined);
246+
this._modules_done.addUnit(tracedUnit);
260247
}
261248
}
262249

263250
_supportInjectCssIfNeeded() {
264-
if (!this._needCssInjection || !this._injectCss || this._isInjectCssTurnedOn) return Promise.resolve();
251+
if (!this._modules_todo.needCssInjection || !this._injectCss || this._isInjectCssTurnedOn) {
252+
return Promise.resolve();
253+
}
265254
this._isInjectCssTurnedOn = true;
266255

267256
return this.capture({
@@ -272,107 +261,96 @@ export default class Bundler {
272261
}
273262

274263
resolve() {
275-
let todo = [];
276264
return this._resolvePrependsAndAppends()
277-
.then(() => this._resolveExplicitDepsIfNeeded())
278-
.then(() => this._supportInjectCssIfNeeded())
279-
.then(() => {
280-
const consults = [];
281-
const rawTodo = Array.from(this._moduleIds_todo);
282-
this._moduleIds_todo.clear();
283-
284-
rawTodo.forEach(id => {
285-
const parsedId = parse(mapId(id, this._paths));
286-
287-
if (parsedId.prefix &&
288-
parsedId.prefix !== 'text!' &&
289-
parsedId.prefix !== 'json!' &&
290-
parsedId.prefix !== 'raw!') {
291-
// Trace any unknown plugin module.
292-
// For simplicity, push it to next resolve cycle.
293-
this._moduleIds_todo.add(parsedId.prefix.slice(0, -1));
265+
.then(this._resolveExplicitDepsIfNeeded)
266+
.then(() => this._modules_todo.acquire(this._onAcquire))
267+
.then(this._supportInjectCssIfNeeded)
268+
.then(() => {
269+
// recursively resolve
270+
if (this._modules_todo.hasTodo()) {
271+
return this.resolve();
294272
}
295-
296-
const possibleIds = nodejsIds(parsedId.bareId);
297-
if (possibleIds.some(id => this._moduleId_done.has(id))) return;
298-
299-
const j = new Promise(resolve => {
300-
resolve(this._onRequire && this._onRequire(parsedId.bareId, parsedId));
301-
}).then(
302-
result => {
303-
// ignore this module id
304-
if (result === false) return;
305-
306-
// require other module ids instead
307-
if (Array.isArray(result) && result.length) {
308-
result.forEach(d => todo.push(parse(d)));
309-
return;
310-
}
311-
312-
// got full content of this module
313-
if (typeof result === 'string') {
314-
return this.capture({
315-
path: '__on_require__/' + parsedId.bareId + (parsedId.ext ? '' : '.js'),
316-
contents: result,
317-
moduleId: parsedId.bareId,
318-
packageName: parsedId.parts[0]
319-
});
320-
}
321-
322-
// process normally if result is not recognizable
323-
todo.push(parsedId);
324-
},
325-
// proceed normally after error
326-
err => {
327-
error('onRequire call failed for ' + parsedId.bareId);
328-
error(err);
329-
todo.push(parsedId);
330-
}
331-
);
332-
consults.push(j);
333273
});
274+
}
334275

335-
if (consults.length) return Promise.all(consults);
336-
})
337-
.then(() => {
338-
let p = Promise.resolve();
339-
340-
todo.forEach(td => {
341-
const bareId = td.bareId;
342-
const packageName = td.parts[0];
343-
const resource = bareId.slice(packageName.length + 1);
344-
345-
const stub = stubModule(bareId);
346-
if (stub) info('Stub module ' + bareId);
347-
348-
if (typeof stub === 'string') {
349-
p = p.then(() => this.capture({
350-
// not a real file path
351-
path:'__stub__/' + bareId + '.js',
352-
contents: stub,
353-
moduleId: bareId,
354-
packageName
355-
}));
356-
} else {
357-
p = p.then(() => this.packageReaderFor(stub || {name: packageName}))
358-
.then(reader => resource ? reader.readResource(resource) : reader.readMain())
359-
.then(unit => this.capture(unit))
360-
.then(tracedUnit => {
361-
this._ensureNpmAlias(tracedUnit, bareId);
362-
})
363-
.catch(err => {
364-
error('Resolving failed for module ' + bareId);
365-
error(err);
276+
// trace missing dep
277+
_onAcquire(id, opts) {
278+
const checkUserSpace = opts.user;
279+
const checkPackageSpace = opts.package;
280+
const requiredBy = opts.requiredBy;
281+
const parsedId = parse(mapId(id, this._paths));
282+
283+
// TODO add a callback point to fillup missing local dep.
284+
// This is needed by dumberify.
285+
if (checkUserSpace && !checkPackageSpace) {
286+
// detected missing local dep
287+
warn(`local dependency ${parsedId.bareId} (requiredBy ${requiredBy.join(', ')}) is missing`);
288+
}
289+
290+
return new Promise(resolve => {
291+
resolve(this._onRequire && this._onRequire(parsedId.bareId, parsedId));
292+
}).then(
293+
result => {
294+
// ignore this module id
295+
if (result === false) return true;
296+
297+
// require other module ids instead
298+
if (Array.isArray(result) && result.length) {
299+
this._modules_todo.process({
300+
moduleId: parsedId.bareId,
301+
packageName: (!checkUserSpace && checkPackageSpace) ? parsedId.parts[0] : undefined,
302+
deps: result
366303
});
304+
return true;
367305
}
368-
});
369306

370-
return p;
371-
})
372-
.then(() => {
373-
if (this._moduleIds_todo.size) {
374-
return this.resolve();
307+
// got full content of this module
308+
if (typeof result === 'string') {
309+
return this.capture({
310+
path: '__on_require__/' + parsedId.bareId + (parsedId.ext ? '' : '.js'),
311+
contents: result,
312+
moduleId: parsedId.bareId,
313+
packageName: parsedId.parts[0]
314+
}).then(() => true);
315+
}
316+
317+
// process normally if result is not recognizable
318+
},
319+
// proceed normally after error
320+
err => {
321+
error('onRequire call failed for ' + parsedId.bareId);
322+
error(err);
375323
}
324+
).then(didRequire => {
325+
if (didRequire === true) return;
326+
327+
const bareId = parsedId.bareId;
328+
const packageName = parsedId.parts[0];
329+
const resource = bareId.slice(packageName.length + 1);
330+
331+
const stub = stubModule(bareId);
332+
if (stub) info('Stub module ' + bareId);
333+
334+
if (typeof stub === 'string') {
335+
return this.capture({
336+
// not a real file path
337+
path:'__stub__/' + bareId + '.js',
338+
contents: stub,
339+
moduleId: bareId,
340+
packageName
341+
});
342+
}
343+
344+
return this.packageReaderFor(stub || {name: packageName})
345+
.then(reader => resource ? reader.readResource(resource) : reader.readMain())
346+
.then(unit => this.capture(unit))
347+
.then(tracedUnit => {
348+
this._ensureNpmAlias(tracedUnit, bareId);
349+
})
350+
.catch(err => {
351+
error('Resolving failed for module ' + bareId);
352+
error(err);
353+
});
376354
});
377355
}
378356

src/modules-done.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Retain all traced module ids in bundler
2+
import {nodejsIds} from 'dumber-module-loader/dist/id-utils';
3+
4+
export default class ModulesDone {
5+
constructor() {
6+
this.userIds = new Set();
7+
this.packageIds = new Set();
8+
}
9+
10+
addUnit(unit) {
11+
this.add(unit.moduleId, !!unit.packageName);
12+
this.add(unit.defined, !!unit.packageName);
13+
}
14+
15+
add(id, inPackageSpace) {
16+
if (typeof id === 'string') {
17+
if (inPackageSpace) {
18+
this.packageIds.add(id);
19+
} else {
20+
this.userIds.add(id);
21+
}
22+
} else if (Array.isArray(id)) {
23+
id.forEach(d => this.add(d, inPackageSpace));
24+
}
25+
}
26+
27+
// incoming id is a parsed bareId
28+
has(id, checkUserSpace, checkPackageSpace) {
29+
const possibleIds = nodejsIds(id);
30+
31+
return possibleIds.some(id => {
32+
let inUserSpace = checkUserSpace && this.userIds.has(id);
33+
let inPackageSpace = checkPackageSpace && this.packageIds.has(id);
34+
return inUserSpace || inPackageSpace;
35+
});
36+
}
37+
}

0 commit comments

Comments
 (0)