@master/css-mcp 2.0.0-rc.88 → 2.0.0

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 (47) hide show
  1. package/README.md +47 -22
  2. package/dist/bin/index.js +4 -5
  3. package/dist/classes.js +153 -168
  4. package/dist/compare.js +99 -83
  5. package/dist/context.d.ts +5 -10
  6. package/dist/context.js +28 -10
  7. package/dist/design-system-schema.js +97 -0
  8. package/dist/directives.js +59 -109
  9. package/dist/document.js +2 -10
  10. package/dist/format.js +67 -56
  11. package/dist/index.d.ts +2 -3
  12. package/dist/index.js +1 -3
  13. package/dist/internal/src/workspace.js +70 -0
  14. package/dist/language.js +27 -23
  15. package/dist/lint.js +86 -96
  16. package/dist/manifest-query.js +40 -100
  17. package/dist/manifest-summary.js +15 -35
  18. package/dist/output-schema.js +207 -0
  19. package/dist/preview-write-lock.js +90 -0
  20. package/dist/project-comparison-schema.js +59 -0
  21. package/dist/project.js +77 -18
  22. package/dist/result.js +67 -8
  23. package/dist/scan.js +93 -73
  24. package/dist/semantic-schema.js +286 -0
  25. package/dist/server.d.ts +10 -9
  26. package/dist/server.js +123 -111
  27. package/dist/setup.js +28 -29
  28. package/dist/source-schema.js +141 -0
  29. package/dist/suggest.js +22 -0
  30. package/dist/tooling-session.js +27 -0
  31. package/package.json +63 -73
  32. package/LICENSE +0 -21
  33. package/dist/classes.d.ts +0 -236
  34. package/dist/compare.d.ts +0 -70
  35. package/dist/contributor.d.ts +0 -149
  36. package/dist/contributor.js +0 -499
  37. package/dist/directives.d.ts +0 -132
  38. package/dist/document.d.ts +0 -24
  39. package/dist/format.d.ts +0 -56
  40. package/dist/language.d.ts +0 -24
  41. package/dist/lint.d.ts +0 -55
  42. package/dist/manifest-query.d.ts +0 -148
  43. package/dist/manifest-summary.d.ts +0 -47
  44. package/dist/project.d.ts +0 -31
  45. package/dist/result.d.ts +0 -8
  46. package/dist/scan.d.ts +0 -156
  47. package/dist/setup.d.ts +0 -67
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, 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.
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, and apply reviewed fix previews.
4
4
 
5
5
  ## Installation
6
6
 
@@ -11,7 +11,7 @@ npm install -D @master/css-mcp
11
11
  You can also run it directly:
12
12
 
13
13
  ```bash
14
- npx -y @master/css-mcp@rc --root /absolute/path/to/project
14
+ npx -y @master/css-mcp@latest --root /absolute/path/to/project
15
15
  ```
16
16
 
17
17
  ## MCP client config
@@ -23,7 +23,7 @@ Most MCP clients accept a command and arguments for stdio servers:
23
23
  "mcpServers": {
24
24
  "master-css": {
25
25
  "command": "npx",
26
- "args": ["-y", "@master/css-mcp@rc", "--root", "/absolute/path/to/project"]
26
+ "args": ["-y", "@master/css-mcp@latest", "--root", "/absolute/path/to/project"]
27
27
  }
28
28
  }
29
29
  }
@@ -35,6 +35,7 @@ Use an absolute `--root` path so the server resolves the intended workspace.
35
35
 
36
36
  | Tool | Use |
37
37
  | --- | --- |
38
+ | `mastercss_suggest_classes` | Suggest project-aware classes for an isolated CSS declaration block without writing files. Preserves the block when conversion needs review; checks declaration output, not surrounding cascade or browser rendering. |
38
39
  | `mastercss_workspace_info` | Report workspace roots, resolved packages, and manifest status. |
39
40
  | `mastercss_setup_audit` | Audit package, entry stylesheet, manifest, integration, and package-resolution setup. |
40
41
  | `mastercss_inspect_class` | Inspect one class and return generated rules and CSS text. |
@@ -42,9 +43,9 @@ Use an absolute `--root` path so the server resolves the intended workspace.
42
43
  | `mastercss_extract_classes` | Extract class positions and validation summaries from files or an in-memory source buffer. |
