skillui 1.1.2 → 1.1.3

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 (61) hide show
  1. package/dist/cli.js +105073 -194
  2. package/package.json +15 -6
  3. package/dist/cli.d.ts +0 -3
  4. package/dist/extractors/components.d.ts +0 -11
  5. package/dist/extractors/components.js +0 -455
  6. package/dist/extractors/framework.d.ts +0 -4
  7. package/dist/extractors/framework.js +0 -126
  8. package/dist/extractors/tokens/computed.d.ts +0 -7
  9. package/dist/extractors/tokens/computed.js +0 -249
  10. package/dist/extractors/tokens/css.d.ts +0 -3
  11. package/dist/extractors/tokens/css.js +0 -510
  12. package/dist/extractors/tokens/http-css.d.ts +0 -14
  13. package/dist/extractors/tokens/http-css.js +0 -1689
  14. package/dist/extractors/tokens/tailwind.d.ts +0 -3
  15. package/dist/extractors/tokens/tailwind.js +0 -353
  16. package/dist/extractors/tokens/tokens-file.d.ts +0 -3
  17. package/dist/extractors/tokens/tokens-file.js +0 -229
  18. package/dist/extractors/ultra/animations.d.ts +0 -21
  19. package/dist/extractors/ultra/animations.js +0 -527
  20. package/dist/extractors/ultra/components-dom.d.ts +0 -13
  21. package/dist/extractors/ultra/components-dom.js +0 -149
  22. package/dist/extractors/ultra/interactions.d.ts +0 -14
  23. package/dist/extractors/ultra/interactions.js +0 -222
  24. package/dist/extractors/ultra/layout.d.ts +0 -14
  25. package/dist/extractors/ultra/layout.js +0 -123
  26. package/dist/extractors/ultra/pages.d.ts +0 -16
  27. package/dist/extractors/ultra/pages.js +0 -228
  28. package/dist/font-resolver.d.ts +0 -10
  29. package/dist/font-resolver.js +0 -280
  30. package/dist/modes/dir.d.ts +0 -6
  31. package/dist/modes/dir.js +0 -213
  32. package/dist/modes/repo.d.ts +0 -6
  33. package/dist/modes/repo.js +0 -76
  34. package/dist/modes/ultra.d.ts +0 -22
  35. package/dist/modes/ultra.js +0 -281
  36. package/dist/modes/url.d.ts +0 -14
  37. package/dist/modes/url.js +0 -161
  38. package/dist/normalizer.d.ts +0 -11
  39. package/dist/normalizer.js +0 -867
  40. package/dist/playwright-loader.d.ts +0 -10
  41. package/dist/playwright-loader.js +0 -71
  42. package/dist/screenshot.d.ts +0 -9
  43. package/dist/screenshot.js +0 -94
  44. package/dist/types-ultra.d.ts +0 -157
  45. package/dist/types-ultra.js +0 -4
  46. package/dist/types.d.ts +0 -182
  47. package/dist/types.js +0 -4
  48. package/dist/writers/animations-md.d.ts +0 -17
  49. package/dist/writers/animations-md.js +0 -313
  50. package/dist/writers/components-md.d.ts +0 -8
  51. package/dist/writers/components-md.js +0 -151
  52. package/dist/writers/design-md.d.ts +0 -7
  53. package/dist/writers/design-md.js +0 -704
  54. package/dist/writers/interactions-md.d.ts +0 -8
  55. package/dist/writers/interactions-md.js +0 -146
  56. package/dist/writers/layout-md.d.ts +0 -8
  57. package/dist/writers/layout-md.js +0 -120
  58. package/dist/writers/skill.d.ts +0 -12
  59. package/dist/writers/skill.js +0 -1006
  60. package/dist/writers/tokens-json.d.ts +0 -11
  61. package/dist/writers/tokens-json.js +0 -164
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "skillui",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Reverse-engineer design systems from any website, repo, or project. Extracts colors, fonts, spacing, animations, and components — packaged as a .skill file for Claude. Pure static analysis, zero AI, zero API keys.",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
7
7
  "skillui": "dist/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "tsc",
10
+ "build": "tsup",
11
11
  "start": "node dist/cli.js",
12
- "dev": "ts-node src/cli.ts",
13
12
  "prepublishOnly": "npm run build"
14
13
  },
