@master/css-language-service 0.0.0-site.1793659883.g69c9aebcf628

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.
Files changed (44) hide show
  1. package/README.md +131 -0
  2. package/dist/common.d.ts +2 -0
  3. package/dist/common.js +1 -0
  4. package/dist/core.d.ts +34 -0
  5. package/dist/core.js +81 -0
  6. package/dist/document-analysis.d.ts +16 -0
  7. package/dist/document-analysis.js +34 -0
  8. package/dist/features/edit-syntax-colors.d.ts +5 -0
  9. package/dist/features/edit-syntax-colors.js +36 -0
  10. package/dist/features/format-directives.d.ts +5 -0
  11. package/dist/features/format-directives.js +60 -0
  12. package/dist/features/inspect-syntax.d.ts +5 -0
  13. package/dist/features/inspect-syntax.js +48 -0
  14. package/dist/features/render-semantic-tokens.d.ts +6 -0
  15. package/dist/features/render-semantic-tokens.js +36 -0
  16. package/dist/features/render-syntax-colors.d.ts +5 -0
  17. package/dist/features/render-syntax-colors.js +47 -0
  18. package/dist/features/suggest-syntax.d.ts +5 -0
  19. package/dist/features/suggest-syntax.js +165 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +8 -0
  22. package/dist/settings.d.ts +21 -0
  23. package/dist/settings.js +64 -0
  24. package/dist/shiki/hast.d.ts +13 -0
  25. package/dist/shiki/hast.js +174 -0
  26. package/dist/shiki/languages.d.ts +8 -0
  27. package/dist/shiki/languages.js +69 -0
  28. package/dist/shiki.d.ts +159 -0
  29. package/dist/shiki.js +522 -0
  30. package/dist/utils/beautify-css.d.ts +2 -0
  31. package/dist/utils/beautify-css.js +12 -0
  32. package/dist/utils/convert-color-token.d.ts +4 -0
  33. package/dist/utils/convert-color-token.js +31 -0
  34. package/dist/utils/create-css-markdown-documentation.d.ts +6 -0
  35. package/dist/utils/create-css-markdown-documentation.js +12 -0
  36. package/dist/utils/create-doc.d.ts +17 -0
  37. package/dist/utils/create-doc.js +20 -0
  38. package/dist/utils/get-position.d.ts +2 -0
  39. package/dist/utils/get-position.js +8 -0
  40. package/dist/utils/get-range.d.ts +3 -0
  41. package/dist/utils/get-range.js +20 -0
  42. package/package.json +60 -0
  43. package/src/shiki.css +7 -0
  44. package/syntaxes/master-css.tmLanguage.json +881 -0
