create-faas-app 8.0.0-beta.1 → 8.0.0-beta.10
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 +321 -140
- package/dist/index.d.ts +4 -22
- package/dist/index.mjs +292 -138
- package/package.json +19 -19
package/dist/index.cjs
CHANGED
|
@@ -1,164 +1,345 @@
|
|
|
1
|
-
'
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
+
__defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
22
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
+
value: mod,
|
|
25
|
+
enumerable: true
|
|
26
|
+
}) : target, mod));
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
let commander = require("commander");
|
|
30
|
+
let node_child_process = require("node:child_process");
|
|
31
|
+
let node_fs = require("node:fs");
|
|
32
|
+
let node_path = require("node:path");
|
|
33
|
+
let enquirer = require("enquirer");
|
|
34
|
+
enquirer = __toESM(enquirer);
|
|
35
|
+
|
|
36
|
+
//#region package.json
|
|
37
|
+
var version = "8.0.0-beta.9";
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/action.ts
|
|
41
|
+
const prompt = enquirer.default.prompt;
|
|
42
|
+
const Validator = { name(input) {
|
|
43
|
+
const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
|
|
44
|
+
if (match !== true) return match;
|
|
45
|
+
if ((0, node_fs.existsSync)(input)) return `${input} folder exists, please try another name`;
|
|
46
|
+
return true;
|
|
47
|
+
} };
|
|
48
|
+
function writeFile(path, content) {
|
|
49
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(path), { recursive: true });
|
|
50
|
+
(0, node_fs.writeFileSync)(path, content);
|
|
51
|
+
}
|
|
52
|
+
function buildPackageJSON(name) {
|
|
53
|
+
return `${JSON.stringify({
|
|
54
|
+
name,
|
|
55
|
+
private: true,
|
|
56
|
+
type: "module",
|
|
57
|
+
version: "1.0.0",
|
|
58
|
+
scripts: {
|
|
59
|
+
dev: "vite",
|
|
60
|
+
build: "vite build",
|
|
61
|
+
start: "node server.ts",
|
|
62
|
+
check: "faas lint",
|
|
63
|
+
test: "vitest run",
|
|
64
|
+
"migrate:latest": "faas knex latest",
|
|
65
|
+
"migrate:rollback": "faas knex rollback",
|
|
66
|
+
"migrate:status": "faas knex status",
|
|
67
|
+
"migrate:current": "faas knex current",
|
|
68
|
+
"migrate:make": "faas knex make"
|
|
69
|
+
},
|
|
70
|
+
dependencies: {
|
|
71
|
+
"@faasjs/core": "*",
|
|
72
|
+
pg: "*",
|
|
73
|
+
react: "*",
|
|
74
|
+
"react-dom": "*"
|
|
75
|
+
},
|
|
76
|
+
devDependencies: {
|
|
77
|
+
"@electric-sql/pglite": "*",
|
|
78
|
+
"@faasjs/dev": "*",
|
|
79
|
+
"@types/node": "*",
|
|
80
|
+
"@types/react": "*",
|
|
81
|
+
"@types/react-dom": "*",
|
|
82
|
+
"@vitejs/plugin-react": "*",
|
|
83
|
+
jsdom: "*",
|
|
84
|
+
"knex-pglite": "*",
|
|
85
|
+
oxlint: "*",
|
|
86
|
+
typescript: "*",
|
|
87
|
+
vite: "*",
|
|
88
|
+
vitest: "*"
|
|
89
|
+
}
|
|
90
|
+
}, null, 2)}
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
function scaffold(rootPath) {
|
|
94
|
+
writeFile((0, node_path.join)(rootPath, ".gitignore"), `node_modules/
|
|
95
|
+
dist/
|
|
96
|
+
coverage/
|
|
97
|
+
.pglite_dev/
|
|
98
|
+
`);
|
|
99
|
+
writeFile((0, node_path.join)(rootPath, "tsconfig.json"), `{
|
|
100
|
+
"compilerOptions": {
|
|
101
|
+
"target": "ES2022",
|
|
102
|
+
"module": "ESNext",
|
|
103
|
+
"moduleResolution": "Bundler",
|
|
104
|
+
"jsx": "react-jsx",
|
|
105
|
+
"strict": true,
|
|
106
|
+
"types": ["vitest/globals"]
|
|
107
|
+
},
|
|
108
|
+
"include": ["src", "vite.config.ts", "server.ts"]
|
|
109
|
+
}
|
|
110
|
+
`);
|
|
111
|
+
writeFile((0, node_path.join)(rootPath, "index.html"), `<!doctype html>
|
|
112
|
+
<html lang="en">
|
|
113
|
+
<head>
|
|
114
|
+
<meta charset="UTF-8" />
|
|
115
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
116
|
+
<title>FaasJS App</title>
|
|
117
|
+
</head>
|
|
118
|
+
<body>
|
|
119
|
+
<div id="root"></div>
|
|
120
|
+
<script type="module" src="/src/main.tsx"><\/script>
|
|
121
|
+
</body>
|
|
122
|
+
</html>
|
|
123
|
+
`);
|
|
124
|
+
writeFile((0, node_path.join)(rootPath, "vite.config.ts"), `import { viteFaasJsServer } from '@faasjs/dev'
|
|
125
|
+
import react from '@vitejs/plugin-react'
|
|
126
|
+
import { defineConfig } from 'vite'
|
|
127
|
+
|
|
128
|
+
export default defineConfig({
|
|
129
|
+
server: {
|
|
130
|
+
host: '0.0.0.0',
|
|
131
|
+
},
|
|
132
|
+
plugins: [react(), viteFaasJsServer()],
|
|
133
|
+
})
|
|
134
|
+
`);
|
|
135
|
+
writeFile((0, node_path.join)(rootPath, "server.ts"), `import { dirname, join } from 'node:path'
|
|
136
|
+
import { fileURLToPath } from 'node:url'
|
|
137
|
+
import { Server, staticHandler } from '@faasjs/core'
|
|
138
|
+
|
|
139
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
140
|
+
const __dirname = dirname(__filename)
|
|
141
|
+
|
|
142
|
+
const publicHandler = staticHandler({
|
|
143
|
+
root: join(__dirname, 'public'),
|
|
144
|
+
notFound: false,
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const distHandler = staticHandler({
|
|
148
|
+
root: join(__dirname, 'dist'),
|
|
149
|
+
notFound: 'index.html',
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
new Server(join(__dirname, 'src'), {
|
|
153
|
+
beforeHandle: async (req, res, ctx) => {
|
|
154
|
+
if (!req.url || req.method !== 'GET') return
|
|
155
|
+
|
|
156
|
+
await publicHandler(req, res, ctx)
|
|
157
|
+
await distHandler(req, res, ctx)
|
|
158
|
+
},
|
|
159
|
+
}).listen()
|
|
160
|
+
`);
|
|
161
|
+
writeFile((0, node_path.join)(rootPath, "src", "faas.yaml"), `defaults:
|
|
162
|
+
server:
|
|
163
|
+
root: .
|
|
164
|
+
base: /
|
|
39
165
|
plugins:
|
|
166
|
+
http:
|
|
167
|
+
config:
|
|
168
|
+
cookie:
|
|
169
|
+
secure: false
|
|
170
|
+
session:
|
|
171
|
+
secret: secret
|
|
172
|
+
knex:
|
|
173
|
+
config:
|
|
174
|
+
client: pg
|
|
175
|
+
pool:
|
|
176
|
+
min: 0
|
|
177
|
+
max: 10
|
|
178
|
+
migrations:
|
|
179
|
+
directory: ./src/db/migrations
|
|
180
|
+
extension: ts
|
|
40
181
|
development:
|
|
182
|
+
plugins:
|
|
183
|
+
knex:
|
|
184
|
+
config:
|
|
185
|
+
client: pglite
|
|
186
|
+
connection: ./.pglite_dev
|
|
41
187
|
testing:
|
|
42
|
-
|
|
188
|
+
plugins:
|
|
189
|
+
knex:
|
|
190
|
+
config:
|
|
191
|
+
client: pglite
|
|
43
192
|
production:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
193
|
+
plugins:
|
|
194
|
+
knex:
|
|
195
|
+
config:
|
|
196
|
+
client: pg
|
|
197
|
+
`);
|
|
198
|
+
writeFile((0, node_path.join)(rootPath, "src", "db", "migrations", ".gitkeep"), "");
|
|
199
|
+
writeFile((0, node_path.join)(rootPath, "src", "main.tsx"), `import { createRoot } from 'react-dom/client'
|
|
200
|
+
import HomePage from './pages/home'
|
|
201
|
+
|
|
202
|
+
createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
|
|
203
|
+
`);
|
|
204
|
+
writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "index.tsx"), `import { useState } from 'react'
|
|
205
|
+
|
|
206
|
+
type ApiResponse = {
|
|
207
|
+
ok: boolean
|
|
208
|
+
data: string
|
|
209
|
+
error: null | {
|
|
210
|
+
code?: string
|
|
211
|
+
message: string
|
|
61
212
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default function HomePage() {
|
|
216
|
+
const [message, setMessage] = useState('Click button to call API')
|
|
217
|
+
const [loading, setLoading] = useState(false)
|
|
218
|
+
|
|
219
|
+
const fetchMessage = async () => {
|
|
220
|
+
setLoading(true)
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const data = await fetch('/home/api/hello', {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: {
|
|
226
|
+
'Content-Type': 'application/json',
|
|
227
|
+
},
|
|
228
|
+
body: JSON.stringify({ name: 'world' }),
|
|
229
|
+
}).then(res => res.json() as Promise<ApiResponse>)
|
|
230
|
+
|
|
231
|
+
if (data.ok) setMessage(data.data)
|
|
232
|
+
else setMessage(data.error?.message || 'Unknown error')
|
|
233
|
+
} catch (error: any) {
|
|
234
|
+
setMessage(error?.message || 'Unknown error')
|
|
235
|
+
} finally {
|
|
236
|
+
setLoading(false)
|
|
237
|
+
}
|
|
74
238
|
}
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
|
|
242
|
+
<h1>FaasJS Starter</h1>
|
|
243
|
+
<p>{message}</p>
|
|
244
|
+
<button type="button" onClick={fetchMessage} disabled={loading}>
|
|
245
|
+
{loading ? 'Loading...' : 'Call /home/api/hello'}
|
|
246
|
+
</button>
|
|
247
|
+
</main>
|
|
248
|
+
)
|
|
75
249
|
}
|
|
76
|
-
`
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"editor.codeActionsOnSave": {
|
|
94
|
-
"source.fixAll": true
|
|
250
|
+
`);
|
|
251
|
+
writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "api", "hello.func.ts"), `import { defineApi, z } from '@faasjs/core'
|
|
252
|
+
|
|
253
|
+
const schema = z
|
|
254
|
+
.object({
|
|
255
|
+
name: z.string().optional(),
|
|
256
|
+
})
|
|
257
|
+
.required()
|
|
258
|
+
|
|
259
|
+
export const func = defineApi({
|
|
260
|
+
schema,
|
|
261
|
+
async handler({ params }) {
|
|
262
|
+
return {
|
|
263
|
+
ok: true,
|
|
264
|
+
data: \`Hello, \${params.name || 'FaasJS'}\`,
|
|
265
|
+
error: null,
|
|
266
|
+
}
|
|
95
267
|
},
|
|
96
|
-
"editor.wordWrap": "on",
|
|
97
|
-
"files.insertFinalNewline": true,
|
|
98
|
-
"files.trimFinalNewlines": true,
|
|
99
|
-
"files.trimTrailingWhitespace": true
|
|
100
|
-
}
|
|
101
|
-
`
|
|
102
|
-
);
|
|
103
|
-
fs.writeFileSync(
|
|
104
|
-
path.join(answers.name, ".vscode", "extensions.json"),
|
|
105
|
-
`{
|
|
106
|
-
"recommendations": [
|
|
107
|
-
"faasjs.faasjs-snippets"
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
`
|
|
111
|
-
);
|
|
112
|
-
child_process.execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
113
|
-
fs.writeFileSync(
|
|
114
|
-
path.join(answers.name, "index.func.ts"),
|
|
115
|
-
`import { useFunc } from '@faasjs/func'
|
|
116
|
-
import { useHttp } from '@faasjs/http'
|
|
117
|
-
|
|
118
|
-
export const func = useFunc(() => {
|
|
119
|
-
const http = useHttp<{ name: string }>()
|
|
120
|
-
|
|
121
|
-
return async () => \`Hello, \${http.params.name}\`
|
|
122
268
|
})
|
|
123
|
-
`
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
`import { test } from '@faasjs/test'
|
|
129
|
-
import { func } from '../index.func'
|
|
130
|
-
|
|
131
|
-
describe('hello', () => {
|
|
269
|
+
`);
|
|
270
|
+
writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"), `import { test } from '@faasjs/dev'
|
|
271
|
+
import { func } from '../hello.func'
|
|
272
|
+
|
|
273
|
+
describe('home/api/hello', () => {
|
|
132
274
|
it('should work', async () => {
|
|
133
275
|
const testFunc = test(func)
|
|
134
276
|
|
|
135
|
-
const { statusCode, data } = await testFunc.JSONhandler
|
|
277
|
+
const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
|
|
136
278
|
|
|
137
279
|
expect(statusCode).toEqual(200)
|
|
138
|
-
expect(data).toEqual(
|
|
280
|
+
expect(data).toEqual({
|
|
281
|
+
ok: true,
|
|
282
|
+
data: 'Hello, world',
|
|
283
|
+
error: null,
|
|
284
|
+
})
|
|
139
285
|
})
|
|
140
286
|
})
|
|
141
|
-
`
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
287
|
+
`);
|
|
288
|
+
}
|
|
289
|
+
async function action(options = {}) {
|
|
290
|
+
const answers = Object.assign(options, {});
|
|
291
|
+
if (!options.name || Validator.name(options.name) !== true) answers.name = await prompt({
|
|
292
|
+
type: "input",
|
|
293
|
+
name: "value",
|
|
294
|
+
message: "Project name",
|
|
295
|
+
initial: "faasjs",
|
|
296
|
+
validate: Validator.name
|
|
297
|
+
}).then((res) => res.value);
|
|
298
|
+
if (!answers.name) return;
|
|
299
|
+
const runtime = process.versions.bun ? "bun" : "npm";
|
|
300
|
+
(0, node_fs.mkdirSync)(answers.name);
|
|
301
|
+
(0, node_fs.writeFileSync)((0, node_path.join)(answers.name, "package.json"), buildPackageJSON(answers.name));
|
|
302
|
+
scaffold(answers.name);
|
|
303
|
+
(0, node_child_process.execSync)(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
304
|
+
if (runtime === "bun") (0, node_child_process.execSync)(`cd ${answers.name} && bun test`, { stdio: "inherit" });
|
|
305
|
+
else (0, node_child_process.execSync)(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
|
|
146
306
|
}
|
|
147
307
|
function action_default(program) {
|
|
148
|
-
|
|
308
|
+
program.description("Create a new faas app").on("--help", () => console.log("Examples:\nnpx create-faas-app")).option("--name <name>", "Project name").action(action);
|
|
149
309
|
}
|
|
150
310
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/index.ts
|
|
313
|
+
/**
|
|
314
|
+
* [](https://github.com/faasjs/faasjs/blob/main/packages/create-faas-app/LICENSE)
|
|
315
|
+
* [](https://www.npmjs.com/package/create-faas-app)
|
|
316
|
+
*
|
|
317
|
+
* Quick way to create a FaasJS project.
|
|
318
|
+
*
|
|
319
|
+
* ## Usage
|
|
320
|
+
*
|
|
321
|
+
* ```bash
|
|
322
|
+
* # use npm
|
|
323
|
+
* npx create-faas-app --name faasjs
|
|
324
|
+
*
|
|
325
|
+
* # use bun
|
|
326
|
+
* bunx create-faas-app --name faasjs
|
|
327
|
+
* ```
|
|
328
|
+
*
|
|
329
|
+
* @packageDocumentation
|
|
330
|
+
*/
|
|
331
|
+
const commander$1 = new commander.Command();
|
|
332
|
+
commander$1.storeOptionsAsProperties(false).allowUnknownOption(true).version(version).name("create-faas-app").exitOverride();
|
|
333
|
+
action_default(commander$1);
|
|
155
334
|
async function main(argv) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
335
|
+
try {
|
|
336
|
+
await commander$1.parseAsync(argv);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "commander.helpDisplayed") return commander$1;
|
|
339
|
+
console.error(error);
|
|
340
|
+
}
|
|
341
|
+
return commander$1;
|
|
162
342
|
}
|
|
163
343
|
|
|
164
|
-
|
|
344
|
+
//#endregion
|
|
345
|
+
exports.main = main;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* [](https://github.com/faasjs/faasjs/blob/main/packages/create-faas-app/LICENSE)
|
|
5
|
-
* [](https://www.npmjs.com/package/create-faas-app)
|
|
6
|
-
*
|
|
7
|
-
* Quick way to create a FaasJS project.
|
|
8
|
-
*
|
|
9
|
-
* ## Usage
|
|
10
|
-
*
|
|
11
|
-
* ```bash
|
|
12
|
-
* # use npm
|
|
13
|
-
* npx create-faas-app --name faasjs
|
|
14
|
-
*
|
|
15
|
-
* # use bun
|
|
16
|
-
* bunx create-faas-app --name faasjs
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @packageDocumentation
|
|
20
|
-
*/
|
|
1
|
+
import { Command } from "commander";
|
|
21
2
|
|
|
3
|
+
//#region src/index.d.ts
|
|
22
4
|
declare function main(argv: string[]): Promise<Command>;
|
|
23
|
-
|
|
24
|
-
export { main };
|
|
5
|
+
//#endregion
|
|
6
|
+
export { main };
|
package/dist/index.mjs
CHANGED
|
@@ -1,162 +1,316 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { execSync } from
|
|
3
|
-
import { mkdirSync, writeFileSync
|
|
4
|
-
import { join } from
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import enquirer from "enquirer";
|
|
6
|
+
|
|
7
|
+
//#region package.json
|
|
8
|
+
var version = "8.0.0-beta.9";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/action.ts
|
|
12
|
+
const prompt = enquirer.prompt;
|
|
13
|
+
const Validator = { name(input) {
|
|
14
|
+
const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
|
|
15
|
+
if (match !== true) return match;
|
|
16
|
+
if (existsSync(input)) return `${input} folder exists, please try another name`;
|
|
17
|
+
return true;
|
|
18
|
+
} };
|
|
19
|
+
function writeFile(path, content) {
|
|
20
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
21
|
+
writeFileSync(path, content);
|
|
22
|
+
}
|
|
23
|
+
function buildPackageJSON(name) {
|
|
24
|
+
return `${JSON.stringify({
|
|
25
|
+
name,
|
|
26
|
+
private: true,
|
|
27
|
+
type: "module",
|
|
28
|
+
version: "1.0.0",
|
|
29
|
+
scripts: {
|
|
30
|
+
dev: "vite",
|
|
31
|
+
build: "vite build",
|
|
32
|
+
start: "node server.ts",
|
|
33
|
+
check: "faas lint",
|
|
34
|
+
test: "vitest run",
|
|
35
|
+
"migrate:latest": "faas knex latest",
|
|
36
|
+
"migrate:rollback": "faas knex rollback",
|
|
37
|
+
"migrate:status": "faas knex status",
|
|
38
|
+
"migrate:current": "faas knex current",
|
|
39
|
+
"migrate:make": "faas knex make"
|
|
40
|
+
},
|
|
41
|
+
dependencies: {
|
|
42
|
+
"@faasjs/core": "*",
|
|
43
|
+
pg: "*",
|
|
44
|
+
react: "*",
|
|
45
|
+
"react-dom": "*"
|
|
46
|
+
},
|
|
47
|
+
devDependencies: {
|
|
48
|
+
"@electric-sql/pglite": "*",
|
|
49
|
+
"@faasjs/dev": "*",
|
|
50
|
+
"@types/node": "*",
|
|
51
|
+
"@types/react": "*",
|
|
52
|
+
"@types/react-dom": "*",
|
|
53
|
+
"@vitejs/plugin-react": "*",
|
|
54
|
+
jsdom: "*",
|
|
55
|
+
"knex-pglite": "*",
|
|
56
|
+
oxlint: "*",
|
|
57
|
+
typescript: "*",
|
|
58
|
+
vite: "*",
|
|
59
|
+
vitest: "*"
|
|
60
|
+
}
|
|
61
|
+
}, null, 2)}
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
function scaffold(rootPath) {
|
|
65
|
+
writeFile(join(rootPath, ".gitignore"), `node_modules/
|
|
66
|
+
dist/
|
|
67
|
+
coverage/
|
|
68
|
+
.pglite_dev/
|
|
69
|
+
`);
|
|
70
|
+
writeFile(join(rootPath, "tsconfig.json"), `{
|
|
71
|
+
"compilerOptions": {
|
|
72
|
+
"target": "ES2022",
|
|
73
|
+
"module": "ESNext",
|
|
74
|
+
"moduleResolution": "Bundler",
|
|
75
|
+
"jsx": "react-jsx",
|
|
76
|
+
"strict": true,
|
|
77
|
+
"types": ["vitest/globals"]
|
|
78
|
+
},
|
|
79
|
+
"include": ["src", "vite.config.ts", "server.ts"]
|
|
80
|
+
}
|
|
81
|
+
`);
|
|
82
|
+
writeFile(join(rootPath, "index.html"), `<!doctype html>
|
|
83
|
+
<html lang="en">
|
|
84
|
+
<head>
|
|
85
|
+
<meta charset="UTF-8" />
|
|
86
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
87
|
+
<title>FaasJS App</title>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<div id="root"></div>
|
|
91
|
+
<script type="module" src="/src/main.tsx"><\/script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>
|
|
94
|
+
`);
|
|
95
|
+
writeFile(join(rootPath, "vite.config.ts"), `import { viteFaasJsServer } from '@faasjs/dev'
|
|
96
|
+
import react from '@vitejs/plugin-react'
|
|
97
|
+
import { defineConfig } from 'vite'
|
|
98
|
+
|
|
99
|
+
export default defineConfig({
|
|
100
|
+
server: {
|
|
101
|
+
host: '0.0.0.0',
|
|
102
|
+
},
|
|
103
|
+
plugins: [react(), viteFaasJsServer()],
|
|
104
|
+
})
|
|
105
|
+
`);
|
|
106
|
+
writeFile(join(rootPath, "server.ts"), `import { dirname, join } from 'node:path'
|
|
107
|
+
import { fileURLToPath } from 'node:url'
|
|
108
|
+
import { Server, staticHandler } from '@faasjs/core'
|
|
109
|
+
|
|
110
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
111
|
+
const __dirname = dirname(__filename)
|
|
112
|
+
|
|
113
|
+
const publicHandler = staticHandler({
|
|
114
|
+
root: join(__dirname, 'public'),
|
|
115
|
+
notFound: false,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const distHandler = staticHandler({
|
|
119
|
+
root: join(__dirname, 'dist'),
|
|
120
|
+
notFound: 'index.html',
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
new Server(join(__dirname, 'src'), {
|
|
124
|
+
beforeHandle: async (req, res, ctx) => {
|
|
125
|
+
if (!req.url || req.method !== 'GET') return
|
|
126
|
+
|
|
127
|
+
await publicHandler(req, res, ctx)
|
|
128
|
+
await distHandler(req, res, ctx)
|
|
129
|
+
},
|
|
130
|
+
}).listen()
|
|
131
|
+
`);
|
|
132
|
+
writeFile(join(rootPath, "src", "faas.yaml"), `defaults:
|
|
133
|
+
server:
|
|
134
|
+
root: .
|
|
135
|
+
base: /
|
|
37
136
|
plugins:
|
|
137
|
+
http:
|
|
138
|
+
config:
|
|
139
|
+
cookie:
|
|
140
|
+
secure: false
|
|
141
|
+
session:
|
|
142
|
+
secret: secret
|
|
143
|
+
knex:
|
|
144
|
+
config:
|
|
145
|
+
client: pg
|
|
146
|
+
pool:
|
|
147
|
+
min: 0
|
|
148
|
+
max: 10
|
|
149
|
+
migrations:
|
|
150
|
+
directory: ./src/db/migrations
|
|
151
|
+
extension: ts
|
|
38
152
|
development:
|
|
153
|
+
plugins:
|
|
154
|
+
knex:
|
|
155
|
+
config:
|
|
156
|
+
client: pglite
|
|
157
|
+
connection: ./.pglite_dev
|
|
39
158
|
testing:
|
|
40
|
-
|
|
159
|
+
plugins:
|
|
160
|
+
knex:
|
|
161
|
+
config:
|
|
162
|
+
client: pglite
|
|
41
163
|
production:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
164
|
+
plugins:
|
|
165
|
+
knex:
|
|
166
|
+
config:
|
|
167
|
+
client: pg
|
|
168
|
+
`);
|
|
169
|
+
writeFile(join(rootPath, "src", "db", "migrations", ".gitkeep"), "");
|
|
170
|
+
writeFile(join(rootPath, "src", "main.tsx"), `import { createRoot } from 'react-dom/client'
|
|
171
|
+
import HomePage from './pages/home'
|
|
172
|
+
|
|
173
|
+
createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
|
|
174
|
+
`);
|
|
175
|
+
writeFile(join(rootPath, "src", "pages", "home", "index.tsx"), `import { useState } from 'react'
|
|
176
|
+
|
|
177
|
+
type ApiResponse = {
|
|
178
|
+
ok: boolean
|
|
179
|
+
data: string
|
|
180
|
+
error: null | {
|
|
181
|
+
code?: string
|
|
182
|
+
message: string
|
|
59
183
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default function HomePage() {
|
|
187
|
+
const [message, setMessage] = useState('Click button to call API')
|
|
188
|
+
const [loading, setLoading] = useState(false)
|
|
189
|
+
|
|
190
|
+
const fetchMessage = async () => {
|
|
191
|
+
setLoading(true)
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const data = await fetch('/home/api/hello', {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: {
|
|
197
|
+
'Content-Type': 'application/json',
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({ name: 'world' }),
|
|
200
|
+
}).then(res => res.json() as Promise<ApiResponse>)
|
|
201
|
+
|
|
202
|
+
if (data.ok) setMessage(data.data)
|
|
203
|
+
else setMessage(data.error?.message || 'Unknown error')
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
setMessage(error?.message || 'Unknown error')
|
|
206
|
+
} finally {
|
|
207
|
+
setLoading(false)
|
|
208
|
+
}
|
|
72
209
|
}
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
|
|
213
|
+
<h1>FaasJS Starter</h1>
|
|
214
|
+
<p>{message}</p>
|
|
215
|
+
<button type="button" onClick={fetchMessage} disabled={loading}>
|
|
216
|
+
{loading ? 'Loading...' : 'Call /home/api/hello'}
|
|
217
|
+
</button>
|
|
218
|
+
</main>
|
|
219
|
+
)
|
|
73
220
|
}
|
|
74
|
-
`
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"editor.codeActionsOnSave": {
|
|
92
|
-
"source.fixAll": true
|
|
221
|
+
`);
|
|
222
|
+
writeFile(join(rootPath, "src", "pages", "home", "api", "hello.func.ts"), `import { defineApi, z } from '@faasjs/core'
|
|
223
|
+
|
|
224
|
+
const schema = z
|
|
225
|
+
.object({
|
|
226
|
+
name: z.string().optional(),
|
|
227
|
+
})
|
|
228
|
+
.required()
|
|
229
|
+
|
|
230
|
+
export const func = defineApi({
|
|
231
|
+
schema,
|
|
232
|
+
async handler({ params }) {
|
|
233
|
+
return {
|
|
234
|
+
ok: true,
|
|
235
|
+
data: \`Hello, \${params.name || 'FaasJS'}\`,
|
|
236
|
+
error: null,
|
|
237
|
+
}
|
|
93
238
|
},
|
|
94
|
-
"editor.wordWrap": "on",
|
|
95
|
-
"files.insertFinalNewline": true,
|
|
96
|
-
"files.trimFinalNewlines": true,
|
|
97
|
-
"files.trimTrailingWhitespace": true
|
|
98
|
-
}
|
|
99
|
-
`
|
|
100
|
-
);
|
|
101
|
-
writeFileSync(
|
|
102
|
-
join(answers.name, ".vscode", "extensions.json"),
|
|
103
|
-
`{
|
|
104
|
-
"recommendations": [
|
|
105
|
-
"faasjs.faasjs-snippets"
|
|
106
|
-
]
|
|
107
|
-
}
|
|
108
|
-
`
|
|
109
|
-
);
|
|
110
|
-
execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
111
|
-
writeFileSync(
|
|
112
|
-
join(answers.name, "index.func.ts"),
|
|
113
|
-
`import { useFunc } from '@faasjs/func'
|
|
114
|
-
import { useHttp } from '@faasjs/http'
|
|
115
|
-
|
|
116
|
-
export const func = useFunc(() => {
|
|
117
|
-
const http = useHttp<{ name: string }>()
|
|
118
|
-
|
|
119
|
-
return async () => \`Hello, \${http.params.name}\`
|
|
120
239
|
})
|
|
121
|
-
`
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
`import { test } from '@faasjs/test'
|
|
127
|
-
import { func } from '../index.func'
|
|
128
|
-
|
|
129
|
-
describe('hello', () => {
|
|
240
|
+
`);
|
|
241
|
+
writeFile(join(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"), `import { test } from '@faasjs/dev'
|
|
242
|
+
import { func } from '../hello.func'
|
|
243
|
+
|
|
244
|
+
describe('home/api/hello', () => {
|
|
130
245
|
it('should work', async () => {
|
|
131
246
|
const testFunc = test(func)
|
|
132
247
|
|
|
133
|
-
const { statusCode, data } = await testFunc.JSONhandler
|
|
248
|
+
const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
|
|
134
249
|
|
|
135
250
|
expect(statusCode).toEqual(200)
|
|
136
|
-
expect(data).toEqual(
|
|
251
|
+
expect(data).toEqual({
|
|
252
|
+
ok: true,
|
|
253
|
+
data: 'Hello, world',
|
|
254
|
+
error: null,
|
|
255
|
+
})
|
|
137
256
|
})
|
|
138
257
|
})
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
258
|
+
`);
|
|
259
|
+
}
|
|
260
|
+
async function action(options = {}) {
|
|
261
|
+
const answers = Object.assign(options, {});
|
|
262
|
+
if (!options.name || Validator.name(options.name) !== true) answers.name = await prompt({
|
|
263
|
+
type: "input",
|
|
264
|
+
name: "value",
|
|
265
|
+
message: "Project name",
|
|
266
|
+
initial: "faasjs",
|
|
267
|
+
validate: Validator.name
|
|
268
|
+
}).then((res) => res.value);
|
|
269
|
+
if (!answers.name) return;
|
|
270
|
+
const runtime = process.versions.bun ? "bun" : "npm";
|
|
271
|
+
mkdirSync(answers.name);
|
|
272
|
+
writeFileSync(join(answers.name, "package.json"), buildPackageJSON(answers.name));
|
|
273
|
+
scaffold(answers.name);
|
|
274
|
+
execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
|
|
275
|
+
if (runtime === "bun") execSync(`cd ${answers.name} && bun test`, { stdio: "inherit" });
|
|
276
|
+
else execSync(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
|
|
144
277
|
}
|
|
145
278
|
function action_default(program) {
|
|
146
|
-
|
|
279
|
+
program.description("Create a new faas app").on("--help", () => console.log("Examples:\nnpx create-faas-app")).option("--name <name>", "Project name").action(action);
|
|
147
280
|
}
|
|
148
281
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/index.ts
|
|
284
|
+
/**
|
|
285
|
+
* [](https://github.com/faasjs/faasjs/blob/main/packages/create-faas-app/LICENSE)
|
|
286
|
+
* [](https://www.npmjs.com/package/create-faas-app)
|
|
287
|
+
*
|
|
288
|
+
* Quick way to create a FaasJS project.
|
|
289
|
+
*
|
|
290
|
+
* ## Usage
|
|
291
|
+
*
|
|
292
|
+
* ```bash
|
|
293
|
+
* # use npm
|
|
294
|
+
* npx create-faas-app --name faasjs
|
|
295
|
+
*
|
|
296
|
+
* # use bun
|
|
297
|
+
* bunx create-faas-app --name faasjs
|
|
298
|
+
* ```
|
|
299
|
+
*
|
|
300
|
+
* @packageDocumentation
|
|
301
|
+
*/
|
|
302
|
+
const commander = new Command();
|
|
303
|
+
commander.storeOptionsAsProperties(false).allowUnknownOption(true).version(version).name("create-faas-app").exitOverride();
|
|
152
304
|
action_default(commander);
|
|
153
305
|
async function main(argv) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
306
|
+
try {
|
|
307
|
+
await commander.parseAsync(argv);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "commander.helpDisplayed") return commander;
|
|
310
|
+
console.error(error);
|
|
311
|
+
}
|
|
312
|
+
return commander;
|
|
160
313
|
}
|
|
161
314
|
|
|
162
|
-
|
|
315
|
+
//#endregion
|
|
316
|
+
export { main };
|
package/package.json
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-faas-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-beta.10",
|
|
4
|
+
"homepage": "https://faasjs.com/doc/create-faas-app",
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/faasjs/faasjs/issues"
|
|
7
|
+
},
|
|
4
8
|
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/faasjs/faasjs.git",
|
|
12
|
+
"directory": "packages/create-faas-app"
|
|
13
|
+
},
|
|
14
|
+
"funding": "https://github.com/sponsors/faasjs",
|
|
15
|
+
"bin": {
|
|
16
|
+
"create-faas-app": "index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"index.js"
|
|
21
|
+
],
|
|
5
22
|
"type": "module",
|
|
6
23
|
"main": "dist/index.cjs",
|
|
7
24
|
"module": "dist/index.mjs",
|
|
8
25
|
"types": "dist/index.d.ts",
|
|
9
|
-
"bin": {
|
|
10
|
-
"create-faas-app": "index.mjs"
|
|
11
|
-
},
|
|
12
26
|
"exports": {
|
|
13
27
|
".": {
|
|
14
28
|
"types": "./dist/index.d.ts",
|
|
@@ -16,23 +30,9 @@
|
|
|
16
30
|
"require": "./dist/index.cjs"
|
|
17
31
|
}
|
|
18
32
|
},
|
|
19
|
-
"homepage": "https://faasjs.com/doc/create-faas-app",
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/faasjs/faasjs.git",
|
|
23
|
-
"directory": "packages/create-faas-app"
|
|
24
|
-
},
|
|
25
|
-
"bugs": {
|
|
26
|
-
"url": "https://github.com/faasjs/faasjs/issues"
|
|
27
|
-
},
|
|
28
|
-
"funding": "https://github.com/sponsors/faasjs",
|
|
29
33
|
"scripts": {
|
|
30
|
-
"build": "
|
|
34
|
+
"build": "tsdown src/index.ts --config ../../tsdown.config.ts"
|
|
31
35
|
},
|
|
32
|
-
"files": [
|
|
33
|
-
"dist",
|
|
34
|
-
"index.js"
|
|
35
|
-
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"commander": ">=14.0.0",
|
|
38
38
|
"enquirer": "*"
|