@bgub/fig-vite 0.1.0-alpha.1 → 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/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## @bgub/fig-vite@0.1.0-alpha.3
2
+
3
+ ### Let Fig's Vite integration own runtime configuration
4
+
5
+ The new `fig()` Vite integration defines Fig's development gate and installs
6
+ Fast Refresh. TanStack Start composes it automatically, so applications no
7
+ longer need to configure Fig's compile-time mode or SSR package bundling
8
+ themselves.
9
+
10
+ `@bgub/fig-vite` now uses the application's Fig DOM renderer as a peer instead
11
+ of installing a private renderer copy.
12
+
13
+ The development gate follows Vite's command rather than its mode: serving
14
+ enables development behavior, while builds—including `--mode development`—strip
15
+ it from production output.
16
+
17
+ Published npm packages now expose development artifacts through a Fig-owned
18
+ condition, allowing the Vite integration to enable diagnostics and Fast Refresh
19
+ for ordinary installs while explicit static overrides remain authoritative and
20
+ default production imports retain their previous dead-code elimination. A
21
+ static `false` override also disables Fast Refresh instrumentation.
22
+
1
23
  ## @bgub/fig-vite@0.1.0-alpha.1
2
24
 
3
25
  ### Refresh resolves the app's renderer runtime
package/README.md CHANGED
@@ -1,15 +1,33 @@
1
1
  # @bgub/fig-vite
2
2
 
3
- Vite integration for Fig. It provides state-preserving Fast Refresh.
3
+ Vite integration for Fig. It sets Fig's compile-time development gate and
4
+ provides state-preserving Fast Refresh.
4
5
 
5
6
  ```ts
6
- import { figRefresh } from "@bgub/fig-vite";
7
+ import { fig } from "@bgub/fig-vite";
7
8
 
8
- const plugins = [figRefresh()];
9
+ const plugins = [fig()];
9
10
  ```
10
11
 
11
12
  Framework adapters may install this plugin for you. In particular,
12
- `@bgub/fig-tanstack-start/plugin/vite` includes `figRefresh()`.
13
+ `@bgub/fig-tanstack-start/plugin/vite` includes `fig()`.
14
+
15
+ Published Fig packages select their precompiled development artifacts
16
+ automatically while Vite is serving. Applications do not need to add export
17
+ conditions or configure `__FIG_DEV__` themselves.
18
+
19
+ Host integrations may explicitly define `__FIG_DEV__` as the static value
20
+ `true` or `false`. The integration uses the same value for source transforms
21
+ and package selection, and disables Fast Refresh instrumentation when the value
22
+ is `false`. Dynamic expressions are not supported.
23
+
24
+ `figRefresh(options)` remains available when another integration already owns
25
+ the development gate and needs only the refresh transform.
26
+
27
+ The complete integration also keeps sibling Fig packages in one SSR module
28
+ graph. Fig's renderer and server packages share ambient root and request state,
29
+ so independently externalizing those packages can split one render across
30
+ different state owners.
13
31
 
14
32
  Fig packages are ESM-only and require Node `^20.19.0 || >=22.12.0`.
15
33
 
package/dist/index.d.ts CHANGED
@@ -1,20 +1,10 @@
1
+ import { Plugin } from "vite";
1
2
  //#region src/index.d.ts
2
3
  interface FigRefreshOptions {
3
4
  include?: RegExp;
4
5
  }
5
- interface FigRefreshPlugin {
6
- apply: "serve";
7
- enforce: "pre";
8
- load(id: string): string | null;
9
- name: string;
10
- resolveId(id: string): string | null;
11
- transform(code: string, id: string, options?: {
12
- ssr?: boolean;
13
- }): Promise<{
14
- code: string;
15
- map: unknown;
16
- } | null>;
17
- }
6
+ type FigRefreshPlugin = Plugin;
7
+ declare function fig(options?: FigRefreshOptions): [Plugin, FigRefreshPlugin];
18
8
  declare function figRefresh(options?: FigRefreshOptions): FigRefreshPlugin;
19
9
  //#endregion
20
- export { FigRefreshOptions, FigRefreshPlugin, figRefresh };
10
+ export { FigRefreshOptions, FigRefreshPlugin, fig, figRefresh };
package/dist/index.js CHANGED
@@ -1,7 +1,20 @@
1
1
  import { fileURLToPath } from "node:url";
2
+ import { defaultClientConditions, defaultServerConditions } from "vite";
2
3
  import * as babel from "@babel/core";
3
4
  import presetTypescript from "@babel/preset-typescript";
4
5
  //#region src/transform.ts
