@react-spa-scaffold/mcp 1.1.3 → 1.2.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.
- package/README.md +19 -15
- package/dist/features/definitions/api.d.ts +3 -0
- package/dist/features/definitions/api.d.ts.map +1 -0
- package/dist/features/definitions/api.js +20 -0
- package/dist/features/definitions/api.js.map +1 -0
- package/dist/features/definitions/ci.d.ts +3 -0
- package/dist/features/definitions/ci.d.ts.map +1 -0
- package/dist/features/definitions/ci.js +25 -0
- package/dist/features/definitions/ci.js.map +1 -0
- package/dist/features/definitions/core.d.ts +3 -0
- package/dist/features/definitions/core.d.ts.map +1 -0
- package/dist/features/definitions/core.js +76 -0
- package/dist/features/definitions/core.js.map +1 -0
- package/dist/features/definitions/devtools.d.ts +3 -0
- package/dist/features/definitions/devtools.d.ts.map +1 -0
- package/dist/features/definitions/devtools.js +53 -0
- package/dist/features/definitions/devtools.js.map +1 -0
- package/dist/features/definitions/forms.d.ts +3 -0
- package/dist/features/definitions/forms.d.ts.map +1 -0
- package/dist/features/definitions/forms.js +32 -0
- package/dist/features/definitions/forms.js.map +1 -0
- package/dist/features/definitions/i18n.d.ts +3 -0
- package/dist/features/definitions/i18n.d.ts.map +1 -0
- package/dist/features/definitions/i18n.js +50 -0
- package/dist/features/definitions/i18n.js.map +1 -0
- package/dist/features/definitions/index.d.ts +15 -0
- package/dist/features/definitions/index.d.ts.map +1 -0
- package/dist/features/definitions/index.js +15 -0
- package/dist/features/definitions/index.js.map +1 -0
- package/dist/features/definitions/mobile.d.ts +3 -0
- package/dist/features/definitions/mobile.d.ts.map +1 -0
- package/dist/features/definitions/mobile.js +19 -0
- package/dist/features/definitions/mobile.js.map +1 -0
- package/dist/features/definitions/observability.d.ts +3 -0
- package/dist/features/definitions/observability.d.ts.map +1 -0
- package/dist/features/definitions/observability.js +21 -0
- package/dist/features/definitions/observability.js.map +1 -0
- package/dist/features/definitions/performance.d.ts +3 -0
- package/dist/features/definitions/performance.d.ts.map +1 -0
- package/dist/features/definitions/performance.js +28 -0
- package/dist/features/definitions/performance.js.map +1 -0
- package/dist/features/definitions/routing.d.ts +3 -0
- package/dist/features/definitions/routing.d.ts.map +1 -0
- package/dist/features/definitions/routing.js +25 -0
- package/dist/features/definitions/routing.js.map +1 -0
- package/dist/features/definitions/state.d.ts +3 -0
- package/dist/features/definitions/state.d.ts.map +1 -0
- package/dist/features/definitions/state.js +27 -0
- package/dist/features/definitions/state.js.map +1 -0
- package/dist/features/definitions/testing.d.ts +3 -0
- package/dist/features/definitions/testing.d.ts.map +1 -0
- package/dist/features/definitions/testing.js +56 -0
- package/dist/features/definitions/testing.js.map +1 -0
- package/dist/features/definitions/theming.d.ts +3 -0
- package/dist/features/definitions/theming.d.ts.map +1 -0
- package/dist/features/definitions/theming.js +26 -0
- package/dist/features/definitions/theming.js.map +1 -0
- package/dist/features/definitions/ui.d.ts +3 -0
- package/dist/features/definitions/ui.d.ts.map +1 -0
- package/dist/features/definitions/ui.js +44 -0
- package/dist/features/definitions/ui.js.map +1 -0
- package/dist/features/index.d.ts +3 -2
- package/dist/features/index.d.ts.map +1 -1
- package/dist/features/index.js +2 -1
- package/dist/features/index.js.map +1 -1
- package/dist/features/registry.d.ts +3 -9
- package/dist/features/registry.d.ts.map +1 -1
- package/dist/features/registry.js +3 -554
- package/dist/features/registry.js.map +1 -1
- package/dist/features/types.d.ts +11 -9
- package/dist/features/types.d.ts.map +1 -1
- package/dist/features/types.js +24 -1
- package/dist/features/types.js.map +1 -1
- package/dist/server.d.ts +2 -7
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +22 -56
- package/dist/server.js.map +1 -1
- package/dist/server.test.d.ts +2 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +14 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/get-example.test.d.ts +5 -0
- package/dist/tools/get-example.test.d.ts.map +1 -0
- package/dist/tools/get-example.test.js +63 -0
- package/dist/tools/get-example.test.js.map +1 -0
- package/dist/tools/get-features.d.ts +0 -5
- package/dist/tools/get-features.d.ts.map +1 -1
- package/dist/tools/get-features.js +0 -2
- package/dist/tools/get-features.js.map +1 -1
- package/dist/tools/get-features.test.d.ts +5 -0
- package/dist/tools/get-features.test.d.ts.map +1 -0
- package/dist/tools/get-features.test.js +45 -0
- package/dist/tools/get-features.test.js.map +1 -0
- package/dist/tools/get-scaffold.d.ts +2 -2
- package/dist/tools/get-scaffold.test.d.ts +5 -0
- package/dist/tools/get-scaffold.test.d.ts.map +1 -0
- package/dist/tools/get-scaffold.test.js +194 -0
- package/dist/tools/get-scaffold.test.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +29 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +23 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +5 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/cache.d.ts +21 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +45 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/docs.test.d.ts +5 -0
- package/dist/utils/docs.test.d.ts.map +1 -0
- package/dist/utils/docs.test.js +37 -0
- package/dist/utils/docs.test.js.map +1 -0
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +19 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/examples/api-patterns.d.ts +3 -0
- package/dist/utils/examples/api-patterns.d.ts.map +1 -0
- package/dist/utils/examples/api-patterns.js +19 -0
- package/dist/utils/examples/api-patterns.js.map +1 -0
- package/dist/utils/examples/component-patterns.d.ts +3 -0
- package/dist/utils/examples/component-patterns.d.ts.map +1 -0
- package/dist/utils/examples/component-patterns.js +71 -0
- package/dist/utils/examples/component-patterns.js.map +1 -0
- package/dist/utils/examples/context-patterns.d.ts +3 -0
- package/dist/utils/examples/context-patterns.d.ts.map +1 -0
- package/dist/utils/examples/context-patterns.js +32 -0
- package/dist/utils/examples/context-patterns.js.map +1 -0
- package/dist/utils/examples/hook-patterns.d.ts +3 -0
- package/dist/utils/examples/hook-patterns.d.ts.map +1 -0
- package/dist/utils/examples/hook-patterns.js +55 -0
- package/dist/utils/examples/hook-patterns.js.map +1 -0
- package/dist/utils/examples/i18n-patterns.d.ts +3 -0
- package/dist/utils/examples/i18n-patterns.d.ts.map +1 -0
- package/dist/utils/examples/i18n-patterns.js +43 -0
- package/dist/utils/examples/i18n-patterns.js.map +1 -0
- package/dist/utils/examples/index.d.ts +12 -0
- package/dist/utils/examples/index.d.ts.map +1 -0
- package/dist/utils/examples/index.js +69 -0
- package/dist/utils/examples/index.js.map +1 -0
- package/dist/utils/examples/mobile-patterns.d.ts +3 -0
- package/dist/utils/examples/mobile-patterns.d.ts.map +1 -0
- package/dist/utils/examples/mobile-patterns.js +38 -0
- package/dist/utils/examples/mobile-patterns.js.map +1 -0
- package/dist/utils/examples/page-patterns.d.ts +3 -0
- package/dist/utils/examples/page-patterns.d.ts.map +1 -0
- package/dist/utils/examples/page-patterns.js +34 -0
- package/dist/utils/examples/page-patterns.js.map +1 -0
- package/dist/utils/examples/store-patterns.d.ts +3 -0
- package/dist/utils/examples/store-patterns.d.ts.map +1 -0
- package/dist/utils/examples/store-patterns.js +40 -0
- package/dist/utils/examples/store-patterns.js.map +1 -0
- package/dist/utils/examples/test-patterns.d.ts +3 -0
- package/dist/utils/examples/test-patterns.d.ts.map +1 -0
- package/dist/utils/examples/test-patterns.js +58 -0
- package/dist/utils/examples/test-patterns.js.map +1 -0
- package/dist/utils/examples/types.d.ts +17 -0
- package/dist/utils/examples/types.d.ts.map +1 -0
- package/dist/utils/examples/types.js +2 -0
- package/dist/utils/examples/types.js.map +1 -0
- package/dist/utils/examples/utility-patterns.d.ts +3 -0
- package/dist/utils/examples/utility-patterns.d.ts.map +1 -0
- package/dist/utils/examples/utility-patterns.js +77 -0
- package/dist/utils/examples/utility-patterns.js.map +1 -0
- package/dist/utils/index.d.ts +5 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -3
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -25
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +11 -40
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/paths.test.d.ts +2 -0
- package/dist/utils/paths.test.d.ts.map +1 -0
- package/dist/utils/paths.test.js +17 -0
- package/dist/utils/paths.test.js.map +1 -0
- package/dist/utils/scaffold/commands.d.ts +11 -0
- package/dist/utils/scaffold/commands.d.ts.map +1 -0
- package/dist/utils/scaffold/commands.js +22 -0
- package/dist/utils/scaffold/commands.js.map +1 -0
- package/dist/utils/scaffold/compute.d.ts +18 -0
- package/dist/utils/scaffold/compute.d.ts.map +1 -0
- package/dist/utils/scaffold/compute.js +78 -0
- package/dist/utils/scaffold/compute.js.map +1 -0
- package/dist/utils/scaffold/dependencies.d.ts +32 -0
- package/dist/utils/scaffold/dependencies.d.ts.map +1 -0
- package/dist/utils/scaffold/dependencies.js +107 -0
- package/dist/utils/scaffold/dependencies.js.map +1 -0
- package/dist/utils/scaffold/file-structure.d.ts +23 -0
- package/dist/utils/scaffold/file-structure.d.ts.map +1 -0
- package/dist/utils/scaffold/file-structure.js +62 -0
- package/dist/utils/scaffold/file-structure.js.map +1 -0
- package/dist/utils/scaffold/generators.d.ts +17 -0
- package/dist/utils/scaffold/generators.d.ts.map +1 -0
- package/dist/utils/{scaffold.js → scaffold/generators.js} +6 -254
- package/dist/utils/scaffold/generators.js.map +1 -0
- package/dist/utils/scaffold/index.d.ts +11 -0
- package/dist/utils/scaffold/index.d.ts.map +1 -0
- package/dist/utils/scaffold/index.js +11 -0
- package/dist/utils/scaffold/index.js.map +1 -0
- package/package.json +1 -1
- package/templates/.github/workflows/release.yml +1 -1
- package/templates/e2e/tests/home.spec.ts +2 -2
- package/templates/e2e/tests/language.spec.ts +7 -3
- package/dist/utils/examples.d.ts +0 -27
- package/dist/utils/examples.d.ts.map +0 -1
- package/dist/utils/examples.js +0 -438
- package/dist/utils/examples.js.map +0 -1
- package/dist/utils/scaffold.d.ts +0 -59
- package/dist/utils/scaffold.d.ts.map +0 -1
- package/dist/utils/scaffold.js.map +0 -1
package/dist/features/types.js
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for feature modules
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* All valid feature IDs as a const tuple.
|
|
6
|
+
* Used for compile-time type safety when referencing features.
|
|
7
|
+
*/
|
|
8
|
+
export const FEATURE_IDS = [
|
|
9
|
+
'core',
|
|
10
|
+
'mobile',
|
|
11
|
+
'routing',
|
|
12
|
+
'ui',
|
|
13
|
+
'forms',
|
|
14
|
+
'state',
|
|
15
|
+
'api',
|
|
16
|
+
'i18n',
|
|
17
|
+
'testing',
|
|
18
|
+
'performance',
|
|
19
|
+
'devtools',
|
|
20
|
+
'ci',
|
|
21
|
+
'observability',
|
|
22
|
+
'theming',
|
|
23
|
+
];
|
|
24
|
+
/** Type guard to check if a string is a valid FeatureId */
|
|
25
|
+
export function isFeatureId(value) {
|
|
26
|
+
return FEATURE_IDS.includes(value);
|
|
27
|
+
}
|
|
5
28
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/features/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/features/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM;IACN,QAAQ;IACR,SAAS;IACT,IAAI;IACJ,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,SAAS;IACT,aAAa;IACb,UAAU;IACV,IAAI;IACJ,eAAe;IACf,SAAS;CACD,CAAC;AAKX,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,WAAW,CAAC,QAAQ,CAAC,KAAkB,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Server for react-spa-scaffold scaffolding
|
|
3
|
-
*
|
|
4
|
-
* This server provides tools and resources for AI agents to scaffold
|
|
5
|
-
* new projects based on the react-spa-scaffold template.
|
|
2
|
+
* MCP Server for react-spa-scaffold scaffolding.
|
|
6
3
|
*/
|
|
7
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
-
/**
|
|
9
|
-
* Create and configure the MCP server
|
|
10
|
-
*/
|
|
5
|
+
/** Creates and configures the MCP server. */
|
|
11
6
|
export declare function createServer(): Server;
|
|
12
7
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAyCnE,6CAA6C;AAC7C,wBAAgB,YAAY,IAAI,MAAM,CA0DrC"}
|
package/dist/server.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Server for react-spa-scaffold scaffolding
|
|
3
|
-
*
|
|
4
|
-
* This server provides tools and resources for AI agents to scaffold
|
|
5
|
-
* new projects based on the react-spa-scaffold template.
|
|
2
|
+
* MCP Server for react-spa-scaffold scaffolding.
|
|
6
3
|
*/
|
|
7
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
5
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
-
import {
|
|
6
|
+
import { TOOL_REGISTRY, getToolDefinitions } from './tools/index.js';
|
|
10
7
|
import { getDocumentationResources, readDocumentation, isValidDocumentationUri } from './resources/index.js';
|
|
11
8
|
import { VERSION } from './version.js';
|
|
12
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
13
|
-
// Response Helpers
|
|
14
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
9
|
function jsonResponse(data) {
|
|
16
10
|
return {
|
|
17
11
|
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
@@ -23,9 +17,6 @@ function errorResponse(message) {
|
|
|
23
17
|
isError: true,
|
|
24
18
|
};
|
|
25
19
|
}
|
|
26
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
27
|
-
// Server Instructions
|
|
28
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
29
20
|
const SERVER_INSTRUCTIONS = `
|
|
30
21
|
react-spa-scaffold MCP Server - Project Scaffolding Assistant
|
|
31
22
|
|
|
@@ -41,63 +32,38 @@ Tips:
|
|
|
41
32
|
- Theming feature automatically includes state (requires Zustand)
|
|
42
33
|
- Check docs://conventions before generating code
|
|
43
34
|
`.trim();
|
|
44
|
-
/**
|
|
45
|
-
* Create and configure the MCP server
|
|
46
|
-
*/
|
|
35
|
+
/** Creates and configures the MCP server. */
|
|
47
36
|
export function createServer() {
|
|
48
|
-
const server = new Server({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
capabilities: {
|
|
53
|
-
tools: {},
|
|
54
|
-
resources: {},
|
|
55
|
-
},
|
|
56
|
-
instructions: SERVER_INSTRUCTIONS,
|
|
57
|
-
});
|
|
58
|
-
// ═══════════════════════════════════════════════════════════════════════
|
|
59
|
-
// TOOLS
|
|
60
|
-
// ═══════════════════════════════════════════════════════════════════════
|
|
61
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
62
|
-
return {
|
|
63
|
-
tools: [getFeaturesToolDefinition, getScaffoldToolDefinition, getExampleToolDefinition],
|
|
64
|
-
};
|
|
65
|
-
});
|
|
37
|
+
const server = new Server({ name: 'react-spa-scaffold-mcp', version: VERSION }, { capabilities: { tools: {}, resources: {} }, instructions: SERVER_INSTRUCTIONS });
|
|
38
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
39
|
+
tools: getToolDefinitions(),
|
|
40
|
+
}));
|
|
66
41
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
67
42
|
const { name, arguments: args } = request.params;
|
|
43
|
+
const toolConfig = TOOL_REGISTRY[name];
|
|
44
|
+
if (!toolConfig) {
|
|
45
|
+
return errorResponse(`Unknown tool: ${name}`);
|
|
46
|
+
}
|
|
68
47
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const parsed = getScaffoldSchema.safeParse(args);
|
|
74
|
-
if (!parsed.success) {
|
|
75
|
-
return errorResponse(`Invalid input: ${parsed.error.message}`);
|
|
76
|
-
}
|
|
77
|
-
return jsonResponse(await getScaffold(parsed.data));
|
|
48
|
+
if (toolConfig.schema) {
|
|
49
|
+
const parsed = toolConfig.schema.safeParse(args);
|
|
50
|
+
if (!parsed.success) {
|
|
51
|
+
return errorResponse(`Invalid input: ${parsed.error.message}`);
|
|
78
52
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!parsed.success) {
|
|
82
|
-
return errorResponse(`Invalid input: ${parsed.error.message}`);
|
|
83
|
-
}
|
|
84
|
-
return jsonResponse(await getExample(parsed.data));
|
|
85
|
-
}
|
|
86
|
-
default:
|
|
87
|
-
return errorResponse(`Unknown tool: ${name}`);
|
|
53
|
+
const result = await toolConfig.handler(parsed.data);
|
|
54
|
+
return jsonResponse(result);
|
|
88
55
|
}
|
|
56
|
+
const result = await toolConfig.handler();
|
|
57
|
+
return jsonResponse(result);
|
|
89
58
|
}
|
|
90
59
|
catch (error) {
|
|
91
60
|
const message = error instanceof Error ? error.message : String(error);
|
|
92
61
|
return errorResponse(`Error executing ${name}: ${message}`);
|
|
93
62
|
}
|
|
94
63
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
99
|
-
return { resources: getDocumentationResources() };
|
|
100
|
-
});
|
|
64
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
65
|
+
resources: getDocumentationResources(),
|
|
66
|
+
}));
|
|
101
67
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
102
68
|
const { uri } = request.params;
|
|
103
69
|
if (!isValidDocumentationUri(uri)) {
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../src/server.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createServer } from './server.js';
|
|
3
|
+
describe('MCP server', () => {
|
|
4
|
+
it('creates server instance', () => {
|
|
5
|
+
const server = createServer();
|
|
6
|
+
expect(server).toBeDefined();
|
|
7
|
+
});
|
|
8
|
+
it('has correct server name and version', () => {
|
|
9
|
+
const server = createServer();
|
|
10
|
+
// @ts-expect-error - accessing internal server info
|
|
11
|
+
expect(server._serverInfo?.name).toBe('react-spa-scaffold-mcp');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../src/server.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,oDAAoD;QACpD,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-example.test.d.ts","sourceRoot":"","sources":["../../src/tools/get-example.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for get_example tool
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { getExample, getExampleSchema } from './get-example.js';
|
|
6
|
+
describe('get_example tool', () => {
|
|
7
|
+
it('rejects unknown pattern via schema', () => {
|
|
8
|
+
const result = getExampleSchema.safeParse({ pattern: 'unknown-pattern' });
|
|
9
|
+
expect(result.success).toBe(false);
|
|
10
|
+
if (!result.success) {
|
|
11
|
+
expect(result.error.message).toContain('Invalid pattern');
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
it('returns code for valid pattern', async () => {
|
|
15
|
+
const result = await getExample({ pattern: 'zustand-store' });
|
|
16
|
+
expect(result).not.toHaveProperty('error');
|
|
17
|
+
expect(result).toHaveProperty('code');
|
|
18
|
+
expect(result).toHaveProperty('filePath');
|
|
19
|
+
expect(result).toHaveProperty('description');
|
|
20
|
+
expect(result).toHaveProperty('keyPoints');
|
|
21
|
+
});
|
|
22
|
+
it('returns usage hint', async () => {
|
|
23
|
+
const result = await getExample({ pattern: 'zustand-store' });
|
|
24
|
+
expect(result).toHaveProperty('usage');
|
|
25
|
+
expect(typeof result.usage).toBe('string');
|
|
26
|
+
});
|
|
27
|
+
it('returns actual code content', async () => {
|
|
28
|
+
const result = await getExample({ pattern: 'hook-state' });
|
|
29
|
+
expect(result.code).toBeTruthy();
|
|
30
|
+
expect(result.code?.length).toBeGreaterThan(50);
|
|
31
|
+
});
|
|
32
|
+
it('works for component patterns', async () => {
|
|
33
|
+
const result = await getExample({ pattern: 'component-ui' });
|
|
34
|
+
expect(result).not.toHaveProperty('error');
|
|
35
|
+
expect(result.code).toContain('export');
|
|
36
|
+
});
|
|
37
|
+
it('works for test patterns', async () => {
|
|
38
|
+
const result = await getExample({ pattern: 'test-component' });
|
|
39
|
+
expect(result).not.toHaveProperty('error');
|
|
40
|
+
expect(result.code).toContain('describe');
|
|
41
|
+
});
|
|
42
|
+
it('works for mobile-context pattern', async () => {
|
|
43
|
+
const result = await getExample({ pattern: 'mobile-context' });
|
|
44
|
+
expect(result).not.toHaveProperty('error');
|
|
45
|
+
expect(result.code).toContain('MobileProvider');
|
|
46
|
+
expect(result.code).toContain('useMobileContext');
|
|
47
|
+
expect(result.filePath).toBe('src/contexts/mobileContext.tsx');
|
|
48
|
+
});
|
|
49
|
+
it('works for use-media-query pattern', async () => {
|
|
50
|
+
const result = await getExample({ pattern: 'use-media-query' });
|
|
51
|
+
expect(result).not.toHaveProperty('error');
|
|
52
|
+
expect(result.code).toContain('BREAKPOINTS');
|
|
53
|
+
expect(result.code).toContain('useMediaQuery');
|
|
54
|
+
expect(result.filePath).toBe('src/hooks/useMediaQuery.ts');
|
|
55
|
+
});
|
|
56
|
+
it('works for use-touch-sizes pattern', async () => {
|
|
57
|
+
const result = await getExample({ pattern: 'use-touch-sizes' });
|
|
58
|
+
expect(result).not.toHaveProperty('error');
|
|
59
|
+
expect(result.code).toContain('useTouchSizes');
|
|
60
|
+
expect(result.filePath).toBe('src/hooks/useTouchSizes.ts');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=get-example.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-example.test.js","sourceRoot":"","sources":["../../src/tools/get-example.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,11 +11,6 @@ export interface FeatureSummary {
|
|
|
11
11
|
description: string;
|
|
12
12
|
required: boolean;
|
|
13
13
|
includes: string[];
|
|
14
|
-
hasOptions: boolean;
|
|
15
|
-
options?: Record<string, {
|
|
16
|
-
description: string;
|
|
17
|
-
default: boolean;
|
|
18
|
-
}>;
|
|
19
14
|
}
|
|
20
15
|
export declare function getFeatures(): FeatureSummary[];
|
|
21
16
|
export declare const getFeaturesToolDefinition: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-features.d.ts","sourceRoot":"","sources":["../../src/tools/get-features.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"get-features.d.ts","sourceRoot":"","sources":["../../src/tools/get-features.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,WAAW,IAAI,cAAc,EAAE,CAW9C;AAED,eAAO,MAAM,yBAAyB;;;;;;kBAqBlB,MAAM,EAAE;;CAE3B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-features.js","sourceRoot":"","sources":["../../src/tools/get-features.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-features.js","sourceRoot":"","sources":["../../src/tools/get-features.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAU7D,MAAM,UAAU,WAAW;IACzB,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAkB,EAAE;QAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;YACd,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE;;;;;;;;;;;;;;;uEAewD;IACrE,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,EAAc;KACzB;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-features.test.d.ts","sourceRoot":"","sources":["../../src/tools/get-features.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for get_features tool
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { getFeatures } from './get-features.js';
|
|
6
|
+
import { FEATURE_IDS } from '../features/index.js';
|
|
7
|
+
describe('get_features tool', () => {
|
|
8
|
+
it('returns all feature IDs', () => {
|
|
9
|
+
const features = getFeatures();
|
|
10
|
+
expect(features).toHaveLength(FEATURE_IDS.length);
|
|
11
|
+
expect(features.map((f) => f.id)).toEqual(expect.arrayContaining([...FEATURE_IDS]));
|
|
12
|
+
});
|
|
13
|
+
it('returns core as required', () => {
|
|
14
|
+
const features = getFeatures();
|
|
15
|
+
const core = features.find((f) => f.id === 'core');
|
|
16
|
+
expect(core).toBeDefined();
|
|
17
|
+
expect(core?.required).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
it('returns proper structure for each feature', () => {
|
|
20
|
+
const features = getFeatures();
|
|
21
|
+
for (const feature of features) {
|
|
22
|
+
expect(feature).toHaveProperty('id');
|
|
23
|
+
expect(feature).toHaveProperty('name');
|
|
24
|
+
expect(feature).toHaveProperty('description');
|
|
25
|
+
expect(feature).toHaveProperty('required');
|
|
26
|
+
expect(feature).toHaveProperty('includes');
|
|
27
|
+
expect(Array.isArray(feature.includes)).toBe(true);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it('mobile feature exists and is not required', () => {
|
|
31
|
+
const features = getFeatures();
|
|
32
|
+
const mobile = features.find((f) => f.id === 'mobile');
|
|
33
|
+
expect(mobile).toBeDefined();
|
|
34
|
+
expect(mobile?.required).toBe(false);
|
|
35
|
+
expect(mobile?.name).toBe('Mobile Support');
|
|
36
|
+
});
|
|
37
|
+
it('theming feature exists and is not required', () => {
|
|
38
|
+
const features = getFeatures();
|
|
39
|
+
const theming = features.find((f) => f.id === 'theming');
|
|
40
|
+
expect(theming).toBeDefined();
|
|
41
|
+
expect(theming?.required).toBe(false);
|
|
42
|
+
expect(theming?.name).toBe('Theming');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=get-features.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-features.test.js","sourceRoot":"","sources":["../../src/tools/get-features.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,9 +16,9 @@ export type GetScaffoldInput = z.infer<typeof getScaffoldSchema>;
|
|
|
16
16
|
export declare function getScaffold(input: GetScaffoldInput): Promise<{
|
|
17
17
|
projectName: string;
|
|
18
18
|
selectedFeatures: string[];
|
|
19
|
-
resolvedFeatures:
|
|
19
|
+
resolvedFeatures: ("core" | "mobile" | "routing" | "ui" | "forms" | "state" | "api" | "i18n" | "testing" | "performance" | "devtools" | "ci" | "observability" | "theming")[];
|
|
20
20
|
featureDetails: {
|
|
21
|
-
id:
|
|
21
|
+
id: "core" | "mobile" | "routing" | "ui" | "forms" | "state" | "api" | "i18n" | "testing" | "performance" | "devtools" | "ci" | "observability" | "theming";
|
|
22
22
|
name: string;
|
|
23
23
|
wasExplicitlySelected: boolean;
|
|
24
24
|
wasAutoIncluded: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-scaffold.test.d.ts","sourceRoot":"","sources":["../../src/tools/get-scaffold.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for get_scaffold tool
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { getScaffold, getScaffoldSchema } from './get-scaffold.js';
|
|
6
|
+
describe('get_scaffold tool', () => {
|
|
7
|
+
it('always includes core feature', async () => {
|
|
8
|
+
const result = await getScaffold({ features: ['routing'] });
|
|
9
|
+
expect(result.resolvedFeatures).toContain('core');
|
|
10
|
+
});
|
|
11
|
+
it('rejects invalid features via schema', () => {
|
|
12
|
+
const result = getScaffoldSchema.safeParse({
|
|
13
|
+
features: ['invalid-feature'],
|
|
14
|
+
});
|
|
15
|
+
expect(result.success).toBe(false);
|
|
16
|
+
if (!result.success) {
|
|
17
|
+
expect(result.error.message).toContain('Invalid features');
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
it('only includes explicitly selected features (plus core)', async () => {
|
|
21
|
+
const result = await getScaffold({ features: ['ui'] });
|
|
22
|
+
expect(result.resolvedFeatures).not.toContain('state');
|
|
23
|
+
expect(result.resolvedFeatures).not.toContain('mobile');
|
|
24
|
+
expect(result.resolvedFeatures).toContain('ui');
|
|
25
|
+
expect(result.resolvedFeatures).toContain('core');
|
|
26
|
+
});
|
|
27
|
+
it('theming feature auto-includes state feature', async () => {
|
|
28
|
+
const result = await getScaffold({ features: ['theming'] });
|
|
29
|
+
expect(result.resolvedFeatures).toContain('theming');
|
|
30
|
+
expect(result.resolvedFeatures).toContain('state');
|
|
31
|
+
expect(result.resolvedFeatures).toContain('core');
|
|
32
|
+
});
|
|
33
|
+
it('marks auto-included features correctly', async () => {
|
|
34
|
+
const result = await getScaffold({ features: ['theming'] });
|
|
35
|
+
const themingDetail = result.featureDetails.find((f) => f.id === 'theming');
|
|
36
|
+
const stateDetail = result.featureDetails.find((f) => f.id === 'state');
|
|
37
|
+
const coreDetail = result.featureDetails.find((f) => f.id === 'core');
|
|
38
|
+
// Theming was explicitly selected
|
|
39
|
+
expect(themingDetail?.wasExplicitlySelected).toBe(true);
|
|
40
|
+
expect(themingDetail?.wasAutoIncluded).toBe(false);
|
|
41
|
+
// State was auto-included (dependency of theming)
|
|
42
|
+
expect(stateDetail?.wasExplicitlySelected).toBe(false);
|
|
43
|
+
expect(stateDetail?.wasAutoIncluded).toBe(true);
|
|
44
|
+
// Core is always auto-included
|
|
45
|
+
expect(coreDetail?.wasExplicitlySelected).toBe(false);
|
|
46
|
+
expect(coreDetail?.wasAutoIncluded).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it('includes theming files when theming feature is selected', async () => {
|
|
49
|
+
const result = await getScaffold({ features: ['theming'] });
|
|
50
|
+
expect(result.fileStructure).toContain('src/hooks/useThemeEffect.ts');
|
|
51
|
+
expect(result.fileStructure).toContain('src/components/shared/ThemeToggle/ThemeToggle.tsx');
|
|
52
|
+
});
|
|
53
|
+
it('excludes theming files when only ui is selected', async () => {
|
|
54
|
+
const result = await getScaffold({ features: ['ui'] });
|
|
55
|
+
expect(result.fileStructure).not.toContain('src/hooks/useThemeEffect.ts');
|
|
56
|
+
expect(result.fileStructure).not.toContain('src/components/shared/ThemeToggle/ThemeToggle.tsx');
|
|
57
|
+
});
|
|
58
|
+
it('includes mobile files when mobile feature is selected', async () => {
|
|
59
|
+
const result = await getScaffold({ features: ['mobile'] });
|
|
60
|
+
expect(result.fileStructure).toContain('src/contexts/mobileContext.tsx');
|
|
61
|
+
expect(result.fileStructure).toContain('src/hooks/useMediaQuery.ts');
|
|
62
|
+
expect(result.fileStructure).toContain('src/hooks/useTouchSizes.ts');
|
|
63
|
+
});
|
|
64
|
+
it('excludes mobile files when only core is selected', async () => {
|
|
65
|
+
const result = await getScaffold({ features: [] });
|
|
66
|
+
expect(result.fileStructure).not.toContain('src/contexts/mobileContext.tsx');
|
|
67
|
+
expect(result.fileStructure).not.toContain('src/hooks/useMediaQuery.ts');
|
|
68
|
+
});
|
|
69
|
+
it('returns valid package.json structure', async () => {
|
|
70
|
+
const result = await getScaffold({ features: ['routing'], projectName: 'test-app' });
|
|
71
|
+
expect(result.packageJson).toBeDefined();
|
|
72
|
+
expect(result.packageJson.name).toBe('test-app');
|
|
73
|
+
expect(result.packageJson.dependencies).toBeDefined();
|
|
74
|
+
expect(result.packageJson.devDependencies).toBeDefined();
|
|
75
|
+
expect(result.packageJson.scripts).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
it('includes react in core dependencies', async () => {
|
|
78
|
+
const result = await getScaffold({ features: [] });
|
|
79
|
+
expect(result.packageJson.dependencies).toHaveProperty('react');
|
|
80
|
+
expect(result.packageJson.dependencies).toHaveProperty('react-dom');
|
|
81
|
+
});
|
|
82
|
+
it('includes routing dependencies when routing feature selected', async () => {
|
|
83
|
+
const result = await getScaffold({ features: ['routing'] });
|
|
84
|
+
expect(result.packageJson.dependencies).toHaveProperty('react-router');
|
|
85
|
+
});
|
|
86
|
+
it('returns file structure', async () => {
|
|
87
|
+
const result = await getScaffold({ features: ['routing'] });
|
|
88
|
+
expect(Array.isArray(result.fileStructure)).toBe(true);
|
|
89
|
+
expect(result.fileStructure.length).toBeGreaterThan(0);
|
|
90
|
+
});
|
|
91
|
+
it('returns setup commands', async () => {
|
|
92
|
+
const result = await getScaffold({ features: ['routing'] });
|
|
93
|
+
expect(Array.isArray(result.setupCommands)).toBe(true);
|
|
94
|
+
expect(result.setupCommands).toContain('npm install');
|
|
95
|
+
});
|
|
96
|
+
it('uses default project name when not provided', async () => {
|
|
97
|
+
const result = await getScaffold({ features: [] });
|
|
98
|
+
expect(result.projectName).toBe('my-app');
|
|
99
|
+
});
|
|
100
|
+
it('marks explicitly selected features correctly', async () => {
|
|
101
|
+
const result = await getScaffold({ features: ['ui', 'state'] });
|
|
102
|
+
const stateDetail = result.featureDetails.find((f) => f.id === 'state');
|
|
103
|
+
const uiDetail = result.featureDetails.find((f) => f.id === 'ui');
|
|
104
|
+
expect(stateDetail?.wasExplicitlySelected).toBe(true);
|
|
105
|
+
expect(stateDetail?.wasAutoIncluded).toBe(false);
|
|
106
|
+
expect(uiDetail?.wasExplicitlySelected).toBe(true);
|
|
107
|
+
expect(uiDetail?.wasAutoIncluded).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it('includes CLAUDE.md in file structure', async () => {
|
|
110
|
+
const result = await getScaffold({ features: [] });
|
|
111
|
+
expect(result.fileStructure).toContain('CLAUDE.md');
|
|
112
|
+
});
|
|
113
|
+
it('returns claudeMd content', async () => {
|
|
114
|
+
const result = await getScaffold({ features: [], projectName: 'test-project' });
|
|
115
|
+
expect(result.claudeMd).toBeDefined();
|
|
116
|
+
expect(typeof result.claudeMd).toBe('string');
|
|
117
|
+
expect(result.claudeMd).toContain('# CLAUDE.md');
|
|
118
|
+
expect(result.claudeMd).toContain('test-project');
|
|
119
|
+
});
|
|
120
|
+
it('claudeMd includes testing section when testing feature selected', async () => {
|
|
121
|
+
const result = await getScaffold({ features: ['testing'] });
|
|
122
|
+
expect(result.claudeMd).toContain('## Testing');
|
|
123
|
+
expect(result.claudeMd).toContain('Vitest');
|
|
124
|
+
});
|
|
125
|
+
it('claudeMd excludes testing section when testing feature not selected', async () => {
|
|
126
|
+
const result = await getScaffold({ features: [] });
|
|
127
|
+
expect(result.claudeMd).not.toContain('## Testing');
|
|
128
|
+
});
|
|
129
|
+
it('claudeMd includes UI section only when ui feature selected', async () => {
|
|
130
|
+
const withUi = await getScaffold({ features: ['ui'] });
|
|
131
|
+
const withoutUi = await getScaffold({ features: [] });
|
|
132
|
+
expect(withUi.claudeMd).toContain('## UI Components');
|
|
133
|
+
expect(withoutUi.claudeMd).not.toContain('## UI Components');
|
|
134
|
+
});
|
|
135
|
+
it('claudeMd includes i18n section only when i18n feature selected', async () => {
|
|
136
|
+
const withI18n = await getScaffold({ features: ['i18n'] });
|
|
137
|
+
const withoutI18n = await getScaffold({ features: [] });
|
|
138
|
+
expect(withI18n.claudeMd).toContain('## Translations');
|
|
139
|
+
expect(withoutI18n.claudeMd).not.toContain('## Translations');
|
|
140
|
+
});
|
|
141
|
+
it('claudeMd includes mobile section only when mobile feature selected', async () => {
|
|
142
|
+
const withMobile = await getScaffold({ features: ['mobile'] });
|
|
143
|
+
const withoutMobile = await getScaffold({ features: [] });
|
|
144
|
+
expect(withMobile.claudeMd).toContain('## Mobile & Responsive Design');
|
|
145
|
+
expect(withMobile.claudeMd).toContain('MobileProvider');
|
|
146
|
+
expect(withoutMobile.claudeMd).not.toContain('## Mobile & Responsive Design');
|
|
147
|
+
});
|
|
148
|
+
it('claudeMd includes theming section only when theming feature selected', async () => {
|
|
149
|
+
const withTheming = await getScaffold({ features: ['theming'] });
|
|
150
|
+
const withoutTheming = await getScaffold({ features: [] });
|
|
151
|
+
expect(withTheming.claudeMd).toContain('## Theming');
|
|
152
|
+
expect(withTheming.claudeMd).toContain('usePreferencesStore');
|
|
153
|
+
expect(withoutTheming.claudeMd).not.toContain('## Theming');
|
|
154
|
+
});
|
|
155
|
+
it('claudeMd project structure shows contexts when mobile is selected', async () => {
|
|
156
|
+
const withMobile = await getScaffold({ features: ['mobile'] });
|
|
157
|
+
const withoutMobile = await getScaffold({ features: [] });
|
|
158
|
+
expect(withMobile.claudeMd).toContain('contexts/');
|
|
159
|
+
expect(withoutMobile.claudeMd).not.toContain('contexts/');
|
|
160
|
+
});
|
|
161
|
+
it('returns config files with content', async () => {
|
|
162
|
+
const result = await getScaffold({ features: ['ui'] });
|
|
163
|
+
expect(typeof result.configFiles).toBe('object');
|
|
164
|
+
expect(result.configFiles).toHaveProperty('components.json');
|
|
165
|
+
});
|
|
166
|
+
it('includes radix-nova style in components.json', async () => {
|
|
167
|
+
const result = await getScaffold({ features: ['ui'] });
|
|
168
|
+
const componentsJson = JSON.parse(result.configFiles['components.json']);
|
|
169
|
+
expect(componentsJson.style).toBe('radix-nova');
|
|
170
|
+
});
|
|
171
|
+
it('always includes .gitignore in fileStructure (core feature)', async () => {
|
|
172
|
+
const result = await getScaffold({ features: [] });
|
|
173
|
+
expect(result.fileStructure).toContain('.gitignore');
|
|
174
|
+
});
|
|
175
|
+
it('always includes .gitignore content in configFiles', async () => {
|
|
176
|
+
const result = await getScaffold({ features: [] });
|
|
177
|
+
expect(result.configFiles).toHaveProperty('.gitignore');
|
|
178
|
+
expect(result.configFiles['.gitignore']).toContain('node_modules');
|
|
179
|
+
expect(result.configFiles['.gitignore']).toContain('dist/');
|
|
180
|
+
});
|
|
181
|
+
it('includes docs in structure and content', async () => {
|
|
182
|
+
const result = await getScaffold({ features: [] });
|
|
183
|
+
expect(result.fileStructure).toContain('docs/ARCHITECTURE.md');
|
|
184
|
+
expect(result.fileStructure).not.toContain('docs/TESTING.md');
|
|
185
|
+
expect(result.docs['docs/ARCHITECTURE.md']).toBeDefined();
|
|
186
|
+
expect(result.docs['docs/TESTING.md']).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
it('adds feature-specific docs when feature selected', async () => {
|
|
189
|
+
const result = await getScaffold({ features: ['testing'] });
|
|
190
|
+
expect(result.fileStructure).toContain('docs/TESTING.md');
|
|
191
|
+
expect(result.docs['docs/TESTING.md']).toContain('# Testing Guidelines');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=get-scaffold.test.js.map
|