@gtkx/cli 2.0.0-beta.2 → 2.0.0-beta.4

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.
Files changed (144) hide show
  1. package/dist/cli.d.ts +1 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +12 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/codegen/config-dependencies.d.ts +3 -0
  6. package/dist/codegen/config-dependencies.d.ts.map +1 -0
  7. package/dist/codegen/config-dependencies.js +355 -0
  8. package/dist/codegen/config-dependencies.js.map +1 -0
  9. package/dist/codegen/reference.d.ts.map +1 -1
  10. package/dist/codegen/reference.js +1 -0
  11. package/dist/codegen/reference.js.map +1 -1
  12. package/dist/codegen/run-codegen.d.ts +3 -2
  13. package/dist/codegen/run-codegen.d.ts.map +1 -1
  14. package/dist/codegen/run-codegen.js +33 -14
  15. package/dist/codegen/run-codegen.js.map +1 -1
  16. package/dist/codegen/store-resolver.d.ts +2 -0
  17. package/dist/codegen/store-resolver.d.ts.map +1 -1
  18. package/dist/codegen/store-resolver.js +6 -3
  19. package/dist/codegen/store-resolver.js.map +1 -1
  20. package/dist/commands/cleanup.d.ts +9 -0
  21. package/dist/commands/cleanup.d.ts.map +1 -0
  22. package/dist/commands/cleanup.js +31 -0
  23. package/dist/commands/cleanup.js.map +1 -0
  24. package/dist/commands/dev.d.ts +1 -0
  25. package/dist/commands/dev.d.ts.map +1 -1
  26. package/dist/commands/dev.js +13 -4
  27. package/dist/commands/dev.js.map +1 -1
  28. package/dist/commands/docs.d.ts.map +1 -1
  29. package/dist/commands/docs.js +1 -0
  30. package/dist/commands/docs.js.map +1 -1
  31. package/dist/deploy/freedesktop/validate.d.ts.map +1 -1
  32. package/dist/deploy/freedesktop/validate.js +11 -4
  33. package/dist/deploy/freedesktop/validate.js.map +1 -1
  34. package/dist/deploy/nfpm/config.d.ts +3 -2
  35. package/dist/deploy/nfpm/config.d.ts.map +1 -1
  36. package/dist/deploy/nfpm/config.js +3 -3
  37. package/dist/deploy/nfpm/config.js.map +1 -1
  38. package/dist/deploy/node-runtime/index.d.ts.map +1 -1
  39. package/dist/deploy/node-runtime/index.js +2 -1
  40. package/dist/deploy/node-runtime/index.js.map +1 -1
  41. package/dist/deploy/payload/stage.d.ts.map +1 -1
  42. package/dist/deploy/payload/stage.js +24 -3
  43. package/dist/deploy/payload/stage.js.map +1 -1
  44. package/dist/deploy/settings/extra-files.d.ts.map +1 -1
  45. package/dist/deploy/settings/extra-files.js +4 -3
  46. package/dist/deploy/settings/extra-files.js.map +1 -1
  47. package/dist/deploy/targets/nfpm-target.d.ts.map +1 -1
  48. package/dist/deploy/targets/nfpm-target.js +12 -7
  49. package/dist/deploy/targets/nfpm-target.js.map +1 -1
  50. package/dist/dev/entry-env.d.ts +2 -1
  51. package/dist/dev/entry-env.d.ts.map +1 -1
  52. package/dist/dev/entry-env.js +2 -1
  53. package/dist/dev/entry-env.js.map +1 -1
  54. package/dist/dev/runner-deps.d.ts +1 -1
  55. package/dist/dev/runner-deps.d.ts.map +1 -1
  56. package/dist/dev/runner-deps.js +3 -3
  57. package/dist/dev/runner-deps.js.map +1 -1
  58. package/dist/dev/runner-main.d.ts.map +1 -1
  59. package/dist/dev/runner-main.js +8 -3
  60. package/dist/dev/runner-main.js.map +1 -1
  61. package/dist/dev/supervisor.d.ts +3 -1
  62. package/dist/dev/supervisor.d.ts.map +1 -1
  63. package/dist/dev/supervisor.js +124 -27
  64. package/dist/dev/supervisor.js.map +1 -1
  65. package/dist/i18n/catalogs.d.ts.map +1 -1
  66. package/dist/i18n/catalogs.js +1 -0
  67. package/dist/i18n/catalogs.js.map +1 -1
  68. package/dist/i18n/source-messages.d.ts +3 -1
  69. package/dist/i18n/source-messages.d.ts.map +1 -1
  70. package/dist/i18n/source-messages.js +167 -37
  71. package/dist/i18n/source-messages.js.map +1 -1
  72. package/dist/internal/build-output.d.ts.map +1 -1
  73. package/dist/internal/build-output.js +53 -6
  74. package/dist/internal/build-output.js.map +1 -1
  75. package/dist/internal/docs-elements.d.ts +1 -0
  76. package/dist/internal/docs-elements.d.ts.map +1 -1
  77. package/dist/internal/docs-elements.js +5 -4
  78. package/dist/internal/docs-elements.js.map +1 -1
  79. package/dist/internal/errors.d.ts +2 -1
  80. package/dist/internal/errors.d.ts.map +1 -1
  81. package/dist/internal/errors.js +1 -1
  82. package/dist/internal/errors.js.map +1 -1
  83. package/dist/internal/parent-process.d.ts +12 -0
  84. package/dist/internal/parent-process.d.ts.map +1 -0
  85. package/dist/internal/parent-process.js +74 -0
  86. package/dist/internal/parent-process.js.map +1 -0
  87. package/dist/internal/prepare-project.d.ts +1 -0
  88. package/dist/internal/prepare-project.d.ts.map +1 -1
  89. package/dist/internal/prepare-project.js +8 -2
  90. package/dist/internal/prepare-project.js.map +1 -1
  91. package/dist/vite-plugins/ensure-store.d.ts +1 -1
  92. package/dist/vite-plugins/ensure-store.d.ts.map +1 -1
  93. package/dist/vite-plugins/ensure-store.js +2 -2
  94. package/dist/vite-plugins/ensure-store.js.map +1 -1
  95. package/dist/vite-plugins/i18n.d.ts +1 -1
  96. package/dist/vite-plugins/i18n.d.ts.map +1 -1
  97. package/dist/vite-plugins/i18n.js +24 -11
  98. package/dist/vite-plugins/i18n.js.map +1 -1
  99. package/dist/vite-plugins/index.d.ts.map +1 -1
  100. package/dist/vite-plugins/index.js +3 -1
  101. package/dist/vite-plugins/index.js.map +1 -1
  102. package/dist/vite-plugins/native.d.ts.map +1 -1
  103. package/dist/vite-plugins/native.js +0 -4
  104. package/dist/vite-plugins/native.js.map +1 -1
  105. package/dist/vite-plugins/settings-worker-env.d.ts +1 -1
  106. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  107. package/dist/vite-plugins/settings-worker-env.js +2 -2
  108. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  109. package/dist/vitest-plugin.d.ts +2 -1
  110. package/dist/vitest-plugin.d.ts.map +1 -1
  111. package/dist/vitest-plugin.js +6 -6
  112. package/dist/vitest-plugin.js.map +1 -1
  113. package/package.json +12 -9
  114. package/src/cli.ts +12 -2
  115. package/src/codegen/config-dependencies.ts +470 -0
  116. package/src/codegen/reference.ts +1 -0
  117. package/src/codegen/run-codegen.ts +39 -17
  118. package/src/codegen/store-resolver.ts +8 -3
  119. package/src/commands/cleanup.ts +36 -0
  120. package/src/commands/dev.ts +26 -4
  121. package/src/commands/docs.ts +1 -0
  122. package/src/deploy/freedesktop/validate.ts +13 -4
  123. package/src/deploy/nfpm/config.ts +9 -3
  124. package/src/deploy/node-runtime/index.ts +2 -1
  125. package/src/deploy/payload/stage.ts +42 -3
  126. package/src/deploy/settings/extra-files.ts +5 -3
  127. package/src/deploy/targets/nfpm-target.ts +13 -6
  128. package/src/dev/entry-env.ts +2 -1
  129. package/src/dev/runner-deps.ts +3 -3
  130. package/src/dev/runner-main.ts +10 -3
  131. package/src/dev/supervisor.ts +164 -30
  132. package/src/i18n/catalogs.ts +1 -0
  133. package/src/i18n/source-messages.ts +240 -41
  134. package/src/internal/build-output.ts +79 -7
  135. package/src/internal/docs-elements.ts +10 -4
  136. package/src/internal/errors.ts +1 -1
  137. package/src/internal/parent-process.ts +110 -0
  138. package/src/internal/prepare-project.ts +9 -3
  139. package/src/vite-plugins/ensure-store.ts +2 -2
  140. package/src/vite-plugins/i18n.ts +30 -14
  141. package/src/vite-plugins/index.ts +8 -1
  142. package/src/vite-plugins/native.ts +0 -5
  143. package/src/vite-plugins/settings-worker-env.ts +2 -2
  144. package/src/vitest-plugin.ts +6 -6
