next-configure 0.1.0
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/color.js +34 -0
- package/dist/index.js +7 -0
- package/dist/main.js +191 -0
- package/package.json +33 -0
package/dist/color.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function clampChannel(value) {
|
|
2
|
+
if (value < 0)
|
|
3
|
+
return 0;
|
|
4
|
+
if (value > 255)
|
|
5
|
+
return 255;
|
|
6
|
+
return Math.round(value);
|
|
7
|
+
}
|
|
8
|
+
function hexToRgb(hex) {
|
|
9
|
+
const normalized = hex.trim().toLowerCase();
|
|
10
|
+
const r = parseInt(normalized.slice(0, 2), 16);
|
|
11
|
+
const g = parseInt(normalized.slice(2, 4), 16);
|
|
12
|
+
const b = parseInt(normalized.slice(4, 6), 16);
|
|
13
|
+
return { r, g, b };
|
|
14
|
+
}
|
|
15
|
+
function rgbToHex(r, g, b) {
|
|
16
|
+
const toHex = (value) => clampChannel(value).toString(16).padStart(2, "0");
|
|
17
|
+
return `${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
18
|
+
}
|
|
19
|
+
export function lightenHex(hex, factor) {
|
|
20
|
+
const { r, g, b } = hexToRgb(hex);
|
|
21
|
+
const amount = Math.max(0, Math.min(1, factor));
|
|
22
|
+
const lr = r + (255 - r) * amount;
|
|
23
|
+
const lg = g + (255 - g) * amount;
|
|
24
|
+
const lb = b + (255 - b) * amount;
|
|
25
|
+
return rgbToHex(lr, lg, lb);
|
|
26
|
+
}
|
|
27
|
+
export function darkenHex(hex, factor) {
|
|
28
|
+
const { r, g, b } = hexToRgb(hex);
|
|
29
|
+
const amount = Math.max(0, Math.min(1, factor));
|
|
30
|
+
const dr = r * (1 - amount);
|
|
31
|
+
const dg = g * (1 - amount);
|
|
32
|
+
const db = b * (1 - amount);
|
|
33
|
+
return rgbToHex(dr, dg, db);
|
|
34
|
+
}
|
package/dist/index.js
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import prompts from "prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { execa } from "execa";
|
|
6
|
+
import { lightenHex, darkenHex } from "./color.js";
|
|
7
|
+
async function ensureNextProject(root) {
|
|
8
|
+
const packageJsonPath = path.join(root, "package.json");
|
|
9
|
+
const exists = await fs.pathExists(packageJsonPath);
|
|
10
|
+
if (!exists) {
|
|
11
|
+
throw new Error("package.json not found. Run this inside a Next.js project.");
|
|
12
|
+
}
|
|
13
|
+
const pkg = (await fs.readJson(packageJsonPath));
|
|
14
|
+
const hasNext = Boolean(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
15
|
+
if (!hasNext) {
|
|
16
|
+
throw new Error("next dependency not found in package.json. Run this inside a Next.js project.");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function detectSrcRoot(root) {
|
|
20
|
+
const srcPath = path.join(root, "src");
|
|
21
|
+
const hasSrc = await fs.pathExists(srcPath);
|
|
22
|
+
if (hasSrc) {
|
|
23
|
+
return srcPath;
|
|
24
|
+
}
|
|
25
|
+
return root;
|
|
26
|
+
}
|
|
27
|
+
async function runShadcnWorkflow() {
|
|
28
|
+
console.log(chalk.cyan("Running shadcn init"));
|
|
29
|
+
await execa("npx", ["shadcn", "init"], { stdio: "inherit" });
|
|
30
|
+
console.log(chalk.cyan("Installing shadcn button component"));
|
|
31
|
+
await execa("npx", ["shadcn", "add", "button"], { stdio: "inherit" });
|
|
32
|
+
}
|
|
33
|
+
async function ensureStandardFolders(root) {
|
|
34
|
+
const targets = ["components", "hooks", "utils"];
|
|
35
|
+
for (const name of targets) {
|
|
36
|
+
const dir = path.join(root, name);
|
|
37
|
+
await fs.ensureDir(dir);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function detectGlobalsCss(root) {
|
|
41
|
+
const candidates = [
|
|
42
|
+
path.join(root, "src", "app", "globals.css"),
|
|
43
|
+
path.join(root, "app", "globals.css")
|
|
44
|
+
];
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
if (await fs.pathExists(candidate)) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw new Error("globals.css not found. Expected src/app/globals.css or app/globals.css.");
|
|
51
|
+
}
|
|
52
|
+
function buildGlobalsCss(primary, secondary) {
|
|
53
|
+
const primaryLight = lightenHex(primary, 0.2);
|
|
54
|
+
const primaryDark = darkenHex(primary, 0.2);
|
|
55
|
+
const secondaryLight = lightenHex(secondary, 0.2);
|
|
56
|
+
const secondaryDark = darkenHex(secondary, 0.2);
|
|
57
|
+
const primaryValue = `#${primary}`;
|
|
58
|
+
const primaryLightValue = `#${primaryLight}`;
|
|
59
|
+
const primaryDarkValue = `#${primaryDark}`;
|
|
60
|
+
const secondaryValue = `#${secondary}`;
|
|
61
|
+
const secondaryLightValue = `#${secondaryLight}`;
|
|
62
|
+
const secondaryDarkValue = `#${secondaryDark}`;
|
|
63
|
+
return `@import "tailwindcss";
|
|
64
|
+
@import "tw-animate-css";
|
|
65
|
+
|
|
66
|
+
@custom-variant dark (&:is(.dark *));
|
|
67
|
+
|
|
68
|
+
@theme inline {
|
|
69
|
+
--color-primary: var(--primary);
|
|
70
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
71
|
+
--color-secondary: var(--secondary);
|
|
72
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
73
|
+
--color-background: var(--background);
|
|
74
|
+
--color-foreground: var(--foreground);
|
|
75
|
+
--color-border: var(--border);
|
|
76
|
+
--color-input: var(--input);
|
|
77
|
+
--color-ring: var(--ring);
|
|
78
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
79
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
80
|
+
--radius-lg: var(--radius);
|
|
81
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
:root {
|
|
85
|
+
--primary: ${primaryValue};
|
|
86
|
+
--primary-light: ${primaryLightValue};
|
|
87
|
+
--primary-dark: ${primaryDarkValue};
|
|
88
|
+
--primary-foreground: #ffffff;
|
|
89
|
+
|
|
90
|
+
--secondary: ${secondaryValue};
|
|
91
|
+
--secondary-light: ${secondaryLightValue};
|
|
92
|
+
--secondary-dark: ${secondaryDarkValue};
|
|
93
|
+
--secondary-foreground: #ffffff;
|
|
94
|
+
|
|
95
|
+
--background: #ffffff;
|
|
96
|
+
--foreground: #020817;
|
|
97
|
+
--border: #e2e8f0;
|
|
98
|
+
--input: #e2e8f0;
|
|
99
|
+
--ring: ${primaryValue};
|
|
100
|
+
--radius: 0.75rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
body {
|
|
104
|
+
margin: 0;
|
|
105
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
106
|
+
background-color: var(--background);
|
|
107
|
+
color: var(--foreground);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
html {
|
|
111
|
+
min-height: 100%;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@layer base {
|
|
115
|
+
* {
|
|
116
|
+
border-color: var(--border);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
body {
|
|
120
|
+
min-height: 100vh;
|
|
121
|
+
-webkit-font-smoothing: antialiased;
|
|
122
|
+
-moz-osx-font-smoothing: grayscale;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.bg-primary { background-color: var(--primary); }
|
|
127
|
+
.bg-primary-light { background-color: var(--primary-light); }
|
|
128
|
+
.bg-primary-dark { background-color: var(--primary-dark); }
|
|
129
|
+
|
|
130
|
+
.text-primary { color: var(--primary); }
|
|
131
|
+
|
|
132
|
+
.bg-secondary { background-color: var(--secondary); }
|
|
133
|
+
.bg-secondary-light { background-color: var(--secondary-light); }
|
|
134
|
+
.bg-secondary-dark { background-color: var(--secondary-dark); }
|
|
135
|
+
|
|
136
|
+
.text-secondary { color: var(--secondary); }
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
export async function runCli() {
|
|
140
|
+
const root = process.cwd();
|
|
141
|
+
await ensureNextProject(root);
|
|
142
|
+
const srcRoot = await detectSrcRoot(root);
|
|
143
|
+
const shadcnAnswer = await prompts({
|
|
144
|
+
type: "confirm",
|
|
145
|
+
name: "install",
|
|
146
|
+
message: "Do you want to install shadcn/ui? (yes/no)",
|
|
147
|
+
initial: true
|
|
148
|
+
});
|
|
149
|
+
if (shadcnAnswer.install) {
|
|
150
|
+
await runShadcnWorkflow();
|
|
151
|
+
}
|
|
152
|
+
const foldersAnswer = await prompts({
|
|
153
|
+
type: "confirm",
|
|
154
|
+
name: "create",
|
|
155
|
+
message: "Do you want to generate standard development folders? (yes/no)",
|
|
156
|
+
initial: true
|
|
157
|
+
});
|
|
158
|
+
if (foldersAnswer.create) {
|
|
159
|
+
await ensureStandardFolders(srcRoot);
|
|
160
|
+
}
|
|
161
|
+
const themeAnswer = await prompts({
|
|
162
|
+
type: "confirm",
|
|
163
|
+
name: "theme",
|
|
164
|
+
message: "Do you want to set color theme? (yes/no)",
|
|
165
|
+
initial: true
|
|
166
|
+
});
|
|
167
|
+
if (!themeAnswer.theme) {
|
|
168
|
+
console.log(chalk.green("Done. No theme changes applied."));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const colors = await prompts([
|
|
172
|
+
{
|
|
173
|
+
type: "text",
|
|
174
|
+
name: "primary",
|
|
175
|
+
message: "Enter primary color (hex without #)",
|
|
176
|
+
validate: (value) => (/^[0-9a-fA-F]{6}$/.test(String(value)) ? true : "Enter a 6 character hex value without #")
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: "text",
|
|
180
|
+
name: "secondary",
|
|
181
|
+
message: "Enter secondary color (hex without #)",
|
|
182
|
+
validate: (value) => (/^[0-9a-fA-F]{6}$/.test(String(value)) ? true : "Enter a 6 character hex value without #")
|
|
183
|
+
}
|
|
184
|
+
]);
|
|
185
|
+
const primary = String(colors.primary);
|
|
186
|
+
const secondary = String(colors.secondary);
|
|
187
|
+
const globalsPath = await detectGlobalsCss(root);
|
|
188
|
+
const cssContent = buildGlobalsCss(primary, secondary);
|
|
189
|
+
await fs.writeFile(globalsPath, cssContent, "utf8");
|
|
190
|
+
console.log(chalk.green("Theme configured successfully."));
|
|
191
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-configure",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Minimal CLI to configure Next.js App Router projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"next-configure": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.json",
|
|
11
|
+
"prepare": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"chalk": "^5.3.0",
|
|
15
|
+
"execa": "^9.3.0",
|
|
16
|
+
"fs-extra": "^11.2.0",
|
|
17
|
+
"prompts": "^2.4.2"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.10.1",
|
|
21
|
+
"@types/fs-extra": "^11.0.4",
|
|
22
|
+
"@types/prompts": "^2.4.9",
|
|
23
|
+
"typescript": "^5.6.3"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|