@bgub/fig-refresh 0.1.0-alpha.2 → 0.1.0-alpha.3

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @bgub/fig-refresh
2
2
 
3
3
  Fast Refresh runtime for [Fig](https://github.com/bgub/fig). It tracks
4
- component families across module re-evaluations and installs the refresh
5
- handler that lets the reconciler swap implementations in place while
4
+ component families across module re-evaluations and connects to a
5
+ renderer-owned refresh adapter that swaps implementations in place while
6
6
  preserving state.
7
7
 
8
8
  This package is wiring, not an API you call from application code: build
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { RefreshUpdate } from "@bgub/fig-reconciler/refresh";
1
+ import { RefreshAdapter, RefreshUpdate } from "@bgub/fig-reconciler/refresh";
2
2
  //#region src/index.d.ts
3
3
  declare function register(type: unknown, id: string): void;
4
4
  declare function setSignature(type: unknown, key: string, forceReset?: boolean): void;
5
- declare function injectScheduleRefresh(scheduleRefresh: (update: RefreshUpdate) => void): void;
5
+ declare function injectRenderer(renderer: RefreshAdapter): void;
6
6
  declare function performRefresh(): RefreshUpdate | null;
7
7
  //#endregion
8
- export { injectScheduleRefresh, performRefresh, register, setSignature };
8
+ export { injectRenderer, performRefresh, register, setSignature };
package/dist/index.js CHANGED
@@ -1,26 +1,19 @@
1
- import { setRefreshHandler } from "@bgub/fig-reconciler/refresh";
2
1
  //#region src/index.ts
3
2
  const familiesByType = /* @__PURE__ */ new WeakMap();
4
3
  const familiesById = /* @__PURE__ */ new Map();
5
4
  const signatures = /* @__PURE__ */ new WeakMap();
6
- const scheduleRefreshFns = /* @__PURE__ */ new Set();
5
+ const renderers = /* @__PURE__ */ new Set();
7
6
  const unscheduledRefreshes = [];
8
7
  let pendingUpdates = [];
9
- let installed = false;
10
8
  function asKey(type) {
11
- return typeof type === "function" || typeof type === "object" && type !== null ? type : null;
12
- }
13
- function ensureInstalled() {
14
- if (installed) return;
15
- installed = true;
16
- setRefreshHandler(resolveFamilyByType);
9
+ if (typeof type === "function") return type;
10
+ return typeof type === "object" && type !== null ? type : null;
17
11
  }
18
12
  function resolveFamilyByType(type) {
19
13
  const key = asKey(type);
20
14
  return key === null ? void 0 : familiesByType.get(key);
21
15
  }
22
16
  function register(type, id) {
23
- ensureInstalled();
24
17
  const key = asKey(type);
25
18
  if (key === null || familiesByType.has(key)) return;
26
19
  let family = familiesById.get(id);
@@ -37,10 +30,11 @@ function setSignature(type, key, forceReset = false) {
37
30
  key
38
31
  });
39
32
  }
