@gtkx/cli 1.4.0 → 1.5.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 +1 -1
- package/dist/builder.d.ts +1 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -2
- package/dist/builder.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +2 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +30 -6
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/commands/codegen.js +1 -1
- package/dist/commands/codegen.js.map +1 -1
- package/dist/deploy/freedesktop/localize.d.ts +6 -0
- package/dist/deploy/freedesktop/localize.d.ts.map +1 -0
- package/dist/deploy/freedesktop/localize.js +107 -0
- package/dist/deploy/freedesktop/localize.js.map +1 -0
- package/dist/deploy/install-hints.d.ts.map +1 -1
- package/dist/deploy/install-hints.js +5 -0
- package/dist/deploy/install-hints.js.map +1 -1
- package/dist/deploy/payload/launcher.d.ts.map +1 -1
- package/dist/deploy/payload/launcher.js +2 -0
- package/dist/deploy/payload/launcher.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts +1 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +20 -11
- 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 +87 -15
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-source.js +21 -10
- package/dist/deploy/targets/flatpak-source.js.map +1 -1
- package/dist/deploy/tools.d.ts +6 -1
- package/dist/deploy/tools.d.ts.map +1 -1
- package/dist/deploy/tools.js +26 -1
- package/dist/deploy/tools.js.map +1 -1
- package/dist/dev/locale-dir.d.ts +3 -0
- package/dist/dev/locale-dir.d.ts.map +1 -0
- package/dist/dev/locale-dir.js +17 -0
- package/dist/dev/locale-dir.js.map +1 -0
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +2 -0
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +6 -0
- package/dist/dev/runner.js.map +1 -1
- package/dist/dev/vite-dev-server.d.ts +1 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -1
- package/dist/dev/vite-dev-server.js.map +1 -1
- package/dist/i18n/binding-extractor.d.ts +30 -0
- package/dist/i18n/binding-extractor.d.ts.map +1 -0
- package/dist/i18n/binding-extractor.js +1324 -0
- package/dist/i18n/binding-extractor.js.map +1 -0
- package/dist/i18n/catalog-template.d.ts +3 -0
- package/dist/i18n/catalog-template.d.ts.map +1 -0
- package/dist/i18n/catalog-template.js +18 -0
- package/dist/i18n/catalog-template.js.map +1 -0
- package/dist/i18n/catalogs.d.ts +21 -0
- package/dist/i18n/catalogs.d.ts.map +1 -0
- package/dist/i18n/catalogs.js +139 -0
- package/dist/i18n/catalogs.js.map +1 -0
- package/dist/i18n/extractor-plugin.d.ts +8 -0
- package/dist/i18n/extractor-plugin.d.ts.map +1 -0
- package/dist/i18n/extractor-plugin.js +247 -0
- package/dist/i18n/extractor-plugin.js.map +1 -0
- package/dist/i18n/metadata-templates.d.ts +11 -0
- package/dist/i18n/metadata-templates.d.ts.map +1 -0
- package/dist/i18n/metadata-templates.js +11 -0
- package/dist/i18n/metadata-templates.js.map +1 -0
- package/dist/i18n/source-messages.d.ts +23 -0
- package/dist/i18n/source-messages.d.ts.map +1 -0
- package/dist/i18n/source-messages.js +403 -0
- package/dist/i18n/source-messages.js.map +1 -0
- package/dist/i18n/types.d.ts +10 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +108 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/internal/source-imports.d.ts +5 -1
- package/dist/internal/source-imports.d.ts.map +1 -1
- package/dist/internal/source-imports.js +23 -44
- package/dist/internal/source-imports.js.map +1 -1
- package/dist/mcp/client.d.ts +0 -1
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +11 -39
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/handlers.d.ts +2 -1
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/settings/render.d.ts +4 -1
- package/dist/settings/render.d.ts.map +1 -1
- package/dist/settings/render.js +11 -2
- package/dist/settings/render.js.map +1 -1
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +3 -1
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
- package/dist/vite-plugins/asset-imports.js +2 -12
- package/dist/vite-plugins/asset-imports.js.map +1 -1
- package/dist/vite-plugins/i18n.d.ts +5 -0
- package/dist/vite-plugins/i18n.d.ts.map +1 -0
- package/dist/vite-plugins/i18n.js +88 -0
- package/dist/vite-plugins/i18n.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +1 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +3 -1
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/resources.d.ts.map +1 -1
- package/dist/vite-plugins/resources.js +10 -55
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/self-contained.d.ts.map +1 -1
- package/dist/vite-plugins/self-contained.js +128 -135
- package/dist/vite-plugins/self-contained.js.map +1 -1
- package/package.json +10 -9
- package/src/builder.ts +3 -2
- package/src/codegen/run-codegen.ts +53 -7
- package/src/commands/codegen.ts +1 -1
- package/src/deploy/freedesktop/localize.ts +144 -0
- package/src/deploy/install-hints.ts +5 -0
- package/src/deploy/payload/launcher.ts +2 -0
- package/src/deploy/payload/stage.ts +35 -10
- package/src/deploy/run-deploy.ts +128 -13
- package/src/deploy/targets/flatpak-source.ts +33 -10
- package/src/deploy/tools.ts +35 -0
- package/src/dev/locale-dir.ts +22 -0
- package/src/dev/runner-main.ts +2 -0
- package/src/dev/runner.ts +8 -0
- package/src/dev/vite-dev-server.ts +1 -0
- package/src/i18n/binding-extractor.ts +1969 -0
- package/src/i18n/catalog-template.ts +24 -0
- package/src/i18n/catalogs.ts +216 -0
- package/src/i18n/extractor-plugin.ts +413 -0
- package/src/i18n/metadata-templates.ts +29 -0
- package/src/i18n/source-messages.ts +608 -0
- package/src/i18n/types.ts +154 -0
- package/src/internal/source-imports.ts +28 -57
- package/src/mcp/client.ts +17 -44
- package/src/mcp/handlers.ts +7 -6
- package/src/settings/render.ts +16 -2
- package/src/settings/schema.ts +3 -1
- package/src/vite-plugins/asset-imports.ts +3 -15
- package/src/vite-plugins/i18n.ts +146 -0
- package/src/vite-plugins/index.ts +3 -0
- package/src/vite-plugins/resources.ts +10 -72
- package/src/vite-plugins/self-contained.ts +150 -169
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import type { CatalogProject } from "../../i18n/catalogs.js";
|
|
5
|
+
import type { StagedMetadata } from "../payload/stage.js";
|
|
6
|
+
import { metadataTemplateFile, type MetadataTemplateKind } from "../../i18n/metadata-templates.js";
|
|
7
|
+
import { runCliTool } from "../../internal/run-cli-tool.js";
|
|
8
|
+
|
|
9
|
+
type MergeMode = "--desktop" | "--xml";
|
|
10
|
+
|
|
11
|
+
type MergeRequest = {
|
|
12
|
+
mode: MergeMode;
|
|
13
|
+
stem: string;
|
|
14
|
+
extension: string;
|
|
15
|
+
template: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type MetadataTemplate = {
|
|
19
|
+
contents: string;
|
|
20
|
+
path: string;
|
|
21
|
+
relativePath: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const metadataTemplates = (project: CatalogProject, metadata: StagedMetadata): MetadataTemplate[] => {
|
|
25
|
+
const template = (kind: MetadataTemplateKind, contents: string): MetadataTemplate => {
|
|
26
|
+
const { path, relativePath } = metadataTemplateFile(project, kind);
|
|
27
|
+
|
|
28
|
+
return { contents, path, relativePath };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
template("desktop", metadata.desktopEntry),
|
|
33
|
+
template("metainfo.xml", metadata.metainfo),
|
|
34
|
+
...(metadata.mimePackage === null ? [] : [template("mime.xml", metadata.mimePackage)]),
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const mergeCatalogs = (project: CatalogProject, workDir: string, request: MergeRequest): string => {
|
|
39
|
+
const { extension, mode, stem, template } = request;
|
|
40
|
+
let current = join(workDir, `${stem}-source${extension}`);
|
|
41
|
+
writeFileSync(current, template);
|
|
42
|
+
|
|
43
|
+
for (const [index, catalog] of project.catalogs.entries()) {
|
|
44
|
+
const output = join(workDir, `${stem}-${String(index)}${extension}`);
|
|
45
|
+
|
|
46
|
+
runCliTool({
|
|
47
|
+
tool: "msgfmt",
|
|
48
|
+
args: [
|
|
49
|
+
mode,
|
|
50
|
+
`--template=${current}`,
|
|
51
|
+
`--locale=${catalog.locale}`,
|
|
52
|
+
`--output-file=${output}`,
|
|
53
|
+
catalog.path,
|
|
54
|
+
],
|
|
55
|
+
target: catalog.path,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
current = output;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return readFileSync(current, "utf8");
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const localizeMetadata = (metadata: StagedMetadata, project: CatalogProject | null): StagedMetadata => {
|
|
65
|
+
if (project === null || project.catalogs.length === 0) {
|
|
66
|
+
return metadata;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const workDir = mkdtempSync(join(tmpdir(), "gtkx-deploy-metadata-"));
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
return {
|
|
73
|
+
desktopEntry: mergeCatalogs(project, workDir, {
|
|
74
|
+
mode: "--desktop",
|
|
75
|
+
stem: "application",
|
|
76
|
+
extension: ".desktop",
|
|
77
|
+
template: metadata.desktopEntry,
|
|
78
|
+
}),
|
|
79
|
+
metainfo: mergeCatalogs(project, workDir, {
|
|
80
|
+
mode: "--xml",
|
|
81
|
+
stem: "metainfo",
|
|
82
|
+
extension: ".metainfo.xml",
|
|
83
|
+
template: metadata.metainfo,
|
|
84
|
+
}),
|
|
85
|
+
mimePackage: metadata.mimePackage === null
|
|
86
|
+
? null
|
|
87
|
+
: mergeCatalogs(project, workDir, {
|
|
88
|
+
mode: "--xml",
|
|
89
|
+
stem: "mime",
|
|
90
|
+
extension: ".xml",
|
|
91
|
+
template: metadata.mimePackage,
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
} finally {
|
|
95
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const extractMetadataMessages = (
|
|
100
|
+
metadata: StagedMetadata,
|
|
101
|
+
project: CatalogProject,
|
|
102
|
+
catalogTemplate?: string,
|
|
103
|
+
): void => {
|
|
104
|
+
const potPath = catalogTemplate ?? join(project.poDir, `${project.domain}.pot`);
|
|
105
|
+
|
|
106
|
+
if (!existsSync(potPath)) {
|
|
107
|
+
throw new Error(`Cannot add the generated metadata to the missing catalog template: ${potPath}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const templates = metadataTemplates(project, metadata);
|
|
111
|
+
const firstTemplate = templates[0];
|
|
112
|
+
|
|
113
|
+
if (firstTemplate === undefined) {
|
|
114
|
+
throw new Error("Cannot extract metadata messages without a generated template");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const metadataDir = dirname(firstTemplate.path);
|
|
118
|
+
mkdirSync(metadataDir, { recursive: true });
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
for (const template of templates) {
|
|
122
|
+
writeFileSync(template.path, template.contents);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
runCliTool({
|
|
126
|
+
tool: "xgettext",
|
|
127
|
+
args: [
|
|
128
|
+
"--join-existing",
|
|
129
|
+
"--force-po",
|
|
130
|
+
"--from-code=UTF-8",
|
|
131
|
+
`--output=${potPath}`,
|
|
132
|
+
...templates.map((template) => template.relativePath),
|
|
133
|
+
],
|
|
134
|
+
target: potPath,
|
|
135
|
+
options: { cwd: project.root },
|
|
136
|
+
});
|
|
137
|
+
} finally {
|
|
138
|
+
for (const template of templates) {
|
|
139
|
+
rmSync(template.path, { force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export { extractMetadataMessages, localizeMetadata };
|
|
@@ -43,8 +43,13 @@ const PACKAGE_FOR_TOOL: Record<string, FamilyPackages> = {
|
|
|
43
43
|
fedora: "flatpak-builder",
|
|
44
44
|
suse: "flatpak-builder",
|
|
45
45
|
},
|
|
46
|
+
msgfmt: { arch: "gettext", debian: "gettext", fedora: "gettext", suse: "gettext-tools" },
|
|
47
|
+
msginit: { arch: "gettext", debian: "gettext", fedora: "gettext", suse: "gettext-tools" },
|
|
48
|
+
msggrep: { arch: "gettext", debian: "gettext", fedora: "gettext", suse: "gettext-tools" },
|
|
49
|
+
msgmerge: { arch: "gettext", debian: "gettext", fedora: "gettext", suse: "gettext-tools" },
|
|
46
50
|
strip: { arch: "binutils", debian: "binutils", fedora: "binutils", suse: "binutils" },
|
|
47
51
|
tar: { arch: "tar", debian: "tar", fedora: "tar", suse: "tar" },
|
|
52
|
+
xgettext: { arch: "gettext", debian: "gettext", fedora: "gettext", suse: "gettext-tools" },
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
const GENERATOR_SOURCE = "git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node";
|
|
@@ -18,6 +18,8 @@ const renderLauncher = (settings: DeploySettings): string => {
|
|
|
18
18
|
"set -e",
|
|
19
19
|
'self=$(readlink -f "$0")',
|
|
20
20
|
'prefix=$(dirname "$(dirname "$self")")',
|
|
21
|
+
'GTKX_LOCALE_DIR="$prefix/share/locale"',
|
|
22
|
+
"export GTKX_LOCALE_DIR",
|
|
21
23
|
...(shouldUseCompileCache ? compileCacheLines(settings.binaryName) : []),
|
|
22
24
|
`exec "${libDir}/${NODE_FILENAME}" "${libDir}/${BUNDLE_FILENAME}" "$@"`,
|
|
23
25
|
"",
|
|
@@ -2,12 +2,12 @@ import { sortStringsBy } from "@gtkx/utils";
|
|
|
2
2
|
import { existsSync, rmSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import type { DeploySettings, DeployTargetName, NodeRuntime, NoticeSection, StagedFile } from "../types.js";
|
|
5
|
+
import { LOCALE_DIRNAME } from "../../i18n/catalogs.js";
|
|
5
6
|
import { BUILD_MANIFEST_FILENAME } from "../../internal/build-manifest.js";
|
|
6
7
|
import { listFilesRecursive } from "../../internal/list-files.js";
|
|
7
8
|
import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
|
|
8
9
|
import { renderCopyright } from "../freedesktop/copyright.js";
|
|
9
10
|
import { renderDbusService } from "../freedesktop/dbus-service.js";
|
|
10
|
-
import { renderDesktopEntry } from "../freedesktop/desktop-entry.js";
|
|
11
11
|
import { renderNotices } from "../notices/render.js";
|
|
12
12
|
import { copyInto, EXECUTABLE_MODE, executableModeFor, writeInto } from "./copy-tree.js";
|
|
13
13
|
import { stageIcons } from "./icons.js";
|
|
@@ -36,6 +36,7 @@ const SHARE_MIME_PACKAGES = "share/mime/packages";
|
|
|
36
36
|
const SHARE_DOC = "share/doc";
|
|
37
37
|
const SHARE_LICENSES = "share/licenses";
|
|
38
38
|
const SHARE_DBUS_SERVICES = "share/dbus-1/services";
|
|
39
|
+
const SHARE_LOCALE = `share/${LOCALE_DIRNAME}`;
|
|
39
40
|
|
|
40
41
|
const PREFIX_FOR: Record<DeployTargetName, string> = {
|
|
41
42
|
appimage: "/usr",
|
|
@@ -53,6 +54,8 @@ const isIconAsset = (rel: string): boolean => rel === DIST_ICONS_DIR || rel.star
|
|
|
53
54
|
const isBuildMetadata = (rel: string): boolean =>
|
|
54
55
|
rel === BUILD_MANIFEST_FILENAME || rel === LEGACY_PACKAGES_MANIFEST_FILENAME;
|
|
55
56
|
|
|
57
|
+
const isLocaleAsset = (rel: string): boolean => rel === LOCALE_DIRNAME || rel.startsWith(`${LOCALE_DIRNAME}/`);
|
|
58
|
+
|
|
56
59
|
const nodeLicenseDestination = (settings: DeploySettings): string =>
|
|
57
60
|
`${licenseDirFor(settings)}/${NODE_FILENAME}/${NODE_LICENSE_FILENAME}`;
|
|
58
61
|
|
|
@@ -64,10 +67,14 @@ const stageRuntimeFiles = (settings: DeploySettings, root: string): StagedFile[]
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
return listFilesRecursive(dist)
|
|
67
|
-
.filter((file) => !isIconAsset(file.rel) && !isBuildMetadata(file.rel))
|
|
70
|
+
.filter((file) => !isIconAsset(file.rel) && !isBuildMetadata(file.rel) && !isLocaleAsset(file.rel))
|
|
68
71
|
.map((file) => copyInto(root, join(libDirFor(settings), file.rel), file.absPath));
|
|
69
72
|
};
|
|
70
73
|
|
|
74
|
+
const stageCatalogs = (settings: DeploySettings, root: string): StagedFile[] =>
|
|
75
|
+
listFilesRecursive(join(settings.paths.dist, LOCALE_DIRNAME))
|
|
76
|
+
.map((file) => copyInto(root, join(SHARE_LOCALE, file.rel), file.absPath));
|
|
77
|
+
|
|
71
78
|
const stageNodeBinary = (settings: DeploySettings, root: string, node: NodeRuntime | null): StagedFile[] =>
|
|
72
79
|
node === null ? [] : [copyInto(root, join(libDirFor(settings), NODE_FILENAME), node.path, EXECUTABLE_MODE)];
|
|
73
80
|
|
|
@@ -104,6 +111,7 @@ const stagePayload = ({ settings, node, metadata }: StageRequest): StagedFile[]
|
|
|
104
111
|
writeInto(root, join("bin", settings.binaryName), renderLauncher(settings), EXECUTABLE_MODE),
|
|
105
112
|
...stageNodeBinary(settings, root, node),
|
|
106
113
|
...stageRuntimeFiles(settings, root),
|
|
114
|
+
...stageCatalogs(settings, root),
|
|
107
115
|
...stageMetadata(settings, root, metadata),
|
|
108
116
|
...stageIcons(settings, root),
|
|
109
117
|
...stageSchemas(settings, root),
|
|
@@ -111,7 +119,18 @@ const stagePayload = ({ settings, node, metadata }: StageRequest): StagedFile[]
|
|
|
111
119
|
]);
|
|
112
120
|
};
|
|
113
121
|
|
|
114
|
-
const
|
|
122
|
+
const withoutDbusActivation = (desktopEntry: string): string =>
|
|
123
|
+
desktopEntry
|
|
124
|
+
.split("\n")
|
|
125
|
+
.filter((line) => line !== "DBusActivatable=true")
|
|
126
|
+
.join("\n");
|
|
127
|
+
|
|
128
|
+
const stageActivation = (
|
|
129
|
+
settings: DeploySettings,
|
|
130
|
+
root: string,
|
|
131
|
+
target: DeployTargetName,
|
|
132
|
+
metadata: StagedMetadata,
|
|
133
|
+
): StagedFile[] => {
|
|
115
134
|
if (!settings.isDbusActivatable) {
|
|
116
135
|
return [];
|
|
117
136
|
}
|
|
@@ -120,7 +139,7 @@ const stageActivation = (settings: DeploySettings, root: string, target: DeployT
|
|
|
120
139
|
return [writeInto(
|
|
121
140
|
root,
|
|
122
141
|
join(SHARE_APPLICATIONS, `${settings.applicationId}.desktop`),
|
|
123
|
-
|
|
142
|
+
withoutDbusActivation(metadata.desktopEntry),
|
|
124
143
|
)];
|
|
125
144
|
}
|
|
126
145
|
|
|
@@ -131,11 +150,16 @@ const stageActivation = (settings: DeploySettings, root: string, target: DeployT
|
|
|
131
150
|
)];
|
|
132
151
|
};
|
|
133
152
|
|
|
134
|
-
const stageOverlay = (
|
|
153
|
+
const stageOverlay = (
|
|
154
|
+
settings: DeploySettings,
|
|
155
|
+
target: DeployTargetName,
|
|
156
|
+
notices: NoticeSection[],
|
|
157
|
+
metadata: StagedMetadata,
|
|
158
|
+
): StagedFile[] => {
|
|
135
159
|
const root = join(settings.paths.overlay, target);
|
|
136
160
|
rmSync(root, { recursive: true, force: true, maxRetries: 5 });
|
|
137
161
|
const licenseFile = settings.paths.licenseFile;
|
|
138
|
-
const activation = stageActivation(settings, root, target);
|
|
162
|
+
const activation = stageActivation(settings, root, target, metadata);
|
|
139
163
|
|
|
140
164
|
if (target === "deb") {
|
|
141
165
|
return [
|
|
@@ -154,11 +178,12 @@ const stageOverlay = (settings: DeploySettings, target: DeployTargetName, notice
|
|
|
154
178
|
const stageOverlays = (
|
|
155
179
|
settings: DeploySettings,
|
|
156
180
|
notices: NoticeSection[],
|
|
181
|
+
metadata: StagedMetadata,
|
|
157
182
|
): Record<DeployTargetName, StagedFile[]> => ({
|
|
158
|
-
appimage: stageOverlay(settings, "appimage", notices),
|
|
159
|
-
deb: stageOverlay(settings, "deb", notices),
|
|
160
|
-
flatpak: stageOverlay(settings, "flatpak", notices),
|
|
161
|
-
rpm: stageOverlay(settings, "rpm", notices),
|
|
183
|
+
appimage: stageOverlay(settings, "appimage", notices, metadata),
|
|
184
|
+
deb: stageOverlay(settings, "deb", notices, metadata),
|
|
185
|
+
flatpak: stageOverlay(settings, "flatpak", notices, metadata),
|
|
186
|
+
rpm: stageOverlay(settings, "rpm", notices, metadata),
|
|
162
187
|
});
|
|
163
188
|
|
|
164
189
|
export {
|
package/src/deploy/run-deploy.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "@gtkx/config";
|
|
2
2
|
import { info, warn } from "@gtkx/utils";
|
|
3
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join, relative } from "node:path";
|
|
5
5
|
import type {
|
|
6
6
|
DeployArtifact,
|
|
@@ -12,7 +12,19 @@ import type {
|
|
|
12
12
|
} from "./types.js";
|
|
13
13
|
import { build as buildApp } from "../builder.js";
|
|
14
14
|
import { ensureGenerated } from "../codegen/run-codegen.js";
|
|
15
|
+
import { replaceCatalogTemplate } from "../i18n/catalog-template.js";
|
|
16
|
+
import {
|
|
17
|
+
type CatalogProject,
|
|
18
|
+
compileCatalogs,
|
|
19
|
+
LOCALE_DIRNAME,
|
|
20
|
+
requiresCatalogInitialization,
|
|
21
|
+
resolveCatalogProject,
|
|
22
|
+
synchronizeCatalogs,
|
|
23
|
+
} from "../i18n/catalogs.js";
|
|
24
|
+
import { extractSourceCatalogTo } from "../i18n/source-messages.js";
|
|
25
|
+
import { discoverSourceFiles } from "../internal/source-imports.js";
|
|
15
26
|
import { renderDesktopEntry } from "./freedesktop/desktop-entry.js";
|
|
27
|
+
import { extractMetadataMessages, localizeMetadata } from "./freedesktop/localize.js";
|
|
16
28
|
import { renderMetainfo } from "./freedesktop/metainfo.js";
|
|
17
29
|
import { renderMimePackage } from "./freedesktop/mime-package.js";
|
|
18
30
|
import { validateDesktopEntry, validateMetainfo } from "./freedesktop/validate.js";
|
|
@@ -32,10 +44,15 @@ import {
|
|
|
32
44
|
DESKTOP_FILE_VALIDATE,
|
|
33
45
|
FLATPAK_NODE_GENERATOR,
|
|
34
46
|
FLATPAK_NODE_GENERATOR_PNPM,
|
|
47
|
+
MSGFMT,
|
|
48
|
+
MSGGREP,
|
|
49
|
+
MSGINIT,
|
|
50
|
+
MSGMERGE,
|
|
35
51
|
probeTools,
|
|
36
52
|
STRIP,
|
|
37
53
|
TAR,
|
|
38
54
|
warnMissingOptional,
|
|
55
|
+
XGETTEXT,
|
|
39
56
|
} from "./tools.js";
|
|
40
57
|
|
|
41
58
|
type DeployOptions = {
|
|
@@ -47,6 +64,14 @@ type DeployOptions = {
|
|
|
47
64
|
shouldSkipBuild: boolean;
|
|
48
65
|
};
|
|
49
66
|
|
|
67
|
+
type PreflightRequest = {
|
|
68
|
+
targets: DeployTarget[];
|
|
69
|
+
settings: DeploySettings;
|
|
70
|
+
project: CatalogProject | null;
|
|
71
|
+
shouldPrintManifests: boolean;
|
|
72
|
+
shouldSkipBuild: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
50
75
|
const BUILD_MODE = "production";
|
|
51
76
|
const BYTES_PER_MIB = 1024 * 1024;
|
|
52
77
|
const WEBKIT_LIBRARY = "WebKit-6.0";
|
|
@@ -198,12 +223,50 @@ const runtimeToolsFor = (targets: DeployTarget[], settings: DeploySettings): Dep
|
|
|
198
223
|
return node.shouldStrip === false ? archiveTools : [...archiveTools, STRIP];
|
|
199
224
|
};
|
|
200
225
|
|
|
201
|
-
const
|
|
226
|
+
const mutableCatalogTools = (project: CatalogProject): DeployTool[] => {
|
|
227
|
+
if (project.catalogs.length === 0) {
|
|
228
|
+
return [MSGGREP, XGETTEXT];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return [
|
|
232
|
+
MSGFMT,
|
|
233
|
+
MSGGREP,
|
|
234
|
+
...(requiresCatalogInitialization(project) ? [MSGINIT] : []),
|
|
235
|
+
MSGMERGE,
|
|
236
|
+
XGETTEXT,
|
|
237
|
+
];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const catalogTools = (project: CatalogProject | null, shouldSkipBuild: boolean): DeployTool[] => {
|
|
241
|
+
if (project === null) {
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!shouldSkipBuild) {
|
|
246
|
+
return mutableCatalogTools(project);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return project.catalogs.length === 0 ? [] : [MSGFMT];
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const preflight = ({
|
|
253
|
+
targets,
|
|
254
|
+
settings,
|
|
255
|
+
project,
|
|
256
|
+
shouldPrintManifests,
|
|
257
|
+
shouldSkipBuild,
|
|
258
|
+
}: PreflightRequest): void => {
|
|
202
259
|
const packagerTools = shouldPrintManifests
|
|
203
260
|
? []
|
|
204
261
|
: [...runtimeToolsFor(targets, settings), ...targets.flatMap((target) => target.tools)];
|
|
205
262
|
|
|
206
|
-
const required = [
|
|
263
|
+
const required = [
|
|
264
|
+
DESKTOP_FILE_VALIDATE,
|
|
265
|
+
APPSTREAMCLI,
|
|
266
|
+
...catalogTools(project, shouldSkipBuild),
|
|
267
|
+
...sourceModeTools(targets, settings),
|
|
268
|
+
];
|
|
269
|
+
|
|
207
270
|
const report = probeTools([...required, ...packagerTools]);
|
|
208
271
|
assertTools(report);
|
|
209
272
|
warnMissingOptional(report);
|
|
@@ -229,19 +292,55 @@ const loadSettings = async (options: DeployOptions): Promise<DeploySettings> =>
|
|
|
229
292
|
return resolveDeploySettings({ root, config, outDirOverride: options.outDir });
|
|
230
293
|
};
|
|
231
294
|
|
|
295
|
+
const synchronizeMetadataCatalogs = async (
|
|
296
|
+
settings: DeploySettings,
|
|
297
|
+
templates: StagedMetadata,
|
|
298
|
+
project: CatalogProject | null,
|
|
299
|
+
): Promise<void> => {
|
|
300
|
+
if (project === null) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const srcDir = join(project.root, "src");
|
|
305
|
+
const sourceFiles = discoverSourceFiles(existsSync(srcDir) ? srcDir : project.root);
|
|
306
|
+
const stagingDir = mkdtempSync(join(project.poDir, ".gtkx-deploy-i18n-"));
|
|
307
|
+
const stagedTemplate = join(stagingDir, `${project.domain}.pot`);
|
|
308
|
+
const template = join(project.poDir, `${project.domain}.pot`);
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
await extractSourceCatalogTo(project, sourceFiles, stagedTemplate);
|
|
312
|
+
extractMetadataMessages(templates, project, stagedTemplate);
|
|
313
|
+
replaceCatalogTemplate(stagedTemplate, template);
|
|
314
|
+
synchronizeCatalogs(project);
|
|
315
|
+
compileCatalogs(project, join(settings.paths.dist, LOCALE_DIRNAME));
|
|
316
|
+
} finally {
|
|
317
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
232
321
|
const buildPayload = async (
|
|
233
322
|
options: DeployOptions,
|
|
234
323
|
settings: DeploySettings,
|
|
235
324
|
targets: DeployTarget[],
|
|
325
|
+
project: CatalogProject | null,
|
|
236
326
|
): Promise<DeployPayload> => {
|
|
237
|
-
const
|
|
238
|
-
validateMetadata(settings, metadata, isFlathubSubmission(settings, targets));
|
|
327
|
+
const templates = renderMetadata(settings);
|
|
239
328
|
|
|
240
329
|
if (!options.shouldSkipBuild) {
|
|
241
330
|
info(`Building ${options.entry}`);
|
|
242
|
-
|
|
331
|
+
|
|
332
|
+
await buildApp({
|
|
333
|
+
entry: options.entry,
|
|
334
|
+
vite: { root: options.cwd },
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
await synchronizeMetadataCatalogs(settings, templates, project);
|
|
338
|
+
} else if (project !== null) {
|
|
339
|
+
compileCatalogs(project, join(settings.paths.dist, LOCALE_DIRNAME));
|
|
243
340
|
}
|
|
244
341
|
|
|
342
|
+
const metadata = localizeMetadata(templates, project);
|
|
343
|
+
validateMetadata(settings, metadata, isFlathubSubmission(settings, targets));
|
|
245
344
|
const buildManifest = readBuildManifest(settings);
|
|
246
345
|
|
|
247
346
|
const builtSettings: DeploySettings = {
|
|
@@ -262,7 +361,7 @@ const buildPayload = async (
|
|
|
262
361
|
node,
|
|
263
362
|
stage,
|
|
264
363
|
notices,
|
|
265
|
-
overlays: stageOverlays(builtSettings, notices),
|
|
364
|
+
overlays: stageOverlays(builtSettings, notices, metadata),
|
|
266
365
|
};
|
|
267
366
|
};
|
|
268
367
|
|
|
@@ -312,15 +411,31 @@ const announce = (settings: DeploySettings, targets: DeployTarget[]): void => {
|
|
|
312
411
|
};
|
|
313
412
|
|
|
314
413
|
const runDeploy = async (options: DeployOptions): Promise<void> => {
|
|
414
|
+
let settings = await loadSettings(options);
|
|
415
|
+
let project = resolveCatalogProject(settings.paths.root, settings.applicationId);
|
|
416
|
+
let targets = targetsFor(resolveTargetNames(options, settings));
|
|
417
|
+
announce(settings, targets);
|
|
418
|
+
|
|
419
|
+
preflight({
|
|
420
|
+
targets,
|
|
421
|
+
settings,
|
|
422
|
+
project,
|
|
423
|
+
shouldPrintManifests: options.shouldPrintManifests,
|
|
424
|
+
shouldSkipBuild: options.shouldSkipBuild,
|
|
425
|
+
});
|
|
426
|
+
|
|
315
427
|
if (!options.shouldSkipBuild) {
|
|
316
|
-
await ensureGenerated(options.cwd, {
|
|
428
|
+
await ensureGenerated(options.cwd, {
|
|
429
|
+
shouldAnnounce: true,
|
|
430
|
+
mode: BUILD_MODE,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
settings = await loadSettings(options);
|
|
434
|
+
project = resolveCatalogProject(settings.paths.root, settings.applicationId);
|
|
435
|
+
targets = targetsFor(resolveTargetNames(options, settings));
|
|
317
436
|
}
|
|
318
437
|
|
|
319
|
-
const
|
|
320
|
-
const targets = targetsFor(resolveTargetNames(options, settings));
|
|
321
|
-
announce(settings, targets);
|
|
322
|
-
preflight(targets, settings, options.shouldPrintManifests);
|
|
323
|
-
const payload = await buildPayload(options, settings, targets);
|
|
438
|
+
const payload = await buildPayload(options, settings, targets, project);
|
|
324
439
|
const rendered = renderTargetManifests(targets, payload);
|
|
325
440
|
|
|
326
441
|
if (options.shouldPrintManifests) {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { posix, relative, resolve } from "node:path";
|
|
3
3
|
import type { DeployPayload, DeploySettings } from "../types.js";
|
|
4
|
+
import { LOCALE_DIRNAME } from "../../i18n/catalogs.js";
|
|
4
5
|
import { listFilesRecursive } from "../../internal/list-files.js";
|
|
5
6
|
import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
|
|
6
7
|
import { renderDbusService } from "../freedesktop/dbus-service.js";
|
|
7
|
-
import { renderDesktopEntry } from "../freedesktop/desktop-entry.js";
|
|
8
|
-
import { renderMetainfo } from "../freedesktop/metainfo.js";
|
|
9
|
-
import { renderMimePackage } from "../freedesktop/mime-package.js";
|
|
10
8
|
import { optional } from "../nfpm/optional.js";
|
|
11
9
|
import { renderNotices } from "../notices/render.js";
|
|
12
10
|
import { executableModeFor } from "../payload/copy-tree.js";
|
|
@@ -60,6 +58,7 @@ const installCommand = (source: string, destination: string, mode: string): stri
|
|
|
60
58
|
|
|
61
59
|
const runtimeInstallCommands = (settings: DeploySettings, nodeExtensionPath: string): string[] => {
|
|
62
60
|
const lib = `${DESTINATION}/lib/${settings.binaryName}`;
|
|
61
|
+
const locale = `${DESTINATION}/share/${LOCALE_DIRNAME}`;
|
|
63
62
|
|
|
64
63
|
return [
|
|
65
64
|
installCommand(`${nodeExtensionPath}/bin/node`, `${lib}/node`, "m755"),
|
|
@@ -69,6 +68,7 @@ const runtimeInstallCommands = (settings: DeploySettings, nodeExtensionPath: str
|
|
|
69
68
|
"test ! -f dist/gschemas.compiled || " +
|
|
70
69
|
installCommand("dist/gschemas.compiled", `${lib}/gschemas.compiled`, "m644"),
|
|
71
70
|
`test ! -d dist/assets || cp -a dist/assets ${lib}/assets`,
|
|
71
|
+
`test ! -d dist/${LOCALE_DIRNAME} || { mkdir -p ${locale} && cp -a dist/${LOCALE_DIRNAME}/. ${locale}/; }`,
|
|
72
72
|
installCommand(LAUNCHER_FILENAME, `${DESTINATION}/bin/${settings.binaryName}`, "m755"),
|
|
73
73
|
];
|
|
74
74
|
};
|
|
@@ -87,10 +87,35 @@ const activationInstallCommands = (settings: DeploySettings): string[] =>
|
|
|
87
87
|
)]
|
|
88
88
|
: [];
|
|
89
89
|
|
|
90
|
-
const
|
|
91
|
-
const
|
|
90
|
+
const stagedContents = (payload: DeployPayload, destination: string): string => {
|
|
91
|
+
const file = payload.stage.find((candidate) => candidate.rel === destination);
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
if (file === undefined) {
|
|
94
|
+
throw new Error(`Cannot render the source Flatpak: ${destination} was not staged`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return readFileSync(file.abs, "utf8");
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const stagedMetadataSources = (payload: DeployPayload): FlatpakModule[] => {
|
|
101
|
+
const settings = payload.settings;
|
|
102
|
+
|
|
103
|
+
return [
|
|
104
|
+
inlineSource(
|
|
105
|
+
`${settings.applicationId}.desktop`,
|
|
106
|
+
stagedContents(payload, posix.join("share", "applications", `${settings.applicationId}.desktop`)),
|
|
107
|
+
),
|
|
108
|
+
inlineSource(
|
|
109
|
+
`${settings.applicationId}.metainfo.xml`,
|
|
110
|
+
stagedContents(payload, posix.join("share", "metainfo", `${settings.applicationId}.metainfo.xml`)),
|
|
111
|
+
),
|
|
112
|
+
...(settings.fileAssociations.length === 0
|
|
113
|
+
? []
|
|
114
|
+
: [inlineSource(
|
|
115
|
+
`${settings.applicationId}.xml`,
|
|
116
|
+
stagedContents(payload, posix.join("share", "mime", "packages", `${settings.applicationId}.xml`)),
|
|
117
|
+
)]),
|
|
118
|
+
];
|
|
94
119
|
};
|
|
95
120
|
|
|
96
121
|
const mimeInstallCommands = (settings: DeploySettings): string[] =>
|
|
@@ -239,9 +264,7 @@ const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
|
|
|
239
264
|
resolveGitSource(settings),
|
|
240
265
|
...(pin === null ? [] : pnpmSources(pin, moduleDirFor(settings))),
|
|
241
266
|
GENERATED_SOURCES,
|
|
242
|
-
|
|
243
|
-
inlineSource(`${settings.applicationId}.metainfo.xml`, renderMetainfo(settings)),
|
|
244
|
-
...mimeSources(settings),
|
|
267
|
+
...stagedMetadataSources(payload),
|
|
245
268
|
inlineSource(LAUNCHER_FILENAME, renderLauncher(settings)),
|
|
246
269
|
inlineSource(NOTICES_FILENAME, renderNotices(settings, payload.notices)),
|
|
247
270
|
...activationSource(settings),
|
package/src/deploy/tools.ts
CHANGED
|
@@ -56,6 +56,36 @@ const FLATPAK_NODE_GENERATOR_PNPM: DeployTool = {
|
|
|
56
56
|
isPresent: () => hasGeneratorPnpmSupport(),
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
const MSGFMT: DeployTool = {
|
|
60
|
+
command: "msgfmt",
|
|
61
|
+
purpose: "compiles the gettext catalogs and localized metadata",
|
|
62
|
+
isOptional: false,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const MSGGREP: DeployTool = {
|
|
66
|
+
command: "msggrep",
|
|
67
|
+
purpose: "retains generated metadata while refreshing the catalog template",
|
|
68
|
+
isOptional: false,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const MSGINIT: DeployTool = {
|
|
72
|
+
command: "msginit",
|
|
73
|
+
purpose: "initializes newly listed gettext catalogs",
|
|
74
|
+
isOptional: false,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const MSGMERGE: DeployTool = {
|
|
78
|
+
command: "msgmerge",
|
|
79
|
+
purpose: "synchronizes translations with the catalog template",
|
|
80
|
+
isOptional: false,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const XGETTEXT: DeployTool = {
|
|
84
|
+
command: "xgettext",
|
|
85
|
+
purpose: "extracts the gettext catalog template",
|
|
86
|
+
isOptional: false,
|
|
87
|
+
};
|
|
88
|
+
|
|
59
89
|
const STRIP: DeployTool = {
|
|
60
90
|
command: "strip",
|
|
61
91
|
purpose: "would shrink the bundled Node.js by removing its symbols",
|
|
@@ -149,8 +179,13 @@ export {
|
|
|
149
179
|
FILE_TOOL,
|
|
150
180
|
FLATPAK,
|
|
151
181
|
FLATPAK_BUILDER,
|
|
182
|
+
MSGFMT,
|
|
183
|
+
MSGGREP,
|
|
184
|
+
MSGINIT,
|
|
185
|
+
MSGMERGE,
|
|
152
186
|
probeTools,
|
|
153
187
|
STRIP,
|
|
154
188
|
TAR,
|
|
155
189
|
warnMissingOptional,
|
|
190
|
+
XGETTEXT,
|
|
156
191
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { compileCatalogs, resolveCatalogProject, synchronizeCatalogs } from "../i18n/catalogs.js";
|
|
2
|
+
import { createRetainedStagingDir } from "../internal/staging-dir.js";
|
|
3
|
+
|
|
4
|
+
const LOCALE_DIR_ENV = "GTKX_LOCALE_DIR";
|
|
5
|
+
const localeDir = createRetainedStagingDir("locale");
|
|
6
|
+
|
|
7
|
+
const prepareDevLocaleDir = (root: string, domain: string): string | null => {
|
|
8
|
+
const project = resolveCatalogProject(root, domain);
|
|
9
|
+
|
|
10
|
+
if (project === null) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const outputDir = localeDir.retain();
|
|
15
|
+
synchronizeCatalogs(project);
|
|
16
|
+
compileCatalogs(project, outputDir);
|
|
17
|
+
process.env[LOCALE_DIR_ENV] = outputDir;
|
|
18
|
+
|
|
19
|
+
return outputDir;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { prepareDevLocaleDir };
|
package/src/dev/runner-main.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { error } from "@gtkx/utils";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { DEV_ENTRY_ENV } from "./entry-env.js";
|
|
5
5
|
import { prepareDevIconDir } from "./icon-dir.js";
|
|
6
|
+
import { prepareDevLocaleDir } from "./locale-dir.js";
|
|
6
7
|
import { createDevRunner } from "./runner.js";
|
|
7
8
|
import { prepareDevSchemaDir } from "./schema-dir.js";
|
|
8
9
|
|
|
@@ -22,6 +23,7 @@ const main = async (): Promise<void> => {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const { config, root } = await loadConfig(cwd, { mode: "development" });
|
|
26
|
+
prepareDevLocaleDir(root, config.applicationId);
|
|
25
27
|
prepareDevSchemaDir(root, config.future?.v2ResourceImports === true);
|
|
26
28
|
prepareDevIconDir(root, config.applicationId, config.applicationIcon);
|
|
27
29
|
const entryPath = resolve(cwd, entryArg);
|