@@ -0,0 +1,110 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ type ProcessIdentity = {
4
+ pid: number;
5
+ parentId: number;
6
+ processGroupId: number;
7
+ startTime: string;
8
+ state: string;
9
+ };
10
+
11
+ type ProcessGroupOwner = Pick<ProcessIdentity, "pid" | "startTime">;
12
+ type ProcessGroupOwnerCapture = { isCaptured: true; owner: ProcessGroupOwner | undefined } |
13
+ { isCaptured: false; error: Error };
14
+
15
+ const initialParentId = process.ppid;
16
+ const STOPPED_PROCESS_STATES: Set<string> = new Set(["Z", "X", "x"]);
17
+
18
+ const processIdentity = (pid: number): ProcessIdentity => {
19
+ const stat = readFileSync(`/proc/${String(pid)}/stat`, "utf8");
20
+ const fields = stat.slice(stat.lastIndexOf(") ") + 2).split(" ");
21
+ const state = fields[0];
22
+ const parentId = Number(fields[1]);
23
+ const processGroupId = Number(fields[2]);
24
+ const startTime = fields[19];
25
+
26
+ if (
27
+ state === undefined ||
28
+ startTime === undefined ||
29
+ !Number.isSafeInteger(parentId) ||
30
+ !Number.isSafeInteger(processGroupId)
31
+ ) {
32
+ throw new Error(`Failed to identify process ${String(pid)}`);
33
+ }
34
+
35
+ return { pid, parentId, processGroupId, startTime, state };
36
+ };
37
+
38
+ const ancestorFor = (process: ProcessIdentity, ancestorId: number): ProcessIdentity | undefined => {
39
+ let current = process;
40
+
41
+ while (current.parentId > 1) {
42
+ const parent = processIdentity(current.parentId);
43
+
44
+ if (parent.pid === ancestorId) {
45
+ return parent;
46
+ }
47
+
48
+ current = parent;
49
+ }
50
+
51
+ return undefined;
52
+ };
53
+
54
+ const processGroupOwner = (process: ProcessIdentity): ProcessGroupOwner => {
55
+ if (process.processGroupId !== process.pid || STOPPED_PROCESS_STATES.has(process.state)) {
56
+ throw new Error(`Failed to identify process group ${String(process.pid)}`);
57
+ }
58
+
59
+ return { pid: process.pid, startTime: process.startTime };
60
+ };
61
+
62
+ const processGroupOwnerFor = (process: ProcessIdentity): ProcessGroupOwner | undefined => {
63
+ if (process.processGroupId === process.pid) {
64
+ return undefined;
65
+ }
66
+
67
+ const groupLeader = processGroupOwner(processIdentity(process.processGroupId));
68
+ const owner = ancestorFor(process, groupLeader.pid);
69
+
70
+ if (owner !== undefined) {
71
+ return groupLeader;
72
+ }
73
+
74
+ const currentGroupLeader = processGroupOwner(processIdentity(groupLeader.pid));
75
+
76
+ if (currentGroupLeader.startTime !== groupLeader.startTime) {
77
+ throw new Error(`Failed to identify process group ${String(groupLeader.pid)}`);
78
+ }
79
+
80
+ return undefined;
81
+ };
82
+
83
+ const captureProcessGroupOwner = (): ProcessGroupOwnerCapture => {
84
+ try {
85
+ const initialProcess = processIdentity(process.pid);
86
+
87
+ if (initialProcess.parentId !== initialParentId) {
88
+ throw new Error("The process that launched GTKX exited during startup");
89
+ }
90
+
91
+ return { isCaptured: true, owner: processGroupOwnerFor(initialProcess) };
92
+ } catch (error) {
93
+ return {
94
+ isCaptured: false,
95
+ error: error instanceof Error ? error : new Error("Failed to identify the process that launched GTKX"),
96
+ };
97
+ }
98
+ };
99
+
100
+ const capturedProcessGroupOwner = captureProcessGroupOwner();
101
+
102
+ const getInitialProcessGroupOwner = (): ProcessGroupOwner | undefined => {
103
+ if (!capturedProcessGroupOwner.isCaptured) {
104
+ throw capturedProcessGroupOwner.error;
105
+ }
106
+
107
+ return capturedProcessGroupOwner.owner;
108
+ };
109
+
110
+ export { getInitialProcessGroupOwner, initialParentId };
@@ -3,21 +3,27 @@ import { type CodegenContext, resolveCodegenContext } from "../codegen/store-res
3
3
  import { resolveCwd, resolveEntry } from "./entry-arg.js";