43
44
  | `mastercss_inspect_directives` | Inspect CSS-first directives and report manifest, dependency, warning, and CSS effects. |
44
45
  | `mastercss_render_css` | Generate CSS from HTML or a class list. |
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, conditions, and aliases. |
47
- | `mastercss_css_compare` | Compare generated CSS for before/after class lists, HTML fragments, or source buffers. |
46
+ | `mastercss_scan_project` | Scan sources, check optional classes, and report scanner state, stylesheet entries, generated CSS metadata, and missing CSS diagnostics. Optionally inspect stylesheet token references and design policies. |
47
+ | `mastercss_manifest_query` | Query active manifest tokens, utilities, mixins, custom media, and token families. |
48
+ | `mastercss_css_compare` | Compare class/content output under one manifest, or two resolved project snapshots including definition changes and known consumers. |
48
49
  | `mastercss_lint_project` | Run class-list diagnostics for workspace files without writing files. |
49
50
  | `mastercss_lint_content` | Run class-list diagnostics on an in-memory source buffer without writing files. |
50
51
  | `mastercss_suggest_syntax` | Return language-service completions and hover context. |
@@ -52,16 +53,33 @@ Use an absolute `--root` path so the server resolves the intended workspace.
52
53
  | `mastercss_preview_directive_format` | Preview Master CSS directive formatting for files, or format in-memory content without writing. |
53
54
  | `mastercss_apply_preview` | Apply a preview after token, hash, and workspace checks. |
54
55
 
55
- ## Contributor tools
56
+ `@theme` defines utility tokens with direct custom-property declarations and optional `static` / `inline` modifiers. Native CSS controls scoped overrides and does not add utility candidates. Directive inspection and manifest queries report the same Rust-backed contract as the compiler and language service.
56
57
 
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
+ ## Inspect token use in stylesheets
58
59
 
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. |
60
+ Pass `designSystem` to `mastercss_scan_project` with the default project context to include a version 1 report in `result.data.designSystem`. This option cannot be combined with `context: "preset"`:
61
+
62
+ ```json
63
+ {
64
+ "designSystem": {
65
+ "version": 1,
66
+ "metadata": {
67
+ "--color-brand-old": {
68
+ "description": "Previous brand color",
69
+ "deprecated": true,
70
+ "replacement": "--color-brand"
71
+ }
72
+ },
73
+ "rawValuePolicy": {
74
+ "requireProperties": ["color", "background-color"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ The report covers project-owned CSS reached from the project's Master CSS entries, plus any `routeStylesheets` globs for stylesheets imported from JavaScript. It includes authored `var()` reference ranges in UTF-16 offsets, selector and at-rule ancestry, source importers, token-family collisions, each component class's attribute vocabulary (`components`), and diagnostics for the supplied metadata and raw-value policy. Ordinary native CSS can reference the same `@theme` tokens as Master CSS classes. Unregistered custom properties may come from native CSS or inheritance and are not automatically missing-token errors.
81
+
82
+ Metadata and policies are analysis inputs: they do not change generated CSS, manifests, or runtime programs. Unrecognized option fields are rejected. Omit `rawValuePolicy` to skip native declaration raw-value checks. Read `project.excluded` and `coverage`: package sources and files outside the project root are excluded, importer links include reference-only context, and the report does not establish DOM usage, cascade winners, computed styles, or browser behavior. `includeSnapshot` remains available in the same call.
65
83
 
66
84
  ## Prompt templates
67
85
 
@@ -69,21 +87,28 @@ These read-only tools are for Master CSS repository contributors, AI coding agen
69
87
  | --- | --- |
70
88
  | `debug-missing-css` | Debug scanner coverage, manifest loading, invalid syntax, native CSS pruning, and stylesheet entry configuration. |
71
89
  | `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.
90
+ | `migrate-to-mastercss` | Plan a conversion from CSS, CSS Modules, Sass, Tailwind CSS, CSS-in-JS or component-library styling to current Master CSS. |
77
91
 
78
92
  ## Machine-readable results
79
93
 
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.
94
+ Every tool returns a `version: 3` envelope with `metadata`, `diagnostics` and `result`. Success data lives in `result.data`; failures have `result.status: "error"` and an error code and message, and set MCP `isError`. JSON text matches `structuredContent`. Metadata identifies the actual context, manifest and package contracts; read the tool’s published output schema for its payload.
81
95
 
