@master/css-mcp 2.0.0-rc.76 → 2.0.0-rc.78
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 +34 -4
- package/dist/bin/index.js +1 -1
- package/dist/classes.d.ts +237 -0
- package/dist/classes.js +224 -0
- package/dist/compare.d.ts +71 -0
- package/dist/compare.js +131 -0
- package/dist/context.js +2 -2
- package/dist/contributor.d.ts +150 -0
- package/dist/contributor.js +497 -0
- package/dist/directives.d.ts +133 -0
- package/dist/directives.js +158 -0
- package/dist/document.d.ts +25 -0
- package/dist/document.js +51 -0
- package/dist/format.d.ts +57 -0
- package/dist/format.js +81 -0
- package/dist/index.d.ts +1 -1
- package/dist/language.js +2 -20
- package/dist/manifest-query.d.ts +149 -0
- package/dist/manifest-query.js +159 -0
- package/dist/manifest-summary.d.ts +48 -0
- package/dist/manifest-summary.js +56 -0
- package/dist/scan.d.ts +1 -1
- package/dist/scan.js +2 -2
- package/dist/server.js +202 -3
- package/dist/setup.d.ts +63 -0
- package/dist/setup.js +168 -0
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Master CSS MCP Server
|
|
2
2
|
|
|
3
|
-
Model Context Protocol server for Master CSS tooling. It lets AI clients inspect a Master CSS workspace, read the active project manifest,
|
|
3
|
+
Model Context Protocol server for Master CSS tooling. It lets AI clients inspect a Master CSS workspace, audit setup, read the active project manifest, inspect CSS-first directives, trace classes through extraction and generated CSS, render or compare CSS output, run lint diagnostics, request syntax suggestions, apply reviewed fix previews, and route Master CSS repository contributor work through deterministic context tools.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -31,27 +31,57 @@ Most MCP clients accept a command and arguments for stdio servers:
|
|
|
31
31
|
|
|
32
32
|
Use an absolute `--root` path so the server resolves the intended workspace.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Project tools
|
|
35
35
|
|
|
36
36
|
| Tool | Use |
|
|
37
37
|
| --- | --- |
|
|
38
38
|
| `mastercss_workspace_info` | Report workspace roots, resolved packages, and manifest status. |
|
|
39
|
+
| `mastercss_setup_audit` | Audit package, entry stylesheet, manifest, integration, and package-resolution setup. |
|
|
39
40
|
| `mastercss_inspect_class` | Inspect one class and return generated rules and CSS text. |
|
|
41
|
+
| `mastercss_trace_class` | Trace one class through source extraction, scanner state, missing CSS classification, and generated rules. |
|
|
42
|
+
| `mastercss_extract_classes` | Extract class positions and validation summaries from files or an in-memory source buffer. |
|
|
43
|
+
| `mastercss_inspect_directives` | Inspect CSS-first directives and report manifest, dependency, warning, and CSS effects. |
|
|
40
44
|
| `mastercss_render_css` | Generate CSS from HTML or a class list. |
|
|
41
45
|
| `mastercss_scan_project` | Scan sources, check optional classes, and report scanner state, stylesheet entries, generated CSS metadata, and missing CSS diagnostics. |
|
|
46
|
+
| `mastercss_manifest_query` | Query active manifest tokens, utilities, variants, modes, at-rules, and aliases. |
|
|
47
|
+
| `mastercss_css_compare` | Compare generated CSS for before/after class lists, HTML fragments, or source buffers. |
|
|
42
48
|
| `mastercss_lint_project` | Run class-list diagnostics for workspace files without writing files. |
|
|
43
49
|
| `mastercss_lint_content` | Run class-list diagnostics on an in-memory source buffer without writing files. |
|
|
44
50
|
| `mastercss_suggest_syntax` | Return language-service completions and hover context. |
|
|
45
51
|
| `mastercss_preview_fixes` | Create a diff preview and confirmation token. |
|
|
52
|
+
| `mastercss_preview_directive_format` | Preview Master CSS directive formatting for files, or format in-memory content without writing. |
|
|
46
53
|
| `mastercss_apply_preview` | Apply a preview after token, hash, and workspace checks. |
|
|
47
54
|
|
|
55
|
+
## Contributor tools
|
|
56
|
+
|
|
57
|
+
These read-only tools are for Master CSS repository contributors, AI coding agents, review bots, and CI support. They expose repository-specific routing as low-token JSON instead of asking an agent to repeatedly infer package ownership, risk, and validation from raw files. In non-Master CSS workspaces they return limited package data instead of guessing.
|
|
58
|
+
|
|
59
|
+
| Tool | Use |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `mastercss_repo_context` | Route changed paths or a diff to affected packages, required context files, risks, and validation commands. |
|
|
62
|
+
| `mastercss_change_impact` | Summarize contributor change risk for CSS output, runtime, extraction, language tooling, ESLint, docs, and package boundaries. |
|
|
63
|
+
| `mastercss_test_router` | Return focused validation commands for contributor changes. |
|
|
64
|
+
| `mastercss_package_graph` | Report workspace package ownership, scripts, exports, workspace dependencies, and dependents. |
|
|
65
|
+
|
|
66
|
+
## Prompt templates
|
|
67
|
+
|
|
68
|
+
| Prompt | Use |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `debug-missing-css` | Debug scanner coverage, manifest loading, invalid syntax, native CSS pruning, and stylesheet entry configuration. |
|
|
71
|
+
| `review-mastercss-classes` | Review class lists and propose only safe, scoped write previews. |
|
|
72
|
+
| `migrate-to-mastercss` | Audit an existing styling system and produce an incremental migration plan before editing. |
|
|
73
|
+
|
|
74
|
+
Use `migrate-to-mastercss` when an MCP-capable AI client is helping migrate CSS, CSS Modules, Sass, Tailwind CSS, CSS-in-JS, component-library styling, or Master CSS v1. The prompt asks the agent to inspect the workspace first, identify stylesheet entries and source extraction coverage, recommend a rendering mode, choose the first reviewable migration batch, and list validation commands and visual review risks.
|
|
75
|
+
|
|
76
|
+
The migration prompt is a planning workflow, not a one-shot converter. Keep the previous styling system in place until each migrated slice builds, runs, and visually matches the old UI.
|
|
77
|
+
|
|
48
78
|
## Machine-readable results
|
|
49
79
|
|
|
50
|
-
Project scan and
|
|
80
|
+
Project scan, lint, setup audit, directive inspection, class extraction, class tracing, CSS comparison, manifest query, and contributor routing reports use `version: 1`. Tooling can rely on stable top-level fields such as `version`, `root`, `manifest`, `inputs`, `files`, `diagnostics`, `risks`, `validation`, and `summary` when present. Nested diagnostic `data` objects may gain additional fields over time.
|
|
51
81
|
|
|
52
82
|
## Safety
|
|
53
83
|
|
|
54
|
-
Read-only tools do not write files. File writes use a two-step preview/apply flow, validate workspace containment, and verify original file hashes before writing.
|
|
84
|
+
Read-only tools do not write files. File writes use a two-step preview/apply flow, validate workspace containment, and verify original file hashes before writing. Lint fixes, generated CSS output writes, and directive formatting for files all use preview tokens before `mastercss_apply_preview` can write anything.
|
|
55
85
|
|
|
56
86
|
## Related docs
|
|
57
87
|
|
package/dist/bin/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { startStdioServer } from "../server.js";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
3
|
import { dirname, resolve } from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
6
|
//#region src/bin/index.ts
|
|
7
7
|
function readPackageVersion() {
|
|
8
8
|
const directory = dirname(fileURLToPath(import.meta.url));
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import MasterCSSMCPContext from "./context.js";
|
|
2
|
+
|
|
3
|
+
//#region src/classes.d.ts
|
|
4
|
+
interface ExtractClassesOptions {
|
|
5
|
+
content?: string;
|
|
6
|
+
filePath?: string;
|
|
7
|
+
patterns?: string[];
|
|
8
|
+
includeRules?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface TraceClassOptions {
|
|
11
|
+
className: string;
|
|
12
|
+
patterns?: string[];
|
|
13
|
+
includeCss?: boolean;
|
|
14
|
+
mode?: string;
|
|
15
|
+
}
|
|
16
|
+
declare function extractClasses(context: MasterCSSMCPContext, options?: ExtractClassesOptions): Promise<{
|
|
17
|
+
version: number;
|
|
18
|
+
root: string;
|
|
19
|
+
manifest: {
|
|
20
|
+
error?: string | undefined;
|
|
21
|
+
status: "loaded" | "error";
|
|
22
|
+
entries: string[];
|
|
23
|
+
};
|
|
24
|
+
inputs: {
|
|
25
|
+
mode: string;
|
|
26
|
+
filePath: string;
|
|
27
|
+
patterns?: undefined;
|
|
28
|
+
files?: undefined;
|
|
29
|
+
};
|
|
30
|
+
files: {
|
|
31
|
+
filePath: string;
|
|
32
|
+
languageId: string;
|
|
33
|
+
classes: {
|
|
34
|
+
raw: string;
|
|
35
|
+
token: string;
|
|
36
|
+
range: {
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
};
|
|
40
|
+
loc: {
|
|
41
|
+
start: import("vscode-languageserver-textdocument").Position;
|
|
42
|
+
end: import("vscode-languageserver-textdocument").Position;
|
|
43
|
+
};
|
|
44
|
+
contextRange: {
|
|
45
|
+
start: number;
|
|
46
|
+
end: number;
|
|
47
|
+
};
|
|
48
|
+
sourceKind: string;
|
|
49
|
+
status: string;
|
|
50
|
+
valid: boolean;
|
|
51
|
+
inspection: {
|
|
52
|
+
valid: boolean;
|
|
53
|
+
base: string;
|
|
54
|
+
suffix: string;
|
|
55
|
+
key: string | undefined;
|
|
56
|
+
value: string | undefined;
|
|
57
|
+
keyToken: string | undefined;
|
|
58
|
+
valueToken: string | undefined;
|
|
59
|
+
stateToken: string | undefined;
|
|
60
|
+
important: boolean | undefined;
|
|
61
|
+
matcherTypes: ("static" | "pattern" | "key" | "variable" | "value")[];
|
|
62
|
+
variables: {
|
|
63
|
+
key: string;
|
|
64
|
+
variable: import("@master/css-language").Variable;
|
|
65
|
+
}[];
|
|
66
|
+
rules: {
|
|
67
|
+
className: string;
|
|
68
|
+
layer: import("@master/css-engine").MasterCSSManifestUtilityLayerName;
|
|
69
|
+
type: import("@master/css-language").UtilityType;
|
|
70
|
+
}[];
|
|
71
|
+
};
|
|
72
|
+
}[];
|
|
73
|
+
}[];
|
|
74
|
+
summary: {
|
|
75
|
+
files: number;
|
|
76
|
+
classes: number;
|
|
77
|
+
valid: number;
|
|
78
|
+
invalid: number;
|
|
79
|
+
diagnostics?: undefined;
|
|
80
|
+
};
|
|
81
|
+
scanner?: undefined;
|
|
82
|
+
diagnostics?: undefined;
|
|
83
|
+
} | {
|
|
84
|
+
version: number;
|
|
85
|
+
root: string;
|
|
86
|
+
manifest: {
|
|
87
|
+
error?: string | undefined;
|
|
88
|
+
status: "loaded" | "error";
|
|
89
|
+
entries: string[];
|
|
90
|
+
};
|
|
91
|
+
inputs: {
|
|
92
|
+
mode: string;
|
|
93
|
+
patterns: string[];
|
|
94
|
+
files: string[];
|
|
95
|
+
filePath?: undefined;
|
|
96
|
+
};
|
|
97
|
+
files: {
|
|
98
|
+
filePath: string;
|
|
99
|
+
languageId: string;
|
|
100
|
+
classes: {
|
|
101
|
+
raw: string;
|
|
102
|
+
token: string;
|
|
103
|
+
range: {
|
|
104
|
+
start: number;
|
|
105
|
+
end: number;
|
|
106
|
+
};
|
|
107
|
+
loc: {
|
|
108
|
+
start: import("vscode-languageserver-textdocument").Position;
|
|
109
|
+
end: import("vscode-languageserver-textdocument").Position;
|
|
110
|
+
};
|
|
111
|
+
contextRange: {
|
|
112
|
+
start: number;
|
|
113
|
+
end: number;
|
|
114
|
+
};
|
|
115
|
+
sourceKind: string;
|
|
116
|
+
status: string;
|
|
117
|
+
valid: boolean;
|
|
118
|
+
inspection: {
|
|
119
|
+
valid: boolean;
|
|
120
|
+
base: string;
|
|
121
|
+
suffix: string;
|
|
122
|
+
key: string | undefined;
|
|
123
|
+
value: string | undefined;
|
|
124
|
+
keyToken: string | undefined;
|
|
125
|
+
valueToken: string | undefined;
|
|
126
|
+
stateToken: string | undefined;
|
|
127
|
+
important: boolean | undefined;
|
|
128
|
+
matcherTypes: ("static" | "pattern" | "key" | "variable" | "value")[];
|
|
129
|
+
variables: {
|
|
130
|
+
key: string;
|
|
131
|
+
variable: import("@master/css-language").Variable;
|
|
132
|
+
}[];
|
|
133
|
+
rules: {
|
|
134
|
+
className: string;
|
|
135
|
+
layer: import("@master/css-engine").MasterCSSManifestUtilityLayerName;
|
|
136
|
+
type: import("@master/css-language").UtilityType;
|
|
137
|
+
}[];
|
|
138
|
+
};
|
|
139
|
+
}[];
|
|
140
|
+
}[];
|
|
141
|
+
scanner: {
|
|
142
|
+
counts: {
|
|
143
|
+
latent: number;
|
|
144
|
+
valid: number;
|
|
145
|
+
invalid: number;
|
|
146
|
+
native: number;
|
|
147
|
+
usedNative: number;
|
|
148
|
+
safelist: number;
|
|
149
|
+
blocklist: number;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
diagnostics: import("@master/css-diagnostics").MasterCSSInspectionDiagnostic[];
|
|
153
|
+
summary: {
|
|
154
|
+
files: number;
|
|
155
|
+
classes: number;
|
|
156
|
+
valid: number;
|
|
157
|
+
invalid: number;
|
|
158
|
+
diagnostics: number;
|
|
159
|
+
};
|
|
160
|
+
}>;
|
|
161
|
+
declare function traceClass(context: MasterCSSMCPContext, options: TraceClassOptions): Promise<{
|
|
162
|
+
version: number;
|
|
163
|
+
root: string;
|
|
164
|
+
manifest: {
|
|
165
|
+
error?: string | undefined;
|
|
166
|
+
status: "loaded" | "error";
|
|
167
|
+
entries: string[];
|
|
168
|
+
};
|
|
169
|
+
inputs: {
|
|
170
|
+
className: string;
|
|
171
|
+
patterns: string[];
|
|
172
|
+
mode: string | undefined;
|
|
173
|
+
};
|
|
174
|
+
className: string;
|
|
175
|
+
status: "present" | "missing";
|
|
176
|
+
reason: "generated" | "native-css" | "invalid" | "safelist" | "blocklisted" | "not-detected";
|
|
177
|
+
detected: boolean;
|
|
178
|
+
occurrences: {
|
|
179
|
+
filePath: string;
|
|
180
|
+
source: string;
|
|
181
|
+
statuses: string[];
|
|
182
|
+
}[];
|
|
183
|
+
inspection: {
|
|
184
|
+
valid: boolean;
|
|
185
|
+
base: string;
|
|
186
|
+
suffix: string;
|
|
187
|
+
key: string | undefined;
|
|
188
|
+
value: string | undefined;
|
|
189
|
+
keyToken: string | undefined;
|
|
190
|
+
valueToken: string | undefined;
|
|
191
|
+
stateToken: string | undefined;
|
|
192
|
+
important: boolean | undefined;
|
|
193
|
+
matcherTypes: ("static" | "pattern" | "key" | "variable" | "value")[];
|
|
194
|
+
variables: {
|
|
195
|
+
key: string;
|
|
196
|
+
variable: import("@master/css-language").Variable;
|
|
197
|
+
}[];
|
|
198
|
+
rules: {
|
|
199
|
+
className: string;
|
|
200
|
+
layer: import("@master/css-engine").MasterCSSManifestUtilityLayerName;
|
|
201
|
+
type: import("@master/css-language").UtilityType;
|
|
202
|
+
}[];
|
|
203
|
+
};
|
|
204
|
+
scanner: {
|
|
205
|
+
counts: {
|
|
206
|
+
latent: number;
|
|
207
|
+
valid: number;
|
|
208
|
+
invalid: number;
|
|
209
|
+
native: number;
|
|
210
|
+
usedNative: number;
|
|
211
|
+
safelist: number;
|
|
212
|
+
blocklist: number;
|
|
213
|
+
};
|
|
214
|
+
safelist: boolean;
|
|
215
|
+
blocklist: boolean;
|
|
216
|
+
};
|
|
217
|
+
css: {
|
|
218
|
+
text?: string | undefined;
|
|
219
|
+
included: boolean;
|
|
220
|
+
bytes: number;
|
|
221
|
+
emittedGlobals: {
|
|
222
|
+
variables: number;
|
|
223
|
+
animations: number;
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
diagnostics: import("@master/css-diagnostics").MasterCSSInspectionDiagnostic[];
|
|
227
|
+
summary: {
|
|
228
|
+
status: "present" | "missing";
|
|
229
|
+
reason: "generated" | "native-css" | "invalid" | "safelist" | "blocklisted" | "not-detected";
|
|
230
|
+
detected: boolean;
|
|
231
|
+
valid: boolean;
|
|
232
|
+
rules: number;
|
|
233
|
+
diagnostics: number;
|
|
234
|
+
};
|
|
235
|
+
}>;
|
|
236
|
+
//#endregion
|
|
237
|
+
export { ExtractClassesOptions, TraceClassOptions, extractClasses, traceClass };
|
package/dist/classes.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { createMCPTextDocument, getLanguageId } from "./document.js";
|
|
2
|
+
import { loadWorkspaceManifest } from "./project.js";
|
|
3
|
+
import { resolveSourceFiles, scanProject } from "./scan.js";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import CSSLanguageService from "@master/css-language-service";
|
|
7
|
+
import { inspectMasterCSSClass } from "@master/css-engine/inspect";
|
|
8
|
+
import { createCSSWithNativeDeclarations } from "@master/css-validator/native-declaration";
|
|
9
|
+
//#region src/classes.ts
|
|
10
|
+
const CLASS_EXTRACTION_VERSION = 1;
|
|
11
|
+
const CLASS_TRACE_VERSION = 1;
|
|
12
|
+
const defaultManifest = createRequire(import.meta.url)("@master/css-preset/default-manifest.json");
|
|
13
|
+
async function createCSS(context) {
|
|
14
|
+
const manifest = await loadWorkspaceManifest(context);
|
|
15
|
+
return {
|
|
16
|
+
manifest,
|
|
17
|
+
css: createCSSWithNativeDeclarations(manifest.status === "loaded" ? manifest.manifest : defaultManifest)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createLanguageService(manifest) {
|
|
21
|
+
return new CSSLanguageService(manifest.status === "loaded" ? { manifest: manifest.manifest } : void 0);
|
|
22
|
+
}
|
|
23
|
+
function compactInspection(css, className, mode, includeRules = false) {
|
|
24
|
+
const inspection = inspectMasterCSSClass(css, className, mode);
|
|
25
|
+
return {
|
|
26
|
+
valid: inspection.rules.length > 0,
|
|
27
|
+
base: inspection.base,
|
|
28
|
+
suffix: inspection.suffix,
|
|
29
|
+
key: inspection.key,
|
|
30
|
+
value: inspection.value,
|
|
31
|
+
keyToken: inspection.keyToken,
|
|
32
|
+
valueToken: inspection.valueToken,
|
|
33
|
+
stateToken: inspection.stateToken,
|
|
34
|
+
important: inspection.important,
|
|
35
|
+
matcherTypes: inspection.matcherTypes,
|
|
36
|
+
variables: inspection.variableEntries.map(({ key, variable }) => ({
|
|
37
|
+
key,
|
|
38
|
+
variable
|
|
39
|
+
})),
|
|
40
|
+
rules: includeRules ? inspection.rules.map((rule) => ({
|
|
41
|
+
className: rule.name,
|
|
42
|
+
layer: rule.layerName,
|
|
43
|
+
type: rule.type,
|
|
44
|
+
text: rule.text
|
|
45
|
+
})) : inspection.rules.map((rule) => ({
|
|
46
|
+
className: rule.name,
|
|
47
|
+
layer: rule.layerName,
|
|
48
|
+
type: rule.type
|
|
49
|
+
}))
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function classifyExtractedClass(token, inspection, discovered) {
|
|
53
|
+
if (discovered?.valid.includes(token)) return "generated";
|
|
54
|
+
if (discovered?.usedNative.includes(token)) return "native-css";
|
|
55
|
+
if (discovered?.invalid.includes(token)) return "invalid";
|
|
56
|
+
if (discovered?.latent.includes(token)) return "latent";
|
|
57
|
+
return inspection.valid ? "generated" : "unknown";
|
|
58
|
+
}
|
|
59
|
+
function extractFromContent(service, css, filePath, content, includeRules, discovered) {
|
|
60
|
+
const document = createMCPTextDocument(filePath, content);
|
|
61
|
+
return service.getClassPositions(document).map((position) => {
|
|
62
|
+
const inspection = compactInspection(css, position.token, void 0, includeRules);
|
|
63
|
+
return {
|
|
64
|
+
raw: position.raw,
|
|
65
|
+
token: position.token,
|
|
66
|
+
range: position.range,
|
|
67
|
+
loc: {
|
|
68
|
+
start: document.positionAt(position.range.start),
|
|
69
|
+
end: document.positionAt(position.range.end)
|
|
70
|
+
},
|
|
71
|
+
contextRange: position.contextRange,
|
|
72
|
+
sourceKind: "class-position",
|
|
73
|
+
status: classifyExtractedClass(position.token, inspection, discovered),
|
|
74
|
+
valid: inspection.valid,
|
|
75
|
+
inspection
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function extractClasses(context, options = {}) {
|
|
80
|
+
const { manifest, css } = await createCSS(context);
|
|
81
|
+
const service = createLanguageService(manifest);
|
|
82
|
+
const includeRules = Boolean(options.includeRules);
|
|
83
|
+
if (options.content !== void 0) {
|
|
84
|
+
const filePath = context.resolveVirtualPath(options.filePath || "index.html");
|
|
85
|
+
const classes = extractFromContent(service, css, filePath, options.content, includeRules);
|
|
86
|
+
return {
|
|
87
|
+
version: CLASS_EXTRACTION_VERSION,
|
|
88
|
+
root: context.root,
|
|
89
|
+
manifest: {
|
|
90
|
+
status: manifest.status,
|
|
91
|
+
entries: manifest.entries,
|
|
92
|
+
...manifest.status === "error" ? { error: manifest.error } : {}
|
|
93
|
+
},
|
|
94
|
+
inputs: {
|
|
95
|
+
mode: "content",
|
|
96
|
+
filePath
|
|
97
|
+
},
|
|
98
|
+
files: [{
|
|
99
|
+
filePath,
|
|
100
|
+
languageId: getLanguageId(filePath),
|
|
101
|
+
classes
|
|
102
|
+
}],
|
|
103
|
+
summary: {
|
|
104
|
+
files: 1,
|
|
105
|
+
classes: classes.length,
|
|
106
|
+
valid: classes.filter((className) => className.valid).length,
|
|
107
|
+
invalid: classes.filter((className) => !className.valid).length
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const filePaths = await resolveSourceFiles(context, options.patterns);
|
|
112
|
+
const scan = await scanProject(context, {
|
|
113
|
+
patterns: options.patterns,
|
|
114
|
+
includeCss: false
|
|
115
|
+
});
|
|
116
|
+
const scanFileByPath = new Map(scan.files.map((file) => [file.filePath, file]));
|
|
117
|
+
const files = await Promise.all(filePaths.map(async (filePath) => {
|
|
118
|
+
const content = await readFile(filePath, "utf8");
|
|
119
|
+
const discovered = scanFileByPath.get(filePath)?.discovered;
|
|
120
|
+
return {
|
|
121
|
+
filePath,
|
|
122
|
+
languageId: getLanguageId(filePath),
|
|
123
|
+
classes: extractFromContent(service, css, filePath, content, includeRules, discovered)
|
|
124
|
+
};
|
|
125
|
+
}));
|
|
126
|
+
const classes = files.flatMap((file) => file.classes);
|
|
127
|
+
return {
|
|
128
|
+
version: CLASS_EXTRACTION_VERSION,
|
|
129
|
+
root: context.root,
|
|
130
|
+
manifest: {
|
|
131
|
+
status: manifest.status,
|
|
132
|
+
entries: manifest.entries,
|
|
133
|
+
...manifest.status === "error" ? { error: manifest.error } : {}
|
|
134
|
+
},
|
|
135
|
+
inputs: {
|
|
136
|
+
mode: "project",
|
|
137
|
+
patterns: options.patterns ?? scan.inputs.patterns,
|
|
138
|
+
files: filePaths
|
|
139
|
+
},
|
|
140
|
+
files,
|
|
141
|
+
scanner: { counts: scan.scanner.counts },
|
|
142
|
+
diagnostics: scan.diagnostics,
|
|
143
|
+
summary: {
|
|
144
|
+
files: files.length,
|
|
145
|
+
classes: classes.length,
|
|
146
|
+
valid: classes.filter((className) => className.valid).length,
|
|
147
|
+
invalid: classes.filter((className) => !className.valid).length,
|
|
148
|
+
diagnostics: scan.summary.diagnostics
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function findClassOccurrences(report, className) {
|
|
153
|
+
return report.files.flatMap((file) => {
|
|
154
|
+
const discovered = file.discovered;
|
|
155
|
+
const statuses = [
|
|
156
|
+
discovered.valid.includes(className) ? "valid" : void 0,
|
|
157
|
+
discovered.invalid.includes(className) ? "invalid" : void 0,
|
|
158
|
+
discovered.usedNative.includes(className) ? "used-native" : void 0,
|
|
159
|
+
discovered.latent.includes(className) ? "latent" : void 0
|
|
160
|
+
].filter((status) => Boolean(status));
|
|
161
|
+
return statuses.length ? [{
|
|
162
|
+
filePath: file.filePath,
|
|
163
|
+
source: file.source,
|
|
164
|
+
statuses
|
|
165
|
+
}] : [];
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async function traceClass(context, options) {
|
|
169
|
+
const [scan, state] = await Promise.all([scanProject(context, {
|
|
170
|
+
patterns: options.patterns,
|
|
171
|
+
classes: [options.className],
|
|
172
|
+
includeCss: options.includeCss
|
|
173
|
+
}), createCSS(context)]);
|
|
174
|
+
const inspection = compactInspection(state.css, options.className, options.mode, true);
|
|
175
|
+
const missingResult = [...scan.missingCSS.present, ...scan.missingCSS.missing].find((result) => result.className === options.className);
|
|
176
|
+
const occurrences = findClassOccurrences(scan, options.className);
|
|
177
|
+
const status = missingResult?.status ?? (inspection.valid ? "present" : "missing");
|
|
178
|
+
const reason = missingResult?.reason ?? (inspection.valid ? "generated" : "not-detected");
|
|
179
|
+
return {
|
|
180
|
+
version: CLASS_TRACE_VERSION,
|
|
181
|
+
root: context.root,
|
|
182
|
+
manifest: {
|
|
183
|
+
status: state.manifest.status,
|
|
184
|
+
entries: state.manifest.entries,
|
|
185
|
+
...state.manifest.status === "error" ? { error: state.manifest.error } : {}
|
|
186
|
+
},
|
|
187
|
+
inputs: {
|
|
188
|
+
className: options.className,
|
|
189
|
+
patterns: options.patterns ?? scan.inputs.patterns,
|
|
190
|
+
mode: options.mode
|
|
191
|
+
},
|
|
192
|
+
className: options.className,
|
|
193
|
+
status,
|
|
194
|
+
reason,
|
|
195
|
+
detected: occurrences.length > 0,
|
|
196
|
+
occurrences,
|
|
197
|
+
inspection,
|
|
198
|
+
scanner: {
|
|
199
|
+
counts: scan.scanner.counts,
|
|
200
|
+
safelist: scan.scanner.classes.safelist.includes(options.className),
|
|
201
|
+
blocklist: scan.scanner.classes.blocklist.includes(options.className)
|
|
202
|
+
},
|
|
203
|
+
css: {
|
|
204
|
+
included: scan.css.included,
|
|
205
|
+
bytes: scan.css.bytes,
|
|
206
|
+
emittedGlobals: scan.css.emittedGlobals,
|
|
207
|
+
...scan.css.text !== void 0 ? { text: scan.css.text } : {}
|
|
208
|
+
},
|
|
209
|
+
diagnostics: scan.diagnostics.filter((diagnostic) => {
|
|
210
|
+
if (diagnostic.sourceKind === "missing-css") return diagnostic.data?.className === options.className;
|
|
211
|
+
return diagnostic.message.includes(options.className);
|
|
212
|
+
}),
|
|
213
|
+
summary: {
|
|
214
|
+
status,
|
|
215
|
+
reason,
|
|
216
|
+
detected: occurrences.length > 0,
|
|
217
|
+
valid: inspection.valid,
|
|
218
|
+
rules: inspection.rules.length,
|
|
219
|
+
diagnostics: scan.diagnostics.length
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
export { extractClasses, traceClass };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import MasterCSSMCPContext from "./context.js";
|
|
2
|
+
|
|
3
|
+
//#region src/compare.d.ts
|
|
4
|
+
interface CompareCSSOptions {
|
|
5
|
+
beforeClassList?: string;
|
|
6
|
+
afterClassList?: string;
|
|
7
|
+
beforeHtml?: string;
|
|
8
|
+
afterHtml?: string;
|
|
9
|
+
beforeContent?: string;
|
|
10
|
+
afterContent?: string;
|
|
11
|
+
filePath?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function compareCSS(context: MasterCSSMCPContext, options: CompareCSSOptions): Promise<{
|
|
14
|
+
version: number;
|
|
15
|
+
root: string;
|
|
16
|
+
manifest: {
|
|
17
|
+
error?: string | undefined;
|
|
18
|
+
status: "loaded" | "error";
|
|
19
|
+
entries: string[];
|
|
20
|
+
};
|
|
21
|
+
inputs: {
|
|
22
|
+
filePath: string;
|
|
23
|
+
before: {
|
|
24
|
+
classes: number;
|
|
25
|
+
invalid: number;
|
|
26
|
+
};
|
|
27
|
+
after: {
|
|
28
|
+
classes: number;
|
|
29
|
+
invalid: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
classes: {
|
|
33
|
+
added: string[];
|
|
34
|
+
removed: string[];
|
|
35
|
+
unchanged: string[];
|
|
36
|
+
};
|
|
37
|
+
invalid: {
|
|
38
|
+
before: string[];
|
|
39
|
+
after: string[];
|
|
40
|
+
added: string[];
|
|
41
|
+
removed: string[];
|
|
42
|
+
};
|
|
43
|
+
css: {
|
|
44
|
+
changed: boolean;
|
|
45
|
+
before: {
|
|
46
|
+
bytes: number;
|
|
47
|
+
text: string;
|
|
48
|
+
};
|
|
49
|
+
after: {
|
|
50
|
+
bytes: number;
|
|
51
|
+
text: string;
|
|
52
|
+
};
|
|
53
|
+
bytesDelta: number;
|
|
54
|
+
diff: string;
|
|
55
|
+
};
|
|
56
|
+
rules: {
|
|
57
|
+
added: string[];
|
|
58
|
+
removed: string[];
|
|
59
|
+
unchanged: number;
|
|
60
|
+
};
|
|
61
|
+
summary: {
|
|
62
|
+
changed: boolean;
|
|
63
|
+
addedClasses: number;
|
|
64
|
+
removedClasses: number;
|
|
65
|
+
addedRules: number;
|
|
66
|
+
removedRules: number;
|
|
67
|
+
bytesDelta: number;
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { CompareCSSOptions, compareCSS };
|