@salty-css/core 0.1.0-feat-define-font.0 → 0.1.0-feat-define-import.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.
package/README.md CHANGED
@@ -57,7 +57,7 @@ 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
60
+ - [defineImport](#importing-additional-css) - pull in external CSS files (relative, public, node_modules, or URL)
61
61
  - [keyframes](#keyframes-animations) - create CSS keyframes animation that can be used and imported in any styling function
62
62
 
63
63
  ### Styling helpers & utility
@@ -320,86 +320,47 @@ Example usage:
320
320
  styled('div', { base: { textStyle: 'headline.large', card: '20px' } });
321
321
  ```
322
322
 
323
- ## Custom fonts
323
+ ## Importing additional CSS
324
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
325
+ Use `defineImport` to pull in CSS that lives outside of Salty's authoring API a reset stylesheet from npm, a Google Fonts URL, an asset in your app's `public/` folder, or a sibling `.css` file. The compiler turns each spec into an `@import` rule in the generated `saltygen/index.css`, so the imported stylesheets travel with the rest of your build.
333
326
 
334
327
  ```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
- });
328
+ // /styles/imports.css.ts
329
+ import { defineImport } from '@salty-css/core/factories';
330
+
331
+ export default defineImport(
332
+ // Relative to this file
333
+ './reset.css',
334
+ // From node_modules (bare specifier — same as Vite / native CSS @import)
335
+ 'modern-normalize/modern-normalize.css',
336
+ // From node_modules (~ prefix same resolver, webpack-style)
337
+ '~normalize.css/normalize.css',
338
+ // From your app's public/ folder (served at the host root)
339
+ '/fonts/inter.css',
340
+ // External URL
341
+ 'https://fonts.googleapis.com/css2?family=Inter&display=swap',
342
+ // Object form attach media or supports() conditions
343
+ { url: './print.css', media: 'print' },
344
+ { url: './p3.css', supports: 'color(display-p3 1 1 1)' }
345
+ );
378
346
  ```
379
347
 
380
- Example usage:
348
+ Path resolution:
381
349
 
382
- ```tsx
383
- import { Inter } from './fonts.css';
384
- import { styled } from '@salty-css/react/styled';
350
+ | Pattern | Behaviour |
351
+ | --------------------------- | ---------------------------------------------------------------------------------------- |
352
+ | `http://`, `https://`, `//` | Emitted verbatim |
353
+ | Starts with `/` | Public-folder URL — emitted verbatim, the browser resolves it against your host |
354
+ | Starts with `./` or `../` | Resolved at build time relative to the file that called `defineImport` |
355
+ | `~package/file.css` | Stripped of the leading `~`, then resolved from `node_modules` and copied into the build |
356
+ | `package/file.css` (bare) | Same `node_modules` resolution as the `~` form |
385
357
 
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>;
358
+ All imports are placed inside a new `imports` cascade layer that sits **before** `reset`, `global`, `templates`, and your component styles. This means your own styles always win over third-party CSS you pull in which is what most teams expect when they drop in something like `modern-normalize`.
389
359
 
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
- });
360
+ The full layer order in the generated `index.css` is:
396
361
 
397
- // Or reference the CSS variable explicitly.
398
- export const Body = styled('p', {
399
- base: {
400
- fontFamily: `var(${Inter.variable})`,
401
- },
402
- });
362
+ ```css
363
+ @layer imports, reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
403
364
  ```
404
365
 
405
366
  ## Keyframes animations
@@ -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 {
@@ -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-jPtMfgXH.cjs");
5
+ const parseStyles = require("./parse-styles-C54MOrPg.cjs");
6
6
  const dashCase = require("./dash-case-DIwKaYgE.cjs");
7
7
  const toHash = require("./to-hash-C05Y906F.cjs");
8
8
  class StylesGenerator {
@@ -0,0 +1,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 dashCase = require("../dash-case-DIwKaYgE.cjs");
14
- const toHash = require("../to-hash-C05Y906F.cjs");
15
13
  const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
16
- const parseStyles = require("../parse-styles-jPtMfgXH.cjs");
14
+ const module$1 = require("module");
15
+ const toHash = require("../to-hash-C05Y906F.cjs");
16
+ const dashCase = require("../dash-case-DIwKaYgE.cjs");
17
+ const parseStyles = require("../parse-styles-C54MOrPg.cjs");
17
18
  const css_merge = require("../css/merge.cjs");
18
19
  const parsers_index = require("../parsers/index.cjs");
19
- const compiler_getFiles = require("./get-files.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}`;
51
55
  };
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";
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) };
60
94
  };
