skillui 1.1.1 → 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.
- package/dist/cli.js +105073 -194
- package/package.json +15 -6
- package/dist/cli.d.ts +0 -3
- package/dist/extractors/components.d.ts +0 -11
- package/dist/extractors/components.js +0 -455
- package/dist/extractors/framework.d.ts +0 -4
- package/dist/extractors/framework.js +0 -126
- package/dist/extractors/tokens/computed.d.ts +0 -7
- package/dist/extractors/tokens/computed.js +0 -249
- package/dist/extractors/tokens/css.d.ts +0 -3
- package/dist/extractors/tokens/css.js +0 -510
- package/dist/extractors/tokens/http-css.d.ts +0 -14
- package/dist/extractors/tokens/http-css.js +0 -1689
- package/dist/extractors/tokens/tailwind.d.ts +0 -3
- package/dist/extractors/tokens/tailwind.js +0 -353
- package/dist/extractors/tokens/tokens-file.d.ts +0 -3
- package/dist/extractors/tokens/tokens-file.js +0 -229
- package/dist/extractors/ultra/animations.d.ts +0 -21
- package/dist/extractors/ultra/animations.js +0 -530
- package/dist/extractors/ultra/components-dom.d.ts +0 -13
- package/dist/extractors/ultra/components-dom.js +0 -152
- package/dist/extractors/ultra/interactions.d.ts +0 -14
- package/dist/extractors/ultra/interactions.js +0 -225
- package/dist/extractors/ultra/layout.d.ts +0 -14
- package/dist/extractors/ultra/layout.js +0 -126
- package/dist/extractors/ultra/pages.d.ts +0 -16
- package/dist/extractors/ultra/pages.js +0 -231
- package/dist/font-resolver.d.ts +0 -10
- package/dist/font-resolver.js +0 -280
- package/dist/modes/dir.d.ts +0 -6
- package/dist/modes/dir.js +0 -213
- package/dist/modes/repo.d.ts +0 -6
- package/dist/modes/repo.js +0 -76
- package/dist/modes/ultra.d.ts +0 -22
- package/dist/modes/ultra.js +0 -285
- package/dist/modes/url.d.ts +0 -14
- package/dist/modes/url.js +0 -161
- package/dist/normalizer.d.ts +0 -11
- package/dist/normalizer.js +0 -867
- package/dist/screenshot.d.ts +0 -9
- package/dist/screenshot.js +0 -94
- package/dist/types-ultra.d.ts +0 -157
- package/dist/types-ultra.js +0 -4
- package/dist/types.d.ts +0 -182
- package/dist/types.js +0 -4
- package/dist/writers/animations-md.d.ts +0 -17
- package/dist/writers/animations-md.js +0 -313
- package/dist/writers/components-md.d.ts +0 -8
- package/dist/writers/components-md.js +0 -151
- package/dist/writers/design-md.d.ts +0 -7
- package/dist/writers/design-md.js +0 -704
- package/dist/writers/interactions-md.d.ts +0 -8
- package/dist/writers/interactions-md.js +0 -146
- package/dist/writers/layout-md.d.ts +0 -8
- package/dist/writers/layout-md.js +0 -120
- package/dist/writers/skill.d.ts +0 -12
- package/dist/writers/skill.js +0 -1006
- package/dist/writers/tokens-json.d.ts +0 -11
- 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.
|
|
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": "
|
|
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
|
-
"
|
|
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
|
|
76
|
-
"dist/**/*.d.ts",
|
|
85
|
+
"dist/cli.js",
|
|
77
86
|
"README.md"
|
|
78
87
|
]
|
|
79
88
|
}
|
package/dist/cli.d.ts
DELETED
|
@@ -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,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
|