@salty-css/core 0.1.0-feat-define-font.0 → 0.1.0-refactor-add-additional-paths-to-config-cache.1

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 (42) hide show
  1. package/README.md +0 -83
  2. package/bin/main.cjs +58 -57
  3. package/bin/main.js +2 -1
  4. package/cache/resolve-dynamic-config-cache.cjs +55 -15
  5. package/cache/resolve-dynamic-config-cache.d.ts +20 -1
  6. package/cache/resolve-dynamic-config-cache.js +56 -16
  7. package/{class-name-generator-B2LriwKm.js → class-name-generator-CMWY5KTJ.js} +1 -1
  8. package/{class-name-generator-BIYysuhW.cjs → class-name-generator-DB5aQwC_.cjs} +1 -1
  9. package/compiler/copy-config-cache.cjs +39 -0
  10. package/compiler/copy-config-cache.d.ts +17 -0
  11. package/compiler/copy-config-cache.js +39 -0
  12. package/compiler/salty-compiler.cjs +21 -41
  13. package/compiler/salty-compiler.d.ts +5 -1
  14. package/compiler/salty-compiler.js +19 -39
  15. package/config/index.cjs +0 -2
  16. package/config/index.js +1 -3
  17. package/css/dynamic-styles.cjs +21 -8
  18. package/css/dynamic-styles.d.ts +39 -0
  19. package/css/dynamic-styles.js +22 -9
  20. package/css/index.cjs +1 -0
  21. package/css/index.js +2 -1
  22. package/css/keyframes.cjs +1 -1
  23. package/css/keyframes.js +1 -1
  24. package/factories/index.cjs +0 -125
  25. package/factories/index.d.ts +0 -1
  26. package/factories/index.js +0 -125
  27. package/generators/index.cjs +1 -1
  28. package/generators/index.js +2 -2
  29. package/instances/classname-instance.cjs +1 -1
  30. package/instances/classname-instance.js +1 -1
  31. package/logger-7xz0pyAz.cjs +12 -0
  32. package/logger-hHmCwThj.js +13 -0
  33. package/package.json +5 -1
  34. package/{parse-styles-jPtMfgXH.cjs → parse-styles-C54MOrPg.cjs} +0 -1
  35. package/{parse-styles--vHKY6Mw.js → parse-styles-CLMTHo2H.js} +0 -1
  36. package/parsers/index.cjs +1 -1
  37. package/parsers/index.js +2 -2
  38. package/runtime/index.cjs +1 -1
  39. package/runtime/index.js +1 -1
  40. package/types/config-types.d.ts +1 -1
  41. package/factories/define-font.d.ts +0 -28
  42. package/types/font-types.d.ts +0 -53
