shakapacker 9.3.0.beta.6 → 9.3.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -105
  3. data/ESLINT_TECHNICAL_DEBT.md +8 -2
  4. data/Gemfile.lock +1 -1
  5. data/README.md +53 -2
  6. data/docs/configuration.md +28 -0
  7. data/docs/rspack_migration_guide.md +238 -2
  8. data/docs/troubleshooting.md +21 -21
  9. data/eslint.config.fast.js +8 -0
  10. data/eslint.config.js +47 -10
  11. data/knip.ts +8 -1
  12. data/lib/install/config/shakapacker.yml +6 -6
  13. data/lib/shakapacker/configuration.rb +227 -4
  14. data/lib/shakapacker/dev_server.rb +88 -1
  15. data/lib/shakapacker/doctor.rb +129 -72
  16. data/lib/shakapacker/instance.rb +85 -1
  17. data/lib/shakapacker/manifest.rb +85 -11
  18. data/lib/shakapacker/runner.rb +12 -8
  19. data/lib/shakapacker/swc_migrator.rb +7 -7
  20. data/lib/shakapacker/version.rb +1 -1
  21. data/lib/shakapacker.rb +143 -3
  22. data/lib/tasks/shakapacker/doctor.rake +1 -1
  23. data/lib/tasks/shakapacker/export_bundler_config.rake +4 -4
  24. data/package/config.ts +0 -1
  25. data/package/configExporter/buildValidator.ts +53 -29
  26. data/package/configExporter/cli.ts +152 -118
  27. data/package/configExporter/configFile.ts +33 -26
  28. data/package/configExporter/fileWriter.ts +3 -3
  29. data/package/configExporter/types.ts +64 -0
  30. data/package/configExporter/yamlSerializer.ts +147 -36
  31. data/package/dev_server.ts +2 -1
  32. data/package/env.ts +1 -1
  33. data/package/environments/base.ts +4 -4
  34. data/package/environments/development.ts +7 -6
  35. data/package/environments/production.ts +6 -7
  36. data/package/environments/test.ts +2 -1
  37. data/package/index.ts +28 -4
  38. data/package/loaders.d.ts +2 -2
  39. data/package/optimization/webpack.ts +29 -31
  40. data/package/plugins/webpack.ts +2 -1
  41. data/package/rspack/index.ts +2 -1
  42. data/package/rules/file.ts +1 -0
  43. data/package/rules/jscommon.ts +1 -0
  44. data/package/utils/helpers.ts +0 -1
  45. data/package/utils/pathValidation.ts +68 -7
  46. data/package/utils/requireOrError.ts +10 -2
  47. data/package/utils/typeGuards.ts +43 -46
  48. data/package/webpack-types.d.ts +2 -2
  49. data/package/webpackDevServerConfig.ts +1 -0
  50. data/package.json +2 -3
  51. data/test/configExporter/integration.test.js +8 -8
  52. data/test/package/configExporter/cli.test.js +440 -0
  53. data/test/package/configExporter/types.test.js +163 -0
  54. data/test/package/configExporter.test.js +271 -7
  55. data/test/package/yamlSerializer.test.js +204 -0
  56. data/test/typescript/pathValidation.test.js +44 -0
  57. data/test/typescript/requireOrError.test.js +49 -0
  58. data/yarn.lock +0 -32
  59. metadata +11 -6
  60. data/.eslintrc.fast.js +0 -40
  61. data/.eslintrc.js +0 -84
  62. data/package-lock.json +0 -13047
@@ -1,35 +1,40 @@
1
1
  // This will be a substantial file - the main CLI entry point
2
- // Migrating from bin/export-bundler-config but streamlined for TypeScript
2
+ // Originally migrated from bin/export-bundler-config, now bin/shakapacker-config
3
3
 
4
4
  import { existsSync, readFileSync, writeFileSync } from "fs"
5
5
  import { resolve, dirname, sep, delimiter, basename } from "path"
6
6
  import { inspect } from "util"
7
7
  import { load as loadYaml } from "js-yaml"
8
8
  import yargs from "yargs"
