create-nattyjs 0.0.1-beta.66 → 0.0.1-beta.67

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.
Files changed (119) hide show
  1. package/index.js +319 -286
  2. package/package.json +9 -2
  3. package/template-fullstack-angular/__examples/apps/api/controllers/requests.controller.ts +13 -0
  4. package/template-fullstack-angular/__examples/apps/web/src/app/app.component.ts +46 -0
  5. package/template-fullstack-angular/__examples/apps/web/src/app/lib/api.ts +15 -0
  6. package/template-fullstack-angular/_gitignore +8 -0
  7. package/template-fullstack-angular/apps/api/app.ts +8 -0
  8. package/template-fullstack-angular/apps/api/natty.config.ts +9 -0
  9. package/template-fullstack-angular/apps/api/package.json +24 -0
  10. package/template-fullstack-angular/apps/api/tsconfig.json +20 -0
  11. package/template-fullstack-angular/apps/web/angular.json +67 -0
  12. package/template-fullstack-angular/apps/web/package.json +30 -0
  13. package/template-fullstack-angular/apps/web/proxy.conf.json +7 -0
  14. package/template-fullstack-angular/apps/web/src/app/app.component.ts +29 -0
  15. package/template-fullstack-angular/apps/web/src/assets/.gitkeep +1 -0
  16. package/template-fullstack-angular/apps/web/src/index.html +12 -0
  17. package/template-fullstack-angular/apps/web/src/main.ts +5 -0
  18. package/template-fullstack-angular/apps/web/src/styles.css +110 -0
  19. package/template-fullstack-angular/apps/web/tsconfig.app.json +13 -0
  20. package/template-fullstack-angular/apps/web/tsconfig.json +31 -0
  21. package/template-fullstack-angular/package.json +20 -0
  22. package/template-fullstack-astro/__examples/apps/api/controllers/requests.controller.ts +13 -0
  23. package/template-fullstack-astro/__examples/apps/web/src/lib/api.ts +15 -0
  24. package/template-fullstack-astro/__examples/apps/web/src/pages/index.astro +53 -0
  25. package/template-fullstack-astro/_gitignore +8 -0
  26. package/template-fullstack-astro/apps/api/app.ts +8 -0
  27. package/template-fullstack-astro/apps/api/natty.config.ts +9 -0
  28. package/template-fullstack-astro/apps/api/package.json +24 -0
  29. package/template-fullstack-astro/apps/api/tsconfig.json +20 -0
  30. package/template-fullstack-astro/apps/web/astro.config.mjs +14 -0
  31. package/template-fullstack-astro/apps/web/package.json +17 -0
  32. package/template-fullstack-astro/apps/web/src/env.d.ts +1 -0
  33. package/template-fullstack-astro/apps/web/src/pages/index.astro +31 -0
  34. package/template-fullstack-astro/apps/web/src/styles/global.css +110 -0
  35. package/template-fullstack-astro/apps/web/tsconfig.json +5 -0
  36. package/template-fullstack-astro/package.json +20 -0
  37. package/template-fullstack-next/__examples/apps/api/controllers/requests.controller.ts +13 -0
  38. package/template-fullstack-next/__examples/apps/web/app/page.tsx +68 -0
  39. package/template-fullstack-next/__examples/apps/web/lib/api.ts +15 -0
  40. package/template-fullstack-next/_gitignore +8 -0
  41. package/template-fullstack-next/apps/api/app.ts +8 -0
  42. package/template-fullstack-next/apps/api/natty.config.ts +9 -0
  43. package/template-fullstack-next/apps/api/package.json +24 -0
  44. package/template-fullstack-next/apps/api/tsconfig.json +20 -0
  45. package/template-fullstack-next/apps/web/app/globals.css +110 -0
  46. package/template-fullstack-next/apps/web/app/layout.tsx +19 -0
  47. package/template-fullstack-next/apps/web/app/page.tsx +22 -0
  48. package/template-fullstack-next/apps/web/next-env.d.ts +4 -0
  49. package/template-fullstack-next/apps/web/next.config.mjs +13 -0
  50. package/template-fullstack-next/apps/web/package.json +22 -0
  51. package/template-fullstack-next/apps/web/tsconfig.json +24 -0
  52. package/template-fullstack-next/package.json +20 -0
  53. package/template-fullstack-nuxt/__examples/apps/api/controllers/requests.controller.ts +13 -0
  54. package/template-fullstack-nuxt/__examples/apps/web/app.vue +41 -0
  55. package/template-fullstack-nuxt/__examples/apps/web/lib/api.ts +15 -0
  56. package/template-fullstack-nuxt/_gitignore +8 -0
  57. package/template-fullstack-nuxt/apps/api/app.ts +8 -0
  58. package/template-fullstack-nuxt/apps/api/natty.config.ts +9 -0
  59. package/template-fullstack-nuxt/apps/api/package.json +24 -0
  60. package/template-fullstack-nuxt/apps/api/tsconfig.json +20 -0
  61. package/template-fullstack-nuxt/apps/web/app.vue +20 -0
  62. package/template-fullstack-nuxt/apps/web/assets/css/main.css +111 -0
  63. package/template-fullstack-nuxt/apps/web/nuxt.config.ts +14 -0
  64. package/template-fullstack-nuxt/apps/web/package.json +14 -0
  65. package/template-fullstack-nuxt/apps/web/tsconfig.json +3 -0
  66. package/template-fullstack-nuxt/package.json +20 -0
  67. package/template-fullstack-react/__examples/apps/api/controllers/requests.controller.ts +13 -0
  68. package/template-fullstack-react/__examples/apps/web/src/App.tsx +66 -0
  69. package/template-fullstack-react/__examples/apps/web/src/lib/api.ts +15 -0
  70. package/template-fullstack-react/_gitignore +7 -0
  71. package/template-fullstack-react/apps/api/app.ts +8 -0
  72. package/template-fullstack-react/apps/api/natty.config.ts +9 -0
  73. package/template-fullstack-react/apps/api/package.json +24 -0
  74. package/template-fullstack-react/apps/api/tsconfig.json +20 -0
  75. package/template-fullstack-react/apps/web/index.html +12 -0
  76. package/template-fullstack-react/apps/web/package.json +22 -0
  77. package/template-fullstack-react/apps/web/src/App.tsx +22 -0
  78. package/template-fullstack-react/apps/web/src/main.tsx +10 -0
  79. package/template-fullstack-react/apps/web/src/style.css +111 -0
  80. package/template-fullstack-react/apps/web/src/vite-env.d.ts +1 -0
  81. package/template-fullstack-react/apps/web/tsconfig.json +17 -0
  82. package/template-fullstack-react/apps/web/vite.config.ts +15 -0
  83. package/template-fullstack-react/package.json +20 -0
  84. package/template-fullstack-sveltekit/__examples/apps/api/controllers/requests.controller.ts +13 -0
  85. package/template-fullstack-sveltekit/__examples/apps/web/src/lib/api.ts +15 -0
  86. package/template-fullstack-sveltekit/__examples/apps/web/src/routes/+page.svelte +45 -0
  87. package/template-fullstack-sveltekit/_gitignore +8 -0
  88. package/template-fullstack-sveltekit/apps/api/app.ts +8 -0
  89. package/template-fullstack-sveltekit/apps/api/natty.config.ts +9 -0
  90. package/template-fullstack-sveltekit/apps/api/package.json +24 -0
  91. package/template-fullstack-sveltekit/apps/api/tsconfig.json +20 -0
  92. package/template-fullstack-sveltekit/apps/web/package.json +24 -0
  93. package/template-fullstack-sveltekit/apps/web/src/app.css +110 -0
  94. package/template-fullstack-sveltekit/apps/web/src/app.d.ts +10 -0
  95. package/template-fullstack-sveltekit/apps/web/src/app.html +11 -0
  96. package/template-fullstack-sveltekit/apps/web/src/routes/+layout.svelte +5 -0
  97. package/template-fullstack-sveltekit/apps/web/src/routes/+page.svelte +18 -0
  98. package/template-fullstack-sveltekit/apps/web/svelte.config.js +12 -0
  99. package/template-fullstack-sveltekit/apps/web/tsconfig.json +14 -0
  100. package/template-fullstack-sveltekit/apps/web/vite.config.ts +14 -0
  101. package/template-fullstack-sveltekit/package.json +20 -0
  102. package/template-fullstack-vue/__examples/apps/api/controllers/requests.controller.ts +13 -0
  103. package/template-fullstack-vue/__examples/apps/web/src/App.vue +122 -0
  104. package/template-fullstack-vue/__examples/apps/web/src/lib/api.ts +15 -0
  105. package/template-fullstack-vue/_gitignore +7 -0
  106. package/template-fullstack-vue/apps/api/app.ts +8 -0
  107. package/template-fullstack-vue/apps/api/natty.config.ts +9 -0
  108. package/template-fullstack-vue/apps/api/package.json +24 -0
  109. package/template-fullstack-vue/apps/api/tsconfig.json +20 -0
  110. package/template-fullstack-vue/apps/web/index.html +12 -0
  111. package/template-fullstack-vue/apps/web/package.json +20 -0
  112. package/template-fullstack-vue/apps/web/src/App.vue +77 -0
  113. package/template-fullstack-vue/apps/web/src/main.ts +5 -0
  114. package/template-fullstack-vue/apps/web/src/style.css +31 -0
  115. package/template-fullstack-vue/apps/web/src/vite-env.d.ts +1 -0
  116. package/template-fullstack-vue/apps/web/tsconfig.json +17 -0
  117. package/template-fullstack-vue/apps/web/vite.config.ts +15 -0
  118. package/template-fullstack-vue/package.json +20 -0
  119. package/template-nattyjs-blank/package.json +8 -8
