@salty-css/core 0.1.0-alpha.3 → 0.1.0-alpha.31

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 (77) hide show
  1. package/README.md +209 -0
  2. package/astro-component-5hrNTCJ5.js +4 -0
  3. package/astro-component-Dj3enX6K.cjs +4 -0
  4. package/bin/commands/build.d.ts +2 -0
  5. package/bin/commands/generate.d.ts +2 -0
  6. package/bin/commands/init.d.ts +2 -0
  7. package/bin/commands/update.d.ts +2 -0
  8. package/bin/commands/version.d.ts +2 -0
  9. package/bin/confirm-install.d.ts +34 -0
  10. package/bin/context.d.ts +22 -0
  11. package/bin/detection/css-file.d.ts +5 -0
  12. package/bin/frameworks/astro.d.ts +4 -0
  13. package/bin/frameworks/index.d.ts +13 -0
  14. package/bin/frameworks/react.d.ts +2 -0
  15. package/bin/frameworks/types.d.ts +27 -0
  16. package/bin/integrations/astro.d.ts +11 -0
  17. package/bin/integrations/eslint.d.ts +6 -0
  18. package/bin/integrations/index.d.ts +21 -0
  19. package/bin/integrations/next.d.ts +9 -0
  20. package/bin/integrations/types.d.ts +29 -0
  21. package/bin/integrations/vite.d.ts +8 -0
  22. package/bin/main.cjs +653 -336
  23. package/bin/main.d.ts +8 -0
  24. package/bin/main.js +653 -336
  25. package/bin/package-json.d.ts +21 -0
  26. package/bin/saltyrc.d.ts +31 -0
  27. package/bin/templates.d.ts +14 -0
  28. package/{class-name-generator-YeSQe_Ik.js → class-name-generator-B0WkxoIg.js} +17 -2
  29. package/{class-name-generator-B2Pb2obX.cjs → class-name-generator-BEOEMEKX.cjs} +17 -2
  30. package/compiler/resolve-import.d.ts +17 -0
  31. package/compiler/salty-compiler.cjs +131 -30
  32. package/compiler/salty-compiler.d.ts +8 -1
  33. package/compiler/salty-compiler.js +133 -31
  34. package/config/index.cjs +4 -0
  35. package/config/index.js +5 -1
  36. package/css/dynamic-styles.cjs +15 -0
  37. package/css/dynamic-styles.d.ts +10 -0
  38. package/css/dynamic-styles.js +15 -0
  39. package/css/index.cjs +3 -0
  40. package/css/index.d.ts +1 -0
  41. package/css/index.js +3 -0
  42. package/css/keyframes.cjs +1 -1
  43. package/css/keyframes.js +1 -1
  44. package/factories/define-font.d.ts +28 -0
  45. package/factories/define-import.d.ts +14 -0
  46. package/factories/index.cjs +141 -0
  47. package/factories/index.d.ts +2 -0
  48. package/factories/index.js +141 -0
  49. package/generators/index.cjs +1 -1
  50. package/generators/index.js +2 -2
  51. package/instances/classname-instance.cjs +1 -1
  52. package/instances/classname-instance.js +1 -1
  53. package/package.json +5 -1
  54. package/parse-styles-BBJ3PWyV.js +514 -0
  55. package/parse-styles-lOMGe_c5.cjs +513 -0
  56. package/parsers/index.cjs +93 -3
  57. package/parsers/index.d.ts +1 -0
  58. package/parsers/index.js +97 -7
  59. package/parsers/parse-templates.d.ts +10 -0
  60. package/parsers/parser-regexes.d.ts +3 -0
  61. package/parsers/resolve-template-variants.d.ts +21 -0
  62. package/parsers/strict.d.ts +2 -0
  63. package/runtime/index.cjs +1 -1
  64. package/runtime/index.js +1 -1
  65. package/{salty.config-cqavVm2t.cjs → salty.config-DogY_sSQ.cjs} +1 -1
  66. package/salty.config-GV37Q-D2.js +4 -0
  67. package/styled-file-BzmB9_Ez.cjs +12 -0
  68. package/{react-styled-file-U02jek-B.cjs → styled-file-CPd_rTW2.cjs} +2 -2
  69. package/{react-styled-file-B99mwk0w.js → styled-file-Cda3EeR6.js} +2 -2
  70. package/styled-file-DLcgYmGN.js +12 -0
  71. package/types/config-types.d.ts +42 -2
  72. package/types/font-types.d.ts +53 -0
  73. package/{react-vanilla-file-D9px70iK.js → vanilla-file-1kOqbCIM.js} +2 -2
  74. package/{react-vanilla-file-Bj6XC8GS.cjs → vanilla-file-r0fp2q_m.cjs} +2 -2
  75. package/parse-styles-BTIoYnBr.js +0 -232
  76. package/parse-styles-CA3TP5n1.cjs +0 -231
  77. package/salty.config-DjosWdPw.js +0 -4
