@skafform/create 0.2.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,51 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const next_1 = require("./next");
7
+ const nuxt_1 = require("./nuxt");
8
+ if (process.argv[2] === "--version" || process.argv[2] === "-v") {
9
+ const { version } = require("../package.json");
10
+ console.log(version);
11
+ process.exit(0);
12
+ }
13
+ if (process.argv[2] === "--help" || process.argv[2] === "-h") {
14
+ console.log(`
15
+ Usage: create-skafform <project-name> [--next|--nuxt]
16
+
17
+ Options:
18
+ --next Scaffold a Next.js project (default)
19
+ --nuxt Scaffold a Nuxt project
20
+ -v, --version Show version number
21
+ -h, --help Show this help message
22
+
23
+ Examples:
24
+ npx create-skafform my-app --next
25
+ npx create-skafform my-app --nuxt
26
+ `);
27
+ process.exit(0);
28
+ }
29
+ const name = process.argv[2];
30
+ const flags = process.argv.slice(3);
31
+ const isNuxt = flags.includes("--nuxt");
32
+ if (!name) {
33
+ console.error("Usage: npx create-skafform <project-name> [--next|--nuxt]");
34
+ process.exit(1);
35
+ }
36
+ if (!/^[a-z0-9-_]+$/.test(name)) {
37
+ console.error("Project name must only contain lowercase letters, numbers, hyphens, and underscores.");
38
+ process.exit(1);
39
+ }
40
+ const targetDir = (0, path_1.resolve)(process.cwd(), name);
41
+ if ((0, fs_1.existsSync)(targetDir)) {
42
+ console.error(`Directory "${name}" already exists.`);
43
+ process.exit(1);
44
+ }
45
+ console.log(`\nCreating ${name}...\n`);
46
+ if (isNuxt) {
47
+ (0, nuxt_1.scaffoldNuxt)(name, targetDir);
48
+ }
49
+ else {
50
+ (0, next_1.scaffoldNext)(name, targetDir);
51
+ }
package/dist/next.js ADDED
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scaffoldNext = scaffoldNext;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ function scaffoldNext(name, targetDir) {
8
+ (0, child_process_1.execSync)(`npx create-next-app@latest ${name} --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"`, { stdio: "inherit" });
9
+ const skafformDir = (0, path_1.join)(targetDir, ".skafform");
10
+ (0, fs_1.mkdirSync)((0, path_1.join)(skafformDir, "contracts"), { recursive: true });
11
+ (0, fs_1.mkdirSync)((0, path_1.join)(skafformDir, "modules"), { recursive: true });
12
+ (0, fs_1.writeFileSync)((0, path_1.join)(skafformDir, "installed.json"), JSON.stringify({ modules: [] }, null, 2));
13
+ (0, fs_1.writeFileSync)((0, path_1.join)(skafformDir, "proxy.ts"), "// Skafform managed — do not edit\n" +
14
+ "import { NextRequest, NextFetchEvent, NextResponse } from \"next/server\"\n" +
15
+ "\n" +
16
+ "type ProxyFn = (req: NextRequest, event: NextFetchEvent) => NextResponse | Response | null | void | Promise<NextResponse | Response | null | void>\n" +
17
+ "\n" +
18
+ "export type SkafformHooks = {\n" +
19
+ " beforeAuth?: (req: NextRequest) => Response | void | Promise<Response | void>\n" +
20
+ " afterAuth?: (req: NextRequest, ctx: { userId: string | null }) => Response | void | Promise<Response | void>\n" +
21
+ "}\n" +
22
+ "\n" +
23
+ "export function withSkafform(hooks: SkafformHooks = {}): ProxyFn {\n" +
24
+ " return async (req, _event) => {\n" +
25
+ " if (hooks.beforeAuth) { const r = await hooks.beforeAuth(req); if (r) return r }\n" +
26
+ " if (hooks.afterAuth) { const r = await hooks.afterAuth(req, { userId: null }); if (r) return r }\n" +
27
+ " return NextResponse.next()\n" +
28
+ " }\n" +
29
+ "}\n" +
30
+ "\n" +
31
+ "export default withSkafform()\n");
32
+ (0, fs_1.writeFileSync)((0, path_1.join)(skafformDir, "csp.ts"), "// Skafform managed — do not edit\n" +
33
+ "const base: Record<string, string[]> = {\n" +
34
+ " \"default-src\": [\"'self'\"],\n" +
35
+ " \"script-src\": [\"'self'\", \"'unsafe-inline'\"],\n" +
36
+ " \"style-src\": [\"'self'\", \"'unsafe-inline'\"],\n" +
37
+ " \"img-src\": [\"'self'\", \"data:\", \"blob:\"],\n" +
38
+ " \"font-src\": [\"'self'\"],\n" +
39
+ " \"connect-src\": [\"'self'\"],\n" +
40
+ " \"frame-src\": [\"'none'\"],\n" +
41
+ " \"object-src\": [\"'none'\"],\n" +
42
+ " \"base-uri\": [\"'self'\"],\n" +
43
+ " \"form-action\": [\"'self'\"],\n" +
44
+ "}\n" +
45
+ "\n" +
46
+ "export function buildCSP(extra: Record<string, string[]> = {}): string {\n" +
47
+ " const merged: Record<string, Set<string>> = {}\n" +
48
+ " for (const [k, v] of Object.entries(base)) merged[k] = new Set(v)\n" +
49
+ " for (const [k, v] of Object.entries(extra)) {\n" +
50
+ " if (!merged[k]) merged[k] = new Set()\n" +
51
+ " for (const src of v) merged[k].add(src)\n" +
52
+ " }\n" +
53
+ " return Object.entries(merged)\n" +
54
+ " .map(([k, v]) => `${k} ${[...v].join(\" \")}`)\n" +
55
+ " .join(\"; \")\n" +
56
+ "}\n" +
57
+ "\n" +
58
+ "export const csp = buildCSP()\n");
59
+ (0, fs_1.writeFileSync)((0, path_1.join)(skafformDir, "providers.tsx"), "// Skafform managed — do not edit\n" +
60
+ "\"use client\"\n" +
61
+ "\n" +
62
+ "export function Providers({ children }: { children: React.ReactNode }) {\n" +
63
+ " return <>{children}</>\n" +
64
+ "}\n");
65
+ (0, fs_1.writeFileSync)((0, path_1.join)(targetDir, "proxy.ts"), "import { withSkafform } from \"./.skafform/proxy\"\n" +
66
+ "\n" +
67
+ "export default withSkafform()\n" +
68
+ "\n" +
69
+ "// To add custom logic:\n" +
70
+ "// export default withSkafform({\n" +
71
+ "// afterAuth(req, { userId }) { /* your logic */ }\n" +
72
+ "// })\n" +
73
+ "\n" +
74
+ "export const config = {\n" +
75
+ " matcher: [\n" +
76
+ " \"/((?!_next|[^?]*\\\\..*|_next/static|_next/image|favicon.ico).*)\",\n" +
77
+ " \"/(api|trpc)(.*)\",\n" +
78
+ " ],\n" +
79
+ "}\n");
80
+ (0, fs_1.writeFileSync)((0, path_1.join)(targetDir, "app", "layout.tsx"), "import type { Metadata } from \"next\";\n" +
81
+ "import { Geist, Geist_Mono } from \"next/font/google\";\n" +
82
+ "import { Providers } from \"@/.skafform/providers\";\n" +
83
+ "import \"./globals.css\";\n" +
84
+ "\n" +
85
+ "const geistSans = Geist({\n" +
86
+ " variable: \"--font-geist-sans\",\n" +
87
+ " subsets: [\"latin\"],\n" +
88
+ "});\n" +
89
+ "\n" +
90
+ "const geistMono = Geist_Mono({\n" +
91
+ " variable: \"--font-geist-mono\",\n" +
92
+ " subsets: [\"latin\"],\n" +
93
+ "});\n" +
94
+ "\n" +
95
+ "export const metadata: Metadata = {\n" +
96
+ " title: \"My App\",\n" +
97
+ " description: \"Built with Skafform\",\n" +
98
+ "};\n" +
99
+ "\n" +
100
+ "export default function RootLayout({\n" +
101
+ " children,\n" +
102
+ "}: Readonly<{\n" +
103
+ " children: React.ReactNode;\n" +
104
+ "}>) {\n" +
105
+ " return (\n" +
106
+ " <html\n" +
107
+ " lang=\"en\"\n" +
108
+ " className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}\n" +
109
+ " >\n" +
110
+ " <body className=\"min-h-full flex flex-col\">\n" +
111
+ " <Providers>{children}</Providers>\n" +
112
+ " </body>\n" +
113
+ " </html>\n" +
114
+ " );\n" +
115
+ "}\n");
116
+ (0, fs_1.writeFileSync)((0, path_1.join)(targetDir, "next.config.ts"), "import type { NextConfig } from \"next\";\n" +
117
+ "import { buildCSP } from \"./.skafform/csp\";\n" +
118
+ "\n" +
119
+ "const csp = buildCSP({\n" +
120
+ " ...(process.env.NODE_ENV === \"development\" ? { \"script-src\": [\"'unsafe-eval'\"] } : {}),\n" +
121
+ " // \"frame-src\": [\"https://www.youtube.com\"],\n" +
122
+ " // \"script-src\": [\"https://my-analytics.com\"],\n" +
123
+ "});\n" +
124
+ "\n" +
125
+ "const nextConfig: NextConfig = {\n" +
126
+ " async headers() {\n" +
127
+ " return [\n" +
128
+ " {\n" +
129
+ " source: \"/(.*)\",\n" +
130
+ " headers: [\n" +
131
+ " { key: \"X-Frame-Options\", value: \"DENY\" },\n" +
132
+ " { key: \"X-Content-Type-Options\", value: \"nosniff\" },\n" +
133
+ " { key: \"Referrer-Policy\", value: \"strict-origin-when-cross-origin\" },\n" +
134
+ " { key: \"Permissions-Policy\", value: \"camera=(), microphone=(), geolocation=()\" },\n" +
135
+ " { key: \"Content-Security-Policy\", value: csp },\n" +
136
+ " ],\n" +
137
+ " },\n" +
138
+ " ];\n" +
139
+ " },\n" +
140
+ "};\n" +
141
+ "\n" +
142
+ "export default nextConfig;\n");
143
+ console.log(`
144
+ ┌─────────────────────────────────────────┐
145
+ │ Skafform Next.js project ready. │
146
+ └─────────────────────────────────────────┘
147
+
148
+ cd ${name}
149
+ npm run dev
150
+
151
+ To add integrations:
152
+ npx skafform serve
153
+ `);
154
+ }
package/dist/nuxt.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scaffoldNuxt = scaffoldNuxt;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ function scaffoldNuxt(name, targetDir) {
8
+ (0, child_process_1.execSync)(`npx nuxi@latest init ${name}`, { stdio: "inherit" });
9
+ const skafformDir = (0, path_1.join)(targetDir, ".skafform");
10
+ (0, fs_1.mkdirSync)((0, path_1.join)(skafformDir, "contracts"), { recursive: true });
11
+ (0, fs_1.mkdirSync)((0, path_1.join)(skafformDir, "modules"), { recursive: true });
12
+ (0, fs_1.writeFileSync)((0, path_1.join)(skafformDir, "installed.json"), JSON.stringify({ modules: [] }, null, 2));
13
+ console.log(`
14
+ ┌─────────────────────────────────────────┐
15
+ │ Skafform Nuxt project ready. │
16
+ └─────────────────────────────────────────┘
17
+
18
+ cd ${name}
19
+ npm run dev
20
+
21
+ To add integrations:
22
+ npx skafform serve
23
+ `);
24
+ }
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@skafform/create",
3
+ "version": "0.2.0",
4
+ "description": "Scaffold a new Skafform project",
5
+ "bin": {
6
+ "create-skafform": "./dist/index.js"
7
+ },
8
+ "files": ["dist"],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^20",
15
+ "typescript": "^5"
16
+ }
17
+ }