create-pb-app 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/package.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "create-pb-app",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": { "create-pb-app": "./src/cli.js" },
6
+ "files": ["src"]
7
+ }
package/src/cli.js ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdirSync, writeFileSync } from 'fs';
4
+ import { join, dirname } from 'path';
5
+ import { execSync } from 'child_process';
6
+ import { createInterface } from 'readline';
7
+ import { getTemplates } from './templates.js';
8
+
9
+ // ANSI colors
10
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
11
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
12
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
13
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
14
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
15
+
16
+ const args = process.argv.slice(2);
17
+
18
+ if (args.includes('--help') || args.includes('-h')) {
19
+ console.log(`
20
+ ${bold('create-pb-app')} — scaffold a new project
21
+
22
+ ${bold('Usage:')}
23
+ ${cyan('create-pb-app')} ${dim('<project-name>')} ${dim('[options]')}
24
+
25
+ ${bold('Options:')}
26
+ ${dim('--skip-install')} Skip npm install
27
+ ${dim('--help, -h')} Show this help message
28
+ `);
29
+ process.exit(0);
30
+ }
31
+
32
+ const skipInstall = args.includes('--skip-install');
33
+ const projectName = args.find((a) => !a.startsWith('-'));
34
+
35
+ if (!projectName) {
36
+ console.error(red('Error: Please provide a project name.'));
37
+ console.error(` ${cyan('create-pb-app')} ${dim('<project-name>')}`);
38
+ process.exit(1);
39
+ }
40
+
41
+ // Validate npm package name (basic check)
42
+ if (!/^[a-z0-9@][a-z0-9._\-/@]*$/.test(projectName)) {
43
+ console.error(red(`Error: "${projectName}" is not a valid npm package name.`));
44
+ process.exit(1);
45
+ }
46
+
47
+ const projectDir = join(process.cwd(), projectName);
48
+
49
+ // Check if directory already exists
50
+ try {
51
+ mkdirSync(projectDir);
52
+ } catch (err) {
53
+ if (err.code === 'EEXIST') {
54
+ console.error(red(`Error: Directory "${projectName}" already exists.`));
55
+ process.exit(1);
56
+ }
57
+ throw err;
58
+ }
59
+
60
+ console.log();
61
+ console.log(bold(`Creating ${green(projectName)}...`));
62
+ console.log();
63
+
64
+ // Write all template files
65
+ const templates = getTemplates(projectName);
66
+
67
+ for (const { filePath, content } of templates) {
68
+ const fullPath = join(projectDir, filePath);
69
+ mkdirSync(dirname(fullPath), { recursive: true });
70
+ writeFileSync(fullPath, content);
71
+ console.log(` ${green('+')} ${filePath}`);
72
+ }
73
+
74
+ console.log();
75
+ console.log(green(`Created ${templates.length} files.`));
76
+ console.log();
77
+
78
+ async function promptInstall() {
79
+ if (skipInstall) return false;
80
+
81
+ return new Promise((resolve) => {
82
+ const rl = createInterface({
83
+ input: process.stdin,
84
+ output: process.stdout
85
+ });
86
+
87
+ rl.question('Install dependencies? (Y/n) ', (answer) => {
88
+ rl.close();
89
+ const a = answer.trim().toLowerCase();
90
+ resolve(a === '' || a === 'y' || a === 'yes');
91
+ });
92
+ });
93
+ }
94
+
95
+ const shouldInstall = await promptInstall();
96
+
97
+ if (shouldInstall) {
98
+ console.log();
99
+ console.log(dim('Running npm install...'));
100
+ console.log();
101
+ try {
102
+ execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
103
+ console.log();
104
+ console.log(green('Dependencies installed.'));
105
+ } catch {
106
+ console.error(red('npm install failed. You can run it manually later.'));
107
+ }
108
+ }
109
+
110
+ console.log();
111
+ console.log(bold('Done! Next steps:'));
112
+ console.log();
113
+ console.log(` ${cyan(`cd ${projectName}`)}`);
114
+ if (!shouldInstall) {
115
+ console.log(` ${cyan('npm install')}`);
116
+ }
117
+ console.log(` ${cyan('npm run dev')}`);
118
+ console.log();
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Returns all template files for a new project.
3
+ * @param {string} projectName
4
+ * @returns {{ filePath: string, content: string }[]}
5
+ */
6
+ export function getTemplates(projectName) {
7
+ return [
8
+ {
9
+ filePath: 'package.json',
10
+ content: JSON.stringify({
11
+ name: projectName,
12
+ version: '1.0.0',
13
+ type: 'module',
14
+ scripts: {
15
+ dev: 'vite',
16
+ build: 'vite build'
17
+ },
18
+ dependencies: {
19
+ 'components-test-pb': '^0.0.3',
20
+ 'css-engine-test-pb': '^0.1.6',
21
+ 'jsx-framework-test-pb': '^0.2.2',
22
+ 'looserouter-test-pb': '^0.1.0'
23
+ },
24
+ devDependencies: {
25
+ typescript: '^5.9.3',
26
+ vite: '^7.3.1'
27
+ }
28
+ }, null, 2) + '\n'
29
+ },
30
+ {
31
+ filePath: 'tsconfig.json',
32
+ content: JSON.stringify({
33
+ compilerOptions: {
34
+ target: 'ES2020',
35
+ module: 'NodeNext',
36
+ jsx: 'react-jsx',
37
+ jsxImportSource: 'jsx-framework-test-pb',
38
+ moduleResolution: 'NodeNext',
39
+ esModuleInterop: true,
40
+ skipLibCheck: true,
41
+ outDir: './dist'
42
+ },
43
+ include: ['src/**/*']
44
+ }, null, 4) + '\n'
45
+ },
46
+ {
47
+ filePath: 'vite.config.ts',
48
+ content: `import { defineConfig } from 'vite';
49
+
50
+ export default defineConfig({});
51
+ `
52
+ },
53
+ {
54
+ filePath: 'index.html',
55
+ content: `<!DOCTYPE html>
56
+ <html lang="en">
57
+
58
+ <head>
59
+ <meta charset="UTF-8">
60
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
61
+ <title>${projectName}</title>
62
+ </head>
63
+
64
+ <body>
65
+ <script type="module" src="./src/App/App.tsx"></script>
66
+ </body>
67
+
68
+ </html>
69
+ `
70
+ },
71
+ {
72
+ filePath: '.gitignore',
73
+ content: `node_modules
74
+ dist
75
+ `
76
+ },
77
+ {
78
+ filePath: 'vercel.json',
79
+ content: JSON.stringify({
80
+ rewrites: [
81
+ {
82
+ source: '/(.*)',
83
+ destination: '/index.html'
84
+ }
85
+ ]
86
+ }, null, 4) + '\n'
87
+ },
88
+ {
89
+ filePath: 'src/App/App.tsx',
90
+ content: `import { render } from 'jsx-framework-test-pb';
91
+ import { Home } from '../Pages/Home';
92
+ import { createRouter, RouterView } from 'looserouter-test-pb';
93
+ import { Main } from '../layouts';
94
+ import { makeStaticStyles } from 'css-engine-test-pb';
95
+
96
+ const useStaticStyles = makeStaticStyles({
97
+ body: {
98
+ margin: 0,
99
+ fontFamily: 'system-ui, -apple-system, sans-serif',
100
+ boxSizing: 'border-box'
101
+ }
102
+ });
103
+
104
+ useStaticStyles();
105
+
106
+ const router = createRouter({
107
+ '/': (ctx) => <Main><Home /></Main>
108
+ });
109
+
110
+ const App = () => <RouterView router={router} />;
111
+
112
+ render(<App />, document.body);
113
+ `
114
+ },
115
+ {
116
+ filePath: 'src/Pages/Home/Home.tsx',
117
+ content: `import { Text } from 'components-test-pb';
118
+
119
+ export function Home() {
120
+ return (
121
+ <>
122
+ <Text>Hello World</Text>
123
+ </>
124
+ );
125
+ }
126
+ `
127
+ },
128
+ {
129
+ filePath: 'src/Pages/Home/index.ts',
130
+ content: `export { Home } from './Home';
131
+ `
132
+ },
133
+ {
134
+ filePath: 'src/layouts/Main.tsx',
135
+ content: `export function Main({ children }) {
136
+ return (
137
+ <>
138
+ <header></header>
139
+ {children}
140
+ <footer></footer>
141
+ </>
142
+ );
143
+ }
144
+ `
145
+ },
146
+ {
147
+ filePath: 'src/layouts/index.ts',
148
+ content: `export { Main } from './Main';
149
+ `
150
+ }
151
+ ];
152
+ }