@@ -0,0 +1,159 @@
1
+ import { HighlightTokenRole, MasterCSSLanguageSession, SemanticTokenModifier, SemanticTokenType } from "@master/css-tooling/language";
2
+ import { MasterCSSLanguageServiceSettings } from "./settings.js";
3
+ import { MasterCSSManifest } from "@master/css-schema/manifest";
4
+ import { getMasterCSSShikiLanguageId, isMasterCSSClassListLanguage, isMasterCSSShikiSupportedLanguage } from "./shiki/languages.js";
5
+ declare const MASTER_CSS_SHIKI_SCOPE_NAME = "master-css.directive.injection";
6
+ declare const MASTER_CSS_SHIKI_INJECT_TO: readonly ['source.css', 'source.css.scss', 'source.css.less', 'source.css.postcss'];
7
+ interface MasterCSSTextMateGrammar {
8
+ name: string;
9
+ scopeName: string;
10
+ injectionSelector: string;
11
+ patterns: any[];
12
+ repository: Record<string, any>;
13
+ [key: string]: any;
14
+ }
15
+ declare const MASTER_CSS_TEXTMATE_GRAMMAR: MasterCSSTextMateGrammar;
16
+ declare function createMasterCSSShikiLanguageRegistration(): MasterCSSTextMateGrammar & {
17
+ injectTo: string[];
18
+ };
19
+ declare const masterCSSShikiLanguage: MasterCSSTextMateGrammar & {
20
+ injectTo: string[];
21
+ };
22
+ type MasterCSSShikiSemanticTokenStyleKey = SemanticTokenType | `${SemanticTokenType}.${SemanticTokenModifier}`;
23
+ type MasterCSSShikiHighlightRoleStyleKey = HighlightTokenRole;
24
+ type MasterCSSShikiSemanticTokenStyle = string | Record<string, string>;
25
+ type MasterCSSShikiSemanticTokenStyles = Partial<Record<MasterCSSShikiSemanticTokenStyleKey, MasterCSSShikiSemanticTokenStyle>>;
26
+ type MasterCSSShikiHighlightRoleStyles = Partial<Record<MasterCSSShikiHighlightRoleStyleKey, MasterCSSShikiSemanticTokenStyle>>;
27
+ interface ShikiToken {
28
+ content: string;
29
+ offset: number;
30
+ color?: string;
31
+ htmlStyle?: Record<string, string>;
32
+ htmlAttrs?: Record<string, unknown>;
33
+ explanation?: {
34
+ content?: string;
35
+ scopes?: {
36
+ scopeName: string;
37
+ }[];
38
+ }[];
39
+ [key: string]: unknown;
40
+ }
41
+ interface ShikiPosition {
42
+ line: number;
43
+ character: number;
44
+ }
45
+ interface ShikiDecoration {
46
+ start: number | ShikiPosition;
47
+ end: number | ShikiPosition;
48
+ tagName?: string;
49
+ properties?: Record<string, unknown>;
50
+ alwaysWrap?: boolean;
51
+ transform?: (element: unknown, type: 'wrapper' | 'line' | 'token') => unknown | undefined;
52
+ }
53
+ interface ShikiCodeToHastOptions {
54
+ lang?: string;
55
+ decorations?: ShikiDecoration[];
56
+ includeExplanation?: 'scopeName' | boolean;
57
+ [key: string]: unknown;
58
+ }
59
+ interface ShikiCodeToTokensResult {
60
+ tokens: ShikiToken[][];
61
+ }
62
+ interface ShikiTransformerContext {
63
+ source: string;
64
+ options: ShikiCodeToHastOptions;
65
+ codeToTokens?: (code: string, options: ShikiCodeToHastOptions) => ShikiCodeToTokensResult;
66
+ }
67
+ interface ShikiTransformer {
68
+ name: string;
69
+ enforce: 'post';
70
+ tokens(this: ShikiTransformerContext, tokens: ShikiToken[][]): ShikiToken[][] | undefined;
71
+ line?(this: ShikiTransformerContext, line: object, lineNumber: number): void;
72
+ root?(this: ShikiTransformerContext, root: unknown): unknown | undefined;
73
+ }
74
+ interface MasterCSSShikiDecoration extends Omit<ShikiDecoration, 'start' | 'end'> {
75
+ start: number;
76
+ end: number;
77
+ type: SemanticTokenType;
78
+ role: HighlightTokenRole;
79
+ modifiers: SemanticTokenModifier[];
80
+ }
81
+ interface MasterCSSShikiClassAttributeValueWrapperOptions {
82
+ tagName?: string;
83
+ properties?: Record<string, unknown>;
84
+ transform?: ShikiDecoration['transform'];
85
+ }
86
+ interface MasterCSSShikiOptions {
87
+ /**
88
+ * Reuse caller-owned language capabilities. Full tooling sessions also work;
89
+ * the transformer never disposes a supplied session.
90
+ */
91
+ session?: Pick<MasterCSSLanguageSession, 'analyzeDocument' | 'classifyClassNames'>;
92
+ /**
93
+ * Class-position and manifest settings used when collecting embedded utilities.
94
+ */
95
+ settings?: MasterCSSLanguageServiceSettings;
96
+ /**
97
+ * Convenience shortcut for `settings.manifest`.
98
+ */
99
+ manifest?: MasterCSSManifest;
100
+ /**
101
+ * Shiki language id. Defaults to the `lang` passed to Shiki.
102
+ */
103
+ lang?: string;
104
+ /**
105
+ * Treat the whole source as a whitespace-separated Master CSS class list.
106
+ * This replaces the old standalone `.mcss` TextMate grammar use case
107
+ * without contributing a Master CSS language.
108
+ */
109
+ classList?: boolean;
110
+ /**
111
+ * Class prefix added to every semantic decoration.
112
+ *
113
+ * @default 'mcss-semantic'
114
+ */
115
+ classPrefix?: string;
116
+ /**
117
+ * Add `data-semantic-token-*` attributes to decorated spans.
118
+ *
119
+ * @default true
120
+ */
121
+ dataAttributes?: boolean;
122
+ /**
123
+ * Force Shiki to wrap semantic tokens instead of reusing the existing
124
+ * TextMate token span when possible.
125
+ */
126
+ alwaysWrap?: boolean;
127
+ /**
128
+ * Wrap each embedded class attribute/string value with its original host
129
+ * language token presentation while keeping Master CSS semantic spans nested
130
+ * inside it.
131
+ *
132
+ * @default true
133
+ */
134
+ classAttributeValueWrapper?: boolean | MasterCSSShikiClassAttributeValueWrapperOptions;
135
+ /**
136
+ * Inline styles applied by semantic token type or type.modifier. These
137
+ * styles intentionally replace the underlying TextMate token style for the
138
+ * decorated range, which keeps tokens such as `block` and `block:hover`
139
+ * visually aligned when they share the same semantic type.
140
+ */
141
+ semanticTokenStyles?: MasterCSSShikiSemanticTokenStyles;
142
+ /**
143
+ * Inline styles applied by CSS-native highlight role. Role styles are the
144
+ * most precise presentation override and are shared across host languages.
145
+ */
146
+ highlightRoleStyles?: MasterCSSShikiHighlightRoleStyles;
147
+ /**
148
+ * Resolve Master CSS semantic token scopes through the active Shiki
149
+ * theme when possible. When disabled, the transformer only splits tokens
150
+ * and attaches semantic metadata/classes unless explicit semantic styles
151
+ * are provided.
152
+ *
153
+ * @default true
154
+ */
155
+ matchCSSSyntaxStyles?: boolean;
156
+ }
157
+ declare function createMasterCSSShikiDecorations(code: string, options?: MasterCSSShikiOptions): MasterCSSShikiDecoration[];
158
+ declare function transformerMasterCSS(options?: MasterCSSShikiOptions): ShikiTransformer;
159
+ export { MASTER_CSS_SHIKI_INJECT_TO, MASTER_CSS_SHIKI_SCOPE_NAME, MASTER_CSS_TEXTMATE_GRAMMAR, MasterCSSShikiClassAttributeValueWrapperOptions, MasterCSSShikiDecoration, MasterCSSShikiHighlightRoleStyleKey, MasterCSSShikiHighlightRoleStyles, MasterCSSShikiOptions, MasterCSSShikiSemanticTokenStyle, MasterCSSShikiSemanticTokenStyleKey, MasterCSSShikiSemanticTokenStyles, MasterCSSTextMateGrammar, createMasterCSSShikiDecorations, createMasterCSSShikiLanguageRegistration, getMasterCSSShikiLanguageId, isMasterCSSClassListLanguage, isMasterCSSShikiSupportedLanguage, masterCSSShikiLanguage, transformerMasterCSS };
package/dist/shiki.js ADDED
@@ -0,0 +1,522 @@
1
+ import { applyClassAttributeValueWrappers } from "./shiki/hast.js";
2
+ import { classAttributeValueWrappersKey, cssDirectiveLanguageIds, getLanguageServiceLanguageId, getMasterCSSShikiLanguageId, isMasterCSSClassListLanguage, isMasterCSSShikiSupportedLanguage, languageServiceLanguageIds } from "./shiki/languages.js";
3
+ import { MASTER_CSS_SEMANTIC_TOKEN_SCOPE_MAP, getMasterCSSSemanticTokenScopeKeys } from "@master/css-tooling/language";
4
+ import masterCSSTextMateGrammar from "../syntaxes/master-css.tmLanguage.json" with { type: "json" };
5
+ import { createLanguageSessionSync } from "@master/css-tooling/language/node";
6
+ import defaultManifestJSON from "@master/css-preset/default-manifest.json" with { type: "json" };
7
+ // src/shiki.ts
8
+ const defaultManifest = defaultManifestJSON;
9
+ const MASTER_CSS_SHIKI_SCOPE_NAME = "master-css.directive.injection";
10
+ const MASTER_CSS_SHIKI_INJECT_TO = [
11
+ "source.css",
12
+ "source.css.scss",
13
+ "source.css.less",
14
+ "source.css.postcss"
15
+ ];
16
+ const MASTER_CSS_TEXTMATE_GRAMMAR = masterCSSTextMateGrammar;
17
+ function createMasterCSSShikiLanguageRegistration() {
18
+ return {
19
+ ...MASTER_CSS_TEXTMATE_GRAMMAR,
20
+ injectTo: [...MASTER_CSS_SHIKI_INJECT_TO]
21
+ };
22
+ }
23
+ const masterCSSShikiLanguage = createMasterCSSShikiLanguageRegistration();
24
+ function createShikiSession(options) {
25
+ return options.session || createLanguageSessionSync({ manifest: options.manifest ?? options.settings?.manifest ?? defaultManifest });
26
+ }
27
+ function stringifyStyle(style) {
28
+ if (typeof style === "string") return style;
29
+ return Object.entries(style).map(([property, value]) => `${property}:${value}`).join(";");
30
+ }
31
+ function resolveSemanticTokenStyle(type, modifiers, styles) {
32
+ if (!styles) return;
33
+ const matchedStyles = [styles[type], ...modifiers.map((modifier) => styles[`${type}.${modifier}`])].filter((style) => Boolean(style));
34
+ if (!matchedStyles.length) return;
35
+ return matchedStyles.map(stringifyStyle).join(";");
36
+ }
37
+ function resolveHighlightTokenStyle(item, options) {
38
+ const roleStyle = options.highlightRoleStyles?.[item.role];
39
+ if (roleStyle) return stringifyStyle(roleStyle);
40
+ return resolveSemanticTokenStyle(item.type, item.modifiers ?? [], options.semanticTokenStyles);
41
+ }
42
+ function splitToken(token, breakpoints) {
43
+ const tokens = [];
44
+ let lastOffset = 0;
45
+ for (const offset of breakpoints) {
46
+ if (offset > lastOffset) tokens.push({
47
+ ...token,
48
+ content: token.content.slice(lastOffset, offset),
49
+ offset: token.offset + lastOffset
50
+ });
51
+ lastOffset = offset;
52
+ }
53
+ if (lastOffset < token.content.length) tokens.push({
54
+ ...token,
55
+ content: token.content.slice(lastOffset),
56
+ offset: token.offset + lastOffset
57
+ });
58
+ return tokens;
59
+ }
60
+ function splitTokensAtOffsets(tokens, breakpoints) {
61
+ const sortedBreakpoints = [...new Set(breakpoints)].sort((a, b) => a - b);
62
+ if (!sortedBreakpoints.length) return tokens;
63
+ return tokens.map((line) => {
64
+ return line.flatMap((token) => {
65
+ const breakpointsInToken = sortedBreakpoints.filter((offset) => token.offset < offset && offset < token.offset + token.content.length).map((offset) => offset - token.offset);
66
+ return breakpointsInToken.length ? splitToken(token, breakpointsInToken) : token;
67
+ });
68
+ });
69
+ }
70
+ function parseStyleProperty(style) {
71
+ if (typeof style === "string") return Object.fromEntries(style.split(";").map((declaration) => declaration.split(":")).filter(([property, value]) => property && value).map(([property, ...value]) => [property.trim(), value.join(":").trim()]));
72
+ if (style && typeof style === "object" && !Array.isArray(style)) return Object.fromEntries(Object.entries(style).filter((entry) => typeof entry[1] === "string"));
73
+ }
74
+ function resolveClassNames(value) {
75
+ if (Array.isArray(value)) return value.flatMap(resolveClassNames);
76
+ return typeof value === "string" ? value.split(/\s+/).filter(Boolean) : [];
77
+ }
78
+ function mergeClassProperty(current, next) {
79
+ return [...resolveClassNames(current), ...resolveClassNames(next)].join(" ");
80
+ }
81
+ function applySemanticDecorationToToken(token, decorations, resolveSyntaxStyle) {
82
+ const tokenStart = token.offset;
83
+ const tokenEnd = token.offset + token.content.length;
84
+ const matchedDecorations = decorations.filter(({ start, end }) => start <= tokenStart && tokenEnd <= end);
85
+ if (!matchedDecorations.length) return token;
86
+ const htmlAttrs = { ...token.htmlAttrs };
87
+ const htmlStyle = { ...token.htmlStyle };
88
+ for (const decoration of matchedDecorations) {
89
+ const properties = decoration.properties ?? {};
90
+ if (properties.class) htmlAttrs.class = mergeClassProperty(htmlAttrs.class, properties.class);
91
+ const style = parseStyleProperty(properties.style);
92
+ if (style) {
93
+ for (const key of Object.keys(htmlStyle)) delete htmlStyle[key];
94
+ Object.assign(htmlStyle, style);
95
+ } else {
96
+ const syntaxStyle = resolveSyntaxStyle?.(token, decoration, decorations);
97
+ if (syntaxStyle) {
98
+ for (const key of Object.keys(htmlStyle)) delete htmlStyle[key];
99
+ Object.assign(htmlStyle, syntaxStyle);
100
+ }
101
+ }
102
+ for (const [name, value] of Object.entries(properties)) {
103
+ if (name === "class" || name === "style" || value === void 0) continue;
104
+ htmlAttrs[name] = typeof value === "string" ? value : String(value);
105
+ }
106
+ }
107
+ return {
108
+ ...token,
109
+ htmlAttrs,
110
+ htmlStyle
111
+ };
112
+ }
113
+ function cloneStyle(style) {
114
+ return style && typeof style === "object" && !Array.isArray(style) ? Object.fromEntries(Object.entries(style).filter((entry) => typeof entry[1] === "string")) : void 0;
115
+ }
116
+ function getTokenStyleObject(token) {
117
+ const htmlStyle = cloneStyle(token.htmlStyle);
118
+ if (htmlStyle) return htmlStyle;
119
+ return typeof token.color === "string" ? { color: token.color } : void 0;
120
+ }
121
+ const SEMANTIC_SCOPE_STYLE_PROBE = [
122
+ "@theme {",
123
+ " :root { --token: var(--value); }",
124
+ "}",
125
+ "@mixin --btn {",
126
+ " display: block; &:hover { color: red; }",
127
+ "}",
128
+ ".x, div > li:hover::before {",
129
+ " color: red !important;",
130
+ " width: 1.5rem;",
131
+ " background: rgb(0 0 0 / .5);",
132
+ " content: \"x\";",
133
+ " content: 'x';",
134
+ " --token: red;",
135
+ "}",
136
+ "@media (width >= 1px) { .y { color: var(--token); } }"
137
+ ].join("\n");
138
+ function normalizeScope(scope) {
139
+ let normalizedScope = scope;
140
+ while (normalizedScope.endsWith(".css") || normalizedScope.endsWith(".master-css")) normalizedScope = normalizedScope.replace(/\.css$/, "").replace(/\.master-css$/, "");
141
+ return normalizedScope;
142
+ }
143
+ function getScopeCandidates(scope) {
144
+ return [.../* @__PURE__ */ new Set([
145
+ scope,
146
+ scope.replace(/\.css$/, ""),
147
+ scope.replace(/\.master-css$/, ""),
148
+ normalizeScope(scope)
149
+ ])];
150
+ }
151
+ function getScopePrefixes(scope) {
152
+ const prefixes = [];
153
+ const bases = [
154
+ scope,
155
+ scope.replace(/\.css$/, ""),
156
+ scope.replace(/\.master-css$/, ""),
157
+ normalizeScope(scope)
158
+ ];
159
+ for (const base of bases) {
160
+ const segments = base.split(".");
161
+ for (let length = segments.length; length >= 2; length--) prefixes.push(segments.slice(0, length).join("."));
162
+ }
163
+ return [...new Set(prefixes)];
164
+ }
165
+ function collectScopeStyleEntries(tokens) {
166
+ const entries = [];
167
+ const entryIndexes = /* @__PURE__ */ new Map();
168
+ for (const token of tokens) {
169
+ const style = getTokenStyleObject(token);
170
+ if (!style) continue;
171
+ for (const explanation of token.explanation ?? []) {
172
+ if (explanation.content && !token.content.includes(explanation.content)) continue;
173
+ const scopes = explanation.scopes ?? [];
174
+ const stringDelimiter = scopes.some(({ scopeName }) => scopeName.startsWith("punctuation.definition.string."));
175
+ for (const { scopeName } of scopes) {
176
+ const existingIndex = entryIndexes.get(scopeName);
177
+ if (existingIndex !== void 0 && !(scopeName.startsWith("string.quoted.") && entries[existingIndex].stringDelimiter && !stringDelimiter)) continue;
178
+ const entry = {
179
+ scope: scopeName,
180
+ normalizedScope: normalizeScope(scopeName),
181
+ style,
182
+ stringDelimiter
183
+ };
184
+ if (existingIndex === void 0) {
185
+ entryIndexes.set(scopeName, entries.length);
186
+ entries.push(entry);
187
+ } else entries[existingIndex] = entry;
188
+ }
189
+ }
190
+ }
191
+ return entries;
192
+ }
193
+ function isQuotedStringDecoration(decoration) {
194
+ return decoration.type === "string" && decoration.modifiers.includes("quoted");
195
+ }
196
+ function getQuotedStringContext(source, decoration, decorations) {
197
+ const index = decorations.indexOf(decoration);
198
+ if (index < 0) return;
199
+ let first = index;
200
+ let last = index;
201
+ while (first > 0 && isQuotedStringDecoration(decorations[first - 1]) && decorations[first - 1].end === decorations[first].start) first--;
202
+ while (last + 1 < decorations.length && isQuotedStringDecoration(decorations[last + 1]) && decorations[last].end === decorations[last + 1].start) last++;
203
+ const quote = source[decorations[first].start];
204
+ if (quote !== "\"" && quote !== "'") return;
205
+ return {
206
+ kind: quote === "\"" ? "double" : "single",
207
+ delimiter: index === first || index === last,
208
+ closing: index === last && index !== first
209
+ };
210
+ }
211
+ function findStyleByScope(entries, scope) {
212
+ for (const candidate of getScopeCandidates(scope)) {
213
+ const exact = entries.find((entry) => entry.scope === candidate || entry.normalizedScope === candidate);
214
+ if (exact) return exact.style;
215
+ }
216
+ for (const prefix of getScopePrefixes(scope)) {
217
+ const prefixed = entries.find((entry) => entry.scope === prefix || entry.normalizedScope === prefix || entry.scope.startsWith(`${prefix}.`) || entry.normalizedScope.startsWith(`${prefix}.`));
218
+ if (prefixed) return prefixed.style;
219
+ }
220
+ }
221
+ function createSemanticScopeStyleResolver(context, options) {
222
+ if (options.matchCSSSyntaxStyles === false || typeof context.codeToTokens !== "function") return;
223
+ try {
224
+ const { lang: _lang, decorations: _decorations, transformers: _transformers, ...tokenOptions } = context.options;
225
+ const scopeStyleEntries = collectScopeStyleEntries(context.codeToTokens(SEMANTIC_SCOPE_STYLE_PROBE, {
226
+ ...tokenOptions,
227
+ lang: "css",
228
+ includeExplanation: "scopeName"
229
+ }).tokens.flat());
230
+ return (_token, decoration, decorations) => {
231
+ if (isQuotedStringDecoration(decoration)) {
232
+ const quoted = getQuotedStringContext(context.source, decoration, decorations);
233
+ if (quoted) {
234
+ const scope = quoted.delimiter ? `punctuation.definition.string.${quoted.closing ? "end" : "begin"}.css` : `string.quoted.${quoted.kind}.css`;
235
+ const style = findStyleByScope(scopeStyleEntries, scope);
236
+ if (style) return style;
237
+ }
238
+ }
239
+ for (const key of getMasterCSSSemanticTokenScopeKeys(decoration.type, decoration.modifiers)) for (const scope of MASTER_CSS_SEMANTIC_TOKEN_SCOPE_MAP[key]) {
240
+ const style = findStyleByScope(scopeStyleEntries, scope);
241
+ if (style) return style;
242
+ }
243
+ };
244
+ } catch {
245
+ return;
246
+ }
247
+ }
248
+ function createSemanticTokenDecorations(tokens, options) {
249
+ const classPrefix = options.classPrefix ?? "mcss-semantic";
250
+ const includeDataAttributes = options.dataAttributes ?? true;
251
+ return tokens.map(({ start, end, type, modifiers: semanticModifiers = [], role: semanticRole }) => {
252
+ const role = semanticRole ?? resolveHighlightRole({
253
+ type,
254
+ modifiers: semanticModifiers
255
+ });
256
+ const modifiers = semanticModifiers.filter((modifier) => modifier !== ROLE_TOKEN_MODIFIERS[role] && !(modifier === "declaration" && type !== "class"));
257
+ const classNames = [
258
+ classPrefix,
259
+ `${classPrefix}-${type}`,
260
+ `${classPrefix}-role-${role.replace(/\./g, "-")}`,
261
+ ...modifiers.map((modifier) => `${classPrefix}-${type}-${modifier}`)
262
+ ];
263
+ const style = resolveHighlightTokenStyle({
264
+ type,
265
+ role,
266
+ modifiers
267
+ }, options);
268
+ return {
269
+ start,
270
+ end,
271
+ type,
272
+ role,
273
+ modifiers,
274
+ alwaysWrap: options.alwaysWrap,
275
+ properties: {
276
+ class: classNames,
277
+ ...style ? { style } : void 0,
278
+ ...includeDataAttributes ? {
279
+ "data-semantic-token-type": type,
280
+ "data-semantic-token-modifiers": modifiers.join(" "),
281
+ "data-highlight-role": role
282
+ } : void 0
283
+ }
284
+ };
285
+ });
286
+ }
287
+ function getClassAttributeValueWrapperOption(options) {
288
+ return options.classAttributeValueWrapper ?? true;
289
+ }
290
+ function shouldCreateClassAttributeValueWrappers(options) {
291
+ if (!getClassAttributeValueWrapperOption(options) || options.classList) return false;
292
+ const lang = options.lang ? languageServiceLanguageIds[options.lang] ?? options.lang : void 0;
293
+ return Boolean(lang && !cssDirectiveLanguageIds.has(lang));
294
+ }
295
+ function getLineStartOffsets(source) {
296
+ const offsets = [0];
297
+ for (let index = 0; index < source.length; index++) if (source[index] === "\n") offsets.push(index + 1);
298
+ return offsets;
299
+ }
300
+ function resolvePositionOffset(source, lineStartOffsets, position) {
301
+ if (typeof position === "number") return position >= 0 && position <= source.length ? position : void 0;
302
+ const lineStart = lineStartOffsets[position.line];
303
+ if (lineStart === void 0) return;
304
+ const nextLineStart = lineStartOffsets[position.line + 1];
305
+ const lineEnd = nextLineStart === void 0 ? source.length : nextLineStart - 1;
306
+ let character = position.character;
307
+ if (character < 0) character = lineEnd - lineStart + character;
308
+ if (character < 0 || lineStart + character > lineEnd) return;
309
+ return lineStart + character;
310
+ }
311
+ function resolveDecorationRange(source, lineStartOffsets, decoration) {
312
+ const start = resolvePositionOffset(source, lineStartOffsets, decoration.start);
313
+ const end = resolvePositionOffset(source, lineStartOffsets, decoration.end);
314
+ return start === void 0 || end === void 0 ? void 0 : {
315
+ start,
316
+ end
317
+ };
318
+ }
319
+ function decorationsPartiallyIntersect(a, b) {
320
+ const isAHasBStart = a.start <= b.start && b.start < a.end;
321
+ const isAHasBEnd = a.start < b.end && b.end <= a.end;
322
+ const isBHasAStart = b.start <= a.start && a.start < b.end;
323
+ const isBHasAEnd = b.start < a.end && a.end <= b.end;
324
+ if (!isAHasBStart && !isAHasBEnd && !isBHasAStart && !isBHasAEnd) return false;
325
+ if (isAHasBStart && isAHasBEnd) return false;
326
+ if (isBHasAStart && isBHasAEnd) return false;
327
+ if (isBHasAStart && a.start === a.end) return false;
328
+ if (isAHasBEnd && b.start === b.end) return false;
329
+ return true;
330
+ }
331
+ function findTokenStyleForRange(tokens, range) {
332
+ for (const line of tokens) for (const token of line) {
333
+ const start = token.offset;
334
+ if (token.offset + token.content.length <= range.start || start >= range.end) continue;
335
+ if (!token.content.trim()) continue;
336
+ const style = getTokenStyleObject(token);
337
+ if (style) return stringifyStyle(style);
338
+ }
339
+ }
340
+ function withoutUndefinedProperties(properties) {
341
+ return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
342
+ }
343
+ const markupShikiLanguageIds = /* @__PURE__ */ new Set([
344
+ "html",
345
+ "angular-html",
346
+ "jsx",
347
+ "tsx",
348
+ "vue",
349
+ "svelte",
350
+ "astro",
351
+ "markdown",
352
+ "mdx"
353
+ ]);
354
+ function collectMarkupAttributeRanges(context, classPositions, lang) {
355
+ const ranges = /* @__PURE__ */ new Set();
356
+ const seen = /* @__PURE__ */ new Set();
357
+ if (!classPositions.length || !markupShikiLanguageIds.has(getMasterCSSShikiLanguageId(lang) ?? "") || !context.codeToTokens) return ranges;
358
+ try {
359
+ const { decorations: _decorations, transformers: _transformers, ...tokenOptions } = context.options;
360
+ const scopedTokens = context.codeToTokens(context.source, {
361
+ ...tokenOptions,
362
+ includeExplanation: "scopeName"
363
+ }).tokens.flatMap((line) => line.map((token) => ({
364
+ start: token.offset,
365
+ end: token.offset + token.content.length,
366
+ scopes: token.explanation?.flatMap(({ scopes }) => scopes?.map(({ scopeName }) => scopeName) ?? []) ?? []
367
+ })));
368
+ for (const { contextRange } of classPositions) {
369
+ const key = `${contextRange.start}:${contextRange.end}`;
370
+ if (seen.has(key)) continue;
371
+ seen.add(key);
372
+ if (scopedTokens.some(({ start, end, scopes }) => start < contextRange.end && end > contextRange.start && scopes.some((scope) => scope.startsWith("meta.attribute.") || scope.startsWith("meta.tag.attributes.")))) {
373
+ ranges.add(key);
374
+ continue;
375
+ }
376
+ let attributeName;
377
+ for (let index = scopedTokens.length - 1; index >= 0; index--) {
378
+ const token = scopedTokens[index];
379
+ if (token.end <= contextRange.start && token.scopes.some((scope) => scope.startsWith("entity.other.attribute-name."))) {
380
+ attributeName = token;
381
+ break;
382
+ }
383
+ }
384
+ if (attributeName && /^\s*=\s*["'`]$/.test(context.source.slice(attributeName.end, contextRange.start))) ranges.add(key);
385
+ }
386
+ } catch {}
387
+ return ranges;
388
+ }
389
+ function createClassAttributeValueWrapperDecorations(source, classPositions, tokens, options, markupAttributeRanges, existingDecorations = []) {
390
+ if (!shouldCreateClassAttributeValueWrappers(options)) return [];
391
+ const contextRanges = /* @__PURE__ */ new Map();
392
+ for (const { contextRange } of classPositions) {
393
+ if (contextRange.start >= contextRange.end) continue;
394
+ contextRanges.set(`${contextRange.start}:${contextRange.end}`, contextRange);
395
+ }
396
+ if (!contextRanges.size) return [];
397
+ const lineStartOffsets = existingDecorations.length ? getLineStartOffsets(source) : [];
398
+ const existingRanges = existingDecorations.map((decoration) => resolveDecorationRange(source, lineStartOffsets, decoration)).filter((range) => Boolean(range));
399
+ const wrapperOption = getClassAttributeValueWrapperOption(options);
400
+ const wrapperOptions = typeof wrapperOption === "object" ? wrapperOption : void 0;
401
+ return [...contextRanges.values()].filter((range) => !existingRanges.some((existingRange) => decorationsPartiallyIntersect(range, existingRange))).map((range) => {
402
+ const style = findTokenStyleForRange(tokens, range);
403
+ const defaultProperties = {
404
+ class: mergeClassProperty("mcss-host mcss-host-role-class-attribute-value", markupAttributeRanges.has(`${range.start}:${range.end}`) ? "mcss-host-active-on-hover" : void 0),
405
+ "data-master-css-host-role": "class-attribute-value",
406
+ style
407
+ };
408
+ const customProperties = wrapperOptions?.properties ?? {};
409
+ return {
410
+ start: range.start,
411
+ end: range.end,
412
+ tagName: wrapperOptions?.tagName ?? "span",
413
+ alwaysWrap: true,
414
+ transform: wrapperOptions?.transform,
415
+ properties: {
416
+ ...withoutUndefinedProperties(defaultProperties),
417
+ ...withoutUndefinedProperties(customProperties),
418
+ class: mergeClassProperty(defaultProperties.class, customProperties.class)
419
+ }
420
+ };
421
+ });
422
+ }
423
+ const ROLE_TOKEN_MODIFIERS = {
424
+ "block.brace": "blockBrace",
425
+ "declaration.separator": "declarationSeparator",
426
+ "declaration.terminator": "declarationTerminator",
427
+ "selector.combinator": "selectorCombinator",
428
+ "selector.punctuation": "selectorPunctuation",
429
+ "selector.pseudoClass.delimiter": "pseudoClassDelimiter",
430
+ "selector.pseudoElement.delimiter": "pseudoElementDelimiter"
431
+ };
432
+ function resolveHighlightRole({ type, modifiers = [] }) {
433
+ const modifierSet = new Set(modifiers);
434
+ if (modifierSet.has("blockBrace")) return "block.brace";
435
+ if (modifierSet.has("declarationSeparator")) return "declaration.separator";
436
+ if (modifierSet.has("declarationTerminator")) return "declaration.terminator";
437
+ if (modifierSet.has("selectorCombinator")) return "selector.combinator";
438
+ if (modifierSet.has("selectorPunctuation")) return "selector.punctuation";
439
+ if (modifierSet.has("pseudoClassDelimiter")) return "selector.pseudoClass.delimiter";
440
+ if (modifierSet.has("pseudoElementDelimiter")) return "selector.pseudoElement.delimiter";
441
+ if (modifierSet.has("pseudoClass")) return "selector.pseudoClass.name";
442
+ if (modifierSet.has("pseudoElement")) return "selector.pseudoElement.name";
443
+ if (modifierSet.has("important")) return "value.important";
444
+ if (modifierSet.has("query")) return "query.keyword";
445
+ if (type === "property") return "declaration.property";
446
+ if (type === "type") return "selector.type";
447
+ if (modifierSet.has("component")) return "utility.component";
448
+ if (modifierSet.has("declaration")) return "utility.semantic";
449
+ if (modifierSet.has("unit")) return "value.unit";
450
+ if (modifierSet.has("valueSeparator")) return "value.separator";
451
+ if (modifierSet.has("valueOperator")) return "value.operator";
452
+ if (modifierSet.has("functionPunctuation")) return "value.function.punctuation";
453
+ if (type === "function") return "value.function";
454
+ if (type === "number") return "value.number";
455
+ if (type === "string") return "value.string";
456
+ if (type === "variable") return "value.variable";
457
+ return "value.keyword";
458
+ }
459
+ function createMasterCSSShikiDecorations(code, options = {}) {
460
+ const analysis = analyzeMasterCSSShikiDocument(code, options);
461
+ return analysis?.semanticTokens.length ? createSemanticTokenDecorations(analysis.semanticTokens, options) : [];
462
+ }
463
+ function analyzeMasterCSSShikiDocument(code, options) {
464
+ const classList = options.classList ?? isMasterCSSClassListLanguage(options.lang);
465
+ const languageId = getLanguageServiceLanguageId(classList ? "plaintext" : options.lang);
466
+ if (!languageId) return;
467
+ const session = createShikiSession(options);
468
+ try {
469
+ const { classPositions, semanticTokens } = session.analyzeDocument({
470
+ source: code,
471
+ languageId,
472
+ ...options.settings ? { settings: {
473
+ ...options.settings.classAttributes ? { classAttributes: options.settings.classAttributes } : {},
474
+ ...options.settings.classFunctions ? { classFunctions: options.settings.classFunctions } : {},
475
+ ...options.settings.classDeclarations ? { classDeclarations: options.settings.classDeclarations } : {}
476
+ } } : {}
477
+ });
478
+ const classNames = [...new Set(classPositions.map(({ token }) => token))];
479
+ const semanticClasses = new Set(classNames.length ? session.classifyClassNames(classNames).classes.filter(({ kind }) => kind === "semantic" || kind === "token").map(({ className }) => className) : []);
480
+ const semanticUtilityStarts = new Set(classPositions.filter(({ token }) => semanticClasses.has(token)).map(({ range }) => range.start));
481
+ return {
482
+ classPositions: [...classPositions],
483
+ semanticTokens: semanticTokens.map((token) => token.type === "enumMember" && semanticUtilityStarts.has(token.start) ? {
484
+ ...token,
485
+ role: "utility.semantic"
486
+ } : token)
487
+ };
488
+ } finally {
489
+ if (!options.session) session.dispose();
490
+ }
491
+ }
492
+ function transformerMasterCSS(options = {}) {
493
+ const sourceLineNumbers = /* @__PURE__ */ new WeakMap();
494
+ return {
495
+ name: "master-css",
496
+ enforce: "post",
497
+ tokens(tokens) {
498
+ const lang = options.lang ?? this.options.lang;
499
+ const classList = options.classList ?? isMasterCSSClassListLanguage(lang);
500
+ const resolvedOptions = {
501
+ ...options,
502
+ lang,
503
+ classList
504
+ };
505
+ const analysis = analyzeMasterCSSShikiDocument(this.source, resolvedOptions);
506
+ const decorations = analysis?.semanticTokens.length ? createSemanticTokenDecorations(analysis.semanticTokens, resolvedOptions) : [];
507
+ const classAttributeValueWrapperDecorations = createClassAttributeValueWrapperDecorations(this.source, analysis?.classPositions ?? [], tokens, resolvedOptions, collectMarkupAttributeRanges(this, analysis?.classPositions ?? [], lang), this.options.decorations ?? []);
508
+ this.options[classAttributeValueWrappersKey] = classAttributeValueWrapperDecorations;
509
+ if (!decorations.length) return;
510
+ const tokensSplitAtSemanticBoundaries = splitTokensAtOffsets(tokens, decorations.flatMap(({ start, end }) => [start, end]));
511
+ const resolveSyntaxStyle = createSemanticScopeStyleResolver(this, resolvedOptions);
512
+ return tokensSplitAtSemanticBoundaries.map((line) => line.map((token) => applySemanticDecorationToToken(token, decorations, resolveSyntaxStyle)));
513
+ },
514
+ line(line, lineNumber) {
515
+ sourceLineNumbers.set(line, lineNumber);
516
+ },
517
+ root(root) {
518
+ applyClassAttributeValueWrappers(root, this.source, this.options["__masterCSSClassAttributeValueWrappers"] ?? [], sourceLineNumbers);
519
+ }
520
+ };
521
+ }
522
+ export { MASTER_CSS_SHIKI_INJECT_TO, MASTER_CSS_SHIKI_SCOPE_NAME, MASTER_CSS_TEXTMATE_GRAMMAR, createMasterCSSShikiDecorations, createMasterCSSShikiLanguageRegistration, getMasterCSSShikiLanguageId, isMasterCSSClassListLanguage, isMasterCSSShikiSupportedLanguage, masterCSSShikiLanguage, transformerMasterCSS };
@@ -0,0 +1,2 @@
1
+ declare function beautifyCSS(text: string): any;
2
+ export { beautifyCSS as default };