@jesscss/plugin-less 2.0.0-alpha.8 → 2.0.0-alpha.9
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 +15 -16
- package/lib/index.cjs +259 -99
- package/lib/index.d.ts +6 -6
- package/lib/index.js +260 -99
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
**The Less language engine for Jess — the Less parser wired in with Less v5
|
|
4
4
|
rendering defaults.**
|
|
5
5
|
|
|
6
|
-
This is the
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
This is the Less language engine behind the shipping alpha surface.
|
|
7
|
+
`plugin-less` layers the Less grammar (`@jesscss/less-parser`) onto the Jess
|
|
8
|
+
compiler, registers the Less built-in functions (`@jesscss/fns`), and sets the
|
|
9
|
+
current output defaults. The `jess` CLI loads it by default, so if you render
|
|
10
|
+
`.less` you are already using it — you don't need to install this package
|
|
11
|
+
separately for normal CLI use.
|
|
12
12
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
@@ -16,10 +16,9 @@ using it — you don't need to install this package separately for normal CLI us
|
|
|
16
16
|
evaluate-and-emit pass.
|
|
17
17
|
- Registers the Less/Sass style-function library so `lighten()`, `percentage()`,
|
|
18
18
|
string and list helpers, etc. are available during evaluation.
|
|
19
|
-
- Owns the
|
|
20
|
-
CLI imports so CLI and engine defaults can never drift.
|
|
19
|
+
- Owns the current Less-facing output defaults used across the alpha surface.
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## Current output defaults
|
|
23
22
|
|
|
24
23
|
The defaults this plugin applies:
|
|
25
24
|
|
|
@@ -29,18 +28,18 @@ The defaults this plugin applies:
|
|
|
29
28
|
- `mathMode: 'parens-division'`, `unitMode: 'preserve'`, `equalityMode: 'less'`,
|
|
30
29
|
`leakyScope: true`, `bubbleRootAtRules: true`.
|
|
31
30
|
|
|
32
|
-
These keep the Less
|
|
33
|
-
|
|
31
|
+
These keep the current Less-facing surface aligned on one set of output
|
|
32
|
+
semantics.
|
|
34
33
|
|
|
35
34
|
## Status
|
|
36
35
|
|
|
37
|
-
**Alpha.** This is the
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
**Alpha.** This is the shipping Less-facing engine in the current Jess alpha. It
|
|
37
|
+
renders real Less, but it is early software with known rendering gaps and
|
|
38
|
+
expected failures; don't ship it to production yet, and please
|
|
40
39
|
[report bugs](https://github.com/jesscss/jess/issues).
|
|
41
40
|
|
|
42
|
-
The programmatic plugin/compiler API is **not yet stabilized** — the `jess`
|
|
43
|
-
|
|
41
|
+
The programmatic plugin/compiler API is **not yet stabilized** — the `jess` CLI
|
|
42
|
+
is the documented public surface for the alpha. Watch the
|
|
44
43
|
[docs site](https://jesscss.github.io/) for the API once it settles.
|
|
45
44
|
|
|
46
45
|
- Project overview & positioning: <https://github.com/jesscss/jess#readme>
|
package/lib/index.cjs
CHANGED
|
@@ -25,14 +25,129 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
27
|
let _jesscss_core = require("@jesscss/core");
|
|
28
|
-
let
|
|
29
|
-
_jesscss_fns = __toESM(_jesscss_fns);
|
|
30
|
-
let _jesscss_less_parser_jess = require("@jesscss/less-parser/jess");
|
|
28
|
+
let _jesscss_core_value = require("@jesscss/core/value");
|
|
31
29
|
let node_path = require("node:path");
|
|
32
30
|
node_path = __toESM(node_path);
|
|
33
31
|
let node_module = require("node:module");
|
|
34
32
|
let _jesscss_style_resolver = require("@jesscss/style-resolver");
|
|
33
|
+
let _jesscss_less_parser = require("@jesscss/less-parser");
|
|
35
34
|
//#region src/index.ts
|
|
35
|
+
const nativeNil = () => {
|
|
36
|
+
const nil = {
|
|
37
|
+
type: "Nil",
|
|
38
|
+
value: "",
|
|
39
|
+
eval: () => nil
|
|
40
|
+
};
|
|
41
|
+
return nil;
|
|
42
|
+
};
|
|
43
|
+
function isRawSequence(value) {
|
|
44
|
+
return Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
function isPluginDetached(value) {
|
|
47
|
+
return !isRawSequence(value) && value.type === "DetachedRuleset";
|
|
48
|
+
}
|
|
49
|
+
function toNativeLessValue(value) {
|
|
50
|
+
if (isRawSequence(value)) return {
|
|
51
|
+
type: "Expression",
|
|
52
|
+
value: value.map(toNativeLessValue),
|
|
53
|
+
valueOf: () => (0, _jesscss_core_value.emitValue)(value)
|
|
54
|
+
};
|
|
55
|
+
if (isPluginDetached(value)) {
|
|
56
|
+
const mixin = {
|
|
57
|
+
type: "Mixin",
|
|
58
|
+
name: nativeNil(),
|
|
59
|
+
args: nativeNil(),
|
|
60
|
+
ruleset: { rules: value.rules.map((rule) => ({
|
|
61
|
+
type: "Declaration",
|
|
62
|
+
name: rule.name,
|
|
63
|
+
value: toNativeLessValue(rule.value),
|
|
64
|
+
eval() {
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
})) },
|
|
68
|
+
eval() {
|
|
69
|
+
return mixin;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return mixin;
|
|
73
|
+
}
|
|
74
|
+
return toNativeValue(value);
|
|
75
|
+
}
|
|
76
|
+
function toNativeValue(value) {
|
|
77
|
+
switch (value.type) {
|
|
78
|
+
case "Dimension": return {
|
|
79
|
+
type: "Dimension",
|
|
80
|
+
value: value.number,
|
|
81
|
+
unit: value.unit,
|
|
82
|
+
valueOf: () => value.number
|
|
83
|
+
};
|
|
84
|
+
case "Quoted": return {
|
|
85
|
+
type: "Quoted",
|
|
86
|
+
value: value.value,
|
|
87
|
+
quote: value.quote,
|
|
88
|
+
escaped: value.escaped,
|
|
89
|
+
valueOf: () => value.bytes
|
|
90
|
+
};
|
|
91
|
+
case "Color": return {
|
|
92
|
+
type: "Color",
|
|
93
|
+
rgb: value.rgb,
|
|
94
|
+
alpha: value.alpha,
|
|
95
|
+
valueOf: () => value.bytes
|
|
96
|
+
};
|
|
97
|
+
case "List": return value.sep === "," || value.sep === "/" ? {
|
|
98
|
+
type: "Value",
|
|
99
|
+
value: value.value.map(toNativeLessValue),
|
|
100
|
+
separator: value.sep,
|
|
101
|
+
valueOf: () => value.bytes
|
|
102
|
+
} : {
|
|
103
|
+
type: "Anonymous",
|
|
104
|
+
value: value.bytes,
|
|
105
|
+
valueOf: () => value.bytes
|
|
106
|
+
};
|
|
107
|
+
default: return {
|
|
108
|
+
type: "Anonymous",
|
|
109
|
+
value: value.bytes,
|
|
110
|
+
valueOf: () => value.bytes
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function isNativeValue(value) {
|
|
115
|
+
return value !== null && typeof value === "object" && "bytes" in value && typeof value.bytes === "string";
|
|
116
|
+
}
|
|
117
|
+
function fromNativeLessValue(value) {
|
|
118
|
+
if (isNativeValue(value)) return value;
|
|
119
|
+
if (typeof value === "number") return (0, _jesscss_core_value.makeDimension)(value);
|
|
120
|
+
if (typeof value === "string") return (0, _jesscss_core_value.makeKeyword)(value);
|
|
121
|
+
if (value && typeof value === "object") {
|
|
122
|
+
const candidate = value;
|
|
123
|
+
if ((candidate.type === "Dimension" || candidate.type === "Num") && typeof candidate.value === "number") return (0, _jesscss_core_value.makeDimension)(candidate.value, typeof candidate.unit === "string" ? candidate.unit : "");
|
|
124
|
+
if (candidate.type === "Quoted" && typeof candidate.value === "string") return (0, _jesscss_core_value.makeQuoted)(candidate.value, candidate.quote === "'" ? "'" : "\"", candidate.escaped === true);
|
|
125
|
+
if (candidate.type === "Expression" && Array.isArray(candidate.value)) return candidate.value.map(fromNativeLessValue);
|
|
126
|
+
if (candidate.type === "Value" && Array.isArray(candidate.value)) {
|
|
127
|
+
const separator = candidate.separator === "/" ? "/" : ",";
|
|
128
|
+
return (0, _jesscss_core_value.makeList)(candidate.value.map((item) => fromNativeLessValue(item)), separator);
|
|
129
|
+
}
|
|
130
|
+
if (typeof candidate.value === "string") return (0, _jesscss_core_value.makeKeyword)(candidate.value);
|
|
131
|
+
if (typeof candidate.valueOf === "function") return (0, _jesscss_core_value.makeKeyword)(String(candidate.valueOf()));
|
|
132
|
+
}
|
|
133
|
+
return (0, _jesscss_core_value.makeKeyword)(value == null ? "" : String(value));
|
|
134
|
+
}
|
|
135
|
+
function invokeNativeLessFunction(fn, args) {
|
|
136
|
+
const result = fn(...args.map(toNativeLessValue));
|
|
137
|
+
return result !== null && typeof result === "object" && "then" in result && typeof result.then === "function" ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
|
|
138
|
+
}
|
|
139
|
+
function nativeLessFn(name, fn) {
|
|
140
|
+
return (0, _jesscss_core_value.defineFunction)(name.toLowerCase(), {
|
|
141
|
+
variadic: true,
|
|
142
|
+
params: [],
|
|
143
|
+
body: (value) => invokeNativeLessFunction(fn, (0, _jesscss_core_value.groupItems)(value))
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function isLoadedPluginModule(value) {
|
|
147
|
+
if (typeof value !== "object" || value === null) return false;
|
|
148
|
+
if (!("functions" in value) || value.functions === void 0) return true;
|
|
149
|
+
return typeof value.functions === "object" && value.functions !== null && Object.values(value.functions).every((fn) => typeof fn === "function");
|
|
150
|
+
}
|
|
36
151
|
/**
|
|
37
152
|
* The Less plugin's default option values — the single source of truth for the
|
|
38
153
|
* v5 defaults. The `LessPlugin` constructor fills any unset option from here,
|
|
@@ -48,16 +163,55 @@ const lessPluginDefaults = {
|
|
|
48
163
|
bubbleRootAtRules: true,
|
|
49
164
|
collapseNesting: false
|
|
50
165
|
};
|
|
166
|
+
/** Match Less's URL normalization without treating URL text as an import path. */
|
|
167
|
+
function normalizeUrlPath(url) {
|
|
168
|
+
const segments = url.split("/");
|
|
169
|
+
const normalized = [];
|
|
170
|
+
for (const segment of segments) {
|
|
171
|
+
if (segment === ".") continue;
|
|
172
|
+
if (segment === "..") {
|
|
173
|
+
if (normalized.length === 0 || normalized[normalized.length - 1] === "..") normalized.push(segment);
|
|
174
|
+
else normalized.pop();
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
normalized.push(segment);
|
|
178
|
+
}
|
|
179
|
+
return normalized.join("/");
|
|
180
|
+
}
|
|
181
|
+
function isUrlRelative(url) {
|
|
182
|
+
if (url.startsWith("/") || url.startsWith("#")) return false;
|
|
183
|
+
const colon = url.indexOf(":");
|
|
184
|
+
if (colon < 0) return true;
|
|
185
|
+
for (let index = 0; index < colon; index++) {
|
|
186
|
+
const code = url.charCodeAt(index);
|
|
187
|
+
if (!(code >= 65 && code <= 90 || code >= 97 && code <= 122) && url[index] !== "-") return true;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
function rewriteUrlPath(url, rootpath) {
|
|
192
|
+
const rewritten = normalizeUrlPath(rootpath + url);
|
|
193
|
+
return url.startsWith(".") && isUrlRelative(rootpath) && !rewritten.startsWith(".") ? `./${rewritten}` : rewritten;
|
|
194
|
+
}
|
|
195
|
+
function escapeUnquotedUrlPath(pathValue) {
|
|
196
|
+
let escaped = "";
|
|
197
|
+
for (const char of pathValue) escaped += char === "(" || char === ")" || char === "'" || char === "\"" || " \n\r\f".includes(char) ? `\\${char}` : char;
|
|
198
|
+
return escaped;
|
|
199
|
+
}
|
|
200
|
+
function jsDelivrPackageSpecifier(candidate) {
|
|
201
|
+
const absolute = candidate.match(/^https?:\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i);
|
|
202
|
+
if (absolute?.[1]) return absolute[1];
|
|
203
|
+
return candidate.match(/^\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i)?.[1] ?? null;
|
|
204
|
+
}
|
|
51
205
|
var LessPlugin = class extends _jesscss_core.AbstractPlugin {
|
|
52
206
|
name = "less";
|
|
53
207
|
supportedExtensions = [".less"];
|
|
54
|
-
parser;
|
|
55
208
|
mathMode;
|
|
56
209
|
unitMode;
|
|
57
210
|
equalityMode;
|
|
58
211
|
leakyScope;
|
|
59
212
|
bubbleRootAtRules;
|
|
60
213
|
collapseNesting;
|
|
214
|
+
pluginHosts = /* @__PURE__ */ new WeakMap();
|
|
61
215
|
constructor(opts = {}) {
|
|
62
216
|
super();
|
|
63
217
|
this.opts = opts;
|
|
@@ -78,41 +232,96 @@ var LessPlugin = class extends _jesscss_core.AbstractPlugin {
|
|
|
78
232
|
this.leakyScope = opts.leakyScope ?? lessPluginDefaults.leakyScope;
|
|
79
233
|
this.bubbleRootAtRules = opts.bubbleRootAtRules ?? lessPluginDefaults.bubbleRootAtRules;
|
|
80
234
|
this.collapseNesting = opts.collapseNesting ?? lessPluginDefaults.collapseNesting;
|
|
81
|
-
this.parser = new _jesscss_less_parser_jess.Parser();
|
|
82
|
-
}
|
|
83
|
-
createTreeContext(filePath, source) {
|
|
84
|
-
return new _jesscss_core.TreeContext({
|
|
85
|
-
file: {
|
|
86
|
-
name: node_path.default.basename(filePath),
|
|
87
|
-
path: node_path.default.dirname(filePath),
|
|
88
|
-
fullPath: filePath,
|
|
89
|
-
source
|
|
90
|
-
},
|
|
91
|
-
mathMode: this.mathMode,
|
|
92
|
-
unitMode: this.unitMode,
|
|
93
|
-
equalityMode: this.equalityMode,
|
|
94
|
-
plugin: this,
|
|
95
|
-
allowExtendSelectors: this.opts.allowExtendSelectors,
|
|
96
|
-
collapseNesting: this.collapseNesting,
|
|
97
|
-
leakyScope: this.leakyScope,
|
|
98
|
-
bubbleRootAtRules: this.bubbleRootAtRules
|
|
99
|
-
});
|
|
100
235
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
236
|
+
transformUrl({ value, quoted, fromFilePath, entryFilePath }) {
|
|
237
|
+
let transformed;
|
|
238
|
+
if (isUrlRelative(value)) {
|
|
239
|
+
const rewriteUrls = this.opts.rewriteUrls;
|
|
240
|
+
const local = value.startsWith(".");
|
|
241
|
+
if (rewriteUrls !== "local" || local) {
|
|
242
|
+
const rebasesImportedUrl = rewriteUrls === true || rewriteUrls === "all" || rewriteUrls === "local" && local;
|
|
243
|
+
let rootpath = this.opts.rootpath ?? "";
|
|
244
|
+
if (!quoted) rootpath = escapeUnquotedUrlPath(rootpath);
|
|
245
|
+
if (rebasesImportedUrl && fromFilePath && entryFilePath) {
|
|
246
|
+
const relativeDirectory = node_path.default.relative(node_path.default.dirname(entryFilePath), node_path.default.dirname(fromFilePath));
|
|
247
|
+
if (relativeDirectory) rootpath += `${relativeDirectory.split(node_path.default.sep).join("/")}/`;
|
|
248
|
+
}
|
|
249
|
+
transformed = rewriteUrlPath(value, rootpath);
|
|
250
|
+
} else transformed = normalizeUrlPath(value);
|
|
251
|
+
} else transformed = normalizeUrlPath(value);
|
|
252
|
+
if (this.opts.urlArgs && !value.trimStart().toLowerCase().startsWith("data:")) {
|
|
253
|
+
const args = `${transformed.includes("?") ? "&" : "?"}${this.opts.urlArgs}`;
|
|
254
|
+
const fragment = transformed.indexOf("#");
|
|
255
|
+
transformed = fragment < 0 ? transformed + args : transformed.slice(0, fragment) + args + transformed.slice(fragment);
|
|
111
256
|
}
|
|
257
|
+
return transformed;
|
|
112
258
|
}
|
|
113
259
|
expandImport(importPath, currentDir) {
|
|
114
260
|
return (0, _jesscss_style_resolver.expandLessImportCandidates)(importPath);
|
|
115
261
|
}
|
|
262
|
+
setContext(context) {
|
|
263
|
+
if (context.opts.mathMode === void 0) context.setOption("mathMode", this.mathMode);
|
|
264
|
+
if (context.opts.unitMode === void 0) context.setOption("unitMode", this.unitMode);
|
|
265
|
+
if (context.opts.equalityMode === void 0) context.setOption("equalityMode", this.equalityMode);
|
|
266
|
+
if (context.opts.leakyScope === void 0) context.setOption("leakyScope", this.leakyScope);
|
|
267
|
+
if (context.opts.bubbleRootAtRules === void 0) context.setOption("bubbleRootAtRules", this.bubbleRootAtRules);
|
|
268
|
+
let host = this.pluginHosts.get(context);
|
|
269
|
+
if (!host) {
|
|
270
|
+
const fns = [];
|
|
271
|
+
const nativeFns = /* @__PURE__ */ new WeakMap();
|
|
272
|
+
const addNativeFn = (name, fn) => {
|
|
273
|
+
const adapted = nativeLessFn(name, fn);
|
|
274
|
+
nativeFns.set(adapted, fn);
|
|
275
|
+
fns.push(adapted);
|
|
276
|
+
return adapted;
|
|
277
|
+
};
|
|
278
|
+
const registry = {
|
|
279
|
+
add: (name, fn) => {
|
|
280
|
+
addNativeFn(name, fn);
|
|
281
|
+
},
|
|
282
|
+
addMultiple: (functions) => {
|
|
283
|
+
for (const [name, fn] of Object.entries(functions)) registry.add(name, fn);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const less = {
|
|
287
|
+
functions: { functionRegistry: registry },
|
|
288
|
+
tree: {
|
|
289
|
+
Dimension: class {
|
|
290
|
+
type = "Dimension";
|
|
291
|
+
constructor(value, unit = "") {
|
|
292
|
+
this.value = value;
|
|
293
|
+
this.unit = unit;
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
Quoted: class {
|
|
297
|
+
type = "Quoted";
|
|
298
|
+
constructor(quote, value, escaped = false) {
|
|
299
|
+
this.quote = quote;
|
|
300
|
+
this.value = value;
|
|
301
|
+
this.escaped = escaped;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const configured = this.opts.plugins ?? [];
|
|
307
|
+
for (const plugin of configured) plugin.install?.(less, void 0, registry);
|
|
308
|
+
host = {
|
|
309
|
+
...fns.length === 0 ? {} : { globalFns: fns },
|
|
310
|
+
loadPlugin: async ({ specifier, options }) => {
|
|
311
|
+
const loaded = await context.getPluginModule(specifier, options);
|
|
312
|
+
const functions = isLoadedPluginModule(loaded.module) ? loaded.module.functions : void 0;
|
|
313
|
+
if (!functions) return [];
|
|
314
|
+
return Object.entries(functions).map(([name, fn]) => addNativeFn(name, fn));
|
|
315
|
+
},
|
|
316
|
+
invokeRawFunction: (fn, args) => {
|
|
317
|
+
const native = nativeFns.get(fn);
|
|
318
|
+
return native ? invokeNativeLessFunction(native, args) : void 0;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
this.pluginHosts.set(context, host);
|
|
322
|
+
}
|
|
323
|
+
context.pluginHost = host;
|
|
324
|
+
}
|
|
116
325
|
resolve(filePath, currentDir, searchPaths) {
|
|
117
326
|
const mapped = (Array.isArray(filePath) ? filePath : [filePath]).map((candidate) => {
|
|
118
327
|
if (candidate.startsWith("@less/test-import-module/")) {
|
|
@@ -124,11 +333,7 @@ var LessPlugin = class extends _jesscss_core.AbstractPlugin {
|
|
|
124
333
|
return node_path.default.join(packagesRoot, "test-import-module", after);
|
|
125
334
|
}
|
|
126
335
|
}
|
|
127
|
-
|
|
128
|
-
if (m?.[1]) return m[1];
|
|
129
|
-
const mProtocolRelative = candidate.match(/^\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i);
|
|
130
|
-
if (mProtocolRelative?.[1]) return mProtocolRelative[1];
|
|
131
|
-
return candidate;
|
|
336
|
+
return jsDelivrPackageSpecifier(candidate) ?? candidate;
|
|
132
337
|
});
|
|
133
338
|
const out = [...super.resolve(mapped, currentDir, searchPaths)];
|
|
134
339
|
const bases = [
|
|
@@ -156,72 +361,27 @@ var LessPlugin = class extends _jesscss_core.AbstractPlugin {
|
|
|
156
361
|
}
|
|
157
362
|
return out;
|
|
158
363
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let tree;
|
|
364
|
+
canResolveImport(specifier) {
|
|
365
|
+
return jsDelivrPackageSpecifier(specifier) !== null;
|
|
366
|
+
}
|
|
367
|
+
safeParse(filePath, source, parseOptions) {
|
|
164
368
|
try {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
context.opts.liftedCommentRanges = parseResult.liftedCommentRanges;
|
|
171
|
-
if ("warnings" in parseResult && parseResult.warnings) for (const warning of parseResult.warnings) {
|
|
172
|
-
const line = warning.token?.startLine ?? 1;
|
|
173
|
-
const column = warning.token?.startColumn ?? 1;
|
|
174
|
-
warnings.push({
|
|
175
|
-
code: "parse/deprecated",
|
|
176
|
-
phase: "parse",
|
|
177
|
-
message: warning.message,
|
|
178
|
-
reason: warning.message,
|
|
179
|
-
fix: "Update your code to use the recommended syntax.",
|
|
180
|
-
file: context.file,
|
|
181
|
-
filePath,
|
|
182
|
-
line,
|
|
183
|
-
column,
|
|
184
|
-
lines: (0, _jesscss_core.extractRelevantLines)(source, line)
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (parseResult.errors.length) for (const error of parseResult.errors) {
|
|
188
|
-
const line = error.token?.startLine ?? 1;
|
|
189
|
-
const diagnostic = (0, _jesscss_core.toDiagnostic)((0, _jesscss_core.getErrorFromParser)([error], void 0, filePath, source, { file: context.file }));
|
|
190
|
-
if (!diagnostic.lines) diagnostic.lines = (0, _jesscss_core.extractRelevantLines)(source, line);
|
|
191
|
-
if ("errors" in diagnostic) errors.push(diagnostic);
|
|
192
|
-
else warnings.push(diagnostic);
|
|
193
|
-
}
|
|
369
|
+
return {
|
|
370
|
+
document: (0, _jesscss_less_parser.parse)(source),
|
|
371
|
+
errors: [],
|
|
372
|
+
warnings: []
|
|
373
|
+
};
|
|
194
374
|
} catch (error) {
|
|
195
|
-
if (error instanceof _jesscss_core.JessError) {
|
|
196
|
-
const diagnostic = (0, _jesscss_core.toDiagnostic)(error);
|
|
197
|
-
if ("errors" in diagnostic) errors.push(diagnostic);
|
|
198
|
-
else warnings.push(diagnostic);
|
|
199
|
-
} else {
|
|
200
|
-
const message = error instanceof Error ? error.message : "Unknown parsing error";
|
|
201
|
-
errors.push({
|
|
202
|
-
code: "internal/unknown",
|
|
203
|
-
phase: "parse",
|
|
204
|
-
message,
|
|
205
|
-
reason: message || "An unexpected error occurred during parsing.",
|
|
206
|
-
fix: "Check the file syntax and ensure it is valid.",
|
|
207
|
-
file: context.file,
|
|
208
|
-
filePath,
|
|
209
|
-
line: 1,
|
|
210
|
-
column: 1,
|
|
211
|
-
lines: (0, _jesscss_core.extractRelevantLines)(source, 1)
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
375
|
return {
|
|
215
|
-
errors,
|
|
216
|
-
|
|
376
|
+
errors: [(0, _jesscss_core.parserDiagnostic)({
|
|
377
|
+
dialect: "Less",
|
|
378
|
+
error,
|
|
379
|
+
filePath,
|
|
380
|
+
source
|
|
381
|
+
})],
|
|
382
|
+
warnings: []
|
|
217
383
|
};
|
|
218
384
|
}
|
|
219
|
-
if (tree && errors.length === 0) this._registerFunctions(tree);
|
|
220
|
-
return {
|
|
221
|
-
tree,
|
|
222
|
-
errors,
|
|
223
|
-
warnings
|
|
224
|
-
};
|
|
225
385
|
}
|
|
226
386
|
};
|
|
227
387
|
const lessPlugin = ((opts) => {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { AbstractPlugin, type ISafeParseResult, type SafeParseOptions } from '@jesscss/core';
|
|
1
|
+
import { AbstractPlugin, type Context, type UrlTransformRequest, type ISafeParseResult, type SafeParseOptions } from '@jesscss/core';
|
|
2
2
|
import type { EqualityMode, MathMode, UnitMode, LessOptions } from 'styles-config';
|
|
3
|
-
import { Parser } from '@jesscss/less-parser/jess';
|
|
4
3
|
export type LessPluginOptions = LessOptions;
|
|
5
4
|
/**
|
|
6
5
|
* The Less plugin's default option values — the single source of truth for the
|
|
@@ -21,19 +20,20 @@ export declare class LessPlugin extends AbstractPlugin {
|
|
|
21
20
|
opts: LessPluginOptions;
|
|
22
21
|
name: string;
|
|
23
22
|
supportedExtensions: string[];
|
|
24
|
-
parser: Parser;
|
|
25
23
|
mathMode: MathMode;
|
|
26
24
|
unitMode: UnitMode;
|
|
27
25
|
equalityMode: EqualityMode;
|
|
28
26
|
leakyScope: boolean;
|
|
29
27
|
bubbleRootAtRules: boolean;
|
|
30
28
|
collapseNesting: boolean;
|
|
29
|
+
private readonly pluginHosts;
|
|
31
30
|
constructor(opts?: LessPluginOptions);
|
|
32
|
-
|
|
33
|
-
private _registerFunctions;
|
|
31
|
+
transformUrl({ value, quoted, fromFilePath, entryFilePath }: UrlTransformRequest): string;
|
|
34
32
|
expandImport(importPath: string, currentDir: string): string[];
|
|
33
|
+
setContext(context: Context): void;
|
|
35
34
|
resolve(filePath: string | string[], currentDir: string, searchPaths: string[]): string[];
|
|
36
|
-
|
|
35
|
+
canResolveImport(specifier: string): boolean;
|
|
36
|
+
safeParse(filePath: string, source: string, parseOptions?: SafeParseOptions): ISafeParseResult;
|
|
37
37
|
}
|
|
38
38
|
export type { LessOptions } from 'styles-config';
|
|
39
39
|
declare const lessPlugin: (opts?: LessPluginOptions) => LessPlugin;
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,126 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { AbstractPlugin,
|
|
3
|
-
import
|
|
4
|
-
import { Parser } from "@jesscss/less-parser/jess";
|
|
2
|
+
import { AbstractPlugin, parserDiagnostic } from "@jesscss/core";
|
|
3
|
+
import { defineFunction, emitValue, groupItems, makeDimension, makeKeyword, makeList, makeQuoted } from "@jesscss/core/value";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { expandLessImportCandidates } from "@jesscss/style-resolver";
|
|
6
|
+
import { parse } from "@jesscss/less-parser";
|
|
7
7
|
//#region src/index.ts
|
|
8
|
+
const nativeNil = () => {
|
|
9
|
+
const nil = {
|
|
10
|
+
type: "Nil",
|
|
11
|
+
value: "",
|
|
12
|
+
eval: () => nil
|
|
13
|
+
};
|
|
14
|
+
return nil;
|
|
15
|
+
};
|
|
16
|
+
function isRawSequence(value) {
|
|
17
|
+
return Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function isPluginDetached(value) {
|
|
20
|
+
return !isRawSequence(value) && value.type === "DetachedRuleset";
|
|
21
|
+
}
|
|
22
|
+
function toNativeLessValue(value) {
|
|
23
|
+
if (isRawSequence(value)) return {
|
|
24
|
+
type: "Expression",
|
|
25
|
+
value: value.map(toNativeLessValue),
|
|
26
|
+
valueOf: () => emitValue(value)
|
|
27
|
+
};
|
|
28
|
+
if (isPluginDetached(value)) {
|
|
29
|
+
const mixin = {
|
|
30
|
+
type: "Mixin",
|
|
31
|
+
name: nativeNil(),
|
|
32
|
+
args: nativeNil(),
|
|
33
|
+
ruleset: { rules: value.rules.map((rule) => ({
|
|
34
|
+
type: "Declaration",
|
|
35
|
+
name: rule.name,
|
|
36
|
+
value: toNativeLessValue(rule.value),
|
|
37
|
+
eval() {
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
})) },
|
|
41
|
+
eval() {
|
|
42
|
+
return mixin;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return mixin;
|
|
46
|
+
}
|
|
47
|
+
return toNativeValue(value);
|
|
48
|
+
}
|
|
49
|
+
function toNativeValue(value) {
|
|
50
|
+
switch (value.type) {
|
|
51
|
+
case "Dimension": return {
|
|
52
|
+
type: "Dimension",
|
|
53
|
+
value: value.number,
|
|
54
|
+
unit: value.unit,
|
|
55
|
+
valueOf: () => value.number
|
|
56
|
+
};
|
|
57
|
+
case "Quoted": return {
|
|
58
|
+
type: "Quoted",
|
|
59
|
+
value: value.value,
|
|
60
|
+
quote: value.quote,
|
|
61
|
+
escaped: value.escaped,
|
|
62
|
+
valueOf: () => value.bytes
|
|
63
|
+
};
|
|
64
|
+
case "Color": return {
|
|
65
|
+
type: "Color",
|
|
66
|
+
rgb: value.rgb,
|
|
67
|
+
alpha: value.alpha,
|
|
68
|
+
valueOf: () => value.bytes
|
|
69
|
+
};
|
|
70
|
+
case "List": return value.sep === "," || value.sep === "/" ? {
|
|
71
|
+
type: "Value",
|
|
72
|
+
value: value.value.map(toNativeLessValue),
|
|
73
|
+
separator: value.sep,
|
|
74
|
+
valueOf: () => value.bytes
|
|
75
|
+
} : {
|
|
76
|
+
type: "Anonymous",
|
|
77
|
+
value: value.bytes,
|
|
78
|
+
valueOf: () => value.bytes
|
|
79
|
+
};
|
|
80
|
+
default: return {
|
|
81
|
+
type: "Anonymous",
|
|
82
|
+
value: value.bytes,
|
|
83
|
+
valueOf: () => value.bytes
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function isNativeValue(value) {
|
|
88
|
+
return value !== null && typeof value === "object" && "bytes" in value && typeof value.bytes === "string";
|
|
89
|
+
}
|
|
90
|
+
function fromNativeLessValue(value) {
|
|
91
|
+
if (isNativeValue(value)) return value;
|
|
92
|
+
if (typeof value === "number") return makeDimension(value);
|
|
93
|
+
if (typeof value === "string") return makeKeyword(value);
|
|
94
|
+
if (value && typeof value === "object") {
|
|
95
|
+
const candidate = value;
|
|
96
|
+
if ((candidate.type === "Dimension" || candidate.type === "Num") && typeof candidate.value === "number") return makeDimension(candidate.value, typeof candidate.unit === "string" ? candidate.unit : "");
|
|
97
|
+
if (candidate.type === "Quoted" && typeof candidate.value === "string") return makeQuoted(candidate.value, candidate.quote === "'" ? "'" : "\"", candidate.escaped === true);
|
|
98
|
+
if (candidate.type === "Expression" && Array.isArray(candidate.value)) return candidate.value.map(fromNativeLessValue);
|
|
99
|
+
if (candidate.type === "Value" && Array.isArray(candidate.value)) {
|
|
100
|
+
const separator = candidate.separator === "/" ? "/" : ",";
|
|
101
|
+
return makeList(candidate.value.map((item) => fromNativeLessValue(item)), separator);
|
|
102
|
+
}
|
|
103
|
+
if (typeof candidate.value === "string") return makeKeyword(candidate.value);
|
|
104
|
+
if (typeof candidate.valueOf === "function") return makeKeyword(String(candidate.valueOf()));
|
|
105
|
+
}
|
|
106
|
+
return makeKeyword(value == null ? "" : String(value));
|
|
107
|
+
}
|
|
108
|
+
function invokeNativeLessFunction(fn, args) {
|
|
109
|
+
const result = fn(...args.map(toNativeLessValue));
|
|
110
|
+
return result !== null && typeof result === "object" && "then" in result && typeof result.then === "function" ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
|
|
111
|
+
}
|
|
112
|
+
function nativeLessFn(name, fn) {
|
|
113
|
+
return defineFunction(name.toLowerCase(), {
|
|
114
|
+
variadic: true,
|
|
115
|
+
params: [],
|
|
116
|
+
body: (value) => invokeNativeLessFunction(fn, groupItems(value))
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function isLoadedPluginModule(value) {
|
|
120
|
+
if (typeof value !== "object" || value === null) return false;
|
|
121
|
+
if (!("functions" in value) || value.functions === void 0) return true;
|
|
122
|
+
return typeof value.functions === "object" && value.functions !== null && Object.values(value.functions).every((fn) => typeof fn === "function");
|
|
123
|
+
}
|
|
8
124
|
/**
|
|
9
125
|
* The Less plugin's default option values — the single source of truth for the
|
|
10
126
|
* v5 defaults. The `LessPlugin` constructor fills any unset option from here,
|
|
@@ -20,16 +136,55 @@ const lessPluginDefaults = {
|
|
|
20
136
|
bubbleRootAtRules: true,
|
|
21
137
|
collapseNesting: false
|
|
22
138
|
};
|
|
139
|
+
/** Match Less's URL normalization without treating URL text as an import path. */
|
|
140
|
+
function normalizeUrlPath(url) {
|
|
141
|
+
const segments = url.split("/");
|
|
142
|
+
const normalized = [];
|
|
143
|
+
for (const segment of segments) {
|
|
144
|
+
if (segment === ".") continue;
|
|
145
|
+
if (segment === "..") {
|
|
146
|
+
if (normalized.length === 0 || normalized[normalized.length - 1] === "..") normalized.push(segment);
|
|
147
|
+
else normalized.pop();
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
normalized.push(segment);
|
|
151
|
+
}
|
|
152
|
+
return normalized.join("/");
|
|
153
|
+
}
|
|
154
|
+
function isUrlRelative(url) {
|
|
155
|
+
if (url.startsWith("/") || url.startsWith("#")) return false;
|
|
156
|
+
const colon = url.indexOf(":");
|
|
157
|
+
if (colon < 0) return true;
|
|
158
|
+
for (let index = 0; index < colon; index++) {
|
|
159
|
+
const code = url.charCodeAt(index);
|
|
160
|
+
if (!(code >= 65 && code <= 90 || code >= 97 && code <= 122) && url[index] !== "-") return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
function rewriteUrlPath(url, rootpath) {
|
|
165
|
+
const rewritten = normalizeUrlPath(rootpath + url);
|
|
166
|
+
return url.startsWith(".") && isUrlRelative(rootpath) && !rewritten.startsWith(".") ? `./${rewritten}` : rewritten;
|
|
167
|
+
}
|
|
168
|
+
function escapeUnquotedUrlPath(pathValue) {
|
|
169
|
+
let escaped = "";
|
|
170
|
+
for (const char of pathValue) escaped += char === "(" || char === ")" || char === "'" || char === "\"" || " \n\r\f".includes(char) ? `\\${char}` : char;
|
|
171
|
+
return escaped;
|
|
172
|
+
}
|
|
173
|
+
function jsDelivrPackageSpecifier(candidate) {
|
|
174
|
+
const absolute = candidate.match(/^https?:\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i);
|
|
175
|
+
if (absolute?.[1]) return absolute[1];
|
|
176
|
+
return candidate.match(/^\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i)?.[1] ?? null;
|
|
177
|
+
}
|
|
23
178
|
var LessPlugin = class extends AbstractPlugin {
|
|
24
179
|
name = "less";
|
|
25
180
|
supportedExtensions = [".less"];
|
|
26
|
-
parser;
|
|
27
181
|
mathMode;
|
|
28
182
|
unitMode;
|
|
29
183
|
equalityMode;
|
|
30
184
|
leakyScope;
|
|
31
185
|
bubbleRootAtRules;
|
|
32
186
|
collapseNesting;
|
|
187
|
+
pluginHosts = /* @__PURE__ */ new WeakMap();
|
|
33
188
|
constructor(opts = {}) {
|
|
34
189
|
super();
|
|
35
190
|
this.opts = opts;
|
|
@@ -50,41 +205,96 @@ var LessPlugin = class extends AbstractPlugin {
|
|
|
50
205
|
this.leakyScope = opts.leakyScope ?? lessPluginDefaults.leakyScope;
|
|
51
206
|
this.bubbleRootAtRules = opts.bubbleRootAtRules ?? lessPluginDefaults.bubbleRootAtRules;
|
|
52
207
|
this.collapseNesting = opts.collapseNesting ?? lessPluginDefaults.collapseNesting;
|
|
53
|
-
this.parser = new Parser();
|
|
54
|
-
}
|
|
55
|
-
createTreeContext(filePath, source) {
|
|
56
|
-
return new TreeContext({
|
|
57
|
-
file: {
|
|
58
|
-
name: path.basename(filePath),
|
|
59
|
-
path: path.dirname(filePath),
|
|
60
|
-
fullPath: filePath,
|
|
61
|
-
source
|
|
62
|
-
},
|
|
63
|
-
mathMode: this.mathMode,
|
|
64
|
-
unitMode: this.unitMode,
|
|
65
|
-
equalityMode: this.equalityMode,
|
|
66
|
-
plugin: this,
|
|
67
|
-
allowExtendSelectors: this.opts.allowExtendSelectors,
|
|
68
|
-
collapseNesting: this.collapseNesting,
|
|
69
|
-
leakyScope: this.leakyScope,
|
|
70
|
-
bubbleRootAtRules: this.bubbleRootAtRules
|
|
71
|
-
});
|
|
72
208
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
209
|
+
transformUrl({ value, quoted, fromFilePath, entryFilePath }) {
|
|
210
|
+
let transformed;
|
|
211
|
+
if (isUrlRelative(value)) {
|
|
212
|
+
const rewriteUrls = this.opts.rewriteUrls;
|
|
213
|
+
const local = value.startsWith(".");
|
|
214
|
+
if (rewriteUrls !== "local" || local) {
|
|
215
|
+
const rebasesImportedUrl = rewriteUrls === true || rewriteUrls === "all" || rewriteUrls === "local" && local;
|
|
216
|
+
let rootpath = this.opts.rootpath ?? "";
|
|
217
|
+
if (!quoted) rootpath = escapeUnquotedUrlPath(rootpath);
|
|
218
|
+
if (rebasesImportedUrl && fromFilePath && entryFilePath) {
|
|
219
|
+
const relativeDirectory = path.relative(path.dirname(entryFilePath), path.dirname(fromFilePath));
|
|
220
|
+
if (relativeDirectory) rootpath += `${relativeDirectory.split(path.sep).join("/")}/`;
|
|
221
|
+
}
|
|
222
|
+
transformed = rewriteUrlPath(value, rootpath);
|
|
223
|
+
} else transformed = normalizeUrlPath(value);
|
|
224
|
+
} else transformed = normalizeUrlPath(value);
|
|
225
|
+
if (this.opts.urlArgs && !value.trimStart().toLowerCase().startsWith("data:")) {
|
|
226
|
+
const args = `${transformed.includes("?") ? "&" : "?"}${this.opts.urlArgs}`;
|
|
227
|
+
const fragment = transformed.indexOf("#");
|
|
228
|
+
transformed = fragment < 0 ? transformed + args : transformed.slice(0, fragment) + args + transformed.slice(fragment);
|
|
83
229
|
}
|
|
230
|
+
return transformed;
|
|
84
231
|
}
|
|
85
232
|
expandImport(importPath, currentDir) {
|
|
86
233
|
return expandLessImportCandidates(importPath);
|
|
87
234
|
}
|
|
235
|
+
setContext(context) {
|
|
236
|
+
if (context.opts.mathMode === void 0) context.setOption("mathMode", this.mathMode);
|
|
237
|
+
if (context.opts.unitMode === void 0) context.setOption("unitMode", this.unitMode);
|
|
238
|
+
if (context.opts.equalityMode === void 0) context.setOption("equalityMode", this.equalityMode);
|
|
239
|
+
if (context.opts.leakyScope === void 0) context.setOption("leakyScope", this.leakyScope);
|
|
240
|
+
if (context.opts.bubbleRootAtRules === void 0) context.setOption("bubbleRootAtRules", this.bubbleRootAtRules);
|
|
241
|
+
let host = this.pluginHosts.get(context);
|
|
242
|
+
if (!host) {
|
|
243
|
+
const fns = [];
|
|
244
|
+
const nativeFns = /* @__PURE__ */ new WeakMap();
|
|
245
|
+
const addNativeFn = (name, fn) => {
|
|
246
|
+
const adapted = nativeLessFn(name, fn);
|
|
247
|
+
nativeFns.set(adapted, fn);
|
|
248
|
+
fns.push(adapted);
|
|
249
|
+
return adapted;
|
|
250
|
+
};
|
|
251
|
+
const registry = {
|
|
252
|
+
add: (name, fn) => {
|
|
253
|
+
addNativeFn(name, fn);
|
|
254
|
+
},
|
|
255
|
+
addMultiple: (functions) => {
|
|
256
|
+
for (const [name, fn] of Object.entries(functions)) registry.add(name, fn);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const less = {
|
|
260
|
+
functions: { functionRegistry: registry },
|
|
261
|
+
tree: {
|
|
262
|
+
Dimension: class {
|
|
263
|
+
type = "Dimension";
|
|
264
|
+
constructor(value, unit = "") {
|
|
265
|
+
this.value = value;
|
|
266
|
+
this.unit = unit;
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
Quoted: class {
|
|
270
|
+
type = "Quoted";
|
|
271
|
+
constructor(quote, value, escaped = false) {
|
|
272
|
+
this.quote = quote;
|
|
273
|
+
this.value = value;
|
|
274
|
+
this.escaped = escaped;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const configured = this.opts.plugins ?? [];
|
|
280
|
+
for (const plugin of configured) plugin.install?.(less, void 0, registry);
|
|
281
|
+
host = {
|
|
282
|
+
...fns.length === 0 ? {} : { globalFns: fns },
|
|
283
|
+
loadPlugin: async ({ specifier, options }) => {
|
|
284
|
+
const loaded = await context.getPluginModule(specifier, options);
|
|
285
|
+
const functions = isLoadedPluginModule(loaded.module) ? loaded.module.functions : void 0;
|
|
286
|
+
if (!functions) return [];
|
|
287
|
+
return Object.entries(functions).map(([name, fn]) => addNativeFn(name, fn));
|
|
288
|
+
},
|
|
289
|
+
invokeRawFunction: (fn, args) => {
|
|
290
|
+
const native = nativeFns.get(fn);
|
|
291
|
+
return native ? invokeNativeLessFunction(native, args) : void 0;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
this.pluginHosts.set(context, host);
|
|
295
|
+
}
|
|
296
|
+
context.pluginHost = host;
|
|
297
|
+
}
|
|
88
298
|
resolve(filePath, currentDir, searchPaths) {
|
|
89
299
|
const mapped = (Array.isArray(filePath) ? filePath : [filePath]).map((candidate) => {
|
|
90
300
|
if (candidate.startsWith("@less/test-import-module/")) {
|
|
@@ -96,11 +306,7 @@ var LessPlugin = class extends AbstractPlugin {
|
|
|
96
306
|
return path.join(packagesRoot, "test-import-module", after);
|
|
97
307
|
}
|
|
98
308
|
}
|
|
99
|
-
|
|
100
|
-
if (m?.[1]) return m[1];
|
|
101
|
-
const mProtocolRelative = candidate.match(/^\/\/cdn\.jsdelivr\.net\/npm\/([^?#]+)(?:[?#].*)?$/i);
|
|
102
|
-
if (mProtocolRelative?.[1]) return mProtocolRelative[1];
|
|
103
|
-
return candidate;
|
|
309
|
+
return jsDelivrPackageSpecifier(candidate) ?? candidate;
|
|
104
310
|
});
|
|
105
311
|
const out = [...super.resolve(mapped, currentDir, searchPaths)];
|
|
106
312
|
const bases = [
|
|
@@ -128,72 +334,27 @@ var LessPlugin = class extends AbstractPlugin {
|
|
|
128
334
|
}
|
|
129
335
|
return out;
|
|
130
336
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
let tree;
|
|
337
|
+
canResolveImport(specifier) {
|
|
338
|
+
return jsDelivrPackageSpecifier(specifier) !== null;
|
|
339
|
+
}
|
|
340
|
+
safeParse(filePath, source, parseOptions) {
|
|
136
341
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
context.opts.liftedCommentRanges = parseResult.liftedCommentRanges;
|
|
143
|
-
if ("warnings" in parseResult && parseResult.warnings) for (const warning of parseResult.warnings) {
|
|
144
|
-
const line = warning.token?.startLine ?? 1;
|
|
145
|
-
const column = warning.token?.startColumn ?? 1;
|
|
146
|
-
warnings.push({
|
|
147
|
-
code: "parse/deprecated",
|
|
148
|
-
phase: "parse",
|
|
149
|
-
message: warning.message,
|
|
150
|
-
reason: warning.message,
|
|
151
|
-
fix: "Update your code to use the recommended syntax.",
|
|
152
|
-
file: context.file,
|
|
153
|
-
filePath,
|
|
154
|
-
line,
|
|
155
|
-
column,
|
|
156
|
-
lines: extractRelevantLines(source, line)
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
if (parseResult.errors.length) for (const error of parseResult.errors) {
|
|
160
|
-
const line = error.token?.startLine ?? 1;
|
|
161
|
-
const diagnostic = toDiagnostic(getErrorFromParser([error], void 0, filePath, source, { file: context.file }));
|
|
162
|
-
if (!diagnostic.lines) diagnostic.lines = extractRelevantLines(source, line);
|
|
163
|
-
if ("errors" in diagnostic) errors.push(diagnostic);
|
|
164
|
-
else warnings.push(diagnostic);
|
|
165
|
-
}
|
|
342
|
+
return {
|
|
343
|
+
document: parse(source),
|
|
344
|
+
errors: [],
|
|
345
|
+
warnings: []
|
|
346
|
+
};
|
|
166
347
|
} catch (error) {
|
|
167
|
-
if (error instanceof JessError) {
|
|
168
|
-
const diagnostic = toDiagnostic(error);
|
|
169
|
-
if ("errors" in diagnostic) errors.push(diagnostic);
|
|
170
|
-
else warnings.push(diagnostic);
|
|
171
|
-
} else {
|
|
172
|
-
const message = error instanceof Error ? error.message : "Unknown parsing error";
|
|
173
|
-
errors.push({
|
|
174
|
-
code: "internal/unknown",
|
|
175
|
-
phase: "parse",
|
|
176
|
-
message,
|
|
177
|
-
reason: message || "An unexpected error occurred during parsing.",
|
|
178
|
-
fix: "Check the file syntax and ensure it is valid.",
|
|
179
|
-
file: context.file,
|
|
180
|
-
filePath,
|
|
181
|
-
line: 1,
|
|
182
|
-
column: 1,
|
|
183
|
-
lines: extractRelevantLines(source, 1)
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
348
|
return {
|
|
187
|
-
errors
|
|
188
|
-
|
|
349
|
+
errors: [parserDiagnostic({
|
|
350
|
+
dialect: "Less",
|
|
351
|
+
error,
|
|
352
|
+
filePath,
|
|
353
|
+
source
|
|
354
|
+
})],
|
|
355
|
+
warnings: []
|
|
189
356
|
};
|
|
190
357
|
}
|
|
191
|
-
if (tree && errors.length === 0) this._registerFunctions(tree);
|
|
192
|
-
return {
|
|
193
|
-
tree,
|
|
194
|
-
errors,
|
|
195
|
-
warnings
|
|
196
|
-
};
|
|
197
358
|
}
|
|
198
359
|
};
|
|
199
360
|
const lessPlugin = ((opts) => {
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"description": "A Less stylesheet engine for Jess",
|
|
8
|
-
"version": "2.0.0-alpha.
|
|
8
|
+
"version": "2.0.0-alpha.9",
|
|
9
9
|
"main": "lib/index.cjs",
|
|
10
10
|
"types": "lib/index.d.ts",
|
|
11
11
|
"exports": {
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
"lib"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jesscss/
|
|
24
|
-
"@jesscss/
|
|
25
|
-
"@jesscss/style-resolver": "2.0.0-alpha.
|
|
26
|
-
"@jesscss/
|
|
27
|
-
"
|
|
23
|
+
"@jesscss/fns": "2.0.0-alpha.9",
|
|
24
|
+
"@jesscss/less-parser": "2.0.0-alpha.9",
|
|
25
|
+
"@jesscss/style-resolver": "2.0.0-alpha.9",
|
|
26
|
+
"@jesscss/core": "2.0.0-alpha.9",
|
|
27
|
+
"@jesscss/plugin-node-modules": "2.0.0-alpha.9",
|
|
28
|
+
"styles-config": "2.0.0-alpha.9"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {},
|
|
30
31
|
"author": "Matthew Dean <matthew-dean@users.noreply.github.com>",
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
"scripts": {
|
|
38
39
|
"ci": "pnpm build && pnpm test",
|
|
39
40
|
"build": "pnpm compile",
|
|
40
|
-
"compile": "tsdown --tsconfig tsconfig.build.json --no-dts && tsc -p tsconfig.build.json --emitDeclarationOnly
|
|
41
|
+
"compile": "tsdown --tsconfig tsconfig.build.json --no-dts && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
41
42
|
"test": "vitest --run --passWithNoTests",
|
|
42
43
|
"lint:fix": "eslint --fix '**/*.{js,ts}'",
|
|
43
44
|
"lint": "eslint '**/*.{js,ts}'"
|