82
96
  ## Safety
83
97
 
84
98
  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.
85
99
 
100
+ Concurrent applies coordinate across MCP processes for the same OS user through a local lock directory under `~/.cache/mastercss`. Hash validation and writes run one apply at a time across all workspace roots. A waiting apply times out after 30 seconds without taking over a live writer; its token can be retried until it expires. External editors do not participate in this lock, so edits made after hash validation can still race with apply. Multi-file writes are sequential and do not provide rollback or crash atomicity.
101
+
86
102
  ## Related docs
87
103
 
88
- - [MCP Server guide](https://rc.css.master.co/guide/mcp-server)
89
- - [AI Coding guide](https://rc.css.master.co/guide/ai-coding)
104
+ - [MCP Server guide](https://css.master.co/guide/ai-coding#connect-the-mcp-server)
105
+ - [AI Coding guide](https://css.master.co/guide/ai-coding)
106
+
107
+
108
+ ## Compare a project before and after editing
109
+
110
+ Call `mastercss_scan_project` with `includeSnapshot: true` before editing and save `result.data.snapshot`. Repeat after editing, then pass the two objects to `mastercss_css_compare` as `beforeSnapshot` and `afterSnapshot`. Both are required; snapshot inputs cannot be mixed with class/content, `filePath` or `context` inputs.
111
+
112
+ The project result is `{ version: 3, mode: 'project', comparison }` inside the usual version 3 envelope. `comparison.version` is 1. It reports changed definitions, known class consumers and files, before/after generated rules, native references, output assets and delivery order. Read `coverage` even when there are no differences: dynamic sources, remote content, binary assets and final browser behavior are not exhaustively checked. Native consumers are conservative at stylesheet granularity.
113
+
114
+ These tools use the installed compiler. Check workspace package versions, language/binding versions and manifest metadata before using documentation or examples; use the project-local, lockfile-pinned MCP package for reproducible sessions.
package/dist/bin/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { startStdioServer } from "../server.js";
2
+ import { startMasterCSSMCPStdioServer } from "../server.js";
3
+ import { readFileSync } from "node:fs";
3
4
  import { dirname, resolve } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
- import { readFileSync } from "node:fs";
6
- //#region src/bin/index.ts
6
+ // src/bin/index.ts
7
7
  function readPackageVersion() {
8
8
  const directory = dirname(fileURLToPath(import.meta.url));
9
9
  try {
@@ -51,10 +51,9 @@ function parseArgs(argv) {
51
51
  return options;
52
52
  }
53
53
  try {
54
- await startStdioServer(parseArgs(process.argv.slice(2)));
54
+ await startMasterCSSMCPStdioServer(parseArgs(process.argv.slice(2)));
55
55
  } catch (error) {
56
56
  console.error(error instanceof Error ? error.message : String(error));
57
57
  process.exit(1);
58
58
  }
59
- //#endregion
60
59
  export {};
package/dist/classes.js CHANGED
@@ -1,52 +1,17 @@
1
- import { createMCPTextDocument, getLanguageId } from "./document.js";
2
- import { loadWorkspaceManifest } from "./project.js";
1
+ import { loadWorkspaceManifest, manifestMetadata, requireWorkspaceManifest } from "./project.js";
2
+ import { compactClassInspection, createMCPToolingSession } from "./tooling-session.js";
3
3
  import { resolveSourceFiles, scanProject } from "./scan.js";
4
- import { createRequire } from "node:module";
4
+ import { createMCPTextDocument, getLanguageId } from "./document.js";
5
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
6
+ // src/classes.ts
10
7
  const CLASS_EXTRACTION_VERSION = 1;
11
8
  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);
9
+ async function createClassInspectionState(context, options = {}) {
10
+ const manifest = await loadWorkspaceManifest(context, options.context);
11
+ const activeManifest = requireWorkspaceManifest(manifest);
15
12
  return {
16
13
  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
- }))
14
+ session: await createMCPToolingSession(activeManifest, manifest.nativeClassNames)
50
15
  };
51
16
  }
52
17
  function classifyExtractedClass(token, inspection, discovered) {
@@ -54,100 +19,118 @@ function classifyExtractedClass(token, inspection, discovered) {
54
19
  if (discovered?.usedNative.includes(token)) return "native-css";
55
20
  if (discovered?.invalid.includes(token)) return "invalid";
56
21
  if (discovered?.latent.includes(token)) return "latent";
57
- return inspection.valid ? "generated" : "unknown";
22
+ return inspection.matchStatus === "matched" ? "generated" : "unknown";
58
23
  }
59
- function extractFromContent(service, css, filePath, content, includeRules, discovered) {
24
+ function extractFromContent(session, filePath, content, includeRules, discovered) {
60
25
  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
- };
26
+ const extracted = session.extractSource({ files: [{
27
+ source: filePath,
28
+ content
29
+ }] }).files[0];
30
+ if (extracted.diagnostics.length) throw Object.assign(new Error(extracted.diagnostics[0].message), {
31
+ code: "SOURCE_PARSE_ERROR",
32
+ diagnostics: extracted.diagnostics
77
33
  });
34
+ return {
35
+ classes: extracted.occurrences.filter((occurrence) => occurrence.included).map((occurrence) => {
36
+ const position = {
37
+ token: occurrence.candidate,
38
+ range: occurrence.range,
39
+ raw: content.slice(occurrence.range.start, occurrence.range.end),
40
+ contextRange: occurrence.contextRange
41
+ };
42
+ const inspection = compactClassInspection(session, position.token, includeRules);
43
+ return {
44
+ raw: position.raw,
45
+ token: position.token,
46
+ range: position.range,
47
+ loc: {
48
+ start: document.positionAt(position.range.start),
49
+ end: document.positionAt(position.range.end)
50
+ },
51
+ contextRange: position.contextRange,
52
+ sourceKind: occurrence.contentKind,
53
+ provenance: occurrence,
54
+ status: classifyExtractedClass(position.token, inspection, discovered),
55
+ matchStatus: inspection.matchStatus,
56
+ cssSyntaxStatus: inspection.cssSyntaxStatus,
57
+ cssValueStatus: inspection.cssValueStatus,
58
+ browserSupport: inspection.browserSupport,
59
+ inspection
60
+ };
61
+ }),
62
+ occurrences: extracted.occurrences
63
+ };
78
64
  }
79
65
  async function extractClasses(context, options = {}) {
80
- const { manifest, css } = await createCSS(context);
81
- const service = createLanguageService(manifest);
66
+ const { manifest, session } = await createClassInspectionState(context, options);
82
67
  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);
68
+ try {
69
+ if (options.content !== void 0) {
70
+ const filePath = context.resolveVirtualPath(options.filePath || "index.html");
71
+ const extracted = extractFromContent(session, filePath, options.content, includeRules);
72
+ const { classes } = extracted;
73
+ return {
74
+ version: CLASS_EXTRACTION_VERSION,
75
+ root: context.root,
76
+ manifest: manifestMetadata(manifest),
77
+ inputs: {
78
+ mode: "content",
79
+ filePath
80
+ },
81
+ files: [{
82
+ filePath,
83
+ languageId: getLanguageId(filePath),
84
+ ...extracted
85
+ }],
86
+ summary: {
87
+ files: 1,
88
+ classes: classes.length,
89
+ matched: classes.filter((className) => className.matchStatus === "matched").length,
90
+ unmatched: classes.filter((className) => className.matchStatus !== "matched").length
91
+ }
92
+ };
93
+ }
94
+ const filePaths = await resolveSourceFiles(context, options.patterns);
95
+ const scan = await scanProject(context, {
96
+ context: options.context,
97
+ patterns: options.patterns,
98
+ includeCss: false
99
+ });
100
+ const scanFileByPath = new Map(scan.files.map((file) => [file.filePath, file]));
101
+ const files = await Promise.all(filePaths.map(async (filePath) => {
102
+ const content = await readFile(filePath, "utf8");
103
+ const discovered = scanFileByPath.get(filePath)?.discovered;
104
+ return {
105
+ filePath,
106
+ languageId: getLanguageId(filePath),
107
+ ...extractFromContent(session, filePath, content, includeRules, discovered)
108
+ };
109
+ }));
110
+ const classes = files.flatMap((file) => file.classes);
86
111
  return {
87
112
  version: CLASS_EXTRACTION_VERSION,
88
113
  root: context.root,
89
- manifest: {
90
- status: manifest.status,
91
- entries: manifest.entries,
92
- ...manifest.status === "error" ? { error: manifest.error } : {}
93
- },
114
+ manifest: manifestMetadata(manifest),
94
115
  inputs: {
95
- mode: "content",
96
- filePath
116
+ mode: "project",
117
+ patterns: options.patterns ?? scan.inputs.patterns,
118
+ files: filePaths
97
119
  },
98
- files: [{
99
- filePath,
100
- languageId: getLanguageId(filePath),
101
- classes
102
- }],
120
+ files,
121
+ scanner: { counts: scan.scanner.counts },
122
+ diagnostics: scan.diagnostics,
103
123
  summary: {
104
- files: 1,
124
+ files: files.length,
105
125
  classes: classes.length,
106
- valid: classes.filter((className) => className.valid).length,
107
- invalid: classes.filter((className) => !className.valid).length
126
+ matched: classes.filter((className) => className.matchStatus === "matched").length,
127
+ unmatched: classes.filter((className) => className.matchStatus !== "matched").length,
128
+ diagnostics: scan.summary.diagnostics
108
129
  }
109
130
  };
131
+ } finally {
132
+ session.dispose?.();
110
133
  }
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
134
  }
152
135
  function findClassOccurrences(report, className) {
153
136
  return report.files.flatMap((file) => {
@@ -169,56 +152,58 @@ async function traceClass(context, options) {
169
152
  const [scan, state] = await Promise.all([scanProject(context, {
170
153
  patterns: options.patterns,
171
154
  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: {
155
+ includeCss: options.includeCss,
156
+ context: options.context
157
+ }), createClassInspectionState(context, options)]);
158
+ try {
159
+ const inspection = compactClassInspection(state.session, options.className, true);
160
+ const missingResult = [...scan.missingCSS.present, ...scan.missingCSS.missing].find((result) => result.className === options.className);
161
+ const occurrences = findClassOccurrences(scan, options.className);
162
+ const status = missingResult?.status ?? (inspection.matchStatus === "matched" ? "present" : "missing");
163
+ const reason = missingResult?.reason ?? (inspection.matchStatus === "matched" ? "generated" : "not-detected");
164
+ return {
165
+ version: CLASS_TRACE_VERSION,
166
+ root: context.root,
167
+ manifest: manifestMetadata(state.manifest),
168
+ inputs: {
169
+ className: options.className,
170
+ patterns: options.patterns ?? scan.inputs.patterns
171
+ },
188
172
  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
173
  status,
215
174
  reason,
216
175
  detected: occurrences.length > 0,
217
- valid: inspection.valid,
218
- rules: inspection.rules.length,
219
- diagnostics: scan.diagnostics.length
220
- }
221
- };
176
+ occurrences,
177
+ inspection,
178
+ scanner: {
179
+ counts: scan.scanner.counts,
180
+ safelist: scan.scanner.classes.safelist.includes(options.className),
181
+ blocklist: scan.scanner.classes.blocklist.includes(options.className)
182
+ },
183
+ css: {
184
+ included: scan.css.included,
185
+ bytes: scan.css.bytes,
186
+ emittedGlobals: scan.css.emittedGlobals,
187
+ ...scan.css.text !== void 0 ? { text: scan.css.text } : {}
188
+ },
189
+ diagnostics: scan.diagnostics.filter((diagnostic) => {
190
+ if (diagnostic.sourceKind === "missing-css") return diagnostic.data?.className === options.className;
191
+ return diagnostic.message.includes(options.className);
192
+ }),
193
+ summary: {
194
+ status,
195
+ reason,
196
+ detected: occurrences.length > 0,
197
+ matchStatus: inspection.matchStatus,
198
+ cssSyntaxStatus: inspection.cssSyntaxStatus,
199
+ cssValueStatus: inspection.cssValueStatus,
200
+ browserSupport: inspection.browserSupport,
201
+ rules: inspection.rules.length,
202
+ diagnostics: scan.diagnostics.length
203
+ }
204
+ };
205
+ } finally {
206
+ state.session.dispose?.();
207
+ }
222
208
  }
223
- //#endregion
224
209
  export { extractClasses, traceClass };