@c9up/ream 0.2.23 → 0.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Codemods.d.ts +1 -0
- package/dist/Codemods.d.ts.map +1 -1
- package/dist/Codemods.js +44 -0
- package/dist/Codemods.js.map +1 -1
- package/dist/Ignitor.d.ts +50 -0
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +15 -15
- package/dist/Ignitor.js.map +1 -1
- package/dist/dev/fullReload.d.ts +23 -0
- package/dist/dev/fullReload.d.ts.map +1 -0
- package/dist/dev/fullReload.js +26 -0
- package/dist/dev/fullReload.js.map +1 -0
- package/dist/dev/hmr.d.ts +52 -4
- package/dist/dev/hmr.d.ts.map +1 -1
- package/dist/dev/hmr.js +75 -9
- package/dist/dev/hmr.js.map +1 -1
- package/dist/dev/hotGlob.d.ts +35 -0
- package/dist/dev/hotGlob.d.ts.map +1 -0
- package/dist/dev/hotGlob.js +92 -0
- package/dist/dev/hotGlob.js.map +1 -0
- package/dist/dev/hotGraph.d.ts +58 -0
- package/dist/dev/hotGraph.d.ts.map +1 -0
- package/dist/dev/hotGraph.js +91 -0
- package/dist/dev/hotGraph.js.map +1 -0
- package/dist/dev/hotWatcher.d.ts +34 -0
- package/dist/dev/hotWatcher.d.ts.map +1 -0
- package/dist/dev/hotWatcher.js +72 -0
- package/dist/dev/hotWatcher.js.map +1 -0
- package/dist/env/loadEnvFiles.d.ts +13 -0
- package/dist/env/loadEnvFiles.d.ts.map +1 -1
- package/dist/env/loadEnvFiles.js +65 -13
- package/dist/env/loadEnvFiles.js.map +1 -1
- package/dist/hot.d.ts +13 -16
- package/dist/hot.d.ts.map +1 -1
- package/dist/hot.js +161 -84
- package/dist/hot.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +4 -7
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/Codemods.ts +53 -0
- package/src/Ignitor.ts +68 -14
- package/src/dev/fullReload.ts +34 -0
- package/src/dev/hmr.ts +102 -9
- package/src/dev/hotGlob.ts +91 -0
- package/src/dev/hotGraph.ts +109 -0
- package/src/dev/hotWatcher.ts +89 -0
- package/src/env/loadEnvFiles.ts +69 -14
- package/src/hot.ts +169 -102
- package/src/index.ts +1 -1
package/dist/dev/hmr.js
CHANGED
|
@@ -36,6 +36,67 @@ function holder() {
|
|
|
36
36
|
export function hotReloadHappened() {
|
|
37
37
|
const store = holder();
|
|
38
38
|
store[COUNTER] = (store[COUNTER] ?? 0) + 1;
|
|
39
|
+
notifyClients();
|
|
40
|
+
}
|
|
41
|
+
const clients = new Set();
|
|
42
|
+
/** The event name the injected script listens for. */
|
|
43
|
+
const HMR_EVENT = 'ream:hmr';
|
|
44
|
+
/**
|
|
45
|
+
* Answer the dev client BEFORE any application middleware runs, and PUSH.
|
|
46
|
+
*
|
|
47
|
+
* Two problems, one change.
|
|
48
|
+
*
|
|
49
|
+
* It used to be an ordinary route polled once a second, per open tab, and a
|
|
50
|
+
* route runs the whole middleware stack. An application that resolves a user
|
|
51
|
+
* from a session cookie therefore ran `SELECT * FROM users` once a second for
|
|
52
|
+
* a response whose entire content is a process id and a counter, and which
|
|
53
|
+
* depends on no user, no session and no body.
|
|
54
|
+
*
|
|
55
|
+
* It is a stream now, so the cost is paid ONCE per tab instead of once a
|
|
56
|
+
* second, and a change reaches the browser immediately rather than up to a
|
|
57
|
+
* second later. Upstream gets the same thing from Vite's websocket; this needs
|
|
58
|
+
* no bundler, because the server already speaks SSE.
|
|
59
|
+
*
|
|
60
|
+
* Registered FIRST rather than merely outside the router: middleware that
|
|
61
|
+
* authenticates lives at either tier, and an application is free to put it in
|
|
62
|
+
* `server.use([...])` — many do, because it must run before routing.
|
|
63
|
+
*
|
|
64
|
+
* Development only. In production nothing injects the script and nothing
|
|
65
|
+
* registers this.
|
|
66
|
+
*/
|
|
67
|
+
export function hmrEndpoint() {
|
|
68
|
+
return async (ctx, next) => {
|
|
69
|
+
// `url()` carries the query string; a client may append a cache-buster.
|
|
70
|
+
const path = ctx.request.url().split('?')[0];
|
|
71
|
+
if (path !== HMR_PATH) {
|
|
72
|
+
await next();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let stream;
|
|
76
|
+
try {
|
|
77
|
+
stream = await ctx.response.sse();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// A host without the streaming NAPI — a mock server in a test, say.
|
|
81
|
+
// Losing the reload is fine; failing the request is not.
|
|
82
|
+
await next();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
clients.add(stream);
|
|
86
|
+
stream.onClose(() => clients.delete(stream));
|
|
87
|
+
// Immediately, so a tab that connects after a restart compares against the
|
|
88
|
+
// CURRENT token rather than waiting for the next swap to learn anything.
|
|
89
|
+
await stream.send(HMR_EVENT, hmrToken());
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Tell every open tab the token moved. */
|
|
93
|
+
function notifyClients() {
|
|
94
|
+
const token = hmrToken();
|
|
95
|
+
for (const client of clients) {
|
|
96
|
+
// Detached: a swap must not wait on a socket, and a dead one is dropped by
|
|
97
|
+
// its own close handler.
|
|
98
|
+
void client.send(HMR_EVENT, token).catch(() => clients.delete(client));
|
|
99
|
+
}
|
|
39
100
|
}
|
|
40
101
|
/** How many hot swaps this process has seen. */
|
|
41
102
|
export function hotReloadCount() {
|
|
@@ -51,10 +112,15 @@ export function hmrToken() {
|
|
|
51
112
|
/**
|
|
52
113
|
* The script injected into HTML pages in dev.
|
|
53
114
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
115
|
+
* An `EventSource`, not a poll. The server pushes the token, so a change
|
|
116
|
+
* reaches the page immediately and an idle tab costs one open connection
|
|
117
|
+
* rather than a request per second through the whole middleware stack.
|
|
118
|
+
*
|
|
119
|
+
* It reloads when the token CHANGES, never when the connection fails: a
|
|
120
|
+
* restarting server is unreachable for a moment, and reloading then would only
|
|
121
|
+
* show the browser's error page. `EventSource` reconnects on its own, and the
|
|
122
|
+
* server greets the new connection with its token — which after a restart is a
|
|
123
|
+
* different boot id, so the reload happens then, once the server can serve it.
|
|
58
124
|
*/
|
|
59
125
|
export function hmrClientScript(nonce) {
|
|
60
126
|
// A nonce-based CSP — which the scaffold ships, via `@c9up/blackhole` —
|
|
@@ -63,10 +129,10 @@ export function hmrClientScript(nonce) {
|
|
|
63
129
|
// are escaped because a nonce is generated, not user input, but a broken
|
|
64
130
|
// attribute would silently disable the tag rather than fail loudly.
|
|
65
131
|
const attribute = nonce === undefined ? '' : ` nonce="${nonce.replace(/"/g, '"')}"`;
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
return `<script${attribute}>(()=>{let t=null;const
|
|
132
|
+
// `addEventListener(HMR_EVENT)`, NOT `onmessage`: the stream names its
|
|
133
|
+
// events, and `onmessage` fires only for unnamed ones — a client wired that
|
|
134
|
+
// way receives nothing at all, silently, which is a mistake this codebase
|
|
135
|
+
// has already made once with relay.
|
|
136
|
+
return `<script${attribute}>(()=>{let t=null;const s=new EventSource(${JSON.stringify(HMR_PATH)});s.addEventListener(${JSON.stringify(HMR_EVENT)},e=>{let n;try{n=JSON.parse(e.data)}catch(_){n=e.data}if(t===null){t=n}else if(n!==t){s.close();location.reload()}})})()</script>`;
|
|
71
137
|
}
|
|
72
138
|
//# sourceMappingURL=hmr.js.map
|
package/dist/dev/hmr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../../src/dev/hmr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,QAAQ,GAAG,aAAa,CAAA;AAErC;;;GAGG;AACH,MAAM,OAAO,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAA;AAE3D,qEAAqE;AACrE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;AAMrD;;;;;GAKG;AACH,SAAS,MAAM;IACb,OAAO,UAAsC,CAAA;AAC/C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAA;IACtB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../../src/dev/hmr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching the globs an application declares under `hotHook.boundaries`.
|
|
3
|
+
*
|
|
4
|
+
* A dependency for this would be `picomatch` plus `fast-glob`, which is two
|
|
5
|
+
* packages and their trees for the three wildcards these patterns actually use.
|
|
6
|
+
* The patterns are written by hand in a `package.json` and read once at boot;
|
|
7
|
+
* what they need is to be right, not fast.
|
|
8
|
+
*
|
|
9
|
+
* The dialect is the one those files already use:
|
|
10
|
+
* * any run of characters inside one path segment
|
|
11
|
+
* ** any number of segments, including none
|
|
12
|
+
* ? exactly one character inside a segment
|
|
13
|
+
* {a,b} either alternative
|
|
14
|
+
*
|
|
15
|
+
* The brace form is the one a `metaFiles` entry uses —
|
|
16
|
+
* `resources/lang/**\/*.{json,yaml,yml}` — and it has to be understood on this
|
|
17
|
+
* side too: the CLI matches the same pattern when it copies those files into
|
|
18
|
+
* the build, and a dialect that differed between the two would let an entry
|
|
19
|
+
* copy at build time and never fire in development, or the reverse.
|
|
20
|
+
*
|
|
21
|
+
* A leading `./` is optional, and matching is done on POSIX-shaped paths
|
|
22
|
+
* relative to the project root, so a Windows checkout and a Linux one read the
|
|
23
|
+
* same pattern the same way.
|
|
24
|
+
*/
|
|
25
|
+
/** Turn one glob into an anchored regular expression. */
|
|
26
|
+
export declare function globToRegExp(pattern: string): RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* A matcher over several globs.
|
|
29
|
+
*
|
|
30
|
+
* Returns a predicate rather than a class: the graph takes a function, and a
|
|
31
|
+
* project with no boundaries gets one that is honestly always false instead of
|
|
32
|
+
* an empty object that has to be checked for.
|
|
33
|
+
*/
|
|
34
|
+
export declare function globMatcher(patterns: readonly string[]): (path: string) => boolean;
|
|
35
|
+
//# sourceMappingURL=hotGlob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotGlob.d.ts","sourceRoot":"","sources":["../../src/dev/hotGlob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgDpD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAOlF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching the globs an application declares under `hotHook.boundaries`.
|
|
3
|
+
*
|
|
4
|
+
* A dependency for this would be `picomatch` plus `fast-glob`, which is two
|
|
5
|
+
* packages and their trees for the three wildcards these patterns actually use.
|
|
6
|
+
* The patterns are written by hand in a `package.json` and read once at boot;
|
|
7
|
+
* what they need is to be right, not fast.
|
|
8
|
+
*
|
|
9
|
+
* The dialect is the one those files already use:
|
|
10
|
+
* * any run of characters inside one path segment
|
|
11
|
+
* ** any number of segments, including none
|
|
12
|
+
* ? exactly one character inside a segment
|
|
13
|
+
* {a,b} either alternative
|
|
14
|
+
*
|
|
15
|
+
* The brace form is the one a `metaFiles` entry uses —
|
|
16
|
+
* `resources/lang/**\/*.{json,yaml,yml}` — and it has to be understood on this
|
|
17
|
+
* side too: the CLI matches the same pattern when it copies those files into
|
|
18
|
+
* the build, and a dialect that differed between the two would let an entry
|
|
19
|
+
* copy at build time and never fire in development, or the reverse.
|
|
20
|
+
*
|
|
21
|
+
* A leading `./` is optional, and matching is done on POSIX-shaped paths
|
|
22
|
+
* relative to the project root, so a Windows checkout and a Linux one read the
|
|
23
|
+
* same pattern the same way.
|
|
24
|
+
*/
|
|
25
|
+
/** Turn one glob into an anchored regular expression. */
|
|
26
|
+
export function globToRegExp(pattern) {
|
|
27
|
+
const cleaned = pattern.replace(/^\.\//, '');
|
|
28
|
+
let out = '';
|
|
29
|
+
for (let i = 0; i < cleaned.length; i += 1) {
|
|
30
|
+
const char = cleaned[i];
|
|
31
|
+
if (char === '*') {
|
|
32
|
+
if (cleaned[i + 1] === '*') {
|
|
33
|
+
// `**/` swallows the separator so that `a/**/b` also matches `a/b`;
|
|
34
|
+
// a trailing `**` simply matches the rest.
|
|
35
|
+
i += 1;
|
|
36
|
+
if (cleaned[i + 1] === '/') {
|
|
37
|
+
i += 1;
|
|
38
|
+
out += '(?:[^/]+/)*';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
out += '.*';
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
out += '[^/]*';
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === '?') {
|
|
49
|
+
out += '[^/]';
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (char === '{') {
|
|
53
|
+
const close = cleaned.indexOf('}', i);
|
|
54
|
+
if (close !== -1) {
|
|
55
|
+
// Alternatives are literal text, never nested globs: that is the whole
|
|
56
|
+
// of the form these patterns use, and a recursive reading would accept
|
|
57
|
+
// shapes the CLI's matcher does not.
|
|
58
|
+
const options = cleaned
|
|
59
|
+
.slice(i + 1, close)
|
|
60
|
+
.split(',')
|
|
61
|
+
.map((option) => option.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
62
|
+
out += `(?:${options.join('|')})`;
|
|
63
|
+
i = close;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Unclosed: read literally rather than throwing. A pattern is a string in
|
|
67
|
+
// a config file, and a malformed one should match nothing, not take the
|
|
68
|
+
// dev server down at boot.
|
|
69
|
+
}
|
|
70
|
+
// Everything else is literal, including the characters a regular
|
|
71
|
+
// expression would otherwise read as syntax.
|
|
72
|
+
out += (char ?? '').replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
73
|
+
}
|
|
74
|
+
return new RegExp(`^${out}$`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A matcher over several globs.
|
|
78
|
+
*
|
|
79
|
+
* Returns a predicate rather than a class: the graph takes a function, and a
|
|
80
|
+
* project with no boundaries gets one that is honestly always false instead of
|
|
81
|
+
* an empty object that has to be checked for.
|
|
82
|
+
*/
|
|
83
|
+
export function globMatcher(patterns) {
|
|
84
|
+
const expressions = patterns.map(globToRegExp);
|
|
85
|
+
if (expressions.length === 0)
|
|
86
|
+
return () => false;
|
|
87
|
+
return (path) => {
|
|
88
|
+
const normalised = path.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
89
|
+
return expressions.some((expression) => expression.test(normalised));
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=hotGlob.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotGlob.js","sourceRoot":"","sources":["../../src/dev/hotGlob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAC5C,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC3B,oEAAoE;gBACpE,2CAA2C;gBAC3C,CAAC,IAAI,CAAC,CAAA;gBACN,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC3B,CAAC,IAAI,CAAC,CAAA;oBACN,GAAG,IAAI,aAAa,CAAA;gBACtB,CAAC;qBAAM,CAAC;oBACN,GAAG,IAAI,IAAI,CAAA;gBACb,CAAC;gBACD,SAAQ;YACV,CAAC;YACD,GAAG,IAAI,OAAO,CAAA;YACd,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,GAAG,IAAI,MAAM,CAAA;YACb,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,uEAAuE;gBACvE,uEAAuE;gBACvE,qCAAqC;gBACrC,MAAM,OAAO,GAAG,OAAO;qBACpB,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;qBACnB,KAAK,CAAC,GAAG,CAAC;qBACV,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAA;gBACjE,GAAG,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;gBACjC,CAAC,GAAG,KAAK,CAAA;gBACT,SAAQ;YACV,CAAC;YACD,0EAA0E;YAC1E,wEAAwE;YACxE,2BAA2B;QAC7B,CAAC;QACD,iEAAiE;QACjE,6CAA6C;QAC7C,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAA;IAC1D,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,QAA2B;IACrD,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAC9C,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,EAAE,CAAC,KAAK,CAAA;IAChD,OAAO,CAAC,IAAY,EAAE,EAAE;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAChE,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAA;IACtE,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The import graph a hot reload reasons over, and what it decides.
|
|
3
|
+
*
|
|
4
|
+
* Owned here rather than taken from `hot-hook`, which used to do this job. The
|
|
5
|
+
* package is 655 lines and four npm dependencies, and it had to be installed by
|
|
6
|
+
* every application because a dev-only loader has no business in a production
|
|
7
|
+
* install of the framework — so a project created before that convention simply
|
|
8
|
+
* lost hot reloading and was told nothing. The part that genuinely needs a
|
|
9
|
+
* library is the part Node now provides itself: `module.registerHooks` for the
|
|
10
|
+
* graph, `fs.watch` for the changes.
|
|
11
|
+
*
|
|
12
|
+
* NO node_modules dependency, deliberately: everything below is arithmetic on
|
|
13
|
+
* paths.
|
|
14
|
+
*
|
|
15
|
+
* The graph is built by the resolver as the program runs, never by reading
|
|
16
|
+
* files. A `resolve` hook is handed the importer and the imported, which is
|
|
17
|
+
* exactly an edge, and it sees dynamic imports as they happen — a static parse
|
|
18
|
+
* of the source could only guess at those.
|
|
19
|
+
*/
|
|
20
|
+
/** What a change to one file means. */
|
|
21
|
+
export type Decision = {
|
|
22
|
+
kind: 'swap';
|
|
23
|
+
invalidated: string[];
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'reload';
|
|
26
|
+
file: string;
|
|
27
|
+
reason: 'outside-boundaries' | 'restart-list';
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'ignore';
|
|
30
|
+
};
|
|
31
|
+
export declare class HotGraph {
|
|
32
|
+
#private;
|
|
33
|
+
constructor(options: {
|
|
34
|
+
isBoundary: (file: string) => boolean;
|
|
35
|
+
isRestart: (file: string) => boolean;
|
|
36
|
+
});
|
|
37
|
+
/** Record that `importer` imported `file`. Called from the resolve hook. */
|
|
38
|
+
link(file: string, importer: string | undefined): void;
|
|
39
|
+
/** The version to hang off the URL, or 0 while the module has never been invalidated. */
|
|
40
|
+
version(file: string): number;
|
|
41
|
+
/** Whether the resolver has ever seen this file. */
|
|
42
|
+
knows(file: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* What to do about `file` having changed.
|
|
45
|
+
*
|
|
46
|
+
* Walks up from the changed file collecting what has to be re-imported, and
|
|
47
|
+
* stops at each boundary: a boundary is imported dynamically, so the next
|
|
48
|
+
* import of it re-resolves, and with it everything below that was collected
|
|
49
|
+
* on the way. A path that reaches a module nobody imports without crossing a
|
|
50
|
+
* boundary cannot be re-imported by anyone, and that is a full reload.
|
|
51
|
+
*
|
|
52
|
+
* Deciding before invalidating matters: a decision that turns out to be a
|
|
53
|
+
* full reload must leave no version bumped behind, or a later swap would
|
|
54
|
+
* hand the caller a URL for a module the process never re-imported.
|
|
55
|
+
*/
|
|
56
|
+
decide(file: string): Decision;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=hotGraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotGraph.d.ts","sourceRoot":"","sources":["../../src/dev/hotGraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,uCAAuC;AACvC,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAAA;CAAE,GAC/E;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEtB,qBAAa,QAAQ;;gBAKP,OAAO,EAAE;QACnB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;QACrC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;KACrC;IAcD,4EAA4E;IAC5E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKtD,yFAAyF;IACzF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI7B,oDAAoD;IACpD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI5B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ;CAuB/B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The import graph a hot reload reasons over, and what it decides.
|
|
3
|
+
*
|
|
4
|
+
* Owned here rather than taken from `hot-hook`, which used to do this job. The
|
|
5
|
+
* package is 655 lines and four npm dependencies, and it had to be installed by
|
|
6
|
+
* every application because a dev-only loader has no business in a production
|
|
7
|
+
* install of the framework — so a project created before that convention simply
|
|
8
|
+
* lost hot reloading and was told nothing. The part that genuinely needs a
|
|
9
|
+
* library is the part Node now provides itself: `module.registerHooks` for the
|
|
10
|
+
* graph, `fs.watch` for the changes.
|
|
11
|
+
*
|
|
12
|
+
* NO node_modules dependency, deliberately: everything below is arithmetic on
|
|
13
|
+
* paths.
|
|
14
|
+
*
|
|
15
|
+
* The graph is built by the resolver as the program runs, never by reading
|
|
16
|
+
* files. A `resolve` hook is handed the importer and the imported, which is
|
|
17
|
+
* exactly an edge, and it sees dynamic imports as they happen — a static parse
|
|
18
|
+
* of the source could only guess at those.
|
|
19
|
+
*/
|
|
20
|
+
export class HotGraph {
|
|
21
|
+
#nodes = new Map();
|
|
22
|
+
#isBoundary;
|
|
23
|
+
#isRestart;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.#isBoundary = options.isBoundary;
|
|
26
|
+
this.#isRestart = options.isRestart;
|
|
27
|
+
}
|
|
28
|
+
#node(file) {
|
|
29
|
+
let node = this.#nodes.get(file);
|
|
30
|
+
if (node === undefined) {
|
|
31
|
+
node = { importers: new Set(), version: 0 };
|
|
32
|
+
this.#nodes.set(file, node);
|
|
33
|
+
}
|
|
34
|
+
return node;
|
|
35
|
+
}
|
|
36
|
+
/** Record that `importer` imported `file`. Called from the resolve hook. */
|
|
37
|
+
link(file, importer) {
|
|
38
|
+
const node = this.#node(file);
|
|
39
|
+
if (importer !== undefined && importer !== file)
|
|
40
|
+
node.importers.add(importer);
|
|
41
|
+
}
|
|
42
|
+
/** The version to hang off the URL, or 0 while the module has never been invalidated. */
|
|
43
|
+
version(file) {
|
|
44
|
+
return this.#nodes.get(file)?.version ?? 0;
|
|
45
|
+
}
|
|
46
|
+
/** Whether the resolver has ever seen this file. */
|
|
47
|
+
knows(file) {
|
|
48
|
+
return this.#nodes.has(file);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What to do about `file` having changed.
|
|
52
|
+
*
|
|
53
|
+
* Walks up from the changed file collecting what has to be re-imported, and
|
|
54
|
+
* stops at each boundary: a boundary is imported dynamically, so the next
|
|
55
|
+
* import of it re-resolves, and with it everything below that was collected
|
|
56
|
+
* on the way. A path that reaches a module nobody imports without crossing a
|
|
57
|
+
* boundary cannot be re-imported by anyone, and that is a full reload.
|
|
58
|
+
*
|
|
59
|
+
* Deciding before invalidating matters: a decision that turns out to be a
|
|
60
|
+
* full reload must leave no version bumped behind, or a later swap would
|
|
61
|
+
* hand the caller a URL for a module the process never re-imported.
|
|
62
|
+
*/
|
|
63
|
+
decide(file) {
|
|
64
|
+
if (this.#isRestart(file))
|
|
65
|
+
return { kind: 'reload', file, reason: 'restart-list' };
|
|
66
|
+
if (!this.#nodes.has(file))
|
|
67
|
+
return { kind: 'ignore' };
|
|
68
|
+
const collected = new Set();
|
|
69
|
+
const queue = [file];
|
|
70
|
+
while (queue.length > 0) {
|
|
71
|
+
const current = queue.pop();
|
|
72
|
+
if (current === undefined || collected.has(current))
|
|
73
|
+
continue;
|
|
74
|
+
collected.add(current);
|
|
75
|
+
// A boundary is where the walk stops: whoever imports it does so
|
|
76
|
+
// dynamically and will ask for it again.
|
|
77
|
+
if (this.#isBoundary(current))
|
|
78
|
+
continue;
|
|
79
|
+
const importers = this.#nodes.get(current)?.importers;
|
|
80
|
+
if (importers === undefined || importers.size === 0) {
|
|
81
|
+
return { kind: 'reload', file, reason: 'outside-boundaries' };
|
|
82
|
+
}
|
|
83
|
+
for (const importer of importers)
|
|
84
|
+
queue.push(importer);
|
|
85
|
+
}
|
|
86
|
+
for (const path of collected)
|
|
87
|
+
this.#node(path).version += 1;
|
|
88
|
+
return { kind: 'swap', invalidated: [...collected] };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=hotGraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotGraph.js","sourceRoot":"","sources":["../../src/dev/hotGraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching the project for the changes a hot reload reacts to.
|
|
3
|
+
*
|
|
4
|
+
* `fs.watch` with `recursive: true`, which Node implements on Linux, macOS and
|
|
5
|
+
* Windows alike — the reason a watcher used to be a dependency here, and is not
|
|
6
|
+
* any more.
|
|
7
|
+
*
|
|
8
|
+
* Two behaviours are worth stating because they are not obvious from the API:
|
|
9
|
+
*
|
|
10
|
+
* A save is rarely one event. Editors write, rename and touch, and a single
|
|
11
|
+
* Ctrl-S can arrive as several notifications for the same file within a few
|
|
12
|
+
* milliseconds. Each one would walk the graph and bump a version, so they are
|
|
13
|
+
* coalesced.
|
|
14
|
+
*
|
|
15
|
+
* `ream dev` also watches from the outside, in Rust. That is not a duplicate:
|
|
16
|
+
* this watcher dies with the process it lives in, so a server that crashed on a
|
|
17
|
+
* syntax error has nothing left to notice the fix. The outer one covers exactly
|
|
18
|
+
* that window, and hands back over by restarting the process.
|
|
19
|
+
*/
|
|
20
|
+
export interface WatcherOptions {
|
|
21
|
+
/** Project root; every reported path is absolute, resolved against it. */
|
|
22
|
+
root: string;
|
|
23
|
+
/** Directories under the root to watch, when they exist. */
|
|
24
|
+
directories: readonly string[];
|
|
25
|
+
/** Individual files to watch — env files sit at the root, so no directory covers them. */
|
|
26
|
+
files: readonly string[];
|
|
27
|
+
/** Called once per settled change, with an absolute path. */
|
|
28
|
+
onChange: (file: string) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface Watcher {
|
|
31
|
+
close: () => void;
|
|
32
|
+
}
|
|
33
|
+
export declare function watchProject(options: WatcherOptions): Watcher;
|
|
34
|
+
//# sourceMappingURL=hotWatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotWatcher.d.ts","sourceRoot":"","sources":["../../src/dev/hotWatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,0FAA0F;IAC1F,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,6DAA6D;IAC7D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA+C7D"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching the project for the changes a hot reload reacts to.
|
|
3
|
+
*
|
|
4
|
+
* `fs.watch` with `recursive: true`, which Node implements on Linux, macOS and
|
|
5
|
+
* Windows alike — the reason a watcher used to be a dependency here, and is not
|
|
6
|
+
* any more.
|
|
7
|
+
*
|
|
8
|
+
* Two behaviours are worth stating because they are not obvious from the API:
|
|
9
|
+
*
|
|
10
|
+
* A save is rarely one event. Editors write, rename and touch, and a single
|
|
11
|
+
* Ctrl-S can arrive as several notifications for the same file within a few
|
|
12
|
+
* milliseconds. Each one would walk the graph and bump a version, so they are
|
|
13
|
+
* coalesced.
|
|
14
|
+
*
|
|
15
|
+
* `ream dev` also watches from the outside, in Rust. That is not a duplicate:
|
|
16
|
+
* this watcher dies with the process it lives in, so a server that crashed on a
|
|
17
|
+
* syntax error has nothing left to notice the fix. The outer one covers exactly
|
|
18
|
+
* that window, and hands back over by restarting the process.
|
|
19
|
+
*/
|
|
20
|
+
import { watch } from 'node:fs';
|
|
21
|
+
import { join, resolve } from 'node:path';
|
|
22
|
+
/** How long to wait for a save to finish arriving before acting on it. */
|
|
23
|
+
const SETTLE_MS = 40;
|
|
24
|
+
export function watchProject(options) {
|
|
25
|
+
const watchers = [];
|
|
26
|
+
const pending = new Map();
|
|
27
|
+
const report = (file) => {
|
|
28
|
+
const existing = pending.get(file);
|
|
29
|
+
if (existing !== undefined)
|
|
30
|
+
clearTimeout(existing);
|
|
31
|
+
const timer = setTimeout(() => {
|
|
32
|
+
pending.delete(file);
|
|
33
|
+
options.onChange(file);
|
|
34
|
+
}, SETTLE_MS);
|
|
35
|
+
// The dev server must still be able to exit while a save is settling.
|
|
36
|
+
timer.unref?.();
|
|
37
|
+
pending.set(file, timer);
|
|
38
|
+
};
|
|
39
|
+
for (const directory of options.directories) {
|
|
40
|
+
const absolute = resolve(options.root, directory);
|
|
41
|
+
try {
|
|
42
|
+
watchers.push(watch(absolute, { recursive: true }, (_event, name) => {
|
|
43
|
+
if (typeof name !== 'string' || name === '')
|
|
44
|
+
return;
|
|
45
|
+
report(join(absolute, name));
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// A directory this project does not have is not an error: the list is
|
|
50
|
+
// the framework's conventional layout, not a requirement.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const file of options.files) {
|
|
54
|
+
const absolute = resolve(options.root, file);
|
|
55
|
+
try {
|
|
56
|
+
watchers.push(watch(absolute, () => report(absolute)));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Same: a `.env` that does not exist is simply not watched.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
close: () => {
|
|
64
|
+
for (const timer of pending.values())
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
pending.clear();
|
|
67
|
+
for (const watcher of watchers)
|
|
68
|
+
watcher.close();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=hotWatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotWatcher.js","sourceRoot":"","sources":["../../src/dev/hotWatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAkB,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEzC,0EAA0E;AAC1E,MAAM,SAAS,GAAG,EAAE,CAAA;AAiBpB,MAAM,UAAU,YAAY,CAAC,OAAuB;IAClD,MAAM,QAAQ,GAAgB,EAAE,CAAA;IAChC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAA;IAEjD,MAAM,MAAM,GAAG,CAAC,IAAY,EAAQ,EAAE;QACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,QAAQ,KAAK,SAAS;YAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;QAClD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACpB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC,EAAE,SAAS,CAAC,CAAA;QACb,sEAAsE;QACtE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;QACf,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC1B,CAAC,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QACjD,IAAI,CAAC;YACH,QAAQ,CAAC,IAAI,CACX,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;gBACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;oBAAE,OAAM;gBACnD,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;YAC9B,CAAC,CAAC,CACH,CAAA;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;YACtE,0DAA0D;QAC5D,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5C,IAAI,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,GAAG,EAAE;YACV,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAA;YACzD,OAAO,CAAC,KAAK,EAAE,CAAA;YACf,KAAK,MAAM,OAAO,IAAI,QAAQ;gBAAE,OAAO,CAAC,KAAK,EAAE,CAAA;QACjD,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.env*` files that will be read, most-specific first.
|
|
3
|
+
*
|
|
4
|
+
* Exported because `@c9up/ream/hot` has to WATCH exactly this list: nothing
|
|
5
|
+
* imports an env file, so hot-hook's dependency tree never sees one and an
|
|
6
|
+
* edit to `.env` changed nothing at all. A second copy of the list in the
|
|
7
|
+
* loader entry would drift from this one the first time the naming changes.
|
|
8
|
+
*
|
|
9
|
+
* @param options.skipEnvLocal Skip `.env.local` — see {@link loadEnvFiles}.
|
|
10
|
+
*/
|
|
11
|
+
export declare function envFileNames(options?: {
|
|
12
|
+
skipEnvLocal?: boolean;
|
|
13
|
+
}): string[];
|
|
1
14
|
/**
|
|
2
15
|
* Load `.env` files into `process.env` — the shared primitive behind both the
|
|
3
16
|
* Ignitor (HTTP/console boot) and `Env.create()` (config/test flow), mirroring
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadEnvFiles.d.ts","sourceRoot":"","sources":["../../src/env/loadEnvFiles.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"loadEnvFiles.d.ts","sourceRoot":"","sources":["../../src/env/loadEnvFiles.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,EAAE,CAW/E;AA4BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CAsCzF"}
|