Skip to content

Commit

Permalink
chore: replace @babel/register with esbuild-wasm (#5180)
Browse files Browse the repository at this point in the history
* 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 <nison.mael@gmail.com>
(cherry picked from commit 8e2028e)
  • Loading branch information
merceyz committed Dec 25, 2023
1 parent a258467 commit 3cc5d0e
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 70 deletions.
40 changes: 11 additions & 29 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
"@babel/preset-env": "^7.23.3",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@babel/register": "^7.22.15",
"@types/jest": "^28.1.6",
"@types/node": "^18.7.6",
"@yarnpkg/cli": "workspace:^",
"@yarnpkg/core": "workspace:^",
"@yarnpkg/eslint-config": "workspace:^",
"@yarnpkg/sdks": "workspace:^",
"clipanion": "3.2.0-rc.4",
"esbuild-wasm": "0.17.5",
"eslint": "^8.2.0",
"jest": "^28.1.3",
"pirates": "^4.0.5",
"tslib": "^1.13.0",
"typescript": "5.2.0-beta"
},
Expand Down
149 changes: 128 additions & 21 deletions scripts/setup-ts-execution.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,134 @@
const crypto = require(`crypto`);
const esbuild = require(`esbuild-wasm`);
const fs = require(`fs`);
const path = require(`path`);
const babel = require(`@babel/core`);
const os = require(`os`);
const root = path.dirname(__dirname);
const pirates = require(`pirates`);
const v8 = require(`v8`);
const zlib = require(`zlib`);

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

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

