@babelize/engine 0.1.0 → 0.1.3
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/analysis/analyze.js +1 -1
- package/dist/detection/stack.d.ts +42 -0
- package/dist/detection/stack.js +277 -0
- package/dist/django/catalog.d.ts +92 -0
- package/dist/django/catalog.js +677 -0
- package/dist/django/extract.d.ts +20 -0
- package/dist/django/extract.js +79 -0
- package/dist/django/inject-file.d.ts +6 -0
- package/dist/django/inject-file.js +52 -0
- package/dist/django/inject.d.ts +11 -0
- package/dist/django/inject.js +205 -0
- package/dist/django/keys.d.ts +21 -0
- package/dist/django/keys.js +63 -0
- package/dist/django/locale-names.d.ts +13 -0
- package/dist/django/locale-names.js +60 -0
- package/dist/django/mo-file.d.ts +6 -0
- package/dist/django/mo-file.js +60 -0
- package/dist/django/plural-forms.d.ts +34 -0
- package/dist/django/plural-forms.js +216 -0
- package/dist/django/po-file.d.ts +38 -0
- package/dist/django/po-file.js +273 -0
- package/dist/django/python-extract.d.ts +34 -0
- package/dist/django/python-extract.js +619 -0
- package/dist/django/python-lexer.d.ts +65 -0
- package/dist/django/python-lexer.js +398 -0
- package/dist/django/template-extract.d.ts +32 -0
- package/dist/django/template-extract.js +680 -0
- package/dist/django/types.d.ts +49 -0
- package/dist/django/types.js +15 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +16 -2
- package/dist/local/pseudo-provider.js +5 -4
- package/dist/local/translate-core.d.ts +5 -2
- package/dist/local/translate-core.js +43 -5
- package/dist/local/translate-messages.js +7 -9
- package/dist/modules/extractor/extract.js +22 -14
- package/dist/modules/finalization/django-finalize.d.ts +15 -0
- package/dist/modules/finalization/django-finalize.js +156 -19
- package/dist/modules/finalization/finalize.d.ts +2 -0
- package/dist/modules/finalization/finalize.js +58 -30
- package/dist/modules/finalization/nextjs-finalize.d.ts +4 -1
- package/dist/modules/finalization/nextjs-finalize.js +8 -9
- package/dist/modules/finalization/writes.d.ts +29 -0
- package/dist/modules/finalization/writes.js +78 -0
- package/dist/modules/format-injection/index.js +3 -2
- package/dist/modules/glossary/index.d.ts +1 -2
- package/dist/modules/glossary/index.js +14 -6
- package/dist/modules/glossary/rules.d.ts +81 -0
- package/dist/modules/glossary/rules.js +244 -0
- package/dist/modules/injection/inject.js +14 -30
- package/dist/modules/keygen/keygen.js +7 -0
- package/dist/modules/locale-builder/locales.js +23 -0
- package/dist/modules/lockfile/index.d.ts +1 -1
- package/dist/modules/lockfile/index.js +2 -1
- package/dist/modules/lockfile/lockfile-manager.d.ts +9 -0
- package/dist/modules/lockfile/lockfile-manager.js +16 -1
- package/dist/modules/lockfile/schema.d.ts +42 -0
- package/dist/modules/lockfile/schema.js +11 -0
- package/dist/modules/translator/icu-branches.d.ts +138 -0
- package/dist/modules/translator/icu-branches.js +393 -0
- package/dist/modules/translator/translate.d.ts +40 -4
- package/dist/modules/translator/translate.js +297 -173
- package/dist/modules/workspace/validate.js +67 -32
- package/dist/orchestrator.d.ts +5 -0
- package/dist/orchestrator.js +125 -14
- package/dist/providers/types.d.ts +10 -2
- package/dist/qa/check-project.d.ts +2 -1
- package/dist/qa/check-project.js +50 -7
- package/dist/qa/gettext-files.d.ts +15 -0
- package/dist/qa/gettext-files.js +115 -0
- package/dist/qa/locale-qa.js +20 -3
- package/dist/runtime/config.js +1 -0
- package/dist/runtime/types.d.ts +64 -0
- package/dist/utils/extraction-options.d.ts +9 -3
- package/dist/utils/extraction-options.js +13 -3
- package/dist/utils/file-utils.js +7 -2
- package/dist/utils/icu-message.d.ts +2 -2
- package/dist/utils/icu-message.js +43 -7
- package/dist/utils/jsx-message.js +7 -0
- package/dist/utils/metadata.d.ts +4 -2
- package/dist/utils/metadata.js +16 -10
- package/dist/utils/not-injected.js +2 -0
- package/dist/utils/placeholder-protection.d.ts +18 -1
- package/dist/utils/placeholder-protection.js +58 -13
- package/dist/utils/translation-sanitizer.d.ts +1 -0
- package/dist/utils/translation-sanitizer.js +23 -9
- package/dist/utils/translator-load-balancer.js +5 -1
- package/dist/utils/untranslated.d.ts +15 -0
- package/dist/utils/untranslated.js +34 -0
- package/package.json +2 -6
- package/dist/injection/django/django-template-injector.d.ts +0 -29
- package/dist/injection/django/django-template-injector.js +0 -216
- package/dist/modules/glossary/glossary.d.ts +0 -9
- package/dist/modules/glossary/glossary.js +0 -27
- package/dist/modules/glossary/masker.d.ts +0 -23
- package/dist/modules/glossary/masker.js +0 -56
- package/dist/modules/locale-builder/po.d.ts +0 -33
- package/dist/modules/locale-builder/po.js +0 -131
- package/dist/modules/translator/translator.d.ts +0 -5
- package/dist/modules/translator/translator.js +0 -46
- package/dist/utils/dtl-mask.d.ts +0 -25
- package/dist/utils/dtl-mask.js +0 -58
- package/dist/utils/py-extractor.d.ts +0 -14
- package/dist/utils/py-extractor.js +0 -285
|
@@ -37,11 +37,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.resolveSettingsPath = resolveSettingsPath;
|
|
40
|
+
exports.resolveDjangoLocaleDir = resolveDjangoLocaleDir;
|
|
41
|
+
exports.parseLocalePaths = parseLocalePaths;
|
|
40
42
|
exports.finalizeDjangoSettings = finalizeDjangoSettings;
|
|
41
43
|
const path = __importStar(require("path"));
|
|
42
44
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
43
45
|
const file_utils_1 = require("../../utils/file-utils");
|
|
44
46
|
const logger_1 = require("../../utils/logger");
|
|
47
|
+
const locale_names_1 = require("../../django/locale-names");
|
|
45
48
|
/**
|
|
46
49
|
* Django settings.py auto-configuration.
|
|
47
50
|
*
|
|
@@ -134,7 +137,106 @@ async function resolveByGlob(workspacePath) {
|
|
|
134
137
|
return path.join(workspacePath, shallowest);
|
|
135
138
|
}
|
|
136
139
|
async function resolveSettingsPath(workspacePath) {
|
|
137
|
-
|
|
140
|
+
const resolved = (await resolveViaManagePy(workspacePath)) ?? (await resolveByGlob(workspacePath));
|
|
141
|
+
if (!resolved)
|
|
142
|
+
return null;
|
|
143
|
+
// settings/dev.py with `from .base import *`: the shared settings live in base.py,
|
|
144
|
+
// and editing only dev.py would leave production without i18n.
|
|
145
|
+
const source = await (0, file_utils_1.readFile)(resolved).catch(() => '');
|
|
146
|
+
if (!/^MIDDLEWARE\s*=/m.test(source)) {
|
|
147
|
+
for (const m of source.matchAll(/^from\s+\.(\w+)\s+import\s+\*/gm)) {
|
|
148
|
+
const sibling = path.join(path.dirname(resolved), `${m[1]}.py`);
|
|
149
|
+
const siblingSource = await (0, file_utils_1.readFile)(sibling).catch(() => '');
|
|
150
|
+
if (/^MIDDLEWARE\s*=/m.test(siblingSource))
|
|
151
|
+
return sibling;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return resolved;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The locale directory Django reads (LOCALE_PATHS[0]), relative to the
|
|
158
|
+
* workspace, '/'-separated. Falls back to <BASE_DIR>/locale — what finalize
|
|
159
|
+
* adds — when LOCALE_PATHS is absent or not a plain BASE_DIR path.
|
|
160
|
+
*/
|
|
161
|
+
async function resolveDjangoLocaleDir(workspacePath, djangoRoot) {
|
|
162
|
+
const fallback = path.posix.join(djangoRoot, 'locale');
|
|
163
|
+
const settingsPath = await resolveSettingsPath(workspacePath);
|
|
164
|
+
if (!settingsPath)
|
|
165
|
+
return fallback;
|
|
166
|
+
const source = await (0, file_utils_1.readFile)(settingsPath).catch(() => '');
|
|
167
|
+
const rel = parseLocalePaths(source);
|
|
168
|
+
return rel ? path.posix.normalize(path.posix.join(djangoRoot, rel)) : fallback;
|
|
169
|
+
}
|
|
170
|
+
/** First LOCALE_PATHS entry as a path relative to BASE_DIR, if it is one. */
|
|
171
|
+
function parseLocalePaths(source) {
|
|
172
|
+
const m = /^LOCALE_PATHS\s*=\s*[[(]/m.exec(source);
|
|
173
|
+
if (!m)
|
|
174
|
+
return null;
|
|
175
|
+
// First element of the list: up to the first comma outside brackets.
|
|
176
|
+
let depth = 0;
|
|
177
|
+
let first = '';
|
|
178
|
+
for (let i = m.index + m[0].length; i < source.length; i++) {
|
|
179
|
+
const ch = source[i];
|
|
180
|
+
if (ch === '(' || ch === '[')
|
|
181
|
+
depth++;
|
|
182
|
+
else if (ch === ')' || ch === ']') {
|
|
183
|
+
if (depth === 0)
|
|
184
|
+
break;
|
|
185
|
+
depth--;
|
|
186
|
+
}
|
|
187
|
+
else if (ch === ',' && depth === 0)
|
|
188
|
+
break;
|
|
189
|
+
first += ch;
|
|
190
|
+
}
|
|
191
|
+
first = first.trim();
|
|
192
|
+
const strings = (t) => [...t.matchAll(/['"]([^'"]+)['"]/g)].map((x) => x[1]);
|
|
193
|
+
// BASE_DIR / 'a' / 'b' str(BASE_DIR / 'a')
|
|
194
|
+
if (/^(?:str\()?\s*BASE_DIR\s*\//.test(first))
|
|
195
|
+
return path.posix.join(...strings(first));
|
|
196
|
+
// os.path.join(BASE_DIR, 'a', 'b') BASE_DIR.joinpath('a') Path(BASE_DIR, 'a')
|
|
197
|
+
if (/^(?:os\.path\.join|Path)\(\s*BASE_DIR\s*,/.test(first) || /^BASE_DIR\.joinpath\(/.test(first)) {
|
|
198
|
+
const parts = strings(first);
|
|
199
|
+
return parts.length ? path.posix.join(...parts) : null;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/** LANGUAGES entries for the source and target locales: ('pt-br', 'Brazilian Portuguese'). */
|
|
204
|
+
function languageEntries(locales) {
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
const out = [];
|
|
207
|
+
for (const l of locales) {
|
|
208
|
+
const code = (0, locale_names_1.djangoLanguageCode)(l);
|
|
209
|
+
if (!l || seen.has(code))
|
|
210
|
+
continue;
|
|
211
|
+
seen.add(code);
|
|
212
|
+
out.push({ code, name: (0, locale_names_1.languageName)(l) });
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
/** Language codes listed in a LANGUAGES setting. */
|
|
217
|
+
function listedLanguages(source) {
|
|
218
|
+
const m = /^LANGUAGES\s*=\s*[[(]/m.exec(source);
|
|
219
|
+
if (!m)
|
|
220
|
+
return null;
|
|
221
|
+
let depth = 0;
|
|
222
|
+
let end = -1;
|
|
223
|
+
for (let i = m.index + m[0].length - 1; i < source.length; i++) {
|
|
224
|
+
const ch = source[i];
|
|
225
|
+
if (ch === '[' || ch === '(')
|
|
226
|
+
depth++;
|
|
227
|
+
else if (ch === ']' || ch === ')') {
|
|
228
|
+
depth--;
|
|
229
|
+
if (depth === 0) {
|
|
230
|
+
end = i + 1;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (end === -1)
|
|
236
|
+
return null;
|
|
237
|
+
const body = source.slice(m.index, end);
|
|
238
|
+
const codes = [...body.matchAll(/[[(,]\s*\(\s*['"]([\w-]+)['"]\s*,/g)].map((x) => x[1].toLowerCase());
|
|
239
|
+
return { codes, start: m.index, end };
|
|
138
240
|
}
|
|
139
241
|
/**
|
|
140
242
|
* Insert LocaleMiddleware into the MIDDLEWARE list.
|
|
@@ -166,7 +268,7 @@ function insertLocaleMiddleware(source) {
|
|
|
166
268
|
const entryLine = lines.find((l) => l.trim().startsWith("'") || l.trim().startsWith('"'));
|
|
167
269
|
const indent = entryLine?.match(/^(\s*)/)?.[1] ?? ' ';
|
|
168
270
|
const quote = entryLine?.trim().startsWith('"') ? '"' : "'";
|
|
169
|
-
const newEntry = `${indent}${quote}${LOCALE_MIDDLEWARE}${quote}
|
|
271
|
+
const newEntry = `${indent}${quote}${LOCALE_MIDDLEWARE}${quote},${source.includes('\r\n') ? '\r' : ''}`;
|
|
170
272
|
const sessionIdx = lines.findIndex((l) => l.includes(SESSION_MIDDLEWARE));
|
|
171
273
|
const commonIdx = lines.findIndex((l) => l.includes(COMMON_MIDDLEWARE));
|
|
172
274
|
let insertAt;
|
|
@@ -191,6 +293,8 @@ function insertLocaleMiddleware(source) {
|
|
|
191
293
|
}
|
|
192
294
|
async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
193
295
|
const { targetLocales = [], sourceLocale = 'en', dryRun = false } = options;
|
|
296
|
+
const djangoRoot = options.djangoRoot ?? '.';
|
|
297
|
+
const localeRel = path.posix.relative(djangoRoot === '.' ? '' : djangoRoot, options.localeDir ?? path.posix.join(djangoRoot, 'locale')) || 'locale';
|
|
194
298
|
const changes = [];
|
|
195
299
|
const warnings = [];
|
|
196
300
|
const settingsPath = await resolveSettingsPath(workspacePath);
|
|
@@ -204,12 +308,13 @@ async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
|
204
308
|
return { settingsPath, changes, warnings };
|
|
205
309
|
}
|
|
206
310
|
let source = original;
|
|
311
|
+
const nl = original.includes('\r\n') ? '\r\n' : '\n';
|
|
207
312
|
// ── USE_I18N ────────────────────────────────────────────────
|
|
208
313
|
// Only flip an explicit False. Anything else the user wrote stays.
|
|
209
314
|
if (hasSetting(source, 'USE_I18N')) {
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
212
|
-
source = source.replace(
|
|
315
|
+
const falseRe = /^USE_I18N[ \t]*=[ \t]*False\b/m;
|
|
316
|
+
if (falseRe.test(source)) {
|
|
317
|
+
source = source.replace(falseRe, 'USE_I18N = True');
|
|
213
318
|
changes.push('Set USE_I18N = True (was False)');
|
|
214
319
|
}
|
|
215
320
|
}
|
|
@@ -227,24 +332,56 @@ async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
|
227
332
|
// ── LOCALE_PATHS ────────────────────────────────────────────
|
|
228
333
|
// Must match where the locale builder writes: <workspace>/locale/<lang>/LC_MESSAGES/
|
|
229
334
|
if (!hasSetting(source, 'LOCALE_PATHS')) {
|
|
335
|
+
const segments = localeRel.split('/').filter(Boolean);
|
|
230
336
|
const value = usesPathlibBaseDir(source)
|
|
231
|
-
?
|
|
232
|
-
:
|
|
233
|
-
source = appendSetting(source, value);
|
|
337
|
+
? `LOCALE_PATHS = [BASE_DIR / ${segments.map((p) => `'${p}'`).join(' / ')}]`
|
|
338
|
+
: `LOCALE_PATHS = [os.path.join(BASE_DIR, ${segments.map((p) => `'${p}'`).join(', ')})]`;
|
|
339
|
+
source = appendSetting(source, value, nl);
|
|
234
340
|
changes.push('Added LOCALE_PATHS');
|
|
235
341
|
}
|
|
342
|
+
else {
|
|
343
|
+
const configured = parseLocalePaths(source);
|
|
344
|
+
if (configured === null) {
|
|
345
|
+
warnings.push(`LOCALE_PATHS in ${path.basename(settingsPath)} isn't a plain BASE_DIR path — make sure it includes ${localeRel}/ (where the .po files were written).`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
236
348
|
// ── LANGUAGE_CODE ───────────────────────────────────────────
|
|
237
349
|
// Only added when missing; an existing value is a deliberate choice.
|
|
238
350
|
if (!hasSetting(source, 'LANGUAGE_CODE')) {
|
|
239
|
-
source = appendSetting(source, `LANGUAGE_CODE = '${sourceLocale}'
|
|
240
|
-
changes.push(`Added LANGUAGE_CODE = '${sourceLocale}'`);
|
|
351
|
+
source = appendSetting(source, `LANGUAGE_CODE = '${(0, locale_names_1.djangoLanguageCode)(sourceLocale)}'`, nl);
|
|
352
|
+
changes.push(`Added LANGUAGE_CODE = '${(0, locale_names_1.djangoLanguageCode)(sourceLocale)}'`);
|
|
241
353
|
}
|
|
242
354
|
// ── LANGUAGES ───────────────────────────────────────────────
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
355
|
+
// Django only activates languages listed here. Ours live in the Babelize
|
|
356
|
+
// block and grow with new target locales; a list the user wrote is theirs —
|
|
357
|
+
// we only point out what it is missing.
|
|
358
|
+
const wanted = languageEntries([sourceLocale, ...targetLocales]);
|
|
359
|
+
const listed = listedLanguages(source);
|
|
360
|
+
if (!listed && targetLocales.length > 0) {
|
|
361
|
+
const entries = wanted.map((e) => ` ('${e.code}', '${e.name.replace(/'/g, "\\'")}'),`).join(nl);
|
|
362
|
+
source = appendSetting(source, `LANGUAGES = [${nl}${entries}${nl}]`, nl);
|
|
363
|
+
changes.push(`Added LANGUAGES (${wanted.length} locales)`);
|
|
364
|
+
}
|
|
365
|
+
else if (listed) {
|
|
366
|
+
const missing = wanted.filter((e) => !listed.codes.includes(e.code) && !listed.codes.includes(e.code.split('-')[0]));
|
|
367
|
+
const managedAt = source.indexOf(MANAGED_BLOCK_HEADER);
|
|
368
|
+
if (missing.length > 0 && managedAt !== -1 && listed.start > managedAt) {
|
|
369
|
+
const body = source.slice(listed.start, listed.end);
|
|
370
|
+
const close = body.lastIndexOf(']');
|
|
371
|
+
if (close !== -1) {
|
|
372
|
+
const indent = /\n([ \t]+)\(/.exec(body)?.[1] ?? ' ';
|
|
373
|
+
const before = body.slice(0, close).replace(/\s*$/, '');
|
|
374
|
+
const sep = before.endsWith(',') || before.endsWith('[') ? '' : ',';
|
|
375
|
+
const added = missing.map((e) => `${indent}('${e.code}', '${e.name.replace(/'/g, "\\'")}'),`).join(nl);
|
|
376
|
+
const next = `${before}${sep}${nl}${added}${nl}${body.slice(close)}`;
|
|
377
|
+
source = source.slice(0, listed.start) + next + source.slice(listed.end);
|
|
378
|
+
changes.push(`Added ${missing.map((e) => e.code).join(', ')} to LANGUAGES`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else if (missing.length > 0) {
|
|
382
|
+
warnings.push(`LANGUAGES in ${path.basename(settingsPath)} doesn't list ${missing.map((e) => `'${e.code}'`).join(', ')} — ` +
|
|
383
|
+
'Django will not switch to those languages until you add them.');
|
|
384
|
+
}
|
|
248
385
|
}
|
|
249
386
|
if (changes.length === 0) {
|
|
250
387
|
logger_1.engineLogger.info('Django settings already configured for i18n', { settingsPath });
|
|
@@ -258,7 +395,7 @@ async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
|
258
395
|
changes,
|
|
259
396
|
dryRun,
|
|
260
397
|
});
|
|
261
|
-
return { settingsPath, changes, warnings };
|
|
398
|
+
return { settingsPath, changes, warnings, original, updated: source };
|
|
262
399
|
}
|
|
263
400
|
/**
|
|
264
401
|
* Append a setting under a single Babelize block at the end of the file.
|
|
@@ -267,9 +404,9 @@ async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
|
267
404
|
* alternative, inserting near related settings, means moving code around a file
|
|
268
405
|
* we cannot parse.
|
|
269
406
|
*/
|
|
270
|
-
function appendSetting(source, statement) {
|
|
407
|
+
function appendSetting(source, statement, nl = '\n') {
|
|
271
408
|
const withBlock = source.includes(MANAGED_BLOCK_HEADER)
|
|
272
409
|
? source
|
|
273
|
-
: `${source.trimEnd()}
|
|
274
|
-
return `${withBlock.trimEnd()}
|
|
410
|
+
: `${source.trimEnd()}${nl}${nl}${MANAGED_BLOCK_HEADER}${nl}`;
|
|
411
|
+
return `${withBlock.trimEnd()}${nl}${statement}${nl}`;
|
|
275
412
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WorkspaceInfo } from '../../runtime/types';
|
|
2
|
+
import { type FinalizationChange } from './writes';
|
|
2
3
|
interface PackageJson {
|
|
3
4
|
dependencies?: Record<string, string>;
|
|
4
5
|
devDependencies?: Record<string, string>;
|
|
@@ -21,6 +22,7 @@ export declare function finalizeProject(workspace: WorkspaceInfo, dryRun: boolea
|
|
|
21
22
|
entryPointModified: boolean;
|
|
22
23
|
packagesInstalled: boolean;
|
|
23
24
|
warnings: string[];
|
|
25
|
+
changedFiles: FinalizationChange[];
|
|
24
26
|
};
|
|
25
27
|
}>;
|
|
26
28
|
/**
|
|
@@ -44,6 +44,7 @@ const ast_helpers_1 = require("../../utils/ast-helpers");
|
|
|
44
44
|
const static_runtime_1 = require("./static-runtime");
|
|
45
45
|
const django_finalize_1 = require("./django-finalize");
|
|
46
46
|
const nextjs_finalize_1 = require("./nextjs-finalize");
|
|
47
|
+
const writes_1 = require("./writes");
|
|
47
48
|
const i18next_icu_1 = require("../../utils/i18next-icu");
|
|
48
49
|
const DEPENDENCIES_BY_FRAMEWORK = {
|
|
49
50
|
react: {
|
|
@@ -235,6 +236,12 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
235
236
|
let entryPointModified = false;
|
|
236
237
|
let packagesInstalled = false;
|
|
237
238
|
let userOwnsInit = false;
|
|
239
|
+
/**
|
|
240
|
+
* Every file finalization writes goes through this recorder, so hosts can
|
|
241
|
+
* ship all of them with the run (the PR, the ZIP, the CLI diff) — including
|
|
242
|
+
* files it creates.
|
|
243
|
+
*/
|
|
244
|
+
const writer = new writes_1.FinalizationWriter(workspace.path, dryRun);
|
|
238
245
|
// Coerced against FRAMEWORKS, not SUPPORTED_FRAMEWORKS. SUPPORTED_FRAMEWORKS
|
|
239
246
|
// omits 'django', so a Django project used to collapse to 'unknown' here —
|
|
240
247
|
// which made the two `framework === 'django'` branches below unreachable and
|
|
@@ -259,15 +266,19 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
259
266
|
requiredDeps = nextjs_finalize_1.NEXT_INTL_DEPENDENCIES;
|
|
260
267
|
const missingDeps = Object.keys(requiredDeps).filter((d) => !deps[d]);
|
|
261
268
|
if (missingDeps.length > 0) {
|
|
269
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
270
|
+
for (const dep of missingDeps) {
|
|
271
|
+
pkg.dependencies[dep] = requiredDeps[dep];
|
|
272
|
+
}
|
|
273
|
+
// Keep the user's key order (a sorted package.json is an unreviewable
|
|
274
|
+
// diff); only the dependency map is sorted, as npm does.
|
|
275
|
+
pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
276
|
+
await writer.write(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
262
277
|
if (!dryRun) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// Keep the user's key order (a sorted package.json is an unreviewable
|
|
268
|
-
// diff); only the dependency map is sorted, as npm does.
|
|
269
|
-
pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
270
|
-
await (0, file_utils_1.writeJsonFile)(packageJsonPath, pkg, false);
|
|
278
|
+
// A committed lockfile must move with package.json, or `npm ci` /
|
|
279
|
+
// `--frozen-lockfile` fails on the merged PR. Snapshot it so the
|
|
280
|
+
// install's update is recorded (and shipped) like any other write.
|
|
281
|
+
const lockfilesBefore = await readLockfiles(workspace.path);
|
|
271
282
|
if (options.installDependencies !== false) {
|
|
272
283
|
try {
|
|
273
284
|
const pkgManager = detectPackageManager(workspace.path);
|
|
@@ -297,6 +308,17 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
297
308
|
warnings.push(`Added dependencies to package.json but auto-install failed. Please run your package manager to install: ${missingDeps.join(', ')}`);
|
|
298
309
|
}
|
|
299
310
|
}
|
|
311
|
+
const lockfilesAfter = await readLockfiles(workspace.path);
|
|
312
|
+
for (const [name, before] of lockfilesBefore) {
|
|
313
|
+
const after = lockfilesAfter.get(name);
|
|
314
|
+
if (after !== undefined && after !== before) {
|
|
315
|
+
writer.record(path.join(workspace.path, name), before, after);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
warnings.push(`${name} was not updated for the new dependencies (${missingDeps.join(', ')}). ` +
|
|
319
|
+
`Run your package manager's install and commit ${name} before merging, or frozen-lockfile installs (npm ci) will fail.`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
300
322
|
}
|
|
301
323
|
dependenciesInvalid = true;
|
|
302
324
|
if (packagesInstalled) {
|
|
@@ -326,9 +348,7 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
326
348
|
sourceLocale: workspace.sourceDirs[0] || 'en',
|
|
327
349
|
targetLocales: workspace.existingLocales || [],
|
|
328
350
|
});
|
|
329
|
-
|
|
330
|
-
await (0, file_utils_1.writeFile)(i18nJsPath, runtimeContent);
|
|
331
|
-
}
|
|
351
|
+
await writer.write(i18nJsPath, runtimeContent);
|
|
332
352
|
configFileCreated = true;
|
|
333
353
|
if (regenerationStatus === 'ours') {
|
|
334
354
|
warnings.push('Updated i18n.js (new version available).');
|
|
@@ -346,19 +366,24 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
346
366
|
targetLocales: options.targetLocales,
|
|
347
367
|
sourceLocale: options.sourceLocale,
|
|
348
368
|
dryRun,
|
|
369
|
+
djangoRoot: workspace.djangoRoot,
|
|
370
|
+
localeDir: workspace.publicDir,
|
|
349
371
|
});
|
|
350
372
|
warnings.push(...django.warnings);
|
|
351
373
|
if (django.changes.length > 0) {
|
|
352
374
|
configFileCreated = true;
|
|
353
|
-
warnings.push(`Configured ${path.basename(django.settingsPath ?? 'settings.py')}: ${django.changes.join('; ')}.`
|
|
354
|
-
|
|
375
|
+
warnings.push(`Configured ${path.basename(django.settingsPath ?? 'settings.py')}: ${django.changes.join('; ')}.`);
|
|
376
|
+
if (django.settingsPath && django.original !== undefined && django.updated !== undefined) {
|
|
377
|
+
// finalizeDjangoSettings writes settings.py itself (it needs the parse); record it here.
|
|
378
|
+
writer.record(django.settingsPath, django.original, django.updated);
|
|
379
|
+
}
|
|
355
380
|
}
|
|
356
381
|
}
|
|
357
382
|
else if (useNextIntl) {
|
|
358
383
|
const nextIntl = await (0, nextjs_finalize_1.finalizeNextIntl)(workspace, dryRun, {
|
|
359
384
|
sourceLocale: options.sourceLocale,
|
|
360
385
|
targetLocales: options.targetLocales,
|
|
361
|
-
});
|
|
386
|
+
}, writer);
|
|
362
387
|
warnings.push(...nextIntl.warnings);
|
|
363
388
|
configFileCreated = configFileCreated || nextIntl.configFileCreated;
|
|
364
389
|
entryPointModified = entryPointModified || nextIntl.entryPointModified;
|
|
@@ -375,8 +400,7 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
375
400
|
const content = await (0, file_utils_1.readFile)(abs);
|
|
376
401
|
const updated = (0, i18next_icu_1.addIcuToGeneratedConfig)(content);
|
|
377
402
|
if (updated !== content) {
|
|
378
|
-
|
|
379
|
-
await (0, file_utils_1.writeFile)(abs, updated);
|
|
403
|
+
await writer.write(abs, updated);
|
|
380
404
|
warnings.push(`Added i18next-icu to ${rel} (needed for messages with placeholders and plurals).`);
|
|
381
405
|
}
|
|
382
406
|
}
|
|
@@ -391,15 +415,13 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
391
415
|
(await (0, file_utils_1.pathExists)(path.join(workspace.path, 'tsconfig.json')));
|
|
392
416
|
const targetPath = isTS ? i18nPath : i18nJsPath;
|
|
393
417
|
const template = I18N_CONFIG_TEMPLATES[framework] || I18N_CONFIG_TEMPLATES['unknown'];
|
|
394
|
-
|
|
395
|
-
await (0, file_utils_1.writeFile)(targetPath, template);
|
|
418
|
+
await writer.write(targetPath, template);
|
|
396
419
|
configFileCreated = true;
|
|
397
420
|
}
|
|
398
421
|
if (framework === 'nextjs' && !userOwnsInit) {
|
|
399
422
|
const providerPath = path.join(workspace.path, srcDir, 'I18nProvider.tsx');
|
|
400
423
|
if (!(await (0, file_utils_1.pathExists)(providerPath))) {
|
|
401
|
-
|
|
402
|
-
await (0, file_utils_1.writeFile)(providerPath, I18N_PROVIDER_TEMPLATE);
|
|
424
|
+
await writer.write(providerPath, I18N_PROVIDER_TEMPLATE);
|
|
403
425
|
configFileCreated = true;
|
|
404
426
|
}
|
|
405
427
|
}
|
|
@@ -439,9 +461,7 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
439
461
|
}
|
|
440
462
|
const modified = modifyVueEntryPoint(content, relativeI18nPath);
|
|
441
463
|
if (modified !== null) {
|
|
442
|
-
|
|
443
|
-
await (0, file_utils_1.writeFile)(entryPath, modified);
|
|
444
|
-
}
|
|
464
|
+
await writer.write(entryPath, modified);
|
|
445
465
|
entryPointModified = true;
|
|
446
466
|
}
|
|
447
467
|
break;
|
|
@@ -464,8 +484,7 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
464
484
|
if (isNextLayout) {
|
|
465
485
|
const modified = modifyNextjsLayout(content, relativeI18nPath);
|
|
466
486
|
if (modified !== null) {
|
|
467
|
-
|
|
468
|
-
await (0, file_utils_1.writeFile)(entryPath, modified);
|
|
487
|
+
await writer.write(entryPath, modified);
|
|
469
488
|
entryPointModified = true;
|
|
470
489
|
}
|
|
471
490
|
break;
|
|
@@ -478,17 +497,15 @@ async function finalizeProject(workspace, dryRun, options = {}) {
|
|
|
478
497
|
content.includes(`import "${relativeI18nPath}.ts"`) ||
|
|
479
498
|
content.includes("import '@/i18n'");
|
|
480
499
|
if (!hasImport) {
|
|
481
|
-
|
|
482
|
-
const newContent = `import '${relativeI18nPath}';\n${content}`;
|
|
483
|
-
await (0, file_utils_1.writeFile)(entryPath, newContent);
|
|
484
|
-
}
|
|
500
|
+
await writer.write(entryPath, `import '${relativeI18nPath}';\n${content}`);
|
|
485
501
|
entryPointModified = true;
|
|
486
502
|
}
|
|
487
503
|
break;
|
|
488
504
|
}
|
|
489
505
|
}
|
|
490
506
|
}
|
|
491
|
-
|
|
507
|
+
const changedFiles = writer.changes;
|
|
508
|
+
return { finalization: { dependenciesInvalid, configFileCreated, entryPointModified, packagesInstalled, warnings, changedFiles } };
|
|
492
509
|
}
|
|
493
510
|
/**
|
|
494
511
|
* Wire Next.js App Router layouts through a client I18nProvider so i18n
|
|
@@ -687,6 +704,17 @@ function modifyVueEntryPoint(content, relativeI18nPath) {
|
|
|
687
704
|
return `import i18n from '${relativeI18nPath}';\n${content}`;
|
|
688
705
|
}
|
|
689
706
|
}
|
|
707
|
+
/** Text lockfiles a package manager rewrites on install (bun.lockb is binary and left to the user). */
|
|
708
|
+
const TEXT_LOCKFILES = ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lock'];
|
|
709
|
+
async function readLockfiles(workspacePath) {
|
|
710
|
+
const found = new Map();
|
|
711
|
+
for (const name of TEXT_LOCKFILES) {
|
|
712
|
+
const abs = path.join(workspacePath, name);
|
|
713
|
+
if (await (0, file_utils_1.pathExists)(abs))
|
|
714
|
+
found.set(name, await (0, file_utils_1.readFile)(abs));
|
|
715
|
+
}
|
|
716
|
+
return found;
|
|
717
|
+
}
|
|
690
718
|
function detectPackageManager(workspacePath) {
|
|
691
719
|
if ((0, fs_1.existsSync)(path.join(workspacePath, 'bun.lock')))
|
|
692
720
|
return 'bun';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FinalizationWriter } from './writes';
|
|
1
2
|
import type { WorkspaceInfo } from '../../runtime/types';
|
|
2
3
|
/**
|
|
3
4
|
* Next.js finalization for next-intl.
|
|
@@ -34,4 +35,6 @@ export declare function wrapRootLayout(content: string, typescript: boolean): st
|
|
|
34
35
|
export declare function finalizeNextIntl(workspace: WorkspaceInfo, dryRun: boolean, options: {
|
|
35
36
|
sourceLocale?: string;
|
|
36
37
|
targetLocales?: string[];
|
|
37
|
-
}
|
|
38
|
+
},
|
|
39
|
+
/** Records every write so it ships with the run (finalizeProject passes its own). */
|
|
40
|
+
writer?: FinalizationWriter): Promise<NextIntlFinalizeResult>;
|
|
@@ -41,6 +41,7 @@ exports.finalizeNextIntl = finalizeNextIntl;
|
|
|
41
41
|
const fs = __importStar(require("fs"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const file_utils_1 = require("../../utils/file-utils");
|
|
44
|
+
const writes_1 = require("./writes");
|
|
44
45
|
const ast_helpers_1 = require("../../utils/ast-helpers");
|
|
45
46
|
/**
|
|
46
47
|
* Next.js finalization for next-intl.
|
|
@@ -234,7 +235,9 @@ async function availableLocales(workspace, options) {
|
|
|
234
235
|
const targets = (options.targetLocales ?? []).filter((l) => l !== source);
|
|
235
236
|
return [...new Set([...targets, ...[...found].sort()])];
|
|
236
237
|
}
|
|
237
|
-
async function finalizeNextIntl(workspace, dryRun, options
|
|
238
|
+
async function finalizeNextIntl(workspace, dryRun, options,
|
|
239
|
+
/** Records every write so it ships with the run (finalizeProject passes its own). */
|
|
240
|
+
writer = new writes_1.FinalizationWriter(workspace.path, dryRun)) {
|
|
238
241
|
const warnings = [];
|
|
239
242
|
let configFileCreated = false;
|
|
240
243
|
let entryPointModified = false;
|
|
@@ -278,8 +281,7 @@ async function finalizeNextIntl(workspace, dryRun, options) {
|
|
|
278
281
|
typescript: requestIsTs,
|
|
279
282
|
});
|
|
280
283
|
if (content !== existingContent) {
|
|
281
|
-
|
|
282
|
-
await (0, file_utils_1.writeFile)(requestFile, content);
|
|
284
|
+
await writer.write(requestFile, content);
|
|
283
285
|
configFileCreated = true;
|
|
284
286
|
}
|
|
285
287
|
}
|
|
@@ -290,8 +292,7 @@ async function finalizeNextIntl(workspace, dryRun, options) {
|
|
|
290
292
|
const created = `import createNextIntlPlugin from 'next-intl/plugin';\n\n` +
|
|
291
293
|
`const withNextIntl = createNextIntlPlugin(${JSON.stringify(pluginPath)});\n\n` +
|
|
292
294
|
`/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nexport default withNextIntl(nextConfig);\n`;
|
|
293
|
-
|
|
294
|
-
await (0, file_utils_1.writeFile)(path.join(root, 'next.config.mjs'), created);
|
|
295
|
+
await writer.write(path.join(root, 'next.config.mjs'), created);
|
|
295
296
|
configFileCreated = true;
|
|
296
297
|
}
|
|
297
298
|
else {
|
|
@@ -302,8 +303,7 @@ async function finalizeNextIntl(workspace, dryRun, options) {
|
|
|
302
303
|
`createNextIntlPlugin(${JSON.stringify(pluginPath)}) from 'next-intl/plugin'.`);
|
|
303
304
|
}
|
|
304
305
|
else if (wrapped !== content) {
|
|
305
|
-
|
|
306
|
-
await (0, file_utils_1.writeFile)(nextConfigPath, wrapped);
|
|
306
|
+
await writer.write(nextConfigPath, wrapped);
|
|
307
307
|
configFileCreated = true;
|
|
308
308
|
}
|
|
309
309
|
}
|
|
@@ -320,8 +320,7 @@ async function finalizeNextIntl(workspace, dryRun, options) {
|
|
|
320
320
|
'wrap it in <NextIntlClientProvider> from next-intl so Client Components can translate.');
|
|
321
321
|
}
|
|
322
322
|
else if (wrapped !== content) {
|
|
323
|
-
|
|
324
|
-
await (0, file_utils_1.writeFile)(layoutPath, wrapped);
|
|
323
|
+
await writer.write(layoutPath, wrapped);
|
|
325
324
|
entryPointModified = true;
|
|
326
325
|
}
|
|
327
326
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** A file finalization created or edited, relative to the workspace root (POSIX separators). */
|
|
2
|
+
export interface FinalizationChange {
|
|
3
|
+
file: string;
|
|
4
|
+
/** '' when the file did not exist before the run. */
|
|
5
|
+
originalContent: string;
|
|
6
|
+
modifiedContent: string;
|
|
7
|
+
/** True when finalization created the file (it was not in the project before). */
|
|
8
|
+
created: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The single path every finalization write goes through.
|
|
12
|
+
*
|
|
13
|
+
* Hosts ship `changes` with the run (the GitHub PR, the ZIP, the CLI report),
|
|
14
|
+
* so a write that bypasses this recorder is a file the PR silently leaves out:
|
|
15
|
+
* `t()` calls merged without the dependency or the init code that makes them
|
|
16
|
+
* work. Records new files as well as edits, and folds repeated writes to one
|
|
17
|
+
* file into a single change (first original, last content).
|
|
18
|
+
*/
|
|
19
|
+
export declare class FinalizationWriter {
|
|
20
|
+
readonly root: string;
|
|
21
|
+
readonly dryRun: boolean;
|
|
22
|
+
private readonly byFile;
|
|
23
|
+
constructor(root: string, dryRun: boolean);
|
|
24
|
+
/** Write `content` to `absPath` (unless dry run) and record it. Returns false when the file already had that content. */
|
|
25
|
+
write(absPath: string, content: string): Promise<boolean>;
|
|
26
|
+
/** Record a write made elsewhere (e.g. by a package manager). `original` null = file was created. */
|
|
27
|
+
record(absPath: string, original: string | null, content: string): void;
|
|
28
|
+
get changes(): FinalizationChange[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FinalizationWriter = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const file_utils_1 = require("../../utils/file-utils");
|
|
39
|
+
/**
|
|
40
|
+
* The single path every finalization write goes through.
|
|
41
|
+
*
|
|
42
|
+
* Hosts ship `changes` with the run (the GitHub PR, the ZIP, the CLI report),
|
|
43
|
+
* so a write that bypasses this recorder is a file the PR silently leaves out:
|
|
44
|
+
* `t()` calls merged without the dependency or the init code that makes them
|
|
45
|
+
* work. Records new files as well as edits, and folds repeated writes to one
|
|
46
|
+
* file into a single change (first original, last content).
|
|
47
|
+
*/
|
|
48
|
+
class FinalizationWriter {
|
|
49
|
+
constructor(root, dryRun) {
|
|
50
|
+
this.root = root;
|
|
51
|
+
this.dryRun = dryRun;
|
|
52
|
+
this.byFile = new Map();
|
|
53
|
+
}
|
|
54
|
+
/** Write `content` to `absPath` (unless dry run) and record it. Returns false when the file already had that content. */
|
|
55
|
+
async write(absPath, content) {
|
|
56
|
+
const original = (await (0, file_utils_1.pathExists)(absPath)) ? await (0, file_utils_1.readFile)(absPath) : null;
|
|
57
|
+
if (original === content)
|
|
58
|
+
return false;
|
|
59
|
+
if (!this.dryRun)
|
|
60
|
+
await (0, file_utils_1.writeFile)(absPath, content);
|
|
61
|
+
this.record(absPath, original, content);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
/** Record a write made elsewhere (e.g. by a package manager). `original` null = file was created. */
|
|
65
|
+
record(absPath, original, content) {
|
|
66
|
+
const file = path.relative(this.root, path.resolve(absPath)).split(path.sep).join('/');
|
|
67
|
+
const prev = this.byFile.get(file);
|
|
68
|
+
if (prev) {
|
|
69
|
+
prev.modifiedContent = content;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.byFile.set(file, { file, originalContent: original ?? '', modifiedContent: content, created: original === null });
|
|
73
|
+
}
|
|
74
|
+
get changes() {
|
|
75
|
+
return [...this.byFile.values()];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.FinalizationWriter = FinalizationWriter;
|
|
@@ -203,7 +203,8 @@ async function injectFormats(workspace, formatStrings, formatConfig, dryRun = fa
|
|
|
203
203
|
if (!fileStrings || fileStrings.length === 0)
|
|
204
204
|
continue;
|
|
205
205
|
try {
|
|
206
|
-
|
|
206
|
+
const originalContent = await (0, file_utils_1.readFile)(filePath);
|
|
207
|
+
let content = originalContent;
|
|
207
208
|
const injections = [];
|
|
208
209
|
for (const formatStr of fileStrings) {
|
|
209
210
|
if (!formatStr.detectedFormat)
|
|
@@ -234,7 +235,7 @@ async function injectFormats(workspace, formatStrings, formatConfig, dryRun = fa
|
|
|
234
235
|
await (0, file_utils_1.writeFile)(filePath, content);
|
|
235
236
|
modifiedFiles.push({
|
|
236
237
|
file: relativePath,
|
|
237
|
-
originalContent
|
|
238
|
+
originalContent,
|
|
238
239
|
modifiedContent: content,
|
|
239
240
|
injections,
|
|
240
241
|
importAdded: false,
|