61
95
  function dotCase(str) {
62
96
  if (!str) return "";
@@ -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", "_fonts.css"];
386
+ const globalCssFilenames = ["_imports.css", "_variables.css", "_reset.css", "_global.css", "_templates.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, fonts, l0, l1, l2, l3, l4, l5, l6, l7, l8;
400
+ let cssContent = `${generatorText}@layer imports, reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
363
401
 
364
402
  ${globalImports.join(
365
403
  "\n"
@@ -406,7 +444,7 @@ ${css}
406
444
  globalStyles: [],
407
445
  variables: [],
408
446
  templates: [],
409
- fonts: []
447
+ imports: []
410
448
  };
411
449
  await Promise.all(
412
450
  [...configFiles].map(async (src) => {
@@ -416,7 +454,7 @@ ${css}
416
454
  else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
417
455
  else if (value.isDefineVariables) generationResults.variables.push(value);
418
456
  else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
419
- else if (value.isDefineFont) generationResults.fonts.push(value);
457
+ else if (value.isDefineImport) generationResults.imports.push(value._setPath(src));
420
458
  });
421
459
  })
422
460
  );
@@ -515,25 +553,25 @@ ${css}
515
553
  const templateTokens = parsers_index.getTemplateTypes(templates);
516
554
  fs.writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
517
555
  configCacheContent.templates = templates;
556
+ const importsPath = path.join(destDir, "css/_imports.css");
557
+ const importRules = [];
558
+ for (const factory of generationResults.imports) {
559
+ const sourceFile = factory._path;
560
+ if (!sourceFile) continue;
561
+ for (const spec of factory._current) {
562
+ try {
563
+ const { rule } = resolveImport(spec, sourceFile, destDir);
564
+ importRules.push(rule);
565
+ } catch (e) {
566
+ const url = typeof spec === "string" ? spec : spec.url;
567
+ logger.error(`Failed to resolve defineImport(${JSON.stringify(url)}) from ${sourceFile}: ${e.message}`);
568
+ }
569
+ }
570
+ }
571
+ fs.writeFileSync(importsPath, importRules.join("\n"));
518
572
  const configTemplateFactories = config.templates ? [defineTemplates.defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
519
573
  const templateFactories = css_merge.mergeFactories(generationResults.templates, configTemplateFactories);
520
574
  configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
521
- const fontsStylesPath = path.join(destDir, "css/_fonts.css");
522
- if (generationResults.fonts.length === 0) {
523
- fs.writeFileSync(fontsStylesPath, "");
524
- } else {
525
- const fontImports = [];
526
- const fontBodies = [];
527
- for (const font of generationResults.fonts) {
528
- const { imports, body } = font._toCss();
529
- fontImports.push(...imports);
530
- fontBodies.push(body);
531
- }
532
- const importBlock = fontImports.length ? `${fontImports.join("\n")}
533
-
534
- ` : "";
535
- fs.writeFileSync(fontsStylesPath, `${importBlock}@layer fonts { ${fontBodies.join(" ")} }`);
536
- }
537
575
  const tsTokensPath = path.join(destDir, "types/css-tokens.d.ts");
538
576
  const tsVariableTokens = [...variableTokens].join("|");
539
577
  const tsTokensTypes = `
@@ -569,7 +607,7 @@ ${css}
569
607
  const rcProject = await this.getRCProjectConfig(this.projectRootDir);
570
608
  const coreConfigPath = path.join(this.projectRootDir, (rcProject == null ? void 0 : rcProject.configDir) || "", "salty.config.ts");
571
609
  const externalModules = this.getExternalModules(coreConfigPath);
572
- const moduleType = await detectCurrentModuleType(this.projectRootDir);
610
+ const moduleType$1 = await moduleType.detectCurrentModuleType(this.projectRootDir);
573
611
  await esbuild__namespace.build({
574
612
  stdin: {
575
613
  contents: currentFile,
@@ -581,7 +619,7 @@ ${css}
581
619
  treeShaking: true,
582
620
  bundle: true,
583
621
  outfile: outputFilePath,
584
- format: moduleType,
622
+ format: moduleType$1,
585
623
  target: ["node20"],
586
624
  keepNames: true,
587
625
  external: externalModules,
@@ -44,7 +44,7 @@ export declare class SaltyCompiler {
44
44
  isGlobalDefine?: boolean;
45
45
  isDefineVariables?: boolean;
46
46
  isDefineTemplates?: boolean;
47
- isDefineFont?: boolean;
47
+ isDefineImport?: boolean;
48
48
  isKeyframes?: boolean;
49
49
  animationName?: string;
50
50
  css?: Promise<string>;
@@ -2,19 +2,20 @@ 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";
5
+ import { resolve, dirname, relative, join, basename, parse } from "path";
6
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
10
  import { isSaltyFile, resolveExportValue, getCorePackageRoot, saltyFileExtensions } from "./helpers.js";
11
- import { d as dashCase } from "../dash-case-DblXvymC.js";
12
- import { t as toHash } from "../to-hash-DAN2LcHK.js";
13
11
  import { d as defineTemplates } from "../define-templates-CVhhgPnd.js";
14
- import { p as parseAndJoinStyles, b as parseVariableTokens } from "../parse-styles--vHKY6Mw.js";
12
+ import { createRequire } from "module";
13
+ import { t as toHash } from "../to-hash-DAN2LcHK.js";
14
+ import { d as dashCase } from "../dash-case-DblXvymC.js";
15
+ import { p as parseAndJoinStyles, b as parseVariableTokens } from "../parse-styles-CLMTHo2H.js";
15
16
  import { mergeObjects, mergeFactories } from "../css/merge.js";
16
17
  import { parseTemplates, getTemplateTypes } from "../parsers/index.js";
17
- import { getPackageJson } from "./get-files.js";
18
+ import { detectCurrentModuleType } from "../util/module-type";
18
19
  import console from "console";
19
20
  const logger = createLogger({
20
21
  level: "debug",
@@ -24,19 +25,53 @@ const logger = createLogger({
24
25
  const logError = (message) => {
25
26
  logger.error(message);
26
27
  };
27
- const readPackageJsonModule = async (dirname) => {
28
- const packageJsonContent = await getPackageJson(dirname);
29
- if (!packageJsonContent) return void 0;
30
- return packageJsonContent.type;
28
+ const EXTERNAL_URL = /^(?:[a-z][a-z0-9+.-]*:)?\/\//i;
29
+ const normaliseSpec = (spec) => {
30
+ if (typeof spec === "string") return { url: spec };
31
+ return spec;
32
+ };
33
+ const ensureRelativePrefix = (path) => {
34
+ if (path.startsWith(".") || path.startsWith("/")) return path;
35
+ return `./${path}`;
31
36
  };
32
- let cachedModuleType;
33
- const detectCurrentModuleType = async (dirname) => {
34
- if (cachedModuleType) return cachedModuleType;
35
- const packageJsonModule = await readPackageJsonModule(dirname);
36
- if (packageJsonModule === "module") cachedModuleType = "esm";
37
- else if (packageJsonModule === "commonjs") cachedModuleType = "cjs";
38
- else if (import.meta.url.endsWith(".cjs")) cachedModuleType = "cjs";
39
- return cachedModuleType || "esm";
37
+ const toPosix = (path) => path.split("\\").join("/");
38
+ const defaultResolveModule = (specifier, sourceFile) => {
39
+ return createRequire(sourceFile).resolve(specifier);
40
+ };
41
+ const defaultCopyAsset = (from, to) => {
42
+ const dir = dirname(to);
43
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
44
+ copyFileSync(from, to);
45
+ };
46
+ const buildRule = (url, { media, supports }) => {
47
+ let rule = `@import url('${url}')`;
48
+ if (supports) rule += ` supports(${supports})`;
49
+ if (media) rule += ` ${media}`;
50
+ return `${rule};`;
51
+ };
52
+ const resolveImport = (spec, sourceFile, destDir, options = {}) => {
53
+ const opts = normaliseSpec(spec);
54
+ const { url } = opts;
55
+ const resolveModule = options.resolveModule ?? defaultResolveModule;
56
+ const copyAsset = options.copyAsset ?? defaultCopyAsset;
57
+ if (EXTERNAL_URL.test(url)) {
58
+ return { rule: buildRule(url, opts) };
59
+ }
60
+ if (url.startsWith("/")) {
61
+ return { rule: buildRule(url, opts) };
62
+ }
63
+ if (url.startsWith("./") || url.startsWith("../")) {
64
+ const absolute2 = resolve(dirname(sourceFile), url);
65
+ const fromImportsFile = relative(join(destDir, "css"), absolute2);
66
+ return { rule: buildRule(ensureRelativePrefix(toPosix(fromImportsFile)), opts) };
67
+ }
68
+ const specifier = url.startsWith("~") ? url.slice(1) : url;
69
+ const absolute = resolveModule(specifier, sourceFile);
70
+ const hash = toHash(absolute, 6);
71
+ const fileName = `${hash}-${basename(absolute)}`;
72
+ const destPath = join(destDir, "imports", fileName);
73
+ copyAsset(absolute, destPath);
74
+ return { rule: buildRule(`../imports/${fileName}`, opts) };
40
75
  };
41
76
  function dotCase(str) {
42
77
  if (!str) return "";
@@ -121,10 +156,10 @@ class SaltyCompiler {
121
156
  * Get the project configuration from the .saltyrc.json file based on the current directory.
122
157
  * If no specific project configuration is found, it falls back to the default project.
123
158
  */
124
- __publicField(this, "getRCProjectConfig", async (dirname) => {
159
+ __publicField(this, "getRCProjectConfig", async (dirname2) => {
125
160
  var _a, _b;
126
- const rcFile = await this.readRCFile(dirname);
127
- const projectConfig = (_a = rcFile.projects) == null ? void 0 : _a.find((project) => dirname.endsWith(project.dir || ""));
161
+ const rcFile = await this.readRCFile(dirname2);
162
+ const projectConfig = (_a = rcFile.projects) == null ? void 0 : _a.find((project) => dirname2.endsWith(project.dir || ""));
128
163
  if (!projectConfig) return (_b = rcFile.projects) == null ? void 0 : _b.find((project) => project.dir === rcFile.defaultProject);
129
164
  return projectConfig;
130
165
  });
@@ -213,6 +248,7 @@ ${currentFile}`;
213
248
  mkdirSync(join(destDir, "types"));
214
249
  mkdirSync(join(destDir, "js"));
215
250
  mkdirSync(join(destDir, "cache"));
251
+ mkdirSync(join(destDir, "imports"));
216
252
  };
217
253
  if (clean) clearDistDir();
218
254
  const files = /* @__PURE__ */ new Set();
@@ -328,7 +364,7 @@ ${currentFile}`;
328
364
  });
329
365
  }
330
366
  const otherGlobalCssFiles = globalCssFiles.map((file) => `@import url('./css/${file}');`).join("\n");
331
- const globalCssFilenames = ["_variables.css", "_reset.css", "_global.css", "_templates.css", "_fonts.css"];
367
+ const globalCssFilenames = ["_imports.css", "_variables.css", "_reset.css", "_global.css", "_templates.css"];
332
368
  const importsWithData = globalCssFilenames.filter((file) => {
333
369
  try {
334
370
  const data = readFileSync(join(destDir, "css", file), "utf8");
@@ -337,9 +373,12 @@ ${currentFile}`;
337
373
  return false;
338
374
  }
339
375
  });
340
- const globalImports = importsWithData.map((file) => `@import url('./css/${file}');`);
376
+ const globalImports = importsWithData.map((file) => {
377
+ const layerSuffix = file === "_imports.css" ? " layer(imports)" : "";
378
+ return `@import url('./css/${file}')${layerSuffix};`;
379
+ });
341
380
  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, fonts, l0, l1, l2, l3, l4, l5, l6, l7, l8;
381
+ let cssContent = `${generatorText}@layer imports, reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
343
382
 
344
383
  ${globalImports.join(
345
384
  "\n"
@@ -386,7 +425,7 @@ ${css}
386
425
  globalStyles: [],
387
426
  variables: [],
388
427
  templates: [],
389
- fonts: []
428
+ imports: []
390
429
  };
391
430
  await Promise.all(
392
431
  [...configFiles].map(async (src) => {
@@ -396,7 +435,7 @@ ${css}
396
435
  else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
397
436
  else if (value.isDefineVariables) generationResults.variables.push(value);
398
437
  else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
399
- else if (value.isDefineFont) generationResults.fonts.push(value);
438
+ else if (value.isDefineImport) generationResults.imports.push(value._setPath(src));
400
439
  });
401
440
  })
402
441
  );
@@ -495,25 +534,25 @@ ${css}
495
534
  const templateTokens = getTemplateTypes(templates);
496
535
  writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
497
536
  configCacheContent.templates = templates;
537
+ const importsPath = join(destDir, "css/_imports.css");
538
+ const importRules = [];
539
+ for (const factory of generationResults.imports) {
540
+ const sourceFile = factory._path;
541
+ if (!sourceFile) continue;
542
+ for (const spec of factory._current) {
543
+ try {
544
+ const { rule } = resolveImport(spec, sourceFile, destDir);
545
+ importRules.push(rule);
546
+ } catch (e) {
547
+ const url = typeof spec === "string" ? spec : spec.url;
548
+ logger.error(`Failed to resolve defineImport(${JSON.stringify(url)}) from ${sourceFile}: ${e.message}`);
549
+ }
550
+ }
551
+ }
552
+ writeFileSync(importsPath, importRules.join("\n"));
498
553
  const configTemplateFactories = config.templates ? [defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
499
554
  const templateFactories = mergeFactories(generationResults.templates, configTemplateFactories);
500
555
  configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
501
- const fontsStylesPath = join(destDir, "css/_fonts.css");
502
- if (generationResults.fonts.length === 0) {
503
- writeFileSync(fontsStylesPath, "");
504
- } else {
505
- const fontImports = [];
506
- const fontBodies = [];
507
- for (const font of generationResults.fonts) {
508
- const { imports, body } = font._toCss();
509
- fontImports.push(...imports);
510
- fontBodies.push(body);
511
- }
512
- const importBlock = fontImports.length ? `${fontImports.join("\n")}
513
-
514
- ` : "";
515
- writeFileSync(fontsStylesPath, `${importBlock}@layer fonts { ${fontBodies.join(" ")} }`);
516
- }
517
556
  const tsTokensPath = join(destDir, "types/css-tokens.d.ts");
518
557
  const tsVariableTokens = [...variableTokens].join("|");
519
558
  const tsTokensTypes = `
package/config/index.cjs CHANGED
@@ -5,11 +5,11 @@ const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
5
5
  const defineConfig = (config) => {
6
6
  return config;
7
7
  };
8
- exports.FontFactory = factories_index.FontFactory;
9
8
  exports.GlobalStylesFactory = factories_index.GlobalStylesFactory;
9
+ exports.ImportFactory = factories_index.ImportFactory;
10
10
  exports.VariablesFactory = factories_index.VariablesFactory;
11
- exports.defineFont = factories_index.defineFont;
12
11
  exports.defineGlobalStyles = factories_index.defineGlobalStyles;
12
+ exports.defineImport = factories_index.defineImport;
13
13
  exports.defineMediaQuery = factories_index.defineMediaQuery;
14
14
  exports.defineVariables = factories_index.defineVariables;
15
15
  exports.TemplateFactory = defineTemplates.TemplateFactory;
package/config/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import { FontFactory, GlobalStylesFactory, VariablesFactory, defineFont, defineGlobalStyles, defineMediaQuery, defineVariables } from "../factories/index.js";
1
+ import { GlobalStylesFactory, ImportFactory, VariablesFactory, 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,
8
7
  GlobalStylesFactory,
8
+ ImportFactory,
9
9
  T as TemplateFactory,
10
10
  a as TemplatesFactory,
11
11
  VariablesFactory,
12
12
  defineConfig,
13
- defineFont,
14
13
  defineGlobalStyles,
14
+ defineImport,
15
15
  defineMediaQuery,
16
16
  d as defineTemplates,
17
17
  defineVariables
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const parseStyles = require("../parse-styles-jPtMfgXH.cjs");
3
+ const parseStyles = require("../parse-styles-C54MOrPg.cjs");
4
4
  const toHash = require("../to-hash-C05Y906F.cjs");
5
5
  const cache_resolveDynamicConfigCache = require("../cache/resolve-dynamic-config-cache.cjs");
6
6
  const getDynamicStylesClassName = (styles) => {
@@ -1,4 +1,4 @@
1
- import { a as parseStyles } from "../parse-styles--vHKY6Mw.js";
1
+ import { a as parseStyles } from "../parse-styles-CLMTHo2H.js";
2
2
  import { t as toHash } from "../to-hash-DAN2LcHK.js";
3
3
  import { resolveDynamicConfigCache } from "../cache/resolve-dynamic-config-cache.js";
4
4
  const getDynamicStylesClassName = (styles) => {
package/css/keyframes.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const parseStyles = require("../parse-styles-jPtMfgXH.cjs");
3
+ const parseStyles = require("../parse-styles-C54MOrPg.cjs");
4
4
  const toHash = require("../to-hash-C05Y906F.cjs");
5
5
  const keyframes = ({ animationName: _name, params: _params, appendInitialStyles, ...keyframes2 }) => {
6
6
  const modifyKeyframes = async (params = {}) => {
package/css/keyframes.js CHANGED
@@ -1,4 +1,4 @@
1
- import { p as parseAndJoinStyles } from "../parse-styles--vHKY6Mw.js";
1
+ import { p as parseAndJoinStyles } from "../parse-styles-CLMTHo2H.js";
2
2
  import { t as toHash } from "../to-hash-DAN2LcHK.js";
3
3
  const keyframes = ({ animationName: _name, params: _params, appendInitialStyles, ...keyframes2 }) => {
4
4
  const modifyKeyframes = async (params = {}) => {
@@ -0,0 +1,14 @@
1
+ export interface ImportSpecOptions {
2
+ url: string;
3
+ media?: string;
4
+ supports?: string;
5
+ }
6
+ export type ImportSpec = string | ImportSpecOptions;
7
+ export declare class ImportFactory {
8
+ _current: ImportSpec[];
9
+ _path?: string;
10
+ constructor(_current: ImportSpec[]);
11
+ get isDefineImport(): boolean;
12
+ _setPath(path: string): this;
13
+ }
14
+ export declare const defineImport: (...specs: ImportSpec[]) => ImportFactory;
@@ -3,128 +3,8 @@ 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
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- const dashCase = require("../dash-case-DIwKaYgE.cjs");
7
- const toHash = require("../to-hash-C05Y906F.cjs");
8
6
  const css_media = require("../css/media.cjs");
9
7
  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
- };
128
8
  class GlobalStylesFactory {
129
9
  constructor(_current) {
130
10
  this._current = _current;
@@ -136,6 +16,20 @@ class GlobalStylesFactory {
136
16
  const defineGlobalStyles = (styles) => {
137
17
  return new GlobalStylesFactory(styles);
138
18
  };
19
+ class ImportFactory {
20
+ constructor(_current) {
21
+ __publicField(this, "_path");
22
+ this._current = _current;
23
+ }
24
+ get isDefineImport() {
25
+ return true;
26
+ }
27
+ _setPath(path) {
28
+ this._path = path;
29
+ return this;
30
+ }
31
+ }
32
+ const defineImport = (...specs) => new ImportFactory(specs);
139
33
  const defineMediaQuery = (mediaFactory) => {
140
34
  return mediaFactory(css_media.media);
141
35
  };
@@ -153,10 +47,10 @@ const defineVariables = (variables) => {
153
47
  exports.TemplateFactory = defineTemplates.TemplateFactory;
154
48
  exports.TemplatesFactory = defineTemplates.TemplatesFactory;
155
49
  exports.defineTemplates = defineTemplates.defineTemplates;
156
- exports.FontFactory = FontFactory;
157
50
  exports.GlobalStylesFactory = GlobalStylesFactory;
51
+ exports.ImportFactory = ImportFactory;
158
52
  exports.VariablesFactory = VariablesFactory;
159
- exports.defineFont = defineFont;
160
53
  exports.defineGlobalStyles = defineGlobalStyles;
54
+ exports.defineImport = defineImport;
161
55
  exports.defineMediaQuery = defineMediaQuery;
162
56
  exports.defineVariables = defineVariables;
@@ -1,5 +1,5 @@
1
- export * from './define-font';
2
1
  export * from './define-global-styles';
2
+ export * from './define-import';
3
3
  export * from './define-media-query';
4
4
  export * from './define-variables';
5
5
  export * from './define-templates';
@@ -1,128 +1,8 @@
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 { d as dashCase } from "../dash-case-DblXvymC.js";
5
- import { t as toHash } from "../to-hash-DAN2LcHK.js";
6
4
  import { media } from "../css/media.js";
7
5
  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
- };
126
6
  class GlobalStylesFactory {
127
7
  constructor(_current) {
128
8
  this._current = _current;
@@ -134,6 +14,20 @@ class GlobalStylesFactory {
134
14
  const defineGlobalStyles = (styles) => {
135
15
  return new GlobalStylesFactory(styles);
136
16
  };
17
+ class ImportFactory {
18
+ constructor(_current) {
19
+ __publicField(this, "_path");
20
+ this._current = _current;
21
+ }
22
+ get isDefineImport() {
23
+ return true;
24
+ }
25
+ _setPath(path) {
26
+ this._path = path;
27
+ return this;
28
+ }
29
+ }
30
+ const defineImport = (...specs) => new ImportFactory(specs);
137
31
  const defineMediaQuery = (mediaFactory) => {
138
32
  return mediaFactory(media);
139
33
  };
@@ -149,13 +43,13 @@ const defineVariables = (variables) => {
149
43
  return new VariablesFactory(variables);
150
44
  };
151
45
  export {
152
- FontFactory,
153
46
  GlobalStylesFactory,
47
+ ImportFactory,
154
48
  T as TemplateFactory,
155
49
  a as TemplatesFactory,
156
50
  VariablesFactory,
157
- defineFont,
158
51
  defineGlobalStyles,
52
+ defineImport,
159
53
  defineMediaQuery,
160
54
  d as defineTemplates,
161
55
  defineVariables
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const classNameGenerator = require("../class-name-generator-BIYysuhW.cjs");
3
+ const classNameGenerator = require("../class-name-generator-DB5aQwC_.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-B2LriwKm.js";
2
- import { C } from "../class-name-generator-B2LriwKm.js";
1
+ import { S as StylesGenerator } from "../class-name-generator-CMWY5KTJ.js";
2
+ import { C } from "../class-name-generator-CMWY5KTJ.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-BIYysuhW.cjs");
3
+ const classNameGenerator = require("../class-name-generator-DB5aQwC_.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-B2LriwKm.js";
1
+ import { C as ClassNameGenerator } from "../class-name-generator-CMWY5KTJ.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-feat-define-font.0",
3
+ "version": "0.1.0-feat-define-import.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "typings": "./dist/index.d.ts",
@@ -154,7 +154,6 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
154
154
  if (typeof value === "object") {
155
155
  if (!value) return void 0;
156
156
  if (value.isColor) return toString(value.toString());
157
- if (value.isDefineFont) return toString(value.toString());
158
157
  if (_key === "defaultVariants") return void 0;
159
158
  if (_key === "variants") {
160
159
  const variantEntries = Object.entries(value);
@@ -153,7 +153,6 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
153
153
  if (typeof value === "object") {
154
154
  if (!value) return void 0;
155
155
  if (value.isColor) return toString(value.toString());
156
- if (value.isDefineFont) return toString(value.toString());
157
156
  if (_key === "defaultVariants") return void 0;
158
157
  if (_key === "variants") {
159
158
  const variantEntries = Object.entries(value);
package/parsers/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const parseStyles = require("../parse-styles-jPtMfgXH.cjs");
3
+ const parseStyles = require("../parse-styles-C54MOrPg.cjs");
4
4
  const dashCase = require("../dash-case-DIwKaYgE.cjs");
5
5
  const toHash = require("../to-hash-C05Y906F.cjs");
6
6
  const parseTemplates = async (obj, path = []) => {
package/parsers/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { p as parseAndJoinStyles } from "../parse-styles--vHKY6Mw.js";
2
- import { a, c, d, b, r } from "../parse-styles--vHKY6Mw.js";
1
+ import { p as parseAndJoinStyles } from "../parse-styles-CLMTHo2H.js";
2
+ import { a, c, d, b, r } from "../parse-styles-CLMTHo2H.js";
3
3
  import { d as dashCase } from "../dash-case-DblXvymC.js";
4
4
  import { t as toHash } from "../to-hash-DAN2LcHK.js";
5
5
  const parseTemplates = async (obj, path = []) => {
package/runtime/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const parseStyles = require("../parse-styles-jPtMfgXH.cjs");
3
+ const parseStyles = require("../parse-styles-C54MOrPg.cjs");
4
4
  const defineRuntime = (config) => {
5
5
  const getDynamicStylesCss = async (styles, scope) => {
6
6
  const parsed = await parseStyles.parseStyles(styles, scope, config);
package/runtime/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as parseStyles } from "../parse-styles--vHKY6Mw.js";
1
+ import { a as parseStyles } from "../parse-styles-CLMTHo2H.js";
2
2
  const defineRuntime = (config) => {
3
3
  const getDynamicStylesCss = async (styles, scope) => {
4
4
  const parsed = await parseStyles(styles, scope, config);
@@ -1,6 +1,5 @@
1
1
  import { BaseStyles, CssStyles, MediaQueryStyles } from '../types';
2
2
  import { OrString } from '../types/util-types';
3
- export * from './font-types';
4
3
  export type GlobalStyles = Record<string, BaseStyles>;
5
4
  export type CssVariableTokensObject = Record<string, unknown>;
6
5
  export interface CssResponsiveVariables {
@@ -92,3 +91,4 @@ export interface CachedConfig {
92
91
  [key: string]: string;
93
92
  };
94
93
  }
94
+ export {};
@@ -1,28 +0,0 @@
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,53 +0,0 @@
1
- export type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
2
- export type FontFormat = 'woff2' | 'woff' | 'truetype' | 'opentype' | 'embedded-opentype' | 'svg' | 'collection';
3
- export type FontStyle = 'normal' | 'italic' | 'oblique' | (string & {});
4
- export type FontWeight = number | 'normal' | 'bold' | 'lighter' | 'bolder' | (string & {});
5
- export interface FontSrc {
6
- url: string;
7
- format?: FontFormat;
8
- /** Optional `tech(...)` descriptor passed straight through to @font-face. */
9
- tech?: string;
10
- }
11
- export interface FontVariant {
12
- weight?: FontWeight;
13
- style?: FontStyle;
14
- stretch?: string;
15
- display?: FontDisplay;
16
- unicodeRange?: string;
17
- ascentOverride?: string;
18
- descentOverride?: string;
19
- lineGapOverride?: string;
20
- sizeAdjust?: string;
21
- /**
22
- * One or more font sources. Strings are treated as URLs and the `format()`
23
- * descriptor is auto-detected from the file extension when possible. Use
24
- * the `{ url, format }` object form for CDN/extensionless URLs where the
25
- * format must be set explicitly.
26
- */
27
- src: string | FontSrc | (string | FontSrc)[];
28
- }
29
- interface DefineFontBase {
30
- /** CSS `font-family` value users will see in styles. */
31
- name: string;
32
- /**
33
- * CSS variable name. Accepts `--font-inter` or `font-inter`; we normalize.
34
- * Optional — when omitted, a deterministic name is derived from the other
35
- * inputs as `--font-<name>-<hash>`.
36
- */
37
- variable?: string;
38
- /** Default `font-display` applied to variants that don't set their own. */
39
- display?: FontDisplay;
40
- /** Family fallback(s) appended after `name` in the generated `font-family` string. */
41
- fallback?: string;
42
- }
43
- export interface DefineFontVariantsOptions extends DefineFontBase {
44
- variants: FontVariant[];
45
- import?: never;
46
- }
47
- export interface DefineFontImportOptions extends DefineFontBase {
48
- /** Remote stylesheet URL (e.g. Google Fonts). Emitted as `@import url(...)`. */
49
- import: string;
50
- variants?: never;
51
- }
52
- export type DefineFontOptions = DefineFontVariantsOptions | DefineFontImportOptions;
53
- export {};