Skip to content

Commit dab3d8f

Browse files
merceyzarcanis
andcommitted
chore: replace @babel/register with esbuild-wasm (#5180)
* chore: use esbuild for running sources * deps: update esbuild * chore: remove babel setup * refactor: only use builtin sourcemap support * refactor: remove weekly cache pruning A fresh cache is 2212kB for 263 files so unlikely to become a problem. * fix: disable dynamic imports * chore: workaround issue on windows * fix: support node nightly * chore: link to issues * perf: use brotli Runtime performance is roughly the same but the cache size shrinks: ```diff $ du -s node_modules/.cache/yarn/ - 2212 node_modules/.cache/yarn/ + 1464 node_modules/.cache/yarn/ ``` * Minor stylistic tweaks --------- Co-authored-by: Maël Nison <[email protected]> (cherry picked from commit 8e2028e)
1 parent 38189ee commit dab3d8f

File tree

6 files changed

+155
-70
lines changed

6 files changed

+155
-70
lines changed

.pnp.cjs

Lines changed: 11 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@
1111
"@babel/preset-env": "^7.23.3",
1212
"@babel/preset-react": "^7.23.3",
1313
"@babel/preset-typescript": "^7.23.3",
14-
"@babel/register": "^7.22.15",
1514
"@types/jest": "^28.1.6",
1615
"@types/node": "^18.7.6",
1716
"@yarnpkg/cli": "workspace:^",
1817
"@yarnpkg/core": "workspace:^",
1918
"@yarnpkg/eslint-config": "workspace:^",
2019
"@yarnpkg/sdks": "workspace:^",
2120
"clipanion": "3.2.0-rc.4",
21+
"esbuild-wasm": "0.17.5",
2222
"eslint": "^8.2.0",
2323
"jest": "^28.1.3",
24+
"pirates": "^4.0.5",
2425
"tslib": "^1.13.0",
2526
"typescript": "5.2.0-beta"
2627
},

scripts/setup-ts-execution.js

Lines changed: 128 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,134 @@
1+
const crypto = require(`crypto`);
2+
const esbuild = require(`esbuild-wasm`);
3+
const fs = require(`fs`);
14
const path = require(`path`);
2-
const babel = require(`@babel/core`);
3-
const os = require(`os`);
4-
const root = path.dirname(__dirname);
5+
const pirates = require(`pirates`);
6+
const v8 = require(`v8`);
7+
const zlib = require(`zlib`);
58

6-
// The cache in @babel/register never clears itself and will therefore grow
7-
// forever causing massive slowdowns if left unchecked for a while
8-
// this ensures a new cache key is generated every week
9-
const weeksSinceUNIXEpoch = Math.floor(Date.now() / 604800000);
9+
/**
10+
* There is an issue on Windows with Node.js v14 (tested v14.19.2 and v14.21.2) where
11+
* ```sh
12+
* node esbuild-wasm\bin\esbuild --service=0.17.5 --ping
13+
* ```
14+
* uses up to 400% CPU and 3.62 GB RAM for a while when an ESM loader is enabled.
15+
*
16+
* ```console
17+
* $ time NODE_OPTIONS="--require ./.pnp.cjs --loader ./.pnp.loader.mjs" node -p "require('esbuild-wasm').transformSync('let foo = 0;')"
18+
* {
19+
* warnings: [],
20+
* code: 'let foo = 0;\n',
21+
* map: '',
22+
* mangleCache: undefined,
23+
* legalComments: undefined
24+
* }
25+
*
26+
* ________________________________________________________
27+
* Executed in 54.99 secs fish external
28+
* usr time 0.00 micros 0.00 micros 0.00 micros
29+
* sys time 0.00 micros 0.00 micros 0.00 micros
30+
* ```
31+
*
32+
* Reported upstream in https://github.com/evanw/esbuild/issues/2888 and seems to boil down to https://github.com/nodejs/node/issues/36616.
33+
*
34+
* To workaround this issue we remove the loader from the NODE_OPTIONS since it's not needed in this case.
35+
*/
1036

11-
if (!process.env.BABEL_CACHE_PATH)
12-
process.env.BABEL_CACHE_PATH = path.join(os.tmpdir(), `babel`, `.babel.${babel.version}.${babel.getEnv()}.${weeksSinceUNIXEpoch}.json`);
37+
if (process.env.NODE_OPTIONS) {
38+
const esmLoaderExpression = /\s*--experimental-loader\s+\S*\.pnp\.loader\.mjs\s*/;
39+
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS.replace(esmLoaderExpression, ` `);
40+
}
1341

