@salty-css/core 0.1.0-refactor-add-additional-paths-to-config-cache.1 → 0.1.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 (60) hide show
  1. package/README.md +355 -374
  2. package/bin/main.cjs +57 -58
  3. package/bin/main.js +1 -2
  4. package/{class-name-generator-CMWY5KTJ.js → class-name-generator-CUEoPowv.js} +18 -4
  5. package/{class-name-generator-DB5aQwC_.cjs → class-name-generator-MtPkBfM_.cjs} +19 -5
  6. package/compiler/helpers.cjs +10 -2
  7. package/compiler/helpers.d.ts +3 -2
  8. package/compiler/helpers.js +11 -3
  9. package/compiler/resolve-import.d.ts +17 -0
  10. package/compiler/salty-compiler.cjs +144 -33
  11. package/compiler/salty-compiler.d.ts +2 -5
  12. package/compiler/salty-compiler.js +149 -38
  13. package/config/index.cjs +4 -0
  14. package/config/index.js +5 -1
  15. package/css/index.cjs +0 -4
  16. package/css/index.d.ts +0 -1
  17. package/css/index.js +0 -4
  18. package/css/keyframes.cjs +2 -2
  19. package/css/keyframes.js +2 -2
  20. package/factories/define-font.d.ts +28 -0
  21. package/factories/define-import.d.ts +14 -0
  22. package/factories/index.cjs +140 -0
  23. package/factories/index.d.ts +2 -0
  24. package/factories/index.js +140 -0
  25. package/generators/index.cjs +3 -3
  26. package/generators/index.js +3 -3
  27. package/helpers/color.d.ts +6 -0
  28. package/instances/classname-instance.cjs +1 -1
  29. package/instances/classname-instance.js +1 -1
  30. package/package.json +1 -13
  31. package/{parse-styles-C54MOrPg.cjs → parse-styles-BbI-2wdn.cjs} +196 -11
  32. package/{parse-styles-CLMTHo2H.js → parse-styles-BgVqQAni.js} +196 -11
  33. package/parsers/index.cjs +93 -5
  34. package/parsers/index.js +97 -9
  35. package/parsers/parse-templates.d.ts +10 -0
  36. package/parsers/parser-regexes.d.ts +1 -0
  37. package/parsers/resolve-template-variants.d.ts +21 -0
  38. package/runtime/index.cjs +16 -3
  39. package/runtime/index.d.ts +7 -0
  40. package/runtime/index.js +16 -3
  41. package/{to-hash-DAN2LcHK.js → to-hash-DSoCPs8D.js} +8 -0
  42. package/{to-hash-C05Y906F.cjs → to-hash-DT2ImSPA.cjs} +8 -0
  43. package/types/config-types.d.ts +33 -2
  44. package/types/font-types.d.ts +53 -0
  45. package/util/index.cjs +3 -4
  46. package/util/index.js +1 -2
  47. package/util/module-type.d.ts +1 -1
  48. package/cache/resolve-dynamic-config-cache.cjs +0 -64
  49. package/cache/resolve-dynamic-config-cache.d.ts +0 -20
  50. package/cache/resolve-dynamic-config-cache.js +0 -64
  51. package/compiler/copy-config-cache.cjs +0 -39
  52. package/compiler/copy-config-cache.d.ts +0 -17
  53. package/compiler/copy-config-cache.js +0 -39
  54. package/css/dynamic-styles.cjs +0 -28
  55. package/css/dynamic-styles.d.ts +0 -49
  56. package/css/dynamic-styles.js +0 -28
  57. package/dash-case-DIwKaYgE.cjs +0 -9
  58. package/dash-case-DblXvymC.js +0 -10
  59. package/logger-7xz0pyAz.cjs +0 -12
  60. package/logger-hHmCwThj.js +0 -13
@@ -5,17 +5,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
6
  const esbuild = require("esbuild");
7
7
  const path = require("path");
8
- const logger = require("../logger-7xz0pyAz.cjs");
8
+ const winston = require("winston");
9
9
  const promises = require("fs/promises");
10
10
  const fs = require("fs");
11
11
  const child_process = require("child_process");
12
12
  const compiler_helpers = require("./helpers.cjs");
13
+ const toHash = require("../to-hash-DT2ImSPA.cjs");
13
14
  const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
14
- const dashCase = require("../dash-case-DIwKaYgE.cjs");
15
- const toHash = require("../to-hash-C05Y906F.cjs");
16
- const parseStyles = require("../parse-styles-C54MOrPg.cjs");
15
+ const module$1 = require("module");
16
+ const parseStyles = require("../parse-styles-BbI-2wdn.cjs");
17
17
  const css_merge = require("../css/merge.cjs");
18
- require("../css/dynamic-styles.cjs");
19
18
  const compiler_getFiles = require("./get-files.cjs");
20
19
  const parsers_index = require("../parsers/index.cjs");
21
20
  const console = require("console");
@@ -37,6 +36,62 @@ function _interopNamespaceDefault(e) {
37
36
  return Object.freeze(n);
38
37
  }