9
- import { ExportOptions, ConfigMetadata, FileOutput } from "./types"
9
+ import {
10
+ ExportOptions,
11
+ ConfigMetadata,
12
+ FileOutput,
13
+ BUILD_ENV_VARS,
14
+ isBuildEnvVar,
15
+ isDangerousEnvVar,
16
+ DEFAULT_EXPORT_DIR,
17
+ DEFAULT_CONFIG_FILE
18
+ } from "./types"
10
19
  import { YamlSerializer } from "./yamlSerializer"
11
20
  import { FileWriter } from "./fileWriter"
12
21
  import { ConfigFileLoader, generateSampleConfigFile } from "./configFile"
13
22
  import { BuildValidator } from "./buildValidator"
23
+ import { safeResolvePath } from "../utils/pathValidation"
14
24
 
15
25
  // Read version from package.json
16
- const packageJson = JSON.parse(
17
- readFileSync(resolve(__dirname, "../../package.json"), "utf8")
18
- )
19
- const VERSION = packageJson.version
20
-
21
- /**
22
- * Environment variable names that can be set by build configurations
23
- */
24
- const BUILD_ENV_VARS = [
25
- "NODE_ENV",
26
- "RAILS_ENV",
27
- "NODE_OPTIONS",
28
- "BABEL_ENV",
29
- "WEBPACK_SERVE",
30
- "CLIENT_BUNDLE_ONLY",
31
- "SERVER_BUNDLE_ONLY"
32
- ] as const
26
+ let VERSION = "unknown"
27
+ try {
28
+ const packageJson = JSON.parse(
29
+ readFileSync(resolve(__dirname, "../../package.json"), "utf8")
30
+ ) as { version?: string }
31
+ VERSION = packageJson.version || "unknown"
32
+ } catch (error) {
33
+ console.warn(
34
+ "Could not read version from package.json:",
35
+ error instanceof Error ? error.message : String(error)
36
+ )
37
+ }
33
38
 
