cindel 1.0.2 → 1.0.4
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/README.md +77 -21
- package/dist/client/hmr-client.d.ts +4 -1
- package/dist/client/hmr-client.d.ts.map +1 -1
- package/dist/client.iife.js +14 -10
- package/dist/client.iife.js.map +2 -2
- package/dist/client.iife.min.js +1 -1
- package/dist/client.iife.min.js.map +3 -3
- package/dist/client.js +14 -10
- package/dist/client.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/cindel)
|
|
6
6
|
[](https://bundlephobia.com/package/cindel@latest)
|
|
7
7
|
[](LICENSE)
|
|
8
|
+
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
## Features
|
|
@@ -387,25 +388,26 @@ process.on("SIGINT", () => server.stop().then(() => process.exit(0)));
|
|
|
387
388
|
- **`string`** treated as a full WebSocket URL
|
|
388
389
|
- **`object`** full config, see below
|
|
389
390
|
|
|
390
|
-
| Option | Type | Default
|
|
391
|
-
| ------------------- | ------------------------------------ |
|
|
392
|
-
| `port` | `number` |
|
|
393
|
-
| `host` | `string` | `'localhost'`
|
|
394
|
-
| `secure` | `boolean` | `false`
|
|
395
|
-
| `wsUrl` | `string` |
|
|
396
|
-
| `httpUrl` | `string` |
|
|
397
|
-
| `wsPath` | `string` | `'/hmr'`
|
|
398
|
-
| `autoReconnect` | `boolean` | `true`
|
|
399
|
-
| `reconnectDelay` | `number` | `2000`
|
|
400
|
-
| `maxReconnectDelay` | `number` | `30000`
|
|
401
|
-
| `skipOnReconnect` | `boolean` | `true`
|
|
402
|
-
| `skip` | `string[]` |
|
|
403
|
-
| `filterSkip` | `(file, allFiles) => boolean` |
|
|
404
|
-
| `cold` | `string[]` |
|
|
405
|
-
| `filterCold` | `(file) => boolean` |
|
|
406
|
-
| `getOverrideTarget` | `(file, allFiles) => string \| null` |
|
|
407
|
-
| `onFileLoaded` | `(file) => void` |
|
|
408
|
-
| `
|
|
391
|
+
| Option | Type | Default | Description |
|
|
392
|
+
| ------------------- | ------------------------------------ | ------------- | ----------------------------------------------------------------- |
|
|
393
|
+
| `port` | `number` | | Port number |
|
|
394
|
+
| `host` | `string` | `'localhost'` | Hostname |
|
|
395
|
+
| `secure` | `boolean` | `false` | Use `wss://` and `https://` |
|
|
396
|
+
| `wsUrl` | `string` | | Explicit WebSocket URL, overrides host/port |
|
|
397
|
+
| `httpUrl` | `string` | | Explicit HTTP base URL for file fetching |
|
|
398
|
+
| `wsPath` | `string` | `'/hmr'` | WebSocket path |
|
|
399
|
+
| `autoReconnect` | `boolean` | `true` | Reconnect on disconnect with exponential backoff |
|
|
400
|
+
| `reconnectDelay` | `number` | `2000` | Base reconnect delay in ms |
|
|
401
|
+
| `maxReconnectDelay` | `number` | `30000` | Maximum reconnect delay cap in ms |
|
|
402
|
+
| `skipOnReconnect` | `boolean` | `true` | Skip files already loaded in the page when the server reconnects |
|
|
403
|
+
| `skip` | `string[]` | | Glob patterns for files to never load |
|
|
404
|
+
| `filterSkip` | `(file, allFiles) => boolean` | | Custom skip logic, OR'd with `skip` |
|
|
405
|
+
| `cold` | `string[]` | | Glob patterns that emit a `cold` event instead of reloading |
|
|
406
|
+
| `filterCold` | `(file) => boolean` | | Custom cold logic, OR'd with `cold` |
|
|
407
|
+
| `getOverrideTarget` | `(file, allFiles) => string \| null` | | Map an override file to the original it replaces |
|
|
408
|
+
| `onFileLoaded` | `(file) => void` | | Called after each file is loaded or reloaded |
|
|
409
|
+
| `loadOrder` | `Stage[]` | | Extra stages prepended before the built-in sorting |
|
|
410
|
+
| `sortFiles` | `(files) => string[]` | | Fully replaces the default sort. When set, `loadOrder` is ignored |
|
|
409
411
|
|
|
410
412
|
### Methods
|
|
411
413
|
|
|
@@ -470,7 +472,7 @@ client
|
|
|
470
472
|
|
|
471
473
|
### Skip and Cold Filters
|
|
472
474
|
|
|
473
|
-
`skip` prevents files from ever being loaded by the client. `cold` marks files that
|
|
475
|
+
`skip` prevents files from ever being loaded by the client. `cold` marks files that emit a `cold` event instead of being hot-reloaded, what happens next is up to your `cold` event handler. Both options accept glob patterns, a custom filter function, or both combined via OR logic. Client and server `cold` patterns are merged on connect.
|
|
474
476
|
|
|
475
477
|
> **Note:** Glob patterns are always relative to the project root, not the watched directory.
|
|
476
478
|
|
|
@@ -486,7 +488,7 @@ new HMRClient({
|
|
|
486
488
|
return allFiles.includes(file.replace(".override.js", ".js"));
|
|
487
489
|
},
|
|
488
490
|
|
|
489
|
-
// These files
|
|
491
|
+
// These files emit a cold event instead of being hot-reloaded
|
|
490
492
|
cold: ["**/*.cold.js", "src/bootstrap.js"],
|
|
491
493
|
|
|
492
494
|
// Custom cold logic
|
|
@@ -496,6 +498,60 @@ new HMRClient({
|
|
|
496
498
|
|
|
497
499
|
---
|
|
498
500
|
|
|
501
|
+
### Load Order
|
|
502
|
+
|
|
503
|
+
When the client receives the initial file list it sorts them before loading. The default order is:
|
|
504
|
+
|
|
505
|
+
1. CSS before JS: stylesheets load first so scripts never run against an unstyled page
|
|
506
|
+
2. Cold files first: files that require a full page reload are loaded before hot-swappable ones
|
|
507
|
+
3. Alphabetical: stable tiebreaker within each group
|
|
508
|
+
|
|
509
|
+
`loadOrder` lets you prepend extra stages to the pipeline without giving up the built-in sorting. Each stage is detected by how many arguments it takes:
|
|
510
|
+
|
|
511
|
+
- **One argument** `f => boolean` return `true` to load that file earlier, `false` to leave it in its normal position
|
|
512
|
+
- **Two arguments** `(a, b) => number` works exactly like a standard `Array.sort` callback
|
|
513
|
+
|
|
514
|
+
The first stage to return a non-zero result wins; the built-in stages always follow as a fallback.
|
|
515
|
+
|
|
516
|
+
```js
|
|
517
|
+
new HMRClient({
|
|
518
|
+
port: 1338,
|
|
519
|
+
|
|
520
|
+
loadOrder: [
|
|
521
|
+
// Load the bootstrap file before everything else
|
|
522
|
+
(f) => f === "src/bootstrap.js",
|
|
523
|
+
|
|
524
|
+
// Load files in the core/ directory before others
|
|
525
|
+
(f) => f.startsWith("core/"),
|
|
526
|
+
|
|
527
|
+
// Higher-level files first (fewer path segments)
|
|
528
|
+
(a, b) => a.split("/").length - b.split("/").length,
|
|
529
|
+
],
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
Each stage is tried in order. The first one that produces a difference between two files decides which loads first. If a stage sees no difference, the next one is tried. The built-in stages (CSS-first, cold-first, alphabetical) always follow as a final fallback.
|
|
534
|
+
|
|
535
|
+
When you need total control over the load order, pass `sortFiles` instead. It receives the full file list and must return a sorted copy. The built-in stages and any `loadOrder` are completely bypassed.
|
|
536
|
+
|
|
537
|
+
```js
|
|
538
|
+
new HMRClient({
|
|
539
|
+
port: 1338,
|
|
540
|
+
|
|
541
|
+
sortFiles: (files) => {
|
|
542
|
+
const order = ["src/reset.css", "src/theme.css", "src/bootstrap.js"];
|
|
543
|
+
return [
|
|
544
|
+
// Pinned files in explicit order
|
|
545
|
+
...order.filter((f) => files.includes(f)),
|
|
546
|
+
// Everything else, alphabetical
|
|
547
|
+
...files.filter((f) => !order.includes(f)).sort(),
|
|
548
|
+
];
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
499
555
|
### Override Detection
|
|
500
556
|
|
|
501
557
|
Override detection lets you maintain a parallel directory of replacement files that shadow originals without modifying them. When an override changes, the client unloads the original before loading the override.
|
|
@@ -46,7 +46,8 @@ export class HMRClient {
|
|
|
46
46
|
* @param {function(string): boolean} [options.filterCold] - Custom cold file logic. Receives `(filePath)`. Combined with `cold` via OR.
|
|
47
47
|
* @param {function(string, string[]): string|null} [options.getOverrideTarget] - Given a changed file, return the path of the original it replaces, or `null`. Receives `(filePath, allFiles)`. When matched, the original is unloaded before the override loads.
|
|
48
48
|
* @param {function(string): void} [options.onFileLoaded] - Called after each file loads or reloads. Receives `(filePath)`.
|
|
49
|
-
* @param {function(string[]): string[]} [options.sortFiles] - Custom sort for the initial file load order.
|
|
49
|
+
* @param {function(string[]): string[]} [options.sortFiles] - Custom sort for the initial file load order. When provided, replaces `defaultSortFiles` entirely and `loadOrder` is ignored.
|
|
50
|
+
* @param {Array<Function>} [options.loadOrder] - Stages prepended before the built-in sort (CSS-first, cold-first, alphabetical). One argument: return true to load that file first. Two arguments: works like a normal sort callback.
|
|
50
51
|
*/
|
|
51
52
|
constructor(options: {
|
|
52
53
|
wsUrl?: string;
|
|
@@ -66,6 +67,7 @@ export class HMRClient {
|
|
|
66
67
|
getOverrideTarget?: (arg0: string, arg1: string[]) => string | null;
|
|
67
68
|
onFileLoaded?: (arg0: string) => void;
|
|
68
69
|
sortFiles?: (arg0: string[]) => string[];
|
|
70
|
+
loadOrder?: Array<Function>;
|
|
69
71
|
});
|
|
70
72
|
wsUrl: any;
|
|
71
73
|
httpUrl: any;
|
|
@@ -82,6 +84,7 @@ export class HMRClient {
|
|
|
82
84
|
allFiles: any[];
|
|
83
85
|
getOverrideTarget: (arg0: string, arg1: string[]) => string | null;
|
|
84
86
|
onFileLoaded: (arg0: string) => void;
|
|
87
|
+
loadOrder: Function[];
|
|
85
88
|
sortFiles: any;
|
|
86
89
|
socket: WebSocket;
|
|
87
90
|
reconnectAttempts: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr-client.d.ts","sourceRoot":"","sources":["../../src/client/hmr-client.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IACE
|
|
1
|
+
{"version":3,"file":"hmr-client.d.ts","sourceRoot":"","sources":["../../src/client/hmr-client.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IACE;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,qBAnBG;QAAyB,KAAK,GAAtB,MAAM;QACW,OAAO,GAAxB,MAAM;QACY,UAAU,GAA5B,OAAO;QACU,IAAI,GAArB,MAAM;QACW,IAAI,GAArB,MAAM;QACY,MAAM,GAAxB,OAAO;QACW,aAAa,GAA/B,OAAO;QACU,cAAc,GAA/B,MAAM;QACW,iBAAiB,GAAlC,MAAM;QACY,eAAe,GAAjC,OAAO;QACY,IAAI,GAAvB,MAAM,EAAE;QACsC,UAAU,GAAxD,CAAS,IAAM,EAAN,MAAM,EAAE,IAAQ,EAAR,MAAM,EAAE,KAAG,OAAO;QAChB,IAAI,GAAvB,MAAM,EAAE;QAC4B,UAAU,GAA9C,CAAS,IAAM,EAAN,MAAM,KAAG,OAAO;QACyB,iBAAiB,GAAnE,CAAS,IAAM,EAAN,MAAM,EAAE,IAAQ,EAAR,MAAM,EAAE,KAAG,MAAM,GAAC,IAAI;QACN,YAAY,GAA7C,CAAS,IAAM,EAAN,MAAM,KAAG,IAAI;QACiB,SAAS,GAAhD,CAAS,IAAQ,EAAR,MAAM,EAAE,KAAG,MAAM,EAAE;QACF,SAAS,GAAnC,KAAK,UAAU;KACzB,EAgEA;IAzDC,WAAkB;IAClB,aAAsB;IACtB,oBAAsB;IAEtB,+BAAyD;IACzD,uBAA+C;IAC/C,uBAAiD;IACjD,0BAAwD;IACxD,yBAAqD;IAGrD,wBAAsC;IACtC,oBAxBkB,MAAM,KAAG,OAAO,CAwBQ;IAG1C,oBAAiF;IACjF,gBAAuE;IAGvE,gBAAkB;IAElB,0BAhCkB,MAAM,QAAE,MAAM,EAAE,KAAG,MAAM,GAAC,IAAI,CAgCO;IACvD,qBAhCkB,MAAM,KAAG,IAAI,CAgCc;IAC7C,sBAAqC;IACrC,eAAmE;IAEnE,kBAAkB;IAClB,0BAA0B;IAC1B,qBAAwB;IACxB,6BAA8B;IAC9B,gCAA2B;IAI3B,qBAAuB;IACvB,6BAAgC;IAEhC,uBAA8C;IAE9C,wEAAwE;IACxE,aADW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CACF;IAC5B,uFAAuF;IACvF,qBADW,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CACC;IAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAYC;IAGH,oCAkBC;IAED,8CAcC;IAED,mCAIC;IAED,sEAoDC;IAED,kCA0BC;IAED,4CAgDC;IAED,8EAkEC;IAED,2CA4CC;IAED,wCA6CC;IAED;;;;;OAKG;IACH,UAJW,MAAM,GAAC,QAAQ,GAAC,KAAK,GAAC,QAAQ,GAAC,MAAM,GAAC,SAAS,GAAC,YAAY,GAAC,OAAO,sBAElE,SAAS,CAQrB;IAED;;;;;OAKG;IACH,YAJW,MAAM,GAAC,QAAQ,GAAC,KAAK,GAAC,QAAQ,GAAC,MAAM,GAAC,SAAS,GAAC,YAAY,GAAC,OAAO,sBAElE,SAAS,CASrB;IAED;;;;;OAKG;IACH,WAJW,MAAM,GAAC,QAAQ,GAAC,KAAK,GAAC,QAAQ,GAAC,MAAM,GAAC,SAAS,GAAC,YAAY,GAAC,OAAO,sBAElE,SAAS,CAoBrB;IAED,uCAQC;IAMD,iCAGC;IAED,oCAWC;IAED;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,CAAC,CAuFzB;IAED;;OAEG;IACH,mBAUC;CACF;2BAvoB0B,kBAAkB"}
|
package/dist/client.iife.js
CHANGED
|
@@ -1762,7 +1762,8 @@ var HMR = (() => {
|
|
|
1762
1762
|
* @param {function(string): boolean} [options.filterCold] - Custom cold file logic. Receives `(filePath)`. Combined with `cold` via OR.
|
|
1763
1763
|
* @param {function(string, string[]): string|null} [options.getOverrideTarget] - Given a changed file, return the path of the original it replaces, or `null`. Receives `(filePath, allFiles)`. When matched, the original is unloaded before the override loads.
|
|
1764
1764
|
* @param {function(string): void} [options.onFileLoaded] - Called after each file loads or reloads. Receives `(filePath)`.
|
|
1765
|
-
* @param {function(string[]): string[]} [options.sortFiles] - Custom sort for the initial file load order.
|
|
1765
|
+
* @param {function(string[]): string[]} [options.sortFiles] - Custom sort for the initial file load order. When provided, replaces `defaultSortFiles` entirely and `loadOrder` is ignored.
|
|
1766
|
+
* @param {Array<Function>} [options.loadOrder] - Stages prepended before the built-in sort (CSS-first, cold-first, alphabetical). One argument: return true to load that file first. Two arguments: works like a normal sort callback.
|
|
1766
1767
|
*/
|
|
1767
1768
|
constructor(options) {
|
|
1768
1769
|
const opts = typeof options === "object" && !Array.isArray(options) ? options : {};
|
|
@@ -1783,6 +1784,7 @@ var HMR = (() => {
|
|
|
1783
1784
|
this.allFiles = [];
|
|
1784
1785
|
this.getOverrideTarget = opts.getOverrideTarget || null;
|
|
1785
1786
|
this.onFileLoaded = opts.onFileLoaded || null;
|
|
1787
|
+
this.loadOrder = opts.loadOrder || [];
|
|
1786
1788
|
this.sortFiles = opts.sortFiles || this.defaultSortFiles.bind(this);
|
|
1787
1789
|
this.socket = null;
|
|
1788
1790
|
this.reconnectAttempts = 0;
|
|
@@ -1810,16 +1812,18 @@ var HMR = (() => {
|
|
|
1810
1812
|
}
|
|
1811
1813
|
defaultSortFiles(files) {
|
|
1812
1814
|
const coldSet = new Set(files.filter((f) => this.isColdFile(f)));
|
|
1815
|
+
const builtinStages = [
|
|
1816
|
+
(f) => f.endsWith(".css"),
|
|
1817
|
+
(f) => coldSet.has(f),
|
|
1818
|
+
(a, b) => a.localeCompare(b)
|
|
1819
|
+
];
|
|
1820
|
+
const stages = [...this.loadOrder, ...builtinStages];
|
|
1813
1821
|
return [...files].sort((a, b) => {
|
|
1814
|
-
const
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
const coldB = coldSet.has(b);
|
|
1820
|
-
if (coldA && !coldB) return -1;
|
|
1821
|
-
if (!coldA && coldB) return 1;
|
|
1822
|
-
return a.localeCompare(b);
|
|
1822
|
+
for (const stage of stages) {
|
|
1823
|
+
const result = stage.length === 2 ? stage(a, b) : stage(b) - stage(a);
|
|
1824
|
+
if (result !== 0) return result;
|
|
1825
|
+
}
|
|
1826
|
+
return 0;
|
|
1823
1827
|
});
|
|
1824
1828
|
}
|
|
1825
1829
|
makeFilter(patterns, callback) {
|