@@ -0,0 +1,21 @@
1
+ import { PathLike } from 'fs';
2
+ export declare const defaultPackageJsonPath: string;
3
+ export interface PackageJson {
4
+ name?: string;
5
+ version?: string;
6
+ scripts?: Record<string, string>;
7
+ dependencies?: Record<string, string>;
8
+ devDependencies?: Record<string, string>;
9
+ [key: string]: unknown;
10
+ }
11
+ export declare const readPackageJson: (filePath?: PathLike) => Promise<PackageJson>;
12
+ export declare const updatePackageJson: (content: string | object, filePath?: PathLike) => Promise<void>;
13
+ export declare const readThisPackageJson: () => Promise<PackageJson>;
14
+ export declare const corePackages: {
15
+ core: (version: string) => string;
16
+ eslintConfigCore: (version: string) => string;
17
+ };
18
+ export declare const addPrepareScript: (pkg: PackageJson) => {
19
+ changed: boolean;
20
+ pkg: PackageJson;
21
+ };
@@ -0,0 +1,31 @@
1
+ import { RCFile } from '../types/cli-types';
2
+ import { FrameworkAdapter } from './frameworks';
3
+ export declare const SALTYRC_FILENAME = ".saltyrc.json";
4
+ export declare const SALTYRC_SCHEMA = "./node_modules/@salty-css/core/.saltyrc.schema.json";
5
+ export declare const SALTYRC_INFO = "This file is used to define projects and their configurations for Salty CSS cli. Do not delete, modify or add this file to .gitignore.";
6
+ export interface ProjectEntry {
7
+ dir: string;
8
+ framework: string;
9
+ [key: string]: unknown;
10
+ }
11
+ export declare const saltyrcPath: (rootDir?: string) => string;
12
+ export declare const readRc: (rootDir?: string) => Promise<RCFile>;
13
+ export declare const readRawRc: (rootDir?: string) => Promise<string | undefined>;
14
+ export declare const getDefaultProject: (rootDir?: string) => Promise<string | undefined>;
15
+ /**
16
+ * Pure transform: returns the new saltyrc content for an upsert. Does not touch the filesystem.
17
+ * - If `existingRaw` is undefined, returns a fresh file with the project as the only entry.
18
+ * - If the project entry already exists, returns the same content (no-op).
19
+ * - Otherwise appends the new entry and returns updated content.
20
+ */
21
+ export declare const upsertProjectInRc: (existingRaw: string | undefined, relativeProjectPath: string, framework: FrameworkAdapter) => {
22
+ content: string;
23
+ changed: boolean;
24
+ created: boolean;
25
+ };
26
+ /**
27
+ * Writes the saltyrc file, creating or updating the project entry for the given dir.
28
+ * Returns true when a write occurred.
29
+ */
30
+ export declare const writeProjectToRc: (cwd: string, relativeProjectPath: string, framework: FrameworkAdapter) => Promise<boolean>;
31
+ export declare const getProjectFramework: (rc: RCFile, relativeProjectPath: string) => string | undefined;
@@ -0,0 +1,14 @@
1
+ declare const templateLoaders: {
2
+ readonly 'salty.config.ts': () => Promise<any>;
3
+ readonly 'saltygen/index.css': () => Promise<any>;
4
+ readonly 'react/styled-file.ts': () => Promise<any>;
5
+ readonly 'react/vanilla-file.ts': () => Promise<any>;
6
+ readonly 'astro/styled-file.ts': () => Promise<any>;
7
+ readonly 'astro/component.astro': () => Promise<any>;
8
+ };
9
+ export type TemplateKey = keyof typeof templateLoaders;
10
+ export declare const readTemplate: <T extends object>(key: TemplateKey, options?: T) => Promise<{
11
+ fileName: "salty.config.ts" | "saltygen/index.css" | "react/styled-file.ts" | "react/vanilla-file.ts" | "astro/styled-file.ts" | "astro/component.astro";
12
+ content: string;
13
+ }>;
14
+ export {};
@@ -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, b as parseTemplateCallSite } from "./parse-styles-BBJ3PWyV.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 {
@@ -50,7 +50,22 @@ class StylesGenerator {
50
50
  if (!(config == null ? void 0 : config.templates) || !this.params.base || this.priority > 0) return [];
51
51
  const templateKeys = Object.keys(config.templates);
52
52
  return Object.entries(this.params.base).reduce((acc, [key, value]) => {
53
- if (templateKeys.includes(key)) acc.push("t_" + toHash(dashCase(`${key}-${value}`), 4));
53
+ if (!templateKeys.includes(key)) return acc;
54
+ const callSite = parseTemplateCallSite(value);
55
+ if (!callSite || callSite.path.length === 0) return acc;
56
+ const { path, variants } = callSite;
57
+ const cumulative = [];
58
+ for (const part of path) {
59
+ cumulative.push(part);
60
+ const baseClassName = dashCase(`${key}-${cumulative.join(".")}`);
61
+ acc.push("t_" + toHash(baseClassName, 4));
62
+ for (const [axis, axisValue] of Object.entries(variants)) {
63
+ if (axisValue === false || axisValue === void 0) continue;
64
+ const valueSuffix = axisValue === true ? "" : `-${dashCase(String(axisValue))}`;
65
+ const variantClassName = `${baseClassName}-${dashCase(axis)}${valueSuffix}`;
66
+ acc.push("tv_" + toHash(variantClassName, 4));
67
+ }
68
+ }
54
69
  return acc;
55
70
  }, []);
56
71
  }
