@standards/cli 1.0.0-alpha.401 → 1.0.0-alpha.403

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/bin.mjs CHANGED
@@ -351,6 +351,15 @@ function registerCodemodCommand(program) {
351
351
  for (const failure of report.failures) process.stderr.write(`${failure.message}\n`);
352
352
  if (report.skipped.length > 0 || report.failures.length > 0) process.exitCode = 1;
353
353
  });
354
+ codemod.command("locale").description("Rewrite a whole dictionary passed as translations to locale, and DEFAULT_*_TRANSLATIONS to EN_*").argument("[paths...]", "files or directories to rewrite", ["."]).option("--dry-run", "report without writing").action(async (paths, opts) => {
355
+ const { runLocale } = await import("./locale-CfZalwmA.mjs");
356
+ const report = await runLocale(paths, { write: !opts.dryRun });
357
+ const verb = opts.dryRun ? "Would rewrite" : "Rewrote";
358
+ process.stdout.write(`${verb} ${report.changed} references in ${report.files} files.\n`);
359
+ for (const { file, line, column } of report.skipped) process.stdout.write(`${file}:${line}:${column} translations pass locale, keep only your overrides\n`);
360
+ for (const failure of report.failures) process.stderr.write(`${failure.message}\n`);
361
+ if (report.skipped.length > 0 || report.failures.length > 0) process.exitCode = 1;
362
+ });
354
363
  codemod.command("engine-imports").description("Move imports of names that left @standards/schema to @standards/engine").argument("[paths...]", "files or directories to rewrite", ["."]).option("--dry-run", "report without writing").option("--root <dir>", "where @standards/schema and @standards/engine are installed", ".").action(async (paths, opts) => {
355
364
  const { runEngineImports } = await import("./engine-imports-K-yySYo3.mjs");
356
365
  const report = await runEngineImports(paths, {
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+ /*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
3
+ import { i as rewriteSourceFiles, n as parseSourceFile, t as applyEdits } from "./source-files-D2Bdfvhx.mjs";
4
+ import ts from "typescript";
5
+ //#region src/codemod/locale.ts
6
+ /** Where the English values went once `DEFAULT_*_TRANSLATIONS` left the package roots. */
7
+ const EN_ENTRY = "@standards/react/locales/en";
8
+ const FORMER_HOMES = /* @__PURE__ */ new Set([
9
+ "@standards/react",
10
+ "@standards/ui",
11
+ EN_ENTRY
12
+ ]);
13
+ const LOCALE_ENTRY = /^@standards\/react\/locales\/(en|fr)$/;
14
+ const RENAMED = { EN_SETTINGS_DIALOG_TRANSLATIONS: "EN_ROLES_SETTINGS_TRANSLATIONS" };
15
+ /** A whole dictionary, by its exported name, and the locale it stands for. */
16
+ const DICTIONARIES = {
17
+ EN_TRANSLATIONS: "en",
18
+ FR_TRANSLATIONS: "fr"
19
+ };
20
+ /** The `@standards/react/locales/en` name of a removed or renamed export. */
21
+ function englishName(name) {
22
+ const match = /^DEFAULT_([A-Z_]*)TRANSLATIONS$/.exec(name);
23
+ return match ? `EN_${match[1]}TRANSLATIONS` : RENAMED[name];
24
+ }
25
+ /** `translations: X` or `translations={X}`, with the expression it passes. */
26
+ function translationsValue(node) {
27
+ if (ts.isPropertyAssignment(node) && node.name.getText() === "translations") return {
28
+ value: node.initializer,
29
+ jsx: false
30
+ };
31
+ if (ts.isJsxAttribute(node) && node.name.getText() === "translations" && node.initializer !== void 0 && ts.isJsxExpression(node.initializer) && node.initializer.expression !== void 0) return {
32
+ value: node.initializer.expression,
33
+ jsx: true
34
+ };
35
+ }
36
+ /** An identifier that reads a binding: not a property name, not an import specifier. */
37
+ function isReference(node) {
38
+ const { parent } = node;
39
+ if (ts.isPropertyAccessExpression(parent)) return parent.expression === node;
40
+ if (ts.isPropertyAssignment(parent) || ts.isPropertySignature(parent)) return parent.name !== node;
41
+ if (ts.isExportSpecifier(parent)) {
42
+ if (parent.parent.parent.moduleSpecifier !== void 0) return false;
43
+ return (parent.propertyName ?? parent.name) === node;
44
+ }
45
+ return !ts.isImportSpecifier(parent);
46
+ }
47
+ /**
48
+ * The text that reads `renamed` where `node` stood, keeping what the old name
49
+ * also spelled: a shorthand property's key, a re-export's exported name.
50
+ */
51
+ function renamedReference(node, renamed) {
52
+ const { parent } = node;
53
+ if (ts.isShorthandPropertyAssignment(parent)) return `${node.text}: ${renamed}`;
54
+ if (ts.isExportSpecifier(parent) && parent.propertyName === void 0) return `${renamed} as ${node.text}`;
55
+ return renamed;
56
+ }
57
+ /** One import declaration, written back with the bindings that stay in each module. */
58
+ function importDeclaration(node, specifier, stay, go) {
59
+ const clause = node.importClause;
60
+ const typeOnly = clause?.isTypeOnly ? " type" : "";
61
+ const defaultName = clause?.name?.text;
62
+ const line = (names, from, withDefault) => {
63
+ const head = [withDefault ? defaultName : void 0, names.length > 0 ? `{ ${names.join(", ")} }` : void 0].filter((part) => part !== void 0).join(", ");
64
+ return head === "" ? "" : `import${typeOnly} ${head} from "${from}";`;
65
+ };
66
+ if (specifier === EN_ENTRY) return line([...stay, ...go], EN_ENTRY, true);
67
+ if (stay.length === 0 && defaultName === void 0) return line(go, EN_ENTRY, false);
68
+ return [line(stay, specifier, true), line(go, EN_ENTRY, false)].filter(Boolean).join("\n");
69
+ }
70
+ /**
71
+ * Moves `DEFAULT_*_TRANSLATIONS` imports to `EN_*` from `@standards/react/locales/en`,
72
+ * and turns a whole dictionary passed as `translations` into `locale`.
73
+ */
74
+ function rewriteLocale(text, fileName) {
75
+ const { file, error } = parseSourceFile(text, fileName);
76
+ if (error !== void 0) return {
77
+ output: text,
78
+ changed: 0,
79
+ skipped: [],
80
+ error
81
+ };
82
+ const edits = [];
83
+ const skipped = [];
84
+ let changed = 0;
85
+ /** Local name → the `EN_*` name its references take. */
86
+ const renames = /* @__PURE__ */ new Map();
87
+ /** Local name of a whole dictionary → its locale. */
88
+ const dictionaries = /* @__PURE__ */ new Map();
89
+ /** Each import naming one of these exports, rewritten once its references are counted. */
90
+ const imports = [];
91
+ const references = /* @__PURE__ */ new Map();
92
+ const replaced = /* @__PURE__ */ new Map();
93
+ for (const node of file.statements) {
94
+ if (!(ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier))) continue;
95
+ const specifier = node.moduleSpecifier.text;
96
+ const bindings = node.importClause?.namedBindings;
97
+ if (bindings === void 0 || !ts.isNamedImports(bindings)) continue;
98
+ const entry = {
99
+ node,
100
+ specifier,
101
+ stay: [],
102
+ go: []
103
+ };
104
+ let relevant = false;
105
+ for (const element of bindings.elements) {
106
+ const imported = (element.propertyName ?? element.name).text;
107
+ const local = element.name.text;
108
+ const english = FORMER_HOMES.has(specifier) ? englishName(imported) : void 0;
109
+ if (english === void 0) entry.stay.push({
110
+ local,
111
+ text: element.getText(file)
112
+ });
113
+ else if (element.propertyName !== void 0) entry.go.push({
114
+ local,
115
+ text: `${element.isTypeOnly ? "type " : ""}${english} as ${local}`
116
+ });
117
+ else {
118
+ renames.set(local, english);
119
+ entry.go.push({
120
+ local,
121
+ text: `${element.isTypeOnly ? "type " : ""}${english}`
122
+ });
123
+ }
124
+ const exported = english ?? imported;
125
+ if (exported in DICTIONARIES && (english !== void 0 || LOCALE_ENTRY.test(specifier))) {
126
+ dictionaries.set(local, DICTIONARIES[exported]);
127
+ relevant = true;
128
+ }
129
+ }
130
+ changed += entry.go.length;
131
+ if (entry.go.length > 0 || relevant) imports.push(entry);
132
+ }
133
+ function visit(node) {
134
+ if (ts.isImportDeclaration(node)) return;
135
+ const site = translationsValue(node);
136
+ if (site !== void 0) {
137
+ const name = ts.isIdentifier(site.value) ? site.value.text : void 0;
138
+ const locale = name === void 0 ? void 0 : dictionaries.get(name);
139
+ if (name !== void 0 && locale !== void 0) {
140
+ const text = site.jsx ? `locale="${locale}"` : `locale: "${locale}"`;
141
+ edits.push({
142
+ start: node.getStart(file),
143
+ end: node.getEnd(),
144
+ text
145
+ });
146
+ replaced.set(name, (replaced.get(name) ?? 0) + 1);
147
+ references.set(name, (references.get(name) ?? 0) + 1);
148
+ changed += 1;
149
+ return;
150
+ }
151
+ if (mentions(site.value, dictionaries)) {
152
+ const { line, character } = file.getLineAndCharacterOfPosition(node.getStart(file));
153
+ skipped.push({
154
+ line: line + 1,
155
+ column: character + 1
156
+ });
157
+ }
158
+ }
159
+ if (ts.isIdentifier(node) && isReference(node)) {
160
+ if (dictionaries.has(node.text)) references.set(node.text, (references.get(node.text) ?? 0) + 1);
161
+ const renamed = renames.get(node.text);
162
+ if (renamed !== void 0) {
163
+ edits.push({
164
+ start: node.getStart(file),
165
+ end: node.getEnd(),
166
+ text: renamedReference(node, renamed)
167
+ });
168
+ changed += 1;
169
+ }
170
+ }
171
+ ts.forEachChild(node, visit);
172
+ }
173
+ visit(file);
174
+ const unused = new Set([...dictionaries.keys()].filter((name) => {
175
+ const uses = replaced.get(name) ?? 0;
176
+ return uses > 0 && uses === references.get(name);
177
+ }));
178
+ for (const { node, specifier, stay, go } of imports) {
179
+ const kept = (bindings) => bindings.filter(({ local }) => !unused.has(local)).map(({ text }) => text);
180
+ const keptStay = kept(stay);
181
+ const keptGo = kept(go);
182
+ if (keptGo.length === 0 && go.length === 0 && keptStay.length === stay.length) continue;
183
+ const text = importDeclaration(node, specifier, keptStay, keptGo);
184
+ const end = node.getEnd();
185
+ edits.push({
186
+ start: node.getStart(file),
187
+ end: text === "" && file.text.charAt(end) === "\n" ? end + 1 : end,
188
+ text
189
+ });
190
+ }
191
+ return {
192
+ output: applyEdits(text, edits),
193
+ changed,
194
+ skipped
195
+ };
196
+ }
197
+ function mentions(node, names) {
198
+ if (ts.isIdentifier(node) && names.has(node.text)) return true;
199
+ return ts.forEachChild(node, (child) => mentions(child, names) || void 0) === true;
200
+ }
201
+ /** Walks `paths`, rewrites every TS or JS file, writes back when `write` is set. */
202
+ async function runLocale(paths, options) {
203
+ const report = {
204
+ files: 0,
205
+ changed: 0,
206
+ skipped: [],
207
+ failures: []
208
+ };
209
+ for (const { path, result } of await rewriteSourceFiles(paths, options.write, rewriteLocale)) {
210
+ if (result.error !== void 0) {
211
+ report.failures.push({
212
+ file: path,
213
+ message: result.error
214
+ });
215
+ continue;
216
+ }
217
+ for (const at of result.skipped) report.skipped.push({
218
+ file: path,
219
+ ...at
220
+ });
221
+ if (result.changed === 0) continue;
222
+ report.files += 1;
223
+ report.changed += result.changed;
224
+ }
225
+ return report;
226
+ }
227
+ //#endregion
228
+ export { runLocale };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standards/cli",
3
- "version": "1.0.0-alpha.401",
3
+ "version": "1.0.0-alpha.403",
4
4
  "description": "CLI tool to interact with Standards API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,9 +28,9 @@
28
28
  "jiti": "2.7.0",
29
29
  "typescript": "^5.6.3",
30
30
  "ws": "8.21.1",
31
- "@standards/client": "1.0.0-alpha.401",
32
- "@standards/constants": "1.0.0-alpha.401",
33
- "@standards/schema": "1.0.0-alpha.401"
31
+ "@standards/client": "1.0.0-alpha.403",
32
+ "@standards/constants": "1.0.0-alpha.403",
33
+ "@standards/schema": "1.0.0-alpha.403"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^25.6.0",