@inlang/paraglide-js
Version:
[](https://inlang.com)
149 lines (148 loc) • 6.11 kB
JavaScript
import { compile } from "../compiler/compile.js";
import fs from "node:fs";
import { resolve, relative } from "node:path";
import { nodeNormalizePath } from "../utilities/node-normalize-path.js";
import { Logger } from "../services/logger/index.js";
const PLUGIN_NAME = "unplugin-paraglide-js";
const logger = new Logger();
/**
* Default isServer which differs per bundler.
*/
let isServer;
let previousCompilation;
export const unpluginFactory = (args) => ({
name: PLUGIN_NAME,
enforce: "pre",
async buildStart() {
const isProduction = process.env.NODE_ENV === "production";
// default to locale-modules for development to speed up the dev server
// https://github.com/opral/inlang-paraglide-js/issues/486
const outputStructure = args.outputStructure ??
(isProduction ? "message-modules" : "locale-modules");
try {
previousCompilation = await compile({
fs: wrappedFs,
previousCompilation,
outputStructure,
// webpack invokes the `buildStart` api in watch mode,
// to avoid cleaning the output directory in watch mode,
// we only clean the output directory if there was no previous compilation
cleanOutdir: previousCompilation === undefined,
isServer,
...args,
});
logger.success(`Compilation complete (${outputStructure})`);
}
catch (error) {
logger.error("Failed to compile project:", error.message);
logger.info("Please check your translation files for syntax errors.");
}
finally {
// in any case add the files to watch
for (const path of Array.from(readFiles)) {
this.addWatchFile(path);
}
}
},
async watchChange(path) {
const shouldCompile = readFiles.has(path) && !path.includes("cache");
if (shouldCompile === false) {
return;
}
const isProduction = process.env.NODE_ENV === "production";
// default to locale-modules for development to speed up the dev server
// https://github.com/opral/inlang-paraglide-js/issues/486
const outputStructure = args.outputStructure ??
(isProduction ? "message-modules" : "locale-modules");
const previouslyReadFiles = new Set(readFiles);
try {
logger.info(`Re-compiling inlang project... File "${relative(process.cwd(), path)}" has changed.`);
// Clear readFiles to track fresh file reads
readFiles.clear();
previousCompilation = await compile({
fs: wrappedFs,
previousCompilation,
outputStructure,
cleanOutdir: false,
isServer,
...args,
});
logger.success(`Re-compilation complete (${outputStructure})`);
// Add any new files to watch
for (const filePath of Array.from(readFiles)) {
this.addWatchFile(filePath);
}
}
catch (e) {
readFiles = previouslyReadFiles;
// Reset compilation result on error
previousCompilation = undefined;
logger.warn("Failed to re-compile project:", e.message);
}
},
vite: {
config: {
handler: () => {
isServer = "import.meta.env?.SSR ?? typeof window === 'undefined'";
},
},
},
webpack(compiler) {
compiler.options.resolve = {
...compiler.options.resolve,
fallback: {
...compiler.options.resolve?.fallback,
// https://stackoverflow.com/a/72989932
async_hooks: false,
},
};
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => {
const isProduction = process.env.NODE_ENV === "production";
// default to locale-modules for development to speed up the dev server
// https://github.com/opral/inlang-paraglide-js/issues/486
const outputStructure = args.outputStructure ??
(isProduction ? "message-modules" : "locale-modules");
try {
previousCompilation = await compile({
fs: wrappedFs,
previousCompilation,
outputStructure,
// clean dir needs to be false. otherwise webpack get's into a race condition
// of deleting the output directory and writing files at the same time for
// multi environment builds
cleanOutdir: false,
...args,
});
logger.success(`Compilation complete (${outputStructure})`);
}
catch (error) {
logger.warn("Failed to compile project:", error.message);
logger.warn("Please check your translation files for syntax errors.");
}
});
},
});
let readFiles = new Set();
// Create a wrapper around the fs object to intercept and store read files
const wrappedFs = {
...fs,
// @ts-expect-error - Node's fs has too many overloads
readFile: (path, options, callback) => {
readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
return fs.readFile(path, options, callback);
},
// @ts-expect-error - Node's fs has too many overloads
readFileSync: (path, options) => {
readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
return fs.readFileSync(path, options);
},
promises: {
...fs.promises,
// @ts-expect-error - Node's fs.promises has too many overloads
readFile: async (path, options) => {
readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
return fs.promises.readFile(path, options);
},
},
// Add other fs methods as needed
};