next-intlayer 9.4.3 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +0 -2
  2. package/dist/cjs/extractorLoader.cjs +148 -0
  3. package/dist/cjs/extractorLoader.cjs.map +1 -0
  4. package/dist/cjs/proxy/intlayerProxy.cjs +1 -1
  5. package/dist/cjs/server/prepareSwcOptimization.cjs +12 -20
  6. package/dist/cjs/server/prepareSwcOptimization.cjs.map +1 -1
  7. package/dist/cjs/server/startContentWatcher.cjs +91 -0
  8. package/dist/cjs/server/startContentWatcher.cjs.map +1 -0
  9. package/dist/cjs/server/withIntlayer.cjs +98 -13
  10. package/dist/cjs/server/withIntlayer.cjs.map +1 -1
  11. package/dist/esm/extractorLoader.mjs +146 -0
  12. package/dist/esm/extractorLoader.mjs.map +1 -0
  13. package/dist/esm/proxy/intlayerProxy.mjs +1 -1
  14. package/dist/esm/server/prepareSwcOptimization.mjs +12 -20
  15. package/dist/esm/server/prepareSwcOptimization.mjs.map +1 -1
  16. package/dist/esm/server/startContentWatcher.mjs +90 -0
  17. package/dist/esm/server/startContentWatcher.mjs.map +1 -0
  18. package/dist/esm/server/withIntlayer.mjs +98 -13
  19. package/dist/esm/server/withIntlayer.mjs.map +1 -1
  20. package/dist/types/@intlayer/babel/dist/types/babel-plugin-intlayer-field-rename.d.ts +1 -0
  21. package/dist/types/@intlayer/babel/dist/types/babel-plugin-intlayer-field-rename.d.ts.map +1 -1
  22. package/dist/types/@intlayer/babel/dist/types/babel-plugin-intlayer-optimize.d.ts +1 -1
  23. package/dist/types/@intlayer/babel/dist/types/babel-plugin-intlayer-purge.d.ts +1 -0
  24. package/dist/types/@intlayer/babel/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts +3 -2
  25. package/dist/types/@intlayer/babel/dist/types/index.d.ts +2 -0
  26. package/dist/types/@intlayer/babel/dist/types/pruneContext.d.ts +1 -0
  27. package/dist/types/@intlayer/babel/dist/types/staticAstReaders.d.ts +2 -0
  28. package/dist/types/@intlayer/babel/dist/types/transformers.d.ts +1 -0
  29. package/dist/types/extractorLoader.d.ts +63 -0
  30. package/dist/types/extractorLoader.d.ts.map +1 -0
  31. package/dist/types/server/prepareSwcOptimization.d.ts +2 -2
  32. package/dist/types/server/prepareSwcOptimization.d.ts.map +1 -1
  33. package/dist/types/server/startContentWatcher.d.ts +5 -0
  34. package/dist/types/server/startContentWatcher.d.ts.map +1 -0
  35. package/dist/types/server/withIntlayer.d.ts +8 -2
  36. package/dist/types/server/withIntlayer.d.ts.map +1 -1
  37. package/package.json +17 -9
@@ -1,20 +1,22 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_server_prepareSwcOptimization = require('./prepareSwcOptimization.cjs');
4
+ const require_server_startContentWatcher = require('./startContentWatcher.cjs');
4
5
  const require_server_swcPluginCompatibility = require('./swcPluginCompatibility.cjs');
5
- let _intlayer_config_defaultValues = require("@intlayer/config/defaultValues");
6
- let _intlayer_config_logger = require("@intlayer/config/logger");
7
6
  let node_path = require("node:path");
8
7
  let _intlayer_config_colors = require("@intlayer/config/colors");
9
8
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
10
- let _intlayer_config_envVars = require("@intlayer/config/envVars");
9
+ let _intlayer_config_logger = require("@intlayer/config/logger");
11
10
  let _intlayer_config_node = require("@intlayer/config/node");
12
11
  let _intlayer_config_utils = require("@intlayer/config/utils");
12
+ let _intlayer_engine_utils = require("@intlayer/engine/utils");
13
+ let _intlayer_config_defaultValues = require("@intlayer/config/defaultValues");
14
+ let _intlayer_config_callers = require("@intlayer/config/callers");
15
+ let _intlayer_config_envVars = require("@intlayer/config/envVars");
13
16
  let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
14
17
  let _intlayer_dictionaries_entry = require("@intlayer/dictionaries-entry");
15
18
  let _intlayer_engine_build = require("@intlayer/engine/build");
16
19
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
17
- let _intlayer_engine_utils = require("@intlayer/engine/utils");
18
20
  let _intlayer_webpack = require("@intlayer/webpack");
19
21
  let defu = require("defu");
20
22
  let next_package_json = require("next/package.json");
@@ -65,7 +67,70 @@ const resolvePluginPath = (pluginPath, intlayerConfig, isTurbopackEnabled) => {
65
67
  if (isTurbopackEnabled) return (0, _intlayer_config_utils.normalizePath)(`./${(0, node_path.relative)(process.cwd(), pluginPathResolved)}`);
66
68
  return pluginPathResolved;
67
69
  };
68
- const getPruneConfig = ({ intlayerConfig, isBuildCommand, isTurbopackEnabled, isDevCommand, isGteNext13, isSwcPluginSupported, nextVersion, swcExtraCallers, fieldRenameMap }) => {
70
+ /**
71
+ * Extensions the extraction loader is registered for.
72
+ *
73
+ * The compiler reads its content out of JSX — element text and the
74
+ * translatable attributes — so a file with no JSX in it has nothing to give,
75
+ * and `*.ts` is left out. `*.js` is in because a plain-JavaScript Next.js app
76
+ * writes its components there.
77
+ */
78
+ const EXTRACTOR_LOADER_EXTENSIONS = [
79
+ "tsx",
80
+ "jsx",
81
+ "js"
82
+ ];
83
+ /**
84
+ * The subset safe to register as a Turbopack rule on any Next.js version.
85
+ *
86
+ * Turbopack rules match every module it processes, dependencies included, and
87
+ * only Next.js 16 can express "skip `node_modules`" (see
88
+ * {@link TURBOPACK_PROJECT_FILES_CONDITION}). Below that, `*.js` would hand
89
+ * every dependency file to a Node loader worker, so it is left out rather than
90
+ * made to cost a cold dev start.
91
+ */
92
+ const TURBOPACK_ALWAYS_SAFE_EXTENSIONS = ["tsx", "jsx"];
93
+ /**
94
+ * Restricts a Turbopack rule to the project's own files. `foreign` is
95
+ * Turbopack's builtin condition for a module inside `node_modules`.
96
+ *
97
+ * Only understood from Next.js 16 on — earlier releases carry no `condition`
98
+ * field on a rule at all.
99
+ */
100
+ const TURBOPACK_PROJECT_FILES_CONDITION = { not: "foreign" };
101
+ /**
102
+ * Whether the Intlayer compiler should extract content for this command.
103
+ *
104
+ * Mirrors `getExtractPluginOptions`, which the loader re-evaluates on its side:
105
+ * this is only about not registering a loader that would do nothing.
106
+ */
107
+ const getIsCompilerEnabled = (intlayerConfig, isDevCommand) => {
108
+ const enabled = intlayerConfig.compiler?.enabled ?? _intlayer_config_defaultValues.COMPILER_ENABLED;
109
+ if (enabled === "build-only") return !isDevCommand;
110
+ return enabled !== false;
111
+ };
112
+ /**
113
+ * Describes the extraction loader for both bundlers, or `null` when the
114
+ * compiler is off or `@intlayer/babel` is not installed.
115
+ *
116
+ * The loader replaces the `babel.config.js` the compiler used to need on
117
+ * Next.js: Turbopack never reads a Babel config, and on webpack the mere
118
+ * presence of one opts the whole project out of SWC — including the
119
+ * `@intlayer/swc` optimize pass.
120
+ */
121
+ const getExtractorLoader = (intlayerConfig, isTurbopackEnabled, isDevCommand) => {
122
+ if (!getIsCompilerEnabled(intlayerConfig, isDevCommand)) return null;
123
+ if (!getIsBabelExtractPluginAvailable(intlayerConfig)) return null;
124
+ try {
125
+ return {
126
+ loaderPath: resolvePluginPath("next-intlayer/extractor-loader", intlayerConfig, isTurbopackEnabled),
127
+ mode: isDevCommand ? "dev" : "build"
128
+ };
129
+ } catch {
130
+ return null;
131
+ }
132
+ };
133
+ const getPruneConfig = ({ intlayerConfig, isBuildCommand, isTurbopackEnabled, isDevCommand, isGteNext13, isSwcPluginSupported, nextVersion, compatCallers, fieldRenameMap }) => {
69
134
  const { optimize, minify, purge } = intlayerConfig.build;
70
135
  const importMode = intlayerConfig.build.importMode ?? intlayerConfig.dictionary?.importMode;
71
136
  const { dictionariesDir, unmergedDictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, mainDir } = intlayerConfig.system;
@@ -91,7 +156,7 @@ const getPruneConfig = ({ intlayerConfig, isBuildCommand, isTurbopackEnabled, is
91
156
  (0, _intlayer_config_logger.colorize)(importMode ?? _intlayer_config_defaultValues.IMPORT_MODE, _intlayer_config_colors.BLUE),
92
157
  (0, _intlayer_config_logger.colorize)(`)`, _intlayer_config_colors.GREY_DARK)
93
158
  ]);
94
- const isPurgePipelineEnabled = !swcExtraCallers?.length && require_server_prepareSwcOptimization.getIsPurgePipelineAvailable(intlayerConfig);
159
+ const isPurgePipelineEnabled = require_server_prepareSwcOptimization.getIsPurgePipelineAvailable(intlayerConfig);
95
160
  if (isPurgePipelineEnabled && minify) logger(`Dictionary minification ${(0, _intlayer_config_logger.colorize)("enabled", _intlayer_config_colors.GREEN)}`);
96
161
  if (isPurgePipelineEnabled && purge) logger(`Dictionary purge unused keys ${(0, _intlayer_config_logger.colorize)("enabled", _intlayer_config_colors.GREEN)}`);
97
162
  } else logger([
@@ -138,7 +203,7 @@ const getPruneConfig = ({ intlayerConfig, isBuildCommand, isTurbopackEnabled, is
138
203
  replaceDictionaryEntry: true,
139
204
  nestingDictionaryKeys,
140
205
  dictionaryModeMap,
141
- extraCallers: swcExtraCallers ?? [],
206
+ extraCallers: (0, _intlayer_config_callers.toSwcExtraCallers)(compatCallers ?? []),
142
207
  fieldRenameMap: fieldRenameMap ?? {},
143
208
  logLevel: require_server_prepareSwcOptimization.resolveSwcLogLevel(intlayerConfig)
144
209
  }]] } };