4
4
 
5
5
  type ProjectArgs = { entry?: string; cwd?: string; config?: string };
6
- type PreparedProject = { cwd: string; entry: string; configFile: string };
6
+ type PreparedProject = { cwd: string; entry: string; configFile: string; configDependencies: string[] };
7
7
  type ResolvedProject = PreparedProject & { context: CodegenContext };
8
8
 
9
9
  const resolveProject = async (args: ProjectArgs, mode: string): Promise<ResolvedProject> => {
10
10
  const cwd = resolveCwd(args);
11
11
  const context = await resolveCodegenContext(cwd, mode, args.config);
12
12
 
13
- return { cwd, entry: resolveEntry(cwd, args.entry), configFile: context.configFile, context };
13
+ return {
14
+ cwd,
15
+ entry: resolveEntry(cwd, args.entry),
16
+ configFile: context.configFile,
17
+ configDependencies: context.configDependencies,
18
+ context,
19
+ };
14
20
  };
15
21
 
16
22
  const prepareProject = async (args: ProjectArgs, mode: string): Promise<PreparedProject> => {
17
23
  const { cwd, entry, context } = await resolveProject(args, mode);
18
24
  await ensureGeneratedIn(context, { shouldAnnounce: true, mode });
19
25
 
20
- return { cwd, entry, configFile: context.configFile };
26
+ return { cwd, entry, configFile: context.configFile, configDependencies: context.configDependencies };
21
27
  };
