@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.
@@ -0,0 +1,188 @@
1
+ import { escapeRegExp, findCSSStatementEnd, isCSSIdentChar } from './source.mjs';
2
+
3
+ const MASTER_CSS_PACKAGE_MODULE_IDS = [
4
+ '@master/css'
5
+ ];
6
+ const MASTER_CSS_PACKAGE_MODULE_ID_SET = new Set(MASTER_CSS_PACKAGE_MODULE_IDS);
7
+ function normalizeMasterCSSModuleIds() {
8
+ return new Set(MASTER_CSS_PACKAGE_MODULE_IDS);
9
+ }
10
+ function isMasterCSSModuleId(id) {
11
+ return MASTER_CSS_PACKAGE_MODULE_ID_SET.has(id);
12
+ }
13
+ function createMasterCSSImportPattern() {
14
+ const ids = [
15
+ ...normalizeMasterCSSModuleIds()
16
+ ].map(escapeRegExp);
17
+ return new RegExp(String.raw`@import\s+(?:url\(\s*)?(['"])(?:${ids.join('|')})\1\s*\)?[^;]*;`);
18
+ }
19
+ function createMasterCSSManifestEntryPattern() {
20
+ const ids = [
21
+ ...normalizeMasterCSSModuleIds()
22
+ ].map(escapeRegExp);
23
+ return new RegExp(String.raw`(?:@master\s*;|@import\s+(?:url\(\s*)?(['"])(?:${ids.join('|')})\1\s*\)?[^;]*;)`);
24
+ }
25
+ function findCSSImportEnd(source, start) {
26
+ const statementEnd = findCSSStatementEnd(source, start);
27
+ return statementEnd.reason === 'semicolon' ? statementEnd.end : -1;
28
+ }
29
+ function parseCSSImportSource(statement) {
30
+ const match = /^\s*@import\s+(?:(["'])(.*?)\1|url\(\s*(?:(["'])(.*?)\3|([^'")\s]+))\s*\))[^;]*;\s*$/s.exec(statement);
31
+ return match?.[2] || match?.[4] || match?.[5];
32
+ }
33
+ function findCSSImportStatements(source) {
34
+ const imports = [];
35
+ let quote = '';
36
+ let comment = false;
37
+ let depth = 0;
38
+ for(let index = 0; index < source.length; index++){
39
+ const char = source[index];
40
+ const next = source[index + 1];
41
+ if (comment) {
42
+ if (char === '*' && next === '/') {
43
+ comment = false;
44
+ index++;
45
+ }
46
+ continue;
47
+ }
48
+ if (quote) {
49
+ if (char === '\\') {
50
+ index++;
51
+ } else if (char === quote) {
52
+ quote = '';
53
+ }
54
+ continue;
55
+ }
56
+ if (char === '/' && next === '*') {
57
+ comment = true;
58
+ index++;
59
+ continue;
60
+ }
61
+ if (char === '"' || char === '\'') {
62
+ quote = char;
63
+ continue;
64
+ }
65
+ if (char === '{') {
66
+ depth++;
67
+ continue;
68
+ }
69
+ if (char === '}') {
70
+ depth--;
71
+ continue;
72
+ }
73
+ if (depth === 0 && source.startsWith('@import', index) && /\s/.test(source[index + '@import'.length] || '')) {
74
+ const end = findCSSImportEnd(source, index);
75
+ if (end === -1) continue;
76
+ imports.push({
77
+ start: index,
78
+ end,
79
+ statement: source.slice(index, end)
80
+ });
81
+ index = end - 1;
82
+ }
83
+ }
84
+ return imports;
85
+ }
86
+ function findMasterDirectiveEnd(source, start) {
87
+ const statementEnd = findCSSStatementEnd(source, start);
88
+ return statementEnd.reason === 'semicolon' ? statementEnd.end : -1;
89
+ }
90
+ function findMasterDirectiveStatements(source) {
91
+ const statements = [];
92
+ let quote = '';
93
+ let comment = false;
94
+ let depth = 0;
95
+ for(let index = 0; index < source.length; index++){
96
+ const char = source[index];
97
+ const next = source[index + 1];
98
+ if (comment) {
99
+ if (char === '*' && next === '/') {
100
+ comment = false;
101
+ index++;
102
+ }
103
+ continue;
104
+ }
105
+ if (quote) {
106
+ if (char === '\\') {
107
+ index++;
108
+ } else if (char === quote) {
109
+ quote = '';
110
+ }
111
+ continue;
112
+ }
113
+ if (char === '/' && next === '*') {
114
+ comment = true;
115
+ index++;
116
+ continue;
117
+ }
118
+ if (char === '"' || char === '\'') {
119
+ quote = char;
120
+ continue;
121
+ }
122
+ if (char === '{') {
123
+ depth++;
124
+ continue;
125
+ }
126
+ if (char === '}') {
127
+ depth--;
128
+ continue;
129
+ }
130
+ if (depth === 0 && source.startsWith('@master', index) && !isCSSIdentChar(source[index + '@master'.length])) {
131
+ const end = findMasterDirectiveEnd(source, index);
132
+ if (end === -1) continue;
133
+ const statement = source.slice(index, end);
134
+ if (statement.replace(/^@master\b/, '').replace(/;$/, '').trim()) {
135
+ index = end - 1;
136
+ continue;
137
+ }
138
+ statements.push({
139
+ start: index,
140
+ end,
141
+ name: ''
142
+ });
143
+ index = end - 1;
144
+ }
145
+ }
146
+ return statements;
147
+ }
148
+ function replaceMasterCSSImports(source, replacement) {
149
+ let replaced = false;
150
+ const code = source.replace(/@import\s+(?:url\(\s*)?(["'])([^"']+)\1\s*\)?[^;]*;/g, (rule, _quote, id)=>{
151
+ if (!isMasterCSSModuleId(id)) return rule;
152
+ replaced = true;
153
+ return replacement;
154
+ });
155
+ return {
156
+ code,
157
+ replaced
158
+ };
159
+ }
160
+ function hasMasterCSSImport(source) {
161
+ return replaceMasterCSSImports(source, '').replaced;
162
+ }
163
+ function hasMasterEntryDirective(source) {
164
+ return findMasterDirectiveStatements(source).some((statement)=>statement.name === '');
165
+ }
166
+ function hasMasterCSSManifestEntrypoint(source) {
167
+ return hasMasterEntryDirective(source) || hasMasterCSSImport(source);
168
+ }
169
+ function removeMasterDirectiveStatements(source) {
170
+ const statements = findMasterDirectiveStatements(source);
171
+ if (!statements.length) return {
172
+ code: source,
173
+ removed: false
174
+ };
175
+ let code = '';
176
+ let index = 0;
177
+ for (const statement of statements){
178
+ code += source.slice(index, statement.start);
179
+ index = statement.end;
180
+ }
181
+ code += source.slice(index);
182
+ return {
183
+ code,
184
+ removed: true
185
+ };
186
+ }
187
+
188
+ export { MASTER_CSS_PACKAGE_MODULE_IDS, createMasterCSSImportPattern, createMasterCSSManifestEntryPattern, findCSSImportEnd, findCSSImportStatements, findMasterDirectiveStatements, hasMasterCSSImport, hasMasterCSSManifestEntrypoint, hasMasterEntryDirective, isMasterCSSModuleId, normalizeMasterCSSModuleIds, parseCSSImportSource, removeMasterDirectiveStatements, replaceMasterCSSImports };
@@ -0,0 +1,29 @@
1
+ import { type SourceRange } from './source';
2
+ export type { CSSStatementEnd, SourceLocation, SourceRange } from './source';
3
+ export type CSSDirectiveRangeName = 'master' | 'settings' | 'source' | 'safelist' | 'blocklist' | 'preserve' | 'reference' | 'theme' | 'defaults' | 'components' | 'utilities' | 'custom-variant' | 'compose' | 'variant' | 'slot';
4
+ export interface CSSQuotedStringRange extends SourceRange {
5
+ quote: '"' | '\'';
6
+ contentRange: SourceRange;
7
+ }
8
+ export interface CSSDeclarationRange extends SourceRange {
9
+ propertyRange: SourceRange;
10
+ separatorRange: SourceRange;
11
+ valueRange: SourceRange;
12
+ terminatorRange?: SourceRange;
13
+ }
14
+ export interface CSSDirectiveRuleRange extends SourceRange {
15
+ name: CSSDirectiveRangeName;
16
+ keywordRange: SourceRange;
17
+ preludeRange: SourceRange;
18
+ blockRange?: SourceRange;
19
+ blockContentRange?: SourceRange;
20
+ blockCloseRange?: SourceRange;
21
+ semicolonRange?: SourceRange;
22
+ quotedStringRanges: CSSQuotedStringRange[];
23
+ depth: number;
24
+ origin: 'source-scan';
25
+ }
26
+ export declare const CSS_DIRECTIVE_RANGE_NAMES: readonly ["master", "settings", "source", "safelist", "blocklist", "preserve", "reference", "theme", "defaults", "components", "utilities", "custom-variant", "compose", "variant", "slot"];
27
+ export declare function collectCSSQuotedStringRanges(source: string, start?: number, end?: number): CSSQuotedStringRange[];
28
+ export declare function collectCSSDeclarationRanges(source: string, start: number, end: number): CSSDeclarationRange[];
29
+ export declare function collectCSSDirectiveRanges(source: string): CSSDirectiveRuleRange[];
@@ -0,0 +1,262 @@
1
+ import { readCSSIdent, findCSSStatementEnd, findCSSBlockEnd, findCSSClosingQuote, skipCSSWhitespace, isCSSIdentStart } from './source.mjs';
2
+
3
+ const CSS_DIRECTIVE_RANGE_NAMES = [
4
+ 'master',
5
+ 'settings',
6
+ 'source',
7
+ 'safelist',
8
+ 'blocklist',
9
+ 'preserve',
10
+ 'reference',
11
+ 'theme',
12
+ 'defaults',
13
+ 'components',
14
+ 'utilities',
15
+ 'custom-variant',
16
+ 'compose',
17
+ 'variant',
18
+ 'slot'
19
+ ];
20
+ const CSS_DIRECTIVE_VARIANT_SHORTHAND_NAME_SET = new Set([
21
+ 'dark',
22
+ 'light'
23
+ ]);
24
+ const CSS_DIRECTIVE_SOURCE_NAME_SET = new Set([
25
+ ...CSS_DIRECTIVE_RANGE_NAMES,
26
+ ...CSS_DIRECTIVE_VARIANT_SHORTHAND_NAME_SET
27
+ ]);
28
+ function collectCSSQuotedStringRanges(source, start = 0, end = source.length) {
29
+ const ranges = [];
30
+ let comment = false;
31
+ for(let index = start; index < end; index++){
32
+ const char = source[index];
33
+ const next = source[index + 1];
34
+ if (comment) {
35
+ if (char === '*' && next === '/') {
36
+ comment = false;
37
+ index++;
38
+ }
39
+ continue;
40
+ }
41
+ if (char === '/' && next === '*') {
42
+ comment = true;
43
+ index++;
44
+ continue;
45
+ }
46
+ if (char === '"' || char === '\'') {
47
+ const close = findCSSClosingQuote(source, index, char, end);
48
+ ranges.push({
49
+ start: index,
50
+ end: close + 1,
51
+ quote: char,
52
+ contentRange: {
53
+ start: index + 1,
54
+ end: Math.max(index + 1, close)
55
+ }
56
+ });
57
+ index = close;
58
+ }
59
+ }
60
+ return ranges;
61
+ }
62
+ function collectCSSDeclarationRanges(source, start, end) {
63
+ const ranges = [];
64
+ let quote = '';
65
+ let comment = false;
66
+ let depth = 0;
67
+ let blockDepth = 0;
68
+ let propertyStart = -1;
69
+ let colon = -1;
70
+ const flushDeclaration = (declarationEnd, terminatorRange)=>{
71
+ if (propertyStart === -1 || colon === -1) return;
72
+ const propertyText = source.slice(propertyStart, colon);
73
+ const propertyLeading = propertyText.search(/\S/);
74
+ if (propertyLeading === -1) return;
75
+ const propertyRangeStart = propertyStart + propertyLeading;
76
+ const propertyRangeEnd = propertyStart + propertyText.search(/\s*$/);
77
+ const valueStart = skipCSSWhitespace(source, colon + 1);
78
+ const rawValueEnd = source.slice(valueStart, declarationEnd).search(/\s*$/);
79
+ const valueEnd = rawValueEnd === -1 ? declarationEnd : valueStart + rawValueEnd;
80
+ ranges.push({
81
+ start: propertyRangeStart,
82
+ end: terminatorRange?.end ?? valueEnd,
83
+ propertyRange: {
84
+ start: propertyRangeStart,
85
+ end: propertyRangeEnd
86
+ },
87
+ separatorRange: {
88
+ start: colon,
89
+ end: colon + 1
90
+ },
91
+ valueRange: {
92
+ start: valueStart,
93
+ end: valueEnd
94
+ },
95
+ ...terminatorRange ? {
96
+ terminatorRange
97
+ } : {}
98
+ });
99
+ };
100
+ for(let index = start; index < end; index++){
101
+ const char = source[index];
102
+ const next = source[index + 1];
103
+ if (comment) {
104
+ if (char === '*' && next === '/') {
105
+ comment = false;
106
+ index++;
107
+ }
108
+ continue;
109
+ }
110
+ if (quote) {
111
+ if (char === '\\') {
112
+ index++;
113
+ } else if (char === quote) {
114
+ quote = '';
115
+ }
116
+ continue;
117
+ }
118
+ if (char === '/' && next === '*') {
119
+ comment = true;
120
+ index++;
121
+ continue;
122
+ }
123
+ if (char === '"' || char === '\'') {
124
+ quote = char;
125
+ continue;
126
+ }
127
+ if (char === '(' || char === '[') {
128
+ depth++;
129
+ continue;
130
+ }
131
+ if (char === ')' || char === ']') {
132
+ depth = Math.max(0, depth - 1);
133
+ continue;
134
+ }
135
+ if (char === '{') {
136
+ propertyStart = -1;
137
+ colon = -1;
138
+ blockDepth++;
139
+ continue;
140
+ }
141
+ if (char === '}') {
142
+ propertyStart = -1;
143
+ colon = -1;
144
+ blockDepth = Math.max(0, blockDepth - 1);
145
+ continue;
146
+ }
147
+ if (blockDepth !== 0) continue;
148
+ if (depth !== 0) continue;
149
+ if (propertyStart === -1 && isCSSIdentStart(char)) {
150
+ propertyStart = index;
151
+ }
152
+ if (char === ':' && colon === -1 && propertyStart !== -1) {
153
+ colon = index;
154
+ continue;
155
+ }
156
+ if (char === ';') {
157
+ flushDeclaration(index, {
158
+ start: index,
159
+ end: index + 1
160
+ });
161
+ propertyStart = -1;
162
+ colon = -1;
163
+ }
164
+ }
165
+ flushDeclaration(end);
166
+ return ranges;
167
+ }
168
+ function collectCSSDirectiveRanges(source) {
169
+ const ranges = [];
170
+ let quote = '';
171
+ let comment = false;
172
+ let depth = 0;
173
+ for(let index = 0; index < source.length; index++){
174
+ const char = source[index];
175
+ const next = source[index + 1];
176
+ if (comment) {
177
+ if (char === '*' && next === '/') {
178
+ comment = false;
179
+ index++;
180
+ }
181
+ continue;
182
+ }
183
+ if (quote) {
184
+ if (char === '\\') {
185
+ index++;
186
+ } else if (char === quote) {
187
+ quote = '';
188
+ }
189
+ continue;
190
+ }
191
+ if (char === '/' && next === '*') {
192
+ comment = true;
193
+ index++;
194
+ continue;
195
+ }
196
+ if (char === '"' || char === '\'') {
197
+ quote = char;
198
+ continue;
199
+ }
200
+ if (char === '{') {
201
+ depth++;
202
+ continue;
203
+ }
204
+ if (char === '}') {
205
+ depth = Math.max(0, depth - 1);
206
+ continue;
207
+ }
208
+ if (char !== '@') continue;
209
+ const name = readCSSIdent(source, index + 1);
210
+ if (!CSS_DIRECTIVE_SOURCE_NAME_SET.has(name.value)) continue;
211
+ const statementEnd = findCSSStatementEnd(source, name.end);
212
+ if (name.value === 'master' && (statementEnd.reason !== 'semicolon' || source.slice(name.end, statementEnd.end - 1).trim())) {
213
+ continue;
214
+ }
215
+ const blockStart = statementEnd.reason === 'block' ? statementEnd.end : -1;
216
+ const blockEnd = blockStart === -1 ? -1 : findCSSBlockEnd(source, blockStart);
217
+ const end = blockStart === -1 ? statementEnd.end : blockEnd === -1 ? source.length : blockEnd + 1;
218
+ const preludeEnd = blockStart === -1 ? statementEnd.reason === 'semicolon' ? statementEnd.end - 1 : statementEnd.end : blockStart;
219
+ const semicolonRange = statementEnd.reason === 'semicolon' ? statementEnd.delimiterRange : undefined;
220
+ ranges.push({
221
+ start: index,
222
+ end,
223
+ name: CSS_DIRECTIVE_VARIANT_SHORTHAND_NAME_SET.has(name.value) ? 'variant' : name.value,
224
+ keywordRange: {
225
+ start: index,
226
+ end: name.end
227
+ },
228
+ preludeRange: {
229
+ start: name.end,
230
+ end: preludeEnd
231
+ },
232
+ ...blockStart !== -1 ? {
233
+ blockRange: {
234
+ start: blockStart,
235
+ end
236
+ },
237
+ blockContentRange: {
238
+ start: blockStart + 1,
239
+ end: blockEnd === -1 ? end : blockEnd
240
+ },
241
+ ...blockEnd === -1 ? {} : {
242
+ blockCloseRange: {
243
+ start: blockEnd,
244
+ end: blockEnd + 1
245
+ }
246
+ }
247
+ } : {},
248
+ ...semicolonRange ? {
249
+ semicolonRange
250
+ } : {},
251
+ quotedStringRanges: collectCSSQuotedStringRanges(source, name.end, preludeEnd),
252
+ depth,
253
+ origin: 'source-scan'
254
+ });
255
+ if (blockStart === -1) {
256
+ index = Math.max(index, end - 1);
257
+ }
258
+ }
259
+ return ranges;
260
+ }
261
+
262
+ export { CSS_DIRECTIVE_RANGE_NAMES, collectCSSDeclarationRanges, collectCSSDirectiveRanges, collectCSSQuotedStringRanges };
@@ -0,0 +1,5 @@
1
+ export * from './class';
2
+ export * from './css-manifest-entry';
3
+ export * from './directive-ranges';
4
+ export { createSourceLocationResolver, cssEscape, escapeRegExp, findCSSBlockEnd, findCSSClosingQuote, findCSSStatementEnd, isCSSIdentChar, isCSSIdentStart, readCSSIdent, removeSourceRanges, replaceSourceRanges, skipCSSWhitespace, type CSSStatementEnd, type SourceLocation, type SourceRange } from './source';
5
+ export * from './units';
package/dist/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ export { collectMasterCSSClassListTokenRanges, findMasterCSSClosingParen, findMasterCSSClosingQuote, pushMasterCSSLexicalToken, splitMasterCSSTopLevel, tokenizeMasterCSSAtQuery, tokenizeMasterCSSGroupedClassToken, tokenizeMasterCSSState, tokenizeMasterCSSValue } from './class.mjs';
2
+ export { MASTER_CSS_PACKAGE_MODULE_IDS, createMasterCSSImportPattern, createMasterCSSManifestEntryPattern, findCSSImportEnd, findCSSImportStatements, findMasterDirectiveStatements, hasMasterCSSImport, hasMasterCSSManifestEntrypoint, hasMasterEntryDirective, isMasterCSSModuleId, normalizeMasterCSSModuleIds, parseCSSImportSource, removeMasterDirectiveStatements, replaceMasterCSSImports } from './css-manifest-entry.mjs';
3
+ export { CSS_DIRECTIVE_RANGE_NAMES, collectCSSDeclarationRanges, collectCSSDirectiveRanges, collectCSSQuotedStringRanges } from './directive-ranges.mjs';
4
+ export { createSourceLocationResolver, cssEscape, escapeRegExp, findCSSBlockEnd, findCSSClosingQuote, findCSSStatementEnd, isCSSIdentChar, isCSSIdentStart, readCSSIdent, removeSourceRanges, replaceSourceRanges, skipCSSWhitespace } from './source.mjs';
5
+ export { MASTER_CSS_VALUE_UNITS, MASTER_CSS_VALUE_UNIT_PATTERN } from './units.mjs';
@@ -0,0 +1,31 @@
1
+ export interface SourceRange {
2
+ start: number;
3
+ end: number;
4
+ }
5
+ export interface SourceLocation {
6
+ line: number;
7
+ column: number;
8
+ }
9
+ export interface CSSStatementEnd {
10
+ end: number;
11
+ reason: 'semicolon' | 'block' | 'eof';
12
+ delimiterRange?: SourceRange;
13
+ }
14
+ export declare function escapeRegExp(source: string): string;
15
+ export declare function cssEscape(value: string): string;
16
+ export declare function isCSSIdentChar(char: string | undefined): boolean;
17
+ export declare function isCSSIdentStart(char: string | undefined): boolean;
18
+ export declare function skipCSSWhitespace(source: string, index: number): number;
19
+ export declare function readCSSIdent(source: string, index: number): {
20
+ start: number;
21
+ end: number;
22
+ value: string;
23
+ };
24
+ export declare function createSourceLocationResolver(source: string): (loc: SourceLocation | undefined) => number;
25
+ export declare function findCSSStatementEnd(source: string, start: number): CSSStatementEnd;
26
+ export declare function findCSSClosingQuote(source: string, start: number, quote: string, limit?: number): number;
27
+ export declare function findCSSBlockEnd(source: string, open: number): number;
28
+ export declare function removeSourceRanges(source: string, ranges: SourceRange[]): string;
29
+ export declare function replaceSourceRanges(source: string, ranges: (SourceRange & {
30
+ replacement: string;
31
+ })[]): string;