@intlayer/chokidar
Version:
Uses chokidar to scan and build Intlayer declaration files into dictionaries based on Intlayer configuration.
120 lines (118 loc) • 5.57 kB
JavaScript
import { writeFileIfChanged } from "../writeFileIfChanged.mjs";
import { getPathHash } from "../utils/getPathHash.mjs";
import { mkdir } from "node:fs/promises";
import { basename, extname, join, relative } from "node:path";
import fg from "fast-glob";
import { kebabCaseToCamelCase, normalizePath } from "@intlayer/config/utils";
//#region src/createType/createModuleAugmentation.ts
const getTypeName = (key) => `${kebabCaseToCamelCase(key)}Content`;
/** Returns lines like: [Locales.FRENCH]: 1; */
const formatLocales = (locales) => locales.map((locale) => ` "${locale}": 1;`).join("\n");
const zodToTsString = (schema) => {
if (!schema) return "any";
const def = schema._def ?? schema.def ?? schema;
switch (def.typeName ?? def.type) {
case "ZodString":
case "string": return "string";
case "ZodNumber":
case "number": return "number";
case "ZodBoolean":
case "boolean": return "boolean";
case "ZodNull":
case "null": return "null";
case "ZodUndefined":
case "undefined": return "undefined";
case "ZodArray":
case "array": return `${zodToTsString(def.type ?? def.element)}[]`;
case "ZodObject":
case "object": {
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
if (!shape) return "Record<string, any>";
return `{\n${Object.entries(shape).map(([k, v]) => ` "${k}": ${zodToTsString(v)};`).join("\n")}\n }`;
}
case "ZodOptional":
case "optional": return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;
case "ZodNullable":
case "nullable": return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;
case "ZodUnion":
case "union": return (def.options ?? []).map(zodToTsString).join(" | ");
case "ZodIntersection":
case "intersection": return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;
case "ZodEnum":
case "enum": return (def.values ?? []).map((v) => `"${v}"`).join(" | ");
case "ZodLiteral":
case "literal": {
const value = def.value;
return typeof value === "string" ? `"${value}"` : String(value);
}
default: return "any";
}
};
/** Generate the content of the module augmentation file */
const generateTypeIndexContent = (typeFiles, configuration, zodToTsFns) => {
const { internationalization, system, editor } = configuration;
const { moduleAugmentationDir } = system;
const { enabled } = editor;
const { locales, requiredLocales, strictMode } = internationalization;
let fileContent = "import \"intlayer\";\n";
const dictionariesRef = typeFiles.map((dictionaryPath) => ({
relativePath: `./${relative(moduleAugmentationDir, dictionaryPath)}`,
id: basename(dictionaryPath, extname(dictionaryPath)),
hash: `_${getPathHash(dictionaryPath)}`
}));
for (const dictionary of dictionariesRef) fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\n`;
fileContent += "\n";
const formattedDictionaryMap = dictionariesRef.map((dictionary) => ` "${dictionary.id}": typeof ${dictionary.hash};`).join("\n");
const declared = locales;
const requiredSanitized = requiredLocales?.length ? requiredLocales.filter((requiredLocales) => declared.includes(requiredLocales)) : declared;
const formattedDeclaredLocales = formatLocales(declared);
const formattedRequiredLocales = formatLocales(requiredSanitized);
const schemas = configuration.schemas ?? {};
const formattedSchemas = Object.entries(schemas).map(([key, schema]) => {
let typeStr = "any";
if (schema) try {
if (zodToTsFns) {
const { node } = zodToTsFns.zodToTs(schema, { auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore() });
if (node.kind !== 133) typeStr = zodToTsFns.printNode(node);
else typeStr = zodToTsString(schema);
} else typeStr = zodToTsString(schema);
} catch (_e) {
typeStr = zodToTsString(schema);
}
return ` "${key}": ${typeStr};`;
}).join("\n");
const strictKey = strictMode === "strict" ? "strict" : strictMode === "inclusive" ? "inclusive" : "loose";
/**
* Module augmentation that ONLY adds keys to registries.
* No types/aliases redefined here—avoids merge conflicts.
*/
fileContent += `declare module 'intlayer' {\n`;
fileContent += ` interface __DictionaryRegistry {\n${formattedDictionaryMap}\n }\n\n`;
fileContent += ` interface __DeclaredLocalesRegistry {\n${formattedDeclaredLocales}\n }\n\n`;
fileContent += ` interface __RequiredLocalesRegistry {\n${formattedRequiredLocales}\n }\n\n`;
fileContent += ` interface __SchemaRegistry {\n${formattedSchemas}\n }\n\n`;
fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\n\n`;
fileContent += ` interface __EditorRegistry { enabled : ${enabled} } \n`;
fileContent += `}\n`;
return fileContent;
};
/** Generate the index file merging all the types */
const createModuleAugmentation = async (configuration) => {
const { moduleAugmentationDir, typesDir } = configuration.system;
await mkdir(moduleAugmentationDir, { recursive: true });
const dictionariesTypesDefinitions = await fg(normalizePath(`${typesDir}/*.ts`), { ignore: ["**/*.d.ts"] });
let zodToTsFns = null;
try {
const mod = await import("zod-to-ts");
zodToTsFns = {
zodToTs: mod.zodToTs,
printNode: mod.printNode,
createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore
};
} catch {}
const tsContent = generateTypeIndexContent(dictionariesTypesDefinitions, configuration, zodToTsFns);
await writeFileIfChanged(join(moduleAugmentationDir, "intlayer.d.ts"), tsContent);
};
//#endregion
export { createModuleAugmentation, getTypeName };
//# sourceMappingURL=createModuleAugmentation.mjs.map