15
14
  "keywords": [
@@ -43,19 +42,30 @@
43
42
  "@babel/traverse": "^7.24.0",
44
43
  "@babel/types": "^7.24.0",
45
44
  "archiver": "^7.0.0",
45
+ "boxen": "^8.0.1",
46
+ "chalk": "^5.6.2",
47
+ "cli-progress": "^3.12.0",
46
48
  "commander": "^12.0.0",
47
49
  "css-tree": "^2.3.1",
48
50
  "culori": "^4.0.0",
49
51
  "glob": "^10.3.0",
52
+ "gradient-string": "^3.0.0",
50
53
  "jiti": "^1.21.0",
54
+ "ora": "^8.2.0",
51
55
  "postcss": "^8.4.0",
56
+ "prompts": "^2.4.2",
52
57
  "simple-git": "^3.24.0",
53
- "tmp": "^0.2.3"
58
+ "strip-ansi": "^7.2.0",
59
+ "tmp": "^0.2.3",
60
+ "tsup": "^8.5.1"
54
61
  },
55
62
  "devDependencies": {
56
63
  "@types/archiver": "^6.0.0",
64
+ "@types/cli-progress": "^3.11.6",
57
65
  "@types/css-tree": "^2.3.0",
66
+ "@types/gradient-string": "^1.1.6",
58
67
  "@types/node": "^20.0.0",
68
+ "@types/prompts": "^2.4.9",
59
69
  "@types/tmp": "^0.2.6",
60
70
  "playwright": "^1.59.1",
61
71
  "typescript": "^5.4.0"
@@ -72,8 +82,7 @@
72
82
  "node": ">=18.0.0"
73
83
  },
74
84
  "files": [
75
- "dist/**/*.js",
76
- "dist/**/*.d.ts",
85
+ "dist/cli.js",
77
86
  "README.md"
78
87
  ]
79
88
  }