22
28
 
23
29
  export { prepareProject, resolveProject };
@@ -3,11 +3,11 @@ import { ensureGenerated } from "../codegen/run-codegen.js";
3
3
 
4
4
  const TEST_MODE = "test";
5
5
 
6
- const gtkxEnsureStore = (): Plugin => ({
6
+ const gtkxEnsureStore = (configFile?: string): Plugin => ({
7
7
  name: "gtkx:ensure-store",
8
8
 
9
9
  async config() {
10
- await ensureGenerated(process.cwd(), { shouldAnnounce: true, mode: TEST_MODE });
10
+ await ensureGenerated(process.cwd(), { shouldAnnounce: true, mode: TEST_MODE, configFile });
11
11
  },
12
12
  });
13
13
 
@@ -1,7 +1,7 @@
1
1
  import type { ConfigLoader } from "@gtkx/config";
2
2
  import type { Plugin, ResolvedConfig } from "vite";
3
3
  import { createConfigLoader } from "@gtkx/config/internal";
4
- import { isPathWithin, toPosixPath } from "@gtkx/utils";
4
+ import { isPathWithin, logger, toPosixPath } from "@gtkx/utils";
5
5
  import { existsSync, mkdirSync } from "node:fs";
6
6
  import { createRequire } from "node:module";
7
7
  import { dirname, join, posix, relative, resolve } from "node:path";
@@ -12,7 +12,7 @@ import {
12
12
  resolveCatalogProject,
13
13
  synchronizeCatalogs,
14
14
  } from "../i18n/catalogs.js";
15
- import { extractSourceCatalog } from "../i18n/source-messages.js";
15
+ import { extractSourceCatalog, SourceExtractionError } from "../i18n/source-messages.js";
16
16
  import { emitI18nTypes } from "../i18n/types.js";
17
17
  import { discoverSourceFiles, sourceLanguage } from "../internal/source-imports.js";
18
18
  import { stripQuery } from "./strip-query.js";
@@ -23,6 +23,7 @@ type I18nState = {
23
23
  outDir: string;
24
24
  project: CatalogProject | null;
25
25
  extraction: Promise<void>;
26
+ hotUpdateTimestamp: number | null;
26
27
  };
27
28
 
28
29
  const BOOTSTRAP_SPECIFIER = "@gtkx/i18n/bootstrap";
@@ -159,12 +160,27 @@ const isProjectSource = (state: I18nState, path: string): boolean => {
159
160
  const refreshProjectMessages = async (
160
161
  state: I18nState,
161
162
  shouldPreserveMetadataMessages: boolean,
162
- reportError: (message: string) => void,
163
+ shouldSynchronizeCatalogs = false,
163
164
  ): Promise<void> => {
164
165
  try {
165
- await queueExtraction(state, shouldPreserveMetadataMessages, false);
166
+ await queueExtraction(state, shouldPreserveMetadataMessages, shouldSynchronizeCatalogs);
166
167
  } catch (error) {
167
- reportError(error instanceof Error ? error.message : String(error));
168
+ logger.error(error instanceof Error ? error.message : String(error));
169
+ }
170
+ };
171
+
172
+ const recoverInitialSourceExtraction = async (
173
+ state: I18nState,
174
+ shouldPreserveMetadataMessages: boolean,
175
+ ): Promise<void> => {
176
+ try {
177
+ await queueExtraction(state, shouldPreserveMetadataMessages, true);
178
+ } catch (error) {
179
+ if (!(error instanceof SourceExtractionError)) {
180
+ throw error;
181
+ }
182
+
183
+ logger.error(error.message);
168
184
  }
169
185
  };
170
186
 
@@ -172,10 +188,12 @@ const gtkxI18n = (
172
188
  entryPath: string,
173
189
  loadConfig: ConfigLoader = createConfigLoader(),
174
190
  shouldPreserveMetadataMessages = true,
191
+ shouldRecoverExtractionErrors = false,
175
192
  ): Plugin => {
176
193
  const state: I18nState = {
177
194
  entryPath,
178
195
  extraction: Promise.resolve(),
196
+ hotUpdateTimestamp: null,
179
197
  i18nRoot: "",
180
198
  outDir: "",
181
199
  project: null,
@@ -187,20 +205,18 @@ const gtkxI18n = (
187
205
 
188
206
  configResolved: (config) => applyResolvedConfig(state, config, loadConfig),
189
207
 
190
- buildStart: () => queueExtraction(state, shouldPreserveMetadataMessages, true),
208
+ buildStart: () => shouldRecoverExtractionErrors
209
+ ? recoverInitialSourceExtraction(state, shouldPreserveMetadataMessages)
210
+ : queueExtraction(state, shouldPreserveMetadataMessages, true),
191
211
 
192
212
  hotUpdate(options) {
193
- if (!isProjectSource(state, options.file)) {
213
+ if (!isProjectSource(state, options.file) || state.hotUpdateTimestamp === options.timestamp) {
194
214
  return;
195
215
  }
196
216
 
197
- return refreshProjectMessages(
198
- state,
199
- shouldPreserveMetadataMessages,
200
- (message) => {
201
- options.server.config.logger.error(message);
202
- },
203
- );
217
+ state.hotUpdateTimestamp = options.timestamp;
218
+
219
+ return refreshProjectMessages(state, shouldPreserveMetadataMessages);
204
220
  },
205
221
 
206
222
  transform(code, id) {
@@ -30,7 +30,14 @@ const gtkxVitePlugins = (options: GtkxVitePluginOptions = {}): Plugin[] => {
30
30
 
31
31
  return [
32
32
  createConfigPlugin({ name: "gtkx:config", loadConfig }),
33
- ...(entryPath === undefined ? [] : [gtkxI18n(entryPath, loadConfig, shouldPreserveI18nMetadata)]),
33
+ ...(entryPath === undefined
34
+ ? []
35
+ : [gtkxI18n(
36
+ entryPath,
37
+ loadConfig,
38
+ shouldPreserveI18nMetadata,
39
+ mode === "development",
40
+ )]),
34
41
  gtkxStoreLinks(),
35
42
  gtkxUndeclaredLibrary(loadConfig),
36
43
  gtkxSettings(buildManifest),
@@ -5,7 +5,6 @@ import { arch, platform } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { stripQuery } from "./strip-query.js";
7
7
 
8
- const EMITTED_BINDING_SPECIFIER = "./gtkx.node";
9
8
  const BINDING_FILENAME = "gtkx.node";
10
9
 
11
10
  function resolveBinaryPath(projectRequire: ReturnType<typeof createRequire>, currentArch: string): string {
@@ -97,10 +96,6 @@ function gtkxNative(root: string): Plugin {
97
96
  });
98
97
  },
99
98
 
100
- resolveId(id) {
101
- return id === EMITTED_BINDING_SPECIFIER ? { id, external: true } : null;
102
- },
103
-
104
99
  transform(code, id) {
105
100
  return transformLoader(code, id, loaderPath(), bindingReferenceId);
106
101
  },
@@ -3,8 +3,8 @@ import type { Plugin } from "vitest/config";
3
3
  import { createConfigLoader } from "@gtkx/config/internal";
4
4
  import { prependSchemaDir, stageAndCompileProjectSchemas } from "../settings/schema.js";
5
5
 
6
- function gtkxSettingsWorkerEnv(): Plugin {
7
- const loadConfig = createConfigLoader();
6
+ function gtkxSettingsWorkerEnv(configFile?: string): Plugin {
7
+ const loadConfig = createConfigLoader({ configFile });
8
8
 
9
9
  return {
10
10
  name: "gtkx:settings-worker-env",
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from "vite";
2
- import gtkxVitest from "@gtkx/vitest";
2
+ import gtkxVitest, { type PluginOptions } from "@gtkx/vitest";
3
3
  import { gtkxEnsureStore } from "./vite-plugins/ensure-store.js";
4
4
  import { gtkxVitePlugins } from "./vite-plugins/index.js";
5
5
  import { gtkxSettingsWorkerEnv } from "./vite-plugins/settings-worker-env.js";
@@ -11,11 +11,11 @@ import { gtkxSettingsWorkerEnv } from "./vite-plugins/settings-worker-env.js";
11
11
  *
12
12
  * @returns The plugins to spread into a Vitest config.
13
13
  */
14
- const gtkx = (): Plugin[] => [
15
- gtkxEnsureStore(),
16
- ...gtkxVitePlugins(),
17
- gtkxSettingsWorkerEnv(),
18
- gtkxVitest(),
14
+ const gtkx = (options: PluginOptions = {}): Plugin[] => [
15
+ gtkxEnsureStore(options.configFile),
16
+ ...gtkxVitePlugins({ configFile: options.configFile }),
17
+ gtkxSettingsWorkerEnv(options.configFile),
18
+ gtkxVitest(options),
19
19
  ];
20
20
 
21
21
  export default gtkx;