Skip to content

Commit 4fdfb00

Browse files
committed
feat: refactor forced main, support forced main in jsDelivr
1 parent a831fad commit 4fdfb00

7 files changed

Lines changed: 177 additions & 69 deletions

File tree

lib/package-file-reader/default.js

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ const {ext} = require('dumber-module-loader/dist/id-utils');
55

66
// default fileReader using nodejs to resolve package
77
module.exports = function(packageConfig, mock) {
8-
let name = packageConfig.name;
8+
const name = packageConfig.name;
99
// decoupling for testing
10-
let _resolve = (mock && mock.resolve) || resolvePackagePath;
11-
let _readFile = (mock && mock.readFile) || fsReadFile;
10+
const _resolve = (mock && mock.resolve) || resolvePackagePath;
11+
const _readFile = (mock && mock.readFile) || fsReadFile;
12+
const dumberForcedMain = packageConfig.main;
1213
let packagePath;
13-
let hardCodedMain = packageConfig.main;
1414

1515
if (packageConfig.location) {
1616
packagePath = packageConfig.location;
@@ -22,49 +22,40 @@ module.exports = function(packageConfig, mock) {
2222
const fp = path.join(packagePath, filePath);
2323
const relativePath = path.relative(path.resolve(), path.resolve(fp)).replace(/\\/g, '/');
2424

25-
if (hardCodedMain && (filePath === 'package.json' || filePath === './package.json')) {
25+
if (filePath === 'package.json' || filePath === './package.json') {
2626
// read version from existing package.json
2727
return _readFile(fp)
2828
.then(buffer => JSON.parse(buffer.toString()))
29-
.then(
30-
meta => meta.version,
31-
() => 'N/A'
32-
)
33-
.then(version => ({
34-
path: relativePath,
35-
contents: JSON.stringify({name, version, main: hardCodedMain})
36-
}));
37-
}
29+
.catch(err => {
30+
warn('Failed to read package.json found at ' + name + '/' + relativePath + ': ' + err.message);
31+
const meta = {name: name, version: 'N/A', main: 'index'};
32+
info('Fall back to ' + JSON.stringify(meta));
33+
return meta;
34+
})
35+
.then(meta => {
36+
// Force name from config. When explicit config, the meta.name might be different.
37+
// e.g. { name: 'foo', location: 'node_modules/bar' }.
38+
if (meta.name !== name) {
39+
meta.name = name;
40+
}
41+
42+
if (dumberForcedMain) {
43+
meta.dumberForcedMain = dumberForcedMain;
44+
}
3845

39-
return _readFile(fp)
40-
.then(
41-
buffer => {
4246
return {
4347
path: relativePath,
44-
contents: buffer.toString(ext(filePath) === '.wasm' ? 'base64' : undefined)
48+
contents: JSON.stringify(meta)
4549
};
46-
},
47-
err => {
48-
if (filePath === 'package.json' || filePath === './package.json') {
49-
warn('No package.json found at ' + name + '/' + relativePath);
50-
const mock = `{"name":${JSON.stringify(name)},"main":"index"}`;
51-
info('Fall back to ' + mock);
52-
return {
53-
path: relativePath,
54-
contents: mock
55-
};
56-
}
57-
throw err;
58-
}
59-
).then(unit => {
60-
if (filePath === 'package.json' || filePath === './package.json') {
61-
const meta = JSON.parse(unit.contents);
62-
if (meta.name !== name) {
63-
meta.name = name;
64-
unit.contents = JSON.stringify(meta);
65-
}
66-
}
67-
return unit;
50+
});
51+
}
52+
53+
return _readFile(fp)
54+
.then(buffer => {
55+
return {
56+
path: relativePath,
57+
contents: buffer.toString(ext(filePath) === '.wasm' ? 'base64' : undefined)
58+
};
6859
});
6960
});
7061
};

lib/package-file-reader/jsDelivr.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function fetchContent(fetchApi, fp) {
99
return fetchApi(fp)
1010
.then(function (response) {
1111
if (!response.ok) {
12-
throw new Error(response.statusText);
12+
throw new Error('Failed to fetch ' + fp + '\n' + response.statusText);
1313
}
1414
if (response.redirected) {
1515
// jsdelivr redirects directory access to a html page that
@@ -41,21 +41,26 @@ module.exports = function(packageConfig, mock) {
4141
const _readFile = fp => fetchContent(_fetch, fp);
4242

4343
const name = packageConfig.name;
44+
let version = packageConfig.version;
45+
const dumberForcedMain = packageConfig.main;
46+
4447
let packagePath;
4548
if (packageConfig.location) {
46-
packagePath = prefix + packageConfig.location;
49+
const m = packageConfig.location.match(/^(.+)@(\d[^@]*)$/);
50+
if (m) {
51+
packagePath = prefix + m[1];
52+
version = m[2];
53+
} else {
54+
packagePath = prefix + packageConfig.location;
55+
}
4756
} else {
4857
packagePath = prefix + name;
4958
}
5059

51-
let version = packageConfig.version;
52-
5360
if (version) {
5461
packagePath += '@' + version;
5562
}
5663

57-
// TODO support hard coded main
58-
5964
return _readFile(packagePath + '/package.json')
6065
.then(json => {
6166
const packageInfo = JSON.parse(json);
@@ -66,14 +71,18 @@ module.exports = function(packageConfig, mock) {
6671
}
6772

6873
return function(filePath) {
74+
if (filePath.startsWith('./')) filePath = filePath.slice(2);
6975
const fp = packagePath + '/' + filePath;
7076
return _readFile(fp).then(text => {
7177
if (filePath === 'package.json' || filePath === './package.json') {
7278
const meta = JSON.parse(text);
7379
if (meta.name !== name) {
7480
meta.name = name;
75-
text = JSON.stringify(meta);
7681
}
82+
if (dumberForcedMain) {
83+
meta.dumberForcedMain = dumberForcedMain;
84+
}
85+
return {path: fp, contents: JSON.stringify(meta)};
7786
}
7887
return {path: fp, contents: text};
7988
});

lib/package-reader.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,20 +200,28 @@ module.exports = class PackageReader {
200200
error(err);
201201
throw err;
202202
}
203+
203204
let metaMain;
204205
// try 1.browser > 2.module > 3.main
205206
// the order is to target browser.
206207
// it probably should use different order for electron app
207208
// for electron 1.module > 2.browser > 3.main
208-
if (typeof metadata.browser === 'string') {
209+
210+
// dumberForcedMain is not in package.json.
211+
// it is the forced main override in dumber config,
212+
// set by package-file-reader/default.js and
213+
// package-file-reader/jsDelivr.js.
214+
if (typeof metadata.dumberForcedMain === 'string') {
215+
metaMain = metadata.dumberForcedMain;
216+
} else if (typeof metadata.browser === 'string') {
209217
// use package.json browser field if possible.
210218
metaMain = metadata.browser;
211219
} else if (typeof metadata.module === 'string' &&
212220
!(metadata.name && metadata.name.startsWith('aurelia-'))) {
213221
// prefer es module format over cjs, just like webpack.
214222
// this improves compatibility with TypeScript.
215-
// ignores aurelia-* core npm packages as their module
216-
// field is pointing to es2015 folder.
223+
// ignores aurelia-* core npm packages as some module
224+
// field might still point to es2015 folder.
217225
metaMain = metadata.module;
218226
} else if (typeof metadata.main === 'string') {
219227
metaMain = metadata.main;

test/package-file-reader/default.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ test('defaultNpmPackageFileReader falls back to main:index when package.json is
1313
.then(
1414
file => {
1515
t.equal(file.path, 'node_modules/foo/package.json');
16-
t.equal(file.contents, '{"name":"foo","main":"index"}');
16+
t.deepEqual(JSON.parse(file.contents), {name:'foo',main:'index',version:'N/A'});
1717
},
1818
err => t.fail(err.stack)
1919
);
@@ -61,7 +61,7 @@ test('defaultNpmPackageFileReader returns fileRead func for package with hard co
6161
.then(
6262
file => {
6363
t.equal(file.path, 'node_modules/foo/package.json');
64-
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'lib/main', version: '2.1.0'});
64+
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'index.js', dumberForcedMain: 'lib/main', version: '2.1.0'});
6565
},
6666
err => t.fail(err.message)
6767
);
@@ -99,17 +99,17 @@ test('defaultNpmPackageFileReader returns fileRead func for package with custom
9999

100100
test('defaultNpmPackageFileReader returns fileRead func for package with custom path and hard coded main', t => {
101101
const defaultFileReader = mockPackageFileReader(buildReadFile({
102-
'packages/foo/package.json': '{"name":"foo","main":"index.js","version":"1.2.0"}'
102+
'packages/bar/package.json': '{"name":"bar","main":"index.js","version":"1.2.0"}'
103103
}));
104104

105-
defaultFileReader({name: 'foo', location: 'packages/foo', main: 'lib/main'})
105+
defaultFileReader({name: 'foo', location: 'packages/bar', main: 'lib/main'})
106106
.then(
107107
fileRead => {
108108
return fileRead('package.json')
109109
.then(
110110
file => {
111-
t.equal(file.path, 'packages/foo/package.json');
112-
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'lib/main', version: '1.2.0'});
111+
t.equal(file.path, 'packages/bar/package.json');
112+
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'index.js', dumberForcedMain: 'lib/main', version: '1.2.0'});
113113
},
114114
err => t.fail(err.message)
115115
);
@@ -131,7 +131,7 @@ test('defaultNpmPackageFileReader returns fileRead func for package with custom
131131
.then(
132132
file => {
133133
t.equal(file.path, 'packages/foo/package.json');
134-
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'lib/main', version: 'N/A'});
134+
t.deepEqual(JSON.parse(file.contents), {name: 'foo', main: 'index', dumberForcedMain: 'lib/main', version: 'N/A'});
135135
},
136136
err => t.fail(err.message)
137137
);

test/package-file-reader/jsDelivr.spec.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,64 @@ test('jsDelivrNpmPackageFileReader reads .wasm file to base64 string', t => {
187187
},
188188
err => t.fail(err)
189189
).then(() => t.end());
190-
});
190+
});
191+
192+
test('jsDelivrNpmPackageFileReader returns fileRead func for package with hard coded main', t => {
193+
fileReader({name: 'foo', main: 'lib/main'})
194+
.then(
195+
fileRead => {
196+
return fileRead('package.json')
197+
.then(
198+
file => {
199+
t.equal(file.path, '//cdn.jsdelivr.net/npm/foo@1.0.1/package.json');
200+
const info = JSON.parse(file.contents);
201+
t.equal(info.name, 'foo');
202+
t.equal(info.version, '1.0.1');
203+
t.equal(info.dumberForcedMain, 'lib/main');
204+
},
205+
err => t.fail(err.message)
206+
);
207+
},
208+
err => t.fail(err)
209+
).then(() => t.end());
210+
});
211+
212+
test('jsDelivrNpmPackageFileReader returns fileRead func for package with custom path and hard coded main', t => {
213+
fileReader({name: 'foo', location: 'bar@2.0.0-rc1', main: 'lib/main'})
214+
.then(
215+
fileRead => {
216+
return fileRead('package.json')
217+
.then(
218+
file => {
219+
t.equal(file.path, '//cdn.jsdelivr.net/npm/bar@2.0.0-rc1/package.json');
220+
const info = JSON.parse(file.contents);
221+
t.equal(info.name, 'foo');
222+
t.equal(info.version, '2.0.0-rc1');
223+
t.equal(info.dumberForcedMain, 'lib/main');
224+
},
225+
err => t.fail(err.message)
226+
);
227+
},
228+
err => t.fail(err)
229+
).then(() => t.end());
230+
});
231+
232+
test('jsDelivrNpmPackageFileReader returns fileRead func for package with custom path, version and hard coded main', t => {
233+
fileReader({name: 'foo', location: 'bar', version: '2.0.0-rc1', main: 'lib/main'})
234+
.then(
235+
fileRead => {
236+
return fileRead('./package.json')
237+
.then(
238+
file => {
239+
t.equal(file.path, '//cdn.jsdelivr.net/npm/bar@2.0.0-rc1/package.json');
240+
const info = JSON.parse(file.contents);
241+
t.equal(info.name, 'foo');
242+
t.equal(info.version, '2.0.0-rc1');
243+
t.equal(info.dumberForcedMain, 'lib/main');
244+
},
245+
err => t.fail(err.message)
246+
);
247+
},
248+
err => t.fail(err)
249+
).then(() => t.end());
250+
});

test/package-reader.spec.js

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,30 @@ test('packageReader reads resource file which is actually main', t => {
166166
});
167167
});
168168

169-
test('packageReader rejects invalid package.json', t => {
169+
test('packageReader tolerate invalid package.json', t => {
170170
getReader('foo', {
171171
'node_modules/foo/package.json': '{"name":"foo", "main": "index"',
172172
'node_modules/foo/index.js': "lorem"
173173
}).then(r => {
174-
r.readMain().then(
175-
() => {
176-
t.fail('should not pass')
174+
r.readResource('index').then(
175+
unit => {
176+
t.equal(r.version, 'N/A');
177+
t.deepEqual(unit, {
178+
path: 'node_modules/foo/index.js',
179+
contents: 'lorem',
180+
moduleId: 'foo/index',
181+
packageName: 'foo',
182+
packageMainPath: 'index.js',
183+
alias: 'foo',
184+
sourceMap: undefined
185+
});
186+
187+
t.equal(r.name, 'foo');
188+
t.equal(r.mainPath, 'index.js');
189+
t.deepEqual(r.browserReplacement, {});
177190
},
178191
err => {
179-
t.pass(err.message);
192+
t.fail(err.message);
180193
}
181194
).then(t.end);
182195
});
@@ -239,7 +252,7 @@ test('packageReader reads module over main field', t => {
239252
});
240253
});
241254

242-
test('packageReader reads browser over main/module field', t => {
255+
test('packageReader reads browser over module/main field', t => {
243256
getReader('foo', {
244257
'node_modules/foo/package.json': '{"name":"foo", "browser": "br", "module": "es", "main": "index"}',
245258
'node_modules/foo/index.js': "lorem",
@@ -269,6 +282,37 @@ test('packageReader reads browser over main/module field', t => {
269282
});
270283
});
271284

285+
test('packageReader reads dumberForcedMain over browser/module/main field', t => {
286+
getReader('foo', {
287+
'node_modules/foo/package.json': '{"name":"foo", "browser": "br", "module": "es", "main": "index", "dumberForcedMain": "hc"}',
288+
'node_modules/foo/index.js': "lorem",
289+
'node_modules/foo/es.js': 'es',
290+
'node_modules/foo/br.js': 'br',
291+
'node_modules/foo/hc.js': 'hc',
292+
}).then(r => {
293+
r.readMain().then(
294+
unit => {
295+
t.deepEqual(unit, {
296+
path: 'node_modules/foo/hc.js',
297+
contents: 'hc',
298+
moduleId: 'foo/hc',
299+
packageName: 'foo',
300+
packageMainPath: 'hc.js',
301+
alias: 'foo',
302+
sourceMap: undefined
303+
});
304+
305+
t.equal(r.name, 'foo');
306+
t.equal(r.mainPath, 'hc.js');
307+
t.deepEqual(r.browserReplacement, {});
308+
},
309+
err => {
310+
t.fail(err.message);
311+
}
312+
).then(t.end);
313+
});
314+
});
315+
272316
test('packageReader reads main file with explicit ext', t => {
273317
getReader('foo.js', {
274318
'node_modules/foo.js/package.json': '{"name":"foo.js", "main": "./main.js"}',
@@ -345,10 +389,7 @@ test('packageReader reads implicit main file', t => {
345389
t.equal(r.mainPath, 'lib/index.js');
346390
t.deepEqual(r.browserReplacement, {});
347391
},
348-
err => {
349-
console.log('err', err);
350-
t.fail(err.message);
351-
}
392+
err => t.fail(err.message)
352393
);
353394
}).then(t.end);
354395
});

0 commit comments

Comments
 (0)