package/dist/cli.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=cli.d.ts.map
@@ -1,11 +0,0 @@
1
- import { ComponentInfo } from '../types';
2
- export declare function extractComponents(projectDir: string): ComponentInfo[];
3
- /**
4
- * Detect which libraries the project uses (icons, state, animation).
5
- */
6
- export declare function detectProjectLibraries(projectDir: string): {
7
- iconLibrary: string | null;
8
- stateLibrary: string | null;
9
- animationLibrary: string | null;
10
- };
11
- //# sourceMappingURL=components.d.ts.map
@@ -1,455 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.extractComponents = extractComponents;
37
- exports.detectProjectLibraries = detectProjectLibraries;
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const COMPONENT_DIRS = [
41
- 'src/components',
42
- 'components',
43
- 'app/components',
44
- 'src/app/components',
45
- 'src/ui',
46
- 'src/common',
47
- 'lib/components',
48
- 'src/lib/components',
49
- 'src/features',
50
- 'src/views',
51
- 'src/pages',
52
- 'app',
53
- 'pages',
54
- ];
55
- const COMPONENT_EXTENSIONS = ['.tsx', '.jsx', '.vue', '.svelte'];
56
- const IGNORE_DIRS = ['node_modules', '.git', 'dist', 'build', '__tests__', '__mocks__', '.next', '.nuxt'];
57
- // Category detection keywords for component names and file paths
58
- const CATEGORY_PATTERNS = {
59
- 'layout': /^(layout|container|grid|stack|flex|page|section|wrapper|sidebar|header|footer|main|shell|frame|panel|divider|spacer|center)/i,
60
- 'navigation': /^(nav|navbar|menu|breadcrumb|tab|tabs|pagination|stepper|link|drawer|appbar|topbar|bottombar)/i,
61
- 'data-display': /^(card|list|table|avatar|badge|chip|tag|stat|metric|tooltip|accordion|collapse|timeline|tree|calendar|chart|graph|progress|indicator)/i,
62
- 'data-input': /^(input|form|select|checkbox|radio|switch|toggle|slider|textarea|datepicker|timepicker|upload|dropdown|combobox|autocomplete|search|filter|rating)/i,
63
- 'feedback': /^(alert|toast|snackbar|notification|banner|skeleton|spinner|loading|error|empty|placeholder|progress)/i,
64
- 'overlay': /^(modal|dialog|popover|sheet|bottomsheet|lightbox|overlay|confirm|command|commandpalette)/i,
65
- 'typography': /^(heading|text|title|paragraph|label|caption|code|highlight|prose|markdown|typography)/i,
66
- 'media': /^(image|video|icon|logo|thumbnail|gallery|carousel|slider|embed|player)/i,
67
- 'other': /./,
68
- };
69
- function extractComponents(projectDir) {
70
- const components = [];
71
- for (const dir of COMPONENT_DIRS) {
72
- const fullDir = path.join(projectDir, dir);
73
- if (fs.existsSync(fullDir)) {
74
- scanDirectory(fullDir, components, projectDir);
75
- }
76
- }
77
- return components;
78
- }
79
- /**
80
- * Detect which libraries the project uses (icons, state, animation).
81
- */
82
- function detectProjectLibraries(projectDir) {
83
- let iconLibrary = null;
84
- let stateLibrary = null;
85
- let animationLibrary = null;
86
- try {
87
- const pkgPath = path.join(projectDir, 'package.json');
88
- if (fs.existsSync(pkgPath)) {
89
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
90
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
91
- // Icon libraries
92
- if (allDeps['lucide-react'] || allDeps['lucide-vue-next'])
93
- iconLibrary = 'Lucide';
94
- else if (allDeps['@heroicons/react'] || allDeps['heroicons'])
95
- iconLibrary = 'Heroicons';
96
- else if (allDeps['react-icons'])
97
- iconLibrary = 'React Icons';
98
- else if (allDeps['@phosphor-icons/react'])
99
- iconLibrary = 'Phosphor';
100
- else if (allDeps['@tabler/icons-react'])
101
- iconLibrary = 'Tabler Icons';
102
- else if (allDeps['@radix-ui/react-icons'])
103
- iconLibrary = 'Radix Icons';
104
- // State management
105
- if (allDeps['zustand'])
106
- stateLibrary = 'Zustand';
107
- else if (allDeps['@reduxjs/toolkit'] || allDeps['redux'])
108
- stateLibrary = 'Redux';
109
- else if (allDeps['jotai'])
110
- stateLibrary = 'Jotai';
111
- else if (allDeps['recoil'])
112
- stateLibrary = 'Recoil';
113
- else if (allDeps['valtio'])
114
- stateLibrary = 'Valtio';
115
- else if (allDeps['pinia'])
116
- stateLibrary = 'Pinia';
117
- else if (allDeps['mobx'])
118
- stateLibrary = 'MobX';
119
- // Animation
120
- if (allDeps['framer-motion'] || allDeps['motion'])
121
- animationLibrary = 'Framer Motion';
122
- else if (allDeps['@react-spring/web'] || allDeps['react-spring'])
123
- animationLibrary = 'React Spring';
124
- else if (allDeps['gsap'])
125
- animationLibrary = 'GSAP';
126
- else if (allDeps['animejs'] || allDeps['anime.js'])
127
- animationLibrary = 'Anime.js';
128
- else if (allDeps['@formkit/auto-animate'])
129
- animationLibrary = 'AutoAnimate';
130
- }
131
- }
132
- catch {
133
- // ignore
134
- }
135
- return { iconLibrary, stateLibrary, animationLibrary };
136
- }
137
- function scanDirectory(dir, components, rootDir) {
138
- try {
139
- const entries = fs.readdirSync(dir, { withFileTypes: true });
140
- for (const entry of entries) {
141
- if (IGNORE_DIRS.includes(entry.name))
142
- continue;
143
- const fullPath = path.join(dir, entry.name);
144
- if (entry.isFile() && COMPONENT_EXTENSIONS.some(ext => entry.name.endsWith(ext))) {
145
- const component = parseComponent(fullPath, rootDir);
146
- if (component)
147
- components.push(component);
148
- }
149
- else if (entry.isDirectory()) {
150
- scanDirectory(fullPath, components, rootDir);
151
- }
152
- }
153
- }
154
- catch {
155
- // Permission errors
156
- }
157
- }
158
- function parseComponent(filePath, rootDir) {
159
- let content;
160
- try {
161
- content = fs.readFileSync(filePath, 'utf-8');
162
- }
163
- catch {
164
- return null;
165
- }
166
- const fileName = path.basename(filePath, path.extname(filePath));
167
- if (['index', 'types', 'utils', 'helpers', 'constants', 'styles', 'hooks', 'context', 'store', 'provider'].includes(fileName.toLowerCase())) {
168
- if (fileName.toLowerCase() === 'index') {
169
- const dirName = path.basename(path.dirname(filePath));
170
- if (dirName === 'components' || dirName === 'ui' || dirName === 'lib')
171
- return null;
172
- return parseComponentContent(dirName, content, filePath, rootDir);
173
- }
174
- return null;
175
- }
176
- return parseComponentContent(fileName, content, filePath, rootDir);
177
- }
178
- function parseComponentContent(name, content, filePath, rootDir) {
179
- const hasJSX = /<[A-Z][a-zA-Z]*[\s/>]/.test(content) || /return\s*\(?\s*</.test(content);
180
- const hasExport = /export\s+(default\s+)?/.test(content);
181
- if (!hasJSX && !hasExport)
182
- return null;
183
- const componentName = toPascalCase(name);
184
- const relativePath = path.relative(rootDir, filePath).replace(/\\/g, '/');
185
- const variants = extractVariants(content);
186
- const cssClasses = extractCSSClasses(content);
187
- const jsxSnippet = extractJSXSnippet(content);
188
- const props = extractProps(content);
189
- const category = categorizeComponent(componentName, relativePath, content, cssClasses);
190
- const { hasAnimation, animationDetails } = detectAnimations(content);
191
- const statePatterns = detectStatePatterns(content);
192
- const tailwindPatterns = extractTailwindPatterns(cssClasses);
193
- return {
194
- name: componentName,
195
- filePath: relativePath,
196
- variants,
197
- cssClasses,
198
- jsxSnippet,
199
- props,
200
- category,
201
- hasAnimation,
202
- animationDetails,
203
- statePatterns,
204
- tailwindPatterns,
205
- };
206
- }
207
- function categorizeComponent(name, filePath, content, classes) {
208
- // Check name first
209
- for (const [cat, pattern] of Object.entries(CATEGORY_PATTERNS)) {
210
- if (cat === 'other')
211
- continue;
212
- if (pattern.test(name))
213
- return cat;
214
- }
215
- // Check file path for hints
216
- const pathLower = filePath.toLowerCase();
217
- if (/\/layout[s]?\//.test(pathLower))
218
- return 'layout';
219
- if (/\/nav|\/menu|\/header|\/footer/.test(pathLower))
220
- return 'navigation';
221
- if (/\/form[s]?\/|\/input[s]?\//.test(pathLower))
222
- return 'data-input';
223
- if (/\/modal[s]?\/|\/dialog[s]?\/|\/overlay[s]?\//.test(pathLower))
224
- return 'overlay';
225
- if (/\/feedback\/|\/toast|\/alert/.test(pathLower))
226
- return 'feedback';
227
- // Check content for category hints
228
- if (/\<form[\s>]|onSubmit|handleSubmit/i.test(content))
229
- return 'data-input';
230
- if (/\<table[\s>]|<thead|<tbody/i.test(content))
231
- return 'data-display';
232
- if (/\<nav[\s>]|useRouter|useNavigate|Link\s/i.test(content))
233
- return 'navigation';
234
- if (/AnimatePresence|createPortal|usePortal/i.test(content))
235
- return 'overlay';
236
- // Check Tailwind classes
237
- const classStr = classes.join(' ');
238
- if (/grid-cols|flex.*gap|justify-between|items-center/.test(classStr))
239
- return 'layout';
240
- return 'other';
241
- }
242
- function detectAnimations(content) {
243
- const details = [];
244
- // Framer Motion
245
- const motionVariants = content.match(/variants\s*=\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/g);
246
- if (motionVariants) {
247
- for (const v of motionVariants) {
248
- const trimmed = v.slice(0, 120);
249
- details.push(`motion-variant: ${trimmed}`);
250
- }
251
- }
252
- if (/motion\.|<motion\./.test(content)) {
253
- details.push('framer-motion');
254
- // Extract spring configs
255
- const springMatch = content.match(/spring\s*:\s*\{([^}]+)\}/);
256
- if (springMatch)
257
- details.push(`spring: {${springMatch[1].trim()}}`);
258
- // Extract transition props
259
- const transitionMatch = content.match(/transition\s*=\s*\{?\{([^}]+)\}/);
260
- if (transitionMatch)
261
- details.push(`transition: {${transitionMatch[1].trim()}}`);
262
- // Detect AnimatePresence
263
- if (/AnimatePresence/.test(content))
264
- details.push('animate-presence');
265
- // Detect layout animations
266
- if (/layoutId|layout=/.test(content))
267
- details.push('layout-animation');
268
- // Extract initial/animate/exit
269
- const animateMatch = content.match(/animate\s*=\s*\{?\{([^}]+)\}/);
270
- if (animateMatch)
271
- details.push(`animate: {${animateMatch[1].trim()}}`);
272
- }
273
- // CSS animations
274
- const animateClasses = content.match(/animate-[\w-]+/g);
275
- if (animateClasses) {
276
- details.push(...[...new Set(animateClasses)].map(c => `tw-${c}`));
277
- }
278
- // CSS transitions in Tailwind
279
- const transitionClasses = content.match(/transition-[\w-]+|duration-[\w-]+|ease-[\w-]+/g);
280
- if (transitionClasses) {
281
- const unique = [...new Set(transitionClasses)];
282
- if (unique.length > 0)
283
- details.push(`tw-transitions: ${unique.join(', ')}`);
284
- }
285
- // Hover/focus transforms
286
- if (/hover:scale|hover:translate|hover:-translate|group-hover:/.test(content)) {
287
- details.push('hover-transforms');
288
- }
289
- return { hasAnimation: details.length > 0, animationDetails: details };
290
- }
291
- function detectStatePatterns(content) {
292
- const patterns = [];
293
- if (/useState/.test(content))
294
- patterns.push('useState');
295
- if (/useReducer/.test(content))
296
- patterns.push('useReducer');
297
- if (/useContext/.test(content))
298
- patterns.push('useContext');
299
- if (/useStore|create\(/.test(content) && /zustand/i.test(content))
300
- patterns.push('zustand');
301
- if (/useQuery|useMutation/.test(content))
302
- patterns.push('react-query');
303
- if (/useSWR/.test(content))
304
- patterns.push('swr');
305
- if (/useForm/.test(content))
306
- patterns.push('react-hook-form');
307
- if (/useRef/.test(content))
308
- patterns.push('useRef');
309
- if (/forwardRef/.test(content))
310
- patterns.push('forwardRef');
311
- if (/React\.memo|memo\(/.test(content))
312
- patterns.push('memo');
313
- return patterns;
314
- }
315
- function extractTailwindPatterns(classes) {
316
- const pattern = {
317
- backgrounds: [],
318
- borders: [],
319
- spacing: [],
320
- typography: [],
321
- effects: [],
322
- layout: [],
323
- interactive: [],
324
- };
325
- for (const cls of classes) {
326
- if (/^bg-/.test(cls))
327
- pattern.backgrounds.push(cls);
328
- else if (/^(border|rounded|ring|outline)/.test(cls))
329
- pattern.borders.push(cls);
330
- else if (/^(p-|px-|py-|pt-|pb-|pl-|pr-|m-|mx-|my-|mt-|mb-|ml-|mr-|gap-|space-)/.test(cls))
331
- pattern.spacing.push(cls);
332
- else if (/^(text-|font-|tracking-|leading-|truncate|line-clamp)/.test(cls))
333
- pattern.typography.push(cls);
334
- else if (/^(shadow|opacity|blur|backdrop|ring|drop-shadow|filter)/.test(cls))
335
- pattern.effects.push(cls);
336
- else if (/^(flex|grid|col-|row-|justify|items-|self-|w-|h-|min-|max-|overflow|relative|absolute|fixed|sticky|z-)/.test(cls))
337
- pattern.layout.push(cls);
338
- else if (/^(hover:|focus:|active:|disabled:|group-|peer-|cursor-|pointer-events|select-)/.test(cls))
339
- pattern.interactive.push(cls);
340
- }
341
- return pattern;
342
- }
343
- function extractVariants(content) {
344
- const variants = [];
345
- const unionMatches = content.matchAll(/['"](\w+)['"]\s*\|/g);
346
- for (const m of unionMatches) {
347
- if (!variants.includes(m[1]) && isLikelyVariant(m[1]))
348
- variants.push(m[1]);
349
- }
350
- const lastUnionMatches = content.matchAll(/\|\s*['"](\w+)['"]/g);
351
- for (const m of lastUnionMatches) {
352
- if (!variants.includes(m[1]) && isLikelyVariant(m[1]))
353
- variants.push(m[1]);
354
- }
355
- // CVA / class-variance-authority
356
- const cvaMatches = content.matchAll(/variants?\s*:\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/gi);
357
- for (const m of cvaMatches) {
358
- const keys = m[1].matchAll(/(\w+)\s*:/g);
359
- for (const k of keys) {
360
- if (!variants.includes(k[1]) && isLikelyVariant(k[1]))
361
- variants.push(k[1]);
362
- }
363
- }
364
- return variants;
365
- }
366
- function isLikelyVariant(str) {
367
- const variantKeywords = [
368
- 'primary', 'secondary', 'tertiary', 'ghost', 'outline', 'link',
369
- 'danger', 'warning', 'success', 'info', 'error',
370
- 'sm', 'md', 'lg', 'xl', '2xl',
371
- 'small', 'medium', 'large',
372
- 'default', 'destructive', 'subtle',
373
- 'solid', 'soft', 'plain', 'minimal', 'filled',
374
- ];
375
- return variantKeywords.includes(str.toLowerCase()) || str.length <= 15;
376
- }
377
- function extractCSSClasses(content) {
378
- const classes = new Set();
379
- // className="..." or class="..."
380
- const classMatches = content.matchAll(/class(?:Name)?\s*=\s*["']([^"']+)["']/g);
381
- for (const m of classMatches) {
382
- for (const cls of m[1].split(/\s+/)) {
383
- if (cls.trim())
384
- classes.add(cls.trim());
385
- }
386
- }
387
- // Template literals: className={`...`}
388
- const templateMatches = content.matchAll(/class(?:Name)?\s*=\s*\{`([^`]+)`\}/g);
389
- for (const m of templateMatches) {
390
- const staticParts = m[1].replace(/\$\{[^}]+\}/g, ' ').split(/\s+/);
391
- for (const cls of staticParts) {
392
- if (cls.trim())
393
- classes.add(cls.trim());
394
- }
395
- }
396
- // cn() / clsx() / classnames() / cva()
397
- const cnMatches = content.matchAll(/(?:cn|clsx|classnames|cva)\s*\(\s*["']([^"']+)["']/g);
398
- for (const m of cnMatches) {
399
- for (const cls of m[1].split(/\s+/)) {
400
- if (cls.trim())
401
- classes.add(cls.trim());
402
- }
403
- }
404
- // Multi-line cn() calls — capture strings inside
405
- const cnBlockMatches = content.matchAll(/(?:cn|clsx|classnames)\s*\(([^)]+)\)/gs);
406
- for (const m of cnBlockMatches) {
407
- const innerStrings = m[1].matchAll(/["']([^"']+)["']/g);
408
- for (const s of innerStrings) {
409
- for (const cls of s[1].split(/\s+/)) {
410
- if (cls.trim() && !cls.includes('${'))
411
- classes.add(cls.trim());
412
- }
413
- }
414
- }
415
- return Array.from(classes).slice(0, 80);
416
- }
417
- function extractJSXSnippet(content) {
418
- const returnMatch = content.match(/return\s*\(\s*\n?([\s\S]*?)\n?\s*\);?/);
419
- if (returnMatch) {
420
- const lines = returnMatch[1].split('\n').slice(0, 40);
421
- return lines.join('\n').trim();
422
- }
423
- const arrowMatch = content.match(/=>\s*\(\s*\n?([\s\S]*?)\n?\s*\);?/);
424
- if (arrowMatch) {
425
- const lines = arrowMatch[1].split('\n').slice(0, 40);
426
- return lines.join('\n').trim();
427
- }
428
- return '';
429
- }
430
- function extractProps(content) {
431
- const props = [];
432
- const propsMatch = content.match(/(?:interface|type)\s+\w*Props\w*\s*(?:=\s*)?\{([^}]+)\}/);
433
- if (propsMatch) {
434
- const propLines = propsMatch[1].matchAll(/(\w+)\s*[?:]?\s*:/g);
435
- for (const m of propLines) {
436
- if (!props.includes(m[1]))
437
- props.push(m[1]);
438
- }
439
- }
440
- const destructMatch = content.match(/\(\s*\{\s*([^}]+)\s*\}\s*(?::\s*\w+)?\s*\)/);
441
- if (destructMatch) {
442
- const parts = destructMatch[1].split(',').map(s => s.trim().split(/[\s=:]/)[0].trim());
443
- for (const p of parts) {
444
- if (p && !props.includes(p) && /^[a-zA-Z]/.test(p))
445
- props.push(p);
446
- }
447
- }
448
- return props;
449
- }
450
- function toPascalCase(str) {
451
- return str
452
- .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
453
- .replace(/^./, s => s.toUpperCase());
454
- }
455
- //# sourceMappingURL=components.js.map
@@ -1,4 +0,0 @@
1
- import { Framework } from '../types';
2
- export declare function detectFrameworks(projectDir: string): Framework[];
3
- export declare function getProjectName(projectDir: string, overrideName?: string): string;
4
- //# sourceMappingURL=framework.d.ts.map
@@ -1,126 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.detectFrameworks = detectFrameworks;
37
- exports.getProjectName = getProjectName;
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const DEP_MAP = [
41
- { pkg: 'tailwindcss', id: 'tailwind', name: 'Tailwind CSS' },
42
- { pkg: 'react', id: 'react', name: 'React' },
43
- { pkg: 'vue', id: 'vue', name: 'Vue' },
44
- { pkg: 'next', id: 'next', name: 'Next.js' },
45
- { pkg: 'nuxt', id: 'nuxt', name: 'Nuxt' },
46
- { pkg: 'svelte', id: 'svelte', name: 'Svelte' },
47
- { pkg: '@angular/core', id: 'angular', name: 'Angular' },
48
- { pkg: 'styled-components', id: 'css-in-js', name: 'CSS-in-JS (styled-components)' },
49
- { pkg: '@emotion/react', id: 'css-in-js', name: 'CSS-in-JS (Emotion)' },
50
- { pkg: '@emotion/styled', id: 'css-in-js', name: 'CSS-in-JS (Emotion)' },
51
- ];
52
- function detectFrameworks(projectDir) {
53
- const frameworks = [];
54
- const seen = new Set();
55
- // Read package.json
56
- const pkgPath = path.join(projectDir, 'package.json');
57
- if (fs.existsSync(pkgPath)) {
58
- try {
59
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
60
- const allDeps = {
61
- ...pkg.dependencies,
62
- ...pkg.devDependencies,
63
- };
64
- for (const mapping of DEP_MAP) {
65
- if (allDeps[mapping.pkg] && !seen.has(mapping.id)) {
66
- seen.add(mapping.id);
67
- frameworks.push({
68
- id: mapping.id,
69
- name: mapping.name,
70
- version: allDeps[mapping.pkg].replace(/[\^~>=<]/g, ''),
71
- });
72
- }
73
- }
74
- }
75
- catch {
76
- // Ignore malformed package.json
77
- }
78
- }
79
- // Check for CSS Modules
80
- if (!seen.has('css-modules')) {
81
- if (hasCSSModules(projectDir)) {
82
- seen.add('css-modules');
83
- frameworks.push({ id: 'css-modules', name: 'CSS Modules' });
84
- }
85
- }
86
- return frameworks;
87
- }
88
- function hasCSSModules(dir, depth = 0) {
89
- if (depth > 4)
90
- return false;
91
- try {
92
- const entries = fs.readdirSync(dir, { withFileTypes: true });
93
- for (const entry of entries) {
94
- if (entry.name === 'node_modules' || entry.name === '.git')
95
- continue;
96
- if (entry.isFile() && /\.module\.(css|scss|less)$/.test(entry.name)) {
97
- return true;
98
- }
99
- if (entry.isDirectory() && depth < 4) {
100
- if (hasCSSModules(path.join(dir, entry.name), depth + 1))
101
- return true;
102
- }
103
- }
104
- }
105
- catch {
106
- // Permission errors etc.
107
- }
108
- return false;
109
- }
110
- function getProjectName(projectDir, overrideName) {
111
- if (overrideName)
112
- return overrideName;
113
- const pkgPath = path.join(projectDir, 'package.json');
114
- if (fs.existsSync(pkgPath)) {
115
- try {
116
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
117
- if (pkg.name)
118
- return pkg.name;
119
- }
120
- catch {
121
- // Ignore
122
- }
123
- }
124
- return path.basename(path.resolve(projectDir));
125
- }
126
- //# sourceMappingURL=framework.js.map
@@ -1,7 +0,0 @@
1
- import { RawTokens } from '../../types';
2
- /**
3
- * URL mode: Extract computed styles from live DOM using Playwright.
4
- * Playwright is an optional peer dependency.
5
- */
6
- export declare function extractComputedTokens(url: string, maxPages?: number): Promise<RawTokens>;
7
- //# sourceMappingURL=computed.d.ts.map