@gtkx/cli 1.2.2 → 1.4.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/README.md +6 -4
- package/dist/builder.d.ts +0 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +6 -6
- package/dist/builder.js.map +1 -1
- package/dist/codegen/freshness.js +1 -1
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +2 -2
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +28 -9
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/commands/build.d.ts +0 -4
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +0 -5
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/codegen.d.ts.map +1 -1
- package/dist/commands/codegen.js +1 -3
- package/dist/commands/codegen.js.map +1 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +3 -0
- package/dist/commands/docs.js.map +1 -1
- package/dist/deploy/depends.d.ts +2 -2
- package/dist/deploy/depends.d.ts.map +1 -1
- package/dist/deploy/depends.js +30 -15
- package/dist/deploy/depends.js.map +1 -1
- package/dist/deploy/nfpm/config.js +1 -1
- package/dist/deploy/nfpm/config.js.map +1 -1
- package/dist/deploy/node-runtime/elf.d.ts +1 -1
- package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
- package/dist/deploy/node-runtime/elf.js +2 -2
- package/dist/deploy/node-runtime/elf.js.map +1 -1
- package/dist/deploy/node-runtime/index.js +2 -2
- package/dist/deploy/node-runtime/index.js.map +1 -1
- package/dist/deploy/notices/collect.d.ts +3 -1
- package/dist/deploy/notices/collect.d.ts.map +1 -1
- package/dist/deploy/notices/collect.js +2 -2
- package/dist/deploy/notices/collect.js.map +1 -1
- package/dist/deploy/notices/packages.d.ts +2 -1
- package/dist/deploy/notices/packages.d.ts.map +1 -1
- package/dist/deploy/notices/packages.js +4 -27
- package/dist/deploy/notices/packages.js.map +1 -1
- package/dist/deploy/payload/icons.d.ts +7 -3
- package/dist/deploy/payload/icons.d.ts.map +1 -1
- package/dist/deploy/payload/icons.js +46 -28
- package/dist/deploy/payload/icons.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +3 -2
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/run-deploy.d.ts.map +1 -1
- package/dist/deploy/run-deploy.js +63 -11
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/settings/build-manifest.d.ts +9 -0
- package/dist/deploy/settings/build-manifest.d.ts.map +1 -0
- package/dist/deploy/settings/build-manifest.js +80 -0
- package/dist/deploy/settings/build-manifest.js.map +1 -0
- package/dist/deploy/settings/index.d.ts.map +1 -1
- package/dist/deploy/settings/index.js +9 -3
- package/dist/deploy/settings/index.js.map +1 -1
- package/dist/deploy/settings/libraries.d.ts +8 -0
- package/dist/deploy/settings/libraries.d.ts.map +1 -0
- package/dist/deploy/settings/libraries.js +39 -0
- package/dist/deploy/settings/libraries.js.map +1 -0
- package/dist/deploy/settings/paths.d.ts +3 -0
- package/dist/deploy/settings/paths.d.ts.map +1 -1
- package/dist/deploy/settings/paths.js +6 -32
- package/dist/deploy/settings/paths.js.map +1 -1
- package/dist/deploy/targets/appimage.d.ts.map +1 -1
- package/dist/deploy/targets/appimage.js +19 -13
- package/dist/deploy/targets/appimage.js.map +1 -1
- package/dist/deploy/targets/flatpak-manifest.d.ts +2 -1
- package/dist/deploy/targets/flatpak-manifest.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-manifest.js +5 -1
- package/dist/deploy/targets/flatpak-manifest.js.map +1 -1
- package/dist/deploy/targets/flatpak-pnpm.js +4 -4
- package/dist/deploy/targets/flatpak-pnpm.js.map +1 -1
- package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-source.js +9 -6
- package/dist/deploy/targets/flatpak-source.js.map +1 -1
- package/dist/deploy/types.d.ts +4 -4
- package/dist/deploy/types.d.ts.map +1 -1
- package/dist/deploy/types.js.map +1 -1
- package/dist/dev/icon-dir.d.ts +1 -1
- package/dist/dev/icon-dir.d.ts.map +1 -1
- package/dist/dev/icon-dir.js +20 -7
- package/dist/dev/icon-dir.js.map +1 -1
- package/dist/dev/runner-deps.d.ts.map +1 -1
- package/dist/dev/runner-deps.js +5 -1
- package/dist/dev/runner-deps.js.map +1 -1
- package/dist/dev/runner-main.js +4 -4
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts +1 -1
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +1 -1
- package/dist/dev/runner.js.map +1 -1
- package/dist/dev/schema-dir.d.ts +1 -1
- package/dist/dev/schema-dir.d.ts.map +1 -1
- package/dist/dev/schema-dir.js +3 -1
- package/dist/dev/schema-dir.js.map +1 -1
- package/dist/internal/build-manifest.d.ts +20 -0
- package/dist/internal/build-manifest.d.ts.map +1 -0
- package/dist/internal/build-manifest.js +6 -0
- package/dist/internal/build-manifest.js.map +1 -0
- package/dist/internal/icon-path.d.ts +13 -0
- package/dist/internal/icon-path.d.ts.map +1 -0
- package/dist/internal/icon-path.js +86 -0
- package/dist/internal/icon-path.js.map +1 -0
- package/dist/internal/source-imports.d.ts +8 -0
- package/dist/internal/source-imports.d.ts.map +1 -0
- package/dist/internal/source-imports.js +144 -0
- package/dist/internal/source-imports.js.map +1 -0
- package/dist/mcp/widget-properties.d.ts.map +1 -1
- package/dist/mcp/widget-properties.js +3 -12
- package/dist/mcp/widget-properties.js.map +1 -1
- package/dist/settings/render.d.ts +7 -1
- package/dist/settings/render.d.ts.map +1 -1
- package/dist/settings/render.js +69 -20
- package/dist/settings/render.js.map +1 -1
- package/dist/settings/schema.d.ts +4 -3
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +122 -15
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/asset-emitter.d.ts +1 -1
- package/dist/vite-plugins/asset-emitter.d.ts.map +1 -1
- package/dist/vite-plugins/asset-emitter.js.map +1 -1
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
- package/dist/vite-plugins/asset-extensions.js +1 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -1
- package/dist/vite-plugins/asset-imports.d.ts +2 -1
- package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
- package/dist/vite-plugins/asset-imports.js +37 -16
- package/dist/vite-plugins/asset-imports.js.map +1 -1
- package/dist/vite-plugins/asset-specifier.d.ts +13 -1
- package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
- package/dist/vite-plugins/asset-specifier.js +39 -1
- package/dist/vite-plugins/asset-specifier.js.map +1 -1
- package/dist/vite-plugins/build-manifest.d.ts +5 -0
- package/dist/vite-plugins/build-manifest.d.ts.map +1 -0
- package/dist/vite-plugins/{bundled-packages.js → build-manifest.js} +20 -14
- package/dist/vite-plugins/build-manifest.js.map +1 -0
- package/dist/vite-plugins/built-url.d.ts +1 -1
- package/dist/vite-plugins/built-url.d.ts.map +1 -1
- package/dist/vite-plugins/built-url.js +37 -24
- package/dist/vite-plugins/built-url.js.map +1 -1
- package/dist/vite-plugins/icons.d.ts +2 -1
- package/dist/vite-plugins/icons.d.ts.map +1 -1
- package/dist/vite-plugins/icons.js +25 -12
- package/dist/vite-plugins/icons.js.map +1 -1
- package/dist/vite-plugins/index.d.ts +2 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +7 -5
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/native.d.ts.map +1 -1
- package/dist/vite-plugins/native.js +23 -4
- package/dist/vite-plugins/native.js.map +1 -1
- package/dist/vite-plugins/resource-init-module.d.ts +7 -3
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
- package/dist/vite-plugins/resource-init-module.js +39 -14
- package/dist/vite-plugins/resource-init-module.js.map +1 -1
- package/dist/vite-plugins/resource-shared.d.ts +3 -1
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
- package/dist/vite-plugins/resource-shared.js +3 -1
- package/dist/vite-plugins/resource-shared.js.map +1 -1
- package/dist/vite-plugins/resources.d.ts +1 -1
- package/dist/vite-plugins/resources.d.ts.map +1 -1
- package/dist/vite-plugins/resources.js +922 -86
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +6 -3
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/settings.d.ts +3 -1
- package/dist/vite-plugins/settings.d.ts.map +1 -1
- package/dist/vite-plugins/settings.js +63 -23
- package/dist/vite-plugins/settings.js.map +1 -1
- package/dist/vite-plugins/store-links.js +1 -1
- package/dist/vite-plugins/store-links.js.map +1 -1
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
- package/dist/vite-plugins/undeclared-library.js +25 -13
- package/dist/vite-plugins/undeclared-library.js.map +1 -1
- package/env.d.ts +11 -192
- package/package.json +14 -14
- package/src/builder.ts +6 -7
- package/src/codegen/freshness.ts +1 -1
- package/src/codegen/run-codegen.ts +33 -10
- package/src/commands/build.ts +0 -5
- package/src/commands/codegen.ts +1 -3
- package/src/commands/docs.ts +3 -0
- package/src/deploy/depends.ts +47 -18
- package/src/deploy/nfpm/config.ts +1 -1
- package/src/deploy/node-runtime/elf.ts +3 -3
- package/src/deploy/node-runtime/index.ts +2 -2
- package/src/deploy/notices/collect.ts +4 -2
- package/src/deploy/notices/packages.ts +5 -43
- package/src/deploy/payload/icons.ts +64 -34
- package/src/deploy/payload/stage.ts +5 -2
- package/src/deploy/run-deploy.ts +87 -12
- package/src/deploy/settings/build-manifest.ts +122 -0
- package/src/deploy/settings/index.ts +9 -4
- package/src/deploy/settings/libraries.ts +57 -0
- package/src/deploy/settings/paths.ts +10 -47
- package/src/deploy/targets/appimage.ts +32 -15
- package/src/deploy/targets/flatpak-manifest.ts +7 -1
- package/src/deploy/targets/flatpak-pnpm.ts +4 -4
- package/src/deploy/targets/flatpak-source.ts +9 -10
- package/src/deploy/types.ts +4 -4
- package/src/dev/icon-dir.ts +31 -7
- package/src/dev/runner-deps.ts +5 -1
- package/src/dev/runner-main.ts +4 -4
- package/src/dev/runner.ts +2 -2
- package/src/dev/schema-dir.ts +3 -1
- package/src/internal/build-manifest.ts +32 -0
- package/src/internal/icon-path.ts +127 -0
- package/src/internal/source-imports.ts +200 -0
- package/src/mcp/widget-properties.ts +3 -14
- package/src/settings/render.ts +99 -21
- package/src/settings/schema.ts +190 -17
- package/src/vite-plugins/asset-emitter.ts +1 -1
- package/src/vite-plugins/asset-extensions.ts +1 -0
- package/src/vite-plugins/asset-imports.ts +60 -19
- package/src/vite-plugins/asset-specifier.ts +73 -1
- package/src/vite-plugins/{bundled-packages.ts → build-manifest.ts} +30 -19
- package/src/vite-plugins/built-url.ts +49 -28
- package/src/vite-plugins/icons.ts +33 -14
- package/src/vite-plugins/index.ts +12 -5
- package/src/vite-plugins/native.ts +31 -4
- package/src/vite-plugins/resource-init-module.ts +44 -15
- package/src/vite-plugins/resource-shared.ts +4 -0
- package/src/vite-plugins/resources.ts +1459 -100
- package/src/vite-plugins/settings-worker-env.ts +7 -3
- package/src/vite-plugins/settings.ts +89 -21
- package/src/vite-plugins/store-links.ts +1 -1
- package/src/vite-plugins/undeclared-library.ts +31 -13
- package/dist/vite-plugins/bundled-packages.d.ts +0 -5
- package/dist/vite-plugins/bundled-packages.d.ts.map +0 -1
- package/dist/vite-plugins/bundled-packages.js.map +0 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { type Stats, statSync } from "node:fs";
|
|
2
|
+
import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
type ResolvedApplicationIcon = { kind: "file"; path: string } |
|
|
5
|
+
{ kind: "none" } |
|
|
6
|
+
{ kind: "theme"; path: string };
|
|
7
|
+
|
|
8
|
+
const ICON_EXTENSIONS: Set<string> = new Set([".svg", ".png", ".xpm"]);
|
|
9
|
+
const SCALABLE_DIR = "hicolor/scalable/apps";
|
|
10
|
+
const SIZE_PATTERN = /^(?<width>[1-9]\d{0,3})x(?<height>[1-9]\d{0,3})$/;
|
|
11
|
+
const SIZE_SEPARATORS = /[-_.]/;
|
|
12
|
+
|
|
13
|
+
const isInside = (parent: string, candidate: string): boolean => {
|
|
14
|
+
const rel = relative(parent, candidate);
|
|
15
|
+
|
|
16
|
+
return rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getStats = (path: string): Stats | undefined => {
|
|
20
|
+
try {
|
|
21
|
+
return statSync(path, { throwIfNoEntry: false });
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const iconExtension = (file: string): string => {
|
|
28
|
+
const extension = extname(file).toLowerCase();
|
|
29
|
+
|
|
30
|
+
if (!ICON_EXTENSIONS.has(extension)) {
|
|
31
|
+
throw new Error(`Cannot use ${file} as an application icon: expected an SVG, PNG, or XPM file`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return extension;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const defaultIconFiles = (root: string, applicationId: string): string[] =>
|
|
38
|
+
[...ICON_EXTENSIONS]
|
|
39
|
+
.map((extension) => join(root, `${applicationId}${extension}`))
|
|
40
|
+
.filter((path) => getStats(path)?.isFile() === true);
|
|
41
|
+
|
|
42
|
+
const resolveDefaultApplicationIcon = (root: string, applicationId: string): ResolvedApplicationIcon => {
|
|
43
|
+
const files = defaultIconFiles(root, applicationId);
|
|
44
|
+
|
|
45
|
+
if (files.length === 0) {
|
|
46
|
+
return { kind: "none" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (files.length > 1) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Found multiple default application icons for ${applicationId}; set \`applicationIcon\` to choose one`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const path = files[0];
|
|
56
|
+
|
|
57
|
+
if (path === undefined) {
|
|
58
|
+
return { kind: "none" };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { kind: "file", path };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const resolveApplicationIcon = (
|
|
65
|
+
root: string,
|
|
66
|
+
applicationId: string,
|
|
67
|
+
configured: string | undefined,
|
|
68
|
+
): ResolvedApplicationIcon => {
|
|
69
|
+
if (configured === undefined) {
|
|
70
|
+
return resolveDefaultApplicationIcon(root, applicationId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isAbsolute(configured)) {
|
|
74
|
+
throw new Error(`Cannot use "${configured}" as the application icon: expected a project-relative path`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const path = resolve(root, configured);
|
|
78
|
+
|
|
79
|
+
if (!isInside(root, path)) {
|
|
80
|
+
throw new Error(`Cannot use "${configured}" as the application icon: it is outside ${root}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const stats = getStats(path);
|
|
84
|
+
|
|
85
|
+
if (stats?.isDirectory() === true) {
|
|
86
|
+
return { kind: "theme", path };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (stats?.isFile() !== true) {
|
|
90
|
+
throw new Error(`Cannot read the application icon "${configured}": no such file or directory under ${root}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
iconExtension(path);
|
|
94
|
+
|
|
95
|
+
return { kind: "file", path };
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const squareSize = (token: string): number | null => {
|
|
99
|
+
const groups = SIZE_PATTERN.exec(token)?.groups;
|
|
100
|
+
|
|
101
|
+
if (groups === undefined || groups.width !== groups.height) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return Number(groups.width);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const iconThemeDir = (file: string, extension: string): string => {
|
|
109
|
+
if (extension === ".svg") {
|
|
110
|
+
return SCALABLE_DIR;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const size = basename(file)
|
|
114
|
+
.split(SIZE_SEPARATORS)
|
|
115
|
+
.map((token) => squareSize(token))
|
|
116
|
+
.find((value) => value !== null);
|
|
117
|
+
|
|
118
|
+
return size === undefined ? SCALABLE_DIR : `hicolor/${String(size)}x${String(size)}/apps`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const relativeIconPath = (applicationId: string, file: string): string => {
|
|
122
|
+
const extension = iconExtension(file);
|
|
123
|
+
|
|
124
|
+
return `${iconThemeDir(file, extension)}/${applicationId}${extension}`;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export { relativeIconPath, resolveApplicationIcon, type ResolvedApplicationIcon };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { ParseResult, ParserOptions } from "vite";
|
|
2
|
+
import { isRecord, sortStringsBy } from "@gtkx/utils";
|
|
3
|
+
import { type Dirent, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { extname, join } from "node:path";
|
|
5
|
+
import { parseSync } from "vite";
|
|
6
|
+
|
|
7
|
+
type SourceImport = {
|
|
8
|
+
importer: string;
|
|
9
|
+
source: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type SourceLanguage = NonNullable<ParserOptions["lang"]>;
|
|
13
|
+
type ParsedModule = ParseResult["module"];
|
|
14
|
+
|
|
15
|
+
const SOURCE_LANGUAGES: Map<string, SourceLanguage> = new Map([
|
|
16
|
+
[".cjs", "jsx"],
|
|
17
|
+
[".cts", "ts"],
|
|
18
|
+
[".js", "jsx"],
|
|
19
|
+
[".jsx", "jsx"],
|
|
20
|
+
[".mjs", "jsx"],
|
|
21
|
+
[".mts", "ts"],
|
|
22
|
+
[".ts", "ts"],
|
|
23
|
+
[".tsx", "tsx"],
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const EXCLUDED_DIRECTORIES: Set<string> = new Set([
|
|
27
|
+
".git",
|
|
28
|
+
".gtkx",
|
|
29
|
+
"build",
|
|
30
|
+
"coverage",
|
|
31
|
+
"dist",
|
|
32
|
+
"node_modules",
|
|
33
|
+
"out-tsc",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const sourceLanguage = (path: string): SourceLanguage | undefined => {
|
|
37
|
+
if (path.endsWith(".d.ts") || path.endsWith(".d.cts") || path.endsWith(".d.mts")) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return SOURCE_LANGUAGES.get(extname(path).toLowerCase());
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const collectSourceEntry = (dir: string, entry: Dirent, found: string[]): void => {
|
|
45
|
+
const path = join(dir, entry.name);
|
|
46
|
+
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
if (!EXCLUDED_DIRECTORIES.has(entry.name)) {
|
|
49
|
+
sourceFilesIn(path, found);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (entry.isFile() && sourceLanguage(path) !== undefined) {
|
|
56
|
+
found.push(path);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const sourceFilesIn = (dir: string, found: string[]): void => {
|
|
61
|
+
let entries: Dirent[];
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
65
|
+
} catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
collectSourceEntry(dir, entry, found);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const staticImportSources = (module: ParsedModule): string[] => [
|
|
75
|
+
...module.staticImports.map((statement) => statement.moduleRequest.value),
|
|
76
|
+
...module.staticExports.flatMap((statement) =>
|
|
77
|
+
statement.entries
|
|
78
|
+
.map((entry) => entry.moduleRequest?.value)
|
|
79
|
+
.filter((source): source is string => source !== undefined),
|
|
80
|
+
),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const staticRuntimeImportSources = (module: ParsedModule): string[] => [
|
|
84
|
+
...module.staticImports
|
|
85
|
+
.filter((statement) => statement.entries.length === 0 || statement.entries.some((entry) => !entry.isType))
|
|
86
|
+
.map((statement) => statement.moduleRequest.value),
|
|
87
|
+
...module.staticExports.flatMap((statement) =>
|
|
88
|
+
statement.entries
|
|
89
|
+
.filter((entry) => !entry.isType)
|
|
90
|
+
.map((entry) => entry.moduleRequest?.value)
|
|
91
|
+
.filter((source): source is string => source !== undefined),
|
|
92
|
+
),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const templateImportSource = (node: Record<string, unknown>): string | null => {
|
|
96
|
+
if (!Array.isArray(node.expressions) || node.expressions.length > 0 || !Array.isArray(node.quasis)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const quasis: unknown[] = node.quasis;
|
|
101
|
+
const first: unknown = quasis[0];
|
|
102
|
+
|
|
103
|
+
if (!isRecord(first) || !isRecord(first.value)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return typeof first.value.cooked === "string" ? first.value.cooked : null;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const literalImportSource = (node: unknown): string | null => {
|
|
111
|
+
if (!isRecord(node)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (node.type === "Literal") {
|
|
116
|
+
return typeof node.value === "string" ? node.value : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return node.type === "TemplateLiteral" ? templateImportSource(node) : null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const collectDynamicImportArray = (nodes: unknown[], found: string[]): void => {
|
|
123
|
+
for (const child of nodes) {
|
|
124
|
+
collectDynamicImportSources(child, found);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const collectDynamicImportRecord = (node: Record<string, unknown>, found: string[]): void => {
|
|
129
|
+
if (node.type === "ImportExpression") {
|
|
130
|
+
const source = literalImportSource(node.source);
|
|
131
|
+
|
|
132
|
+
if (source !== null) {
|
|
133
|
+
found.push(source);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
collectDynamicImportArray(Object.values(node), found);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const collectDynamicImportSources = (node: unknown, found: string[]): void => {
|
|
143
|
+
if (Array.isArray(node)) {
|
|
144
|
+
collectDynamicImportArray(node, found);
|
|
145
|
+
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!isRecord(node)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
collectDynamicImportRecord(node, found);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const parseImportsInWith = (
|
|
157
|
+
path: string,
|
|
158
|
+
staticSources: (module: ParsedModule) => string[],
|
|
159
|
+
): SourceImport[] | null => {
|
|
160
|
+
const lang = sourceLanguage(path);
|
|
161
|
+
|
|
162
|
+
if (lang === undefined) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let parsed: ParseResult;
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
parsed = parseSync(path, readFileSync(path, "utf8"), { lang });
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (parsed.errors.length > 0) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const sources = staticSources(parsed.module);
|
|
179
|
+
collectDynamicImportSources(parsed.program, sources);
|
|
180
|
+
|
|
181
|
+
return [...new Set(sources)].map((source) => ({ importer: path, source }));
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const parseImportsIn = (path: string): SourceImport[] | null => parseImportsInWith(path, staticImportSources);
|
|
185
|
+
|
|
186
|
+
const parseRuntimeImportsIn = (path: string): SourceImport[] | null =>
|
|
187
|
+
parseImportsInWith(path, staticRuntimeImportSources);
|
|
188
|
+
|
|
189
|
+
const importsIn = (path: string): SourceImport[] => parseImportsIn(path) ?? [];
|
|
190
|
+
const importKey = (entry: SourceImport): string => `${entry.importer}\0${entry.source}`;
|
|
191
|
+
|
|
192
|
+
const discoverSourceImports = (dir: string): SourceImport[] => {
|
|
193
|
+
const files: string[] = [];
|
|
194
|
+
sourceFilesIn(dir, files);
|
|
195
|
+
const imports = files.flatMap((path) => importsIn(path));
|
|
196
|
+
|
|
197
|
+
return sortStringsBy(imports, importKey);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export { discoverSourceImports, parseRuntimeImportsIn, type SourceImport };
|
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
getHandle,
|
|
7
7
|
getInstanceType,
|
|
8
8
|
type Handle,
|
|
9
|
+
isReadableProperty,
|
|
9
10
|
TYPE_ENUM,
|
|
10
11
|
TYPE_FLAGS,
|
|
11
12
|
typeIsA,
|
|
12
13
|
typeName,
|
|
13
14
|
} from "@gtkx/runtime";
|
|
14
15
|
import { fromValue, getValueType } from "@gtkx/runtime/internal";
|
|
15
|
-
import {
|
|
16
|
+
import { errorMessage, kebabCase } from "@gtkx/utils";
|
|
16
17
|
import type { WidgetRegistry } from "./widget-registry.js";
|
|
17
18
|
|
|
18
19
|
type PropertyRepresentation = Omit<SerializedProperty, "type">;
|
|
@@ -39,18 +40,6 @@ function prototypesFor(widget: Gtk.Widget): object[] {
|
|
|
39
40
|
return prototypes;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
function getAccessor(widget: Gtk.Widget, member: string): PropertyDescriptor | undefined {
|
|
43
|
-
for (const prototype of prototypesFor(widget)) {
|
|
44
|
-
const descriptor = Object.getOwnPropertyDescriptor(prototype, member);
|
|
45
|
-
|
|
46
|
-
if (descriptor !== undefined) {
|
|
47
|
-
return descriptor;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
43
|
function getReadableNames(prototype: object): string[] {
|
|
55
44
|
const descriptors = Object.entries(Object.getOwnPropertyDescriptors(prototype));
|
|
56
45
|
|
|
@@ -64,7 +53,7 @@ function readablePropertyNames(widget: Gtk.Widget): string[] {
|
|
|
64
53
|
}
|
|
65
54
|
|
|
66
55
|
const requireReadableProperty = (widget: Gtk.Widget, property: string): void => {
|
|
67
|
-
if (
|
|
56
|
+
if (isReadableProperty(getInstanceType(widget), property)) {
|
|
68
57
|
return;
|
|
69
58
|
}
|
|
70
59
|
|
package/src/settings/render.ts
CHANGED
|
@@ -1,19 +1,56 @@
|
|
|
1
|
-
import { upperFirst } from "@gtkx/utils";
|
|
1
|
+
import { camelCase, sanitizeIdentifier, upperFirst } from "@gtkx/utils";
|
|
2
2
|
import type { ParsedKey, ParsedSchema, ParsedSchemaFile } from "./parser.js";
|
|
3
3
|
|
|
4
|
+
type NamedSchema = {
|
|
5
|
+
schema: ParsedSchema;
|
|
6
|
+
exportName: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type EnvAssetDeclarations = {
|
|
10
|
+
blocked: string[];
|
|
11
|
+
icons: string[];
|
|
12
|
+
legacy: string[];
|
|
13
|
+
resources: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
4
16
|
const GTKX_ENV_MODULE_HEADER = `/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* \`gtkx build\`; do not edit.
|
|
17
|
+
* Project import declarations generated by GTKX. Regenerated by
|
|
18
|
+
* \`gtkx codegen\`, \`gtkx dev\`, and \`gtkx build\`; do not edit.
|
|
8
19
|
*/`;
|
|
9
20
|
|
|
10
21
|
const ENUM_KIND = "enum";
|
|
11
22
|
const FLAGS_KIND = "flags";
|
|
23
|
+
const ID_SEPARATOR = /[._]/;
|
|
24
|
+
const NAME_SEPARATOR = "_";
|
|
12
25
|
|
|
13
26
|
const toJsStringLiteral = (value: string): string =>
|
|
14
27
|
JSON.stringify(value).replaceAll("\u{2028}", String.raw`\u2028`).replaceAll("\u{2029}", String.raw`\u2029`);
|
|
15
28
|
|
|
16
|
-
const getExportName = (schemaId: string): string =>
|
|
29
|
+
const getExportName = (schemaId: string): string =>
|
|
30
|
+
sanitizeIdentifier(schemaId.split(ID_SEPARATOR).map((part) => camelCase(part)).join(NAME_SEPARATOR));
|
|
31
|
+
|
|
32
|
+
const collisionError = (file: ParsedSchemaFile, taken: string, schemaId: string, name: string): Error =>
|
|
33
|
+
new Error(
|
|
34
|
+
`${file.fileName} declares ${taken} and ${schemaId}, which both export \`${name}\`. ` +
|
|
35
|
+
"Rename one of the schema ids so that every schema in the file gets an export of its own.",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const getNamedSchemas = (file: ParsedSchemaFile): NamedSchema[] => {
|
|
39
|
+
const owners: Map<string, string> = new Map();
|
|
40
|
+
|
|
41
|
+
return file.schemas.map((schema) => {
|
|
42
|
+
const exportName = getExportName(schema.id);
|
|
43
|
+
const taken = owners.get(exportName);
|
|
44
|
+
|
|
45
|
+
if (taken !== undefined) {
|
|
46
|
+
throw collisionError(file, taken, schema.id, exportName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
owners.set(exportName, schema.id);
|
|
50
|
+
|
|
51
|
+
return { schema, exportName };
|
|
52
|
+
});
|
|
53
|
+
};
|
|
17
54
|
|
|
18
55
|
const runtimeKindForKey = (key: ParsedKey): string => {
|
|
19
56
|
if (key.enumId !== null) {
|
|
@@ -37,26 +74,27 @@ const getRuntimeKeys = (schema: ParsedSchema): string => {
|
|
|
37
74
|
|
|
38
75
|
const renderRuntimeModule = (file: ParsedSchemaFile): string => {
|
|
39
76
|
const lines: string[] = [];
|
|
77
|
+
const named = getNamedSchemas(file);
|
|
40
78
|
|
|
41
|
-
for (const [index, schema] of
|
|
79
|
+
for (const [index, { schema, exportName }] of named.entries()) {
|
|
42
80
|
const keysName = `keys_${String(index)}`;
|
|
43
81
|
const id = toJsStringLiteral(schema.id);
|
|
44
82
|
lines.push(`const ${keysName} = ${getRuntimeKeys(schema)};`);
|
|
45
83
|
|
|
46
84
|
if (schema.path === null) {
|
|
47
85
|
lines.push(
|
|
48
|
-
`export const ${
|
|
86
|
+
`export const ${exportName} = { id: ${id}, keys: ${keysName}, ` +
|
|
49
87
|
`at: (path) => ({ id: ${id}, path, keys: ${keysName} }) };`,
|
|
50
88
|
);
|
|
51
89
|
} else {
|
|
52
|
-
lines.push(`export const ${
|
|
90
|
+
lines.push(`export const ${exportName} = { id: ${id}, path: null, keys: ${keysName} };`);
|
|
53
91
|
}
|
|
54
92
|
}
|
|
55
93
|
|
|
56
|
-
const first =
|
|
94
|
+
const first = named[0];
|
|
57
95
|
|
|
58
96
|
if (first !== undefined) {
|
|
59
|
-
lines.push(`export default ${
|
|
97
|
+
lines.push(`export default ${first.exportName};`);
|
|
60
98
|
}
|
|
61
99
|
|
|
62
100
|
return lines.join("\n");
|
|
@@ -106,9 +144,7 @@ const boundRefTypeLines = (interfaceName: string, indent: string): string[] => [
|
|
|
106
144
|
`${indent}keys: ${interfaceName};`,
|
|
107
145
|
];
|
|
108
146
|
|
|
109
|
-
const renderSchemaConst = (schema:
|
|
110
|
-
const exportName = getExportName(schema.id);
|
|
111
|
-
|
|
147
|
+
const renderSchemaConst = ({ schema, exportName }: NamedSchema, interfaceName: string): string[] => {
|
|
112
148
|
if (schema.path !== null) {
|
|
113
149
|
return [
|
|
114
150
|
` const ${exportName}: {`,
|
|
@@ -130,17 +166,17 @@ const renderSchemaConst = (schema: ParsedSchema, interfaceName: string): string[
|
|
|
130
166
|
};
|
|
131
167
|
|
|
132
168
|
const renderFileModule = (file: ParsedSchemaFile, usedNames: Set<string>): string[] => {
|
|
133
|
-
const lines = [`declare module
|
|
169
|
+
const lines = [`declare module ${toJsStringLiteral(file.fileName)} {`];
|
|
134
170
|
const exportNames: string[] = [];
|
|
135
171
|
|
|
136
|
-
for (const [index,
|
|
172
|
+
for (const [index, named] of getNamedSchemas(file).entries()) {
|
|
137
173
|
if (index > 0) {
|
|
138
174
|
lines.push("");
|
|
139
175
|
}
|
|
140
176
|
|
|
141
|
-
const interfaceName = allocateInterfaceName(schema.id, usedNames);
|
|
142
|
-
lines.push(...renderKeysType(interfaceName, schema), "", ...renderSchemaConst(
|
|
143
|
-
exportNames.push(
|
|
177
|
+
const interfaceName = allocateInterfaceName(named.schema.id, usedNames);
|
|
178
|
+
lines.push(...renderKeysType(interfaceName, named.schema), "", ...renderSchemaConst(named, interfaceName));
|
|
179
|
+
exportNames.push(named.exportName);
|
|
144
180
|
}
|
|
145
181
|
|
|
146
182
|
const [firstExport] = exportNames;
|
|
@@ -154,14 +190,56 @@ const renderFileModule = (file: ParsedSchemaFile, usedNames: Set<string>): strin
|
|
|
154
190
|
return lines;
|
|
155
191
|
};
|
|
156
192
|
|
|
157
|
-
const
|
|
193
|
+
const renderResourceModule = (specifier: string): string => [
|
|
194
|
+
`declare module ${toJsStringLiteral(specifier)} {`,
|
|
195
|
+
" const path: string;",
|
|
196
|
+
" export { path };",
|
|
197
|
+
" export default path;",
|
|
198
|
+
"}",
|
|
199
|
+
].join("\n");
|
|
200
|
+
|
|
201
|
+
const renderIconModule = (specifier: string): string => [
|
|
202
|
+
`declare module ${toJsStringLiteral(specifier)} {`,
|
|
203
|
+
" const iconName: string;",
|
|
204
|
+
" export default iconName;",
|
|
205
|
+
"}",
|
|
206
|
+
].join("\n");
|
|
207
|
+
|
|
208
|
+
const renderLegacyAssetModule = (specifier: string): string => [
|
|
209
|
+
`declare module ${toJsStringLiteral(specifier)} {`,
|
|
210
|
+
" const resourceUri: string;",
|
|
211
|
+
" const path: string;",
|
|
212
|
+
" export { path };",
|
|
213
|
+
" export default resourceUri;",
|
|
214
|
+
"}",
|
|
215
|
+
].join("\n");
|
|
216
|
+
|
|
217
|
+
const renderBlockedAssetModule = (specifier: string): string => [
|
|
218
|
+
`declare module ${toJsStringLiteral(specifier)} {`,
|
|
219
|
+
" const assetImportRequiresResourceOrUrlQuery: {",
|
|
220
|
+
" readonly __gtkxAssetImportRequiresResourceOrUrlQuery: unique symbol;",
|
|
221
|
+
" };",
|
|
222
|
+
" export default assetImportRequiresResourceOrUrlQuery;",
|
|
223
|
+
"}",
|
|
224
|
+
].join("\n");
|
|
225
|
+
|
|
226
|
+
const renderEnvModule = (
|
|
227
|
+
files: ParsedSchemaFile[],
|
|
228
|
+
assetDeclarations?: EnvAssetDeclarations,
|
|
229
|
+
): string => {
|
|
230
|
+
const assets = assetDeclarations ?? { blocked: [], icons: [], legacy: [], resources: [] };
|
|
158
231
|
const usedNames: Set<string> = new Set();
|
|
159
232
|
|
|
160
|
-
const
|
|
233
|
+
const schemas = files
|
|
161
234
|
.filter((file) => file.schemas.length > 0)
|
|
162
235
|
.map((file) => renderFileModule(file, usedNames).join("\n"));
|
|
163
236
|
|
|
164
|
-
|
|
237
|
+
const blocked = assets.blocked.map((specifier) => renderBlockedAssetModule(specifier));
|
|
238
|
+
const icons = assets.icons.map((specifier) => renderIconModule(specifier));
|
|
239
|
+
const legacy = assets.legacy.map((specifier) => renderLegacyAssetModule(specifier));
|
|
240
|
+
const resources = assets.resources.map((specifier) => renderResourceModule(specifier));
|
|
241
|
+
|
|
242
|
+
return `${[GTKX_ENV_MODULE_HEADER, ...blocked, ...icons, ...legacy, ...resources, ...schemas].join("\n\n")}\n`;
|
|
165
243
|
};
|
|
166
244
|
|
|
167
245
|
export { renderRuntimeModule, renderEnvModule };
|