34
39
  /**
35
40
  * Saves current values of build environment variables for later restoration
@@ -103,6 +108,15 @@ export async function run(args: string[]): Promise<number> {
103
108
  // Apply defaults
104
109
  const resolvedOptions = applyDefaults(options)
105
110
 
111
+ // Validate paths for security AFTER defaults are applied
112
+ // Use safeResolvePath which validates and resolves symlinks
113
+ if (resolvedOptions.output) {
114
+ safeResolvePath(appRoot, resolvedOptions.output)
115
+ }
116
+ if (resolvedOptions.saveDir) {
117
+ safeResolvePath(appRoot, resolvedOptions.saveDir)
118
+ }
119
+
106
120
  // Validate after defaults are applied
107
121
  if (resolvedOptions.annotate && resolvedOptions.format !== "yaml") {
108
122
  throw new Error(
@@ -114,8 +128,7 @@ export async function run(args: string[]): Promise<number> {
114
128
  if (resolvedOptions.build) {
115
129
  const loader = new ConfigFileLoader(resolvedOptions.configFile)
116
130
  if (!loader.exists()) {
117
- const configPath =
118
- resolvedOptions.configFile || "config/shakapacker-builds.yml"
131
+ const configPath = resolvedOptions.configFile || DEFAULT_CONFIG_FILE
119
132
  throw new Error(
120
133
  `--build requires a config file but ${configPath} not found. Run --init to create it.`
121
134
  )
@@ -144,7 +157,7 @@ export async function run(args: string[]): Promise<number> {
144
157
  }
145
158
  }
146
159
 
147
- function parseArguments(args: string[]): ExportOptions {
160
+ export function parseArguments(args: string[]): ExportOptions {
148
161
  const argv = yargs(args)
149
162
  .version(VERSION)
150
163
  .usage(
@@ -164,8 +177,7 @@ QUICK START (for troubleshooting):
164
177
  .option("init", {
165
178
  type: "boolean",
166
179
  default: false,
167
- description:
168
- "Generate config/shakapacker-builds.yml (use with --ssr for SSR builds)"
180
+ description: `Generate ${DEFAULT_CONFIG_FILE} (use with --ssr for SSR builds)`
169
181
  })
170
182
  .option("ssr", {
171
183
  type: "boolean",
@@ -188,8 +200,7 @@ QUICK START (for troubleshooting):
188
200
  })
189
201
  .option("config-file", {
190
202
  type: "string",
191
- description:
192
- "Path to config file (default: config/shakapacker-builds.yml)"
203
+ description: `Path to config file (default: ${DEFAULT_CONFIG_FILE})`
193
204
  })
194
205
  // Validation Options
195
206
  .option("validate", {
@@ -235,11 +246,26 @@ QUICK START (for troubleshooting):
235
246
  "Enable inline documentation (YAML only, default with --doctor or file output)"
236
247
  })
237
248
  .option("depth", {
238
- type: "number",
249
+ // Note: type omitted to allow string "null" (yargs would reject it).
250
+ // Coerce function handles validation for both numbers and "null".
239
251
  default: 20,
240
252
  coerce: (value: number | string) => {
253
+ // Handle "null" string for unlimited depth
241
254
  if (value === "null" || value === null) return null
242
- return typeof value === "number" ? value : parseInt(String(value), 10)
255
+
256
+ // Reject non-numeric types (arrays, objects, etc.)
257
+ if (typeof value !== "number" && typeof value !== "string") {
258
+ throw new Error(
259
+ `--depth must be a number or 'null', got: ${typeof value}`
260
+ )
261
+ }
262
+
263
+ const parsed =
264
+ typeof value === "number" ? value : parseInt(String(value), 10)
265
+ if (Number.isNaN(parsed)) {
266
+ throw new Error(`--depth must be a number or 'null', got: ${value}`)
267
+ }
268
+ return parsed
243
269
  },
244
270
  description: "Inspection depth (use 'null' for unlimited)"
245
271
  })
@@ -319,6 +345,21 @@ QUICK START (for troubleshooting):
319
345
  "--validate cannot be used with --build or --all-builds."
320
346
  )
321
347
  }
348
+ if (argv["all-builds"] && argv.output) {
349
+ throw new Error(
350
+ "--all-builds and --output are mutually exclusive. Use --save-dir instead."
351
+ )
352
+ }
353
+ if (argv["all-builds"] && argv.stdout) {
354
+ throw new Error(
355
+ "--all-builds and --stdout are mutually exclusive. Use --save-dir instead."
356
+ )
357
+ }
358
+ if (argv.stdout && argv.output) {
359
+ throw new Error(
360
+ "--stdout and --output are mutually exclusive. Use one or the other."
361
+ )
362
+ }
322
363
  if (argv.ssr && !argv.init) {
323
364
  throw new Error(
324
365
  "--ssr can only be used with --init. Use: bin/shakapacker-config --init --ssr"
@@ -351,28 +392,25 @@ QUICK START (for troubleshooting):
351
392
 
352
393
  # Advanced output options
353
394
  bin/shakapacker-config --build=dev --stdout # View in terminal
354
- bin/shakapacker-config --build=dev --output=config.yaml # Save to specific file`
395
+ bin/shakapacker-config --build=dev --output=config.yml # Save to specific file`
355
396
  )
356
397
  .strict()
357
398
  .parseSync()
358
399
 
359
400
  // Type assertions are safe here because yargs validates choices at runtime
360
401
  // Handle --webpack and --rspack flags
361
- let bundler: "webpack" | "rspack" | undefined = argv.bundler as
362
- | "webpack"
363
- | "rspack"
364
- | undefined
402
+ let { bundler } = argv
365
403
  if (argv.webpack) bundler = "webpack"
366
404
  if (argv.rspack) bundler = "rspack"
367
405
 
368
406
  return {
369
407
  bundler,
370
- env: argv.env as "development" | "production" | "test" | undefined,
408
+ env: argv.env,
371
409
  clientOnly: argv["client-only"],
372
410
  serverOnly: argv["server-only"],
373
411
  output: argv.output,
374
- depth: argv.depth as number | null,
375
- format: argv.format as "yaml" | "json" | "inspect" | undefined,
412
+ depth: argv.depth,
413
+ format: argv.format,
376
414
  help: false, // yargs handles help internally
377
415
  verbose: argv.verbose,
378
416
  doctor: argv.doctor,
@@ -411,17 +449,14 @@ function applyDefaults(options: ExportOptions): ExportOptions {
411
449
  !updatedOptions.output &&
412
450
  !updatedOptions.saveDir
413
451
  ) {
414
- updatedOptions.saveDir = resolve(
415
- process.cwd(),
416
- "shakapacker-config-exports"
417
- )
452
+ updatedOptions.saveDir = resolve(process.cwd(), DEFAULT_EXPORT_DIR)
418
453
  }
419
454
 
420
455
  return updatedOptions
421
456
  }
422
457
 
423
458
  function runInitCommand(options: ExportOptions): number {
424
- const configPath = options.configFile || "config/shakapacker-builds.yml"
459
+ const configPath = options.configFile || DEFAULT_CONFIG_FILE
425
460
  const fullPath = resolve(process.cwd(), configPath)
426
461
 
427
462
  // Check if SSR variant is requested via --ssr flag
@@ -528,7 +563,7 @@ async function runValidateCommand(options: ExportOptions): Promise<number> {
528
563
  // Validate that config file exists
529
564
  const loader = new ConfigFileLoader(options.configFile)
530
565
  if (!loader.exists()) {
531
- const configPath = options.configFile || "config/shakapacker-builds.yml"
566
+ const configPath = options.configFile || DEFAULT_CONFIG_FILE
532
567
  throw new Error(
533
568
  `Config file ${configPath} not found. Run --init to create it.`
534
569
  )
@@ -561,12 +596,12 @@ async function runValidateCommand(options: ExportOptions): Promise<number> {
561
596
  // Handle empty builds edge case
562
597
  if (buildsToValidate.length === 0) {
563
598
  throw new Error(
564
- `No builds found in config file. Add at least one build to config/shakapacker-builds.yml or run --init to see examples.`
599
+ `No builds found in config file. Add at least one build to ${DEFAULT_CONFIG_FILE} or run --init to see examples.`
565
600
  )
566
601
  }
567
602
  }
568
603
 
569
- console.log("\n" + "=".repeat(80))
604
+ console.log(`\n${"=".repeat(80)}`)
570
605
  console.log("🔍 Validating Builds")
571
606
  console.log("=".repeat(80))
572
607
  console.log(`\nValidating ${buildsToValidate.length} build(s)...\n`)
@@ -577,7 +612,7 @@ async function runValidateCommand(options: ExportOptions): Promise<number> {
577
612
  " This includes all webpack/rspack compilation logs, warnings, and progress messages"
578
613
  )
579
614
  console.log(" Use without --verbose to see only errors and summaries\n")
580
- console.log("=".repeat(80) + "\n")
615
+ console.log(`${"=".repeat(80)}\n`)
581
616
  }
582
617
 
583
618
  const results = []
@@ -585,7 +620,7 @@ async function runValidateCommand(options: ExportOptions): Promise<number> {
585
620
  // Validate each build
586
621
  for (const buildName of buildsToValidate) {
587
622
  if (options.verbose) {
588
- console.log("\n" + "=".repeat(80))
623
+ console.log(`\n${"=".repeat(80)}`)
589
624
  console.log(`📦 VALIDATING BUILD: ${buildName}`)
590
625
  console.log("=".repeat(80))
591
626
  } else {
@@ -611,7 +646,11 @@ async function runValidateCommand(options: ExportOptions): Promise<number> {
611
646
  "development"
612
647
 
613
648
  // Auto-detect bundler using the build's environment
614
- const defaultBundler = await autoDetectBundler(buildEnv, appRoot)
649
+ const defaultBundler = await autoDetectBundler(
650
+ buildEnv,
651
+ appRoot,
652
+ options.verbose
653
+ )
615
654
 
616
655
  // Resolve build config with the correct default bundler
617
656
  const resolvedBuild = loader.resolveBuild(
@@ -670,8 +709,7 @@ async function runAllBuildsCommand(options: ExportOptions): Promise<number> {
670
709
 
671
710
  const loader = new ConfigFileLoader(resolvedOptions.configFile)
672
711
  if (!loader.exists()) {
673
- const configPath =
674
- resolvedOptions.configFile || "config/shakapacker-builds.yml"
712
+ const configPath = resolvedOptions.configFile || DEFAULT_CONFIG_FILE
675
713
  throw new Error(
676
714
  `Config file ${configPath} not found. Run --init to create it.`
677
715
  )
@@ -719,7 +757,7 @@ async function runAllBuildsCommand(options: ExportOptions): Promise<number> {
719
757
  }
720
758
 
721
759
  // Print summary
722
- console.log("\n" + "=".repeat(80))
760
+ console.log(`\n${"=".repeat(80)}`)
723
761
  console.log("✅ All Builds Exported!")
724
762
  console.log("=".repeat(80))
725
763
  console.log(`\nCreated ${createdFiles.length} configuration file(s) in:`)
@@ -728,7 +766,7 @@ async function runAllBuildsCommand(options: ExportOptions): Promise<number> {
728
766
  createdFiles.forEach((file) => {
729
767
  console.log(` ✓ ${basename(file)}`)
730
768
  })
731
- console.log("\n" + "=".repeat(80) + "\n")
769
+ console.log(`\n${"=".repeat(80)}\n`)
732
770
 
733
771
  return 0
734
772
  } catch (error: unknown) {
@@ -749,7 +787,7 @@ async function runDoctorMode(
749
787
  const savedEnv = saveBuildEnvironmentVariables()
750
788
 
751
789
  try {
752
- console.log("\n" + "=".repeat(80))
790
+ console.log(`\n${"=".repeat(80)}`)
753
791
  console.log("🔍 Config Exporter - Doctor Mode")
754
792
  console.log("=".repeat(80))
755
793
 
@@ -758,7 +796,7 @@ async function runDoctorMode(
758
796
  const createdFiles: string[] = []
759
797
 
760
798
  // Check if config file exists - always use it for doctor mode
761
- const configFilePath = options.configFile || "config/shakapacker-builds.yml"
799
+ const configFilePath = options.configFile || DEFAULT_CONFIG_FILE
762
800
  const loader = new ConfigFileLoader(configFilePath)
763
801
 
764
802
  if (loader.exists()) {
@@ -899,7 +937,7 @@ async function runDoctorMode(
899
937
 
900
938
  function printDoctorSummary(createdFiles: string[], targetDir: string): void {
901
939
  // Print summary
902
- console.log("\n" + "=".repeat(80))
940
+ console.log(`\n${"=".repeat(80)}`)
903
941
  console.log("✅ Export Complete!")
904
942
  console.log("=".repeat(80))
905
943
  console.log(`\nCreated ${createdFiles.length} configuration file(s) in:`)
@@ -922,14 +960,14 @@ function printDoctorSummary(createdFiles: string[], targetDir: string): void {
922
960
  }
923
961
 
924
962
  if (shouldSuggestGitignore) {
925
- console.log("\n" + "─".repeat(80))
963
+ console.log(`\n${"─".repeat(80)}`)
926
964
  console.log(
927
965
  "💡 Tip: Add the export directory to .gitignore to avoid committing config files:"
928
966
  )
929
967
  console.log(`\n echo "${dirName}/" >> .gitignore\n`)
930
968
  }
931
969
 
932
- console.log("\n" + "=".repeat(80) + "\n")
970
+ console.log(`\n${"=".repeat(80)}\n`)
933
971
  }
934
972
 
935
973
  async function runSaveMode(
@@ -946,7 +984,7 @@ async function runSaveMode(
946
984
  if (options.output) {
947
985
  // Single file output
948
986
  const combined = configs.map((c) => c.config)
949
- const metadata = configs[0].metadata
987
+ const { metadata } = configs[0]
950
988
  metadata.configCount = combined.length
951
989
 
952
990
  const output = formatConfig(
@@ -986,15 +1024,15 @@ async function runStdoutMode(
986
1024
  options: ExportOptions,
987
1025
  appRoot: string
988
1026
  ): Promise<void> {
989
- const configs = await loadConfigsForEnv(options.env!, options, appRoot)
1027
+ const configs = await loadConfigsForEnv(options.env, options, appRoot)
990
1028
  const combined = configs.map((c) => c.config)
991
- const metadata = configs[0].metadata
1029
+ const { metadata } = configs[0]
992
1030
  metadata.configCount = combined.length
993
1031
 
994
1032
  const config = combined.length === 1 ? combined[0] : combined
995
1033
  const output = formatConfig(config, metadata, options, appRoot)
996
1034
 
997
- console.log("\n" + "=".repeat(80) + "\n")
1035
+ console.log(`\n${"=".repeat(80)}\n`)
998
1036
  console.log(output)
999
1037
  }
1000
1038
 
@@ -1002,9 +1040,9 @@ async function runSingleFileMode(
1002
1040
  options: ExportOptions,
1003
1041
  appRoot: string
1004
1042
  ): Promise<void> {
1005
- const configs = await loadConfigsForEnv(options.env!, options, appRoot)
1043
+ const configs = await loadConfigsForEnv(options.env, options, appRoot)
1006
1044
  const combined = configs.map((c) => c.config)
1007
- const metadata = configs[0].metadata
1045
+ const { metadata } = configs[0]
1008
1046
  metadata.configCount = combined.length
1009
1047
 
1010
1048
  const config = combined.length === 1 ? combined[0] : combined
@@ -1031,7 +1069,11 @@ async function loadConfigsForEnv(
1031
1069
  // Use a temporary env for auto-detection, will be overridden by build config
1032
1070
  const tempEnv = env || "development"
1033
1071
  const loader = new ConfigFileLoader(options.configFile)
1034
- const defaultBundler = await autoDetectBundler(tempEnv, appRoot)
1072
+ const defaultBundler = await autoDetectBundler(
1073
+ tempEnv,
1074
+ appRoot,
1075
+ options.verbose
1076
+ )
1035
1077
  const resolvedBuild = loader.resolveBuild(
1036
1078
  options.build,
1037
1079
  options,
@@ -1046,36 +1088,27 @@ async function loadConfigsForEnv(
1046
1088
 
1047
1089
  // Set environment variables from config
1048
1090
  // Security: Only allow specific environment variables to prevent malicious configs
1049
- const DANGEROUS_ENV_VARS = [
1050
- "PATH",
1051
- "HOME",
1052
- "LD_PRELOAD",
1053
- "LD_LIBRARY_PATH",
1054
- "DYLD_LIBRARY_PATH",
1055
- "DYLD_INSERT_LIBRARIES"
1056
- ]
1057
-
1058
- if (process.env.VERBOSE) {
1091
+ if (options.verbose) {
1059
1092
  console.log(
1060
1093
  `[Config Exporter] Setting environment variables from build config...`
1061
1094
  )
1062
1095
  }
1063
1096
 
1064
1097
  for (const [key, value] of Object.entries(resolvedBuild.environment)) {
1065
- if (DANGEROUS_ENV_VARS.includes(key)) {
1098
+ if (isDangerousEnvVar(key)) {
1066
1099
  console.warn(
1067
1100
  `[Config Exporter] Warning: Skipping dangerous environment variable: ${key}`
1068
1101
  )
1069
1102
  continue
1070
1103
  }
1071
- if (!(BUILD_ENV_VARS as readonly string[]).includes(key)) {
1104
+ if (!isBuildEnvVar(key)) {
1072
1105
  console.warn(
1073
1106
  `[Config Exporter] Warning: Skipping non-whitelisted environment variable: ${key}. ` +
1074
1107
  `Allowed variables are: ${BUILD_ENV_VARS.join(", ")}`
1075
1108
  )
1076
1109
  continue
1077
1110
  }
1078
- if (process.env.VERBOSE) {
1111
+ if (options.verbose) {
1079
1112
  console.log(`[Config Exporter] ${key}=${value}`)
1080
1113
  }
1081
1114
  process.env[key] = value
@@ -1125,7 +1158,9 @@ async function loadConfigsForEnv(
1125
1158
  finalEnv = env || "development"
1126
1159
 
1127
1160
  // Auto-detect bundler if not specified
1128
- bundler = options.bundler || (await autoDetectBundler(finalEnv, appRoot))
1161
+ bundler =
1162
+ options.bundler ||
1163
+ (await autoDetectBundler(finalEnv, appRoot, options.verbose))
1129
1164
 
1130
1165
  // Set environment variables
1131
1166
  process.env.NODE_ENV = finalEnv
@@ -1141,9 +1176,10 @@ async function loadConfigsForEnv(
1141
1176
 
1142
1177
  // Find and load config file
1143
1178
  const configFile =
1144
- customConfigFile || findConfigFile(bundler, appRoot, finalEnv)
1179
+ customConfigFile ||
1180
+ findConfigFile(bundler, appRoot, finalEnv, options.verbose)
1145
1181
  // Quiet mode for cleaner output - only show if verbose or errors
1146
- if (process.env.VERBOSE) {
1182
+ if (options.verbose) {
1147
1183
  console.log(`[Config Exporter] Loading config: ${configFile}`)
1148
1184
  console.log(`[Config Exporter] Environment: ${finalEnv}`)
1149
1185
  console.log(`[Config Exporter] Bundler: ${bundler}`)
@@ -1156,7 +1192,6 @@ async function loadConfigsForEnv(
1156
1192
  // Register ts-node for TypeScript config files
1157
1193
  if (configFile.endsWith(".ts")) {
1158
1194
  try {
1159
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1160
1195
  require("ts-node/register/transpile-only")
1161
1196
  } catch (error) {
1162
1197
  throw new Error(
@@ -1205,7 +1240,6 @@ async function loadConfigsForEnv(
1205
1240
  }
1206
1241
  })
1207
1242
 
1208
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1209
1243
  let loadedConfig: any
1210
1244
  try {
1211
1245
  loadedConfig = require(configFile)
@@ -1283,9 +1317,9 @@ async function loadConfigsForEnv(
1283
1317
  }
1284
1318
 
1285
1319
  throw new Error(
1286
- `Failed to execute config function: ${errorMessage}\n` +
1287
- envDetails.join("\n") +
1288
- `\nTip: ${suggestion}`
1320
+ `Failed to execute config function: ${errorMessage}\n${envDetails.join(
1321
+ "\n"
1322
+ )}\nTip: ${suggestion}`
1289
1323
  )
1290
1324
  }
1291
1325
  }
@@ -1338,7 +1372,7 @@ async function loadConfigsForEnv(
1338
1372
  }
1339
1373
 
1340
1374
  // Debug logging
1341
- if (process.env.VERBOSE || buildOutputs.length > 0) {
1375
+ if (options.verbose || buildOutputs.length > 0) {
1342
1376
  console.log(
1343
1377
  `[Config Exporter] Webpack returned ${configs.length} config(s), buildOutputs: [${buildOutputs.join(", ")}]`
1344
1378
  )
@@ -1442,39 +1476,37 @@ function formatConfig(
1442
1476
  return value
1443
1477
  }
1444
1478
  return JSON.stringify({ metadata, config }, jsonReplacer, 2)
1445
- } else {
1446
- // inspect format
1447
- const inspectOptions = {
1448
- depth: options.depth,
1449
- colors: false,
1450
- maxArrayLength: null,
1451
- maxStringLength: null,
1452
- breakLength: 120,
1453
- compact: false
1454
- }
1455
-
1456
- let output =
1457
- "=== METADATA ===\n\n" + inspect(metadata, inspectOptions) + "\n\n"
1458
- output += "=== CONFIG ===\n\n"
1479
+ }
1480
+ // inspect format
1481
+ const inspectOptions = {
1482
+ depth: options.depth,
1483
+ colors: false,
1484
+ maxArrayLength: null,
1485
+ maxStringLength: null,
1486
+ breakLength: 120,
1487
+ compact: false
1488
+ }
1459
1489
 
1460
- if (Array.isArray(config)) {
1461
- output += `Total configs: ${config.length}\n\n`
1462
- config.forEach((cfg, index) => {
1463
- output += `--- Config [${index}] ---\n\n`
1464
- output += inspect(cfg, inspectOptions) + "\n\n"
1465
- })
1466
- } else {
1467
- output += inspect(config, inspectOptions) + "\n"
1468
- }
1490
+ let output = `=== METADATA ===\n\n${inspect(metadata, inspectOptions)}\n\n`
1491
+ output += "=== CONFIG ===\n\n"
1469
1492
 
1470
- return output
1493
+ if (Array.isArray(config)) {
1494
+ output += `Total configs: ${config.length}\n\n`
1495
+ config.forEach((cfg, index) => {
1496
+ output += `--- Config [${index}] ---\n\n`
1497
+ output += `${inspect(cfg, inspectOptions)}\n\n`
1498
+ })
1499
+ } else {
1500
+ output += `${inspect(config, inspectOptions)}\n`
1471
1501
  }
1502
+
1503
+ return output
1472
1504
  }
1473
1505
 
1474
1506
  function cleanConfig(obj: any, rootPath: string): any {
1475
1507
  const makePathRelative = (str: string): string => {
1476
1508
  if (typeof str === "string" && str.startsWith(rootPath)) {
1477
- return "./" + str.substring(rootPath.length + 1)
1509
+ return `./${str.substring(rootPath.length + 1)}`
1478
1510
  }
1479
1511
  return str
1480
1512
  }
@@ -1542,11 +1574,12 @@ let shakapackerConfigCache: {
1542
1574
 
1543
1575
  function loadShakapackerConfig(
1544
1576
  env: string,
1545
- appRoot: string
1577
+ appRoot: string,
1578
+ verbose = false
1546
1579
  ): { bundler: "webpack" | "rspack"; configPath: string } {
1547
1580
  // Return cached result if same environment
1548
1581
  if (shakapackerConfigCache && shakapackerConfigCache.env === env) {
1549
- if (process.env.VERBOSE) {
1582
+ if (verbose) {
1550
1583
  console.log(
1551
1584
  `[Config Exporter] Using cached bundler config for env: ${env}`
1552
1585
  )
@@ -1554,7 +1587,7 @@ function loadShakapackerConfig(
1554
1587
  return shakapackerConfigCache.result
1555
1588
  }
1556
1589
 
1557
- if (process.env.VERBOSE) {
1590
+ if (verbose) {
1558
1591
  console.log(`[Config Exporter] Loading shakapacker config for env: ${env}`)
1559
1592
  }
1560
1593
 
@@ -1623,18 +1656,20 @@ function loadShakapackerConfig(
1623
1656
  */
