@xndrjs/i18n 0.8.0 → 0.8.2-alpha.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 -2
- package/dist/cli/audit.js +739 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/codegen.js +1471 -0
- package/dist/cli/codegen.js.map +1 -0
- package/dist/cli/setup.js +148 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/codegen/index.js +43 -40
- package/dist/codegen/index.js.map +1 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +56 -10
- package/dist/index.js.map +1 -1
- package/package.json +6 -9
- package/bin/audit.mjs +0 -14
- package/bin/codegen.mjs +0 -17
- package/bin/setup.mjs +0 -14
- package/src/IcuTranslationProviderMulti.test.ts +0 -215
- package/src/IcuTranslationProviderMulti.ts +0 -176
- package/src/audit/audit-dictionaries.test.ts +0 -134
- package/src/audit/audit-dictionaries.ts +0 -178
- package/src/audit/run-audit.test.ts +0 -169
- package/src/audit/run-audit.ts +0 -94
- package/src/builder-load-registry.ts +0 -39
- package/src/builder-loaders.ts +0 -15
- package/src/builder-types.test.ts +0 -49
- package/src/builder-types.ts +0 -24
- package/src/builder.test.ts +0 -260
- package/src/builder.ts +0 -10
- package/src/codegen/codegen-config-schema.ts +0 -156
- package/src/codegen/config.test.ts +0 -231
- package/src/codegen/config.ts +0 -42
- package/src/codegen/constants.ts +0 -9
- package/src/codegen/delivery-artifacts.test.ts +0 -167
- package/src/codegen/delivery-artifacts.ts +0 -154
- package/src/codegen/dictionary-spec-contract.test.ts +0 -28
- package/src/codegen/dictionary-spec-contract.ts +0 -138
- package/src/codegen/emit/dictionary-schema-file.ts +0 -113
- package/src/codegen/emit/instance-file.test.ts +0 -71
- package/src/codegen/emit/instance-file.ts +0 -257
- package/src/codegen/emit/namespace-loaders-file.test.ts +0 -251
- package/src/codegen/emit/namespace-loaders-file.ts +0 -229
- package/src/codegen/emit/types-file.test.ts +0 -96
- package/src/codegen/emit/types-file.ts +0 -142
- package/src/codegen/fixtures/billing-namespace.json +0 -6
- package/src/codegen/generate-i18n-types.test.ts +0 -1137
- package/src/codegen/generate-i18n-types.ts +0 -20
- package/src/codegen/icu-analysis.ts +0 -110
- package/src/codegen/locale-fallback.ts +0 -73
- package/src/codegen/locale-policy.test.ts +0 -35
- package/src/codegen/locale-policy.ts +0 -30
- package/src/codegen/paths.ts +0 -44
- package/src/codegen/project-locales-set-namespace.test.ts +0 -134
- package/src/codegen/read-dictionary.test.ts +0 -594
- package/src/codegen/read-dictionary.ts +0 -260
- package/src/codegen/regenerate-namespaces.ts +0 -180
- package/src/codegen/run-codegen.test.ts +0 -230
- package/src/codegen/run-codegen.ts +0 -252
- package/src/codegen/types.ts +0 -10
- package/src/codegen/write-file-if-changed.test.ts +0 -42
- package/src/codegen/write-file-if-changed.ts +0 -20
- package/src/codegen-config/build-config.ts +0 -15
- package/src/codegen-config/codegen-config.test.ts +0 -36
- package/src/codegen-config/index.ts +0 -37
- package/src/codegen-config/type-names.ts +0 -20
- package/src/codegen-config/write-config.ts +0 -8
- package/src/engine.ts +0 -47
- package/src/fetch-artifact.ts +0 -16
- package/src/format-core.ts +0 -91
- package/src/i18n-handle.ts +0 -202
- package/src/icu/extract-variables.test.ts +0 -199
- package/src/icu/extract-variables.ts +0 -227
- package/src/icu/parse-template.ts +0 -31
- package/src/index.ts +0 -58
- package/src/project-locales.test.ts +0 -280
- package/src/project-locales.ts +0 -211
- package/src/resolve-locale.test.ts +0 -91
- package/src/resolve-locale.ts +0 -88
- package/src/scope-multi.ts +0 -105
- package/src/scope-types.ts +0 -35
- package/src/scope.test.ts +0 -172
- package/src/serialized-state.test.ts +0 -114
- package/src/serialized-state.ts +0 -28
- package/src/setup/setup-i18n.test.ts +0 -85
- package/src/setup/setup-i18n.ts +0 -156
- package/src/types.ts +0 -65
- package/src/validation/create-args-schema.ts +0 -76
- package/src/validation/create-normalized-schema.ts +0 -52
- package/src/validation/errors.ts +0 -43
- package/src/validation/index.ts +0 -202
- package/src/validation/normalize.ts +0 -299
- package/src/validation/to-dictionary.ts +0 -67
- package/src/validation/types.ts +0 -82
- package/src/validation/validate-normalized.ts +0 -133
- package/src/validation/validation.test.ts +0 -562
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import path4 from 'path';
|
|
4
|
+
import fs2 from 'fs';
|
|
5
|
+
import { parse } from 'yaml';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/codegen/constants.ts
|
|
19
|
+
var IDENTIFIER_NAME_PATTERN, IDENTIFIER_NAME_REQUIREMENT;
|
|
20
|
+
var init_constants = __esm({
|
|
21
|
+
"src/codegen/constants.ts"() {
|
|
22
|
+
IDENTIFIER_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
23
|
+
IDENTIFIER_NAME_REQUIREMENT = "allowed: letters, digits, underscore; must not start with a digit";
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// src/codegen/delivery-artifacts.ts
|
|
28
|
+
function getDeliveryArtifactsStructureIssues(deliveryArtifacts) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
for (const [area, locales] of Object.entries(deliveryArtifacts)) {
|
|
31
|
+
if (!DELIVERY_AREA_NAME_PATTERN.test(area)) {
|
|
32
|
+
issues.push({
|
|
33
|
+
path: ["deliveryArtifacts", area],
|
|
34
|
+
message: `Invalid delivery area name "${area}": must match ${DELIVERY_AREA_NAME_PATTERN.source}`
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (locales.length === 0) {
|
|
38
|
+
issues.push({
|
|
39
|
+
path: ["deliveryArtifacts", area],
|
|
40
|
+
message: `Delivery area "${area}" must include at least one locale.`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const localeToArea = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const [area, locales] of Object.entries(deliveryArtifacts)) {
|
|
46
|
+
for (const locale of locales) {
|
|
47
|
+
const previousArea = localeToArea.get(locale);
|
|
48
|
+
if (previousArea) {
|
|
49
|
+
issues.push({
|
|
50
|
+
path: ["deliveryArtifacts", area],
|
|
51
|
+
message: `Locale "${locale}" appears in both "${previousArea}" and "${area}".`
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
localeToArea.set(locale, area);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return issues;
|
|
59
|
+
}
|
|
60
|
+
var DELIVERY_AREA_NAME_PATTERN;
|
|
61
|
+
var init_delivery_artifacts = __esm({
|
|
62
|
+
"src/codegen/delivery-artifacts.ts"() {
|
|
63
|
+
DELIVERY_AREA_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/codegen-config/type-names.ts
|
|
68
|
+
function typeNamesForProject(project) {
|
|
69
|
+
return {
|
|
70
|
+
paramsTypeName: `${project}Params`,
|
|
71
|
+
schemaTypeName: `${project}Schema`,
|
|
72
|
+
localeTypeName: `${project}Locale`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
var init_type_names = __esm({
|
|
76
|
+
"src/codegen-config/type-names.ts"() {
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
function resolveCodegenPaths(config) {
|
|
80
|
+
const typeNames = typeNamesForProject(config.projectName);
|
|
81
|
+
const codegenPath = config.codegenPath;
|
|
82
|
+
return {
|
|
83
|
+
codegenPath,
|
|
84
|
+
typesOutput: path4.join(codegenPath, GENERATED_BASENAMES.types),
|
|
85
|
+
instanceOutput: path4.join(codegenPath, GENERATED_BASENAMES.instance),
|
|
86
|
+
namespaceLoadersOutput: path4.join(codegenPath, GENERATED_BASENAMES.namespaceLoaders),
|
|
87
|
+
dictionarySchemaOutput: path4.join(codegenPath, GENERATED_BASENAMES.dictionarySchema),
|
|
88
|
+
artifactsPath: config.artifactsPath ?? codegenPath,
|
|
89
|
+
paramsTypeName: typeNames.paramsTypeName,
|
|
90
|
+
schemaTypeName: typeNames.schemaTypeName,
|
|
91
|
+
localeTypeName: typeNames.localeTypeName,
|
|
92
|
+
localeFallbackConstName: DEFAULT_LOCALE_FALLBACK_CONST_NAME,
|
|
93
|
+
factoryName: DEFAULT_FACTORY_NAME,
|
|
94
|
+
deliveryAreaTypeName: `${config.projectName}DeliveryArea`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function resolveArtifactsPath(config) {
|
|
98
|
+
return config.artifactsPath ?? config.codegenPath;
|
|
99
|
+
}
|
|
100
|
+
function formatCodegenConfigIssues(error) {
|
|
101
|
+
const issueLines = error.issues.map((issue) => {
|
|
102
|
+
const path7 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
103
|
+
return ` - ${path7}: ${issue.message}`;
|
|
104
|
+
});
|
|
105
|
+
return [
|
|
106
|
+
"[Codegen Error] Invalid i18n.codegen.json:",
|
|
107
|
+
...issueLines,
|
|
108
|
+
"",
|
|
109
|
+
`Allowed keys: ${codegenConfigKeys.join(", ")}`
|
|
110
|
+
].join("\n");
|
|
111
|
+
}
|
|
112
|
+
var localeFallbackSchema, deliveryArtifactsSchema, DELIVERY_MODES, LOADER_STRATEGIES, PROJECT_NAME_PATTERN, codegenConfigShape, codegenConfigKeys, codegenConfigSchema, GENERATED_BASENAMES, DEFAULT_FACTORY_NAME, DEFAULT_LOCALE_FALLBACK_CONST_NAME;
|
|
113
|
+
var init_codegen_config_schema = __esm({
|
|
114
|
+
"src/codegen/codegen-config-schema.ts"() {
|
|
115
|
+
init_constants();
|
|
116
|
+
init_delivery_artifacts();
|
|
117
|
+
init_type_names();
|
|
118
|
+
localeFallbackSchema = z.record(z.string(), z.union([z.string(), z.null()]));
|
|
119
|
+
deliveryArtifactsSchema = z.record(z.string(), z.array(z.string().min(1)));
|
|
120
|
+
DELIVERY_MODES = ["split-by-locale", "custom"];
|
|
121
|
+
LOADER_STRATEGIES = ["import", "fetch"];
|
|
122
|
+
PROJECT_NAME_PATTERN = /^[A-Z][a-zA-Z0-9]*$/;
|
|
123
|
+
codegenConfigShape = {
|
|
124
|
+
/** PascalCase project id — generates `{project}Params`, `{project}Schema`, `{project}Locale`. */
|
|
125
|
+
projectName: z.string().min(1).regex(PROJECT_NAME_PATTERN, 'projectName must be PascalCase (e.g. "MyApp")'),
|
|
126
|
+
namespaces: z.record(z.string(), z.string().min(1)),
|
|
127
|
+
/**
|
|
128
|
+
* Directory for generated TypeScript modules:
|
|
129
|
+
* `i18n-types.generated.ts`, `instance.generated.ts`,
|
|
130
|
+
* `namespace-loaders.generated.ts`, `dictionary-schema.generated.ts`.
|
|
131
|
+
*/
|
|
132
|
+
codegenPath: z.string().min(1),
|
|
133
|
+
localeFallback: localeFallbackSchema.optional(),
|
|
134
|
+
delivery: z.enum(DELIVERY_MODES).optional().default("split-by-locale"),
|
|
135
|
+
deliveryArtifacts: deliveryArtifactsSchema.optional(),
|
|
136
|
+
/** Directory for delivery JSON (`translations/`). Defaults to {@link codegenPath}. */
|
|
137
|
+
artifactsPath: z.string().min(1).optional(),
|
|
138
|
+
/**
|
|
139
|
+
* How generated `namespaceLoaders` resolve artifacts.
|
|
140
|
+
* - `import` (default): dynamic `import()` — JSON is bundled; content updates need a rebuild.
|
|
141
|
+
* - `fetch`: runtime `fetchImpl({ locale, namespace, area? })` via required `createI18n({ fetchImpl })`.
|
|
142
|
+
* Codegen does not know URLs — mapping id → transport is an application concern.
|
|
143
|
+
*/
|
|
144
|
+
loaderStrategy: z.enum(LOADER_STRATEGIES).optional().default("import")
|
|
145
|
+
};
|
|
146
|
+
codegenConfigKeys = Object.keys(
|
|
147
|
+
codegenConfigShape
|
|
148
|
+
);
|
|
149
|
+
codegenConfigSchema = z.object(codegenConfigShape).strict().superRefine((config, ctx) => {
|
|
150
|
+
for (const namespace of Object.keys(config.namespaces)) {
|
|
151
|
+
if (!IDENTIFIER_NAME_PATTERN.test(namespace)) {
|
|
152
|
+
ctx.addIssue({
|
|
153
|
+
code: "custom",
|
|
154
|
+
path: ["namespaces", namespace],
|
|
155
|
+
message: `Invalid namespace name "${namespace}" (${IDENTIFIER_NAME_REQUIREMENT}).`
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (config.delivery === "custom") {
|
|
160
|
+
if (!config.deliveryArtifacts) {
|
|
161
|
+
ctx.addIssue({
|
|
162
|
+
code: "custom",
|
|
163
|
+
path: ["deliveryArtifacts"],
|
|
164
|
+
message: 'deliveryArtifacts is required when delivery is "custom".'
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
for (const issue of getDeliveryArtifactsStructureIssues(config.deliveryArtifacts)) {
|
|
168
|
+
ctx.addIssue({
|
|
169
|
+
code: "custom",
|
|
170
|
+
path: issue.path,
|
|
171
|
+
message: issue.message
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} else if (config.deliveryArtifacts !== void 0) {
|
|
176
|
+
ctx.addIssue({
|
|
177
|
+
code: "custom",
|
|
178
|
+
path: ["deliveryArtifacts"],
|
|
179
|
+
message: 'deliveryArtifacts is only allowed when delivery is "custom".'
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
GENERATED_BASENAMES = {
|
|
184
|
+
types: "i18n-types.generated.ts",
|
|
185
|
+
instance: "instance.generated.ts",
|
|
186
|
+
namespaceLoaders: "namespace-loaders.generated.ts",
|
|
187
|
+
dictionarySchema: "dictionary-schema.generated.ts"
|
|
188
|
+
};
|
|
189
|
+
DEFAULT_FACTORY_NAME = "createI18n";
|
|
190
|
+
DEFAULT_LOCALE_FALLBACK_CONST_NAME = "LOCALE_FALLBACK";
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// src/codegen/config.ts
|
|
195
|
+
var config_exports = {};
|
|
196
|
+
__export(config_exports, {
|
|
197
|
+
DEFAULT_FACTORY_NAME: () => DEFAULT_FACTORY_NAME,
|
|
198
|
+
DEFAULT_LOCALE_FALLBACK_CONST_NAME: () => DEFAULT_LOCALE_FALLBACK_CONST_NAME,
|
|
199
|
+
GENERATED_BASENAMES: () => GENERATED_BASENAMES,
|
|
200
|
+
loadConfig: () => loadConfig,
|
|
201
|
+
resolveArtifactsPath: () => resolveArtifactsPath,
|
|
202
|
+
resolveCodegenPaths: () => resolveCodegenPaths,
|
|
203
|
+
resolveNamespaces: () => resolveNamespaces
|
|
204
|
+
});
|
|
205
|
+
function loadConfig(configPath) {
|
|
206
|
+
let raw;
|
|
207
|
+
try {
|
|
208
|
+
raw = JSON.parse(fs2.readFileSync(configPath, "utf8"));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
211
|
+
throw new Error(`[Codegen Error] Failed to parse config JSON (${configPath}): ${message}`);
|
|
212
|
+
}
|
|
213
|
+
const result = codegenConfigSchema.safeParse(raw);
|
|
214
|
+
if (!result.success) {
|
|
215
|
+
throw new Error(formatCodegenConfigIssues(result.error));
|
|
216
|
+
}
|
|
217
|
+
return result.data;
|
|
218
|
+
}
|
|
219
|
+
function resolveNamespaces(config) {
|
|
220
|
+
return Object.entries(config.namespaces).map(([namespace, filePath]) => ({
|
|
221
|
+
namespace,
|
|
222
|
+
filePath
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
var init_config = __esm({
|
|
226
|
+
"src/codegen/config.ts"() {
|
|
227
|
+
init_codegen_config_schema();
|
|
228
|
+
init_codegen_config_schema();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// src/resolve-locale.ts
|
|
233
|
+
function resolveLocaleTemplate(localeByKey, requestedLocale, fallbackMap) {
|
|
234
|
+
const chain = [requestedLocale];
|
|
235
|
+
let currentLocale = requestedLocale;
|
|
236
|
+
while (true) {
|
|
237
|
+
const template = localeByKey?.[currentLocale];
|
|
238
|
+
if (template !== void 0) {
|
|
239
|
+
return { template, resolvedLocale: currentLocale, fallbackChain: chain };
|
|
240
|
+
}
|
|
241
|
+
if (!fallbackMap) {
|
|
242
|
+
return void 0;
|
|
243
|
+
}
|
|
244
|
+
const fallback = fallbackMap[currentLocale];
|
|
245
|
+
if (fallback === void 0 || fallback === null) {
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
if (chain.includes(fallback)) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`[i18n] Circular locale fallback detected: ${[...chain, fallback].join(" \u2192 ")}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
chain.push(fallback);
|
|
254
|
+
currentLocale = fallback;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
var init_resolve_locale = __esm({
|
|
258
|
+
"src/resolve-locale.ts"() {
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// src/project-locales.ts
|
|
263
|
+
function classifyAreaLocales(areaLocales, localeFallback) {
|
|
264
|
+
const areaSet = new Set(areaLocales);
|
|
265
|
+
const full = [];
|
|
266
|
+
const preserve = [];
|
|
267
|
+
for (const locale of areaLocales) {
|
|
268
|
+
const fallback = localeFallback?.[locale];
|
|
269
|
+
if (fallback !== null && fallback !== void 0 && areaSet.has(fallback)) {
|
|
270
|
+
preserve.push(locale);
|
|
271
|
+
} else {
|
|
272
|
+
full.push(locale);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return { full, preserve };
|
|
276
|
+
}
|
|
277
|
+
function projectKeyDictionary(dictionary, locales, localeFallback) {
|
|
278
|
+
const targetLocales = [...new Set(locales)];
|
|
279
|
+
const result = {};
|
|
280
|
+
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
281
|
+
const projected = {};
|
|
282
|
+
for (const locale of targetLocales) {
|
|
283
|
+
const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);
|
|
284
|
+
if (resolved !== void 0) {
|
|
285
|
+
projected[locale] = resolved.template;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (Object.keys(projected).length > 0) {
|
|
289
|
+
result[key] = projected;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
function projectNamespaceLocalesCore(dictionary, locales, localeFallback) {
|
|
295
|
+
return projectKeyDictionary(dictionary, locales, localeFallback);
|
|
296
|
+
}
|
|
297
|
+
function projectNamespaceForDeliveryAreaCore(dictionary, areaLocales, localeFallback) {
|
|
298
|
+
const { full, preserve } = classifyAreaLocales(areaLocales, localeFallback);
|
|
299
|
+
const fullProjection = full.length > 0 ? projectKeyDictionary(dictionary, full, localeFallback) : void 0;
|
|
300
|
+
const result = {};
|
|
301
|
+
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
302
|
+
const projected = {};
|
|
303
|
+
if (fullProjection !== void 0) {
|
|
304
|
+
const fullEntry = fullProjection[key];
|
|
305
|
+
if (fullEntry !== void 0) {
|
|
306
|
+
Object.assign(projected, fullEntry);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
for (const locale of preserve) {
|
|
310
|
+
const template = localesByKey[locale];
|
|
311
|
+
if (template !== void 0) {
|
|
312
|
+
projected[locale] = template;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (Object.keys(projected).length > 0) {
|
|
316
|
+
result[key] = projected;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
var init_project_locales = __esm({
|
|
322
|
+
"src/project-locales.ts"() {
|
|
323
|
+
init_resolve_locale();
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
function relativePosixPath(from, to) {
|
|
327
|
+
return path4.relative(from, to).replace(/\\/g, "/");
|
|
328
|
+
}
|
|
329
|
+
var init_paths = __esm({
|
|
330
|
+
"src/codegen/paths.ts"() {
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
function writeFileIfChanged(absolutePath, content) {
|
|
334
|
+
if (fs2.existsSync(absolutePath)) {
|
|
335
|
+
const currentContent = fs2.readFileSync(absolutePath, "utf8");
|
|
336
|
+
if (currentContent === content) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
fs2.mkdirSync(path4.dirname(absolutePath), { recursive: true });
|
|
341
|
+
fs2.writeFileSync(absolutePath, content);
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
var init_write_file_if_changed = __esm({
|
|
345
|
+
"src/codegen/write-file-if-changed.ts"() {
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// src/codegen/read-dictionary.ts
|
|
350
|
+
var read_dictionary_exports = {};
|
|
351
|
+
__export(read_dictionary_exports, {
|
|
352
|
+
SUPPORTED_DICTIONARY_EXTENSIONS: () => SUPPORTED_DICTIONARY_EXTENSIONS,
|
|
353
|
+
getDictionaryFormat: () => getDictionaryFormat,
|
|
354
|
+
prepareDictionaryEntries: () => prepareDictionaryEntries,
|
|
355
|
+
readDictionaryFile: () => readDictionaryFile,
|
|
356
|
+
resolveAreaJsonPath: () => resolveAreaJsonPath,
|
|
357
|
+
resolveCompiledJsonPath: () => resolveCompiledJsonPath,
|
|
358
|
+
resolveSplitJsonPath: () => resolveSplitJsonPath,
|
|
359
|
+
splitDictionaryByDeliveryArea: () => splitDictionaryByDeliveryArea,
|
|
360
|
+
splitDictionaryByLocale: () => splitDictionaryByLocale
|
|
361
|
+
});
|
|
362
|
+
function getDictionaryFormat(filePath) {
|
|
363
|
+
const extension = path4.extname(filePath).toLowerCase();
|
|
364
|
+
if (extension === ".json") {
|
|
365
|
+
return "json";
|
|
366
|
+
}
|
|
367
|
+
if (extension === ".yaml" || extension === ".yml") {
|
|
368
|
+
return "yaml";
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function resolveCompiledJsonPath(sourcePath, generatedDirRelative) {
|
|
373
|
+
const baseName = `${path4.basename(sourcePath, path4.extname(sourcePath))}.json`;
|
|
374
|
+
return path4.join(generatedDirRelative, "translations", baseName).replace(/\\/g, "/");
|
|
375
|
+
}
|
|
376
|
+
function resolveSplitJsonPath(sourcePath, locale, generatedDirRelative) {
|
|
377
|
+
const baseName = path4.basename(sourcePath, path4.extname(sourcePath));
|
|
378
|
+
return path4.join(generatedDirRelative, "translations", `${baseName}.${locale}.json`).replace(/\\/g, "/");
|
|
379
|
+
}
|
|
380
|
+
function resolveAreaJsonPath(sourcePath, area, generatedDirRelative) {
|
|
381
|
+
const baseName = path4.basename(sourcePath, path4.extname(sourcePath));
|
|
382
|
+
return path4.join(generatedDirRelative, "translations", `${baseName}.${area}.json`).replace(/\\/g, "/");
|
|
383
|
+
}
|
|
384
|
+
function splitDictionaryByLocale(dictionary, locales, localeFallback) {
|
|
385
|
+
const byLocale = {};
|
|
386
|
+
for (const locale of locales) {
|
|
387
|
+
byLocale[locale] = projectNamespaceLocalesCore(dictionary, [locale], localeFallback);
|
|
388
|
+
}
|
|
389
|
+
return byLocale;
|
|
390
|
+
}
|
|
391
|
+
function splitDictionaryByDeliveryArea(dictionary, deliveryArtifacts, localeFallback) {
|
|
392
|
+
const byArea = {};
|
|
393
|
+
for (const [area, areaLocales] of Object.entries(deliveryArtifacts)) {
|
|
394
|
+
byArea[area] = projectNamespaceForDeliveryAreaCore(dictionary, areaLocales, localeFallback);
|
|
395
|
+
}
|
|
396
|
+
return byArea;
|
|
397
|
+
}
|
|
398
|
+
function assertDictionaryShape(value, context) {
|
|
399
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
400
|
+
throw new Error(`[Codegen Error] ${context} must be a plain object.`);
|
|
401
|
+
}
|
|
402
|
+
for (const [key, localesByKey] of Object.entries(value)) {
|
|
403
|
+
if (!IDENTIFIER_NAME_PATTERN.test(key)) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`[Codegen Error] ${context}: invalid key "${key}" (${IDENTIFIER_NAME_REQUIREMENT}).`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (localesByKey === null || typeof localesByKey !== "object" || Array.isArray(localesByKey)) {
|
|
409
|
+
throw new Error(`[Codegen Error] ${context}: key "${key}" must map locales to strings.`);
|
|
410
|
+
}
|
|
411
|
+
for (const [locale, template] of Object.entries(localesByKey)) {
|
|
412
|
+
if (typeof template !== "string") {
|
|
413
|
+
throw new Error(
|
|
414
|
+
`[Codegen Error] ${context}: key "${key}", locale "${locale}" must be a string.`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function readDictionaryFile(absolutePath) {
|
|
421
|
+
const format = getDictionaryFormat(absolutePath);
|
|
422
|
+
if (!format) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`[Codegen Error] Unsupported dictionary format "${path4.extname(absolutePath)}" for ${absolutePath}. Use .json, .yaml, or .yml.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const source = fs2.readFileSync(absolutePath, "utf8");
|
|
428
|
+
let parsed;
|
|
429
|
+
try {
|
|
430
|
+
parsed = format === "json" ? JSON.parse(source) : parse(source);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
433
|
+
throw new Error(`[Codegen Error] Failed to parse dictionary ${absolutePath}: ${message}`);
|
|
434
|
+
}
|
|
435
|
+
assertDictionaryShape(parsed, `Dictionary file ${absolutePath}`);
|
|
436
|
+
return parsed;
|
|
437
|
+
}
|
|
438
|
+
function writeCompiledJson(absoluteJsonPath, dictionary) {
|
|
439
|
+
return writeFileIfChanged(absoluteJsonPath, `${JSON.stringify(dictionary, null, 2)}
|
|
440
|
+
`);
|
|
441
|
+
}
|
|
442
|
+
function prepareDictionaryEntries(projectRoot, entries, generatedDirRelative, options) {
|
|
443
|
+
const delivery = options.delivery;
|
|
444
|
+
const { dictionariesByNamespace, localeFallback, requestLocales, deliveryArtifacts } = options;
|
|
445
|
+
const resolvedEntries = [];
|
|
446
|
+
const splitPathsByNamespace = {};
|
|
447
|
+
const compiledFiles = [];
|
|
448
|
+
if (delivery === "split-by-locale" && (!requestLocales || requestLocales.length === 0)) {
|
|
449
|
+
throw new Error(
|
|
450
|
+
"[Codegen Error] split-by-locale delivery requires at least one request locale."
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (delivery === "custom" && (!deliveryArtifacts || Object.keys(deliveryArtifacts).length === 0)) {
|
|
454
|
+
throw new Error("[Codegen Error] custom delivery requires deliveryArtifacts.");
|
|
455
|
+
}
|
|
456
|
+
for (const entry of entries) {
|
|
457
|
+
const sourceAbsolutePath = path4.resolve(projectRoot, entry.filePath);
|
|
458
|
+
const format = getDictionaryFormat(entry.filePath);
|
|
459
|
+
if (!format) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`[Codegen Error] Namespace "${entry.namespace}" uses unsupported dictionary extension in "${entry.filePath}". Use .json, .yaml, or .yml.`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
const dictionary = dictionariesByNamespace[entry.namespace];
|
|
465
|
+
if (!dictionary) {
|
|
466
|
+
throw new Error(
|
|
467
|
+
`[Codegen Error] Missing parsed dictionary for namespace "${entry.namespace}".`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
if (delivery === "split-by-locale") {
|
|
471
|
+
const splitPaths2 = {};
|
|
472
|
+
const dictionariesByLocale = splitDictionaryByLocale(
|
|
473
|
+
dictionary,
|
|
474
|
+
requestLocales,
|
|
475
|
+
localeFallback
|
|
476
|
+
);
|
|
477
|
+
for (const locale of requestLocales) {
|
|
478
|
+
const splitRelativePath = resolveSplitJsonPath(
|
|
479
|
+
entry.filePath,
|
|
480
|
+
locale,
|
|
481
|
+
generatedDirRelative
|
|
482
|
+
);
|
|
483
|
+
const splitAbsolutePath = path4.resolve(projectRoot, splitRelativePath);
|
|
484
|
+
const wroteFile = writeCompiledJson(splitAbsolutePath, dictionariesByLocale[locale]);
|
|
485
|
+
if (wroteFile) {
|
|
486
|
+
compiledFiles.push(
|
|
487
|
+
`${relativePosixPath(projectRoot, sourceAbsolutePath)} \u2192 ${splitRelativePath}`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
splitPaths2[locale] = splitRelativePath;
|
|
491
|
+
}
|
|
492
|
+
splitPathsByNamespace[entry.namespace] = splitPaths2;
|
|
493
|
+
resolvedEntries.push(entry);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const splitPaths = {};
|
|
497
|
+
const dictionariesByArea = splitDictionaryByDeliveryArea(
|
|
498
|
+
dictionary,
|
|
499
|
+
deliveryArtifacts,
|
|
500
|
+
localeFallback
|
|
501
|
+
);
|
|
502
|
+
for (const area of Object.keys(deliveryArtifacts).sort()) {
|
|
503
|
+
const areaRelativePath = resolveAreaJsonPath(entry.filePath, area, generatedDirRelative);
|
|
504
|
+
const areaAbsolutePath = path4.resolve(projectRoot, areaRelativePath);
|
|
505
|
+
const wroteFile = writeCompiledJson(areaAbsolutePath, dictionariesByArea[area]);
|
|
506
|
+
if (wroteFile) {
|
|
507
|
+
compiledFiles.push(
|
|
508
|
+
`${relativePosixPath(projectRoot, sourceAbsolutePath)} \u2192 ${areaRelativePath}`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
splitPaths[area] = areaRelativePath;
|
|
512
|
+
}
|
|
513
|
+
splitPathsByNamespace[entry.namespace] = splitPaths;
|
|
514
|
+
resolvedEntries.push(entry);
|
|
515
|
+
}
|
|
516
|
+
return { resolvedEntries, splitPathsByNamespace, compiledFiles };
|
|
517
|
+
}
|
|
518
|
+
var SUPPORTED_DICTIONARY_EXTENSIONS;
|
|
519
|
+
var init_read_dictionary = __esm({
|
|
520
|
+
"src/codegen/read-dictionary.ts"() {
|
|
521
|
+
init_constants();
|
|
522
|
+
init_project_locales();
|
|
523
|
+
init_paths();
|
|
524
|
+
init_write_file_if_changed();
|
|
525
|
+
SUPPORTED_DICTIONARY_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// src/audit/run-audit.ts
|
|
530
|
+
init_config();
|
|
531
|
+
|
|
532
|
+
// src/codegen/locale-fallback.ts
|
|
533
|
+
init_resolve_locale();
|
|
534
|
+
function collectRequestLocales(dictionaryLocales, fallback) {
|
|
535
|
+
const all = new Set(dictionaryLocales);
|
|
536
|
+
if (!fallback) {
|
|
537
|
+
return all;
|
|
538
|
+
}
|
|
539
|
+
for (const [locale, target] of Object.entries(fallback)) {
|
|
540
|
+
all.add(locale);
|
|
541
|
+
if (target !== null) {
|
|
542
|
+
all.add(target);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return all;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/codegen/locale-policy.ts
|
|
549
|
+
function buildRequiredLocales(dictionaryLocales, configFallback) {
|
|
550
|
+
return [...collectRequestLocales(dictionaryLocales, configFallback)].sort();
|
|
551
|
+
}
|
|
552
|
+
function enrichLocaleFallback(dictionaryLocales, configFallback) {
|
|
553
|
+
const enriched = { ...configFallback };
|
|
554
|
+
for (const locale of buildRequiredLocales(dictionaryLocales, configFallback)) {
|
|
555
|
+
if (!(locale in enriched)) {
|
|
556
|
+
enriched[locale] = null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const sortedEntries = Object.entries(enriched).sort(
|
|
560
|
+
([left], [right]) => left.localeCompare(right)
|
|
561
|
+
);
|
|
562
|
+
return Object.fromEntries(sortedEntries);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// src/audit/audit-dictionaries.ts
|
|
566
|
+
init_resolve_locale();
|
|
567
|
+
function isDirectlyMissing(localesByKey, locale, treatEmptyAsMissing) {
|
|
568
|
+
const template = localesByKey[locale];
|
|
569
|
+
if (template === void 0) {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
return treatEmptyAsMissing && template === "";
|
|
573
|
+
}
|
|
574
|
+
function isEffectivelyMissing(localesByKey, locale, localeFallback, treatEmptyAsMissing) {
|
|
575
|
+
const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);
|
|
576
|
+
if (resolved === void 0) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
return treatEmptyAsMissing && resolved.template === "";
|
|
580
|
+
}
|
|
581
|
+
function collectDictionaryLocales(namespaces) {
|
|
582
|
+
const locales = /* @__PURE__ */ new Set();
|
|
583
|
+
for (const dictionary of Object.values(namespaces)) {
|
|
584
|
+
for (const localesByKey of Object.values(dictionary)) {
|
|
585
|
+
for (const locale of Object.keys(localesByKey)) {
|
|
586
|
+
locales.add(locale);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return locales;
|
|
591
|
+
}
|
|
592
|
+
function auditDictionaries(options) {
|
|
593
|
+
const { namespaces, config, treatEmptyAsMissing = true } = options;
|
|
594
|
+
const dictionaryLocales = collectDictionaryLocales(namespaces);
|
|
595
|
+
const requiredLocales = buildRequiredLocales(dictionaryLocales, config.localeFallback);
|
|
596
|
+
const localeFallbackForAudit = config.localeFallback ? enrichLocaleFallback(dictionaryLocales, config.localeFallback) : void 0;
|
|
597
|
+
const summary = {};
|
|
598
|
+
const missingDirectByLocale = {};
|
|
599
|
+
const missingEffectiveByLocale = {};
|
|
600
|
+
for (const [namespace, dictionary] of Object.entries(namespaces)) {
|
|
601
|
+
summary[namespace] = {};
|
|
602
|
+
missingDirectByLocale[namespace] = {};
|
|
603
|
+
missingEffectiveByLocale[namespace] = {};
|
|
604
|
+
for (const locale of requiredLocales) {
|
|
605
|
+
summary[namespace][locale] = { missingDirect: 0, missingEffective: 0 };
|
|
606
|
+
missingDirectByLocale[namespace][locale] = [];
|
|
607
|
+
missingEffectiveByLocale[namespace][locale] = [];
|
|
608
|
+
}
|
|
609
|
+
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
610
|
+
for (const locale of requiredLocales) {
|
|
611
|
+
if (isDirectlyMissing(localesByKey, locale, treatEmptyAsMissing)) {
|
|
612
|
+
summary[namespace][locale].missingDirect += 1;
|
|
613
|
+
missingDirectByLocale[namespace][locale].push(key);
|
|
614
|
+
}
|
|
615
|
+
if (isEffectivelyMissing(localesByKey, locale, localeFallbackForAudit, treatEmptyAsMissing)) {
|
|
616
|
+
summary[namespace][locale].missingEffective += 1;
|
|
617
|
+
missingEffectiveByLocale[namespace][locale].push(key);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (const locale of requiredLocales) {
|
|
622
|
+
missingDirectByLocale[namespace][locale].sort();
|
|
623
|
+
missingEffectiveByLocale[namespace][locale].sort();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
requiredLocales,
|
|
628
|
+
...localeFallbackForAudit !== void 0 ? { localeFallback: localeFallbackForAudit } : {},
|
|
629
|
+
summary,
|
|
630
|
+
missingDirectByLocale,
|
|
631
|
+
missingEffectiveByLocale
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function reportHasGaps(report, criterion) {
|
|
635
|
+
for (const namespace of Object.keys(report.missingDirectByLocale)) {
|
|
636
|
+
for (const locale of report.requiredLocales) {
|
|
637
|
+
const direct = report.missingDirectByLocale[namespace]?.[locale] ?? [];
|
|
638
|
+
const effective = report.missingEffectiveByLocale[namespace]?.[locale] ?? [];
|
|
639
|
+
if (criterion === "direct" && direct.length > 0) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (criterion === "effective" && effective.length > 0) {
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
if (criterion === "any" && (direct.length > 0 || effective.length > 0)) {
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
async function runAuditFromConfig(options) {
|
|
653
|
+
const { resolveNamespaces: resolveNamespaces2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
654
|
+
const { readDictionaryFile: readDictionaryFile2 } = await Promise.resolve().then(() => (init_read_dictionary(), read_dictionary_exports));
|
|
655
|
+
const config = options.config;
|
|
656
|
+
const entries = resolveNamespaces2(config);
|
|
657
|
+
const namespaces = {};
|
|
658
|
+
for (const entry of entries) {
|
|
659
|
+
const absolutePath = path4.resolve(options.projectRoot, entry.filePath);
|
|
660
|
+
namespaces[entry.namespace] = readDictionaryFile2(absolutePath);
|
|
661
|
+
}
|
|
662
|
+
const report = auditDictionaries({
|
|
663
|
+
namespaces,
|
|
664
|
+
config,
|
|
665
|
+
...options.treatEmptyAsMissing !== void 0 ? { treatEmptyAsMissing: options.treatEmptyAsMissing } : {}
|
|
666
|
+
});
|
|
667
|
+
const exitCode = options.failOn && reportHasGaps(report, options.failOn) ? 1 : 0;
|
|
668
|
+
return { report, exitCode };
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/audit/run-audit.ts
|
|
672
|
+
var FAIL_ON_VALUES = /* @__PURE__ */ new Set(["effective", "direct", "any"]);
|
|
673
|
+
function parseAuditArgs(argv) {
|
|
674
|
+
const configArgIndex = argv.indexOf("--config");
|
|
675
|
+
const outArgIndex = argv.indexOf("--out");
|
|
676
|
+
const failOnArgIndex = argv.indexOf("--fail-on");
|
|
677
|
+
const configPath = path4.resolve(
|
|
678
|
+
process.cwd(),
|
|
679
|
+
configArgIndex >= 0 ? argv[configArgIndex + 1] : "i18n/i18n.codegen.json"
|
|
680
|
+
);
|
|
681
|
+
const outPath = outArgIndex >= 0 ? path4.resolve(process.cwd(), argv[outArgIndex + 1]) : void 0;
|
|
682
|
+
let failOn;
|
|
683
|
+
if (failOnArgIndex >= 0) {
|
|
684
|
+
const value = argv[failOnArgIndex + 1];
|
|
685
|
+
if (!value || !FAIL_ON_VALUES.has(value)) {
|
|
686
|
+
throw new Error(`[Audit Error] --fail-on must be one of: effective, direct, any`);
|
|
687
|
+
}
|
|
688
|
+
failOn = value;
|
|
689
|
+
}
|
|
690
|
+
const treatEmptyAsMissing = !argv.includes("--allow-empty");
|
|
691
|
+
return {
|
|
692
|
+
configPath,
|
|
693
|
+
treatEmptyAsMissing,
|
|
694
|
+
...outPath !== void 0 ? { outPath } : {},
|
|
695
|
+
...failOn !== void 0 ? { failOn } : {}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
async function runAuditCli(argv = process.argv.slice(2)) {
|
|
699
|
+
let options;
|
|
700
|
+
let config;
|
|
701
|
+
try {
|
|
702
|
+
options = parseAuditArgs(argv);
|
|
703
|
+
if (!fs2.existsSync(options.configPath)) {
|
|
704
|
+
console.error(`[Audit Error] Config file not found: ${options.configPath}`);
|
|
705
|
+
return 2;
|
|
706
|
+
}
|
|
707
|
+
config = loadConfig(options.configPath);
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
710
|
+
return 2;
|
|
711
|
+
}
|
|
712
|
+
const projectRoot = path4.dirname(options.configPath);
|
|
713
|
+
const { report, exitCode } = await runAuditFromConfig({
|
|
714
|
+
projectRoot,
|
|
715
|
+
config,
|
|
716
|
+
treatEmptyAsMissing: options.treatEmptyAsMissing,
|
|
717
|
+
...options.failOn !== void 0 ? { failOn: options.failOn } : {}
|
|
718
|
+
});
|
|
719
|
+
const json = `${JSON.stringify(report, null, 2)}
|
|
720
|
+
`;
|
|
721
|
+
if (options.outPath) {
|
|
722
|
+
fs2.mkdirSync(path4.dirname(options.outPath), { recursive: true });
|
|
723
|
+
fs2.writeFileSync(options.outPath, json);
|
|
724
|
+
} else {
|
|
725
|
+
process.stdout.write(json);
|
|
726
|
+
}
|
|
727
|
+
return exitCode;
|
|
728
|
+
}
|
|
729
|
+
async function main() {
|
|
730
|
+
const exitCode = await runAuditCli();
|
|
731
|
+
process.exit(exitCode);
|
|
732
|
+
}
|
|
733
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === path4.resolve(process.argv[1])) {
|
|
734
|
+
void main();
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export { parseAuditArgs, runAuditCli };
|
|
738
|
+
//# sourceMappingURL=audit.js.map
|
|
739
|
+
//# sourceMappingURL=audit.js.map
|