i18next-cli 1.39.0 → 1.39.1
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/cjs/cli.js +1 -1
- package/dist/cjs/rename-key.js +78 -97
- package/dist/esm/cli.js +1 -1
- package/dist/esm/rename-key.js +78 -97
- package/package.json +1 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ const program = new commander.Command();
|
|
|
28
28
|
program
|
|
29
29
|
.name('i18next-cli')
|
|
30
30
|
.description('A unified, high-performance i18next CLI.')
|
|
31
|
-
.version('1.39.
|
|
31
|
+
.version('1.39.1'); // This string is replaced with the actual version at build time by rollup
|
|
32
32
|
// new: global config override option
|
|
33
33
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
34
34
|
program
|
package/dist/cjs/rename-key.js
CHANGED
|
@@ -159,9 +159,10 @@ async function updateSourceFiles(oldParts, newParts, config, dryRun, logger) {
|
|
|
159
159
|
? config.extract.input
|
|
160
160
|
: [config.extract.input];
|
|
161
161
|
const normalizedPatterns = inputPatterns.map(pattern => pattern.replace(/\\/g, '/'));
|
|
162
|
+
// glob accepts array of patterns; do not force cwd to avoid accidental path rewriting
|
|
162
163
|
const sourceFiles = await glob.glob(normalizedPatterns, {
|
|
163
164
|
ignore: [...defaultIgnore, ...userIgnore],
|
|
164
|
-
|
|
165
|
+
nodir: true
|
|
165
166
|
});
|
|
166
167
|
const results = [];
|
|
167
168
|
for (const file of sourceFiles) {
|
|
@@ -181,122 +182,102 @@ async function updateSourceFiles(oldParts, newParts, config, dryRun, logger) {
|
|
|
181
182
|
return results;
|
|
182
183
|
}
|
|
183
184
|
async function replaceKeyInSource(code, oldParts, newParts, config) {
|
|
184
|
-
//
|
|
185
|
+
// Simpler and robust regex-based replacement that covers tests' patterns
|
|
185
186
|
return replaceKeyWithRegex(code, oldParts, newParts, config);
|
|
186
187
|
}
|
|
187
188
|
function replaceKeyWithRegex(code, oldParts, newParts, config) {
|
|
188
189
|
let changes = 0;
|
|
189
190
|
let newCode = code;
|
|
190
191
|
const nsSeparator = config.extract.nsSeparator ?? ':';
|
|
191
|
-
// Helper to determine which key form to use in replacement
|
|
192
|
-
const getReplacementKey = (originalKey) => {
|
|
193
|
-
const hasNamespace = nsSeparator && originalKey.includes(String(nsSeparator));
|
|
194
|
-
return hasNamespace ? newParts.fullKey : newParts.key;
|
|
195
|
-
};
|
|
196
|
-
// Pattern 1: Function calls - respect configured functions
|
|
197
192
|
const configuredFunctions = config.extract.functions || ['t', '*.t'];
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
// Helper to create function-prefix regex fragment
|
|
194
|
+
const fnPrefixToRegex = (fnPattern) => {
|
|
200
195
|
if (fnPattern.startsWith('*.')) {
|
|
201
|
-
//
|
|
202
|
-
const suffix = fnPattern.
|
|
203
|
-
|
|
204
|
-
// Match: anyIdentifier.t('key')
|
|
205
|
-
functionPatterns.push({
|
|
206
|
-
pattern: new RegExp(`\\w+${escapedSuffix}\\((['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g'),
|
|
207
|
-
original: oldParts.fullKey
|
|
208
|
-
});
|
|
209
|
-
functionPatterns.push({
|
|
210
|
-
pattern: new RegExp(`\\w+${escapedSuffix}\\((['"\`])${escapeRegex(oldParts.key)}\\1`, 'g'),
|
|
211
|
-
original: oldParts.key
|
|
212
|
-
});
|
|
196
|
+
// '*.t' -> match anyIdentifier.t
|
|
197
|
+
const suffix = fnPattern.slice(2);
|
|
198
|
+
return `\\b[\\w$]+\\.${escapeRegex(suffix)}`; // e.g. \b[\w$]+\.t
|
|
213
199
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
200
|
+
// exact function name (may include dot like 'i18n.t' or 'translate')
|
|
201
|
+
return `\\b${escapeRegex(fnPattern)}`;
|
|
202
|
+
};
|
|
203
|
+
// Replace exact string-key usages inside function calls: fn('key') or fn(`key`) or fn("key")
|
|
204
|
+
for (const fnPattern of configuredFunctions) {
|
|
205
|
+
const prefix = fnPrefixToRegex(fnPattern);
|
|
206
|
+
// Match fullKey first (namespace-prefixed in source)
|
|
207
|
+
if (oldParts.fullKey) {
|
|
208
|
+
const regexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g');
|
|
209
|
+
newCode = newCode.replace(regexFull, (match, q) => {
|
|
210
|
+
changes++;
|
|
211
|
+
const replacementKey = (oldParts.fullKey.includes(nsSeparator || ':') ? newParts.fullKey : newParts.key);
|
|
212
|
+
// preserve surrounding characters up to the opening quote
|
|
213
|
+
return match.replace(oldParts.fullKey, replacementKey);
|
|
224
214
|
});
|
|
225
215
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
216
|
+
// Then match bare key (no namespace in source)
|
|
217
|
+
const regexKey = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g');
|
|
218
|
+
newCode = newCode.replace(regexKey, (match) => {
|
|
219
|
+
changes++;
|
|
220
|
+
const replacementKey = newParts.key;
|
|
221
|
+
return match.replace(new RegExp(escapeRegex(oldParts.key)), replacementKey);
|
|
222
|
+
});
|
|
223
|
+
// Handle ns option in options object: fn('key', { ns: 'oldNs', ... })
|
|
224
|
+
if (oldParts.namespace && newParts.namespace && oldParts.namespace !== newParts.namespace) {
|
|
225
|
+
// We want to change only when key matches and ns value equals oldParts.namespace
|
|
226
|
+
const nsRegexFullKey = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*,\\s*\\{([^}]*)\\bns\\s*:\\s*(['"\`])${escapeRegex(oldParts.namespace)}\\3([^}]*)\\}\\s*\\)`, 'g');
|
|
227
|
+
newCode = newCode.replace(nsRegexFullKey, (match, keyQ, beforeNs, nsQ, afterNs) => {
|
|
231
228
|
changes++;
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
if (functionNameMatch) {
|
|
235
|
-
return `${functionNameMatch[1]}(${quote}${replacement}${quote}`;
|
|
236
|
-
}
|
|
237
|
-
return match;
|
|
229
|
+
// replace ns value
|
|
230
|
+
return match.replace(new RegExp(`(\\bns\\s*:\\s*['"\`])${escapeRegex(oldParts.namespace ?? '')}(['"\`])`), `$1${newParts.namespace ?? ''}$2`);
|
|
238
231
|
});
|
|
232
|
+
// same but if the call used fullKey (ns included inside key string), still update ns option if present
|
|
233
|
+
if (oldParts.fullKey) {
|
|
234
|
+
const nsRegexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1\\s*,\\s*\\{([^}]*)\\bns\\s*:\\s*(['"\`])${escapeRegex(oldParts.namespace)}\\3([^}]*)\\}\\s*\\)`, 'g');
|
|
235
|
+
newCode = newCode.replace(nsRegexFull, (match) => {
|
|
236
|
+
changes++;
|
|
237
|
+
return match.replace(new RegExp(`(\\bns\\s*:\\s*['"\`])${escapeRegex(oldParts.namespace ?? '')}(['"\`])`), `$1${newParts.namespace ?? ''}$2`);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
|
-
//
|
|
242
|
-
// Respect configured function names (including wildcard patterns)
|
|
242
|
+
// Selector API: dot-notation: fn(($) => $.old.key)
|
|
243
243
|
for (const fnPattern of configuredFunctions) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// Replace property chain with dot-notation replacement
|
|
264
|
-
return `${prefix}${param}.${replacementKey}${suffix}`;
|
|
265
|
-
});
|
|
244
|
+
const prefix = fnPrefixToRegex(fnPattern);
|
|
245
|
+
// match forms like: prefix( $ => $.old.key )
|
|
246
|
+
// capture the arrow param name and the rest
|
|
247
|
+
// We'll attempt to match the param and the dotted property chain equal to oldParts.key (exact)
|
|
248
|
+
const dotRegex = new RegExp(`${prefix}\\s*\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*\\1\\.${escapeRegex(oldParts.key)}\\s*\\)`, 'g');
|
|
249
|
+
newCode = newCode.replace(dotRegex, (match, param) => {
|
|
250
|
+
changes++;
|
|
251
|
+
// Determine replacement (if source used namespace in key it would be fullKey, but in selector dot-notation it's always key form)
|
|
252
|
+
const replacementKey = newParts.key;
|
|
253
|
+
return match.replace(`.${oldParts.key}`, `.${replacementKey}`);
|
|
254
|
+
});
|
|
255
|
+
// Bracket notation: fn(($) => $["Old Key"]) etc.
|
|
256
|
+
const bracketRegex = new RegExp(`${prefix}\\s*\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*\\1\\s*\\[\\s*(['"\`])${escapeRegex(oldParts.key)}\\2\\s*\\]\\s*\\)`, 'g');
|
|
257
|
+
newCode = newCode.replace(bracketRegex, (match, param, quote) => {
|
|
258
|
+
changes++;
|
|
259
|
+
const replacementKey = newParts.key;
|
|
260
|
+
// If replacementKey is a valid identifier, convert to dot-notation, otherwise keep bracket form with preserved quote style
|
|
261
|
+
if (/^[A-Za-z_$][\w$]*$/.test(replacementKey)) {
|
|
262
|
+
return match.replace(new RegExp(`\\[\\s*['"\`]${escapeRegex(oldParts.key)}['"\`]\\s*\\]`), `.${replacementKey}`);
|
|
266
263
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const selectorBracketRegex = new RegExp(`(\\b${patternPrefix}\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*)\\2\\[\\s*(['"\`])${escapeRegex(original)}\\3\\s*\\](\\s*\\))`, 'g');
|
|
270
|
-
if (selectorBracketRegex.test(newCode)) {
|
|
271
|
-
const replacementKey = getReplacementKey(original);
|
|
272
|
-
const isIdentifier = (s) => /^[A-Za-z_$][\w$]*$/.test(s);
|
|
273
|
-
newCode = newCode.replace(selectorBracketRegex, (match, prefix, param, quote, suffix) => {
|
|
274
|
-
changes++;
|
|
275
|
-
// If the replacement is a valid identifier, convert to dot-notation, otherwise keep bracket-notation
|
|
276
|
-
if (isIdentifier(replacementKey)) {
|
|
277
|
-
return `${prefix}${param}.${replacementKey}${suffix}`;
|
|
278
|
-
}
|
|
279
|
-
return `${prefix}${param}[${quote}${replacementKey}${quote}]${suffix}`;
|
|
280
|
-
});
|
|
264
|
+
else {
|
|
265
|
+
return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `$1${replacementKey}$1`);
|
|
281
266
|
}
|
|
282
|
-
}
|
|
267
|
+
});
|
|
283
268
|
}
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const i18nKeyPatterns = [
|
|
289
|
-
{ pattern: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g'), original: oldParts.fullKey },
|
|
290
|
-
{ pattern: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g'), original: oldParts.key }
|
|
269
|
+
// JSX i18nKey attribute (handles all quote types)
|
|
270
|
+
const jsxPatterns = [
|
|
271
|
+
{ orig: oldParts.fullKey, regex: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g') },
|
|
272
|
+
{ orig: oldParts.key, regex: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g') }
|
|
291
273
|
];
|
|
292
|
-
for (const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
274
|
+
for (const p of jsxPatterns) {
|
|
275
|
+
newCode = newCode.replace(p.regex, (match, q) => {
|
|
276
|
+
changes++;
|
|
277
|
+
const nsSepStr = nsSeparator === false ? ':' : nsSeparator;
|
|
278
|
+
const replacement = (p.orig === oldParts.fullKey && oldParts.fullKey.includes(nsSepStr)) ? newParts.fullKey : newParts.key;
|
|
279
|
+
return `i18nKey=${q}${replacement}${q}`;
|
|
280
|
+
});
|
|
300
281
|
}
|
|
301
282
|
return { newCode, changes };
|
|
302
283
|
}
|
package/dist/esm/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ const program = new Command();
|
|
|
26
26
|
program
|
|
27
27
|
.name('i18next-cli')
|
|
28
28
|
.description('A unified, high-performance i18next CLI.')
|
|
29
|
-
.version('1.39.
|
|
29
|
+
.version('1.39.1'); // This string is replaced with the actual version at build time by rollup
|
|
30
30
|
// new: global config override option
|
|
31
31
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
32
32
|
program
|
package/dist/esm/rename-key.js
CHANGED
|
@@ -157,9 +157,10 @@ async function updateSourceFiles(oldParts, newParts, config, dryRun, logger) {
|
|
|
157
157
|
? config.extract.input
|
|
158
158
|
: [config.extract.input];
|
|
159
159
|
const normalizedPatterns = inputPatterns.map(pattern => pattern.replace(/\\/g, '/'));
|
|
160
|
+
// glob accepts array of patterns; do not force cwd to avoid accidental path rewriting
|
|
160
161
|
const sourceFiles = await glob(normalizedPatterns, {
|
|
161
162
|
ignore: [...defaultIgnore, ...userIgnore],
|
|
162
|
-
|
|
163
|
+
nodir: true
|
|
163
164
|
});
|
|
164
165
|
const results = [];
|
|
165
166
|
for (const file of sourceFiles) {
|
|
@@ -179,122 +180,102 @@ async function updateSourceFiles(oldParts, newParts, config, dryRun, logger) {
|
|
|
179
180
|
return results;
|
|
180
181
|
}
|
|
181
182
|
async function replaceKeyInSource(code, oldParts, newParts, config) {
|
|
182
|
-
//
|
|
183
|
+
// Simpler and robust regex-based replacement that covers tests' patterns
|
|
183
184
|
return replaceKeyWithRegex(code, oldParts, newParts, config);
|
|
184
185
|
}
|
|
185
186
|
function replaceKeyWithRegex(code, oldParts, newParts, config) {
|
|
186
187
|
let changes = 0;
|
|
187
188
|
let newCode = code;
|
|
188
189
|
const nsSeparator = config.extract.nsSeparator ?? ':';
|
|
189
|
-
// Helper to determine which key form to use in replacement
|
|
190
|
-
const getReplacementKey = (originalKey) => {
|
|
191
|
-
const hasNamespace = nsSeparator && originalKey.includes(String(nsSeparator));
|
|
192
|
-
return hasNamespace ? newParts.fullKey : newParts.key;
|
|
193
|
-
};
|
|
194
|
-
// Pattern 1: Function calls - respect configured functions
|
|
195
190
|
const configuredFunctions = config.extract.functions || ['t', '*.t'];
|
|
196
|
-
|
|
197
|
-
|
|
191
|
+
// Helper to create function-prefix regex fragment
|
|
192
|
+
const fnPrefixToRegex = (fnPattern) => {
|
|
198
193
|
if (fnPattern.startsWith('*.')) {
|
|
199
|
-
//
|
|
200
|
-
const suffix = fnPattern.
|
|
201
|
-
|
|
202
|
-
// Match: anyIdentifier.t('key')
|
|
203
|
-
functionPatterns.push({
|
|
204
|
-
pattern: new RegExp(`\\w+${escapedSuffix}\\((['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g'),
|
|
205
|
-
original: oldParts.fullKey
|
|
206
|
-
});
|
|
207
|
-
functionPatterns.push({
|
|
208
|
-
pattern: new RegExp(`\\w+${escapedSuffix}\\((['"\`])${escapeRegex(oldParts.key)}\\1`, 'g'),
|
|
209
|
-
original: oldParts.key
|
|
210
|
-
});
|
|
194
|
+
// '*.t' -> match anyIdentifier.t
|
|
195
|
+
const suffix = fnPattern.slice(2);
|
|
196
|
+
return `\\b[\\w$]+\\.${escapeRegex(suffix)}`; // e.g. \b[\w$]+\.t
|
|
211
197
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
198
|
+
// exact function name (may include dot like 'i18n.t' or 'translate')
|
|
199
|
+
return `\\b${escapeRegex(fnPattern)}`;
|
|
200
|
+
};
|
|
201
|
+
// Replace exact string-key usages inside function calls: fn('key') or fn(`key`) or fn("key")
|
|
202
|
+
for (const fnPattern of configuredFunctions) {
|
|
203
|
+
const prefix = fnPrefixToRegex(fnPattern);
|
|
204
|
+
// Match fullKey first (namespace-prefixed in source)
|
|
205
|
+
if (oldParts.fullKey) {
|
|
206
|
+
const regexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g');
|
|
207
|
+
newCode = newCode.replace(regexFull, (match, q) => {
|
|
208
|
+
changes++;
|
|
209
|
+
const replacementKey = (oldParts.fullKey.includes(nsSeparator || ':') ? newParts.fullKey : newParts.key);
|
|
210
|
+
// preserve surrounding characters up to the opening quote
|
|
211
|
+
return match.replace(oldParts.fullKey, replacementKey);
|
|
222
212
|
});
|
|
223
213
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
214
|
+
// Then match bare key (no namespace in source)
|
|
215
|
+
const regexKey = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g');
|
|
216
|
+
newCode = newCode.replace(regexKey, (match) => {
|
|
217
|
+
changes++;
|
|
218
|
+
const replacementKey = newParts.key;
|
|
219
|
+
return match.replace(new RegExp(escapeRegex(oldParts.key)), replacementKey);
|
|
220
|
+
});
|
|
221
|
+
// Handle ns option in options object: fn('key', { ns: 'oldNs', ... })
|
|
222
|
+
if (oldParts.namespace && newParts.namespace && oldParts.namespace !== newParts.namespace) {
|
|
223
|
+
// We want to change only when key matches and ns value equals oldParts.namespace
|
|
224
|
+
const nsRegexFullKey = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*,\\s*\\{([^}]*)\\bns\\s*:\\s*(['"\`])${escapeRegex(oldParts.namespace)}\\3([^}]*)\\}\\s*\\)`, 'g');
|
|
225
|
+
newCode = newCode.replace(nsRegexFullKey, (match, keyQ, beforeNs, nsQ, afterNs) => {
|
|
229
226
|
changes++;
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
if (functionNameMatch) {
|
|
233
|
-
return `${functionNameMatch[1]}(${quote}${replacement}${quote}`;
|
|
234
|
-
}
|
|
235
|
-
return match;
|
|
227
|
+
// replace ns value
|
|
228
|
+
return match.replace(new RegExp(`(\\bns\\s*:\\s*['"\`])${escapeRegex(oldParts.namespace ?? '')}(['"\`])`), `$1${newParts.namespace ?? ''}$2`);
|
|
236
229
|
});
|
|
230
|
+
// same but if the call used fullKey (ns included inside key string), still update ns option if present
|
|
231
|
+
if (oldParts.fullKey) {
|
|
232
|
+
const nsRegexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1\\s*,\\s*\\{([^}]*)\\bns\\s*:\\s*(['"\`])${escapeRegex(oldParts.namespace)}\\3([^}]*)\\}\\s*\\)`, 'g');
|
|
233
|
+
newCode = newCode.replace(nsRegexFull, (match) => {
|
|
234
|
+
changes++;
|
|
235
|
+
return match.replace(new RegExp(`(\\bns\\s*:\\s*['"\`])${escapeRegex(oldParts.namespace ?? '')}(['"\`])`), `$1${newParts.namespace ?? ''}$2`);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
|
-
//
|
|
240
|
-
// Respect configured function names (including wildcard patterns)
|
|
240
|
+
// Selector API: dot-notation: fn(($) => $.old.key)
|
|
241
241
|
for (const fnPattern of configuredFunctions) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Replace property chain with dot-notation replacement
|
|
262
|
-
return `${prefix}${param}.${replacementKey}${suffix}`;
|
|
263
|
-
});
|
|
242
|
+
const prefix = fnPrefixToRegex(fnPattern);
|
|
243
|
+
// match forms like: prefix( $ => $.old.key )
|
|
244
|
+
// capture the arrow param name and the rest
|
|
245
|
+
// We'll attempt to match the param and the dotted property chain equal to oldParts.key (exact)
|
|
246
|
+
const dotRegex = new RegExp(`${prefix}\\s*\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*\\1\\.${escapeRegex(oldParts.key)}\\s*\\)`, 'g');
|
|
247
|
+
newCode = newCode.replace(dotRegex, (match, param) => {
|
|
248
|
+
changes++;
|
|
249
|
+
// Determine replacement (if source used namespace in key it would be fullKey, but in selector dot-notation it's always key form)
|
|
250
|
+
const replacementKey = newParts.key;
|
|
251
|
+
return match.replace(`.${oldParts.key}`, `.${replacementKey}`);
|
|
252
|
+
});
|
|
253
|
+
// Bracket notation: fn(($) => $["Old Key"]) etc.
|
|
254
|
+
const bracketRegex = new RegExp(`${prefix}\\s*\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*\\1\\s*\\[\\s*(['"\`])${escapeRegex(oldParts.key)}\\2\\s*\\]\\s*\\)`, 'g');
|
|
255
|
+
newCode = newCode.replace(bracketRegex, (match, param, quote) => {
|
|
256
|
+
changes++;
|
|
257
|
+
const replacementKey = newParts.key;
|
|
258
|
+
// If replacementKey is a valid identifier, convert to dot-notation, otherwise keep bracket form with preserved quote style
|
|
259
|
+
if (/^[A-Za-z_$][\w$]*$/.test(replacementKey)) {
|
|
260
|
+
return match.replace(new RegExp(`\\[\\s*['"\`]${escapeRegex(oldParts.key)}['"\`]\\s*\\]`), `.${replacementKey}`);
|
|
264
261
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const selectorBracketRegex = new RegExp(`(\\b${patternPrefix}\\(\\s*\\(?\\s*([a-zA-Z_$][\\w$]*)\\s*\\)?\\s*=>\\s*)\\2\\[\\s*(['"\`])${escapeRegex(original)}\\3\\s*\\](\\s*\\))`, 'g');
|
|
268
|
-
if (selectorBracketRegex.test(newCode)) {
|
|
269
|
-
const replacementKey = getReplacementKey(original);
|
|
270
|
-
const isIdentifier = (s) => /^[A-Za-z_$][\w$]*$/.test(s);
|
|
271
|
-
newCode = newCode.replace(selectorBracketRegex, (match, prefix, param, quote, suffix) => {
|
|
272
|
-
changes++;
|
|
273
|
-
// If the replacement is a valid identifier, convert to dot-notation, otherwise keep bracket-notation
|
|
274
|
-
if (isIdentifier(replacementKey)) {
|
|
275
|
-
return `${prefix}${param}.${replacementKey}${suffix}`;
|
|
276
|
-
}
|
|
277
|
-
return `${prefix}${param}[${quote}${replacementKey}${quote}]${suffix}`;
|
|
278
|
-
});
|
|
262
|
+
else {
|
|
263
|
+
return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `$1${replacementKey}$1`);
|
|
279
264
|
}
|
|
280
|
-
}
|
|
265
|
+
});
|
|
281
266
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const i18nKeyPatterns = [
|
|
287
|
-
{ pattern: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g'), original: oldParts.fullKey },
|
|
288
|
-
{ pattern: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g'), original: oldParts.key }
|
|
267
|
+
// JSX i18nKey attribute (handles all quote types)
|
|
268
|
+
const jsxPatterns = [
|
|
269
|
+
{ orig: oldParts.fullKey, regex: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g') },
|
|
270
|
+
{ orig: oldParts.key, regex: new RegExp(`i18nKey=(['"\`])${escapeRegex(oldParts.key)}\\1`, 'g') }
|
|
289
271
|
];
|
|
290
|
-
for (const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
272
|
+
for (const p of jsxPatterns) {
|
|
273
|
+
newCode = newCode.replace(p.regex, (match, q) => {
|
|
274
|
+
changes++;
|
|
275
|
+
const nsSepStr = nsSeparator === false ? ':' : nsSeparator;
|
|
276
|
+
const replacement = (p.orig === oldParts.fullKey && oldParts.fullKey.includes(nsSepStr)) ? newParts.fullKey : newParts.key;
|
|
277
|
+
return `i18nKey=${q}${replacement}${q}`;
|
|
278
|
+
});
|
|
298
279
|
}
|
|
299
280
|
return { newCode, changes };
|
|
300
281
|
}
|