1624
1657
  async function autoDetectBundler(
1625
1658
  env: string,
1626
- appRoot: string
1659
+ appRoot: string,
1660
+ verbose = false
1627
1661
  ): Promise<"webpack" | "rspack"> {
1628
- const { bundler } = loadShakapackerConfig(env, appRoot)
1662
+ const { bundler } = loadShakapackerConfig(env, appRoot, verbose)
1629
1663
  return bundler
1630
1664
  }
1631
1665
 
1632
1666
  function findConfigFile(
1633
1667
  bundler: "webpack" | "rspack",
1634
1668
  appRoot: string,
1635
- env: string
1669
+ env: string,
1670
+ verbose = false
1636
1671
  ): string {
1637
- const { configPath } = loadShakapackerConfig(env, appRoot)
1672
+ const { configPath } = loadShakapackerConfig(env, appRoot, verbose)
1638
1673
  const extensions = ["ts", "js"]
1639
1674
 
1640
1675
  if (bundler === "rspack") {
@@ -1692,7 +1727,6 @@ function setupNodePath(appRoot: string): void {
1692
1727
  ? `${nodePaths.join(delimiter)}${delimiter}${existingNodePath}`
1693
1728
  : nodePaths.join(delimiter)
1694
1729
 
1695
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1696
1730
  require("module").Module._initPaths()
1697
1731
  }
1698
1732
  }