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.
- package/.env +15 -0
- package/.env.development +6 -0
- package/.env.eshn +5 -0
- package/README.md +141 -5
- package/bin/buner.js +566 -0
- package/cli/README.md +1 -0
- package/cli/buner.ts +234 -0
- package/cli/cli.ts +125 -0
- package/cli/create-app.ts +59 -0
- package/cli/helpers/copy.ts +62 -0
- package/cli/helpers/format-files.ts +189 -0
- package/cli/helpers/git.ts +77 -0
- package/cli/helpers/install.ts +26 -0
- package/cli/helpers/is-folder-empty.ts +40 -0
- package/cli/helpers/is-writeable.ts +14 -0
- package/cli/helpers/make-dir.ts +7 -0
- package/cli/helpers/validate-pkg.ts +17 -0
- package/cli/install-template.ts +77 -0
- package/eslint.config.mjs +187 -0
- package/index.html +44 -0
- package/integration.ts +179 -0
- package/migrate-scss.ts +42 -0
- package/package.json +135 -7
- package/prerender.ts +229 -0
- package/public/.nojekyll +1 -0
- package/public/400.html +1 -0
- package/public/401.html +21 -0
- package/public/403.html +252 -0
- package/public/404.css +51 -0
- package/public/404.html +29 -0
- package/public/__images__/awww.jpeg +0 -0
- package/public/__images__/bat-body.png +0 -0
- package/public/__images__/bat-wing.png +0 -0
- package/public/__images__/haunted-house-background.png +0 -0
- package/public/__images__/haunted-house-foreground.png +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Bold.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-BoldItalic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Italic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Regular.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-SemiBold.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-SemiBoldItalic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText.woff2 +0 -0
- package/public/assets/fonts/crimson-text/OFL.txt +93 -0
- package/public/assets/fonts/work-sans/OFL.txt +93 -0
- package/public/assets/fonts/work-sans/README.txt +81 -0
- package/public/assets/fonts/work-sans/WorkSans-Italic-VariableFont_wght.ttf +0 -0
- package/public/assets/fonts/work-sans/WorkSans-VariableFont_wght.ttf +0 -0
- package/public/assets/fonts/work-sans/WorkSans.woff2 +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Black.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-BlackItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Bold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-BoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraBold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraBoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraLight.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraLightItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Italic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Light.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-LightItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Medium.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-MediumItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Regular.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-SemiBold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-SemiBoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Thin.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ThinItalic.ttf +0 -0
- package/public/assets/images/icons.svg +67 -0
- package/public/assets/images/logo.svg +14 -0
- package/public/assets/images/root.svg +49 -0
- package/public/assets/vendors/axios@0.24.0/axios.js +2275 -0
- package/public/assets/vendors/axios@0.24.0/axios.map +1 -0
- package/public/assets/vendors/axios@0.24.0/axios.min.js +2 -0
- package/public/assets/vendors/axios@0.24.0/axios.min.map +1 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +3 -0
- package/public/icon-128.png +0 -0
- package/public/icon-16.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-48.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/json/avatar.json +42 -0
- package/public/manifest.webmanifest +29 -0
- package/public/mockServiceWorker.js +349 -0
- package/public/pl-states.svg +4 -0
- package/public/samples/01.svg +1 -0
- package/public/samples/Airbnb.svg +3 -0
- package/public/samples/Facebook.svg +3 -0
- package/public/samples/Google.svg +8 -0
- package/public/samples/Microsoft.svg +7 -0
- package/public/samples/Spotify.svg +3 -0
- package/public/samples/alexandra-stolz.svg +35 -0
- package/public/samples/browserconfig.xml +9 -0
- package/public/samples/cliff-curtis.jpg +0 -0
- package/public/samples/emilia-clarke.jpg +0 -0
- package/public/samples/favicon.ico +0 -0
- package/public/samples/icons/android-chrome-192x192.png +0 -0
- package/public/samples/icons/apple-touch-icon.png +0 -0
- package/public/samples/icons/favicon-144x144.png +0 -0
- package/public/samples/icons/favicon-150x150.png +0 -0
- package/public/samples/icons/favicon-16x16.png +0 -0
- package/public/samples/icons/favicon-32x32.png +0 -0
- package/public/samples/icons/favicon-48x48.png +0 -0
- package/public/samples/icons/favicon-70x70.png +0 -0
- package/public/samples/icons/favicon.ico +0 -0
- package/public/samples/image-1.svg +166 -0
- package/public/samples/image-2.svg +110 -0
- package/public/samples/image-3.svg +113 -0
- package/public/samples/janet-bray.svg +36 -0
- package/public/samples/kate-winslet.jpg +0 -0
- package/public/samples/manifest.json +19 -0
- package/public/samples/michelle-yeoh.jpg +0 -0
- package/public/samples/peg-legge.svg +37 -0
- package/public/samples/richard-guerra.svg +42 -0
- package/public/samples/rose-leslie.jpg +0 -0
- package/public/samples/sample-1.svg +365 -0
- package/public/samples/sample-2.svg +129 -0
- package/public/samples/sample-3.svg +93 -0
- package/public/samples/sample-4.svg +168 -0
- package/public/samples/sample-5.svg +155 -0
- package/public/samples/sample-6.svg +445 -0
- package/public/samples/sample-7.svg +404 -0
- package/public/samples/sample-8.png +0 -0
- package/public/staticwebapp.config.json +138 -0
- package/scripts.ts +56 -0
- package/server.ts +29 -0
- package/states.ts +63 -0
- package/styles.ts +232 -0
- package/tsconfig.json +71 -25
- package/types.d.ts +54 -0
- package/vite.config.ts +3 -0
- package/xpack/alias.ts +21 -0
- package/xpack/config.ts +59 -0
- package/xpack/create-server.ts +68 -0
- package/xpack/create-vite-dev-server.ts +33 -0
- package/xpack/deploy/deploy-inte.ts +3 -0
- package/xpack/filename.ts +43 -0
- package/xpack/hooks/build-start.ts +17 -0
- package/xpack/hooks/close-bundle.ts +19 -0
- package/xpack/hooks/handle-hot-update.ts +22 -0
- package/xpack/hooks/options.ts +55 -0
- package/xpack/hooks/resolve-dynamic-import.ts +18 -0
- package/xpack/hooks/transform-index-html.ts +18 -0
- package/xpack/hooks/transform.ts +72 -0
- package/xpack/hooks/write-bundle.ts +16 -0
- package/xpack/manual-chunk.ts +56 -0
- package/xpack/paths.ts +30 -0
- package/xpack/renderer.ts +141 -0
- package/xpack/root/active-item-options.tsx +98 -0
- package/xpack/root/frame-controls.tsx +139 -0
- package/xpack/root/index.tsx +107 -0
- package/xpack/root/rendered-item.tsx +25 -0
- package/xpack/root/root-context.ts +22 -0
- package/xpack/root/root-nav.tsx +162 -0
- package/xpack/root/state-animation-html.tsx +18 -0
- package/xpack/root/template.tsx +23 -0
- package/xpack/root/use-click-outside.ts +37 -0
- package/xpack/scripts/color-mode.entry.ts +28 -0
- package/xpack/scripts/mock-api.entry.ts +11 -0
- package/xpack/scripts/pl-states.entry.ts +321 -0
- package/xpack/scripts/root.entry.ts +135 -0
- package/xpack/scripts/theme-critical.entry.ts +20 -0
- package/xpack/states.schema.json +61 -0
- package/xpack/styles/_border.scss +22 -0
- package/xpack/styles/_breakpoint.scss +117 -0
- package/xpack/styles/_form.scss +23 -0
- package/xpack/styles/_px2rem.scss +5 -0
- package/xpack/styles/_reset.scss +134 -0
- package/xpack/styles/_state-toggle.scss +121 -0
- package/xpack/styles/_theme.scss +68 -0
- package/xpack/styles/_top-panel.scss +87 -0
- package/xpack/styles/_xpack-root.scss +322 -0
- package/xpack/styles/pl-states.scss +308 -0
- package/xpack/styles/root.scss +129 -0
- package/.github/workflows/deploy.yaml +0 -32
- 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 };
|