You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/add-texts-label-batch.ts

150 lines
6.1 KiB

/**
* One-off: add `texts.label` to a list of morfos + corresponding entry in the
* langs catalog. Used to batch-fix passive components missing A-1.3 in the
* audit.
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
const REPO = join(HERE, '..');
const MORFO_DIR = join(REPO, 'src', 'uix', 'morfo', 'components');
const LANGS_DIR = join(REPO, 'src', 'uix', 'langs', 'components');
interface Entry {
kebab: string;
label: { es: string; en: string };
}
const BATCH: Entry[] = [
{ kebab: 'alert-dialog', label: { es: 'Diálogo de alerta', en: 'Alert dialog' } },
{ kebab: 'clipboard', label: { es: 'Portapapeles', en: 'Clipboard' } },
{ kebab: 'color-picker', label: { es: 'Selector de color', en: 'Color picker' } },
{ kebab: 'link-preview', label: { es: 'Vista previa de enlace', en: 'Link preview' } },
{ kebab: 'pin-input', label: { es: 'Código PIN', en: 'PIN input' } },
{ kebab: 'scroll-area', label: { es: 'Área desplazable', en: 'Scroll area' } },
{ kebab: 'date-range-field', label: { es: 'Rango de fechas', en: 'Date range field' } },
{ kebab: 'virtual-grid', label: { es: 'Cuadrícula virtual', en: 'Virtual grid' } },
{ kebab: 'virtual-list', label: { es: 'Lista virtual', en: 'Virtual list' } },
{ kebab: 'time-picker', label: { es: 'Selector de hora', en: 'Time picker' } },
{ kebab: 'time-range-field', label: { es: 'Rango de horas', en: 'Time range field' } },
{
kebab: 'time-range-picker',
label: { es: 'Selector de rango de horas', en: 'Time range picker' }
}
];
function addLabelToMorfo(kebab: string): boolean {
const path = join(MORFO_DIR, `${kebab}.ts`);
const src = readFileSync(path, 'utf8');
if (src.includes(`#?components.${kebab}.label`)) return false; // already has it
// Find `texts: {` block
const m = src.match(/^\ttexts:\s*\{/m);
if (m) {
// Insert label as first entry
const insertAt = m.index! + m[0].length;
const labelLine = `\n\t\tlabel: '#?components.${kebab}.label|${kebab[0].toUpperCase()}${kebab.slice(1).replace(/-(.)/g, (_, c) => ' ' + c.toUpperCase())}',`;
const next = src.slice(0, insertAt) + labelLine + src.slice(insertAt);
writeFileSync(path, next, 'utf8');
return true;
}
// No texts block — insert one after `scope:` line
const scopeRe = /(\n\tscope:\s*\[[^\]]+\],?\n)/;
const sm = src.match(scopeRe);
if (!sm) {
console.error(`No scope: line in ${path}`);
return false;
}
const label = `\tscope: [...},\n\ttexts: {\n\t\tlabel: '#?components.${kebab}.label|Generated'\n\t},\n`;
// Simpler: locate the kebab line and insert texts after it
const kebabRe = new RegExp(`(\\n\\tkebab:\\s*'${kebab}'.*?\\n)`);
const km = src.match(kebabRe);
if (!km) {
console.error(`No kebab: '${kebab}' line`);
return false;
}
// Find next significant property after scope to anchor insert
const scopeEndRe = /\tscope:\s*\[[^\]]+\][^\n]*\n/;
const sem = src.match(scopeEndRe);
if (!sem) return false;
const start = sem.index! + sem[0].length;
const camel =
kebab[0].toUpperCase() + kebab.slice(1).replace(/-(.)/g, (_, c) => ' ' + c.toUpperCase());
const textsBlock = `\ttexts: {\n\t\tlabel: '#?components.${kebab}.label|${camel}'\n\t},\n`;
const next = src.slice(0, start) + textsBlock + src.slice(start);
writeFileSync(path, next, 'utf8');
return true;
}
function kebabToCamel(kebab: string): string {
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
}
function addLabelToLangs(entry: Entry): boolean {
const path = join(LANGS_DIR, `${entry.kebab}.ts`);
const camel = kebabToCamel(entry.kebab);
let src: string;
try {
src = readFileSync(path, 'utf8');
} catch {
// Create new langs file
src = `import type { LangNode } from '$libs/langs';\n\n/**\n * Default strings for the ${entry.kebab} component. Merged under\n * \`components.${entry.kebab}.*\` by \`ActiveUix\` (via \`componentLangs\` barrel).\n */\nexport const ${camel}Langs = {\n} satisfies LangNode;\n`;
writeFileSync(path, src, 'utf8');
console.log(` + created ${path.replace(REPO, '')}`);
// Re-read
src = readFileSync(path, 'utf8');
// Add to barrel
addToBarrel(entry.kebab, camel);
}
if (/^\s*label:\s*\{/m.test(src)) return false; // already has it
const exportRe = new RegExp(`(export const ${camel}Langs\\s*=\\s*\\{)`);
const m = src.match(exportRe);
if (!m) {
console.error(`No export const ${camel}Langs in ${path}`);
return false;
}
const insertAt = m.index! + m[0].length;
const labelBlock = `\n\tlabel: {\n\t\tes: '${entry.label.es}',\n\t\ten: '${entry.label.en}'\n\t},`;
const next = src.slice(0, insertAt) + labelBlock + src.slice(insertAt);
writeFileSync(path, next, 'utf8');
return true;
}
function addToBarrel(kebab: string, camel: string): void {
const barrelPath = join(LANGS_DIR, 'index.ts');
let src = readFileSync(barrelPath, 'utf8');
if (src.includes(`from './${kebab}'`)) return;
// Insert import in alphabetical-ish position by appending before last import
const importLine = `import { ${camel}Langs } from './${kebab}';\n`;
const lastImportMatch = [...src.matchAll(/^import .* from '\.\/[^']+';$/gm)].pop();
if (lastImportMatch) {
const insertAt = lastImportMatch.index! + lastImportMatch[0].length + 1;
src = src.slice(0, insertAt) + importLine + src.slice(insertAt);
}
// Add entry to satisfies block — insert before closing brace
const closeBraceMatch = src.match(/\n\} satisfies LangNode;/);
if (closeBraceMatch) {
const keyName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(kebab) ? kebab : `'${kebab}'`;
const entry = `\t${keyName}: ${camel}Langs,\n`;
const insertAt = closeBraceMatch.index!;
src = src.slice(0, insertAt) + entry + src.slice(insertAt);
}
writeFileSync(barrelPath, src, 'utf8');
console.log(` + barrel updated for ${kebab}`);
}
let added = 0;
for (const entry of BATCH) {
const m = addLabelToMorfo(entry.kebab);
const l = addLabelToLangs(entry);
if (m || l) {
console.log(`✓ ${entry.kebab}${m ? ' [morfo]' : ''}${l ? ' [langs]' : ''}`);
added++;
} else {
console.log(`- ${entry.kebab} (already has label)`);
}
}
console.log(`\nAdded labels to ${added}/${BATCH.length} components.`);

Powered by TurnKey Linux.