@shift-css/cli 0.0.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/README.md +185 -0
- package/dist/index.js +709 -0
- package/dist/index.js.map +18 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# @shift-css/cli
|
|
2
|
+
|
|
3
|
+
CLI tool for initializing [Shift CSS](https://getshiftcss.com) in your project.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Run directly with npx (recommended)
|
|
9
|
+
npx shift-css init
|
|
10
|
+
|
|
11
|
+
# Or install globally
|
|
12
|
+
npm install -g @shift-css/cli
|
|
13
|
+
shift-css init
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### `shift-css init`
|
|
19
|
+
|
|
20
|
+
Initialize Shift CSS in your project with the correct layer hierarchy.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx shift-css init
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**What it does:**
|
|
27
|
+
|
|
28
|
+
1. **Scans** your project for existing CSS frameworks
|
|
29
|
+
2. **Asks** about your project type (Greenfield or Hybrid)
|
|
30
|
+
3. **Configures** your brand color (presets or hex code)
|
|
31
|
+
4. **Creates** `shift.config.json` with your settings
|
|
32
|
+
5. **Scaffolds** your main stylesheet with proper cascade layers
|
|
33
|
+
|
|
34
|
+
### Example Output
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
๐จ Shift CSS Init
|
|
38
|
+
|
|
39
|
+
โ Scanning project...
|
|
40
|
+
โ Detected existing CSS: bootstrap
|
|
41
|
+
|
|
42
|
+
โ What type of project is this?
|
|
43
|
+
โ โ New project (Greenfield)
|
|
44
|
+
โ โ Existing project (Hybrid)
|
|
45
|
+
|
|
46
|
+
โ Choose your brand color:
|
|
47
|
+
โ โ Plasma (Electric Blue - High-tech default)
|
|
48
|
+
โ โ Laser (Cyber-Pink - Neon futurism)
|
|
49
|
+
โ โ Acid (Toxic Green - Engineering edge)
|
|
50
|
+
โ โ Void (Monochrome - Industrial minimal)
|
|
51
|
+
โ โ Custom
|
|
52
|
+
|
|
53
|
+
โน Plasma โ Blue (Hue: 260)
|
|
54
|
+
|
|
55
|
+
โ Where should the stylesheet be created?
|
|
56
|
+
โ src/styles/shift.css
|
|
57
|
+
|
|
58
|
+
โ Files to create
|
|
59
|
+
โ Config: shift.config.json
|
|
60
|
+
โ Stylesheet: src/styles/shift.css
|
|
61
|
+
โ Mode: Hybrid
|
|
62
|
+
|
|
63
|
+
โ Proceed with initialization?
|
|
64
|
+
โ Yes
|
|
65
|
+
|
|
66
|
+
โ Created shift.config.json
|
|
67
|
+
โ Created src/styles/shift.css
|
|
68
|
+
|
|
69
|
+
โจ Shift CSS initialized!
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Color Presets
|
|
73
|
+
|
|
74
|
+
Curated themes optimized for Shift CSS:
|
|
75
|
+
|
|
76
|
+
| Preset | Hue | Visual Identity |
|
|
77
|
+
|--------|-----|-----------------|
|
|
78
|
+
| **Plasma** | 260 | Electric Blue - High-tech default |
|
|
79
|
+
| **Laser** | 320 | Cyber-Pink - Neon futurism |
|
|
80
|
+
| **Acid** | 140 | Toxic Green - Engineering edge |
|
|
81
|
+
| **Void** | 0 | Monochrome - Industrial minimal |
|
|
82
|
+
|
|
83
|
+
### Using Your Brand Color
|
|
84
|
+
|
|
85
|
+
Don't know OKLCH hues? Paste your hex code:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
โ Choose your brand color:
|
|
89
|
+
โ โ Custom
|
|
90
|
+
|
|
91
|
+
โน Hue guide: 20=Red, 90=Yellow, 140=Green, 260=Blue, 320=Purple
|
|
92
|
+
|
|
93
|
+
โ Enter a hex code (#a855f7) or hue (0-360):
|
|
94
|
+
โ #a855f7
|
|
95
|
+
|
|
96
|
+
โ Converted #a855f7 โ Purple (Hue: 271)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The CLI auto-converts hex to OKLCH hueโuse what you know, get the power of perceptually uniform colors.
|
|
100
|
+
|
|
101
|
+
## Generated Files
|
|
102
|
+
|
|
103
|
+
### shift.config.json
|
|
104
|
+
|
|
105
|
+
Configuration file storing your project settings:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"hues": {
|
|
110
|
+
"primary": 260,
|
|
111
|
+
"secondary": 280,
|
|
112
|
+
"accent": 45,
|
|
113
|
+
"neutral": 260
|
|
114
|
+
},
|
|
115
|
+
"mode": "greenfield",
|
|
116
|
+
"paths": {
|
|
117
|
+
"stylesheet": "src/styles/shift.css"
|
|
118
|
+
},
|
|
119
|
+
"version": 1
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Stylesheet (Greenfield mode)
|
|
124
|
+
|
|
125
|
+
```css
|
|
126
|
+
@layer shift.tokens, shift.base, shift.components, shift.utilities;
|
|
127
|
+
|
|
128
|
+
@import "@shift-css/core/tokens";
|
|
129
|
+
@import "@shift-css/core/base";
|
|
130
|
+
@import "@shift-css/core/components";
|
|
131
|
+
@import "@shift-css/core/utilities";
|
|
132
|
+
|
|
133
|
+
/* Your custom styles below */
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Stylesheet (Hybrid mode)
|
|
137
|
+
|
|
138
|
+
```css
|
|
139
|
+
@layer legacy, shift.tokens, shift.base, shift.components, shift.utilities;
|
|
140
|
+
|
|
141
|
+
@import "@shift-css/core/tokens";
|
|
142
|
+
@import "@shift-css/core/base";
|
|
143
|
+
@import "@shift-css/core/components";
|
|
144
|
+
@import "@shift-css/core/utilities";
|
|
145
|
+
|
|
146
|
+
/* Legacy framework imports - add your existing CSS here */
|
|
147
|
+
@layer legacy {
|
|
148
|
+
/* @import "bootstrap/dist/css/bootstrap.min.css"; */
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Your custom styles below */
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Architecture Modes
|
|
155
|
+
|
|
156
|
+
### Greenfield
|
|
157
|
+
|
|
158
|
+
For new projects without existing CSS frameworks. Shift CSS has full control over the cascade.
|
|
159
|
+
|
|
160
|
+
### Hybrid
|
|
161
|
+
|
|
162
|
+
For projects with existing CSS (Bootstrap, Tailwind, etc.). The `@layer legacy` block gives existing styles the lowest specificity, so Shift CSS can override them without `!important`.
|
|
163
|
+
|
|
164
|
+
## Framework Detection
|
|
165
|
+
|
|
166
|
+
The CLI automatically detects:
|
|
167
|
+
|
|
168
|
+
| Framework | Detection Method |
|
|
169
|
+
| --------------- | ----------------------------------------- |
|
|
170
|
+
| Bootstrap | Filename + content patterns |
|
|
171
|
+
| Tailwind CSS | Filename + content patterns (`--tw-`) |
|
|
172
|
+
| Bulma | Filename + content patterns |
|
|
173
|
+
| Foundation | Filename + content patterns |
|
|
174
|
+
| Large CSS files | Files >10KB with common entry point names |
|
|
175
|
+
|
|
176
|
+
## Options
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
shift-css --help # Show help
|
|
180
|
+
shift-css --version # Show version
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import pc4 from "picocolors";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import pc3 from "picocolors";
|
|
8
|
+
|
|
9
|
+
// src/core/detector.ts
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
var FRAMEWORK_SIGNATURES = [
|
|
12
|
+
{
|
|
13
|
+
name: "bootstrap",
|
|
14
|
+
filePatterns: [
|
|
15
|
+
/bootstrap\.css$/i,
|
|
16
|
+
/bootstrap\.min\.css$/i,
|
|
17
|
+
/bootstrap[\d.-]+\.css$/i,
|
|
18
|
+
/bootstrap[\d.-]+\.min\.css$/i
|
|
19
|
+
],
|
|
20
|
+
contentPatterns: [/\.btn-primary\s*\{/, /\.container-fluid\s*\{/, /\.navbar-/, /--bs-/],
|
|
21
|
+
priority: 10,
|
|
22
|
+
icon: "\uD83C\uDD71๏ธ",
|
|
23
|
+
displayName: "Bootstrap"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "tailwind",
|
|
27
|
+
filePatterns: [
|
|
28
|
+
/tailwind\.css$/i,
|
|
29
|
+
/tailwind\.min\.css$/i,
|
|
30
|
+
/tailwind\.output\.css$/i,
|
|
31
|
+
/tailwind-output\.css$/i
|
|
32
|
+
],
|
|
33
|
+
contentPatterns: [
|
|
34
|
+
/--tw-/,
|
|
35
|
+
/\.hover\\:/,
|
|
36
|
+
/\.focus\\:/,
|
|
37
|
+
/@tailwind\s+(base|components|utilities)/,
|
|
38
|
+
/\.-?m[trblxy]?-\[/
|
|
39
|
+
],
|
|
40
|
+
priority: 10,
|
|
41
|
+
icon: "\uD83C\uDF0A",
|
|
42
|
+
displayName: "Tailwind CSS"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "bulma",
|
|
46
|
+
filePatterns: [/bulma\.css$/i, /bulma\.min\.css$/i],
|
|
47
|
+
contentPatterns: [/\.is-primary\s*\{/, /\.columns\s*\{/, /\.navbar-burger/],
|
|
48
|
+
priority: 10,
|
|
49
|
+
icon: "\uD83C\uDFA8",
|
|
50
|
+
displayName: "Bulma"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "foundation",
|
|
54
|
+
filePatterns: [/foundation\.css$/i, /foundation\.min\.css$/i],
|
|
55
|
+
contentPatterns: [/\.callout\s*\{/, /\.top-bar\s*\{/, /\.orbit/],
|
|
56
|
+
priority: 10,
|
|
57
|
+
icon: "\uD83C\uDFDB๏ธ",
|
|
58
|
+
displayName: "Foundation"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "generic-large",
|
|
62
|
+
filePatterns: [],
|
|
63
|
+
contentPatterns: [],
|
|
64
|
+
priority: 1,
|
|
65
|
+
minSize: 10 * 1024,
|
|
66
|
+
icon: "\uD83D\uDCC4",
|
|
67
|
+
displayName: "Large CSS File"
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
function matchFilename(basename) {
|
|
71
|
+
for (const sig of FRAMEWORK_SIGNATURES) {
|
|
72
|
+
if (sig.filePatterns.some((pattern) => pattern.test(basename))) {
|
|
73
|
+
return { type: sig.name, signature: sig };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function matchContent(content) {
|
|
79
|
+
for (const sig of FRAMEWORK_SIGNATURES) {
|
|
80
|
+
if (sig.contentPatterns.length === 0)
|
|
81
|
+
continue;
|
|
82
|
+
const matches = sig.contentPatterns.filter((pattern) => pattern.test(content));
|
|
83
|
+
if (matches.length >= 2) {
|
|
84
|
+
return { type: sig.name, signature: sig };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
async function detectFrameworks(files) {
|
|
90
|
+
const detected = [];
|
|
91
|
+
const processedPaths = new Set;
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
if (processedPaths.has(file.path))
|
|
94
|
+
continue;
|
|
95
|
+
const filenameMatch = matchFilename(file.basename);
|
|
96
|
+
if (filenameMatch) {
|
|
97
|
+
detected.push({
|
|
98
|
+
type: filenameMatch.type,
|
|
99
|
+
file,
|
|
100
|
+
confidence: "high",
|
|
101
|
+
matchedBy: "filename"
|
|
102
|
+
});
|
|
103
|
+
processedPaths.add(file.path);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (file.size > 5 * 1024) {
|
|
107
|
+
try {
|
|
108
|
+
const content = await readFileHead(file.path, 50 * 1024);
|
|
109
|
+
const contentMatch = matchContent(content);
|
|
110
|
+
if (contentMatch) {
|
|
111
|
+
detected.push({
|
|
112
|
+
type: contentMatch.type,
|
|
113
|
+
file,
|
|
114
|
+
confidence: "medium",
|
|
115
|
+
matchedBy: "content"
|
|
116
|
+
});
|
|
117
|
+
processedPaths.add(file.path);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const genericSig = FRAMEWORK_SIGNATURES.find((s) => s.name === "generic-large");
|
|
125
|
+
if (genericSig?.minSize && file.size > genericSig.minSize) {
|
|
126
|
+
const entryNames = ["app.css", "main.css", "styles.css", "style.css", "global.css"];
|
|
127
|
+
if (entryNames.includes(file.basename.toLowerCase())) {
|
|
128
|
+
detected.push({
|
|
129
|
+
type: "generic-large",
|
|
130
|
+
file,
|
|
131
|
+
confidence: "low",
|
|
132
|
+
matchedBy: "size"
|
|
133
|
+
});
|
|
134
|
+
processedPaths.add(file.path);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return detected.sort((a, b) => b.file.size - a.file.size);
|
|
139
|
+
}
|
|
140
|
+
async function readFileHead(filepath, maxBytes) {
|
|
141
|
+
const content = await readFile(filepath, "utf-8");
|
|
142
|
+
return content.slice(0, maxBytes);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/core/generator.ts
|
|
146
|
+
import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
|
|
147
|
+
import { dirname, join } from "node:path";
|
|
148
|
+
|
|
149
|
+
// src/types.ts
|
|
150
|
+
var DEFAULT_CONFIG = {
|
|
151
|
+
hues: {
|
|
152
|
+
primary: 260,
|
|
153
|
+
secondary: 320,
|
|
154
|
+
accent: 45,
|
|
155
|
+
neutral: 260
|
|
156
|
+
},
|
|
157
|
+
mode: "greenfield",
|
|
158
|
+
paths: {
|
|
159
|
+
stylesheet: "src/styles/shift.css"
|
|
160
|
+
},
|
|
161
|
+
version: 1
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/core/generator.ts
|
|
165
|
+
var SHIFT_LAYERS = "shift.tokens, shift.base, shift.components, shift.utilities";
|
|
166
|
+
function generateLayerOrder(mode) {
|
|
167
|
+
if (mode === "hybrid") {
|
|
168
|
+
return `@layer legacy, ${SHIFT_LAYERS};`;
|
|
169
|
+
}
|
|
170
|
+
return `@layer ${SHIFT_LAYERS};`;
|
|
171
|
+
}
|
|
172
|
+
function generateConfig(options = {}) {
|
|
173
|
+
return {
|
|
174
|
+
...DEFAULT_CONFIG,
|
|
175
|
+
...options,
|
|
176
|
+
hues: {
|
|
177
|
+
...DEFAULT_CONFIG.hues,
|
|
178
|
+
...options.hues
|
|
179
|
+
},
|
|
180
|
+
paths: {
|
|
181
|
+
...DEFAULT_CONFIG.paths,
|
|
182
|
+
...options.paths
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function generateStylesheet(mode, detectedFrameworks = []) {
|
|
187
|
+
const lines = [];
|
|
188
|
+
lines.push("/**");
|
|
189
|
+
lines.push(" * Shift CSS Entry Point");
|
|
190
|
+
lines.push(" * Initialized by shift-css init");
|
|
191
|
+
lines.push(" *");
|
|
192
|
+
if (mode === "hybrid") {
|
|
193
|
+
lines.push(" * Layer order (lowest โ highest specificity):");
|
|
194
|
+
lines.push(" * 1. legacy - Your existing frameworks");
|
|
195
|
+
lines.push(" * 2. shift.tokens - Design tokens (colors, spacing, typography)");
|
|
196
|
+
lines.push(" * 3. shift.base - Reset and base styles");
|
|
197
|
+
lines.push(" * 4. shift.components - UI components");
|
|
198
|
+
lines.push(" * 5. shift.utilities - Utility classes");
|
|
199
|
+
lines.push(" * 6. (unlayered) - Your custom overrides (highest specificity)");
|
|
200
|
+
} else {
|
|
201
|
+
lines.push(" * Layer order (lowest โ highest specificity):");
|
|
202
|
+
lines.push(" * 1. shift.tokens - Design tokens (colors, spacing, typography)");
|
|
203
|
+
lines.push(" * 2. shift.base - Reset and base styles");
|
|
204
|
+
lines.push(" * 3. shift.components - UI components");
|
|
205
|
+
lines.push(" * 4. shift.utilities - Utility classes");
|
|
206
|
+
lines.push(" * 5. (unlayered) - Your custom overrides (highest specificity)");
|
|
207
|
+
}
|
|
208
|
+
lines.push(" */");
|
|
209
|
+
lines.push("");
|
|
210
|
+
lines.push(generateLayerOrder(mode));
|
|
211
|
+
lines.push("");
|
|
212
|
+
lines.push('@import "@shift-css/core/tokens";');
|
|
213
|
+
lines.push('@import "@shift-css/core/base";');
|
|
214
|
+
lines.push('@import "@shift-css/core/components";');
|
|
215
|
+
lines.push('@import "@shift-css/core/utilities";');
|
|
216
|
+
lines.push("");
|
|
217
|
+
if (mode === "hybrid") {
|
|
218
|
+
lines.push("/* Legacy framework imports - add your existing CSS here */");
|
|
219
|
+
lines.push("@layer legacy {");
|
|
220
|
+
if (detectedFrameworks.length > 0) {
|
|
221
|
+
for (const fw of detectedFrameworks) {
|
|
222
|
+
lines.push(` /* @import "${fw.file.relativePath}"; */`);
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
lines.push(' /* @import "your-existing-framework.css"; */');
|
|
226
|
+
}
|
|
227
|
+
lines.push("}");
|
|
228
|
+
lines.push("");
|
|
229
|
+
}
|
|
230
|
+
lines.push("/* Your custom styles below */");
|
|
231
|
+
lines.push("");
|
|
232
|
+
return lines.join(`
|
|
233
|
+
`);
|
|
234
|
+
}
|
|
235
|
+
async function writeConfig(rootDir, config) {
|
|
236
|
+
const configPath = join(rootDir, "shift.config.json");
|
|
237
|
+
const content = JSON.stringify(config, null, "\t");
|
|
238
|
+
await writeFile(configPath, `${content}
|
|
239
|
+
`, "utf-8");
|
|
240
|
+
return configPath;
|
|
241
|
+
}
|
|
242
|
+
async function writeStylesheet(rootDir, relativePath, content) {
|
|
243
|
+
const fullPath = join(rootDir, relativePath);
|
|
244
|
+
const dir = dirname(fullPath);
|
|
245
|
+
await mkdir(dir, { recursive: true });
|
|
246
|
+
await writeFile(fullPath, content, "utf-8");
|
|
247
|
+
return fullPath;
|
|
248
|
+
}
|
|
249
|
+
async function configExists(rootDir) {
|
|
250
|
+
try {
|
|
251
|
+
await readFile2(join(rootDir, "shift.config.json"), "utf-8");
|
|
252
|
+
return true;
|
|
253
|
+
} catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async function stylesheetExists(rootDir, relativePath) {
|
|
258
|
+
try {
|
|
259
|
+
await readFile2(join(rootDir, relativePath), "utf-8");
|
|
260
|
+
return true;
|
|
261
|
+
} catch {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/core/scanner.ts
|
|
267
|
+
import { readdir, stat } from "node:fs/promises";
|
|
268
|
+
import { basename, join as join2, relative } from "node:path";
|
|
269
|
+
var IGNORED_DIRS = new Set([
|
|
270
|
+
"node_modules",
|
|
271
|
+
".git",
|
|
272
|
+
".svn",
|
|
273
|
+
".hg",
|
|
274
|
+
"dist",
|
|
275
|
+
"build",
|
|
276
|
+
"coverage",
|
|
277
|
+
".next",
|
|
278
|
+
".nuxt",
|
|
279
|
+
".output",
|
|
280
|
+
".vercel",
|
|
281
|
+
".shift-backup"
|
|
282
|
+
]);
|
|
283
|
+
async function scanDirectory(dir, rootDir, files) {
|
|
284
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
285
|
+
for (const entry of entries) {
|
|
286
|
+
const fullPath = join2(dir, entry.name);
|
|
287
|
+
if (entry.isDirectory()) {
|
|
288
|
+
if (!IGNORED_DIRS.has(entry.name)) {
|
|
289
|
+
await scanDirectory(fullPath, rootDir, files);
|
|
290
|
+
}
|
|
291
|
+
} else if (entry.isFile() && entry.name.endsWith(".css")) {
|
|
292
|
+
const stats = await stat(fullPath);
|
|
293
|
+
files.push({
|
|
294
|
+
path: fullPath,
|
|
295
|
+
relativePath: relative(rootDir, fullPath),
|
|
296
|
+
basename: basename(fullPath),
|
|
297
|
+
size: stats.size
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async function scanForCssFiles(rootDir) {
|
|
303
|
+
const files = [];
|
|
304
|
+
await scanDirectory(rootDir, rootDir, files);
|
|
305
|
+
return files.sort((a, b) => b.size - a.size);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/ui/display.ts
|
|
309
|
+
import pc from "picocolors";
|
|
310
|
+
function showStylesheetPreview(content) {
|
|
311
|
+
console.log("");
|
|
312
|
+
console.log(pc.bold(" Stylesheet Preview:"));
|
|
313
|
+
console.log("");
|
|
314
|
+
console.log(pc.dim(" โ".repeat(40)));
|
|
315
|
+
console.log("");
|
|
316
|
+
const lines = content.split(`
|
|
317
|
+
`);
|
|
318
|
+
const maxLines = 25;
|
|
319
|
+
const displayLines = lines.slice(0, maxLines);
|
|
320
|
+
for (const line of displayLines) {
|
|
321
|
+
if (line.startsWith("/**") || line.startsWith(" *") || line.startsWith(" */")) {
|
|
322
|
+
console.log(` ${pc.dim(line)}`);
|
|
323
|
+
} else if (line.startsWith("@layer") || line.startsWith("@import")) {
|
|
324
|
+
console.log(` ${pc.cyan(line)}`);
|
|
325
|
+
} else if (line.includes("/*") && line.includes("*/")) {
|
|
326
|
+
console.log(` ${pc.dim(line)}`);
|
|
327
|
+
} else {
|
|
328
|
+
console.log(` ${line}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (lines.length > maxLines) {
|
|
332
|
+
console.log(` ${pc.dim(`... ${lines.length - maxLines} more lines`)}`);
|
|
333
|
+
}
|
|
334
|
+
console.log("");
|
|
335
|
+
console.log(pc.dim(" โ".repeat(40)));
|
|
336
|
+
console.log("");
|
|
337
|
+
}
|
|
338
|
+
function showNextSteps(result) {
|
|
339
|
+
console.log("");
|
|
340
|
+
console.log(pc.bold(" Next steps:"));
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log(` 1. ${pc.cyan("npm install @shift-css/core")}`);
|
|
343
|
+
console.log(` 2. Import ${pc.cyan(result.stylesheetPath.split("/").pop())} in your app`);
|
|
344
|
+
if (result.mode === "hybrid" && result.detectedFrameworks?.length) {
|
|
345
|
+
console.log(` 3. Uncomment your framework imports in the @layer legacy block`);
|
|
346
|
+
console.log(` 4. Start using Shift CSS components alongside your existing code`);
|
|
347
|
+
} else {
|
|
348
|
+
console.log(` 3. Start using Shift CSS components and utilities`);
|
|
349
|
+
}
|
|
350
|
+
console.log("");
|
|
351
|
+
console.log(pc.dim(` \uD83D\uDCDA Documentation: https://getshiftcss.com`));
|
|
352
|
+
console.log("");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/ui/prompts.ts
|
|
356
|
+
import * as p from "@clack/prompts";
|
|
357
|
+
import pc2 from "picocolors";
|
|
358
|
+
|
|
359
|
+
// src/core/color.ts
|
|
360
|
+
function hexToRgb(hex) {
|
|
361
|
+
const cleaned = hex.replace(/^#/, "");
|
|
362
|
+
const fullHex = cleaned.length === 3 ? cleaned.split("").map((c) => c + c).join("") : cleaned;
|
|
363
|
+
if (fullHex.length !== 6)
|
|
364
|
+
return null;
|
|
365
|
+
const result = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(fullHex);
|
|
366
|
+
if (!result)
|
|
367
|
+
return null;
|
|
368
|
+
return {
|
|
369
|
+
r: Number.parseInt(result[1], 16),
|
|
370
|
+
g: Number.parseInt(result[2], 16),
|
|
371
|
+
b: Number.parseInt(result[3], 16)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function rgbToHue(r, g, b) {
|
|
375
|
+
const rNorm = r / 255;
|
|
376
|
+
const gNorm = g / 255;
|
|
377
|
+
const bNorm = b / 255;
|
|
378
|
+
const max = Math.max(rNorm, gNorm, bNorm);
|
|
379
|
+
const min = Math.min(rNorm, gNorm, bNorm);
|
|
380
|
+
const delta = max - min;
|
|
381
|
+
if (delta === 0)
|
|
382
|
+
return 250;
|
|
383
|
+
let hue;
|
|
384
|
+
if (max === rNorm) {
|
|
385
|
+
hue = (gNorm - bNorm) / delta % 6;
|
|
386
|
+
} else if (max === gNorm) {
|
|
387
|
+
hue = (bNorm - rNorm) / delta + 2;
|
|
388
|
+
} else {
|
|
389
|
+
hue = (rNorm - gNorm) / delta + 4;
|
|
390
|
+
}
|
|
391
|
+
hue = Math.round(hue * 60);
|
|
392
|
+
if (hue < 0)
|
|
393
|
+
hue += 360;
|
|
394
|
+
return hue;
|
|
395
|
+
}
|
|
396
|
+
function hexToHue(hex) {
|
|
397
|
+
const rgb = hexToRgb(hex);
|
|
398
|
+
if (!rgb)
|
|
399
|
+
return null;
|
|
400
|
+
return rgbToHue(rgb.r, rgb.g, rgb.b);
|
|
401
|
+
}
|
|
402
|
+
function isHexColor(value) {
|
|
403
|
+
return /^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/.test(value.trim());
|
|
404
|
+
}
|
|
405
|
+
function getColorName(hue) {
|
|
406
|
+
const h = (hue % 360 + 360) % 360;
|
|
407
|
+
if (h >= 0 && h < 15)
|
|
408
|
+
return "Red";
|
|
409
|
+
if (h >= 15 && h < 45)
|
|
410
|
+
return "Orange";
|
|
411
|
+
if (h >= 45 && h < 75)
|
|
412
|
+
return "Yellow";
|
|
413
|
+
if (h >= 75 && h < 105)
|
|
414
|
+
return "Lime";
|
|
415
|
+
if (h >= 105 && h < 135)
|
|
416
|
+
return "Green";
|
|
417
|
+
if (h >= 135 && h < 165)
|
|
418
|
+
return "Teal";
|
|
419
|
+
if (h >= 165 && h < 195)
|
|
420
|
+
return "Cyan";
|
|
421
|
+
if (h >= 195 && h < 225)
|
|
422
|
+
return "Sky";
|
|
423
|
+
if (h >= 225 && h < 255)
|
|
424
|
+
return "Blue";
|
|
425
|
+
if (h >= 255 && h < 285)
|
|
426
|
+
return "Indigo";
|
|
427
|
+
if (h >= 285 && h < 315)
|
|
428
|
+
return "Purple";
|
|
429
|
+
if (h >= 315 && h < 345)
|
|
430
|
+
return "Pink";
|
|
431
|
+
return "Red";
|
|
432
|
+
}
|
|
433
|
+
var PRESETS = [
|
|
434
|
+
{ name: "Plasma", hue: 260, description: "Electric Blue - High-tech default" },
|
|
435
|
+
{ name: "Laser", hue: 320, description: "Cyber-Pink - Neon futurism" },
|
|
436
|
+
{ name: "Acid", hue: 140, description: "Toxic Green - Engineering edge" },
|
|
437
|
+
{ name: "Void", hue: 0, description: "Monochrome - Industrial minimal" }
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
// src/ui/prompts.ts
|
|
441
|
+
function showIntro() {
|
|
442
|
+
console.clear();
|
|
443
|
+
p.intro(pc2.bgMagenta(pc2.white(" \uD83C\uDFA8 Shift CSS Init ")));
|
|
444
|
+
}
|
|
445
|
+
function showOutro(message) {
|
|
446
|
+
p.outro(message);
|
|
447
|
+
}
|
|
448
|
+
function handleCancel() {
|
|
449
|
+
p.cancel("Setup cancelled");
|
|
450
|
+
process.exit(0);
|
|
451
|
+
}
|
|
452
|
+
function createSpinner() {
|
|
453
|
+
return p.spinner();
|
|
454
|
+
}
|
|
455
|
+
function showNote(message, title) {
|
|
456
|
+
p.note(message, title);
|
|
457
|
+
}
|
|
458
|
+
async function askArchitectureMode(detectedFrameworks) {
|
|
459
|
+
const hasFrameworks = detectedFrameworks.length > 0;
|
|
460
|
+
const frameworkHint = hasFrameworks ? `Detected: ${detectedFrameworks.map((f) => f.type).join(", ")}` : undefined;
|
|
461
|
+
const mode = await p.select({
|
|
462
|
+
message: "What type of project is this?",
|
|
463
|
+
options: [
|
|
464
|
+
{
|
|
465
|
+
value: "greenfield",
|
|
466
|
+
label: "New project (Greenfield)",
|
|
467
|
+
hint: "Pure Shift CSS, no legacy frameworks"
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
value: "hybrid",
|
|
471
|
+
label: "Existing project (Hybrid)",
|
|
472
|
+
hint: frameworkHint ?? "Shift CSS alongside existing CSS"
|
|
473
|
+
}
|
|
474
|
+
],
|
|
475
|
+
initialValue: hasFrameworks ? "hybrid" : "greenfield"
|
|
476
|
+
});
|
|
477
|
+
if (p.isCancel(mode))
|
|
478
|
+
handleCancel();
|
|
479
|
+
return mode;
|
|
480
|
+
}
|
|
481
|
+
async function askPrimaryHue() {
|
|
482
|
+
const presetOptions = PRESETS.map((preset2) => ({
|
|
483
|
+
value: preset2.hue,
|
|
484
|
+
label: preset2.name,
|
|
485
|
+
hint: preset2.description
|
|
486
|
+
}));
|
|
487
|
+
const hue = await p.select({
|
|
488
|
+
message: "Choose your brand color:",
|
|
489
|
+
options: [
|
|
490
|
+
...presetOptions,
|
|
491
|
+
{ value: -1, label: "Custom", hint: "Enter hex code or hue value" }
|
|
492
|
+
]
|
|
493
|
+
});
|
|
494
|
+
if (p.isCancel(hue))
|
|
495
|
+
handleCancel();
|
|
496
|
+
if (hue === -1) {
|
|
497
|
+
p.log.info(pc2.dim("Hue guide: 20=Red, 90=Yellow, 140=Green, 260=Blue, 320=Purple"));
|
|
498
|
+
const customValue = await p.text({
|
|
499
|
+
message: "Enter a hex code (#a855f7) or hue (0-360):",
|
|
500
|
+
placeholder: "#a855f7 or 260",
|
|
501
|
+
validate: (value) => {
|
|
502
|
+
const trimmed2 = value.trim();
|
|
503
|
+
if (isHexColor(trimmed2)) {
|
|
504
|
+
const parsedHue = hexToHue(trimmed2);
|
|
505
|
+
if (parsedHue === null)
|
|
506
|
+
return "Invalid hex color";
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const num = Number.parseInt(trimmed2, 10);
|
|
510
|
+
if (Number.isNaN(num))
|
|
511
|
+
return "Enter a hex code (#ff0000) or hue number (0-360)";
|
|
512
|
+
if (num < 0 || num > 360)
|
|
513
|
+
return "Hue must be between 0 and 360";
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
if (p.isCancel(customValue))
|
|
518
|
+
handleCancel();
|
|
519
|
+
const trimmed = customValue.trim();
|
|
520
|
+
if (isHexColor(trimmed)) {
|
|
521
|
+
const parsedHue = hexToHue(trimmed);
|
|
522
|
+
if (parsedHue !== null) {
|
|
523
|
+
const colorName = getColorName(parsedHue);
|
|
524
|
+
p.log.success(`Converted ${pc2.cyan(trimmed)} โ ${pc2.magenta(colorName)} (Hue: ${parsedHue})`);
|
|
525
|
+
return parsedHue;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return Number.parseInt(trimmed, 10);
|
|
529
|
+
}
|
|
530
|
+
const preset = PRESETS.find((p2) => p2.hue === hue);
|
|
531
|
+
if (preset) {
|
|
532
|
+
const colorName = getColorName(preset.hue);
|
|
533
|
+
p.log.info(`${pc2.magenta(preset.name)} โ ${colorName} (Hue: ${preset.hue})`);
|
|
534
|
+
}
|
|
535
|
+
return hue;
|
|
536
|
+
}
|
|
537
|
+
async function askStylesheetPath(defaultPath) {
|
|
538
|
+
const path = await p.text({
|
|
539
|
+
message: "Where should the stylesheet be created?",
|
|
540
|
+
placeholder: defaultPath,
|
|
541
|
+
defaultValue: defaultPath,
|
|
542
|
+
validate: (value) => {
|
|
543
|
+
if (!value)
|
|
544
|
+
return "Please enter a path";
|
|
545
|
+
if (!value.endsWith(".css"))
|
|
546
|
+
return "Path must end with .css";
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
if (p.isCancel(path))
|
|
551
|
+
handleCancel();
|
|
552
|
+
return path;
|
|
553
|
+
}
|
|
554
|
+
async function confirmOverwrite(filePath) {
|
|
555
|
+
const confirmed = await p.confirm({
|
|
556
|
+
message: `${pc2.yellow(filePath)} already exists. Overwrite it?`,
|
|
557
|
+
initialValue: false
|
|
558
|
+
});
|
|
559
|
+
if (p.isCancel(confirmed))
|
|
560
|
+
handleCancel();
|
|
561
|
+
return confirmed;
|
|
562
|
+
}
|
|
563
|
+
async function confirmInit(configPath, stylesheetPath, mode) {
|
|
564
|
+
const modeLabel = mode === "greenfield" ? "Greenfield" : "Hybrid";
|
|
565
|
+
showNote([
|
|
566
|
+
`${pc2.bold("Config:")} ${pc2.cyan(configPath)}`,
|
|
567
|
+
`${pc2.bold("Stylesheet:")} ${pc2.cyan(stylesheetPath)}`,
|
|
568
|
+
`${pc2.bold("Mode:")} ${pc2.cyan(modeLabel)}`
|
|
569
|
+
].join(`
|
|
570
|
+
`), "Files to create");
|
|
571
|
+
const confirmed = await p.confirm({
|
|
572
|
+
message: "Proceed with initialization?",
|
|
573
|
+
initialValue: true
|
|
574
|
+
});
|
|
575
|
+
if (p.isCancel(confirmed))
|
|
576
|
+
handleCancel();
|
|
577
|
+
return confirmed;
|
|
578
|
+
}
|
|
579
|
+
function logSuccess(message) {
|
|
580
|
+
p.log.success(message);
|
|
581
|
+
}
|
|
582
|
+
function logWarning(message) {
|
|
583
|
+
p.log.warn(message);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/commands/init.ts
|
|
587
|
+
async function initCommand() {
|
|
588
|
+
const rootDir = process.cwd();
|
|
589
|
+
showIntro();
|
|
590
|
+
const hasConfig = await configExists(rootDir);
|
|
591
|
+
if (hasConfig) {
|
|
592
|
+
logWarning("This project already has a shift.config.json");
|
|
593
|
+
const shouldOverwrite = await confirmOverwrite("shift.config.json");
|
|
594
|
+
if (!shouldOverwrite) {
|
|
595
|
+
showOutro(pc3.yellow("Initialization cancelled"));
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const spinner2 = createSpinner();
|
|
600
|
+
spinner2.start("Scanning project...");
|
|
601
|
+
const cssFiles = await scanForCssFiles(rootDir);
|
|
602
|
+
const detectedFrameworks = await detectFrameworks(cssFiles);
|
|
603
|
+
if (detectedFrameworks.length > 0) {
|
|
604
|
+
spinner2.stop(`Detected existing CSS: ${detectedFrameworks.map((f) => f.type).join(", ")}`);
|
|
605
|
+
} else {
|
|
606
|
+
spinner2.stop("Project scanned");
|
|
607
|
+
}
|
|
608
|
+
const mode = await askArchitectureMode(detectedFrameworks);
|
|
609
|
+
const primaryHue = await askPrimaryHue();
|
|
610
|
+
const defaultStylesheetPath = DEFAULT_CONFIG.paths.stylesheet;
|
|
611
|
+
const stylesheetPath = await askStylesheetPath(defaultStylesheetPath);
|
|
612
|
+
const hasStylesheet = await stylesheetExists(rootDir, stylesheetPath);
|
|
613
|
+
if (hasStylesheet) {
|
|
614
|
+
const shouldOverwrite = await confirmOverwrite(stylesheetPath);
|
|
615
|
+
if (!shouldOverwrite) {
|
|
616
|
+
showOutro(pc3.yellow("Initialization cancelled"));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const stylesheetContent = generateStylesheet(mode, detectedFrameworks);
|
|
621
|
+
showStylesheetPreview(stylesheetContent);
|
|
622
|
+
const shouldProceed = await confirmInit("shift.config.json", stylesheetPath, mode);
|
|
623
|
+
if (!shouldProceed) {
|
|
624
|
+
showOutro(pc3.yellow("Initialization cancelled"));
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const config = generateConfig({
|
|
628
|
+
mode,
|
|
629
|
+
hues: {
|
|
630
|
+
primary: primaryHue,
|
|
631
|
+
secondary: DEFAULT_CONFIG.hues.secondary,
|
|
632
|
+
accent: DEFAULT_CONFIG.hues.accent,
|
|
633
|
+
neutral: primaryHue
|
|
634
|
+
},
|
|
635
|
+
paths: {
|
|
636
|
+
stylesheet: stylesheetPath
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
spinner2.start("Creating files...");
|
|
640
|
+
const configPath = await writeConfig(rootDir, config);
|
|
641
|
+
const fullStylesheetPath = await writeStylesheet(rootDir, stylesheetPath, stylesheetContent);
|
|
642
|
+
spinner2.stop("Files created");
|
|
643
|
+
logSuccess(`Created ${pc3.cyan("shift.config.json")}`);
|
|
644
|
+
logSuccess(`Created ${pc3.cyan(stylesheetPath)}`);
|
|
645
|
+
const result = {
|
|
646
|
+
configPath,
|
|
647
|
+
stylesheetPath: fullStylesheetPath,
|
|
648
|
+
mode,
|
|
649
|
+
detectedFrameworks: detectedFrameworks.length > 0 ? detectedFrameworks : undefined
|
|
650
|
+
};
|
|
651
|
+
showNextSteps(result);
|
|
652
|
+
showOutro(pc3.green("โจ Shift CSS initialized!"));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/index.ts
|
|
656
|
+
var VERSION = "0.0.1";
|
|
657
|
+
function showHelp() {
|
|
658
|
+
console.log(`
|
|
659
|
+
${pc4.bold("Shift CSS CLI")} ${pc4.dim(`v${VERSION}`)}
|
|
660
|
+
|
|
661
|
+
${pc4.dim("Usage:")}
|
|
662
|
+
${pc4.cyan("shift-css")} ${pc4.green("<command>")} ${pc4.dim("[options]")}
|
|
663
|
+
|
|
664
|
+
${pc4.dim("Commands:")}
|
|
665
|
+
${pc4.green("init")} Set up Shift CSS in your project
|
|
666
|
+
Detects existing frameworks and wraps them in @layer legacy
|
|
667
|
+
|
|
668
|
+
${pc4.dim("Options:")}
|
|
669
|
+
${pc4.yellow("--help, -h")} Show this help message
|
|
670
|
+
${pc4.yellow("--version, -v")} Show version number
|
|
671
|
+
|
|
672
|
+
${pc4.dim("Examples:")}
|
|
673
|
+
${pc4.cyan("npx shift-css init")}
|
|
674
|
+
${pc4.cyan("npx shift-css --help")}
|
|
675
|
+
|
|
676
|
+
${pc4.dim("Learn more:")} ${pc4.underline("https://getshiftcss.com")}
|
|
677
|
+
`);
|
|
678
|
+
}
|
|
679
|
+
function showVersion() {
|
|
680
|
+
console.log(`shift-css v${VERSION}`);
|
|
681
|
+
}
|
|
682
|
+
async function main() {
|
|
683
|
+
const args = process.argv.slice(2);
|
|
684
|
+
const command = args[0];
|
|
685
|
+
if (command === "--help" || command === "-h") {
|
|
686
|
+
showHelp();
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (command === "--version" || command === "-v") {
|
|
690
|
+
showVersion();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (command === "init" || !command) {
|
|
694
|
+
await initCommand();
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
console.error(pc4.red(`Unknown command: ${command}`));
|
|
698
|
+
console.log(pc4.dim(`Run ${pc4.cyan("shift-css --help")} for usage information.`));
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
main().catch((error) => {
|
|
702
|
+
console.error(pc4.red("Error:"), error.message);
|
|
703
|
+
if (process.env.DEBUG) {
|
|
704
|
+
console.error(error.stack);
|
|
705
|
+
}
|
|
706
|
+
process.exit(1);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
//# debugId=D1C3128B75F8A46664756E2164756E21
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/commands/init.ts", "../src/core/detector.ts", "../src/core/generator.ts", "../src/types.ts", "../src/core/scanner.ts", "../src/ui/display.ts", "../src/ui/prompts.ts", "../src/core/color.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"#!/usr/bin/env node\n/**\n * Shift CSS CLI\n *\n * A \"Guided Hand\" CLI tool that helps set up CSS layer compatibility\n *\n * Usage:\n * npx shift-css init - Initialize Shift CSS in your project\n * npx shift-css --help - Show help\n */\n\nimport pc from 'picocolors';\nimport { initCommand } from './commands/init.ts';\n\nconst VERSION = '0.0.1';\n\nfunction showHelp(): void {\n\tconsole.log(`\n${pc.bold('Shift CSS CLI')} ${pc.dim(`v${VERSION}`)}\n\n${pc.dim('Usage:')}\n ${pc.cyan('shift-css')} ${pc.green('<command>')} ${pc.dim('[options]')}\n\n${pc.dim('Commands:')}\n ${pc.green('init')} Set up Shift CSS in your project\n Detects existing frameworks and wraps them in @layer legacy\n\n${pc.dim('Options:')}\n ${pc.yellow('--help, -h')} Show this help message\n ${pc.yellow('--version, -v')} Show version number\n\n${pc.dim('Examples:')}\n ${pc.cyan('npx shift-css init')}\n ${pc.cyan('npx shift-css --help')}\n\n${pc.dim('Learn more:')} ${pc.underline('https://getshiftcss.com')}\n`);\n}\n\nfunction showVersion(): void {\n\tconsole.log(`shift-css v${VERSION}`);\n}\n\nasync function main(): Promise<void> {\n\tconst args = process.argv.slice(2);\n\tconst command = args[0];\n\n\t// Handle flags\n\tif (command === '--help' || command === '-h') {\n\t\tshowHelp();\n\t\treturn;\n\t}\n\n\tif (command === '--version' || command === '-v') {\n\t\tshowVersion();\n\t\treturn;\n\t}\n\n\t// Handle commands\n\tif (command === 'init' || !command) {\n\t\tawait initCommand();\n\t\treturn;\n\t}\n\n\t// Unknown command\n\tconsole.error(pc.red(`Unknown command: ${command}`));\n\tconsole.log(pc.dim(`Run ${pc.cyan('shift-css --help')} for usage information.`));\n\tprocess.exit(1);\n}\n\nmain().catch((error: Error) => {\n\tconsole.error(pc.red('Error:'), error.message);\n\tif (process.env.DEBUG) {\n\t\tconsole.error(error.stack);\n\t}\n\tprocess.exit(1);\n});\n",
|
|
6
|
+
"/**\n * Init Command\n * Initialize Shift CSS in a project\n */\n\nimport pc from 'picocolors';\nimport { detectFrameworks } from '../core/detector.ts';\nimport {\n\tconfigExists,\n\tgenerateConfig,\n\tgenerateStylesheet,\n\tstylesheetExists,\n\twriteConfig,\n\twriteStylesheet,\n} from '../core/generator.ts';\nimport { scanForCssFiles } from '../core/scanner.ts';\nimport { DEFAULT_CONFIG, type InitResult } from '../types.ts';\nimport { showNextSteps, showStylesheetPreview } from '../ui/display.ts';\nimport {\n\taskArchitectureMode,\n\taskPrimaryHue,\n\taskStylesheetPath,\n\tconfirmInit,\n\tconfirmOverwrite,\n\tcreateSpinner,\n\tlogSuccess,\n\tlogWarning,\n\tshowIntro,\n\tshowOutro,\n} from '../ui/prompts.ts';\n\n/**\n * Main init command\n */\nexport async function initCommand(): Promise<void> {\n\tconst rootDir = process.cwd();\n\n\tshowIntro();\n\n\t// Check if already initialized\n\tconst hasConfig = await configExists(rootDir);\n\tif (hasConfig) {\n\t\tlogWarning('This project already has a shift.config.json');\n\t\tconst shouldOverwrite = await confirmOverwrite('shift.config.json');\n\t\tif (!shouldOverwrite) {\n\t\t\tshowOutro(pc.yellow('Initialization cancelled'));\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Step 1: Scan for existing CSS frameworks (silent, for detection)\n\tconst spinner = createSpinner();\n\tspinner.start('Scanning project...');\n\n\tconst cssFiles = await scanForCssFiles(rootDir);\n\tconst detectedFrameworks = await detectFrameworks(cssFiles);\n\n\tif (detectedFrameworks.length > 0) {\n\t\tspinner.stop(`Detected existing CSS: ${detectedFrameworks.map((f) => f.type).join(', ')}`);\n\t} else {\n\t\tspinner.stop('Project scanned');\n\t}\n\n\t// Step 2: Ask for architecture mode\n\tconst mode = await askArchitectureMode(detectedFrameworks);\n\n\t// Step 3: Ask for primary hue\n\tconst primaryHue = await askPrimaryHue();\n\n\t// Step 4: Ask for stylesheet path\n\tconst defaultStylesheetPath = DEFAULT_CONFIG.paths.stylesheet;\n\tconst stylesheetPath = await askStylesheetPath(defaultStylesheetPath);\n\n\t// Check if stylesheet already exists\n\tconst hasStylesheet = await stylesheetExists(rootDir, stylesheetPath);\n\tif (hasStylesheet) {\n\t\tconst shouldOverwrite = await confirmOverwrite(stylesheetPath);\n\t\tif (!shouldOverwrite) {\n\t\t\tshowOutro(pc.yellow('Initialization cancelled'));\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Step 5: Preview what will be created\n\tconst stylesheetContent = generateStylesheet(mode, detectedFrameworks);\n\tshowStylesheetPreview(stylesheetContent);\n\n\t// Step 6: Confirm and create files\n\tconst shouldProceed = await confirmInit('shift.config.json', stylesheetPath, mode);\n\n\tif (!shouldProceed) {\n\t\tshowOutro(pc.yellow('Initialization cancelled'));\n\t\treturn;\n\t}\n\n\t// Create config\n\tconst config = generateConfig({\n\t\tmode,\n\t\thues: {\n\t\t\tprimary: primaryHue,\n\t\t\tsecondary: DEFAULT_CONFIG.hues.secondary,\n\t\t\taccent: DEFAULT_CONFIG.hues.accent,\n\t\t\tneutral: primaryHue, // Match neutral to primary for cohesion\n\t\t},\n\t\tpaths: {\n\t\t\tstylesheet: stylesheetPath,\n\t\t},\n\t});\n\n\tspinner.start('Creating files...');\n\n\tconst configPath = await writeConfig(rootDir, config);\n\tconst fullStylesheetPath = await writeStylesheet(rootDir, stylesheetPath, stylesheetContent);\n\n\tspinner.stop('Files created');\n\n\t// Success!\n\tlogSuccess(`Created ${pc.cyan('shift.config.json')}`);\n\tlogSuccess(`Created ${pc.cyan(stylesheetPath)}`);\n\n\tconst result: InitResult = {\n\t\tconfigPath,\n\t\tstylesheetPath: fullStylesheetPath,\n\t\tmode,\n\t\tdetectedFrameworks: detectedFrameworks.length > 0 ? detectedFrameworks : undefined,\n\t};\n\n\t// Show next steps based on mode\n\tshowNextSteps(result);\n\n\tshowOutro(pc.green('โจ Shift CSS initialized!'));\n}\n",
|
|
7
|
+
"/**\n * Framework Detector\n * Identifies CSS frameworks by filename and content patterns\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type { CssFile, DetectedFramework, FrameworkSignature, FrameworkType } from '../types.ts';\n\n/** Framework signatures for detection */\nexport const FRAMEWORK_SIGNATURES: FrameworkSignature[] = [\n\t{\n\t\tname: 'bootstrap',\n\t\tfilePatterns: [\n\t\t\t/bootstrap\\.css$/i,\n\t\t\t/bootstrap\\.min\\.css$/i,\n\t\t\t/bootstrap[\\d.-]+\\.css$/i,\n\t\t\t/bootstrap[\\d.-]+\\.min\\.css$/i,\n\t\t],\n\t\tcontentPatterns: [/\\.btn-primary\\s*\\{/, /\\.container-fluid\\s*\\{/, /\\.navbar-/, /--bs-/],\n\t\tpriority: 10,\n\t\ticon: '๐
ฑ๏ธ',\n\t\tdisplayName: 'Bootstrap',\n\t},\n\t{\n\t\tname: 'tailwind',\n\t\tfilePatterns: [\n\t\t\t/tailwind\\.css$/i,\n\t\t\t/tailwind\\.min\\.css$/i,\n\t\t\t/tailwind\\.output\\.css$/i,\n\t\t\t/tailwind-output\\.css$/i,\n\t\t],\n\t\tcontentPatterns: [\n\t\t\t/--tw-/,\n\t\t\t/\\.hover\\\\:/,\n\t\t\t/\\.focus\\\\:/,\n\t\t\t/@tailwind\\s+(base|components|utilities)/,\n\t\t\t/\\.-?m[trblxy]?-\\[/,\n\t\t],\n\t\tpriority: 10,\n\t\ticon: '๐',\n\t\tdisplayName: 'Tailwind CSS',\n\t},\n\t{\n\t\tname: 'bulma',\n\t\tfilePatterns: [/bulma\\.css$/i, /bulma\\.min\\.css$/i],\n\t\tcontentPatterns: [/\\.is-primary\\s*\\{/, /\\.columns\\s*\\{/, /\\.navbar-burger/],\n\t\tpriority: 10,\n\t\ticon: '๐จ',\n\t\tdisplayName: 'Bulma',\n\t},\n\t{\n\t\tname: 'foundation',\n\t\tfilePatterns: [/foundation\\.css$/i, /foundation\\.min\\.css$/i],\n\t\tcontentPatterns: [/\\.callout\\s*\\{/, /\\.top-bar\\s*\\{/, /\\.orbit/],\n\t\tpriority: 10,\n\t\ticon: '๐๏ธ',\n\t\tdisplayName: 'Foundation',\n\t},\n\t{\n\t\tname: 'generic-large',\n\t\tfilePatterns: [],\n\t\tcontentPatterns: [],\n\t\tpriority: 1,\n\t\tminSize: 10 * 1024, // 10KB\n\t\ticon: '๐',\n\t\tdisplayName: 'Large CSS File',\n\t},\n];\n\n/**\n * Get framework signature by name\n */\nexport function getSignature(type: FrameworkType): FrameworkSignature | undefined {\n\treturn FRAMEWORK_SIGNATURES.find((s) => s.name === type);\n}\n\n/**\n * Check if filename matches any framework pattern\n */\nfunction matchFilename(\n\tbasename: string\n): { type: FrameworkType; signature: FrameworkSignature } | null {\n\tfor (const sig of FRAMEWORK_SIGNATURES) {\n\t\tif (sig.filePatterns.some((pattern) => pattern.test(basename))) {\n\t\t\treturn { type: sig.name, signature: sig };\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Check if content matches framework patterns\n */\nfunction matchContent(\n\tcontent: string\n): { type: FrameworkType; signature: FrameworkSignature } | null {\n\tfor (const sig of FRAMEWORK_SIGNATURES) {\n\t\tif (sig.contentPatterns.length === 0) continue;\n\t\tconst matches = sig.contentPatterns.filter((pattern) => pattern.test(content));\n\t\t// Require at least 2 content pattern matches for confidence\n\t\tif (matches.length >= 2) {\n\t\t\treturn { type: sig.name, signature: sig };\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Detect frameworks from a list of CSS files\n */\nexport async function detectFrameworks(files: CssFile[]): Promise<DetectedFramework[]> {\n\tconst detected: DetectedFramework[] = [];\n\tconst processedPaths = new Set<string>();\n\n\tfor (const file of files) {\n\t\tif (processedPaths.has(file.path)) continue;\n\n\t\t// Pass 1: Filename matching (fast, high confidence)\n\t\tconst filenameMatch = matchFilename(file.basename);\n\t\tif (filenameMatch) {\n\t\t\tdetected.push({\n\t\t\t\ttype: filenameMatch.type,\n\t\t\t\tfile,\n\t\t\t\tconfidence: 'high',\n\t\t\t\tmatchedBy: 'filename',\n\t\t\t});\n\t\t\tprocessedPaths.add(file.path);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Pass 2: Content analysis for larger files\n\t\tif (file.size > 5 * 1024) {\n\t\t\t// >5KB\n\t\t\ttry {\n\t\t\t\t// Read first 50KB for pattern matching\n\t\t\t\tconst content = await readFileHead(file.path, 50 * 1024);\n\n\t\t\t\tconst contentMatch = matchContent(content);\n\t\t\t\tif (contentMatch) {\n\t\t\t\t\tdetected.push({\n\t\t\t\t\t\ttype: contentMatch.type,\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tconfidence: 'medium',\n\t\t\t\t\t\tmatchedBy: 'content',\n\t\t\t\t\t});\n\t\t\t\t\tprocessedPaths.add(file.path);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip files we can't read\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Pass 3: Size-based detection for large generic files\n\t\tconst genericSig = FRAMEWORK_SIGNATURES.find((s) => s.name === 'generic-large');\n\t\tif (genericSig?.minSize && file.size > genericSig.minSize) {\n\t\t\t// Only flag as generic-large if it's a likely entry point name\n\t\t\tconst entryNames = ['app.css', 'main.css', 'styles.css', 'style.css', 'global.css'];\n\t\t\tif (entryNames.includes(file.basename.toLowerCase())) {\n\t\t\t\tdetected.push({\n\t\t\t\t\ttype: 'generic-large',\n\t\t\t\t\tfile,\n\t\t\t\t\tconfidence: 'low',\n\t\t\t\t\tmatchedBy: 'size',\n\t\t\t\t});\n\t\t\t\tprocessedPaths.add(file.path);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort by file size descending\n\treturn detected.sort((a, b) => b.file.size - a.file.size);\n}\n\n/**\n * Read first N bytes of a file\n */\nasync function readFileHead(filepath: string, maxBytes: number): Promise<string> {\n\tconst content = await readFile(filepath, 'utf-8');\n\treturn content.slice(0, maxBytes);\n}\n\n/**\n * Check if a file already has @layer declarations\n */\nexport async function hasLayerDeclarations(filepath: string): Promise<boolean> {\n\ttry {\n\t\tconst content = await readFile(filepath, 'utf-8');\n\t\treturn /@layer\\s+/.test(content);\n\t} catch {\n\t\treturn false;\n\t}\n}\n",
|
|
8
|
+
"/**\n * Code Generator\n * Creates shift.config.json and scaffolds stylesheets\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport {\n\ttype ArchitectureMode,\n\tDEFAULT_CONFIG,\n\ttype DetectedFramework,\n\ttype ShiftConfig,\n} from '../types.ts';\n\n/** Shift CSS layer order */\nconst SHIFT_LAYERS = 'shift.tokens, shift.base, shift.components, shift.utilities';\n\n/**\n * Generate the layer order declaration based on architecture mode\n */\nexport function generateLayerOrder(mode: ArchitectureMode): string {\n\tif (mode === 'hybrid') {\n\t\treturn `@layer legacy, ${SHIFT_LAYERS};`;\n\t}\n\treturn `@layer ${SHIFT_LAYERS};`;\n}\n\n/**\n * Generate config file content\n */\nexport function generateConfig(options: Partial<ShiftConfig> = {}): ShiftConfig {\n\treturn {\n\t\t...DEFAULT_CONFIG,\n\t\t...options,\n\t\thues: {\n\t\t\t...DEFAULT_CONFIG.hues,\n\t\t\t...options.hues,\n\t\t},\n\t\tpaths: {\n\t\t\t...DEFAULT_CONFIG.paths,\n\t\t\t...options.paths,\n\t\t},\n\t};\n}\n\n/**\n * Generate stylesheet content based on architecture mode\n */\nexport function generateStylesheet(\n\tmode: ArchitectureMode,\n\tdetectedFrameworks: DetectedFramework[] = []\n): string {\n\tconst lines: string[] = [];\n\n\t// Header comment\n\tlines.push('/**');\n\tlines.push(' * Shift CSS Entry Point');\n\tlines.push(' * Initialized by shift-css init');\n\tlines.push(' *');\n\tif (mode === 'hybrid') {\n\t\tlines.push(' * Layer order (lowest โ highest specificity):');\n\t\tlines.push(' * 1. legacy - Your existing frameworks');\n\t\tlines.push(' * 2. shift.tokens - Design tokens (colors, spacing, typography)');\n\t\tlines.push(' * 3. shift.base - Reset and base styles');\n\t\tlines.push(' * 4. shift.components - UI components');\n\t\tlines.push(' * 5. shift.utilities - Utility classes');\n\t\tlines.push(' * 6. (unlayered) - Your custom overrides (highest specificity)');\n\t} else {\n\t\tlines.push(' * Layer order (lowest โ highest specificity):');\n\t\tlines.push(' * 1. shift.tokens - Design tokens (colors, spacing, typography)');\n\t\tlines.push(' * 2. shift.base - Reset and base styles');\n\t\tlines.push(' * 3. shift.components - UI components');\n\t\tlines.push(' * 4. shift.utilities - Utility classes');\n\t\tlines.push(' * 5. (unlayered) - Your custom overrides (highest specificity)');\n\t}\n\tlines.push(' */');\n\tlines.push('');\n\n\t// Layer order declaration\n\tlines.push(generateLayerOrder(mode));\n\tlines.push('');\n\n\t// Shift CSS imports\n\tlines.push('@import \"@shift-css/core/tokens\";');\n\tlines.push('@import \"@shift-css/core/base\";');\n\tlines.push('@import \"@shift-css/core/components\";');\n\tlines.push('@import \"@shift-css/core/utilities\";');\n\tlines.push('');\n\n\t// Legacy layer for hybrid mode\n\tif (mode === 'hybrid') {\n\t\tlines.push('/* Legacy framework imports - add your existing CSS here */');\n\t\tlines.push('@layer legacy {');\n\n\t\tif (detectedFrameworks.length > 0) {\n\t\t\tfor (const fw of detectedFrameworks) {\n\t\t\t\tlines.push(` /* @import \"${fw.file.relativePath}\"; */`);\n\t\t\t}\n\t\t} else {\n\t\t\tlines.push(' /* @import \"your-existing-framework.css\"; */');\n\t\t}\n\n\t\tlines.push('}');\n\t\tlines.push('');\n\t}\n\n\t// Custom styles section\n\tlines.push('/* Your custom styles below */');\n\tlines.push('');\n\n\treturn lines.join('\\n');\n}\n\n/**\n * Write config file to disk\n */\nexport async function writeConfig(rootDir: string, config: ShiftConfig): Promise<string> {\n\tconst configPath = join(rootDir, 'shift.config.json');\n\tconst content = JSON.stringify(config, null, '\\t');\n\tawait writeFile(configPath, `${content}\\n`, 'utf-8');\n\treturn configPath;\n}\n\n/**\n * Write stylesheet to disk\n */\nexport async function writeStylesheet(\n\trootDir: string,\n\trelativePath: string,\n\tcontent: string\n): Promise<string> {\n\tconst fullPath = join(rootDir, relativePath);\n\tconst dir = dirname(fullPath);\n\n\t// Create directory if it doesn't exist\n\tawait mkdir(dir, { recursive: true });\n\tawait writeFile(fullPath, content, 'utf-8');\n\n\treturn fullPath;\n}\n\n/**\n * Check if config file already exists\n */\nexport async function configExists(rootDir: string): Promise<boolean> {\n\ttry {\n\t\tawait readFile(join(rootDir, 'shift.config.json'), 'utf-8');\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Read existing config file\n */\nexport async function readConfig(rootDir: string): Promise<ShiftConfig | null> {\n\ttry {\n\t\tconst content = await readFile(join(rootDir, 'shift.config.json'), 'utf-8');\n\t\treturn JSON.parse(content) as ShiftConfig;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Check if stylesheet already exists\n */\nexport async function stylesheetExists(rootDir: string, relativePath: string): Promise<boolean> {\n\ttry {\n\t\tawait readFile(join(rootDir, relativePath), 'utf-8');\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n",
|
|
9
|
+
"/**\n * Shift CSS CLI - Type Definitions\n */\n\n/**\n * Architecture mode for the project\n * - greenfield: Pure Shift CSS, no legacy frameworks\n * - hybrid: Shift CSS alongside existing CSS frameworks\n */\nexport type ArchitectureMode = 'greenfield' | 'hybrid';\n\n/**\n * Shift CSS configuration file schema (shift.config.json)\n */\nexport interface ShiftConfig {\n\t/** OKLCH seed hues for color generation */\n\thues: {\n\t\tprimary: number;\n\t\tsecondary: number;\n\t\taccent: number;\n\t\tneutral: number;\n\t};\n\t/** Project architecture mode */\n\tmode: ArchitectureMode;\n\t/** Path configuration */\n\tpaths: {\n\t\t/** Main stylesheet path */\n\t\tstylesheet: string;\n\t};\n\t/** Schema version for future compatibility */\n\tversion: 1;\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG: ShiftConfig = {\n\thues: {\n\t\tprimary: 260, // Plasma - Electric Blue\n\t\tsecondary: 320, // Laser - Cyber-Pink\n\t\taccent: 45, // Gold\n\t\tneutral: 260, // Blue-tinted grays\n\t},\n\tmode: 'greenfield',\n\tpaths: {\n\t\tstylesheet: 'src/styles/shift.css',\n\t},\n\tversion: 1,\n};\n\n/**\n * Framework detection types (for hybrid mode)\n */\nexport type FrameworkType = 'bootstrap' | 'tailwind' | 'bulma' | 'foundation' | 'generic-large';\n\nexport type Confidence = 'high' | 'medium' | 'low';\nexport type MatchedBy = 'filename' | 'content' | 'size';\n\nexport interface CssFile {\n\t/** Absolute path */\n\tpath: string;\n\t/** Relative to project root */\n\trelativePath: string;\n\t/** Filename only */\n\tbasename: string;\n\t/** File size in bytes */\n\tsize: number;\n}\n\nexport interface DetectedFramework {\n\ttype: FrameworkType;\n\tfile: CssFile;\n\tconfidence: Confidence;\n\tmatchedBy: MatchedBy;\n}\n\nexport interface FrameworkSignature {\n\tname: FrameworkType;\n\t/** Filename patterns to match */\n\tfilePatterns: RegExp[];\n\t/** CSS content patterns to match */\n\tcontentPatterns: RegExp[];\n\t/** Higher priority = more specific match */\n\tpriority: number;\n\t/** Minimum file size for generic detection */\n\tminSize?: number;\n\t/** Display icon */\n\ticon: string;\n\t/** Display name */\n\tdisplayName: string;\n}\n\n/**\n * Init command result\n */\nexport interface InitResult {\n\t/** Path to created/updated config file */\n\tconfigPath: string;\n\t/** Path to created/updated stylesheet */\n\tstylesheetPath: string;\n\t/** Selected architecture mode */\n\tmode: ArchitectureMode;\n\t/** Detected frameworks (in hybrid mode) */\n\tdetectedFrameworks?: DetectedFramework[];\n}\n\nexport type UserAction = 'apply' | 'copy' | 'skip';\n",
|
|
10
|
+
"/**\n * File System Scanner\n * Discovers CSS files in a project\n */\n\nimport { readdir, stat } from 'node:fs/promises';\nimport { basename, join, relative } from 'node:path';\nimport type { CssFile } from '../types.ts';\n\n/** Directories to skip during scanning */\nconst IGNORED_DIRS = new Set([\n\t'node_modules',\n\t'.git',\n\t'.svn',\n\t'.hg',\n\t'dist',\n\t'build',\n\t'coverage',\n\t'.next',\n\t'.nuxt',\n\t'.output',\n\t'.vercel',\n\t'.shift-backup',\n]);\n\n/** Entry point file names in priority order */\nconst ENTRY_POINT_NAMES = [\n\t'main.css',\n\t'styles.css',\n\t'style.css',\n\t'index.css',\n\t'app.css',\n\t'global.css',\n\t'globals.css',\n];\n\n/** Entry point directories in priority order */\nconst ENTRY_POINT_DIRS = ['src', 'styles', 'css', 'assets/css', 'public/css', ''];\n\n/**\n * Recursively scan directory for CSS files\n */\nasync function scanDirectory(dir: string, rootDir: string, files: CssFile[]): Promise<void> {\n\tconst entries = await readdir(dir, { withFileTypes: true });\n\n\tfor (const entry of entries) {\n\t\tconst fullPath = join(dir, entry.name);\n\n\t\tif (entry.isDirectory()) {\n\t\t\tif (!IGNORED_DIRS.has(entry.name)) {\n\t\t\t\tawait scanDirectory(fullPath, rootDir, files);\n\t\t\t}\n\t\t} else if (entry.isFile() && entry.name.endsWith('.css')) {\n\t\t\tconst stats = await stat(fullPath);\n\t\t\tfiles.push({\n\t\t\t\tpath: fullPath,\n\t\t\t\trelativePath: relative(rootDir, fullPath),\n\t\t\t\tbasename: basename(fullPath),\n\t\t\t\tsize: stats.size,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Scan project for all CSS files\n */\nexport async function scanForCssFiles(rootDir: string): Promise<CssFile[]> {\n\tconst files: CssFile[] = [];\n\tawait scanDirectory(rootDir, rootDir, files);\n\t// Sort by size descending (larger files first)\n\treturn files.sort((a, b) => b.size - a.size);\n}\n\n/**\n * Find the most likely entry point CSS file\n */\nexport function findEntryPoint(files: CssFile[]): CssFile | null {\n\t// Check each directory in priority order\n\tfor (const dir of ENTRY_POINT_DIRS) {\n\t\tfor (const name of ENTRY_POINT_NAMES) {\n\t\t\tconst targetPath = dir ? `${dir}/${name}` : name;\n\t\t\tconst match = files.find(\n\t\t\t\t(f) => f.relativePath === targetPath || f.relativePath === targetPath.replace(/\\//g, '\\\\')\n\t\t\t);\n\t\t\tif (match) return match;\n\t\t}\n\t}\n\n\t// Fallback: find any file with an entry point name\n\tfor (const name of ENTRY_POINT_NAMES) {\n\t\tconst match = files.find((f) => f.basename === name);\n\t\tif (match) return match;\n\t}\n\n\treturn null;\n}\n\n/**\n * Get potential entry point candidates for user selection\n */\nexport function getEntryPointCandidates(files: CssFile[]): CssFile[] {\n\tconst candidates: CssFile[] = [];\n\tconst seen = new Set<string>();\n\n\t// First, add known entry point names\n\tfor (const name of ENTRY_POINT_NAMES) {\n\t\tconst matches = files.filter((f) => f.basename === name);\n\t\tfor (const match of matches) {\n\t\t\tif (!seen.has(match.path)) {\n\t\t\t\tcandidates.push(match);\n\t\t\t\tseen.add(match.path);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Then add large CSS files that might be entry points\n\tconst largeCssFiles = files.filter(\n\t\t(f) => f.size > 1024 && !seen.has(f.path) && !f.relativePath.includes('vendor')\n\t);\n\tfor (const file of largeCssFiles.slice(0, 5)) {\n\t\tcandidates.push(file);\n\t}\n\n\treturn candidates;\n}\n",
|
|
11
|
+
"/**\n * Display Utilities\n * Colorful terminal output formatting\n */\n\nimport pc from 'picocolors';\nimport { getSignature } from '../core/detector.ts';\nimport type { DetectedFramework, InitResult } from '../types.ts';\n\n/**\n * Format file size for display\n */\nexport function formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tconst kb = bytes / 1024;\n\tif (kb < 1024) return `${kb.toFixed(1)} KB`;\n\tconst mb = kb / 1024;\n\treturn `${mb.toFixed(1)} MB`;\n}\n\n/**\n * Display detected frameworks in a nice list\n */\nexport function showDetectedFrameworks(frameworks: DetectedFramework[]): void {\n\tconsole.log('');\n\tconsole.log(pc.bold(' Detected CSS Files:'));\n\tconsole.log('');\n\n\tfor (const fw of frameworks) {\n\t\tconst sig = getSignature(fw.type);\n\t\tconst icon = sig?.icon ?? '๐';\n\t\tconst name = sig?.displayName ?? fw.type;\n\t\tconst size = formatSize(fw.file.size);\n\t\tconst confidence = fw.confidence === 'high' ? '' : pc.dim(` (${fw.confidence} confidence)`);\n\n\t\tconsole.log(` ${icon} ${pc.cyan(fw.file.relativePath)}`);\n\t\tconsole.log(` ${pc.dim(name)} ยท ${pc.dim(size)}${confidence}`);\n\t\tconsole.log('');\n\t}\n}\n\n/**\n * Display stylesheet preview\n */\nexport function showStylesheetPreview(content: string): void {\n\tconsole.log('');\n\tconsole.log(pc.bold(' Stylesheet Preview:'));\n\tconsole.log('');\n\tconsole.log(pc.dim(' โ'.repeat(40)));\n\tconsole.log('');\n\n\tconst lines = content.split('\\n');\n\tconst maxLines = 25;\n\tconst displayLines = lines.slice(0, maxLines);\n\n\tfor (const line of displayLines) {\n\t\tif (line.startsWith('/**') || line.startsWith(' *') || line.startsWith(' */')) {\n\t\t\tconsole.log(` ${pc.dim(line)}`);\n\t\t} else if (line.startsWith('@layer') || line.startsWith('@import')) {\n\t\t\tconsole.log(` ${pc.cyan(line)}`);\n\t\t} else if (line.includes('/*') && line.includes('*/')) {\n\t\t\tconsole.log(` ${pc.dim(line)}`);\n\t\t} else {\n\t\t\tconsole.log(` ${line}`);\n\t\t}\n\t}\n\n\tif (lines.length > maxLines) {\n\t\tconsole.log(` ${pc.dim(`... ${lines.length - maxLines} more lines`)}`);\n\t}\n\n\tconsole.log('');\n\tconsole.log(pc.dim(' โ'.repeat(40)));\n\tconsole.log('');\n}\n\n/**\n * Display a boxed code snippet for copying\n */\nexport function showCopySnippet(code: string): void {\n\tconsole.log('');\n\tconsole.log(pc.bold(' Copy this code to your CSS file:'));\n\tconsole.log('');\n\tconsole.log(pc.dim(' โ'.repeat(40)));\n\tconsole.log('');\n\n\tfor (const line of code.split('\\n')) {\n\t\tconsole.log(` ${pc.cyan(line)}`);\n\t}\n\n\tconsole.log('');\n\tconsole.log(pc.dim(' โ'.repeat(40)));\n\tconsole.log('');\n}\n\n/**\n * Display success message\n */\nexport function showSuccess(message: string): void {\n\tconsole.log(` ${pc.green('โ')} ${message}`);\n}\n\n/**\n * Display warning message\n */\nexport function showWarning(message: string): void {\n\tconsole.log(` ${pc.yellow('โ ')} ${message}`);\n}\n\n/**\n * Display error message\n */\nexport function showError(message: string): void {\n\tconsole.log(` ${pc.red('โ')} ${message}`);\n}\n\n/**\n * Display info message\n */\nexport function showInfo(message: string): void {\n\tconsole.log(` ${pc.blue('โน')} ${message}`);\n}\n\n/**\n * Display next steps after completion\n */\nexport function showNextSteps(result: InitResult): void {\n\tconsole.log('');\n\tconsole.log(pc.bold(' Next steps:'));\n\tconsole.log('');\n\n\t// Step 1: Install\n\tconsole.log(` 1. ${pc.cyan('npm install @shift-css/core')}`);\n\n\t// Step 2: Import the stylesheet\n\tconsole.log(` 2. Import ${pc.cyan(result.stylesheetPath.split('/').pop())} in your app`);\n\n\t// Step 3: Mode-specific guidance\n\tif (result.mode === 'hybrid' && result.detectedFrameworks?.length) {\n\t\tconsole.log(` 3. Uncomment your framework imports in the @layer legacy block`);\n\t\tconsole.log(` 4. Start using Shift CSS components alongside your existing code`);\n\t} else {\n\t\tconsole.log(` 3. Start using Shift CSS components and utilities`);\n\t}\n\n\tconsole.log('');\n\tconsole.log(pc.dim(` ๐ Documentation: https://getshiftcss.com`));\n\tconsole.log('');\n}\n",
|
|
12
|
+
"/**\n * Interactive Prompts\n * User interaction using @clack/prompts\n */\n\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { getColorName, hexToHue, isHexColor, PRESETS } from '../core/color.ts';\nimport type { ArchitectureMode, DetectedFramework } from '../types.ts';\n\n/**\n * Show intro banner\n */\nexport function showIntro(): void {\n\tconsole.clear();\n\tp.intro(pc.bgMagenta(pc.white(' ๐จ Shift CSS Init ')));\n}\n\n/**\n * Show outro message\n */\nexport function showOutro(message: string): void {\n\tp.outro(message);\n}\n\n/**\n * Show cancel message and exit\n */\nexport function handleCancel(): never {\n\tp.cancel('Setup cancelled');\n\tprocess.exit(0);\n}\n\n/**\n * Create a spinner for async operations\n */\nexport function createSpinner(): ReturnType<typeof p.spinner> {\n\treturn p.spinner();\n}\n\n/**\n * Show a note box\n */\nexport function showNote(message: string, title?: string): void {\n\tp.note(message, title);\n}\n\n/**\n * Ask for architecture mode\n */\nexport async function askArchitectureMode(\n\tdetectedFrameworks: DetectedFramework[]\n): Promise<ArchitectureMode> {\n\tconst hasFrameworks = detectedFrameworks.length > 0;\n\n\tconst frameworkHint = hasFrameworks\n\t\t? `Detected: ${detectedFrameworks.map((f) => f.type).join(', ')}`\n\t\t: undefined;\n\n\tconst mode = await p.select({\n\t\tmessage: 'What type of project is this?',\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: 'greenfield' as const,\n\t\t\t\tlabel: 'New project (Greenfield)',\n\t\t\t\thint: 'Pure Shift CSS, no legacy frameworks',\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: 'hybrid' as const,\n\t\t\t\tlabel: 'Existing project (Hybrid)',\n\t\t\t\thint: frameworkHint ?? 'Shift CSS alongside existing CSS',\n\t\t\t},\n\t\t],\n\t\tinitialValue: hasFrameworks ? ('hybrid' as const) : ('greenfield' as const),\n\t});\n\n\tif (p.isCancel(mode)) handleCancel();\n\n\treturn mode as ArchitectureMode;\n}\n\n/**\n * Ask for primary seed hue with presets and hex input\n */\nexport async function askPrimaryHue(): Promise<number> {\n\t// Build options from presets + custom\n\tconst presetOptions = PRESETS.map((preset) => ({\n\t\tvalue: preset.hue,\n\t\tlabel: preset.name,\n\t\thint: preset.description,\n\t}));\n\n\tconst hue = await p.select({\n\t\tmessage: 'Choose your brand color:',\n\t\toptions: [\n\t\t\t...presetOptions,\n\t\t\t{ value: -1, label: 'Custom', hint: 'Enter hex code or hue value' },\n\t\t],\n\t});\n\n\tif (p.isCancel(hue)) handleCancel();\n\n\tif (hue === -1) {\n\t\t// Show cheat sheet\n\t\tp.log.info(pc.dim('Hue guide: 20=Red, 90=Yellow, 140=Green, 260=Blue, 320=Purple'));\n\n\t\tconst customValue = await p.text({\n\t\t\tmessage: 'Enter a hex code (#a855f7) or hue (0-360):',\n\t\t\tplaceholder: '#a855f7 or 260',\n\t\t\tvalidate: (value) => {\n\t\t\t\tconst trimmed = value.trim();\n\n\t\t\t\t// Check if hex color\n\t\t\t\tif (isHexColor(trimmed)) {\n\t\t\t\t\tconst parsedHue = hexToHue(trimmed);\n\t\t\t\t\tif (parsedHue === null) return 'Invalid hex color';\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\t// Check if number\n\t\t\t\tconst num = Number.parseInt(trimmed, 10);\n\t\t\t\tif (Number.isNaN(num)) return 'Enter a hex code (#ff0000) or hue number (0-360)';\n\t\t\t\tif (num < 0 || num > 360) return 'Hue must be between 0 and 360';\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t});\n\n\t\tif (p.isCancel(customValue)) handleCancel();\n\n\t\tconst trimmed = (customValue as string).trim();\n\n\t\t// Convert hex to hue\n\t\tif (isHexColor(trimmed)) {\n\t\t\tconst parsedHue = hexToHue(trimmed);\n\t\t\tif (parsedHue !== null) {\n\t\t\t\tconst colorName = getColorName(parsedHue);\n\t\t\t\tp.log.success(\n\t\t\t\t\t`Converted ${pc.cyan(trimmed)} โ ${pc.magenta(colorName)} (Hue: ${parsedHue})`\n\t\t\t\t);\n\t\t\t\treturn parsedHue;\n\t\t\t}\n\t\t}\n\n\t\t// Parse as number\n\t\treturn Number.parseInt(trimmed, 10);\n\t}\n\n\t// Show what preset was selected\n\tconst preset = PRESETS.find((p) => p.hue === hue);\n\tif (preset) {\n\t\tconst colorName = getColorName(preset.hue);\n\t\tp.log.info(`${pc.magenta(preset.name)} โ ${colorName} (Hue: ${preset.hue})`);\n\t}\n\n\treturn hue as number;\n}\n\n/**\n * Ask for stylesheet path\n */\nexport async function askStylesheetPath(defaultPath: string): Promise<string> {\n\tconst path = await p.text({\n\t\tmessage: 'Where should the stylesheet be created?',\n\t\tplaceholder: defaultPath,\n\t\tdefaultValue: defaultPath,\n\t\tvalidate: (value) => {\n\t\t\tif (!value) return 'Please enter a path';\n\t\t\tif (!value.endsWith('.css')) return 'Path must end with .css';\n\t\t\treturn undefined;\n\t\t},\n\t});\n\n\tif (p.isCancel(path)) handleCancel();\n\n\treturn path as string;\n}\n\n/**\n * Confirm overwriting existing files\n */\nexport async function confirmOverwrite(filePath: string): Promise<boolean> {\n\tconst confirmed = await p.confirm({\n\t\tmessage: `${pc.yellow(filePath)} already exists. Overwrite it?`,\n\t\tinitialValue: false,\n\t});\n\n\tif (p.isCancel(confirmed)) handleCancel();\n\n\treturn confirmed as boolean;\n}\n\n/**\n * Confirm project initialization\n */\nexport async function confirmInit(\n\tconfigPath: string,\n\tstylesheetPath: string,\n\tmode: ArchitectureMode\n): Promise<boolean> {\n\tconst modeLabel = mode === 'greenfield' ? 'Greenfield' : 'Hybrid';\n\n\tshowNote(\n\t\t[\n\t\t\t`${pc.bold('Config:')} ${pc.cyan(configPath)}`,\n\t\t\t`${pc.bold('Stylesheet:')} ${pc.cyan(stylesheetPath)}`,\n\t\t\t`${pc.bold('Mode:')} ${pc.cyan(modeLabel)}`,\n\t\t].join('\\n'),\n\t\t'Files to create'\n\t);\n\n\tconst confirmed = await p.confirm({\n\t\tmessage: 'Proceed with initialization?',\n\t\tinitialValue: true,\n\t});\n\n\tif (p.isCancel(confirmed)) handleCancel();\n\n\treturn confirmed as boolean;\n}\n\n/**\n * Show log message\n */\nexport function log(message: string): void {\n\tp.log.message(message);\n}\n\n/**\n * Show success log\n */\nexport function logSuccess(message: string): void {\n\tp.log.success(message);\n}\n\n/**\n * Show warning log\n */\nexport function logWarning(message: string): void {\n\tp.log.warn(message);\n}\n\n/**\n * Show error log\n */\nexport function logError(message: string): void {\n\tp.log.error(message);\n}\n",
|
|
13
|
+
"/**\n * Color Utilities\n * Hex to OKLCH hue conversion\n */\n\n/**\n * Parse a hex color string to RGB values\n */\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } | null {\n\t// Remove # if present\n\tconst cleaned = hex.replace(/^#/, '');\n\n\t// Support 3-char shorthand (#f0f -> #ff00ff)\n\tconst fullHex =\n\t\tcleaned.length === 3\n\t\t\t? cleaned\n\t\t\t\t\t.split('')\n\t\t\t\t\t.map((c) => c + c)\n\t\t\t\t\t.join('')\n\t\t\t: cleaned;\n\n\tif (fullHex.length !== 6) return null;\n\n\tconst result = /^([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(fullHex);\n\tif (!result) return null;\n\n\treturn {\n\t\tr: Number.parseInt(result[1], 16),\n\t\tg: Number.parseInt(result[2], 16),\n\t\tb: Number.parseInt(result[3], 16),\n\t};\n}\n\n/**\n * Convert RGB to HSL and extract hue\n * Returns hue in degrees (0-360)\n */\nfunction rgbToHue(r: number, g: number, b: number): number {\n\t// Normalize to 0-1\n\tconst rNorm = r / 255;\n\tconst gNorm = g / 255;\n\tconst bNorm = b / 255;\n\n\tconst max = Math.max(rNorm, gNorm, bNorm);\n\tconst min = Math.min(rNorm, gNorm, bNorm);\n\tconst delta = max - min;\n\n\t// Achromatic (gray) - return neutral hue\n\tif (delta === 0) return 250;\n\n\tlet hue: number;\n\n\tif (max === rNorm) {\n\t\thue = ((gNorm - bNorm) / delta) % 6;\n\t} else if (max === gNorm) {\n\t\thue = (bNorm - rNorm) / delta + 2;\n\t} else {\n\t\thue = (rNorm - gNorm) / delta + 4;\n\t}\n\n\thue = Math.round(hue * 60);\n\tif (hue < 0) hue += 360;\n\n\treturn hue;\n}\n\n/**\n * Convert hex color to OKLCH hue\n * Note: This is a simplified conversion that uses HSL hue as approximation\n * For production, consider using colorjs.io for accurate OKLCH conversion\n */\nexport function hexToHue(hex: string): number | null {\n\tconst rgb = hexToRgb(hex);\n\tif (!rgb) return null;\n\treturn rgbToHue(rgb.r, rgb.g, rgb.b);\n}\n\n/**\n * Check if a string looks like a hex color\n */\nexport function isHexColor(value: string): boolean {\n\treturn /^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/.test(value.trim());\n}\n\n/**\n * Get a human-readable color name for a hue\n */\nexport function getColorName(hue: number): string {\n\t// Normalize hue to 0-360\n\tconst h = ((hue % 360) + 360) % 360;\n\n\tif (h >= 0 && h < 15) return 'Red';\n\tif (h >= 15 && h < 45) return 'Orange';\n\tif (h >= 45 && h < 75) return 'Yellow';\n\tif (h >= 75 && h < 105) return 'Lime';\n\tif (h >= 105 && h < 135) return 'Green';\n\tif (h >= 135 && h < 165) return 'Teal';\n\tif (h >= 165 && h < 195) return 'Cyan';\n\tif (h >= 195 && h < 225) return 'Sky';\n\tif (h >= 225 && h < 255) return 'Blue';\n\tif (h >= 255 && h < 285) return 'Indigo';\n\tif (h >= 285 && h < 315) return 'Purple';\n\tif (h >= 315 && h < 345) return 'Pink';\n\treturn 'Red';\n}\n\n/**\n * Shift CSS preset themes\n */\nexport interface ColorPreset {\n\tname: string;\n\thue: number;\n\tdescription: string;\n}\n\nexport const PRESETS: ColorPreset[] = [\n\t{ name: 'Plasma', hue: 260, description: 'Electric Blue - High-tech default' },\n\t{ name: 'Laser', hue: 320, description: 'Cyber-Pink - Neon futurism' },\n\t{ name: 'Acid', hue: 140, description: 'Toxic Green - Engineering edge' },\n\t{ name: 'Void', hue: 0, description: 'Monochrome - Industrial minimal' },\n];\n"
|
|
14
|
+
],
|
|
15
|
+
"mappings": "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",
|
|
16
|
+
"debugId": "D1C3128B75F8A46664756E2164756E21",
|
|
17
|
+
"names": []
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shift-css/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI tool for Shift CSS framework setup and migration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"shift-css": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "bun run scripts/build.ts",
|
|
17
|
+
"dev": "bun run src/index.ts",
|
|
18
|
+
"test": "bun test"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@clack/prompts": "^0.7.0",
|
|
22
|
+
"picocolors": "^1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/bun": "^1.1.14",
|
|
26
|
+
"@types/node": "^22.10.5",
|
|
27
|
+
"tsx": "^4.19.2",
|
|
28
|
+
"typescript": "^5.7.3"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"css",
|
|
35
|
+
"cli",
|
|
36
|
+
"shift-css",
|
|
37
|
+
"migration",
|
|
38
|
+
"cascade-layers"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/shiftcss/shift-css.git",
|
|
44
|
+
"directory": "packages/cli"
|
|
45
|
+
}
|
|
46
|
+
}
|