buner 0.0.2 → 1.0.0

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 (175) hide show
  1. package/.env +15 -0
  2. package/.env.development +6 -0
  3. package/.env.eshn +5 -0
  4. package/README.md +141 -5
  5. package/bin/buner.js +566 -0
  6. package/cli/README.md +1 -0
  7. package/cli/buner.ts +234 -0
  8. package/cli/cli.ts +125 -0
  9. package/cli/create-app.ts +59 -0
  10. package/cli/helpers/copy.ts +62 -0
  11. package/cli/helpers/format-files.ts +189 -0
  12. package/cli/helpers/git.ts +77 -0
  13. package/cli/helpers/install.ts +26 -0
  14. package/cli/helpers/is-folder-empty.ts +40 -0
  15. package/cli/helpers/is-writeable.ts +14 -0
  16. package/cli/helpers/make-dir.ts +7 -0
  17. package/cli/helpers/validate-pkg.ts +17 -0
  18. package/cli/install-template.ts +77 -0
  19. package/eslint.config.mjs +187 -0
  20. package/index.html +44 -0
  21. package/integration.ts +179 -0
  22. package/migrate-scss.ts +42 -0
  23. package/package.json +135 -7
  24. package/prerender.ts +229 -0
  25. package/public/.nojekyll +1 -0
  26. package/public/400.html +1 -0
  27. package/public/401.html +21 -0
  28. package/public/403.html +252 -0
  29. package/public/404.css +51 -0
  30. package/public/404.html +29 -0
  31. package/public/__images__/awww.jpeg +0 -0
  32. package/public/__images__/bat-body.png +0 -0
  33. package/public/__images__/bat-wing.png +0 -0
  34. package/public/__images__/haunted-house-background.png +0 -0
  35. package/public/__images__/haunted-house-foreground.png +0 -0
  36. package/public/assets/fonts/crimson-text/CrimsonText-Bold.ttf +0 -0
  37. package/public/assets/fonts/crimson-text/CrimsonText-BoldItalic.ttf +0 -0
  38. package/public/assets/fonts/crimson-text/CrimsonText-Italic.ttf +0 -0
  39. package/public/assets/fonts/crimson-text/CrimsonText-Regular.ttf +0 -0
  40. package/public/assets/fonts/crimson-text/CrimsonText-SemiBold.ttf +0 -0
  41. package/public/assets/fonts/crimson-text/CrimsonText-SemiBoldItalic.ttf +0 -0
  42. package/public/assets/fonts/crimson-text/CrimsonText.woff2 +0 -0
  43. package/public/assets/fonts/crimson-text/OFL.txt +93 -0
  44. package/public/assets/fonts/work-sans/OFL.txt +93 -0
  45. package/public/assets/fonts/work-sans/README.txt +81 -0
  46. package/public/assets/fonts/work-sans/WorkSans-Italic-VariableFont_wght.ttf +0 -0
  47. package/public/assets/fonts/work-sans/WorkSans-VariableFont_wght.ttf +0 -0
  48. package/public/assets/fonts/work-sans/WorkSans.woff2 +0 -0
  49. package/public/assets/fonts/work-sans/static/WorkSans-Black.ttf +0 -0
  50. package/public/assets/fonts/work-sans/static/WorkSans-BlackItalic.ttf +0 -0
  51. package/public/assets/fonts/work-sans/static/WorkSans-Bold.ttf +0 -0
  52. package/public/assets/fonts/work-sans/static/WorkSans-BoldItalic.ttf +0 -0
  53. package/public/assets/fonts/work-sans/static/WorkSans-ExtraBold.ttf +0 -0
  54. package/public/assets/fonts/work-sans/static/WorkSans-ExtraBoldItalic.ttf +0 -0
  55. package/public/assets/fonts/work-sans/static/WorkSans-ExtraLight.ttf +0 -0
  56. package/public/assets/fonts/work-sans/static/WorkSans-ExtraLightItalic.ttf +0 -0
  57. package/public/assets/fonts/work-sans/static/WorkSans-Italic.ttf +0 -0
  58. package/public/assets/fonts/work-sans/static/WorkSans-Light.ttf +0 -0
  59. package/public/assets/fonts/work-sans/static/WorkSans-LightItalic.ttf +0 -0
  60. package/public/assets/fonts/work-sans/static/WorkSans-Medium.ttf +0 -0
  61. package/public/assets/fonts/work-sans/static/WorkSans-MediumItalic.ttf +0 -0
  62. package/public/assets/fonts/work-sans/static/WorkSans-Regular.ttf +0 -0
  63. package/public/assets/fonts/work-sans/static/WorkSans-SemiBold.ttf +0 -0
  64. package/public/assets/fonts/work-sans/static/WorkSans-SemiBoldItalic.ttf +0 -0
  65. package/public/assets/fonts/work-sans/static/WorkSans-Thin.ttf +0 -0
  66. package/public/assets/fonts/work-sans/static/WorkSans-ThinItalic.ttf +0 -0
  67. package/public/assets/images/icons.svg +67 -0
  68. package/public/assets/images/logo.svg +14 -0
  69. package/public/assets/images/root.svg +49 -0
  70. package/public/assets/vendors/axios@0.24.0/axios.js +2275 -0
  71. package/public/assets/vendors/axios@0.24.0/axios.map +1 -0
  72. package/public/assets/vendors/axios@0.24.0/axios.min.js +2 -0
  73. package/public/assets/vendors/axios@0.24.0/axios.min.map +1 -0
  74. package/public/favicon.ico +0 -0
  75. package/public/favicon.svg +3 -0
  76. package/public/icon-128.png +0 -0
  77. package/public/icon-16.png +0 -0
  78. package/public/icon-192.png +0 -0
  79. package/public/icon-48.png +0 -0
  80. package/public/icon-512.png +0 -0
  81. package/public/json/avatar.json +42 -0
  82. package/public/manifest.webmanifest +29 -0
  83. package/public/mockServiceWorker.js +349 -0
  84. package/public/pl-states.svg +4 -0
  85. package/public/samples/01.svg +1 -0
  86. package/public/samples/Airbnb.svg +3 -0
  87. package/public/samples/Facebook.svg +3 -0
  88. package/public/samples/Google.svg +8 -0
  89. package/public/samples/Microsoft.svg +7 -0
  90. package/public/samples/Spotify.svg +3 -0
  91. package/public/samples/alexandra-stolz.svg +35 -0
  92. package/public/samples/browserconfig.xml +9 -0
  93. package/public/samples/cliff-curtis.jpg +0 -0
  94. package/public/samples/emilia-clarke.jpg +0 -0
  95. package/public/samples/favicon.ico +0 -0
  96. package/public/samples/icons/android-chrome-192x192.png +0 -0
  97. package/public/samples/icons/apple-touch-icon.png +0 -0
  98. package/public/samples/icons/favicon-144x144.png +0 -0
  99. package/public/samples/icons/favicon-150x150.png +0 -0
  100. package/public/samples/icons/favicon-16x16.png +0 -0
  101. package/public/samples/icons/favicon-32x32.png +0 -0
  102. package/public/samples/icons/favicon-48x48.png +0 -0
  103. package/public/samples/icons/favicon-70x70.png +0 -0
  104. package/public/samples/icons/favicon.ico +0 -0
  105. package/public/samples/image-1.svg +166 -0
  106. package/public/samples/image-2.svg +110 -0
  107. package/public/samples/image-3.svg +113 -0
  108. package/public/samples/janet-bray.svg +36 -0
  109. package/public/samples/kate-winslet.jpg +0 -0
  110. package/public/samples/manifest.json +19 -0
  111. package/public/samples/michelle-yeoh.jpg +0 -0
  112. package/public/samples/peg-legge.svg +37 -0
  113. package/public/samples/richard-guerra.svg +42 -0
  114. package/public/samples/rose-leslie.jpg +0 -0
  115. package/public/samples/sample-1.svg +365 -0
  116. package/public/samples/sample-2.svg +129 -0
  117. package/public/samples/sample-3.svg +93 -0
  118. package/public/samples/sample-4.svg +168 -0
  119. package/public/samples/sample-5.svg +155 -0
  120. package/public/samples/sample-6.svg +445 -0
  121. package/public/samples/sample-7.svg +404 -0
  122. package/public/samples/sample-8.png +0 -0
  123. package/public/staticwebapp.config.json +138 -0
  124. package/scripts.ts +56 -0
  125. package/server.ts +29 -0
  126. package/states.ts +63 -0
  127. package/styles.ts +232 -0
  128. package/tsconfig.json +71 -25
  129. package/types.d.ts +54 -0
  130. package/vite.config.ts +3 -0
  131. package/xpack/alias.ts +21 -0
  132. package/xpack/config.ts +59 -0
  133. package/xpack/create-server.ts +68 -0
  134. package/xpack/create-vite-dev-server.ts +33 -0
  135. package/xpack/deploy/deploy-inte.ts +3 -0
  136. package/xpack/filename.ts +43 -0
  137. package/xpack/hooks/build-start.ts +17 -0
  138. package/xpack/hooks/close-bundle.ts +19 -0
  139. package/xpack/hooks/handle-hot-update.ts +22 -0
  140. package/xpack/hooks/options.ts +55 -0
  141. package/xpack/hooks/resolve-dynamic-import.ts +18 -0
  142. package/xpack/hooks/transform-index-html.ts +18 -0
  143. package/xpack/hooks/transform.ts +72 -0
  144. package/xpack/hooks/write-bundle.ts +16 -0
  145. package/xpack/manual-chunk.ts +56 -0
  146. package/xpack/paths.ts +30 -0
  147. package/xpack/renderer.ts +141 -0
  148. package/xpack/root/active-item-options.tsx +98 -0
  149. package/xpack/root/frame-controls.tsx +139 -0
  150. package/xpack/root/index.tsx +107 -0
  151. package/xpack/root/rendered-item.tsx +25 -0
  152. package/xpack/root/root-context.ts +22 -0
  153. package/xpack/root/root-nav.tsx +162 -0
  154. package/xpack/root/state-animation-html.tsx +18 -0
  155. package/xpack/root/template.tsx +23 -0
  156. package/xpack/root/use-click-outside.ts +37 -0
  157. package/xpack/scripts/color-mode.entry.ts +28 -0
  158. package/xpack/scripts/mock-api.entry.ts +11 -0
  159. package/xpack/scripts/pl-states.entry.ts +321 -0
  160. package/xpack/scripts/root.entry.ts +135 -0
  161. package/xpack/scripts/theme-critical.entry.ts +20 -0
  162. package/xpack/states.schema.json +61 -0
  163. package/xpack/styles/_border.scss +22 -0
  164. package/xpack/styles/_breakpoint.scss +117 -0
  165. package/xpack/styles/_form.scss +23 -0
  166. package/xpack/styles/_px2rem.scss +5 -0
  167. package/xpack/styles/_reset.scss +134 -0
  168. package/xpack/styles/_state-toggle.scss +121 -0
  169. package/xpack/styles/_theme.scss +68 -0
  170. package/xpack/styles/_top-panel.scss +87 -0
  171. package/xpack/styles/_xpack-root.scss +322 -0
  172. package/xpack/styles/pl-states.scss +308 -0
  173. package/xpack/styles/root.scss +129 -0
  174. package/.github/workflows/deploy.yaml +0 -32
  175. package/index.ts +0 -1
