UNPKG

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
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