@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/dist/project.js CHANGED
@@ -1,42 +1,101 @@
1
+ import { resolveMasterCSSWorkspacePackages } from "./internal/src/workspace.js";
1
2
  import { getErrorMessage } from "./result.js";
2
- import { loadProjectManifest } from "@master/css-project/manifest";
3
- import { findCSSManifestEntryFiles } from "@master/css-project/entries";
4
- import { resolveMasterCSSWorkspacePackages } from "@master/css-project/workspace";
5
- //#region src/project.ts
6
- async function loadWorkspaceManifest(context) {
3
+ import { createHash } from "node:crypto";
4
+ import { getToolingBindingInfo } from "@master/css-tooling/node";
5
+ import { discoverManifestEntries, loadProjectManifest } from "@master/css-compiler/project";
6
+ import defaultManifestJSON from "@master/css-preset/default-manifest.json" with { type: "json" };
7
+ // src/project.ts
8
+ const defaultManifest = defaultManifestJSON;
9
+ function manifestFingerprint(manifest) {
10
+ return createHash("sha256").update(JSON.stringify(manifest)).digest("hex");
11
+ }
12
+ async function loadWorkspaceManifest(context, requested = "project", explicitEntries) {
13
+ let entries = [];
14
+ const nativeClassNames = [];
15
+ let versions;
7
16
  try {
8
- const result = await loadProjectManifest(context.root);
17
+ versions = getToolingBindingInfo();
18
+ if (requested === "project") {
19
+ entries = explicitEntries ?? await discoverManifestEntries({ root: context.root });
20
+ if (!entries.length) return {
21
+ status: "error",
22
+ reason: "entry-not-found",
23
+ entries,
24
+ nativeClassNames,
25
+ dependencies: [],
26
+ warnings: [],
27
+ diagnostics: [],
28
+ context: requested,
29
+ versions,
30
+ fingerprint: null,
31
+ error: "No project CSS entry was found. Add a project entry or explicitly select context: \"preset\"."
32
+ };
33
+ }
34
+ const result = requested === "preset" ? {
35
+ manifest: defaultManifest,
36
+ nativeClassNames,
37
+ entries: [],
38
+ dependencies: [],
39
+ diagnostics: []
40
+ } : await loadProjectManifest({
41
+ root: context.root,
42
+ entries,
43
+ baseManifest: {
44
+ version: 6,
45
+ languageVersion: 18
46
+ }
47
+ });
9
48
  return {
10
49
  status: "loaded",
50
+ context: requested,
51
+ versions,
52
+ fingerprint: manifestFingerprint(result.manifest),
11
53
  manifest: result.manifest,
54
+ nativeClassNames: result.nativeClassNames,
12
55
  entries: result.entries,
13
56
  dependencies: result.dependencies ?? [],
14
- warnings: result.warnings ?? []
57
+ diagnostics: result.diagnostics,
58
+ warnings: result.diagnostics.map((diagnostic) => diagnostic.message)
15
59
  };
16
60
  } catch (error) {
17
61
  return {
18
62
  status: "error",
19
- entries: [],
63
+ reason: "entry-load-failed",
64
+ context: requested,
65
+ versions,
66
+ fingerprint: null,
67
+ entries,
68
+ nativeClassNames,
20
69
  dependencies: [],
21
70
  warnings: [],
71
+ diagnostics: error && typeof error === "object" && "diagnostics" in error && Array.isArray(error.diagnostics) ? error.diagnostics : error && typeof error === "object" && "diagnostic" in error ? [error.diagnostic] : [],
22
72
  error: getErrorMessage(error)
23
73
  };
24
74
  }
25
75
  }
76
+ function requireWorkspaceManifest(result) {
77
+ if (result.status === "loaded") return result.manifest;
78
+ throw Object.assign(new Error(result.error), {
79
+ code: result.reason === "entry-not-found" ? "PROJECT_ENTRY_NOT_FOUND" : "PROJECT_MANIFEST_LOAD_FAILED",
80
+ context: result.context,
81
+ manifest: manifestMetadata(result),
82
+ diagnostics: result.diagnostics
83
+ });
84
+ }
85
+ function manifestMetadata(result) {
86
+ const { manifest: _manifest, nativeClassNames: _nativeClassNames, ...metadata } = {
87
+ manifest: void 0,
88
+ ...result
89
+ };
90
+ return metadata;
91
+ }
26
92
  async function getWorkspaceInfo(context) {
27
- const [manifest, entries] = await Promise.all([loadWorkspaceManifest(context), findCSSManifestEntryFiles(context.root).catch(() => [])]);
93
+ const manifest = await loadWorkspaceManifest(context);
28
94
  return {
29
95
  root: context.root,
30
96
  roots: context.roots,
31
97
  packages: resolveMasterCSSWorkspacePackages(context.root),
32
- manifest: {
33
- status: manifest.status,
34
- entries: manifest.status === "loaded" ? manifest.entries : entries,
35
- dependencies: manifest.dependencies,
36
- warnings: manifest.warnings,
37
- ...manifest.status === "error" ? { error: manifest.error } : {}
38
- }
98
+ manifest: manifestMetadata(manifest)
39
99
  };
40
100
  }
41
- //#endregion
42
- export { getWorkspaceInfo, loadWorkspaceManifest };
101
+ export { getWorkspaceInfo, loadWorkspaceManifest, manifestFingerprint, manifestMetadata, requireWorkspaceManifest };
package/dist/result.js CHANGED
@@ -1,22 +1,81 @@
1
- //#region src/result.ts
1
+ const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
2
+ function normalizeResult(value) {
3
+ const payload = record(value);
4
+ const manifest = record(payload.manifest);
5
+ const context = record(payload.context);
6
+ const semantic = context.context ? context : manifest.context ? manifest : typeof payload.context === "string" ? payload : {};
7
+ const diagnostics = Array.isArray(payload.diagnostics) ? payload.diagnostics : [];
8
+ const failed = payload.status === "error";
9
+ const error = record(payload.error);
10
+ return {
11
+ version: 3,
12
+ metadata: {
13
+ versions: semantic.versions ?? null,
14
+ context: semantic.context ?? (typeof payload.context === "string" ? payload.context : null),
15
+ manifestFingerprint: semantic.fingerprint ?? null,
16
+ entries: semantic.entries ?? [],
17
+ dependencies: semantic.dependencies ?? []
18
+ },
19
+ diagnostics,
20
+ result: failed ? {
21
+ status: "error",
22
+ error: {
23
+ code: error.code ?? "TOOL_EXECUTION_FAILED",
24
+ message: error.message ?? "Tool execution failed"
25
+ }
26
+ } : {
27
+ status: "success",
28
+ data: value
29
+ }
30
+ };
31
+ }
2
32
  function toJSONText(value) {
3
33
  return JSON.stringify(value, null, 2);
4
34
  }
5
35
  function jsonToolResult(value) {
6
- return { content: [{
7
- type: "text",
8
- text: toJSONText(value)
9
- }] };
36
+ const text = toJSONText(normalizeResult(value));
37
+ const structuredContent = JSON.parse(text);
38
+ return {
39
+ structuredContent,
40
+ content: [{
41
+ type: "text",
42
+ text
43
+ }],
44
+ ...record(structuredContent.result).status === "error" ? { isError: true } : {}
45
+ };
10
46
  }
11
47
  function jsonResourceResult(uri, value) {
12
48
  return { contents: [{
13
49
  uri: String(uri),
14
50
  mimeType: "application/json",
15
- text: toJSONText(value)
51
+ text: toJSONText(normalizeResult(value))
16
52
  }] };
17
53
  }
18
54
  function getErrorMessage(error) {
19
55
  return error instanceof Error ? error.message : String(error);
20
56
  }
21
- //#endregion
22
- export { getErrorMessage, jsonResourceResult, jsonToolResult, toJSONText };
57
+ async function executeTool(operation) {
58
+ try {
59
+ return jsonToolResult(await operation());
60
+ } catch (error) {
61
+ const detail = record(error);
62
+ const code = typeof detail.code === "string" ? detail.code : "TOOL_EXECUTION_FAILED";
63
+ const message = getErrorMessage(error);
64
+ return jsonToolResult({
65
+ status: "error",
66
+ error: {
67
+ code,
68
+ message
69
+ },
70
+ context: detail.context,
71
+ manifest: detail.manifest,
72
+ diagnostics: [{
73
+ code,
74
+ phase: "compiler",
75
+ severity: "error",
76
+ message
77
+ }, ...Array.isArray(detail.diagnostics) ? detail.diagnostics : []]
78
+ });
79
+ }
80
+ }
81
+ export { executeTool, getErrorMessage, jsonResourceResult, jsonToolResult, toJSONText };
package/dist/scan.js CHANGED
@@ -1,115 +1,136 @@
1
- import { loadWorkspaceManifest } from "./project.js";
2
- import { createRequire } from "node:module";
3
- import { inspectMasterCSSClass } from "@master/css-engine/inspect";
4
- import { createCSSWithNativeDeclarations } from "@master/css-validator/native-declaration";
5
- import { resolve } from "node:path";
1
+ import { loadWorkspaceManifest, manifestMetadata, requireWorkspaceManifest } from "./project.js";
2
+ import { compactClassInspection, createMCPToolingSession } from "./tooling-session.js";
3
+ import { extname, resolve } from "node:path";
4
+ import { createToolingSessionSync } from "@master/css-tooling/node";
5
+ import { SourcePolicy } from "@master/css-tooling/scanner/node";
6
6
  import fg from "fast-glob";
7
- import { createMasterCSSInspectionReport } from "@master/css-diagnostics";
8
- import { parseHTML } from "@master/css-server";
9
- //#region src/scan.ts
10
- const DEFAULT_SOURCE_PATTERNS = ["**/*.{html,htm,js,jsx,cjs,ts,tsx,mts,cts,svelte,astro,vue,md,mdx,pug,php}"];
7
+ import { renderClassNamesSync } from "@master/css/node";
8
+ import { captureMasterCSSProject, createMasterCSSInspectionReport } from "@master/css-compiler/diagnostics";
9
+ // src/scan.ts
10
+ const DEFAULT_SOURCE_PATTERNS = ["**/*.{html,htm,js,jsx,mjs,cjs,ts,tsx,mts,cts,svelte,astro,vue,md,mdx,pug,php}"];
11
11
  const DEFAULT_IGNORE_PATTERNS = ["**/node_modules/**", "node_modules"];
12
- const defaultManifest = createRequire(import.meta.url)("@master/css-preset/default-manifest.json");
13
12
  async function resolveSourceFiles(context, patterns = DEFAULT_SOURCE_PATTERNS, ignore = DEFAULT_IGNORE_PATTERNS) {
14
13
  context.validateGlobPatterns(patterns);
15
14
  const sources = await fg(patterns, {
16
15
  cwd: context.root,
17
16
  ignore,
18
- onlyFiles: true
17
+ onlyFiles: true,
18
+ followSymbolicLinks: false
19
19
  });
20
- return Promise.all(sources.map((source) => context.resolveExistingFile(source)));
20
+ const policy = new SourcePolicy(context.root, { exclude: ignore });
21
+ return Promise.all(sources.filter((source) => policy.accepts(source, patterns !== DEFAULT_SOURCE_PATTERNS)).map((source) => context.resolveExistingFile(source)));
21
22
  }
22
23
  async function scanProject(context, options = {}) {
23
- const [report, manifest] = await Promise.all([createMasterCSSInspectionReport({
24
+ if (options.designSystem && options.context === "preset") throw new Error("designSystem inspection requires project context; omit context or select \"project\".");
25
+ if (options.routeStylesheets?.length && !options.designSystem) throw new Error("routeStylesheets requires designSystem.");
26
+ let routeStylesheets;
27
+ if (options.routeStylesheets?.length) {
28
+ context.validateGlobPatterns(options.routeStylesheets);
29
+ const sources = await fg(options.routeStylesheets, {
30
+ cwd: context.root,
31
+ ignore: DEFAULT_IGNORE_PATTERNS,
32
+ onlyFiles: true,
33
+ followSymbolicLinks: false
34
+ });
35
+ if (sources.some((source) => !source.endsWith(".css"))) throw new Error("routeStylesheets must match .css files only.");
36
+ routeStylesheets = await Promise.all(sources.sort().map((source) => context.resolveExistingFile(source)));
37
+ }
38
+ const manifest = await loadWorkspaceManifest(context, options.context);
39
+ const inspectionOptions = {
40
+ manifest: requireWorkspaceManifest(manifest),
24
41
  cwd: context.root,
25
42
  patterns: options.patterns,
26
43
  classes: options.classes,
27
44
  includeCss: options.includeCss,
45
+ designSystem: options.designSystem,
46
+ routeStylesheets,
28
47
  resolveExistingFile: (filePath) => context.resolveExistingFile(filePath),
29
48
  validatePatterns: (patterns) => context.validateGlobPatterns(patterns)
30
- }), loadWorkspaceManifest(context)]);
49
+ };
50
+ if (options.includeSnapshot) {
51
+ const { report, snapshot } = await captureMasterCSSProject(inspectionOptions);
52
+ return {
53
+ ...report,
54
+ root: context.root,
55
+ manifest: manifestMetadata(manifest),
56
+ snapshot
57
+ };
58
+ }
31
59
  return {
32
- ...report,
60
+ ...await createMasterCSSInspectionReport(inspectionOptions),
33
61
  root: context.root,
34
- manifest: {
35
- status: manifest.status,
36
- entries: manifest.entries,
37
- dependencies: manifest.dependencies,
38
- warnings: manifest.warnings,
39
- ...manifest.status === "error" ? { error: manifest.error } : {}
40
- }
62
+ manifest: manifestMetadata(manifest)
41
63
  };
42
64
  }
43
65
  async function renderCSS(context, options) {
44
- const manifest = await loadWorkspaceManifest(context);
45
- const css = createCSSWithNativeDeclarations(manifest.status === "loaded" ? manifest.manifest : defaultManifest);
46
- const classes = options.html ? parseHTML(options.html).classes : (options.classList ?? "").split(/\s+/).map((className) => className.trim()).filter(Boolean);
47
- const invalid = [];
48
- for (const className of classes) {
49
- const rules = css.generate(className);
50
- if (rules.length) for (const rule of rules) rule.layer.insert(rule);
51
- else invalid.push(className);
66
+ const manifest = await loadWorkspaceManifest(context, options.context);
67
+ let classes;
68
+ if (options.html) {
69
+ const tooling = createToolingSessionSync({
70
+ manifest: requireWorkspaceManifest(manifest),
71
+ nativeClassNames: manifest.nativeClassNames
72
+ });
73
+ try {
74
+ classes = [...tooling.extractSource({ files: [{
75
+ source: "index.html",
76
+ content: options.html,
77
+ kind: "html"
78
+ }] }).files[0]?.candidates ?? []];
79
+ } finally {
80
+ tooling.dispose();
81
+ }
82
+ } else classes = (options.classList ?? "").split(/\s+/).map((className) => className.trim()).filter(Boolean);
83
+ const rendered = renderClassNamesSync(classes, { manifest: requireWorkspaceManifest(manifest) });
84
+ const tooling = createMCPToolingSession(requireWorkspaceManifest(manifest), manifest.nativeClassNames);
85
+ let inspections;
86
+ try {
87
+ inspections = classes.map((className) => compactClassInspection(tooling, className, true));
88
+ } finally {
89
+ tooling.dispose();
52
90
  }
53
91
  return {
54
- manifest: {
55
- status: manifest.status,
56
- entries: manifest.entries,
57
- ...manifest.status === "error" ? { error: manifest.error } : {}
58
- },
92
+ manifest: manifestMetadata(manifest),
59
93
  classes,
60
- invalid,
94
+ inspections,
95
+ diagnostics: inspections.flatMap((inspection) => inspection.diagnostics ?? []),
96
+ invalid: rendered.invalidClassNames,
61
97
  css: {
62
- bytes: css.text.length,
63
- text: css.text
98
+ bytes: Buffer.byteLength(rendered.cssText, "utf8"),
99
+ text: rendered.cssText
64
100
  }
65
101
  };
66
102
  }
67
103
  async function inspectClass(context, options) {
68
- const manifest = await loadWorkspaceManifest(context);
69
- const inspection = inspectMasterCSSClass(createCSSWithNativeDeclarations(manifest.status === "loaded" ? manifest.manifest : defaultManifest), options.className, options.mode);
70
- const rules = inspection.rules.map((rule) => ({
71
- className: rule.name,
72
- layer: rule.layerName,
73
- type: rule.type,
74
- text: rule.text
75
- }));
76
- return {
77
- manifest: {
78
- status: manifest.status,
79
- entries: manifest.entries,
80
- ...manifest.status === "error" ? { error: manifest.error } : {}
81
- },
82
- className: options.className,
83
- mode: options.mode,
84
- valid: rules.length > 0,
85
- base: inspection.base,
86
- suffix: inspection.suffix,
87
- key: inspection.key,
88
- value: inspection.value,
89
- keyToken: inspection.keyToken,
90
- valueToken: inspection.valueToken,
91
- stateToken: inspection.stateToken,
92
- important: inspection.important,
93
- matcherTypes: inspection.matcherTypes,
94
- variables: inspection.variableEntries.map(({ key, variable }) => ({
95
- key,
96
- variable
97
- })),
98
- rules,
99
- css: rules.map((rule) => rule.text).join("")
100
- };
104
+ const manifest = await loadWorkspaceManifest(context, options.context);
105
+ const session = createMCPToolingSession(requireWorkspaceManifest(manifest), manifest.nativeClassNames);
106
+ try {
107
+ const inspection = session.inspectClassName(options.className);
108
+ const compact = compactClassInspection(session, options.className, true, inspection);
109
+ return {
110
+ manifest: manifestMetadata(manifest),
111
+ ...compact,
112
+ css: inspection.rules.map((rule) => rule.text).join("")
113
+ };
114
+ } finally {
115
+ session.dispose?.();
116
+ }
101
117
  }
102
118
  async function previewGeneratedCSS(context, options) {
119
+ if (extname(options.outputPath).toLowerCase() !== ".css") throw new Error("outputPath must name a .css file.");
103
120
  const report = await scanProject(context, {
104
121
  patterns: options.patterns,
105
122
  classes: options.classes,
106
- includeCss: true
123
+ includeCss: true,
124
+ context: options.context
107
125
  });
108
126
  const preview = await context.createPreview([{
109
127
  filePath: resolve(context.root, options.outputPath),
110
128
  afterText: report.css.text ?? ""
111
129
  }], options.ttlMs);
112
130
  return {
131
+ manifest: report.manifest,
132
+ diagnostics: report.diagnostics,
133
+ inspections: report.inspections,
113
134
  mode: "generated-css",
114
135
  outputPath: await context.resolveWritableFile(options.outputPath),
115
136
  preview,
@@ -122,5 +143,4 @@ async function previewGeneratedCSS(context, options) {
122
143
  }
123
144
  };
124
145
  }
125
- //#endregion
126
146
  export { inspectClass, previewGeneratedCSS, renderCSS, resolveSourceFiles, scanProject };