@@ -203,15 +268,26 @@ const withIntlayerSync = (nextConfig = {}, configOptions) => {
203
268
  const isTurbopackEnabled = configOptions?.enableTurbopack ?? isTurbopackEnabledFromCommand;
204
269
  if (isTurbopackEnabled && typeof nextConfig.webpack !== "undefined") appLogger("Turbopack is enabled but a custom webpack config is present. It will be ignored.");
205
270
  const { isBuildCommand, isDevCommand } = getCommandsEvent();
271
+ const extractorLoader = getExtractorLoader(intlayerConfig, isTurbopackEnabled ?? false, isDevCommand);
272
+ if (isTurbopackEnabled && isDevCommand) require_server_startContentWatcher.startContentWatcher(intlayerConfig);
206
273
  const turboConfig = {
207
274
  resolveAlias: (0, _intlayer_config_utils.getAlias)({
208
275
  configuration: intlayerConfig,
209
276
  formatter: (value) => `./${value}`
210
277
  }),
211
- rules: { "*.node": {
212
- as: "*.node",
213
- loaders: ["node-loader"]
214
- } }
278
+ rules: {
279
+ "*.node": {
280
+ as: "*.node",
281
+ loaders: ["node-loader"]
282
+ },
283
+ ...extractorLoader ? Object.fromEntries((isGteNext16 ? EXTRACTOR_LOADER_EXTENSIONS : TURBOPACK_ALWAYS_SAFE_EXTENSIONS).map((extension) => [`*.${extension}`, {
284
+ loaders: [{
285
+ loader: extractorLoader.loaderPath,
286
+ options: { mode: extractorLoader.mode }
287
+ }],
288
+ ...isGteNext16 ? { condition: TURBOPACK_PROJECT_FILES_CONDITION } : {}
289
+ }])) : {}
290
+ }
215
291
  };
216
292
  const serverExternalPackages = [
217
293
  "esbuild",
@@ -289,6 +365,15 @@ const withIntlayerSync = (nextConfig = {}, configOptions) => {
289
365
  test: /\.node$/,
290
366
  loader: "node-loader"
291
367
  });
368
+ if (extractorLoader) config.module.rules.push({
369
+ test: new RegExp(`\\.(${EXTRACTOR_LOADER_EXTENSIONS.join("|")})$`),
370
+ exclude: /node_modules/,
371
+ enforce: "pre",
372
+ use: [{
373
+ loader: extractorLoader.loaderPath,
374
+ options: { mode: extractorLoader.mode }
375
+ }]
376
+ });
292
377
  config.resolve.alias = {
293
378
  ...config.resolve.alias,
294
379
  ...(0, _intlayer_config_utils.getAlias)({
@@ -310,7 +395,7 @@ const withIntlayerSync = (nextConfig = {}, configOptions) => {
310
395
  isGteNext13,
311
396
  isSwcPluginSupported,
312
397
  nextVersion,
313
- swcExtraCallers: configOptions?.swcExtraCallers,
398
+ compatCallers: configOptions?.compatCallers,
314
399
  fieldRenameMap: configOptions?.fieldRenameMap
315
400
  });
316
401
  const intlayerNextConfig = (0, defu.defu)(getNewConfig(), pruneConfig);
@@ -345,7 +430,7 @@ const withIntlayer = async (nextConfig = {}, configOptions) => {
345
430
  const { isSwcPluginSupported } = getNextVersionFlags(intlayerConfig);
346
431
  const fieldRenameMap = isBuildCommand ? await require_server_prepareSwcOptimization.prepareSwcOptimization(intlayerConfig, {
347
432
  configOptions: resolvedConfigOptions,
348
- swcExtraCallers: configOptions?.swcExtraCallers,
433
+ compatCallers: configOptions?.compatCallers,
349
434
  isSwcPluginUsable: isSwcPluginSupported && getIsSwcPluginAvailable(intlayerConfig)
350
435
  }) : {};
351
436
  const nextConfigResolved = await nextConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"withIntlayer.cjs","names":["nextPackageJSON","getProjectRequire","compareVersions","getIsSwcPluginSupported","normalizePath","relative","getAppLogger","join","colorize","ANSIColors","MINIMUM_SWC_PLUGIN_NEXT_VERSION","IMPORT_MODE","getIsPurgePipelineAvailable","buildComponentFilesList","getDictionaries","getNestedDictionaryGraph","resolveSwcLogLevel","getConfiguration","getAlias","getUnusedNodeTypes","formatNodeTypeToEnvVar","formatOptimizedNestingEnvVar","formatDictionarySelectorEnvVar","getHasDictionarySelector","getConfigEnvVars","resolve","IntlayerPlugin","prepareIntlayer","prepareSwcOptimization"],"sources":["../../../src/server/withIntlayer.ts"],"sourcesContent":["import { join, relative, resolve } from 'node:path';\nimport type { SwcExtraCallerConfig } from '@intlayer/config/callers';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { IMPORT_MODE } from '@intlayer/config/defaultValues';\nimport {\n formatDictionarySelectorEnvVar,\n formatNodeTypeToEnvVar,\n formatOptimizedNestingEnvVar,\n getConfigEnvVars,\n} from '@intlayer/config/envVars';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport {\n compareVersions,\n getAlias,\n getHasDictionarySelector,\n getProjectRequire,\n getUnusedNodeTypes,\n normalizePath,\n} from '@intlayer/config/utils';\nimport { getNestedDictionaryGraph } from '@intlayer/core/dictionaryManipulator';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { prepareIntlayer } from '@intlayer/engine/build';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { buildComponentFilesList, runOnce } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { IntlayerPlugin } from '@intlayer/webpack';\nimport { defu } from 'defu';\nimport type { NextConfig } from 'next';\nimport type { NextJsWebpackConfig } from 'next/dist/server/config-shared';\nimport nextPackageJSON from 'next/package.json' with { type: 'json' };\nimport {\n type FieldRenameMapByDictionaryKey,\n getIsPurgePipelineAvailable,\n prepareSwcOptimization,\n resolveSwcLogLevel,\n} from './prepareSwcOptimization';\nimport {\n getIsSwcPluginSupported,\n MINIMUM_SWC_PLUGIN_NEXT_VERSION,\n} from './swcPluginCompatibility';\n\nexport {\n getIsSwcPluginSupported,\n MINIMUM_SWC_PLUGIN_NEXT_VERSION,\n} from './swcPluginCompatibility';\n\n/**\n * Resolve the Next.js version from the *user's* project at runtime.\n * A static `import from 'next/package.json'` would resolve relative to\n * next-intlayer's own node_modules, which may differ in a monorepo.\n */\nconst getNextVersionFlags = (intlayerConfig: IntlayerConfig) => {\n let nextVersion = nextPackageJSON.version;\n\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n const pkg = requireFunction('next/package.json') as { version: string };\n nextVersion = pkg.version;\n } catch {\n // keep default\n }\n\n return {\n nextVersion,\n isGteNext13: compareVersions(nextVersion, '≥', '13.0.0'),\n isGteNext15: compareVersions(nextVersion, '≥', '15.0.0'),\n isGteNext16: compareVersions(nextVersion, '≥', '16.0.0'),\n isTurbopackStable: compareVersions(nextVersion, '≥', '15.3.0'),\n /**\n * Whether this Next.js release can load the `@intlayer/swc` Wasm plugin.\n * See {@link MINIMUM_SWC_PLUGIN_NEXT_VERSION}.\n */\n isSwcPluginSupported: getIsSwcPluginSupported(nextVersion),\n };\n};\n\n// Check if SWC plugin is available\nconst getIsSwcPluginAvailable = (intlayerConfig: IntlayerConfig) => {\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n requireFunction.resolve('@intlayer/swc');\n return true;\n } catch (_e) {\n return false;\n }\n};\n\n// Check if Babel plugin is available\nconst getIsBabelExtractPluginAvailable = (intlayerConfig: IntlayerConfig) => {\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n requireFunction.resolve('@intlayer/babel');\n return true;\n } catch (_e) {\n return false;\n }\n};\n\nconst resolvePluginPath = (\n pluginPath: string,\n intlayerConfig: IntlayerConfig,\n isTurbopackEnabled: boolean\n): string => {\n const requireFunction = intlayerConfig.build?.require ?? getProjectRequire();\n const pluginPathResolved = requireFunction?.resolve(pluginPath);\n\n if (isTurbopackEnabled)\n // Relative path for turbopack\n return normalizePath(`./${relative(process.cwd(), pluginPathResolved)}`);\n\n // Absolute path for webpack\n return pluginPathResolved;\n};\n\ntype GetPruneConfigParams = {\n intlayerConfig: IntlayerConfig;\n isBuildCommand: boolean;\n isTurbopackEnabled: boolean;\n isDevCommand: boolean;\n isGteNext13: boolean;\n /** Whether the resolved Next.js version can load the Wasm plugin. */\n isSwcPluginSupported: boolean;\n /** Resolved Next.js version, reported when the plugin has to stand down. */\n nextVersion: string;\n swcExtraCallers?: SwcExtraCallerConfig[];\n fieldRenameMap?: FieldRenameMapByDictionaryKey;\n};\n\nconst getPruneConfig = ({\n intlayerConfig,\n isBuildCommand,\n isTurbopackEnabled,\n isDevCommand,\n isGteNext13,\n isSwcPluginSupported,\n nextVersion,\n swcExtraCallers,\n fieldRenameMap,\n}: GetPruneConfigParams): Partial<NextConfig> => {\n const { optimize, minify, purge } = intlayerConfig.build;\n const importMode =\n intlayerConfig.build.importMode ?? intlayerConfig.dictionary?.importMode;\n const {\n dictionariesDir,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n mainDir,\n } = intlayerConfig.system;\n const { baseDir } = intlayerConfig.system;\n const logger = getAppLogger(intlayerConfig);\n\n if (optimize === false) {\n return {};\n }\n if (optimize === undefined && !isBuildCommand) {\n return {};\n }\n\n if (!isGteNext13) return {};\n\n const isSwcPluginAvailable = getIsSwcPluginAvailable(intlayerConfig);\n\n runOnce(\n join(baseDir, '.intlayer', 'cache', 'intlayer-prune-plugin-enabled.lock'),\n () => {\n if (isSwcPluginAvailable && !isSwcPluginSupported) {\n logger(\n [\n `Build optimization ${colorize('disabled', ANSIColors.GREY_DARK)}:`,\n colorize('@intlayer/swc', ANSIColors.GREY_LIGHT),\n colorize(\n `cannot run on Next.js ${nextVersion} — its bundled SWC predates the`,\n ANSIColors.GREY\n ),\n colorize(\n 'stable Wasm plugin ABI. Upgrade to Next.js',\n ANSIColors.GREY\n ),\n colorize(MINIMUM_SWC_PLUGIN_NEXT_VERSION, ANSIColors.BLUE),\n colorize('or later to enable it.', ANSIColors.GREY),\n ],\n { level: 'warn' }\n );\n } else if (isSwcPluginAvailable) {\n logger([\n `Build optimization ${colorize('enabled', ANSIColors.GREEN)}`,\n colorize(`(import mode:`, ANSIColors.GREY_DARK),\n colorize(importMode ?? IMPORT_MODE, ANSIColors.BLUE),\n colorize(`)`, ANSIColors.GREY_DARK),\n ]);\n\n // The purge / minify pipeline stands down — with its own explanation —\n // when compat-adapter callers hide call sites from the usage analyser,\n // or when `@intlayer/babel` (which runs it) is not resolvable. The\n // editor only stands the field-rename step down, not the pipeline.\n const isPurgePipelineEnabled =\n !swcExtraCallers?.length &&\n getIsPurgePipelineAvailable(intlayerConfig);\n\n // The purge pipeline reports the field-rename step standing down when\n // the editor is enabled, so this line stays a plain one.\n if (isPurgePipelineEnabled && minify) {\n logger(\n `Dictionary minification ${colorize('enabled', ANSIColors.GREEN)}`\n );\n }\n\n if (isPurgePipelineEnabled && purge) {\n logger(\n `Dictionary purge unused keys ${colorize('enabled', ANSIColors.GREEN)}`\n );\n }\n } else {\n logger([\n colorize('Recommended: Install', ANSIColors.GREY),\n colorize('@intlayer/swc', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to enable build optimization. See documentation:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://intlayer.org/docs/bundle-optimization',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n }\n },\n {\n cacheTimeoutMs: 1000 * 30, // 30 seconds\n }\n );\n\n runOnce(\n join(\n baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-compiler-plugin-enabled.lock'\n ),\n () => {\n const isBabelExtractPluginAvailable =\n getIsBabelExtractPluginAvailable(intlayerConfig);\n\n if (isBabelExtractPluginAvailable) {\n let isEnabled = intlayerConfig.compiler?.enabled ?? true;\n\n if (isEnabled === 'build-only') {\n isEnabled = !isDevCommand;\n }\n\n // Only the enabled state is reported: disabling the compiler is an\n // explicit configuration choice, so announcing it on every build adds\n // noise without telling the user anything they did not ask for.\n if (isEnabled) {\n logger('Intlayer compiler enabled');\n }\n }\n },\n {\n cacheTimeoutMs: 1000 * 30, // 30 seconds\n }\n );\n\n // Registering the plugin on a host that cannot load it is not a degraded\n // build but a failed one: SWC aborts with `failed to invoke plugin`. Standing\n // down leaves the app running off the runtime dictionary registry, which the\n // transform would otherwise have emptied.\n if (!isSwcPluginAvailable || !isSwcPluginSupported) {\n return {};\n }\n\n const dictionariesEntryPath = join(mainDir, 'dictionaries.mjs');\n\n const dynamicDictionariesEntryPath = join(\n mainDir,\n 'dynamic_dictionaries.mjs'\n );\n\n const unmergedDictionariesEntryPath = join(\n mainDir,\n 'unmerged_dictionaries.mjs'\n );\n\n const fetchDictionariesEntryPath = join(mainDir, 'fetch_dictionaries.mjs');\n\n const filesListPattern = buildComponentFilesList(intlayerConfig);\n\n const filesList = [\n ...filesListPattern,\n dictionariesEntryPath, // should add dictionariesEntryPath to replace it by a empty object if import made dynamic\n unmergedDictionariesEntryPath, // should add dictionariesEntryPath to replace it by a empty object if import made dynamic\n ];\n\n const dictionaries = getDictionaries(intlayerConfig);\n\n const dictionaryModeMap: Record<string, 'static' | 'dynamic' | 'fetch'> = {};\n\n (Object.values(dictionaries) as Dictionary[]).forEach((dictionary) => {\n dictionaryModeMap[dictionary.key] =\n dictionary.importMode ?? importMode ?? IMPORT_MODE;\n });\n\n // Dictionaries holding `nest()` references: their static import is re-pointed\n // at the companion module carrying the nest targets.\n const nestingDictionaryKeys = [\n ...getNestedDictionaryGraph(Object.values(dictionaries)).keys(),\n ];\n\n return {\n experimental: {\n swcPlugins: [\n [\n resolvePluginPath(\n '@intlayer/swc',\n intlayerConfig,\n isTurbopackEnabled\n ),\n {\n dictionariesDir,\n dictionariesEntryPath,\n unmergedDictionariesEntryPath,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n dynamicDictionariesEntryPath,\n fetchDictionariesDir,\n fetchDictionariesEntryPath,\n importMode,\n filesList,\n replaceDictionaryEntry: true,\n nestingDictionaryKeys,\n dictionaryModeMap,\n extraCallers: swcExtraCallers ?? [],\n // Rewrites `content.title` → `content.a` to match the compiled\n // dictionaries the `build.minify` pass already renamed.\n fieldRenameMap: fieldRenameMap ?? {},\n logLevel: resolveSwcLogLevel(intlayerConfig),\n },\n ],\n ],\n },\n };\n};\n\nconst getCommandsEvent = () => {\n const lifecycleEvent = process.env['npm_lifecycle_event'];\n const lifecycleScript = process.env['npm_lifecycle_script'] ?? '';\n\n const isDevCommand =\n lifecycleEvent === 'dev' ||\n process.argv.some((arg) => arg === 'dev') ||\n /(^|\\s)(next\\s+)?dev(\\s|$)/.test(lifecycleScript);\n\n const isBuildCommand =\n lifecycleEvent === 'build' ||\n process.argv.some((arg) => arg === 'build') ||\n /(^|\\s)(next\\s+)?build(\\s|$)/.test(lifecycleScript);\n\n const isStartCommand =\n lifecycleEvent === 'start' ||\n process.argv.some((arg) => arg === 'start') ||\n /(^|\\s)(next\\s+)?start(\\s|$)/.test(lifecycleScript);\n\n return {\n isDevCommand,\n isBuildCommand,\n isStartCommand,\n };\n};\n\ntype WebpackParams = Parameters<NextJsWebpackConfig>;\n\ntype WithIntlayerOptions = GetConfigurationOptions & {\n enableTurbopack?: boolean;\n swcExtraCallers?: SwcExtraCallerConfig[];\n\n /**\n * Field-rename tables produced by the purge / minify pipeline, forwarded to\n * the `@intlayer/swc` plugin so it rewrites source accesses to match the\n * renamed dictionaries.\n *\n * @internal Set by {@link withIntlayer}; `withIntlayerSync` on its own runs\n * no dictionary rewrite, so it leaves this unset and no rename is applied.\n */\n fieldRenameMap?: FieldRenameMapByDictionaryKey;\n};\n\n/**\n * Pin the env file used to resolve the Intlayer configuration to the Next.js\n * command being run (`dev` → `development`, `build`/`start` → `production`).\n *\n * Next.js loads the config file in several processes during a single command\n * (the main `build` process plus one or more Turbopack/webpack workers), and\n * `process.env.NODE_ENV` is not guaranteed to hold the same value in all of\n * them. Since `getConfiguration` falls back to `NODE_ENV` to pick its env file\n * (`.env.development.local` vs `.env.production.local`), those processes could\n * otherwise resolve *different* env values (e.g. `applicationURL`,\n * `INTLAYER_CLIENT_ID`). The resulting configuration would differ between\n * processes, defeating the `isCachedConfigurationUpToDate` check and forcing a\n * redundant full dictionary rebuild.\n *\n * Passing an explicit `env` keeps configuration resolution deterministic across\n * every process of the command. An `env` already set by the caller is\n * respected, and when the command cannot be determined we fall back to\n * `getConfiguration`'s own default (i.e. leave `env` unset).\n */\nconst resolveConfigOptions = (\n configOptions?: WithIntlayerOptions\n): WithIntlayerOptions | undefined => {\n // Respect an explicit override from the caller (idempotent on re-entry).\n if (configOptions?.env) return configOptions;\n\n const { isDevCommand, isBuildCommand, isStartCommand } = getCommandsEvent();\n\n const env = isDevCommand\n ? 'development'\n : isBuildCommand || isStartCommand\n ? 'production'\n : undefined;\n\n if (!env) return configOptions;\n\n return {\n ...configOptions,\n env,\n };\n};\n\n/**\n * A Next.js plugin that adds the intlayer configuration to the webpack configuration\n * and sets the environment variables\n *\n * Usage:\n *\n * ```ts\n * // next.config.js\n * export default withIntlayerSync(nextConfig)\n * ```\n */\nexport const withIntlayerSync = <T extends Partial<NextConfig>>(\n nextConfig: T = {} as T,\n configOptions?: WithIntlayerOptions\n): NextConfig & T => {\n if (typeof nextConfig !== 'object') {\n nextConfig = {} as T;\n }\n\n const resolvedConfigOptions = resolveConfigOptions(configOptions);\n\n const intlayerConfig = getConfiguration(resolvedConfigOptions);\n\n logConfigDetails(resolvedConfigOptions);\n\n const appLogger = getAppLogger(intlayerConfig);\n\n const {\n nextVersion,\n isGteNext13,\n isGteNext15,\n isGteNext16,\n isTurbopackStable,\n isSwcPluginSupported,\n } = getNextVersionFlags(intlayerConfig);\n\n const isTurbopackEnabledFromCommand = isGteNext16\n ? // Next@16 enables turbopack by default; disable with --webpack\n !process.env['npm_lifecycle_script']?.includes('--webpack')\n : // Next@15 uses --turbopack, Next@14 uses --turbo\n process.env['npm_lifecycle_script']?.includes('--turbo');\n\n const isTurbopackEnabled =\n configOptions?.enableTurbopack ?? isTurbopackEnabledFromCommand;\n\n if (isTurbopackEnabled && typeof nextConfig.webpack !== 'undefined') {\n appLogger(\n 'Turbopack is enabled but a custom webpack config is present. It will be ignored.'\n );\n }\n\n const { isBuildCommand, isDevCommand } = getCommandsEvent();\n\n // Only provide turbo-specific config if user explicitly sets it\n const turboConfig = {\n resolveAlias: getAlias({\n configuration: intlayerConfig,\n formatter: (value: string) => `./${value}`, // prefix by './' to consider the path as relative to the project root. This is necessary for turbopack to work correctly.\n }),\n\n rules: {\n '*.node': {\n as: '*.node',\n loaders: ['node-loader'],\n },\n },\n };\n\n const serverExternalPackages = [\n 'esbuild',\n 'module',\n 'fs',\n 'chokidar',\n 'fsevents',\n 'recast',\n '@intlayer/engine',\n '@intlayer/webpack',\n ];\n\n let env: Record<string, string> = {};\n\n if (isBuildCommand) {\n const dictionaries = getDictionaries(intlayerConfig);\n\n if (Object.keys(dictionaries).length === 0) {\n appLogger('No dictionaries found. Please check your configuration.', {\n isVerbose: true,\n });\n }\n\n const unusedNodeTypes = getUnusedNodeTypes(dictionaries);\n\n if (unusedNodeTypes && unusedNodeTypes.length > 0) {\n appLogger(\n [\n 'Filtering out unused logic:',\n unusedNodeTypes\n .filter(\n (key) => !['reactNode', 'solidNode', 'preactNode'].includes(key)\n )\n .map((key) => colorize(key, ANSIColors.BLUE))\n .join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n }\n\n env = {\n ...env,\n\n // Tree shacking based on unused node types\n ...formatNodeTypeToEnvVar(unusedNodeTypes),\n\n // Selects the local `nest()` resolver, which reads the nest targets\n // attached to each dictionary by the optimize transform instead of the\n // registry that transform empties.\n ...formatOptimizedNestingEnvVar(intlayerConfig.build.optimize !== false),\n\n // Tree shacking the dictionary selector logic\n // (collections / variants)\n ...formatDictionarySelectorEnvVar(getHasDictionarySelector(dictionaries)),\n\n // Tree shacking based on config\n ...getConfigEnvVars(intlayerConfig),\n };\n }\n\n const getNewConfig = (): Partial<NextConfig> => {\n let config: Partial<NextConfig> = {\n env,\n };\n\n if (isGteNext15) {\n config = {\n ...config,\n serverExternalPackages,\n };\n }\n\n if (isGteNext13 && !isGteNext15) {\n config = {\n ...config,\n experimental: {\n ...(config?.experimental ?? {}),\n serverComponentsExternalPackages: serverExternalPackages,\n },\n };\n }\n\n if (isTurbopackEnabled) {\n if (isGteNext15 && isTurbopackStable) {\n config = {\n ...config,\n turbopack: turboConfig,\n };\n } else {\n config = {\n ...config,\n experimental: {\n ...(config?.experimental ?? {}),\n // @ts-ignore exist in next@14\n turbo: turboConfig,\n },\n };\n }\n } else {\n config = {\n ...config,\n webpack: (config: WebpackParams['0'], options: WebpackParams[1]) => {\n // Only add Intlayer plugin on server side (node runtime)\n const { isServer, nextRuntime } = options;\n\n // If the user has defined their own webpack config, call it\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, options);\n }\n\n // Rspack set external as false by default\n // Overwrite it to allow pushing the desired externals\n if (config.externals === false) {\n config.externals = [];\n }\n\n // Mark server-only modules as externals (function form handles subpaths)\n const externalExact = new Set([\n 'esbuild',\n 'module',\n 'fs',\n 'chokidar',\n 'fsevents',\n 'recast',\n ]);\n const externalPrefixes = ['@intlayer/engine', '@intlayer/webpack'];\n config.externals.push(\n (\n { request }: { request?: string },\n callback: (err: Error | null, result?: string) => void\n ) => {\n if (\n request &&\n (externalExact.has(request) ||\n externalPrefixes.some(\n (prefix) =>\n request === prefix || request.startsWith(`${prefix}/`)\n ))\n ) {\n return callback(null, `commonjs ${request}`);\n }\n callback(null);\n }\n );\n\n // Use `node-loader` for any `.node` files\n config.module.rules.push({\n test: /\\.node$/,\n loader: 'node-loader',\n });\n\n // Always alias on the server (node/edge) for stability.\n // On the client, alias only when not using live sync.\n config.resolve.alias = {\n ...config.resolve.alias,\n ...getAlias({\n configuration: intlayerConfig,\n formatter: (value: string) => resolve(value), // get absolute path\n }),\n };\n\n // Activate watch mode webpack plugin\n if (isDevCommand && isServer && nextRuntime === 'nodejs') {\n // Optional as rspack not support plugin yet\n config.plugins.push(new IntlayerPlugin(intlayerConfig));\n }\n\n return config;\n },\n };\n }\n\n return config;\n };\n\n const pruneConfig: Partial<NextConfig> = getPruneConfig({\n intlayerConfig,\n isBuildCommand,\n isTurbopackEnabled: isTurbopackEnabled ?? false,\n isDevCommand,\n isGteNext13,\n isSwcPluginSupported,\n nextVersion,\n swcExtraCallers: configOptions?.swcExtraCallers,\n fieldRenameMap: configOptions?.fieldRenameMap,\n });\n\n const intlayerNextConfig: Partial<NextConfig> = defu(\n getNewConfig(),\n pruneConfig\n );\n\n // Merge the new config with the user's config\n const result = defu(intlayerNextConfig, nextConfig) as NextConfig & T;\n\n return result;\n};\n\n/**\n * A Next.js plugin that adds the intlayer configuration to the webpack configuration\n * and sets the environment variables\n *\n * Usage:\n *\n * ```ts\n * // next.config.js\n * export default withIntlayer(nextConfig)\n * ```\n *\n * > Node withIntlayer is a promise function. Use withIntlayerSync instead if you want to use it synchronously.\n * > Using the promise allows to prepare the intlayer dictionaries before the build starts.\n *\n */\nexport const withIntlayer = async <T extends NextConfig | Partial<NextConfig>>(\n nextConfig: T | Promise<T> = {} as T,\n configOptions?: WithIntlayerOptions\n): Promise<NextConfig & T> => {\n const { isBuildCommand, isDevCommand, isStartCommand } = getCommandsEvent();\n\n process.env.INTLAYER_IS_DEV_COMMAND = isDevCommand ? 'true' : 'false';\n\n const resolvedConfigOptions = resolveConfigOptions(configOptions);\n\n const intlayerConfig = getConfiguration(resolvedConfigOptions);\n\n const { mode } = intlayerConfig.build;\n\n // Only call prepareIntlayer during `dev` or `build` (not during `start`)\n // If prod: clean and rebuild once\n // If dev: rebuild only once if it's more than 1 hour since last rebuild\n if (!isStartCommand && (isDevCommand || isBuildCommand || mode === 'auto')) {\n // prepareIntlayer use runOnce to ensure to run only once because will run twice on client and server side otherwise\n await prepareIntlayer(intlayerConfig, {\n clean: isBuildCommand,\n cacheTimeoutMs: isBuildCommand\n ? 1000 * 30 // 30 seconds for build (to ensure to rebuild all dictionaries)\n : 1000 * 60 * 60, // 1 hour for dev (default cache timeout)\n env: isBuildCommand ? 'prod' : 'dev',\n });\n }\n\n // Remove unused content fields and rename the remaining ones to short\n // aliases, then hand the rename tables to `withIntlayerSync` so the SWC\n // plugin rewrites the matching source accesses. Only meaningful for a\n // production build — the optimize pipeline is off during `next dev`.\n //\n // `isSwcPluginUsable` gates the dictionary rewrite on the plugin actually\n // running: it is the only half that fixes up the source, so minifying without\n // it would leave the code reading field names the dictionaries no longer have.\n const { isSwcPluginSupported } = getNextVersionFlags(intlayerConfig);\n\n const fieldRenameMap = isBuildCommand\n ? await prepareSwcOptimization(intlayerConfig, {\n configOptions: resolvedConfigOptions,\n swcExtraCallers: configOptions?.swcExtraCallers,\n isSwcPluginUsable:\n isSwcPluginSupported && getIsSwcPluginAvailable(intlayerConfig),\n })\n : {};\n\n const nextConfigResolved = await nextConfig;\n\n return withIntlayerSync(nextConfigResolved, {\n ...resolvedConfigOptions,\n fieldRenameMap,\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"withIntlayer.cjs","names":["nextPackageJSON","getProjectRequire","compareVersions","getIsSwcPluginSupported","normalizePath","relative","COMPILER_ENABLED","getAppLogger","join","colorize","ANSIColors","MINIMUM_SWC_PLUGIN_NEXT_VERSION","IMPORT_MODE","getIsPurgePipelineAvailable","buildComponentFilesList","getDictionaries","getNestedDictionaryGraph","toSwcExtraCallers","resolveSwcLogLevel","getConfiguration","getAlias","getUnusedNodeTypes","formatNodeTypeToEnvVar","formatOptimizedNestingEnvVar","formatDictionarySelectorEnvVar","getHasDictionarySelector","getConfigEnvVars","resolve","IntlayerPlugin","prepareIntlayer","prepareSwcOptimization"],"sources":["../../../src/server/withIntlayer.ts"],"sourcesContent":["import { join, relative, resolve } from 'node:path';\nimport {\n type CallerDescriptor,\n toSwcExtraCallers,\n} from '@intlayer/config/callers';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { COMPILER_ENABLED, IMPORT_MODE } from '@intlayer/config/defaultValues';\nimport {\n formatDictionarySelectorEnvVar,\n formatNodeTypeToEnvVar,\n formatOptimizedNestingEnvVar,\n getConfigEnvVars,\n} from '@intlayer/config/envVars';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport {\n compareVersions,\n getAlias,\n getHasDictionarySelector,\n getProjectRequire,\n getUnusedNodeTypes,\n normalizePath,\n} from '@intlayer/config/utils';\nimport { getNestedDictionaryGraph } from '@intlayer/core/dictionaryManipulator';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { prepareIntlayer } from '@intlayer/engine/build';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { buildComponentFilesList, runOnce } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { IntlayerPlugin } from '@intlayer/webpack';\nimport { defu } from 'defu';\nimport type { NextConfig } from 'next';\nimport type { NextJsWebpackConfig } from 'next/dist/server/config-shared';\nimport nextPackageJSON from 'next/package.json' with { type: 'json' };\nimport {\n type FieldRenameMapByDictionaryKey,\n getIsPurgePipelineAvailable,\n prepareSwcOptimization,\n resolveSwcLogLevel,\n} from './prepareSwcOptimization';\nimport { startContentWatcher } from './startContentWatcher';\nimport {\n getIsSwcPluginSupported,\n MINIMUM_SWC_PLUGIN_NEXT_VERSION,\n} from './swcPluginCompatibility';\n\nexport {\n getIsSwcPluginSupported,\n MINIMUM_SWC_PLUGIN_NEXT_VERSION,\n} from './swcPluginCompatibility';\n\n/**\n * Resolve the Next.js version from the *user's* project at runtime.\n * A static `import from 'next/package.json'` would resolve relative to\n * next-intlayer's own node_modules, which may differ in a monorepo.\n */\nconst getNextVersionFlags = (intlayerConfig: IntlayerConfig) => {\n let nextVersion = nextPackageJSON.version;\n\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n const pkg = requireFunction('next/package.json') as { version: string };\n nextVersion = pkg.version;\n } catch {\n // keep default\n }\n\n return {\n nextVersion,\n isGteNext13: compareVersions(nextVersion, '≥', '13.0.0'),\n isGteNext15: compareVersions(nextVersion, '≥', '15.0.0'),\n isGteNext16: compareVersions(nextVersion, '≥', '16.0.0'),\n isTurbopackStable: compareVersions(nextVersion, '≥', '15.3.0'),\n /**\n * Whether this Next.js release can load the `@intlayer/swc` Wasm plugin.\n * See {@link MINIMUM_SWC_PLUGIN_NEXT_VERSION}.\n */\n isSwcPluginSupported: getIsSwcPluginSupported(nextVersion),\n };\n};\n\n// Check if SWC plugin is available\nconst getIsSwcPluginAvailable = (intlayerConfig: IntlayerConfig) => {\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n requireFunction.resolve('@intlayer/swc');\n return true;\n } catch (_e) {\n return false;\n }\n};\n\n// Check if Babel plugin is available\nconst getIsBabelExtractPluginAvailable = (intlayerConfig: IntlayerConfig) => {\n try {\n const requireFunction =\n intlayerConfig.build?.require ?? getProjectRequire();\n requireFunction.resolve('@intlayer/babel');\n return true;\n } catch (_e) {\n return false;\n }\n};\n\nconst resolvePluginPath = (\n pluginPath: string,\n intlayerConfig: IntlayerConfig,\n isTurbopackEnabled: boolean\n): string => {\n const requireFunction = intlayerConfig.build?.require ?? getProjectRequire();\n const pluginPathResolved = requireFunction?.resolve(pluginPath);\n\n if (isTurbopackEnabled)\n // Relative path for turbopack\n return normalizePath(`./${relative(process.cwd(), pluginPathResolved)}`);\n\n // Absolute path for webpack\n return pluginPathResolved;\n};\n\n/**\n * Extensions the extraction loader is registered for.\n *\n * The compiler reads its content out of JSX — element text and the\n * translatable attributes — so a file with no JSX in it has nothing to give,\n * and `*.ts` is left out. `*.js` is in because a plain-JavaScript Next.js app\n * writes its components there.\n */\nconst EXTRACTOR_LOADER_EXTENSIONS = ['tsx', 'jsx', 'js'] as const;\n\n/**\n * The subset safe to register as a Turbopack rule on any Next.js version.\n *\n * Turbopack rules match every module it processes, dependencies included, and\n * only Next.js 16 can express \"skip `node_modules`\" (see\n * {@link TURBOPACK_PROJECT_FILES_CONDITION}). Below that, `*.js` would hand\n * every dependency file to a Node loader worker, so it is left out rather than\n * made to cost a cold dev start.\n */\nconst TURBOPACK_ALWAYS_SAFE_EXTENSIONS = ['tsx', 'jsx'] as const;\n\n/**\n * Restricts a Turbopack rule to the project's own files. `foreign` is\n * Turbopack's builtin condition for a module inside `node_modules`.\n *\n * Only understood from Next.js 16 on — earlier releases carry no `condition`\n * field on a rule at all.\n */\nconst TURBOPACK_PROJECT_FILES_CONDITION = { not: 'foreign' } as const;\n\n/** Mode reported to the extraction loader, which cannot read the command. */\ntype ExtractorLoaderMode = 'dev' | 'build';\n\n/**\n * Whether the Intlayer compiler should extract content for this command.\n *\n * Mirrors `getExtractPluginOptions`, which the loader re-evaluates on its side:\n * this is only about not registering a loader that would do nothing.\n */\nconst getIsCompilerEnabled = (\n intlayerConfig: IntlayerConfig,\n isDevCommand: boolean\n): boolean => {\n const enabled = intlayerConfig.compiler?.enabled ?? COMPILER_ENABLED;\n\n if (enabled === 'build-only') return !isDevCommand;\n\n return enabled !== false;\n};\n\n/**\n * Describes the extraction loader for both bundlers, or `null` when the\n * compiler is off or `@intlayer/babel` is not installed.\n *\n * The loader replaces the `babel.config.js` the compiler used to need on\n * Next.js: Turbopack never reads a Babel config, and on webpack the mere\n * presence of one opts the whole project out of SWC — including the\n * `@intlayer/swc` optimize pass.\n */\nconst getExtractorLoader = (\n intlayerConfig: IntlayerConfig,\n isTurbopackEnabled: boolean,\n isDevCommand: boolean\n): { loaderPath: string; mode: ExtractorLoaderMode } | null => {\n if (!getIsCompilerEnabled(intlayerConfig, isDevCommand)) return null;\n if (!getIsBabelExtractPluginAvailable(intlayerConfig)) return null;\n\n try {\n return {\n loaderPath: resolvePluginPath(\n 'next-intlayer/extractor-loader',\n intlayerConfig,\n isTurbopackEnabled\n ),\n mode: isDevCommand ? 'dev' : 'build',\n };\n } catch {\n // An installed `next-intlayer` that cannot resolve its own subpath means a\n // version predating the loader — the Babel setup still applies there.\n return null;\n }\n};\n\ntype GetPruneConfigParams = {\n intlayerConfig: IntlayerConfig;\n isBuildCommand: boolean;\n isTurbopackEnabled: boolean;\n isDevCommand: boolean;\n isGteNext13: boolean;\n /** Whether the resolved Next.js version can load the Wasm plugin. */\n isSwcPluginSupported: boolean;\n /** Resolved Next.js version, reported when the plugin has to stand down. */\n nextVersion: string;\n compatCallers?: CallerDescriptor[];\n fieldRenameMap?: FieldRenameMapByDictionaryKey;\n};\n\nconst getPruneConfig = ({\n intlayerConfig,\n isBuildCommand,\n isTurbopackEnabled,\n isDevCommand,\n isGteNext13,\n isSwcPluginSupported,\n nextVersion,\n compatCallers,\n fieldRenameMap,\n}: GetPruneConfigParams): Partial<NextConfig> => {\n const { optimize, minify, purge } = intlayerConfig.build;\n const importMode =\n intlayerConfig.build.importMode ?? intlayerConfig.dictionary?.importMode;\n const {\n dictionariesDir,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n mainDir,\n } = intlayerConfig.system;\n const { baseDir } = intlayerConfig.system;\n const logger = getAppLogger(intlayerConfig);\n\n if (optimize === false) {\n return {};\n }\n if (optimize === undefined && !isBuildCommand) {\n return {};\n }\n\n if (!isGteNext13) return {};\n\n const isSwcPluginAvailable = getIsSwcPluginAvailable(intlayerConfig);\n\n runOnce(\n join(baseDir, '.intlayer', 'cache', 'intlayer-prune-plugin-enabled.lock'),\n () => {\n if (isSwcPluginAvailable && !isSwcPluginSupported) {\n logger(\n [\n `Build optimization ${colorize('disabled', ANSIColors.GREY_DARK)}:`,\n colorize('@intlayer/swc', ANSIColors.GREY_LIGHT),\n colorize(\n `cannot run on Next.js ${nextVersion} — its bundled SWC predates the`,\n ANSIColors.GREY\n ),\n colorize(\n 'stable Wasm plugin ABI. Upgrade to Next.js',\n ANSIColors.GREY\n ),\n colorize(MINIMUM_SWC_PLUGIN_NEXT_VERSION, ANSIColors.BLUE),\n colorize('or later to enable it.', ANSIColors.GREY),\n ],\n { level: 'warn' }\n );\n } else if (isSwcPluginAvailable) {\n logger([\n `Build optimization ${colorize('enabled', ANSIColors.GREEN)}`,\n colorize(`(import mode:`, ANSIColors.GREY_DARK),\n colorize(importMode ?? IMPORT_MODE, ANSIColors.BLUE),\n colorize(`)`, ANSIColors.GREY_DARK),\n ]);\n\n // The purge / minify pipeline stands down — with its own explanation —\n // when `@intlayer/babel` (which runs it) is not resolvable. The editor\n // only stands the field-rename step down, not the pipeline.\n const isPurgePipelineEnabled =\n getIsPurgePipelineAvailable(intlayerConfig);\n\n // The purge pipeline reports the field-rename step standing down when\n // the editor is enabled, so this line stays a plain one.\n if (isPurgePipelineEnabled && minify) {\n logger(\n `Dictionary minification ${colorize('enabled', ANSIColors.GREEN)}`\n );\n }\n\n if (isPurgePipelineEnabled && purge) {\n logger(\n `Dictionary purge unused keys ${colorize('enabled', ANSIColors.GREEN)}`\n );\n }\n } else {\n logger([\n colorize('Recommended: Install', ANSIColors.GREY),\n colorize('@intlayer/swc', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to enable build optimization. See documentation:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://intlayer.org/docs/bundle-optimization',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n }\n },\n {\n cacheTimeoutMs: 1000 * 30, // 30 seconds\n }\n );\n\n runOnce(\n join(\n baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-compiler-plugin-enabled.lock'\n ),\n () => {\n const isBabelExtractPluginAvailable =\n getIsBabelExtractPluginAvailable(intlayerConfig);\n\n if (isBabelExtractPluginAvailable) {\n let isEnabled = intlayerConfig.compiler?.enabled ?? true;\n\n if (isEnabled === 'build-only') {\n isEnabled = !isDevCommand;\n }\n\n // Only the enabled state is reported: disabling the compiler is an\n // explicit configuration choice, so announcing it on every build adds\n // noise without telling the user anything they did not ask for.\n if (isEnabled) {\n logger('Intlayer compiler enabled');\n }\n }\n },\n {\n cacheTimeoutMs: 1000 * 30, // 30 seconds\n }\n );\n\n // Registering the plugin on a host that cannot load it is not a degraded\n // build but a failed one: SWC aborts with `failed to invoke plugin`. Standing\n // down leaves the app running off the runtime dictionary registry, which the\n // transform would otherwise have emptied.\n if (!isSwcPluginAvailable || !isSwcPluginSupported) {\n return {};\n }\n\n const dictionariesEntryPath = join(mainDir, 'dictionaries.mjs');\n\n const dynamicDictionariesEntryPath = join(\n mainDir,\n 'dynamic_dictionaries.mjs'\n );\n\n const unmergedDictionariesEntryPath = join(\n mainDir,\n 'unmerged_dictionaries.mjs'\n );\n\n const fetchDictionariesEntryPath = join(mainDir, 'fetch_dictionaries.mjs');\n\n const filesListPattern = buildComponentFilesList(intlayerConfig);\n\n const filesList = [\n ...filesListPattern,\n dictionariesEntryPath, // should add dictionariesEntryPath to replace it by a empty object if import made dynamic\n unmergedDictionariesEntryPath, // should add dictionariesEntryPath to replace it by a empty object if import made dynamic\n ];\n\n const dictionaries = getDictionaries(intlayerConfig);\n\n const dictionaryModeMap: Record<string, 'static' | 'dynamic' | 'fetch'> = {};\n\n (Object.values(dictionaries) as Dictionary[]).forEach((dictionary) => {\n dictionaryModeMap[dictionary.key] =\n dictionary.importMode ?? importMode ?? IMPORT_MODE;\n });\n\n // Dictionaries holding `nest()` references: their static import is re-pointed\n // at the companion module carrying the nest targets.\n const nestingDictionaryKeys = [\n ...getNestedDictionaryGraph(Object.values(dictionaries)).keys(),\n ];\n\n return {\n experimental: {\n swcPlugins: [\n [\n resolvePluginPath(\n '@intlayer/swc',\n intlayerConfig,\n isTurbopackEnabled\n ),\n {\n dictionariesDir,\n dictionariesEntryPath,\n unmergedDictionariesEntryPath,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n dynamicDictionariesEntryPath,\n fetchDictionariesDir,\n fetchDictionariesEntryPath,\n importMode,\n filesList,\n replaceDictionaryEntry: true,\n nestingDictionaryKeys,\n dictionaryModeMap,\n // Compat adapters reach the Wasm plugin as descriptors serialised\n // to its wire format; with none injected the option is empty and\n // the plugin runs the base intlayer rewrite alone.\n extraCallers: toSwcExtraCallers(compatCallers ?? []),\n // Rewrites `content.title` → `content.a` to match the compiled\n // dictionaries the `build.minify` pass already renamed.\n fieldRenameMap: fieldRenameMap ?? {},\n logLevel: resolveSwcLogLevel(intlayerConfig),\n },\n ],\n ],\n },\n };\n};\n\nconst getCommandsEvent = () => {\n const lifecycleEvent = process.env['npm_lifecycle_event'];\n const lifecycleScript = process.env['npm_lifecycle_script'] ?? '';\n\n const isDevCommand =\n lifecycleEvent === 'dev' ||\n process.argv.some((arg) => arg === 'dev') ||\n /(^|\\s)(next\\s+)?dev(\\s|$)/.test(lifecycleScript);\n\n const isBuildCommand =\n lifecycleEvent === 'build' ||\n process.argv.some((arg) => arg === 'build') ||\n /(^|\\s)(next\\s+)?build(\\s|$)/.test(lifecycleScript);\n\n const isStartCommand =\n lifecycleEvent === 'start' ||\n process.argv.some((arg) => arg === 'start') ||\n /(^|\\s)(next\\s+)?start(\\s|$)/.test(lifecycleScript);\n\n return {\n isDevCommand,\n isBuildCommand,\n isStartCommand,\n };\n};\n\ntype WebpackParams = Parameters<NextJsWebpackConfig>;\n\ntype WithIntlayerOptions = GetConfigurationOptions & {\n enableTurbopack?: boolean;\n /**\n * Compat-adapter caller descriptors injected by a compat package's bundler\n * plugin (e.g. `@intlayer/next-intl/plugin`). They drive both the SWC\n * optimize rewrite and the Node-side usage analysis, so one registry slice\n * is all a compat plugin has to pass.\n */\n compatCallers?: CallerDescriptor[];\n\n /**\n * Field-rename tables produced by the purge / minify pipeline, forwarded to\n * the `@intlayer/swc` plugin so it rewrites source accesses to match the\n * renamed dictionaries.\n *\n * @internal Set by {@link withIntlayer}; `withIntlayerSync` on its own runs\n * no dictionary rewrite, so it leaves this unset and no rename is applied.\n */\n fieldRenameMap?: FieldRenameMapByDictionaryKey;\n};\n\n/**\n * Pin the env file used to resolve the Intlayer configuration to the Next.js\n * command being run (`dev` → `development`, `build`/`start` → `production`).\n *\n * Next.js loads the config file in several processes during a single command\n * (the main `build` process plus one or more Turbopack/webpack workers), and\n * `process.env.NODE_ENV` is not guaranteed to hold the same value in all of\n * them. Since `getConfiguration` falls back to `NODE_ENV` to pick its env file\n * (`.env.development.local` vs `.env.production.local`), those processes could\n * otherwise resolve *different* env values (e.g. `applicationURL`,\n * `INTLAYER_CLIENT_ID`). The resulting configuration would differ between\n * processes, defeating the `isCachedConfigurationUpToDate` check and forcing a\n * redundant full dictionary rebuild.\n *\n * Passing an explicit `env` keeps configuration resolution deterministic across\n * every process of the command. An `env` already set by the caller is\n * respected, and when the command cannot be determined we fall back to\n * `getConfiguration`'s own default (i.e. leave `env` unset).\n */\nconst resolveConfigOptions = (\n configOptions?: WithIntlayerOptions\n): WithIntlayerOptions | undefined => {\n // Respect an explicit override from the caller (idempotent on re-entry).\n if (configOptions?.env) return configOptions;\n\n const { isDevCommand, isBuildCommand, isStartCommand } = getCommandsEvent();\n\n const env = isDevCommand\n ? 'development'\n : isBuildCommand || isStartCommand\n ? 'production'\n : undefined;\n\n if (!env) return configOptions;\n\n return {\n ...configOptions,\n env,\n };\n};\n\n/**\n * A Next.js plugin that adds the intlayer configuration to the webpack configuration\n * and sets the environment variables\n *\n * Usage:\n *\n * ```ts\n * // next.config.js\n * export default withIntlayerSync(nextConfig)\n * ```\n */\nexport const withIntlayerSync = <T extends Partial<NextConfig>>(\n nextConfig: T = {} as T,\n configOptions?: WithIntlayerOptions\n): NextConfig & T => {\n if (typeof nextConfig !== 'object') {\n nextConfig = {} as T;\n }\n\n const resolvedConfigOptions = resolveConfigOptions(configOptions);\n\n const intlayerConfig = getConfiguration(resolvedConfigOptions);\n\n logConfigDetails(resolvedConfigOptions);\n\n const appLogger = getAppLogger(intlayerConfig);\n\n const {\n nextVersion,\n isGteNext13,\n isGteNext15,\n isGteNext16,\n isTurbopackStable,\n isSwcPluginSupported,\n } = getNextVersionFlags(intlayerConfig);\n\n const isTurbopackEnabledFromCommand = isGteNext16\n ? // Next@16 enables turbopack by default; disable with --webpack\n !process.env['npm_lifecycle_script']?.includes('--webpack')\n : // Next@15 uses --turbopack, Next@14 uses --turbo\n process.env['npm_lifecycle_script']?.includes('--turbo');\n\n const isTurbopackEnabled =\n configOptions?.enableTurbopack ?? isTurbopackEnabledFromCommand;\n\n if (isTurbopackEnabled && typeof nextConfig.webpack !== 'undefined') {\n appLogger(\n 'Turbopack is enabled but a custom webpack config is present. It will be ignored.'\n );\n }\n\n const { isBuildCommand, isDevCommand } = getCommandsEvent();\n\n const extractorLoader = getExtractorLoader(\n intlayerConfig,\n isTurbopackEnabled ?? false,\n isDevCommand\n );\n\n // Turbopack cannot run `IntlayerPlugin`, which is what starts the content\n // watcher on webpack. Without this the dev server had to be wrapped in\n // `intlayer watch --with next dev` for a `.content` edit to reach `.intlayer`.\n if (isTurbopackEnabled && isDevCommand) {\n startContentWatcher(intlayerConfig);\n }\n\n // Only provide turbo-specific config if user explicitly sets it\n const turboConfig = {\n resolveAlias: getAlias({\n configuration: intlayerConfig,\n formatter: (value: string) => `./${value}`, // prefix by './' to consider the path as relative to the project root. This is necessary for turbopack to work correctly.\n }),\n\n rules: {\n '*.node': {\n as: '*.node',\n loaders: ['node-loader'],\n },\n\n // Turbopack serialises loader options to JSON, so the loader reads the\n // Intlayer configuration itself and only the command mode is passed here.\n //\n // No `as`: it renames the module, and naming the extension the rule\n // already matches turns `Component.tsx` into `Component.tsx.tsx`, which\n // then fails to resolve. Left out, Turbopack keeps handling the loader\n // output as the TSX/JSX it still is.\n ...(extractorLoader\n ? Object.fromEntries(\n (isGteNext16\n ? EXTRACTOR_LOADER_EXTENSIONS\n : TURBOPACK_ALWAYS_SAFE_EXTENSIONS\n ).map((extension) => [\n `*.${extension}`,\n {\n loaders: [\n {\n loader: extractorLoader.loaderPath,\n options: { mode: extractorLoader.mode },\n },\n ],\n ...(isGteNext16\n ? { condition: TURBOPACK_PROJECT_FILES_CONDITION }\n : {}),\n },\n ])\n )\n : {}),\n },\n };\n\n const serverExternalPackages = [\n 'esbuild',\n 'module',\n 'fs',\n 'chokidar',\n 'fsevents',\n 'recast',\n '@intlayer/engine',\n '@intlayer/webpack',\n ];\n\n let env: Record<string, string> = {};\n\n if (isBuildCommand) {\n const dictionaries = getDictionaries(intlayerConfig);\n\n if (Object.keys(dictionaries).length === 0) {\n appLogger('No dictionaries found. Please check your configuration.', {\n isVerbose: true,\n });\n }\n\n const unusedNodeTypes = getUnusedNodeTypes(dictionaries);\n\n if (unusedNodeTypes && unusedNodeTypes.length > 0) {\n appLogger(\n [\n 'Filtering out unused logic:',\n unusedNodeTypes\n .filter(\n (key) => !['reactNode', 'solidNode', 'preactNode'].includes(key)\n )\n .map((key) => colorize(key, ANSIColors.BLUE))\n .join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n }\n\n env = {\n ...env,\n\n // Tree shacking based on unused node types\n ...formatNodeTypeToEnvVar(unusedNodeTypes),\n\n // Selects the local `nest()` resolver, which reads the nest targets\n // attached to each dictionary by the optimize transform instead of the\n // registry that transform empties.\n ...formatOptimizedNestingEnvVar(intlayerConfig.build.optimize !== false),\n\n // Tree shacking the dictionary selector logic\n // (collections / variants)\n ...formatDictionarySelectorEnvVar(getHasDictionarySelector(dictionaries)),\n\n // Tree shacking based on config\n ...getConfigEnvVars(intlayerConfig),\n };\n }\n\n const getNewConfig = (): Partial<NextConfig> => {\n let config: Partial<NextConfig> = {\n env,\n };\n\n if (isGteNext15) {\n config = {\n ...config,\n serverExternalPackages,\n };\n }\n\n if (isGteNext13 && !isGteNext15) {\n config = {\n ...config,\n experimental: {\n ...(config?.experimental ?? {}),\n serverComponentsExternalPackages: serverExternalPackages,\n },\n };\n }\n\n if (isTurbopackEnabled) {\n if (isGteNext15 && isTurbopackStable) {\n config = {\n ...config,\n turbopack: turboConfig,\n };\n } else {\n config = {\n ...config,\n experimental: {\n ...(config?.experimental ?? {}),\n // @ts-ignore exist in next@14\n turbo: turboConfig,\n },\n };\n }\n } else {\n config = {\n ...config,\n webpack: (config: WebpackParams['0'], options: WebpackParams[1]) => {\n // Only add Intlayer plugin on server side (node runtime)\n const { isServer, nextRuntime } = options;\n\n // If the user has defined their own webpack config, call it\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, options);\n }\n\n // Rspack set external as false by default\n // Overwrite it to allow pushing the desired externals\n if (config.externals === false) {\n config.externals = [];\n }\n\n // Mark server-only modules as externals (function form handles subpaths)\n const externalExact = new Set([\n 'esbuild',\n 'module',\n 'fs',\n 'chokidar',\n 'fsevents',\n 'recast',\n ]);\n const externalPrefixes = ['@intlayer/engine', '@intlayer/webpack'];\n config.externals.push(\n (\n { request }: { request?: string },\n callback: (err: Error | null, result?: string) => void\n ) => {\n if (\n request &&\n (externalExact.has(request) ||\n externalPrefixes.some(\n (prefix) =>\n request === prefix || request.startsWith(`${prefix}/`)\n ))\n ) {\n return callback(null, `commonjs ${request}`);\n }\n callback(null);\n }\n );\n\n // Use `node-loader` for any `.node` files\n config.module.rules.push({\n test: /\\.node$/,\n loader: 'node-loader',\n });\n\n // `enforce: 'pre'` puts the extraction ahead of `next-swc-loader`,\n // so the loader reads the original JSX rather than its compiled\n // output. Declaring it here (instead of in a `babel.config.js`)\n // keeps Next.js on SWC, and with it the `@intlayer/swc` optimize\n // pass a Babel config would silently disable.\n if (extractorLoader) {\n config.module.rules.push({\n test: new RegExp(\n `\\\\.(${EXTRACTOR_LOADER_EXTENSIONS.join('|')})$`\n ),\n exclude: /node_modules/,\n enforce: 'pre',\n use: [\n {\n loader: extractorLoader.loaderPath,\n options: { mode: extractorLoader.mode },\n },\n ],\n });\n }\n\n // Always alias on the server (node/edge) for stability.\n // On the client, alias only when not using live sync.\n config.resolve.alias = {\n ...config.resolve.alias,\n ...getAlias({\n configuration: intlayerConfig,\n formatter: (value: string) => resolve(value), // get absolute path\n }),\n };\n\n // Activate watch mode webpack plugin\n if (isDevCommand && isServer && nextRuntime === 'nodejs') {\n // Optional as rspack not support plugin yet\n config.plugins.push(new IntlayerPlugin(intlayerConfig));\n }\n\n return config;\n },\n };\n }\n\n return config;\n };\n\n const pruneConfig: Partial<NextConfig> = getPruneConfig({\n intlayerConfig,\n isBuildCommand,\n isTurbopackEnabled: isTurbopackEnabled ?? false,\n isDevCommand,\n isGteNext13,\n isSwcPluginSupported,\n nextVersion,\n compatCallers: configOptions?.compatCallers,\n fieldRenameMap: configOptions?.fieldRenameMap,\n });\n\n const intlayerNextConfig: Partial<NextConfig> = defu(\n getNewConfig(),\n pruneConfig\n );\n\n // Merge the new config with the user's config\n const result = defu(intlayerNextConfig, nextConfig) as NextConfig & T;\n\n return result;\n};\n\n/**\n * A Next.js plugin that adds the intlayer configuration to the webpack configuration\n * and sets the environment variables\n *\n * Usage:\n *\n * ```ts\n * // next.config.js\n * export default withIntlayer(nextConfig)\n * ```\n *\n * > Node withIntlayer is a promise function. Use withIntlayerSync instead if you want to use it synchronously.\n * > Using the promise allows to prepare the intlayer dictionaries before the build starts.\n *\n */\nexport const withIntlayer = async <T extends NextConfig | Partial<NextConfig>>(\n nextConfig: T | Promise<T> = {} as T,\n configOptions?: WithIntlayerOptions\n): Promise<NextConfig & T> => {\n const { isBuildCommand, isDevCommand, isStartCommand } = getCommandsEvent();\n\n process.env.INTLAYER_IS_DEV_COMMAND = isDevCommand ? 'true' : 'false';\n\n const resolvedConfigOptions = resolveConfigOptions(configOptions);\n\n const intlayerConfig = getConfiguration(resolvedConfigOptions);\n\n const { mode } = intlayerConfig.build;\n\n // Only call prepareIntlayer during `dev` or `build` (not during `start`)\n // If prod: clean and rebuild once\n // If dev: rebuild only once if it's more than 1 hour since last rebuild\n if (!isStartCommand && (isDevCommand || isBuildCommand || mode === 'auto')) {\n // prepareIntlayer use runOnce to ensure to run only once because will run twice on client and server side otherwise\n await prepareIntlayer(intlayerConfig, {\n clean: isBuildCommand,\n cacheTimeoutMs: isBuildCommand\n ? 1000 * 30 // 30 seconds for build (to ensure to rebuild all dictionaries)\n : 1000 * 60 * 60, // 1 hour for dev (default cache timeout)\n env: isBuildCommand ? 'prod' : 'dev',\n });\n }\n\n // Remove unused content fields and rename the remaining ones to short\n // aliases, then hand the rename tables to `withIntlayerSync` so the SWC\n // plugin rewrites the matching source accesses. Only meaningful for a\n // production build — the optimize pipeline is off during `next dev`.\n //\n // `isSwcPluginUsable` gates the dictionary rewrite on the plugin actually\n // running: it is the only half that fixes up the source, so minifying without\n // it would leave the code reading field names the dictionaries no longer have.\n const { isSwcPluginSupported } = getNextVersionFlags(intlayerConfig);\n\n const fieldRenameMap = isBuildCommand\n ? await prepareSwcOptimization(intlayerConfig, {\n configOptions: resolvedConfigOptions,\n compatCallers: configOptions?.compatCallers,\n isSwcPluginUsable:\n isSwcPluginSupported && getIsSwcPluginAvailable(intlayerConfig),\n })\n : {};\n\n const nextConfigResolved = await nextConfig;\n\n return withIntlayerSync(nextConfigResolved, {\n ...resolvedConfigOptions,\n fieldRenameMap,\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import { dirname, join, relative } from "node:path";
2
+ import * as ANSIColors from "@intlayer/config/colors";
3
+ import { colorize, colorizePath, getAppLogger } from "@intlayer/config/logger";
4
+ import { getConfiguration } from "@intlayer/config/node";
5
+ import { getProjectRequire, normalizePath } from "@intlayer/config/utils";
6
+ import { withFileLock } from "@intlayer/engine/utils";
7
+
8
+ //#region src/extractorLoader.ts
9
+ const REQUIRED_BABEL_EXPORTS = [
10
+ "detectPackageName",
11
+ "extractContent",
12
+ "getExtractPluginOptions",
13
+ "writeContentHelper"
14
+ ];
15
+ /**
16
+ * Serialises the dictionary writes of every process taking part in the build.
17
+ * `writeContentHelper` rebuilds the merged `.intlayer` artifacts, which is a
18
+ * read-modify-write over every dictionary on disk — two extractions running it
19
+ * at the same time would each merge a half-written state.
20
+ */
21
+ const WRITE_LOCK_FILE_NAME = "intlayer-compiler-write.lock";
22
+ /** Set once per process: the loader runs in a long-lived bundler worker. */
23
+ let cachedConfiguration;
24
+ let cachedExtractOptions;
25
+ let cachedFilesListSet;
26
+ let cachedBabelModule;
27
+ /** Intlayer package detected per component directory. */
28
+ const packageNameByDirectory = /* @__PURE__ */ new Map();
29
+ /**
30
+ * Content already written for a given dictionary key, as a stable hash.
31
+ *
32
+ * Next.js compiles the same file once per compilation (client, server, edge),
33
+ * and Turbopack re-runs the loader whenever the file changes. Re-writing a
34
+ * dictionary whose content did not move costs a full `.intlayer` rebuild for
35
+ * nothing.
36
+ */
37
+ const writtenContentHashByDictionaryKey = /* @__PURE__ */ new Map();
38
+ /** Serialises the writes issued by this process, before the file lock. */
39
+ let pendingWrite = Promise.resolve();
40
+ /**
41
+ * Lazily loads `@intlayer/babel`, or `null` when it is not installed or too old
42
+ * to expose the extraction API.
43
+ */
44
+ const loadIntlayerBabel = (configuration) => {
45
+ if (cachedBabelModule !== void 0) return cachedBabelModule;
46
+ try {
47
+ const intlayerBabel = (configuration.build?.require ?? getProjectRequire())("@intlayer/babel");
48
+ cachedBabelModule = REQUIRED_BABEL_EXPORTS.every((exportName) => typeof intlayerBabel?.[exportName] === "function") ? intlayerBabel : null;
49
+ } catch {
50
+ cachedBabelModule = null;
51
+ }
52
+ return cachedBabelModule;
53
+ };
54
+ const getOptions = (context) => {
55
+ if (typeof context.getOptions === "function") return context.getOptions();
56
+ if (typeof context.query === "object" && context.query) return context.query;
57
+ return {};
58
+ };
59
+ const hashContent = (content) => JSON.stringify(Object.keys(content).sort().map((key) => [key, content[key]]));
60
+ /**
61
+ * Writes one extracted dictionary, skipping the write when its content is
62
+ * already on disk and holding the cross-process lock while `.intlayer` is
63
+ * rebuilt.
64
+ */
65
+ const writeDictionary = async (intlayerBabel, configuration, dictionaryKey, content, filePath) => {
66
+ const contentHash = hashContent(content);
67
+ if (writtenContentHashByDictionaryKey.get(dictionaryKey) === contentHash) return;
68
+ const lockFilePath = join(configuration.system.baseDir, ".intlayer", "cache", WRITE_LOCK_FILE_NAME);
69
+ await withFileLock(lockFilePath, () => intlayerBabel.writeContentHelper(content, dictionaryKey, filePath, configuration));
70
+ writtenContentHashByDictionaryKey.set(dictionaryKey, contentHash);
71
+ };
72
+ /**
73
+ * Extracts the translatable content of one file and returns the rewritten
74
+ * source, or the original source when there is nothing to extract.
75
+ */
76
+ const extractFile = async (filePath, source, mode) => {
77
+ cachedConfiguration ??= getConfiguration();
78
+ const configuration = cachedConfiguration;
79
+ const intlayerBabel = loadIntlayerBabel(configuration);
80
+ if (!intlayerBabel) return source;
81
+ cachedExtractOptions ??= intlayerBabel.getExtractPluginOptions(configuration, mode);
82
+ if (!cachedExtractOptions.enabled) return source;
83
+ cachedFilesListSet ??= new Set((cachedExtractOptions.filesList ?? []).map(normalizePath));
84
+ if (!cachedFilesListSet.has(normalizePath(filePath))) return source;
85
+ const appLogger = getAppLogger(configuration);
86
+ const componentDirectory = dirname(filePath);
87
+ let packageName = packageNameByDirectory.get(componentDirectory);
88
+ if (!packageName) {
89
+ packageName = intlayerBabel.detectPackageName(componentDirectory);
90
+ packageNameByDirectory.set(componentDirectory, packageName);
91
+ }
92
+ const result = await intlayerBabel.extractContent(filePath, packageName, {
93
+ configuration,
94
+ code: source,
95
+ declarationOnly: !configuration.compiler.saveComponents,
96
+ onExtract: ({ key, content }) => {
97
+ pendingWrite = pendingWrite.then(() => writeDictionary(intlayerBabel, configuration, key, content, filePath)).catch((error) => {
98
+ appLogger([`${colorize("Compiler:", ANSIColors.GREY_DARK)} Failed to write the dictionary extracted from ${colorizePath(relative(configuration.system.baseDir, filePath))}:`, error], { level: "error" });
99
+ });
100
+ return pendingWrite;
101
+ }
102
+ });
103
+ if (!result?.transformedCode) return source;
104
+ appLogger(`${colorize("Compiler:", ANSIColors.GREY_DARK)} Extracted content from ${colorizePath(relative(configuration.system.baseDir, filePath))}`, { level: "debug" });
105
+ return result.transformedCode;
106
+ };
107
+ /**
108
+ * Webpack-compatible loader running the Intlayer compiler extraction pass.
109
+ *
110
+ * It replaces the `babel.config.js` setup on Next.js: Turbopack never reads a
111
+ * Babel config, and on webpack a Babel config would opt the whole project out
112
+ * of SWC. Registered automatically by `withIntlayer` on both bundlers.
113
+ *
114
+ * @example next.config.ts — registered by hand
115
+ * ```ts
116
+ * export default {
117
+ * turbopack: {
118
+ * rules: {
119
+ * // No `as`: it renames the module, so naming the extension the rule
120
+ * // already matches would ask for `Component.tsx.tsx`.
121
+ * '*.tsx': {
122
+ * loaders: [
123
+ * {
124
+ * loader: require.resolve('next-intlayer/extractor-loader'),
125
+ * options: { mode: 'dev' },
126
+ * },
127
+ * ],
128
+ * },
129
+ * },
130
+ * },
131
+ * };
132
+ * ```
133
+ */
134
+ const intlayerExtractorLoader = function(source) {
135
+ const callback = this.async();
136
+ const { mode } = getOptions(this);
137
+ const filePath = this.resourcePath;
138
+ extractFile(filePath, source, mode ?? "build").then((code) => callback(null, code)).catch((error) => {
139
+ getAppLogger(cachedConfiguration)([`${colorize("Compiler:", ANSIColors.GREY_DARK)} Failed to extract content from ${colorizePath(filePath)}:`, error], { level: "error" });
140
+ callback(null, source);
141
+ });
142
+ };
143
+
144
+ //#endregion
145
+ export { intlayerExtractorLoader as default };
146
+ //# sourceMappingURL=extractorLoader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractorLoader.mjs","names":[],"sources":["../../src/extractorLoader.ts"],"sourcesContent":["import { dirname, join, relative } from 'node:path';\nimport type {\n detectPackageName as DetectPackageName,\n extractContent as ExtractContent,\n ExtractPluginOptions,\n getExtractPluginOptions as GetExtractPluginOptions,\n PackageName,\n writeContentHelper as WriteContentHelper,\n} from '@intlayer/babel';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { getProjectRequire, normalizePath } from '@intlayer/config/utils';\nimport { withFileLock } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * Options the loader accepts. Turbopack serialises loader options to JSON, so\n * every field has to stay a plain JSON value — the Intlayer configuration\n * itself (which carries functions, e.g. `compiler.output`) is therefore loaded\n * by the loader rather than handed to it.\n */\nexport type IntlayerExtractorLoaderOptions = {\n /**\n * Whether the host bundler is serving (`dev`) or building (`build`).\n *\n * Passed explicitly because a Turbopack loader runs in a worker process whose\n * environment does not necessarily carry `INTLAYER_IS_DEV_COMMAND`.\n */\n mode?: 'dev' | 'build';\n};\n\n/**\n * Structural view of the loader context, limited to what this loader uses.\n * Typed locally rather than imported from `webpack` because the same file is\n * also run by Turbopack's webpack-loader compatibility layer, and `webpack` is\n * only an optional peer dependency.\n */\ntype IntlayerLoaderContext = {\n /** Absolute path of the file being compiled. */\n resourcePath: string;\n /** Switches the loader to asynchronous mode. */\n async: () => (error: Error | null, content?: string) => void;\n /** webpack 5 / Turbopack accessor for the loader options. */\n getOptions?: () => IntlayerExtractorLoaderOptions;\n /** Legacy options holder, used when `getOptions` is unavailable. */\n query?: IntlayerExtractorLoaderOptions | string;\n};\n\n/**\n * Subset of `@intlayer/babel` the loader needs. The package is an optional\n * dependency, so it is required lazily and never imported at module scope.\n */\ntype IntlayerBabelModule = {\n detectPackageName: typeof DetectPackageName;\n extractContent: typeof ExtractContent;\n getExtractPluginOptions: typeof GetExtractPluginOptions;\n writeContentHelper: typeof WriteContentHelper;\n};\n\nconst REQUIRED_BABEL_EXPORTS = [\n 'detectPackageName',\n 'extractContent',\n 'getExtractPluginOptions',\n 'writeContentHelper',\n] as const satisfies readonly (keyof IntlayerBabelModule)[];\n\n/**\n * Serialises the dictionary writes of every process taking part in the build.\n * `writeContentHelper` rebuilds the merged `.intlayer` artifacts, which is a\n * read-modify-write over every dictionary on disk — two extractions running it\n * at the same time would each merge a half-written state.\n */\nconst WRITE_LOCK_FILE_NAME = 'intlayer-compiler-write.lock';\n\n/** Set once per process: the loader runs in a long-lived bundler worker. */\nlet cachedConfiguration: IntlayerConfig | undefined;\nlet cachedExtractOptions: ExtractPluginOptions | undefined;\nlet cachedFilesListSet: Set<string> | undefined;\nlet cachedBabelModule: IntlayerBabelModule | null | undefined;\n\n/** Intlayer package detected per component directory. */\nconst packageNameByDirectory = new Map<string, PackageName>();\n\n/**\n * Content already written for a given dictionary key, as a stable hash.\n *\n * Next.js compiles the same file once per compilation (client, server, edge),\n * and Turbopack re-runs the loader whenever the file changes. Re-writing a\n * dictionary whose content did not move costs a full `.intlayer` rebuild for\n * nothing.\n */\nconst writtenContentHashByDictionaryKey = new Map<string, string>();\n\n/** Serialises the writes issued by this process, before the file lock. */\nlet pendingWrite: Promise<void> = Promise.resolve();\n\n/**\n * Lazily loads `@intlayer/babel`, or `null` when it is not installed or too old\n * to expose the extraction API.\n */\nconst loadIntlayerBabel = (\n configuration: IntlayerConfig\n): IntlayerBabelModule | null => {\n if (cachedBabelModule !== undefined) return cachedBabelModule;\n\n try {\n const requireFunction = configuration.build?.require ?? getProjectRequire();\n\n const intlayerBabel = requireFunction(\n '@intlayer/babel'\n ) as Partial<IntlayerBabelModule>;\n\n const hasEveryRequiredExport = REQUIRED_BABEL_EXPORTS.every(\n (exportName) => typeof intlayerBabel?.[exportName] === 'function'\n );\n\n cachedBabelModule = hasEveryRequiredExport\n ? (intlayerBabel as IntlayerBabelModule)\n : null;\n } catch {\n cachedBabelModule = null;\n }\n\n return cachedBabelModule;\n};\n\nconst getOptions = (\n context: IntlayerLoaderContext\n): IntlayerExtractorLoaderOptions => {\n if (typeof context.getOptions === 'function') return context.getOptions();\n if (typeof context.query === 'object' && context.query) return context.query;\n\n return {};\n};\n\nconst hashContent = (content: Record<string, string>): string =>\n JSON.stringify(\n Object.keys(content)\n .sort()\n .map((key) => [key, content[key]])\n );\n\n/**\n * Writes one extracted dictionary, skipping the write when its content is\n * already on disk and holding the cross-process lock while `.intlayer` is\n * rebuilt.\n */\nconst writeDictionary = async (\n intlayerBabel: IntlayerBabelModule,\n configuration: IntlayerConfig,\n dictionaryKey: string,\n content: Record<string, string>,\n filePath: string\n): Promise<void> => {\n const contentHash = hashContent(content);\n\n if (writtenContentHashByDictionaryKey.get(dictionaryKey) === contentHash) {\n return;\n }\n\n const lockFilePath = join(\n configuration.system.baseDir,\n '.intlayer',\n 'cache',\n WRITE_LOCK_FILE_NAME\n );\n\n await withFileLock(lockFilePath, () =>\n intlayerBabel.writeContentHelper(\n content,\n dictionaryKey,\n filePath,\n configuration\n )\n );\n\n writtenContentHashByDictionaryKey.set(dictionaryKey, contentHash);\n};\n\n/**\n * Extracts the translatable content of one file and returns the rewritten\n * source, or the original source when there is nothing to extract.\n */\nconst extractFile = async (\n filePath: string,\n source: string,\n mode: 'dev' | 'build'\n): Promise<string> => {\n cachedConfiguration ??= getConfiguration();\n const configuration = cachedConfiguration;\n\n const intlayerBabel = loadIntlayerBabel(configuration);\n\n // Without `@intlayer/babel` there is no extraction to run; `withIntlayer`\n // only registers this loader when the package resolves, so reaching this\n // branch means it was removed after the config was read.\n if (!intlayerBabel) return source;\n\n cachedExtractOptions ??= intlayerBabel.getExtractPluginOptions(\n configuration,\n mode\n );\n\n if (!cachedExtractOptions.enabled) return source;\n\n cachedFilesListSet ??= new Set(\n (cachedExtractOptions.filesList ?? []).map(normalizePath)\n );\n\n if (!cachedFilesListSet.has(normalizePath(filePath))) return source;\n\n const appLogger = getAppLogger(configuration);\n const componentDirectory = dirname(filePath);\n\n let packageName = packageNameByDirectory.get(componentDirectory);\n\n if (!packageName) {\n packageName = intlayerBabel.detectPackageName(componentDirectory);\n packageNameByDirectory.set(componentDirectory, packageName);\n }\n\n const result = await intlayerBabel.extractContent(filePath, packageName, {\n configuration,\n code: source,\n // `extractContent` rewrites the component on disk unless told otherwise,\n // so the config flag has to be forwarded — the transform is meant to stay\n // in the bundle output until the user opts into `saveComponents`.\n declarationOnly: !configuration.compiler.saveComponents,\n // Supplying `onExtract` turns off the internal dictionary write, so the\n // writes stay serialised behind the lock below.\n onExtract: ({ key, content }) => {\n pendingWrite = pendingWrite\n .then(() =>\n writeDictionary(intlayerBabel, configuration, key, content, filePath)\n )\n .catch((error) => {\n appLogger(\n [\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Failed to write the dictionary extracted from ${colorizePath(\n relative(configuration.system.baseDir, filePath)\n )}:`,\n error,\n ],\n { level: 'error' }\n );\n });\n\n return pendingWrite;\n },\n });\n\n if (!result?.transformedCode) return source;\n\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Extracted content from ${colorizePath(\n relative(configuration.system.baseDir, filePath)\n )}`,\n { level: 'debug' }\n );\n\n return result.transformedCode;\n};\n\n/**\n * Webpack-compatible loader running the Intlayer compiler extraction pass.\n *\n * It replaces the `babel.config.js` setup on Next.js: Turbopack never reads a\n * Babel config, and on webpack a Babel config would opt the whole project out\n * of SWC. Registered automatically by `withIntlayer` on both bundlers.\n *\n * @example next.config.ts — registered by hand\n * ```ts\n * export default {\n * turbopack: {\n * rules: {\n * // No `as`: it renames the module, so naming the extension the rule\n * // already matches would ask for `Component.tsx.tsx`.\n * '*.tsx': {\n * loaders: [\n * {\n * loader: require.resolve('next-intlayer/extractor-loader'),\n * options: { mode: 'dev' },\n * },\n * ],\n * },\n * },\n * },\n * };\n * ```\n */\nconst intlayerExtractorLoader = function (\n this: IntlayerLoaderContext,\n source: string\n): void {\n const callback = this.async();\n const { mode } = getOptions(this);\n const filePath = this.resourcePath;\n\n extractFile(filePath, source, mode ?? 'build')\n .then((code) => callback(null, code))\n .catch((error) => {\n // An extraction failure must not take the build down: the file simply\n // keeps its own hardcoded strings.\n const appLogger = getAppLogger(cachedConfiguration);\n\n appLogger(\n [\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Failed to extract content from ${colorizePath(filePath)}:`,\n error,\n ],\n { level: 'error' }\n );\n\n callback(null, source);\n });\n};\n\nexport default intlayerExtractorLoader;\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { localeDetector as localeDetector$1 } from "./localeDetector.mjs";
2
+ import { getAppLogger } from "@intlayer/config/logger";
2
3
  import { formatProxyEnabledMessage, getCanonicalPath, getDomainHostname, getDomainOrigin, getInternalPath, getLocaleFromDomain, getRewriteRules, isProxyStorageLocaleEnabled, resolveLocalizedPath, resolveProxyMode } from "@intlayer/core/localization";
3
4
  import { getLocaleFromStorageServer, setLocaleInStorageServer } from "@intlayer/core/utils";
4
5
  import { internationalization, log, routing } from "@intlayer/config/built";
5
6
  import { ROUTING_MODE } from "@intlayer/config/defaultValues";
6
- import { getAppLogger } from "@intlayer/config/logger";
7
7
  import { NextResponse } from "next/server";
8
8
 
9
9
  //#region src/proxy/intlayerProxy.ts
@@ -1,6 +1,6 @@
1
- import { colorize, getAppLogger } from "@intlayer/config/logger";
2
1
  import { dirname, join } from "node:path";
3
2
  import * as ANSIColors from "@intlayer/config/colors";
3
+ import { colorize, getAppLogger } from "@intlayer/config/logger";
4
4
  import { getProjectRequire } from "@intlayer/config/utils";
5
5
  import { runOnce } from "@intlayer/engine/utils";
6
6
  import { readFileSync, rmSync } from "node:fs";
@@ -45,25 +45,16 @@ const resolveSwcLogLevel = (intlayerConfig) => {
45
45
  /**
46
46
  * Whether the purge / minify pipeline should run for this build.
47
47
  *
48
- * Compat-adapter callers disable it: their descriptors reach `withIntlayer`
49
- * only in the SWC plugin's lossy wire format, so the usage analyser would not
50
- * recognise `useTranslation(...)` & co. and would purge fields those call sites
51
- * still read.
48
+ * Compat-adapter callers do not disable it: their descriptors reach the usage
49
+ * analyser intact (`compatCallers`), which records the dictionaries they read
50
+ * and marks those as unrenamable so purging stays field-accurate and the
51
+ * minify step leaves them alone.
52
52
  */
53
- const getIsPurgePipelineEnabled = (intlayerConfig, swcExtraCallers, isSwcPluginUsable) => {
53
+ const getIsPurgePipelineEnabled = (intlayerConfig, isSwcPluginUsable) => {
54
54
  const { purge, minify, optimize } = intlayerConfig.build;
55
55
  if (optimize === false) return false;
56
56
  if (!purge && !minify) return false;
57
57
  if (!isSwcPluginUsable) return false;
58
- if (swcExtraCallers && swcExtraCallers.length > 0) {
59
- getAppLogger(intlayerConfig)([
60
- "Dictionary purge and minification are",
61
- colorize("disabled", ANSIColors.GREY_DARK),
62
- "because compat-adapter callers are configured — their call sites are",
63
- "not visible to the usage analyser."
64
- ], { level: "warn" });
65
- return false;
66
- }
67
58
  return true;
68
59
  };
69
60
  /**
@@ -102,7 +93,7 @@ const getIsPurgePipelineAvailable = (intlayerConfig) => loadIntlayerBabel(intlay
102
93
  * Runs the purge / minify pipeline and writes the resulting field-rename
103
94
  * tables to the cache file so every process of the build reads the same ones.
104
95
  */
105
- const runPurgePipelineOnce = async (intlayerConfig, configOptions, dictionaries, fieldRenameMapFilePath) => {
96
+ const runPurgePipelineOnce = async (intlayerConfig, configOptions, dictionaries, compatCallers, fieldRenameMapFilePath) => {
106
97
  const appLogger = getAppLogger(intlayerConfig);
107
98
  const intlayerBabel = loadIntlayerBabel(intlayerConfig);
108
99
  if (!intlayerBabel) {
@@ -121,7 +112,8 @@ const runPurgePipelineOnce = async (intlayerConfig, configOptions, dictionaries,
121
112
  try {
122
113
  fieldRenameMap = serializeFieldRenameMap(runIntlayerPurgePipeline(getPurgePluginOptions({
123
114
  configOptions,
124
- dictionaries
115
+ dictionaries,
116
+ overrides: compatCallers?.length ? { compatCallers } : void 0
125
117
  })));
126
118
  } catch (pipelineError) {
127
119
  appLogger([
@@ -170,13 +162,13 @@ const removeFieldRenameMapFile = (fieldRenameMapFilePath) => {
170
162
  * pipeline is disabled, unavailable, or renamed nothing.
171
163
  */
172
164
  const prepareSwcOptimization = async (intlayerConfig, params) => {
173
- const { configOptions, dictionaries, swcExtraCallers, isSwcPluginUsable } = params;
165
+ const { configOptions, dictionaries, compatCallers, isSwcPluginUsable } = params;
174
166
  const fieldRenameMapFilePath = getCacheFilePath(intlayerConfig, FIELD_RENAME_MAP_FILE_NAME);
175
- if (!getIsPurgePipelineEnabled(intlayerConfig, swcExtraCallers, isSwcPluginUsable)) {
167
+ if (!getIsPurgePipelineEnabled(intlayerConfig, isSwcPluginUsable)) {
176
168
  removeFieldRenameMapFile(fieldRenameMapFilePath);
177
169
  return {};
178
170
  }
179
- await runOnce(getCacheFilePath(intlayerConfig, PURGE_SENTINEL_FILE_NAME), () => runPurgePipelineOnce(intlayerConfig, configOptions, dictionaries, fieldRenameMapFilePath), { cacheTimeoutMs: PURGE_CACHE_TIMEOUT_MS });
171
+ await runOnce(getCacheFilePath(intlayerConfig, PURGE_SENTINEL_FILE_NAME), () => runPurgePipelineOnce(intlayerConfig, configOptions, dictionaries, compatCallers, fieldRenameMapFilePath), { cacheTimeoutMs: PURGE_CACHE_TIMEOUT_MS });
180
172
  return readFieldRenameMapFile(fieldRenameMapFilePath);
181
173
  };
182
174