@@ -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-lOMGe_c5.cjs");
6
6
  const dashCase = require("./dash-case-DIwKaYgE.cjs");
7
7
  const toHash = require("./to-hash-C05Y906F.cjs");
8
8
  class StylesGenerator {
@@ -51,7 +51,22 @@ class StylesGenerator {
51
51
  if (!(config == null ? void 0 : config.templates) || !this.params.base || this.priority > 0) return [];
52
52
  const templateKeys = Object.keys(config.templates);
53
53
  return Object.entries(this.params.base).reduce((acc, [key, value]) => {
54
- if (templateKeys.includes(key)) acc.push("t_" + toHash.toHash(dashCase.dashCase(`${key}-${value}`), 4));
54
+ if (!templateKeys.includes(key)) return acc;
55
+ const callSite = parseStyles.parseTemplateCallSite(value);
56
+ if (!callSite || callSite.path.length === 0) return acc;
57
+ const { path, variants } = callSite;
58
+ const cumulative = [];
59
+ for (const part of path) {
60
+ cumulative.push(part);
61
+ const baseClassName = dashCase.dashCase(`${key}-${cumulative.join(".")}`);
62
+ acc.push("t_" + toHash.toHash(baseClassName, 4));
63
+ for (const [axis, axisValue] of Object.entries(variants)) {
64
+ if (axisValue === false || axisValue === void 0) continue;
65
+ const valueSuffix = axisValue === true ? "" : `-${dashCase.dashCase(String(axisValue))}`;
66
+ const variantClassName = `${baseClassName}-${dashCase.dashCase(axis)}${valueSuffix}`;
67
+ acc.push("tv_" + toHash.toHash(variantClassName, 4));
68
+ }
69
+ }
55
70
  return acc;
56
71
  }, []);
57
72
  }
@@ -0,0 +1,17 @@
1
+ import { ImportSpec } from '../factories/define-import';
2
+ export interface ResolveImportOptions {
3
+ /**
4
+ * Override for node_modules resolution. Receives the bare specifier (with any leading `~` already
5
+ * stripped) and the source file path of the salty file that called `defineImport`. Must return the
6
+ * absolute filesystem path of the resolved CSS file. Defaults to `createRequire(sourceFile).resolve`.
7
+ */
8
+ resolveModule?: (specifier: string, sourceFile: string) => string;
9
+ /**
10
+ * Override for copying resolved node_modules CSS into `<destDir>/imports/`. Receives the absolute
11
+ * source and destination paths. Defaults to `copyFileSync`.
12
+ */
13
+ copyAsset?: (from: string, to: string) => void;
14
+ }
15
+ export declare const resolveImport: (spec: ImportSpec, sourceFile: string, destDir: string, options?: ResolveImportOptions) => {
16
+ rule: string;
17
+ };
@@ -10,15 +10,15 @@ 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 defineTemplates = require("../define-templates-Deq1aCbN.cjs");
14
13
  const dashCase = require("../dash-case-DIwKaYgE.cjs");
15
14
  const toHash = require("../to-hash-C05Y906F.cjs");
16
- const parseStyles = require("../parse-styles-CA3TP5n1.cjs");
15
+ const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
16
+ const module$1 = require("module");
17
+ const parseStyles = require("../parse-styles-lOMGe_c5.cjs");
17
18
  const css_merge = require("../css/merge.cjs");
18
- const compiler_getFiles = require("./get-files.cjs");
19
19
  const parsers_index = require("../parsers/index.cjs");
20
+ const moduleType = require("../util/module-type");
20
21
  const console = require("console");
21
- var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
22
22
  function _interopNamespaceDefault(e) {
23
23
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
24
24
  if (e) {
@@ -44,19 +44,53 @@ const logger = winston.createLogger({
44
44
  const logError = (message) => {
45
45
  logger.error(message);
46
46
  };
47
- const readPackageJsonModule = async (dirname) => {
48
- const packageJsonContent = await compiler_getFiles.getPackageJson(dirname);
49
- if (!packageJsonContent) return void 0;
50
- return packageJsonContent.type;
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);
51
64
  };
52
- let cachedModuleType;
53
- const detectCurrentModuleType = async (dirname) => {
54
- if (cachedModuleType) return cachedModuleType;
55
- const packageJsonModule = await readPackageJsonModule(dirname);
56
- if (packageJsonModule === "module") cachedModuleType = "esm";
57
- else if (packageJsonModule === "commonjs") cachedModuleType = "cjs";
58
- else if ((typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("compiler/salty-compiler.cjs", document.baseURI).href).endsWith(".cjs")) cachedModuleType = "cjs";
59
- return cachedModuleType || "esm";
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) };
60
94
  };
61
95
  function dotCase(str) {
62
96
  if (!str) return "";
@@ -111,7 +145,7 @@ const saltyReset = {
111
145
  }
112
146
  };
113
147
  class SaltyCompiler {
114
- constructor(projectRootDir) {
148
+ constructor(projectRootDir, options = {}) {
115
149
  __publicField(this, "importFile", (path2) => {
116
150
  const now = Date.now();
117
151
  return import(
@@ -173,7 +207,7 @@ class SaltyCompiler {
173
207
  const destDir = await this.getDestDir();
174
208
  const coreConfigPath = path.join(this.projectRootDir, (rcProject == null ? void 0 : rcProject.configDir) || "", "salty.config.ts");
175
209
  const coreConfigDest = path.join(destDir, "salty.config.js");
176
- const moduleType = await detectCurrentModuleType(this.projectRootDir);
210
+ const moduleType$1 = await moduleType.detectCurrentModuleType(this.projectRootDir);
177
211
  const externalModules = this.getExternalModules(coreConfigPath);
178
212
  await esbuild__namespace.build({
179
213
  entryPoints: [coreConfigPath],
@@ -181,7 +215,7 @@ class SaltyCompiler {
181
215
  treeShaking: true,
182
216
  bundle: true,
183
217
  outfile: coreConfigDest,
184
- format: moduleType,
218
+ format: moduleType$1,
185
219
  external: externalModules
186
220
  });
187
221
  const { config } = await this.importFile(coreConfigDest);
@@ -233,6 +267,7 @@ ${currentFile}`;
233
267
  fs.mkdirSync(path.join(destDir, "types"));
234
268
  fs.mkdirSync(path.join(destDir, "js"));
235
269
  fs.mkdirSync(path.join(destDir, "cache"));
270
+ fs.mkdirSync(path.join(destDir, "imports"));
236
271
  };
237
272
  if (clean) clearDistDir();
238
273
  const files = /* @__PURE__ */ new Set();
@@ -348,7 +383,7 @@ ${currentFile}`;
348
383
  });
349
384
  }
350
385
  const otherGlobalCssFiles = globalCssFiles.map((file) => `@import url('./css/${file}');`).join("\n");
351
- const globalCssFilenames = ["_variables.css", "_reset.css", "_global.css", "_templates.css"];
386
+ const globalCssFilenames = ["_imports.css", "_variables.css", "_reset.css", "_global.css", "_templates.css", "_fonts.css"];
352
387
  const importsWithData = globalCssFilenames.filter((file) => {
353
388
  try {
354
389
  const data = fs.readFileSync(path.join(destDir, "css", file), "utf8");
@@ -357,9 +392,12 @@ ${currentFile}`;
357
392
  return false;
358
393
  }
359
394
  });
360
- const globalImports = importsWithData.map((file) => `@import url('./css/${file}');`);
395
+ const globalImports = importsWithData.map((file) => {
396
+ const layerSuffix = file === "_imports.css" ? " layer(imports)" : "";
397
+ return `@import url('./css/${file}')${layerSuffix};`;
398
+ });
361
399
  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;
400
+ let cssContent = `${generatorText}@layer imports, reset, global, templates, fonts, l0, l1, l2, l3, l4, l5, l6, l7, l8;
363
401
 
364
402
  ${globalImports.join(
365
403
  "\n"
@@ -405,7 +443,9 @@ ${css}
405
443
  mediaQueries: [],
406
444
  globalStyles: [],
407
445
  variables: [],
408
- templates: []
446
+ templates: [],
447
+ imports: [],
448
+ fonts: []
409
449
  };
410
450
  await Promise.all(
411
451
  [...configFiles].map(async (src) => {
@@ -415,6 +455,8 @@ ${css}
415
455
  else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
416
456
  else if (value.isDefineVariables) generationResults.variables.push(value);
417
457
  else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
458
+ else if (value.isDefineImport) generationResults.imports.push(value._setPath(src));
459
+ else if (value.isDefineFont) generationResults.fonts.push(value);
418
460
  });
419
461
  })
420
462
  );
@@ -511,23 +553,79 @@ ${css}
511
553
  const templates = css_merge.mergeObjects(config.templates, generationResults.templates);
512
554
  const templateStylesString = await parsers_index.parseTemplates(templates);
513
555
  const templateTokens = parsers_index.getTemplateTypes(templates);
556
+ const templateVariantMaps = parsers_index.getTemplateVariantMaps(templates);
514
557
  fs.writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
515
558
  configCacheContent.templates = templates;
559
+ const importsPath = path.join(destDir, "css/_imports.css");
560
+ const importRules = [];
561
+ for (const factory of generationResults.imports) {
562
+ const sourceFile = factory._path;
563
+ if (!sourceFile) continue;
564
+ for (const spec of factory._current) {
565
+ try {
566
+ const { rule } = resolveImport(spec, sourceFile, destDir);
567
+ importRules.push(rule);
568
+ } catch (e) {
569
+ const url = typeof spec === "string" ? spec : spec.url;
570
+ logger.error(`Failed to resolve defineImport(${JSON.stringify(url)}) from ${sourceFile}: ${e.message}`);
571
+ }
572
+ }
573
+ }
574
+ fs.writeFileSync(importsPath, importRules.join("\n"));
516
575
  const configTemplateFactories = config.templates ? [defineTemplates.defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
517
576
  const templateFactories = css_merge.mergeFactories(generationResults.templates, configTemplateFactories);
518
577
  configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
578
+ const fontsStylesPath = path.join(destDir, "css/_fonts.css");
579
+ if (generationResults.fonts.length === 0) {
580
+ fs.writeFileSync(fontsStylesPath, "");
581
+ } else {
582
+ const fontImports = [];
583
+ const fontBodies = [];
584
+ for (const font of generationResults.fonts) {
585
+ const { imports, body } = font._toCss();
586
+ fontImports.push(...imports);
587
+ fontBodies.push(body);
588
+ }
589
+ const importBlock = fontImports.length ? `${fontImports.join("\n")}
590
+
591
+ ` : "";
592
+ fs.writeFileSync(fontsStylesPath, `${importBlock}@layer fonts { ${fontBodies.join(" ")} }`);
593
+ }
519
594
  const tsTokensPath = path.join(destDir, "types/css-tokens.d.ts");
520
595
  const tsVariableTokens = [...variableTokens].join("|");
596
+ const pascal = (str) => str.split(/[.\-_]/).filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
597
+ const templateVariantMapEntries = Object.entries(templateVariantMaps);
598
+ const hasVariantMaps = templateVariantMapEntries.some(([, pathMap]) => Object.keys(pathMap).length > 0);
599
+ const tsTemplateVariantMap = hasVariantMaps ? `type TemplateVariantTokens = {
600
+ ${templateVariantMapEntries.filter(([, pathMap]) => Object.keys(pathMap).length > 0).map(([templateKey, pathMap]) => {
601
+ const pathEntries = Object.entries(pathMap).map(
602
+ ([dotPath, axes]) => `'${dotPath}': { ${Object.entries(axes).map(([axis, valueType]) => `${axis}?: ${valueType}`).join("; ")} }`
603
+ ).join(";\n ");
604
+ return `${templateKey}: {
605
+ ${pathEntries}
606
+ }`;
607
+ }).join(";\n ")}
608
+ }` : `type TemplateVariantTokens = Record<string, Record<string, Record<string, never>>>;`;
609
+ const tsTemplateVariantAliases = templateVariantMapEntries.flatMap(
610
+ ([templateKey, pathMap]) => Object.keys(pathMap).map((dotPath) => `type ${pascal(templateKey)}${pascal(dotPath)}Variants = TemplateVariantTokens['${templateKey}']['${dotPath}'];`)
611
+ ).join("\n ");
521
612
  const tsTokensTypes = `
522
613
  // Variable types
523
- type VariableTokens = ${tsVariableTokens || `''`};
614
+ type VariableTokens = ${tsVariableTokens || `''`};
524
615
  type PropertyValueToken = \`{\${VariableTokens}}\`;
525
-
616
+
526
617
  // Template types
527
618
  type TemplateTokens = {
528
- ${Object.entries(templateTokens).map(([key, value]) => `${key}?: ${value}`).join("\n")}
619
+ ${Object.entries(templateTokens).map(([key, value]) => {
620
+ if (!value || value === "any") return `${key}?: ${value || "any"}`;
621
+ return `${key}?: ${value} | \`\${${value}}@\${string}\``;
622
+ }).join("\n")}
529
623
  }
530
-
624
+
625
+ // Template variant types (per docs/template-variants-spec.md §7)
626
+ ${tsTemplateVariantMap}
627
+ ${tsTemplateVariantAliases}
628
+
531
629
  // Media query types
532
630
  type MediaQueryKeys = ${mediaQueryKeys || `''`};
533
631
  `;
@@ -546,11 +644,12 @@ ${css}
546
644
  let currentFile = fs.readFileSync(sourceFilePath, "utf8");
547
645
  currentFile = this.replaceStyledTag(currentFile);
548
646
  currentFile = this.addConfigCache(currentFile);
549
- const outputFilePath = path.join(outputDirectory, "js", hashedName + ".js");
647
+ const currentFileHash = toHash.toHash(currentFile);
648
+ const outputFilePath = path.join(outputDirectory, "js", `${hashedName}-${currentFileHash}.js`);
550
649
  const rcProject = await this.getRCProjectConfig(this.projectRootDir);
551
650
  const coreConfigPath = path.join(this.projectRootDir, (rcProject == null ? void 0 : rcProject.configDir) || "", "salty.config.ts");
552
651
  const externalModules = this.getExternalModules(coreConfigPath);
553
- const moduleType = await detectCurrentModuleType(this.projectRootDir);
652
+ const moduleType$1 = await moduleType.detectCurrentModuleType(this.projectRootDir);
554
653
  await esbuild__namespace.build({
555
654
  stdin: {
556
655
  contents: currentFile,
@@ -562,7 +661,7 @@ ${css}
562
661
  treeShaking: true,
563
662
  bundle: true,
564
663
  outfile: outputFilePath,
565
- format: moduleType,
664
+ format: moduleType$1,
566
665
  target: ["node20"],
567
666
  keepNames: true,
568
667
  external: externalModules,
@@ -690,11 +789,13 @@ ${newContent}
690
789
  });
691
790
  });
692
791
  this.projectRootDir = projectRootDir;
792
+ this.options = options;
693
793
  if (typeof process === "undefined") {
694
794
  throw new Error("SaltyServer can only be used in a Node.js environment.");
695
795
  }
696
796
  }
697
797
  get isProduction() {
798
+ if (this.options.mode) return this.options.mode === "production";
698
799
  try {
699
800
  return process.env["NODE_ENV"] !== "development";
700
801
  } catch {
@@ -1,9 +1,14 @@
1
1
  import { CachedConfig, SaltyConfig } from '../config';
2
+ export type SaltyCompilerMode = 'production' | 'development';
3
+ export interface SaltyCompilerOptions {
4
+ mode?: SaltyCompilerMode;
5
+ }
2
6
  export declare class SaltyCompiler {
3
7
  projectRootDir: string;
8
+ private options;
4
9
  importFile: (path: string) => Promise<any>;
5
10
  private cache;
6
- constructor(projectRootDir: string);
11
+ constructor(projectRootDir: string, options?: SaltyCompilerOptions);
7
12
  get isProduction(): boolean;
8
13
  /**
9
14
  * Locate and read the .saltyrc.json file starting from the current directory and moving up the directory tree.
@@ -39,6 +44,8 @@ export declare class SaltyCompiler {
39
44
  isGlobalDefine?: boolean;
40
45
  isDefineVariables?: boolean;
41
46
  isDefineTemplates?: boolean;
47
+ isDefineImport?: boolean;
48
+ isDefineFont?: boolean;
42
49
  isKeyframes?: boolean;
43
50
  animationName?: string;
44
51
  css?: Promise<string>;