40
- function injectScheduleRefresh(scheduleRefresh) {
41
- ensureInstalled();
42
- scheduleRefreshFns.add(scheduleRefresh);
43
- for (const update of unscheduledRefreshes) scheduleRefresh(update);
33
+ function injectRenderer(renderer) {
34
+ if (renderers.has(renderer)) return;
35
+ renderer.setRefreshHandler(resolveFamilyByType);
36
+ renderers.add(renderer);
37
+ for (const update of unscheduledRefreshes) renderer.scheduleRefresh(update);
44
38
  unscheduledRefreshes.length = 0;
45
39
  }
46
40
  function performRefresh() {
@@ -60,11 +54,11 @@ function performRefresh() {
60
54
  staleFamilies,
61
55
  updatedFamilies
62
56
  };
63
- if (scheduleRefreshFns.size === 0) {
57
+ if (renderers.size === 0) {
64
58
  unscheduledRefreshes.push(update);
65
59
  return update;
66
60
  }
67
- for (const scheduleRefresh of scheduleRefreshFns) scheduleRefresh(update);
61
+ for (const renderer of renderers) renderer.scheduleRefresh(update);
68
62
  return update;
69
63
  }
70
64
  function isSignatureStale(prevType, nextType) {
@@ -78,6 +72,6 @@ function isSignatureStale(prevType, nextType) {
78
72
  return prev.key !== next.key;
79
73
  }
80
74
  //#endregion
81
- export { injectScheduleRefresh, performRefresh, register, setSignature };
75
+ export { injectRenderer, performRefresh, register, setSignature };
82
76
 
83
77
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n type RefreshFamily,\n type RefreshUpdate,\n setRefreshHandler,\n} from \"@bgub/fig-reconciler/refresh\";\n\n// The Fig Fast Refresh runtime. A bundler transform (dev only) registers each\n// component version under a stable id and records its hook signature, then calls\n// performRefresh() from an import.meta.hot.accept handler. The runtime groups\n// versions into families, decides which can re-render in place vs must remount,\n// and drives the reconciler.\n\ninterface Signature {\n forceReset: boolean;\n key: string;\n}\n\nconst familiesByType = new WeakMap<object, RefreshFamily>();\nconst familiesById = new Map<string, RefreshFamily>();\nconst signatures = new WeakMap<object, Signature>();\nconst scheduleRefreshFns = new Set<(update: RefreshUpdate) => void>();\nconst unscheduledRefreshes: RefreshUpdate[] = [];\nlet pendingUpdates: Array<[RefreshFamily, object]> = [];\nlet installed = false;\n\nfunction asKey(type: unknown): object | null {\n return typeof type === \"function\" ||\n (typeof type === \"object\" && type !== null)\n ? (type as object)\n : null;\n}\n\n// Install the reconciler handler lazily so importing the runtime has no effect\n// until something actually registers (keeps it tree-shakeable / prod-safe).\nfunction ensureInstalled(): void {\n if (installed) return;\n installed = true;\n setRefreshHandler(resolveFamilyByType);\n}\n\nfunction resolveFamilyByType(type: unknown): RefreshFamily | undefined {\n const key = asKey(type);\n return key === null ? undefined : familiesByType.get(key);\n}\n\n// Register a component version under a stable id (e.g. \"src/App.tsx#App\"). The\n// first version creates a family; later versions of the same id queue an update.\nexport function register(type: unknown, id: string): void {\n ensureInstalled();\n const key = asKey(type);\n if (key === null || familiesByType.has(key)) return;\n\n let family = familiesById.get(id);\n if (family === undefined) {\n family = { current: type };\n familiesById.set(id, family);\n } else {\n pendingUpdates.push([family, key]);\n }\n familiesByType.set(key, family);\n}\n\n// Record a component's hook signature. Differing signatures between versions\n// (or forceReset) mean a remount; identical signatures re-render in place.\nexport function setSignature(\n type: unknown,\n key: string,\n forceReset = false,\n): void {\n const target = asKey(type);\n if (target !== null) signatures.set(target, { forceReset, key });\n}\n\n// Connect a renderer's scheduleRefresh (e.g. from @bgub/fig-dom). The app's dev\n// bootstrap calls this once.\nexport function injectScheduleRefresh(\n scheduleRefresh: (update: RefreshUpdate) => void,\n): void {\n ensureInstalled();\n scheduleRefreshFns.add(scheduleRefresh);\n for (const update of unscheduledRefreshes) scheduleRefresh(update);\n unscheduledRefreshes.length = 0;\n}\n\n// Apply queued registrations: advance each family to its newest version, bucket\n// it as updated (re-render in place) or stale (remount), and drive the renderers.\nexport function performRefresh(): RefreshUpdate | null {\n if (pendingUpdates.length === 0) return null;\n\n const updates = pendingUpdates;\n pendingUpdates = [];\n\n const updatedFamilies = new Set<RefreshFamily>();\n const staleFamilies = new Set<RefreshFamily>();\n\n for (const [family, nextType] of updates) {\n const prevType = family.current;\n family.current = nextType;\n if (isSignatureStale(prevType, nextType)) staleFamilies.add(family);\n else updatedFamilies.add(family);\n }\n\n // If a family was edited more than once and any edit was stale, prefer remount.\n for (const family of staleFamilies) updatedFamilies.delete(family);\n\n const update: RefreshUpdate = { staleFamilies, updatedFamilies };\n if (scheduleRefreshFns.size === 0) {\n unscheduledRefreshes.push(update);\n return update;\n }\n for (const scheduleRefresh of scheduleRefreshFns) scheduleRefresh(update);\n return update;\n}\n\nfunction isSignatureStale(prevType: unknown, nextType: unknown): boolean {\n const prevKey = asKey(prevType);\n const nextKey = asKey(nextType);\n const prev = prevKey === null ? undefined : signatures.get(prevKey);\n const next = nextKey === null ? undefined : signatures.get(nextKey);\n\n if (next?.forceReset === true) return true;\n // Neither version recorded a signature: treat as a safe in-place update.\n if (prev === undefined && next === undefined) return false;\n // One has a signature and the other doesn't, or the keys differ: remount.\n if (prev === undefined || next === undefined) return true;\n return prev.key !== next.key;\n}\n"],"mappings":";;AAiBA,MAAM,iCAAiB,IAAI,QAA+B;AAC1D,MAAM,+BAAe,IAAI,IAA2B;AACpD,MAAM,6BAAa,IAAI,QAA2B;AAClD,MAAM,qCAAqB,IAAI,IAAqC;AACpE,MAAM,uBAAwC,CAAC;AAC/C,IAAI,iBAAiD,CAAC;AACtD,IAAI,YAAY;AAEhB,SAAS,MAAM,MAA8B;CAC3C,OAAO,OAAO,SAAS,cACpB,OAAO,SAAS,YAAY,SAAS,OACnC,OACD;AACN;AAIA,SAAS,kBAAwB;CAC/B,IAAI,WAAW;CACf,YAAY;CACZ,kBAAkB,mBAAmB;AACvC;AAEA,SAAS,oBAAoB,MAA0C;CACrE,MAAM,MAAM,MAAM,IAAI;CACtB,OAAO,QAAQ,OAAO,KAAA,IAAY,eAAe,IAAI,GAAG;AAC1D;AAIA,SAAgB,SAAS,MAAe,IAAkB;CACxD,gBAAgB;CAChB,MAAM,MAAM,MAAM,IAAI;CACtB,IAAI,QAAQ,QAAQ,eAAe,IAAI,GAAG,GAAG;CAE7C,IAAI,SAAS,aAAa,IAAI,EAAE;CAChC,IAAI,WAAW,KAAA,GAAW;EACxB,SAAS,EAAE,SAAS,KAAK;EACzB,aAAa,IAAI,IAAI,MAAM;CAC7B,OACE,eAAe,KAAK,CAAC,QAAQ,GAAG,CAAC;CAEnC,eAAe,IAAI,KAAK,MAAM;AAChC;AAIA,SAAgB,aACd,MACA,KACA,aAAa,OACP;CACN,MAAM,SAAS,MAAM,IAAI;CACzB,IAAI,WAAW,MAAM,WAAW,IAAI,QAAQ;EAAE;EAAY;CAAI,CAAC;AACjE;AAIA,SAAgB,sBACd,iBACM;CACN,gBAAgB;CAChB,mBAAmB,IAAI,eAAe;CACtC,KAAK,MAAM,UAAU,sBAAsB,gBAAgB,MAAM;CACjE,qBAAqB,SAAS;AAChC;AAIA,SAAgB,iBAAuC;CACrD,IAAI,eAAe,WAAW,GAAG,OAAO;CAExC,MAAM,UAAU;CAChB,iBAAiB,CAAC;CAElB,MAAM,kCAAkB,IAAI,IAAmB;CAC/C,MAAM,gCAAgB,IAAI,IAAmB;CAE7C,KAAK,MAAM,CAAC,QAAQ,aAAa,SAAS;EACxC,MAAM,WAAW,OAAO;EACxB,OAAO,UAAU;EACjB,IAAI,iBAAiB,UAAU,QAAQ,GAAG,cAAc,IAAI,MAAM;OAC7D,gBAAgB,IAAI,MAAM;CACjC;CAGA,KAAK,MAAM,UAAU,eAAe,gBAAgB,OAAO,MAAM;CAEjE,MAAM,SAAwB;EAAE;EAAe;CAAgB;CAC/D,IAAI,mBAAmB,SAAS,GAAG;EACjC,qBAAqB,KAAK,MAAM;EAChC,OAAO;CACT;CACA,KAAK,MAAM,mBAAmB,oBAAoB,gBAAgB,MAAM;CACxE,OAAO;AACT;AAEA,SAAS,iBAAiB,UAAmB,UAA4B;CACvE,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,OAAO,YAAY,OAAO,KAAA,IAAY,WAAW,IAAI,OAAO;CAClE,MAAM,OAAO,YAAY,OAAO,KAAA,IAAY,WAAW,IAAI,OAAO;CAElE,IAAI,MAAM,eAAe,MAAM,OAAO;CAEtC,IAAI,SAAS,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO;CAErD,IAAI,SAAS,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO;CACrD,OAAO,KAAK,QAAQ,KAAK;AAC3B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n RefreshAdapter,\n RefreshFamily,\n RefreshUpdate,\n} from \"@bgub/fig-reconciler/refresh\";\n\n// The Fig Fast Refresh runtime. A bundler transform (dev only) registers each\n// component version under a stable id and records its hook signature, then calls\n// performRefresh() from an import.meta.hot.accept handler. The runtime groups\n// versions into families, decides which can re-render in place vs must remount,\n// and drives the reconciler.\n\ninterface Signature {\n forceReset: boolean;\n key: string;\n}\n\nconst familiesByType = new WeakMap<object, RefreshFamily>();\nconst familiesById = new Map<string, RefreshFamily>();\nconst signatures = new WeakMap<object, Signature>();\nconst renderers = new Set<RefreshAdapter>();\nconst unscheduledRefreshes: RefreshUpdate[] = [];\nlet pendingUpdates: Array<[RefreshFamily, object]> = [];\n\nfunction asKey(type: unknown): object | null {\n if (typeof type === \"function\") return type;\n return typeof type === \"object\" && type !== null ? type : null;\n}\n\nfunction resolveFamilyByType(type: unknown): RefreshFamily | undefined {\n const key = asKey(type);\n return key === null ? undefined : familiesByType.get(key);\n}\n\n// Register a component version under a stable id (e.g. \"src/App.tsx#App\"). The\n// first version creates a family; later versions of the same id queue an update.\nexport function register(type: unknown, id: string): void {\n const key = asKey(type);\n if (key === null || familiesByType.has(key)) return;\n\n let family = familiesById.get(id);\n if (family === undefined) {\n family = { current: type };\n familiesById.set(id, family);\n } else {\n pendingUpdates.push([family, key]);\n }\n familiesByType.set(key, family);\n}\n\n// Record a component's hook signature. Differing signatures between versions\n// (or forceReset) mean a remount; identical signatures re-render in place.\nexport function setSignature(\n type: unknown,\n key: string,\n forceReset = false,\n): void {\n const target = asKey(type);\n if (target !== null) signatures.set(target, { forceReset, key });\n}\n\n// Connect the runtime to a renderer-owned reconciler. The adapter installs the\n// family resolver and schedules updates through the same reconciler instance.\nexport function injectRenderer(renderer: RefreshAdapter): void {\n if (renderers.has(renderer)) return;\n\n renderer.setRefreshHandler(resolveFamilyByType);\n renderers.add(renderer);\n for (const update of unscheduledRefreshes) {\n renderer.scheduleRefresh(update);\n }\n unscheduledRefreshes.length = 0;\n}\n\n// Apply queued registrations: advance each family to its newest version, bucket\n// it as updated (re-render in place) or stale (remount), and drive the renderers.\nexport function performRefresh(): RefreshUpdate | null {\n if (pendingUpdates.length === 0) return null;\n\n const updates = pendingUpdates;\n pendingUpdates = [];\n\n const updatedFamilies = new Set<RefreshFamily>();\n const staleFamilies = new Set<RefreshFamily>();\n\n for (const [family, nextType] of updates) {\n const prevType = family.current;\n family.current = nextType;\n if (isSignatureStale(prevType, nextType)) staleFamilies.add(family);\n else updatedFamilies.add(family);\n }\n\n // If a family was edited more than once and any edit was stale, prefer remount.\n for (const family of staleFamilies) updatedFamilies.delete(family);\n\n const update: RefreshUpdate = { staleFamilies, updatedFamilies };\n if (renderers.size === 0) {\n unscheduledRefreshes.push(update);\n return update;\n }\n for (const renderer of renderers) renderer.scheduleRefresh(update);\n return update;\n}\n\nfunction isSignatureStale(prevType: unknown, nextType: unknown): boolean {\n const prevKey = asKey(prevType);\n const nextKey = asKey(nextType);\n const prev = prevKey === null ? undefined : signatures.get(prevKey);\n const next = nextKey === null ? undefined : signatures.get(nextKey);\n\n if (next?.forceReset === true) return true;\n // Neither version recorded a signature: treat as a safe in-place update.\n if (prev === undefined && next === undefined) return false;\n // One has a signature and the other doesn't, or the keys differ: remount.\n if (prev === undefined || next === undefined) return true;\n return prev.key !== next.key;\n}\n"],"mappings":";AAiBA,MAAM,iCAAiB,IAAI,QAA+B;AAC1D,MAAM,+BAAe,IAAI,IAA2B;AACpD,MAAM,6BAAa,IAAI,QAA2B;AAClD,MAAM,4BAAY,IAAI,IAAoB;AAC1C,MAAM,uBAAwC,CAAC;AAC/C,IAAI,iBAAiD,CAAC;AAEtD,SAAS,MAAM,MAA8B;CAC3C,IAAI,OAAO,SAAS,YAAY,OAAO;CACvC,OAAO,OAAO,SAAS,YAAY,SAAS,OAAO,OAAO;AAC5D;AAEA,SAAS,oBAAoB,MAA0C;CACrE,MAAM,MAAM,MAAM,IAAI;CACtB,OAAO,QAAQ,OAAO,KAAA,IAAY,eAAe,IAAI,GAAG;AAC1D;AAIA,SAAgB,SAAS,MAAe,IAAkB;CACxD,MAAM,MAAM,MAAM,IAAI;CACtB,IAAI,QAAQ,QAAQ,eAAe,IAAI,GAAG,GAAG;CAE7C,IAAI,SAAS,aAAa,IAAI,EAAE;CAChC,IAAI,WAAW,KAAA,GAAW;EACxB,SAAS,EAAE,SAAS,KAAK;EACzB,aAAa,IAAI,IAAI,MAAM;CAC7B,OACE,eAAe,KAAK,CAAC,QAAQ,GAAG,CAAC;CAEnC,eAAe,IAAI,KAAK,MAAM;AAChC;AAIA,SAAgB,aACd,MACA,KACA,aAAa,OACP;CACN,MAAM,SAAS,MAAM,IAAI;CACzB,IAAI,WAAW,MAAM,WAAW,IAAI,QAAQ;EAAE;EAAY;CAAI,CAAC;AACjE;AAIA,SAAgB,eAAe,UAAgC;CAC7D,IAAI,UAAU,IAAI,QAAQ,GAAG;CAE7B,SAAS,kBAAkB,mBAAmB;CAC9C,UAAU,IAAI,QAAQ;CACtB,KAAK,MAAM,UAAU,sBACnB,SAAS,gBAAgB,MAAM;CAEjC,qBAAqB,SAAS;AAChC;AAIA,SAAgB,iBAAuC;CACrD,IAAI,eAAe,WAAW,GAAG,OAAO;CAExC,MAAM,UAAU;CAChB,iBAAiB,CAAC;CAElB,MAAM,kCAAkB,IAAI,IAAmB;CAC/C,MAAM,gCAAgB,IAAI,IAAmB;CAE7C,KAAK,MAAM,CAAC,QAAQ,aAAa,SAAS;EACxC,MAAM,WAAW,OAAO;EACxB,OAAO,UAAU;EACjB,IAAI,iBAAiB,UAAU,QAAQ,GAAG,cAAc,IAAI,MAAM;OAC7D,gBAAgB,IAAI,MAAM;CACjC;CAGA,KAAK,MAAM,UAAU,eAAe,gBAAgB,OAAO,MAAM;CAEjE,MAAM,SAAwB;EAAE;EAAe;CAAgB;CAC/D,IAAI,UAAU,SAAS,GAAG;EACxB,qBAAqB,KAAK,MAAM;EAChC,OAAO;CACT;CACA,KAAK,MAAM,YAAY,WAAW,SAAS,gBAAgB,MAAM;CACjE,OAAO;AACT;AAEA,SAAS,iBAAiB,UAAmB,UAA4B;CACvE,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,OAAO,YAAY,OAAO,KAAA,IAAY,WAAW,IAAI,OAAO;CAClE,MAAM,OAAO,YAAY,OAAO,KAAA,IAAY,WAAW,IAAI,OAAO;CAElE,IAAI,MAAM,eAAe,MAAM,OAAO;CAEtC,IAAI,SAAS,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO;CAErD,IAAI,SAAS,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO;CACrD,OAAO,KAAK,QAAQ,KAAK;AAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgub/fig-refresh",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Fig Fast Refresh runtime (HMR)",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/bgub/fig",
@@ -31,11 +31,8 @@
31
31
  "publishConfig": {
32
32
  "access": "public"
33
33
  },
34
- "peerDependencies": {
35
- "@bgub/fig-reconciler": "0.1.0-alpha.2"
36
- },
37
- "devDependencies": {
38
- "@bgub/fig-reconciler": "0.1.0-alpha.2"
34
+ "dependencies": {
35
+ "@bgub/fig-reconciler": "0.1.0-alpha.3"
39
36
  },
40
37
  "engines": {
41
38
  "node": "^20.19.0 || >=22.12.0"