@inlang/paraglide-js
Version:
[](https://www.npmjs.com/package/@inlang/paraglide-js) [ => {
if (value === "message-modules" || value === "locale-modules") {
return value;
}
throw new InvalidArgumentError(`Invalid output structure "${value}". Expected "message-modules" or "locale-modules".`);
};
export const compileCommand = new Command()
.name("compile")
.summary("Compiles inlang Paraglide-JS")
.requiredOption("--project <path>", 'The path to the inlang project. Example: "./project.inlang"', DEFAULT_PROJECT_PATH)
.requiredOption("--outdir <path>", 'The path to the output directory. Example: "./src/paraglide"', DEFAULT_OUTDIR)
.option("--strategy <items...>", [
"The strategy to be used.",
"",
"Example: --strategy cookie globalVariable baseLocale",
"Read more on https://paraglidejs.com/strategy",
].join("\n"))
.requiredOption("--silent", "Only log errors to the console", false)
.option("--emit-ts-declarations", "Emit .d.ts files for the generated output (requires the typescript package)", defaultCompilerOptions.emitTsDeclarations)
.option("--no-emit-ts-declarations", "Do not emit .d.ts files for the generated output")
.option("--emit-git-ignore", "Emit a .gitignore in the output directory", defaultCompilerOptions.emitGitIgnore)
.option("--no-emit-git-ignore", "Do not emit a .gitignore in the output directory")
.option("--emit-prettier-ignore", "Emit a .prettierignore in the output directory", defaultCompilerOptions.emitPrettierIgnore)
.option("--no-emit-prettier-ignore", "Do not emit a .prettierignore in the output directory")
.option("--emit-readme", "Emit a README.md in the output directory (helps LLMs understand the generated code)", defaultCompilerOptions.emitReadme)
.option("--no-emit-readme", "Do not emit README.md in the output directory")
.option("--is-server <expression>", [
"JavaScript expression for runtime `isServer` (enables server/client tree-shaking).",
'Quote if the expression contains spaces, e.g. --is-server \'typeof window === "undefined"\'.',
"Vite SSR example: --is-server 'import.meta.env.SSR'",
].join(" "))
.option("--output-structure <structure>", [
'The output structure for compiled messages. Either "message-modules" or "locale-modules".',
"",
'"message-modules" gives each message its own module (better tree-shaking).',
'"locale-modules" bundles messages per locale (fewer files, better for large projects in dev).',
].join("\n"), parseOutputStructure, defaultCompilerOptions.outputStructure)
.option("--watch", "Watch project files and recompile on change", false)
.action(async (options) => {
const logger = new Logger({ silent: options.silent, prefix: true });
const path = resolve(process.cwd(), options.project);
const compileOptions = {
project: path,
outdir: options.outdir,
strategy: options.strategy ?? defaultCompilerOptions.strategy,
emitTsDeclarations: options.emitTsDeclarations ??
defaultCompilerOptions.emitTsDeclarations,
emitGitIgnore: options.emitGitIgnore ?? defaultCompilerOptions.emitGitIgnore,
emitPrettierIgnore: options.emitPrettierIgnore ??
defaultCompilerOptions.emitPrettierIgnore,
emitReadme: options.emitReadme ?? defaultCompilerOptions.emitReadme,
isServer: options.isServer ?? defaultCompilerOptions.isServer,
outputStructure: options.outputStructure ??
defaultCompilerOptions.outputStructure,
};
if (!options.watch) {
logger.info(`Compiling inlang project ...`);
try {
await compile(compileOptions);
}
catch (e) {
logger.error("Error while compiling inlang project.");
logger.error(e);
process.exit(1);
}
logger.success(`Successfully compiled inlang project.`);
process.exit(0);
}
const { fs: trackedFs, readFiles, clearReadFiles } = createTrackedFs();
const fileWatchers = new Map();
const directoryWatchers = new Map();
let previousCompilation;
let compileTimer;
let compileInProgress = false;
let compileRequested = false;
const updateWatchers = (files) => {
const { files: nextFiles, directories: nextDirectories, isIgnoredPath, } = getWatchTargets(files, { outdir: options.outdir });
for (const [filePath, watcher] of fileWatchers) {
if (!nextFiles.has(filePath)) {
watcher.close();
fileWatchers.delete(filePath);
}
}
for (const [directoryPath, watcher] of directoryWatchers) {
if (!nextDirectories.has(directoryPath)) {
watcher.close();
directoryWatchers.delete(directoryPath);
}
}
const attachWatcherErrorHandler = (watcher, targetPath, targetType) => {
watcher.on("error", (error) => {
logger.warn(`Watch ${targetType} error for ${targetPath}: ${error.message}`);
});
};
for (const filePath of nextFiles) {
if (fileWatchers.has(filePath)) {
continue;
}
try {
const watcher = fs.watch(filePath, () => {
scheduleCompile(filePath);
});
attachWatcherErrorHandler(watcher, filePath, "file");
fileWatchers.set(filePath, watcher);
}
catch (error) {
logger.warn(`Failed to watch file: ${filePath}`);
}
}
for (const directoryPath of nextDirectories) {
if (directoryWatchers.has(directoryPath)) {
continue;
}
try {
const watcher = fs.watch(directoryPath, (eventType, filename) => {
if (!filename) {
scheduleCompile(directoryPath);
return;
}
const changedPath = resolve(directoryPath, filename.toString());
if (isIgnoredPath(changedPath)) {
return;
}
scheduleCompile(changedPath);
});
attachWatcherErrorHandler(watcher, directoryPath, "directory");
directoryWatchers.set(directoryPath, watcher);
}
catch (error) {
logger.warn(`Failed to watch directory: ${directoryPath}`);
}
}
};
const runCompile = async (changedPath) => {
if (compileInProgress) {
compileRequested = true;
return;
}
compileInProgress = true;
const previouslyReadFiles = new Set(readFiles);
clearReadFiles();
if (changedPath) {
logger.info(`Re-compiling inlang project... File "${relative(process.cwd(), changedPath)}" has changed.`);
}
else {
logger.info("Compiling inlang project ...");
}
try {
const seededPrevious = previousCompilation ??
(await seedPreviousCompilationFromOutdir({
outdir: compileOptions.outdir,
}));
previousCompilation = await compile({
...compileOptions,
fs: trackedFs,
previousCompilation: seededPrevious,
cleanOutdir: false,
});
if (changedPath) {
logger.success("Re-compilation complete.");
}
else {
logger.success("Successfully compiled inlang project.");
}
}
catch (e) {
if (previouslyReadFiles.size > 0) {
clearReadFiles();
for (const filePath of previouslyReadFiles) {
readFiles.add(filePath);
}
}
previousCompilation = undefined;
logger.error("Error while compiling inlang project.");
logger.error(e);
}
finally {
updateWatchers(readFiles);
compileInProgress = false;
if (compileRequested) {
compileRequested = false;
runCompile();
}
}
};
const scheduleCompile = (changedPath) => {
if (compileTimer) {
clearTimeout(compileTimer);
}
compileTimer = setTimeout(() => {
runCompile(changedPath);
}, 100);
};
const closeWatchers = () => {
for (const watcher of fileWatchers.values()) {
watcher.close();
}
for (const watcher of directoryWatchers.values()) {
watcher.close();
}
fileWatchers.clear();
directoryWatchers.clear();
};
process.on("SIGINT", () => {
closeWatchers();
process.exit(0);
});
await runCompile();
logger.info("Watching for changes...");
});
//# sourceMappingURL=command.js.map