@salty-css/core 0.1.0-alpha.9 → 0.1.0-refactor-add-additional-paths-to-config-cache.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 (44) hide show
  1. package/bin/confirm-install.d.ts +34 -0
  2. package/bin/context.d.ts +3 -0
  3. package/bin/integrations/index.d.ts +11 -3
  4. package/bin/integrations/types.d.ts +14 -2
  5. package/bin/main.cjs +197 -95
  6. package/bin/main.js +151 -49
  7. package/cache/resolve-dynamic-config-cache.cjs +64 -16
  8. package/cache/resolve-dynamic-config-cache.d.ts +20 -1
  9. package/cache/resolve-dynamic-config-cache.js +65 -17
  10. package/{class-name-generator-YeSQe_Ik.js → class-name-generator-CMWY5KTJ.js} +1 -1
  11. package/{class-name-generator-B2Pb2obX.cjs → class-name-generator-DB5aQwC_.cjs} +1 -1
  12. package/compiler/copy-config-cache.cjs +39 -0
  13. package/compiler/copy-config-cache.d.ts +17 -0
  14. package/compiler/copy-config-cache.js +39 -0
  15. package/compiler/salty-compiler.cjs +22 -21
  16. package/compiler/salty-compiler.d.ts +11 -1
  17. package/compiler/salty-compiler.js +20 -19
  18. package/css/dynamic-styles.cjs +21 -8
  19. package/css/dynamic-styles.d.ts +39 -0
  20. package/css/dynamic-styles.js +22 -9
  21. package/css/index.cjs +1 -0
  22. package/css/index.js +2 -1
  23. package/css/keyframes.cjs +1 -1
  24. package/css/keyframes.js +1 -1
  25. package/generators/index.cjs +1 -1
  26. package/generators/index.js +2 -2
  27. package/instances/classname-instance.cjs +1 -1
  28. package/instances/classname-instance.js +1 -1
  29. package/logger-7xz0pyAz.cjs +12 -0
  30. package/logger-hHmCwThj.js +13 -0
  31. package/package.json +5 -1
  32. package/{parse-styles-CA3TP5n1.cjs → parse-styles-C54MOrPg.cjs} +106 -7
  33. package/{parse-styles-BTIoYnBr.js → parse-styles-CLMTHo2H.js} +107 -8
  34. package/parsers/index.cjs +2 -1
  35. package/parsers/index.d.ts +1 -0
  36. package/parsers/index.js +4 -3
  37. package/parsers/parser-regexes.d.ts +3 -0
  38. package/parsers/strict.d.ts +2 -0
  39. package/runtime/index.cjs +1 -1
  40. package/runtime/index.js +1 -1
  41. package/{salty.config-cqavVm2t.cjs → salty.config-DogY_sSQ.cjs} +1 -1
  42. package/salty.config-GV37Q-D2.js +4 -0
  43. package/types/config-types.d.ts +9 -0
  44. package/salty.config-DjosWdPw.js +0 -4
package/bin/main.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import { Command } from "commander";
2
2
  import { existsSync, watch } from "fs";
3
- import { l as logger, a as logError, SaltyCompiler } from "../compiler/salty-compiler.js";
3
+ import { SaltyCompiler } from "../compiler/salty-compiler.js";
4
4
  import { isSaltyFile } from "../compiler/helpers.js";
5
5
  import { c as checkShouldRestart } from "../should-restart-CXIO0jxY.js";
6
6
  import { join, relative, parse, format } from "path";
7
7
  import { readFile, writeFile, mkdir } from "fs/promises";
8
8
  import { exec } from "child_process";
9
9
  import ora from "ora";
10
+ import { l as logger, a as logError } from "../logger-hHmCwThj.js";
10
11
  import { p as pascalCase } from "../pascal-case-F3Usi5Wf.js";
11
12
  import ejs from "ejs";
13
+ import { createInterface } from "readline/promises";
12
14
  const defaultPackageJsonPath = join(process.cwd(), "package.json");
13
15
  const readPackageJson = async (filePath = defaultPackageJsonPath) => {
14
16
  const content = await readFile(filePath, "utf-8").then(JSON.parse).catch(() => void 0);
@@ -138,17 +140,21 @@ const buildContext = async (opts) => {
138
140
  packageJson,
139
141
  rcFile,
140
142
  cliVersion: cliPackageJson.version || "0.0.0",
141
- skipInstall: !!opts.skipInstall
143
+ skipInstall: !!opts.skipInstall,
144
+ yes: !!opts.yes
142
145
  };
143
146
  };