39
38
  const esbuild__namespace = /* @__PURE__ */ _interopNamespaceDefault(esbuild);
39
+ const logger = winston.createLogger({
40
+ level: "debug",
41
+ format: winston.format.combine(winston.format.colorize(), winston.format.cli()),
42
+ transports: [new winston.transports.Console({})]
43
+ });
44
+ const logError = (message) => {
45
+ logger.error(message);
46
+ };
47
+ const EXTERNAL_URL = /^(?:[a-z][a-z0-9+.-]*:)?\/\//i;
48
+ const normaliseSpec = (spec) => {
49
+ if (typeof spec === "string") return { url: spec };
50
+ return spec;
51
+ };
52
+ const ensureRelativePrefix = (path2) => {
53
+ if (path2.startsWith(".") || path2.startsWith("/")) return path2;
54
+ return `./${path2}`;
55
+ };
56
+ const toPosix = (path2) => path2.split("\\").join("/");
57
+ const defaultResolveModule = (specifier, sourceFile) => {
58
+ return module$1.createRequire(sourceFile).resolve(specifier);
59
+ };
60
+ const defaultCopyAsset = (from, to) => {
61
+ const dir = path.dirname(to);
62
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
63
+ fs.copyFileSync(from, to);
64
+ };
65
+ const buildRule = (url, { media, supports }) => {
66
+ let rule = `@import url('${url}')`;
67
+ if (supports) rule += ` supports(${supports})`;
68
+ if (media) rule += ` ${media}`;
69
+ return `${rule};`;
70
+ };
71
+ const resolveImport = (spec, sourceFile, destDir, options = {}) => {
72
+ const opts = normaliseSpec(spec);
73
+ const { url } = opts;
74
+ const resolveModule = options.resolveModule ?? defaultResolveModule;
75
+ const copyAsset = options.copyAsset ?? defaultCopyAsset;
76
+ if (EXTERNAL_URL.test(url)) {
77
+ return { rule: buildRule(url, opts) };
78
+ }
79
+ if (url.startsWith("/")) {
80
+ return { rule: buildRule(url, opts) };
81
+ }
82
+ if (url.startsWith("./") || url.startsWith("../")) {
83
+ const absolute2 = path.resolve(path.dirname(sourceFile), url);
84
+ const fromImportsFile = path.relative(path.join(destDir, "css"), absolute2);
85
+ return { rule: buildRule(ensureRelativePrefix(toPosix(fromImportsFile)), opts) };
86
+ }
87
+ const specifier = url.startsWith("~") ? url.slice(1) : url;
88
+ const absolute = resolveModule(specifier, sourceFile);
89
+ const hash = toHash.toHash(absolute, 6);
90
+ const fileName = `${hash}-${path.basename(absolute)}`;
91
+ const destPath = path.join(destDir, "imports", fileName);
92
+ copyAsset(absolute, destPath);
93
+ return { rule: buildRule(`../imports/${fileName}`, opts) };
94
+ };
40
95
  const readPackageJsonModule = async (dirname) => {
41
96
  const packageJsonContent = await compiler_getFiles.getPackageJson(dirname);
42
97
  if (!packageJsonContent) return void 0;
@@ -161,14 +216,6 @@ class SaltyCompiler {
161
216
  this.cache.destDir = destDir;
162
217
  return destDir;
163
218
  });