package/index.js CHANGED
@@ -1,316 +1,349 @@
1
- #!/usr/bin/env node
2
- import fs from 'fs'
3
- import path from 'path'
4
- import minimist from 'minimist'
5
- const argv = minimist(process.argv.slice(2), { string: ['_'] });
6
- import prompts from 'prompts'
7
- import {execa} from 'execa'
8
- import {fileURLToPath} from 'url';
9
-
10
- import {
11
- yellow,
12
- green,
13
- blue,
14
- red,
15
- reset
16
- } from 'kolorist';
17
- import { bold } from 'kleur/colors';
18
-
19
- import chalk from 'chalk';
20
- import ora from 'ora';
21
-
22
- const gradients = [
23
- `#00FF97`,
24
- `#29FF8F`,
25
- `#58FF38`,
26
- `#3BFF24`,
27
- `#93FF05`,
28
- `#4EFF00`,
29
- `#6FF500`,
30
- `#14E684`,
31
- `#36D629`,
32
- `#38C266`,
33
- ];
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import minimist from "minimist";
5
+ import prompts from "prompts";
6
+ import { fileURLToPath } from "url";
7
+ import { red, reset } from "kolorist";
34
8
 
35
- export const arrow = '■■▶';
9
+ const argv = minimist(process.argv.slice(2), {
10
+ string: ["template"],
11
+ boolean: ["examples"],
12
+ alias: {
13
+ t: "template",
14
+ e: "examples",
15
+ },
16
+ });
36
17
 
