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 +7 -0
- package/src/cli.js +118 -0
- package/src/templates.js +152 -0
package/package.json
ADDED
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();
|
package/src/templates.js
ADDED
|
@@ -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
|
+
}
|