Skip to content

Commit 3043967

Browse files
WIP
1 parent bbe841c commit 3043967

File tree

15 files changed

+212
-107
lines changed

15 files changed

+212
-107
lines changed

packages/wmr/src/lib/npm-middleware.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ export function npmEtagCache() {
66
const url = new URL(req.url, 'https://localhost');
77
let id = path.posix.normalize(url.pathname);
88

9-
if (!id.startsWith('/@npm/@id/')) {
9+
if (!id.startsWith('/@npm/')) {
1010
return next();
1111
}
1212

13-
id = id.slice('/@npm/@id/'.length);
13+
id = id.slice('/@npm/'.length);
1414
if (!isValidPackageName(id)) {
1515
return next();
1616
}

packages/wmr/src/lib/plugins.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import htmPlugin from '../plugins/htm-plugin.js';
23
import sucrasePlugin from '../plugins/sucrase-plugin.js';
34
import wmrPlugin from '../plugins/wmr/plugin.js';
@@ -28,6 +29,7 @@ import { lessPlugin } from '../plugins/less-plugin.js';
2829
import { workerPlugin } from '../plugins/worker-plugin.js';
2930
import { npmPlugin } from '../plugins/npm-plugin/index.js';
3031
import tsConfigPathsPlugin from '../plugins/tsconfig-paths-plugin.js';
32+
import { getNpmPlugins } from '../plugins/npm-plugin/npm-bundle.js';
3133

3234
/**
3335
* @param {import("wmr").Options & { isIIFEWorker?: boolean}} options
@@ -51,6 +53,14 @@ export function getPlugins(options) {
5153
registry
5254
} = options;
5355

56+
const npmCacheDir = path.join(cwd, '.cache', '@npm');
57+
58+
/**
59+
* Map of package name to folder on disk
60+
* @type {Map<string, string>}
61+
*/
62+
const resolutionCache = new Map();
63+
5464
// Plugins are pre-sorted
5565
let split = plugins.findIndex(p => p.enforce === 'post');
5666
if (split === -1) split = plugins.length;
@@ -102,7 +112,20 @@ export function getPlugins(options) {
102112
// Only transpile CommonJS in node_modules and explicit .cjs files:
103113
include: /(^npm\/|[/\\]node_modules[/\\]|\.cjs$)/
104114
}),
105-
npmPlugin({ cwd, autoInstall, production, registryUrl: registry }),
115+
116+
...(production
117+
? getNpmPlugins({
118+
autoInstall,
119+
production,
120+
cacheDir: npmCacheDir,
121+
cwd,
122+
registryUrl: registry,
123+
resolutionCache,
124+
browserReplacement: new Map()
125+
})
126+
: []),
127+
!production &&
128+
npmPlugin({ cwd, cacheDir: npmCacheDir, autoInstall, production, registryUrl: registry, resolutionCache, alias }),
106129
resolveExtensionsPlugin({
107130
extensions: ['.ts', '.tsx', '.js', '.cjs'],
108131
index: true

packages/wmr/src/plugins/npm-plugin/commonjs.js

+21-13
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,28 @@ export function commonjsPlugin({ production }) {
2020

2121
if (!hasCjsKeywords && hasEsmKeywords) return;
2222

23-
const result = transform(code, {
24-
parse: this.parse,
25-
plugins: [
26-
replace({ 'process.env.NODE_ENV': 'development', __DEV__: !!production }),
27-
optimize(),
28-
commonjsToEsm()
29-
]
30-
});
23+
let result;
24+
try {
25+
result = transform(code, {
26+
parse: this.parse,
27+
plugins: [
28+
replace({ 'process.env.NODE_ENV': 'development', __DEV__: !!production }),
29+
optimize(),
30+
commonjsToEsm()
31+
]
32+
});
3133

32-
return {
33-
code: result.code,
34-
// FIXME: Sourcemap
35-
map: null
36-
};
34+
if (code !== result.code) {
35+
console.log('CJS', id, result.code);
36+
return {
37+
code: result.code,
38+
map: result.map
39+
};
40+
}
41+
} catch (err) {
42+
console.log('ERR', code);
43+
throw err;
44+
}
3745
}
3846
};
3947
}

packages/wmr/src/plugins/npm-plugin/index.js

+8-12
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ const log = debug('npm', 196);
1414
* @param {boolean} options.autoInstall
1515
* @param {boolean} options.production
1616
* @param {string} options.registryUrl
17+
* @param {string} options.cacheDir
18+
* @param {Record<string, string>} options.alias
19+
* @param {Map<string, string>} options.resolutionCache
1720
* @returns {import('rollup').Plugin}
1821
*/
19-
export function npmPlugin({ cwd, autoInstall, production, registryUrl }) {
22+
export function npmPlugin({ cwd, cacheDir, autoInstall, production, registryUrl, resolutionCache, alias }) {
2023
const PREFIX = '\0npm:';
2124

22-
const cacheDir = path.join(cwd, '.cache', '@npm');
23-
2425
/** @type {Map<string, { code: string, map: any }>} */
2526
const chunkCache = new Map();
2627

@@ -38,10 +39,11 @@ export function npmPlugin({ cwd, autoInstall, production, registryUrl }) {
3839
* @param {object} options
3940
* @param {string} options.packageName
4041
* @param {string} options.diskCacheDir
42+
* @param {Record<string, string>} options.alias
4143
* @param {Map<string, string>} options.resolutionCache
4244
* @returns {Promise<{ code: string, map: any }>}
4345
*/
44-
async function bundleNpmPackage(id, { packageName, diskCacheDir, resolutionCache }) {
46+
async function bundleNpmPackage(id, { packageName, diskCacheDir, resolutionCache, alias }) {
4547
const deferred = new Deferred();
4648
pending.set(id, deferred);
4749

@@ -53,7 +55,7 @@ export function npmPlugin({ cwd, autoInstall, production, registryUrl }) {
5355
}
5456

5557
log(kl.dim(`bundle: `) + kl.cyan(id));
56-
let result = await npmBundle(id, { autoInstall, production, cacheDir, cwd, resolutionCache, registryUrl });
58+
let result = await npmBundle(id, { autoInstall, production, cacheDir, cwd, resolutionCache, registryUrl, alias });
5759

5860
await Promise.all(
5961
result.output.map(async chunkOrAsset => {
@@ -88,12 +90,6 @@ export function npmPlugin({ cwd, autoInstall, production, registryUrl }) {
8890
return chunk;
8991
}
9092

91-
/**
92-
* Map of package name to folder on disk
93-
* @type {Map<string, string>}
94-
*/
95-
const resolutionCache = new Map();
96-
9793
return {
9894
name: 'npm-plugin',
9995
async resolveId(id) {
@@ -114,7 +110,7 @@ export function npmPlugin({ cwd, autoInstall, production, registryUrl }) {
114110
log(kl.dim(`asset ${id}, wait for bundling `) + kl.cyan(name));
115111
const diskCacheDir = path.join(cacheDir, escapeFilename(name));
116112
if (!deferred) {
117-
await bundleNpmPackage(name, { packageName: name, diskCacheDir, resolutionCache });
113+
await bundleNpmPackage(name, { packageName: name, diskCacheDir, resolutionCache, alias });
118114
} else {
119115
await deferred;
120116
}

packages/wmr/src/plugins/npm-plugin/npm-bundle.js

+54-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { npmAutoInstall } from './npm-auto-install.js';
1111
import jsonPlugin from '../json-plugin.js';
1212
import sizeWarningPlugin from './size-warning-plugin.js';
1313
import { onWarn } from '../../lib/output-utils.js';
14+
import aliasPlugin from '../aliases-plugin.js';
1415

1516
/** @type {import('rollup').WarningHandlerWithDefault} */
1617
function customWarn(warning) {
@@ -22,6 +23,41 @@ function customWarn(warning) {
2223
onWarn(warning);
2324
}
2425

26+
/**
27+
* @param {object} options
28+
* @param {boolean} options.autoInstall
29+
* @param {boolean} options.production
30+
* @param {string} options.cacheDir
31+
* @param {string} options.cwd
32+
* @param {string} options.registryUrl
33+
* @param {string} [options.requestId]
34+
* @param {Map<string, string>} options.resolutionCache
35+
* @param {Map<string, string>} options.browserReplacement
36+
* @returns {import('rollup').Plugin[]}
37+
*/
38+
export function getNpmPlugins({
39+
autoInstall,
40+
production,
41+
cacheDir,
42+
cwd,
43+
resolutionCache,
44+
registryUrl,
45+
browserReplacement,
46+
requestId
47+
}) {
48+
// @ts-ignore
49+
return [
50+
browserFieldPlugin({ browserReplacement }),
51+
!production && requestId && npmExternalDeps({ requestId }),
52+
!process.env.DISABLE_LOCAL_NPM && npmLocalPackage({ root: cwd }),
53+
autoInstall && npmAutoInstall({ cacheDir, registryUrl }),
54+
npmLoad({ browserReplacement, resolutionCache, production }),
55+
commonjsPlugin({ production }),
56+
subPackageLegacy(),
57+
sizeWarningPlugin()
58+
].filter(Boolean);
59+
}
60+
2561
/**
2662
* @param {string} requestId
2763
* @param {object} options
@@ -30,29 +66,38 @@ function customWarn(warning) {
3066
* @param {string} options.cacheDir
3167
* @param {string} options.cwd
3268
* @param {string} options.registryUrl
69+
* @param {Record<string, string>} options.alias
3370
* @param {Map<string, string>} options.resolutionCache
3471
*/
35-
export async function npmBundle(requestId, { autoInstall, production, cacheDir, cwd, resolutionCache, registryUrl }) {
72+
export async function npmBundle(
73+
requestId,
74+
{ autoInstall, production, cacheDir, cwd, resolutionCache, registryUrl, alias }
75+
) {
3676
const meta = getPackageInfo(requestId);
3777
const pkgName = meta.name;
3878

3979
/** @type {Map<string, string>} */
4080
const browserReplacement = new Map();
4181

82+
console.log('REQUEST', requestId);
83+
4284
const bundle = await rollup.rollup({
4385
input: requestId,
4486
external: [...builtinModules],
4587
onwarn: customWarn,
4688
plugins: [
47-
browserFieldPlugin({ browserReplacement }),
48-
npmExternalDeps({ requestId }),
49-
!process.env.DISABLE_LOCAL_NPM && npmLocalPackage({ root: cwd }),
50-
autoInstall && npmAutoInstall({ cacheDir, registryUrl }),
51-
npmLoad({ browserReplacement, resolutionCache }),
89+
aliasPlugin({ alias }),
5290
jsonPlugin({ root: cwd }),
53-
commonjsPlugin({ production }),
54-
subPackageLegacy({ rootId: requestId }),
55-
sizeWarningPlugin()
91+
...getNpmPlugins({
92+
requestId,
93+
autoInstall,
94+
production,
95+
cacheDir,
96+
cwd,
97+
resolutionCache,
98+
registryUrl,
99+
browserReplacement
100+
})
56101
]
57102
});
58103

packages/wmr/src/plugins/npm-plugin/npm-load.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ const log = debug('npm-load');
1111
* @param {object} options
1212
* @param {Map<string, string>} options.browserReplacement
1313
* @param {Map<string, string>} options.resolutionCache
14+
* @param {boolean} options.production
1415
* @returns {import('rollup').Plugin}
1516
*/
16-
export function npmLoad({ browserReplacement, resolutionCache }) {
17+
export function npmLoad({ browserReplacement, resolutionCache, production }) {
1718
return {
1819
name: 'npm-load',
1920
async resolveId(id, importer) {
@@ -82,7 +83,7 @@ export function npmLoad({ browserReplacement, resolutionCache }) {
8283
const subPkg = await readJson(path.join(modDir, pathname, 'package.json'));
8384
entry = path.join(modDir, pathname, subPkg.module || subPkg.main || 'index.js');
8485
} catch (err) {
85-
entry = pathname;
86+
entry = path.join(modDir, pathname);
8687
}
8788
}
8889
}
@@ -93,7 +94,7 @@ export function npmLoad({ browserReplacement, resolutionCache }) {
9394

9495
// Some packages use non-js entry files, but rollup only supports js.
9596
// So we expect other plugins to handle assets.
96-
if (!/\.(?:[tj]sx?|[cm]js|[mc]ts)/.test(path.extname(entry))) {
97+
if (!production && !/\.(?:[tj]sx?|[cm]js|[mc]ts)/.test(path.extname(entry))) {
9798
return {
9899
code: '',
99100
map: null,

packages/wmr/src/plugins/npm-plugin/sub-package-legacy.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import { isDirectory } from '../../lib/fs-utils.js';
55
/**
66
* Legacy way of defining package entry points before the
77
* "export" field in `package.json` was a thing.
8-
* @param {object} options
9-
* @param {string} options.rootId
108
* @returns {import('rollup').Plugin}
119
*/
12-
export function subPackageLegacy({ rootId }) {
10+
export function subPackageLegacy() {
1311
return {
1412
name: 'legacy-sub-package',
1513
async resolveId(id, importer) {

packages/wmr/src/plugins/npm-plugin/utils.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,11 @@ export function isValidPackageName(id) {
4949
!/node_modules|favicon\.ico/.test(id) &&
5050
// Must not be a built-in node module
5151
!builtins.has(id) &&
52-
// Must be lowercase
53-
id.toLowerCase() === id &&
52+
// Package name must be lowercase and contain path segment
53+
// if scoped
54+
/^(?:@[^/A-Z]+\/[^/A-Z]+|[^/A-Z]+)/.test(id) &&
5455
// Must not contain special characters
55-
!/[~'!()*;,?:&=+$]/.test(id) &&
56-
// Must contain a second path segment if scoped
57-
((id[0] === '@' && id.indexOf('/') > 0) || true);
56+
!/[~'!()*;,?:&=+$]/.test(id);
5857

5958
return isValid;
6059
}

packages/wmr/src/wmr-middleware.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export default function wmrMiddleware(options) {
246246

247247
// Workaround for transform forcing extensionless ids to be
248248
// non-js
249-
let hasIdPrefix = false;
249+
let isVirtual = false;
250250

251251
let file = '';
252252
let id = path;
@@ -257,14 +257,18 @@ export default function wmrMiddleware(options) {
257257
// Path for virtual modules that refer to an unprefixed id.
258258
if (path.startsWith('/@id/')) {
259259
// Virtual paths have no exact file match, so we don't set `file`
260-
hasIdPrefix = true;
260+
isVirtual = true;
261261
id = path.slice('/@id/'.length);
262262

263263
// Add back leading slash if it was part of the virtual id.
264264
// Example: `/@windicss/windi.css`
265265
if (req.path.startsWith('/@id//')) {
266266
id = '/' + id;
267267
}
268+
} else if (path.startsWith('/@npm/')) {
269+
// Virtual paths have no exact file match, so we don't set `file`
270+
id = path.slice('/@npm/'.length);
271+
isVirtual = true;
268272
} else if (path.startsWith('/@alias/')) {
269273
id = posix.normalize(path.slice('/@alias/'.length));
270274

@@ -279,7 +283,7 @@ export default function wmrMiddleware(options) {
279283

280284
if (path.startsWith('/@id/')) {
281285
// Virtual paths have no exact file match, so we don't set `file`
282-
hasIdPrefix = true;
286+
isVirtual = true;
283287
path = path.slice('/@id'.length);
284288
}
285289

@@ -296,7 +300,7 @@ export default function wmrMiddleware(options) {
296300
// Normalize the cacheKey so it matches what will be in the WRITE_CACHE, where we store in native paths
297301
cacheKey = cacheKey.split(posix.sep).join(sep);
298302

299-
if (!hasIdPrefix) {
303+
if (!isVirtual) {
300304
id = `./${id}`;
301305
}
302306

@@ -331,7 +335,7 @@ export default function wmrMiddleware(options) {
331335
} else if (queryParams.has('asset')) {
332336
cacheKey += '?asset';
333337
transform = TRANSFORMS.asset;
334-
} else if (prefix || hasIdPrefix || isModule || /\.([mc]js|[tj]sx?)$/.test(file) || STYLE_REG.test(file)) {
338+
} else if (prefix || isVirtual || isModule || /\.([mc]js|[tj]sx?)$/.test(file) || STYLE_REG.test(file)) {
335339
transform = TRANSFORMS.js;
336340
} else if (file.startsWith(root + sep) && (await isFile(file))) {
337341
// Ignore dotfiles
@@ -591,7 +595,7 @@ export const TRANSFORMS = {
591595
spec = relative(root, spec).split(sep).join(posix.sep);
592596
}
593597
// Retain bare specifiers when serializing to url
594-
else if (!/^\.?\.\//.test(spec)) {
598+
else if (!/^\.?\.\//.test(spec) && prefix !== 'npm') {
595599
spec = `@id/${spec}`;
596600
}
597601

0 commit comments

Comments
 (0)