package/README.md CHANGED
@@ -57,7 +57,6 @@ To get help with problems, [Join Salty CSS Discord server](https://discord.gg/R6
57
57
  - [defineVariables](#variables) - create CSS variables (tokens) that can be used in any styling function
58
58
  - [defineMediaQuery](#media-queries) - create CSS media queries and use them in any styling function
59
59
  - [defineTemplates](#templates) - create reusable templates that can be applied when same styles are used over and over again
60
- - [defineFont](#custom-fonts) - register custom fonts via `@font-face` (or a remote stylesheet) and expose them as a CSS variable
61
60
  - [keyframes](#keyframes-animations) - create CSS keyframes animation that can be used and imported in any styling function
62
61
 
63
62
  ### Styling helpers & utility
@@ -320,88 +319,6 @@ Example usage:
320
319
  styled('div', { base: { textStyle: 'headline.large', card: '20px' } });
321
320
  ```
322
321
 
323
- ## Custom fonts
324
-
325
- Register custom fonts that will be emitted as `@font-face` declarations and exposed as a CSS variable. Mirrors the developer experience of Next.js / Astro font loaders, but generated at build time alongside the rest of your Salty CSS output.
326
-
327
- The returned object stringifies to its `font-family` value and exposes helpers for explicit usage:
328
-
329
- - `Font.variable` → CSS variable name (e.g. `--font-inter`)
330
- - `Font.fontFamily` → final `font-family` string with fallbacks
331
- - `Font.className` → class that sets the variable + applies the font on a subtree
332
- - `Font.style` → object you can spread on a React `style` prop
333
-
334
- ```ts
335
- // /styles/fonts.css.ts
336
- import { defineFont } from '@salty-css/core/factories';
337
-
338
- // 1. Local or self-hosted @font-face sources.
339
- // URLs are passed through as-is (use a public-folder path or a CDN URL).
340
- export const Inter = defineFont({
341
- name: 'Inter', // CSS font-family value
342
- variable: '--font-inter', // Optional — accepts 'font-inter' too; if omitted we derive `--font-<name>-<hash>` from the inputs
343
- display: 'swap', // Optional default applied to variants without their own `display`
344
- fallback: ['system-ui', 'sans-serif'], // Optional family fallbacks appended after `name`
345
- variants: [
346
- {
347
- weight: 400,
348
- style: 'normal',
349
- // Shorthand: pass a string and the `format()` descriptor is auto-detected
350
- // from the file extension (woff2, woff, ttf, otf, eot, svg, ttc).
351
- src: '/fonts/inter-400.woff2',
352
- },
353
- {
354
- weight: 700,
355
- style: 'normal',
356
- // Multiple sources can be a string array — first entry is preferred;
357
- // the browser picks the first format it supports.
358
- src: ['/fonts/inter-700.woff2', '/fonts/inter-700.ttf'],
359
- },
360
- {
361
- weight: 400,
362
- style: 'italic',
363
- // Use the `{ url, format }` object form when the URL has no recognisable
364
- // extension (signed CDN URLs, query-only endpoints, etc.). You can also
365
- // mix strings and objects in the same array.
366
- src: ['/fonts/inter-400-italic.woff2', { url: 'https://cdn.example.com/inter-italic', format: 'woff' }],
367
- },
368
- ],
369
- });
370
-
371
- // 2. Remote stylesheet (Google Fonts, etc.). Emits `@import url(...)` and still
372
- // registers the CSS variable so usage stays the same as the @font-face flow.
373
- export const InterCdn = defineFont({
374
- name: 'Inter',
375
- variable: '--font-inter',
376
- import: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
377
- });
378
- ```
379
-
380
- Example usage:
381
-
382
- ```tsx
383
- import { Inter } from './fonts.css';
384
- import { styled } from '@salty-css/react/styled';
385
-
386
- // Apply the font globally by attaching its className high up in the tree.
387
- // This sets `--font-inter` on the subtree and applies `font-family: var(--font-inter)`.
388
- export const App = ({ children }) => <div className={Inter.className}>{children}</div>;
389
-
390
- // `Inter` stringifies to its font-family value (with fallbacks), so it can be used directly.
391
- export const Heading = styled('h1', {
392
- base: {
393
- fontFamily: `${Inter}`,
394
- },
395
- });
396
-
397
- // Or reference the CSS variable explicitly.
398
- export const Body = styled('p', {
399
- base: {
400
- fontFamily: `var(${Inter.variable})`,
401
- },
402
- });
403
- ```
404
-
405
322
  ## Keyframes animations
406
323
 
407
324
  ```ts
package/bin/main.cjs CHANGED
@@ -9,6 +9,7 @@ const path = require("path");
9
9
  const promises = require("fs/promises");
10
10
  const child_process = require("child_process");
11
11
  const ora = require("ora");
12
+ const logger = require("../logger-7xz0pyAz.cjs");
12
13
  const pascalCase = require("../pascal-case-By_l58S-.cjs");
13
14
  const ejs = require("ejs");
14
15
  const promises$1 = require("readline/promises");
@@ -65,9 +66,9 @@ async function formatWithPrettier(filePath) {
65
66
  const hasPrettier = hasPrettierInstalled();
66
67
  if (!hasPrettier) return;
67
68
  await execAsync(`./node_modules/.bin/prettier --write "${filePath}"`);
68
- compiler_saltyCompiler.logger.info(`Formatted ${filePath} with Prettier`);
69
+ logger.logger.info(`Formatted ${filePath} with Prettier`);
69
70
  } catch (error) {
70
- compiler_saltyCompiler.logger.error(`Error formatting ${filePath} with Prettier:`, error);
71
+ logger.logger.error(`Error formatting ${filePath} with Prettier:`, error);
71
72
  }
72
73
  }
73
74
  const SALTYRC_FILENAME = ".saltyrc.json";
@@ -110,8 +111,8 @@ const writeProjectToRc = async (cwd, relativeProjectPath, framework) => {
110
111
  const existing = await readRawRc(cwd);
111
112
  const { content, changed, created } = upsertProjectInRc(existing, relativeProjectPath, framework);
112
113
  if (!changed) return false;
113
- if (created) compiler_saltyCompiler.logger.info("Creating file: " + path2);
114
- else compiler_saltyCompiler.logger.info("Edit file: " + path2);
114
+ if (created) logger.logger.info("Creating file: " + path2);
115
+ else logger.logger.info("Edit file: " + path2);
115
116
  await promises.writeFile(path2, content);
116
117
  await formatWithPrettier(path2);
117
118
  return true;
@@ -148,18 +149,18 @@ const buildContext = async (opts) => {
148
149
  };
149
150
  const registerBuildCommand = (program, defaultProject) => {
150
151
  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) {
151
- compiler_saltyCompiler.logger.info("Building the Salty-CSS project...");
152
+ logger.logger.info("Building the Salty-CSS project...");
152
153
  const { dir = _dir, watch, mode } = this.opts();
153
154
  if (mode !== void 0 && mode !== "production" && mode !== "development") {
154
- return compiler_saltyCompiler.logError(`Invalid --mode "${mode}". Expected "production" or "development".`);
155
+ return logger.logError(`Invalid --mode "${mode}". Expected "production" or "development".`);
155
156
  }
156
157
  const resolved = dir ?? await getDefaultProject();
157
- if (!resolved) return compiler_saltyCompiler.logError("Project directory must be provided. Add it as the first argument after build command or use the --dir option.");
158
+ if (!resolved) return logger.logError("Project directory must be provided. Add it as the first argument after build command or use the --dir option.");
158
159
  const projectDir = resolveProjectDir(resolved);
159
160
  const compiler = new compiler_saltyCompiler.SaltyCompiler(projectDir, { mode });
160
161
  await compiler.generateCss();
161
162
  if (watch) {
162
- compiler_saltyCompiler.logger.info("Watching for changes in the project directory...");
163
+ logger.logger.info("Watching for changes in the project directory...");
163
164
  fs.watch(projectDir, { recursive: true }, async (_event, filePath) => {
164
165
  const shouldRestart$1 = await shouldRestart.checkShouldRestart(filePath);
165
166
  if (shouldRestart$1) {
@@ -245,13 +246,13 @@ const readTemplate = async (key, options) => {
245
246
  const registerGenerateCommand = (program, defaultProject) => {
246
247
  program.command("generate [file] [directory]").alias("g").description("Generate a new component file.").option("-f, --file <file>", "File to generate.").option("-d, --dir <dir>", "Project directory to generate the file in.").option("-t, --tag <tag>", "HTML tag of the component.", "div").option("-n, --name <name>", "Name of the component.").option("-c, --className <className>", "CSS class of the component.").option("-r, --reactComponent", "Generate a wrapper component file alongside the styled definition.").action(async function(_file, _dir = defaultProject) {
247
248
  const { file = _file, dir = _dir, tag, name, className, reactComponent = false } = this.opts();
248
- if (!file) return compiler_saltyCompiler.logError("File to generate must be provided. Add it as the first argument after generate command or use the --file option.");
249
- if (!dir) return compiler_saltyCompiler.logError("Project directory must be provided. Add it as the second argument after generate command or use the --dir option.");
249
+ if (!file) return logger.logError("File to generate must be provided. Add it as the first argument after generate command or use the --file option.");
250
+ if (!dir) return logger.logError("Project directory must be provided. Add it as the second argument after generate command or use the --dir option.");
250
251
  let ctx;
251
252
  try {
252
253
  ctx = await buildContext({ dir, requirePackageJson: false });
253
254
  } catch (err) {
254
- return compiler_saltyCompiler.logError(err instanceof Error ? err.message : String(err));
255
+ return logger.logError(err instanceof Error ? err.message : String(err));
255
256
  }
256
257
  const rcFramework = getFramework(getProjectFramework(ctx.rcFile, ctx.relativeProjectPath));
257
258
  const framework = rcFramework ?? await detectFramework(ctx);
@@ -265,13 +266,13 @@ const registerGenerateCommand = (program, defaultProject) => {
265
266
  const formattedStyledFilePath = path.format(parsedFilePath);
266
267
  const alreadyExists = await promises.readFile(formattedStyledFilePath, "utf-8").catch(() => void 0);
267
268
  if (alreadyExists !== void 0) {
268
- compiler_saltyCompiler.logger.error("File already exists: " + formattedStyledFilePath);
269
+ logger.logger.error("File already exists: " + formattedStyledFilePath);
269
270
  return;
270
271
  }
271
272
  let styledComponentName = pascalCase.pascalCase(name || parsedFilePath.base.replace(/\.css\.\w+$/, ""));
272
273
  if (reactComponent) {
273
274
  if (!framework.templates.component) {
274
- return compiler_saltyCompiler.logError(`--reactComponent is not supported for the ${framework.name} framework.`);
275
+ return logger.logError(`--reactComponent is not supported for the ${framework.name} framework.`);
275
276
  }
276
277
  const componentName = styledComponentName + "Component";
277
278
  styledComponentName = styledComponentName + "Wrapper";
@@ -287,13 +288,13 @@ const registerGenerateCommand = (program, defaultProject) => {
287
288
  parsedFilePath.ext = framework.templates.component.wrapperExt;
288
289
  parsedFilePath.base = parsedFilePath.name + parsedFilePath.ext;
289
290
  const formattedWrapperPath = path.format(parsedFilePath);
290
- compiler_saltyCompiler.logger.info("Generating a new file: " + formattedWrapperPath);
291
+ logger.logger.info("Generating a new file: " + formattedWrapperPath);
291
292
  await promises.writeFile(formattedWrapperPath, wrapperContent);
292
293
  await formatWithPrettier(formattedWrapperPath);
293
294
  }
294
295
  const styledKey = reactComponent && framework.templates.component ? framework.templates.component.styled : framework.templates.styled;
295
296
  const { content } = await readTemplate(styledKey, { tag, name: styledComponentName, className });
296
- compiler_saltyCompiler.logger.info("Generating a new file: " + formattedStyledFilePath);
297
+ logger.logger.info("Generating a new file: " + formattedStyledFilePath);
297
298
  await promises.writeFile(formattedStyledFilePath, content);
298
299
  await formatWithPrettier(formattedStyledFilePath);
299
300
  });
@@ -394,13 +395,13 @@ const astroIntegration = {
394
395
  const existing = await promises.readFile(configPath, "utf-8").catch(() => void 0);
395
396
  if (existing === void 0) return null;
396
397
  const result = editAstroConfig(existing);
397
- if (result.warning) compiler_saltyCompiler.logger.warn(result.warning);
398
+ if (result.warning) logger.logger.warn(result.warning);
398
399
  if (result.content === null) return null;
399
400
  const newContent = result.content;
400
401
  return {
401
402
  packages: [`-D ${astroPackage(ctx.cliVersion)}`],
402
403
  execute: async () => {
403
- compiler_saltyCompiler.logger.info("Adding Salty-CSS integration to Astro config: " + configPath);
404
+ logger.logger.info("Adding Salty-CSS integration to Astro config: " + configPath);
404
405
  await promises.writeFile(configPath, newContent);
405
406
  await formatWithPrettier(configPath);
406
407
  return { changed: true };
@@ -467,17 +468,17 @@ const eslintIntegration = {
467
468
  plan: async (ctx, configPath) => {
468
469
  const existing = await promises.readFile(configPath, "utf-8").catch(() => void 0);
469
470
  if (existing === void 0) {
470
- compiler_saltyCompiler.logger.error("Could not read ESLint config file.");
471
+ logger.logger.error("Could not read ESLint config file.");
471
472
  return null;
472
473
  }
473
474
  const result = editEslintConfig(existing, configPath.endsWith("js"));
474
- if (result.warning) compiler_saltyCompiler.logger.warn(result.warning);
475
+ if (result.warning) logger.logger.warn(result.warning);
475
476
  if (result.content === null) return null;
476
477
  const newContent = result.content;
477
478
  return {
478
479
  packages: [corePackages.eslintConfigCore(ctx.cliVersion)],
479
480
  execute: async () => {
480
- compiler_saltyCompiler.logger.info("Edit file: " + configPath);
481
+ logger.logger.info("Edit file: " + configPath);
481
482
  await promises.writeFile(configPath, newContent);
482
483
  await formatWithPrettier(configPath);
483
484
  return { changed: true };
@@ -520,7 +521,7 @@ const nextIntegration = {
520
521
  return {
521
522
  packages: [`-D ${nextPackage(ctx.cliVersion)}`],
522
523
  execute: async () => {
523
- compiler_saltyCompiler.logger.info("Adding Salty-CSS plugin to Next.js config...");
524
+ logger.logger.info("Adding Salty-CSS plugin to Next.js config...");
524
525
  await promises.writeFile(configPath, content);
525
526
  await formatWithPrettier(configPath);
526
527
  return { changed: true };
@@ -551,8 +552,8 @@ const viteIntegration = {
551
552
  return {
552
553
  packages: [`-D ${vitePackage(ctx.cliVersion)}`],
553
554
  execute: async () => {
554
- compiler_saltyCompiler.logger.info("Edit file: " + configPath);
555
- compiler_saltyCompiler.logger.info("Adding Salty-CSS plugin to Vite config...");
555
+ logger.logger.info("Edit file: " + configPath);
556
+ logger.logger.info("Adding Salty-CSS plugin to Vite config...");
556
557
  await promises.writeFile(configPath, content);
557
558
  await formatWithPrettier(configPath);
558
559
  return { changed: true };
@@ -583,12 +584,12 @@ const applyIntegrationPlans = async (planned) => {
583
584
  const writeProjectFile = async (projectDir, fileName, content) => {
584
585
  const filePath = path.join(projectDir, fileName);
585
586
  if (fs.existsSync(filePath)) {
586
- compiler_saltyCompiler.logger.debug("File already exists: " + filePath);
587
+ logger.logger.debug("File already exists: " + filePath);
587
588
  return;
588
589
  }
589
590
  const additionalFolders = fileName.split("/").slice(0, -1).join("/");
590
591
  if (additionalFolders) await promises.mkdir(path.join(projectDir, additionalFolders), { recursive: true });
591
- compiler_saltyCompiler.logger.info("Creating file: " + filePath);
592
+ logger.logger.info("Creating file: " + filePath);
592
593
  await promises.writeFile(filePath, content);
593
594
  await formatWithPrettier(filePath);
594
595
  };
@@ -597,13 +598,13 @@ const ensureGitignoreSaltygen = async (rootDir) => {
597
598
  const existing = await promises.readFile(path$1, "utf-8").catch(() => void 0);
598
599
  if (existing === void 0) return;
599
600
  if (existing.includes("saltygen")) return;
600
- compiler_saltyCompiler.logger.info("Edit file: " + path$1);
601
+ logger.logger.info("Edit file: " + path$1);
601
602
  await promises.writeFile(path$1, existing + "\n\n# Salty-CSS\nsaltygen\n");
602
603
  };
603
604
  const importSaltygenIntoCss = async (projectDir, explicitCssFile) => {
604
605
  const target = explicitCssFile ?? await findGlobalCssFile(projectDir);
605
606
  if (!target) {
606
- compiler_saltyCompiler.logger.warn("Could not find a CSS file to import the generated CSS. Please add it manually.");
607
+ logger.logger.warn("Could not find a CSS file to import the generated CSS. Please add it manually.");
607
608
  return;
608
609
  }
609
610
  const cssFilePath = path.join(projectDir, target);
@@ -612,14 +613,14 @@ const importSaltygenIntoCss = async (projectDir, explicitCssFile) => {
612
613
  if (cssFileContent.includes("saltygen")) return;
613
614
  const cssFileFolder = path.join(cssFilePath, "..");
614
615
  const relPath = path.relative(cssFileFolder, path.join(projectDir, "saltygen/index.css"));
615
- compiler_saltyCompiler.logger.info("Adding global import statement to CSS file: " + cssFilePath);
616
+ logger.logger.info("Adding global import statement to CSS file: " + cssFilePath);
616
617
  await promises.writeFile(cssFilePath, `@import '${relPath}';
617
618
  ` + cssFileContent);
618
619
  await formatWithPrettier(cssFilePath);
619
620
  };
620
621
  const wirePrepareScript = async () => {
621
622
  const pkg = await readPackageJson().catch(() => {
622
- compiler_saltyCompiler.logError("Could not read package.json file.");
623
+ logger.logError("Could not read package.json file.");
623
624
  return void 0;
624
625
  });
625
626
  if (!pkg) return;
@@ -631,11 +632,11 @@ const registerInitCommand = (program) => {
631
632
  try {
632
633
  const opts = this.opts();
633
634
  const dir = opts.dir ?? _dir;
634
- if (!dir) return compiler_saltyCompiler.logError("Project directory must be provided. Add it as the first argument after init command or use the --dir option.");
635
+ if (!dir) return logger.logError("Project directory must be provided. Add it as the first argument after init command or use the --dir option.");
635
636
  const ctx = await buildContext({ dir, skipInstall: opts.skipInstall, yes: opts.yes });
636
- compiler_saltyCompiler.logger.info("Initializing a new Salty-CSS project!");
637
+ logger.logger.info("Initializing a new Salty-CSS project!");
637
638
  const framework = await detectFramework(ctx);
638
- compiler_saltyCompiler.logger.info(`Detected framework: ${framework.name}`);
639
+ logger.logger.info(`Detected framework: ${framework.name}`);
639
640
  const plannedIntegrations = await planIntegrations(ctx);
640
641
  if (!ctx.skipInstall) {
641
642
  const packages = [
@@ -654,29 +655,29 @@ const registerInitCommand = (program) => {
654
655
  await importSaltygenIntoCss(ctx.projectDir, opts.cssFile);
655
656
  await applyIntegrationPlans(plannedIntegrations);
656
657
  await wirePrepareScript();
657
- compiler_saltyCompiler.logger.info("Running the build to generate initial CSS...");
658
+ logger.logger.info("Running the build to generate initial CSS...");
658
659
  const compiler = new compiler_saltyCompiler.SaltyCompiler(ctx.projectDir);
659
660
  await compiler.generateCss();
660
- compiler_saltyCompiler.logger.info("🎉 Salty CSS project initialized successfully!");
661
- compiler_saltyCompiler.logger.info("Next steps:");
662
- compiler_saltyCompiler.logger.info("1. Configure variables and templates in `salty.config.ts`");
663
- compiler_saltyCompiler.logger.info("2. Create a new component with `npx salty-css generate [component-name]`");
664
- compiler_saltyCompiler.logger.info("3. Run `npx salty-css build` to generate the CSS");
665
- compiler_saltyCompiler.logger.info("4. Read about the features in the documentation: https://salty-css.dev");
666
- compiler_saltyCompiler.logger.info("5. Star the project on GitHub: https://github.com/margarita-form/salty-css ⭐");
661
+ logger.logger.info("🎉 Salty CSS project initialized successfully!");
662
+ logger.logger.info("Next steps:");
663
+ logger.logger.info("1. Configure variables and templates in `salty.config.ts`");
664
+ logger.logger.info("2. Create a new component with `npx salty-css generate [component-name]`");
665
+ logger.logger.info("3. Run `npx salty-css build` to generate the CSS");
666
+ logger.logger.info("4. Read about the features in the documentation: https://salty-css.dev");
667
+ logger.logger.info("5. Star the project on GitHub: https://github.com/margarita-form/salty-css ⭐");
667
668
  } catch (err) {
668
- return compiler_saltyCompiler.logError(err instanceof Error ? err.message : String(err));
669
+ return logger.logError(err instanceof Error ? err.message : String(err));
669
670
  }
670
671
  });
671
672
  };
672
673
  const getSaltyCssPackages = async () => {
673
674
  const packageJSONPath = path.join(process.cwd(), "package.json");
674
- const packageJson = await readPackageJson(packageJSONPath).catch((err) => compiler_saltyCompiler.logError(err));
675
- if (!packageJson) return compiler_saltyCompiler.logError("Could not read package.json file.");
675
+ const packageJson = await readPackageJson(packageJSONPath).catch((err) => logger.logError(err));
676
+ if (!packageJson) return logger.logError("Could not read package.json file.");
676
677
  const allDependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
677
678
  const saltyCssPackages = Object.entries(allDependencies).filter(([name]) => name === "salty-css" || name.startsWith("@salty-css/"));
678
679
  if (!saltyCssPackages.length) {
679
- return compiler_saltyCompiler.logError(
680
+ return logger.logError(
680
681
  "No Salty-CSS packages found in package.json. Make sure you are running update command in the same directory! Used package.json path: " + packageJSONPath
681
682
  );
682
683
  }
@@ -686,7 +687,7 @@ const registerUpdateCommand = (program) => {
686
687
  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") {
687
688
  const { legacyPeerDeps, version = _version, yes = false, dir } = this.opts();
688
689
  const saltyCssPackages = await getSaltyCssPackages();
689
- if (!saltyCssPackages) return compiler_saltyCompiler.logError("Could not update Salty-CSS packages as any were found in package.json.");
690
+ if (!saltyCssPackages) return logger.logError("Could not update Salty-CSS packages as any were found in package.json.");
690
691
  const cli = await readThisPackageJson();
691
692
  const packagesToUpdate = saltyCssPackages.map(([name]) => {
692
693
  if (version === "@") return `${name}@${cli.version}`;
@@ -695,16 +696,16 @@ const registerUpdateCommand = (program) => {
695
696
  try {
696
697
  await confirmInstall(packagesToUpdate, yes);
697
698
  } catch (err) {
698
- return compiler_saltyCompiler.logError(err instanceof Error ? err.message : String(err));
699
+ return logger.logError(err instanceof Error ? err.message : String(err));
699
700
  }
700
701
  if (legacyPeerDeps) {
701
- compiler_saltyCompiler.logger.warn("Using legacy peer dependencies to update packages.");
702
+ logger.logger.warn("Using legacy peer dependencies to update packages.");
702
703
  await npmInstall(...packagesToUpdate, "--legacy-peer-deps");
703
704
  } else {
704
705
  await npmInstall(...packagesToUpdate);
705
706
  }
706
707
  const updatedPackages = await getSaltyCssPackages();
707
- if (!updatedPackages) return compiler_saltyCompiler.logError("Something went wrong while reading the updated packages.");
708
+ if (!updatedPackages) return logger.logError("Something went wrong while reading the updated packages.");
708
709
  const mappedByVersions = updatedPackages.reduce((acc, [name, version2]) => {
709
710
  if (!acc[version2]) acc[version2] = [];
710
711
  acc[version2].push(name);
@@ -713,41 +714,41 @@ const registerUpdateCommand = (program) => {
713
714
  const versionsCount = Object.keys(mappedByVersions).length;
714
715
  if (versionsCount === 1) {
715
716
  const v = Object.keys(mappedByVersions)[0];
716
- compiler_saltyCompiler.logger.info(`Updated to all Salty CSS packages successfully to ${v.replace(/^\^/, "")}`);
717
+ logger.logger.info(`Updated to all Salty CSS packages successfully to ${v.replace(/^\^/, "")}`);
717
718
  } else {
718
719
  for (const [v, names] of Object.entries(mappedByVersions)) {
719
- compiler_saltyCompiler.logger.info(`Updated to ${v.replace(/^\^/, "")}: ${names.join(", ")}`);
720
+ logger.logger.info(`Updated to ${v.replace(/^\^/, "")}: ${names.join(", ")}`);
720
721
  }
721
722
  }
722
723
  const project = dir ?? await getDefaultProject();
723
724
  if (!project) {
724
- compiler_saltyCompiler.logger.warn("Skipping rebuild: no project directory configured. Run `salty-css build [dir]` manually.");
725
+ logger.logger.warn("Skipping rebuild: no project directory configured. Run `salty-css build [dir]` manually.");
725
726
  return;
726
727
  }
727
728
  const shouldRebuild = await confirmYesNo("Rebuild Salty CSS now?", { yes });
728
729
  if (!shouldRebuild) return;
729
730
  const projectDir = resolveProjectDir(project);
730
- compiler_saltyCompiler.logger.info("Rebuilding Salty-CSS project...");
731
+ logger.logger.info("Rebuilding Salty-CSS project...");
731
732
  await new compiler_saltyCompiler.SaltyCompiler(projectDir).generateCss();
732
- compiler_saltyCompiler.logger.info("Rebuild complete.");
733
+ logger.logger.info("Rebuild complete.");
733
734
  });
734
735
  };
735
736
  const registerVersionOption = (program) => {
736
737
  program.option("-v, --version", "Show the current version of Salty-CSS.").action(async function() {
737
738
  const cli = await readThisPackageJson();
738
- compiler_saltyCompiler.logger.info("CLI is running: " + cli.version);
739
+ logger.logger.info("CLI is running: " + cli.version);
739
740
  const packageJSONPath = path.join(process.cwd(), "package.json");
740
- const packageJson = await readPackageJson(packageJSONPath).catch((err) => compiler_saltyCompiler.logError(err));
741
+ const packageJson = await readPackageJson(packageJSONPath).catch((err) => logger.logError(err));
741
742
  if (!packageJson) return;
742
743
  const allDependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
743
744
  const saltyCssPackages = Object.keys(allDependencies).filter((dep) => dep === "salty-css" || dep.startsWith("@salty-css/"));
744
745
  if (!saltyCssPackages.length) {
745
- return compiler_saltyCompiler.logError(
746
+ return logger.logError(
746
747
  "No Salty-CSS packages found in package.json. Make sure you are running update command in the same directory! Used package.json path: " + packageJSONPath
747
748
  );
748
749
  }
749
750
  for (const dep of saltyCssPackages) {
750
- compiler_saltyCompiler.logger.info(`${dep}: ${allDependencies[dep]}`);
751
+ logger.logger.info(`${dep}: ${allDependencies[dep]}`);
751
752
  }
752
753
  });
753
754
  };
package/bin/main.js CHANGED
@@ -1,12 +1,13 @@
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";
12
13
  import { createInterface } from "readline/promises";
@@ -2,23 +2,63 @@
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 {
5
+ const CACHE_FILENAME = "config-cache.json";
6
+ const ENV_VAR = "SALTY_CONFIG_CACHE_PATH";
7
+ const defaultRoots = ["", "src", "dist", "build", "public", ".next", ".next/server", ".vercel/output/functions"];
8
+ const memo = /* @__PURE__ */ new Map();
9
+ let warned = false;
10
+ const isProduction = () => {
11
+ try {
12
+ return process.env["NODE_ENV"] === "production";
13
+ } catch {
14
+ return false;
15
+ }
16
+ };
17
+ const toAbsolute = (p, cwd) => path.isAbsolute(p) ? p : path.join(cwd, p);
18
+ const candidatePathsFrom = (entry, cwd) => {
19
+ const abs = toAbsolute(entry, cwd);
20
+ if (abs.endsWith(".json")) return [abs];
21
+ return [path.join(abs, CACHE_FILENAME), path.join(abs, "cache", CACHE_FILENAME), path.join(abs, "saltygen", "cache", CACHE_FILENAME)];
22
+ };
23
+ const tryRead = async (path2) => {
24
+ const useMemo = isProduction();
25
+ if (useMemo && memo.has(path2)) return memo.get(path2) ?? void 0;
26
+ try {
27
+ const contents = await promises.readFile(path2, "utf8");
28
+ if (!contents) {
29
+ if (useMemo) memo.set(path2, null);
30
+ return void 0;
16
31
  }
32
+ const parsed = JSON.parse(contents);
33
+ if (useMemo) memo.set(path2, parsed);
34
+ return parsed;
35
+ } catch {
36
+ if (useMemo) memo.set(path2, null);
37
+ return void 0;
17
38
  }
18
- if (!contents) {
19
- console.warn(`Could not find config cache file (${filename}) in any of the expected locations.`);
20
- return {};
39
+ };
40
+ const resolveDynamicConfigCache = async (options = {}) => {
41
+ var _a;
42
+ const cwd = options.cwd ?? process.cwd();
43
+ const envPath = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[ENV_VAR] : void 0;
44
+ const ordered = [];
45
+ if (options.primaryPath) ordered.push(...candidatePathsFrom(options.primaryPath, cwd));
46
+ if (envPath) ordered.push(...candidatePathsFrom(envPath, cwd));
47
+ if (options.extraPaths) for (const p of options.extraPaths) ordered.push(...candidatePathsFrom(p, cwd));
48
+ for (const root of defaultRoots) ordered.push(...candidatePathsFrom(root, cwd));
49
+ for (const candidate of ordered) {
50
+ const result = await tryRead(candidate);
51
+ if (result) return result;
52
+ }
53
+ if (!warned) {
54
+ warned = true;
55
+ console.warn(`Could not find config cache file (${CACHE_FILENAME}) in any of the expected locations.`);
21
56
  }
22
- return JSON.parse(contents);
57
+ return {};
58
+ };
59
+ const _resetDynamicConfigCacheMemo = () => {
60
+ memo.clear();
61
+ warned = false;
23
62
  };
63
+ exports._resetDynamicConfigCacheMemo = _resetDynamicConfigCacheMemo;
24
64
  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,64 @@
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 {
2
+ import { join, isAbsolute } from "path";
3
+ const CACHE_FILENAME = "config-cache.json";
4
+ const ENV_VAR = "SALTY_CONFIG_CACHE_PATH";
5
+ const defaultRoots = ["", "src", "dist", "build", "public", ".next", ".next/server", ".vercel/output/functions"];
6
+ const memo = /* @__PURE__ */ new Map();
7
+ let warned = false;
8
+ const isProduction = () => {
9
+ try {
10
+ return process.env["NODE_ENV"] === "production";
11
+ } catch {
12
+ return false;
13
+ }
14
+ };
15
+ const toAbsolute = (p, cwd) => isAbsolute(p) ? p : join(cwd, p);
16
+ const candidatePathsFrom = (entry, cwd) => {
17
+ const abs = toAbsolute(entry, cwd);
18
+ if (abs.endsWith(".json")) return [abs];
19
+ return [join(abs, CACHE_FILENAME), join(abs, "cache", CACHE_FILENAME), join(abs, "saltygen", "cache", CACHE_FILENAME)];
20
+ };
21
+ const tryRead = async (path) => {
22
+ const useMemo = isProduction();
23
+ if (useMemo && memo.has(path)) return memo.get(path) ?? void 0;
24
+ try {
25
+ const contents = await readFile(path, "utf8");
26
+ if (!contents) {
27
+ if (useMemo) memo.set(path, null);
28
+ return void 0;
14
29
  }
30
+ const parsed = JSON.parse(contents);
31
+ if (useMemo) memo.set(path, parsed);
32
+ return parsed;
33
+ } catch {
34
+ if (useMemo) memo.set(path, null);
35
+ return void 0;
15
36
  }
16
- if (!contents) {
17
- console.warn(`Could not find config cache file (${filename}) in any of the expected locations.`);
18
- return {};
37
+ };
38
+ const resolveDynamicConfigCache = async (options = {}) => {
39
+ var _a;
40
+ const cwd = options.cwd ?? process.cwd();
41
+ const envPath = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[ENV_VAR] : void 0;
42
+ const ordered = [];
43
+ if (options.primaryPath) ordered.push(...candidatePathsFrom(options.primaryPath, cwd));
44
+ if (envPath) ordered.push(...candidatePathsFrom(envPath, cwd));
45
+ if (options.extraPaths) for (const p of options.extraPaths) ordered.push(...candidatePathsFrom(p, cwd));
46
+ for (const root of defaultRoots) ordered.push(...candidatePathsFrom(root, cwd));
47
+ for (const candidate of ordered) {
48
+ const result = await tryRead(candidate);
49
+ if (result) return result;
50
+ }
51
+ if (!warned) {
52
+ warned = true;
53
+ console.warn(`Could not find config cache file (${CACHE_FILENAME}) in any of the expected locations.`);
19
54
  }
20
- return JSON.parse(contents);
55
+ return {};
56
+ };
57
+ const _resetDynamicConfigCacheMemo = () => {
58
+ memo.clear();
59
+ warned = false;
21
60
  };
22
61
  export {
62
+ _resetDynamicConfigCacheMemo,
23
63
  resolveDynamicConfigCache
24
64
  };
@@ -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--vHKY6Mw.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 {