Skip to content

Commit e7ac06f

Browse files
authored
Updates the TS patch (#2026)
* Updates the TS patch * Removes debug * Fixes the e2e test path * Fixes e2e * Uses createRequireFromPath when necessary * Reverts changeFile * Adds debug * Adds versions * Debug
1 parent 4767469 commit e7ac06f

14 files changed

+246
-107
lines changed

.github/workflows/e2e-typescript-workflow.yml

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ jobs:
2828
run: |
2929
node ./scripts/run-yarn.js build:cli
3030
31+
- name: 'Checking some behaviors against the latest TypeScript'
32+
run: |
33+
yarn up typescript
34+
yarn node ./scripts/test-typescript-patch.js
35+
3136
- name: 'Installing all versions of TS'
3237
run: |
3338
source scripts/e2e-setup-ci.sh

.pnp.js

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

.vscode/settings.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
"editor.tabSize": 2,
33
"files.eol": "\n",
44
"typescript.tsdk": ".yarn/sdks/typescript/lib",
5+
"typescript.tsserver.watchOptions": {
6+
"watchFile": "useFsEventsOnParentDirectory",
7+
"watchDirectory": "useFsEvents"
8+
},
59
"npm.packageManager": "yarn",
610
"eslint.validate": [
711
"javascript",
812
"typescript",
913
"typescriptreact"
1014
],
11-
"eslint.enable": true,
1215
"eslint.nodePath": ".yarn/sdks",
1316
"editor.codeActionsOnSave": {
1417
"source.fixAll.eslint": true
Binary file not shown.
Binary file not shown.

.yarn/versions/99af5406.yml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
releases:
2+
"@yarnpkg/cli": minor
3+
"@yarnpkg/fslib": patch
4+
"@yarnpkg/plugin-compat": minor
5+
6+
declined:
7+
- "@yarnpkg/plugin-constraints"
8+
- "@yarnpkg/plugin-dlx"
9+
- "@yarnpkg/plugin-essentials"
10+
- "@yarnpkg/plugin-exec"
11+
- "@yarnpkg/plugin-file"
12+
- "@yarnpkg/plugin-git"
13+
- "@yarnpkg/plugin-github"
14+
- "@yarnpkg/plugin-http"
15+
- "@yarnpkg/plugin-init"
16+
- "@yarnpkg/plugin-interactive-tools"
17+
- "@yarnpkg/plugin-link"
18+
- "@yarnpkg/plugin-node-modules"
19+
- "@yarnpkg/plugin-npm"
20+
- "@yarnpkg/plugin-npm-cli"
21+
- "@yarnpkg/plugin-pack"
22+
- "@yarnpkg/plugin-patch"
23+
- "@yarnpkg/plugin-pnp"
24+
- "@yarnpkg/plugin-stage"
25+
- "@yarnpkg/plugin-typescript"
26+
- "@yarnpkg/plugin-version"
27+
- "@yarnpkg/plugin-workspace-tools"
28+
- vscode-zipfs
29+
- "@yarnpkg/builder"
30+
- "@yarnpkg/core"
31+
- "@yarnpkg/doctor"
32+
- "@yarnpkg/json-proxy"
33+
- "@yarnpkg/pnp"
34+
- "@yarnpkg/pnpify"
35+
- "@yarnpkg/shell"

packages/plugin-compat/extra/typescript/gen-typescript-patch.sh

+35-28
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ JSPATCH="$THIS_DIR"/../../sources/patches/typescript.patch.ts
88

99
FIRST_PR_COMMIT="5d50de3"
1010

11+
# Defines which commits need to be cherry-picked onto which other commit to
12+
# generate a patch suitable for the specified range.
1113
HASHES=(
12-
# Patch # Base # Ranges
13-
"426f5a7" "e39bdc3" ">=3.2 <3.5"
14-
"426f5a7" "cf7b2d4" ">=3.5 <=3.6"
15-
"426f5a7" "cda54b8" ">3.6 <3.7"
16-
"2f85932" "e39bdc3" ">=3.7 <3.9"
17-
"3af06df" "551f0dd" ">=3.9 <4.1"
18-
"ce3da5a" "69972a3" ">=4.1"
14+
# From # To # Onto # Ranges
15+
"5d50de3" "426f5a7" "e39bdc3" ">=3.2 <3.5"
16+
"5d50de3" "426f5a7" "cf7b2d4" ">=3.5 <=3.6"
17+
"5d50de3" "426f5a7" "cda54b8" ">3.6 <3.7"
18+
"5d50de3" "2f85932" "e39bdc3" ">=3.7 <3.9"
19+
"5d50de3" "3af06df" "551f0dd" ">=3.9 <4.0"
20+
"6dbdd2f" "6dbdd2f" "56865f7" ">=4.0 <4.1"
21+
"746d79b" "746d79b" "69972a3" ">=4.1"
1922
)
2023

2124
mkdir -p "$TEMP_DIR"
@@ -44,33 +47,34 @@ reset-git() {
4447
}
4548

4649
build-dir-for() {
47-
local HASH="$1"
48-
local CHERRY_PICK="$2"
50+
local CHERRYPICK_ONTO="$1"
51+
local CHERRYPICK_TO="$2"
4952

50-
local BUILD_DIR="$TEMP_DIR"/builds/"$HASH"
53+
local BUILD_DIR="$TEMP_DIR"/builds/"$CHERRYPICK_ONTO"
5154

52-
if [[ ! -z "$CHERRY_PICK" ]]; then
53-
BUILD_DIR="$BUILD_DIR-$CHERRY_PICK"
55+
if [[ ! -z "$CHERRYPICK_TO" ]]; then
56+
BUILD_DIR="$BUILD_DIR-$CHERRYPICK_TO"
5457
fi
5558

5659
echo "$BUILD_DIR"
5760
}
5861

5962
make-build-for() {
60-
local HASH="$1"
61-
local CHERRY_PICK="$2"
63+
local CHERRYPICK_ONTO="$1"
64+
local CHERRYPICK_FROM="$2"
65+
local CHERRYPICK_TO="$3"
6266

63-
local BUILD_DIR="$(build-dir-for "$HASH" "$CHERRY_PICK")"
67+
local BUILD_DIR="$(build-dir-for "$CHERRYPICK_ONTO" "$CHERRYPICK_TO")"
6468

6569
if [[ ! -e "$BUILD_DIR" ]]; then
6670
mkdir -p "$BUILD_DIR"
67-
reset-git "$HASH"
71+
reset-git "$CHERRYPICK_ONTO"
6872

69-
if [[ ! -z "$CHERRY_PICK" ]]; then
70-
if git merge-base --is-ancestor "$HASH" "$CHERRY_PICK"; then
71-
git merge --no-edit "$CHERRY_PICK"
73+
if [[ ! -z "$CHERRYPICK_TO" ]]; then
74+
if git merge-base --is-ancestor "$CHERRYPICK_ONTO" "$CHERRYPICK_TO"; then
75+
git merge --no-edit "$CHERRYPICK_TO"
7276
else
73-
git cherry-pick "$FIRST_PR_COMMIT"^.."$CHERRY_PICK"
77+
git cherry-pick "$CHERRYPICK_FROM"^.."$CHERRYPICK_TO"
7478
fi
7579
fi
7680

@@ -81,6 +85,8 @@ make-build-for() {
8185
break
8286
else
8387
echo "Something is wrong; typescript.js got generated with a stupid size" >& /dev/stderr
88+
cat -e lib/typescript.js
89+
8490
if [[ $n -eq 1 ]]; then
8591
exit 1
8692
fi
@@ -100,16 +106,17 @@ rm -f "$PATCHFILE" && touch "$PATCHFILE"
100106
rm -f "$JSPATCH" && touch "$JSPATCH"
101107

102108
while [[ ${#HASHES[@]} -gt 0 ]]; do
103-
HASH="${HASHES[0]}"
104-
BASE="${HASHES[1]}"
105-
RANGE="${HASHES[2]}"
106-
HASHES=("${HASHES[@]:3}")
109+
CHERRYPICK_FROM="${HASHES[0]}"
110+
CHERRYPICK_TO="${HASHES[1]}"
111+
CHERRYPICK_ONTO="${HASHES[2]}"
112+
RANGE="${HASHES[3]}"
113+
HASHES=("${HASHES[@]:4}")
107114

108-
make-build-for "$BASE"
109-
ORIG_DIR=$(build-dir-for "$BASE")
115+
make-build-for "$CHERRYPICK_ONTO"
116+
ORIG_DIR=$(build-dir-for "$CHERRYPICK_ONTO")
110117

111-
make-build-for "$BASE" "$HASH"
112-
PATCHED_DIR=$(build-dir-for "$BASE" "$HASH")
118+
make-build-for "$CHERRYPICK_ONTO" "$CHERRYPICK_FROM" "$CHERRYPICK_TO"
119+
PATCHED_DIR=$(build-dir-for "$CHERRYPICK_ONTO" "$CHERRYPICK_FROM")
113120

114121
DIFF="$THIS_DIR"/patch."${HASH}"-on-"${BASE}".diff
115122

packages/plugin-compat/sources/patches/typescript.patch.ts

+1-1
Large diffs are not rendered by default.

packages/yarnpkg-fslib/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# `@yarnpkg/fslib`
2+
3+
A TypeScript library abstracting the Node filesystem APIs. We use it for three main reasons:
4+
5+
## Type-safe paths
6+
7+
Our library has two path types, `NativePath` and `PortablePath`. Most interfaces only accept the later, and instances of the former need to be transformed back and forth using our type-safe utilities before being usable.
8+
9+
## Custom filesystems
10+
11+
The FSLib implements various transparent filesystem layers for a variety of purposes. For instance we use it in Yarn in order to abstract away the zip archive manipulation logic, which is implemented in `ZipFS` and exposed through a Node-like interface (called `FakeFS`).
12+
13+
All `FakeFS` implementations can be transparently layered on top of the builtin Node `fs` module, and that's for instance how we can add support for in-zip package loading without you having to care about the exact package format.
14+
15+
## Promisified API
16+
17+
All methods from the `FakeFS` interface are promisified by default (and suffixed for greater clarity, for instance we offer both `readFileSync` and `readFilePromise`).

packages/yarnpkg-fslib/sources/algorithms/copyPromise.ts

+48-24
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {FakeFS} from '../FakeFS';
44
import {Path, convertPath} from '../path';
55

66
// 1980-01-01, like Fedora
7-
const defaultTime = 315532800;
7+
const defaultTime = new Date(315532800 * 1000);
88

99
export type CopyOptions = {
1010
stableTime: boolean,
@@ -45,32 +45,40 @@ async function copyImpl<P1 extends Path, P2 extends Path>(prelayout: Operations,
4545
const destinationStat = await maybeLStat(destinationFs, destination);
4646
const sourceStat = await sourceFs.lstatPromise(source);
4747

48-
if (opts.stableTime)
49-
postlayout.push(() => updateTime(destination, defaultTime, defaultTime));
50-
else
51-
postlayout.push(() => updateTime(destination, sourceStat.atime, sourceStat.mtime));
48+
const referenceTime = opts.stableTime
49+
? {mtime: defaultTime, atime: defaultTime} as const
50+
: sourceStat;
5251

52+
let updated: boolean;
5353
switch (true) {
5454
case sourceStat.isDirectory(): {
55-
await copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
55+
updated = await copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
5656
} break;
5757

5858
case sourceStat.isFile(): {
59-
await copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
59+
updated = await copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
6060
} break;
6161

6262
case sourceStat.isSymbolicLink(): {
63-
await copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
63+
updated = await copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts);
6464
} break;
6565

6666
default: {
6767
throw new Error(`Unsupported file type (${sourceStat.mode})`);
6868
} break;
6969
}
7070

71-
postlayout.push(() => {
72-
return destinationFs.chmodPromise(destination, sourceStat.mode & 0o777);
73-
});
71+
if (updated || destinationStat?.mtime?.getTime() !== referenceTime.mtime.getTime() || destinationStat?.atime?.getTime() !== referenceTime.atime.getTime()) {
72+
postlayout.push(() => updateTime(destination, referenceTime.atime, referenceTime.mtime));
73+
updated = true;
74+
}
75+
76+
if (destinationStat === null || (destinationStat.mode & 0o777) !== (sourceStat.mode & 0o777)) {
77+
postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 0o777));
78+
updated = true;
79+
}
80+
81+
return updated;
7482
}
7583

7684
async function maybeLStat<P extends Path>(baseFs: FakeFS<P>, p: P) {
@@ -87,24 +95,36 @@ async function copyFolder<P1 extends Path, P2 extends Path>(prelayout: Operation
8795
prelayout.push(async () => destinationFs.removePromise(destination));
8896
destinationStat = null;
8997
} else {
90-
return;
98+
return false;
9199
}
92100
}
93101

94-
if (destinationStat === null)
102+
let updated = false;
103+
104+
if (destinationStat === null) {
95105
prelayout.push(async () => destinationFs.mkdirPromise(destination, {mode: sourceStat.mode}));
106+
updated = true;
107+
}
96108

97109
const entries = await sourceFs.readdirPromise(source);
98110

99111
if (opts.stableSort) {
100112
for (const entry of entries.sort()) {
101-
await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), opts);
113+
if (await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), opts)) {
114+
updated = true;
115+
}
102116
}
103117
} else {
104-
await Promise.all(entries.map(async entry => {
118+
const entriesUpdateStatus = await Promise.all(entries.map(async entry => {
105119
await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), opts);
106120
}));
121+
122+
if (entriesUpdateStatus.some(status => status)) {
123+
updated = true;
124+
}
107125
}
126+
127+
return updated;
108128
}
109129

110130
async function copyFile<P1 extends Path, P2 extends Path>(prelayout: Operations, postlayout: Operations, updateTime: typeof FakeFS.prototype.utimesPromise, destinationFs: FakeFS<P1>, destination: P1, destinationStat: Stats | null, sourceFs: FakeFS<P2>, source: P2, sourceStat: Stats, opts: CopyOptions) {
@@ -113,15 +133,16 @@ async function copyFile<P1 extends Path, P2 extends Path>(prelayout: Operations,
113133
prelayout.push(async () => destinationFs.removePromise(destination));
114134
destinationStat = null;
115135
} else {
116-
return;
136+
return false;
117137
}
118138
}
119139

120-
if (destinationFs as any === sourceFs as any) {
121-
prelayout.push(async () => destinationFs.copyFilePromise(source as any as P1, destination, fs.constants.COPYFILE_FICLONE));
122-
} else {
123-
prelayout.push(async () => destinationFs.writeFilePromise(destination, await sourceFs.readFilePromise(source)));
124-
}
140+
const op = destinationFs as any === sourceFs as any
141+
? async () => destinationFs.copyFilePromise(source as any as P1, destination, fs.constants.COPYFILE_FICLONE)
142+
: async () => destinationFs.writeFilePromise(destination, await sourceFs.readFilePromise(source));
143+
144+
prelayout.push(async () => op());
145+
return true;
125146
}
126147

