@gtkx/cli 0.21.0 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -0
- package/bin/gtkx-dev-runner.js +9 -0
- package/dist/builder.d.ts +2 -50
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +22 -56
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -124
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +11 -0
- package/dist/codegen/freshness.d.ts.map +1 -0
- package/dist/codegen/freshness.js +42 -0
- package/dist/codegen/freshness.js.map +1 -0
- package/dist/codegen/report.d.ts +3 -0
- package/dist/codegen/report.d.ts.map +1 -0
- package/dist/codegen/report.js +16 -0
- package/dist/codegen/report.js.map +1 -0
- package/dist/codegen/run-codegen.d.ts +35 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -0
- package/dist/codegen/run-codegen.js +133 -0
- package/dist/codegen/run-codegen.js.map +1 -0
- package/dist/codegen/store-resolver.d.ts +21 -0
- package/dist/codegen/store-resolver.d.ts.map +1 -0
- package/dist/codegen/store-resolver.js +52 -0
- package/dist/codegen/store-resolver.js.map +1 -0
- package/dist/commands/build.d.ts +9 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +33 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/codegen.d.ts +9 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +40 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +21 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +19 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +59 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/dev/icon-dir.d.ts +2 -0
- package/dist/dev/icon-dir.d.ts.map +1 -0
- package/dist/dev/icon-dir.js +13 -0
- package/dist/dev/icon-dir.js.map +1 -0
- package/dist/dev/refresh-tracker.d.ts +6 -0
- package/dist/dev/refresh-tracker.d.ts.map +1 -0
- package/dist/dev/refresh-tracker.js +18 -0
- package/dist/dev/refresh-tracker.js.map +1 -0
- package/dist/dev/runner-deps.d.ts +3 -0
- package/dist/dev/runner-deps.d.ts.map +1 -0
- package/dist/dev/runner-deps.js +39 -0
- package/dist/dev/runner-deps.js.map +1 -0
- package/dist/dev/runner-main.d.ts +2 -0
- package/dist/dev/runner-main.d.ts.map +1 -0
- package/dist/dev/runner-main.js +28 -0
- package/dist/dev/runner-main.js.map +1 -0
- package/dist/dev/runner.d.ts +23 -0
- package/dist/dev/runner.d.ts.map +1 -0
- package/dist/dev/runner.js +88 -0
- package/dist/dev/runner.js.map +1 -0
- package/dist/dev/schema-dir.d.ts +2 -0
- package/dist/dev/schema-dir.d.ts.map +1 -0
- package/dist/dev/schema-dir.js +10 -0
- package/dist/dev/schema-dir.js.map +1 -0
- package/dist/dev/supervisor.d.ts +17 -0
- package/dist/dev/supervisor.d.ts.map +1 -0
- package/dist/dev/supervisor.js +142 -0
- package/dist/dev/supervisor.js.map +1 -0
- package/dist/dev/vite-dev-server.d.ts +21 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -0
- package/dist/dev/vite-dev-server.js +12 -0
- package/dist/dev/vite-dev-server.js.map +1 -0
- package/dist/internal/banner.d.ts +3 -0
- package/dist/internal/banner.d.ts.map +1 -0
- package/dist/internal/banner.js +8 -0
- package/dist/internal/banner.js.map +1 -0
- package/dist/internal/data-dir.d.ts +3 -0
- package/dist/internal/data-dir.d.ts.map +1 -0
- package/dist/internal/data-dir.js +37 -0
- package/dist/internal/data-dir.js.map +1 -0
- package/dist/internal/entry-arg.d.ts +19 -0
- package/dist/internal/entry-arg.d.ts.map +1 -0
- package/dist/internal/entry-arg.js +33 -0
- package/dist/internal/entry-arg.js.map +1 -0
- package/dist/internal/errors.d.ts +4 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +27 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/list-files.d.ts +6 -0
- package/dist/internal/list-files.d.ts.map +1 -0
- package/dist/internal/list-files.js +13 -0
- package/dist/internal/list-files.js.map +1 -0
- package/dist/internal/resolve-cli-tool.d.ts +2 -0
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
- package/dist/internal/resolve-cli-tool.js +20 -0
- package/dist/internal/resolve-cli-tool.js.map +1 -0
- package/dist/internal/staging-dir.d.ts +3 -0
- package/dist/internal/staging-dir.d.ts.map +1 -0
- package/dist/internal/staging-dir.js +16 -0
- package/dist/internal/staging-dir.js.map +1 -0
- package/dist/internal/xdg-data-dirs.d.ts +2 -0
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
- package/dist/internal/xdg-data-dirs.js +8 -0
- package/dist/internal/xdg-data-dirs.js.map +1 -0
- package/dist/mcp/client.d.ts +23 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +157 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/handlers.d.ts +8 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/handlers.js +119 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/serialize-widget.d.ts +10 -0
- package/dist/mcp/serialize-widget.d.ts.map +1 -0
- package/dist/mcp/serialize-widget.js +20 -0
- package/dist/mcp/serialize-widget.js.map +1 -0
- package/dist/mcp/testing-loader.d.ts +6 -0
- package/dist/mcp/testing-loader.d.ts.map +1 -0
- package/dist/mcp/testing-loader.js +25 -0
- package/dist/mcp/testing-loader.js.map +1 -0
- package/dist/mcp/widget-registry.d.ts +13 -0
- package/dist/mcp/widget-registry.d.ts.map +1 -0
- package/dist/mcp/widget-registry.js +38 -0
- package/dist/mcp/widget-registry.js.map +1 -0
- package/dist/refresh-runtime.d.ts +10 -20
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +12 -36
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -0
- package/dist/settings/compile.d.ts.map +1 -0
- package/dist/settings/compile.js +19 -0
- package/dist/settings/compile.js.map +1 -0
- package/dist/settings/parser.d.ts +23 -0
- package/dist/settings/parser.d.ts.map +1 -0
- package/dist/settings/parser.js +117 -0
- package/dist/settings/parser.js.map +1 -0
- package/dist/settings/render.d.ts +4 -0
- package/dist/settings/render.d.ts.map +1 -0
- package/dist/settings/render.js +148 -0
- package/dist/settings/render.js.map +1 -0
- package/dist/settings/schema.d.ts +13 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +103 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/vite-plugins/asset-extensions.d.ts +4 -0
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
- package/dist/vite-plugins/asset-extensions.js +27 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -0
- package/dist/vite-plugins/built-url.d.ts +3 -0
- package/dist/vite-plugins/built-url.d.ts.map +1 -0
- package/dist/{vite-plugin-gtkx-built-url.js → vite-plugins/built-url.js} +4 -17
- package/dist/vite-plugins/built-url.js.map +1 -0
- package/dist/vite-plugins/css.d.ts +3 -0
- package/dist/vite-plugins/css.d.ts.map +1 -0
- package/dist/vite-plugins/css.js +30 -0
- package/dist/vite-plugins/css.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +7 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +15 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +5 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +66 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
- package/dist/vite-plugins/icons.d.ts +3 -0
- package/dist/vite-plugins/icons.d.ts.map +1 -0
- package/dist/vite-plugins/icons.js +47 -0
- package/dist/vite-plugins/icons.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +3 -0
- package/dist/vite-plugins/index.d.ts.map +1 -0
- package/dist/vite-plugins/index.js +21 -0
- package/dist/vite-plugins/index.js.map +1 -0
- package/dist/vite-plugins/native.d.ts +3 -0
- package/dist/vite-plugins/native.d.ts.map +1 -0
- package/dist/vite-plugins/native.js +60 -0
- package/dist/vite-plugins/native.js.map +1 -0
- package/dist/vite-plugins/react-compiler.d.ts +4 -0
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
- package/dist/vite-plugins/react-compiler.js +56 -0
- package/dist/vite-plugins/react-compiler.js.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts +3 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.js +11 -0
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
- package/dist/vite-plugins/resource-init-module.d.ts +5 -0
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
- package/dist/vite-plugins/resource-init-module.js +50 -0
- package/dist/vite-plugins/resource-init-module.js.map +1 -0
- package/dist/vite-plugins/resource-shared.d.ts +6 -0
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
- package/dist/vite-plugins/resource-shared.js +21 -0
- package/dist/vite-plugins/resource-shared.js.map +1 -0
- package/dist/vite-plugins/resources.d.ts +4 -0
- package/dist/vite-plugins/resources.d.ts.map +1 -0
- package/dist/vite-plugins/resources.js +220 -0
- package/dist/vite-plugins/resources.js.map +1 -0
- package/dist/vite-plugins/settings-worker-env.d.ts +3 -0
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/settings-worker-env.js +21 -0
- package/dist/vite-plugins/settings-worker-env.js.map +1 -0
- package/dist/vite-plugins/settings.d.ts +3 -0
- package/dist/vite-plugins/settings.d.ts.map +1 -0
- package/dist/vite-plugins/settings.js +190 -0
- package/dist/vite-plugins/settings.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +3 -0
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
- package/dist/vite-plugins/undeclared-library.js +47 -0
- package/dist/vite-plugins/undeclared-library.js.map +1 -0
- package/dist/vite-plugins/virtual-module.d.ts +22 -0
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
- package/dist/vite-plugins/virtual-module.js +18 -0
- package/dist/vite-plugins/virtual-module.js.map +1 -0
- package/dist/vitest-plugin.d.ts +10 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +12 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/env.d.ts +131 -8
- package/package.json +32 -38
- package/src/babel-modules.d.ts +13 -0
- package/src/builder.ts +28 -84
- package/src/cli.ts +22 -0
- package/src/codegen/freshness.ts +52 -0
- package/src/codegen/report.ts +20 -0
- package/src/codegen/run-codegen.ts +185 -0
- package/src/codegen/store-resolver.ts +75 -0
- package/src/commands/build.ts +37 -0
- package/src/commands/codegen.ts +44 -0
- package/src/commands/dev.ts +25 -0
- package/src/commands/docs.ts +66 -0
- package/src/dev/icon-dir.ts +11 -0
- package/src/dev/refresh-tracker.ts +21 -0
- package/src/dev/runner-deps.ts +42 -0
- package/src/dev/runner-main.ts +33 -0
- package/src/dev/runner.ts +126 -0
- package/src/dev/schema-dir.ts +11 -0
- package/src/dev/supervisor.ts +178 -0
- package/src/dev/vite-dev-server.ts +34 -0
- package/src/internal/banner.ts +9 -0
- package/src/internal/data-dir.ts +39 -0
- package/src/internal/entry-arg.ts +38 -0
- package/src/internal/errors.ts +28 -0
- package/src/internal/list-files.ts +17 -0
- package/src/internal/resolve-cli-tool.ts +21 -0
- package/src/internal/staging-dir.ts +16 -0
- package/src/internal/xdg-data-dirs.ts +7 -0
- package/src/mcp/client.ts +177 -0
- package/src/mcp/handlers.ts +156 -0
- package/src/mcp/index.ts +22 -0
- package/src/mcp/serialize-widget.ts +34 -0
- package/src/mcp/testing-loader.ts +32 -0
- package/src/mcp/widget-registry.ts +43 -0
- package/src/refresh-runtime.ts +12 -41
- package/src/settings/compile.ts +19 -0
- package/src/settings/parser.ts +154 -0
- package/src/settings/render.ts +167 -0
- package/src/settings/schema.ts +115 -0
- package/src/vite-plugins/asset-extensions.ts +28 -0
- package/src/{vite-plugin-gtkx-built-url.ts → vite-plugins/built-url.ts} +3 -16
- package/src/vite-plugins/css.ts +38 -0
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -0
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +79 -0
- package/src/vite-plugins/icons.ts +58 -0
- package/src/vite-plugins/index.ts +22 -0
- package/src/vite-plugins/native.ts +74 -0
- package/src/vite-plugins/react-compiler.ts +70 -0
- package/src/vite-plugins/react-dom-prebundle.ts +14 -0
- package/src/vite-plugins/resource-init-module.ts +54 -0
- package/src/vite-plugins/resource-shared.ts +27 -0
- package/src/vite-plugins/resources.ts +272 -0
- package/src/vite-plugins/settings-worker-env.ts +25 -0
- package/src/vite-plugins/settings.ts +221 -0
- package/src/vite-plugins/undeclared-library.ts +69 -0
- package/src/vite-plugins/virtual-module.ts +37 -0
- package/src/vitest-plugin.ts +14 -0
- package/dist/create.d.ts +0 -55
- package/dist/create.d.ts.map +0 -1
- package/dist/create.js +0 -236
- package/dist/create.js.map +0 -1
- package/dist/dev-server.d.ts +0 -37
- package/dist/dev-server.d.ts.map +0 -1
- package/dist/dev-server.js +0 -130
- package/dist/dev-server.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -31
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -430
- package/dist/mcp-client.js.map +0 -1
- package/dist/templates.d.ts +0 -9
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -16
- package/dist/templates.js.map +0 -1
- package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
- package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-assets.js +0 -57
- package/dist/vite-plugin-gtkx-assets.js.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
- package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
- package/dist/vite-plugin-gtkx-gsettings.d.ts +0 -28
- package/dist/vite-plugin-gtkx-gsettings.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-gsettings.js +0 -147
- package/dist/vite-plugin-gtkx-gsettings.js.map +0 -1
- package/dist/vite-plugin-gtkx-native.d.ts +0 -14
- package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-native.js +0 -53
- package/dist/vite-plugin-gtkx-native.js.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
- package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.js +0 -37
- package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
- package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
- package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
- package/src/cli.tsx +0 -154
- package/src/create.ts +0 -326
- package/src/dev-server.tsx +0 -162
- package/src/index.ts +0 -3
- package/src/mcp-client.ts +0 -518
- package/src/templates.ts +0 -26
- package/src/vite-plugin-gtkx-assets.ts +0 -69
- package/src/vite-plugin-gtkx-gsettings.ts +0 -168
- package/src/vite-plugin-gtkx-native.ts +0 -64
- package/src/vite-plugin-gtkx-refresh.ts +0 -54
- package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
- package/templates/claude/EXAMPLES.md.ejs +0 -744
- package/templates/claude/SKILL.md.ejs +0 -93
- package/templates/claude/WIDGETS.md.ejs +0 -820
- package/templates/config/vitest.config.ts.ejs +0 -11
- package/templates/gitignore.ejs +0 -4
- package/templates/package.json.ejs +0 -16
- package/templates/src/app.tsx.ejs +0 -32
- package/templates/src/dev.tsx.ejs +0 -5
- package/templates/src/gtkx-env.d.ts.ejs +0 -1
- package/templates/src/index.tsx.ejs +0 -5
- package/templates/tests/app.test.tsx.ejs +0 -16
- package/templates/tsconfig.json.ejs +0 -14
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { XMLParser } from "fast-xml-parser";
|
|
2
|
+
|
|
3
|
+
export type ParsedKey = {
|
|
4
|
+
name: string;
|
|
5
|
+
variantType: string | null;
|
|
6
|
+
enumId: string | null;
|
|
7
|
+
flagsId: string | null;
|
|
8
|
+
choices: string[];
|
|
9
|
+
summary: string | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ParsedSchema = {
|
|
13
|
+
id: string;
|
|
14
|
+
path: string | null;
|
|
15
|
+
keys: ParsedKey[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ParsedSchemaFile = {
|
|
19
|
+
fileName: string;
|
|
20
|
+
schemas: ParsedSchema[];
|
|
21
|
+
enums: Map<string, string[]>;
|
|
22
|
+
flags: Map<string, string[]>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class SchemaParseError extends Error {}
|
|
26
|
+
|
|
27
|
+
type RawNode = Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
const MULTI_TAGS: Set<string> = new Set(["schema", "key", "enum", "flags", "value", "choice"]);
|
|
30
|
+
|
|
31
|
+
const PARSER = new XMLParser({
|
|
32
|
+
ignoreAttributes: false,
|
|
33
|
+
attributeNamePrefix: "@_",
|
|
34
|
+
parseAttributeValue: false,
|
|
35
|
+
parseTagValue: false,
|
|
36
|
+
trimValues: true,
|
|
37
|
+
isArray: (name) => MULTI_TAGS.has(name),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const isRawNode = (value: unknown): value is RawNode => typeof value === "object" && value !== null;
|
|
41
|
+
|
|
42
|
+
const attr = (node: RawNode, name: string): string | null => {
|
|
43
|
+
const value = node[`@_${name}`];
|
|
44
|
+
return typeof value === "string" ? value : null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const text = (node: RawNode, name: string): string | null => {
|
|
48
|
+
const value = node[name];
|
|
49
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const children = (node: RawNode, name: string): RawNode[] => {
|
|
53
|
+
const value = node[name];
|
|
54
|
+
if (!Array.isArray(value)) return [];
|
|
55
|
+
return value.filter(isRawNode);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const parseNicks = (definition: RawNode): string[] =>
|
|
59
|
+
children(definition, "value")
|
|
60
|
+
.map((value) => attr(value, "nick"))
|
|
61
|
+
.filter((nick): nick is string => nick !== null);
|
|
62
|
+
|
|
63
|
+
const parseDefinitions = (schemalist: RawNode, tag: string): Map<string, string[]> => {
|
|
64
|
+
const definitions = new Map<string, string[]>();
|
|
65
|
+
for (const definition of children(schemalist, tag)) {
|
|
66
|
+
const id = attr(definition, "id");
|
|
67
|
+
if (id !== null) definitions.set(id, parseNicks(definition));
|
|
68
|
+
}
|
|
69
|
+
return definitions;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const parseChoices = (key: RawNode): string[] => {
|
|
73
|
+
const choices = key.choices;
|
|
74
|
+
if (!isRawNode(choices)) return [];
|
|
75
|
+
return children(choices, "choice")
|
|
76
|
+
.map((choice) => attr(choice, "value"))
|
|
77
|
+
.filter((value): value is string => value !== null);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const parseKey = (key: RawNode, fileName: string): ParsedKey => {
|
|
81
|
+
const name = attr(key, "name");
|
|
82
|
+
if (name === null) {
|
|
83
|
+
throw new SchemaParseError(`A <key> in ${fileName} has no name attribute`);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name,
|
|
87
|
+
variantType: attr(key, "type"),
|
|
88
|
+
enumId: attr(key, "enum"),
|
|
89
|
+
flagsId: attr(key, "flags"),
|
|
90
|
+
choices: parseChoices(key),
|
|
91
|
+
summary: text(key, "summary"),
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
type RawSchema = {
|
|
96
|
+
id: string;
|
|
97
|
+
path: string | null;
|
|
98
|
+
extendsId: string | null;
|
|
99
|
+
keys: ParsedKey[];
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const parseRawSchema = (schema: RawNode, fileName: string): RawSchema => {
|
|
103
|
+
const id = attr(schema, "id");
|
|
104
|
+
if (id === null) {
|
|
105
|
+
throw new SchemaParseError(`A <schema> in ${fileName} has no id attribute`);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
id,
|
|
109
|
+
path: attr(schema, "path"),
|
|
110
|
+
extendsId: attr(schema, "extends"),
|
|
111
|
+
keys: children(schema, "key").map((key) => parseKey(key, fileName)),
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const mergeInheritedKeys = (schema: RawSchema, byId: Map<string, RawSchema>): ParsedKey[] => {
|
|
116
|
+
const merged = new Map<string, ParsedKey>();
|
|
117
|
+
const visited = new Set<string>();
|
|
118
|
+
const collect = (current: RawSchema): void => {
|
|
119
|
+
if (visited.has(current.id)) return;
|
|
120
|
+
visited.add(current.id);
|
|
121
|
+
if (current.extendsId !== null) {
|
|
122
|
+
const parent = byId.get(current.extendsId);
|
|
123
|
+
if (parent !== undefined) collect(parent);
|
|
124
|
+
}
|
|
125
|
+
for (const key of current.keys) merged.set(key.name, key);
|
|
126
|
+
};
|
|
127
|
+
collect(schema);
|
|
128
|
+
return [...merged.values()];
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const parseSchemaXml = (xml: string, fileName: string): ParsedSchemaFile => {
|
|
132
|
+
let document: unknown;
|
|
133
|
+
try {
|
|
134
|
+
document = PARSER.parse(xml);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new SchemaParseError(`Failed to parse ${fileName} as XML: ${String(error)}`, { cause: error });
|
|
137
|
+
}
|
|
138
|
+
if (!isRawNode(document) || !("schemalist" in document)) {
|
|
139
|
+
throw new SchemaParseError(`${fileName} has no <schemalist> root element`);
|
|
140
|
+
}
|
|
141
|
+
const schemalist = isRawNode(document.schemalist) ? document.schemalist : {};
|
|
142
|
+
const rawSchemas = children(schemalist, "schema").map((schema) => parseRawSchema(schema, fileName));
|
|
143
|
+
const byId = new Map(rawSchemas.map((schema) => [schema.id, schema]));
|
|
144
|
+
return {
|
|
145
|
+
fileName,
|
|
146
|
+
schemas: rawSchemas.map((schema) => ({
|
|
147
|
+
id: schema.id,
|
|
148
|
+
path: schema.path,
|
|
149
|
+
keys: mergeInheritedKeys(schema, byId),
|
|
150
|
+
})),
|
|
151
|
+
enums: parseDefinitions(schemalist, "enum"),
|
|
152
|
+
flags: parseDefinitions(schemalist, "flags"),
|
|
153
|
+
};
|
|
154
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { upperFirst } from "@gtkx/utils";
|
|
2
|
+
import type { ParsedKey, ParsedSchema, ParsedSchemaFile } from "./parser.js";
|
|
3
|
+
|
|
4
|
+
const GTKX_ENV_MODULE_HEADER = `/**
|
|
5
|
+
* GSettings schema modules generated by GTKX from the project's
|
|
6
|
+
* \`.gschema.xml\` files. Regenerated by \`gtkx codegen\`, \`gtkx dev\`, and
|
|
7
|
+
* \`gtkx build\`; do not edit.
|
|
8
|
+
*/`;
|
|
9
|
+
|
|
10
|
+
const VARIANT_TS_TYPES: Record<string, string> = {
|
|
11
|
+
b: "boolean",
|
|
12
|
+
i: "number",
|
|
13
|
+
u: "number",
|
|
14
|
+
x: "number",
|
|
15
|
+
t: "number",
|
|
16
|
+
d: "number",
|
|
17
|
+
s: "string",
|
|
18
|
+
as: "string[]",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const VARIANT_FALLBACK_TS_TYPE = `import("@gtkx/gi/glib").Variant`;
|
|
22
|
+
|
|
23
|
+
const ENUM_KIND = "enum";
|
|
24
|
+
const FLAGS_KIND = "flags";
|
|
25
|
+
|
|
26
|
+
const toJsStringLiteral = (value: string): string =>
|
|
27
|
+
JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
|
|
28
|
+
|
|
29
|
+
const exportNameFor = (schemaId: string): string => schemaId.replaceAll(".", "_");
|
|
30
|
+
|
|
31
|
+
const unionOf = (values: string[]): string => values.map(toJsStringLiteral).join(" | ");
|
|
32
|
+
|
|
33
|
+
const tsTypeForKey = (key: ParsedKey, file: ParsedSchemaFile): string => {
|
|
34
|
+
if (key.enumId !== null) {
|
|
35
|
+
const nicks = file.enums.get(key.enumId);
|
|
36
|
+
return nicks !== undefined && nicks.length > 0 ? unionOf(nicks) : "string";
|
|
37
|
+
}
|
|
38
|
+
if (key.flagsId !== null) {
|
|
39
|
+
const nicks = file.flags.get(key.flagsId);
|
|
40
|
+
return nicks !== undefined && nicks.length > 0 ? `(${unionOf(nicks)})[]` : "string[]";
|
|
41
|
+
}
|
|
42
|
+
if (key.variantType === "s" && key.choices.length > 0) {
|
|
43
|
+
return unionOf(key.choices);
|
|
44
|
+
}
|
|
45
|
+
return VARIANT_TS_TYPES[key.variantType ?? ""] ?? VARIANT_FALLBACK_TS_TYPE;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const runtimeKindForKey = (key: ParsedKey): string => {
|
|
49
|
+
if (key.enumId !== null) return ENUM_KIND;
|
|
50
|
+
if (key.flagsId !== null) return FLAGS_KIND;
|
|
51
|
+
return key.variantType ?? "";
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const runtimeKeysFor = (schema: ParsedSchema): string => {
|
|
55
|
+
const entries = schema.keys.map(
|
|
56
|
+
(key) => `${toJsStringLiteral(key.name)}: ${toJsStringLiteral(runtimeKindForKey(key))}`,
|
|
57
|
+
);
|
|
58
|
+
return `{ ${entries.join(", ")} }`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const renderRuntimeModule = (file: ParsedSchemaFile): string => {
|
|
62
|
+
const lines: string[] = [];
|
|
63
|
+
file.schemas.forEach((schema, index) => {
|
|
64
|
+
const keysName = `keys_${index}`;
|
|
65
|
+
const id = toJsStringLiteral(schema.id);
|
|
66
|
+
lines.push(`const ${keysName} = ${runtimeKeysFor(schema)};`);
|
|
67
|
+
if (schema.path === null) {
|
|
68
|
+
lines.push(
|
|
69
|
+
`export const ${exportNameFor(schema.id)} = { id: ${id}, keys: ${keysName}, at: (path) => ({ id: ${id}, path, keys: ${keysName} }) };`,
|
|
70
|
+
);
|
|
71
|
+
} else {
|
|
72
|
+
lines.push(`export const ${exportNameFor(schema.id)} = { id: ${id}, path: null, keys: ${keysName} };`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const first = file.schemas[0];
|
|
76
|
+
if (first !== undefined) {
|
|
77
|
+
lines.push(`export default ${exportNameFor(first.id)};`);
|
|
78
|
+
}
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const sanitizeSummary = (summary: string): string => summary.replaceAll("*/", "*\\/");
|
|
83
|
+
|
|
84
|
+
const interfaceNameFor = (schemaId: string, usedNames: Set<string>): string => {
|
|
85
|
+
const base = `${schemaId
|
|
86
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
87
|
+
.filter((part) => part.length > 0)
|
|
88
|
+
.map(upperFirst)
|
|
89
|
+
.join("")}Keys`;
|
|
90
|
+
let name = base;
|
|
91
|
+
let suffix = 2;
|
|
92
|
+
while (usedNames.has(name)) {
|
|
93
|
+
name = `${base}${suffix}`;
|
|
94
|
+
suffix += 1;
|
|
95
|
+
}
|
|
96
|
+
usedNames.add(name);
|
|
97
|
+
return name;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const renderKeysType = (name: string, schema: ParsedSchema, file: ParsedSchemaFile): string[] => {
|
|
101
|
+
const lines = [` type ${name} = {`];
|
|
102
|
+
for (const key of schema.keys) {
|
|
103
|
+
if (key.summary !== null) {
|
|
104
|
+
lines.push(` /** ${sanitizeSummary(key.summary)} */`);
|
|
105
|
+
}
|
|
106
|
+
lines.push(` ${toJsStringLiteral(key.name)}: ${tsTypeForKey(key, file)};`);
|
|
107
|
+
}
|
|
108
|
+
lines.push(" };");
|
|
109
|
+
return lines;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const boundRefTypeLines = (interfaceName: string, indent: string): string[] => [
|
|
113
|
+
`${indent}id: string;`,
|
|
114
|
+
`${indent}path: string | null;`,
|
|
115
|
+
`${indent}keys: { [P in keyof ${interfaceName}]: string };`,
|
|
116
|
+
`${indent}__keys__?: ${interfaceName};`,
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const renderSchemaConst = (schema: ParsedSchema, interfaceName: string): string[] => {
|
|
120
|
+
const exportName = exportNameFor(schema.id);
|
|
121
|
+
if (schema.path !== null) {
|
|
122
|
+
return [
|
|
123
|
+
` const ${exportName}: {`,
|
|
124
|
+
` id: ${toJsStringLiteral(schema.id)};`,
|
|
125
|
+
...boundRefTypeLines(interfaceName, " ").slice(1),
|
|
126
|
+
" };",
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
return [
|
|
130
|
+
` const ${exportName}: {`,
|
|
131
|
+
` id: ${JSON.stringify(schema.id)};`,
|
|
132
|
+
` keys: { [P in keyof ${interfaceName}]: string };`,
|
|
133
|
+
` __keys__?: ${interfaceName};`,
|
|
134
|
+
" at(path: string): {",
|
|
135
|
+
...boundRefTypeLines(interfaceName, " "),
|
|
136
|
+
" };",
|
|
137
|
+
" };",
|
|
138
|
+
];
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const renderFileModule = (file: ParsedSchemaFile, usedNames: Set<string>): string[] => {
|
|
142
|
+
const lines = [`declare module "${file.fileName}" {`];
|
|
143
|
+
const exportNames: string[] = [];
|
|
144
|
+
file.schemas.forEach((schema, index) => {
|
|
145
|
+
if (index > 0) lines.push("");
|
|
146
|
+
const interfaceName = interfaceNameFor(schema.id, usedNames);
|
|
147
|
+
lines.push(...renderKeysType(interfaceName, schema, file));
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push(...renderSchemaConst(schema, interfaceName));
|
|
150
|
+
exportNames.push(exportNameFor(schema.id));
|
|
151
|
+
});
|
|
152
|
+
if (exportNames.length > 0) {
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push(` export { ${exportNames.join(", ")} };`);
|
|
155
|
+
lines.push(` export default ${exportNames[0]};`);
|
|
156
|
+
}
|
|
157
|
+
lines.push("}");
|
|
158
|
+
return lines;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const renderEnvModule = (files: ParsedSchemaFile[]): string => {
|
|
162
|
+
const usedNames = new Set<string>();
|
|
163
|
+
const blocks = files
|
|
164
|
+
.filter((file) => file.schemas.length > 0)
|
|
165
|
+
.map((file) => renderFileModule(file, usedNames).join("\n"));
|
|
166
|
+
return `${[GTKX_ENV_MODULE_HEADER, ...blocks].join("\n\n")}\n`;
|
|
167
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { copyFileSync, type Dirent, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join, relative } from "node:path";
|
|
5
|
+
import { sortStrings, warn } from "@gtkx/utils";
|
|
6
|
+
import { DATA_IMPORT_PREFIX } from "../internal/data-dir.js";
|
|
7
|
+
import { removeTempDir } from "../internal/staging-dir.js";
|
|
8
|
+
import { compileSchemas } from "./compile.js";
|
|
9
|
+
import { type ParsedSchemaFile, parseSchemaXml, SchemaParseError } from "./parser.js";
|
|
10
|
+
import { renderEnvModule } from "./render.js";
|
|
11
|
+
|
|
12
|
+
export const SCHEMA_SUFFIX = ".gschema.xml";
|
|
13
|
+
|
|
14
|
+
export const prependSchemaDir = (dir: string, existing: string | undefined): string => {
|
|
15
|
+
if (existing === undefined || existing.length === 0) return dir;
|
|
16
|
+
if (existing.split(":").includes(dir)) return existing;
|
|
17
|
+
return `${dir}:${existing}`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const STAGED_NAME_LENGTH = 16;
|
|
21
|
+
|
|
22
|
+
const stagedSchemaName = (filePath: string): string =>
|
|
23
|
+
`${createHash("sha1").update(filePath).digest("hex").slice(0, STAGED_NAME_LENGTH)}${SCHEMA_SUFFIX}`;
|
|
24
|
+
|
|
25
|
+
export const stageSchema = (dir: string, filePath: string): void => {
|
|
26
|
+
copyFileSync(filePath, join(dir, stagedSchemaName(filePath)));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const toForwardSlashes = (value: string): string => value.replaceAll(/[/\\]/g, "/");
|
|
30
|
+
|
|
31
|
+
const moduleSpecifierFor = (dataDirAbs: string, filePath: string): string =>
|
|
32
|
+
`${DATA_IMPORT_PREFIX}/${toForwardSlashes(relative(dataDirAbs, filePath))}`;
|
|
33
|
+
|
|
34
|
+
type SchemaEnvResult = {
|
|
35
|
+
path: string;
|
|
36
|
+
written: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const readVisibleEntries = (dir: string): Dirent[] => {
|
|
40
|
+
try {
|
|
41
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => !entry.name.startsWith("."));
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const isSchemaFile = (entry: Dirent): boolean => entry.isFile() && entry.name.endsWith(SCHEMA_SUFFIX);
|
|
48
|
+
|
|
49
|
+
export const findSchemaFiles = (dataDir: string): string[] => {
|
|
50
|
+
const found: string[] = [];
|
|
51
|
+
const walk = (dir: string): void => {
|
|
52
|
+
for (const entry of readVisibleEntries(dir)) {
|
|
53
|
+
const full = join(dir, entry.name);
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
walk(full);
|
|
56
|
+
} else if (isSchemaFile(entry)) {
|
|
57
|
+
found.push(full);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
walk(dataDir);
|
|
62
|
+
return sortStrings(found);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const stageAndCompileProjectSchemas = (root: string, dataDir: string | null): string | null => {
|
|
66
|
+
if (dataDir === null) return null;
|
|
67
|
+
const schemaFiles = findSchemaFiles(join(root, dataDir));
|
|
68
|
+
if (schemaFiles.length === 0) return null;
|
|
69
|
+
|
|
70
|
+
const dir = mkdtempSync(join(tmpdir(), "gtkx-schemas-"));
|
|
71
|
+
for (const filePath of schemaFiles) stageSchema(dir, filePath);
|
|
72
|
+
compileSchemas(dir);
|
|
73
|
+
process.once("exit", () => removeTempDir(dir));
|
|
74
|
+
|
|
75
|
+
return dir;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const schemaEnvPath = (rootDir: string): string => join(rootDir, "node_modules", ".gtkx", "env.d.ts");
|
|
79
|
+
|
|
80
|
+
const parseProjectSchemas = (schemaFiles: string[], dataDirAbs: string): ParsedSchemaFile[] => {
|
|
81
|
+
const parsed: ParsedSchemaFile[] = [];
|
|
82
|
+
for (const filePath of schemaFiles) {
|
|
83
|
+
const specifier = moduleSpecifierFor(dataDirAbs, filePath);
|
|
84
|
+
try {
|
|
85
|
+
parsed.push(parseSchemaXml(readFileSync(filePath, "utf-8"), specifier));
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (!(error instanceof SchemaParseError)) throw error;
|
|
88
|
+
warn(`Skipping ${filePath} in schema type generation: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return parsed;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const writeIfChanged = (path: string, content: string): boolean => {
|
|
95
|
+
let existing: string | null = null;
|
|
96
|
+
try {
|
|
97
|
+
existing = readFileSync(path, "utf-8");
|
|
98
|
+
} catch {
|
|
99
|
+
existing = null;
|
|
100
|
+
}
|
|
101
|
+
if (existing === content) return false;
|
|
102
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
103
|
+
writeFileSync(path, content);
|
|
104
|
+
return true;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const emitSchemaEnv = (rootDir: string, dataDir: string | null): SchemaEnvResult => {
|
|
108
|
+
const dataDirAbs = dataDir === null ? null : join(rootDir, dataDir);
|
|
109
|
+
const schemaFiles = dataDirAbs === null ? [] : findSchemaFiles(dataDirAbs);
|
|
110
|
+
const parsed = dataDirAbs === null ? [] : parseProjectSchemas(schemaFiles, dataDirAbs);
|
|
111
|
+
const content = renderEnvModule(parsed);
|
|
112
|
+
const path = schemaEnvPath(rootDir);
|
|
113
|
+
const written = writeIfChanged(path, content);
|
|
114
|
+
return { path, written };
|
|
115
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const ASSET_EXTENSIONS = [
|
|
2
|
+
"png",
|
|
3
|
+
"jpg",
|
|
4
|
+
"jpeg",
|
|
5
|
+
"gif",
|
|
6
|
+
"svg",
|
|
7
|
+
"webp",
|
|
8
|
+
"webm",
|
|
9
|
+
"mp4",
|
|
10
|
+
"ogg",
|
|
11
|
+
"mp3",
|
|
12
|
+
"wav",
|
|
13
|
+
"flac",
|
|
14
|
+
"aac",
|
|
15
|
+
"woff",
|
|
16
|
+
"woff2",
|
|
17
|
+
"eot",
|
|
18
|
+
"ttf",
|
|
19
|
+
"otf",
|
|
20
|
+
"ico",
|
|
21
|
+
"avif",
|
|
22
|
+
"data",
|
|
23
|
+
"gpa",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const ASSET_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})$`, "i");
|
|
27
|
+
|
|
28
|
+
export const ASSET_PATH_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})(?:\?.*)?$`, "i");
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Vite plugin that configures `renderBuiltUrl` for resolving asset imports
|
|
5
|
-
* to filesystem paths at runtime.
|
|
6
|
-
*
|
|
7
|
-
* When `assetBase` is provided, assets resolve relative to the executable
|
|
8
|
-
* directory using `path.join(path.dirname(process.execPath), assetBase, filename)`.
|
|
9
|
-
* This supports FHS-compliant layouts where assets live in `../share/<app>/`.
|
|
10
|
-
*
|
|
11
|
-
* When `assetBase` is omitted, assets resolve relative to the bundle file
|
|
12
|
-
* via `import.meta.url`, which works when assets are co-located with the
|
|
13
|
-
* executable.
|
|
14
|
-
*
|
|
15
|
-
* Only applies when the user has not already configured
|
|
16
|
-
* `experimental.renderBuiltUrl` in their Vite config.
|
|
17
|
-
*/
|
|
18
3
|
export function gtkxBuiltUrl(assetBase?: string): Plugin {
|
|
19
4
|
return {
|
|
20
5
|
name: "gtkx:built-url",
|
|
6
|
+
apply: "build",
|
|
21
7
|
|
|
22
8
|
config(userConfig) {
|
|
23
9
|
if (userConfig.experimental?.renderBuiltUrl) {
|
|
@@ -37,8 +23,9 @@ export function gtkxBuiltUrl(assetBase?: string): Plugin {
|
|
|
37
23
|
};
|
|
38
24
|
}
|
|
39
25
|
|
|
26
|
+
const filenameLiteral = JSON.stringify(`./${filename}`);
|
|
40
27
|
return {
|
|
41
|
-
runtime: `new URL(${
|
|
28
|
+
runtime: `new URL(${filenameLiteral}, import.meta.url).pathname`,
|
|
42
29
|
};
|
|
43
30
|
},
|
|
44
31
|
},
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
import { createVirtualNamespace } from "./virtual-module.js";
|
|
4
|
+
|
|
5
|
+
const CSS_RE = /\.css$/i;
|
|
6
|
+
const INJECT_SUFFIX = "?inject";
|
|
7
|
+
const VIRTUAL_PREFIX = "\0gtkx-css:";
|
|
8
|
+
const { isVirtual, fromVirtualId, resolveToVirtual } = createVirtualNamespace(VIRTUAL_PREFIX);
|
|
9
|
+
|
|
10
|
+
export function gtkxCss(): Plugin {
|
|
11
|
+
return {
|
|
12
|
+
name: "gtkx:css",
|
|
13
|
+
enforce: "pre",
|
|
14
|
+
|
|
15
|
+
async resolveId(source, importer, options) {
|
|
16
|
+
if (!CSS_RE.test(source)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const virtualId = await resolveToVirtual(this, { source, importer, options });
|
|
21
|
+
if (virtualId === undefined) return;
|
|
22
|
+
|
|
23
|
+
return virtualId + INJECT_SUFFIX;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
load(id) {
|
|
27
|
+
if (isVirtual(id) && id.endsWith(INJECT_SUFFIX)) {
|
|
28
|
+
const filePath = fromVirtualId(id.slice(0, -INJECT_SUFFIX.length));
|
|
29
|
+
const content = readFileSync(filePath, "utf-8");
|
|
30
|
+
return [`import { injectGlobal } from "@gtkx/css";`, `injectGlobal(${JSON.stringify(content)});`].join(
|
|
31
|
+
"\n",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const REFRESH_REG = "$RefreshReg$";
|
|
2
|
+
|
|
3
|
+
export const REFRESH_SIG = "$RefreshSig$";
|
|
4
|
+
|
|
5
|
+
export const REFRESH_RUNTIME_SPECIFIER = "@gtkx/cli/refresh-runtime";
|
|
6
|
+
|
|
7
|
+
const REFRESH_INCLUDE = /\.[tj]sx?$/;
|
|
8
|
+
const REFRESH_EXCLUDE = /node_modules|[/\\]dist[/\\]|[/\\]\.gtkx[/\\]/;
|
|
9
|
+
|
|
10
|
+
export const shouldTransformForRefresh = (
|
|
11
|
+
id: string,
|
|
12
|
+
transformOptions: { ssr?: boolean | undefined } | undefined,
|
|
13
|
+
): boolean => {
|
|
14
|
+
if (!transformOptions?.ssr) return false;
|
|
15
|
+
if (!REFRESH_INCLUDE.test(id)) return false;
|
|
16
|
+
if (REFRESH_EXCLUDE.test(id)) return false;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { type Options as SwcOptions, transform } from "@swc/core";
|
|
3
|
+
import type { Plugin } from "vite";
|
|
4
|
+
import { REFRESH_REG, REFRESH_RUNTIME_SPECIFIER, REFRESH_SIG, shouldTransformForRefresh } from "./refresh-filter.js";
|
|
5
|
+
|
|
6
|
+
export function gtkxSwcRefresh(): Plugin {
|
|
7
|
+
return {
|
|
8
|
+
name: "gtkx:swc-refresh",
|
|
9
|
+
enforce: "pre",
|
|
10
|
+
apply: "serve",
|
|
11
|
+
|
|
12
|
+
async transform(code, id, transformOptions) {
|
|
13
|
+
if (!shouldTransformForRefresh(id, transformOptions)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const isTsx = id.endsWith(".tsx");
|
|
18
|
+
const isTs = id.endsWith(".ts") || isTsx;
|
|
19
|
+
|
|
20
|
+
const swcOptions: SwcOptions = {
|
|
21
|
+
filename: id,
|
|
22
|
+
sourceFileName: id,
|
|
23
|
+
sourceMaps: true,
|
|
24
|
+
jsc: {
|
|
25
|
+
parser: isTs ? { syntax: "typescript", tsx: isTsx } : { syntax: "ecmascript", jsx: true },
|
|
26
|
+
transform: {
|
|
27
|
+
react: {
|
|
28
|
+
runtime: "automatic",
|
|
29
|
+
development: true,
|
|
30
|
+
refresh: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
target: "es2022",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const result = await transform(code, swcOptions);
|
|
38
|
+
|
|
39
|
+
return result.map === undefined ? { code: result.code } : { code: result.code, map: result.map };
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function gtkxRefreshRuntime(): Plugin {
|
|
45
|
+
return {
|
|
46
|
+
name: "gtkx:refresh-runtime",
|
|
47
|
+
enforce: "post",
|
|
48
|
+
apply: "serve",
|
|
49
|
+
|
|
50
|
+
resolveId(id) {
|
|
51
|
+
if (id !== REFRESH_RUNTIME_SPECIFIER) return undefined;
|
|
52
|
+
return fileURLToPath(import.meta.resolve(REFRESH_RUNTIME_SPECIFIER));
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
transform(code, id, transformOptions) {
|
|
56
|
+
if (!shouldTransformForRefresh(id, transformOptions)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!code.includes(REFRESH_REG) && !code.includes(REFRESH_SIG)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const moduleIdJson = JSON.stringify(id);
|
|
65
|
+
|
|
66
|
+
const header = `
|
|
67
|
+
import { createModuleRegistration as __createModuleRegistration__ } from "${REFRESH_RUNTIME_SPECIFIER}";
|
|
68
|
+
const { ${REFRESH_REG}, ${REFRESH_SIG} } = __createModuleRegistration__(${moduleIdJson});
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
code: header + code,
|
|
73
|
+
map: null,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const gtkxFastRefresh = (): Plugin[] => [gtkxSwcRefresh(), gtkxRefreshRuntime()];
|