144
147
  const registerBuildCommand = (program, defaultProject) => {
145
- program.command("build [directory]").alias("b").description("Build the Salty-CSS project.").option("-d, --dir <dir>", "Project directory to build the project in.").option("--watch", "Watch for changes and rebuild the project.").action(async function(_dir = defaultProject) {
148
+ program.command("build [directory]").alias("b").description("Build the Salty-CSS project.").option("-d, --dir <dir>", "Project directory to build the project in.").option("--watch", "Watch for changes and rebuild the project.").option("--mode <mode>", 'Build mode: "production" or "development". Defaults to NODE_ENV-based detection.').action(async function(_dir = defaultProject) {
146
149
  logger.info("Building the Salty-CSS project...");
147
- const { dir = _dir, watch: watch$1 } = this.opts();
150
+ const { dir = _dir, watch: watch$1, mode } = this.opts();
151
+ if (mode !== void 0 && mode !== "production" && mode !== "development") {
152
+ return logError(`Invalid --mode "${mode}". Expected "production" or "development".`);
153
+ }
148
154
  const resolved = dir ?? await getDefaultProject();
149
155
  if (!resolved) return logError("Project directory must be provided. Add it as the first argument after build command or use the --dir option.");
150
156
  const projectDir = resolveProjectDir(resolved);
151
- const compiler = new SaltyCompiler(projectDir);
157
+ const compiler = new SaltyCompiler(projectDir, { mode });
152
158
  await compiler.generateCss();
153
159
  if (watch$1) {
154
160
  logger.info("Watching for changes in the project directory...");
@@ -222,7 +228,7 @@ const getFramework = (name) => {
222
228
  return frameworksByName[name];
223
229
  };
224
230
  const templateLoaders = {
225
- "salty.config.ts": () => import("../salty.config-DjosWdPw.js"),
231
+ "salty.config.ts": () => import("../salty.config-GV37Q-D2.js"),
226
232
  "saltygen/index.css": () => import("../index-DKz1QXqs.js"),
227
233
  "react/styled-file.ts": () => import("../styled-file-Cda3EeR6.js"),
228
234
  "react/vanilla-file.ts": () => import("../vanilla-file-1kOqbCIM.js"),
@@ -290,6 +296,52 @@ const registerGenerateCommand = (program, defaultProject) => {
290
296
  await formatWithPrettier(formattedStyledFilePath);
291
297
  });
292
298
  };
299
+ const formatPackageForDisplay = (spec) => {
300
+ const trimmed = spec.trim();
301
+ if (trimmed.startsWith("-D ")) return `${trimmed.slice(3).trim()} (dev)`;
302
+ return trimmed;
303
+ };
304
+ const renderPackageList = (packages) => {
305
+ return packages.map((p) => ` + ${formatPackageForDisplay(p)}`).join("\n");
306
+ };
307
+ const confirmInstall = async (packages, yes, options = {}) => {
308
+ if (yes) return;
309
+ if (packages.length === 0) return;
310
+ const input = options.input ?? process.stdin;
311
+ const output = options.output ?? process.stdout;
312
+ const isTTY = options.isTTY ?? process.stdin.isTTY ?? false;
313
+ if (!isTTY) {
314
+ throw new Error("Cannot prompt for install confirmation: stdin is not a TTY. Re-run with --yes to install the listed packages without prompting.");
315
+ }
316
+ output.write(`The following packages will be installed:
317
+ ${renderPackageList(packages)}
318
+ `);
319
+ const rl = createInterface({ input, output, terminal: false });
320
+ try {
321
+ const answer = (await rl.question("Proceed? (y/N) ")).trim().toLowerCase();
322
+ if (answer !== "y" && answer !== "yes") {
323
+ throw new Error("Install cancelled by user.");
324
+ }
325
+ } finally {
326
+ rl.close();
327
+ }
328
+ };
329
+ const confirmYesNo = async (question, options = {}) => {
330
+ if (options.yes) return true;
331
+ const input = options.input ?? process.stdin;
332
+ const output = options.output ?? process.stdout;
333
+ const isTTY = options.isTTY ?? process.stdin.isTTY ?? false;
334
+ if (!isTTY) return false;
335
+ const suffix = options.defaultYes ? "(Y/n)" : "(y/N)";
336
+ const rl = createInterface({ input, output, terminal: false });
337
+ try {
338
+ const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
339
+ if (answer === "") return !!options.defaultYes;
340
+ return answer === "y" || answer === "yes";
341
+ } finally {
342
+ rl.close();
343
+ }
344
+ };
293
345
  const CSS_FILE_FOLDERS = ["src", "public", "assets", "styles", "css", "app"];
294
346
  const CSS_SECOND_LEVEL_FOLDERS = ["styles", "css", "app", "pages"];
295
347
  const CSS_FILE_NAMES = ["index", "styles", "main", "app", "global", "globals"];
@@ -336,17 +388,22 @@ const editAstroConfig = (existing) => {
336
388
  const astroIntegration = {
337
389
  name: "astro",
338
390
  detect: (ctx) => findAstroConfig(ctx.projectDir),
339
- apply: async (ctx, configPath) => {
391
+ plan: async (ctx, configPath) => {
340
392
  const existing = await readFile(configPath, "utf-8").catch(() => void 0);
341
- if (existing === void 0) return { changed: false };
393
+ if (existing === void 0) return null;
342
394
  const result = editAstroConfig(existing);
343
395
  if (result.warning) logger.warn(result.warning);
344
- if (result.content === null) return { changed: false };
345
- if (!ctx.skipInstall) await npmInstall(`-D ${astroPackage(ctx.cliVersion)}`);
346
- logger.info("Adding Salty-CSS integration to Astro config: " + configPath);
347
- await writeFile(configPath, result.content);
348
- await formatWithPrettier(configPath);
349
- return { changed: true };
396
+ if (result.content === null) return null;
397
+ const newContent = result.content;
398
+ return {
399
+ packages: [`-D ${astroPackage(ctx.cliVersion)}`],
400
+ execute: async () => {
401
+ logger.info("Adding Salty-CSS integration to Astro config: " + configPath);
402
+ await writeFile(configPath, newContent);
403
+ await formatWithPrettier(configPath);
404
+ return { changed: true };
405
+ }
406
+ };
350
407
  }
351
408
  };
352
409
  const ESLINT_CONFIG_CANDIDATES = [
@@ -405,20 +462,25 @@ const eslintIntegration = {
405
462
  const candidates = eslintConfigCandidates(ctx.projectDir, ctx.cwd);
406
463
  return candidates.find((p) => existsSync(p)) ?? null;
407
464
  },
408
- apply: async (ctx, configPath) => {
465
+ plan: async (ctx, configPath) => {
409
466
  const existing = await readFile(configPath, "utf-8").catch(() => void 0);
410
467
  if (existing === void 0) {
411
468
  logger.error("Could not read ESLint config file.");
412
- return { changed: false };
469
+ return null;
413
470
  }
414
- if (!ctx.skipInstall) await npmInstall(corePackages.eslintConfigCore(ctx.cliVersion));
415
471
  const result = editEslintConfig(existing, configPath.endsWith("js"));
416
472
  if (result.warning) logger.warn(result.warning);
417
- if (result.content === null) return { changed: false };
418
- logger.info("Edit file: " + configPath);
419
- await writeFile(configPath, result.content);
420
- await formatWithPrettier(configPath);
421
- return { changed: true };
473
+ if (result.content === null) return null;
474
+ const newContent = result.content;
475
+ return {
476
+ packages: [corePackages.eslintConfigCore(ctx.cliVersion)],
477
+ execute: async () => {
478
+ logger.info("Edit file: " + configPath);
479
+ await writeFile(configPath, newContent);
480
+ await formatWithPrettier(configPath);
481
+ return { changed: true };
482
+ }
483
+ };
422
484
  }
423
485
  };
424
486
  const nextConfigFiles = ["next.config.js", "next.config.cjs", "next.config.ts", "next.config.mjs"];
@@ -448,16 +510,20 @@ const nextIntegration = {
448
510
  const found = nextConfigFiles.map((file) => join(ctx.projectDir, file)).find((p) => existsSync(p));
449
511
  return found ?? null;
450
512
  },
451
- apply: async (ctx, configPath) => {
513
+ plan: async (ctx, configPath) => {
452
514
  const existing = await readFile(configPath, "utf-8").catch(() => void 0);
453
- if (existing === void 0) return { changed: false };
515
+ if (existing === void 0) return null;
454
516
  const { content } = editNextConfig(existing);
455
- if (content === null) return { changed: false };
456
- if (!ctx.skipInstall) await npmInstall(`-D ${nextPackage(ctx.cliVersion)}`);
457
- logger.info("Adding Salty-CSS plugin to Next.js config...");
458
- await writeFile(configPath, content);
459
- await formatWithPrettier(configPath);
460
- return { changed: true };
517
+ if (content === null) return null;
518
+ return {
519
+ packages: [`-D ${nextPackage(ctx.cliVersion)}`],
520
+ execute: async () => {
521
+ logger.info("Adding Salty-CSS plugin to Next.js config...");
522
+ await writeFile(configPath, content);
523
+ await formatWithPrettier(configPath);
524
+ return { changed: true };
525
+ }
526
+ };
461
527
  }
462
528
  };
463
529
  const vitePackage = (version) => `@salty-css/vite@${version}`;
@@ -475,27 +541,40 @@ const viteIntegration = {
475
541
  const path = join(ctx.projectDir, "vite.config.ts");
476
542
  return existsSync(path) ? path : null;
477
543
  },
478
- apply: async (ctx, configPath) => {
544
+ plan: async (ctx, configPath) => {
479
545
  const existing = await readFile(configPath, "utf-8").catch(() => void 0);
480
- if (existing === void 0) return { changed: false };
546
+ if (existing === void 0) return null;
481
547
  const { content } = editViteConfig(existing);
482
- if (content === null) return { changed: false };
483
- logger.info("Edit file: " + configPath);
484
- if (!ctx.skipInstall) await npmInstall(`-D ${vitePackage(ctx.cliVersion)}`);
485
- logger.info("Adding Salty-CSS plugin to Vite config...");
486
- await writeFile(configPath, content);
487
- await formatWithPrettier(configPath);
488
- return { changed: true };
548
+ if (content === null) return null;
549
+ return {
550
+ packages: [`-D ${vitePackage(ctx.cliVersion)}`],
551
+ execute: async () => {
552
+ logger.info("Edit file: " + configPath);
553
+ logger.info("Adding Salty-CSS plugin to Vite config...");
554
+ await writeFile(configPath, content);
555
+ await formatWithPrettier(configPath);
556
+ return { changed: true };
557
+ }
558
+ };
489
559
  }
490
560
  };
491
561
  const buildIntegrationRegistry = [eslintIntegration, viteIntegration, nextIntegration, astroIntegration];
492
- const detectAndApplyIntegrations = async (ctx) => {
493
- const results = [];
562
+ const planIntegrations = async (ctx) => {
563
+ const planned = [];
494
564
  for (const integration of buildIntegrationRegistry) {
495
565
  const configPath = await integration.detect(ctx);
496
566
  if (!configPath) continue;
497
- const result = await integration.apply(ctx, configPath);
498
- results.push({ name: integration.name, configPath, changed: result.changed });
567
+ const plan = await integration.plan(ctx, configPath);
568
+ if (!plan) continue;
569
+ planned.push({ name: integration.name, configPath, plan });
570
+ }
571
+ return planned;
572
+ };
573
+ const applyIntegrationPlans = async (planned) => {
574
+ const results = [];
575
+ for (const { name, configPath, plan } of planned) {
576
+ const result = await plan.execute();
577
+ results.push({ name, configPath, changed: result.changed });
499
578
  }
500
579
  return results;
501
580
  };
@@ -546,17 +625,24 @@ const wirePrepareScript = async () => {
546
625
  await updatePackageJson(next);
547
626
  };
548
627
  const registerInitCommand = (program) => {
549
- program.command("init [directory]").description("Initialize a new Salty-CSS project.").option("-d, --dir <dir>", "Project directory to initialize the project in.").option("--css-file <css-file>", "Existing CSS file where to import the generated CSS. Path must be relative to the given project directory.").option("--skip-install", "Skip installing dependencies.").action(async function(_dir = ".") {
628
+ program.command("init [directory]").description("Initialize a new Salty-CSS project.").option("-d, --dir <dir>", "Project directory to initialize the project in.").option("--css-file <css-file>", "Existing CSS file where to import the generated CSS. Path must be relative to the given project directory.").option("--skip-install", "Skip installing dependencies.").option("-y, --yes", "Skip the install confirmation prompt.").action(async function(_dir = ".") {
550
629
  try {
551
630
  const opts = this.opts();
552
631
  const dir = opts.dir ?? _dir;
553
632
  if (!dir) return logError("Project directory must be provided. Add it as the first argument after init command or use the --dir option.");
554
- const ctx = await buildContext({ dir, skipInstall: opts.skipInstall });
633
+ const ctx = await buildContext({ dir, skipInstall: opts.skipInstall, yes: opts.yes });
555
634
  logger.info("Initializing a new Salty-CSS project!");
556
635
  const framework = await detectFramework(ctx);
557
636
  logger.info(`Detected framework: ${framework.name}`);
637
+ const plannedIntegrations = await planIntegrations(ctx);
558
638
  if (!ctx.skipInstall) {
559
- await npmInstall(corePackages.core(ctx.cliVersion), framework.runtimePackage(ctx.cliVersion));
639
+ const packages = [
640
+ corePackages.core(ctx.cliVersion),
641
+ framework.runtimePackage(ctx.cliVersion),
642
+ ...plannedIntegrations.flatMap((p) => p.plan.packages)
643
+ ];
644
+ await confirmInstall(packages, ctx.yes);
645
+ await npmInstall(...packages);
560
646
  }
561
647
  const projectFiles = await Promise.all([readTemplate("salty.config.ts"), readTemplate("saltygen/index.css")]);
562
648
  await mkdir(ctx.projectDir, { recursive: true });
@@ -564,7 +650,7 @@ const registerInitCommand = (program) => {
564
650
  await writeProjectToRc(ctx.cwd, ctx.relativeProjectPath, framework);
565
651
  await ensureGitignoreSaltygen(ctx.cwd);
566
652
  await importSaltygenIntoCss(ctx.projectDir, opts.cssFile);
567
- await detectAndApplyIntegrations(ctx);
653
+ await applyIntegrationPlans(plannedIntegrations);
568
654
  await wirePrepareScript();
569
655
  logger.info("Running the build to generate initial CSS...");
570
656
  const compiler = new SaltyCompiler(ctx.projectDir);
@@ -595,8 +681,8 @@ const getSaltyCssPackages = async () => {
595
681
  return saltyCssPackages;
596
682
  };
597
683
  const registerUpdateCommand = (program) => {
598
- program.command("update [version]").alias("up").description("Update Salty-CSS packages to the latest or specified version.").option("-v, --version <version>", "Version to update to.").option("--legacy-peer-deps <legacyPeerDeps>", "Use legacy peer dependencies (not recommended).", false).action(async function(_version = "latest") {
599
- const { legacyPeerDeps, version = _version } = this.opts();
684
+ program.command("update [version]").alias("up").description("Update Salty-CSS packages to the latest or specified version.").option("-v, --version <version>", "Version to update to.").option("--legacy-peer-deps <legacyPeerDeps>", "Use legacy peer dependencies (not recommended).", false).option("-y, --yes", "Skip confirmation prompts (install and rebuild).").option("-d, --dir <dir>", "Project directory to rebuild after updating.").action(async function(_version = "latest") {
685
+ const { legacyPeerDeps, version = _version, yes = false, dir } = this.opts();
600
686
  const saltyCssPackages = await getSaltyCssPackages();
601
687
  if (!saltyCssPackages) return logError("Could not update Salty-CSS packages as any were found in package.json.");
602
688
  const cli = await readThisPackageJson();
@@ -604,6 +690,11 @@ const registerUpdateCommand = (program) => {
604
690
  if (version === "@") return `${name}@${cli.version}`;
605
691
  return `${name}@${version.replace(/^@/, "")}`;
606
692
  });
693
+ try {
694
+ await confirmInstall(packagesToUpdate, yes);
695
+ } catch (err) {
696
+ return logError(err instanceof Error ? err.message : String(err));
697
+ }
607
698
  if (legacyPeerDeps) {
608
699
  logger.warn("Using legacy peer dependencies to update packages.");
609
700
  await npmInstall(...packagesToUpdate, "--legacy-peer-deps");
@@ -626,6 +717,17 @@ const registerUpdateCommand = (program) => {
626
717
  logger.info(`Updated to ${v.replace(/^\^/, "")}: ${names.join(", ")}`);
627
718
  }
628
719
  }
720
+ const project = dir ?? await getDefaultProject();
721
+ if (!project) {
722
+ logger.warn("Skipping rebuild: no project directory configured. Run `salty-css build [dir]` manually.");
723
+ return;
724
+ }
725
+ const shouldRebuild = await confirmYesNo("Rebuild Salty CSS now?", { yes });
726
+ if (!shouldRebuild) return;
727
+ const projectDir = resolveProjectDir(project);
728
+ logger.info("Rebuilding Salty-CSS project...");
729
+ await new SaltyCompiler(projectDir).generateCss();
730
+ logger.info("Rebuild complete.");
629
731
  });
630
732
  };
631
733
  const registerVersionOption = (program) => {
@@ -2,23 +2,71 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const promises = require("fs/promises");
4
4
  const path = require("path");
5
- const resolveDynamicConfigCache = async () => {
6
- const currentDir = process.cwd();
7
- const filename = "config-cache.json";
8
- const patterns = ["", "saltygen", "src", "src/saltygen", "cache", "src/cache", "saltygen/cache", "src/saltygen/cache"];
9
- let contents = "";
10
- for (const pattern of patterns) {
11
- const potentialPath = path.join(currentDir, pattern, filename);
12
- try {
13
- contents = await promises.readFile(potentialPath, "utf8");
14
- break;
15
- } catch {
16
- }
5
+ const CACHE_FILENAME = "config-cache.json";
6
+ const ENV_VAR = "SALTY_CONFIG_CACHE_PATH";
7
+ const defaultPatterns = [
8
+ "",
9
+ "saltygen",
10
+ "src",
11
+ "src/saltygen",
12
+ "cache",
13
+ "src/cache",
14
+ "saltygen/cache",
15
+ "src/saltygen/cache",
16
+ "dist",
17
+ "dist/cache",
18
+ "dist/saltygen/cache",
19
+ "build",
20
+ "build/cache",
21
+ "build/saltygen/cache",
22
+ "public/saltygen/cache",
23
+ ".next",
24
+ ".next/server",
25
+ ".next/server/cache",
26
+ ".vercel/output/functions"
27
+ ];
28
+ const memo = /* @__PURE__ */ new Map();
29
+ let warned = false;
30
+ const toAbsolute = (p, cwd) => path.isAbsolute(p) ? p : path.join(cwd, p);
31
+ const candidatePathsFrom = (entry, cwd) => {
32
+ const abs = toAbsolute(entry, cwd);
33
+ if (abs.endsWith(".json")) return [abs];
34
+ return [path.join(abs, CACHE_FILENAME), path.join(abs, "cache", CACHE_FILENAME), path.join(abs, "saltygen", "cache", CACHE_FILENAME)];
35
+ };
36
+ const tryRead = async (path2) => {
37
+ if (memo.has(path2)) return memo.get(path2);
38
+ try {
39
+ const contents = await promises.readFile(path2, "utf8");
40
+ if (!contents) return void 0;
41
+ const parsed = JSON.parse(contents);
42
+ memo.set(path2, parsed);
43
+ return parsed;
44
+ } catch {
45
+ return void 0;
46
+ }
47
+ };
48
+ const resolveDynamicConfigCache = async (options = {}) => {
49
+ var _a;
50
+ const cwd = options.cwd ?? process.cwd();
51
+ const envPath = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[ENV_VAR] : void 0;
52
+ const ordered = [];
53
+ if (options.primaryPath) ordered.push(...candidatePathsFrom(options.primaryPath, cwd));
54
+ if (envPath) ordered.push(...candidatePathsFrom(envPath, cwd));
55
+ if (options.extraPaths) for (const p of options.extraPaths) ordered.push(...candidatePathsFrom(p, cwd));
56
+ for (const pattern of defaultPatterns) ordered.push(path.join(cwd, pattern, CACHE_FILENAME));
57
+ for (const candidate of ordered) {
58
+ const result = await tryRead(candidate);
59
+ if (result) return result;
17
60
  }
18
- if (!contents) {
19
- console.warn(`Could not find config cache file (${filename}) in any of the expected locations.`);
20
- return {};
61
+ if (!warned) {
62
+ warned = true;
63
+ console.warn(`Could not find config cache file (${CACHE_FILENAME}) in any of the expected locations.`);
21
64
  }
22
- return JSON.parse(contents);
65
+ return {};
66
+ };
67
+ const _resetDynamicConfigCacheMemo = () => {
68
+ memo.clear();
69
+ warned = false;
23
70
  };
71
+ exports._resetDynamicConfigCacheMemo = _resetDynamicConfigCacheMemo;
24
72
  exports.resolveDynamicConfigCache = resolveDynamicConfigCache;
@@ -1 +1,20 @@
1
- export declare const resolveDynamicConfigCache: () => Promise<any>;
1
+ export interface ResolveDynamicConfigCacheOptions {
2
+ /**
3
+ * Highest-priority path checked first. If it resolves, no other paths are tried.
4
+ * Absolute, or relative to `cwd`.
5
+ */
6
+ primaryPath?: string;
7
+ /**
8
+ * Extra paths checked before the built-in defaults. Absolute, or relative to `cwd`.
9
+ */
10
+ extraPaths?: string[];
11
+ /**
12
+ * Base directory for resolving relative paths. Defaults to `process.cwd()`.
13
+ */
14
+ cwd?: string;
15
+ }
16
+ export declare const resolveDynamicConfigCache: (options?: ResolveDynamicConfigCacheOptions) => Promise<Record<string, unknown>>;
17
+ /**
18
+ * Clear the in-memory cache of parsed config-cache.json files. Test-only.
19
+ */
20
+ export declare const _resetDynamicConfigCacheMemo: () => void;
@@ -1,24 +1,72 @@
1
1
  import { readFile } from "fs/promises";
2
- import { join } from "path";
3
- const resolveDynamicConfigCache = async () => {
4
- const currentDir = process.cwd();
5
- const filename = "config-cache.json";
6
- const patterns = ["", "saltygen", "src", "src/saltygen", "cache", "src/cache", "saltygen/cache", "src/saltygen/cache"];
7
- let contents = "";
8
- for (const pattern of patterns) {
9
- const potentialPath = join(currentDir, pattern, filename);
10
- try {
11
- contents = await readFile(potentialPath, "utf8");
12
- break;
13
- } catch {
14
- }
2
+ import { join, isAbsolute } from "path";
3
+ const CACHE_FILENAME = "config-cache.json";
4
+ const ENV_VAR = "SALTY_CONFIG_CACHE_PATH";
5
+ const defaultPatterns = [
6
+ "",
7
+ "saltygen",
8
+ "src",
9
+ "src/saltygen",
10
+ "cache",
11
+ "src/cache",
12
+ "saltygen/cache",
13
+ "src/saltygen/cache",
14
+ "dist",
15
+ "dist/cache",
16
+ "dist/saltygen/cache",
17
+ "build",
18
+ "build/cache",
19
+ "build/saltygen/cache",
20
+ "public/saltygen/cache",
21
+ ".next",
22
+ ".next/server",
23
+ ".next/server/cache",
24
+ ".vercel/output/functions"
25
+ ];
26
+ const memo = /* @__PURE__ */ new Map();
27
+ let warned = false;
28
+ const toAbsolute = (p, cwd) => isAbsolute(p) ? p : join(cwd, p);
29
+ const candidatePathsFrom = (entry, cwd) => {
30
+ const abs = toAbsolute(entry, cwd);
31
+ if (abs.endsWith(".json")) return [abs];
32
+ return [join(abs, CACHE_FILENAME), join(abs, "cache", CACHE_FILENAME), join(abs, "saltygen", "cache", CACHE_FILENAME)];
33
+ };
34
+ const tryRead = async (path) => {
35
+ if (memo.has(path)) return memo.get(path);
36
+ try {
37
+ const contents = await readFile(path, "utf8");
38
+ if (!contents) return void 0;
39
+ const parsed = JSON.parse(contents);
40
+ memo.set(path, parsed);
41
+ return parsed;
42
+ } catch {
43
+ return void 0;
44
+ }
45
+ };
46
+ const resolveDynamicConfigCache = async (options = {}) => {
47
+ var _a;
48
+ const cwd = options.cwd ?? process.cwd();
49
+ const envPath = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[ENV_VAR] : void 0;
50
+ const ordered = [];
51
+ if (options.primaryPath) ordered.push(...candidatePathsFrom(options.primaryPath, cwd));
52
+ if (envPath) ordered.push(...candidatePathsFrom(envPath, cwd));
53
+ if (options.extraPaths) for (const p of options.extraPaths) ordered.push(...candidatePathsFrom(p, cwd));
54
+ for (const pattern of defaultPatterns) ordered.push(join(cwd, pattern, CACHE_FILENAME));
55
+ for (const candidate of ordered) {
56
+ const result = await tryRead(candidate);
57
+ if (result) return result;
15
58
  }
16
- if (!contents) {
17
- console.warn(`Could not find config cache file (${filename}) in any of the expected locations.`);
18
- return {};
59
+ if (!warned) {
60
+ warned = true;
61
+ console.warn(`Could not find config cache file (${CACHE_FILENAME}) in any of the expected locations.`);
19
62
  }
20
- return JSON.parse(contents);
63
+ return {};
64
+ };
65
+ const _resetDynamicConfigCacheMemo = () => {
66
+ memo.clear();
67
+ warned = false;
21
68
  };
22
69
  export {
70
+ _resetDynamicConfigCacheMemo,
23
71
  resolveDynamicConfigCache
24
72
  };
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { p as parseAndJoinStyles } from "./parse-styles-BTIoYnBr.js";
4
+ import { p as parseAndJoinStyles } from "./parse-styles-CLMTHo2H.js";
5
5
  import { d as dashCase } from "./dash-case-DblXvymC.js";
6
6
  import { t as toHash } from "./to-hash-DAN2LcHK.js";
7
7
  class StylesGenerator {
@@ -2,7 +2,7 @@
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
- const parseStyles = require("./parse-styles-CA3TP5n1.cjs");
5
+ const parseStyles = require("./parse-styles-C54MOrPg.cjs");
6
6
  const dashCase = require("./dash-case-DIwKaYgE.cjs");
7
7
  const toHash = require("./to-hash-C05Y906F.cjs");
8
8
  class StylesGenerator {
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const promises = require("fs/promises");
4
+ const path = require("path");
5
+ const logger = require("../logger-7xz0pyAz.cjs");
6
+ const DEFAULT_SUBPATH = path.join("saltygen", "cache", "config-cache.json");
7
+ const resolveCopyConfigCacheDestinations = (option, defaultOutDir, cwd = process.cwd()) => {
8
+ if (option === false) return [];
9
+ const userPaths = [];
10
+ if (typeof option === "string") userPaths.push(option);
11
+ else if (Array.isArray(option)) userPaths.push(...option);
12
+ const destinations = [];
13
+ const useDefault = option === void 0 || option === true;
14
+ if (useDefault && defaultOutDir) destinations.push(defaultOutDir);
15
+ destinations.push(...userPaths);
16
+ return destinations.map((entry) => {
17
+ const abs = path.isAbsolute(entry) ? entry : path.resolve(cwd, entry);
18
+ if (abs.endsWith(".json")) return abs;
19
+ return path.join(abs, DEFAULT_SUBPATH);
20
+ });
21
+ };
22
+ const copyConfigCacheTo = async (compiler, destinations) => {
23
+ if (destinations.length === 0) return;
24
+ const source = await compiler.getConfigCachePath();
25
+ await Promise.all(
26
+ destinations.map(async (destination) => {
27
+ if (destination === source) return;
28
+ try {
29
+ await promises.mkdir(path.dirname(destination), { recursive: true });
30
+ await promises.copyFile(source, destination);
31
+ logger.logger.info(`Copied Salty config cache → ${destination}`);
32
+ } catch (error) {
33
+ logger.logger.warn(`Failed to copy Salty config cache to ${destination}: ${error.message}`);
34
+ }
35
+ })
36
+ );
37
+ };
38
+ exports.copyConfigCacheTo = copyConfigCacheTo;
39
+ exports.resolveCopyConfigCacheDestinations = resolveCopyConfigCacheDestinations;
@@ -0,0 +1,17 @@
1
+ import { SaltyCompiler } from './salty-compiler';
2
+ /**
3
+ * `copyConfigCache` plugin option:
4
+ * - `true` → copy to the bundler's resolved output dir (default).
5
+ * - `false` → no-op.
6
+ * - `string | string[]` → copy to the default destination PLUS each listed path.
7
+ *
8
+ * Each path can be a directory (the file is written as `saltygen/cache/config-cache.json` inside)
9
+ * or a path ending in `.json` (used verbatim).
10
+ */
11
+ export type CopyConfigCacheOption = boolean | string | string[];
12
+ export declare const resolveCopyConfigCacheDestinations: (option: CopyConfigCacheOption | undefined, defaultOutDir: string | undefined, cwd?: string) => string[];
13
+ /**
14
+ * Copy the compiler's `config-cache.json` to each absolute destination path.
15
+ * Creates parent directories as needed; silently skips destinations equal to the source.
16
+ */
17
+ export declare const copyConfigCacheTo: (compiler: SaltyCompiler, destinations: string[]) => Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { mkdir, copyFile } from "fs/promises";
2
+ import { join, dirname, isAbsolute, resolve } from "path";
3
+ import { l as logger } from "../logger-hHmCwThj.js";
4
+ const DEFAULT_SUBPATH = join("saltygen", "cache", "config-cache.json");
5
+ const resolveCopyConfigCacheDestinations = (option, defaultOutDir, cwd = process.cwd()) => {
6
+ if (option === false) return [];
7
+ const userPaths = [];
8
+ if (typeof option === "string") userPaths.push(option);
9
+ else if (Array.isArray(option)) userPaths.push(...option);
10
+ const destinations = [];
11
+ const useDefault = option === void 0 || option === true;
12
+ if (useDefault && defaultOutDir) destinations.push(defaultOutDir);
13
+ destinations.push(...userPaths);
14
+ return destinations.map((entry) => {
15
+ const abs = isAbsolute(entry) ? entry : resolve(cwd, entry);
16
+ if (abs.endsWith(".json")) return abs;
17
+ return join(abs, DEFAULT_SUBPATH);
18
+ });
19
+ };
20
+ const copyConfigCacheTo = async (compiler, destinations) => {
21
+ if (destinations.length === 0) return;
22
+ const source = await compiler.getConfigCachePath();
23
+ await Promise.all(
24
+ destinations.map(async (destination) => {
25
+ if (destination === source) return;
26
+ try {
27
+ await mkdir(dirname(destination), { recursive: true });
28
+ await copyFile(source, destination);
29
+ logger.info(`Copied Salty config cache → ${destination}`);
30
+ } catch (error) {
31
+ logger.warn(`Failed to copy Salty config cache to ${destination}: ${error.message}`);
32
+ }
33
+ })
34
+ );
35
+ };
36
+ export {
37
+ copyConfigCacheTo,
38
+ resolveCopyConfigCacheDestinations
39
+ };