next-intlayer
Version:
Simplify internationalization i18n in Next.js with context providers, hooks, locale detection, and multilingual content integration.
202 lines (200 loc) • 7.82 kB
JavaScript
import { isAbsolute, join, relative, resolve } from "node:path";
import { prepareIntlayer, runOnce } from "@intlayer/chokidar";
import { ANSIColors, colorize, compareVersions, getAlias, getAppLogger, getConfiguration, getProjectRequire, normalizePath } from "@intlayer/config";
import { getDictionaries } from "@intlayer/dictionaries-entry";
import { IntlayerPlugin } from "@intlayer/webpack";
import { defu } from "defu";
import fg from "fast-glob";
import nextPackageJSON from "next/package.json" with { type: "json" };
//#region src/server/withIntlayer.ts
const isGteNext13 = compareVersions(nextPackageJSON.version, "≥", "13.0.0");
const isGteNext15 = compareVersions(nextPackageJSON.version, "≥", "15.0.0");
const isGteNext16 = compareVersions(nextPackageJSON.version, "≥", "16.0.0");
const isTurbopackStable = compareVersions(nextPackageJSON.version, "≥", "15.3.0");
const isTurbopackEnabledFromCommand = isGteNext16 ? !process.env.npm_lifecycle_script?.includes("--webpack") : process.env.npm_lifecycle_script?.includes("--turbo");
const getIsSwcPluginAvailable = (intlayerConfig) => {
try {
(intlayerConfig.build?.require ?? getProjectRequire()).resolve("@intlayer/swc");
return true;
} catch (_e) {
return false;
}
};
const resolvePluginPath = (pluginPath, intlayerConfig, isTurbopackEnabled) => {
const pluginPathResolved = (intlayerConfig.build?.require ?? getProjectRequire())?.resolve(pluginPath);
if (isTurbopackEnabled) return normalizePath(`./${relative(process.cwd(), pluginPathResolved)}`);
return pluginPathResolved;
};
const getPruneConfig = (intlayerConfig, isBuildCommand, isTurbopackEnabled) => {
const { optimize, traversePattern, importMode } = intlayerConfig.build;
const { dictionariesDir, unmergedDictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, mainDir, baseDir } = intlayerConfig.content;
const logger = getAppLogger(intlayerConfig);
if (optimize === false) return {};
if (optimize === void 0 && !isBuildCommand) return {};
if (!isGteNext13) return {};
if (!getIsSwcPluginAvailable(intlayerConfig)) {
logger([
colorize("Recommended: Install", ANSIColors.GREY),
colorize("@intlayer/swc", ANSIColors.GREY_LIGHT),
colorize("package to enable build optimization. See documentation: ", ANSIColors.GREY),
colorize("https://intlayer.org/docs/en/bundle_optimization", ANSIColors.GREY_LIGHT)
]);
return {};
}
runOnce(join(baseDir, ".intlayer", "cache", "intlayer-prune-plugin-enabled.lock"), () => logger("Build optimization enabled"), { cacheTimeoutMs: 1e3 * 10 });
const dictionariesEntryPath = join(mainDir, "dictionaries.mjs");
const dynamicDictionariesEntryPath = join(mainDir, "dynamic_dictionaries.mjs");
const unmergedDictionariesEntryPath = join(mainDir, "unmerged_dictionaries.mjs");
const fetchDictionariesEntryPath = join(mainDir, "fetch_dictionaries.mjs");
const filesList = [
...fg.sync(traversePattern, { cwd: baseDir }).map((file) => {
if (isAbsolute(file)) return file;
return join(baseDir, file);
}),
dictionariesEntryPath,
unmergedDictionariesEntryPath
];
const dictionaries = getDictionaries(intlayerConfig);
const liveSyncKeys = Object.values(dictionaries).filter((dictionary) => dictionary.live).map((dictionary) => dictionary.key);
return { experimental: { swcPlugins: [[resolvePluginPath("@intlayer/swc", intlayerConfig, isTurbopackEnabled), {
dictionariesDir,
dictionariesEntryPath,
unmergedDictionariesEntryPath,
unmergedDictionariesDir,
dynamicDictionariesDir,
dynamicDictionariesEntryPath,
fetchDictionariesDir,
fetchDictionariesEntryPath,
importMode,
filesList,
replaceDictionaryEntry: true,
liveSyncKeys
}]] } };
};
const getCommandsEvent = () => {
const lifecycleEvent = process.env.npm_lifecycle_event;
const lifecycleScript = process.env.npm_lifecycle_script ?? "";
return {
isDevCommand: lifecycleEvent === "dev" || process.argv.some((arg) => arg === "dev") || /(^|\s)(next\s+)?dev(\s|$)/.test(lifecycleScript),
isBuildCommand: lifecycleEvent === "build" || process.argv.some((arg) => arg === "build") || /(^|\s)(next\s+)?build(\s|$)/.test(lifecycleScript),
isStartCommand: lifecycleEvent === "start" || process.argv.some((arg) => arg === "start") || /(^|\s)(next\s+)?start(\s|$)/.test(lifecycleScript)
};
};
/**
* A Next.js plugin that adds the intlayer configuration to the webpack configuration
* and sets the environment variables
*
* Usage:
*
* ```ts
* // next.config.js
* export default withIntlayerSync(nextConfig)
* ```
*/
const withIntlayerSync = (nextConfig = {}, configOptions) => {
if (typeof nextConfig !== "object") nextConfig = {};
const intlayerConfig = getConfiguration(configOptions);
const logger = getAppLogger(intlayerConfig);
const isTurbopackEnabled = configOptions?.enableTurbopack ?? isTurbopackEnabledFromCommand;
if (isTurbopackEnabled && typeof nextConfig.webpack !== "undefined") logger("Turbopack is enabled but a custom webpack config is present. It will be ignored.");
const { isBuildCommand, isDevCommand } = getCommandsEvent();
const turboConfig = {
resolveAlias: getAlias({
configuration: intlayerConfig,
formatter: (value) => `./${value}`
}),
rules: { "*.node": {
as: "*.node",
loaders: ["node-loader"]
} }
};
const serverExternalPackages = [
"esbuild",
"module",
"fs",
"chokidar",
"fsevents"
];
const getNewConfig = () => {
let config = {};
if (isGteNext15) config = {
...config,
serverExternalPackages
};
if (isGteNext13 && !isGteNext15) config = {
...config,
experimental: {
...config?.experimental ?? {},
serverComponentsExternalPackages: serverExternalPackages
}
};
if (isTurbopackEnabled) if (isGteNext15 && isTurbopackStable) config = {
...config,
turbopack: turboConfig
};
else config = {
...config,
experimental: {
...config?.experimental ?? {},
turbo: turboConfig
}
};
else config = {
...config,
webpack: (config$1, options) => {
const { isServer, nextRuntime } = options;
if (typeof nextConfig.webpack === "function") config$1 = nextConfig.webpack(config$1, options);
if (config$1.externals === false) config$1.externals = [];
config$1.externals.push({
esbuild: "esbuild",
module: "module",
fs: "fs",
chokidar: "chokidar",
fsevents: "fsevents"
});
config$1.module.rules.push({
test: /\.node$/,
loader: "node-loader"
});
config$1.resolve.alias = {
...config$1.resolve.alias,
...getAlias({
configuration: intlayerConfig,
formatter: (value) => resolve(value)
})
};
if (isDevCommand && isServer && nextRuntime === "nodejs") config$1.plugins.push(new IntlayerPlugin(intlayerConfig));
return config$1;
}
};
return config;
};
const pruneConfig = getPruneConfig(intlayerConfig, isBuildCommand, isTurbopackEnabled ?? false);
return defu(defu(getNewConfig(), pruneConfig), nextConfig);
};
/**
* A Next.js plugin that adds the intlayer configuration to the webpack configuration
* and sets the environment variables
*
* Usage:
*
* ```ts
* // next.config.js
* export default withIntlayer(nextConfig)
* ```
*
* > Node withIntlayer is a promise function. Use withIntlayerSync instead if you want to use it synchronously.
* > Using the promise allows to prepare the intlayer dictionaries before the build starts.
*
*/
const withIntlayer = async (nextConfig = {}, configOptions) => {
const { isBuildCommand, isDevCommand } = getCommandsEvent();
if (isDevCommand || isBuildCommand) await prepareIntlayer(getConfiguration(configOptions), {
clean: isBuildCommand,
cacheTimeoutMs: isBuildCommand ? 1e3 * 30 : 1e3 * 60 * 60
});
return withIntlayerSync(nextConfig, configOptions);
};
//#endregion
export { withIntlayer, withIntlayerSync };
//# sourceMappingURL=withIntlayer.mjs.map