buner 1.0.5 → 1.0.7

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/cli/buner.ts DELETED
@@ -1,264 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- import path from 'path';
4
- import { execSync, spawn, SpawnOptions } from 'child_process';
5
- import { fileURLToPath } from 'url';
6
-
7
- import { Command } from 'commander';
8
- import chalk from 'chalk';
9
- import fetch from 'node-fetch';
10
- import prompts from 'prompts';
11
-
12
- import packageJson from '../package.json';
13
-
14
- import { createApp } from './create-app.js';
15
- import { validateNpmName } from './helpers/validate-pkg.js';
16
-
17
- const { green, yellow, bold, cyan, red } = chalk;
18
- const packageName = 'buner';
19
-
20
- // Package's own directory (where server.ts, styles.ts, etc. live)
21
- const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
22
-
23
- /** Resolve a file path relative to the buner package directory */
24
- const pkg = (file: string) => path.join(packageDir, file);
25
-
26
- /** Resolve a binary from buner's own node_modules */
27
- const bin = (name: string) => {
28
- const ext = process.platform === 'win32' ? '.cmd' : '';
29
- const local = path.join(packageDir, 'node_modules', '.bin', name + ext);
30
-
31
- try {
32
- if (require('fs').existsSync(local)) return local;
33
- } catch {
34
- // ignore
35
- }
36
-
37
- // Fallback: try to find it in the consumer's node_modules
38
- const consumerBin = path.join(process.cwd(), 'node_modules', '.bin', name + ext);
39
-
40
- try {
41
- if (require('fs').existsSync(consumerBin)) return consumerBin;
42
- } catch {
43
- // ignore
44
- }
45
-
46
- // Last resort: rely on PATH
47
- return name;
48
- };
49
-
50
- const run = (cmd: string, args: string[] = [], options: SpawnOptions = {}) => {
51
- return new Promise<void>((resolve, reject) => {
52
- const child = spawn(cmd, args, {
53
- stdio: 'inherit',
54
- shell: true,
55
- cwd: process.cwd(),
56
- ...options,
57
- });
58
-
59
- child.on('close', (code) => {
60
- if (code !== 0) {
61
- reject(new Error(`Command "${cmd} ${args.join(' ')}" exited with code ${code}`));
62
- } else {
63
- resolve();
64
- }
65
- });
66
-
67
- child.on('error', reject);
68
- });
69
- };
70
-
71
- const runSync = (cmd: string) => {
72
- execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
73
- };
74
-
75
- const onPromptState = (state: { value?: string; aborted?: boolean }) => {
76
- if (state?.aborted) {
77
- process.stdout.write('\x1B[?25h');
78
- process.stdout.write('\n');
79
- process.exit(1);
80
- }
81
- };
82
-
83
- const parseVersion = (version: string): number => {
84
- return parseInt(version.replaceAll('.', ''));
85
- };
86
-
87
- const update = fetch(`https://registry.npmjs.org/${packageJson.name}/latest`)
88
- .then((res) => res.json())
89
- .catch(() => null);
90
-
91
- async function notifyUpdate(): Promise<void> {
92
- try {
93
- const data = (await update) as { version: string };
94
-
95
- if (data.version && parseVersion(data.version) !== parseVersion(packageJson.version)) {
96
- const updateMessage = `npm update -g ${packageName}`;
97
-
98
- console.log(
99
- yellow(bold(`A new version of '${packageName}' is available!`)) + '\n' + 'You can update by running: ' + cyan(updateMessage) + '\n'
100
- );
101
- }
102
- } catch {
103
- // ignore error
104
- }
105
- }
106
-
107
- const program = new Command();
108
-
109
- program.name(packageName).description('Frontend build toolkit for Vite + React SSR projects').version(packageJson.version);
110
-
111
- // buner create [dir]
112
- program
113
- .command('create')
114
- .argument('[project-directory]', 'the project name', '')
115
- .description('Scaffold a new frontend project')
116
- .action(async (projectPath: string) => {
117
- if (!projectPath) {
118
- const validation = validateNpmName('my-app');
119
-
120
- const res = await prompts({
121
- onState: onPromptState,
122
- type: 'text',
123
- name: 'path',
124
- message: 'What is your project named?',
125
- initial: 'my-app',
126
- validate: (name) => {
127
- const validation = validateNpmName(path.basename(path.resolve(name)));
128
-
129
- if (validation.valid) {
130
- return true;
131
- }
132
-
133
- return `Invalid project name ${validation?.problems?.[0] ? validation?.problems?.[0] : ''}`;
134
- },
135
- });
136
-
137
- if (typeof res.path === 'string') {
138
- projectPath = res.path.trim();
139
- }
140
- }
141
-
142
- if (!projectPath) {
143
- console.log(
144
- '\nPlease specify the project directory:\n' +
145
- ` ${cyan('buner create')} ${green('<project-directory>')}\n` +
146
- 'For example:\n' +
147
- ` ${cyan('buner create')} ${green('my-app')}\n`
148
- );
149
- process.exit(1);
150
- }
151
-
152
- const resolvedProjectPath = path.resolve(projectPath);
153
-
154
- await createApp({ appPath: resolvedProjectPath });
155
- await notifyUpdate();
156
- });
157
-
158
- // buner dev
159
- program
160
- .command('dev')
161
- .description('Start development mode with all watchers')
162
- .action(async () => {
163
- await run(bin('concurrently'), [
164
- '--kill-others',
165
- `"${bin('tsx')} ${pkg('styles.ts')} --watch"`,
166
- `"${bin('tsx')} ${pkg('states.ts')} --watch"`,
167
- `"${bin('cross-env')} scriptOnly=true ${bin('vite')} build --config ${pkg('vite.config.ts')} --mode development --watch"`,
168
- `"${bin('tsx')} ${pkg('server.ts')} --mode development"`,
169
- ]);
170
- });
171
-
172
- // buner serve
173
- program
174
- .command('serve')
175
- .description('Start the SSR dev server')
176
- .option('--mode <mode>', 'server mode', 'development')
177
- .action(async (opts) => {
178
- await run(bin('tsx'), [pkg('server.ts'), '--mode', opts.mode]);
179
- });
180
-
181
- // buner build
182
- program
183
- .command('build')
184
- .description('Build the project (static + SSR)')
185
- .action(async () => {
186
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --outDir dist/static`);
187
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
188
- });
189
-
190
- // buner generate
191
- program
192
- .command('generate')
193
- .description('Full static site generation (states + styles + build + prerender)')
194
- .option('--mode <mode>', 'build mode', 'production')
195
- .action(async (opts) => {
196
- runSync(`${bin('tsx')} ${pkg('states.ts')}`);
197
- runSync(`${bin('tsx')} ${pkg('styles.ts')}`);
198
- if (opts.mode === 'production') {
199
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --outDir dist/static`);
200
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
201
- } else {
202
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --outDir dist/static --mode ${opts.mode}`);
203
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
204
- }
205
- runSync(`${bin('tsx')} ${pkg('prerender.ts')} --add-hash --mode ${opts.mode}`);
206
- });
207
-
208
- // buner eshn
209
- program
210
- .command('eshn')
211
- .description('Generate with --mode eshn')
212
- .action(async () => {
213
- runSync(`${bin('tsx')} ${pkg('states.ts')}`);
214
- runSync(`${bin('tsx')} ${pkg('styles.ts')}`);
215
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --outDir dist/static --mode eshn`);
216
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server --mode eshn`);
217
- runSync(`${bin('tsx')} ${pkg('prerender.ts')} --add-hash --mode eshn`);
218
- });
219
-
220
- // buner inte
221
- program
222
- .command('inte')
223
- .description('Build and integrate with backend (styles + build + prerender + integration)')
224
- .action(async () => {
225
- runSync(`${bin('tsx')} ${pkg('styles.ts')}`);
226
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --outDir dist/static`);
227
- runSync(`${bin('vite')} build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
228
- runSync(`${bin('tsx')} ${pkg('prerender.ts')}`);
229
- runSync(`${bin('tsx')} ${pkg('integration.ts')}`);
230
- });
231
-
232
- // buner styles
233
- program
234
- .command('styles')
235
- .description('Compile SCSS')
236
- .option('--watch', 'Watch for changes')
237
- .action(async (opts) => {
238
- const args = [pkg('styles.ts')];
239
-
240
- if (opts.watch) args.push('--watch');
241
- await run(bin('tsx'), args);
242
- });
243
-
244
- // buner prerender
245
- program
246
- .command('prerender')
247
- .description('Pre-render HTML files')
248
- .option('--add-hash', 'Add content hashes to asset URLs')
249
- .option('--mode <mode>', 'build mode', 'production')
250
- .action(async (opts) => {
251
- const args = [pkg('prerender.ts')];
252
-
253
- if (opts.addHash) args.push('--add-hash');
254
- args.push('--mode', opts.mode);
255
- await run(bin('tsx'), args);
256
- });
257
-
258
- program.parseAsync(process.argv).catch(async (error) => {
259
- console.log(red(error));
260
- await notifyUpdate();
261
- process.exit(1);
262
- });
263
-
264
- export { packageName };
package/cli/cli.ts DELETED
@@ -1,125 +0,0 @@
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 };
package/cli/create-app.ts DELETED
@@ -1,59 +0,0 @@
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 };
@@ -1,61 +0,0 @@
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
- '.github',
13
- '.npmrc',
14
- 'bin',
15
- 'cli',
16
- 'xpack',
17
- '.vscode',
18
- '.build',
19
- 'node_modules',
20
- 'server.ts',
21
- 'prerender.ts',
22
- 'integration.ts',
23
- 'styles.ts',
24
- 'scripts.ts',
25
- 'states.ts',
26
- 'migrate-scss.ts',
27
- 'vite.config.ts',
28
- 'vite.cli.config.ts',
29
- 'src',
30
- 'public/samples',
31
- 'public/assets/vendors',
32
- ];
33
-
34
- const copy = async (src: string | string[], dest: string, { cwd }: CopyOption) => {
35
- const sourceFiles = await globby(src, {
36
- cwd,
37
- dot: true,
38
- absolute: false,
39
- gitignore: true,
40
- ignore: excludeFiles,
41
- });
42
-
43
- const destRelativeToCwd = path.resolve(dest);
44
-
45
- return Promise.all(
46
- sourceFiles.map(async (p) => {
47
- const dirname = path.dirname(p);
48
- const basename = path.basename(p);
49
-
50
- const from = path.resolve(cwd, p);
51
- const filePath = path.join(destRelativeToCwd, dirname, basename);
52
-
53
- // Ensure the destination directory exists
54
- await fs.mkdir(path.dirname(filePath), { recursive: true });
55
-
56
- return fs.copyFile(from, filePath);
57
- })
58
- );
59
- };
60
-
61
- export { copy };
@@ -1,197 +0,0 @@
1
- import path from 'path';
2
- import os from 'os';
3
- import fs from 'fs/promises';
4
-
5
- const writeFile = async (filePath: string, data: string) => {
6
- await fs.mkdir(path.dirname(filePath), { recursive: true });
7
- await fs.writeFile(filePath, data);
8
- };
9
-
10
- const formatFiles = async (root: string) => {
11
- let filePath, data;
12
-
13
- // mock handler file
14
- filePath = path.join(root, 'src/mocks/handlers.ts');
15
- data = "import { handlers } from './consts';\n\nexport { handlers };";
16
-
17
- await writeFile(filePath, data + os.EOL);
18
-
19
- // example mock file
20
- filePath = path.join(root, 'src/mocks/example/index.ts');
21
- data = `
22
- import { handlers } from '@mocks/handlers';
23
- import { rest } from 'msw';
24
-
25
- handlers.push(
26
- rest.get('/api/example', (req, res, ctx) => {
27
- return res(ctx.status(200), ctx.json({test: 'test'}));
28
- })
29
- );
30
- `;
31
-
32
- await writeFile(filePath, data + os.EOL);
33
-
34
- // react loader
35
- filePath = path.join(root, 'src/react-loader.tsx');
36
- data = [
37
- "import { lazy } from 'react';",
38
- '',
39
- 'export const blocks: { [name: string]: any } = {',
40
- "\troot: lazy(() => import('./organisms/root/Root')),",
41
- '};',
42
- '',
43
- ].join('\n');
44
-
45
- await writeFile(filePath, data);
46
-
47
- // src/_types/atoms.d.ts
48
- filePath = path.join(root, 'src/_types/atoms.d.ts');
49
-
50
- await writeFile(
51
- filePath,
52
- `
53
- import { BasedAtomicModel } from "./_general";
54
- `
55
- );
56
-
57
- // src/_types/molecules.d.d.ts
58
- filePath = path.join(root, 'src/_types/molecules.d.ts');
59
-
60
- await writeFile(
61
- filePath,
62
- `
63
- import { BasedAtomicModel } from "./_general";
64
- `
65
- );
66
-
67
- // src/_types/organisms.d.ts
68
- filePath = path.join(root, 'src/_types/organisms.d.ts');
69
-
70
- await writeFile(
71
- filePath,
72
- `
73
- import { BasedAtomicModel } from "./_general";
74
-
75
- interface FooterModel extends BasedAtomicModel {}
76
-
77
- interface HeaderModel extends BasedAtomicModel {}
78
- `
79
- );
80
-
81
- // pages/Home.tsx
82
- filePath = path.join(root, 'src/pages/Home.tsx');
83
-
84
- await writeFile(
85
- filePath,
86
- `
87
- import Template from '@templates/home/Home';
88
-
89
- const Home = () => {
90
- return <Template footer={footer} header={header} />;
91
- };
92
-
93
- export default Home;
94
- `
95
- );
96
-
97
- // templates/home/Home.tsx
98
- filePath = path.join(root, 'src/templates/home/Home.tsx');
99
-
100
- await writeFile(
101
- filePath,
102
- `
103
- import { FooterModel, HeaderModel } from '@_types/organisms';
104
- import Footer from '@organisms/footer/Footer';
105
- import Header from '@organisms/header/Header';
106
-
107
- interface Props {
108
- header?: HeaderModel;
109
- footer?: FooterModel;
110
- }
111
-
112
- const Home = (model: Props) => {
113
- const { header, footer } = model;
114
-
115
- return (
116
- <>
117
- {header && <Header {...header} />}
118
-
119
- <main>
120
- // write components here
121
- </main>
122
-
123
- <Footer {...footer} />
124
- </>
125
- );
126
- };
127
-
128
- export default Home;
129
- `
130
- );
131
-
132
- // src/organisms/header/Header.tsx
133
- filePath = path.join(root, 'src/organisms/header/Header.tsx');
134
-
135
- await writeFile(
136
- filePath,
137
- `
138
- import { getModifiers } from '@helpers/functions';
139
- import RequireCss from '@helpers/RequireCss';
140
- import { HeaderModel } from '@_types/organisms';
141
-
142
- const Header = (model: HeaderModel) => {
143
- const modifiers = getModifiers(model, 'zzz-o-header');
144
-
145
- return (
146
- <header className={modifiers}>
147
- <h2>Header</h2>
148
- <RequireCss path="b-header" />
149
- </header>
150
- );
151
- };
152
-
153
- export default Header;
154
- `
155
- );
156
-
157
- // src/organisms/header/Header.scss
158
- filePath = path.join(root, 'src/organisms/header/Header.scss');
159
-
160
- await writeFile(filePath, '');
161
-
162
- // src/organisms/footer/Footer.tsx
163
- filePath = path.join(root, 'src/organisms/footer/Footer.tsx');
164
-
165
- await writeFile(
166
- filePath,
167
- `
168
- import { getModifiers } from '@helpers/functions';
169
- import RequireCss from '@helpers/RequireCss';
170
- import { FooterModel } from '@_types/organisms';
171
-
172
- const Footer = (model: FooterModel) => {
173
- const modifiers = getModifiers(model, 'zzz-o-footer');
174
-
175
- return (
176
- <footer className={modifiers}>
177
- <h2>Footer</h2>
178
- <RequireCss path="b-footer" />
179
- </footer>
180
- );
181
- };
182
-
183
- export default Footer;
184
- `
185
- );
186
-
187
- // src/organisms/footer/Footer.scss
188
- filePath = path.join(root, 'src/organisms/footer/Footer.scss');
189
-
190
- await writeFile(filePath, '');
191
-
192
- return ['xxx'];
193
- };
194
-
195
- // formatFiles('E:\\projects\\test-x-pkg');
196
-
197
- export { formatFiles };