Skip to content

Commit d955efa

Browse files
authored
Adds a patchfile for fsevents (yarnpkg#692)
* Adds a patchfile for fsevents * Fix & debug * Removes old-style from the tests * Increases the timeout on CI? * Refactors the test * Removes snapshot * Escapes heredoc * Adds the release file * No backticks allowed in Bash * Checks whether the problem comes from the patch or native * Skips fsevents@1 and goes to latest * Calls the watch fn * Fixes the 2.x patch * Adds the CI tests to the README * Updates FSEvents * Fixes the patch * Fixes the checked-in artifacts
1 parent 4499649 commit d955efa

17 files changed

+328
-208
lines changed

.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ indent_size = 2
1414
[*.md]
1515
# trailing whitespace is significant in markdown -> do not remove
1616
trim_trailing_whitespace = false
17+
18+
[*.patch]
19+
trim_trailing_whitespace = false
20+
insert_final_newline = false
+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
on:
2+
schedule:
3+
- cron: '0 */4 * * *'
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
paths:
9+
- .github/workflows/e2e-fsevents-workflow.yml
10+
- scripts/e2e-setup-ci.sh
11+
12+
name: 'E2E FSEvents'
13+
jobs:
14+
chore:
15+
name: 'Validating FSEvents'
16+
runs-on: macos-latest
17+
18+
steps:
19+
- uses: actions/checkout@master
20+
21+
- name: 'Use Node.js 10.x'
22+
uses: actions/setup-node@master
23+
with:
24+
node-version: 10.x
25+
26+
- name: 'Build the standard bundle'
27+
run: |
28+
node ./scripts/run-yarn.js build:cli
29+
30+
- name: 'Running the integration test (FSEvents ^1)'
31+
run: |
32+
source scripts/e2e-setup-ci.sh
33+
yarn init
34+
yarn add fsevents@^1
35+
36+
cat > test.js <<EOT
37+
const assert = require('assert');
38+
const fsevents = require('fsevents');
39+
const fs = require('fs');
40+
const path = require('path');
41+
42+
async function sleep(n) {
43+
return new Promise(resolve => {
44+
setTimeout(resolve, n);
45+
});
46+
}
47+
48+
async function runTestOn(dir, fileName) {
49+
const file = path.join(dir, fileName);
50+
51+
const events = [];
52+
const watcher = fsevents(dir);
53+
watcher.on('change', (...args) => events.push(args));
54+
watcher.start();
55+
56+
async function main() {
57+
await sleep(1000);
58+
fs.writeFileSync(file, '');
59+
await sleep(1000);
60+
61+
if (events.length === 0)
62+
throw new Error('No events recorded');
63+
64+
for (const [p] of events) {
65+
if (!p.startsWith(dir)) {
66+
throw new Error(p);
67+
}
68+
}
69+
}
70+
71+
try {
72+
return await main();
73+
} finally {
74+
watcher.stop();
75+
}
76+
}
77+
78+
async function registerTest(dir, fileName) {
79+
try {
80+
await runTestOn(dir, fileName);
81+
console.log('Succeeded test for ' + dir);
82+
} catch (error) {
83+
console.log('Failed test for ' + dir + ': ' + error.message);
84+
process.exitCode = 1;
85+
}
86+
}
87+
88+
Promise.resolve().then(() => {
89+
return registerTest(process.cwd(), 'hello');
90+
}).then(() => {
91+
return registerTest(process.cwd() + '/\$\$virtual/foo/0', 'world');
92+
});
93+
EOT
94+
95+
yarn node ./test.js
96+
97+
- name: 'Running the integration test (FSEvents latest)'
98+
run: |
99+
source scripts/e2e-setup-ci.sh
100+
yarn init
101+
yarn add fsevents@latest
102+
103+
cat > test.js <<EOT
104+
const assert = require('assert');
105+
const fsevents = require('fsevents');
106+
const fs = require('fs');
107+
const path = require('path');
108+
109+
async function sleep(n) {
110+
return new Promise(resolve => {
111+
setTimeout(resolve, n);
112+
});
113+
}
114+
115+
async function runTestOn(dir, fileName) {
116+
const file = path.join(dir, fileName);
117+
118+
const events = [];
119+
const stop = fsevents.watch(dir, (...args) => events.push(args));
120+
121+
async function main() {
122+
await sleep(1000);
123+
fs.writeFileSync(file, '');
124+
await sleep(1000);
125+
126+
if (events.length === 0)
127+
throw new Error('No events recorded');
128+
129+
for (const [p] of events) {
130+
if (!p.startsWith(dir)) {
131+
throw new Error(p);
132+
}
133+
}
134+
}
135+
136+
try {
137+
return await main();
138+
} finally {
139+
await stop();
140+
}
141+
}
142+
143+
async function registerTest(dir, fileName) {
144+
try {
145+
await runTestOn(dir, fileName);
146+
console.log('Succeeded test for ' + dir);
147+
} catch (error) {
148+
console.log('Failed test for ' + dir + ': ' + error.message);
149+
process.exitCode = 1;
150+
}
151+
}
152+
153+
Promise.resolve().then(() => {
154+
return registerTest(process.cwd(), 'hello');
155+
}).then(() => {
156+
return registerTest(process.cwd() + '/\$\$virtual/foo/0', 'world');
157+
});
158+
EOT
159+
160+
yarn node ./test.js

.pnp.js

+11-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
12.6 KB
Binary file not shown.
804 KB
Binary file not shown.

.yarn/versions/d732ee1c.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
releases:
2+
"@yarnpkg/[email protected]": prerelease
3+
"@yarnpkg/[email protected]": prerelease
4+
"@yarnpkg/[email protected]": prerelease
5+
6+
declined:
7+
- "@yarnpkg/[email protected]"
8+
- "@yarnpkg/[email protected]"
9+
- "@yarnpkg/[email protected]"
10+
- "@yarnpkg/[email protected]"
11+
- "@yarnpkg/[email protected]"
12+
- "@yarnpkg/[email protected]"
13+
- "@yarnpkg/[email protected]"
14+
- "@yarnpkg/[email protected]"
15+
- "@yarnpkg/[email protected]"
16+
- "@yarnpkg/[email protected]"
17+
- "@yarnpkg/[email protected]"
18+
- "@yarnpkg/[email protected]"
19+
- "@yarnpkg/[email protected]"
20+
- "@yarnpkg/[email protected]"

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ On top of our classic integration tests, we also run Yarn every day against the
5050
</td><td valign="top">
5151

5252
[![](https://github.com/yarnpkg/berry/workflows/E2E%20ESLint/badge.svg?event=schedule)](https://github.com/yarnpkg/berry/blob/master/.github/workflows/e2e-eslint-workflow.yml)<br/>
53+
[![](https://github.com/yarnpkg/berry/workflows/E2E%20FSEvents/badge.svg?event=schedule)](https://github.com/yarnpkg/berry/blob/master/.github/workflows/e2e-fsevents-workflow.yml)<br/>
5354
[![](https://github.com/yarnpkg/berry/workflows/E2E%20Husky/badge.svg?event=schedule)](https://github.com/yarnpkg/berry/blob/master/.github/workflows/e2e-husky-workflow.yml)<br/>
5455
[![](https://github.com/yarnpkg/berry/workflows/E2E%20Jest/badge.svg?event=schedule)](https://github.com/yarnpkg/berry/blob/master/.github/workflows/e2e-jest-workflow.yml)<br/>
5556
[![](https://github.com/yarnpkg/berry/workflows/E2E%20Mocha/badge.svg?event=schedule)](https://github.com/yarnpkg/berry/blob/master/.github/workflows/e2e-mocha-workflow.yml)<br/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
diff --git a/fsevents.js b/fsevents.js
2+
semver exclusivity ^1
3+
--- a/fsevents.js
4+
+++ b/fsevents.js
5+
@@ -36,11 +36,15 @@ module.exports.Constants = Native.Constants;
6+
var defer = global.setImmediate || process.nextTick;
7+
8+
function watch(path) {
9+
- var fse = new FSEvents(String(path || ''), handler);
10+
+ var VFS = require('./vfs');
11+
+ var vfs = new VFS(String(path || ''));
12+
+
13+
+ var fse = new FSEvents(vfs.resolvedPath, handler);
14+
EventEmitter.call(fse);
15+
return fse;
16+
17+
function handler(path, flags, id) {
18+
+ path = vfs.transpose(path);
19+
defer(function() {
20+
fse.emit('fsevent', path, flags, id);
21+
var info = getInfo(path, flags);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
diff --git a/fsevents.js b/fsevents.js
2+
semver exclusivity ^2
3+
--- a/fsevents.js
4+
+++ b/fsevents.js
5+
@@ -21,5 +21,7 @@ function watch(path, handler) {
6+
throw new TypeError(`fsevents argument 2 must be a function and not a ${typeof handler}`);
7+
}
8+
9+
- let instance = Native.start(path, handler);
10+
+ let VFS = require('./vfs');
11+
+ let vfs = new VFS(path);
12+
+ let instance = Native.start(vfs.resolvedPath, vfs.wrap(handler));
13+
if (!instance) throw new Error(`could not watch: ${path}`);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
diff --git a/vfs.js b/vfs.js
2+
new file mode 100644
3+
--- /dev/null
4+
+++ b/vfs.js
5+
@@ -0,0 +1,41 @@
6+
+const path = require(`path`);
7+
+
8+
+const NUMBER_REGEXP = /^[0-9]+$/;
9+
+const VIRTUAL_REGEXP = /^(\/(?:[^\/]+\/)*?\$\$virtual)((?:\/([^\/]+)(?:\/([^\/]+))?)?((?:\/.*)?))$/;
10+
+
11+
+function resolveVirtual(p) {
12+
+ const match = p.match(VIRTUAL_REGEXP);
13+
+ if (!match)
14+
+ return p;
15+
+
16+
+ const target = path.dirname(match[1]);
17+
+ if (!match[3] || !match[4])
18+
+ return target;
19+
+
20+
+ const isnum = NUMBER_REGEXP.test(match[4]);
21+
+ if (!isnum)
22+
+ return p;
23+
+
24+
+ const depth = Number(match[4]);
25+
+ const backstep = `../`.repeat(depth);
26+
+ const subpath = (match[5] || `.`);
27+
+
28+
+ return resolveVirtual(path.join(target, backstep, subpath));
29+
+}
30+
+
31+
+module.exports = class FsePnp {
32+
+ constructor(p) {
33+
+ this.normalizedPath = path.resolve(p);
34+
+ this.resolvedPath = resolveVirtual(this.normalizedPath);
35+
+ }
36+
+
37+
+ transpose(p) {
38+
+ return this.normalizedPath + p.substr(this.resolvedPath.length);
39+
+ }
40+
+
41+
+ wrap(fn) {
42+
+ return (path, ...args) => {
43+
+ return fn(this.transpose(path), ...args);
44+
+ };
45+
+ }
46+
+};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
set -ex
2+
3+
THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
4+
TEMP_DIR="$(mktemp -d)"
5+
6+
echo $TEMP_DIR
7+
8+
PATCHFILE="$THIS_DIR"/../../sources/patches/fsevents.patch.ts
9+
rm -f "$PATCHFILE" && touch "$PATCHFILE"
10+
11+
echo 'export const patch =' \
12+
>> "$PATCHFILE"
13+
(cat "$THIS_DIR"/1.2.11.patch \
14+
"$THIS_DIR"/2.1.2.patch \
15+
"$THIS_DIR"/common.patch) \
16+
> "$TEMP_DIR"/patch.tmp || true
17+
node "$THIS_DIR"/../jsonEscape.js < "$TEMP_DIR"/patch.tmp \
18+
>> "$PATCHFILE"
19+
echo ';' \
20+
>> "$PATCHFILE"

packages/plugin-compat/sources/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {Hooks as CoreHooks, Plugin, structUtils} from '@yarnpkg/core';
22
import {Hooks as PatchHooks} from '@yarnpkg/plugin-patch';
33

4+
import {patch as fseventsPatch} from './patches/fsevents.patch';
45
import {patch as resolvePatch} from './patches/resolve.patch';
56
import {patch as typescriptPatch} from './patches/typescript.patch';
67

78
const PATCHES = new Map([
9+
[structUtils.makeIdent(null, `fsevents`).identHash, fseventsPatch],
810
[structUtils.makeIdent(null, `resolve`).identHash, resolvePatch],
911
[structUtils.makeIdent(null, `typescript`).identHash, typescriptPatch],
1012
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const patch =
2+
"diff --git a/fsevents.js b/fsevents.js\nsemver exclusivity ^1\n--- a/fsevents.js\n+++ b/fsevents.js\n@@ -36,11 +36,15 @@ module.exports.Constants = Native.Constants;\n var defer = global.setImmediate || process.nextTick;\n\n function watch(path) {\n- var fse = new FSEvents(String(path || ''), handler);\n+ var VFS = require('./vfs');\n+ var vfs = new VFS(String(path || ''));\n+\n+ var fse = new FSEvents(vfs.resolvedPath, handler);\n EventEmitter.call(fse);\n return fse;\n\n function handler(path, flags, id) {\n+ path = vfs.transpose(path);\n defer(function() {\n fse.emit('fsevent', path, flags, id);\n var info = getInfo(path, flags);\ndiff --git a/fsevents.js b/fsevents.js\nsemver exclusivity ^2\n--- a/fsevents.js\n+++ b/fsevents.js\n@@ -21,5 +21,7 @@ function watch(path, handler) {\n throw new TypeError(`fsevents argument 2 must be a function and not a ${typeof handler}`);\n }\n\n- let instance = Native.start(path, handler);\n+ let VFS = require('./vfs');\n+ let vfs = new VFS(path);\n+ let instance = Native.start(vfs.resolvedPath, vfs.wrap(handler));\n if (!instance) throw new Error(`could not watch: ${path}`);\ndiff --git a/vfs.js b/vfs.js\nnew file mode 100644\n--- /dev/null\n+++ b/vfs.js\n@@ -0,0 +1,41 @@\n+const path = require(`path`);\n+\n+const NUMBER_REGEXP = /^[0-9]+$/;\n+const VIRTUAL_REGEXP = /^(\\/(?:[^\\/]+\\/)*?\\$\\$virtual)((?:\\/([^\\/]+)(?:\\/([^\\/]+))?)?((?:\\/.*)?))$/;\n+\n+function resolveVirtual(p) {\n+ const match = p.match(VIRTUAL_REGEXP);\n+ if (!match)\n+ return p;\n+\n+ const target = path.dirname(match[1]);\n+ if (!match[3] || !match[4])\n+ return target;\n+\n+ const isnum = NUMBER_REGEXP.test(match[4]);\n+ if (!isnum)\n+ return p;\n+\n+ const depth = Number(match[4]);\n+ const backstep = `../`.repeat(depth);\n+ const subpath = (match[5] || `.`);\n+\n+ return resolveVirtual(path.join(target, backstep, subpath));\n+}\n+\n+module.exports = class FsePnp {\n+ constructor(p) {\n+ this.normalizedPath = path.resolve(p);\n+ this.resolvedPath = resolveVirtual(this.normalizedPath);\n+ }\n+\n+ transpose(p) {\n+ return this.normalizedPath + p.substr(this.resolvedPath.length);\n+ }\n+\n+ wrap(fn) {\n+ return (path, ...args) => {\n+ return fn(this.transpose(path), ...args);\n+ };\n+ }\n+};\n"
3+
;

0 commit comments

Comments
 (0)