create-lacis 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/index.js +201 -0
- package/package.json +35 -0
- package/templates/_common/_gitignore +3 -0
- package/templates/_common/routes/index.ts +5 -0
- package/templates/_common/tsconfig.json +10 -0
- package/templates/base/package.json +15 -0
- package/templates/base/server.ts +4 -0
- package/templates/bun/package.json +12 -0
- package/templates/bun/server.ts +4 -0
- package/templates/netlify/netlify/functions/index.ts +5 -0
- package/templates/netlify/netlify.toml +12 -0
- package/templates/netlify/package.json +12 -0
- package/templates/vercel/api/index.ts +4 -0
- package/templates/vercel/package.json +12 -0
- package/templates/vercel/vercel.json +3 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import { existsSync as existsSync3 } from "fs";
|
|
6
|
+
import { rm } from "fs/promises";
|
|
7
|
+
import { join as join3 } from "path";
|
|
8
|
+
|
|
9
|
+
// src/scaffold.ts
|
|
10
|
+
import { appendFile, cp, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
11
|
+
import { existsSync } from "fs";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { dirname, join } from "path";
|
|
14
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
var TEMPLATES = join(__dirname, "..", "templates");
|
|
16
|
+
var VALIDATOR_DEPS = {
|
|
17
|
+
zod: { zod: "latest", "zod-to-json-schema": "latest" },
|
|
18
|
+
valibot: { valibot: "latest", "@valibot/to-json-schema": "latest" },
|
|
19
|
+
arktype: { arktype: "latest" },
|
|
20
|
+
none: {}
|
|
21
|
+
};
|
|
22
|
+
var PLATFORM_GITIGNORE_EXTRAS = {
|
|
23
|
+
vercel: ".vercel\n",
|
|
24
|
+
netlify: ".netlify\n"
|
|
25
|
+
};
|
|
26
|
+
var PLATFORM_TEMPLATE = {
|
|
27
|
+
node: "base",
|
|
28
|
+
bun: "bun",
|
|
29
|
+
vercel: "vercel",
|
|
30
|
+
netlify: "netlify"
|
|
31
|
+
};
|
|
32
|
+
var ROUTE_CONTENT = {
|
|
33
|
+
none: `import type { Request, Response } from 'lacis'
|
|
34
|
+
|
|
35
|
+
export const GET = async (_req: Request, res: Response) => {
|
|
36
|
+
res.status(200).json({ message: 'Hello from lacis!' })
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
zod: `import { defineHandler } from 'lacis'
|
|
40
|
+
import { z } from 'zod'
|
|
41
|
+
|
|
42
|
+
export const GET = defineHandler({
|
|
43
|
+
query: z.object({ name: z.string().optional() }),
|
|
44
|
+
meta: { summary: 'Hello endpoint', tags: ['hello'] },
|
|
45
|
+
handler: async (req, res) => {
|
|
46
|
+
const { name } = req.query
|
|
47
|
+
res.status(200).json({ message: \`Hello, \${name ?? 'world'}!\` })
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
`,
|
|
51
|
+
valibot: `import { defineHandler } from 'lacis'
|
|
52
|
+
import * as v from 'valibot'
|
|
53
|
+
|
|
54
|
+
export const GET = defineHandler({
|
|
55
|
+
query: v.object({ name: v.optional(v.string()) }),
|
|
56
|
+
meta: { summary: 'Hello endpoint', tags: ['hello'] },
|
|
57
|
+
handler: async (req, res) => {
|
|
58
|
+
const { name } = req.query
|
|
59
|
+
res.status(200).json({ message: \`Hello, \${name ?? 'world'}!\` })
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
`,
|
|
63
|
+
arktype: `import { defineHandler } from 'lacis'
|
|
64
|
+
import { type } from 'arktype'
|
|
65
|
+
|
|
66
|
+
export const GET = defineHandler({
|
|
67
|
+
query: type({ 'name?': 'string' }),
|
|
68
|
+
meta: { summary: 'Hello endpoint', tags: ['hello'] },
|
|
69
|
+
handler: async (req, res) => {
|
|
70
|
+
const { name } = req.query
|
|
71
|
+
res.status(200).json({ message: \`Hello, \${name ?? 'world'}!\` })
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
`
|
|
75
|
+
};
|
|
76
|
+
async function scaffold({ name, platform, validator, targetDir }) {
|
|
77
|
+
await mkdir(targetDir, { recursive: true });
|
|
78
|
+
await cp(join(TEMPLATES, "_common"), targetDir, { recursive: true });
|
|
79
|
+
await cp(join(TEMPLATES, PLATFORM_TEMPLATE[platform] ?? "base"), targetDir, { recursive: true });
|
|
80
|
+
const gitignoreSrc = join(targetDir, "_gitignore");
|
|
81
|
+
if (existsSync(gitignoreSrc)) {
|
|
82
|
+
await rename(gitignoreSrc, join(targetDir, ".gitignore"));
|
|
83
|
+
}
|
|
84
|
+
const extras = PLATFORM_GITIGNORE_EXTRAS[platform];
|
|
85
|
+
if (extras) await appendFile(join(targetDir, ".gitignore"), extras);
|
|
86
|
+
const pkgPath = join(targetDir, "package.json");
|
|
87
|
+
const pkg = JSON.parse((await readFile(pkgPath, "utf-8")).replace(/\{\{name\}\}/g, name));
|
|
88
|
+
pkg.dependencies = { ...pkg.dependencies, ...VALIDATOR_DEPS[validator] ?? {} };
|
|
89
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
90
|
+
await writeFile(
|
|
91
|
+
join(targetDir, "routes", "index.ts"),
|
|
92
|
+
ROUTE_CONTENT[validator] ?? ROUTE_CONTENT.none,
|
|
93
|
+
"utf-8"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/install.ts
|
|
98
|
+
import { existsSync as existsSync2 } from "fs";
|
|
99
|
+
import { join as join2 } from "path";
|
|
100
|
+
import { execSync } from "child_process";
|
|
101
|
+
var INSTALL_CMD = {
|
|
102
|
+
bun: "bun install",
|
|
103
|
+
pnpm: "pnpm install",
|
|
104
|
+
yarn: "yarn install",
|
|
105
|
+
npm: "npm install"
|
|
106
|
+
};
|
|
107
|
+
function detectPackageManager() {
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
if (existsSync2(join2(cwd, "bun.lock")) || existsSync2(join2(cwd, "bun.lockb"))) return "bun";
|
|
110
|
+
if (existsSync2(join2(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
111
|
+
if (existsSync2(join2(cwd, "yarn.lock"))) return "yarn";
|
|
112
|
+
return "npm";
|
|
113
|
+
}
|
|
114
|
+
function install({ targetDir, pm }) {
|
|
115
|
+
const cmd = INSTALL_CMD[pm] ?? "npm install";
|
|
116
|
+
try {
|
|
117
|
+
execSync(cmd, { cwd: targetDir, stdio: "pipe" });
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new Error(err.stderr?.toString().trim() ?? String(err));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/index.ts
|
|
124
|
+
async function main() {
|
|
125
|
+
console.log();
|
|
126
|
+
p.intro("lacis \u2014 create a new project");
|
|
127
|
+
const name = await p.text({
|
|
128
|
+
message: "Project name?",
|
|
129
|
+
placeholder: "my-app",
|
|
130
|
+
defaultValue: "my-app",
|
|
131
|
+
validate(value) {
|
|
132
|
+
const v = value || "my-app";
|
|
133
|
+
if (!/^[a-z0-9][a-z0-9\-_.]*$/.test(v)) return "Lowercase letters, numbers, - _ or . only";
|
|
134
|
+
if (existsSync3(join3(process.cwd(), v))) return `"${v}" already exists`;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
if (p.isCancel(name)) {
|
|
138
|
+
p.cancel("Cancelled");
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
const projectName = name || "my-app";
|
|
142
|
+
const platform = await p.select({
|
|
143
|
+
message: "Platform?",
|
|
144
|
+
options: [
|
|
145
|
+
{ value: "node", label: "Node", hint: "default" },
|
|
146
|
+
{ value: "bun", label: "Bun" },
|
|
147
|
+
{ value: "vercel", label: "Vercel" },
|
|
148
|
+
{ value: "netlify", label: "Netlify" }
|
|
149
|
+
]
|
|
150
|
+
});
|
|
151
|
+
if (p.isCancel(platform)) {
|
|
152
|
+
p.cancel("Cancelled");
|
|
153
|
+
process.exit(0);
|
|
154
|
+
}
|
|
155
|
+
const validator = await p.select({
|
|
156
|
+
message: "Add a validation library?",
|
|
157
|
+
options: [
|
|
158
|
+
{ value: "none", label: "None" },
|
|
159
|
+
{ value: "zod", label: "Zod", hint: "most popular" },
|
|
160
|
+
{ value: "valibot", label: "Valibot", hint: "smallest bundle" },
|
|
161
|
+
{ value: "arktype", label: "ArkType", hint: "native JSON Schema" }
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
if (p.isCancel(validator)) {
|
|
165
|
+
p.cancel("Cancelled");
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
const targetDir = join3(process.cwd(), projectName);
|
|
169
|
+
const s = p.spinner();
|
|
170
|
+
s.start("Scaffolding...");
|
|
171
|
+
try {
|
|
172
|
+
await scaffold({
|
|
173
|
+
name: projectName,
|
|
174
|
+
platform,
|
|
175
|
+
validator,
|
|
176
|
+
targetDir
|
|
177
|
+
});
|
|
178
|
+
s.stop("Scaffolded.");
|
|
179
|
+
} catch (err) {
|
|
180
|
+
s.stop("Scaffolding failed.");
|
|
181
|
+
await rm(targetDir, { recursive: true, force: true }).catch(() => {
|
|
182
|
+
});
|
|
183
|
+
p.cancel(String(err));
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
const pm = detectPackageManager();
|
|
187
|
+
s.start("Installing packages...");
|
|
188
|
+
try {
|
|
189
|
+
install({ targetDir, pm });
|
|
190
|
+
s.stop("Packages installed.");
|
|
191
|
+
} catch {
|
|
192
|
+
s.stop(`Install failed \u2014 run manually: cd ${projectName} && ${pm} install`);
|
|
193
|
+
}
|
|
194
|
+
p.note(`cd ${projectName}
|
|
195
|
+
lacis dev`, "Next steps");
|
|
196
|
+
p.outro("Done!");
|
|
197
|
+
}
|
|
198
|
+
main().catch((err) => {
|
|
199
|
+
console.error("[create-lacis]", err);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-lacis",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a new lacis project",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/getlacis/create-lacis"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"bin": {
|
|
13
|
+
"create-lacis": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"templates"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"validate": "tsx scripts/validate.ts",
|
|
23
|
+
"test": "tsx scripts/test.ts",
|
|
24
|
+
"prepack": "npm run build && tsx scripts/validate.ts"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@clack/prompts": "^0.9.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.13.5",
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"tsx": "^4.19.2",
|
|
33
|
+
"typescript": "^5.7.0"
|
|
34
|
+
}
|
|
35
|
+
}
|