@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 +51 -0
- package/dist/next.js +154 -0
- package/dist/nuxt.js +24 -0
- package/package.json +17 -0
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
|
+
}
|