37
- const gradientRefrences = [
38
- ...gradients,
39
- ...[...gradients].reverse(),
40
- ...gradients,
41
- ];
18
+ const cwd = process.cwd();
42
19
 
20
+ const renameFiles = {
21
+ _gitignore: ".gitignore",
22
+ };
43
23
 
44
- const sleepProcess = (time) =>
45
- new Promise((resolve) => {
46
- setTimeout(resolve, time);
47
- });
48
-
49
- function getInstallAnimationFrames() {
50
- const animationFrames = [];
51
- for (let i = 0; i < gradients.length * 2; i++) {
52
- const end = i + gradients.length - 1;
53
- animationFrames.push(
54
- gradientRefrences
55
- .slice(i, end)
56
- .map((g) => chalk.bgHex(g)(' '))
57
- .join('')
58
- );
59
- }
60
- return animationFrames;
24
+ const TEMPLATE_DEFINITIONS = [
25
+ {
26
+ id: "api-only",
27
+ aliases: ["nattyjs-blank"],
28
+ title: "API Only",
29
+ description: "Backend API (current blank template)",
30
+ templateDir: "template-nattyjs-blank",
31
+ exampleExcludedPaths: ["controllers"],
32
+ },
33
+ {
34
+ id: "fullstack-vue",
35
+ aliases: ["vue", "nattyjs-vue"],
36
+ title: "Fullstack + Vue",
37
+ description: "NattyJS API + Vue (Vite)",
38
+ templateDir: "template-fullstack-vue",
39
+ exampleOverlayDir: "__examples",
40
+ },
41
+ {
42
+ id: "fullstack-react",
43
+ aliases: ["react", "nattyjs-react"],
44
+ title: "Fullstack + React",
45
+ description: "NattyJS API + React (Vite)",
46
+ templateDir: "template-fullstack-react",
47
+ exampleOverlayDir: "__examples",
48
+ },
49
+ {
50
+ id: "fullstack-next",
51
+ aliases: ["next", "nextjs", "nattyjs-next"],
52
+ title: "Fullstack + Next.js",
53
+ description: "NattyJS API + Next.js",
54
+ templateDir: "template-fullstack-next",
55
+ exampleOverlayDir: "__examples",
56
+ },
57
+ {
58
+ id: "fullstack-nuxt",
59
+ aliases: ["nuxt", "nuxtjs", "nattyjs-nuxt"],
60
+ title: "Fullstack + Nuxt",
61
+ description: "NattyJS API + Nuxt",
62
+ templateDir: "template-fullstack-nuxt",
63
+ exampleOverlayDir: "__examples",
64
+ },
65
+ {
66
+ id: "fullstack-angular",
67
+ aliases: ["angular", "ng", "nattyjs-angular"],
68
+ title: "Fullstack + Angular",
69
+ description: "NattyJS API + Angular",
70
+ templateDir: "template-fullstack-angular",
71
+ exampleOverlayDir: "__examples",
72
+ },
73
+ {
74
+ id: "fullstack-astro",
75
+ aliases: ["astro", "nattyjs-astro"],
76
+ title: "Fullstack + Astro",
77
+ description: "NattyJS API + Astro",
78
+ templateDir: "template-fullstack-astro",
79
+ exampleOverlayDir: "__examples",
80
+ },
81
+ {
82
+ id: "fullstack-sveltekit",
83
+ aliases: ["sveltekit", "svelte-kit", "svelte", "nattyjs-sveltekit"],
84
+ title: "Fullstack + SvelteKit",
85
+ description: "NattyJS API + SvelteKit",
86
+ templateDir: "template-fullstack-sveltekit",
87
+ exampleOverlayDir: "__examples",
88
+ },
89
+ ];
90
+
91
+ if (process.env.NATTYJS_PROMPT_INJECT) {
92
+ prompts.inject(JSON.parse(process.env.NATTYJS_PROMPT_INJECT));
61
93
  }
62
94
 
63
- function getAnimationFrames() {
64
- const animationFrames = [];
65
- for (let i = 1; i <= gradients.length; i++) {
66
- const spaces = Array.from(
67
- new Array(Math.abs(gradients.length - i - 1)),
68
- () => ' '
69
- );
70
- const gradientColors = gradients.slice(0, i).map((item) => chalk.bgHex(item)(' '));
71
- animationFrames.push([...spaces, ...gradientColors].join(''));
72
- }
73
- return animationFrames;
95
+ function readJson(filePath) {
96
+ if (!fs.existsSync(filePath)) {
97
+ return undefined;
98
+ }
99
+
100
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
74
101
  }
75
102
 
103
+ function getTemplateById(value) {
104
+ if (!value) {
105
+ return undefined;
106
+ }
76
107
 
77
- async function showSpinner(text) {
78
- const animationFrames = getAnimationFrames();
79
- const startSpinner = ora({
80
- spinner: {
81
- interval: 30,
82
- frames:animationFrames,
83
- },
84
- text: `${arrow} ${text}`,
85
- });
86
- startSpinner.start();
87
- await sleepProcess((animationFrames.length - 1) * startSpinner.interval);
88
- startSpinner.stop();
89
- const spinner = ora({
90
- spinner: {
91
- interval: 80,
92
- frames: getInstallAnimationFrames(),
93
- },
94
- text: `${arrow} ${text}`,
95
- }).start();
96
-
97
- return spinner;
108
+ return TEMPLATE_DEFINITIONS.find((template) => {
109
+ return template.id === value || template.aliases.includes(value);
110
+ });
98
111
  }
99
-
100
- const cwd = process.cwd()
101
112
 
102
- const SCAFFOLDING_TEMPLATES = [
103
- {
104
- title:'Blank Project',
105
- name: 'nattyjs-blank',
106
- color: yellow
107
- }
113
+ function isValidPackageName(projectName) {
114
+ return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName);
115
+ }
108
116
 
109
- ]
117
+ function toValidPackageName(projectName) {
118
+ return projectName
119
+ .trim()
120
+ .toLowerCase()
121
+ .replace(/\s+/g, "-")
122
+ .replace(/^[._]/, "")
123
+ .replace(/[^a-z0-9-~]+/g, "-");
124
+ }
110
125
 
126
+ function isEmpty(dirPath) {
127
+ const files = fs.readdirSync(dirPath);
128
+ return files.length === 0 || (files.length === 1 && files[0] === ".git");
129
+ }
111
130
 
112
- const renameFiles = {
113
- _gitignore: '.gitignore'
114
- }
131
+ function emptyDir(dir) {
132
+ if (!fs.existsSync(dir)) {
133
+ return;
134
+ }
115
135
 
116
- function readFileSync(path,isString=false) {
117
- if(fs.existsSync(path)){
118
- var content = fs.readFileSync(path, "utf-8");
119
- content = isString ? content: JSON.parse(content);
120
- return content;
136
+ for (const file of fs.readdirSync(dir)) {
137
+ const absolutePath = path.resolve(dir, file);
138
+ if (fs.lstatSync(absolutePath).isDirectory()) {
139
+ emptyDir(absolutePath);
140
+ fs.rmdirSync(absolutePath);
141
+ } else {
142
+ fs.unlinkSync(absolutePath);
143
+ }
121
144
  }
122
145
  }
123
146
 
124
- async function initNatty() {
125
- let targetDir = argv._[0]
126
- let template = argv.template || argv.t
127
-
128
- const defaultProjectName = !targetDir ? 'nattyjs-project' : targetDir
129
-
130
- let result = {}
131
-
132
- try {
133
- result = await prompts(
134
- [
135
- {
136
- type: targetDir ? null : 'text',
137
- name: 'projectName',
138
- message: reset('Project name:'),
139
- initial: defaultProjectName,
140
- onState: (state) =>
141
- (targetDir = state.value.trim() || defaultProjectName)
142
- },
143
- {
144
- type: () =>
145
- !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm',
146
- name: 'overwrite',
147
- message: () =>
148
- (targetDir === '.'
149
- ? 'Current directory'
150
- : `Target directory "${targetDir}"`) +
151
- ` is not empty. Remove existing files and continue?`
152
- },
153
- {
154
- type: (_, { overwrite } = {}) => {
155
- if (overwrite === false) {
156
- throw new Error(red('✖') + ' Operation cancelled')
157
- }
158
- return null
159
- },
160
- name: 'overwriteChecker'
161
- },
162
- {
163
- type: () => (isValidPackageName(targetDir) ? null : 'text'),
164
- name: 'packageName',
165
- message: reset('Package name:'),
166
- initial: () => toValidPackageName(targetDir),
167
- validate: (dir) =>
168
- isValidPackageName(dir) || 'Invalid package.json name'
169
- },
170
- {
171
- type: 'select',
172
- name: 'variant',
173
- message: reset('Select a scaffolding template:'),
174
- // @ts-ignore
175
- choices: () =>
176
- SCAFFOLDING_TEMPLATES.map((template) => {
177
- const templateColor = template.color
178
- return {
179
- title: templateColor(template.title),
180
- value: template.name
181
- }
182
- })
183
- }
184
- ],
185
- {
186
- onCancel: () => {
187
- throw new Error(red('✖') + ' Operation cancelled')
188
- }
189
- }
190
- )
191
- } catch (cancelled) {
192
- console.log(cancelled.message)
193
- return
147
+ function copy(src, dest) {
148
+ const stat = fs.statSync(src);
149
+ if (stat.isDirectory()) {
150
+ copyDir(src, dest);
151
+ return;
194
152
  }
195
-
196
- const { framework, overwrite, packageName, variant } = result
197
-
198
- const root = path.join(cwd, targetDir)
199
-
200
- if (overwrite) {
201
- emptyDir(root)
202
- } else if (!fs.existsSync(root)) {
203
- fs.mkdirSync(root)
153
+
154
+ fs.copyFileSync(src, dest);
155
+ }
156
+
157
+ function copyDir(srcDir, destDir) {
158
+ fs.mkdirSync(destDir, { recursive: true });
159
+ for (const file of fs.readdirSync(srcDir)) {
160
+ const srcFile = path.resolve(srcDir, file);
161
+ const destFile = path.resolve(destDir, file);
162
+ copy(srcFile, destFile);
204
163
  }
205
- template = variant || framework || template
206
-
207
- console.log(`\nScaffolding project in ${root}...`)
208
- const __filename = fileURLToPath(import.meta.url);
209
- const __dirname = path.dirname(__filename);
210
- const templateDir = path.join(__dirname, `template-${template}`)
211
- const write = (file, content) => {
212
- const targetPath = renameFiles[file]
213
- ? path.join(root, renameFiles[file])
214
- : path.join(root, file)
215
- if (content) {
216
- fs.writeFileSync(targetPath, content)
217
- } else {
218
- copy(path.join(templateDir, file), targetPath)
219
- }
164
+ }
165
+
166
+ function pkgFromUserAgent(userAgent) {
167
+ if (!userAgent) {
168
+ return undefined;
220
169
  }
221
-
222
- const files = fs.readdirSync(templateDir)
223
- for (const file of files.filter((f) => f !== 'package.json')) {
224
- write(file)
170
+
171
+ const pkgSpec = userAgent.split(" ")[0];
172
+ const [name, version] = pkgSpec.split("/");
173
+ return { name, version };
174
+ }
175
+
176
+ function shouldSkipTemplateFile(relativePath, template, includeExamples) {
177
+ if (includeExamples) {
178
+ return false;
225
179
  }
226
- const pkg = readFileSync(path.join(templateDir, `package.json`))
227
-
228
- pkg.name = packageName || targetDir
229
-
230
- write('package.json', JSON.stringify(pkg, null, 2))
231
-
232
- const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
233
- const pkgManager = pkgInfo ? pkgInfo.name : 'npm'
234
- console.log(`\nDone. Now run:\n`)
235
- if (root !== cwd) {
236
- console.log(` cd ${path.relative(cwd, root)}`)
180
+
181
+ const excludedPaths = template.exampleExcludedPaths || [];
182
+ return excludedPaths.some((excludedPath) => {
183
+ return relativePath === excludedPath || relativePath.startsWith(`${excludedPath}${path.sep}`);
184
+ });
185
+ }
186
+
187
+ function copyTemplateContents(templateDir, root, template, includeExamples) {
188
+ const entries = fs.readdirSync(templateDir);
189
+ for (const entry of entries) {
190
+ if (entry === "package.json") {
191
+ continue;
192
+ }
193
+
194
+ if (entry === template.exampleOverlayDir) {
195
+ continue;
196
+ }
197
+
198
+ if (shouldSkipTemplateFile(entry, template, includeExamples)) {
199
+ continue;
200
+ }
201
+
202
+ const sourcePath = path.join(templateDir, entry);
203
+ const targetPath = renameFiles[entry]
204
+ ? path.join(root, renameFiles[entry])
205
+ : path.join(root, entry);
206
+
207
+ copy(sourcePath, targetPath);
237
208
  }
238
- switch (pkgManager) {
239
- case 'yarn':
240
- console.log(' yarn')
241
- console.log(' yarn dev')
242
- break
243
- default:
244
- console.log(` ${pkgManager} install`)
245
- console.log(` ${pkgManager} run dev`)
246
- break
209
+
210
+ if (includeExamples && template.exampleOverlayDir) {
211
+ const overlayDir = path.join(templateDir, template.exampleOverlayDir);
212
+ if (fs.existsSync(overlayDir)) {
213
+ copyDir(overlayDir, root);
214
+ }
247
215
  }
248
- console.log()
249
- }
250
-
251
- function copy(src, dest) {
252
- const stat = fs.statSync(src)
253
- if (stat.isDirectory()) {
254
- copyDir(src, dest)
255
- } else {
256
- fs.copyFileSync(src, dest)
216
+ }
217
+
218
+ async function resolveProjectOptions() {
219
+ let targetDir = typeof argv._[0] === "string" ? argv._[0].trim() : "";
220
+ let selectedTemplate = getTemplateById(argv.template);
221
+ const defaultProjectName = targetDir || "nattyjs-project";
222
+
223
+ try {
224
+ return await prompts(
225
+ [
226
+ {
227
+ type: targetDir ? null : "text",
228
+ name: "projectName",
229
+ message: reset("Project name:"),
230
+ initial: defaultProjectName,
231
+ validate: (value) => {
232
+ const trimmed = value.trim();
233
+ return trimmed.length > 0 || "Project name is required";
234
+ },
235
+ onState: (state) => {
236
+ targetDir = state.value.trim() || defaultProjectName;
237
+ },
238
+ },
239
+ {
240
+ type: () => {
241
+ return !targetDir || !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : "confirm";
242
+ },
243
+ name: "overwrite",
244
+ message: () => {
245
+ const label = targetDir === "." ? "Current directory" : `Target directory "${targetDir}"`;
246
+ return `${label} is not empty. Remove existing files and continue?`;
247
+ },
248
+ initial: false,
249
+ },
250
+ {
251
+ type: (_, { overwrite } = {}) => {
252
+ if (overwrite === false) {
253
+ throw new Error(`${red("x")} Operation cancelled`);
254
+ }
255
+ return null;
256
+ },
257
+ name: "overwriteChecker",
258
+ },
259
+ {
260
+ type: () => (isValidPackageName(path.basename(targetDir || defaultProjectName)) ? null : "text"),
261
+ name: "packageName",
262
+ message: reset("Package name:"),
263
+ initial: () => toValidPackageName(path.basename(targetDir || defaultProjectName)),
264
+ validate: (value) => isValidPackageName(value) || "Invalid package.json name",
265
+ },
266
+ {
267
+ type: selectedTemplate ? null : "select",
268
+ name: "templateId",
269
+ message: reset("Select a template:"),
270
+ choices: TEMPLATE_DEFINITIONS.map((template) => ({
271
+ title: template.title,
272
+ description: template.description,
273
+ value: template.id,
274
+ })),
275
+ },
276
+ {
277
+ type: typeof argv.examples === "boolean" ? null : "toggle",
278
+ name: "includeExamples",
279
+ message: reset("Include examples?"),
280
+ initial: true,
281
+ active: "Yes",
282
+ inactive: "No",
283
+ },
284
+ ],
285
+ {
286
+ onCancel: () => {
287
+ throw new Error(`${red("x")} Operation cancelled`);
288
+ },
289
+ }
290
+ );
291
+ } catch (error) {
292
+ console.log(error.message);
293
+ return undefined;
257
294
  }
258
- }
259
-
260
- function isValidPackageName(projectName) {
261
- return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
262
- projectName
263
- )
264
- }
265
-
266
- function toValidPackageName(projectName) {
267
- return projectName
268
- .trim()
269
- .toLowerCase()
270
- .replace(/\s+/g, '-')
271
- .replace(/^[._]/, '')
272
- .replace(/[^a-z0-9-~]+/g, '-')
273
- }
274
-
275
- function copyDir(srcDir, destDir) {
276
- fs.mkdirSync(destDir, { recursive: true })
277
- for (const file of fs.readdirSync(srcDir)) {
278
- const srcFile = path.resolve(srcDir, file)
279
- const destFile = path.resolve(destDir, file)
280
- copy(srcFile, destFile)
295
+ }
296
+
297
+ async function initNatty() {
298
+ const promptResult = await resolveProjectOptions();
299
+ if (!promptResult) {
300
+ return;
281
301
  }
282
- }
283
-
284
- function isEmpty(path) {
285
- const files = fs.readdirSync(path)
286
- return files.length === 0 || (files.length === 1 && files[0] === '.git')
287
- }
288
-
289
- function emptyDir(dir) {
290
- if (!fs.existsSync(dir)) {
291
- return
302
+
303
+ const targetDir = (promptResult.projectName || argv._[0] || "").trim() || "nattyjs-project";
304
+ const template = getTemplateById(promptResult.templateId || argv.template) || TEMPLATE_DEFINITIONS[0];
305
+ const includeExamples = typeof argv.examples === "boolean" ? argv.examples : promptResult.includeExamples !== false;
306
+ const root = path.join(cwd, targetDir);
307
+
308
+ if (promptResult.overwrite) {
309
+ emptyDir(root);
310
+ } else if (!fs.existsSync(root)) {
311
+ fs.mkdirSync(root, { recursive: true });
292
312
  }
293
- for (const file of fs.readdirSync(dir)) {
294
- const abs = path.resolve(dir, file)
295
- if (fs.lstatSync(abs).isDirectory()) {
296
- emptyDir(abs)
297
- fs.rmdirSync(abs)
298
- } else {
299
- fs.unlinkSync(abs)
300
- }
313
+
314
+ const __filename = fileURLToPath(import.meta.url);
315
+ const __dirname = path.dirname(__filename);
316
+ const templateDir = path.join(__dirname, template.templateDir);
317
+
318
+ console.log(`\nScaffolding project in ${root}...`);
319
+
320
+ copyTemplateContents(templateDir, root, template, includeExamples);
321
+
322
+ const packageJsonPath = path.join(templateDir, "package.json");
323
+ const pkg = readJson(packageJsonPath);
324
+ const packageName = promptResult.packageName || toValidPackageName(path.basename(targetDir));
325
+ pkg.name = packageName;
326
+ fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(pkg, null, 2));
327
+
328
+ const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
329
+ const pkgManager = pkgInfo ? pkgInfo.name : "npm";
330
+
331
+ console.log("\nDone. Now run:\n");
332
+ if (root !== cwd) {
333
+ console.log(` cd ${path.relative(cwd, root)}`);
301
334
  }
302
- }
303
-
304
- function pkgFromUserAgent(userAgent) {
305
- if (!userAgent) return undefined
306
- const pkgSpec = userAgent.split(' ')[0]
307
- const pkgSpecArr = pkgSpec.split('/')
308
- return {
309
- name: pkgSpecArr[0],
310
- version: pkgSpecArr[1]
335
+
336
+ if (pkgManager === "yarn") {
337
+ console.log(" yarn");
338
+ console.log(" yarn dev");
339
+ } else {
340
+ console.log(` ${pkgManager} install`);
341
+ console.log(` ${pkgManager} run dev`);
311
342
  }
312
- }
313
343
 
314
- initNatty().catch((e) => {
315
- console.error(e)
316
- })
344
+ console.log();
345
+ }
346
+
347
+ initNatty().catch((error) => {
348
+ console.error(error);
349
+ });
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "create-nattyjs",
3
- "version": "0.0.1-beta.66",
3
+ "version": "0.0.1-beta.67",
4
4
  "description": "Create NattyJS API Project",
5
5
  "main": "./index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "type": "module",
8
8
  "files": [
9
- "template-nattyjs-blank"
9
+ "template-nattyjs-blank",
10
+ "template-fullstack-vue",
11
+ "template-fullstack-react",
12
+ "template-fullstack-next",
13
+ "template-fullstack-nuxt",
14
+ "template-fullstack-angular",
15
+ "template-fullstack-astro",
16
+ "template-fullstack-sveltekit"
10
17
  ],
11
18
  "scripts": {
12
19
  "test": "echo \"No tests defined\""
@@ -0,0 +1,13 @@
1
+ import { anonymous } from "@nattyjs/core"
2
+
3
+ export class RequestsController {
4
+ @anonymous()
5
+ async get(): Promise<Array<{ name: string }>> {
6
+ return [{ name: "NattyJS" }, { name: "Angular" }]
7
+ }
8
+
9
+ @anonymous()
10
+ async getBy(id: number): Promise<{ name: string; id: number }> {
11
+ return { name: "NattyJS", id }
12
+ }
13
+ }