skillui 1.1.2 → 1.1.4

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 (62) hide show
  1. package/README.md +20 -15
  2. package/dist/cli.js +105073 -194
  3. package/package.json +15 -6
  4. package/dist/cli.d.ts +0 -3
  5. package/dist/extractors/components.d.ts +0 -11
  6. package/dist/extractors/components.js +0 -455
  7. package/dist/extractors/framework.d.ts +0 -4
  8. package/dist/extractors/framework.js +0 -126
  9. package/dist/extractors/tokens/computed.d.ts +0 -7
  10. package/dist/extractors/tokens/computed.js +0 -249
  11. package/dist/extractors/tokens/css.d.ts +0 -3
  12. package/dist/extractors/tokens/css.js +0 -510
  13. package/dist/extractors/tokens/http-css.d.ts +0 -14
  14. package/dist/extractors/tokens/http-css.js +0 -1689
  15. package/dist/extractors/tokens/tailwind.d.ts +0 -3
  16. package/dist/extractors/tokens/tailwind.js +0 -353
  17. package/dist/extractors/tokens/tokens-file.d.ts +0 -3
  18. package/dist/extractors/tokens/tokens-file.js +0 -229
  19. package/dist/extractors/ultra/animations.d.ts +0 -21
  20. package/dist/extractors/ultra/animations.js +0 -527
  21. package/dist/extractors/ultra/components-dom.d.ts +0 -13
  22. package/dist/extractors/ultra/components-dom.js +0 -149
  23. package/dist/extractors/ultra/interactions.d.ts +0 -14
  24. package/dist/extractors/ultra/interactions.js +0 -222
  25. package/dist/extractors/ultra/layout.d.ts +0 -14
  26. package/dist/extractors/ultra/layout.js +0 -123
  27. package/dist/extractors/ultra/pages.d.ts +0 -16
  28. package/dist/extractors/ultra/pages.js +0 -228
  29. package/dist/font-resolver.d.ts +0 -10
  30. package/dist/font-resolver.js +0 -280
  31. package/dist/modes/dir.d.ts +0 -6
  32. package/dist/modes/dir.js +0 -213
  33. package/dist/modes/repo.d.ts +0 -6
  34. package/dist/modes/repo.js +0 -76
  35. package/dist/modes/ultra.d.ts +0 -22
  36. package/dist/modes/ultra.js +0 -281
  37. package/dist/modes/url.d.ts +0 -14
  38. package/dist/modes/url.js +0 -161
  39. package/dist/normalizer.d.ts +0 -11
  40. package/dist/normalizer.js +0 -867
  41. package/dist/playwright-loader.d.ts +0 -10
  42. package/dist/playwright-loader.js +0 -71
  43. package/dist/screenshot.d.ts +0 -9
  44. package/dist/screenshot.js +0 -94
  45. package/dist/types-ultra.d.ts +0 -157
  46. package/dist/types-ultra.js +0 -4
  47. package/dist/types.d.ts +0 -182
  48. package/dist/types.js +0 -4
  49. package/dist/writers/animations-md.d.ts +0 -17
  50. package/dist/writers/animations-md.js +0 -313
  51. package/dist/writers/components-md.d.ts +0 -8
  52. package/dist/writers/components-md.js +0 -151
  53. package/dist/writers/design-md.d.ts +0 -7
  54. package/dist/writers/design-md.js +0 -704
  55. package/dist/writers/interactions-md.d.ts +0 -8
  56. package/dist/writers/interactions-md.js +0 -146
  57. package/dist/writers/layout-md.d.ts +0 -8
  58. package/dist/writers/layout-md.js +0 -120
  59. package/dist/writers/skill.d.ts +0 -12
  60. package/dist/writers/skill.js +0 -1006
  61. package/dist/writers/tokens-json.d.ts +0 -11
  62. 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.4",
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