6
+ function figDevGateBabelPlugin(api) {
7
+ const t = api.types;
8
+ return {
9
+ name: "fig-dev-gate",
10
+ visitor: { ReferencedIdentifier(path) {
11
+ if (path.node.name !== "__FIG_DEV__") return;
12
+ if (path.scope.hasBinding("__FIG_DEV__")) return;
13
+ const development = this.opts?.development;
14
+ path.replaceWith(t.booleanLiteral(development === true));
15
+ } }
16
+ };
17
+ }
5
18
  function figRefreshBabelPlugin(api) {
6
19
  const t = api.types;
7
20
  const template = api.template;
@@ -163,6 +176,37 @@ function isBuiltinFigHookName(name) {
163
176
  }
164
177
  async function transformModule(code, id) {
165
178
  const result = await babel.transformAsync(code, {
179
+ ...transformOptions(id),
180
+ plugins: [[figRefreshBabelPlugin, { moduleId: id }]]
181
+ });
182
+ if (result?.code == null || !result.code.includes("virtual:fig-refresh")) return null;
183
+ return {
184
+ code: result.code,
185
+ map: mutableSourceMap(result.map)
186
+ };
187
+ }
188
+ async function transformDevGate(code, id, development) {
189
+ const result = await babel.transformAsync(code, {
190
+ ...transformOptions(id),
191
+ plugins: [[figDevGateBabelPlugin, { development }]]
192
+ });
193
+ if (result?.code == null) throw new Error(`Could not transform the Fig development gate in ${id}.`);
194
+ return {
195
+ code: result.code,
196
+ map: mutableSourceMap(result.map)
197
+ };
198
+ }
199
+ function mutableSourceMap(map) {
200
+ if (map === null) return void 0;
201
+ return {
202
+ ...map,
203
+ names: [...map.names],
204
+ sources: [...map.sources],
205
+ sourcesContent: map.sourcesContent === void 0 ? void 0 : [...map.sourcesContent]
206
+ };
207
+ }
208
+ function transformOptions(id) {
209
+ return {
166
210
  babelrc: false,
167
211
  configFile: false,
168
212
  filename: id,
@@ -171,21 +215,46 @@ async function transformModule(code, id) {
171
215
  ignoreExtensions: true,
172
216
  onlyRemoveTypeImports: true
173
217
  }]],
174
- parserOpts: { plugins: id.endsWith("x") ? ["jsx"] : [] },
175
- plugins: [[figRefreshBabelPlugin, { moduleId: id }]]
176
- });
177
- if (result?.code == null || !result.code.includes("virtual:fig-refresh")) return null;
178
- return {
179
- code: result.code,
180
- map: result.map
218
+ parserOpts: { plugins: id.endsWith("x") ? ["jsx"] : [] }
181
219
  };
182
220
  }
183
221
  //#endregion
184
222
  //#region src/index.ts
185
223
  const VIRTUAL_ID = "virtual:fig-refresh";
186
224
  const RESOLVED_VIRTUAL_ID = "\0virtual:fig-refresh";
225
+ const DEVELOPMENT_CONDITION = "fig-development";
226
+ const FIG_PACKAGE_PATTERN = /^@bgub\/fig(?:$|[-/])/;
187
227
  const REFRESH_RUNTIME_IMPORT = viteFileImport(import.meta.resolve("@bgub/fig-refresh"));
188
228
  const DOM_REFRESH_IMPORT = "@bgub/fig-dom/refresh";