require(`@babel/register`)({
root,
extensions: [`.tsx`, `.ts`, `.js`],
only: [
p => {
if (p && p.endsWith(`.js`)) {
const normalizedP = p.replace(/\\/g, `/`);
return normalizedP.includes(`packages/yarnpkg-pnp/sources/node`) || normalizedP.endsWith(`packages/yarnpkg-pnp/sources/loader/node-options.js`);
}

return true;
// Needed by the worker spawned by esbuild
if (process.versions.pnp)
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ``} -r ${JSON.stringify(require.resolve(`pnpapi`))}`;

const resolveVirtual = process.versions.pnp
? require(`pnpapi`).resolveVirtual
: undefined;

// esbuild only supports major.minor.patch, no pre-release (nightly) specifier is allowed
// so we reduce the version down to major.minor
const NODE_VERSION = process.versions.node.split(`.`, 2).join(`.`);

const cache = {
version: `${esbuild.version}\0${NODE_VERSION}`,
files: new Map(),
isDirty: false,
};

const cachePath = path.join(__dirname, `../node_modules/.cache/yarn/esbuild-transpile-cache.bin`);
try {
const cacheData = v8.deserialize(zlib.brotliDecompressSync(fs.readFileSync(cachePath)));
if (cacheData.version === cache.version) {
cache.files = cacheData.files;
}
} catch {}

function persistCache() {
if (!cache.isDirty)
return;

cache.isDirty = false;

const data = v8.serialize({
version: cache.version,
files: cache.files,
});

fs.mkdirSync(path.dirname(cachePath), {recursive: true});

const tmpPath = cachePath + crypto.randomBytes(8).toString(`hex`);
fs.writeFileSync(tmpPath, zlib.brotliCompressSync(data, {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 4,
},
}));

fs.renameSync(tmpPath, cachePath);
}

process.once(`exit`, persistCache);
process.nextTick(persistCache);

process.setSourceMapsEnabled && process.setSourceMapsEnabled(true);

function compileFile(sourceCode, filename) {
filename = (resolveVirtual && resolveVirtual(filename)) || filename;

const cacheEntry = cache.files.get(filename);
if (cacheEntry && cacheEntry.source === sourceCode)
return cacheEntry.code;

const res = esbuild.transformSync(sourceCode, {
target: `node${NODE_VERSION}`,
loader: path.extname(filename).slice(1),
sourcefile: filename,
sourcemap: `inline`,
platform: `node`,
format: `cjs`,
supported: {
'dynamic-import': false,
},
],
});

cache.isDirty = true;
cache.files.set(filename, {
source: sourceCode,
code: res.code,
});

return res.code;
}

pirates.addHook(compileFile, {
extensions: [`.tsx`, `.ts`, `.js`],
matcher(p) {
if (p && p.endsWith(`.js`)) {
const normalizedP = p.replace(/\\/g, `/`);
return normalizedP.includes(`packages/yarnpkg-pnp/sources/node`) || normalizedP.endsWith(`packages/yarnpkg-pnp/sources/loader/node-options.js`);
}

return true;
},
});
33 changes: 14 additions & 19 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1803,21 +1803,6 @@ __metadata:
languageName: node
linkType: hard

"@babel/register@npm:^7.22.15":
version: 7.22.15
resolution: "@babel/register@npm:7.22.15"
dependencies:
clone-deep: ^4.0.1
find-cache-dir: ^2.0.0
make-dir: ^2.1.0
pirates: ^4.0.5
source-map-support: ^0.5.16
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 5497be6773608cd2d874210edd14499fce464ddbea170219da55955afe4c9173adb591164193458fd639e43b7d1314088a6186f4abf241476c59b3f0da6afd6f
languageName: node
linkType: hard

"@babel/regjsgen@npm:^0.8.0":
version: 0.8.0
resolution: "@babel/regjsgen@npm:0.8.0"
Expand Down Expand Up @@ -5997,16 +5982,17 @@ __metadata:
"@babel/preset-env": ^7.23.3
"@babel/preset-react": ^7.23.3
"@babel/preset-typescript": ^7.23.3
"@babel/register": ^7.22.15
"@types/jest": ^28.1.6
"@types/node": ^18.7.6
"@yarnpkg/cli": "workspace:^"
"@yarnpkg/core": "workspace:^"
"@yarnpkg/eslint-config": "workspace:^"
"@yarnpkg/sdks": "workspace:^"
clipanion: 3.2.0-rc.4
esbuild-wasm: 0.17.5
eslint: ^8.2.0
jest: ^28.1.3
pirates: ^4.0.5
tslib: ^1.13.0
typescript: 5.2.0-beta
dependenciesMeta:
Expand Down Expand Up @@ -11202,6 +11188,15 @@ __metadata:
languageName: node
linkType: hard

"esbuild-wasm@npm:0.17.5":
version: 0.17.5
resolution: "esbuild-wasm@npm:0.17.5"
bin:
esbuild: bin/esbuild
checksum: b7ed5d29bfb4aaa64374ec70b26fc325f26f80f3192195ac55681abc7ffe49e1844398055ad7c80e7544aa9db3dde5fd3e204ee6f6304ec378678036ddc1fef4
languageName: node
linkType: hard

"esbuild@npm:esbuild-wasm@^0.15.15":
version: 0.15.15
resolution: "esbuild-wasm@npm:0.15.15"
Expand Down Expand Up @@ -12409,7 +12404,7 @@ __metadata:
languageName: node
linkType: hard

"find-cache-dir@npm:^2.0.0, find-cache-dir@npm:^2.1.0":
"find-cache-dir@npm:^2.1.0":
version: 2.1.0
resolution: "find-cache-dir@npm:2.1.0"
dependencies:
Expand Down Expand Up @@ -17807,7 +17802,7 @@ __metadata:
languageName: node
linkType: hard

"make-dir@npm:^2.0.0, make-dir@npm:^2.1.0":
"make-dir@npm:^2.0.0":
version: 2.1.0
resolution: "make-dir@npm:2.1.0"
dependencies:
Expand Down Expand Up @@ -23979,7 +23974,7 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard

"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":
"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":
version: 0.5.20
resolution: "source-map-support@npm:0.5.20"
dependencies:
Expand Down

0 comments on commit 3cc5d0e

Please sign in to comment.