164
- /**
165
- * Absolute path to the `config-cache.json` written during `generateCss()`.
166
- * Plugins read this to copy the file into the production bundle output.
167
- */
168
- __publicField(this, "getConfigCachePath", async () => {
169
- const destDir = await this.getDestDir();
170
- return path.join(destDir, "cache/config-cache.json");
171
- });
172
219
  __publicField(this, "generateConfig", async () => {
173
220
  const rcProject = await this.getRCProjectConfig(this.projectRootDir);
174
221
  const destDir = await this.getDestDir();
@@ -202,7 +249,8 @@ ${currentFile}`;
202
249
  }
203
250
  });
204
251
  __publicField(this, "getConfigCache", async () => {
205
- const coreConfigDest = await this.getConfigCachePath();
252
+ const destDir = await this.getDestDir();
253
+ const coreConfigDest = path.join(destDir, "cache/config-cache.json");
206
254
  const contents = fs.readFileSync(coreConfigDest, "utf8");
207
255
  if (!contents) throw new Error("Could not find config cache file");
208
256
  return JSON.parse(contents);
@@ -220,8 +268,8 @@ ${currentFile}`;
220
268
  __publicField(this, "generateCss", async (clean = true) => {
221
269
  try {
222
270
  const start = Date.now();
223
- if (this.isProduction) logger.logger.info("Generating CSS in production mode! 🔥");
224
- else logger.logger.info("Generating CSS in development mode! 🚀");
271
+ if (this.isProduction) logger.info("Generating CSS in production mode! 🔥");
272
+ else logger.info("Generating CSS in development mode! 🚀");
225
273
  const globalCssFiles = [];
226
274
  const cssFiles = [];
227
275
  const destDir = await this.getDestDir();
@@ -233,6 +281,7 @@ ${currentFile}`;
233
281
  fs.mkdirSync(path.join(destDir, "types"));
234
282
  fs.mkdirSync(path.join(destDir, "js"));
235
283
  fs.mkdirSync(path.join(destDir, "cache"));
284
+ fs.mkdirSync(path.join(destDir, "imports"));
236
285
  };
237
286
  if (clean) clearDistDir();
238
287
  const files = /* @__PURE__ */ new Set();
@@ -342,13 +391,13 @@ ${currentFile}`;
342
391
  const cssContent2 = localCssFile.map((file) => `@import url('./${file}');`).join("\n");
343
392
  const hashName = toHash.toHash(src, 6);
344
393
  const parsedPath = path.parse(src);
345
- const dasherized = dashCase.dashCase(parsedPath.name);
394
+ const dasherized = toHash.dashCase(parsedPath.name);
346
395
  const cssFile2 = path.join(destDir, `css/f_${dasherized}-${hashName}.css`);
347
396
  fs.writeFileSync(cssFile2, cssContent2 || `/* Empty file */`);
348
397
  });
349
398
  }
350
399
  const otherGlobalCssFiles = globalCssFiles.map((file) => `@import url('./css/${file}');`).join("\n");
351
- const globalCssFilenames = ["_variables.css", "_reset.css", "_global.css", "_templates.css"];
400
+ const globalCssFilenames = ["_imports.css", "_variables.css", "_reset.css", "_global.css", "_templates.css", "_fonts.css"];
352
401
  const importsWithData = globalCssFilenames.filter((file) => {
353
402
  try {
354
403
  const data = fs.readFileSync(path.join(destDir, "css", file), "utf8");
@@ -357,9 +406,12 @@ ${currentFile}`;
357
406
  return false;
358
407
  }
359
408
  });
360
- const globalImports = importsWithData.map((file) => `@import url('./css/${file}');`);
409
+ const globalImports = importsWithData.map((file) => {
410
+ const layerSuffix = file === "_imports.css" ? " layer(imports)" : "";
411
+ return `@import url('./css/${file}')${layerSuffix};`;
412
+ });
361
413
  const generatorText = "/*!\n * Generated with Salty CSS (https://salty-css.dev)\n * Do not edit this file directly\n */\n";
362
- let cssContent = `${generatorText}@layer reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
414
+ let cssContent = `${generatorText}@layer imports, reset, global, templates, fonts, l0, l1, l2, l3, l4, l5, l6, l7, l8;
363
415
 
364
416
  ${globalImports.join(
365
417
  "\n"
@@ -393,7 +445,7 @@ ${css}
393
445
  const end = Date.now();
394
446
  const time = end - start;
395
447
  const emoji = time < 200 ? "🔥" : time < 500 ? "🚀" : time < 1e3 ? "🎉" : time < 2e3 ? "🚗" : time < 5e3 ? "🤔" : "🥴";
396
- logger.logger.info(`Generated CSS in ${time}ms! ${emoji}`);
448
+ logger.info(`Generated CSS in ${time}ms! ${emoji}`);
397
449
  } catch (e) {
398
450
  console.error(e);
399
451
  }
@@ -405,7 +457,9 @@ ${css}
405
457
  mediaQueries: [],
406
458
  globalStyles: [],
407
459
  variables: [],
408
- templates: []
460
+ templates: [],
461
+ imports: [],
462
+ fonts: []
409
463
  };
410
464
  await Promise.all(
411
465
  [...configFiles].map(async (src) => {
@@ -415,6 +469,8 @@ ${css}
415
469
  else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
416
470
  else if (value.isDefineVariables) generationResults.variables.push(value);
417
471
  else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
472
+ else if (value.isDefineImport) generationResults.imports.push(value._setPath(src));
473
+ else if (value.isDefineFont) generationResults.fonts.push(value);
418
474
  });
419
475
  })
420
476
  );
@@ -433,10 +489,10 @@ ${css}
433
489
  if (typeof value2 === "function") return await parseVariable(await value2());
434
490
  if (typeof value2 === "object") return await parseVariables(value2, [...path2, key]);
435
491
  const dottedKey = dotCase(key);
436
- const dashedKey = dashCase.dashCase(key);
492
+ const dashedKey = toHash.dashCase(key);
437
493
  const tsName = [...path2, dottedKey].join(".");
438
494
  variableTokens.add(`"${tsName}"`);
439
- const cssName = [...path2.map(dashCase.dashCase), dashedKey].join("-");
495
+ const cssName = [...path2.map(toHash.dashCase), dashedKey].join("-");
440
496
  const result = parseStyles.parseVariableTokens(value2);
441
497
  if (!result) return `--${cssName}: ${value2};`;
442
498
  return `--${cssName}: ${result.transformed};`;
@@ -511,32 +567,85 @@ ${css}
511
567
  const templates = css_merge.mergeObjects(config.templates, generationResults.templates);
512
568
  const templateStylesString = await parsers_index.parseTemplates(templates);
513
569
  const templateTokens = parsers_index.getTemplateTypes(templates);
570
+ const templateVariantMaps = parsers_index.getTemplateVariantMaps(templates);
514
571
  fs.writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
515
572
  configCacheContent.templates = templates;
573
+ const importsPath = path.join(destDir, "css/_imports.css");
574
+ const importRules = [];
575
+ for (const factory of generationResults.imports) {
576
+ const sourceFile = factory._path;
577
+ if (!sourceFile) continue;
578
+ for (const spec of factory._current) {
579
+ try {
580
+ const { rule } = resolveImport(spec, sourceFile, destDir);
581
+ importRules.push(rule);
582
+ } catch (e) {
583
+ const url = typeof spec === "string" ? spec : spec.url;
584
+ logger.error(`Failed to resolve defineImport(${JSON.stringify(url)}) from ${sourceFile}: ${e.message}`);
585
+ }
586
+ }
587
+ }
588
+ fs.writeFileSync(importsPath, importRules.join("\n"));
516
589
  const configTemplateFactories = config.templates ? [defineTemplates.defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
517
590
  const templateFactories = css_merge.mergeFactories(generationResults.templates, configTemplateFactories);
518
591
  configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
592
+ const fontsStylesPath = path.join(destDir, "css/_fonts.css");
593
+ if (generationResults.fonts.length === 0) {
594
+ fs.writeFileSync(fontsStylesPath, "");
595
+ } else {
596
+ const fontImports = [];
597
+ const fontBodies = [];
598
+ for (const font of generationResults.fonts) {
599
+ const { imports, body } = font._toCss();
600
+ fontImports.push(...imports);
601
+ fontBodies.push(body);
602
+ }
603
+ const importBlock = fontImports.length ? `${fontImports.join("\n")}
604
+
605
+ ` : "";
606
+ fs.writeFileSync(fontsStylesPath, `${importBlock}@layer fonts { ${fontBodies.join(" ")} }`);
607
+ }
519
608
  const tsTokensPath = path.join(destDir, "types/css-tokens.d.ts");
520
609
  const tsVariableTokens = [...variableTokens].join("|");
610
+ const pascal = (str) => str.split(/[.\-_]/).filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
611
+ const templateVariantMapEntries = Object.entries(templateVariantMaps);
612
+ const hasVariantMaps = templateVariantMapEntries.some(([, pathMap]) => Object.keys(pathMap).length > 0);
613
+ const tsTemplateVariantMap = hasVariantMaps ? `type TemplateVariantTokens = {
614
+ ${templateVariantMapEntries.filter(([, pathMap]) => Object.keys(pathMap).length > 0).map(([templateKey, pathMap]) => {
615
+ const pathEntries = Object.entries(pathMap).map(
616
+ ([dotPath, axes]) => `'${dotPath}': { ${Object.entries(axes).map(([axis, valueType]) => `${axis}?: ${valueType}`).join("; ")} }`
617
+ ).join(";\n ");
618
+ return `${templateKey}: {
619
+ ${pathEntries}
620
+ }`;
621
+ }).join(";\n ")}
622
+ }` : `type TemplateVariantTokens = Record<string, Record<string, Record<string, never>>>;`;
623
+ const tsTemplateVariantAliases = templateVariantMapEntries.flatMap(
624
+ ([templateKey, pathMap]) => Object.keys(pathMap).map((dotPath) => `type ${pascal(templateKey)}${pascal(dotPath)}Variants = TemplateVariantTokens['${templateKey}']['${dotPath}'];`)
625
+ ).join("\n ");
521
626
  const tsTokensTypes = `
522
627
  // Variable types
523
- type VariableTokens = ${tsVariableTokens || `''`};
628
+ type VariableTokens = ${tsVariableTokens || `''`};
524
629
  type PropertyValueToken = \`{\${VariableTokens}}\`;
525
-
630
+
526
631
  // Template types
527
632
  type TemplateTokens = {
528
- ${Object.entries(templateTokens).map(([key, value]) => `${key}?: ${value}`).join("\n")}
633
+ ${Object.entries(templateTokens).map(([key, value]) => {
634
+ if (!value || value === "any") return `${key}?: ${value || "any"}`;
635
+ return `${key}?: ${value} | \`\${${value}}@\${string}\``;
636
+ }).join("\n")}
529
637
  }
530
-
638
+
639
+ // Template variant types (per docs/template-variants-spec.md §7)
640
+ ${tsTemplateVariantMap}
641
+ ${tsTemplateVariantAliases}
642
+
531
643
  // Media query types
532
644
  type MediaQueryKeys = ${mediaQueryKeys || `''`};
533
645
  `;
534
646
  fs.writeFileSync(tsTokensPath, tsTokensTypes);
535
- const configCachePath = await this.getConfigCachePath();
647
+ const configCachePath = path.join(destDir, "cache/config-cache.json");
536
648
  fs.writeFileSync(configCachePath, JSON.stringify(configCacheContent, null, 2));
537
- const corePackageRoot = compiler_helpers.getCorePackageRoot();
538
- const configCacheSecondaryPath = path.join(corePackageRoot, "cache/config-cache.json");
539
- fs.writeFileSync(configCacheSecondaryPath, JSON.stringify(configCacheContent, null, 2));
540
649
  });
541
650
  __publicField(this, "compileSaltyFile", async (sourceFilePath, outputDirectory) => {
542
651
  const hashedName = toHash.toHash(sourceFilePath);
@@ -665,7 +774,7 @@ ${newContent}
665
774
  const cssContent = cssFiles.flat().map((file2) => `@import url('./${file2}');`).join("\n");
666
775
  const hashName = toHash.toHash(file, 6);
667
776
  const parsedPath = path.parse(file);
668
- const dasherized = dashCase.dashCase(parsedPath.name);
777
+ const dasherized = toHash.dashCase(parsedPath.name);
669
778
  const cssFile = path.join(destDir, `css/f_${dasherized}-${hashName}.css`);
670
779
  fs.writeFileSync(cssFile, cssContent || `/* Empty file */`);
671
780
  }
@@ -706,3 +815,5 @@ ${newContent}
706
815
  }
707
816
  }
708
817
  exports.SaltyCompiler = SaltyCompiler;
818
+ exports.logError = logError;
819
+ exports.logger = logger;
@@ -26,11 +26,6 @@ export declare class SaltyCompiler {
26
26
  * Caches the result to avoid redundant computations.
27
27
  */
28
28
  getDestDir: () => Promise<string>;
29
- /**
30
- * Absolute path to the `config-cache.json` written during `generateCss()`.
31
- * Plugins read this to copy the file into the production bundle output.
32
- */
33
- getConfigCachePath: () => Promise<string>;
34
29
  private generateConfig;
35
30
  private addConfigCache;
36
31
  private getConfigCache;
@@ -49,6 +44,8 @@ export declare class SaltyCompiler {
49
44
  isGlobalDefine?: boolean;
50
45
  isDefineVariables?: boolean;
51
46
  isDefineTemplates?: boolean;
47
+ isDefineImport?: boolean;
48
+ isDefineFont?: boolean;
52
49
  isKeyframes?: boolean;
53
50
  animationName?: string;
54
51
  css?: Promise<string>;
@@ -2,30 +2,85 @@ 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
4
  import * as esbuild from "esbuild";
5
- import { join, parse } from "path";
6
- import { l as logger } from "../logger-hHmCwThj.js";
5
+ import { resolve, dirname, relative, join, basename, parse } from "path";
6
+ import { createLogger, transports, format } from "winston";
7
7
  import { readFile } from "fs/promises";
8
- import { readFileSync, existsSync, mkdirSync, statSync, readdirSync, writeFileSync } from "fs";
8
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, statSync, readdirSync, writeFileSync } from "fs";
9
9
  import { execSync } from "child_process";
10
- import { isSaltyFile, resolveExportValue, getCorePackageRoot, saltyFileExtensions } from "./helpers.js";
10
+ import { isSaltyFile, resolveExportValue, saltyFileExtensions } from "./helpers.js";
11
+ import { t as toHash, d as dashCase } from "../to-hash-DSoCPs8D.js";
11
12
  import { d as defineTemplates } from "../define-templates-CVhhgPnd.js";
12
- import { d as dashCase } from "../dash-case-DblXvymC.js";
13
- import { t as toHash } from "../to-hash-DAN2LcHK.js";
14
- import { p as parseAndJoinStyles, b as parseVariableTokens } from "../parse-styles-CLMTHo2H.js";
13
+ import { createRequire } from "module";
14
+ import { p as parseAndJoinStyles, c as parseVariableTokens } from "../parse-styles-BgVqQAni.js";
15
15
  import { mergeObjects, mergeFactories } from "../css/merge.js";
16
- import "../css/dynamic-styles.js";
17
16
  import { getPackageJson } from "./get-files.js";
18
- import { parseTemplates, getTemplateTypes } from "../parsers/index.js";
17
+ import { parseTemplates, getTemplateTypes, getTemplateVariantMaps } from "../parsers/index.js";
19
18
  import console from "console";
20
- const readPackageJsonModule = async (dirname) => {
21
- const packageJsonContent = await getPackageJson(dirname);
19
+ const logger = createLogger({
20
+ level: "debug",
21
+ format: format.combine(format.colorize(), format.cli()),
22
+ transports: [new transports.Console({})]
23
+ });
24
+ const logError = (message) => {
25
+ logger.error(message);
26
+ };
27
+ const EXTERNAL_URL = /^(?:[a-z][a-z0-9+.-]*:)?\/\//i;
28
+ const normaliseSpec = (spec) => {
29
+ if (typeof spec === "string") return { url: spec };
30
+ return spec;
31
+ };
32
+ const ensureRelativePrefix = (path) => {
33
+ if (path.startsWith(".") || path.startsWith("/")) return path;
34
+ return `./${path}`;
35
+ };
36
+ const toPosix = (path) => path.split("\\").join("/");
37
+ const defaultResolveModule = (specifier, sourceFile) => {
38
+ return createRequire(sourceFile).resolve(specifier);
39
+ };
40
+ const defaultCopyAsset = (from, to) => {
41
+ const dir = dirname(to);
42
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
43
+ copyFileSync(from, to);
44
+ };
45
+ const buildRule = (url, { media, supports }) => {
46
+ let rule = `@import url('${url}')`;
47
+ if (supports) rule += ` supports(${supports})`;
48
+ if (media) rule += ` ${media}`;
49
+ return `${rule};`;
50
+ };
51
+ const resolveImport = (spec, sourceFile, destDir, options = {}) => {
52
+ const opts = normaliseSpec(spec);
53
+ const { url } = opts;
54
+ const resolveModule = options.resolveModule ?? defaultResolveModule;
55
+ const copyAsset = options.copyAsset ?? defaultCopyAsset;
56
+ if (EXTERNAL_URL.test(url)) {
57
+ return { rule: buildRule(url, opts) };
58
+ }
59
+ if (url.startsWith("/")) {
60
+ return { rule: buildRule(url, opts) };
61
+ }
62
+ if (url.startsWith("./") || url.startsWith("../")) {
63
+ const absolute2 = resolve(dirname(sourceFile), url);
64
+ const fromImportsFile = relative(join(destDir, "css"), absolute2);
65
+ return { rule: buildRule(ensureRelativePrefix(toPosix(fromImportsFile)), opts) };
66
+ }
67
+ const specifier = url.startsWith("~") ? url.slice(1) : url;
68
+ const absolute = resolveModule(specifier, sourceFile);
69
+ const hash = toHash(absolute, 6);
70
+ const fileName = `${hash}-${basename(absolute)}`;
71
+ const destPath = join(destDir, "imports", fileName);
72
+ copyAsset(absolute, destPath);
73
+ return { rule: buildRule(`../imports/${fileName}`, opts) };
74
+ };
75
+ const readPackageJsonModule = async (dirname2) => {
76
+ const packageJsonContent = await getPackageJson(dirname2);
22
77
  if (!packageJsonContent) return void 0;
23
78
  return packageJsonContent.type;
24
79
  };
25
80
  let cachedModuleType;
26
- const detectCurrentModuleType = async (dirname) => {
81
+ const detectCurrentModuleType = async (dirname2) => {
27
82
  if (cachedModuleType) return cachedModuleType;
28
- const packageJsonModule = await readPackageJsonModule(dirname);
83
+ const packageJsonModule = await readPackageJsonModule(dirname2);
29
84
  if (packageJsonModule === "module") cachedModuleType = "esm";
30
85
  else if (packageJsonModule === "commonjs") cachedModuleType = "cjs";
31
86
  else if (import.meta.url.endsWith(".cjs")) cachedModuleType = "cjs";
@@ -114,10 +169,10 @@ class SaltyCompiler {
114
169
  * Get the project configuration from the .saltyrc.json file based on the current directory.
115
170
  * If no specific project configuration is found, it falls back to the default project.
116
171
  */
117
- __publicField(this, "getRCProjectConfig", async (dirname) => {
172
+ __publicField(this, "getRCProjectConfig", async (dirname2) => {
118
173
  var _a, _b;
119
- const rcFile = await this.readRCFile(dirname);
120
- const projectConfig = (_a = rcFile.projects) == null ? void 0 : _a.find((project) => dirname.endsWith(project.dir || ""));
174
+ const rcFile = await this.readRCFile(dirname2);
175
+ const projectConfig = (_a = rcFile.projects) == null ? void 0 : _a.find((project) => dirname2.endsWith(project.dir || ""));
121
176
  if (!projectConfig) return (_b = rcFile.projects) == null ? void 0 : _b.find((project) => project.dir === rcFile.defaultProject);
122
177
  return projectConfig;
123
178
  });
@@ -141,14 +196,6 @@ class SaltyCompiler {
141
196
  this.cache.destDir = destDir;
142
197
  return destDir;
143
198
  });
144
- /**
145
- * Absolute path to the `config-cache.json` written during `generateCss()`.
146
- * Plugins read this to copy the file into the production bundle output.
147
- */
148
- __publicField(this, "getConfigCachePath", async () => {
149
- const destDir = await this.getDestDir();
150
- return join(destDir, "cache/config-cache.json");
151
- });
152
199
  __publicField(this, "generateConfig", async () => {
153
200
  const rcProject = await this.getRCProjectConfig(this.projectRootDir);
154
201
  const destDir = await this.getDestDir();
@@ -182,7 +229,8 @@ ${currentFile}`;
182
229
  }
