@salty-css/core 0.1.0-alpha.8 → 0.1.0-feat-define-font.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 (41) hide show
  1. package/README.md +83 -0
  2. package/bin/confirm-install.d.ts +34 -0
  3. package/bin/context.d.ts +3 -0
  4. package/bin/integrations/index.d.ts +11 -3
  5. package/bin/integrations/types.d.ts +14 -2
  6. package/bin/main.cjs +149 -48
  7. package/bin/main.js +149 -48
  8. package/{class-name-generator-YeSQe_Ik.js → class-name-generator-B2LriwKm.js} +1 -1
  9. package/{class-name-generator-B2Pb2obX.cjs → class-name-generator-BIYysuhW.cjs} +1 -1
  10. package/compiler/salty-compiler.cjs +28 -7
  11. package/compiler/salty-compiler.d.ts +7 -1
  12. package/compiler/salty-compiler.js +28 -7
  13. package/config/index.cjs +2 -0
  14. package/config/index.js +3 -1
  15. package/css/dynamic-styles.cjs +1 -1
  16. package/css/dynamic-styles.js +1 -1
  17. package/css/keyframes.cjs +1 -1
  18. package/css/keyframes.js +1 -1
  19. package/factories/define-font.d.ts +28 -0
  20. package/factories/index.cjs +125 -0
  21. package/factories/index.d.ts +1 -0
  22. package/factories/index.js +125 -0
  23. package/generators/index.cjs +1 -1
  24. package/generators/index.js +2 -2
  25. package/instances/classname-instance.cjs +1 -1
  26. package/instances/classname-instance.js +1 -1
  27. package/package.json +1 -1
  28. package/{parse-styles-BTIoYnBr.js → parse-styles--vHKY6Mw.js} +108 -8
  29. package/{parse-styles-CA3TP5n1.cjs → parse-styles-jPtMfgXH.cjs} +107 -7
  30. package/parsers/index.cjs +2 -1
  31. package/parsers/index.d.ts +1 -0
  32. package/parsers/index.js +4 -3
  33. package/parsers/parser-regexes.d.ts +3 -0
  34. package/parsers/strict.d.ts +2 -0
  35. package/runtime/index.cjs +1 -1
  36. package/runtime/index.js +1 -1
  37. package/{salty.config-cqavVm2t.cjs → salty.config-DogY_sSQ.cjs} +1 -1
  38. package/salty.config-GV37Q-D2.js +4 -0
  39. package/types/config-types.d.ts +10 -1
  40. package/types/font-types.d.ts +53 -0
  41. package/salty.config-DjosWdPw.js +0 -4
@@ -0,0 +1,28 @@
1
+ import { DefineFontOptions } from '../types/font-types';
2
+ export interface FontCss {
3
+ /** `@import url(...)` lines that must sit at the top of the stylesheet, before any `@layer`. */
4
+ imports: string[];
5
+ /** Body that goes inside the `@layer fonts { ... }` wrapper. */
6
+ body: string;
7
+ }
8
+ export declare class FontFactory {
9
+ readonly _options: DefineFontOptions;
10
+ readonly variable: string;
11
+ readonly fontFamily: string;
12
+ readonly className: string;
13
+ constructor(_options: DefineFontOptions);
14
+ get isDefineFont(): true;
15
+ /** Acts as a string equal to the resolved font-family value. */
16
+ toString(): string;
17
+ /** Inline-style helper: spread onto a React `style` prop. */
18
+ get style(): Record<string, string>;
19
+ /** Build the CSS pieces written to `_fonts.css`. */
20
+ _toCss(): FontCss;
21
+ }
22
+ /**
23
+ * Define a custom font that is registered globally as `@font-face` and exposed
24
+ * as a CSS variable. The returned object stringifies to its `font-family`
25
+ * value, and exposes `.variable`, `.fontFamily`, `.className`, and `.style`
26
+ * for use in styles and components.
27
+ */
28
+ export declare const defineFont: (options: DefineFontOptions) => FontFactory;
@@ -1,7 +1,130 @@
1
1
  "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
2
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const dashCase = require("../dash-case-DIwKaYgE.cjs");
7
+ const toHash = require("../to-hash-C05Y906F.cjs");
3
8
  const css_media = require("../css/media.cjs");
4
9
  const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
