@springmicro/cli 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -31,7 +31,7 @@ $ npm install -g @springmicro/cli
31
31
  $ springmicro COMMAND
32
32
  running command...
33
33
  $ springmicro (--version)
34
- @springmicro/cli/0.2.0 win32-x64 node-v20.15.0
34
+ @springmicro/cli/0.2.1 win32-x64 node-v20.15.0
35
35
  $ springmicro --help [COMMAND]
36
36
  USAGE
37
37
  $ springmicro COMMAND
@@ -69,6 +69,7 @@ USAGE
69
69
 
70
70
  <!-- commands -->
71
71
  * [`springmicro add auth`](#springmicro-add-auth)
72
+ * [`springmicro add form`](#springmicro-add-form)
72
73
  * [`springmicro help [COMMAND]`](#springmicro-help-command)
73
74
  * [`springmicro init PROJECTTYPE`](#springmicro-init-projecttype)
74
75
  * [`springmicro init astro`](#springmicro-init-astro)
@@ -98,7 +99,27 @@ EXAMPLES
98
99
  $ springmicro add auth
99
100
  ```
100
101
 
101
- _See code: [src/commands/add/auth.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.0/src/commands/add/auth.ts)_
102
+ _See code: [src/commands/add/auth.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.1/src/commands/add/auth.ts)_
103
+
104
+ ## `springmicro add form`
105
+
106
+ add a form to your project
107
+
108
+ ```
109
+ USAGE
110
+ $ springmicro add form [-t <value>]
111
+
112
+ FLAGS
113
+ -t, --template=<value> form template (contact|quote)
114
+
115
+ DESCRIPTION
116
+ add a form to your project
117
+
118
+ EXAMPLES
119
+ $ springmicro add form
120
+ ```
121
+
122
+ _See code: [src/commands/add/form.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.1/src/commands/add/form.ts)_
102
123
 
103
124
  ## `springmicro help [COMMAND]`
104
125
 
@@ -141,7 +162,7 @@ EXAMPLES
141
162
  $ springmicro init astro -n example
142
163
  ```
143
164
 
144
- _See code: [src/commands/init/index.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.0/src/commands/init/index.ts)_
165
+ _See code: [src/commands/init/index.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.1/src/commands/init/index.ts)_
145
166
 
146
167
  ## `springmicro init astro`
147
168
 
@@ -161,7 +182,7 @@ EXAMPLES
161
182
  $ springmicro init astro -n <project-name>
162
183
  ```
163
184
 
164
- _See code: [src/commands/init/astro.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.0/src/commands/init/astro.ts)_
185
+ _See code: [src/commands/init/astro.ts](https://github.com/SpringMicro1/springmicrohost-js/blob/v0.2.1/src/commands/init/astro.ts)_
165
186
 
166
187
  ## `springmicro plugins`
167
188
 
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ import forms from '../../data/form/index.js';
3
+ export default class Form extends Command {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ template: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ extractEnvVars(formString: string): string[];
10
+ writeForm(template: keyof typeof forms): void;
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,51 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import forms from '../../data/form/index.js';
3
+ import { checkUnixShellAndSpawn } from '../../utils/shell.js';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+ import chalk from 'chalk';
7
+ export default class Form extends Command {
8
+ // static override args = {
9
+ // file: Args.string({description: 'file to read'}),
10
+ // }
11
+ static description = 'add a form to your project';
12
+ static examples = ['<%= config.bin %> <%= command.id %>'];
13
+ static flags = {
14
+ // flag with a value (-t, --template=VALUE)
15
+ template: Flags.string({ char: 't', description: 'form template (contact|quote)' }),
16
+ };
17
+ extractEnvVars(formString) {
18
+ const envVarRegex = /import\.meta\.env\.(\w+)/g;
19
+ let match;
20
+ const envVars = [];
21
+ while ((match = envVarRegex.exec(formString)) !== null) {
22
+ envVars.push(match[1]);
23
+ }
24
+ return envVars;
25
+ }
26
+ writeForm(template) {
27
+ const baseDir = path.posix.join('src', 'components', 'forms');
28
+ const baseDirApi = path.posix.join('src', 'pages', 'api', 'forms');
29
+ fs.mkdirSync(baseDir, { recursive: true });
30
+ fs.mkdirSync(baseDirApi, { recursive: true });
31
+ const filePath = path.posix.join(baseDir, forms[template].name + '.tsx');
32
+ fs.writeFileSync(filePath, forms[template].tsx);
33
+ const filePathApi = path.posix.join(baseDirApi, forms[template].name.toLowerCase() + '.ts');
34
+ fs.writeFileSync(filePathApi, forms[template].api);
35
+ this.log('');
36
+ this.log(chalk.bgCyan.black('FINISHED!'));
37
+ const envVars = this.extractEnvVars(forms[template].api);
38
+ if (envVars.length > 0) {
39
+ this.log('');
40
+ this.log(chalk.cyan('Make sure to set the following environment variables:'));
41
+ envVars.forEach((v) => this.log(chalk.cyan(`- ${v}`)));
42
+ }
43
+ }
44
+ async run() {
45
+ const { args, flags } = await this.parse(Form);
46
+ const template = flags.template ?? 'contact';
47
+ if (Object.keys(forms).includes(template)) {
48
+ await checkUnixShellAndSpawn('sh', [path.posix.join(this.config.root, 'dist', 'scripts', 'forms.sh')], path.join(this.config.root, 'dist', 'scripts', 'forms.bat'), [], () => this.writeForm(template));
49
+ }
50
+ }
51
+ }
@@ -47,6 +47,8 @@ export default class Astro extends Command {
47
47
  tsConfig['compilerOptions'] = {
48
48
  ...tsConfig['compilerOptions'],
49
49
  baseUrl: '.',
50
+ // allow implicit type imports
51
+ verbatimModuleSyntax: false,
50
52
  paths: {
51
53
  '@/*': ['./src/*'],
52
54
  },
@@ -0,0 +1,10 @@
1
+ type FormStrings = {
2
+ tsx: string;
3
+ name: string;
4
+ api: string;
5
+ };
6
+ declare const _default: {
7
+ contact: FormStrings;
8
+ quote: FormStrings;
9
+ };
10
+ export default _default;
@@ -0,0 +1,274 @@
1
+ const contact = {
2
+ name: 'Contact',
3
+ tsx: `import { zodResolver } from "@hookform/resolvers/zod";
4
+ import { useForm } from "react-hook-form";
5
+ import { z } from "zod";
6
+ import { Button } from "@/components/ui/button";
7
+ import { toast } from "sonner";
8
+ import {
9
+ Form,
10
+ FormControl,
11
+ FormDescription,
12
+ FormField,
13
+ FormItem,
14
+ FormLabel,
15
+ FormMessage,
16
+ } from "@/components/ui/form";
17
+ import { Input } from "@/components/ui/input";
18
+ import { Textarea } from "@/components/ui/textarea"; // Assume you have a Textarea component
19
+
20
+ const formSchema = z.object({
21
+ name: z.string().min(1, {
22
+ message: "Name is required.",
23
+ }),
24
+ email: z.string().email({
25
+ message: "Invalid email address.",
26
+ }),
27
+ message: z.string().min(1, {
28
+ message: "Message is required.",
29
+ }),
30
+ });
31
+
32
+ export function ContactForm() {
33
+ // 1. Define your form.
34
+ const form = useForm<z.infer<typeof formSchema>>({
35
+ resolver: zodResolver(formSchema),
36
+ defaultValues: {
37
+ name: "",
38
+ email: "",
39
+ message: "",
40
+ },
41
+ });
42
+
43
+ // 2. Define a submit handler.
44
+ async function onSubmit(values: z.infer<typeof formSchema>) {
45
+ // Do something with the form values.
46
+ console.log(values);
47
+ const res = await fetch("/api/forms/contact", {
48
+ method: "POST",
49
+ body: JSON.stringify(values),
50
+ headers: { "Content-Type": "application/json" },
51
+ });
52
+ if (res.ok) {
53
+ // success toast
54
+ toast("Form submitted!");
55
+ } else {
56
+ // error toast
57
+ toast("Error submitting form, refresh and try again.");
58
+ }
59
+ }
60
+
61
+ return (
62
+ <Form {...form}>
63
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
64
+ <FormField
65
+ control={form.control}
66
+ name="name"
67
+ render={({ field }) => (
68
+ <FormItem>
69
+ <FormLabel>Name</FormLabel>
70
+ <FormControl>
71
+ <Input placeholder="Your Name" {...field} />
72
+ </FormControl>
73
+ <FormMessage />
74
+ </FormItem>
75
+ )}
76
+ />
77
+ <FormField
78
+ control={form.control}
79
+ name="email"
80
+ render={({ field }) => (
81
+ <FormItem>
82
+ <FormLabel>Email</FormLabel>
83
+ <FormControl>
84
+ <Input
85
+ type="email"
86
+ placeholder="your.email@example.com"
87
+ {...field}
88
+ />
89
+ </FormControl>
90
+ <FormMessage />
91
+ </FormItem>
92
+ )}
93
+ />
94
+ <FormField
95
+ control={form.control}
96
+ name="message"
97
+ render={({ field }) => (
98
+ <FormItem>
99
+ <FormLabel>Message</FormLabel>
100
+ <FormControl>
101
+ <Textarea rows={4} placeholder="Your message" {...field} />
102
+ </FormControl>
103
+ <FormMessage />
104
+ </FormItem>
105
+ )}
106
+ />
107
+ <Button type="submit">Submit</Button>
108
+ </form>
109
+ </Form>
110
+ );
111
+ }
112
+
113
+ `,
114
+ api: `import { emailOnlySGJSON } from "@springmicro/forms";
115
+ import type { APIRoute } from "astro";
116
+
117
+ export const POST: APIRoute = async ({ params, request }) => {
118
+ const subject = "Contact Form Submission";
119
+ const json = await request.json();
120
+ const to: string[] = [json.email];
121
+ const cc: string[] = [];
122
+ const bcc: string[] = [];
123
+
124
+ const res = await emailOnlySGJSON(
125
+ import.meta.env.SENDGRID_API_KEY,
126
+ subject,
127
+ json,
128
+ to,
129
+ cc,
130
+ bcc
131
+ );
132
+ return res;
133
+ };
134
+ `,
135
+ };
136
+ const quote = {
137
+ name: 'Quote',
138
+ tsx: `import { zodResolver } from "@hookform/resolvers/zod";
139
+ import { useForm } from "react-hook-form";
140
+ import { z } from "zod";
141
+ import { Button } from "@/components/ui/button";
142
+ import { toast } from "sonner";
143
+ import {
144
+ Form,
145
+ FormControl,
146
+ FormDescription,
147
+ FormField,
148
+ FormItem,
149
+ FormLabel,
150
+ FormMessage,
151
+ } from "@/components/ui/form";
152
+ import { Input } from "@/components/ui/input";
153
+ import { Textarea } from "@/components/ui/textarea"; // Assume you have a Textarea component
154
+
155
+ const formSchema = z.object({
156
+ name: z.string().min(1, {
157
+ message: "Name is required.",
158
+ }),
159
+ email: z.string().email({
160
+ message: "Invalid email address.",
161
+ }),
162
+ message: z.string().min(1, {
163
+ message: "Message is required.",
164
+ }),
165
+ });
166
+
167
+ export function QuoteForm() {
168
+ // 1. Define your form.
169
+ const form = useForm<z.infer<typeof formSchema>>({
170
+ resolver: zodResolver(formSchema),
171
+ defaultValues: {
172
+ name: "",
173
+ email: "",
174
+ message: "",
175
+ },
176
+ });
177
+
178
+ // 2. Define a submit handler.
179
+ async function onSubmit(values: z.infer<typeof formSchema>) {
180
+ // Do something with the form values.
181
+ console.log(values);
182
+ const res = await fetch("/api/forms/quote", {
183
+ method: "POST",
184
+ body: JSON.stringify(values),
185
+ headers: { "Content-Type": "application/json" },
186
+ });
187
+ if (res.ok) {
188
+ // success toast
189
+ toast("Form submitted!");
190
+ } else {
191
+ // error toast
192
+ toast("Error submitting form, refresh and try again.");
193
+ }
194
+ }
195
+
196
+ return (
197
+ <Form {...form}>
198
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
199
+ <FormField
200
+ control={form.control}
201
+ name="name"
202
+ render={({ field }) => (
203
+ <FormItem>
204
+ <FormLabel>Name</FormLabel>
205
+ <FormControl>
206
+ <Input placeholder="Your Name" {...field} />
207
+ </FormControl>
208
+ <FormMessage />
209
+ </FormItem>
210
+ )}
211
+ />
212
+ <FormField
213
+ control={form.control}
214
+ name="email"
215
+ render={({ field }) => (
216
+ <FormItem>
217
+ <FormLabel>Email</FormLabel>
218
+ <FormControl>
219
+ <Input
220
+ type="email"
221
+ placeholder="your.email@example.com"
222
+ {...field}
223
+ />
224
+ </FormControl>
225
+ <FormMessage />
226
+ </FormItem>
227
+ )}
228
+ />
229
+ <FormField
230
+ control={form.control}
231
+ name="message"
232
+ render={({ field }) => (
233
+ <FormItem>
234
+ <FormLabel>Message</FormLabel>
235
+ <FormControl>
236
+ <Textarea rows={4} placeholder="Your message" {...field} />
237
+ </FormControl>
238
+ <FormMessage />
239
+ </FormItem>
240
+ )}
241
+ />
242
+ <Button type="submit">Submit</Button>
243
+ </form>
244
+ </Form>
245
+ );
246
+ }
247
+
248
+ `,
249
+ api: `import { emailOnlySGJSON } from "@springmicro/forms";
250
+ import type { APIRoute } from "astro";
251
+
252
+ export const POST: APIRoute = async ({ params, request }) => {
253
+ const subject = "Request a Quote Submission";
254
+ const json = await request.json();
255
+ const to: string[] = [json.email];
256
+ const cc: string[] = [];
257
+ const bcc: string[] = [];
258
+
259
+ const res = await emailOnlySGJSON(
260
+ import.meta.env.SENDGRID_API_KEY,
261
+ subject,
262
+ json,
263
+ to,
264
+ cc,
265
+ bcc
266
+ );
267
+ return res;
268
+ };
269
+ `,
270
+ };
271
+ export default {
272
+ contact,
273
+ quote,
274
+ };
@@ -0,0 +1,10 @@
1
+ @echo off
2
+
3
+ REM Use PowerShell to simulate 'yes no' command and pipe to pnpm
4
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add form
5
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add button
6
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add textarea
7
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add label
8
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add input
9
+ powershell -Command "while ($true) { Write-Output 'n' }" | pnpm dlx shadcn-ui@latest add sonner
10
+ pnpm add @springmicro/forms
@@ -0,0 +1,8 @@
1
+ # no to overwriting shadcn components that have already been installed
2
+ yes no | pnpm dlx shadcn-ui@latest add form
3
+ yes no | pnpm dlx shadcn-ui@latest add button
4
+ yes no | pnpm dlx shadcn-ui@latest add textarea
5
+ yes no | pnpm dlx shadcn-ui@latest add label
6
+ yes no | pnpm dlx shadcn-ui@latest add input
7
+ yes no | pnpm dlx shadcn-ui@latest add sonner
8
+ pnpm add @springmicro/forms
@@ -24,6 +24,39 @@
24
24
  "auth.js"
25
25
  ]
26
26
  },
27
+ "add:form": {
28
+ "aliases": [],
29
+ "args": {},
30
+ "description": "add a form to your project",
31
+ "examples": [
32
+ "<%= config.bin %> <%= command.id %>"
33
+ ],
34
+ "flags": {
35
+ "template": {
36
+ "char": "t",
37
+ "description": "form template (contact|quote)",
38
+ "name": "template",
39
+ "hasDynamicHelp": false,
40
+ "multiple": false,
41
+ "type": "option"
42
+ }
43
+ },
44
+ "hasDynamicHelp": false,
45
+ "hiddenAliases": [],
46
+ "id": "add:form",
47
+ "pluginAlias": "@springmicro/cli",
48
+ "pluginName": "@springmicro/cli",
49
+ "pluginType": "core",
50
+ "strict": true,
51
+ "enableJsonFlag": false,
52
+ "isESM": true,
53
+ "relativePath": [
54
+ "dist",
55
+ "commands",
56
+ "add",
57
+ "form.js"
58
+ ]
59
+ },
27
60
  "init:astro": {
28
61
  "aliases": [],
29
62
  "args": {},
@@ -101,5 +134,5 @@
101
134
  ]
102
135
  }
103
136
  },
104
- "version": "0.2.0"
137
+ "version": "0.2.1"
105
138
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@springmicro/cli",
3
3
  "description": "A new CLI generated with oclif",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "author": "David Buckley",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -76,5 +76,5 @@
76
76
  "version": "oclif readme && git add README.md"
77
77
  },
78
78
  "types": "dist/index.d.ts",
79
- "gitHead": "bada90b1f5c1efcd122b4a69436098408162677c"
79
+ "gitHead": "5060bfe477b5be4d15de32991fb8ebc839143430"
80
80
  }