create-faas-app 8.0.0-beta.3 → 8.0.0-beta.5
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.cjs +243 -89
- package/dist/index.mjs +244 -90
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var enquirer = require('enquirer');
|
|
|
10
10
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
|
-
version: "v8.0.0-beta.
|
|
13
|
+
version: "v8.0.0-beta.4"};
|
|
14
14
|
var Validator = {
|
|
15
15
|
name(input) {
|
|
16
16
|
const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
|
|
@@ -20,126 +20,280 @@ var Validator = {
|
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
23
|
+
function writeFile(path$1, content) {
|
|
24
|
+
fs.mkdirSync(path.dirname(path$1), {
|
|
25
|
+
recursive: true
|
|
26
|
+
});
|
|
27
|
+
fs.writeFileSync(path$1, content);
|
|
28
|
+
}
|
|
29
|
+
function buildPackageJSON(name) {
|
|
30
|
+
return `${JSON.stringify(
|
|
31
|
+
{
|
|
32
|
+
name,
|
|
33
|
+
private: true,
|
|
34
|
+
type: "module",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
scripts: {
|
|
37
|
+
dev: "vite",
|
|
38
|
+
build: "vite build",
|
|
39
|
+
start: "node server.ts",
|
|
40
|
+
check: "tsc --noEmit && biome check .",
|
|
41
|
+
test: "vitest run"
|
|
42
|
+
},
|
|
43
|
+
dependencies: {
|
|
44
|
+
"@faasjs/func": "*",
|
|
45
|
+
"@faasjs/http": "*",
|
|
46
|
+
faasjs: "*",
|
|
47
|
+
react: "*",
|
|
48
|
+
"react-dom": "*",
|
|
49
|
+
zod: "*"
|
|
50
|
+
},
|
|
51
|
+
devDependencies: {
|
|
52
|
+
"@biomejs/biome": "*",
|
|
53
|
+
"@faasjs/dev": "*",
|
|
54
|
+
"@faasjs/lint": "*",
|
|
55
|
+
"@types/node": "*",
|
|
56
|
+
"@types/react": "*",
|
|
57
|
+
"@types/react-dom": "*",
|
|
58
|
+
"@vitejs/plugin-react": "*",
|
|
59
|
+
jsdom: "*",
|
|
60
|
+
typescript: "*",
|
|
61
|
+
vite: "*",
|
|
62
|
+
vitest: "*"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
null,
|
|
66
|
+
2
|
|
67
|
+
)}
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
function scaffold(rootPath) {
|
|
71
|
+
writeFile(
|
|
72
|
+
path.join(rootPath, ".gitignore"),
|
|
73
|
+
`node_modules/
|
|
74
|
+
dist/
|
|
75
|
+
coverage/
|
|
44
76
|
`
|
|
45
77
|
);
|
|
46
|
-
|
|
47
|
-
path.join(
|
|
78
|
+
writeFile(
|
|
79
|
+
path.join(rootPath, "biome.json"),
|
|
48
80
|
`{
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"scripts": {
|
|
53
|
-
"serve": "faas server",
|
|
54
|
-
"test": "vitest run"
|
|
55
|
-
},
|
|
56
|
-
"dependencies": {
|
|
57
|
-
"faasjs": "*"
|
|
58
|
-
},
|
|
59
|
-
"devDependencies": {
|
|
60
|
-
"vitest": "*"
|
|
61
|
-
}
|
|
62
|
-
}`
|
|
81
|
+
"extends": ["@faasjs/lint/biome"]
|
|
82
|
+
}
|
|
83
|
+
`
|
|
63
84
|
);
|
|
64
|
-
|
|
65
|
-
path.join(
|
|
85
|
+
writeFile(
|
|
86
|
+
path.join(rootPath, "tsconfig.json"),
|
|
66
87
|
`{
|
|
67
88
|
"compilerOptions": {
|
|
68
|
-
"
|
|
69
|
-
"esModuleInterop": true,
|
|
70
|
-
"target": "ES2019",
|
|
89
|
+
"target": "ES2022",
|
|
71
90
|
"module": "ESNext",
|
|
72
|
-
"moduleResolution": "
|
|
73
|
-
"
|
|
74
|
-
|
|
91
|
+
"moduleResolution": "Bundler",
|
|
92
|
+
"jsx": "react-jsx",
|
|
93
|
+
"strict": true,
|
|
94
|
+
"types": ["vitest/globals"]
|
|
95
|
+
},
|
|
96
|
+
"include": ["src", "vite.config.ts", "server.ts"]
|
|
75
97
|
}
|
|
76
98
|
`
|
|
77
99
|
);
|
|
78
|
-
|
|
79
|
-
path.join(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
writeFile(
|
|
101
|
+
path.join(rootPath, "index.html"),
|
|
102
|
+
`<!doctype html>
|
|
103
|
+
<html lang="en">
|
|
104
|
+
<head>
|
|
105
|
+
<meta charset="UTF-8" />
|
|
106
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
107
|
+
<title>FaasJS App</title>
|
|
108
|
+
</head>
|
|
109
|
+
<body>
|
|
110
|
+
<div id="root"></div>
|
|
111
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|
|
84
114
|
`
|
|
85
115
|
);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
writeFile(
|
|
117
|
+
path.join(rootPath, "vite.config.ts"),
|
|
118
|
+
`import { viteFaasJsServer } from '@faasjs/dev'
|
|
119
|
+
import react from '@vitejs/plugin-react'
|
|
120
|
+
import { defineConfig } from 'vite'
|
|
121
|
+
|
|
122
|
+
export default defineConfig({
|
|
123
|
+
server: {
|
|
124
|
+
host: '0.0.0.0',
|
|
95
125
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"files.trimFinalNewlines": true,
|
|
99
|
-
"files.trimTrailingWhitespace": true
|
|
100
|
-
}
|
|
126
|
+
plugins: [react(), viteFaasJsServer()],
|
|
127
|
+
})
|
|
101
128
|
`
|
|
102
129
|
);
|
|
103
|
-
|
|
104
|
-
path.join(
|
|
105
|
-
`{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
writeFile(
|
|
131
|
+
path.join(rootPath, "server.ts"),
|
|
132
|
+
`import { dirname, join } from 'node:path'
|
|
133
|
+
import { fileURLToPath } from 'node:url'
|
|
134
|
+
import { Server, staticHandler } from '@faasjs/server'
|
|
135
|
+
|
|
136
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
137
|
+
const __dirname = dirname(__filename)
|
|
138
|
+
|
|
139
|
+
const publicHandler = staticHandler({
|
|
140
|
+
root: join(__dirname, 'public'),
|
|
141
|
+
notFound: false,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const distHandler = staticHandler({
|
|
145
|
+
root: join(__dirname, 'dist'),
|
|
146
|
+
notFound: 'index.html',
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
new Server(join(__dirname, 'src'), {
|
|
150
|
+
beforeHandle: async (req, res, ctx) => {
|
|
151
|
+
if (!req.url || req.method !== 'GET') return
|
|
152
|
+
|
|
153
|
+
await publicHandler(req, res, ctx)
|
|
154
|
+
await distHandler(req, res, ctx)
|
|
155
|
+
},
|
|
156
|
+
}).listen()
|
|
157
|
+
`
|
|
158
|
+
);
|
|
159
|
+
writeFile(
|
|
160
|
+
path.join(rootPath, "src", "faas.yaml"),
|
|
161
|
+
`defaults:
|
|
162
|
+
plugins:
|
|
163
|
+
http:
|
|
164
|
+
config:
|
|
165
|
+
cookie:
|
|
166
|
+
secure: false
|
|
167
|
+
session:
|
|
168
|
+
secret: secret
|
|
169
|
+
development:
|
|
170
|
+
testing:
|
|
171
|
+
production:
|
|
172
|
+
`
|
|
173
|
+
);
|
|
174
|
+
writeFile(
|
|
175
|
+
path.join(rootPath, "src", "main.tsx"),
|
|
176
|
+
`import { createRoot } from 'react-dom/client'
|
|
177
|
+
import HomePage from './pages/home'
|
|
178
|
+
|
|
179
|
+
createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
|
|
180
|
+
`
|
|
181
|
+
);
|
|
182
|
+
writeFile(
|
|
183
|
+
path.join(rootPath, "src", "pages", "home", "index.tsx"),
|
|
184
|
+
`import { useState } from 'react'
|
|
185
|
+
|
|
186
|
+
type ApiResponse = {
|
|
187
|
+
ok: boolean
|
|
188
|
+
data: string
|
|
189
|
+
error: null | {
|
|
190
|
+
code?: string
|
|
191
|
+
message: string
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default function HomePage() {
|
|
196
|
+
const [message, setMessage] = useState('Click button to call API')
|
|
197
|
+
const [loading, setLoading] = useState(false)
|
|
198
|
+
|
|
199
|
+
const fetchMessage = async () => {
|
|
200
|
+
setLoading(true)
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const data = await fetch('/home/api/hello', {
|
|
204
|
+
method: 'POST',
|
|
205
|
+
headers: {
|
|
206
|
+
'Content-Type': 'application/json',
|
|
207
|
+
},
|
|
208
|
+
body: JSON.stringify({ name: 'world' }),
|
|
209
|
+
}).then(res => res.json() as Promise<ApiResponse>)
|
|
210
|
+
|
|
211
|
+
if (data.ok) setMessage(data.data)
|
|
212
|
+
else setMessage(data.error?.message || 'Unknown error')
|
|
213
|
+
} catch (error: any) {
|
|
214
|
+
setMessage(error?.message || 'Unknown error')
|
|
215
|
+
} finally {
|
|
216
|
+
setLoading(false)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
|
|
222
|
+
<h1>FaasJS Starter</h1>
|
|
223
|
+
<p>{message}</p>
|
|
224
|
+
<button type="button" onClick={fetchMessage} disabled={loading}>
|
|
225
|
+
{loading ? 'Loading...' : 'Call /home/api/hello'}
|
|
226
|
+
</button>
|
|
227
|
+
</main>
|
|
228
|
+
)
|
|
109
229
|
}
|
|
110
230
|
`
|
|
111
231
|
);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
import { useHttp } from '@faasjs/http'
|
|
232
|
+
writeFile(
|
|
233
|
+
path.join(rootPath, "src", "pages", "home", "api", "hello.func.ts"),
|
|
234
|
+
`import { defineFunc } from '@faasjs/func'
|
|
235
|
+
import { z } from 'zod'
|
|
117
236
|
|
|
118
|
-
|
|
119
|
-
|
|
237
|
+
const schema = z
|
|
238
|
+
.object({
|
|
239
|
+
name: z.string().optional(),
|
|
240
|
+
})
|
|
241
|
+
.required()
|
|
120
242
|
|
|
121
|
-
|
|
122
|
-
})
|
|
243
|
+
export const func = defineFunc<{ params?: z.infer<typeof schema> }>(
|
|
244
|
+
async ({ event }) => {
|
|
245
|
+
const parsed = schema.parse(event.params || {})
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
ok: true,
|
|
249
|
+
data: \`Hello, \${parsed.name || 'FaasJS'}\`,
|
|
250
|
+
error: null,
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
)
|
|
123
254
|
`
|
|
124
255
|
);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
import { func } from '../index.func'
|
|
256
|
+
writeFile(
|
|
257
|
+
path.join(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"),
|
|
258
|
+
`import { test } from '@faasjs/dev'
|
|
259
|
+
import { func } from '../hello.func'
|
|
130
260
|
|
|
131
|
-
describe('hello', () => {
|
|
261
|
+
describe('home/api/hello', () => {
|
|
132
262
|
it('should work', async () => {
|
|
133
263
|
const testFunc = test(func)
|
|
134
264
|
|
|
135
|
-
const { statusCode, data } = await testFunc.JSONhandler
|
|
265
|
+
const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
|
|
136
266
|
|
|
137
267
|
expect(statusCode).toEqual(200)
|
|
138
|
-
expect(data).toEqual(
|
|
268
|
+
expect(data).toEqual({
|
|
269
|
+
ok: true,
|
|
270
|
+
data: 'Hello, world',
|
|
271
|
+
error: null,
|
|
272
|
+
})
|
|
139
273
|
})
|
|
140
274
|
})
|
|
141
275
|
`
|
|
142
276
|
);
|
|
277
|
+
}
|
|
278
|
+
async function action(options = {}) {
|
|
279
|
+
const answers = Object.assign(options, {});
|
|
280
|
+
if (!options.name || Validator.name(options.name) !== true)
|
|
281
|
+
answers.name = await enquirer.prompt({
|
|
282
|
+
type: "input",
|
|
283
|
+
name: "value",
|
|
284
|
+
message: "Project name",
|
|
285
|
+
initial: "faasjs",
|
|
286
|
+
validate: Validator.name
|
|
287
|
+
}).then((res) => res.value);
|
|
288
|
+
if (!answers.name) return;
|
|
289
|
+
const runtime = process.versions.bun ? "bun" : "npm";
|
|
290
|
+
fs.mkdirSync(answers.name);
|
|
291
|
+
fs.writeFileSync(
|
|
292
|
+
path.join(answers.name, "package.json"),
|
|
293
|
+
buildPackageJSON(answers.name)
|
|
294
|
+
);
|
|
295
|
+
scaffold(answers.name);
|
|
296
|
+
child_process.execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
143
297
|
if (runtime === "bun") {
|
|
144
298
|
child_process.execSync(`cd ${answers.name} && bun test`, { stdio: "inherit" });
|
|
145
299
|
} else child_process.execSync(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
import { mkdirSync, writeFileSync, existsSync } from 'fs';
|
|
4
|
-
import { join } from 'path';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
5
|
import { prompt } from 'enquirer';
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
|
-
version: "v8.0.0-beta.
|
|
11
|
+
version: "v8.0.0-beta.4"};
|
|
12
12
|
var Validator = {
|
|
13
13
|
name(input) {
|
|
14
14
|
const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
|
|
@@ -18,126 +18,280 @@ var Validator = {
|
|
|
18
18
|
return true;
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
21
|
+
function writeFile(path, content) {
|
|
22
|
+
mkdirSync(dirname(path), {
|
|
23
|
+
recursive: true
|
|
24
|
+
});
|
|
25
|
+
writeFileSync(path, content);
|
|
26
|
+
}
|
|
27
|
+
function buildPackageJSON(name) {
|
|
28
|
+
return `${JSON.stringify(
|
|
29
|
+
{
|
|
30
|
+
name,
|
|
31
|
+
private: true,
|
|
32
|
+
type: "module",
|
|
33
|
+
version: "1.0.0",
|
|
34
|
+
scripts: {
|
|
35
|
+
dev: "vite",
|
|
36
|
+
build: "vite build",
|
|
37
|
+
start: "node server.ts",
|
|
38
|
+
check: "tsc --noEmit && biome check .",
|
|
39
|
+
test: "vitest run"
|
|
40
|
+
},
|
|
41
|
+
dependencies: {
|
|
42
|
+
"@faasjs/func": "*",
|
|
43
|
+
"@faasjs/http": "*",
|
|
44
|
+
faasjs: "*",
|
|
45
|
+
react: "*",
|
|
46
|
+
"react-dom": "*",
|
|
47
|
+
zod: "*"
|
|
48
|
+
},
|
|
49
|
+
devDependencies: {
|
|
50
|
+
"@biomejs/biome": "*",
|
|
51
|
+
"@faasjs/dev": "*",
|
|
52
|
+
"@faasjs/lint": "*",
|
|
53
|
+
"@types/node": "*",
|
|
54
|
+
"@types/react": "*",
|
|
55
|
+
"@types/react-dom": "*",
|
|
56
|
+
"@vitejs/plugin-react": "*",
|
|
57
|
+
jsdom: "*",
|
|
58
|
+
typescript: "*",
|
|
59
|
+
vite: "*",
|
|
60
|
+
vitest: "*"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
null,
|
|
64
|
+
2
|
|
65
|
+
)}
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
function scaffold(rootPath) {
|
|
69
|
+
writeFile(
|
|
70
|
+
join(rootPath, ".gitignore"),
|
|
71
|
+
`node_modules/
|
|
72
|
+
dist/
|
|
73
|
+
coverage/
|
|
42
74
|
`
|
|
43
75
|
);
|
|
44
|
-
|
|
45
|
-
join(
|
|
76
|
+
writeFile(
|
|
77
|
+
join(rootPath, "biome.json"),
|
|
46
78
|
`{
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"scripts": {
|
|
51
|
-
"serve": "faas server",
|
|
52
|
-
"test": "vitest run"
|
|
53
|
-
},
|
|
54
|
-
"dependencies": {
|
|
55
|
-
"faasjs": "*"
|
|
56
|
-
},
|
|
57
|
-
"devDependencies": {
|
|
58
|
-
"vitest": "*"
|
|
59
|
-
}
|
|
60
|
-
}`
|
|
79
|
+
"extends": ["@faasjs/lint/biome"]
|
|
80
|
+
}
|
|
81
|
+
`
|
|
61
82
|
);
|
|
62
|
-
|
|
63
|
-
join(
|
|
83
|
+
writeFile(
|
|
84
|
+
join(rootPath, "tsconfig.json"),
|
|
64
85
|
`{
|
|
65
86
|
"compilerOptions": {
|
|
66
|
-
"
|
|
67
|
-
"esModuleInterop": true,
|
|
68
|
-
"target": "ES2019",
|
|
87
|
+
"target": "ES2022",
|
|
69
88
|
"module": "ESNext",
|
|
70
|
-
"moduleResolution": "
|
|
71
|
-
"
|
|
72
|
-
|
|
89
|
+
"moduleResolution": "Bundler",
|
|
90
|
+
"jsx": "react-jsx",
|
|
91
|
+
"strict": true,
|
|
92
|
+
"types": ["vitest/globals"]
|
|
93
|
+
},
|
|
94
|
+
"include": ["src", "vite.config.ts", "server.ts"]
|
|
73
95
|
}
|
|
74
96
|
`
|
|
75
97
|
);
|
|
76
|
-
|
|
77
|
-
join(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
writeFile(
|
|
99
|
+
join(rootPath, "index.html"),
|
|
100
|
+
`<!doctype html>
|
|
101
|
+
<html lang="en">
|
|
102
|
+
<head>
|
|
103
|
+
<meta charset="UTF-8" />
|
|
104
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
105
|
+
<title>FaasJS App</title>
|
|
106
|
+
</head>
|
|
107
|
+
<body>
|
|
108
|
+
<div id="root"></div>
|
|
109
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
|
82
112
|
`
|
|
83
113
|
);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
114
|
+
writeFile(
|
|
115
|
+
join(rootPath, "vite.config.ts"),
|
|
116
|
+
`import { viteFaasJsServer } from '@faasjs/dev'
|
|
117
|
+
import react from '@vitejs/plugin-react'
|
|
118
|
+
import { defineConfig } from 'vite'
|
|
119
|
+
|
|
120
|
+
export default defineConfig({
|
|
121
|
+
server: {
|
|
122
|
+
host: '0.0.0.0',
|
|
93
123
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"files.trimFinalNewlines": true,
|
|
97
|
-
"files.trimTrailingWhitespace": true
|
|
98
|
-
}
|
|
124
|
+
plugins: [react(), viteFaasJsServer()],
|
|
125
|
+
})
|
|
99
126
|
`
|
|
100
127
|
);
|
|
101
|
-
|
|
102
|
-
join(
|
|
103
|
-
`{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
writeFile(
|
|
129
|
+
join(rootPath, "server.ts"),
|
|
130
|
+
`import { dirname, join } from 'node:path'
|
|
131
|
+
import { fileURLToPath } from 'node:url'
|
|
132
|
+
import { Server, staticHandler } from '@faasjs/server'
|
|
133
|
+
|
|
134
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
135
|
+
const __dirname = dirname(__filename)
|
|
136
|
+
|
|
137
|
+
const publicHandler = staticHandler({
|
|
138
|
+
root: join(__dirname, 'public'),
|
|
139
|
+
notFound: false,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const distHandler = staticHandler({
|
|
143
|
+
root: join(__dirname, 'dist'),
|
|
144
|
+
notFound: 'index.html',
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
new Server(join(__dirname, 'src'), {
|
|
148
|
+
beforeHandle: async (req, res, ctx) => {
|
|
149
|
+
if (!req.url || req.method !== 'GET') return
|
|
150
|
+
|
|
151
|
+
await publicHandler(req, res, ctx)
|
|
152
|
+
await distHandler(req, res, ctx)
|
|
153
|
+
},
|
|
154
|
+
}).listen()
|
|
155
|
+
`
|
|
156
|
+
);
|
|
157
|
+
writeFile(
|
|
158
|
+
join(rootPath, "src", "faas.yaml"),
|
|
159
|
+
`defaults:
|
|
160
|
+
plugins:
|
|
161
|
+
http:
|
|
162
|
+
config:
|
|
163
|
+
cookie:
|
|
164
|
+
secure: false
|
|
165
|
+
session:
|
|
166
|
+
secret: secret
|
|
167
|
+
development:
|
|
168
|
+
testing:
|
|
169
|
+
production:
|
|
170
|
+
`
|
|
171
|
+
);
|
|
172
|
+
writeFile(
|
|
173
|
+
join(rootPath, "src", "main.tsx"),
|
|
174
|
+
`import { createRoot } from 'react-dom/client'
|
|
175
|
+
import HomePage from './pages/home'
|
|
176
|
+
|
|
177
|
+
createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
|
|
178
|
+
`
|
|
179
|
+
);
|
|
180
|
+
writeFile(
|
|
181
|
+
join(rootPath, "src", "pages", "home", "index.tsx"),
|
|
182
|
+
`import { useState } from 'react'
|
|
183
|
+
|
|
184
|
+
type ApiResponse = {
|
|
185
|
+
ok: boolean
|
|
186
|
+
data: string
|
|
187
|
+
error: null | {
|
|
188
|
+
code?: string
|
|
189
|
+
message: string
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export default function HomePage() {
|
|
194
|
+
const [message, setMessage] = useState('Click button to call API')
|
|
195
|
+
const [loading, setLoading] = useState(false)
|
|
196
|
+
|
|
197
|
+
const fetchMessage = async () => {
|
|
198
|
+
setLoading(true)
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const data = await fetch('/home/api/hello', {
|
|
202
|
+
method: 'POST',
|
|
203
|
+
headers: {
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
},
|
|
206
|
+
body: JSON.stringify({ name: 'world' }),
|
|
207
|
+
}).then(res => res.json() as Promise<ApiResponse>)
|
|
208
|
+
|
|
209
|
+
if (data.ok) setMessage(data.data)
|
|
210
|
+
else setMessage(data.error?.message || 'Unknown error')
|
|
211
|
+
} catch (error: any) {
|
|
212
|
+
setMessage(error?.message || 'Unknown error')
|
|
213
|
+
} finally {
|
|
214
|
+
setLoading(false)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
|
|
220
|
+
<h1>FaasJS Starter</h1>
|
|
221
|
+
<p>{message}</p>
|
|
222
|
+
<button type="button" onClick={fetchMessage} disabled={loading}>
|
|
223
|
+
{loading ? 'Loading...' : 'Call /home/api/hello'}
|
|
224
|
+
</button>
|
|
225
|
+
</main>
|
|
226
|
+
)
|
|
107
227
|
}
|
|
108
228
|
`
|
|
109
229
|
);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
import { useHttp } from '@faasjs/http'
|
|
230
|
+
writeFile(
|
|
231
|
+
join(rootPath, "src", "pages", "home", "api", "hello.func.ts"),
|
|
232
|
+
`import { defineFunc } from '@faasjs/func'
|
|
233
|
+
import { z } from 'zod'
|
|
115
234
|
|
|
116
|
-
|
|
117
|
-
|
|
235
|
+
const schema = z
|
|
236
|
+
.object({
|
|
237
|
+
name: z.string().optional(),
|
|
238
|
+
})
|
|
239
|
+
.required()
|
|
118
240
|
|
|
119
|
-
|
|
120
|
-
})
|
|
241
|
+
export const func = defineFunc<{ params?: z.infer<typeof schema> }>(
|
|
242
|
+
async ({ event }) => {
|
|
243
|
+
const parsed = schema.parse(event.params || {})
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
ok: true,
|
|
247
|
+
data: \`Hello, \${parsed.name || 'FaasJS'}\`,
|
|
248
|
+
error: null,
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
)
|
|
121
252
|
`
|
|
122
253
|
);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
import { func } from '../index.func'
|
|
254
|
+
writeFile(
|
|
255
|
+
join(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"),
|
|
256
|
+
`import { test } from '@faasjs/dev'
|
|
257
|
+
import { func } from '../hello.func'
|
|
128
258
|
|
|
129
|
-
describe('hello', () => {
|
|
259
|
+
describe('home/api/hello', () => {
|
|
130
260
|
it('should work', async () => {
|
|
131
261
|
const testFunc = test(func)
|
|
132
262
|
|
|
133
|
-
const { statusCode, data } = await testFunc.JSONhandler
|
|
263
|
+
const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
|
|
134
264
|
|
|
135
265
|
expect(statusCode).toEqual(200)
|
|
136
|
-
expect(data).toEqual(
|
|
266
|
+
expect(data).toEqual({
|
|
267
|
+
ok: true,
|
|
268
|
+
data: 'Hello, world',
|
|
269
|
+
error: null,
|
|
270
|
+
})
|
|
137
271
|
})
|
|
138
272
|
})
|
|
139
273
|
`
|
|
140
274
|
);
|
|
275
|
+
}
|
|
276
|
+
async function action(options = {}) {
|
|
277
|
+
const answers = Object.assign(options, {});
|
|
278
|
+
if (!options.name || Validator.name(options.name) !== true)
|
|
279
|
+
answers.name = await prompt({
|
|
280
|
+
type: "input",
|
|
281
|
+
name: "value",
|
|
282
|
+
message: "Project name",
|
|
283
|
+
initial: "faasjs",
|
|
284
|
+
validate: Validator.name
|
|
285
|
+
}).then((res) => res.value);
|
|
286
|
+
if (!answers.name) return;
|
|
287
|
+
const runtime = process.versions.bun ? "bun" : "npm";
|
|
288
|
+
mkdirSync(answers.name);
|
|
289
|
+
writeFileSync(
|
|
290
|
+
join(answers.name, "package.json"),
|
|
291
|
+
buildPackageJSON(answers.name)
|
|
292
|
+
);
|
|
293
|
+
scaffold(answers.name);
|
|
294
|
+
execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
141
295
|
if (runtime === "bun") {
|
|
142
296
|
execSync(`cd ${answers.name} && bun test`, { stdio: "inherit" });
|
|
143
297
|
} else execSync(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
|