10
+ const FONT_FORMAT_BY_EXTENSION = {
11
+ woff2: "woff2",
12
+ woff: "woff",
13
+ ttf: "truetype",
14
+ otf: "opentype",
15
+ eot: "embedded-opentype",
16
+ svg: "svg",
17
+ ttc: "collection"
18
+ };
19
+ const detectFontFormat = (url) => {
20
+ const cleaned = url.split("?")[0].split("#")[0];
21
+ const dot = cleaned.lastIndexOf(".");
22
+ if (dot === -1) return void 0;
23
+ const ext = cleaned.slice(dot + 1).toLowerCase();
24
+ return FONT_FORMAT_BY_EXTENSION[ext];
25
+ };
26
+ const toFontSrc = (entry) => {
27
+ if (typeof entry === "string") return { url: entry, format: detectFontFormat(entry) };
28
+ return entry;
29
+ };
30
+ const normalizeSources = (src) => {
31
+ if (Array.isArray(src)) return src.map(toFontSrc);
32
+ return [toFontSrc(src)];
33
+ };
34
+ const normalizeVariable = (variable) => {
35
+ const trimmed = variable.trim();
36
+ const stripped = trimmed.replace(/^--/, "");
37
+ if (!stripped) throw new Error(`defineFont: invalid \`variable\` value "${variable}".`);
38
+ return `--${dashCase.dashCase(stripped)}`;
39
+ };
40
+ const deriveVariable = (options) => {
41
+ const hashSource = [options.name, options.fallback, "variants" in options ? options.variants : void 0, "import" in options ? options.import : void 0];
42
+ return `--font-${dashCase.dashCase(options.name)}-${toHash.toHash(hashSource, 6)}`;
43
+ };
44
+ const quoteFamily = (name) => {
45
+ if (/^["'].*["']$/.test(name)) return name;
46
+ if (/\s/.test(name)) return `"${name}"`;
47
+ return name;
48
+ };
49
+ const buildFontFamilyValue = (name, fallback) => {
50
+ const head = quoteFamily(name);
51
+ if (!fallback || fallback.length === 0) return head;
52
+ return [head, fallback].join(", ");
53
+ };
54
+ const formatSrc = (src) => {
55
+ const parts = [`url("${src.url}")`];
56
+ if (src.format) parts.push(`format("${src.format}")`);
57
+ if (src.tech) parts.push(`tech(${src.tech})`);
58
+ return parts.join(" ");
59
+ };
60
+ const variantToFontFace = (name, variant, defaultDisplay) => {
61
+ const sources = normalizeSources(variant.src);
62
+ if (sources.length === 0) {
63
+ throw new Error(`defineFont(${name}): variant must declare at least one \`src\`.`);
64
+ }
65
+ const lines = [`font-family: ${quoteFamily(name)};`, `src: ${sources.map(formatSrc).join(", ")};`, `font-display: ${variant.display ?? defaultDisplay};`];
66
+ if (variant.weight !== void 0) lines.push(`font-weight: ${variant.weight};`);
67
+ if (variant.style !== void 0) lines.push(`font-style: ${variant.style};`);
68
+ if (variant.stretch !== void 0) lines.push(`font-stretch: ${variant.stretch};`);
69
+ if (variant.unicodeRange !== void 0) lines.push(`unicode-range: ${variant.unicodeRange};`);
70
+ if (variant.ascentOverride !== void 0) lines.push(`ascent-override: ${variant.ascentOverride};`);
71
+ if (variant.descentOverride !== void 0) lines.push(`descent-override: ${variant.descentOverride};`);
72
+ if (variant.lineGapOverride !== void 0) lines.push(`line-gap-override: ${variant.lineGapOverride};`);
73
+ if (variant.sizeAdjust !== void 0) lines.push(`size-adjust: ${variant.sizeAdjust};`);
74
+ return `@font-face { ${lines.join(" ")} }`;
75
+ };
76
+ class FontFactory {
77
+ constructor(_options) {
78
+ __publicField(this, "variable");
79
+ __publicField(this, "fontFamily");
80
+ __publicField(this, "className");
81
+ this._options = _options;
82
+ if (!_options || !_options.name) {
83
+ throw new Error("defineFont: `name` is required.");
84
+ }
85
+ if ("variants" in _options && "import" in _options && _options.import !== void 0 && _options.variants !== void 0) {
86
+ throw new Error("defineFont: provide either `variants` or `import`, not both.");
87
+ }
88
+ if (!("variants" in _options && _options.variants) && !("import" in _options && _options.import)) {
89
+ throw new Error("defineFont: must provide either `variants` or `import`.");
90
+ }
91
+ this.variable = _options.variable ? normalizeVariable(_options.variable) : deriveVariable(_options);
92
+ this.fontFamily = buildFontFamilyValue(_options.name, _options.fallback);
93
+ this.className = `font-${dashCase.dashCase(_options.name)}`;
94
+ }
95
+ get isDefineFont() {
96
+ return true;
97
+ }
98
+ /** Acts as a string equal to the resolved font-family value. */
99
+ toString() {
100
+ return this.fontFamily;
101
+ }
102
+ /** Inline-style helper: spread onto a React `style` prop. */
103
+ get style() {
104
+ return {
105
+ fontFamily: this.fontFamily,
106
+ [this.variable]: this.fontFamily
107
+ };
108
+ }
109
+ /** Build the CSS pieces written to `_fonts.css`. */
110
+ _toCss() {
111
+ const imports = [];
112
+ const blocks = [];
113
+ if ("import" in this._options && this._options.import) {
114
+ imports.push(`@import url("${this._options.import}");`);
115
+ } else if ("variants" in this._options && this._options.variants) {
116
+ const display = this._options.display ?? "swap";
117
+ for (const variant of this._options.variants) {
118
+ blocks.push(variantToFontFace(this._options.name, variant, display));
119
+ }
120
+ }
121
+ blocks.push(`:root { ${this.variable}: ${this.fontFamily}; }`, `.${this.className} { font-family: var(${this.variable}); }`);
122
+ return { imports, body: blocks.join(" ") };
123
+ }
124
+ }
125
+ const defineFont = (options) => {
126
+ return new FontFactory(options);
127
+ };
5
128
  class GlobalStylesFactory {
6
129
  constructor(_current) {
7
130
  this._current = _current;
@@ -30,8 +153,10 @@ const defineVariables = (variables) => {
30
153
  exports.TemplateFactory = defineTemplates.TemplateFactory;
31
154
  exports.TemplatesFactory = defineTemplates.TemplatesFactory;
32
155
  exports.defineTemplates = defineTemplates.defineTemplates;
156
+ exports.FontFactory = FontFactory;
33
157
  exports.GlobalStylesFactory = GlobalStylesFactory;
34
158
  exports.VariablesFactory = VariablesFactory;
159
+ exports.defineFont = defineFont;
35
160
  exports.defineGlobalStyles = defineGlobalStyles;
36
161
  exports.defineMediaQuery = defineMediaQuery;
37
162
  exports.defineVariables = defineVariables;
@@ -1,3 +1,4 @@
1
+ export * from './define-font';
1
2
  export * from './define-global-styles';
2
3
  export * from './define-media-query';
3
4
  export * from './define-variables';
@@ -1,5 +1,128 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { d as dashCase } from "../dash-case-DblXvymC.js";
5
+ import { t as toHash } from "../to-hash-DAN2LcHK.js";
1
6
  import { media } from "../css/media.js";
2
7
  import { T, a, d } from "../define-templates-CVhhgPnd.js";
8
+ const FONT_FORMAT_BY_EXTENSION = {
9
+ woff2: "woff2",
10
+ woff: "woff",
11
+ ttf: "truetype",
12
+ otf: "opentype",
13
+ eot: "embedded-opentype",
14
+ svg: "svg",
15
+ ttc: "collection"
16
+ };
17
+ const detectFontFormat = (url) => {
18
+ const cleaned = url.split("?")[0].split("#")[0];
19
+ const dot = cleaned.lastIndexOf(".");
20
+ if (dot === -1) return void 0;
21
+ const ext = cleaned.slice(dot + 1).toLowerCase();
22
+ return FONT_FORMAT_BY_EXTENSION[ext];
23
+ };
24
+ const toFontSrc = (entry) => {
25
+ if (typeof entry === "string") return { url: entry, format: detectFontFormat(entry) };
26
+ return entry;
27
+ };
28
+ const normalizeSources = (src) => {
29
+ if (Array.isArray(src)) return src.map(toFontSrc);
30
+ return [toFontSrc(src)];
31
+ };
32
+ const normalizeVariable = (variable) => {
33
+ const trimmed = variable.trim();
34
+ const stripped = trimmed.replace(/^--/, "");
35
+ if (!stripped) throw new Error(`defineFont: invalid \`variable\` value "${variable}".`);
36
+ return `--${dashCase(stripped)}`;
37
+ };
38
+ const deriveVariable = (options) => {
39
+ const hashSource = [options.name, options.fallback, "variants" in options ? options.variants : void 0, "import" in options ? options.import : void 0];
40
+ return `--font-${dashCase(options.name)}-${toHash(hashSource, 6)}`;
41
+ };
42
+ const quoteFamily = (name) => {
43
+ if (/^["'].*["']$/.test(name)) return name;
44
+ if (/\s/.test(name)) return `"${name}"`;
45
+ return name;
46
+ };
47
+ const buildFontFamilyValue = (name, fallback) => {
48
+ const head = quoteFamily(name);
49
+ if (!fallback || fallback.length === 0) return head;
50
+ return [head, fallback].join(", ");
51
+ };
52
+ const formatSrc = (src) => {
53
+ const parts = [`url("${src.url}")`];
54
+ if (src.format) parts.push(`format("${src.format}")`);
55
+ if (src.tech) parts.push(`tech(${src.tech})`);
56
+ return parts.join(" ");
57
+ };
58
+ const variantToFontFace = (name, variant, defaultDisplay) => {
59
+ const sources = normalizeSources(variant.src);
60
+ if (sources.length === 0) {
61
+ throw new Error(`defineFont(${name}): variant must declare at least one \`src\`.`);
62
+ }
63
+ const lines = [`font-family: ${quoteFamily(name)};`, `src: ${sources.map(formatSrc).join(", ")};`, `font-display: ${variant.display ?? defaultDisplay};`];
64
+ if (variant.weight !== void 0) lines.push(`font-weight: ${variant.weight};`);
65
+ if (variant.style !== void 0) lines.push(`font-style: ${variant.style};`);
66
+ if (variant.stretch !== void 0) lines.push(`font-stretch: ${variant.stretch};`);
67
+ if (variant.unicodeRange !== void 0) lines.push(`unicode-range: ${variant.unicodeRange};`);
68
+ if (variant.ascentOverride !== void 0) lines.push(`ascent-override: ${variant.ascentOverride};`);
69
+ if (variant.descentOverride !== void 0) lines.push(`descent-override: ${variant.descentOverride};`);
70
+ if (variant.lineGapOverride !== void 0) lines.push(`line-gap-override: ${variant.lineGapOverride};`);
71
+ if (variant.sizeAdjust !== void 0) lines.push(`size-adjust: ${variant.sizeAdjust};`);
72
+ return `@font-face { ${lines.join(" ")} }`;
73
+ };
74
+ class FontFactory {
75
+ constructor(_options) {
76
+ __publicField(this, "variable");
77
+ __publicField(this, "fontFamily");
78
+ __publicField(this, "className");
79
+ this._options = _options;
80
+ if (!_options || !_options.name) {
81
+ throw new Error("defineFont: `name` is required.");
82
+ }
83
+ if ("variants" in _options && "import" in _options && _options.import !== void 0 && _options.variants !== void 0) {
84
+ throw new Error("defineFont: provide either `variants` or `import`, not both.");
85
+ }
86
+ if (!("variants" in _options && _options.variants) && !("import" in _options && _options.import)) {
87
+ throw new Error("defineFont: must provide either `variants` or `import`.");
88
+ }
89
+ this.variable = _options.variable ? normalizeVariable(_options.variable) : deriveVariable(_options);
90
+ this.fontFamily = buildFontFamilyValue(_options.name, _options.fallback);
91
+ this.className = `font-${dashCase(_options.name)}`;
92
+ }
93
+ get isDefineFont() {
94
+ return true;
95
+ }
96
+ /** Acts as a string equal to the resolved font-family value. */
97
+ toString() {
98
+ return this.fontFamily;
99
+ }
100
+ /** Inline-style helper: spread onto a React `style` prop. */
101
+ get style() {
102
+ return {
103
+ fontFamily: this.fontFamily,
104
+ [this.variable]: this.fontFamily
105
+ };
106
+ }
107
+ /** Build the CSS pieces written to `_fonts.css`. */
108
+ _toCss() {
109
+ const imports = [];
110
+ const blocks = [];
111
+ if ("import" in this._options && this._options.import) {
112
+ imports.push(`@import url("${this._options.import}");`);
113
+ } else if ("variants" in this._options && this._options.variants) {
114
+ const display = this._options.display ?? "swap";
115
+ for (const variant of this._options.variants) {
116
+ blocks.push(variantToFontFace(this._options.name, variant, display));
117
+ }
118
+ }
119
+ blocks.push(`:root { ${this.variable}: ${this.fontFamily}; }`, `.${this.className} { font-family: var(${this.variable}); }`);
120
+ return { imports, body: blocks.join(" ") };
121
+ }
122
+ }
123
+ const defineFont = (options) => {
124
+ return new FontFactory(options);
125
+ };
3
126
  class GlobalStylesFactory {
4
127
  constructor(_current) {
5
128
  this._current = _current;
@@ -26,10 +149,12 @@ const defineVariables = (variables) => {
26
149
  return new VariablesFactory(variables);
27
150
  };
28
151
  export {
152
+ FontFactory,
29
153
  GlobalStylesFactory,
30
154
  T as TemplateFactory,
31
155
  a as TemplatesFactory,
32
156
  VariablesFactory,
157
+ defineFont,
33
158
  defineGlobalStyles,
34
159
  defineMediaQuery,
35
160
  d as defineTemplates,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const classNameGenerator = require("../class-name-generator-B2Pb2obX.cjs");
3
+ const classNameGenerator = require("../class-name-generator-BIYysuhW.cjs");
4
4
  const dashCase = require("../dash-case-DIwKaYgE.cjs");
5
5
  class StyledGenerator extends classNameGenerator.StylesGenerator {
6
6
  constructor(tagName, _params) {
@@ -1,5 +1,5 @@
1
- import { S as StylesGenerator } from "../class-name-generator-YeSQe_Ik.js";
2
- import { C } from "../class-name-generator-YeSQe_Ik.js";
1
+ import { S as StylesGenerator } from "../class-name-generator-B2LriwKm.js";
2
+ import { C } from "../class-name-generator-B2LriwKm.js";
3
3
  import { d as dashCase } from "../dash-case-DblXvymC.js";
4
4
  class StyledGenerator extends StylesGenerator {
5
5
  constructor(tagName, _params) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const classNameGenerator = require("../class-name-generator-B2Pb2obX.cjs");
3
+ const classNameGenerator = require("../class-name-generator-BIYysuhW.cjs");
4
4
  const classNameInstance = (params) => {
5
5
  const generator = new classNameGenerator.ClassNameGenerator(params);
6
6
  const createClass = (classNameStr) => {
@@ -1,4 +1,4 @@
1
- import { C as ClassNameGenerator } from "../class-name-generator-YeSQe_Ik.js";
1
+ import { C as ClassNameGenerator } from "../class-name-generator-B2LriwKm.js";
2
2
  const classNameInstance = (params) => {
3
3
  const generator = new ClassNameGenerator(params);
4
4
  const createClass = (classNameStr) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salty-css/core",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-feat-define-font.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "typings": "./dist/index.d.ts",
@@ -20,7 +20,7 @@ const parseValueTokens = (tokenNames) => (value) => {
20
20
  const hasToken = /\{[^{}]+\}/g.test(value);
21
21
  if (!hasToken) return void 0;
22
22
  const transformed = value.replace(/\{([^{}]+)\}/g, (...args) => {
23
- const variable = dashCase(args[1].replaceAll(".", "-"));
23
+ const variable = dashCase(args[1].trim().replaceAll(".", "-"));
24
24
  if (tokenNames && !tokenNames.includes(variable)) console.warn(`Token ${variable} might not exist`);
25
25
  if (variable.startsWith("-")) return `-${variable}`;
26
26
  return `var(--${variable})`;
@@ -74,22 +74,44 @@ const addUnit = (key, value, config) => {
74
74
  return `${value}`;
75
75
  };
76
76
  const vendorPrefixes = ["Webkit", "Moz", "ms", "O"];
77
+ const isVendorPrefixed = (key) => {
78
+ return vendorPrefixes.some((prefix) => {
79
+ if (!key.startsWith(prefix)) return false;
80
+ const next = key.charAt(prefix.length);
81
+ return next >= "A" && next <= "Z";
82
+ });
83
+ };
77
84
  const propertyNameCheck = (key) => {
78
85
  if (key.startsWith("-")) return key;
79
- if (vendorPrefixes.some((prefix) => key.startsWith(prefix))) return `-${dashCase(key)}`;
86
+ if (isVendorPrefixed(key)) return `-${dashCase(key)}`;
80
87
  return dashCase(key);
81
88
  };
89
+ const reportParserIssue = (strict, message) => {
90
+ if (strict === true) throw new Error(`[salty-css] ${message}`);
91
+ if (strict === "warn") console.warn(`[salty-css] ${message}`);
92
+ };
93
+ const pseudoTypoRegex = /^&(hover|focus(-(visible|within))?|active|visited|checked|disabled|enabled|empty|target|first-child|last-child|first-of-type|last-of-type|placeholder|placeholder-shown|root)\b/;
94
+ const templateLiteralLeftoverRegex = /\$\{[^}]+\}/;
95
+ const bareAtRuleRegex = /^@(media|supports|container|layer)\s*$/;
82
96
  const parseStyles = async (styles, currentScope = "", config, omitTemplates = false) => {
83
97
  if (!styles) throw new Error("No styles provided to parseStyles function!");
84
98
  const cssStyles = /* @__PURE__ */ new Set();
85
99
  const entries = Object.entries(styles);
100
+ const strict = config == null ? void 0 : config.strict;
86
101
  const processStyleEntry = async ([key, value]) => {
87
102
  var _a, _b;
88
103
  const _key = key.trim().replace(/^\?+/g, "");
89
104
  const propertyName = propertyNameCheck(_key);
90
105
  const toString = (val, eol = ";") => `${propertyName}:${val}${eol}`;
91
106
  const context = { scope: currentScope, config };
92
- if (typeof value === "function") return processStyleEntry([key, value(context)]);
107
+ if (typeof value === "function") {
108
+ try {
109
+ return await processStyleEntry([key, value(context)]);
110
+ } catch (error) {
111
+ reportParserIssue(strict, `Function value for "${_key}" threw: ${(error == null ? void 0 : error.message) ?? error}`);
112
+ return void 0;
113
+ }
114
+ }
93
115
  if (value instanceof Promise) return processStyleEntry([key, await value]);
94
116
  if ((config == null ? void 0 : config.templates) && ((_a = config.templatePaths) == null ? void 0 : _a[_key])) {
95
117
  try {
@@ -123,9 +145,15 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
123
145
  console.warn(`Template "${_key}" with path of "${value}" was not found in config!`);
124
146
  return void 0;
125
147
  }
148
+ const isVariantArrayKey = _key === "compoundVariants" || _key === "anyOfVariants";
149
+ if (!isVariantArrayKey && Array.isArray(value)) {
150
+ if (value.length === 0) return void 0;
151
+ return processStyleEntry([key, value.join(", ")]);
152
+ }
126
153
  if (typeof value === "object") {
127
154
  if (!value) return void 0;
128
155
  if (value.isColor) return toString(value.toString());
156
+ if (value.isDefineFont) return toString(value.toString());
129
157
  if (_key === "defaultVariants") return void 0;
130
158
  if (_key === "variants") {
131
159
  const variantEntries = Object.entries(value);
@@ -159,31 +187,62 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
159
187
  return `.${prop}-${val}`;
160
188
  });
161
189
  const scope2 = `${currentScope}:where(${scopes.join(", ")})`;
162
- console.log(`Union variant scope: ${scope2}`);
163
190
  const results2 = await parseStyles(css2, scope2, config);
164
191
  results2.forEach((res) => cssStyles.add(res));
165
192
  }
166
193
  return void 0;
167
194
  }
168
195
  if (_key.startsWith("@")) {
196
+ if (bareAtRuleRegex.test(_key)) reportParserIssue(strict, `At-rule "${_key}" is missing its condition (e.g. "@media (min-width: 600px)").`);
169
197
  const mediaQuery = ((_b = config == null ? void 0 : config.mediaQueries) == null ? void 0 : _b[_key]) || _key;
170
198
  const results2 = await parseAndJoinStyles(value, currentScope, config);
171
199
  const query = `${mediaQuery} { ${results2} }`;
172
200
  cssStyles.add(query);
173
201
  return void 0;
174
202
  }
175
- const scope = key.includes("&") ? _key.replaceAll("&", currentScope) : _key.startsWith(":") ? `${currentScope}${_key}` : `${currentScope} ${_key}`;
203
+ if (Object.keys(value).length === 0) return void 0;
204
+ if (pseudoTypoRegex.test(_key)) {
205
+ reportParserIssue(strict, `Selector "${_key}" looks like a missing-colon typo (did you mean "&:${_key.slice(1)}"?).`);
206
+ }
207
+ const scope = combineSelectors(currentScope, _key);
176
208
  const results = await parseStyles(value, scope, config);
177
209
  results.forEach((result) => cssStyles.add(result));
178
210
  return void 0;
179
211
  }
212
+ if (_key.startsWith("$")) {
213
+ reportParserIssue(strict, `Property key "${_key}" looks like a SCSS variable — Salty does not support those.`);
214
+ return void 0;
215
+ }
216
+ if (_key.includes(":")) {
217
+ reportParserIssue(strict, `Property key "${_key}" contains a colon — did you accidentally paste a whole declaration as a key?`);
218
+ return void 0;
219
+ }
220
+ if (value === void 0 || value === null) {
221
+ reportParserIssue(strict, `Property "${_key}" has a ${value === void 0 ? "undefined" : "null"} value — skipping.`);
222
+ return void 0;
223
+ }
224
+ if (typeof value === "boolean") {
225
+ reportParserIssue(strict, `Property "${_key}" has a boolean value (${value}) — skipping.`);
226
+ return void 0;
227
+ }
228
+ if (value === "") return void 0;
180
229
  if (typeof value === "number") {
230
+ if (!Number.isFinite(value)) {
231
+ reportParserIssue(strict, `Property "${_key}" has a non-finite numeric value (${value}) — skipping.`);
232
+ return void 0;
233
+ }
181
234
  const withUnit = addUnit(propertyName, value, config);
182
235
  return toString(withUnit);
183
236
  }
184
237
  if (typeof value !== "string") {
185
- if ("toString" in value) value = value.toString();
186
- else throw new Error(`Invalid value type for property ${propertyName}`);
238
+ if (value && typeof value === "object" && "toString" in value) value = value.toString();
239
+ else {
240
+ reportParserIssue(strict, `Property "${_key}" has an unsupported value type (${typeof value}) — skipping.`);
241
+ return void 0;
242
+ }
243
+ }
244
+ if (typeof value === "string" && templateLiteralLeftoverRegex.test(value)) {
245
+ reportParserIssue(strict, `Property "${_key}" value "${value}" contains an unresolved \`\${...}\` — did you forget to interpolate?`);
187
246
  }
188
247
  return toString(value);
189
248
  };
@@ -223,10 +282,51 @@ const parseAndJoinStyles = async (styles, currentClass, config, omitTemplates =
223
282
  const css = await parseStyles(styles, currentClass, config, omitTemplates);
224
283
  return css.join("\n");
225
284
  };
285
+ const splitTopLevelCommas = (selector) => {
286
+ const parts = [];
287
+ let depth = 0;
288
+ let buf = "";
289
+ for (const ch of selector) {
290
+ if (ch === "(" || ch === "[") depth++;
291
+ else if (ch === ")" || ch === "]") depth = Math.max(0, depth - 1);
292
+ if (ch === "," && depth === 0) {
293
+ const trimmed2 = buf.trim();
294
+ if (trimmed2) parts.push(trimmed2);
295
+ buf = "";
296
+ } else {
297
+ buf += ch;
298
+ }
299
+ }
300
+ const trimmed = buf.trim();
301
+ if (trimmed) parts.push(trimmed);
302
+ return parts;
303
+ };
304
+ const joinSelector = (parent, child) => {
305
+ if (child.includes("&")) return child.replaceAll("&", parent);
306
+ if (child.startsWith(":")) return `${parent}${child}`;
307
+ return `${parent} ${child}`;
308
+ };
309
+ const combineSelectors = (currentScope, key) => {
310
+ if (!currentScope) return key;
311
+ const parents = splitTopLevelCommas(currentScope);
312
+ const children = splitTopLevelCommas(key);
313
+ if (!children.length) return currentScope;
314
+ if (parents.length <= 1 && children.length <= 1) {
315
+ return joinSelector(parents[0] ?? currentScope, children[0]);
316
+ }
317
+ const combos = [];
318
+ for (const p of parents) {
319
+ for (const c of children) {
320
+ combos.push(joinSelector(p, c));
321
+ }
322
+ }
323
+ return combos.join(", ");
324
+ };
226
325
  export {
227
326
  parseStyles as a,
228
327
  parseVariableTokens as b,
229
328
  parseValueModifiers as c,
230
329
  parseValueTokens as d,
231
- parseAndJoinStyles as p
330
+ parseAndJoinStyles as p,
331
+ reportParserIssue as r
232
332
  };