create-paljs 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/bin/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { CreatePal } from '../dist/index.js';
4
+
5
+ const app = new CreatePal();
6
+ app.run().catch((error) => {
7
+ console.error('āŒ Error:', error.message);
8
+ process.exit(1);
9
+ });
@@ -0,0 +1,21 @@
1
+ export declare class CreatePal {
2
+ private projectName;
3
+ private projectDir;
4
+ private selectedTemplate;
5
+ private packageManager;
6
+ private gitInit;
7
+ private skipInstall;
8
+ private verbose;
9
+ run(): Promise<void>;
10
+ private detectPackageManager;
11
+ private showHelp;
12
+ private printBanner;
13
+ private prompt;
14
+ private promptChoice;
15
+ private downloadTemplate;
16
+ private prepareProject;
17
+ private installDependencies;
18
+ private initGit;
19
+ private printSuccess;
20
+ private spinner;
21
+ }
package/dist/index.js ADDED
@@ -0,0 +1,271 @@
1
+ import gradient from 'gradient-string';
2
+ import { downloadTemplate } from 'giget';
3
+ import { execa } from 'execa';
4
+ import { existsSync, readFileSync, writeFileSync, cpSync, rmSync, readdirSync } from 'fs';
5
+ import { join, isAbsolute } from 'path';
6
+ import { templates, getTemplateByAlias } from './templates.js';
7
+ const VERSION = '1.0.0';
8
+ export class CreatePal {
9
+ constructor() {
10
+ this.projectName = '';
11
+ this.projectDir = '';
12
+ this.selectedTemplate = '';
13
+ this.packageManager = 'npm';
14
+ this.gitInit = false;
15
+ this.skipInstall = false;
16
+ this.verbose = false;
17
+ }
18
+ async run() {
19
+ const args = process.argv.slice(2);
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === '--template' || arg === '-t') {
23
+ this.selectedTemplate = args[++i];
24
+ }
25
+ else if (arg === '--git' || arg === '-g') {
26
+ this.gitInit = true;
27
+ }
28
+ else if (arg === '--skip-install') {
29
+ this.skipInstall = true;
30
+ }
31
+ else if (arg === '--verbose' || arg === '-v') {
32
+ this.verbose = true;
33
+ }
34
+ else if (arg === '--help' || arg === '-h') {
35
+ this.showHelp();
36
+ process.exit(0);
37
+ }
38
+ else if (!arg.startsWith('-')) {
39
+ this.projectName = arg;
40
+ }
41
+ }
42
+ this.packageManager = this.detectPackageManager();
43
+ this.printBanner();
44
+ if (!this.projectName) {
45
+ this.projectName = await this.prompt('project name', 'my-pal-app');
46
+ }
47
+ this.projectDir = isAbsolute(this.projectName)
48
+ ? this.projectName
49
+ : join(process.cwd(), this.projectName);
50
+ if (!this.selectedTemplate) {
51
+ this.selectedTemplate = await this.promptChoice('Select a starter kit', templates.map((t) => ({ name: `${t.name} - ${t.hint}`, value: t.alias })));
52
+ }
53
+ const template = getTemplateByAlias(this.selectedTemplate);
54
+ if (!template) {
55
+ throw new Error(`Unknown template: ${this.selectedTemplate}`);
56
+ }
57
+ await this.downloadTemplate(template.source);
58
+ await this.prepareProject();
59
+ if (!this.skipInstall) {
60
+ await this.installDependencies();
61
+ }
62
+ if (this.gitInit) {
63
+ await this.initGit();
64
+ }
65
+ this.printSuccess();
66
+ }
67
+ detectPackageManager() {
68
+ const npmExecPath = process.env['npm_execpath'];
69
+ const npmConfig = process.env['npm_config_user_agent'];
70
+ if (npmConfig?.includes('pnpm'))
71
+ return 'pnpm';
72
+ if (npmConfig?.includes('yarn'))
73
+ return 'yarn';
74
+ if (npmConfig?.includes('bun'))
75
+ return 'bun';
76
+ if (npmExecPath?.includes('pnpm'))
77
+ return 'pnpm';
78
+ if (npmExecPath?.includes('yarn'))
79
+ return 'yarn';
80
+ if (npmExecPath?.includes('bun'))
81
+ return 'bun';
82
+ return 'npm';
83
+ }
84
+ showHelp() {
85
+ console.log(`
86
+ Usage: create-pal [project-name] [options]
87
+
88
+ Options:
89
+ -t, --template <name> Starter kit template (api, admin, client)
90
+ -g, --git Initialize git repository
91
+ --skip-install Skip installing dependencies
92
+ -v, --verbose Enable verbose mode
93
+ -h, --help Show this help message
94
+
95
+ Examples:
96
+ create-pal my-project
97
+ create-pal my-project --template api
98
+ create-pal my-project --git
99
+ create-pal my-project --skip-install
100
+ `);
101
+ }
102
+ printBanner() {
103
+ const title = `
104
+ ╔═══════════════════════════════════════════════════════════╗
105
+ ā•‘ ā•‘
106
+ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā•‘
107
+ ā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•‘ ā•‘
108
+ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā•‘ ā•‘
109
+ ā•‘ ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā•”ā• ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•‘
110
+ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā•šā•ā• ā–ˆā–ˆā•‘ ā•‘
111
+ ā•‘ ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•‘
112
+ ā•‘ ā•‘
113
+ ā•‘ The batteries-included full-stack framework ā•‘
114
+ ā•‘ ā•‘
115
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
116
+ `.trim();
117
+ console.log(gradient.pastel.multiline(title));
118
+ console.log();
119
+ }
120
+ async prompt(question, defaultValue) {
121
+ const readline = await import('readline');
122
+ const rl = readline.createInterface({
123
+ input: process.stdin,
124
+ output: process.stdout,
125
+ });
126
+ return new Promise((resolve) => {
127
+ rl.question(`${question} (${defaultValue}): `, (answer) => {
128
+ rl.close();
129
+ resolve(answer.trim() || defaultValue);
130
+ });
131
+ });
132
+ }
133
+ async promptChoice(question, choices) {
134
+ console.log(`\n${question}:`);
135
+ choices.forEach((choice, index) => {
136
+ console.log(` ${index + 1}. ${choice.name}`);
137
+ });
138
+ console.log();
139
+ const answer = await this.prompt('Enter choice number', '1');
140
+ const index = parseInt(answer, 10) - 1;
141
+ if (index >= 0 && index < choices.length) {
142
+ return choices[index].value;
143
+ }
144
+ return choices[0].value;
145
+ }
146
+ async downloadTemplate(source) {
147
+ console.log(`\nšŸ“¦ Downloading template from: ${source}`);
148
+ if (existsSync(this.projectDir)) {
149
+ const files = readdirSync(this.projectDir);
150
+ if (files.length > 0) {
151
+ rmSync(this.projectDir, { recursive: true, force: true });
152
+ }
153
+ }
154
+ await downloadTemplate(source, {
155
+ dir: this.projectDir,
156
+ registry: false,
157
+ });
158
+ console.log('āœ… Template downloaded successfully');
159
+ }
160
+ async prepareProject() {
161
+ console.log('\nšŸ”§ Preparing project...');
162
+ const pkgJsonPath = join(this.projectDir, 'package.json');
163
+ if (existsSync(pkgJsonPath)) {
164
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
165
+ pkgJson.name = this.projectName.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
166
+ writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
167
+ }
168
+ const envExample = join(this.projectDir, '.env.example');
169
+ const envFile = join(this.projectDir, '.env');
170
+ if (existsSync(envExample) && !existsSync(envFile)) {
171
+ cpSync(envExample, envFile);
172
+ }
173
+ const gitignore = join(this.projectDir, '.gitignore');
174
+ const gitignoreContent = `
175
+ node_modules/
176
+ dist/
177
+ build/
178
+ .turbo/
179
+ .env
180
+ *.log
181
+ coverage/
182
+ .vscode/
183
+ .idea/
184
+ `;
185
+ if (existsSync(gitignore)) {
186
+ const existing = readFileSync(gitignore, 'utf-8');
187
+ if (!existing.includes('node_modules/')) {
188
+ writeFileSync(gitignore, existing + gitignoreContent);
189
+ }
190
+ }
191
+ else {
192
+ writeFileSync(gitignore, gitignoreContent.trim() + '\n');
193
+ }
194
+ const lockFiles = [
195
+ join(this.projectDir, 'package-lock.json'),
196
+ join(this.projectDir, 'pnpm-lock.yaml'),
197
+ join(this.projectDir, 'yarn.lock'),
198
+ ];
199
+ lockFiles.forEach((file) => {
200
+ if (existsSync(file)) {
201
+ rmSync(file);
202
+ }
203
+ });
204
+ console.log('āœ… Project prepared successfully');
205
+ }
206
+ async installDependencies() {
207
+ if (!existsSync(join(this.projectDir, 'package.json'))) {
208
+ console.log('āš ļø No package.json found, skipping install');
209
+ return;
210
+ }
211
+ console.log(`\nšŸ“¦ Installing dependencies using ${this.packageManager}...`);
212
+ const spinner = this.spinner('Installing');
213
+ try {
214
+ await execa(this.packageManager, ['install'], {
215
+ cwd: this.projectDir,
216
+ stdio: this.verbose ? 'inherit' : 'pipe',
217
+ });
218
+ spinner.stop();
219
+ console.log('āœ… Dependencies installed successfully');
220
+ }
221
+ catch (error) {
222
+ spinner.stop();
223
+ throw new Error(`Failed to install dependencies: ${error.message}`);
224
+ }
225
+ }
226
+ async initGit() {
227
+ console.log('\nšŸ“‚ Initializing git repository...');
228
+ try {
229
+ await execa('git', ['init'], {
230
+ cwd: this.projectDir,
231
+ stdio: this.verbose ? 'inherit' : 'pipe',
232
+ });
233
+ console.log('āœ… Git repository initialized');
234
+ }
235
+ catch {
236
+ console.log('āš ļø Failed to initialize git (git may not be installed)');
237
+ }
238
+ }
239
+ printSuccess() {
240
+ const message = `
241
+ ╔═══════════════════════════════════════════════════════════╗
242
+ ā•‘ ā•‘
243
+ ā•‘ āœ… Your Pal project has been created successfully! ā•‘
244
+ ā•‘ ā•‘
245
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
246
+
247
+ Next steps:
248
+ cd ${this.projectName}
249
+ npm run dev
250
+
251
+ Documentation: https://github.com/paljs/paljs
252
+ `.trim();
253
+ console.log(gradient.pastel.multiline(message));
254
+ }
255
+ spinner(message) {
256
+ const frames = ['ā ‹', 'ā ™', 'ā ¹', 'ā ø', 'ā ¼', 'ā “', 'ā ¦', 'ā §', 'ā ‡', 'ā '];
257
+ let i = 0;
258
+ const interval = setInterval(() => {
259
+ process.stdout.write(`\r${frames[i++ % frames.length]} ${message}`);
260
+ }, 80);
261
+ return {
262
+ stop: () => {
263
+ clearInterval(interval);
264
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
265
+ },
266
+ update: (msg) => {
267
+ process.stdout.write(`\r${msg}`);
268
+ },
269
+ };
270
+ }
271
+ }
@@ -0,0 +1,9 @@
1
+ export interface Template {
2
+ name: string;
3
+ alias: string;
4
+ hint: string;
5
+ source: string;
6
+ }
7
+ export declare const templates: Template[];
8
+ export declare function getTemplateByAlias(alias: string): Template | undefined;
9
+ export declare function getTemplateByName(name: string): Template | undefined;
@@ -0,0 +1,26 @@
1
+ export const templates = [
2
+ {
3
+ name: 'API',
4
+ alias: 'api',
5
+ hint: 'Type-safe REST API with authentication and database',
6
+ source: 'github:paljs/starter-kits#main:api',
7
+ },
8
+ {
9
+ name: 'Admin',
10
+ alias: 'admin',
11
+ hint: 'Admin dashboard with authentication and UI components',
12
+ source: 'github:paljs/starter-kits#main:admin',
13
+ },
14
+ {
15
+ name: 'Client',
16
+ alias: 'client',
17
+ hint: 'Full-stack client application with frontend and API',
18
+ source: 'github:paljs/starter-kits#main:client',
19
+ },
20
+ ];
21
+ export function getTemplateByAlias(alias) {
22
+ return templates.find((t) => t.alias === alias);
23
+ }
24
+ export function getTemplateByName(name) {
25
+ return templates.find((t) => t.name.toLowerCase() === name.toLowerCase());
26
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "create-paljs",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a new PalJS project",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-paljs": "./bin/index.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/paljs/paljs.git"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "bin"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "dev": "tsx src/index.ts",
21
+ "test": "echo \"No tests yet\" && exit 0"
22
+ },
23
+ "dependencies": {
24
+ "gradient-string": "^3.0.0",
25
+ "giget": "^3.1.2",
26
+ "execa": "^9.5.2"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^20.0.0",
30
+ "tsx": "^4.0.0",
31
+ "typescript": "^5.9.3"
32
+ },
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ },
36
+ "keywords": [
37
+ "paljs",
38
+ "framework",
39
+ "scaffold",
40
+ "create",
41
+ "boilerplate",
42
+ "rpal"
43
+ ]
44
+ }