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 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
+ }
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ .env
@@ -0,0 +1,5 @@
1
+ import type { Request, Response } from 'lacis'
2
+
3
+ export const GET = async (_req: Request, res: Response) => {
4
+ res.status(200).json({ message: 'Hello from lacis!' })
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true
8
+ },
9
+ "exclude": ["node_modules"]
10
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "lacis build",
7
+ "dev": "tsx watch server.ts"
8
+ },
9
+ "dependencies": {
10
+ "lacis": "latest"
11
+ },
12
+ "devDependencies": {
13
+ "tsx": "latest"
14
+ }
15
+ }
@@ -0,0 +1,4 @@
1
+ import { createServer, getRoutesDir } from 'lacis'
2
+
3
+ const routesDir = getRoutesDir()
4
+ createServer(routesDir, { platform: 'node', port: 3000 })
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "lacis build",
7
+ "dev": "bun --watch server.ts"
8
+ },
9
+ "dependencies": {
10
+ "lacis": "latest"
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ import { createServer, getRoutesDir } from 'lacis'
2
+
3
+ const routesDir = getRoutesDir()
4
+ createServer(routesDir, { platform: 'bun', port: 3000 })
@@ -0,0 +1,5 @@
1
+ import { netlifyAdapter } from 'lacis/adapters'
2
+ import { routes } from '../../routes/_manifest.js'
3
+
4
+ const handler = netlifyAdapter.createHandler({ routes }) as Function
5
+ export { handler }
@@ -0,0 +1,12 @@
1
+ [build]
2
+ command = "npm run build"
3
+
4
+ [functions]
5
+ directory = "netlify/functions"
6
+ node_bundler = "esbuild"
7
+
8
+ [[redirects]]
9
+ from = "/*"
10
+ to = "/.netlify/functions/index"
11
+ status = 200
12
+ force = true
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "lacis build",
7
+ "dev": "lacis dev"
8
+ },
9
+ "dependencies": {
10
+ "lacis": "latest"
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ import { vercelAdapter } from 'lacis/adapters'
2
+ import { routes } from '../routes/_manifest.js'
3
+
4
+ export default vercelAdapter.createHandler({ routes })
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "lacis build",
7
+ "dev": "lacis dev"
8
+ },
9
+ "dependencies": {
10
+ "lacis": "latest"
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "rewrites": [{ "source": "/(.*)", "destination": "/api/index" }]
3
+ }