create-lovstudio-app 1.0.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/README.md +61 -0
- package/dist/index.mjs +384 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# create-lovstudio-app
|
|
2
|
+
|
|
3
|
+
Create a new Lovstudio Next.js project with one command.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-lovstudio-app my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with your preferred package manager:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm create lovstudio-app my-app
|
|
15
|
+
yarn create lovstudio-app my-app
|
|
16
|
+
bun create lovstudio-app my-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
The CLI creates a project based on the [Lovstudio Next.js template](https://github.com/lovstudio/lovweb) with:
|
|
22
|
+
|
|
23
|
+
- ⚡️ **Next.js 15** with App Router
|
|
24
|
+
- 🔒 **Supabase Auth** with cookie-based sessions
|
|
25
|
+
- 🗄️ **DrizzleORM** with PostgreSQL
|
|
26
|
+
- 🎨 **Tailwind CSS v4** + shadcn/ui
|
|
27
|
+
- 🌐 **i18n** with next-intl (Chinese/English)
|
|
28
|
+
- 📊 **Monitoring** with Sentry + PostHog
|
|
29
|
+
- 🛡️ **Security** with Arcjet
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
create-lovstudio-app [project-name] [options]
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--skip-install Skip dependency installation
|
|
38
|
+
--use-npm Use npm as package manager
|
|
39
|
+
--use-pnpm Use pnpm as package manager
|
|
40
|
+
--use-yarn Use yarn as package manager
|
|
41
|
+
--use-bun Use bun as package manager
|
|
42
|
+
-h, --help Display help
|
|
43
|
+
-v, --version Display version
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Interactive Mode
|
|
47
|
+
|
|
48
|
+
When you run `create-lovstudio-app`, it will prompt you to:
|
|
49
|
+
|
|
50
|
+
1. Enter project name
|
|
51
|
+
2. Select features to include:
|
|
52
|
+
- Supabase Auth (default: yes)
|
|
53
|
+
- i18n support (default: yes)
|
|
54
|
+
- Storybook (default: no)
|
|
55
|
+
- Monitoring (default: yes)
|
|
56
|
+
3. Choose package manager
|
|
57
|
+
4. Initialize git repository
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { cac } from "cac";
|
|
5
|
+
|
|
6
|
+
// src/create.ts
|
|
7
|
+
import { execSync as execSync2 } from "child_process";
|
|
8
|
+
import fs2 from "fs";
|
|
9
|
+
import path2 from "path";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
import pc3 from "picocolors";
|
|
12
|
+
|
|
13
|
+
// src/prompts.ts
|
|
14
|
+
import prompts from "prompts";
|
|
15
|
+
import pc from "picocolors";
|
|
16
|
+
|
|
17
|
+
// src/utils/package-manager.ts
|
|
18
|
+
import { execSync } from "child_process";
|
|
19
|
+
function detectPackageManager() {
|
|
20
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
21
|
+
if (userAgent.startsWith("pnpm"))
|
|
22
|
+
return "pnpm";
|
|
23
|
+
if (userAgent.startsWith("yarn"))
|
|
24
|
+
return "yarn";
|
|
25
|
+
if (userAgent.startsWith("bun"))
|
|
26
|
+
return "bun";
|
|
27
|
+
return "npm";
|
|
28
|
+
}
|
|
29
|
+
function isPackageManagerInstalled(pm) {
|
|
30
|
+
try {
|
|
31
|
+
execSync(`${pm} --version`, { stdio: "ignore" });
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getInstallCommand(pm) {
|
|
38
|
+
switch (pm) {
|
|
39
|
+
case "pnpm":
|
|
40
|
+
return "pnpm install";
|
|
41
|
+
case "yarn":
|
|
42
|
+
return "yarn";
|
|
43
|
+
case "bun":
|
|
44
|
+
return "bun install";
|
|
45
|
+
default:
|
|
46
|
+
return "npm install";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/prompts.ts
|
|
51
|
+
async function promptProjectConfig(defaultName) {
|
|
52
|
+
const detectedPm = detectPackageManager();
|
|
53
|
+
const response = await prompts(
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
name: "projectName",
|
|
58
|
+
message: "Project name:",
|
|
59
|
+
initial: defaultName || "my-lovstudio-app",
|
|
60
|
+
validate: (value) => {
|
|
61
|
+
if (!value)
|
|
62
|
+
return "Project name is required";
|
|
63
|
+
if (!/^[a-z0-9-_]+$/i.test(value))
|
|
64
|
+
return "Project name can only contain letters, numbers, hyphens and underscores";
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "multiselect",
|
|
70
|
+
name: "features",
|
|
71
|
+
message: "Select features:",
|
|
72
|
+
instructions: pc.dim("(space to select, enter to confirm)"),
|
|
73
|
+
choices: [
|
|
74
|
+
{ title: "Supabase Auth", value: "supabase", selected: true },
|
|
75
|
+
{ title: "i18n (Chinese/English)", value: "i18n", selected: true },
|
|
76
|
+
{ title: "Storybook", value: "storybook", selected: false },
|
|
77
|
+
{ title: "Monitoring (Sentry + PostHog)", value: "monitoring", selected: true }
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "select",
|
|
82
|
+
name: "packageManager",
|
|
83
|
+
message: "Package manager:",
|
|
84
|
+
choices: [
|
|
85
|
+
{ title: `pnpm ${detectedPm === "pnpm" ? pc.dim("(detected)") : pc.dim("(recommended)")}`, value: "pnpm" },
|
|
86
|
+
{ title: `npm ${detectedPm === "npm" ? pc.dim("(detected)") : ""}`, value: "npm" },
|
|
87
|
+
{ title: `yarn ${detectedPm === "yarn" ? pc.dim("(detected)") : ""}`, value: "yarn" },
|
|
88
|
+
{ title: `bun ${detectedPm === "bun" ? pc.dim("(detected)") : ""}`, value: "bun" }
|
|
89
|
+
],
|
|
90
|
+
initial: detectedPm === "pnpm" ? 0 : detectedPm === "npm" ? 1 : detectedPm === "yarn" ? 2 : 3
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: "confirm",
|
|
94
|
+
name: "initGit",
|
|
95
|
+
message: "Initialize a git repository?",
|
|
96
|
+
initial: true
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
{
|
|
100
|
+
onCancel: () => {
|
|
101
|
+
console.log(pc.red("\nOperation cancelled"));
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
if (!isPackageManagerInstalled(response.packageManager)) {
|
|
107
|
+
console.log(pc.yellow(`
|
|
108
|
+
\u26A0 ${response.packageManager} is not installed. Falling back to npm.`));
|
|
109
|
+
response.packageManager = "npm";
|
|
110
|
+
}
|
|
111
|
+
const featureSet = new Set(response.features || []);
|
|
112
|
+
return {
|
|
113
|
+
projectName: response.projectName,
|
|
114
|
+
features: {
|
|
115
|
+
supabase: featureSet.has("supabase"),
|
|
116
|
+
i18n: featureSet.has("i18n"),
|
|
117
|
+
storybook: featureSet.has("storybook"),
|
|
118
|
+
monitoring: featureSet.has("monitoring")
|
|
119
|
+
},
|
|
120
|
+
packageManager: response.packageManager,
|
|
121
|
+
initGit: response.initGit
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/template.ts
|
|
126
|
+
import { downloadTemplate } from "giget";
|
|
127
|
+
var TEMPLATE_REPO = "github:lovstudio/lovweb";
|
|
128
|
+
async function download(dest, _config) {
|
|
129
|
+
await downloadTemplate(TEMPLATE_REPO, {
|
|
130
|
+
dir: dest,
|
|
131
|
+
force: true
|
|
132
|
+
// Can specify ref: 'v1.0.0' to lock version
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/postprocess.ts
|
|
137
|
+
import fs from "fs";
|
|
138
|
+
import path from "path";
|
|
139
|
+
var ALWAYS_REMOVE = [
|
|
140
|
+
"cli",
|
|
141
|
+
".changeset",
|
|
142
|
+
".github",
|
|
143
|
+
"CHANGELOG.md",
|
|
144
|
+
".coderabbit.yaml",
|
|
145
|
+
"crowdin.yml",
|
|
146
|
+
"codecov.yml",
|
|
147
|
+
"checkly.config.ts",
|
|
148
|
+
"unlighthouse.config.ts",
|
|
149
|
+
".claude",
|
|
150
|
+
"CLAUDE.md",
|
|
151
|
+
"AGENTS.md"
|
|
152
|
+
];
|
|
153
|
+
var STORYBOOK_FILES = [
|
|
154
|
+
".storybook"
|
|
155
|
+
];
|
|
156
|
+
var STORYBOOK_PATTERNS = [".stories.tsx", ".stories.ts"];
|
|
157
|
+
var I18N_FILES = [
|
|
158
|
+
"src/locales",
|
|
159
|
+
"crowdin.yml"
|
|
160
|
+
];
|
|
161
|
+
var MONITORING_FILES = [
|
|
162
|
+
"src/instrumentation.ts",
|
|
163
|
+
"src/instrumentation-client.ts",
|
|
164
|
+
"sentry.client.config.ts",
|
|
165
|
+
"sentry.server.config.ts",
|
|
166
|
+
"sentry.edge.config.ts"
|
|
167
|
+
];
|
|
168
|
+
async function processTemplate(projectPath, config) {
|
|
169
|
+
for (const file of ALWAYS_REMOVE) {
|
|
170
|
+
await removeIfExists(path.join(projectPath, file));
|
|
171
|
+
}
|
|
172
|
+
if (!config.features.storybook) {
|
|
173
|
+
for (const file of STORYBOOK_FILES) {
|
|
174
|
+
await removeIfExists(path.join(projectPath, file));
|
|
175
|
+
}
|
|
176
|
+
await removeMatchingFiles(projectPath, STORYBOOK_PATTERNS);
|
|
177
|
+
}
|
|
178
|
+
if (!config.features.i18n) {
|
|
179
|
+
for (const file of I18N_FILES) {
|
|
180
|
+
await removeIfExists(path.join(projectPath, file));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!config.features.monitoring) {
|
|
184
|
+
for (const file of MONITORING_FILES) {
|
|
185
|
+
await removeIfExists(path.join(projectPath, file));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
await updatePackageJson(projectPath, config);
|
|
189
|
+
await ensureEnvExample(projectPath, config);
|
|
190
|
+
}
|
|
191
|
+
async function removeIfExists(filePath) {
|
|
192
|
+
try {
|
|
193
|
+
const stat = await fs.promises.stat(filePath);
|
|
194
|
+
if (stat.isDirectory()) {
|
|
195
|
+
await fs.promises.rm(filePath, { recursive: true, force: true });
|
|
196
|
+
} else {
|
|
197
|
+
await fs.promises.unlink(filePath);
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function removeMatchingFiles(dir, patterns) {
|
|
203
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
const fullPath = path.join(dir, entry.name);
|
|
206
|
+
if (entry.isDirectory()) {
|
|
207
|
+
if (entry.name === "node_modules" || entry.name === ".git")
|
|
208
|
+
continue;
|
|
209
|
+
await removeMatchingFiles(fullPath, patterns);
|
|
210
|
+
} else if (patterns.some((pattern) => entry.name.endsWith(pattern))) {
|
|
211
|
+
await fs.promises.unlink(fullPath);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function updatePackageJson(projectPath, config) {
|
|
216
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
217
|
+
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8"));
|
|
218
|
+
pkg.name = config.projectName;
|
|
219
|
+
pkg.version = "0.1.0";
|
|
220
|
+
pkg.private = true;
|
|
221
|
+
delete pkg.repository;
|
|
222
|
+
delete pkg.bugs;
|
|
223
|
+
delete pkg.homepage;
|
|
224
|
+
if (!config.features.storybook) {
|
|
225
|
+
delete pkg.scripts?.storybook;
|
|
226
|
+
delete pkg.scripts?.["build-storybook"];
|
|
227
|
+
delete pkg.devDependencies?.["@chromatic-com/storybook"];
|
|
228
|
+
delete pkg.devDependencies?.["@storybook/addon-essentials"];
|
|
229
|
+
delete pkg.devDependencies?.["@storybook/addon-interactions"];
|
|
230
|
+
delete pkg.devDependencies?.["@storybook/addon-onboarding"];
|
|
231
|
+
delete pkg.devDependencies?.["@storybook/blocks"];
|
|
232
|
+
delete pkg.devDependencies?.["@storybook/nextjs"];
|
|
233
|
+
delete pkg.devDependencies?.["@storybook/react"];
|
|
234
|
+
delete pkg.devDependencies?.["@storybook/test"];
|
|
235
|
+
delete pkg.devDependencies?.storybook;
|
|
236
|
+
}
|
|
237
|
+
if (!config.features.monitoring) {
|
|
238
|
+
delete pkg.dependencies?.["@sentry/nextjs"];
|
|
239
|
+
delete pkg.dependencies?.["posthog-js"];
|
|
240
|
+
delete pkg.dependencies?.["@logtape/logtape"];
|
|
241
|
+
delete pkg.dependencies?.["@logtape/otel"];
|
|
242
|
+
delete pkg.dependencies?.["@better-stack/browser-sdk"];
|
|
243
|
+
}
|
|
244
|
+
if (config.packageManager !== "pnpm") {
|
|
245
|
+
delete pkg.packageManager;
|
|
246
|
+
}
|
|
247
|
+
await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
248
|
+
}
|
|
249
|
+
async function ensureEnvExample(projectPath, config) {
|
|
250
|
+
const envExamplePath = path.join(projectPath, ".env.example");
|
|
251
|
+
let envContent = `# Database
|
|
252
|
+
DATABASE_URL=
|
|
253
|
+
|
|
254
|
+
# Supabase
|
|
255
|
+
NEXT_PUBLIC_SUPABASE_URL=
|
|
256
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
|
257
|
+
`;
|
|
258
|
+
if (config.features.monitoring) {
|
|
259
|
+
envContent += `
|
|
260
|
+
# Monitoring (optional)
|
|
261
|
+
SENTRY_DSN=
|
|
262
|
+
NEXT_PUBLIC_POSTHOG_KEY=
|
|
263
|
+
NEXT_PUBLIC_POSTHOG_HOST=
|
|
264
|
+
BETTER_STACK_SOURCE_TOKEN=
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
envContent += `
|
|
268
|
+
# Security (optional)
|
|
269
|
+
ARCJET_KEY=
|
|
270
|
+
`;
|
|
271
|
+
await fs.promises.writeFile(envExamplePath, envContent);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/utils/logger.ts
|
|
275
|
+
import pc2 from "picocolors";
|
|
276
|
+
var logger = {
|
|
277
|
+
info: (msg) => console.log(pc2.cyan(msg)),
|
|
278
|
+
success: (msg) => console.log(pc2.green(`\u2714 ${msg}`)),
|
|
279
|
+
warn: (msg) => console.log(pc2.yellow(`\u26A0 ${msg}`)),
|
|
280
|
+
error: (msg) => console.log(pc2.red(`\u2716 ${msg}`)),
|
|
281
|
+
log: (msg) => console.log(msg),
|
|
282
|
+
blank: () => console.log()
|
|
283
|
+
};
|
|
284
|
+
function banner() {
|
|
285
|
+
console.log();
|
|
286
|
+
console.log(pc2.cyan("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
287
|
+
console.log(pc2.cyan("\u2502") + pc2.bold(" \u{1F3E0} Create Lovstudio App ") + pc2.cyan("\u2502"));
|
|
288
|
+
console.log(pc2.cyan("\u2502") + pc2.dim(" Next.js 15 + TypeScript ") + pc2.cyan("\u2502"));
|
|
289
|
+
console.log(pc2.cyan("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
290
|
+
console.log();
|
|
291
|
+
}
|
|
292
|
+
function printNextSteps(projectName, packageManager) {
|
|
293
|
+
const pmRun = packageManager === "npm" ? "npm run" : packageManager;
|
|
294
|
+
console.log();
|
|
295
|
+
console.log(pc2.green("\u{1F389} Success!") + ` Created ${pc2.bold(projectName)}`);
|
|
296
|
+
console.log();
|
|
297
|
+
console.log(pc2.dim("Next steps:"));
|
|
298
|
+
console.log();
|
|
299
|
+
console.log(` ${pc2.cyan("cd")} ${projectName}`);
|
|
300
|
+
console.log(` ${pc2.cyan("cp")} .env.example .env.local`);
|
|
301
|
+
console.log(` ${pc2.cyan(pmRun)} dev`);
|
|
302
|
+
console.log();
|
|
303
|
+
console.log(pc2.dim("\u{1F4DA} Documentation: https://github.com/lovstudio/lovweb"));
|
|
304
|
+
console.log();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/create.ts
|
|
308
|
+
async function create(projectName, options) {
|
|
309
|
+
banner();
|
|
310
|
+
const config = await promptProjectConfig(projectName);
|
|
311
|
+
if (!config)
|
|
312
|
+
return;
|
|
313
|
+
if (options?.useNpm)
|
|
314
|
+
config.packageManager = "npm";
|
|
315
|
+
if (options?.usePnpm)
|
|
316
|
+
config.packageManager = "pnpm";
|
|
317
|
+
if (options?.useYarn)
|
|
318
|
+
config.packageManager = "yarn";
|
|
319
|
+
if (options?.useBun)
|
|
320
|
+
config.packageManager = "bun";
|
|
321
|
+
const projectPath = path2.resolve(process.cwd(), config.projectName);
|
|
322
|
+
if (fs2.existsSync(projectPath)) {
|
|
323
|
+
const files = fs2.readdirSync(projectPath);
|
|
324
|
+
if (files.length > 0) {
|
|
325
|
+
logger.error(`Directory ${pc3.bold(config.projectName)} is not empty.`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
console.log();
|
|
330
|
+
const downloadSpinner = ora("Downloading template...").start();
|
|
331
|
+
try {
|
|
332
|
+
await download(projectPath, config);
|
|
333
|
+
downloadSpinner.succeed("Downloaded template");
|
|
334
|
+
} catch (error) {
|
|
335
|
+
downloadSpinner.fail("Failed to download template");
|
|
336
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
const processSpinner = ora("Processing template...").start();
|
|
340
|
+
try {
|
|
341
|
+
await processTemplate(projectPath, config);
|
|
342
|
+
processSpinner.succeed("Processed template");
|
|
343
|
+
} catch (error) {
|
|
344
|
+
processSpinner.fail("Failed to process template");
|
|
345
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
if (!options?.skipInstall) {
|
|
349
|
+
const installSpinner = ora(`Installing dependencies with ${config.packageManager}...`).start();
|
|
350
|
+
try {
|
|
351
|
+
const installCmd = getInstallCommand(config.packageManager);
|
|
352
|
+
execSync2(installCmd, {
|
|
353
|
+
cwd: projectPath,
|
|
354
|
+
stdio: "pipe"
|
|
355
|
+
});
|
|
356
|
+
installSpinner.succeed("Installed dependencies");
|
|
357
|
+
} catch (error) {
|
|
358
|
+
installSpinner.fail("Failed to install dependencies");
|
|
359
|
+
logger.warn("You can try installing manually later.");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (config.initGit) {
|
|
363
|
+
const gitSpinner = ora("Initializing git repository...").start();
|
|
364
|
+
try {
|
|
365
|
+
execSync2("git init", { cwd: projectPath, stdio: "pipe" });
|
|
366
|
+
execSync2("git add -A", { cwd: projectPath, stdio: "pipe" });
|
|
367
|
+
execSync2('git commit -m "Initial commit from create-lovstudio-app"', {
|
|
368
|
+
cwd: projectPath,
|
|
369
|
+
stdio: "pipe"
|
|
370
|
+
});
|
|
371
|
+
gitSpinner.succeed("Initialized git repository");
|
|
372
|
+
} catch {
|
|
373
|
+
gitSpinner.fail("Failed to initialize git repository");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
printNextSteps(config.projectName, config.packageManager);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/index.ts
|
|
380
|
+
var cli = cac("create-lovstudio-app");
|
|
381
|
+
cli.command("[project-name]", "Create a new Lovstudio Next.js project").option("--skip-install", "Skip dependency installation").option("--use-npm", "Use npm as package manager").option("--use-pnpm", "Use pnpm as package manager").option("--use-yarn", "Use yarn as package manager").option("--use-bun", "Use bun as package manager").action(create);
|
|
382
|
+
cli.help();
|
|
383
|
+
cli.version("1.0.0");
|
|
384
|
+
cli.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-lovstudio-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a new Lovstudio Next.js project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-lovstudio-app": "./dist/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"release": "pnpm build && npm publish",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"create",
|
|
20
|
+
"lovstudio",
|
|
21
|
+
"nextjs",
|
|
22
|
+
"template",
|
|
23
|
+
"boilerplate",
|
|
24
|
+
"supabase",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"author": "Lovstudio",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/lovstudio/lovweb.git",
|
|
32
|
+
"directory": "cli"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/lovstudio/lovweb/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/lovstudio/lovweb#readme",
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"cac": "^6.7.14",
|
|
43
|
+
"giget": "^1.2.3",
|
|
44
|
+
"ora": "^8.1.1",
|
|
45
|
+
"picocolors": "^1.1.1",
|
|
46
|
+
"prompts": "^2.4.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.10.2",
|
|
50
|
+
"@types/prompts": "^2.4.9",
|
|
51
|
+
"tsup": "^8.3.5",
|
|
52
|
+
"typescript": "^5.7.2"
|
|
53
|
+
}
|
|
54
|
+
}
|