@gtkx/cli 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts.map +1 -1
- package/dist/codegen/freshness.js +2 -1
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/codegen/reference.d.ts +17 -0
- package/dist/codegen/reference.d.ts.map +1 -0
- package/dist/codegen/reference.js +37 -0
- package/dist/codegen/reference.js.map +1 -0
- package/dist/codegen/report.d.ts.map +1 -1
- package/dist/codegen/report.js +17 -3
- package/dist/codegen/report.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +2 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +63 -21
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +10 -16
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +68 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/deploy/notices/gtkx.js +1 -1
- package/dist/deploy/notices/gtkx.js.map +1 -1
- package/dist/deploy/notices/libraries.d.ts.map +1 -1
- package/dist/deploy/notices/libraries.js +2 -1
- package/dist/deploy/notices/libraries.js.map +1 -1
- package/dist/deploy/settings/libraries.d.ts.map +1 -1
- package/dist/deploy/settings/libraries.js +7 -9
- package/dist/deploy/settings/libraries.js.map +1 -1
- package/dist/deploy/tools.d.ts.map +1 -1
- package/dist/deploy/tools.js +1 -3
- package/dist/deploy/tools.js.map +1 -1
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +2 -1
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/internal/agent-rules.d.ts +7 -0
- package/dist/internal/agent-rules.d.ts.map +1 -0
- package/dist/internal/agent-rules.js +106 -0
- package/dist/internal/agent-rules.js.map +1 -0
- package/dist/internal/docs-elements.d.ts +8 -0
- package/dist/internal/docs-elements.d.ts.map +1 -0
- package/dist/internal/docs-elements.js +12 -0
- package/dist/internal/docs-elements.js.map +1 -0
- package/dist/internal/source-imports.d.ts.map +1 -1
- package/dist/internal/source-imports.js +2 -3
- package/dist/internal/source-imports.js.map +1 -1
- package/dist/mcp/client-config.d.ts +22 -0
- package/dist/mcp/client-config.d.ts.map +1 -0
- package/dist/mcp/client-config.js +94 -0
- package/dist/mcp/client-config.js.map +1 -0
- package/dist/vite-plugins/animated.d.ts +5 -0
- package/dist/vite-plugins/animated.d.ts.map +1 -0
- package/dist/vite-plugins/animated.js +325 -0
- package/dist/vite-plugins/animated.js.map +1 -0
- package/dist/vite-plugins/asset-imports.js +2 -2
- package/dist/vite-plugins/asset-imports.js.map +1 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +2 -0
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/resources.js +2 -2
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +2 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/settings.d.ts.map +1 -1
- package/dist/vite-plugins/settings.js +2 -1
- package/dist/vite-plugins/settings.js.map +1 -1
- package/package.json +10 -9
- package/src/cli.ts +5 -0
- package/src/codegen/freshness.ts +2 -1
- package/src/codegen/reference.ts +59 -0
- package/src/codegen/report.ts +18 -3
- package/src/codegen/run-codegen.ts +81 -24
- package/src/commands/docs.ts +10 -28
- package/src/commands/mcp.ts +81 -0
- package/src/deploy/notices/gtkx.ts +1 -1
- package/src/deploy/notices/libraries.ts +2 -1
- package/src/deploy/settings/libraries.ts +7 -10
- package/src/deploy/tools.ts +1 -3
- package/src/dev/runner-main.ts +2 -1
- package/src/internal/agent-rules.ts +135 -0
- package/src/internal/docs-elements.ts +21 -0
- package/src/internal/source-imports.ts +3 -3
- package/src/mcp/client-config.ts +130 -0
- package/src/vite-plugins/animated.ts +486 -0
- package/src/vite-plugins/asset-imports.ts +2 -2
- package/src/vite-plugins/index.ts +2 -0
- package/src/vite-plugins/resources.ts +2 -2
- package/src/vite-plugins/settings-worker-env.ts +2 -2
- package/src/vite-plugins/settings.ts +2 -1
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import type { ConfigLoader } from "@gtkx/config";
|
|
2
|
+
import type { Plugin, UserConfig } from "vite";
|
|
3
|
+
import { type GeneratedElement, readGeneratedElements } from "@gtkx/codegen";
|
|
4
|
+
import { createConfigLoader } from "@gtkx/config/internal";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { parseSync } from "vite";
|
|
8
|
+
import { sourceLanguage } from "../internal/source-imports.js";
|
|
9
|
+
import { stripQuery } from "./strip-query.js";
|
|
10
|
+
|
|
11
|
+
type AstNode = {
|
|
12
|
+
type: string;
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
} & Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
type Edit = {
|
|
18
|
+
start: number;
|
|
19
|
+
end: number;
|
|
20
|
+
replacement: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Rewrite = {
|
|
24
|
+
edits: Edit[];
|
|
25
|
+
hasMemberEdits: boolean;
|
|
26
|
+
hasCallEdits: boolean;
|
|
27
|
+
hasDynamicUses: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type RewrittenModule = { code: string; hasDynamicUses: boolean; hasMemberEdits: boolean };
|
|
31
|
+
|
|
32
|
+
type PluginState = {
|
|
33
|
+
isEnabled: boolean;
|
|
34
|
+
isBuild: boolean;
|
|
35
|
+
root: string;
|
|
36
|
+
elements: Map<string, GeneratedElement> | null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const ANIMATED_PACKAGE = "@gtkx/animated";
|
|
40
|
+
const INTERNAL_SPECIFIER = "@gtkx/animated/internal";
|
|
41
|
+
const VIRTUAL_ID = "virtual:gtkx-animated";
|
|
42
|
+
const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
|
|
43
|
+
const NAMESPACE_HELPER = "__gtkxAnimated";
|
|
44
|
+
const CALL_HELPER = "__gtkxWithAnimated";
|
|
45
|
+
|
|
46
|
+
const BINDING_PARENT_KEYS: Record<string, string> = {
|
|
47
|
+
VariableDeclarator: "id",
|
|
48
|
+
ImportSpecifier: "local",
|
|
49
|
+
ImportDefaultSpecifier: "local",
|
|
50
|
+
ImportNamespaceSpecifier: "local",
|
|
51
|
+
FunctionDeclaration: "id",
|
|
52
|
+
ClassDeclaration: "id",
|
|
53
|
+
CatchClause: "param",
|
|
54
|
+
AssignmentPattern: "left",
|
|
55
|
+
RestElement: "argument",
|
|
56
|
+
Property: "value",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const BINDING_PARENT_ARRAY_KEYS: Record<string, string> = {
|
|
60
|
+
ArrowFunctionExpression: "params",
|
|
61
|
+
FunctionDeclaration: "params",
|
|
62
|
+
FunctionExpression: "params",
|
|
63
|
+
ArrayPattern: "elements",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const isAstNode = (value: unknown): value is AstNode =>
|
|
67
|
+
typeof value === "object" &&
|
|
68
|
+
value !== null &&
|
|
69
|
+
typeof (value as AstNode).type === "string" &&
|
|
70
|
+
typeof (value as AstNode).start === "number" &&
|
|
71
|
+
typeof (value as AstNode).end === "number";
|
|
72
|
+
|
|
73
|
+
const walkChildren = (node: AstNode, visit: (node: AstNode, parent: AstNode | null) => void): void => {
|
|
74
|
+
for (const [key, child] of Object.entries(node)) {
|
|
75
|
+
if (key !== "type") {
|
|
76
|
+
walk(child, node, visit);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const walk = (value: unknown, parent: AstNode | null, visit: (node: AstNode, parent: AstNode | null) => void): void => {
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
for (const item of value) {
|
|
84
|
+
walk(item, parent, visit);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!isAstNode(value)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
visit(value, parent);
|
|
95
|
+
walkChildren(value, visit);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const identifierName = (node: unknown): string | undefined => {
|
|
99
|
+
if (!isAstNode(node)) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (node.type !== "Identifier" && node.type !== "JSXIdentifier") {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return typeof node.name === "string" ? node.name : undefined;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const isValueImportSpecifier = (specifier: unknown): specifier is AstNode =>
|
|
111
|
+
isAstNode(specifier) && specifier.type === "ImportSpecifier" && specifier.importKind !== "type";
|
|
112
|
+
|
|
113
|
+
const animatedSpecifierLocal = (node: AstNode): string | undefined => {
|
|
114
|
+
const specifiers = (node.specifiers as unknown[] | undefined) ?? [];
|
|
115
|
+
let local: string | undefined;
|
|
116
|
+
|
|
117
|
+
for (const specifier of specifiers) {
|
|
118
|
+
if (isValueImportSpecifier(specifier) && identifierName(specifier.imported) === "animated") {
|
|
119
|
+
local ??= identifierName(specifier.local);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return local;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const animatedLocalName = (program: AstNode): string | undefined => {
|
|
127
|
+
let local: string | undefined;
|
|
128
|
+
|
|
129
|
+
walk(program, null, (node) => {
|
|
130
|
+
if (node.type !== "ImportDeclaration" || (node.source as AstNode | undefined)?.value !== ANIMATED_PACKAGE) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (node.importKind === "type") {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
local ??= animatedSpecifierLocal(node);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return local;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const isBindingSlot = (node: AstNode, parent: AstNode): boolean => {
|
|
145
|
+
const key = BINDING_PARENT_KEYS[parent.type];
|
|
146
|
+
|
|
147
|
+
return key !== undefined && parent[key] === node;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const isBindingListEntry = (node: AstNode, parent: AstNode): boolean => {
|
|
151
|
+
const arrayKey = BINDING_PARENT_ARRAY_KEYS[parent.type];
|
|
152
|
+
const entries = arrayKey === undefined ? undefined : parent[arrayKey];
|
|
153
|
+
|
|
154
|
+
return Array.isArray(entries) && entries.includes(node);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const isBindingParent = (node: AstNode, parent: AstNode): boolean =>
|
|
158
|
+
isBindingSlot(node, parent) || isBindingListEntry(node, parent);
|
|
159
|
+
|
|
160
|
+
const isBindingNamed = (node: AstNode, parent: AstNode, name: string): boolean =>
|
|
161
|
+
node.type === "Identifier" &&
|
|
162
|
+
node.name === name &&
|
|
163
|
+
isBindingParent(node, parent);
|
|
164
|
+
|
|
165
|
+
const countDeclarations = (program: AstNode, name: string): number => {
|
|
166
|
+
const spans: Set<number> = new Set();
|
|
167
|
+
|
|
168
|
+
walk(program, null, (node, parent) => {
|
|
169
|
+
if (parent !== null && isBindingNamed(node, parent, name)) {
|
|
170
|
+
spans.add(node.start);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return spans.size;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const isRewritableJsxMember = (node: AstNode, local: string): boolean =>
|
|
178
|
+
node.type === "JSXMemberExpression" && identifierName(node.object) === local;
|
|
179
|
+
|
|
180
|
+
const isRewritableObjectMember = (node: AstNode, local: string): boolean =>
|
|
181
|
+
node.type === "MemberExpression" &&
|
|
182
|
+
node.computed !== true &&
|
|
183
|
+
node.optional !== true &&
|
|
184
|
+
identifierName(node.object) === local;
|
|
185
|
+
|
|
186
|
+
const isForbiddenTarget = (node: AstNode, parent: AstNode | null): boolean =>
|
|
187
|
+
parent !== null &&
|
|
188
|
+
((parent.type === "AssignmentExpression" && parent.left === node) ||
|
|
189
|
+
(parent.type === "UpdateExpression" && parent.argument === node) ||
|
|
190
|
+
(parent.type === "UnaryExpression" && parent.operator === "delete" && parent.argument === node));
|
|
191
|
+
|
|
192
|
+
const isRewritableMember = (node: AstNode, parent: AstNode | null, local: string): boolean => {
|
|
193
|
+
if (isRewritableJsxMember(node, local)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return isRewritableObjectMember(node, local) && !isForbiddenTarget(node, parent);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const collectMemberEdit = (
|
|
201
|
+
rewrite: Rewrite,
|
|
202
|
+
claimed: Set<number>,
|
|
203
|
+
node: AstNode,
|
|
204
|
+
elements: Map<string, GeneratedElement>,
|
|
205
|
+
): void => {
|
|
206
|
+
const property = identifierName(node.property);
|
|
207
|
+
|
|
208
|
+
if (property !== undefined && elements.get(property)?.isMountable === true) {
|
|
209
|
+
const object = node.object as AstNode;
|
|
210
|
+
rewrite.edits.push({ start: object.start, end: object.end, replacement: NAMESPACE_HELPER });
|
|
211
|
+
rewrite.hasMemberEdits = true;
|
|
212
|
+
claimed.add(object.start);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const collectCallEdit = (rewrite: Rewrite, claimed: Set<number>, node: AstNode): void => {
|
|
217
|
+
const callee = node.callee as AstNode;
|
|
218
|
+
rewrite.edits.push({ start: callee.start, end: callee.end, replacement: CALL_HELPER });
|
|
219
|
+
rewrite.hasCallEdits = true;
|
|
220
|
+
claimed.add(callee.start);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const isPropertyReference = (node: AstNode, parent: AstNode | null): boolean =>
|
|
224
|
+
parent?.type === "ImportSpecifier" ||
|
|
225
|
+
(parent?.type === "MemberExpression" && parent.property === node) ||
|
|
226
|
+
(parent?.type === "JSXMemberExpression" && parent.property === node);
|
|
227
|
+
|
|
228
|
+
const hasDynamicUse = (program: AstNode, local: string, claimed: Set<number>): boolean => {
|
|
229
|
+
let isFound = false;
|
|
230
|
+
|
|
231
|
+
walk(program, null, (node, parent) => {
|
|
232
|
+
if (identifierName(node) === local && !claimed.has(node.start) && !isPropertyReference(node, parent)) {
|
|
233
|
+
isFound = true;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return isFound;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const collectRewrite = (program: AstNode, local: string, elements: Map<string, GeneratedElement>): Rewrite => {
|
|
241
|
+
const rewrite: Rewrite = { edits: [], hasMemberEdits: false, hasCallEdits: false, hasDynamicUses: false };
|
|
242
|
+
const claimed: Set<number> = new Set();
|
|
243
|
+
|
|
244
|
+
walk(program, null, (node, parent) => {
|
|
245
|
+
if (isRewritableMember(node, parent, local)) {
|
|
246
|
+
collectMemberEdit(rewrite, claimed, node, elements);
|
|
247
|
+
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (node.type === "CallExpression" && identifierName(node.callee) === local) {
|
|
252
|
+
collectCallEdit(rewrite, claimed, node);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
rewrite.hasDynamicUses = hasDynamicUse(program, local, claimed);
|
|
257
|
+
|
|
258
|
+
return rewrite;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const lastImportEnd = (program: AstNode): number => {
|
|
262
|
+
const statements = (program.body as unknown[] | undefined) ?? [];
|
|
263
|
+
let end = 0;
|
|
264
|
+
|
|
265
|
+
for (const statement of statements) {
|
|
266
|
+
if (isAstNode(statement) && statement.type === "ImportDeclaration") {
|
|
267
|
+
end = Math.max(end, statement.end);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return end;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const helperImports = (rewrite: Rewrite): string => {
|
|
275
|
+
const lines: string[] = [];
|
|
276
|
+
|
|
277
|
+
if (rewrite.hasMemberEdits) {
|
|
278
|
+
lines.push(`import * as ${NAMESPACE_HELPER} from ${JSON.stringify(VIRTUAL_ID)};`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (rewrite.hasCallEdits) {
|
|
282
|
+
lines.push(`import { withAnimated as ${CALL_HELPER} } from ${JSON.stringify(INTERNAL_SPECIFIER)};`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return lines.join("\n");
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const applyEdits = (code: string, edits: Edit[]): string => {
|
|
289
|
+
const ordered = edits.toSorted((a, b) => b.start - a.start);
|
|
290
|
+
let result = code;
|
|
291
|
+
|
|
292
|
+
for (const edit of ordered) {
|
|
293
|
+
result = `${result.slice(0, edit.start)}${edit.replacement}${result.slice(edit.end)}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return result;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const parseProgram = (path: string, code: string): AstNode | undefined => {
|
|
300
|
+
const lang = sourceLanguage(path);
|
|
301
|
+
|
|
302
|
+
if (lang === undefined) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const parsed = parseSync(path, code, { lang });
|
|
307
|
+
|
|
308
|
+
return parsed.errors.length > 0 ? undefined : (parsed.program as unknown as AstNode);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const unchangedRewrite = (code: string, rewrite: Rewrite): RewrittenModule | undefined =>
|
|
312
|
+
rewrite.hasDynamicUses ? { code, hasDynamicUses: true, hasMemberEdits: false } : undefined;
|
|
313
|
+
|
|
314
|
+
const hasHelperCollision = (code: string): boolean =>
|
|
315
|
+
code.includes(NAMESPACE_HELPER) || code.includes(CALL_HELPER);
|
|
316
|
+
|
|
317
|
+
const rewriteModule = (
|
|
318
|
+
code: string,
|
|
319
|
+
id: string,
|
|
320
|
+
elements: Map<string, GeneratedElement>,
|
|
321
|
+
): RewrittenModule | undefined => {
|
|
322
|
+
if (hasHelperCollision(code)) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const program = parseProgram(stripQuery(id), code);
|
|
327
|
+
|
|
328
|
+
if (program === undefined) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const local = animatedLocalName(program);
|
|
333
|
+
|
|
334
|
+
if (local === undefined || countDeclarations(program, local) > 1) {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const rewrite = collectRewrite(program, local, elements);
|
|
339
|
+
|
|
340
|
+
if (rewrite.edits.length === 0) {
|
|
341
|
+
return unchangedRewrite(code, rewrite);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const insertAt = lastImportEnd(program);
|
|
345
|
+
const helperEdit: Edit = { start: insertAt, end: insertAt, replacement: `\n${helperImports(rewrite)}` };
|
|
346
|
+
const withHelpers = applyEdits(code, [...rewrite.edits, helperEdit]);
|
|
347
|
+
|
|
348
|
+
return { code: withHelpers, hasDynamicUses: rewrite.hasDynamicUses, hasMemberEdits: rewrite.hasMemberEdits };
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const renderVirtualModule = (elements: Map<string, GeneratedElement>): string => {
|
|
352
|
+
const mountable = elements.values().filter((element) => element.isMountable).toArray();
|
|
353
|
+
const imports = mountable.map((element) => `${element.glibName} as $${element.glibName}`);
|
|
354
|
+
|
|
355
|
+
const consts = mountable.map(
|
|
356
|
+
(element) => `export const ${element.glibName} = /* @__PURE__ */ withAnimated($${element.glibName});`,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
`import { ${imports.join(", ")} } from "@gtkx/jsx";\n` +
|
|
361
|
+
`import { withAnimated } from ${JSON.stringify(INTERNAL_SPECIFIER)};\n` +
|
|
362
|
+
`${consts.join("\n")}\n`
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const loadElements = (state: PluginState): Map<string, GeneratedElement> | null => {
|
|
367
|
+
if (state.elements !== null) {
|
|
368
|
+
return state.elements;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
const requireFromRoot = createRequire(join(state.root, "package.json"));
|
|
373
|
+
const jsxStoreDir = dirname(requireFromRoot.resolve("@gtkx/jsx/package.json"));
|
|
374
|
+
state.elements = new Map(readGeneratedElements(jsxStoreDir).map((element) => [element.glibName, element]));
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return state.elements;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const resolveVirtualId = (state: PluginState, source: string): string | undefined => {
|
|
383
|
+
if (source === VIRTUAL_ID && state.isEnabled) {
|
|
384
|
+
return RESOLVED_VIRTUAL_ID;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return undefined;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const loadVirtualModule = (state: PluginState, id: string): string | undefined => {
|
|
391
|
+
if (id !== RESOLVED_VIRTUAL_ID || !state.isEnabled) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const elements = loadElements(state);
|
|
396
|
+
|
|
397
|
+
return elements === null ? undefined : renderVirtualModule(elements);
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const dynamicUseWarning = (id: string, hasDynamicUses: boolean): string[] =>
|
|
401
|
+
hasDynamicUses
|
|
402
|
+
? [
|
|
403
|
+
`${stripQuery(id)} uses the animated binding dynamically, which keeps the whole generated ` +
|
|
404
|
+
"widget namespace in the bundle; use animated(Component) calls to let unused widgets be dropped",
|
|
405
|
+
]
|
|
406
|
+
: [];
|
|
407
|
+
|
|
408
|
+
const memberUseWarning = (id: string, hasMemberEdits: boolean): string[] =>
|
|
409
|
+
hasMemberEdits
|
|
410
|
+
? [
|
|
411
|
+
`${stripQuery(id)} reads widget components off the animated binding, which GTKX 2.0 removes; ` +
|
|
412
|
+
"import the component and call animated(Component) instead",
|
|
413
|
+
]
|
|
414
|
+
: [];
|
|
415
|
+
|
|
416
|
+
const warningsFor = (state: PluginState, id: string, rewritten: RewrittenModule): string[] =>
|
|
417
|
+
state.isBuild
|
|
418
|
+
? [
|
|
419
|
+
...dynamicUseWarning(id, rewritten.hasDynamicUses),
|
|
420
|
+
...memberUseWarning(id, rewritten.hasMemberEdits),
|
|
421
|
+
]
|
|
422
|
+
: [];
|
|
423
|
+
|
|
424
|
+
const transformCode = (
|
|
425
|
+
state: PluginState,
|
|
426
|
+
code: string,
|
|
427
|
+
id: string,
|
|
428
|
+
): { code: string; warnings: string[] } | undefined => {
|
|
429
|
+
if (!state.isEnabled || id.startsWith("\0") || !code.includes(ANIMATED_PACKAGE)) {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const elements = loadElements(state);
|
|
434
|
+
|
|
435
|
+
if (elements === null) {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const rewritten = rewriteModule(code, id, elements);
|
|
440
|
+
|
|
441
|
+
if (rewritten === undefined) {
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return { code: rewritten.code, warnings: warningsFor(state, id, rewritten) };
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
function gtkxAnimated(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
|
|
449
|
+
const state: PluginState = { isEnabled: false, isBuild: false, root: process.cwd(), elements: null };
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
name: "gtkx:animated",
|
|
453
|
+
enforce: "pre",
|
|
454
|
+
|
|
455
|
+
async config(config: UserConfig) {
|
|
456
|
+
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
457
|
+
state.isEnabled = loaded.config.future?.v2TreeShaking === true;
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
configResolved(config) {
|
|
461
|
+
state.root = config.root;
|
|
462
|
+
state.isBuild = config.command === "build";
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
resolveId(source) {
|
|
466
|
+
return resolveVirtualId(state, source);
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
load(id) {
|
|
470
|
+
return loadVirtualModule(state, id);
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
transform(code, id) {
|
|
474
|
+
const result = transformCode(state, code, id);
|
|
475
|
+
const warnings = result?.warnings ?? [];
|
|
476
|
+
|
|
477
|
+
for (const warning of warnings) {
|
|
478
|
+
this.warn(warning);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result?.code;
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export { gtkxAnimated };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConfigLoader } from "@gtkx/config";
|
|
2
2
|
import type { ParseResult, Plugin, UserConfig } from "vite";
|
|
3
|
-
import { createConfigLoader } from "@gtkx/config/internal";
|
|
3
|
+
import { createConfigLoader, resolveFuture } from "@gtkx/config/internal";
|
|
4
4
|
import { parseSync } from "vite";
|
|
5
5
|
import { sourceLanguage } from "../internal/source-imports.js";
|
|
6
6
|
import { ASSET_MENTION_RE } from "./asset-extensions.js";
|
|
@@ -151,7 +151,7 @@ function gtkxAssetImports(loadConfig: ConfigLoader = createConfigLoader()): Plug
|
|
|
151
151
|
|
|
152
152
|
async config(config: UserConfig) {
|
|
153
153
|
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
154
|
-
state.isV2 = loaded.config.future
|
|
154
|
+
state.isV2 = resolveFuture(loaded.config.future).isResourceImported;
|
|
155
155
|
},
|
|
156
156
|
|
|
157
157
|
transform(code, id) {
|
|
@@ -2,6 +2,7 @@ import type { Plugin } from "vite";
|
|
|
2
2
|
import { createConfigLoader } from "@gtkx/config/internal";
|
|
3
3
|
import createConfigPlugin from "@gtkx/config/vite-plugin";
|
|
4
4
|
import type { BuildManifestCollector } from "../internal/build-manifest.js";
|
|
5
|
+
import { gtkxAnimated } from "./animated.js";
|
|
5
6
|
import { gtkxAssetImports } from "./asset-imports.js";
|
|
6
7
|
import { gtkxBuiltUrl } from "./built-url.js";
|
|
7
8
|
import { gtkxCss } from "./css.js";
|
|
@@ -32,6 +33,7 @@ const gtkxVitePlugins = (
|
|
|
32
33
|
gtkxBuiltUrl(),
|
|
33
34
|
gtkxResources(loadConfig, entryPath),
|
|
34
35
|
gtkxCss(),
|
|
36
|
+
gtkxAnimated(loadConfig),
|
|
35
37
|
gtkxReactCompiler(loadConfig),
|
|
36
38
|
];
|
|
37
39
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConfigLoader } from "@gtkx/config";
|
|
2
2
|
import type { ModuleNode, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from "vite";
|
|
3
|
-
import { createConfigLoader, resourceBasePath } from "@gtkx/config/internal";
|
|
3
|
+
import { createConfigLoader, resolveFuture, resourceBasePath } from "@gtkx/config/internal";
|
|
4
4
|
import { error, info, isRecord, sortStrings } from "@gtkx/utils";
|
|
5
5
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { basename, dirname, extname, isAbsolute, join, posix, relative, sep } from "node:path";
|
|
@@ -1353,7 +1353,7 @@ const resolveResourceConfig = async (state: PluginState, config: UserConfig, loa
|
|
|
1353
1353
|
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
1354
1354
|
state.prefix = resourceBasePath(loaded.config.applicationId);
|
|
1355
1355
|
state.root = loaded.root;
|
|
1356
|
-
state.isV2 = loaded.config.future
|
|
1356
|
+
state.isV2 = resolveFuture(loaded.config.future).isResourceImported;
|
|
1357
1357
|
|
|
1358
1358
|
return {
|
|
1359
1359
|
assetsInclude: [ASSET_RE],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { UserConfig } from "vite";
|
|
2
2
|
import type { Plugin } from "vitest/config";
|
|
3
|
-
import { createConfigLoader } from "@gtkx/config/internal";
|
|
3
|
+
import { createConfigLoader, resolveFuture } from "@gtkx/config/internal";
|
|
4
4
|
import { resolveDataDir } from "../internal/data-dir.js";
|
|
5
5
|
import { prependSchemaDir, stageAndCompileProjectSchemas } from "../settings/schema.js";
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ function gtkxSettingsWorkerEnv(): Plugin {
|
|
|
13
13
|
|
|
14
14
|
async config(config: UserConfig) {
|
|
15
15
|
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
16
|
-
const dataDir = loaded.config.future
|
|
16
|
+
const dataDir = resolveFuture(loaded.config.future).isResourceImported ? null : resolveDataDir(loaded.root);
|
|
17
17
|
const dir = stageAndCompileProjectSchemas(loaded.root, dataDir);
|
|
18
18
|
|
|
19
19
|
if (dir === null) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ConfigLoader } from "@gtkx/config";
|
|
2
2
|
import type { ModuleNode, Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
|
|
3
|
+
import { resolveFuture } from "@gtkx/config/internal";
|
|
3
4
|
import { error, errorMessage, info, sortStrings } from "@gtkx/utils";
|
|
4
5
|
import { readFileSync } from "node:fs";
|
|
5
6
|
import { basename, join } from "node:path";
|
|
@@ -106,7 +107,7 @@ const scheduleSchemaEnvSync = (state: PluginState): void => {
|
|
|
106
107
|
|
|
107
108
|
const applyUserConfig = async (state: PluginState, config: UserConfig, loadConfig: ConfigLoader): Promise<void> => {
|
|
108
109
|
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
109
|
-
state.isV2ResourceImports = loaded.config.future
|
|
110
|
+
state.isV2ResourceImports = resolveFuture(loaded.config.future).isResourceImported;
|
|
110
111
|
state.dataDir = state.isV2ResourceImports ? null : resolveDataDir(loaded.root);
|
|
111
112
|
};
|
|
112
113
|
|