229
+ function fig(options = {}) {
230
+ return [figConfig(), figRefresh(options)];
231
+ }
232
+ function figConfig() {
233
+ return {
234
+ name: "fig:config",
235
+ config(config, environment) {
236
+ return {
237
+ ...config.define?.__FIG_DEV__ === void 0 ? { define: { __FIG_DEV__: JSON.stringify(environment.command === "serve") } } : {},
238
+ ssr: { noExternal: [FIG_PACKAGE_PATTERN] }
239
+ };
240
+ },
241
+ configEnvironment(name, config, environment) {
242
+ if (!developmentGate(config.define?.__FIG_DEV__)) return null;
243
+ const defaults = environment.isSsrTargetWebworker === true || (config.consumer ?? (name === "client" ? "client" : "server")) === "client" ? defaultClientConditions : defaultServerConditions;
244
+ return { resolve: { conditions: [.../* @__PURE__ */ new Set([...config.resolve?.conditions ?? defaults, DEVELOPMENT_CONDITION])] } };
245
+ },
246
+ async transform(code, id) {
247
+ const gate = this.environment.config.define?.__FIG_DEV__;
248
+ if (gate === void 0 || !code.includes("__FIG_DEV__")) return null;
249
+ return transformDevGate(code, id, developmentGate(gate));
250
+ }
251
+ };
252
+ }
253
+ function developmentGate(value) {
254
+ if (value === true || value === "true") return true;
255
+ if (value === false || value === "false") return false;
256
+ throw new Error("Fig's __FIG_DEV__ definition must be the static value true or false.");
257
+ }
189
258
  function figRefresh(options = {}) {
190
259
  const include = options.include ?? /\.[jt]sx?$/;
191
260
  return {
@@ -200,6 +269,8 @@ function figRefresh(options = {}) {
200
269
  },
201
270
  async transform(code, id, transformOptions) {
202
271
  if (transformOptions?.ssr === true) return null;
272
+ const gate = this.environment.config.define?.__FIG_DEV__;
273
+ if (gate !== void 0 && !developmentGate(gate)) return null;
203
274
  const clean = id.split("?")[0] ?? id;
204
275
  if (clean.includes("/node_modules/") || !include.test(clean)) return null;
205
276
  return transformModule(code, clean);
@@ -207,10 +278,10 @@ function figRefresh(options = {}) {
207
278
  };
208
279
  }
209
280
  function virtualModuleCode() {
210
- return `import { injectScheduleRefresh, performRefresh, register, setSignature } from ${JSON.stringify(REFRESH_RUNTIME_IMPORT)};
211
- import { scheduleRefresh } from ${JSON.stringify(DOM_REFRESH_IMPORT)};
281
+ return `import { injectRenderer, performRefresh, register, setSignature } from ${JSON.stringify(REFRESH_RUNTIME_IMPORT)};
282
+ import { domRefreshAdapter } from ${JSON.stringify(DOM_REFRESH_IMPORT)};
212
283
 
213
- injectScheduleRefresh(scheduleRefresh);
284
+ injectRenderer(domRefreshAdapter);
214
285
 
215
286
  let queued = false;
216
287
  export function enqueueRefresh() {
@@ -229,6 +300,6 @@ function viteFileImport(url) {
229
300
  return `/@fs/${fileURLToPath(url)}`;
230
301
  }
231
302
  //#endregion
232
- export { figRefresh };
303
+ export { fig, figRefresh };
233
304
 
234
305
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/transform.ts","../src/index.ts"],"sourcesContent":["import * as babel from \"@babel/core\";\nimport type { NodePath, PluginObject } from \"@babel/core\";\nimport presetTypescript from \"@babel/preset-typescript\";\n\ninterface TransformResult {\n code: string;\n map: unknown;\n}\n\ninterface FunctionRecord {\n fnPath: NodePath;\n isComponent: boolean;\n isCustomHook: boolean;\n name: string;\n}\n\ninterface SignatureResult {\n forceReset: boolean;\n signature: string;\n}\n\n// Babel visitor: find top-level component declarations (PascalCase functions),\n// then inject calls to the Fig refresh runtime + a self-accepting HMR boundary.\n// Emits register/setSignature directly (no react-refresh global protocol).\nfunction figRefreshBabelPlugin(api: typeof babel): PluginObject {\n const t = api.types;\n const template = api.template;\n\n return {\n name: \"fig-refresh\",\n visitor: {\n Program: {\n exit(path) {\n const moduleId = (this as { opts?: { moduleId?: string } }).opts\n ?.moduleId;\n if (moduleId === undefined) return;\n\n const functions = new Map<string, FunctionRecord>();\n\n for (const statement of path.get(\"body\")) {\n const exported =\n statement.isExportNamedDeclaration() ||\n statement.isExportDefaultDeclaration();\n const declaration = exported\n ? statement.get(\"declaration\")\n : statement;\n if (\n !Array.isArray(declaration) &&\n (declaration.isFunctionDeclaration() ||\n declaration.isVariableDeclaration())\n ) {\n collectFunction(declaration, functions);\n }\n }\n\n const components = [...functions.values()].filter(\n (record) => record.isComponent,\n );\n if (components.length === 0) return;\n\n const canSelfAccept = exportsOnlyComponents(path, functions);\n for (const component of components) {\n const result = signatureFor(component, [component.name]);\n (component as FunctionRecord & SignatureResult).signature =\n result.signature;\n (component as FunctionRecord & SignatureResult).forceReset =\n result.forceReset;\n }\n\n path.node.body.unshift(\n template.statement.ast(\n canSelfAccept\n ? `import { register as __figReg, setSignature as __figSig, enqueueRefresh as __figRefresh } from \"virtual:fig-refresh\";`\n : `import { register as __figReg, setSignature as __figSig } from \"virtual:fig-refresh\";`,\n ),\n );\n\n const tail = [];\n for (const component of components) {\n const signature =\n (component as FunctionRecord & Partial<SignatureResult>)\n .signature ?? \"\";\n const forceReset =\n (component as FunctionRecord & Partial<SignatureResult>)\n .forceReset === true;\n tail.push(\n template.statement.ast(\n `__figReg(${component.name}, ${JSON.stringify(\n `${moduleId}#${component.name}`,\n )});`,\n ),\n );\n tail.push(\n template.statement.ast(\n forceReset\n ? `__figSig(${component.name}, ${JSON.stringify(\n signature,\n )}, true);`\n : `__figSig(${component.name}, ${JSON.stringify(\n signature,\n )});`,\n ),\n );\n }\n if (canSelfAccept) {\n tail.push(\n template.statement.ast(\n `if (import.meta.hot) { import.meta.hot.accept(); __figRefresh(); }`,\n ),\n );\n }\n path.node.body.push(...(tail as never[]));\n\n function collectFunction(\n declaration:\n | NodePath<babel.types.FunctionDeclaration>\n | NodePath<babel.types.VariableDeclaration>,\n into: Map<string, FunctionRecord>,\n ): void {\n if (declaration.isFunctionDeclaration()) {\n const id = declaration.node.id;\n if (id != null) {\n const isComponent = isComponentName(id.name);\n const isCustomHook = isCustomHookName(id.name);\n if (isComponent || isCustomHook) {\n into.set(id.name, {\n fnPath: declaration,\n isComponent,\n isCustomHook,\n name: id.name,\n });\n }\n }\n return;\n }\n\n if (declaration.isVariableDeclaration()) {\n for (const declarator of declaration.get(\"declarations\")) {\n const id = declarator.node.id;\n const init = declarator.node.init;\n if (\n t.isIdentifier(id) &&\n init != null &&\n (t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init))\n ) {\n const isComponent = isComponentName(id.name);\n const isCustomHook = isCustomHookName(id.name);\n if (!isComponent && !isCustomHook) continue;\n into.set(id.name, {\n fnPath: declarator.get(\"init\") as NodePath,\n isComponent,\n isCustomHook,\n name: id.name,\n });\n }\n }\n }\n }\n\n function signatureFor(\n record: FunctionRecord,\n stack: string[],\n ): SignatureResult {\n const hookNames: string[] = [];\n let forceReset = false;\n\n record.fnPath.traverse({\n CallExpression(call) {\n if (call.getFunctionParent()?.node !== record.fnPath.node) {\n return;\n }\n\n const callee = call.node.callee;\n const hookName = hookCallName(callee);\n if (hookName === null) {\n return;\n }\n\n hookNames.push(hookName);\n const hookRecord = functions.get(hookName);\n if (hookRecord?.isCustomHook !== true) {\n if (!isBuiltinFigHookName(hookName)) forceReset = true;\n return;\n }\n\n if (stack.includes(hookName)) {\n forceReset = true;\n return;\n }\n\n const nested = signatureFor(hookRecord, [...stack, hookName]);\n forceReset = forceReset || nested.forceReset;\n if (nested.signature !== \"\") {\n hookNames.push(\n ...nested.signature.split(\"\\n\").map((line) => `>${line}`),\n );\n }\n },\n });\n\n return { forceReset, signature: hookNames.join(\"\\n\") };\n }\n\n function hookCallName(\n callee:\n | babel.types.Expression\n | babel.types.Import\n | babel.types.Super\n | babel.types.V8IntrinsicIdentifier,\n ): string | null {\n if (t.isIdentifier(callee)) {\n return isCustomHookName(callee.name) ? callee.name : null;\n }\n if (\n t.isMemberExpression(callee) &&\n !callee.computed &&\n t.isIdentifier(callee.property) &&\n isCustomHookName(callee.property.name)\n ) {\n return callee.property.name;\n }\n return null;\n }\n\n function exportsOnlyComponents(\n program: NodePath<babel.types.Program>,\n records: Map<string, FunctionRecord>,\n ): boolean {\n for (const statement of program.get(\"body\")) {\n if (statement.isExportNamedDeclaration()) {\n if (statement.node.exportKind === \"type\") continue;\n const declaration = statement.get(\"declaration\");\n if (!Array.isArray(declaration) && declaration.node != null) {\n if (\n !declarationExportsOnlyComponents(declaration.node, records)\n ) {\n return false;\n }\n continue;\n }\n\n if (statement.node.source != null) {\n for (const specifier of statement.node.specifiers) {\n if (\n \"exportKind\" in specifier &&\n specifier.exportKind === \"type\"\n ) {\n continue;\n }\n return false;\n }\n continue;\n }\n\n for (const specifier of statement.get(\"specifiers\")) {\n if (\n specifier.isExportSpecifier() &&\n specifier.node.exportKind === \"type\"\n ) {\n continue;\n }\n if (!specifier.isExportSpecifier()) return false;\n const local = specifier.node.local;\n if (!t.isIdentifier(local)) return false;\n if (records.get(local.name)?.isComponent !== true) {\n return false;\n }\n }\n continue;\n }\n\n if (statement.isExportDefaultDeclaration()) {\n const declaration = statement.get(\"declaration\");\n if (Array.isArray(declaration)) return false;\n if (declaration.isFunctionDeclaration()) {\n const id = declaration.node.id;\n if (id == null || !isComponentName(id.name)) return false;\n continue;\n }\n if (\n declaration.isIdentifier() &&\n records.get(declaration.node.name)?.isComponent === true\n ) {\n continue;\n }\n return false;\n }\n\n if (statement.isExportAllDeclaration()) return false;\n }\n\n return true;\n }\n\n function declarationExportsOnlyComponents(\n declaration: babel.types.Declaration,\n records: Map<string, FunctionRecord>,\n ): boolean {\n if (t.isFunctionDeclaration(declaration)) {\n const id = declaration.id;\n return id != null && records.get(id.name)?.isComponent === true;\n }\n\n if (!t.isVariableDeclaration(declaration)) return false;\n\n for (const declarator of declaration.declarations) {\n if (!t.isIdentifier(declarator.id)) return false;\n if (records.get(declarator.id.name)?.isComponent !== true) {\n return false;\n }\n }\n return true;\n }\n },\n },\n },\n };\n}\n\nfunction isComponentName(name: string): boolean {\n const first = name[0];\n return first !== undefined && first >= \"A\" && first <= \"Z\";\n}\n\nfunction isCustomHookName(name: string): boolean {\n return /^use[A-Z]/.test(name);\n}\n\nfunction isBuiltinFigHookName(name: string): boolean {\n return (\n name === \"useActionState\" ||\n name === \"useBeforeLayout\" ||\n name === \"useBeforePaint\" ||\n name === \"useCallback\" ||\n name === \"useSyncExternalStore\" ||\n name === \"useId\" ||\n name === \"useDeferredValue\" ||\n name === \"useMemo\" ||\n name === \"useReactive\" ||\n name === \"useStableEvent\" ||\n name === \"useState\" ||\n name === \"useTransition\"\n );\n}\n\n// Transform a single module. Returns null when it has no components (so the\n// caller leaves it for the regular pipeline).\nexport async function transformModule(\n code: string,\n id: string,\n): Promise<TransformResult | null> {\n const result = await babel.transformAsync(code, {\n babelrc: false,\n configFile: false,\n filename: id,\n sourceMaps: true,\n presets: [\n [\n presetTypescript,\n {\n ignoreExtensions: true,\n onlyRemoveTypeImports: true,\n },\n ],\n ],\n parserOpts: { plugins: id.endsWith(\"x\") ? [\"jsx\"] : [] },\n plugins: [[figRefreshBabelPlugin, { moduleId: id }]],\n });\n\n if (result?.code == null || !result.code.includes(\"virtual:fig-refresh\")) {\n return null;\n }\n return { code: result.code, map: result.map };\n}\n","import { fileURLToPath } from \"node:url\";\nimport { transformModule } from \"./transform.ts\";\n\nconst VIRTUAL_ID = \"virtual:fig-refresh\";\nconst RESOLVED_VIRTUAL_ID = \"\\0virtual:fig-refresh\";\nconst REFRESH_RUNTIME_IMPORT = viteFileImport(\n import.meta.resolve(\"@bgub/fig-refresh\"),\n);\n// A bare specifier so the app's resolve config (aliases, dedupe, prebundling)\n// applies; a file path would load a second copy of the scheduler state next to\n// the one `@bgub/fig-dom` wires up internally.\nconst DOM_REFRESH_IMPORT = \"@bgub/fig-dom/refresh\";\n\nexport interface FigRefreshOptions {\n // Files to consider for the refresh transform. Defaults to JS/TS(X).\n include?: RegExp;\n}\n\n// Minimal structural shape of a Vite plugin (avoids a hard dep on vite types).\nexport interface FigRefreshPlugin {\n apply: \"serve\";\n enforce: \"pre\";\n load(id: string): string | null;\n name: string;\n resolveId(id: string): string | null;\n transform(\n code: string,\n id: string,\n options?: { ssr?: boolean },\n ): Promise<{ code: string; map: unknown } | null>;\n}\n\nexport function figRefresh(options: FigRefreshOptions = {}): FigRefreshPlugin {\n const include = options.include ?? /\\.[jt]sx?$/;\n\n return {\n apply: \"serve\",\n enforce: \"pre\",\n name: \"fig:refresh\",\n resolveId(id) {\n return id === VIRTUAL_ID ? RESOLVED_VIRTUAL_ID : null;\n },\n load(id) {\n return id === RESOLVED_VIRTUAL_ID ? virtualModuleCode() : null;\n },\n async transform(code, id, transformOptions) {\n if (transformOptions?.ssr === true) return null;\n\n const clean = id.split(\"?\")[0] ?? id;\n if (clean.includes(\"/node_modules/\") || !include.test(clean)) return null;\n return transformModule(code, clean);\n },\n };\n}\n\n// The virtual runtime module: wires the renderer to the refresh runtime once,\n// and exposes register/setSignature plus a microtask-batched refresh trigger.\nfunction virtualModuleCode(): string {\n return `import { injectScheduleRefresh, performRefresh, register, setSignature } from ${JSON.stringify(\n REFRESH_RUNTIME_IMPORT,\n )};\nimport { scheduleRefresh } from ${JSON.stringify(DOM_REFRESH_IMPORT)};\n\ninjectScheduleRefresh(scheduleRefresh);\n\nlet queued = false;\nexport function enqueueRefresh() {\n if (queued) return;\n queued = true;\n queueMicrotask(() => {\n queued = false;\n performRefresh();\n });\n}\n\nexport { register, setSignature };\n`;\n}\n\nfunction viteFileImport(url: string): string {\n return `/@fs/${fileURLToPath(url)}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/transform.ts","../src/index.ts"],"sourcesContent":["import * as babel from \"@babel/core\";\nimport type { InputOptions, NodePath, PluginObject } from \"@babel/core\";\nimport presetTypescript from \"@babel/preset-typescript\";\n\ninterface FunctionRecord {\n fnPath: NodePath;\n isComponent: boolean;\n isCustomHook: boolean;\n name: string;\n}\n\ninterface SignatureResult {\n forceReset: boolean;\n signature: string;\n}\n\nfunction figDevGateBabelPlugin(api: typeof babel): PluginObject {\n const t = api.types;\n\n return {\n name: \"fig-dev-gate\",\n visitor: {\n ReferencedIdentifier(path) {\n if (path.node.name !== \"__FIG_DEV__\") return;\n if (path.scope.hasBinding(\"__FIG_DEV__\")) return;\n const development = (this as { opts?: { development?: boolean } }).opts\n ?.development;\n path.replaceWith(t.booleanLiteral(development === true));\n },\n },\n };\n}\n\n// Babel visitor: find top-level component declarations (PascalCase functions),\n// then inject calls to the Fig refresh runtime + a self-accepting HMR boundary.\n// Emits register/setSignature directly (no react-refresh global protocol).\nfunction figRefreshBabelPlugin(api: typeof babel): PluginObject {\n const t = api.types;\n const template = api.template;\n\n return {\n name: \"fig-refresh\",\n visitor: {\n Program: {\n exit(path) {\n const moduleId = (this as { opts?: { moduleId?: string } }).opts\n ?.moduleId;\n if (moduleId === undefined) return;\n\n const functions = new Map<string, FunctionRecord>();\n\n for (const statement of path.get(\"body\")) {\n const exported =\n statement.isExportNamedDeclaration() ||\n statement.isExportDefaultDeclaration();\n const declaration = exported\n ? statement.get(\"declaration\")\n : statement;\n if (\n !Array.isArray(declaration) &&\n (declaration.isFunctionDeclaration() ||\n declaration.isVariableDeclaration())\n ) {\n collectFunction(declaration, functions);\n }\n }\n\n const components = [...functions.values()].filter(\n (record) => record.isComponent,\n );\n if (components.length === 0) return;\n\n const canSelfAccept = exportsOnlyComponents(path, functions);\n for (const component of components) {\n const result = signatureFor(component, [component.name]);\n (component as FunctionRecord & SignatureResult).signature =\n result.signature;\n (component as FunctionRecord & SignatureResult).forceReset =\n result.forceReset;\n }\n\n path.node.body.unshift(\n template.statement.ast(\n canSelfAccept\n ? `import { register as __figReg, setSignature as __figSig, enqueueRefresh as __figRefresh } from \"virtual:fig-refresh\";`\n : `import { register as __figReg, setSignature as __figSig } from \"virtual:fig-refresh\";`,\n ),\n );\n\n const tail = [];\n for (const component of components) {\n const signature =\n (component as FunctionRecord & Partial<SignatureResult>)\n .signature ?? \"\";\n const forceReset =\n (component as FunctionRecord & Partial<SignatureResult>)\n .forceReset === true;\n tail.push(\n template.statement.ast(\n `__figReg(${component.name}, ${JSON.stringify(\n `${moduleId}#${component.name}`,\n )});`,\n ),\n );\n tail.push(\n template.statement.ast(\n forceReset\n ? `__figSig(${component.name}, ${JSON.stringify(\n signature,\n )}, true);`\n : `__figSig(${component.name}, ${JSON.stringify(\n signature,\n )});`,\n ),\n );\n }\n if (canSelfAccept) {\n tail.push(\n template.statement.ast(\n `if (import.meta.hot) { import.meta.hot.accept(); __figRefresh(); }`,\n ),\n );\n }\n path.node.body.push(...(tail as never[]));\n\n function collectFunction(\n declaration:\n | NodePath<babel.types.FunctionDeclaration>\n | NodePath<babel.types.VariableDeclaration>,\n into: Map<string, FunctionRecord>,\n ): void {\n if (declaration.isFunctionDeclaration()) {\n const id = declaration.node.id;\n if (id != null) {\n const isComponent = isComponentName(id.name);\n const isCustomHook = isCustomHookName(id.name);\n if (isComponent || isCustomHook) {\n into.set(id.name, {\n fnPath: declaration,\n isComponent,\n isCustomHook,\n name: id.name,\n });\n }\n }\n return;\n }\n\n if (declaration.isVariableDeclaration()) {\n for (const declarator of declaration.get(\"declarations\")) {\n const id = declarator.node.id;\n const init = declarator.node.init;\n if (\n t.isIdentifier(id) &&\n init != null &&\n (t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init))\n ) {\n const isComponent = isComponentName(id.name);\n const isCustomHook = isCustomHookName(id.name);\n if (!isComponent && !isCustomHook) continue;\n into.set(id.name, {\n fnPath: declarator.get(\"init\") as NodePath,\n isComponent,\n isCustomHook,\n name: id.name,\n });\n }\n }\n }\n }\n\n function signatureFor(\n record: FunctionRecord,\n stack: string[],\n ): SignatureResult {\n const hookNames: string[] = [];\n let forceReset = false;\n\n record.fnPath.traverse({\n CallExpression(call) {\n if (call.getFunctionParent()?.node !== record.fnPath.node) {\n return;\n }\n\n const callee = call.node.callee;\n const hookName = hookCallName(callee);\n if (hookName === null) {\n return;\n }\n\n hookNames.push(hookName);\n const hookRecord = functions.get(hookName);\n if (hookRecord?.isCustomHook !== true) {\n if (!isBuiltinFigHookName(hookName)) forceReset = true;\n return;\n }\n\n if (stack.includes(hookName)) {\n forceReset = true;\n return;\n }\n\n const nested = signatureFor(hookRecord, [...stack, hookName]);\n forceReset = forceReset || nested.forceReset;\n if (nested.signature !== \"\") {\n hookNames.push(\n ...nested.signature.split(\"\\n\").map((line) => `>${line}`),\n );\n }\n },\n });\n\n return { forceReset, signature: hookNames.join(\"\\n\") };\n }\n\n function hookCallName(\n callee:\n | babel.types.Expression\n | babel.types.Import\n | babel.types.Super\n | babel.types.V8IntrinsicIdentifier,\n ): string | null {\n if (t.isIdentifier(callee)) {\n return isCustomHookName(callee.name) ? callee.name : null;\n }\n if (\n t.isMemberExpression(callee) &&\n !callee.computed &&\n t.isIdentifier(callee.property) &&\n isCustomHookName(callee.property.name)\n ) {\n return callee.property.name;\n }\n return null;\n }\n\n function exportsOnlyComponents(\n program: NodePath<babel.types.Program>,\n records: Map<string, FunctionRecord>,\n ): boolean {\n for (const statement of program.get(\"body\")) {\n if (statement.isExportNamedDeclaration()) {\n if (statement.node.exportKind === \"type\") continue;\n const declaration = statement.get(\"declaration\");\n if (!Array.isArray(declaration) && declaration.node != null) {\n if (\n !declarationExportsOnlyComponents(declaration.node, records)\n ) {\n return false;\n }\n continue;\n }\n\n if (statement.node.source != null) {\n for (const specifier of statement.node.specifiers) {\n if (\n \"exportKind\" in specifier &&\n specifier.exportKind === \"type\"\n ) {\n continue;\n }\n return false;\n }\n continue;\n }\n\n for (const specifier of statement.get(\"specifiers\")) {\n if (\n specifier.isExportSpecifier() &&\n specifier.node.exportKind === \"type\"\n ) {\n continue;\n }\n if (!specifier.isExportSpecifier()) return false;\n const local = specifier.node.local;\n if (!t.isIdentifier(local)) return false;\n if (records.get(local.name)?.isComponent !== true) {\n return false;\n }\n }\n continue;\n }\n\n if (statement.isExportDefaultDeclaration()) {\n const declaration = statement.get(\"declaration\");\n if (Array.isArray(declaration)) return false;\n if (declaration.isFunctionDeclaration()) {\n const id = declaration.node.id;\n if (id == null || !isComponentName(id.name)) return false;\n continue;\n }\n if (\n declaration.isIdentifier() &&\n records.get(declaration.node.name)?.isComponent === true\n ) {\n continue;\n }\n return false;\n }\n\n if (statement.isExportAllDeclaration()) return false;\n }\n\n return true;\n }\n\n function declarationExportsOnlyComponents(\n declaration: babel.types.Declaration,\n records: Map<string, FunctionRecord>,\n ): boolean {\n if (t.isFunctionDeclaration(declaration)) {\n const id = declaration.id;\n return id != null && records.get(id.name)?.isComponent === true;\n }\n\n if (!t.isVariableDeclaration(declaration)) return false;\n\n for (const declarator of declaration.declarations) {\n if (!t.isIdentifier(declarator.id)) return false;\n if (records.get(declarator.id.name)?.isComponent !== true) {\n return false;\n }\n }\n return true;\n }\n },\n },\n },\n };\n}\n\nfunction isComponentName(name: string): boolean {\n const first = name[0];\n return first !== undefined && first >= \"A\" && first <= \"Z\";\n}\n\nfunction isCustomHookName(name: string): boolean {\n return /^use[A-Z]/.test(name);\n}\n\nfunction isBuiltinFigHookName(name: string): boolean {\n return (\n name === \"useActionState\" ||\n name === \"useBeforeLayout\" ||\n name === \"useBeforePaint\" ||\n name === \"useCallback\" ||\n name === \"useSyncExternalStore\" ||\n name === \"useId\" ||\n name === \"useDeferredValue\" ||\n name === \"useMemo\" ||\n name === \"useReactive\" ||\n name === \"useStableEvent\" ||\n name === \"useState\" ||\n name === \"useTransition\"\n );\n}\n\n// Transform a single module. Returns null when it has no components (so the\n// caller leaves it for the regular pipeline).\nexport async function transformModule(code: string, id: string) {\n const result = await babel.transformAsync(code, {\n ...transformOptions(id),\n plugins: [[figRefreshBabelPlugin, { moduleId: id }]],\n });\n\n if (result?.code == null || !result.code.includes(\"virtual:fig-refresh\")) {\n return null;\n }\n return { code: result.code, map: mutableSourceMap(result.map) };\n}\n\nexport async function transformDevGate(\n code: string,\n id: string,\n development: boolean,\n) {\n const result = await babel.transformAsync(code, {\n ...transformOptions(id),\n plugins: [[figDevGateBabelPlugin, { development }]],\n });\n if (result?.code == null) {\n throw new Error(`Could not transform the Fig development gate in ${id}.`);\n }\n return { code: result.code, map: mutableSourceMap(result.map) };\n}\n\nfunction mutableSourceMap(map: babel.FileResult[\"map\"]) {\n if (map === null) return undefined;\n return {\n ...map,\n names: [...map.names],\n sources: [...map.sources],\n sourcesContent:\n map.sourcesContent === undefined ? undefined : [...map.sourcesContent],\n };\n}\n\nfunction transformOptions(id: string): InputOptions {\n return {\n babelrc: false,\n configFile: false,\n filename: id,\n sourceMaps: true,\n presets: [\n [\n presetTypescript,\n {\n ignoreExtensions: true,\n onlyRemoveTypeImports: true,\n },\n ],\n ],\n parserOpts: { plugins: id.endsWith(\"x\") ? [\"jsx\"] : [] },\n };\n}\n","import { fileURLToPath } from \"node:url\";\nimport { defaultClientConditions, defaultServerConditions } from \"vite\";\nimport type { Plugin } from \"vite\";\nimport { transformDevGate, transformModule } from \"./transform.ts\";\n\nconst VIRTUAL_ID = \"virtual:fig-refresh\";\nconst RESOLVED_VIRTUAL_ID = \"\\0virtual:fig-refresh\";\nconst DEVELOPMENT_CONDITION = \"fig-development\";\nconst FIG_PACKAGE_PATTERN = /^@bgub\\/fig(?:$|[-/])/;\nconst REFRESH_RUNTIME_IMPORT = viteFileImport(\n import.meta.resolve(\"@bgub/fig-refresh\"),\n);\n// Resolve the adapter from the app graph so it owns the same DOM renderer and\n// reconciler as the application.\nconst DOM_REFRESH_IMPORT = \"@bgub/fig-dom/refresh\";\n\nexport interface FigRefreshOptions {\n // Files to consider for the refresh transform. Defaults to JS/TS(X).\n include?: RegExp;\n}\n\n// Kept as a named alias for compatibility with the original public surface.\nexport type FigRefreshPlugin = Plugin;\n\nexport function fig(\n options: FigRefreshOptions = {},\n): [Plugin, FigRefreshPlugin] {\n return [figConfig(), figRefresh(options)];\n}\n\nfunction figConfig(): Plugin {\n return {\n name: \"fig:config\",\n config(config, environment) {\n return {\n ...(config.define?.__FIG_DEV__ === undefined\n ? {\n define: {\n __FIG_DEV__: JSON.stringify(environment.command === \"serve\"),\n },\n }\n : {}),\n // Fig's renderer, server, and framework adapters share ambient state.\n // Keeping sibling packages in one SSR graph prevents an SSR packager\n // from splitting that state across independently evaluated modules.\n ssr: { noExternal: [FIG_PACKAGE_PATTERN] },\n };\n },\n configEnvironment(name, config, environment) {\n if (!developmentGate(config.define?.__FIG_DEV__)) return null;\n\n const clientEnvironment =\n environment.isSsrTargetWebworker === true ||\n (config.consumer ?? (name === \"client\" ? \"client\" : \"server\")) ===\n \"client\";\n const defaults = clientEnvironment\n ? defaultClientConditions\n : defaultServerConditions;\n return {\n resolve: {\n conditions: [\n ...new Set([\n ...(config.resolve?.conditions ?? defaults),\n DEVELOPMENT_CONDITION,\n ]),\n ],\n },\n };\n },\n async transform(code, id) {\n const gate = this.environment.config.define?.__FIG_DEV__;\n if (gate === undefined || !code.includes(\"__FIG_DEV__\")) return null;\n\n return transformDevGate(code, id, developmentGate(gate));\n },\n };\n}\n\nfunction developmentGate(value: unknown): boolean {\n if (value === true || value === \"true\") return true;\n if (value === false || value === \"false\") return false;\n throw new Error(\n \"Fig's __FIG_DEV__ definition must be the static value true or false.\",\n );\n}\n\nexport function figRefresh(options: FigRefreshOptions = {}): FigRefreshPlugin {\n const include = options.include ?? /\\.[jt]sx?$/;\n\n return {\n apply: \"serve\",\n enforce: \"pre\",\n name: \"fig:refresh\",\n resolveId(id) {\n return id === VIRTUAL_ID ? RESOLVED_VIRTUAL_ID : null;\n },\n load(id) {\n return id === RESOLVED_VIRTUAL_ID ? virtualModuleCode() : null;\n },\n async transform(code, id, transformOptions) {\n if (transformOptions?.ssr === true) return null;\n const gate = this.environment.config.define?.__FIG_DEV__;\n if (gate !== undefined && !developmentGate(gate)) return null;\n\n const clean = id.split(\"?\")[0] ?? id;\n if (clean.includes(\"/node_modules/\") || !include.test(clean)) return null;\n return transformModule(code, clean);\n },\n };\n}\n\n// The virtual runtime module: wires the renderer to the refresh runtime once,\n// and exposes register/setSignature plus a microtask-batched refresh trigger.\nfunction virtualModuleCode(): string {\n return `import { injectRenderer, performRefresh, register, setSignature } from ${JSON.stringify(\n REFRESH_RUNTIME_IMPORT,\n )};\nimport { domRefreshAdapter } from ${JSON.stringify(DOM_REFRESH_IMPORT)};\n\ninjectRenderer(domRefreshAdapter);\n\nlet queued = false;\nexport function enqueueRefresh() {\n if (queued) return;\n queued = true;\n queueMicrotask(() => {\n queued = false;\n performRefresh();\n });\n}\n\nexport { register, setSignature };\n`;\n}\n\nfunction viteFileImport(url: string): string {\n return `/@fs/${fileURLToPath(url)}`;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgub/fig-vite",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Fig Vite plugin for state-preserving Fast Refresh",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/bgub/fig",
@@ -37,11 +37,16 @@
37
37
  "dependencies": {
38
38
  "@babel/core": "^8.0.1",
39
39
  "@babel/preset-typescript": "^8.0.1",
40
- "@bgub/fig-dom": "0.1.0-alpha.1",
41
- "@bgub/fig-refresh": "0.1.0-alpha.1"
40
+ "@bgub/fig-refresh": "0.1.0-alpha.3"
41
+ },
42
+ "peerDependencies": {
43
+ "vite": "^7.0.0 || ^8.0.0",
44
+ "@bgub/fig-dom": "0.1.0-alpha.3"
42
45
  },
43
46
  "devDependencies": {
44
- "@types/node": "^20.19.0"
47
+ "@types/node": "^20.19.0",
48
+ "vite": "8.1.4",
49
+ "@bgub/fig-dom": "0.1.0-alpha.3"
45
50
  },
46
51
  "engines": {
47
52
  "node": "^20.19.0 || >=22.12.0"