127148
async function copySymlink<P1 extends Path, P2 extends Path>(prelayout: Operations, postlayout: Operations, updateTime: typeof FakeFS.prototype.utimesPromise, destinationFs: FakeFS<P1>, destination: P1, destinationStat: Stats | null, sourceFs: FakeFS<P2>, source: P2, sourceStat: Stats, opts: CopyOptions) {
@@ -130,10 +151,13 @@ async function copySymlink<P1 extends Path, P2 extends Path>(prelayout: Operatio
130151
prelayout.push(async () => destinationFs.removePromise(destination));
131152
destinationStat = null;
132153
} else {
133-
return;
154+
return false;
134155
}
135156
}
136157

137-
const target = await sourceFs.readlinkPromise(source);
138-
prelayout.push(async () => destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, target), destination));
158+
prelayout.push(async () => {
159+
await destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination);
160+
});
161+
162+
return true;
139163
}

scripts/test-typescript-patch.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const {createRequire, createRequireFromPath} = require(`module`);
2+
const path = require(`path`);
3+
const ts = require(`typescript`);
4+
5+
const tsconfigFile = ts.readJsonConfigFile(
6+
require.resolve(`@yarnpkg/monorepo/package.json`),
7+
ts.sys.readFile,
8+
);
9+
10+
const compilerOptions = ts.parseJsonSourceFileConfigFileContent(
11+
tsconfigFile,
12+
ts.sys,
13+
path.dirname(tsconfigFile.fileName),
14+
);
15+
16+
const compilerHost = ts.createCompilerHost(compilerOptions,);
17+
const program = ts.createProgram(compilerOptions.fileNames, compilerOptions, compilerHost);
18+
const moduleSpecifierResolutionHost = ts.createModuleSpecifierResolutionHost(program, compilerHost);
19+
const rootSourceFile = program.getSourceFile(require.resolve(`@yarnpkg/core/sources/Project.ts`));
20+
21+
const TESTS = [
22+
[`@yarnpkg/core/sources/Configuration.ts`, `./Configuration`],
23+
[`@yarnpkg/fslib/README.md`, `@yarnpkg/fslib/README.md`],
24+
[`@yarnpkg/fslib/package.json`, `@yarnpkg/fslib/package`],
25+
[`@yarnpkg/fslib/sources/ZipFS.ts`, `@yarnpkg/fslib/sources/ZipFS`],
26+
[`@yarnpkg/fslib/sources/index.ts`, `@yarnpkg/fslib`],
27+
];
28+
29+
const requireFactory = createRequire
30+
? createRequire
31+
: createRequireFromPath;
32+
33+
for (const [test, expected] of TESTS) {
34+
const actual = ts.moduleSpecifiers.getModuleSpecifier(
35+
compilerOptions,
36+
rootSourceFile,
37+
rootSourceFile.fileName,
38+
requireFactory(rootSourceFile.fileName).resolve(test),
39+
moduleSpecifierResolutionHost,
40+
);
41+
42+
if (actual === expected) {
43+
console.log(`\x1b[32m✓\x1b[0m ${actual}`);
44+
} else {
45+
console.log(`\x1b[31m✗\x1b[0m ${actual} !== ${expected}`);
46+
process.exitCode = 1;
47+
}
48+
}

0 commit comments

Comments
 (0)