@bgub/fig-vite 0.0.0-tegami-trusted-publish-setup → 0.1.0-alpha.2
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 +41 -0
- package/LICENSE +21 -0
- package/README.md +16 -3
- package/dist/index.d.ts +20 -0
- package/dist/index.js +234 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## @bgub/fig-vite@0.1.0-alpha.1
|
|
2
|
+
|
|
3
|
+
### Refresh resolves the app's renderer runtime
|
|
4
|
+
|
|
5
|
+
`figRefresh` now imports `@bgub/fig-dom/refresh` through its bare specifier
|
|
6
|
+
rather than a resolved `/@fs/` path, so app-level aliases, dedupe, and
|
|
7
|
+
prebundling apply and the refresh scheduler cannot be instantiated twice.
|
|
8
|
+
|
|
9
|
+
### TanStack Start's client graph is prebundled
|
|
10
|
+
|
|
11
|
+
The TanStack Start adapter now prebundles `@tanstack/start-client-core` in
|
|
12
|
+
development while leaving its application-bound router and Start imports as
|
|
13
|
+
external Vite modules. This reduces the module-request waterfall without
|
|
14
|
+
freezing generated app entries or the linked Fig adapter packages. Production
|
|
15
|
+
continues to use Vite's normal application bundling.
|
|
16
|
+
|
|
17
|
+
### Use one data-resource API in every environment
|
|
18
|
+
|
|
19
|
+
`dataResource` now covers shared, browser, and server-only loaders without a
|
|
20
|
+
second API. Server-only loaders belong behind the framework's server module
|
|
21
|
+
boundary; browser code uses an explicit key-only resource when it needs the
|
|
22
|
+
same hydrated value.
|
|
23
|
+
|
|
24
|
+
The pass-through `serverDataResource` API, `@bgub/fig/server` entry point,
|
|
25
|
+
`figData` Vite transform, and generated browser resource stubs are removed.
|
|
26
|
+
|
|
27
|
+
### TanStack Start gains state-preserving Fast Refresh
|
|
28
|
+
|
|
29
|
+
The TanStack Start Vite adapter now installs Fig Fast Refresh automatically.
|
|
30
|
+
Component edits update in place and preserve hook state in accepted modules.
|
|
31
|
+
|
|
32
|
+
`@bgub/fig-vite` is now a public package containing the reusable Fast Refresh
|
|
33
|
+
and server data-resource transforms.
|
|
34
|
+
|
|
35
|
+
## @bgub/fig-vite@0.1.0-alpha.0 (alpha)
|
|
36
|
+
|
|
37
|
+
### Initial alpha release
|
|
38
|
+
|
|
39
|
+
Fig Fast Refresh and server data-resource transforms for Vite.
|
|
40
|
+
|
|
41
|
+
# Changelog
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Ben Gubler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bgub/fig-vite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vite integration for Fig. It provides state-preserving Fast Refresh.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```ts
|
|
6
|
+
import { figRefresh } from "@bgub/fig-vite";
|
|
7
|
+
|
|
8
|
+
const plugins = [figRefresh()];
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Framework adapters may install this plugin for you. In particular,
|
|
12
|
+
`@bgub/fig-tanstack-start/plugin/vite` includes `figRefresh()`.
|
|
13
|
+
|
|
14
|
+
Fig packages are ESM-only and require Node `^20.19.0 || >=22.12.0`.
|
|
15
|
+
|
|
16
|
+
## License
|
|
17
|
+
|
|
18
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
interface FigRefreshOptions {
|
|
3
|
+
include?: RegExp;
|
|
4
|
+
}
|
|
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
|
+
}
|
|
18
|
+
declare function figRefresh(options?: FigRefreshOptions): FigRefreshPlugin;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { FigRefreshOptions, FigRefreshPlugin, figRefresh };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import * as babel from "@babel/core";
|
|
3
|
+
import presetTypescript from "@babel/preset-typescript";
|
|
4
|
+
//#region src/transform.ts
|
|
5
|
+
function figRefreshBabelPlugin(api) {
|
|
6
|
+
const t = api.types;
|
|
7
|
+
const template = api.template;
|
|
8
|
+
return {
|
|
9
|
+
name: "fig-refresh",
|
|
10
|
+
visitor: { Program: { exit(path) {
|
|
11
|
+
const moduleId = this.opts?.moduleId;
|
|
12
|
+
if (moduleId === void 0) return;
|
|
13
|
+
const functions = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const statement of path.get("body")) {
|
|
15
|
+
const declaration = statement.isExportNamedDeclaration() || statement.isExportDefaultDeclaration() ? statement.get("declaration") : statement;
|
|
16
|
+
if (!Array.isArray(declaration) && (declaration.isFunctionDeclaration() || declaration.isVariableDeclaration())) collectFunction(declaration, functions);
|
|
17
|
+
}
|
|
18
|
+
const components = [...functions.values()].filter((record) => record.isComponent);
|
|
19
|
+
if (components.length === 0) return;
|
|
20
|
+
const canSelfAccept = exportsOnlyComponents(path, functions);
|
|
21
|
+
for (const component of components) {
|
|
22
|
+
const result = signatureFor(component, [component.name]);
|
|
23
|
+
component.signature = result.signature;
|
|
24
|
+
component.forceReset = result.forceReset;
|
|
25
|
+
}
|
|
26
|
+
path.node.body.unshift(template.statement.ast(canSelfAccept ? `import { register as __figReg, setSignature as __figSig, enqueueRefresh as __figRefresh } from "virtual:fig-refresh";` : `import { register as __figReg, setSignature as __figSig } from "virtual:fig-refresh";`));
|
|
27
|
+
const tail = [];
|
|
28
|
+
for (const component of components) {
|
|
29
|
+
const signature = component.signature ?? "";
|
|
30
|
+
const forceReset = component.forceReset === true;
|
|
31
|
+
tail.push(template.statement.ast(`__figReg(${component.name}, ${JSON.stringify(`${moduleId}#${component.name}`)});`));
|
|
32
|
+
tail.push(template.statement.ast(forceReset ? `__figSig(${component.name}, ${JSON.stringify(signature)}, true);` : `__figSig(${component.name}, ${JSON.stringify(signature)});`));
|
|
33
|
+
}
|
|
34
|
+
if (canSelfAccept) tail.push(template.statement.ast(`if (import.meta.hot) { import.meta.hot.accept(); __figRefresh(); }`));
|
|
35
|
+
path.node.body.push(...tail);
|
|
36
|
+
function collectFunction(declaration, into) {
|
|
37
|
+
if (declaration.isFunctionDeclaration()) {
|
|
38
|
+
const id = declaration.node.id;
|
|
39
|
+
if (id != null) {
|
|
40
|
+
const isComponent = isComponentName(id.name);
|
|
41
|
+
const isCustomHook = isCustomHookName(id.name);
|
|
42
|
+
if (isComponent || isCustomHook) into.set(id.name, {
|
|
43
|
+
fnPath: declaration,
|
|
44
|
+
isComponent,
|
|
45
|
+
isCustomHook,
|
|
46
|
+
name: id.name
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (declaration.isVariableDeclaration()) for (const declarator of declaration.get("declarations")) {
|
|
52
|
+
const id = declarator.node.id;
|
|
53
|
+
const init = declarator.node.init;
|
|
54
|
+
if (t.isIdentifier(id) && init != null && (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init))) {
|
|
55
|
+
const isComponent = isComponentName(id.name);
|
|
56
|
+
const isCustomHook = isCustomHookName(id.name);
|
|
57
|
+
if (!isComponent && !isCustomHook) continue;
|
|
58
|
+
into.set(id.name, {
|
|
59
|
+
fnPath: declarator.get("init"),
|
|
60
|
+
isComponent,
|
|
61
|
+
isCustomHook,
|
|
62
|
+
name: id.name
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function signatureFor(record, stack) {
|
|
68
|
+
const hookNames = [];
|
|
69
|
+
let forceReset = false;
|
|
70
|
+
record.fnPath.traverse({ CallExpression(call) {
|
|
71
|
+
if (call.getFunctionParent()?.node !== record.fnPath.node) return;
|
|
72
|
+
const callee = call.node.callee;
|
|
73
|
+
const hookName = hookCallName(callee);
|
|
74
|
+
if (hookName === null) return;
|
|
75
|
+
hookNames.push(hookName);
|
|
76
|
+
const hookRecord = functions.get(hookName);
|
|
77
|
+
if (hookRecord?.isCustomHook !== true) {
|
|
78
|
+
if (!isBuiltinFigHookName(hookName)) forceReset = true;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (stack.includes(hookName)) {
|
|
82
|
+
forceReset = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const nested = signatureFor(hookRecord, [...stack, hookName]);
|
|
86
|
+
forceReset = forceReset || nested.forceReset;
|
|
87
|
+
if (nested.signature !== "") hookNames.push(...nested.signature.split("\n").map((line) => `>${line}`));
|
|
88
|
+
} });
|
|
89
|
+
return {
|
|
90
|
+
forceReset,
|
|
91
|
+
signature: hookNames.join("\n")
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function hookCallName(callee) {
|
|
95
|
+
if (t.isIdentifier(callee)) return isCustomHookName(callee.name) ? callee.name : null;
|
|
96
|
+
if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.property) && isCustomHookName(callee.property.name)) return callee.property.name;
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function exportsOnlyComponents(program, records) {
|
|
100
|
+
for (const statement of program.get("body")) {
|
|
101
|
+
if (statement.isExportNamedDeclaration()) {
|
|
102
|
+
if (statement.node.exportKind === "type") continue;
|
|
103
|
+
const declaration = statement.get("declaration");
|
|
104
|
+
if (!Array.isArray(declaration) && declaration.node != null) {
|
|
105
|
+
if (!declarationExportsOnlyComponents(declaration.node, records)) return false;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (statement.node.source != null) {
|
|
109
|
+
for (const specifier of statement.node.specifiers) {
|
|
110
|
+
if ("exportKind" in specifier && specifier.exportKind === "type") continue;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
for (const specifier of statement.get("specifiers")) {
|
|
116
|
+
if (specifier.isExportSpecifier() && specifier.node.exportKind === "type") continue;
|
|
117
|
+
if (!specifier.isExportSpecifier()) return false;
|
|
118
|
+
const local = specifier.node.local;
|
|
119
|
+
if (!t.isIdentifier(local)) return false;
|
|
120
|
+
if (records.get(local.name)?.isComponent !== true) return false;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (statement.isExportDefaultDeclaration()) {
|
|
125
|
+
const declaration = statement.get("declaration");
|
|
126
|
+
if (Array.isArray(declaration)) return false;
|
|
127
|
+
if (declaration.isFunctionDeclaration()) {
|
|
128
|
+
const id = declaration.node.id;
|
|
129
|
+
if (id == null || !isComponentName(id.name)) return false;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (declaration.isIdentifier() && records.get(declaration.node.name)?.isComponent === true) continue;
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (statement.isExportAllDeclaration()) return false;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
function declarationExportsOnlyComponents(declaration, records) {
|
|
140
|
+
if (t.isFunctionDeclaration(declaration)) {
|
|
141
|
+
const id = declaration.id;
|
|
142
|
+
return id != null && records.get(id.name)?.isComponent === true;
|
|
143
|
+
}
|
|
144
|
+
if (!t.isVariableDeclaration(declaration)) return false;
|
|
145
|
+
for (const declarator of declaration.declarations) {
|
|
146
|
+
if (!t.isIdentifier(declarator.id)) return false;
|
|
147
|
+
if (records.get(declarator.id.name)?.isComponent !== true) return false;
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
} } }
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function isComponentName(name) {
|
|
155
|
+
const first = name[0];
|
|
156
|
+
return first !== void 0 && first >= "A" && first <= "Z";
|
|
157
|
+
}
|
|
158
|
+
function isCustomHookName(name) {
|
|
159
|
+
return /^use[A-Z]/.test(name);
|
|
160
|
+
}
|
|
161
|
+
function isBuiltinFigHookName(name) {
|
|
162
|
+
return name === "useActionState" || name === "useBeforeLayout" || name === "useBeforePaint" || name === "useCallback" || name === "useSyncExternalStore" || name === "useId" || name === "useDeferredValue" || name === "useMemo" || name === "useReactive" || name === "useStableEvent" || name === "useState" || name === "useTransition";
|
|
163
|
+
}
|
|
164
|
+
async function transformModule(code, id) {
|
|
165
|
+
const result = await babel.transformAsync(code, {
|
|
166
|
+
babelrc: false,
|
|
167
|
+
configFile: false,
|
|
168
|
+
filename: id,
|
|
169
|
+
sourceMaps: true,
|
|
170
|
+
presets: [[presetTypescript, {
|
|
171
|
+
ignoreExtensions: true,
|
|
172
|
+
onlyRemoveTypeImports: true
|
|
173
|
+
}]],
|
|
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
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/index.ts
|
|
185
|
+
const VIRTUAL_ID = "virtual:fig-refresh";
|
|
186
|
+
const RESOLVED_VIRTUAL_ID = "\0virtual:fig-refresh";
|
|
187
|
+
const REFRESH_RUNTIME_IMPORT = viteFileImport(import.meta.resolve("@bgub/fig-refresh"));
|
|
188
|
+
const DOM_REFRESH_IMPORT = "@bgub/fig-dom/refresh";
|
|
189
|
+
function figRefresh(options = {}) {
|
|
190
|
+
const include = options.include ?? /\.[jt]sx?$/;
|
|
191
|
+
return {
|
|
192
|
+
apply: "serve",
|
|
193
|
+
enforce: "pre",
|
|
194
|
+
name: "fig:refresh",
|
|
195
|
+
resolveId(id) {
|
|
196
|
+
return id === VIRTUAL_ID ? RESOLVED_VIRTUAL_ID : null;
|
|
197
|
+
},
|
|
198
|
+
load(id) {
|
|
199
|
+
return id === RESOLVED_VIRTUAL_ID ? virtualModuleCode() : null;
|
|
200
|
+
},
|
|
201
|
+
async transform(code, id, transformOptions) {
|
|
202
|
+
if (transformOptions?.ssr === true) return null;
|
|
203
|
+
const clean = id.split("?")[0] ?? id;
|
|
204
|
+
if (clean.includes("/node_modules/") || !include.test(clean)) return null;
|
|
205
|
+
return transformModule(code, clean);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function virtualModuleCode() {
|
|
210
|
+
return `import { injectScheduleRefresh, performRefresh, register, setSignature } from ${JSON.stringify(REFRESH_RUNTIME_IMPORT)};
|
|
211
|
+
import { scheduleRefresh } from ${JSON.stringify(DOM_REFRESH_IMPORT)};
|
|
212
|
+
|
|
213
|
+
injectScheduleRefresh(scheduleRefresh);
|
|
214
|
+
|
|
215
|
+
let queued = false;
|
|
216
|
+
export function enqueueRefresh() {
|
|
217
|
+
if (queued) return;
|
|
218
|
+
queued = true;
|
|
219
|
+
queueMicrotask(() => {
|
|
220
|
+
queued = false;
|
|
221
|
+
performRefresh();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { register, setSignature };
|
|
226
|
+
`;
|
|
227
|
+
}
|
|
228
|
+
function viteFileImport(url) {
|
|
229
|
+
return `/@fs/${fileURLToPath(url)}`;
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
export { figRefresh };
|
|
233
|
+
|
|
234
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgub/fig-vite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"description": "Fig Vite plugin for state-preserving Fast Refresh",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"homepage": "https://github.com/bgub/fig",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"author": "Ben Gubler <nebrelbug@gmail.com>",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/bgub/fig",
|
|
28
|
+
"directory": "packages/fig-vite"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/bgub/fig/issues"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@babel/core": "^8.0.1",
|
|
39
|
+
"@babel/preset-typescript": "^8.0.1",
|
|
40
|
+
"@bgub/fig-dom": "0.1.0-alpha.2",
|
|
41
|
+
"@bgub/fig-refresh": "0.1.0-alpha.2"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.19.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsdown --config ../../tsdown.config.ts && node ../../scripts/strip-declaration-map-comments.mjs dist",
|
|
51
|
+
"test": "vitest run --config ../../vite.config.ts src"
|
|
52
|
+
}
|
|
53
|
+
}
|