Skip to content

Commit a787771

Browse files
authored
test: transpile using esbuild (#5581)
**What's the problem this PR addresses?** We use esbuild to build and run Yarn from sources but not for our test files. Follow-up to #5180 **How did you fix it?** Use esbuild to transpile files in our testing setup. **Checklist** - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). - [x] I have set the packages that need to be released for my changes to be effective. - [x] I will check that all automated PR checks pass before the PR gets reviewed.
1 parent 57ed709 commit a787771

9 files changed

+625
-620
lines changed

.pnp.cjs

+489-497
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

babel.config.js

-10
This file was deleted.

jest.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ module.exports = {
88
],
99
setupFiles: [require.resolve(`@yarnpkg/cli/polyfills`)],
1010
testTimeout: 50000,
11+
transform: {
12+
"\\.[jt]sx?$": require.resolve(`./scripts/setup-ts-jest.js`),
13+
},
1114
};

package.json

-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
],
88
"devDependencies": {
99
"@arcanis/sherlock": "^2.0.3",
10-
"@babel/core": "^7.18.10",
11-
"@babel/preset-env": "^7.18.10",
12-
"@babel/preset-react": "^7.18.6",
13-
"@babel/preset-typescript": "^7.18.6",
1410
"@types/jest": "^28.1.6",
1511
"@types/micromatch": "^4.0.1",
1612
"@types/node": "^18.17.15",

scripts/extract-hooks.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async function processFile(file: ts.SourceFile) {
7575
return hooks;
7676
}
7777

78-
async function execute(files: Array<string>) {
78+
export async function execute(files: Array<string>) {
7979
const allHooks = new Map<string, HookDefinition>();
8080

8181
for (const relativePath of files) {
@@ -115,8 +115,6 @@ async function execute(files: Array<string>) {
115115
return allHooksArray;
116116
}
117117

118-
exports.execute = execute;
119-
120118
if (require.main === module) {
121119
Cli.from([
122120
class extends Command {

scripts/setup-ts-cache.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const crypto = require(`crypto`);
2+
const esbuild = require(`esbuild-wasm`);
3+
const fs = require(`fs`);
4+
const path = require(`path`);
5+
const v8 = require(`v8`);
6+
const zlib = require(`zlib`);
7+
8+
// Needed by the worker spawned by esbuild
9+
if (process.versions.pnp)
10+
// Unquoted because Yarn doesn't support it quoted yet
11+
// TODO: make Yarn support quoted PnP requires in NODE_OPTIONS
12+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ``} --require ${require.resolve(`pnpapi`)}`;
13+
14+
const resolveVirtual = process.versions.pnp
15+
? require(`pnpapi`).resolveVirtual
16+
: undefined;
17+
18+
// esbuild only supports major.minor.patch, no pre-release (nightly) specifier is allowed
19+
// so we reduce the version down to major.minor
20+
const NODE_VERSION = process.versions.node.split(`.`, 2).join(`.`);
21+
22+
const cache = {
23+
version: `1\0${esbuild.version}\0${NODE_VERSION}`,
24+
files: new Map(),
25+
isDirty: false,
26+
};
27+
28+
const cachePath = path.join(__dirname, `../node_modules/.cache/yarn/esbuild-transpile-cache.bin`);
29+
try {
30+
const cacheData = v8.deserialize(zlib.brotliDecompressSync(fs.readFileSync(cachePath)));
31+
if (cacheData.version === cache.version) {
32+
cache.files = cacheData.files;
33+
}
34+
} catch {}
35+
36+
function persistCache() {
37+
if (!cache.isDirty)
38+
return;
39+
40+
cache.isDirty = false;
41+
42+
const data = v8.serialize({
43+
version: cache.version,
44+
files: cache.files,
45+
});
46+
47+
fs.mkdirSync(path.dirname(cachePath), {recursive: true});
48+
49+
const tmpPath = cachePath + crypto.randomBytes(8).toString(`hex`);
50+
fs.writeFileSync(tmpPath, zlib.brotliCompressSync(data, {
51+
params: {
52+
[zlib.constants.BROTLI_PARAM_QUALITY]: 4,
53+
},
54+
}));
55+
56+
try {
57+
fs.renameSync(tmpPath, cachePath);
58+
} catch {
59+
fs.unlinkSync(tmpPath);
60+
}
61+
}
62+
63+
process.once(`exit`, persistCache);
64+
process.nextTick(persistCache);
65+
66+
process.setSourceMapsEnabled?.(true);
67+
68+
function compileFile(sourceCode, filename) {
69+
filename = resolveVirtual?.(filename) ?? filename;
70+
71+
const cacheEntry = cache.files.get(filename);
72+
if (cacheEntry?.source === sourceCode)
73+
return {code: cacheEntry.code, map: cacheEntry.map};
74+
75+
const res = esbuild.transformSync(sourceCode, {
76+
target: `node${NODE_VERSION}`,
77+
loader: path.extname(filename).slice(1),
78+
sourcefile: filename,
79+
sourcemap: `both`,
80+
platform: `node`,
81+
format: `cjs`,
82+
supported: {
83+
'dynamic-import': false,
84+
},
85+
});
86+
87+
cache.isDirty = true;
88+
cache.files.set(filename, {
89+
source: sourceCode,
90+
code: res.code,
91+
map: res.map,
92+
});
93+
94+
return {code: res.code, map: res.map};
95+
}
96+
97+
module.exports = {
98+
compileFile,
99+
version: cache.version,
100+
};

scripts/setup-ts-execution.js

+8-100
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,11 @@
1-
const crypto = require(`crypto`);
2-
const esbuild = require(`esbuild-wasm`);
3-
const fs = require(`fs`);
4-
const path = require(`path`);
51
const pirates = require(`pirates`);
6-
const v8 = require(`v8`);
7-
const zlib = require(`zlib`);
2+
const {compileFile} = require(`./setup-ts-cache.js`);
83

9-
// Needed by the worker spawned by esbuild
10-
if (process.versions.pnp)
11-
// Unquoted because Yarn doesn't support it quoted yet
12-
// TODO: make Yarn support quoted PnP requires in NODE_OPTIONS
13-
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ``} --require ${require.resolve(`pnpapi`)}`;
14-
15-
const resolveVirtual = process.versions.pnp
16-
? require(`pnpapi`).resolveVirtual
17-
: undefined;
18-
19-
// esbuild only supports major.minor.patch, no pre-release (nightly) specifier is allowed
20-
// so we reduce the version down to major.minor
21-
const NODE_VERSION = process.versions.node.split(`.`, 2).join(`.`);
22-
23-
const cache = {
24-
version: `${esbuild.version}\0${NODE_VERSION}`,
25-
files: new Map(),
26-
isDirty: false,
27-
};
28-
29-
const cachePath = path.join(__dirname, `../node_modules/.cache/yarn/esbuild-transpile-cache.bin`);
30-
try {
31-
const cacheData = v8.deserialize(zlib.brotliDecompressSync(fs.readFileSync(cachePath)));
32-
if (cacheData.version === cache.version) {
33-
cache.files = cacheData.files;
34-
}
35-
} catch {}
36-
37-
function persistCache() {
38-
if (!cache.isDirty)
39-
return;
40-
41-
cache.isDirty = false;
42-
43-
const data = v8.serialize({
44-
version: cache.version,
45-
files: cache.files,
46-
});
47-
48-
fs.mkdirSync(path.dirname(cachePath), {recursive: true});
49-
50-
const tmpPath = cachePath + crypto.randomBytes(8).toString(`hex`);
51-
fs.writeFileSync(tmpPath, zlib.brotliCompressSync(data, {
52-
params: {
53-
[zlib.constants.BROTLI_PARAM_QUALITY]: 4,
54-
},
55-
}));
56-
57-
fs.renameSync(tmpPath, cachePath);
58-
}
59-
60-
process.once(`exit`, persistCache);
61-
process.nextTick(persistCache);
62-
63-
process.setSourceMapsEnabled?.(true);
64-
65-
function compileFile(sourceCode, filename) {
66-
filename = resolveVirtual?.(filename) ?? filename;
67-
68-
const cacheEntry = cache.files.get(filename);
69-
if (cacheEntry?.source === sourceCode)
70-
return cacheEntry.code;
71-
72-
const res = esbuild.transformSync(sourceCode, {
73-
target: `node${NODE_VERSION}`,
74-
loader: path.extname(filename).slice(1),
75-
sourcefile: filename,
76-
sourcemap: `inline`,
77-
platform: `node`,
78-
format: `cjs`,
79-
supported: {
80-
'dynamic-import': false,
81-
},
82-
});
83-
84-
cache.isDirty = true;
85-
cache.files.set(filename, {
86-
source: sourceCode,
87-
code: res.code,
88-
});
89-
90-
return res.code;
91-
}
92-
93-
pirates.addHook(compileFile, {
94-
extensions: [`.tsx`, `.ts`, `.js`],
95-
matcher(p) {
96-
if (p?.endsWith(`.js`)) {
97-
const normalizedP = p.replace(/\\/g, `/`);
98-
return normalizedP.includes(`packages/yarnpkg-pnp/sources/node`) || normalizedP.endsWith(`packages/yarnpkg-pnp/sources/loader/node-options.js`);
99-
}
100-
101-
return true;
4+
pirates.addHook(
5+
(code, filename) => {
6+
return compileFile(code, filename).code;
7+
},
8+
{
9+
extensions: [`.tsx`, `.ts`, `.js`],
10210
},
103-
});
11+
);

scripts/setup-ts-jest.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const {createHash} = require(`node:crypto`);
2+
const {compileFile, version} = require(`./setup-ts-cache.js`);
3+
4+
module.exports = {
5+
getCacheKey(sourceText, sourcePath, transformOptions) {
6+
return createHash(`sha1`)
7+
.update(sourceText)
8+
.update(`\0`)
9+
.update(version)
10+
.digest(`hex`)
11+
.substring(0, 32);
12+
},
13+
process(sourceText, sourcePath, options) {
14+
if (/[\\/]node_modules[\\/]/.test(sourcePath)) {
15+
return {
16+
code: sourceText,
17+
};
18+
}
19+
20+
return compileFile(sourceText, sourcePath);
21+
},
22+
};

yarn.lock

+2-6
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ __metadata:
285285
languageName: node
286286
linkType: hard
287287

288-
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.10, @babel/core@npm:^7.19.6, @babel/core@npm:^7.23.3":
288+
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.19.6, @babel/core@npm:^7.23.3":
289289
version: 7.23.9
290290
resolution: "@babel/core@npm:7.23.9"
291291
dependencies:
@@ -1536,7 +1536,7 @@ __metadata:
15361536
languageName: node
15371537
linkType: hard
15381538

1539-
"@babel/preset-env@npm:^7.18.10, @babel/preset-env@npm:^7.19.4, @babel/preset-env@npm:^7.22.9":
1539+
"@babel/preset-env@npm:^7.19.4, @babel/preset-env@npm:^7.22.9":
15401540
version: 7.23.9
15411541
resolution: "@babel/preset-env@npm:7.23.9"
15421542
dependencies:
@@ -5449,10 +5449,6 @@ __metadata:
54495449
resolution: "@yarnpkg/monorepo@workspace:."
54505450
dependencies:
54515451
"@arcanis/sherlock": "npm:^2.0.3"
5452-
"@babel/core": "npm:^7.18.10"
5453-
"@babel/preset-env": "npm:^7.18.10"
5454-
"@babel/preset-react": "npm:^7.18.6"
5455-
"@babel/preset-typescript": "npm:^7.18.6"
54565452
"@iarna/toml": "npm:^2.2.5"
54575453
"@types/jest": "npm:^28.1.6"
54585454
"@types/micromatch": "npm:^4.0.1"

0 commit comments

Comments
 (0)