styles-config 2.0.0-alpha.1 → 2.0.0-alpha.11
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 +29 -11
- package/lib/index.cjs +249 -0
- package/lib/index.js +217 -4
- package/lib/options.d.ts +27 -0
- package/lib/types.d.ts +131 -28
- package/package.json +16 -8
- package/eslint.config.mjs +0 -6
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/loader.d.ts.map +0 -1
- package/lib/loader.js +0 -99
- package/lib/loader.js.map +0 -1
- package/lib/options.d.ts.map +0 -1
- package/lib/options.js +0 -128
- package/lib/options.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/src/index.ts +0 -3
- package/src/loader.ts +0 -112
- package/src/options.ts +0 -172
- package/src/types.ts +0 -359
- package/test/options.test.ts +0 -256
- package/tsconfig.build.json +0 -10
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
# styles-config
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
**A shared configuration schema and loader for styling tools (Jess, Less, Sass,
|
|
4
|
+
Tailwind, …).**
|
|
5
|
+
|
|
6
|
+
`styles-config` is part of the [Jess](https://github.com/jesscss/jess) project.
|
|
7
|
+
It is published **unscoped** — the package name is `styles-config`, not
|
|
8
|
+
`@jesscss/config`.
|
|
4
9
|
|
|
5
10
|
## Overview
|
|
6
11
|
|
|
7
|
-
`styles-config` provides a unified configuration format and loading system that
|
|
12
|
+
`styles-config` provides a unified configuration format and loading system that
|
|
13
|
+
works across multiple CSS preprocessors and styling frameworks. It lets you define
|
|
14
|
+
configuration once and use it with different tools, or maintain separate
|
|
15
|
+
configurations for different frameworks. Jess itself reads its compile options
|
|
16
|
+
through this loader.
|
|
8
17
|
|
|
9
18
|
## Features
|
|
10
19
|
|
|
@@ -16,14 +25,12 @@ A general-purpose configuration system for styling frameworks including Jess, Le
|
|
|
16
25
|
|
|
17
26
|
## Installation
|
|
18
27
|
|
|
19
|
-
```
|
|
28
|
+
```sh
|
|
20
29
|
npm install styles-config
|
|
21
|
-
# or
|
|
22
|
-
pnpm add styles-config
|
|
23
|
-
# or
|
|
24
|
-
yarn add styles-config
|
|
25
30
|
```
|
|
26
31
|
|
|
32
|
+
Published to npm under both the `latest` and `alpha` dist-tags.
|
|
33
|
+
|
|
27
34
|
## Configuration File Format
|
|
28
35
|
|
|
29
36
|
Create a configuration file in your project root. Supported file names:
|
|
@@ -39,8 +46,9 @@ interface StylesConfig {
|
|
|
39
46
|
searchPaths?: string[];
|
|
40
47
|
enableJavaScript?: boolean;
|
|
41
48
|
mathMode?: 'always' | 'parens-division' | 'parens' | 'strict';
|
|
42
|
-
unitMode?: 'loose' | 'strict';
|
|
43
|
-
|
|
49
|
+
unitMode?: 'loose' | 'preserve' | 'strict';
|
|
50
|
+
functionMode?: 'preserve' | 'error';
|
|
51
|
+
equalityMode?: 'less' | 'sass' | 'exact';
|
|
44
52
|
};
|
|
45
53
|
input?: InputOptions | InputOptions[];
|
|
46
54
|
output?: OutputOptions | OutputOptions[];
|
|
@@ -78,7 +86,7 @@ export default {
|
|
|
78
86
|
compile: {
|
|
79
87
|
mathMode: 'parens-division',
|
|
80
88
|
unitMode: 'loose',
|
|
81
|
-
equalityMode: '
|
|
89
|
+
equalityMode: 'less',
|
|
82
90
|
searchPaths: ['./src/styles', './node_modules']
|
|
83
91
|
},
|
|
84
92
|
input: [
|
|
@@ -320,6 +328,16 @@ This package is designed to be extensible. To add support for a new framework:
|
|
|
320
328
|
2. Add the extension mapping in `src/options.ts` if needed
|
|
321
329
|
3. Update the documentation
|
|
322
330
|
|
|
331
|
+
## Status
|
|
332
|
+
|
|
333
|
+
Alpha. Part of the Jess monorepo, which is early software under active
|
|
334
|
+
development. Please [report issues](https://github.com/jesscss/jess/issues).
|
|
335
|
+
|
|
336
|
+
## Links
|
|
337
|
+
|
|
338
|
+
- Repository: <https://github.com/jesscss/jess>
|
|
339
|
+
- Documentation: <https://jesscss.github.io/> (currently pre-alpha content)
|
|
340
|
+
|
|
323
341
|
## License
|
|
324
342
|
|
|
325
|
-
MIT
|
|
343
|
+
[MIT](https://github.com/jesscss/jess/blob/dev/LICENSE)
|
package/lib/index.cjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let cosmiconfig = require("cosmiconfig");
|
|
25
|
+
let picomatch = require("picomatch");
|
|
26
|
+
picomatch = __toESM(picomatch);
|
|
27
|
+
let path = require("path");
|
|
28
|
+
path = __toESM(path);
|
|
29
|
+
//#region src/loader.ts
|
|
30
|
+
const explorer = (0, cosmiconfig.cosmiconfig)("styles", {
|
|
31
|
+
searchPlaces: [
|
|
32
|
+
"styles.config.ts",
|
|
33
|
+
"styles.config.js",
|
|
34
|
+
"styles.config.mts",
|
|
35
|
+
"styles.config.mjs",
|
|
36
|
+
"styles.config.cjs",
|
|
37
|
+
"styles.config.cts"
|
|
38
|
+
],
|
|
39
|
+
loaders: {
|
|
40
|
+
".mts": cosmiconfig.defaultLoadersSync[".ts"],
|
|
41
|
+
".cts": cosmiconfig.defaultLoadersSync[".ts"],
|
|
42
|
+
".mjs": cosmiconfig.defaultLoadersSync[".js"],
|
|
43
|
+
".cjs": cosmiconfig.defaultLoadersSync[".cjs"]
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
const explorerSync = (0, cosmiconfig.cosmiconfigSync)("styles", {
|
|
47
|
+
searchPlaces: [
|
|
48
|
+
"styles.config.ts",
|
|
49
|
+
"styles.config.js",
|
|
50
|
+
"styles.config.mts",
|
|
51
|
+
"styles.config.mjs",
|
|
52
|
+
"styles.config.cjs",
|
|
53
|
+
"styles.config.cts"
|
|
54
|
+
],
|
|
55
|
+
loaders: {
|
|
56
|
+
".mts": cosmiconfig.defaultLoadersSync[".ts"],
|
|
57
|
+
".cts": cosmiconfig.defaultLoadersSync[".ts"],
|
|
58
|
+
".mjs": cosmiconfig.defaultLoadersSync[".js"],
|
|
59
|
+
".cjs": cosmiconfig.defaultLoadersSync[".cjs"]
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Load styles configuration from the file system (async)
|
|
64
|
+
* @param searchFrom - Directory to search from (defaults to process.cwd())
|
|
65
|
+
* @returns Configuration object or null if not found
|
|
66
|
+
*/
|
|
67
|
+
async function loadConfig(searchFrom) {
|
|
68
|
+
const result = await explorer.search(searchFrom);
|
|
69
|
+
return result?.config ? normalizeConfig(result.config) : null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Load styles configuration from the file system (sync)
|
|
73
|
+
* @param searchFrom - Directory to search from (defaults to process.cwd())
|
|
74
|
+
* @returns Configuration object or empty object if not found
|
|
75
|
+
*/
|
|
76
|
+
function loadConfigSync(searchFrom) {
|
|
77
|
+
const result = explorerSync.search(searchFrom);
|
|
78
|
+
return result?.config ? normalizeConfig(result.config) : {};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load styles configuration with metadata (sync).
|
|
82
|
+
* Includes config file path when a config file is discovered.
|
|
83
|
+
*/
|
|
84
|
+
function loadConfigSyncWithMeta(searchFrom) {
|
|
85
|
+
const result = explorerSync.search(searchFrom);
|
|
86
|
+
return {
|
|
87
|
+
config: result?.config ? normalizeConfig(result.config) : {},
|
|
88
|
+
configFilePath: result?.filepath
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load styles configuration from a specific file path (async)
|
|
93
|
+
* @param filePath - Path to the config file
|
|
94
|
+
* @returns Configuration object or null if not found
|
|
95
|
+
*/
|
|
96
|
+
async function loadConfigFromPath(filePath) {
|
|
97
|
+
const result = await explorer.load(filePath);
|
|
98
|
+
return result?.config ? normalizeConfig(result.config) : null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Load styles configuration from a specific file path (sync)
|
|
102
|
+
* @param filePath - Path to the config file
|
|
103
|
+
* @returns Configuration object or empty object if not found
|
|
104
|
+
*/
|
|
105
|
+
function loadConfigFromPathSync(filePath) {
|
|
106
|
+
const result = explorerSync.load(filePath);
|
|
107
|
+
return result?.config ? normalizeConfig(result.config) : {};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Normalize config object - handle default exports and ensure proper type
|
|
111
|
+
*/
|
|
112
|
+
function isStylesConfig(value) {
|
|
113
|
+
return typeof value === "object" && value !== null;
|
|
114
|
+
}
|
|
115
|
+
function normalizeConfig(config) {
|
|
116
|
+
if (isStylesConfig(config) && "default" in config) return normalizeConfig(config.default);
|
|
117
|
+
if (isStylesConfig(config)) return config;
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/options.ts
|
|
122
|
+
/**
|
|
123
|
+
* Expand the `strict` convenience preset. When `strict` is truthy, fills the
|
|
124
|
+
* strict bundle for any governed option left `undefined` — an explicitly set
|
|
125
|
+
* option always wins. Modeled after `tsconfig`'s `strict`: it only *sets*
|
|
126
|
+
* semantic options, it is not itself a mode. Sets the strictest value of each
|
|
127
|
+
* governed axis (`equalityMode: 'exact'` is the no-coercion dialect).
|
|
128
|
+
*
|
|
129
|
+
* Returns a new object (never mutates the input); a no-op when `strict` is falsy.
|
|
130
|
+
*/
|
|
131
|
+
function applyStrictPreset(opts) {
|
|
132
|
+
if (!opts?.strict) return opts;
|
|
133
|
+
const filled = { ...opts };
|
|
134
|
+
filled.unitMode ??= "strict";
|
|
135
|
+
filled.equalityMode ??= "exact";
|
|
136
|
+
filled.leakyScope ??= false;
|
|
137
|
+
filled.allowOverloadedImport ??= false;
|
|
138
|
+
return filled;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Map of file extensions to language keys
|
|
142
|
+
*/
|
|
143
|
+
const extensionToLanguage = new Map([
|
|
144
|
+
[".less", "less"],
|
|
145
|
+
[".scss", "scss"],
|
|
146
|
+
[".sass", "scss"],
|
|
147
|
+
[".jess", "jess"],
|
|
148
|
+
[".css", "css"]
|
|
149
|
+
]);
|
|
150
|
+
/**
|
|
151
|
+
* Infer language from a file path's extension. Exported so a consumer that has
|
|
152
|
+
* to route by dialect (built-in function set, plugin choice) reads the SAME
|
|
153
|
+
* extension map the option resolution uses instead of keeping its own copy.
|
|
154
|
+
*/
|
|
155
|
+
function inferLanguage(filePath) {
|
|
156
|
+
if (!filePath) return;
|
|
157
|
+
const ext = path.default.extname(filePath).toLowerCase();
|
|
158
|
+
return extensionToLanguage.get(ext);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if a file path matches a pattern (exact path, relative path, or glob)
|
|
162
|
+
*/
|
|
163
|
+
function matchesFile(pattern, filePath) {
|
|
164
|
+
if (!pattern || !filePath) return false;
|
|
165
|
+
const normalizedPattern = path.default.normalize(pattern);
|
|
166
|
+
const normalizedFile = path.default.normalize(filePath);
|
|
167
|
+
if (normalizedPattern === normalizedFile) return true;
|
|
168
|
+
if (path.default.basename(normalizedFile) === normalizedPattern) return true;
|
|
169
|
+
const isMatch = (0, picomatch.default)(pattern, { dot: true });
|
|
170
|
+
return isMatch(filePath) || isMatch(normalizedFile);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get matching options from an array of file-based options.
|
|
174
|
+
* Returns merged options from:
|
|
175
|
+
* 1. All entries without a `file` property (defaults)
|
|
176
|
+
* 2. All entries whose `file` pattern matches the given path
|
|
177
|
+
*
|
|
178
|
+
* Later entries override earlier ones.
|
|
179
|
+
*/
|
|
180
|
+
function getMatchingOptions(options, filePath) {
|
|
181
|
+
if (!options) return {};
|
|
182
|
+
const optionsArray = Array.isArray(options) ? options : [options];
|
|
183
|
+
let result = {};
|
|
184
|
+
for (const opt of optionsArray) if (!opt.file || filePath && matchesFile(opt.file, filePath)) {
|
|
185
|
+
const { file, ...rest } = opt;
|
|
186
|
+
result = {
|
|
187
|
+
...result,
|
|
188
|
+
...rest
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get merged options by combining compile, language, input, and output settings.
|
|
195
|
+
*
|
|
196
|
+
* Merge priority (later wins):
|
|
197
|
+
* 1. compile options (base)
|
|
198
|
+
* 2. language-specific options (inferred from input extension or explicitly specified)
|
|
199
|
+
* 3. matched input options (if input path provided and matches)
|
|
200
|
+
* 4. matched output options (if output path provided and matches)
|
|
201
|
+
*
|
|
202
|
+
* @param config - The styles configuration object
|
|
203
|
+
* @param params - Options specifying language, input file, and output file
|
|
204
|
+
* @returns Merged options object
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* // Get Less options for a specific input/output (language inferred from .less extension)
|
|
208
|
+
* const options = getOptions(config, {
|
|
209
|
+
* input: 'src/styles/main.less',
|
|
210
|
+
* output: 'dist/main.css'
|
|
211
|
+
* });
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* // Explicitly specify language
|
|
215
|
+
* const options = getOptions(config, { language: 'less' });
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Get base options without language-specific settings
|
|
219
|
+
* const options = getOptions(config);
|
|
220
|
+
*/
|
|
221
|
+
function getOptions(config = {}, params = {}) {
|
|
222
|
+
const { input: inputFile, output: outputFile } = params;
|
|
223
|
+
const { compile = {}, input, output, language: languageConfig = {} } = config;
|
|
224
|
+
const language = params.language ?? inferLanguage(inputFile);
|
|
225
|
+
const languageOptions = language ? languageConfig[language] ?? {} : {};
|
|
226
|
+
const matchedInput = getMatchingOptions(input, inputFile);
|
|
227
|
+
const matchedOutput = getMatchingOptions(output, outputFile);
|
|
228
|
+
return {
|
|
229
|
+
mathMode: compile.mathMode,
|
|
230
|
+
unitMode: compile.unitMode,
|
|
231
|
+
equalityMode: compile.equalityMode,
|
|
232
|
+
allowExtendSelectors: compile.allowExtendSelectors,
|
|
233
|
+
processImports: compile.processImports,
|
|
234
|
+
disableScriptModules: compile.disableScriptModules ?? compile.disablePluginRule,
|
|
235
|
+
paths: compile.searchPaths,
|
|
236
|
+
...languageOptions,
|
|
237
|
+
...matchedInput,
|
|
238
|
+
...matchedOutput
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
exports.applyStrictPreset = applyStrictPreset;
|
|
243
|
+
exports.getOptions = getOptions;
|
|
244
|
+
exports.inferLanguage = inferLanguage;
|
|
245
|
+
exports.loadConfig = loadConfig;
|
|
246
|
+
exports.loadConfigFromPath = loadConfigFromPath;
|
|
247
|
+
exports.loadConfigFromPathSync = loadConfigFromPathSync;
|
|
248
|
+
exports.loadConfigSync = loadConfigSync;
|
|
249
|
+
exports.loadConfigSyncWithMeta = loadConfigSyncWithMeta;
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,217 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
//#
|
|
1
|
+
import { cosmiconfig, cosmiconfigSync, defaultLoadersSync } from "cosmiconfig";
|
|
2
|
+
import picomatch from "picomatch";
|
|
3
|
+
import path from "path";
|
|
4
|
+
//#region src/loader.ts
|
|
5
|
+
const explorer = cosmiconfig("styles", {
|
|
6
|
+
searchPlaces: [
|
|
7
|
+
"styles.config.ts",
|
|
8
|
+
"styles.config.js",
|
|
9
|
+
"styles.config.mts",
|
|
10
|
+
"styles.config.mjs",
|
|
11
|
+
"styles.config.cjs",
|
|
12
|
+
"styles.config.cts"
|
|
13
|
+
],
|
|
14
|
+
loaders: {
|
|
15
|
+
".mts": defaultLoadersSync[".ts"],
|
|
16
|
+
".cts": defaultLoadersSync[".ts"],
|
|
17
|
+
".mjs": defaultLoadersSync[".js"],
|
|
18
|
+
".cjs": defaultLoadersSync[".cjs"]
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const explorerSync = cosmiconfigSync("styles", {
|
|
22
|
+
searchPlaces: [
|
|
23
|
+
"styles.config.ts",
|
|
24
|
+
"styles.config.js",
|
|
25
|
+
"styles.config.mts",
|
|
26
|
+
"styles.config.mjs",
|
|
27
|
+
"styles.config.cjs",
|
|
28
|
+
"styles.config.cts"
|
|
29
|
+
],
|
|
30
|
+
loaders: {
|
|
31
|
+
".mts": defaultLoadersSync[".ts"],
|
|
32
|
+
".cts": defaultLoadersSync[".ts"],
|
|
33
|
+
".mjs": defaultLoadersSync[".js"],
|
|
34
|
+
".cjs": defaultLoadersSync[".cjs"]
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Load styles configuration from the file system (async)
|
|
39
|
+
* @param searchFrom - Directory to search from (defaults to process.cwd())
|
|
40
|
+
* @returns Configuration object or null if not found
|
|
41
|
+
*/
|
|
42
|
+
async function loadConfig(searchFrom) {
|
|
43
|
+
const result = await explorer.search(searchFrom);
|
|
44
|
+
return result?.config ? normalizeConfig(result.config) : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Load styles configuration from the file system (sync)
|
|
48
|
+
* @param searchFrom - Directory to search from (defaults to process.cwd())
|
|
49
|
+
* @returns Configuration object or empty object if not found
|
|
50
|
+
*/
|
|
51
|
+
function loadConfigSync(searchFrom) {
|
|
52
|
+
const result = explorerSync.search(searchFrom);
|
|
53
|
+
return result?.config ? normalizeConfig(result.config) : {};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Load styles configuration with metadata (sync).
|
|
57
|
+
* Includes config file path when a config file is discovered.
|
|
58
|
+
*/
|
|
59
|
+
function loadConfigSyncWithMeta(searchFrom) {
|
|
60
|
+
const result = explorerSync.search(searchFrom);
|
|
61
|
+
return {
|
|
62
|
+
config: result?.config ? normalizeConfig(result.config) : {},
|
|
63
|
+
configFilePath: result?.filepath
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Load styles configuration from a specific file path (async)
|
|
68
|
+
* @param filePath - Path to the config file
|
|
69
|
+
* @returns Configuration object or null if not found
|
|
70
|
+
*/
|
|
71
|
+
async function loadConfigFromPath(filePath) {
|
|
72
|
+
const result = await explorer.load(filePath);
|
|
73
|
+
return result?.config ? normalizeConfig(result.config) : null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Load styles configuration from a specific file path (sync)
|
|
77
|
+
* @param filePath - Path to the config file
|
|
78
|
+
* @returns Configuration object or empty object if not found
|
|
79
|
+
*/
|
|
80
|
+
function loadConfigFromPathSync(filePath) {
|
|
81
|
+
const result = explorerSync.load(filePath);
|
|
82
|
+
return result?.config ? normalizeConfig(result.config) : {};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Normalize config object - handle default exports and ensure proper type
|
|
86
|
+
*/
|
|
87
|
+
function isStylesConfig(value) {
|
|
88
|
+
return typeof value === "object" && value !== null;
|
|
89
|
+
}
|
|
90
|
+
function normalizeConfig(config) {
|
|
91
|
+
if (isStylesConfig(config) && "default" in config) return normalizeConfig(config.default);
|
|
92
|
+
if (isStylesConfig(config)) return config;
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/options.ts
|
|
97
|
+
/**
|
|
98
|
+
* Expand the `strict` convenience preset. When `strict` is truthy, fills the
|
|
99
|
+
* strict bundle for any governed option left `undefined` — an explicitly set
|
|
100
|
+
* option always wins. Modeled after `tsconfig`'s `strict`: it only *sets*
|
|
101
|
+
* semantic options, it is not itself a mode. Sets the strictest value of each
|
|
102
|
+
* governed axis (`equalityMode: 'exact'` is the no-coercion dialect).
|
|
103
|
+
*
|
|
104
|
+
* Returns a new object (never mutates the input); a no-op when `strict` is falsy.
|
|
105
|
+
*/
|
|
106
|
+
function applyStrictPreset(opts) {
|
|
107
|
+
if (!opts?.strict) return opts;
|
|
108
|
+
const filled = { ...opts };
|
|
109
|
+
filled.unitMode ??= "strict";
|
|
110
|
+
filled.equalityMode ??= "exact";
|
|
111
|
+
filled.leakyScope ??= false;
|
|
112
|
+
filled.allowOverloadedImport ??= false;
|
|
113
|
+
return filled;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Map of file extensions to language keys
|
|
117
|
+
*/
|
|
118
|
+
const extensionToLanguage = new Map([
|
|
119
|
+
[".less", "less"],
|
|
120
|
+
[".scss", "scss"],
|
|
121
|
+
[".sass", "scss"],
|
|
122
|
+
[".jess", "jess"],
|
|
123
|
+
[".css", "css"]
|
|
124
|
+
]);
|
|
125
|
+
/**
|
|
126
|
+
* Infer language from a file path's extension. Exported so a consumer that has
|
|
127
|
+
* to route by dialect (built-in function set, plugin choice) reads the SAME
|
|
128
|
+
* extension map the option resolution uses instead of keeping its own copy.
|
|
129
|
+
*/
|
|
130
|
+
function inferLanguage(filePath) {
|
|
131
|
+
if (!filePath) return;
|
|
132
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
133
|
+
return extensionToLanguage.get(ext);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a file path matches a pattern (exact path, relative path, or glob)
|
|
137
|
+
*/
|
|
138
|
+
function matchesFile(pattern, filePath) {
|
|
139
|
+
if (!pattern || !filePath) return false;
|
|
140
|
+
const normalizedPattern = path.normalize(pattern);
|
|
141
|
+
const normalizedFile = path.normalize(filePath);
|
|
142
|
+
if (normalizedPattern === normalizedFile) return true;
|
|
143
|
+
if (path.basename(normalizedFile) === normalizedPattern) return true;
|
|
144
|
+
const isMatch = picomatch(pattern, { dot: true });
|
|
145
|
+
return isMatch(filePath) || isMatch(normalizedFile);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get matching options from an array of file-based options.
|
|
149
|
+
* Returns merged options from:
|
|
150
|
+
* 1. All entries without a `file` property (defaults)
|
|
151
|
+
* 2. All entries whose `file` pattern matches the given path
|
|
152
|
+
*
|
|
153
|
+
* Later entries override earlier ones.
|
|
154
|
+
*/
|
|
155
|
+
function getMatchingOptions(options, filePath) {
|
|
156
|
+
if (!options) return {};
|
|
157
|
+
const optionsArray = Array.isArray(options) ? options : [options];
|
|
158
|
+
let result = {};
|
|
159
|
+
for (const opt of optionsArray) if (!opt.file || filePath && matchesFile(opt.file, filePath)) {
|
|
160
|
+
const { file, ...rest } = opt;
|
|
161
|
+
result = {
|
|
162
|
+
...result,
|
|
163
|
+
...rest
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get merged options by combining compile, language, input, and output settings.
|
|
170
|
+
*
|
|
171
|
+
* Merge priority (later wins):
|
|
172
|
+
* 1. compile options (base)
|
|
173
|
+
* 2. language-specific options (inferred from input extension or explicitly specified)
|
|
174
|
+
* 3. matched input options (if input path provided and matches)
|
|
175
|
+
* 4. matched output options (if output path provided and matches)
|
|
176
|
+
*
|
|
177
|
+
* @param config - The styles configuration object
|
|
178
|
+
* @param params - Options specifying language, input file, and output file
|
|
179
|
+
* @returns Merged options object
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* // Get Less options for a specific input/output (language inferred from .less extension)
|
|
183
|
+
* const options = getOptions(config, {
|
|
184
|
+
* input: 'src/styles/main.less',
|
|
185
|
+
* output: 'dist/main.css'
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* // Explicitly specify language
|
|
190
|
+
* const options = getOptions(config, { language: 'less' });
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // Get base options without language-specific settings
|
|
194
|
+
* const options = getOptions(config);
|
|
195
|
+
*/
|
|
196
|
+
function getOptions(config = {}, params = {}) {
|
|
197
|
+
const { input: inputFile, output: outputFile } = params;
|
|
198
|
+
const { compile = {}, input, output, language: languageConfig = {} } = config;
|
|
199
|
+
const language = params.language ?? inferLanguage(inputFile);
|
|
200
|
+
const languageOptions = language ? languageConfig[language] ?? {} : {};
|
|
201
|
+
const matchedInput = getMatchingOptions(input, inputFile);
|
|
202
|
+
const matchedOutput = getMatchingOptions(output, outputFile);
|
|
203
|
+
return {
|
|
204
|
+
mathMode: compile.mathMode,
|
|
205
|
+
unitMode: compile.unitMode,
|
|
206
|
+
equalityMode: compile.equalityMode,
|
|
207
|
+
allowExtendSelectors: compile.allowExtendSelectors,
|
|
208
|
+
processImports: compile.processImports,
|
|
209
|
+
disableScriptModules: compile.disableScriptModules ?? compile.disablePluginRule,
|
|
210
|
+
paths: compile.searchPaths,
|
|
211
|
+
...languageOptions,
|
|
212
|
+
...matchedInput,
|
|
213
|
+
...matchedOutput
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { applyStrictPreset, getOptions, inferLanguage, loadConfig, loadConfigFromPath, loadConfigFromPathSync, loadConfigSync, loadConfigSyncWithMeta };
|
package/lib/options.d.ts
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import type { StylesConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options bag the `strict` preset can expand. Any bag carrying a `strict` flag
|
|
4
|
+
* plus the semantic modes it governs.
|
|
5
|
+
*/
|
|
6
|
+
export interface StrictPresetOptions {
|
|
7
|
+
strict?: boolean;
|
|
8
|
+
unitMode?: 'loose' | 'preserve' | 'strict';
|
|
9
|
+
equalityMode?: 'less' | 'sass' | 'exact';
|
|
10
|
+
leakyScope?: boolean;
|
|
11
|
+
allowOverloadedImport?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Expand the `strict` convenience preset. When `strict` is truthy, fills the
|
|
15
|
+
* strict bundle for any governed option left `undefined` — an explicitly set
|
|
16
|
+
* option always wins. Modeled after `tsconfig`'s `strict`: it only *sets*
|
|
17
|
+
* semantic options, it is not itself a mode. Sets the strictest value of each
|
|
18
|
+
* governed axis (`equalityMode: 'exact'` is the no-coercion dialect).
|
|
19
|
+
*
|
|
20
|
+
* Returns a new object (never mutates the input); a no-op when `strict` is falsy.
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyStrictPreset<T extends StrictPresetOptions>(opts: T): T;
|
|
2
23
|
/**
|
|
3
24
|
* Options for retrieving merged configuration
|
|
4
25
|
*/
|
|
@@ -18,6 +39,12 @@ export interface GetOptionsParams {
|
|
|
18
39
|
*/
|
|
19
40
|
output?: string;
|
|
20
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Infer language from a file path's extension. Exported so a consumer that has
|
|
44
|
+
* to route by dialect (built-in function set, plugin choice) reads the SAME
|
|
45
|
+
* extension map the option resolution uses instead of keeping its own copy.
|
|
46
|
+
*/
|
|
47
|
+
export declare function inferLanguage(filePath: string | undefined): string | undefined;
|
|
21
48
|
/**
|
|
22
49
|
* Get merged options by combining compile, language, input, and output settings.
|
|
23
50
|
*
|