183
230
  });
184
231
  __publicField(this, "getConfigCache", async () => {
185
- const coreConfigDest = await this.getConfigCachePath();
232
+ const destDir = await this.getDestDir();
233
+ const coreConfigDest = join(destDir, "cache/config-cache.json");
186
234
  const contents = readFileSync(coreConfigDest, "utf8");
187
235
  if (!contents) throw new Error("Could not find config cache file");
188
236
  return JSON.parse(contents);
@@ -213,6 +261,7 @@ ${currentFile}`;
213
261
  mkdirSync(join(destDir, "types"));
214
262
  mkdirSync(join(destDir, "js"));
215
263
  mkdirSync(join(destDir, "cache"));
264
+ mkdirSync(join(destDir, "imports"));
216
265
  };
217
266
  if (clean) clearDistDir();
218
267
  const files = /* @__PURE__ */ new Set();
@@ -328,7 +377,7 @@ ${currentFile}`;
328
377
  });
329
378
  }
330
379
  const otherGlobalCssFiles = globalCssFiles.map((file) => `@import url('./css/${file}');`).join("\n");
331
- const globalCssFilenames = ["_variables.css", "_reset.css", "_global.css", "_templates.css"];
380
+ const globalCssFilenames = ["_imports.css", "_variables.css", "_reset.css", "_global.css", "_templates.css", "_fonts.css"];
332
381
  const importsWithData = globalCssFilenames.filter((file) => {
333
382
  try {
334
383
  const data = readFileSync(join(destDir, "css", file), "utf8");
@@ -337,9 +386,12 @@ ${currentFile}`;
337
386
  return false;
338
387
  }
339
388
  });
340
- const globalImports = importsWithData.map((file) => `@import url('./css/${file}');`);
389
+ const globalImports = importsWithData.map((file) => {
390
+ const layerSuffix = file === "_imports.css" ? " layer(imports)" : "";
391
+ return `@import url('./css/${file}')${layerSuffix};`;
392
+ });
341
393
  const generatorText = "/*!\n * Generated with Salty CSS (https://salty-css.dev)\n * Do not edit this file directly\n */\n";
342
- let cssContent = `${generatorText}@layer reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
394
+ let cssContent = `${generatorText}@layer imports, reset, global, templates, fonts, l0, l1, l2, l3, l4, l5, l6, l7, l8;
343
395
 
344
396
  ${globalImports.join(
345
397
  "\n"
@@ -385,7 +437,9 @@ ${css}
385
437
  mediaQueries: [],
386
438
  globalStyles: [],
387
439
  variables: [],
388
- templates: []
440
+ templates: [],
441
+ imports: [],
442
+ fonts: []
389
443
  };
390
444
  await Promise.all(
391
445
  [...configFiles].map(async (src) => {
@@ -395,6 +449,8 @@ ${css}
395
449
  else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
396
450
  else if (value.isDefineVariables) generationResults.variables.push(value);
397
451
  else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
452
+ else if (value.isDefineImport) generationResults.imports.push(value._setPath(src));
453
+ else if (value.isDefineFont) generationResults.fonts.push(value);
398
454
  });
399
455
  })
400
456
  );
@@ -491,32 +547,85 @@ ${css}
491
547
  const templates = mergeObjects(config.templates, generationResults.templates);
492
548
  const templateStylesString = await parseTemplates(templates);
493
549
  const templateTokens = getTemplateTypes(templates);
550
+ const templateVariantMaps = getTemplateVariantMaps(templates);
494
551
  writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
495
552
  configCacheContent.templates = templates;
553
+ const importsPath = join(destDir, "css/_imports.css");
554
+ const importRules = [];
555
+ for (const factory of generationResults.imports) {
556
+ const sourceFile = factory._path;
557
+ if (!sourceFile) continue;
558
+ for (const spec of factory._current) {
559
+ try {
560
+ const { rule } = resolveImport(spec, sourceFile, destDir);
561
+ importRules.push(rule);
562
+ } catch (e) {
563
+ const url = typeof spec === "string" ? spec : spec.url;
564
+ logger.error(`Failed to resolve defineImport(${JSON.stringify(url)}) from ${sourceFile}: ${e.message}`);
565
+ }
566
+ }
567
+ }
568
+ writeFileSync(importsPath, importRules.join("\n"));
496
569
  const configTemplateFactories = config.templates ? [defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
497
570
  const templateFactories = mergeFactories(generationResults.templates, configTemplateFactories);
498
571
  configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
572
+ const fontsStylesPath = join(destDir, "css/_fonts.css");
573
+ if (generationResults.fonts.length === 0) {
574
+ writeFileSync(fontsStylesPath, "");
575
+ } else {
576
+ const fontImports = [];
577
+ const fontBodies = [];
578
+ for (const font of generationResults.fonts) {
579
+ const { imports, body } = font._toCss();
580
+ fontImports.push(...imports);
581
+ fontBodies.push(body);
582
+ }
583
+ const importBlock = fontImports.length ? `${fontImports.join("\n")}
584
+
585
+ ` : "";
586
+ writeFileSync(fontsStylesPath, `${importBlock}@layer fonts { ${fontBodies.join(" ")} }`);
587
+ }
499
588
  const tsTokensPath = join(destDir, "types/css-tokens.d.ts");
500
589
  const tsVariableTokens = [...variableTokens].join("|");
590
+ const pascal = (str) => str.split(/[.\-_]/).filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
591
+ const templateVariantMapEntries = Object.entries(templateVariantMaps);
592
+ const hasVariantMaps = templateVariantMapEntries.some(([, pathMap]) => Object.keys(pathMap).length > 0);
593
+ const tsTemplateVariantMap = hasVariantMaps ? `type TemplateVariantTokens = {
594
+ ${templateVariantMapEntries.filter(([, pathMap]) => Object.keys(pathMap).length > 0).map(([templateKey, pathMap]) => {
595
+ const pathEntries = Object.entries(pathMap).map(
596
+ ([dotPath, axes]) => `'${dotPath}': { ${Object.entries(axes).map(([axis, valueType]) => `${axis}?: ${valueType}`).join("; ")} }`
597
+ ).join(";\n ");
598
+ return `${templateKey}: {
599
+ ${pathEntries}
600
+ }`;
601
+ }).join(";\n ")}
602
+ }` : `type TemplateVariantTokens = Record<string, Record<string, Record<string, never>>>;`;
603
+ const tsTemplateVariantAliases = templateVariantMapEntries.flatMap(
604
+ ([templateKey, pathMap]) => Object.keys(pathMap).map((dotPath) => `type ${pascal(templateKey)}${pascal(dotPath)}Variants = TemplateVariantTokens['${templateKey}']['${dotPath}'];`)
605
+ ).join("\n ");
501
606
  const tsTokensTypes = `
502
607
  // Variable types
503
- type VariableTokens = ${tsVariableTokens || `''`};
608
+ type VariableTokens = ${tsVariableTokens || `''`};
504
609
  type PropertyValueToken = \`{\${VariableTokens}}\`;
505
-
610
+
506
611
  // Template types
507
612
  type TemplateTokens = {
508
- ${Object.entries(templateTokens).map(([key, value]) => `${key}?: ${value}`).join("\n")}
613
+ ${Object.entries(templateTokens).map(([key, value]) => {
614
+ if (!value || value === "any") return `${key}?: ${value || "any"}`;
615
+ return `${key}?: ${value} | \`\${${value}}@\${string}\``;
616
+ }).join("\n")}
509
617
  }