package/cli/buner.ts ADDED
@@ -0,0 +1,234 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import path from 'path';
4
+ import { execSync, spawn, SpawnOptions } from 'child_process';
5
+
6
+ import { Command } from 'commander';
7
+ import chalk from 'chalk';
8
+ import fetch from 'node-fetch';
9
+ import prompts from 'prompts';
10
+
11
+ import packageJson from '../package.json';
12
+
13
+ import { createApp } from './create-app.js';
14
+ import { validateNpmName } from './helpers/validate-pkg.js';
15
+
16
+ const { green, yellow, bold, cyan, red } = chalk;
17
+ const packageName = 'buner';
18
+
19
+ const run = (cmd: string, args: string[] = [], options: SpawnOptions = {}) => {
20
+ return new Promise<void>((resolve, reject) => {
21
+ const child = spawn(cmd, args, {
22
+ stdio: 'inherit',
23
+ shell: true,
24
+ cwd: process.cwd(),
25
+ ...options,
26
+ });
27
+
28
+ child.on('close', (code) => {
29
+ if (code !== 0) {
30
+ reject(new Error(`Command "${cmd} ${args.join(' ')}" exited with code ${code}`));
31
+ } else {
32
+ resolve();
33
+ }
34
+ });
35
+
36
+ child.on('error', reject);
37
+ });
38
+ };
39
+
40
+ const runSync = (cmd: string) => {
41
+ execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
42
+ };
43
+
44
+ const onPromptState = (state: { value?: string; aborted?: boolean }) => {
45
+ if (state?.aborted) {
46
+ process.stdout.write('\x1B[?25h');
47
+ process.stdout.write('\n');
48
+ process.exit(1);
49
+ }
50
+ };
51
+
52
+ const parseVersion = (version: string): number => {
53
+ return parseInt(version.replaceAll('.', ''));
54
+ };
55
+
56
+ const update = fetch(`https://registry.npmjs.org/${packageJson.name}/latest`)
57
+ .then((res) => res.json())
58
+ .catch(() => null);
59
+
60
+ async function notifyUpdate(): Promise<void> {
61
+ try {
62
+ const data = (await update) as { version: string };
63
+
64
+ if (data.version && parseVersion(data.version) !== parseVersion(packageJson.version)) {
65
+ const updateMessage = `npm update -g ${packageName}`;
66
+
67
+ console.log(
68
+ yellow(bold(`A new version of '${packageName}' is available!`)) + '\n' + 'You can update by running: ' + cyan(updateMessage) + '\n'
69
+ );
70
+ }
71
+ } catch {
72
+ // ignore error
73
+ }
74
+ }
75
+
76
+ const program = new Command();
77
+
78
+ program.name(packageName).description('Frontend build toolkit for Vite + React SSR projects').version(packageJson.version);
79
+
80
+ // buner create [dir]
81
+ program
82
+ .command('create')
83
+ .argument('[project-directory]', 'the project name', '')
84
+ .description('Scaffold a new frontend project')
85
+ .action(async (projectPath: string) => {
86
+ if (!projectPath) {
87
+ const validation = validateNpmName('my-app');
88
+
89
+ const res = await prompts({
90
+ onState: onPromptState,
91
+ type: 'text',
92
+ name: 'path',
93
+ message: 'What is your project named?',
94
+ initial: 'my-app',
95
+ validate: (name) => {
96
+ const validation = validateNpmName(path.basename(path.resolve(name)));
97
+
98
+ if (validation.valid) {
99
+ return true;
100
+ }
101
+
102
+ return `Invalid project name ${validation?.problems?.[0] ? validation?.problems?.[0] : ''}`;
103
+ },
104
+ });
105
+
106
+ if (typeof res.path === 'string') {
107
+ projectPath = res.path.trim();
108
+ }
109
+ }
110
+
111
+ if (!projectPath) {
112
+ console.log(
113
+ '\nPlease specify the project directory:\n' +
114
+ ` ${cyan('buner create')} ${green('<project-directory>')}\n` +
115
+ 'For example:\n' +
116
+ ` ${cyan('buner create')} ${green('my-app')}\n`
117
+ );
118
+ process.exit(1);
119
+ }
120
+
121
+ const resolvedProjectPath = path.resolve(projectPath);
122
+
123
+ await createApp({ appPath: resolvedProjectPath });
124
+ await notifyUpdate();
125
+ });
126
+
127
+ // buner dev
128
+ program
129
+ .command('dev')
130
+ .description('Start development mode with all watchers')
131
+ .action(async () => {
132
+ await run('npx', [
133
+ 'concurrently',
134
+ '--kill-others',
135
+ '"bun styles.ts --watch"',
136
+ '"bun states.ts --watch"',
137
+ '"cross-env scriptOnly=true npx vite build --mode development --watch"',
138
+ '"bun server.ts --mode development"',
139
+ ]);
140
+ });
141
+
142
+ // buner serve
143
+ program
144
+ .command('serve')
145
+ .description('Start the SSR dev server')
146
+ .option('--mode <mode>', 'server mode', 'development')
147
+ .action(async (opts) => {
148
+ await run('bun', ['server.ts', '--mode', opts.mode]);
149
+ });
150
+
151
+ // buner build
152
+ program
153
+ .command('build')
154
+ .description('Build the project (static + SSR)')
155
+ .action(async () => {
156
+ runSync('npx vite build --outDir dist/static');
157
+ runSync('npx vite build --ssr src/entry-server.tsx --outDir dist/server');
158
+ });
159
+
160
+ // buner generate
161
+ program
162
+ .command('generate')
163
+ .description('Full static site generation (states + styles + build + prerender)')
164
+ .option('--mode <mode>', 'build mode', 'production')
165
+ .action(async (opts) => {
166
+ runSync('bun states.ts');
167
+ runSync('bun styles.ts');
168
+ if (opts.mode === 'production') {
169
+ runSync('npx vite build --outDir dist/static');
170
+ runSync('npx vite build --ssr src/entry-server.tsx --outDir dist/server');
171
+ } else {
172
+ runSync(`npx vite build --outDir dist/static --mode ${opts.mode}`);
173
+ runSync(`npx vite build --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
174
+ }
175
+ runSync(`bun prerender.ts --add-hash --mode ${opts.mode}`);
176
+ });
177
+
178
+ // buner eshn
179
+ program
180
+ .command('eshn')
181
+ .description('Generate with --mode eshn')
182
+ .action(async () => {
183
+ runSync('bun states.ts');
184
+ runSync('bun styles.ts');
185
+ runSync('npx vite build --outDir dist/static --mode eshn');
186
+ runSync('npx vite build --ssr src/entry-server.tsx --outDir dist/server --mode eshn');
187
+ runSync('bun prerender.ts --add-hash --mode eshn');
188
+ });
189
+
190
+ // buner inte
191
+ program
192
+ .command('inte')
193
+ .description('Build and integrate with backend (styles + build + prerender + integration)')
194
+ .action(async () => {
195
+ runSync('bun styles.ts');
196
+ runSync('npx vite build --outDir dist/static');
197
+ runSync('npx vite build --ssr src/entry-server.tsx --outDir dist/server');
198
+ runSync('bun prerender.ts');
199
+ runSync('bun integration.ts');
200
+ });
201
+
202
+ // buner styles
203
+ program
204
+ .command('styles')
205
+ .description('Compile SCSS')
206
+ .option('--watch', 'Watch for changes')
207
+ .action(async (opts) => {
208
+ const args = ['styles.ts'];
209
+
210
+ if (opts.watch) args.push('--watch');
211
+ await run('bun', args);
212
+ });
213
+
214
+ // buner prerender
215
+ program
216
+ .command('prerender')
217
+ .description('Pre-render HTML files')
218
+ .option('--add-hash', 'Add content hashes to asset URLs')
219
+ .option('--mode <mode>', 'build mode', 'production')
220
+ .action(async (opts) => {
221
+ const args = ['prerender.ts'];
222
+
223
+ if (opts.addHash) args.push('--add-hash');
224
+ args.push('--mode', opts.mode);
225
+ await run('bun', args);
226
+ });
227
+
228
+ program.parseAsync(process.argv).catch(async (error) => {
229
+ console.log(red(error));
230
+ await notifyUpdate();
231
+ process.exit(1);
232
+ });
233
+
234
+ export { packageName };
package/cli/cli.ts ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ import path from 'path';
5
+
6
+ import { Command } from 'commander';
7
+ import chalk from 'chalk';
8
+ import fetch from 'node-fetch';
9
+ import prompts from 'prompts';
10
+
11
+ import packageJson from '../package.json';
12
+
13
+ import { createApp } from './create-app.js';
14
+ import { validateNpmName } from './helpers/validate-pkg.js';
15
+
16
+ let projectPath = '';
17
+ const program = new Command();
18
+ const { green, yellow, bold, cyan, red } = chalk;
19
+ const packageName = 'x-pkg';
20
+
21
+ const onPromptState = (state: { value?: string; aborted?: boolean }) => {
22
+ if (state?.aborted) {
23
+ // If we don't re-enable the terminal cursor before exiting
24
+ // the program, the cursor will remain hidden
25
+ process.stdout.write('\x1B[?25h');
26
+ process.stdout.write('\n');
27
+ process.exit(1);
28
+ }
29
+ };
30
+
31
+ program
32
+ .name(packageName)
33
+ .description('Create a FE framework')
34
+ .version(packageJson.version)
35
+ .argument('[project-directory]', 'the project name', '')
36
+ .usage(`${green('[project-directory]')} [options]`)
37
+ .action((name) => {
38
+ projectPath = name;
39
+ })
40
+ .allowUnknownOption()
41
+ .parse(process.argv);
42
+
43
+ const parseVersion = (version: string): number => {
44
+ return parseInt(version.replaceAll('.', ''));
45
+ };
46
+
47
+ const update = fetch(`https://registry.npmjs.org/${packageJson.name}/latest`)
48
+ .then((res) => res.json())
49
+ .catch(() => null);
50
+
51
+ async function notifyUpdate(): Promise<void> {
52
+ try {
53
+ const data = (await update) as { version: string };
54
+
55
+ if (data.version && parseVersion(data.version) !== parseVersion(packageJson.version)) {
56
+ const updateMessage = `npm update ${packageName}`;
57
+
58
+ console.log(
59
+ yellow(bold(`A new version of '${packageName}' is available!`)) + '\n' + 'You can update by running: ' + cyan(updateMessage) + '\n'
60
+ );
61
+ }
62
+
63
+ process.exit();
64
+ } catch {
65
+ // ignore error
66
+ }
67
+ }
68
+
69
+ const run = async () => {
70
+ const validation = validateNpmName(path.basename(path.resolve(projectPath)));
71
+
72
+ if (!validation.valid) {
73
+ const res = await prompts({
74
+ onState: onPromptState,
75
+ type: 'text',
76
+ name: 'path',
77
+ message: 'What is your project named?',
78
+ initial: 'my-app',
79
+ validate: (name) => {
80
+ const validation = validateNpmName(path.basename(path.resolve(name)));
81
+
82
+ if (validation.valid) {
83
+ return true;
84
+ }
85
+
86
+ return `Invalid project name ${validation?.problems?.[0] ? validation?.problems?.[0] : ''}`;
87
+ },
88
+ });
89
+
90
+ if (typeof res.path === 'string') {
91
+ projectPath = res.path.trim();
92
+ }
93
+ }
94
+
95
+ if (!projectPath) {
96
+ console.log(
97
+ '\nPlease specify the project directory:\n' +
98
+ ` ${cyan(program.name())} ${green('<project-directory>')}\n` +
99
+ 'For example:\n' +
100
+ ` ${cyan(program.name())} ${green('my-app')}\n\n` +
101
+ `Run ${cyan(`${program.name()} --help`)} to see all options.`
102
+ );
103
+ process.exit(1);
104
+ }
105
+
106
+ const resolvedProjectPath = path.resolve(projectPath);
107
+
108
+ await createApp({
109
+ appPath: resolvedProjectPath,
110
+ });
111
+ };
112
+
113
+ run()
114
+ .then(async () => {
115
+ await notifyUpdate();
116
+ })
117
+ .catch(async (error) => {
118
+ console.log(red(error));
119
+
120
+ await notifyUpdate();
121
+
122
+ process.exit(1);
123
+ });
124
+
125
+ export { packageName };
@@ -0,0 +1,59 @@
1
+ /* eslint-disable no-console */
2
+ import path from 'path';
3
+
4
+ import chalk from 'chalk';
5
+
6
+ import { isWriteable } from './helpers/is-writeable.js';
7
+ import { makeDir } from './helpers/make-dir.js';
8
+ import { isFolderEmpty } from './helpers/is-folder-empty.js';
9
+ import { tryGitInit } from './helpers/git.js';
10
+ import { installTemplate } from './install-template.js';
11
+
12
+ interface Props {
13
+ appPath: string;
14
+ }
15
+
16
+ const { green } = chalk;
17
+
18
+ const createApp = async (model: Props) => {
19
+ const { appPath } = model;
20
+ const root = path.resolve(appPath);
21
+
22
+ if (!(await isWriteable(path.dirname(root)))) {
23
+ console.error('The application path is not writable, please check folder permissions and try again.');
24
+
25
+ console.error('It is likely you do not have write permissions for this folder.');
26
+
27
+ process.exit(1);
28
+ }
29
+
30
+ const appName = path.basename(root);
31
+
32
+ await makeDir(root);
33
+
34
+ if (!isFolderEmpty(root)) {
35
+ console.log(`\nThe directory ${green(appName)} contains files that could conflict or not empty`);
36
+ console.log('\nEither try using a new directory name, or remove these files.');
37
+
38
+ process.exit(1);
39
+ }
40
+
41
+ console.log(`\nCreating a new app in ${green(root)}.`);
42
+
43
+ process.chdir(root);
44
+
45
+ await installTemplate({
46
+ appName,
47
+ root,
48
+ });
49
+
50
+ console.log('\nInitializing a git repository.');
51
+
52
+ if (tryGitInit(root)) {
53
+ console.log('\nInitialized a git repository.');
54
+ }
55
+
56
+ console.log(`\n${green('Success!')} Created ${appName} at ${appPath}`);
57
+ };
58
+
59
+ export { createApp };
@@ -0,0 +1,62 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+
4
+ import { globby } from 'globby';
5
+
6
+ interface CopyOption {
7
+ cwd: string;
8
+ }
9
+
10
+ const excludeFiles = [
11
+ '.git',
12
+ 'bin',
13
+ '.vscode',
14
+ '.build',
15
+ 'public/samples',
16
+ 'public/assets/vendors',
17
+ 'src/_api/!(_base.ts)',
18
+ 'src/_data',
19
+ 'src/assets/scripts/!(color-mode|main|mock-api|pl-states|root|theme-critical).entry.ts',
20
+ 'src/atoms',
21
+ 'src/mocks/avatar',
22
+ 'src/mocks/user',
23
+ 'src/molecules',
24
+ 'src/organisms/!(root|header|footer)/*',
25
+ 'src/pages/!(Root|Home).tsx',
26
+ 'src/templates/!(root|home)/*',
27
+ '!cli',
28
+ '!vite.cli.config.ts',
29
+ ];
30
+
31
+ const copy = async (src: string | string[], dest: string, { cwd }: CopyOption) => {
32
+ const sourceFiles = await globby(src, {
33
+ cwd,
34
+ dot: true,
35
+ absolute: false,
36
+ gitignore: true,
37
+ ignore: excludeFiles,
38
+ });
39
+
40
+ const destRelativeToCwd = path.resolve(dest);
41
+
42
+ await fs.mkdir(path.join(destRelativeToCwd, 'src/atoms'), { recursive: true });
43
+ await fs.mkdir(path.join(destRelativeToCwd, 'src/molecules'), { recursive: true });
44
+ await fs.mkdir(path.join(destRelativeToCwd, 'src/mocks/example'), { recursive: true });
45
+
46
+ return Promise.all(
47
+ sourceFiles.map(async (p) => {
48
+ const dirname = path.dirname(p);
49
+ const basename = path.basename(p);
50
+
51
+ const from = path.resolve(cwd, p);
52
+ const filePath = path.join(destRelativeToCwd, dirname, basename);
53
+
54
+ // Ensure the destination directory exists
55
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
56
+
57
+ return fs.copyFile(from, filePath);
58
+ })
59
+ );
60
+ };
61
+
62
+ export { copy };
@@ -0,0 +1,189 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import fs from 'fs/promises';
4
+
5
+ const formatFiles = async (root: string) => {
6
+ let filePath, data;
7
+
8
+ // mock handler file
9
+ filePath = path.join(root, 'src/mocks/handlers.ts');
10
+ data = "import { handlers } from './consts';\n\nexport { handlers };";
11
+
12
+ await fs.writeFile(filePath, data + os.EOL);
13
+
14
+ // example mock file
15
+ filePath = path.join(root, 'src/mocks/example/index.ts');
16
+ data = `
17
+ import { handlers } from '@mocks/handlers';
18
+ import { rest } from 'msw';
19
+
20
+ handlers.push(
21
+ rest.get('/api/example', (req, res, ctx) => {
22
+ return res(ctx.status(200), ctx.json({test: 'test'}));
23
+ })
24
+ );
25
+ `;
26
+
27
+ await fs.writeFile(filePath, data + os.EOL);
28
+
29
+ // react loader
30
+ filePath = path.join(root, 'src/react-loader.tsx');
31
+ data = await fs.readFile(filePath, 'utf8');
32
+ data = data.replace(
33
+ /const blocks((?!};).)*};/s,
34
+ "const blocks: { [name: string]: any } = {\n\troot: lazy(() => import('./organisms/root/Root')),\n};"
35
+ );
36
+
37
+ await fs.writeFile(filePath, data);
38
+
39
+ // src/_types/atoms.d.ts
40
+ filePath = path.join(root, 'src/_types/atoms.d.ts');
41
+
42
+ await fs.writeFile(
43
+ filePath,
44
+ `
45
+ import { BasedAtomicModel } from "./_general";
46
+ `
47
+ );
48
+
49
+ // src/_types/molecules.d.d.ts
50
+ filePath = path.join(root, 'src/_types/molecules.d.ts');
51
+
52
+ await fs.writeFile(
53
+ filePath,
54
+ `
55
+ import { BasedAtomicModel } from "./_general";
56
+ `
57
+ );
58
+
59
+ // src/_types/organisms.d.ts
60
+ filePath = path.join(root, 'src/_types/organisms.d.ts');
61
+
62
+ await fs.writeFile(
63
+ filePath,
64
+ `
65
+ import { BasedAtomicModel } from "./_general";
66
+
67
+ interface FooterModel extends BasedAtomicModel {}
68
+
69
+ interface HeaderModel extends BasedAtomicModel {}
70
+ `
71
+ );
72
+
73
+ // pages/Home.tsx
74
+ filePath = path.join(root, 'src/pages/Home.tsx');
75
+
76
+ await fs.writeFile(
77
+ filePath,
78
+ `
79
+ import Template from '@templates/home/Home';
80
+
81
+ const Home = () => {
82
+ return <Template footer={footer} header={header} />;
83
+ };
84
+
85
+ export default Home;
86
+ `
87
+ );
88
+
89
+ // templates/home/Home.tsx
90
+ filePath = path.join(root, 'src/templates/home/Home.tsx');
91
+
92
+ await fs.writeFile(
93
+ filePath,
94
+ `
95
+ import { FooterModel, HeaderModel } from '@_types/organisms';
96
+ import Footer from '@organisms/footer/Footer';
97
+ import Header from '@organisms/header/Header';
98
+
99
+ interface Props {
100
+ header?: HeaderModel;
101
+ footer?: FooterModel;
102
+ }
103
+
104
+ const Home = (model: Props) => {
105
+ const { header, footer } = model;
106
+
107
+ return (
108
+ <>
109
+ {header && <Header {...header} />}
110
+
111
+ <main>
112
+ // write components here
113
+ </main>
114
+
115
+ <Footer {...footer} />
116
+ </>
117
+ );
118
+ };
119
+
120
+ export default Home;
121
+ `
122
+ );
123
+
124
+ // src/organisms/header/Header.tsx
125
+ filePath = path.join(root, 'src/organisms/header/Header.tsx');
126
+
127
+ await fs.writeFile(
128
+ filePath,
129
+ `
130
+ import { getModifiers } from '@helpers/functions';
131
+ import RequireCss from '@helpers/RequireCss';
132
+ import { HeaderModel } from '@_types/organisms';
133
+
134
+ const Header = (model: HeaderModel) => {
135
+ const modifiers = getModifiers(model, 'zzz-o-header');
136
+
137
+ return (
138
+ <header className={modifiers}>
139
+ <h2>Header</h2>
140
+ <RequireCss path="b-header" />
141
+ </header>
142
+ );
143
+ };
144
+
145
+ export default Header;
146
+ `
147
+ );
148
+
149
+ // src/organisms/header/Header.scss
150
+ filePath = path.join(root, 'src/organisms/header/Header.scss');
151
+
152
+ await fs.writeFile(filePath, '');
153
+
154
+ // src/organisms/footer/Footer.tsx
155
+ filePath = path.join(root, 'src/organisms/footer/Footer.tsx');
156
+
157
+ await fs.writeFile(
158
+ filePath,
159
+ `
160
+ import { getModifiers } from '@helpers/functions';
161
+ import RequireCss from '@helpers/RequireCss';
162
+ import { FooterModel } from '@_types/organisms';
163
+
164
+ const Footer = (model: FooterModel) => {
165
+ const modifiers = getModifiers(model, 'zzz-o-footer');
166
+
167
+ return (
168
+ <footer className={modifiers}>
169
+ <h2>Footer</h2>
170
+ <RequireCss path="b-footer" />
171
+ </footer>
172
+ );
173
+ };
174
+
175
+ export default Footer;
176
+ `
177
+ );
178
+
179
+ // src/organisms/footer/Footer.scss
180
+ filePath = path.join(root, 'src/organisms/footer/Footer.scss');
181
+
182
+ await fs.writeFile(filePath, '');
183
+
184
+ return ['xxx'];
185
+ };
186
+
187
+ // formatFiles('E:\\projects\\test-x-pkg');
188
+
189
+ export { formatFiles };