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.
146 lines
6.1 KiB
146 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.`);
|