@starklab/stark-mcp 0.1.0 → 0.2.1
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/package.json +10 -4
- package/src/adopt/adoptScanReport.js +124 -0
- package/src/adopt/catalog.js +26 -6
- package/src/adopt/foreignDiscoveryResolver.js +276 -0
- package/src/adopt/foreignPropSchemaResolver.js +134 -0
- package/src/adopt/foreignScanReport.js +210 -0
- package/src/adopt/foreignScoringResolver.js +192 -0
- package/src/adopt/foreignSystemConfig.js +356 -0
- package/src/adopt/installedPackageDiscoveryResolver.js +602 -0
- package/src/adopt/installedPackagePropSchemaResolver.js +279 -0
- package/src/adopt/installedPackageScoringResolver.js +153 -0
- package/src/adopt/installedSystemAutoDetector.js +51 -0
- package/src/adopt/installedSystemScan.js +101 -0
- package/src/adopt/jsxOpportunityHelpers.js +99 -0
- package/src/adopt/moduleGraph.js +39 -8
- package/src/adopt/opportunityResolver.js +255 -0
- package/src/adopt/opportunitySignaturesNative.js +47 -0
- package/src/adopt/usageRulesResolver.js +298 -0
- package/src/adopt/vecnaMaterializer.js +165 -0
- package/src/adopt/vecnaVerifier.js +127 -0
- package/src/cli.js +407 -1
- package/src/server.js +38 -14
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Home.jsx +0 -21
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Menu.jsx +0 -13
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Profile.jsx +0 -11
- package/src/adopt/__fixtures__/dominion-fixture-app/src/theme.css +0 -34
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/AppButton.jsx +0 -8
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/BrandButton.jsx +0 -9
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/CardBase.jsx +0 -9
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/FeatureCard.jsx +0 -7
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/SectionCard.jsx +0 -12
- package/src/adopt/dominionFixture.test.js +0 -165
- package/src/adopt/propApiResolver.test.js +0 -229
- package/src/adopt/referenceResolver.test.js +0 -213
- package/src/adopt/rnTailwindResolver.test.js +0 -263
- package/src/adopt/rnTokenAliasResolver.test.js +0 -260
- package/src/adopt/tailwindResolver.test.js +0 -178
- package/src/adopt/targetDiscovery.test.js +0 -227
- package/src/adopt/tokenAliasResolver.test.js +0 -319
- package/src/adopt/wrapperResolver.test.js +0 -324
- package/src/data.test.js +0 -231
package/src/data.test.js
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync, readdirSync } from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
toSlug,
|
|
9
|
-
listComponents,
|
|
10
|
-
getComponentUsage,
|
|
11
|
-
getComponentProps,
|
|
12
|
-
getComponentTokens,
|
|
13
|
-
buildManifest,
|
|
14
|
-
getLayoutCatalog,
|
|
15
|
-
getLayoutSchema,
|
|
16
|
-
validateLayout,
|
|
17
|
-
getGenerationProtocol,
|
|
18
|
-
ejectComponent,
|
|
19
|
-
} from './data.js';
|
|
20
|
-
|
|
21
|
-
describe('toSlug', () => {
|
|
22
|
-
it('converts PascalCase to kebab-case', () => {
|
|
23
|
-
expect(toSlug('TextInput')).toBe('text-input');
|
|
24
|
-
expect(toSlug('BadgeStatus')).toBe('badge-status');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('is idempotent on already-kebab input', () => {
|
|
28
|
-
expect(toSlug('text-input')).toBe('text-input');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('is case-insensitive for plain names', () => {
|
|
32
|
-
expect(toSlug('button')).toBe('button');
|
|
33
|
-
expect(toSlug('Button')).toBe('button');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe('listComponents', () => {
|
|
38
|
-
it('includes Button with a resolved usage status', () => {
|
|
39
|
-
const components = listComponents();
|
|
40
|
-
const button = components.find(c => c.slug === 'button');
|
|
41
|
-
expect(button).toBeTruthy();
|
|
42
|
-
expect(button.name).toBe('Button');
|
|
43
|
-
expect(button.status).toBe('stable');
|
|
44
|
-
expect(button.description).toContain('action');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('is sorted by name', () => {
|
|
48
|
-
const names = listComponents().map(c => c.name);
|
|
49
|
-
const sorted = [...names].sort((a, b) => a.localeCompare(b));
|
|
50
|
-
expect(names).toEqual(sorted);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('getComponentUsage', () => {
|
|
55
|
-
it('returns dos/donts for a known component, case-insensitively', () => {
|
|
56
|
-
const usage = getComponentUsage('button');
|
|
57
|
-
expect(Array.isArray(usage.dos)).toBe(true);
|
|
58
|
-
expect(Array.isArray(usage.donts)).toBe(true);
|
|
59
|
-
expect(usage.dos.length).toBeGreaterThan(0);
|
|
60
|
-
|
|
61
|
-
const same = getComponentUsage('Button');
|
|
62
|
-
expect(same.dos).toEqual(usage.dos);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('throws a helpful error for an unknown component', () => {
|
|
66
|
-
expect(() => getComponentUsage('NotAComponent')).toThrow(/list_components/);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('getComponentProps', () => {
|
|
71
|
-
it('returns the propMap for Button on web', () => {
|
|
72
|
-
const props = getComponentProps('Button', 'web');
|
|
73
|
-
expect(props.component).toBe('Button');
|
|
74
|
-
const variant = props.propMap.find(p => p.react === 'variant');
|
|
75
|
-
expect(variant).toBeTruthy();
|
|
76
|
-
expect(variant.values).toMatchObject({ primary: 'primary' });
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('throws for an unsupported platform', () => {
|
|
80
|
-
expect(() => getComponentProps('Button', 'not-a-platform')).toThrow(/does not support platform/);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('omits tokens by default', () => {
|
|
84
|
-
const props = getComponentProps('Button', 'web');
|
|
85
|
-
expect(props.tokens).toBeUndefined();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('includes token JSON when includeTokens is true', () => {
|
|
89
|
-
const props = getComponentProps('Button', 'web', true);
|
|
90
|
-
expect(props.tokens).toBeTruthy();
|
|
91
|
-
expect(props.tokens.button).toBeTruthy();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('returns null tokens for a component with no token file', () => {
|
|
95
|
-
const props = getComponentProps('Container', 'web', true);
|
|
96
|
-
expect(props.tokens).toBeNull();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('getComponentTokens', () => {
|
|
101
|
-
it('returns the DTCG token tree for a component that has one', () => {
|
|
102
|
-
const tokens = getComponentTokens('Button');
|
|
103
|
-
expect(tokens.button).toBeTruthy();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('returns null for a component with no token file', () => {
|
|
107
|
-
expect(getComponentTokens('Grid')).toBeNull();
|
|
108
|
-
expect(getComponentTokens('Stack')).toBeNull();
|
|
109
|
-
expect(getComponentTokens('RadioGroup')).toBeNull();
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
describe('buildManifest', () => {
|
|
114
|
-
it('includes every listed component, with usage, props, and tokens rolled in', () => {
|
|
115
|
-
const manifest = buildManifest();
|
|
116
|
-
expect(manifest.componentCount).toBe(listComponents().length);
|
|
117
|
-
expect(manifest.components.length).toBe(manifest.componentCount);
|
|
118
|
-
|
|
119
|
-
const button = manifest.components.find(c => c.name === 'Button');
|
|
120
|
-
expect(button.usage.dos.length).toBeGreaterThan(0);
|
|
121
|
-
expect(button.props.web.propMap.length).toBeGreaterThan(0);
|
|
122
|
-
expect(button.tokens.button).toBeTruthy();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('handles a component with no token file without throwing', () => {
|
|
126
|
-
const manifest = buildManifest();
|
|
127
|
-
const container = manifest.components.find(c => c.name === 'Container');
|
|
128
|
-
expect(container).toBeTruthy();
|
|
129
|
-
expect(container.tokens).toBeNull();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('only includes platforms the component actually supports', () => {
|
|
133
|
-
const manifest = buildManifest();
|
|
134
|
-
const button = manifest.components.find(c => c.name === 'Button');
|
|
135
|
-
expect(Object.keys(button.props)).toContain('web');
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('layout catalog / schema', () => {
|
|
140
|
-
it('lists Button in the web layout catalog with nodeType', () => {
|
|
141
|
-
const catalog = getLayoutCatalog('web');
|
|
142
|
-
const entry = catalog.find(c => c.name === 'Button');
|
|
143
|
-
expect(entry).toBeTruthy();
|
|
144
|
-
expect(entry.schema.nodeType).toBe('Button');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('get_layout_schema returns the same entry as filtering the catalog', () => {
|
|
148
|
-
const schema = getLayoutSchema('Button', 'web');
|
|
149
|
-
expect(schema.schema.nodeType).toBe('Button');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('throws for a component with no standalone layout node', () => {
|
|
153
|
-
expect(() => getLayoutSchema('DefinitelyNotReal', 'web')).toThrow(/get_layout_catalog/);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe('validateLayout', () => {
|
|
158
|
-
it('returns a findings array for an empty layout', () => {
|
|
159
|
-
const result = validateLayout({ layout: { page: { sections: [] } } });
|
|
160
|
-
expect(Array.isArray(result.findings)).toBe(true);
|
|
161
|
-
expect(typeof result.hasBlocking).toBe('boolean');
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe('ejectComponent', () => {
|
|
166
|
-
let tmpDir;
|
|
167
|
-
|
|
168
|
-
afterEach(() => {
|
|
169
|
-
if (tmpDir) rmSync(tmpDir, { recursive: true, force: true });
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('copies a component\'s jsx and css into the target directory', () => {
|
|
173
|
-
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'stark-eject-test-'));
|
|
174
|
-
const target = path.join(tmpDir, 'Button');
|
|
175
|
-
const result = ejectComponent('button', { outDir: target });
|
|
176
|
-
|
|
177
|
-
expect(result.component).toBe('Button');
|
|
178
|
-
expect(result.files.sort()).toEqual(['Button.css', 'Button.jsx']);
|
|
179
|
-
expect(readdirSync(target).sort()).toEqual(['Button.css', 'Button.jsx']);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('excludes .figma.tsx code-connect metadata', () => {
|
|
183
|
-
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'stark-eject-test-'));
|
|
184
|
-
const target = path.join(tmpDir, 'DataTable');
|
|
185
|
-
const result = ejectComponent('DataTable', { outDir: target });
|
|
186
|
-
|
|
187
|
-
expect(result.files.some(f => f.endsWith('.figma.tsx'))).toBe(false);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('throws for an unknown component', () => {
|
|
191
|
-
expect(() => ejectComponent('NotAComponent')).toThrow(/Unknown component/);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('copies from stk-react-native when platform is native', () => {
|
|
195
|
-
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'stark-eject-test-'));
|
|
196
|
-
const target = path.join(tmpDir, 'Button');
|
|
197
|
-
const result = ejectComponent('Button', { outDir: target, platform: 'native' });
|
|
198
|
-
|
|
199
|
-
expect(result.platform).toBe('native');
|
|
200
|
-
expect(result.files).toEqual(['Button.jsx']);
|
|
201
|
-
expect(readdirSync(target)).toEqual(['Button.jsx']);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('throws for an unsupported eject platform', () => {
|
|
205
|
-
expect(() => ejectComponent('Button', { platform: 'not-a-platform' })).toThrow(/Unsupported platform/);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('refuses to overwrite a non-empty target without force', () => {
|
|
209
|
-
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'stark-eject-test-'));
|
|
210
|
-
const target = path.join(tmpDir, 'Button');
|
|
211
|
-
ejectComponent('Button', { outDir: target });
|
|
212
|
-
|
|
213
|
-
expect(() => ejectComponent('Button', { outDir: target })).toThrow(/already exists/);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('overwrites a non-empty target when force is set', () => {
|
|
217
|
-
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'stark-eject-test-'));
|
|
218
|
-
const target = path.join(tmpDir, 'Button');
|
|
219
|
-
ejectComponent('Button', { outDir: target });
|
|
220
|
-
|
|
221
|
-
expect(() => ejectComponent('Button', { outDir: target, force: true })).not.toThrow();
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
describe('getGenerationProtocol', () => {
|
|
226
|
-
it('returns the versioned step checklist', () => {
|
|
227
|
-
const protocol = getGenerationProtocol();
|
|
228
|
-
expect(protocol.version).toBeGreaterThanOrEqual(1);
|
|
229
|
-
expect(protocol.steps.length).toBeGreaterThan(0);
|
|
230
|
-
});
|
|
231
|
-
});
|