510
-
618
+
619
+ // Template variant types (per docs/template-variants-spec.md §7)
620
+ ${tsTemplateVariantMap}
621
+ ${tsTemplateVariantAliases}
622
+
511
623
  // Media query types
512
624
  type MediaQueryKeys = ${mediaQueryKeys || `''`};
513
625
  `;
514
626
  writeFileSync(tsTokensPath, tsTokensTypes);
515
- const configCachePath = await this.getConfigCachePath();
627
+ const configCachePath = join(destDir, "cache/config-cache.json");
516
628
  writeFileSync(configCachePath, JSON.stringify(configCacheContent, null, 2));
517
- const corePackageRoot = getCorePackageRoot();
518
- const configCacheSecondaryPath = join(corePackageRoot, "cache/config-cache.json");
519
- writeFileSync(configCacheSecondaryPath, JSON.stringify(configCacheContent, null, 2));
520
629
  });
521
630
  __publicField(this, "compileSaltyFile", async (sourceFilePath, outputDirectory) => {
522
631
  const hashedName = toHash(sourceFilePath);
@@ -686,5 +795,7 @@ ${newContent}
686
795
  }
687
796
  }
688
797
  export {
689
- SaltyCompiler
798
+ SaltyCompiler,
799
+ logError as a,
800
+ logger as l
690
801
  };
package/config/index.cjs CHANGED
@@ -5,9 +5,13 @@ const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
5
5
  const defineConfig = (config) => {
6
6
  return config;
7
7
  };
8
+ exports.FontFactory = factories_index.FontFactory;
8
9
  exports.GlobalStylesFactory = factories_index.GlobalStylesFactory;
10
+ exports.ImportFactory = factories_index.ImportFactory;
9
11
  exports.VariablesFactory = factories_index.VariablesFactory;
12
+ exports.defineFont = factories_index.defineFont;
10
13
  exports.defineGlobalStyles = factories_index.defineGlobalStyles;
14
+ exports.defineImport = factories_index.defineImport;
11
15
  exports.defineMediaQuery = factories_index.defineMediaQuery;
12
16
  exports.defineVariables = factories_index.defineVariables;
13
17
  exports.TemplateFactory = defineTemplates.TemplateFactory;
package/config/index.js CHANGED
@@ -1,15 +1,19 @@
1
- import { GlobalStylesFactory, VariablesFactory, defineGlobalStyles, defineMediaQuery, defineVariables } from "../factories/index.js";
1
+ import { FontFactory, GlobalStylesFactory, ImportFactory, VariablesFactory, defineFont, defineGlobalStyles, defineImport, defineMediaQuery, defineVariables } from "../factories/index.js";
2
2
  import { T, a, d } from "../define-templates-CVhhgPnd.js";
3
3
  const defineConfig = (config) => {
4
4
  return config;
5
5
  };
6
6
  export {
7
+ FontFactory,
7
8
  GlobalStylesFactory,
9
+ ImportFactory,
8
10
  T as TemplateFactory,
9
11
  a as TemplatesFactory,
10
12
  VariablesFactory,
11
13
  defineConfig,
14
+ defineFont,
12
15
  defineGlobalStyles,
16
+ defineImport,
13
17
  defineMediaQuery,
14
18
  d as defineTemplates,
15
19
  defineVariables