@salty-css/core 0.2.0-alpha.1 → 0.2.0-alpha.3

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.
@@ -38,6 +38,16 @@
38
38
  "saltygenDir": {
39
39
  "type": "string",
40
40
  "description": "Directory where the salty css files will be generated to, relative to the project root."
41
+ },
42
+ "include": {
43
+ "type": "array",
44
+ "description": "Optional glob patterns (relative to the project root) that limit which files the compiler scans. When set and non-empty, only matching files are compiled. When omitted, the whole project is scanned. Supports **, * and ?.",
45
+ "items": { "type": "string" }
46
+ },
47
+ "exclude": {
48
+ "type": "array",
49
+ "description": "Optional glob patterns (relative to the project root) skipped by the compiler, in addition to the always-skipped node_modules and saltygen folders. Exclusions take precedence over include. Supports **, * and ?.",
50
+ "items": { "type": "string" }
41
51
  }
42
52
  },
43
53
  "required": ["dir", "framework"]
package/bin/main.cjs CHANGED
@@ -85,14 +85,15 @@ const getDefaultProject = async (rootDir = process.cwd()) => {
85
85
  const rc = await readRc(rootDir);
86
86
  return rc.defaultProject;
87
87
  };
88
- const upsertProjectInRc = (existingRaw, relativeProjectPath, framework) => {
88
+ const upsertProjectInRc = (existingRaw, relativeProjectPath, framework, pathDefaults = {}) => {
89
89
  const projectPath = path.join(relativeProjectPath, framework.srcDirectory);
90
+ const newEntry = { dir: projectPath, framework: framework.name, ...pathDefaults };
90
91
  if (existingRaw === void 0) {
91
92
  const fresh = {
92
93
  $schema: SALTYRC_SCHEMA,
93
94
  info: SALTYRC_INFO,
94
95
  defaultProject: projectPath,
95
- projects: [{ dir: projectPath, framework: framework.name }]
96
+ projects: [newEntry]
96
97
  };
97
98
  return { content: JSON.stringify(fresh, null, 2), changed: true, created: true };
98
99
  }
@@ -100,15 +101,15 @@ const upsertProjectInRc = (existingRaw, relativeProjectPath, framework) => {
100
101
  const projects = rc.projects || [];
101
102
  const exists = projects.some((p) => p.dir === projectPath);
102
103
  if (exists) return { content: existingRaw, changed: false, created: false };
103
- projects.push({ dir: projectPath, framework: framework.name });
104
+ projects.push(newEntry);
104
105
  rc.projects = [...projects];
105
106
  const next = JSON.stringify(rc, null, 2);
106
107
  return { content: next, changed: next !== existingRaw, created: false };
107
108
  };
108
- const writeProjectToRc = async (cwd, relativeProjectPath, framework) => {
109
+ const writeProjectToRc = async (cwd, relativeProjectPath, framework, pathDefaults = {}) => {
109
110
  const path2 = saltyrcPath(cwd);
110
111
  const existing = await readRawRc(cwd);
111
- const { content, changed, created } = upsertProjectInRc(existing, relativeProjectPath, framework);
112
+ const { content, changed, created } = upsertProjectInRc(existing, relativeProjectPath, framework, pathDefaults);
112
113
  if (!changed) return false;
113
114
  if (created) compiler_saltyCompiler.logger.info("Creating file: " + path2);
114
115
  else compiler_saltyCompiler.logger.info("Edit file: " + path2);
@@ -580,6 +581,18 @@ const applyIntegrationPlans = async (planned) => {
580
581
  }
581
582
  return results;
582
583
  };
584
+ const BUILD_OUTPUTS = {
585
+ vite: ["dist/**"],
586
+ next: [".next/**", "out/**"]
587
+ };
588
+ const computePathDefaults = ({ framework, integrations, hasSrcDir }) => {
589
+ if (framework === "astro") return {};
590
+ const exclude = [...new Set(integrations.flatMap((name) => BUILD_OUTPUTS[name] ?? []))];
591
+ const result = {};
592
+ if (hasSrcDir) result.include = ["src/**"];
593
+ if (exclude.length) result.exclude = exclude;
594
+ return result;
595
+ };
583
596
  const writeProjectFile = async (projectDir, fileName, content) => {
584
597
  const filePath = path.join(projectDir, fileName);
585
598
  if (fs.existsSync(filePath)) {
@@ -649,7 +662,12 @@ const registerInitCommand = (program) => {
649
662
  const projectFiles = await Promise.all([readTemplate("salty.config.ts"), readTemplate("saltygen/index.css")]);
650
663
  await promises.mkdir(ctx.projectDir, { recursive: true });
651
664
  await Promise.all(projectFiles.map(({ fileName, content }) => writeProjectFile(ctx.projectDir, fileName, content)));
652
- await writeProjectToRc(ctx.cwd, ctx.relativeProjectPath, framework);
665
+ const pathDefaults = computePathDefaults({
666
+ framework: framework.name,
667
+ integrations: plannedIntegrations.map((p) => p.name),
668
+ hasSrcDir: fs.existsSync(path.join(ctx.projectDir, "src"))
669
+ });
670
+ await writeProjectToRc(ctx.cwd, ctx.relativeProjectPath, framework, pathDefaults);
653
671
  await ensureGitignoreSaltygen(ctx.cwd);
654
672
  await importSaltygenIntoCss(ctx.projectDir, opts.cssFile);
655
673
  await applyIntegrationPlans(plannedIntegrations);
package/bin/main.js CHANGED
@@ -82,14 +82,15 @@ const getDefaultProject = async (rootDir = process.cwd()) => {
82
82
  const rc = await readRc(rootDir);
83
83
  return rc.defaultProject;
84
84
  };
85
- const upsertProjectInRc = (existingRaw, relativeProjectPath, framework) => {
85
+ const upsertProjectInRc = (existingRaw, relativeProjectPath, framework, pathDefaults = {}) => {
86
86
  const projectPath = join(relativeProjectPath, framework.srcDirectory);
87
+ const newEntry = { dir: projectPath, framework: framework.name, ...pathDefaults };
87
88
  if (existingRaw === void 0) {
88
89
  const fresh = {
89
90
  $schema: SALTYRC_SCHEMA,
90
91
  info: SALTYRC_INFO,
91
92
  defaultProject: projectPath,
92
- projects: [{ dir: projectPath, framework: framework.name }]
93
+ projects: [newEntry]
93
94
  };
94
95
  return { content: JSON.stringify(fresh, null, 2), changed: true, created: true };
95
96
  }
@@ -97,15 +98,15 @@ const upsertProjectInRc = (existingRaw, relativeProjectPath, framework) => {
97
98
  const projects = rc.projects || [];
98
99
  const exists = projects.some((p) => p.dir === projectPath);
99
100
  if (exists) return { content: existingRaw, changed: false, created: false };
100
- projects.push({ dir: projectPath, framework: framework.name });
101
+ projects.push(newEntry);
101
102
  rc.projects = [...projects];
102
103
  const next = JSON.stringify(rc, null, 2);
103
104
  return { content: next, changed: next !== existingRaw, created: false };
104
105
  };
105
- const writeProjectToRc = async (cwd, relativeProjectPath, framework) => {
106
+ const writeProjectToRc = async (cwd, relativeProjectPath, framework, pathDefaults = {}) => {
106
107
  const path = saltyrcPath(cwd);
107
108
  const existing = await readRawRc(cwd);
108
- const { content, changed, created } = upsertProjectInRc(existing, relativeProjectPath, framework);
109
+ const { content, changed, created } = upsertProjectInRc(existing, relativeProjectPath, framework, pathDefaults);
109
110
  if (!changed) return false;
110
111
  if (created) logger.info("Creating file: " + path);
111
112
  else logger.info("Edit file: " + path);
@@ -577,6 +578,18 @@ const applyIntegrationPlans = async (planned) => {
577
578
  }
578
579
  return results;
579
580
  };
581
+ const BUILD_OUTPUTS = {
582
+ vite: ["dist/**"],
583
+ next: [".next/**", "out/**"]
584
+ };
585
+ const computePathDefaults = ({ framework, integrations, hasSrcDir }) => {
586
+ if (framework === "astro") return {};
587
+ const exclude = [...new Set(integrations.flatMap((name) => BUILD_OUTPUTS[name] ?? []))];
588
+ const result = {};
589
+ if (hasSrcDir) result.include = ["src/**"];
590
+ if (exclude.length) result.exclude = exclude;
591
+ return result;
592
+ };
580
593
  const writeProjectFile = async (projectDir, fileName, content) => {
581
594
  const filePath = join(projectDir, fileName);
582
595
  if (existsSync(filePath)) {
@@ -646,7 +659,12 @@ const registerInitCommand = (program) => {
646
659
  const projectFiles = await Promise.all([readTemplate("salty.config.ts"), readTemplate("saltygen/index.css")]);
647
660
  await mkdir(ctx.projectDir, { recursive: true });
648
661
  await Promise.all(projectFiles.map(({ fileName, content }) => writeProjectFile(ctx.projectDir, fileName, content)));
649
- await writeProjectToRc(ctx.cwd, ctx.relativeProjectPath, framework);
662
+ const pathDefaults = computePathDefaults({
663
+ framework: framework.name,
664
+ integrations: plannedIntegrations.map((p) => p.name),
665
+ hasSrcDir: existsSync(join(ctx.projectDir, "src"))
666
+ });
667
+ await writeProjectToRc(ctx.cwd, ctx.relativeProjectPath, framework, pathDefaults);
650
668
  await ensureGitignoreSaltygen(ctx.cwd);
651
669
  await importSaltygenIntoCss(ctx.projectDir, opts.cssFile);
652
670
  await applyIntegrationPlans(plannedIntegrations);
@@ -0,0 +1,20 @@
1
+ import { FrameworkName } from './frameworks';
2
+ export interface PathDefaultsInput {
3
+ framework: FrameworkName;
4
+ /** Names of build integrations detected for the project, e.g. ['vite'] or ['next','eslint']. */
5
+ integrations: string[];
6
+ /** Whether a `src/` directory exists at the project root. */
7
+ hasSrcDir: boolean;
8
+ }
9
+ export interface PathDefaults {
10
+ include?: string[];
11
+ exclude?: string[];
12
+ }
13
+ /**
14
+ * Compute sensible default `include` / `exclude` globs for a freshly initialized project.
15
+ * - Astro is already scoped to `src/` by its plugin (it roots the compiler there), so it
16
+ * gets no defaults.
17
+ * - React / Vite / Next: exclude known build outputs, and narrow the walk to `src/**` only
18
+ * when a `src/` folder actually exists (projects with root-level salty files keep the full walk).
19
+ */
20
+ export declare const computePathDefaults: ({ framework, integrations, hasSrcDir }: PathDefaultsInput) => PathDefaults;
package/bin/saltyrc.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RCFile } from '../types/cli-types';
2
2
  import { FrameworkAdapter } from './frameworks';
3
+ import { PathDefaults } from './path-defaults';
3
4
  export declare const SALTYRC_FILENAME = ".saltyrc.json";
4
5
  export declare const SALTYRC_SCHEMA = "./node_modules/@salty-css/core/.saltyrc.schema.json";
5
6
  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.";
@@ -18,7 +19,7 @@ export declare const getDefaultProject: (rootDir?: string) => Promise<string | u
18
19
  * - If the project entry already exists, returns the same content (no-op).
19
20
  * - Otherwise appends the new entry and returns updated content.
20
21
  */
21
- export declare const upsertProjectInRc: (existingRaw: string | undefined, relativeProjectPath: string, framework: FrameworkAdapter) => {
22
+ export declare const upsertProjectInRc: (existingRaw: string | undefined, relativeProjectPath: string, framework: FrameworkAdapter, pathDefaults?: PathDefaults) => {
22
23
  content: string;
23
24
  changed: boolean;
24
25
  created: boolean;
@@ -27,5 +28,5 @@ export declare const upsertProjectInRc: (existingRaw: string | undefined, relati
27
28
  * Writes the saltyrc file, creating or updating the project entry for the given dir.
28
29
  * Returns true when a write occurred.
29
30
  */
30
- export declare const writeProjectToRc: (cwd: string, relativeProjectPath: string, framework: FrameworkAdapter) => Promise<boolean>;
31
+ export declare const writeProjectToRc: (cwd: string, relativeProjectPath: string, framework: FrameworkAdapter, pathDefaults?: PathDefaults) => Promise<boolean>;
31
32
  export declare const getProjectFramework: (rc: RCFile, relativeProjectPath: string) => string | undefined;
@@ -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-BbI-2wdn.cjs");
5
+ const parseStyles = require("./parse-styles-DWT4tDyS.cjs");
6
6
  const toHash = require("./to-hash-DT2ImSPA.cjs");
7
7
  class StylesGenerator {
8
8
  constructor(params) {
@@ -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, b as parseTemplateCallSite } from "./parse-styles-BgVqQAni.js";
4
+ import { p as parseAndJoinStyles, b as parseTemplateCallSite } from "./parse-styles-BlUpavVg.js";
5
5
  import { t as toHash, d as dashCase } from "./to-hash-DSoCPs8D.js";
6
6
  class StylesGenerator {
7
7
  constructor(params) {
@@ -11,9 +11,10 @@ const fs = require("fs");
11
11
  const child_process = require("child_process");
12
12
  const compiler_helpers = require("./helpers.cjs");
13
13
  const toHash = require("../to-hash-DT2ImSPA.cjs");
14
+ const globMatch = require("../glob-match-CT1uFlp-.cjs");
14
15
  const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
15
16
  const module$1 = require("module");
16
- const parseStyles = require("../parse-styles-BbI-2wdn.cjs");
17
+ const parseStyles = require("../parse-styles-DWT4tDyS.cjs");
17
18
  const css_merge = require("../css/merge.cjs");
18
19
  const compiler_getFiles = require("./get-files.cjs");
19
20
  const parsers_index = require("../parsers/index.cjs");
@@ -196,6 +197,23 @@ class SaltyCompiler {
196
197
  if (!projectConfig) return (_b = rcFile.projects) == null ? void 0 : _b.find((project) => project.dir === rcFile.defaultProject);
197
198
  return projectConfig;
198
199
  });
200
+ /**
201
+ * Read the optional `include` / `exclude` glob filters for the current project from
202
+ * `.saltyrc.json`. Used to scope which files the compiler discovers and watches.
203
+ */
204
+ __publicField(this, "getPathFilter", async () => {
205
+ const projectConfig = await this.getRCProjectConfig(this.projectRootDir);
206
+ return { include: projectConfig == null ? void 0 : projectConfig.include, exclude: projectConfig == null ? void 0 : projectConfig.exclude };
207
+ });
208
+ /**
209
+ * Whether a file should be compiled: it must be a salty file and pass the project's
210
+ * `include` / `exclude` filters. `relPath` is the path relative to the project root.
211
+ */
212
+ __publicField(this, "isAllowedSaltyFile", (file, filter) => {
213
+ if (!compiler_helpers.isSaltyFile(file)) return false;
214
+ const relPath = globMatch.normalizePath(path.relative(this.projectRootDir, file));
215
+ return globMatch.isPathAllowed(relPath, filter);
216
+ });
199
217
  __publicField(this, "getExternalModules", (coreConfigPath) => {
200
218
  if (this.cache.externalModules.length > 0) return this.cache.externalModules;
201
219
  const content = fs.readFileSync(coreConfigPath, "utf8");
@@ -286,16 +304,23 @@ ${currentFile}`;
286
304
  if (clean) clearDistDir();
287
305
  const files = /* @__PURE__ */ new Set();
288
306
  const configFiles = /* @__PURE__ */ new Set();
307
+ const pathFilter = await this.getPathFilter();
308
+ const { exclude } = pathFilter;
309
+ const projectRootDir = this.projectRootDir;
289
310
  async function collectFiles(src) {
290
311
  const foldersToSkip = ["node_modules", "saltygen"];
291
312
  const stats = fs.statSync(src);
292
313
  if (stats.isDirectory()) {
293
- const files2 = fs.readdirSync(src);
294
314
  const shouldSkip = foldersToSkip.some((folder) => src.includes(folder));
295
315
  if (shouldSkip) return;
316
+ if (exclude == null ? void 0 : exclude.length) {
317
+ const relDir = globMatch.normalizePath(path.relative(projectRootDir, src));
318
+ if (relDir && !globMatch.isPathAllowed(relDir, { exclude })) return;
319
+ }
320
+ const files2 = fs.readdirSync(src);
296
321
  await Promise.all(files2.map((file) => collectFiles(path.join(src, file))));
297
322
  } else if (stats.isFile()) {
298
- const validFile = compiler_helpers.isSaltyFile(src);
323
+ const validFile = compiler_helpers.isSaltyFile(src) && globMatch.isPathAllowed(globMatch.normalizePath(path.relative(projectRootDir, src)), pathFilter);
299
324
  if (validFile) {
300
325
  files.add(src);
301
326
  const contents = fs.readFileSync(src, "utf8");
@@ -704,7 +729,8 @@ ${css}
704
729
  __publicField(this, "generateFile", async (file) => {
705
730
  try {
706
731
  const destDir = await this.getDestDir();
707
- const validFile = compiler_helpers.isSaltyFile(file);
732
+ const pathFilter = await this.getPathFilter();
733
+ const validFile = this.isAllowedSaltyFile(file, pathFilter);
708
734
  if (validFile) {
709
735
  const cssFiles = [];
710
736
  const config = await this.getConfig();
@@ -20,6 +20,16 @@ export declare class SaltyCompiler {
20
20
  * If no specific project configuration is found, it falls back to the default project.
21
21
  */
22
22
  private getRCProjectConfig;
23
+ /**
24
+ * Read the optional `include` / `exclude` glob filters for the current project from
25
+ * `.saltyrc.json`. Used to scope which files the compiler discovers and watches.
26
+ */
27
+ private getPathFilter;
28
+ /**
29
+ * Whether a file should be compiled: it must be a salty file and pass the project's
30
+ * `include` / `exclude` filters. `relPath` is the path relative to the project root.
31
+ */
32
+ private isAllowedSaltyFile;
23
33
  private getExternalModules;
24
34
  /**
25
35
  * Get the destination directory for generated files based on the project configuration.
@@ -9,9 +9,10 @@ import { existsSync, mkdirSync, copyFileSync, readFileSync, statSync, readdirSyn
9
9
  import { execSync } from "child_process";
10
10
  import { isSaltyFile, resolveExportValue, saltyFileExtensions } from "./helpers.js";
11
11
  import { t as toHash, d as dashCase } from "../to-hash-DSoCPs8D.js";
12
+ import { n as normalizePath, i as isPathAllowed } from "../glob-match-C28iYp1A.js";
12
13
  import { d as defineTemplates } from "../define-templates-CVhhgPnd.js";
13
14
  import { createRequire } from "module";
14
- import { p as parseAndJoinStyles, c as parseVariableTokens } from "../parse-styles-BgVqQAni.js";
15
+ import { p as parseAndJoinStyles, c as parseVariableTokens } from "../parse-styles-BlUpavVg.js";
15
16
  import { mergeObjects, mergeFactories } from "../css/merge.js";
16
17
  import { getPackageJson } from "./get-files.js";
17
18
  import { parseTemplates, getTemplateTypes, getTemplateVariantMaps } from "../parsers/index.js";
@@ -176,6 +177,23 @@ class SaltyCompiler {
176
177
  if (!projectConfig) return (_b = rcFile.projects) == null ? void 0 : _b.find((project) => project.dir === rcFile.defaultProject);
177
178
  return projectConfig;
178
179
  });
180
+ /**
181
+ * Read the optional `include` / `exclude` glob filters for the current project from
182
+ * `.saltyrc.json`. Used to scope which files the compiler discovers and watches.
183
+ */
184
+ __publicField(this, "getPathFilter", async () => {
185
+ const projectConfig = await this.getRCProjectConfig(this.projectRootDir);
186
+ return { include: projectConfig == null ? void 0 : projectConfig.include, exclude: projectConfig == null ? void 0 : projectConfig.exclude };
187
+ });
188
+ /**
189
+ * Whether a file should be compiled: it must be a salty file and pass the project's
190
+ * `include` / `exclude` filters. `relPath` is the path relative to the project root.
191
+ */
192
+ __publicField(this, "isAllowedSaltyFile", (file, filter) => {
193
+ if (!isSaltyFile(file)) return false;
194
+ const relPath = normalizePath(relative(this.projectRootDir, file));
195
+ return isPathAllowed(relPath, filter);
196
+ });
179
197
  __publicField(this, "getExternalModules", (coreConfigPath) => {
180
198
  if (this.cache.externalModules.length > 0) return this.cache.externalModules;
181
199
  const content = readFileSync(coreConfigPath, "utf8");
@@ -266,16 +284,23 @@ ${currentFile}`;
266
284
  if (clean) clearDistDir();
267
285
  const files = /* @__PURE__ */ new Set();
268
286
  const configFiles = /* @__PURE__ */ new Set();
287
+ const pathFilter = await this.getPathFilter();
288
+ const { exclude } = pathFilter;
289
+ const projectRootDir = this.projectRootDir;
269
290
  async function collectFiles(src) {
270
291
  const foldersToSkip = ["node_modules", "saltygen"];
271
292
  const stats = statSync(src);
272
293
  if (stats.isDirectory()) {
273
- const files2 = readdirSync(src);
274
294
  const shouldSkip = foldersToSkip.some((folder) => src.includes(folder));
275
295
  if (shouldSkip) return;
296
+ if (exclude == null ? void 0 : exclude.length) {
297
+ const relDir = normalizePath(relative(projectRootDir, src));
298
+ if (relDir && !isPathAllowed(relDir, { exclude })) return;
299
+ }
300
+ const files2 = readdirSync(src);
276
301
  await Promise.all(files2.map((file) => collectFiles(join(src, file))));
277
302
  } else if (stats.isFile()) {
278
- const validFile = isSaltyFile(src);
303
+ const validFile = isSaltyFile(src) && isPathAllowed(normalizePath(relative(projectRootDir, src)), pathFilter);
279
304
  if (validFile) {
280
305
  files.add(src);
281
306
  const contents = readFileSync(src, "utf8");
@@ -684,7 +709,8 @@ ${css}
684
709
  __publicField(this, "generateFile", async (file) => {
685
710
  try {
686
711
  const destDir = await this.getDestDir();
687
- const validFile = isSaltyFile(file);
712
+ const pathFilter = await this.getPathFilter();
713
+ const validFile = this.isAllowedSaltyFile(file, pathFilter);
688
714
  if (validFile) {
689
715
  const cssFiles = [];
690
716
  const config = await this.getConfig();
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-BbI-2wdn.cjs");
3
+ const parseStyles = require("../parse-styles-DWT4tDyS.cjs");
4
4
  const toHash = require("../to-hash-DT2ImSPA.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-BgVqQAni.js";
1
+ import { p as parseAndJoinStyles } from "../parse-styles-BlUpavVg.js";
2
2
  import { t as toHash } from "../to-hash-DSoCPs8D.js";
3
3
  const keyframes = ({ animationName: _name, params: _params, appendInitialStyles, ...keyframes2 }) => {
4
4
  const modifyKeyframes = async (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-MtPkBfM_.cjs");
3
+ const classNameGenerator = require("../class-name-generator-BcbeZdcV.cjs");
4
4
  const toHash = require("../to-hash-DT2ImSPA.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-CUEoPowv.js";
2
- import { C } from "../class-name-generator-CUEoPowv.js";
1
+ import { S as StylesGenerator } from "../class-name-generator-DJ27JHW_.js";
2
+ import { C } from "../class-name-generator-DJ27JHW_.js";
3
3
  import { d as dashCase } from "../to-hash-DSoCPs8D.js";
4
4
  class StyledGenerator extends StylesGenerator {
5
5
  constructor(tagName, _params) {
@@ -0,0 +1,74 @@
1
+ const normalizePath = (path) => path.replace(/\\/g, "/");
2
+ const tokenCache = /* @__PURE__ */ new Map();
3
+ const tokenize = (pattern) => {
4
+ const cached = tokenCache.get(pattern);
5
+ if (cached) return cached;
6
+ const normalized = normalizePath(pattern);
7
+ const tokens = [];
8
+ for (let i = 0; i < normalized.length; i++) {
9
+ const char = normalized[i];
10
+ if (char === "*") {
11
+ let stars = 1;
12
+ while (normalized[i + 1] === "*") {
13
+ i++;
14
+ stars++;
15
+ }
16
+ tokens.push(stars >= 2 ? { type: "globstar" } : { type: "star" });
17
+ } else if (char === "?") {
18
+ tokens.push({ type: "single" });
19
+ } else {
20
+ tokens.push({ type: "lit", char });
21
+ }
22
+ }
23
+ tokenCache.set(pattern, tokens);
24
+ return tokens;
25
+ };
26
+ const matchesGlob = (pattern, path) => {
27
+ const tokens = tokenize(pattern);
28
+ const normalized = normalizePath(path);
29
+ const P = tokens.length;
30
+ const S = normalized.length;
31
+ const memo = new Uint8Array((P + 1) * (S + 1));
32
+ const match = (ti, si) => {
33
+ const key = ti * (S + 1) + si;
34
+ const seen = memo[key];
35
+ if (seen) return seen === 2;
36
+ let result;
37
+ if (ti === P) {
38
+ result = si === S;
39
+ } else {
40
+ const token = tokens[ti];
41
+ switch (token.type) {
42
+ case "lit":
43
+ result = si < S && normalized[si] === token.char && match(ti + 1, si + 1);
44
+ break;
45
+ case "single":
46
+ result = si < S && normalized[si] !== "/" && match(ti + 1, si + 1);
47
+ break;
48
+ case "star":
49
+ result = match(ti + 1, si) || si < S && normalized[si] !== "/" && match(ti, si + 1);
50
+ break;
51
+ case "globstar":
52
+ result = match(ti + 1, si) || si < S && match(ti, si + 1);
53
+ break;
54
+ }
55
+ }
56
+ memo[key] = result ? 2 : 1;
57
+ return result;
58
+ };
59
+ return match(0, 0);
60
+ };
61
+ const matchesAnyGlob = (relPath, patterns) => {
62
+ return patterns.some((pattern) => matchesGlob(pattern, relPath));
63
+ };
64
+ const isPathAllowed = (relPath, { include, exclude } = {}) => {
65
+ if ((exclude == null ? void 0 : exclude.length) && matchesAnyGlob(relPath, exclude)) return false;
66
+ if (include == null ? void 0 : include.length) return matchesAnyGlob(relPath, include);
67
+ return true;
68
+ };
69
+ export {
70
+ matchesGlob as a,
71
+ isPathAllowed as i,
72
+ matchesAnyGlob as m,
73
+ normalizePath as n
74
+ };
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ const normalizePath = (path) => path.replace(/\\/g, "/");
3
+ const tokenCache = /* @__PURE__ */ new Map();
4
+ const tokenize = (pattern) => {
5
+ const cached = tokenCache.get(pattern);
6
+ if (cached) return cached;
7
+ const normalized = normalizePath(pattern);
8
+ const tokens = [];
9
+ for (let i = 0; i < normalized.length; i++) {
10
+ const char = normalized[i];
11
+ if (char === "*") {
12
+ let stars = 1;
13
+ while (normalized[i + 1] === "*") {
14
+ i++;
15
+ stars++;
16
+ }
17
+ tokens.push(stars >= 2 ? { type: "globstar" } : { type: "star" });
18
+ } else if (char === "?") {
19
+ tokens.push({ type: "single" });
20
+ } else {
21
+ tokens.push({ type: "lit", char });
22
+ }
23
+ }
24
+ tokenCache.set(pattern, tokens);
25
+ return tokens;
26
+ };
27
+ const matchesGlob = (pattern, path) => {
28
+ const tokens = tokenize(pattern);
29
+ const normalized = normalizePath(path);
30
+ const P = tokens.length;
31
+ const S = normalized.length;
32
+ const memo = new Uint8Array((P + 1) * (S + 1));
33
+ const match = (ti, si) => {
34
+ const key = ti * (S + 1) + si;
35
+ const seen = memo[key];
36
+ if (seen) return seen === 2;
37
+ let result;
38
+ if (ti === P) {
39
+ result = si === S;
40
+ } else {
41
+ const token = tokens[ti];
42
+ switch (token.type) {
43
+ case "lit":
44
+ result = si < S && normalized[si] === token.char && match(ti + 1, si + 1);
45
+ break;
46
+ case "single":
47
+ result = si < S && normalized[si] !== "/" && match(ti + 1, si + 1);
48
+ break;
49
+ case "star":
50
+ result = match(ti + 1, si) || si < S && normalized[si] !== "/" && match(ti, si + 1);
51
+ break;
52
+ case "globstar":
53
+ result = match(ti + 1, si) || si < S && match(ti, si + 1);
54
+ break;
55
+ }
56
+ }
57
+ memo[key] = result ? 2 : 1;
58
+ return result;
59
+ };
60
+ return match(0, 0);
61
+ };
62
+ const matchesAnyGlob = (relPath, patterns) => {
63
+ return patterns.some((pattern) => matchesGlob(pattern, relPath));
64
+ };
65
+ const isPathAllowed = (relPath, { include, exclude } = {}) => {
66
+ if ((exclude == null ? void 0 : exclude.length) && matchesAnyGlob(relPath, exclude)) return false;
67
+ if (include == null ? void 0 : include.length) return matchesAnyGlob(relPath, include);
68
+ return true;
69
+ };
70
+ exports.isPathAllowed = isPathAllowed;
71
+ exports.matchesAnyGlob = matchesAnyGlob;
72
+ exports.matchesGlob = matchesGlob;
73
+ exports.normalizePath = normalizePath;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const classNameGenerator = require("../class-name-generator-MtPkBfM_.cjs");
3
+ const classNameGenerator = require("../class-name-generator-BcbeZdcV.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-CUEoPowv.js";
1
+ import { C as ClassNameGenerator } from "../class-name-generator-DJ27JHW_.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.2.0-alpha.1",
3
+ "version": "0.2.0-alpha.3",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "typings": "./dist/index.d.ts",
@@ -373,6 +373,11 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
373
373
  }
374
374
  return void 0;
375
375
  }
376
+ if (_key === "global") {
377
+ const results2 = await parseStyles(value, "", config);
378
+ results2.forEach((res) => cssStyles.add(res));
379
+ return void 0;
380
+ }
376
381
  if (_key.startsWith("@")) {
377
382
  if (bareAtRuleRegex.test(_key)) reportParserIssue(strict, `At-rule "${_key}" is missing its condition (e.g. "@media (min-width: 600px)").`);
378
383
  const innerScope = keyframesAtRuleRegex.test(_key) ? "" : currentScope;
@@ -374,6 +374,11 @@ const parseStyles = async (styles, currentScope = "", config, omitTemplates = fa
374
374
  }
375
375
  return void 0;
376
376
  }
377
+ if (_key === "global") {
378
+ const results2 = await parseStyles(value, "", config);
379
+ results2.forEach((res) => cssStyles.add(res));
380
+ return void 0;
381
+ }
377
382
  if (_key.startsWith("@")) {
378
383
  if (bareAtRuleRegex.test(_key)) reportParserIssue(strict, `At-rule "${_key}" is missing its condition (e.g. "@media (min-width: 600px)").`);
379
384
  const innerScope = keyframesAtRuleRegex.test(_key) ? "" : currentScope;
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-BbI-2wdn.cjs");
3
+ const parseStyles = require("../parse-styles-DWT4tDyS.cjs");
4
4
  const toHash = require("../to-hash-DT2ImSPA.cjs");
5
5
  const RICH_META_KEYS = /* @__PURE__ */ new Set(["base", "variants", "defaultVariants", "compoundVariants", "anyOfVariants"]);
6
6
  const isChildEntry = (key, value) => {
package/parsers/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { i as isRichTemplateNode, p as parseAndJoinStyles } from "../parse-styles-BgVqQAni.js";
2
- import { a, d, e, c, r } from "../parse-styles-BgVqQAni.js";
1
+ import { i as isRichTemplateNode, p as parseAndJoinStyles } from "../parse-styles-BlUpavVg.js";
2
+ import { a, d, e, c, r } from "../parse-styles-BlUpavVg.js";
3
3
  import { d as dashCase, t as toHash } from "../to-hash-DSoCPs8D.js";
4
4
  const RICH_META_KEYS = /* @__PURE__ */ new Set(["base", "variants", "defaultVariants", "compoundVariants", "anyOfVariants"]);
5
5
  const isChildEntry = (key, value) => {
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-BbI-2wdn.cjs");
3
+ const parseStyles = require("../parse-styles-DWT4tDyS.cjs");
4
4
  const toHash = require("../to-hash-DT2ImSPA.cjs");
5
5
  const defineRuntime = (config) => {
6
6
  const className = (styles) => toHash.toHash(styles);
package/runtime/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as parseStyles } from "../parse-styles-BgVqQAni.js";
1
+ import { a as parseStyles } from "../parse-styles-BlUpavVg.js";
2
2
  import { t as toHash } from "../to-hash-DSoCPs8D.js";
3
3
  const defineRuntime = (config) => {
4
4
  const className = (styles) => toHash(styles);
@@ -6,5 +6,17 @@ export interface RCFile {
6
6
  components?: string;
7
7
  configDir?: string;
8
8
  saltygenDir?: string;
9
+ /**
10
+ * Optional glob patterns (relative to the project root) that limit which files the
11
+ * compiler scans. When set and non-empty, only matching files are compiled. When
12
+ * omitted, the whole project is scanned. Supports `**`, `*`, and `?`.
13
+ */
14
+ include?: string[];
15
+ /**
16
+ * Optional glob patterns (relative to the project root) that are skipped by the
17
+ * compiler, in addition to the always-skipped `node_modules` and `saltygen` folders.
18
+ * Exclusions take precedence over `include`. Supports `**`, `*`, and `?`.
19
+ */
20
+ exclude?: string[];
9
21
  }[];
10
22
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Minimal, dependency-free glob matching for `.saltyrc.json` `include` / `exclude`.
3
+ *
4
+ * Supports the three glob tokens users expect from tsconfig-style patterns:
5
+ * - `**` matches any number of characters, including `/` (crosses directories).
6
+ * - `*` matches any number of characters except `/` (single path segment).
7
+ * - `?` matches a single character except `/`.
8
+ *
9
+ * Patterns and paths are matched with `/` as the separator; callers should
10
+ * normalize Windows separators before matching (see `normalizePath`).
11
+ *
12
+ * Matching is done with a memoized dynamic-programming walk rather than by
13
+ * translating to a `.*`-based RegExp. A regex translation of `**` → `.*` is
14
+ * vulnerable to catastrophic backtracking (ReDoS): a pattern like
15
+ * `**a**a**a…X` becomes `^.*a.*a.*a…X$` and can hang for seconds on a long
16
+ * non-matching path. The DP walk below visits each `(token, char)` pair at most
17
+ * once, giving a hard `O(pattern x path)` bound with no backtracking.
18
+ */
19
+ /** Normalize a path to use `/` separators so globs match cross-platform. */
20
+ export declare const normalizePath: (path: string) => string;
21
+ /** True when `path` matches the glob `pattern` (full, anchored match). */
22
+ export declare const matchesGlob: (pattern: string, path: string) => boolean;
23
+ /** True when `relPath` matches at least one of the glob `patterns`. */
24
+ export declare const matchesAnyGlob: (relPath: string, patterns: string[]) => boolean;
25
+ export interface PathFilterOptions {
26
+ include?: string[];
27
+ exclude?: string[];
28
+ }
29
+ /**
30
+ * Decide whether a project-relative path is allowed by the configured filters.
31
+ * - Anything matching `exclude` is rejected.
32
+ * - When `include` is non-empty, only matching paths are allowed.
33
+ * - When `include` is empty/undefined, everything not excluded is allowed.
34
+ */
35
+ export declare const isPathAllowed: (relPath: string, { include, exclude }?: PathFilterOptions) => boolean;
package/util/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const toHash = require("../to-hash-DT2ImSPA.cjs");
4
+ const globMatch = require("../glob-match-CT1uFlp-.cjs");
4
5
  const pascalCase = require("../pascal-case-By_l58S-.cjs");
5
6
  function camelCase(str) {
6
7
  if (!str) return "";
@@ -9,5 +10,9 @@ function camelCase(str) {
9
10
  }
10
11
  exports.dashCase = toHash.dashCase;
11
12
  exports.toHash = toHash.toHash;
13
+ exports.isPathAllowed = globMatch.isPathAllowed;
14
+ exports.matchesAnyGlob = globMatch.matchesAnyGlob;
15
+ exports.matchesGlob = globMatch.matchesGlob;
16
+ exports.normalizePath = globMatch.normalizePath;
12
17
  exports.pascalCase = pascalCase.pascalCase;
13
18
  exports.camelCase = camelCase;
package/util/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './camel-case';
2
2
  export * from './dash-case';
3
+ export * from './glob-match';
3
4
  export * from './pascal-case';
4
5
  export * from './to-hash';
package/util/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { d, t } from "../to-hash-DSoCPs8D.js";
2
+ import { i, m, a, n } from "../glob-match-C28iYp1A.js";
2
3
  import { p } from "../pascal-case-F3Usi5Wf.js";
3
4
  function camelCase(str) {
4
5
  if (!str) return "";
@@ -8,6 +9,10 @@ function camelCase(str) {
8
9
  export {
9
10
  camelCase,
10
11
  d as dashCase,
12
+ i as isPathAllowed,
13
+ m as matchesAnyGlob,
14
+ a as matchesGlob,
15
+ n as normalizePath,
11
16
  p as pascalCase,
12
17
  t as toHash
13
18
  };