14-
require(`@babel/register`)({
15-
root,
16-
extensions: [`.tsx`, `.ts`, `.js`],
17-
only: [
18-
p => {
19-
if (p && p.endsWith(`.js`)) {
20-
const normalizedP = p.replace(/\\/g, `/`);
21-
return normalizedP.includes(`packages/yarnpkg-pnp/sources/node`) || normalizedP.endsWith(`packages/yarnpkg-pnp/sources/loader/node-options.js`);
22-
}
23-
24-
return true;
42+
// Needed by the worker spawned by esbuild
43+
if (process.versions.pnp)
44+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ``} -r ${JSON.stringify(require.resolve(`pnpapi`))}`;
45+
46+
const resolveVirtual = process.versions.pnp
47+
? require(`pnpapi`).resolveVirtual
48+
: undefined;
49+
50+
// esbuild only supports major.minor.patch, no pre-release (nightly) specifier is allowed
51+
// so we reduce the version down to major.minor
52+
const NODE_VERSION = process.versions.node.split(`.`, 2).join(`.`);
53+
54+
const cache = {
55+
version: `${esbuild.version}\0${NODE_VERSION}`,
56+
files: new Map(),
57+
isDirty: false,
58+
};
59+
60+
const cachePath = path.join(__dirname, `../node_modules/.cache/yarn/esbuild-transpile-cache.bin`);
61+
try {
62+
const cacheData = v8.deserialize(zlib.brotliDecompressSync(fs.readFileSync(cachePath)));
63+
if (cacheData.version === cache.version) {
64+
cache.files = cacheData.files;
65+
}
66+
} catch {}
67+
68+
function persistCache() {
69+
if (!cache.isDirty)
70+
return;
71+
72+
cache.isDirty = false;
73+
74+
const data = v8.serialize({
75+
version: cache.version,
76+
files: cache.files,
77+
});
78+
79+
fs.mkdirSync(path.dirname(cachePath), {recursive: true});
80+
81+
const tmpPath = cachePath + crypto.randomBytes(8).toString(`hex`);
82+
fs.writeFileSync(tmpPath, zlib.brotliCompressSync(data, {
83+
params: {
84+
[zlib.constants.BROTLI_PARAM_QUALITY]: 4,
85+
},
86+
}));
87+
88+
fs.renameSync(tmpPath, cachePath);
89+
}
90+
91+
process.once(`exit`, persistCache);
92+
process.nextTick(persistCache);
93+
94+
process.setSourceMapsEnabled && process.setSourceMapsEnabled(true);
95+
96+
function compileFile(sourceCode, filename) {
97+
filename = (resolveVirtual && resolveVirtual(filename)) || filename;
98+
99+
const cacheEntry = cache.files.get(filename);
100+
if (cacheEntry && cacheEntry.source === sourceCode)
101+
return cacheEntry.code;
102+
103+
const res = esbuild.transformSync(sourceCode, {
104+
target: `node${NODE_VERSION}`,
105+
loader: path.extname(filename).slice(1),
106+
sourcefile: filename,
107+
sourcemap: `inline`,
108+
platform: `node`,
109+
format: `cjs`,
110+
supported: {
111+
'dynamic-import': false,
25112
},
26-
],
113+
});
114+
115+
cache.isDirty = true;
116+
cache.files.set(filename, {
117+
source: sourceCode,
118+
code: res.code,
119+
});
120+
121+
return res.code;
122+
}
123+
124+
pirates.addHook(compileFile, {
125+
extensions: [`.tsx`, `.ts`, `.js`],
126+
matcher(p) {
127+
if (p && p.endsWith(`.js`)) {
128+
const normalizedP = p.replace(/\\/g, `/`);
129+
return normalizedP.includes(`packages/yarnpkg-pnp/sources/node`) || normalizedP.endsWith(`packages/yarnpkg-pnp/sources/loader/node-options.js`);
130+
}
131+
132+
return true;
133+
},
27134
});

yarn.lock

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,21 +1803,6 @@ __metadata:
18031803
languageName: node
18041804
linkType: hard
18051805

1806-
"@babel/register@npm:^7.22.15":
1807-
version: 7.22.15
1808-
resolution: "@babel/register@npm:7.22.15"
1809-
dependencies:
1810-
clone-deep: ^4.0.1
1811-
find-cache-dir: ^2.0.0
1812-
make-dir: ^2.1.0
1813-
pirates: ^4.0.5
1814-
source-map-support: ^0.5.16
1815-
peerDependencies:
1816-
"@babel/core": ^7.0.0-0
1817-
checksum: 5497be6773608cd2d874210edd14499fce464ddbea170219da55955afe4c9173adb591164193458fd639e43b7d1314088a6186f4abf241476c59b3f0da6afd6f
1818-
languageName: node
1819-
linkType: hard
1820-
18211806
"@babel/regjsgen@npm:^0.8.0":
18221807
version: 0.8.0
18231808
resolution: "@babel/regjsgen@npm:0.8.0"
@@ -5997,16 +5982,17 @@ __metadata:
59975982
"@babel/preset-env": ^7.23.3
59985983
"@babel/preset-react": ^7.23.3
59995984
"@babel/preset-typescript": ^7.23.3
6000-
"@babel/register": ^7.22.15
60015985
"@types/jest": ^28.1.6
60025986
"@types/node": ^18.7.6
60035987
"@yarnpkg/cli": "workspace:^"
60045988
"@yarnpkg/core": "workspace:^"
60055989
"@yarnpkg/eslint-config": "workspace:^"
60065990
"@yarnpkg/sdks": "workspace:^"
60075991
clipanion: 3.2.0-rc.4
5992+
esbuild-wasm: 0.17.5
60085993
eslint: ^8.2.0
60095994
jest: ^28.1.3
5995+
pirates: ^4.0.5
60105996
tslib: ^1.13.0
60115997
typescript: 5.2.0-beta
60125998
dependenciesMeta:
@@ -11202,6 +11188,15 @@ __metadata:
1120211188
languageName: node
1120311189
linkType: hard
1120411190

11191+
"esbuild-wasm@npm:0.17.5":
11192+
version: 0.17.5
11193+
resolution: "esbuild-wasm@npm:0.17.5"
11194+
bin:
11195+
esbuild: bin/esbuild
11196+
checksum: b7ed5d29bfb4aaa64374ec70b26fc325f26f80f3192195ac55681abc7ffe49e1844398055ad7c80e7544aa9db3dde5fd3e204ee6f6304ec378678036ddc1fef4
11197+
languageName: node
11198+
linkType: hard
11199+
1120511200
"esbuild@npm:esbuild-wasm@^0.15.15":
1120611201
version: 0.15.15
1120711202
resolution: "esbuild-wasm@npm:0.15.15"
@@ -12409,7 +12404,7 @@ __metadata:
1240912404
languageName: node
1241012405
linkType: hard
1241112406

12412-
"find-cache-dir@npm:^2.0.0, find-cache-dir@npm:^2.1.0":
12407+
"find-cache-dir@npm:^2.1.0":
1241312408
version: 2.1.0
1241412409
resolution: "find-cache-dir@npm:2.1.0"
1241512410
dependencies:
@@ -17807,7 +17802,7 @@ __metadata:
1780717802
languageName: node
1780817803
linkType: hard
1780917804

17810-
"make-dir@npm:^2.0.0, make-dir@npm:^2.1.0":
17805+
"make-dir@npm:^2.0.0":
1781117806
version: 2.1.0
1781217807
resolution: "make-dir@npm:2.1.0"
1781317808
dependencies:
@@ -23979,7 +23974,7 @@ resolve@^2.0.0-next.3:
2397923974
languageName: node
2398023975
linkType: hard
2398123976

23982-
"source-map-support@npm:^0.5.16, source-map-support@npm:^0.5.17, source-map-support@npm:^0.5.19, source-map-support@npm:~0.5.12, source-map-support@npm:~0.5.20":
23977+
"source-map-support@npm:^0.5.17, source-map-support@npm:^0.5.19, source-map-support@npm:~0.5.12, source-map-support@npm:~0.5.20":
2398323978
version: 0.5.20
2398423979
resolution: "source-map-support@npm:0.5.20"
2398523980
dependencies:

0 commit comments

Comments
 (0)