@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.
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -2
- package/dist/cli.js.map +1 -1
- package/dist/codegen/config-dependencies.d.ts +3 -0
- package/dist/codegen/config-dependencies.d.ts.map +1 -0
- package/dist/codegen/config-dependencies.js +355 -0
- package/dist/codegen/config-dependencies.js.map +1 -0
- package/dist/codegen/reference.d.ts.map +1 -1
- package/dist/codegen/reference.js +1 -0
- package/dist/codegen/reference.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +3 -2
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +33 -14
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/codegen/store-resolver.d.ts +2 -0
- package/dist/codegen/store-resolver.d.ts.map +1 -1
- package/dist/codegen/store-resolver.js +6 -3
- package/dist/codegen/store-resolver.js.map +1 -1
- package/dist/commands/cleanup.d.ts +9 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +31 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +13 -4
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +1 -0
- package/dist/commands/docs.js.map +1 -1
- package/dist/deploy/freedesktop/validate.d.ts.map +1 -1
- package/dist/deploy/freedesktop/validate.js +11 -4
- package/dist/deploy/freedesktop/validate.js.map +1 -1
- package/dist/deploy/nfpm/config.d.ts +3 -2
- package/dist/deploy/nfpm/config.d.ts.map +1 -1
- package/dist/deploy/nfpm/config.js +3 -3
- package/dist/deploy/nfpm/config.js.map +1 -1
- package/dist/deploy/node-runtime/index.d.ts.map +1 -1
- package/dist/deploy/node-runtime/index.js +2 -1
- package/dist/deploy/node-runtime/index.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +24 -3
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/settings/extra-files.d.ts.map +1 -1
- package/dist/deploy/settings/extra-files.js +4 -3
- package/dist/deploy/settings/extra-files.js.map +1 -1
- package/dist/deploy/targets/nfpm-target.d.ts.map +1 -1
- package/dist/deploy/targets/nfpm-target.js +12 -7
- package/dist/deploy/targets/nfpm-target.js.map +1 -1
- package/dist/dev/entry-env.d.ts +2 -1
- package/dist/dev/entry-env.d.ts.map +1 -1
- package/dist/dev/entry-env.js +2 -1
- package/dist/dev/entry-env.js.map +1 -1
- package/dist/dev/runner-deps.d.ts +1 -1
- package/dist/dev/runner-deps.d.ts.map +1 -1
- package/dist/dev/runner-deps.js +3 -3
- package/dist/dev/runner-deps.js.map +1 -1
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +8 -3
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/supervisor.d.ts +3 -1
- package/dist/dev/supervisor.d.ts.map +1 -1
- package/dist/dev/supervisor.js +124 -27
- package/dist/dev/supervisor.js.map +1 -1
- package/dist/i18n/catalogs.d.ts.map +1 -1
- package/dist/i18n/catalogs.js +1 -0
- package/dist/i18n/catalogs.js.map +1 -1
- package/dist/i18n/source-messages.d.ts +3 -1
- package/dist/i18n/source-messages.d.ts.map +1 -1
- package/dist/i18n/source-messages.js +167 -37
- package/dist/i18n/source-messages.js.map +1 -1
- package/dist/internal/build-output.d.ts.map +1 -1
- package/dist/internal/build-output.js +53 -6
- package/dist/internal/build-output.js.map +1 -1
- package/dist/internal/docs-elements.d.ts +1 -0
- package/dist/internal/docs-elements.d.ts.map +1 -1
- package/dist/internal/docs-elements.js +5 -4
- package/dist/internal/docs-elements.js.map +1 -1
- package/dist/internal/errors.d.ts +2 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +1 -1
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/parent-process.d.ts +12 -0
- package/dist/internal/parent-process.d.ts.map +1 -0
- package/dist/internal/parent-process.js +74 -0
- package/dist/internal/parent-process.js.map +1 -0
- package/dist/internal/prepare-project.d.ts +1 -0
- package/dist/internal/prepare-project.d.ts.map +1 -1
- package/dist/internal/prepare-project.js +8 -2
- package/dist/internal/prepare-project.js.map +1 -1
- package/dist/vite-plugins/ensure-store.d.ts +1 -1
- package/dist/vite-plugins/ensure-store.d.ts.map +1 -1
- package/dist/vite-plugins/ensure-store.js +2 -2
- package/dist/vite-plugins/ensure-store.js.map +1 -1
- package/dist/vite-plugins/i18n.d.ts +1 -1
- package/dist/vite-plugins/i18n.d.ts.map +1 -1
- package/dist/vite-plugins/i18n.js +24 -11
- package/dist/vite-plugins/i18n.js.map +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/native.d.ts.map +1 -1
- package/dist/vite-plugins/native.js +0 -4
- package/dist/vite-plugins/native.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.d.ts +1 -1
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +2 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vitest-plugin.d.ts +2 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js +6 -6
- package/dist/vitest-plugin.js.map +1 -1
- package/package.json +12 -9
- package/src/cli.ts +12 -2
- package/src/codegen/config-dependencies.ts +470 -0
- package/src/codegen/reference.ts +1 -0
- package/src/codegen/run-codegen.ts +39 -17
- package/src/codegen/store-resolver.ts +8 -3
- package/src/commands/cleanup.ts +36 -0
- package/src/commands/dev.ts +26 -4
- package/src/commands/docs.ts +1 -0
- package/src/deploy/freedesktop/validate.ts +13 -4
- package/src/deploy/nfpm/config.ts +9 -3
- package/src/deploy/node-runtime/index.ts +2 -1
- package/src/deploy/payload/stage.ts +42 -3
- package/src/deploy/settings/extra-files.ts +5 -3
- package/src/deploy/targets/nfpm-target.ts +13 -6
- package/src/dev/entry-env.ts +2 -1
- package/src/dev/runner-deps.ts +3 -3
- package/src/dev/runner-main.ts +10 -3
- package/src/dev/supervisor.ts +164 -30
- package/src/i18n/catalogs.ts +1 -0
- package/src/i18n/source-messages.ts +240 -41
- package/src/internal/build-output.ts +79 -7
- package/src/internal/docs-elements.ts +10 -4
- package/src/internal/errors.ts +1 -1
- package/src/internal/parent-process.ts +110 -0
- package/src/internal/prepare-project.ts +9 -3
- package/src/vite-plugins/ensure-store.ts +2 -2
- package/src/vite-plugins/i18n.ts +30 -14
- package/src/vite-plugins/index.ts +8 -1
- package/src/vite-plugins/native.ts +0 -5
- package/src/vite-plugins/settings-worker-env.ts +2 -2
- package/src/vitest-plugin.ts +6 -6
|
@@ -71,8 +71,9 @@ type TranslationReferences = {
|
|
|
71
71
|
calls: Set<number>;
|
|
72
72
|
elements: Set<number>;
|
|
73
73
|
entryLocations: Map<string, SourcePoint[]>;
|
|
74
|
-
locations:
|
|
74
|
+
locations: Map<string, SourcePoint>;
|
|
75
75
|
masks: SourceMask[];
|
|
76
|
+
sourcePoints: Map<number, SourcePoint>;
|
|
76
77
|
};
|
|
77
78
|
type SourceMask = { end: number; start: number };
|
|
78
79
|
type SourcePoint = { column: number; line: number };
|
|
@@ -86,6 +87,7 @@ type ImportedBinding = {
|
|
|
86
87
|
type TranslationHookBinding = { call: types.CallExpression; isCanonical: boolean };
|
|
87
88
|
type TranslationMemberExpression = types.MemberExpression | types.OptionalMemberExpression;
|
|
88
89
|
type ResolvedBinding = NonNullable<ReturnType<Scope["getBinding"]>>;
|
|
90
|
+
type LocatableNode = { loc?: { start: SourcePoint } | null | undefined };
|
|
89
91
|
|
|
90
92
|
const CONTEXT_SEPARATOR = "\u{4}";
|
|
91
93
|
const DEFAULT_NAMESPACE = "translation";
|
|
@@ -112,6 +114,114 @@ const STATIC_KEY_WRAPPER_TYPES: ReadonlySet<string> = new Set([
|
|
|
112
114
|
"TSNonNullExpression",
|
|
113
115
|
]);
|
|
114
116
|
|
|
117
|
+
class SourceExtractionError extends Error {}
|
|
118
|
+
|
|
119
|
+
const sourceErrorMessage = (error: unknown): string => {
|
|
120
|
+
if (!(error instanceof Error)) {
|
|
121
|
+
return String(error);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
"code" in error &&
|
|
126
|
+
error.code === "BABEL_PARSE_ERROR"
|
|
127
|
+
) {
|
|
128
|
+
const separator = error.message.indexOf(": ");
|
|
129
|
+
|
|
130
|
+
if (separator !== -1) {
|
|
131
|
+
return error.message.slice(separator + 2);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return error.message;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const projectSourcePath = (root: string, path: string): string => {
|
|
139
|
+
const absolute = isAbsolute(path) ? resolve(path) : resolve(root, path);
|
|
140
|
+
|
|
141
|
+
return toPosixPath(isPathInside(root, absolute) ? relative(root, absolute) : path);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const sourceExtractionError = (
|
|
145
|
+
root: string,
|
|
146
|
+
path: string,
|
|
147
|
+
point: SourcePoint | null,
|
|
148
|
+
error: unknown,
|
|
149
|
+
): SourceExtractionError => {
|
|
150
|
+
if (error instanceof SourceExtractionError) {
|
|
151
|
+
return error;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const file = projectSourcePath(root, path);
|
|
155
|
+
const location = point === null
|
|
156
|
+
? file
|
|
157
|
+
: `${file}:${String(point.line)}:${String(point.column + 1)}`;
|
|
158
|
+
|
|
159
|
+
return new SourceExtractionError(`${location}: ${sourceErrorMessage(error)}`, { cause: error });
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const locatablePoint = (node: LocatableNode): SourcePoint | null => {
|
|
163
|
+
const location = node.loc?.start;
|
|
164
|
+
|
|
165
|
+
return location === undefined ? null : { column: location.column, line: location.line };
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const isSourcePoint = (value: unknown): value is SourcePoint =>
|
|
169
|
+
typeof value === "object" &&
|
|
170
|
+
value !== null &&
|
|
171
|
+
"column" in value &&
|
|
172
|
+
typeof value.column === "number" &&
|
|
173
|
+
"line" in value &&
|
|
174
|
+
typeof value.line === "number";
|
|
175
|
+
|
|
176
|
+
const errorSourcePoint = (error: unknown): SourcePoint | null => {
|
|
177
|
+
if (typeof error !== "object" || error === null) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if ("loc" in error && isSourcePoint(error.loc)) {
|
|
182
|
+
return error.loc;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return "location" in error && isSourcePoint(error.location) ? error.location : null;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const atSourceNode = <T>(
|
|
189
|
+
root: string,
|
|
190
|
+
path: string,
|
|
191
|
+
node: LocatableNode,
|
|
192
|
+
operation: () => T,
|
|
193
|
+
): T => {
|
|
194
|
+
try {
|
|
195
|
+
return operation();
|
|
196
|
+
} catch (error) {
|
|
197
|
+
throw sourceExtractionError(root, path, locatablePoint(node), error);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const atExtractedLocation = <T>(
|
|
202
|
+
root: string,
|
|
203
|
+
locations: SourceLocation[],
|
|
204
|
+
operation: () => T,
|
|
205
|
+
): T => {
|
|
206
|
+
const location = locations.find((candidate) =>
|
|
207
|
+
candidate.line !== undefined && candidate.column !== undefined);
|
|
208
|
+
|
|
209
|
+
if (location?.line === undefined || location.column === undefined) {
|
|
210
|
+
return operation();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
return operation();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
throw sourceExtractionError(
|
|
217
|
+
root,
|
|
218
|
+
location.file,
|
|
219
|
+
{ column: location.column, line: location.line },
|
|
220
|
+
error,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
115
225
|
const quietLogger = (): { logger: Logger; reports: string[] } => {
|
|
116
226
|
const reports: string[] = [];
|
|
117
227
|
|
|
@@ -223,11 +333,15 @@ const expandedPlural = (entry: ExtractedKey): { baseKey: string; variant: Plural
|
|
|
223
333
|
};
|
|
224
334
|
};
|
|
225
335
|
|
|
226
|
-
const groupedPlurals = (entries: ExtractedKey[]): PluralGroup[] => {
|
|
336
|
+
const groupedPlurals = (root: string, entries: ExtractedKey[]): PluralGroup[] => {
|
|
227
337
|
const groups: Map<string, PluralGroup> = new Map();
|
|
228
338
|
|
|
229
339
|
for (const entry of entries) {
|
|
230
|
-
const { baseKey, variant } =
|
|
340
|
+
const { baseKey, variant } = atExtractedLocation(
|
|
341
|
+
root,
|
|
342
|
+
entry.locations ?? [],
|
|
343
|
+
() => expandedPlural(entry),
|
|
344
|
+
);
|
|
231
345
|
const namespace = namespaceFor(entry);
|
|
232
346
|
const key = `${namespace}\0${baseKey}`;
|
|
233
347
|
const group = groups.get(key) ?? { baseKey, namespace, variants: [] };
|
|
@@ -276,8 +390,13 @@ const pluralMessage = ({ baseKey, namespace, variants }: PluralGroup): SourceMes
|
|
|
276
390
|
};
|
|
277
391
|
};
|
|
278
392
|
|
|
279
|
-
const pluralMessages = (entries: ExtractedKey[]): SourceMessage[] =>
|
|
280
|
-
groupedPlurals(entries).map((group) =>
|
|
393
|
+
const pluralMessages = (root: string, entries: ExtractedKey[]): SourceMessage[] =>
|
|
394
|
+
groupedPlurals(root, entries).map((group) =>
|
|
395
|
+
atExtractedLocation(
|
|
396
|
+
root,
|
|
397
|
+
group.variants.flatMap((variant) => variant.locations),
|
|
398
|
+
() => pluralMessage(group),
|
|
399
|
+
));
|
|
281
400
|
|
|
282
401
|
const assertCardinal = (entry: ExtractedKey): void => {
|
|
283
402
|
if (entry.isOrdinal === true) {
|
|
@@ -285,12 +404,14 @@ const assertCardinal = (entry: ExtractedKey): void => {
|
|
|
285
404
|
}
|
|
286
405
|
};
|
|
287
406
|
|
|
288
|
-
const sourceMessages = (entries: ExtractedKey[]): SourceMessage[] => {
|
|
407
|
+
const sourceMessages = (root: string, entries: ExtractedKey[]): SourceMessage[] => {
|
|
289
408
|
const points: ExtractedKey[] = [];
|
|
290
409
|
const plurals: ExtractedKey[] = [];
|
|
291
410
|
|
|
292
411
|
for (const entry of entries) {
|
|
293
|
-
|
|
412
|
+
atExtractedLocation(root, entry.locations ?? [], () => {
|
|
413
|
+
assertCardinal(entry);
|
|
414
|
+
});
|
|
294
415
|
|
|
295
416
|
if (entry.isExpandedPlural === true) {
|
|
296
417
|
plurals.push(entry);
|
|
@@ -304,7 +425,11 @@ const sourceMessages = (entries: ExtractedKey[]): SourceMessage[] => {
|
|
|
304
425
|
points.push(entry);
|
|
305
426
|
}
|
|
306
427
|
|
|
307
|
-
return [
|
|
428
|
+
return [
|
|
429
|
+
...points.map((entry) =>
|
|
430
|
+
atExtractedLocation(root, entry.locations ?? [], () => pointMessage(entry))),
|
|
431
|
+
...pluralMessages(root, plurals),
|
|
432
|
+
];
|
|
308
433
|
};
|
|
309
434
|
|
|
310
435
|
const compareMessages = (left: SourceMessage, right: SourceMessage): number =>
|
|
@@ -745,16 +870,14 @@ const registerSourceMask = (node: types.Node, references: TranslationReferences)
|
|
|
745
870
|
const sourceLocationKey = (line: number, column: number): string => `${String(line)}:${String(column)}`;
|
|
746
871
|
|
|
747
872
|
const sourcePoint = (node: types.Node): SourcePoint | null => {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
return location === undefined ? null : { column: location.column, line: location.line };
|
|
873
|
+
return locatablePoint(node);
|
|
751
874
|
};
|
|
752
875
|
|
|
753
876
|
const registerSourceLocation = (node: types.Node, references: TranslationReferences): SourcePoint | null => {
|
|
754
877
|
const point = sourcePoint(node);
|
|
755
878
|
|
|
756
879
|
if (point !== null) {
|
|
757
|
-
references.locations.
|
|
880
|
+
references.locations.set(sourceLocationKey(point.line, point.column), point);
|
|
758
881
|
}
|
|
759
882
|
|
|
760
883
|
return point;
|
|
@@ -901,6 +1024,11 @@ const registerCallReference = (
|
|
|
901
1024
|
references.calls.add(nodeStart(callPath));
|
|
902
1025
|
const firstArgument = callPath.node.arguments[0];
|
|
903
1026
|
const point = registerSourceLocation(firstArgument ?? callPath.node, references);
|
|
1027
|
+
|
|
1028
|
+
if (point !== null) {
|
|
1029
|
+
references.sourcePoints.set(nodeStart(callPath), point);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
904
1032
|
registerEntryLocation(extractedEntryKey(callPath.node, keyPrefix), point, references);
|
|
905
1033
|
};
|
|
906
1034
|
|
|
@@ -910,6 +1038,26 @@ const parserPlugins = (lang: ReturnType<typeof sourceLanguage>): ("decorators" |
|
|
|
910
1038
|
...(lang === "jsx" || lang === "tsx" ? ["jsx" as const] : []),
|
|
911
1039
|
];
|
|
912
1040
|
|
|
1041
|
+
const parseTranslationReferences = (
|
|
1042
|
+
root: string,
|
|
1043
|
+
path: string,
|
|
1044
|
+
source: string,
|
|
1045
|
+
lang: ReturnType<typeof sourceLanguage>,
|
|
1046
|
+
) => {
|
|
1047
|
+
try {
|
|
1048
|
+
return parseBabelSync(source, {
|
|
1049
|
+
ast: true,
|
|
1050
|
+
babelrc: false,
|
|
1051
|
+
configFile: false,
|
|
1052
|
+
filename: projectSourcePath(root, path),
|
|
1053
|
+
parserOpts: { createParenthesizedExpressions: true, plugins: parserPlugins(lang) },
|
|
1054
|
+
sourceType: "module",
|
|
1055
|
+
});
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
throw sourceExtractionError(root, path, errorSourcePoint(error), error);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
|
|
913
1061
|
const registerIdentifierTranslationCall = (
|
|
914
1062
|
callPath: NodePath<types.CallExpression>,
|
|
915
1063
|
callee: NodePath,
|
|
@@ -1010,6 +1158,19 @@ const registerUnrecognizedElementMasks = (
|
|
|
1010
1158
|
}
|
|
1011
1159
|
};
|
|
1012
1160
|
|
|
1161
|
+
const registerElementReference = (
|
|
1162
|
+
elementPath: NodePath,
|
|
1163
|
+
references: TranslationReferences,
|
|
1164
|
+
): void => {
|
|
1165
|
+
const start = nodeStart(elementPath);
|
|
1166
|
+
const point = registerSourceLocation(elementPath.node, references);
|
|
1167
|
+
references.elements.add(start);
|
|
1168
|
+
|
|
1169
|
+
if (point !== null) {
|
|
1170
|
+
references.sourcePoints.set(start, point);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1013
1174
|
const registerTranslationElement = (elementPath: NodePath, references: TranslationReferences): void => {
|
|
1014
1175
|
const name = elementPath.get("name");
|
|
1015
1176
|
|
|
@@ -1024,8 +1185,7 @@ const registerTranslationElement = (elementPath: NodePath, references: Translati
|
|
|
1024
1185
|
}
|
|
1025
1186
|
|
|
1026
1187
|
if (kind === "canonical") {
|
|
1027
|
-
|
|
1028
|
-
registerSourceLocation(elementPath.node, references);
|
|
1188
|
+
registerElementReference(elementPath, references);
|
|
1029
1189
|
} else if (TRANS_ELEMENTS.has(name.node.name)) {
|
|
1030
1190
|
registerUnrecognizedElementMasks(elementPath, name, references);
|
|
1031
1191
|
}
|
|
@@ -1043,18 +1203,12 @@ const maskUnrecognizedTranslations = (source: string, references: TranslationRef
|
|
|
1043
1203
|
};
|
|
1044
1204
|
|
|
1045
1205
|
const translationReferences = (
|
|
1206
|
+
root: string,
|
|
1046
1207
|
path: string,
|
|
1047
1208
|
source: string,
|
|
1048
1209
|
lang: ReturnType<typeof sourceLanguage>,
|
|
1049
1210
|
): TranslationReferences => {
|
|
1050
|
-
const parsed =
|
|
1051
|
-
ast: true,
|
|
1052
|
-
babelrc: false,
|
|
1053
|
-
configFile: false,
|
|
1054
|
-
filename: path,
|
|
1055
|
-
parserOpts: { createParenthesizedExpressions: true, plugins: parserPlugins(lang) },
|
|
1056
|
-
sourceType: "module",
|
|
1057
|
-
});
|
|
1211
|
+
const parsed = parseTranslationReferences(root, path, source, lang);
|
|
1058
1212
|
|
|
1059
1213
|
if (parsed === null) {
|
|
1060
1214
|
throw new Error(`Translation extraction could not parse ${path}`);
|
|
@@ -1064,22 +1218,31 @@ const translationReferences = (
|
|
|
1064
1218
|
calls: new Set(),
|
|
1065
1219
|
elements: new Set(),
|
|
1066
1220
|
entryLocations: new Map(),
|
|
1067
|
-
locations: new
|
|
1221
|
+
locations: new Map(),
|
|
1068
1222
|
masks: [],
|
|
1223
|
+
sourcePoints: new Map(),
|
|
1069
1224
|
};
|
|
1070
1225
|
|
|
1071
1226
|
traverse(parsed, {
|
|
1072
1227
|
CallExpression(callPath) {
|
|
1073
|
-
|
|
1228
|
+
atSourceNode(root, path, callPath.node, () => {
|
|
1229
|
+
registerTranslationCall(callPath, references);
|
|
1230
|
+
});
|
|
1074
1231
|
},
|
|
1075
1232
|
JSXOpeningElement(elementPath) {
|
|
1076
|
-
|
|
1233
|
+
atSourceNode(root, path, elementPath.node, () => {
|
|
1234
|
+
registerTranslationElement(elementPath, references);
|
|
1235
|
+
});
|
|
1077
1236
|
},
|
|
1078
1237
|
OptionalCallExpression(callPath) {
|
|
1079
|
-
|
|
1238
|
+
atSourceNode(root, path, callPath.node, () => {
|
|
1239
|
+
assertSupportedOptionalCall(callPath);
|
|
1240
|
+
});
|
|
1080
1241
|
},
|
|
1081
1242
|
TaggedTemplateExpression(tagPath) {
|
|
1082
|
-
|
|
1243
|
+
atSourceNode(root, path, tagPath.node, () => {
|
|
1244
|
+
assertUnsupportedTranslationTag(tagPath);
|
|
1245
|
+
});
|
|
1083
1246
|
},
|
|
1084
1247
|
});
|
|
1085
1248
|
|
|
@@ -1165,22 +1328,44 @@ const assertSupportedTransElement = (node: ESTree.JSXOpeningElement, references:
|
|
|
1165
1328
|
}
|
|
1166
1329
|
};
|
|
1167
1330
|
|
|
1168
|
-
const assertSupportedTranslationSyntax = (path: string, source: string): TranslationReferences => {
|
|
1331
|
+
const assertSupportedTranslationSyntax = (root: string, path: string, source: string): TranslationReferences => {
|
|
1169
1332
|
const lang = sourceLanguage(path);
|
|
1170
1333
|
|
|
1171
1334
|
if (lang === undefined) {
|
|
1172
|
-
return {
|
|
1335
|
+
return {
|
|
1336
|
+
calls: new Set(),
|
|
1337
|
+
elements: new Set(),
|
|
1338
|
+
entryLocations: new Map(),
|
|
1339
|
+
locations: new Map(),
|
|
1340
|
+
masks: [],
|
|
1341
|
+
sourcePoints: new Map(),
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
let parsed: ReturnType<typeof parseSync>;
|
|
1346
|
+
|
|
1347
|
+
try {
|
|
1348
|
+
parsed = parseSync(projectSourcePath(root, path), source, { lang });
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
throw sourceExtractionError(root, path, errorSourcePoint(error), error);
|
|
1173
1351
|
}
|
|
1174
1352
|
|
|
1175
|
-
const
|
|
1176
|
-
const references = translationReferences(path, source, lang);
|
|
1353
|
+
const references = translationReferences(root, path, source, lang);
|
|
1177
1354
|
|
|
1178
1355
|
new Visitor({
|
|
1179
1356
|
CallExpression(node) {
|
|
1180
|
-
|
|
1357
|
+
try {
|
|
1358
|
+
assertSupportedTranslationCall(node, references);
|
|
1359
|
+
} catch (error) {
|
|
1360
|
+
throw sourceExtractionError(root, path, references.sourcePoints.get(node.start) ?? null, error);
|
|
1361
|
+
}
|
|
1181
1362
|
},
|
|
1182
1363
|
JSXOpeningElement(node) {
|
|
1183
|
-
|
|
1364
|
+
try {
|
|
1365
|
+
assertSupportedTransElement(node, references);
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
throw sourceExtractionError(root, path, references.sourcePoints.get(node.start) ?? null, error);
|
|
1368
|
+
}
|
|
1184
1369
|
},
|
|
1185
1370
|
}).visit(parsed.program);
|
|
1186
1371
|
|
|
@@ -1476,12 +1661,19 @@ const extractedSourcePoints = (root: string, entries: ExtractedKeysMap): Set<str
|
|
|
1476
1661
|
return points;
|
|
1477
1662
|
};
|
|
1478
1663
|
|
|
1479
|
-
const
|
|
1664
|
+
const missingSourcePoint = (
|
|
1480
1665
|
file: string,
|
|
1481
1666
|
references: TranslationReferences,
|
|
1482
1667
|
extracted: Set<string>,
|
|
1483
|
-
):
|
|
1484
|
-
references.locations
|
|
1668
|
+
): SourcePoint | null => {
|
|
1669
|
+
for (const [location, point] of references.locations) {
|
|
1670
|
+
if (!extracted.has(`${file}\0${location}`)) {
|
|
1671
|
+
return point;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
return null;
|
|
1676
|
+
};
|
|
1485
1677
|
|
|
1486
1678
|
const assertAllReferencesExtracted = (
|
|
1487
1679
|
root: string,
|
|
@@ -1491,8 +1683,15 @@ const assertAllReferencesExtracted = (
|
|
|
1491
1683
|
const extracted = extractedSourcePoints(root, entries);
|
|
1492
1684
|
|
|
1493
1685
|
for (const [file, sourceReferences] of references) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1686
|
+
const point = missingSourcePoint(file, sourceReferences, extracted);
|
|
1687
|
+
|
|
1688
|
+
if (point !== null) {
|
|
1689
|
+
throw sourceExtractionError(
|
|
1690
|
+
root,
|
|
1691
|
+
file,
|
|
1692
|
+
point,
|
|
1693
|
+
new Error("A translation expression could not be extracted safely"),
|
|
1694
|
+
);
|
|
1496
1695
|
}
|
|
1497
1696
|
}
|
|
1498
1697
|
};
|
|
@@ -1508,7 +1707,7 @@ const findSourceMessages = async (root: string, sourceFiles: string[]): Promise<
|
|
|
1508
1707
|
const references: Map<string, TranslationReferences> = new Map();
|
|
1509
1708
|
|
|
1510
1709
|
for (const [file, source] of sources) {
|
|
1511
|
-
references.set(file, assertSupportedTranslationSyntax(file, source));
|
|
1710
|
+
references.set(file, assertSupportedTranslationSyntax(root, file, source));
|
|
1512
1711
|
}
|
|
1513
1712
|
|
|
1514
1713
|
let extracted: ExtractedKeysMap | undefined;
|
|
@@ -1542,7 +1741,7 @@ const findSourceMessages = async (root: string, sourceFiles: string[]): Promise<
|
|
|
1542
1741
|
throw new Error(reports.join("\n") || "i18next extraction failed");
|
|
1543
1742
|
}
|
|
1544
1743
|
|
|
1545
|
-
return sourceMessages(extracted.values().toArray())
|
|
1744
|
+
return sourceMessages(root, extracted.values().toArray())
|
|
1546
1745
|
.map((message) => inferLocations(message, sources))
|
|
1547
1746
|
.toSorted(compareMessages);
|
|
1548
1747
|
};
|
|
@@ -1785,4 +1984,4 @@ const extractSourceCatalog = async (
|
|
|
1785
1984
|
extractCatalogTemplate({ project, messages, shouldPreserveMetadataMessages });
|
|
1786
1985
|
};
|
|
1787
1986
|
|
|
1788
|
-
export { extractSourceCatalog, extractSourceCatalogTo };
|
|
1987
|
+
export { extractSourceCatalog, extractSourceCatalogTo, SourceExtractionError };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isPathInside, isRecord } from "@gtkx/utils";
|
|
2
2
|
import { lstatSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
BUILD_MANIFEST_FILENAME,
|
|
6
6
|
BUILD_MANIFEST_GENERATOR,
|
|
@@ -53,13 +53,85 @@ const isReusableBuildDirectory = (path: string): boolean => {
|
|
|
53
53
|
return stats.isDirectory() && (readdirSync(path).length === 0 || isGtkxBuildDirectory(path));
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const
|
|
57
|
-
|
|
56
|
+
const outputName = (root: string, path: string): string => relative(root, path) || ".";
|
|
57
|
+
|
|
58
|
+
const gtkxBuildAncestor = (root: string, outDir: string): string | null => {
|
|
59
|
+
let ancestor = dirname(outDir);
|
|
60
|
+
|
|
61
|
+
while (isPathInside(root, ancestor)) {
|
|
62
|
+
if (isGtkxBuildDirectory(ancestor)) {
|
|
63
|
+
return ancestor;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ancestor = dirname(ancestor);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const childDirectories = (parent: string): string[] =>
|
|
73
|
+
readdirSync(parent, { withFileTypes: true })
|
|
74
|
+
.filter((entry) => entry.isDirectory())
|
|
75
|
+
.map((entry) => join(parent, entry.name));
|
|
76
|
+
|
|
77
|
+
const gtkxBuildDescendant = (root: string): string | null => {
|
|
78
|
+
const pending = childDirectories(root);
|
|
79
|
+
|
|
80
|
+
while (pending.length > 0) {
|
|
81
|
+
const path = pending.pop();
|
|
82
|
+
|
|
83
|
+
if (path === undefined) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (isGtkxBuildDirectory(path)) {
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pending.push(...childDirectories(path));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
};
|
|
58
96
|
|
|
59
97
|
const assertSafeBuildLocation = (root: string, outDir: string): void => {
|
|
60
|
-
if (!isPathInside(root, outDir)
|
|
61
|
-
throw
|
|
98
|
+
if (!isPathInside(root, outDir)) {
|
|
99
|
+
throw new Error(`Build output ${outDir} must be below the project root ${root}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (hasSymlinkComponent(root, outDir)) {
|
|
103
|
+
throw new Error(`Build output ${outputName(root, outDir)} passes through a symbolic link`);
|
|
62
104
|
}
|
|
105
|
+
|
|
106
|
+
const ancestor = gtkxBuildAncestor(root, outDir);
|
|
107
|
+
|
|
108
|
+
if (ancestor !== null) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Build output ${outputName(root, outDir)} is nested inside the earlier GTKX build ` +
|
|
111
|
+
outputName(root, ancestor),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const nonReusableBuildOutputError = (root: string, outDir: string): Error => {
|
|
117
|
+
const stats = lstatSync(outDir, { throwIfNoEntry: false });
|
|
118
|
+
|
|
119
|
+
if (stats?.isDirectory() !== true) {
|
|
120
|
+
return new Error(`Build output ${outputName(root, outDir)} exists and is not a directory`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const descendant = gtkxBuildDescendant(outDir);
|
|
124
|
+
|
|
125
|
+
if (descendant !== null) {
|
|
126
|
+
return new Error(
|
|
127
|
+
`Build output ${outputName(root, outDir)} contains the earlier GTKX build ` +
|
|
128
|
+
outputName(root, descendant),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return new Error(
|
|
133
|
+
`Build output ${outputName(root, outDir)} is nonempty and is not an earlier GTKX build`,
|
|
134
|
+
);
|
|
63
135
|
};
|
|
64
136
|
|
|
65
137
|
const resolveBuildOutDir = (root: string, configured?: string): string => {
|
|
@@ -69,7 +141,7 @@ const resolveBuildOutDir = (root: string, configured?: string): string => {
|
|
|
69
141
|
assertSafeBuildLocation(root, outDir);
|
|
70
142
|
|
|
71
143
|
if (!isReusableBuildDirectory(outDir)) {
|
|
72
|
-
throw
|
|
144
|
+
throw nonReusableBuildOutputError(root, outDir);
|
|
73
145
|
}
|
|
74
146
|
|
|
75
147
|
return outDir;
|
|
@@ -80,7 +152,7 @@ const prepareBuildOutDir = (root: string, outDir: string): PreparedBuildOutDir =
|
|
|
80
152
|
const prepared = prepareOutputDirectory(root, outDir, isReusableBuildDirectory);
|
|
81
153
|
|
|
82
154
|
if (prepared.status === "unsafe") {
|
|
83
|
-
throw
|
|
155
|
+
throw new Error(`Build output ${outputName(root, outDir)} became unsafe while preparing it`);
|
|
84
156
|
}
|
|
85
157
|
|
|
86
158
|
const transaction: OutputDirectoryTransaction = prepared.transaction;
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ElementProps,
|
|
3
|
+
type OmittedProps,
|
|
4
|
+
resolveStore,
|
|
5
|
+
} from "@gtkx/codegen";
|
|
6
|
+
import { readBuiltinElementsForDocs } from "@gtkx/codegen/internal";
|
|
2
7
|
import { existsSync } from "node:fs";
|
|
3
8
|
|
|
4
9
|
type DocsElements = {
|
|
5
10
|
props: ElementProps;
|
|
6
11
|
omittedProps: OmittedProps;
|
|
12
|
+
acceptedChildTypes: Record<string, string[]>;
|
|
7
13
|
};
|
|
8
14
|
|
|
9
15
|
const resolveDocsElements = async (cwd: string): Promise<DocsElements> => {
|
|
10
16
|
const { gi } = resolveStore(cwd);
|
|
11
17
|
|
|
12
18
|
if (!existsSync(gi.storeDir)) {
|
|
13
|
-
return { props: {}, omittedProps: {} };
|
|
19
|
+
return { props: {}, omittedProps: {}, acceptedChildTypes: {} };
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
const { props, omittedProps } = await
|
|
22
|
+
const { props, omittedProps, acceptedChildTypes } = await readBuiltinElementsForDocs();
|
|
17
23
|
|
|
18
|
-
return { props, omittedProps };
|
|
24
|
+
return { props, omittedProps, acceptedChildTypes };
|
|
19
25
|
};
|
|
20
26
|
|
|
21
27
|
export { resolveDocsElements };
|
package/src/internal/errors.ts
CHANGED