@gtkx/cli 1.3.0 → 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 +5 -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/run-codegen.d.ts +1 -2
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +11 -6
- 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/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-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/env.d.ts +11 -192
- package/package.json +10 -10
- package/src/builder.ts +6 -7
- package/src/codegen/run-codegen.ts +10 -7
- package/src/commands/build.ts +0 -5
- package/src/commands/codegen.ts +1 -3
- 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-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/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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, rmSync, statSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
3
|
import type {
|
|
4
4
|
DeployArtifact,
|
|
5
5
|
DeployManifest,
|
|
@@ -10,18 +10,21 @@ import type {
|
|
|
10
10
|
} from "../types.js";
|
|
11
11
|
import { runCliTool } from "../../internal/run-cli-tool.js";
|
|
12
12
|
import { copyInto, copyTree, EXECUTABLE_MODE, writeInto } from "../payload/copy-tree.js";
|
|
13
|
-
import {
|
|
13
|
+
import { type ApplicationIconVariant, classifyApplicationIcon } from "../payload/icons.js";
|
|
14
14
|
import { FILE_TOOL } from "../tools.js";
|
|
15
15
|
import { resolveAppimageTooling } from "../vendored/appimagetool.js";
|
|
16
16
|
|
|
17
|
+
type RankedIcon = {
|
|
18
|
+
file: StagedFile;
|
|
19
|
+
variant: ApplicationIconVariant;
|
|
20
|
+
};
|
|
21
|
+
|
|
17
22
|
const PREFIX = "/usr";
|
|
18
23
|
const TARGET_DIR = "appimage";
|
|
19
24
|
const APPRUN_FILENAME = "AppRun";
|
|
20
25
|
const APPDIR_NAME = "AppDir";
|
|
21
26
|
const USR_DIR = "usr";
|
|
22
|
-
const ICON_EXTENSIONS = [".svg", ".png", ".xpm"];
|
|
23
27
|
const EXTRACT_AND_RUN = "APPIMAGE_EXTRACT_AND_RUN";
|
|
24
|
-
const SCALABLE_RANK = Number.MAX_SAFE_INTEGER;
|
|
25
28
|
|
|
26
29
|
const appimageTarget: DeployTarget = {
|
|
27
30
|
name: "appimage",
|
|
@@ -50,31 +53,45 @@ const renderManifests = (payload: DeployPayload): DeployManifest[] => [
|
|
|
50
53
|
{ path: appRunPathFor(payload.settings), contents: renderAppRun(payload.settings) },
|
|
51
54
|
];
|
|
52
55
|
|
|
53
|
-
const
|
|
54
|
-
|
|
56
|
+
const rankedIcon = (applicationId: string, file: StagedFile): RankedIcon | null => {
|
|
57
|
+
const variant = classifyApplicationIcon(applicationId, file.rel);
|
|
58
|
+
|
|
59
|
+
return variant === null ? null : { file, variant };
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const isLargerIcon = (candidate: RankedIcon, best: RankedIcon): boolean =>
|
|
63
|
+
candidate.variant.scalable === best.variant.scalable
|
|
64
|
+
? candidate.variant.effectivePixels > best.variant.effectivePixels
|
|
65
|
+
: candidate.variant.scalable;
|
|
66
|
+
|
|
67
|
+
const largerIcon = (best: RankedIcon | undefined, candidate: RankedIcon): RankedIcon =>
|
|
68
|
+
best === undefined || isLargerIcon(candidate, best) ? candidate : best;
|
|
55
69
|
|
|
56
|
-
const largestIcon = (
|
|
57
|
-
let best:
|
|
70
|
+
const largestIcon = (applicationId: string, files: StagedFile[]): StagedFile | undefined => {
|
|
71
|
+
let best: RankedIcon | undefined;
|
|
58
72
|
|
|
59
|
-
for (const file of
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
const candidate = rankedIcon(applicationId, file);
|
|
75
|
+
|
|
76
|
+
if (candidate === null) {
|
|
77
|
+
continue;
|
|
62
78
|
}
|
|
79
|
+
|
|
80
|
+
best = largerIcon(best, candidate);
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
return best;
|
|
83
|
+
return best?.file;
|
|
66
84
|
};
|
|
67
85
|
|
|
68
86
|
const rootIconFor = (payload: DeployPayload): { rel: string; abs: string } => {
|
|
69
87
|
const { applicationId } = payload.settings;
|
|
70
|
-
const
|
|
71
|
-
const icon = largestIcon(payload.stage.filter((file) => names.has(file.rel.split("/").at(-1) ?? "")));
|
|
88
|
+
const icon = largestIcon(applicationId, payload.stage);
|
|
72
89
|
|
|
73
90
|
if (icon === undefined) {
|
|
74
91
|
throw new Error(`Cannot build an AppImage without a ${applicationId} icon in the staged icon tree`);
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
return { rel: icon.rel
|
|
94
|
+
return { rel: basename(icon.rel), abs: icon.abs };
|
|
78
95
|
};
|
|
79
96
|
|
|
80
97
|
const buildAppDir = (payload: DeployPayload): string => {
|
|
@@ -110,6 +110,12 @@ const runtimeKeys = (settings: DeploySettings): FlatpakManifest => {
|
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
const runtimeLabelFor = (settings: DeploySettings): string => {
|
|
114
|
+
const flatpak = settings.deploy.flatpak ?? {};
|
|
115
|
+
|
|
116
|
+
return `${flatpak.runtime ?? DEFAULT_RUNTIME}//${flatpak.runtimeVersion ?? DEFAULT_RUNTIME_VERSION}`;
|
|
117
|
+
};
|
|
118
|
+
|
|
113
119
|
const renderFlatpakManifest = (payload: DeployPayload): FlatpakManifest => {
|
|
114
120
|
const settings = payload.settings;
|
|
115
121
|
|
|
@@ -123,4 +129,4 @@ const renderFlatpakManifest = (payload: DeployPayload): FlatpakManifest => {
|
|
|
123
129
|
};
|
|
124
130
|
};
|
|
125
131
|
|
|
126
|
-
export { branchFor, finishArgsFor, hasDisplaySocket, renderFlatpakManifest };
|
|
132
|
+
export { branchFor, finishArgsFor, hasDisplaySocket, renderFlatpakManifest, runtimeLabelFor };
|
|
@@ -120,12 +120,11 @@ const metadataInstallCommands = (settings: DeploySettings): string[] => [
|
|
|
120
120
|
const schemaInstallCommands = (settings: DeploySettings): string[] =>
|
|
121
121
|
settings.paths.schemaFiles.map((file) => {
|
|
122
122
|
const name = posix.basename(file);
|
|
123
|
+
const source = projectRelative(settings, file);
|
|
124
|
+
const destination = `${DESTINATION}/share/glib-2.0/schemas/${shellArgument(name)}`;
|
|
125
|
+
assertInsideProject(settings, { destination, source, resolved: file });
|
|
123
126
|
|
|
124
|
-
return installCommand(
|
|
125
|
-
projectRelative(settings, file),
|
|
126
|
-
`${DESTINATION}/share/glib-2.0/schemas/${shellArgument(name)}`,
|
|
127
|
-
"m644",
|
|
128
|
-
);
|
|
127
|
+
return installCommand(source, destination, "m644");
|
|
129
128
|
});
|
|
130
129
|
|
|
131
130
|
const iconInstallCommand = (settings: DeploySettings, source: string, rel: string): string =>
|
|
@@ -136,17 +135,17 @@ const iconInstallCommand = (settings: DeploySettings, source: string, rel: strin
|
|
|
136
135
|
);
|
|
137
136
|
|
|
138
137
|
const iconInstallCommands = (settings: DeploySettings): string[] => {
|
|
139
|
-
const
|
|
138
|
+
const source = settings.paths.applicationIcon;
|
|
140
139
|
|
|
141
|
-
if (
|
|
142
|
-
return [iconInstallCommand(settings,
|
|
140
|
+
if (source.kind === "file") {
|
|
141
|
+
return [iconInstallCommand(settings, source.path, iconPathFor(settings, source.path))];
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
if (
|
|
144
|
+
if (source.kind === "none") {
|
|
146
145
|
return [];
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
return listFilesRecursive(
|
|
148
|
+
return listFilesRecursive(source.path).map((icon) => iconInstallCommand(settings, icon.absPath, icon.rel));
|
|
150
149
|
};
|
|
151
150
|
|
|
152
151
|
const assertInsideProject = (settings: DeploySettings, installed: InstalledFile): void => {
|
package/src/deploy/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Config } from "@gtkx/config";
|
|
2
|
+
import type { ResolvedApplicationIcon } from "../internal/icon-path.js";
|
|
2
3
|
|
|
3
4
|
type DeployConfig = NonNullable<Config["deploy"]>;
|
|
4
5
|
type DeployTargetName = NonNullable<DeployConfig["targets"]>[number];
|
|
@@ -21,7 +22,7 @@ type NodeRuntime = {
|
|
|
21
22
|
path: string;
|
|
22
23
|
licenseFile: string | null;
|
|
23
24
|
version: string;
|
|
24
|
-
|
|
25
|
+
glibcMinimum: string | null;
|
|
25
26
|
isStripped: boolean;
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -106,9 +107,7 @@ type DeployPaths = {
|
|
|
106
107
|
overlay: string;
|
|
107
108
|
targets: string;
|
|
108
109
|
output: string;
|
|
109
|
-
|
|
110
|
-
iconsDir: string | null;
|
|
111
|
-
iconFile: string | null;
|
|
110
|
+
applicationIcon: ResolvedApplicationIcon;
|
|
112
111
|
licenseFile: string | null;
|
|
113
112
|
schemaFiles: string[];
|
|
114
113
|
};
|
|
@@ -145,6 +144,7 @@ type DeploySettings = {
|
|
|
145
144
|
arch: DeployArch;
|
|
146
145
|
paths: DeployPaths;
|
|
147
146
|
libraries: string[];
|
|
147
|
+
minimumLibraryVersions: Record<string, string>;
|
|
148
148
|
deploy: DeployConfig;
|
|
149
149
|
};
|
|
150
150
|
|
package/src/dev/icon-dir.ts
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { mkdirSync, symlinkSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
relativeIconPath,
|
|
5
|
+
resolveApplicationIcon,
|
|
6
|
+
type ResolvedApplicationIcon,
|
|
7
|
+
} from "../internal/icon-path.js";
|
|
8
|
+
import { createRetainedStagingDir } from "../internal/staging-dir.js";
|
|
3
9
|
import { prependXdgDataDir } from "../internal/xdg-data-dirs.js";
|
|
4
10
|
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
7
|
-
|
|
11
|
+
const stageIconSource = (shareDir: string, applicationId: string, source: ResolvedApplicationIcon): void => {
|
|
12
|
+
if (source.kind === "theme") {
|
|
13
|
+
symlinkSync(source.path, join(shareDir, "icons"), "dir");
|
|
14
|
+
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (source.kind === "none") {
|
|
19
|
+
return;
|
|
8
20
|
}
|
|
9
21
|
|
|
10
|
-
const
|
|
22
|
+
const target = join(shareDir, "icons", relativeIconPath(applicationId, source.path));
|
|
23
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
24
|
+
symlinkSync(source.path, target, "file");
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const prepareDevIconDir = (
|
|
28
|
+
root: string,
|
|
29
|
+
applicationId: string,
|
|
30
|
+
configured: string | undefined,
|
|
31
|
+
): string | null => {
|
|
32
|
+
const source = resolveApplicationIcon(root, applicationId, configured);
|
|
11
33
|
|
|
12
|
-
if (
|
|
34
|
+
if (source.kind === "none") {
|
|
13
35
|
return null;
|
|
14
36
|
}
|
|
15
37
|
|
|
38
|
+
const shareDir = createRetainedStagingDir("icons").retain();
|
|
39
|
+
stageIconSource(shareDir, applicationId, source);
|
|
16
40
|
process.env.XDG_DATA_DIRS = prependXdgDataDir(shareDir, process.env.XDG_DATA_DIRS);
|
|
17
41
|
|
|
18
42
|
return shareDir;
|
package/src/dev/runner-deps.ts
CHANGED
|
@@ -86,7 +86,11 @@ const defaultDevRunnerDeps = (): DevRunnerDeps => ({
|
|
|
86
86
|
isRefreshBoundary,
|
|
87
87
|
staleExportName,
|
|
88
88
|
readFileRevision,
|
|
89
|
-
plugins: () => [
|
|
89
|
+
plugins: (entryPath) => [
|
|
90
|
+
...gtkxVitePlugins(DEV_MODE, entryPath),
|
|
91
|
+
...gtkxFastRefresh(),
|
|
92
|
+
gtkxReactDomPrebundle(),
|
|
93
|
+
],
|
|
90
94
|
log: info,
|
|
91
95
|
exit: (code: number): never => process.exit(code),
|
|
92
96
|
});
|
package/src/dev/runner-main.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { loadConfig } from "@gtkx/config";
|
|
1
2
|
import { error } from "@gtkx/utils";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
|
-
import { resolveDataDir } from "../internal/data-dir.js";
|
|
4
4
|
import { DEV_ENTRY_ENV } from "./entry-env.js";
|
|
5
5
|
import { prepareDevIconDir } from "./icon-dir.js";
|
|
6
6
|
import { createDevRunner } from "./runner.js";
|
|
@@ -21,9 +21,9 @@ const main = async (): Promise<void> => {
|
|
|
21
21
|
process.exit(1);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
prepareDevSchemaDir(
|
|
26
|
-
prepareDevIconDir(
|
|
24
|
+
const { config, root } = await loadConfig(cwd, { mode: "development" });
|
|
25
|
+
prepareDevSchemaDir(root, config.future?.v2ResourceImports === true);
|
|
26
|
+
prepareDevIconDir(root, config.applicationId, config.applicationIcon);
|
|
27
27
|
const entryPath = resolve(cwd, entryArg);
|
|
28
28
|
const { defaultDevRunnerDeps } = await import("./runner-deps.js");
|
|
29
29
|
const runner = createDevRunner(defaultDevRunnerDeps());
|
package/src/dev/runner.ts
CHANGED
|
@@ -32,7 +32,7 @@ type DevRunnerDeps = {
|
|
|
32
32
|
isRefreshBoundary(module: Record<string, unknown>): boolean;
|
|
33
33
|
staleExportName(previous: Record<string, unknown>, current: Record<string, unknown>): string | null;
|
|
34
34
|
readFileRevision(path: string): Promise<string>;
|
|
35
|
-
plugins(): Plugin[];
|
|
35
|
+
plugins(entryPath: string): Plugin[];
|
|
36
36
|
log(message: string): void;
|
|
37
37
|
exit(code: number): never;
|
|
38
38
|
};
|
|
@@ -602,7 +602,7 @@ const createSession = (server: DevServer, deps: DevRunnerDeps): DevSession => {
|
|
|
602
602
|
|
|
603
603
|
const createDevRunner = (deps: DevRunnerDeps): DevRunner => ({
|
|
604
604
|
async run(entryPath: string): Promise<void> {
|
|
605
|
-
const server = await deps.createServer(createDevServerConfig(process.cwd(), deps.plugins()));
|
|
605
|
+
const server = await deps.createServer(createDevServerConfig(process.cwd(), deps.plugins(entryPath)));
|
|
606
606
|
const session = createSession(server, deps);
|
|
607
607
|
deps.installShutdownHandlers(onShutdownSignal(session));
|
|
608
608
|
|
package/src/dev/schema-dir.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { resolveDataDir } from "../internal/data-dir.js";
|
|
1
2
|
import { prependSchemaDir, stageAndCompileProjectSchemas } from "../settings/schema.js";
|
|
2
3
|
|
|
3
|
-
const prepareDevSchemaDir = (root: string,
|
|
4
|
+
const prepareDevSchemaDir = (root: string, isV2ResourceImports: boolean): string | null => {
|
|
5
|
+
const dataDir = isV2ResourceImports ? null : resolveDataDir(root);
|
|
4
6
|
const dir = stageAndCompileProjectSchemas(root, dataDir);
|
|
5
7
|
|
|
6
8
|
if (dir === null) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type RecordedPackage = {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string | null;
|
|
4
|
+
dir: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type BuildManifest = {
|
|
8
|
+
generator: typeof BUILD_MANIFEST_GENERATOR;
|
|
9
|
+
formatVersion: typeof BUILD_MANIFEST_FORMAT_VERSION;
|
|
10
|
+
schemas: string[];
|
|
11
|
+
packages: RecordedPackage[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type BuildManifestCollector = {
|
|
15
|
+
schemas: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const BUILD_MANIFEST_FILENAME = "gtkx-schemas.json";
|
|
19
|
+
const BUILD_MANIFEST_GENERATOR = "gtkx-build";
|
|
20
|
+
const BUILD_MANIFEST_FORMAT_VERSION = 1;
|
|
21
|
+
|
|
22
|
+
const createBuildManifestCollector = (): BuildManifestCollector => ({ schemas: [] });
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
BUILD_MANIFEST_FILENAME,
|
|
26
|
+
BUILD_MANIFEST_FORMAT_VERSION,
|
|
27
|
+
BUILD_MANIFEST_GENERATOR,
|
|
28
|
+
createBuildManifestCollector,
|
|
29
|
+
type BuildManifest,
|
|
30
|
+
type BuildManifestCollector,
|
|
31
|
+
type RecordedPackage,
|
|
32
|
+
};
|
|
@@ -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 };
|