@master/css-lexer 2.0.0-rc.70
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 +37 -0
- package/dist/class.d.ts +32 -0
- package/dist/class.mjs +526 -0
- package/dist/css-manifest-entry.d.ts +30 -0
- package/dist/css-manifest-entry.mjs +188 -0
- package/dist/directive-ranges.d.ts +29 -0
- package/dist/directive-ranges.mjs +262 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/source.d.ts +31 -0
- package/dist/source.mjs +211 -0
- package/dist/units.d.ts +2 -0
- package/dist/units.mjs +70 -0
- package/package.json +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @master/css-lexer
|
|
2
|
+
|
|
3
|
+
Dependency-free lexical source scanners for Master CSS tooling.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @master/css-lexer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Responsibility
|
|
12
|
+
|
|
13
|
+
`@master/css-lexer` identifies source ranges, directive boundaries, quoted strings, class lexical tokens, CSS manifest entry statements, and unit constants shared by Master CSS packages.
|
|
14
|
+
|
|
15
|
+
It does not validate classes, generate CSS, resolve manifests, compile directives, or extract source-level class candidates.
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {
|
|
21
|
+
collectCSSDirectiveRanges,
|
|
22
|
+
findCSSManifestEntryStatements,
|
|
23
|
+
tokenizeMasterCSSClass,
|
|
24
|
+
} from '@master/css-lexer'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Common uses:
|
|
28
|
+
|
|
29
|
+
| API area | Purpose |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| Source primitives | Stable offsets and source locations. |
|
|
32
|
+
| Directive ranges | Locate CSS directives and quoted arguments without compiling them. |
|
|
33
|
+
| Manifest entry scanners | Detect `@master;` and `@import '@master/css'` entry markers. |
|
|
34
|
+
| Class tokenizers | Split Master CSS class strings into lexical token ranges. |
|
|
35
|
+
| Unit constants | Shared lexical constants for CSS units. |
|
|
36
|
+
|
|
37
|
+
Use `@master/css-source` for source-format-aware class candidate extraction.
|
package/dist/class.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type MasterCSSLexicalTokenType = 'class' | 'enumMember' | 'property' | 'variable' | 'function' | 'number' | 'string' | 'keyword' | 'modifier' | 'operator' | 'type';
|
|
2
|
+
export type MasterCSSLexicalTokenModifier = 'declaration' | 'defaultLibrary' | 'component' | 'directive' | 'important' | 'pseudoClass' | 'pseudoElement' | 'query' | 'quoted' | 'selector' | 'unit';
|
|
3
|
+
export type MasterCSSLexicalTokenRole = 'block.brace' | 'declaration.property' | 'declaration.separator' | 'declaration.terminator' | 'directive.keyword' | 'directive.modifier' | 'directive.parameter' | 'directive.terminator' | 'query.keyword' | 'query.feature' | 'query.operator' | 'query.punctuation' | 'query.value' | 'query.number' | 'query.unit' | 'selector.attribute' | 'selector.class' | 'selector.combinator' | 'selector.id' | 'selector.pseudoClass.delimiter' | 'selector.pseudoClass.name' | 'selector.pseudoElement.delimiter' | 'selector.pseudoElement.name' | 'selector.punctuation' | 'selector.type' | 'theme.variable' | 'utility.component' | 'utility.semantic' | 'value.color' | 'value.function.name' | 'value.function.punctuation' | 'value.important' | 'value.keyword' | 'value.number' | 'value.operator' | 'value.separator' | 'value.string' | 'value.string.quote' | 'value.unit' | 'value.variable';
|
|
4
|
+
export interface MasterCSSLexicalTokenItem {
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
type: MasterCSSLexicalTokenType;
|
|
8
|
+
role: MasterCSSLexicalTokenRole;
|
|
9
|
+
modifiers?: MasterCSSLexicalTokenModifier[];
|
|
10
|
+
}
|
|
11
|
+
export interface MasterCSSSplitPart {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
separatorStart?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface MasterCSSClassListTokenRange {
|
|
17
|
+
start: number;
|
|
18
|
+
end: number;
|
|
19
|
+
token: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MasterCSSValueTokenizeOptions {
|
|
22
|
+
isKnownVariable?: (name: string) => boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function pushMasterCSSLexicalToken(tokens: MasterCSSLexicalTokenItem[], start: number, length: number, type: MasterCSSLexicalTokenType, role: MasterCSSLexicalTokenRole, modifiers?: MasterCSSLexicalTokenModifier[]): void;
|
|
25
|
+
export declare function findMasterCSSClosingQuote(source: string, start: number, quote: string): number;
|
|
26
|
+
export declare function findMasterCSSClosingParen(source: string, open: number): number;
|
|
27
|
+
export declare function splitMasterCSSTopLevel(source: string, separator: string): MasterCSSSplitPart[];
|
|
28
|
+
export declare function collectMasterCSSClassListTokenRanges(classList: string): MasterCSSClassListTokenRange[];
|
|
29
|
+
export declare function tokenizeMasterCSSValue(valueText: string, valueStart: number, options?: MasterCSSValueTokenizeOptions): MasterCSSLexicalTokenItem[];
|
|
30
|
+
export declare function tokenizeMasterCSSAtQuery(queryText: string, queryStart: number): MasterCSSLexicalTokenItem[];
|
|
31
|
+
export declare function tokenizeMasterCSSState(token: string, stateStart: number, offset: number): MasterCSSLexicalTokenItem[];
|
|
32
|
+
export declare function tokenizeMasterCSSGroupedClassToken(token: string, offset: number, tokenizeClassToken: (token: string, offset: number) => MasterCSSLexicalTokenItem[]): MasterCSSLexicalTokenItem[] | undefined;
|
package/dist/class.mjs
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { MASTER_CSS_VALUE_UNITS } from './units.mjs';
|
|
2
|
+
|
|
3
|
+
const IDENTIFIER_RE = /^[A-Za-z_][\w-]*/;
|
|
4
|
+
const CSS_CUSTOM_FUNCTION_RE = /^--[A-Za-z_][\w-]*/;
|
|
5
|
+
const HASH_RE = /^#[\w-]+/;
|
|
6
|
+
const VARIABLE_RE = /^\$[A-Za-z0-9-]+(?:\/[+-]?(?:\d+\.\d+|\.\d+|\d+|[A-Za-z0-9_.%-]+))?/;
|
|
7
|
+
const NUMERIC_RE = /^([+-]?(?:\d+\.\d+|\.\d+|\d+))([A-Za-z%]+)?/;
|
|
8
|
+
const SIZE_PAIR_RE = /^([+-]?(?:\d+\.\d+|\.\d+|\d+))x([+-]?(?:\d+\.\d+|\.\d+|\d+))(?![0-9A-Za-z])/;
|
|
9
|
+
const MASTER_CSS_VALUE_UNIT_SET = new Set(MASTER_CSS_VALUE_UNITS);
|
|
10
|
+
function pushMasterCSSLexicalToken(tokens, start, length, type, role, modifiers) {
|
|
11
|
+
if (length <= 0) return;
|
|
12
|
+
tokens.push({
|
|
13
|
+
start,
|
|
14
|
+
end: start + length,
|
|
15
|
+
type,
|
|
16
|
+
role,
|
|
17
|
+
modifiers
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function findMasterCSSClosingQuote(source, start, quote) {
|
|
21
|
+
for(let i = start + 1; i < source.length; i++){
|
|
22
|
+
if (source[i] === '\\') {
|
|
23
|
+
i++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (source[i] === quote) return i;
|
|
27
|
+
}
|
|
28
|
+
return source.length - 1;
|
|
29
|
+
}
|
|
30
|
+
function findMasterCSSClosingParen(source, open) {
|
|
31
|
+
let quote = '';
|
|
32
|
+
let depth = 0;
|
|
33
|
+
for(let i = open; i < source.length; i++){
|
|
34
|
+
const char = source[i];
|
|
35
|
+
if (quote) {
|
|
36
|
+
if (char === '\\') i++;
|
|
37
|
+
else if (char === quote) quote = '';
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (char === '"' || char === '\'') {
|
|
41
|
+
quote = char;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (char === '(') {
|
|
45
|
+
depth++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === ')') {
|
|
49
|
+
depth--;
|
|
50
|
+
if (depth === 0) return i;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return source.length - 1;
|
|
54
|
+
}
|
|
55
|
+
function splitMasterCSSTopLevel(source, separator) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
let quote = '';
|
|
58
|
+
let depth = 0;
|
|
59
|
+
let start = 0;
|
|
60
|
+
for(let i = 0; i < source.length; i++){
|
|
61
|
+
const char = source[i];
|
|
62
|
+
if (quote) {
|
|
63
|
+
if (char === '\\') i++;
|
|
64
|
+
else if (char === quote) quote = '';
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (char === '"' || char === '\'') {
|
|
68
|
+
quote = char;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (char === '(' || char === '[' || char === '{') {
|
|
72
|
+
depth++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (char === ')' || char === ']' || char === '}') {
|
|
76
|
+
depth = Math.max(0, depth - 1);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (depth === 0 && char === separator) {
|
|
80
|
+
parts.push({
|
|
81
|
+
start,
|
|
82
|
+
end: i,
|
|
83
|
+
separatorStart: i
|
|
84
|
+
});
|
|
85
|
+
start = i + 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
parts.push({
|
|
89
|
+
start,
|
|
90
|
+
end: source.length
|
|
91
|
+
});
|
|
92
|
+
return parts;
|
|
93
|
+
}
|
|
94
|
+
function collectMasterCSSClassListTokenRanges(classList) {
|
|
95
|
+
const ranges = [];
|
|
96
|
+
for (const match of classList.matchAll(/[^\s]+/g)){
|
|
97
|
+
if (match.index === undefined) continue;
|
|
98
|
+
ranges.push({
|
|
99
|
+
start: match.index,
|
|
100
|
+
end: match.index + match[0].length,
|
|
101
|
+
token: match[0]
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return ranges;
|
|
105
|
+
}
|
|
106
|
+
function withUnitModifier(modifiers = []) {
|
|
107
|
+
return [
|
|
108
|
+
...modifiers,
|
|
109
|
+
'unit'
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
function pushNumberOrKeyword(tokens, absoluteStart, raw, modifiers, options) {
|
|
113
|
+
if (!raw) return;
|
|
114
|
+
if (options?.isKnownVariable?.(raw)) {
|
|
115
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, raw.length, 'variable', 'value.variable', modifiers);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const numeric = NUMERIC_RE.exec(raw);
|
|
119
|
+
if (!numeric || numeric[0].length !== raw.length) {
|
|
120
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, raw.length, 'enumMember', raw.startsWith('#') ? 'value.color' : 'value.keyword', modifiers);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const [, numberText, unit] = numeric;
|
|
124
|
+
if (unit && !MASTER_CSS_VALUE_UNIT_SET.has(unit)) {
|
|
125
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, raw.length, 'enumMember', 'value.keyword', modifiers);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, numberText.length, 'number', 'value.number', modifiers);
|
|
129
|
+
if (unit) {
|
|
130
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart + numberText.length, unit.length, 'enumMember', 'value.unit', withUnitModifier(modifiers));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function pushQueryNumberOrValue(tokens, absoluteStart, raw) {
|
|
134
|
+
const numeric = NUMERIC_RE.exec(raw);
|
|
135
|
+
if (!numeric || numeric[0].length !== raw.length) {
|
|
136
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, raw.length, 'enumMember', 'query.value', [
|
|
137
|
+
'query'
|
|
138
|
+
]);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const [, numberText, unit] = numeric;
|
|
142
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart, numberText.length, 'number', 'query.number', [
|
|
143
|
+
'query'
|
|
144
|
+
]);
|
|
145
|
+
if (unit) {
|
|
146
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart + numberText.length, unit.length, 'enumMember', 'query.unit', [
|
|
147
|
+
'query',
|
|
148
|
+
'unit'
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function pushSlashSeparatedValue(tokens, absoluteStart, raw, options) {
|
|
153
|
+
let segmentStart = 0;
|
|
154
|
+
for(let i = 0; i < raw.length; i++){
|
|
155
|
+
if (raw[i] !== '/') continue;
|
|
156
|
+
pushNumberOrKeyword(tokens, absoluteStart + segmentStart, raw.slice(segmentStart, i), undefined, options);
|
|
157
|
+
pushMasterCSSLexicalToken(tokens, absoluteStart + i, 1, 'operator', 'value.separator');
|
|
158
|
+
segmentStart = i + 1;
|
|
159
|
+
}
|
|
160
|
+
pushNumberOrKeyword(tokens, absoluteStart + segmentStart, raw.slice(segmentStart), undefined, options);
|
|
161
|
+
}
|
|
162
|
+
function tokenizeMasterCSSValue(valueText, valueStart, options) {
|
|
163
|
+
const tokens = [];
|
|
164
|
+
for(let i = 0; i < valueText.length;){
|
|
165
|
+
const char = valueText[i];
|
|
166
|
+
if (/\s/.test(char)) {
|
|
167
|
+
i++;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (char === '"' || char === '\'') {
|
|
171
|
+
const end = findMasterCSSClosingQuote(valueText, i, char);
|
|
172
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'string', 'value.string.quote', [
|
|
173
|
+
'quoted'
|
|
174
|
+
]);
|
|
175
|
+
if (end > i + 1) {
|
|
176
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i + 1, end - i - 1, 'string', 'value.string', [
|
|
177
|
+
'quoted'
|
|
178
|
+
]);
|
|
179
|
+
}
|
|
180
|
+
pushMasterCSSLexicalToken(tokens, valueStart + end, 1, 'string', 'value.string.quote', [
|
|
181
|
+
'quoted'
|
|
182
|
+
]);
|
|
183
|
+
i = end + 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (char === '$') {
|
|
187
|
+
const match = valueText.slice(i).match(VARIABLE_RE);
|
|
188
|
+
if (match) {
|
|
189
|
+
const variable = match[0];
|
|
190
|
+
const slash = variable.lastIndexOf('/');
|
|
191
|
+
if (slash > 0) {
|
|
192
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, slash, 'variable', 'value.variable');
|
|
193
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i + slash, 1, 'operator', 'value.separator');
|
|
194
|
+
pushNumberOrKeyword(tokens, valueStart + i + slash + 1, variable.slice(slash + 1));
|
|
195
|
+
} else {
|
|
196
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, variable.length, 'variable', 'value.variable');
|
|
197
|
+
}
|
|
198
|
+
i += variable.length;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (char === '#') {
|
|
203
|
+
const match = valueText.slice(i).match(HASH_RE);
|
|
204
|
+
if (match) {
|
|
205
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, match[0].length, 'enumMember', 'value.color');
|
|
206
|
+
i += match[0].length;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (char === '-' && valueText[i + 1] === '-') {
|
|
211
|
+
const customFunction = valueText.slice(i).match(CSS_CUSTOM_FUNCTION_RE);
|
|
212
|
+
if (customFunction) {
|
|
213
|
+
const word = customFunction[0];
|
|
214
|
+
const wordEnd = i + word.length;
|
|
215
|
+
if (word === '--value' && valueText[wordEnd] === '(') {
|
|
216
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, word.length, 'function', 'value.function.name');
|
|
217
|
+
pushMasterCSSLexicalToken(tokens, valueStart + wordEnd, 1, 'operator', 'value.function.punctuation');
|
|
218
|
+
i = wordEnd + 1;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const pair = valueText.slice(i).match(SIZE_PAIR_RE);
|
|
224
|
+
if (pair) {
|
|
225
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, pair[1].length, 'number', 'value.number');
|
|
226
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i + pair[1].length, 1, 'operator', 'value.separator', [
|
|
227
|
+
'unit'
|
|
228
|
+
]);
|
|
229
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i + pair[1].length + 1, pair[2].length, 'number', 'value.number');
|
|
230
|
+
i += pair[0].length;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (/[+-]?(?:\d|\.)/.test(char)) {
|
|
234
|
+
const match = valueText.slice(i).match(NUMERIC_RE);
|
|
235
|
+
if (match) {
|
|
236
|
+
const raw = match[0];
|
|
237
|
+
pushNumberOrKeyword(tokens, valueStart + i, raw, undefined, options);
|
|
238
|
+
i += raw.length;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const identifier = valueText.slice(i).match(IDENTIFIER_RE);
|
|
243
|
+
if (identifier) {
|
|
244
|
+
const word = identifier[0];
|
|
245
|
+
const wordEnd = i + word.length;
|
|
246
|
+
if (valueText[wordEnd] === '(') {
|
|
247
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, word.length, 'function', 'value.function.name');
|
|
248
|
+
pushMasterCSSLexicalToken(tokens, valueStart + wordEnd, 1, 'operator', 'value.function.punctuation');
|
|
249
|
+
if (word === 'url') {
|
|
250
|
+
const close = findMasterCSSClosingParen(valueText, wordEnd);
|
|
251
|
+
const innerStart = wordEnd + 1;
|
|
252
|
+
const innerEnd = Math.max(innerStart, close);
|
|
253
|
+
const inner = valueText.slice(innerStart, innerEnd);
|
|
254
|
+
if (inner) {
|
|
255
|
+
if (inner[0] === '"' || inner[0] === '\'') {
|
|
256
|
+
tokens.push(...tokenizeMasterCSSValue(inner, valueStart + innerStart, options));
|
|
257
|
+
} else {
|
|
258
|
+
pushMasterCSSLexicalToken(tokens, valueStart + innerStart, inner.length, 'string', 'value.string');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (valueText[close] === ')') {
|
|
262
|
+
pushMasterCSSLexicalToken(tokens, valueStart + close, 1, 'operator', 'value.function.punctuation');
|
|
263
|
+
i = close + 1;
|
|
264
|
+
} else {
|
|
265
|
+
i = innerEnd;
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
i = wordEnd + 1;
|
|
269
|
+
}
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
let end = wordEnd;
|
|
273
|
+
while(end < valueText.length && /[A-Za-z0-9.%/-]/.test(valueText[end]))end++;
|
|
274
|
+
pushSlashSeparatedValue(tokens, valueStart + i, valueText.slice(i, end), options);
|
|
275
|
+
i = end;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (char === '|' || char === '_' || char === ',' || char === '/') {
|
|
279
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'operator', 'value.separator');
|
|
280
|
+
i++;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (char === '(' || char === ')') {
|
|
284
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'operator', 'value.function.punctuation');
|
|
285
|
+
i++;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (char === '+' || char === '*' || char === '-') {
|
|
289
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'operator', 'value.operator');
|
|
290
|
+
i++;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (char === '!') {
|
|
294
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'operator', 'value.important', [
|
|
295
|
+
'important'
|
|
296
|
+
]);
|
|
297
|
+
i++;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
pushMasterCSSLexicalToken(tokens, valueStart + i, 1, 'enumMember', 'value.keyword');
|
|
301
|
+
i++;
|
|
302
|
+
}
|
|
303
|
+
return tokens;
|
|
304
|
+
}
|
|
305
|
+
function startsAtFeatureOperator(value) {
|
|
306
|
+
return value[0] === ':' || value.startsWith('>=') || value.startsWith('<=') || value[0] === '>' || value[0] === '<' || value[0] === '=';
|
|
307
|
+
}
|
|
308
|
+
function pushAtWord(tokens, start, value, next) {
|
|
309
|
+
if (startsAtFeatureOperator(next)) {
|
|
310
|
+
pushMasterCSSLexicalToken(tokens, start, value.length, 'property', 'query.feature', [
|
|
311
|
+
'query'
|
|
312
|
+
]);
|
|
313
|
+
} else {
|
|
314
|
+
pushQueryNumberOrValue(tokens, start, value);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function startsNamedQueryRange(value) {
|
|
318
|
+
return value[0] === '&' || value[0] === ',';
|
|
319
|
+
}
|
|
320
|
+
function tokenizeMasterCSSAtQuery(queryText, queryStart) {
|
|
321
|
+
const tokens = [];
|
|
322
|
+
let i = 0;
|
|
323
|
+
const keyword = queryText.match(/^@[A-Za-z0-9-]+/);
|
|
324
|
+
if (keyword) {
|
|
325
|
+
const [keywordText] = keyword;
|
|
326
|
+
if (startsNamedQueryRange(queryText.slice(keywordText.length))) {
|
|
327
|
+
pushMasterCSSLexicalToken(tokens, queryStart, 1, 'keyword', 'query.keyword', [
|
|
328
|
+
'query'
|
|
329
|
+
]);
|
|
330
|
+
pushQueryNumberOrValue(tokens, queryStart + 1, keywordText.slice(1));
|
|
331
|
+
} else {
|
|
332
|
+
pushMasterCSSLexicalToken(tokens, queryStart, keywordText.length, 'keyword', 'query.keyword', [
|
|
333
|
+
'query'
|
|
334
|
+
]);
|
|
335
|
+
}
|
|
336
|
+
i += keyword[0].length;
|
|
337
|
+
} else if (queryText[i] === '@') {
|
|
338
|
+
pushMasterCSSLexicalToken(tokens, queryStart, 1, 'keyword', 'query.keyword', [
|
|
339
|
+
'query'
|
|
340
|
+
]);
|
|
341
|
+
i++;
|
|
342
|
+
}
|
|
343
|
+
while(i < queryText.length){
|
|
344
|
+
const twoChars = queryText.slice(i, i + 2);
|
|
345
|
+
if (twoChars === '>=' || twoChars === '<=') {
|
|
346
|
+
pushMasterCSSLexicalToken(tokens, queryStart + i, 2, 'operator', 'query.operator', [
|
|
347
|
+
'query'
|
|
348
|
+
]);
|
|
349
|
+
i += 2;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const char = queryText[i];
|
|
353
|
+
if (char === '(' || char === ')') {
|
|
354
|
+
pushMasterCSSLexicalToken(tokens, queryStart + i, 1, 'operator', 'query.punctuation', [
|
|
355
|
+
'query'
|
|
356
|
+
]);
|
|
357
|
+
i++;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (char === ',' || char === ':') {
|
|
361
|
+
pushMasterCSSLexicalToken(tokens, queryStart + i, 1, 'operator', 'query.punctuation', [
|
|
362
|
+
'query'
|
|
363
|
+
]);
|
|
364
|
+
i++;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (char === '&' || char === '!' || char === '>' || char === '<' || char === '=') {
|
|
368
|
+
pushMasterCSSLexicalToken(tokens, queryStart + i, 1, 'operator', 'query.operator', [
|
|
369
|
+
'query'
|
|
370
|
+
]);
|
|
371
|
+
i++;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (/\s/.test(char)) {
|
|
375
|
+
i++;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const word = queryText.slice(i).match(/^[#A-Za-z0-9_.%-]+/);
|
|
379
|
+
if (word) {
|
|
380
|
+
const next = queryText.slice(i + word[0].length, i + word[0].length + 2);
|
|
381
|
+
pushAtWord(tokens, queryStart + i, word[0], next);
|
|
382
|
+
i += word[0].length;
|
|
383
|
+
} else {
|
|
384
|
+
i++;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return tokens;
|
|
388
|
+
}
|
|
389
|
+
function tokenizeMasterCSSState(token, stateStart, offset) {
|
|
390
|
+
const tokens = [];
|
|
391
|
+
for(let i = stateStart; i < token.length;){
|
|
392
|
+
const char = token[i];
|
|
393
|
+
if (char === '!') {
|
|
394
|
+
pushMasterCSSLexicalToken(tokens, offset + i, 1, 'operator', 'value.important', [
|
|
395
|
+
'important'
|
|
396
|
+
]);
|
|
397
|
+
i++;
|
|
398
|
+
} else if (char === '_' || char === '>' || char === '+' || char === '~') {
|
|
399
|
+
pushMasterCSSLexicalToken(tokens, offset + i, 1, 'operator', 'selector.combinator', [
|
|
400
|
+
'selector'
|
|
401
|
+
]);
|
|
402
|
+
i++;
|
|
403
|
+
const match = token.slice(i).match(/^\*?[A-Za-z][\w-]*/);
|
|
404
|
+
if (match) {
|
|
405
|
+
pushMasterCSSLexicalToken(tokens, offset + i, match[0].length, 'type', 'selector.type', [
|
|
406
|
+
'selector'
|
|
407
|
+
]);
|
|
408
|
+
i += match[0].length;
|
|
409
|
+
}
|
|
410
|
+
} else if (char === '.' || char === '#') {
|
|
411
|
+
const role = char === '.' ? 'selector.class' : 'selector.id';
|
|
412
|
+
pushMasterCSSLexicalToken(tokens, offset + i, 1, 'operator', 'selector.punctuation', [
|
|
413
|
+
'selector'
|
|
414
|
+
]);
|
|
415
|
+
const nameStart = i + 1;
|
|
416
|
+
const match = token.slice(nameStart).match(/^[\w-]+/);
|
|
417
|
+
if (match) {
|
|
418
|
+
pushMasterCSSLexicalToken(tokens, offset + nameStart, match[0].length, char === '.' ? 'class' : 'variable', role, [
|
|
419
|
+
'selector'
|
|
420
|
+
]);
|
|
421
|
+
i = nameStart + match[0].length;
|
|
422
|
+
} else {
|
|
423
|
+
i = nameStart;
|
|
424
|
+
}
|
|
425
|
+
} else if (char === '(' || char === ',') {
|
|
426
|
+
pushMasterCSSLexicalToken(tokens, offset + i, 1, 'operator', 'selector.punctuation', [
|
|
427
|
+
'selector'
|
|
428
|
+
]);
|
|
429
|
+
i++;
|
|
430
|
+
const match = token.slice(i).match(/^\*?[A-Za-z][\w-]*/);
|
|
431
|
+
if (match) {
|
|
432
|
+
pushMasterCSSLexicalToken(tokens, offset + i, match[0].length, 'type', 'selector.type', [
|
|
433
|
+
'selector'
|
|
434
|
+
]);
|
|
435
|
+
i += match[0].length;
|
|
436
|
+
}
|
|
437
|
+
} else if (char === ')' || char === '[' || char === ']') {
|
|
438
|
+
pushMasterCSSLexicalToken(tokens, offset + i, 1, 'operator', 'selector.punctuation', [
|
|
439
|
+
'selector'
|
|
440
|
+
]);
|
|
441
|
+
i++;
|
|
442
|
+
} else if (char === '@') {
|
|
443
|
+
const nextAt = token.indexOf('@', i + 1);
|
|
444
|
+
const end = nextAt >= 0 ? nextAt : token.length;
|
|
445
|
+
tokens.push(...tokenizeMasterCSSAtQuery(token.slice(i, end), offset + i));
|
|
446
|
+
i = end;
|
|
447
|
+
} else if (char === ':') {
|
|
448
|
+
const colonLength = token[i + 1] === ':' ? 2 : 1;
|
|
449
|
+
const pseudoModifier = colonLength === 2 ? 'pseudoElement' : 'pseudoClass';
|
|
450
|
+
const delimiterRole = colonLength === 2 ? 'selector.pseudoElement.delimiter' : 'selector.pseudoClass.delimiter';
|
|
451
|
+
const nameRole = colonLength === 2 ? 'selector.pseudoElement.name' : 'selector.pseudoClass.name';
|
|
452
|
+
pushMasterCSSLexicalToken(tokens, offset + i, colonLength, 'operator', delimiterRole, [
|
|
453
|
+
'selector',
|
|
454
|
+
pseudoModifier
|
|
455
|
+
]);
|
|
456
|
+
const nameStart = i + colonLength;
|
|
457
|
+
const match = token.slice(nameStart).match(/^[\w-]+/);
|
|
458
|
+
if (match) {
|
|
459
|
+
pushMasterCSSLexicalToken(tokens, offset + nameStart, match[0].length, 'modifier', nameRole, [
|
|
460
|
+
pseudoModifier
|
|
461
|
+
]);
|
|
462
|
+
i = nameStart + match[0].length;
|
|
463
|
+
} else {
|
|
464
|
+
i = nameStart;
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
i++;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return tokens;
|
|
471
|
+
}
|
|
472
|
+
function findMasterCSSGroupedClassClose(token) {
|
|
473
|
+
let quote = '';
|
|
474
|
+
let depth = 0;
|
|
475
|
+
for(let i = 1; i < token.length; i++){
|
|
476
|
+
const char = token[i];
|
|
477
|
+
if (quote) {
|
|
478
|
+
if (char === '\\') i++;
|
|
479
|
+
else if (char === quote) quote = '';
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (char === '"' || char === '\'') {
|
|
483
|
+
quote = char;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (char === '(' || char === '[' || char === '{') {
|
|
487
|
+
depth++;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (char === ')' || char === ']') {
|
|
491
|
+
depth = Math.max(0, depth - 1);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (char === '}') {
|
|
495
|
+
if (depth === 0) return i;
|
|
496
|
+
depth--;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function tokenizeMasterCSSGroupedClassToken(token, offset, tokenizeClassToken) {
|
|
501
|
+
const startsWithGroup = token.startsWith('{');
|
|
502
|
+
const groupClose = startsWithGroup ? findMasterCSSGroupedClassClose(token) : undefined;
|
|
503
|
+
const bodyStart = startsWithGroup ? 1 : 0;
|
|
504
|
+
const bodyEnd = groupClose ?? token.length;
|
|
505
|
+
const body = token.slice(bodyStart, bodyEnd);
|
|
506
|
+
if (!startsWithGroup && !body.includes(';')) return;
|
|
507
|
+
const tokens = [];
|
|
508
|
+
if (startsWithGroup) pushMasterCSSLexicalToken(tokens, offset, 1, 'operator', 'block.brace');
|
|
509
|
+
for (const part of splitMasterCSSTopLevel(body, ';')){
|
|
510
|
+
const partText = body.slice(part.start, part.end).trim();
|
|
511
|
+
if (partText) {
|
|
512
|
+
const leadingWhitespace = body.slice(part.start, part.end).search(/\S/);
|
|
513
|
+
tokens.push(...tokenizeClassToken(partText, offset + bodyStart + part.start + Math.max(0, leadingWhitespace)));
|
|
514
|
+
}
|
|
515
|
+
if (part.separatorStart !== undefined) {
|
|
516
|
+
pushMasterCSSLexicalToken(tokens, offset + bodyStart + part.separatorStart, 1, 'operator', 'declaration.terminator');
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (groupClose !== undefined) {
|
|
520
|
+
pushMasterCSSLexicalToken(tokens, offset + groupClose, 1, 'operator', 'block.brace');
|
|
521
|
+
tokens.push(...tokenizeMasterCSSState(token, groupClose + 1, offset));
|
|
522
|
+
}
|
|
523
|
+
return tokens;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export { collectMasterCSSClassListTokenRanges, findMasterCSSClosingParen, findMasterCSSClosingQuote, pushMasterCSSLexicalToken, splitMasterCSSTopLevel, tokenizeMasterCSSAtQuery, tokenizeMasterCSSGroupedClassToken, tokenizeMasterCSSState, tokenizeMasterCSSValue };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const MASTER_CSS_PACKAGE_MODULE_IDS: readonly ["@master/css"];
|
|
2
|
+
export interface CSSImportStatement {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
statement: string;
|
|
6
|
+
}
|
|
7
|
+
export interface MasterCSSDirectiveStatement {
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function normalizeMasterCSSModuleIds(): Set<string>;
|
|
13
|
+
export declare function isMasterCSSModuleId(id: string): boolean;
|
|
14
|
+
export declare function createMasterCSSImportPattern(): RegExp;
|
|
15
|
+
export declare function createMasterCSSManifestEntryPattern(): RegExp;
|
|
16
|
+
export declare function findCSSImportEnd(source: string, start: number): number;
|
|
17
|
+
export declare function parseCSSImportSource(statement: string): string | undefined;
|
|
18
|
+
export declare function findCSSImportStatements(source: string): CSSImportStatement[];
|
|
19
|
+
export declare function findMasterDirectiveStatements(source: string): MasterCSSDirectiveStatement[];
|
|
20
|
+
export declare function replaceMasterCSSImports(source: string, replacement: string): {
|
|
21
|
+
code: string;
|
|
22
|
+
replaced: boolean;
|
|
23
|
+
};
|
|
24
|
+
export declare function hasMasterCSSImport(source: string): boolean;
|
|
25
|
+
export declare function hasMasterEntryDirective(source: string): boolean;
|
|
26
|
+
export declare function hasMasterCSSManifestEntrypoint(source: string): boolean;
|
|
27
|
+
export declare function removeMasterDirectiveStatements(source: string): {
|
|
28
|
+
code: string;
|
|
29
|
+
removed: boolean;
|
|
30
|
+
};
|