create-faas-app 8.0.0-beta.19 → 8.0.0-beta.20

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.mjs CHANGED
@@ -1,224 +1,51 @@
1
1
  import { Command } from "commander";
2
2
  import { execSync } from "node:child_process";
3
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import enquirer from "enquirer";
6
7
  //#region package.json
7
- var version = "8.0.0-beta.18";
8
+ var version = "8.0.0-beta.19";
8
9
  //#endregion
9
10
  //#region src/action.ts
10
11
  const prompt = enquirer.prompt;
11
12
  const validateName = (input) => Validator.name(input);
13
+ const templateRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "template");
12
14
  const Validator = { name(input) {
13
15
  const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
14
16
  if (match !== true) return match;
15
17
  if (existsSync(input)) return `${input} folder exists, please try another name`;
16
18
  return true;
17
19
  } };
18
- function writeFile(path, content) {
19
- mkdirSync(dirname(path), { recursive: true });
20
- writeFileSync(path, content);
20
+ function getTemplateNames() {
21
+ return readdirSync(templateRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
21
22
  }
22
- function buildPackageJSON(name) {
23
- return `${JSON.stringify({
24
- name,
25
- private: true,
26
- type: "module",
27
- version: "1.0.0",
28
- scripts: {
29
- dev: "node --import @faasjs/node-utils/register-hooks vite",
30
- build: "vite build",
31
- start: "node --import @faasjs/node-utils/register-hooks server.ts",
32
- test: "vitest run"
33
- },
34
- dependencies: {
35
- "@faasjs/core": "*",
36
- react: "*",
37
- "react-dom": "*"
38
- },
39
- devDependencies: {
40
- "@faasjs/dev": "*",
41
- "@types/node": "*",
42
- "@types/react": "*",
43
- "@types/react-dom": "*",
44
- "@vitejs/plugin-react": "*",
45
- jsdom: "*",
46
- oxfmt: "*",
47
- oxlint: "*",
48
- typescript: "*",
49
- vite: "*",
50
- vitest: "*"
51
- }
52
- }, null, 2)}
53
- `;
23
+ function resolveTemplateName(template = "basic") {
24
+ const templates = getTemplateNames();
25
+ if (templates.includes(template)) return template;
26
+ throw new Error(`Unknown template "${template}". Available templates: ${templates.join(", ")}`);
54
27
  }
55
- function scaffold(rootPath) {
56
- writeFile(join(rootPath, ".gitignore"), `node_modules/
57
- dist/
58
- coverage/
59
- `);
60
- writeFile(join(rootPath, "tsconfig.json"), `{
61
- "compilerOptions": {
62
- "target": "ES2022",
63
- "module": "ESNext",
64
- "moduleResolution": "Bundler",
65
- "jsx": "react-jsx",
66
- "strict": true,
67
- "types": ["vitest/globals"]
68
- },
69
- "include": ["src", "vite.config.ts", "server.ts"]
28
+ function renderTemplate(content, replacements) {
29
+ return Object.entries(replacements).reduce((result, [key, value]) => result.replaceAll(`{{${key}}}`, value), content);
70
30
  }
71
- `);
72
- writeFile(join(rootPath, "index.html"), `<!doctype html>
73
- <html lang="en">
74
- <head>
75
- <meta charset="UTF-8" />
76
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
- <title>FaasJS App</title>
78
- </head>
79
- <body>
80
- <div id="root"></div>
81
- <script type="module" src="/src/main.tsx"><\/script>
82
- </body>
83
- </html>
84
- `);
85
- writeFile(join(rootPath, "vite.config.ts"), `import { viteFaasJsServer } from '@faasjs/dev'
86
- import react from '@vitejs/plugin-react'
87
- import { defineConfig } from 'vite'
88
-
89
- export default defineConfig({
90
- server: {
91
- host: '0.0.0.0',
92
- },
93
- plugins: [react(), viteFaasJsServer()],
94
- })
95
- `);
96
- writeFile(join(rootPath, "server.ts"), `import { dirname, join } from 'node:path'
97
- import { fileURLToPath } from 'node:url'
98
- import { Server, staticHandler } from '@faasjs/core'
99
-
100
- const __filename = fileURLToPath(import.meta.url)
101
- const __dirname = dirname(__filename)
102
-
103
- const publicHandler = staticHandler({
104
- root: join(__dirname, 'public'),
105
- notFound: false,
106
- })
107
-
108
- const distHandler = staticHandler({
109
- root: join(__dirname, 'dist'),
110
- notFound: 'index.html',
111
- })
112
-
113
- new Server(join(__dirname, 'src'), {
114
- beforeHandle: async (req, res, ctx) => {
115
- if (!req.url || req.method !== 'GET') return
116
-
117
- await publicHandler(req, res, ctx)
118
- await distHandler(req, res, ctx)
119
- },
120
- }).listen()
121
- `);
122
- writeFile(join(rootPath, "src", "faas.yaml"), `defaults:
123
- server:
124
- root: .
125
- base: /
126
- plugins:
127
- http:
128
- config:
129
- cookie:
130
- secure: false
131
- session:
132
- secret: secret
133
- `);
134
- writeFile(join(rootPath, "src", "main.tsx"), `import { createRoot } from 'react-dom/client'
135
- import HomePage from './pages/home'
136
-
137
- createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
138
- `);
139
- writeFile(join(rootPath, "src", "pages", "home", "index.tsx"), `import { useState } from 'react'
140
-
141
- type ApiResponse = {
142
- ok: boolean
143
- data: string
144
- error: null | {
145
- code?: string
146
- message: string
147
- }
148
- }
149
-
150
- export default function HomePage() {
151
- const [message, setMessage] = useState('Click button to call API')
152
- const [loading, setLoading] = useState(false)
153
-
154
- const fetchMessage = async () => {
155
- setLoading(true)
156
-
157
- try {
158
- const data = await fetch('/pages/home/api/hello', {
159
- method: 'POST',
160
- headers: {
161
- 'Content-Type': 'application/json',
162
- },
163
- body: JSON.stringify({ name: 'world' }),
164
- }).then(res => res.json() as Promise<ApiResponse>)
165
-
166
- if (data.ok) setMessage(data.data)
167
- else setMessage(data.error?.message || 'Unknown error')
168
- } catch (error: any) {
169
- setMessage(error?.message || 'Unknown error')
170
- } finally {
171
- setLoading(false)
172
- }
173
- }
174
-
175
- return (
176
- <main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
177
- <h1>FaasJS App</h1>
178
- <p>{message}</p>
179
- <button type="button" onClick={fetchMessage} disabled={loading}>
180
- {loading ? 'Loading...' : 'Call /pages/home/api/hello'}
181
- </button>
182
- </main>
183
- )
31
+ function copyTemplateDirectory(sourcePath, targetPath, replacements) {
32
+ mkdirSync(targetPath, { recursive: true });
33
+ for (const entry of readdirSync(sourcePath, { withFileTypes: true })) {
34
+ const nextSourcePath = join(sourcePath, entry.name);
35
+ const nextTargetPath = join(targetPath, entry.name === "gitignore" ? ".gitignore" : entry.name);
36
+ if (entry.isDirectory()) {
37
+ copyTemplateDirectory(nextSourcePath, nextTargetPath, replacements);
38
+ continue;
39
+ }
40
+ writeFileSync(nextTargetPath, renderTemplate(readFileSync(nextSourcePath, "utf8"), replacements));
41
+ }
184
42
  }
185
- `);
186
- writeFile(join(rootPath, "src", "pages", "home", "api", "hello.func.ts"), `import { defineApi, z } from '@faasjs/core'
187
-
188
- export const func = defineApi({
189
- schema: z
190
- .object({
191
- name: z.string().optional(),
192
- }),
193
- async handler({ params }) {
194
- return {
195
- ok: true,
196
- data: \`Hello, \${params.name || 'FaasJS'}\`,
197
- error: null,
198
- }
199
- },
200
- })
201
- `);
202
- writeFile(join(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"), `import { test } from '@faasjs/dev'
203
- import { func } from '../hello.func'
204
-
205
- describe('pages/home/api/hello', () => {
206
- it('should work', async () => {
207
- const testFunc = test(func)
208
-
209
- const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
210
-
211
- expect(statusCode).toEqual(200)
212
- expect(data).toEqual({
213
- ok: true,
214
- data: 'Hello, world',
215
- error: null,
216
- })
217
- })
218
- })
219
- `);
43
+ function scaffold(rootPath, replacements, templateName) {
44
+ mkdirSync(rootPath);
45
+ copyTemplateDirectory(join(templateRoot, templateName), rootPath, replacements);
220
46
  }
221
47
  async function action(options = {}) {
48
+ const templateName = resolveTemplateName(options.template);
222
49
  const answers = Object.assign(options, {});
223
50
  if (!options.name || Validator.name(options.name) !== true) answers.name = await prompt({
224
51
  type: "input",
@@ -229,15 +56,18 @@ async function action(options = {}) {
229
56
  }).then((res) => res.value);
230
57
  if (!answers.name) return;
231
58
  const runtime = process.versions.bun ? "bun" : "npm";
232
- mkdirSync(answers.name);
233
- writeFileSync(join(answers.name, "package.json"), buildPackageJSON(answers.name));
234
- scaffold(answers.name);
59
+ scaffold(answers.name, { name: answers.name }, templateName);
235
60
  execSync(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
236
61
  if (runtime === "bun") execSync(`cd ${answers.name} && bun test`, { stdio: "inherit" });
237
62
  else execSync(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
238
63
  }
239
64
  function action_default(program) {
240
- program.description("Create a new faas app").on("--help", () => console.log("Examples:\nnpx create-faas-app")).option("--name <name>", "Project name").action(action);
65
+ program.description("Create a new faas app").on("--help", () => console.log(`Examples:
66
+ npx create-faas-app --name faasjs
67
+ npx create-faas-app --name faasjs-admin --template antd
68
+
69
+ Templates:
70
+ ${getTemplateNames().join(", ")}`)).option("--name <name>", "Project name").option("--template <template>", "Template name", "basic").action(action);
241
71
  }
242
72
  //#endregion
243
73
  //#region src/index.ts
@@ -252,9 +82,11 @@ function action_default(program) {
252
82
  * ```bash
253
83
  * # use npm
254
84
  * npx create-faas-app --name faasjs
85
+ * npx create-faas-app --name faasjs-admin --template antd
255
86
  *
256
87
  * # use bun
257
88
  * bunx create-faas-app --name faasjs
89
+ * bunx create-faas-app --name faasjs-admin --template antd
258
90
  * ```
259
91
  */
260
92
  const commander = new Command();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-faas-app",
3
- "version": "8.0.0-beta.19",
3
+ "version": "8.0.0-beta.20",
4
4
  "homepage": "https://faasjs.com/doc/create-faas-app",
5
5
  "bugs": {
6
6
  "url": "https://github.com/faasjs/faasjs/issues"
@@ -17,17 +17,17 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
- "index.js"
20
+ "index.mjs",
21
+ "template"
21
22
  ],
22
23
  "type": "module",
23
- "main": "dist/index.cjs",
24
+ "main": "dist/index.mjs",
24
25
  "module": "dist/index.mjs",
25
26
  "types": "dist/index.d.ts",
26
27
  "exports": {
27
28
  ".": {
28
29
  "types": "./dist/index.d.ts",
29
- "import": "./dist/index.mjs",
30
- "require": "./dist/index.cjs"
30
+ "default": "./dist/index.mjs"
31
31
  }
32
32
  },
33
33
  "dependencies": {
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FaasJS Ant Design App</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vp dev",
8
+ "build": "vp build",
9
+ "start": "node --import @faasjs/node-utils/register-hooks server.ts",
10
+ "test": "vp test"
11
+ },
12
+ "dependencies": {
13
+ "@faasjs/ant-design": "*",
14
+ "@faasjs/core": "*"
15
+ },
16
+ "devDependencies": {
17
+ "@faasjs/dev": "*"
18
+ },
19
+ "overrides": {
20
+ "vite": "npm:@voidzero-dev/vite-plus-core",
21
+ "vitest": "npm:@voidzero-dev/vite-plus-test"
22
+ },
23
+ "engines": {
24
+ "node": ">=24.0.0",
25
+ "npm": ">=11.0.0"
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import { dirname, join } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ import { Server, staticHandler } from '@faasjs/core'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+
9
+ const publicHandler = staticHandler({
10
+ root: join(__dirname, 'public'),
11
+ notFound: false,
12
+ })
13
+
14
+ const distHandler = staticHandler({
15
+ root: join(__dirname, 'dist'),
16
+ notFound: 'index.html',
17
+ })
18
+
19
+ new Server(join(__dirname, 'src'), {
20
+ beforeHandle: async (req, res, ctx) => {
21
+ if (!req.url || req.method !== 'GET') return
22
+
23
+ await publicHandler(req, res, ctx)
24
+ await distHandler(req, res, ctx)
25
+ },
26
+ }).listen()
@@ -0,0 +1,11 @@
1
+ defaults:
2
+ server:
3
+ root: .
4
+ base: /
5
+ plugins:
6
+ http:
7
+ config:
8
+ cookie:
9
+ secure: false
10
+ session:
11
+ secret: secret
@@ -0,0 +1,25 @@
1
+ import { App } from '@faasjs/ant-design'
2
+ import { createRoot } from 'react-dom/client'
3
+
4
+ import HomePage from './pages/home'
5
+
6
+ createRoot(document.getElementById('root') as HTMLElement).render(
7
+ <App
8
+ browserRouterProps={false}
9
+ configProviderProps={{
10
+ theme: {
11
+ token: {
12
+ borderRadius: 16,
13
+ colorPrimary: '#1677ff',
14
+ },
15
+ },
16
+ }}
17
+ faasConfigProviderProps={{
18
+ faasClientOptions: {
19
+ baseUrl: '/',
20
+ },
21
+ }}
22
+ >
23
+ <HomePage />
24
+ </App>,
25
+ )
@@ -0,0 +1,17 @@
1
+ import { test } from '@faasjs/dev'
2
+ import { describe, it, expect } from 'vitest'
3
+
4
+ import { func } from '../hello.func'
5
+
6
+ describe('pages/home/api/hello', () => {
7
+ it('should work', async () => {
8
+ const wrapped = test(func)
9
+
10
+ const { statusCode, data } = await wrapped.JSONhandler({ name: 'world' })
11
+
12
+ expect(statusCode).toEqual(200)
13
+ expect(data).toEqual({
14
+ message: 'Hello, world!',
15
+ })
16
+ })
17
+ })
@@ -0,0 +1,12 @@
1
+ import { defineApi, z } from '@faasjs/core'
2
+
3
+ export const func = defineApi({
4
+ schema: z.object({
5
+ name: z.string().min(1).optional(),
6
+ }),
7
+ async handler({ params }) {
8
+ return {
9
+ message: `Hello, ${params.name || 'FaasJS'}!`,
10
+ }
11
+ },
12
+ })
@@ -0,0 +1,79 @@
1
+ import { faas, useApp } from '@faasjs/ant-design'
2
+ import { Button, Card, Input, Space, Typography } from 'antd'
3
+ import { useState } from 'react'
4
+
5
+ type HelloResponse = {
6
+ message?: string
7
+ }
8
+
9
+ export default function HomePage() {
10
+ const app = useApp()
11
+ const [name, setName] = useState('FaasJS')
12
+ const [messageText, setMessageText] = useState('Click button to call API')
13
+ const [loading, setLoading] = useState(false)
14
+
15
+ const callApi = async () => {
16
+ setLoading(true)
17
+
18
+ try {
19
+ const response = await faas('/pages/home/api/hello', {
20
+ name: name.trim() || undefined,
21
+ })
22
+ const nextMessage = (response.data as HelloResponse | undefined)?.message || 'Empty response'
23
+
24
+ setMessageText(nextMessage)
25
+ app.message.success('API call succeeded')
26
+ } catch (error: unknown) {
27
+ const errorMessage = error instanceof Error ? error.message : 'Request failed'
28
+
29
+ setMessageText(errorMessage)
30
+ app.notification.error({
31
+ message: 'API call failed',
32
+ description: errorMessage,
33
+ })
34
+ } finally {
35
+ setLoading(false)
36
+ }
37
+ }
38
+
39
+ return (
40
+ <div
41
+ style={{
42
+ minHeight: '100vh',
43
+ display: 'grid',
44
+ placeItems: 'center',
45
+ padding: 24,
46
+ background: 'linear-gradient(135deg, #f5f7fa 0%, #e4ecfb 100%)',
47
+ }}
48
+ >
49
+ <Card
50
+ style={{
51
+ width: '100%',
52
+ maxWidth: 560,
53
+ boxShadow: '0 20px 45px rgba(15, 23, 42, 0.08)',
54
+ }}
55
+ >
56
+ <Space direction="vertical" size="large" style={{ width: '100%' }}>
57
+ <div>
58
+ <Typography.Title level={2}>FaasJS Ant Design App</Typography.Title>
59
+ <Typography.Paragraph type="secondary">
60
+ Call a FaasJS API through the Ant Design app shell.
61
+ </Typography.Paragraph>
62
+ </div>
63
+
64
+ <Input
65
+ value={name}
66
+ onChange={(event) => setName(event.target.value)}
67
+ placeholder="What should the API greet?"
68
+ />
69
+
70
+ <Button type="primary" loading={loading} onClick={callApi}>
71
+ Call /pages/home/api/hello
72
+ </Button>
73
+
74
+ <Typography.Paragraph style={{ marginBottom: 0 }}>{messageText}</Typography.Paragraph>
75
+ </Space>
76
+ </Card>
77
+ </div>
78
+ )
79
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@faasjs/types/tsconfig/build.json",
3
+ "include": ["src", "vite.config.ts", "server.ts"]
4
+ }
@@ -0,0 +1,6 @@
1
+ import { viteConfig } from '@faasjs/dev'
2
+ import { defineConfig } from 'vite-plus'
3
+
4
+ export default defineConfig({
5
+ ...viteConfig,
6
+ })
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FaasJS App</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vp dev",
8
+ "build": "vp build",
9
+ "start": "node --import @faasjs/node-utils/register-hooks server.ts",
10
+ "test": "vp test"
11
+ },
12
+ "dependencies": {
13
+ "@faasjs/core": "*",
14
+ "@faasjs/react": "*"
15
+ },
16
+ "devDependencies": {
17
+ "@faasjs/dev": "*"
18
+ },
19
+ "overrides": {
20
+ "vite": "npm:@voidzero-dev/vite-plus-core",
21
+ "vitest": "npm:@voidzero-dev/vite-plus-test"
22
+ },
23
+ "engines": {
24
+ "node": ">=24.0.0",
25
+ "npm": ">=11.0.0"
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import { dirname, join } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ import { Server, staticHandler } from '@faasjs/core'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+
9
+ const publicHandler = staticHandler({
10
+ root: join(__dirname, 'public'),
11
+ notFound: false,
12
+ })
13
+
14
+ const distHandler = staticHandler({
15
+ root: join(__dirname, 'dist'),
16
+ notFound: 'index.html',
17
+ })
18
+
19
+ new Server(join(__dirname, 'src'), {
20
+ beforeHandle: async (req, res, ctx) => {
21
+ if (!req.url || req.method !== 'GET') return
22
+
23
+ await publicHandler(req, res, ctx)
24
+ await distHandler(req, res, ctx)
25
+ },
26
+ }).listen()
@@ -0,0 +1,11 @@
1
+ defaults:
2
+ server:
3
+ root: .
4
+ base: /
5
+ plugins:
6
+ http:
7
+ config:
8
+ cookie:
9
+ secure: false
10
+ session:
11
+ secret: secret
@@ -0,0 +1,5 @@
1
+ import { createRoot } from 'react-dom/client'
2
+
3
+ import HomePage from './pages/home'
4
+
5
+ createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
@@ -0,0 +1,17 @@
1
+ import { test } from '@faasjs/dev'
2
+ import { describe, it, expect } from 'vitest'
3
+
4
+ import { func } from '../hello.func'
5
+
6
+ describe('pages/home/api/hello', () => {
7
+ it('should work', async () => {
8
+ const wrapped = test(func)
9
+
10
+ const { statusCode, data } = await wrapped.JSONhandler({ name: 'world' })
11
+
12
+ expect(statusCode).toEqual(200)
13
+ expect(data).toEqual({
14
+ message: 'Hello, world!',
15
+ })
16
+ })
17
+ })
@@ -0,0 +1,12 @@
1
+ import { defineApi, z } from '@faasjs/core'
2
+
3
+ export const func = defineApi({
4
+ schema: z.object({
5
+ name: z.string().min(1).optional(),
6
+ }),
7
+ async handler({ params }) {
8
+ return {
9
+ message: `Hello, ${params.name || 'FaasJS'}!`,
10
+ }
11
+ },
12
+ })
@@ -0,0 +1,51 @@
1
+ import { useState } from 'react'
2
+
3
+ import { faas } from '../../react-client'
4
+
5
+ type HelloResponse = {
6
+ message?: string
7
+ }
8
+
9
+ export default function HomePage() {
10
+ const [name, setName] = useState('FaasJS')
11
+ const [message, setMessage] = useState('Click button to call API')
12
+ const [loading, setLoading] = useState(false)
13
+
14
+ const callApi = async () => {
15
+ setLoading(true)
16
+
17
+ try {
18
+ const response = await faas('/pages/home/api/hello', {
19
+ name: name.trim() || undefined,
20
+ })
21
+
22
+ const data = response.data as HelloResponse | undefined
23
+
24
+ setMessage(data?.message || 'Empty response')
25
+ } catch (error: unknown) {
26
+ setMessage(error instanceof Error ? error.message : 'Request failed')
27
+ } finally {
28
+ setLoading(false)
29
+ }
30
+ }
31
+
32
+ return (
33
+ <main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
34
+ <h1>FaasJS App</h1>
35
+ <p>{message}</p>
36
+
37
+ <label style={{ display: 'block', marginBottom: 12 }}>
38
+ Name:
39
+ <input
40
+ style={{ marginLeft: 8 }}
41
+ value={name}
42
+ onChange={(event) => setName(event.target.value)}
43
+ />
44
+ </label>
45
+
46
+ <button type="button" onClick={callApi} disabled={loading}>
47
+ {loading ? 'Loading...' : 'Call /pages/home/api/hello'}
48
+ </button>
49
+ </main>
50
+ )
51
+ }
@@ -0,0 +1,8 @@
1
+ import { FaasReactClient } from '@faasjs/react'
2
+
3
+ const client = FaasReactClient({
4
+ baseUrl: '/',
5
+ })
6
+
7
+ export const faas = client.faas
8
+ export const useFaas = client.useFaas
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@faasjs/types/tsconfig/build.json",
3
+ "include": ["src", "vite.config.ts", "server.ts"]
4
+ }
@@ -0,0 +1,6 @@
1
+ import { viteConfig } from '@faasjs/dev'
2
+ import { defineConfig } from 'vite-plus'
3
+
4
+ export default defineConfig({
5
+ ...viteConfig,
6
+ })
package/dist/index.cjs DELETED
@@ -1,310 +0,0 @@
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") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
- let commander = require("commander");
25
- let node_child_process = require("node:child_process");
26
- let node_fs = require("node:fs");
27
- let node_path = require("node:path");
28
- let enquirer = require("enquirer");
29
- enquirer = __toESM(enquirer);
30
- //#region package.json
31
- var version = "8.0.0-beta.18";
32
- //#endregion
33
- //#region src/action.ts
34
- const prompt = enquirer.default.prompt;
35
- const validateName = (input) => Validator.name(input);
36
- const Validator = { name(input) {
37
- const match = /^[a-z0-9-_]+$/i.test(input) ? true : "Must be a-z, 0-9 or -_";
38
- if (match !== true) return match;
39
- if ((0, node_fs.existsSync)(input)) return `${input} folder exists, please try another name`;
40
- return true;
41
- } };
42
- function writeFile(path, content) {
43
- (0, node_fs.mkdirSync)((0, node_path.dirname)(path), { recursive: true });
44
- (0, node_fs.writeFileSync)(path, content);
45
- }
46
- function buildPackageJSON(name) {
47
- return `${JSON.stringify({
48
- name,
49
- private: true,
50
- type: "module",
51
- version: "1.0.0",
52
- scripts: {
53
- dev: "node --import @faasjs/node-utils/register-hooks vite",
54
- build: "vite build",
55
- start: "node --import @faasjs/node-utils/register-hooks server.ts",
56
- test: "vitest run"
57
- },
58
- dependencies: {
59
- "@faasjs/core": "*",
60
- react: "*",
61
- "react-dom": "*"
62
- },
63
- devDependencies: {
64
- "@faasjs/dev": "*",
65
- "@types/node": "*",
66
- "@types/react": "*",
67
- "@types/react-dom": "*",
68
- "@vitejs/plugin-react": "*",
69
- jsdom: "*",
70
- oxfmt: "*",
71
- oxlint: "*",
72
- typescript: "*",
73
- vite: "*",
74
- vitest: "*"
75
- }
76
- }, null, 2)}
77
- `;
78
- }
79
- function scaffold(rootPath) {
80
- writeFile((0, node_path.join)(rootPath, ".gitignore"), `node_modules/
81
- dist/
82
- coverage/
83
- `);
84
- writeFile((0, node_path.join)(rootPath, "tsconfig.json"), `{
85
- "compilerOptions": {
86
- "target": "ES2022",
87
- "module": "ESNext",
88
- "moduleResolution": "Bundler",
89
- "jsx": "react-jsx",
90
- "strict": true,
91
- "types": ["vitest/globals"]
92
- },
93
- "include": ["src", "vite.config.ts", "server.ts"]
94
- }
95
- `);
96
- writeFile((0, node_path.join)(rootPath, "index.html"), `<!doctype html>
97
- <html lang="en">
98
- <head>
99
- <meta charset="UTF-8" />
100
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
101
- <title>FaasJS App</title>
102
- </head>
103
- <body>
104
- <div id="root"></div>
105
- <script type="module" src="/src/main.tsx"><\/script>
106
- </body>
107
- </html>
108
- `);
109
- writeFile((0, node_path.join)(rootPath, "vite.config.ts"), `import { viteFaasJsServer } from '@faasjs/dev'
110
- import react from '@vitejs/plugin-react'
111
- import { defineConfig } from 'vite'
112
-
113
- export default defineConfig({
114
- server: {
115
- host: '0.0.0.0',
116
- },
117
- plugins: [react(), viteFaasJsServer()],
118
- })
119
- `);
120
- writeFile((0, node_path.join)(rootPath, "server.ts"), `import { dirname, join } from 'node:path'
121
- import { fileURLToPath } from 'node:url'
122
- import { Server, staticHandler } from '@faasjs/core'
123
-
124
- const __filename = fileURLToPath(import.meta.url)
125
- const __dirname = dirname(__filename)
126
-
127
- const publicHandler = staticHandler({
128
- root: join(__dirname, 'public'),
129
- notFound: false,
130
- })
131
-
132
- const distHandler = staticHandler({
133
- root: join(__dirname, 'dist'),
134
- notFound: 'index.html',
135
- })
136
-
137
- new Server(join(__dirname, 'src'), {
138
- beforeHandle: async (req, res, ctx) => {
139
- if (!req.url || req.method !== 'GET') return
140
-
141
- await publicHandler(req, res, ctx)
142
- await distHandler(req, res, ctx)
143
- },
144
- }).listen()
145
- `);
146
- writeFile((0, node_path.join)(rootPath, "src", "faas.yaml"), `defaults:
147
- server:
148
- root: .
149
- base: /
150
- plugins:
151
- http:
152
- config:
153
- cookie:
154
- secure: false
155
- session:
156
- secret: secret
157
- `);
158
- writeFile((0, node_path.join)(rootPath, "src", "main.tsx"), `import { createRoot } from 'react-dom/client'
159
- import HomePage from './pages/home'
160
-
161
- createRoot(document.getElementById('root') as HTMLElement).render(<HomePage />)
162
- `);
163
- writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "index.tsx"), `import { useState } from 'react'
164
-
165
- type ApiResponse = {
166
- ok: boolean
167
- data: string
168
- error: null | {
169
- code?: string
170
- message: string
171
- }
172
- }
173
-
174
- export default function HomePage() {
175
- const [message, setMessage] = useState('Click button to call API')
176
- const [loading, setLoading] = useState(false)
177
-
178
- const fetchMessage = async () => {
179
- setLoading(true)
180
-
181
- try {
182
- const data = await fetch('/pages/home/api/hello', {
183
- method: 'POST',
184
- headers: {
185
- 'Content-Type': 'application/json',
186
- },
187
- body: JSON.stringify({ name: 'world' }),
188
- }).then(res => res.json() as Promise<ApiResponse>)
189
-
190
- if (data.ok) setMessage(data.data)
191
- else setMessage(data.error?.message || 'Unknown error')
192
- } catch (error: any) {
193
- setMessage(error?.message || 'Unknown error')
194
- } finally {
195
- setLoading(false)
196
- }
197
- }
198
-
199
- return (
200
- <main style={{ margin: '5rem auto', maxWidth: 420, padding: 24 }}>
201
- <h1>FaasJS App</h1>
202
- <p>{message}</p>
203
- <button type="button" onClick={fetchMessage} disabled={loading}>
204
- {loading ? 'Loading...' : 'Call /pages/home/api/hello'}
205
- </button>
206
- </main>
207
- )
208
- }
209
- `);
210
- writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "api", "hello.func.ts"), `import { defineApi, z } from '@faasjs/core'
211
-
212
- export const func = defineApi({
213
- schema: z
214
- .object({
215
- name: z.string().optional(),
216
- }),
217
- async handler({ params }) {
218
- return {
219
- ok: true,
220
- data: \`Hello, \${params.name || 'FaasJS'}\`,
221
- error: null,
222
- }
223
- },
224
- })
225
- `);
226
- writeFile((0, node_path.join)(rootPath, "src", "pages", "home", "api", "__tests__", "hello.test.ts"), `import { test } from '@faasjs/dev'
227
- import { func } from '../hello.func'
228
-
229
- describe('pages/home/api/hello', () => {
230
- it('should work', async () => {
231
- const testFunc = test(func)
232
-
233
- const { statusCode, data } = await testFunc.JSONhandler({ name: 'world' })
234
-
235
- expect(statusCode).toEqual(200)
236
- expect(data).toEqual({
237
- ok: true,
238
- data: 'Hello, world',
239
- error: null,
240
- })
241
- })
242
- })
243
- `);
244
- }
245
- async function action(options = {}) {
246
- const answers = Object.assign(options, {});
247
- if (!options.name || Validator.name(options.name) !== true) answers.name = await prompt({
248
- type: "input",
249
- name: "value",
250
- message: "Project name",
251
- initial: "faasjs",
252
- validate: validateName
253
- }).then((res) => res.value);
254
- if (!answers.name) return;
255
- const runtime = process.versions.bun ? "bun" : "npm";
256
- (0, node_fs.mkdirSync)(answers.name);
257
- (0, node_fs.writeFileSync)((0, node_path.join)(answers.name, "package.json"), buildPackageJSON(answers.name));
258
- scaffold(answers.name);
259
- (0, node_child_process.execSync)(`cd ${answers.name} && ${runtime} install`, { stdio: "inherit" });
260
- if (runtime === "bun") (0, node_child_process.execSync)(`cd ${answers.name} && bun test`, { stdio: "inherit" });
261
- else (0, node_child_process.execSync)(`cd ${answers.name} && npm run test`, { stdio: "inherit" });
262
- }
263
- function action_default(program) {
264
- program.description("Create a new faas app").on("--help", () => console.log("Examples:\nnpx create-faas-app")).option("--name <name>", "Project name").action(action);
265
- }
266
- //#endregion
267
- //#region src/index.ts
268
- /**
269
- * [![License: MIT](https://img.shields.io/npm/l/create-faas-app.svg)](https://github.com/faasjs/faasjs/blob/main/packages/create-faas-app/LICENSE)
270
- * [![NPM Version](https://img.shields.io/npm/v/create-faas-app.svg)](https://www.npmjs.com/package/create-faas-app)
271
- *
272
- * Quick way to create a FaasJS project.
273
- *
274
- * ## Usage
275
- *
276
- * ```bash
277
- * # use npm
278
- * npx create-faas-app --name faasjs
279
- *
280
- * # use bun
281
- * bunx create-faas-app --name faasjs
282
- * ```
283
- */
284
- const commander$1 = new commander.Command();
285
- commander$1.storeOptionsAsProperties(false).allowUnknownOption(true).version(version).name("create-faas-app").exitOverride();
286
- action_default(commander$1);
287
- /**
288
- * Run the `create-faas-app` CLI with a provided argv array.
289
- *
290
- * @param argv - CLI arguments forwarded to Commander.
291
- * @returns Commander program instance after parsing.
292
- *
293
- * @example
294
- * ```ts
295
- * import { main } from 'create-faas-app'
296
- *
297
- * await main(['node', 'create-faas-app', '--help'])
298
- * ```
299
- */
300
- async function main(argv) {
301
- try {
302
- await commander$1.parseAsync(argv);
303
- } catch (error) {
304
- if (typeof error === "object" && error !== null && "code" in error && error.code === "commander.helpDisplayed") return commander$1;
305
- console.error(error);
306
- }
307
- return commander$1;
308
- }
309
- //#endregion
310
- exports.main = main;