create-ripple 0.1.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/LICENSE +21 -0
- package/README.md +50 -0
- package/package.json +51 -0
- package/src/commands/create.js +144 -0
- package/src/constants.js +20 -0
- package/src/index.js +52 -0
- package/src/lib/project-creator.js +206 -0
- package/src/lib/prompts.js +136 -0
- package/src/lib/templates.js +56 -0
- package/src/lib/validation.js +155 -0
- package/tests/integration/cli.test.js +179 -0
- package/tests/integration/project-creator.test.js +215 -0
- package/tests/unit/prompts.test.js +207 -0
- package/tests/unit/templates.test.js +163 -0
- package/tests/unit/validation.test.js +192 -0
- package/vitest.config.js +22 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ripple
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# create-ripple-app
|
|
2
|
+
|
|
3
|
+
Interactive CLI tool for creating new Ripple applications.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### Interactive Mode
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm create ripple-app
|
|
11
|
+
# or
|
|
12
|
+
npx create-ripple-app
|
|
13
|
+
# or
|
|
14
|
+
yarn create ripple-app
|
|
15
|
+
# or
|
|
16
|
+
pnpm create ripple-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### With Arguments
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm create ripple-app my-app
|
|
23
|
+
# or
|
|
24
|
+
npx create-ripple-app my-app basic
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- 🎯 **Interactive prompts** - Guides you through project setup
|
|
30
|
+
- 📁 **Template selection** - Choose from predefined templates
|
|
31
|
+
- ✅ **Project validation** - Ensures valid project names
|
|
32
|
+
- 🎨 **Beautiful CLI** - Colored output with progress indicators
|
|
33
|
+
- ⚡ **Fast setup** - Quickly scaffold new Ripple projects
|
|
34
|
+
|
|
35
|
+
## Templates
|
|
36
|
+
|
|
37
|
+
### Basic
|
|
38
|
+
A minimal Ripple application with:
|
|
39
|
+
- Vite for development and building
|
|
40
|
+
- TypeScript support
|
|
41
|
+
- Prettier for code formatting
|
|
42
|
+
- Basic project structure
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Node.js 18.0.0 or higher
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-ripple",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Interactive CLI tool for creating Ripple applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Ripple Team",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"ripple",
|
|
10
|
+
"cli",
|
|
11
|
+
"scaffold",
|
|
12
|
+
"template",
|
|
13
|
+
"create-app"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/trueadm/ripple.git",
|
|
18
|
+
"directory": "packages/create-ripple"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/trueadm/ripple/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://ripplejs.com",
|
|
24
|
+
"bin": {
|
|
25
|
+
"create-ripple": "./src/index.js"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"test:coverage": "vitest run --coverage",
|
|
34
|
+
"test:ui": "vitest --ui",
|
|
35
|
+
"format": "prettier --write .",
|
|
36
|
+
"format:check": "prettier --check ."
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"commander": "^12.1.0",
|
|
40
|
+
"prompts": "^2.4.2",
|
|
41
|
+
"kleur": "^4.1.5",
|
|
42
|
+
"degit": "^2.8.4",
|
|
43
|
+
"ora": "^8.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"prettier": "^3.6.2",
|
|
47
|
+
"vitest": "catalog:",
|
|
48
|
+
"jsdom": "catalog:",
|
|
49
|
+
"@types/prompts": "^2.4.9"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { green, cyan, dim, red } from 'kleur/colors';
|
|
4
|
+
import { validateProjectName } from '../lib/validation.js';
|
|
5
|
+
import { validateTemplate, getTemplateNames } from '../lib/templates.js';
|
|
6
|
+
import {
|
|
7
|
+
promptProjectName,
|
|
8
|
+
promptTemplate,
|
|
9
|
+
promptOverwrite,
|
|
10
|
+
promptPackageManager,
|
|
11
|
+
promptGitInit
|
|
12
|
+
} from '../lib/prompts.js';
|
|
13
|
+
import { createProject } from '../lib/project-creator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create command handler
|
|
17
|
+
* @param {string} projectName - Project name (optional)
|
|
18
|
+
* @param {object} options - Command options
|
|
19
|
+
*/
|
|
20
|
+
export async function createCommand(projectName, options) {
|
|
21
|
+
console.log();
|
|
22
|
+
console.log(cyan('🌊 Welcome to Create Ripple App!'));
|
|
23
|
+
console.log(dim("Let's create a new Ripple application"));
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// Step 1: Get or validate project name
|
|
27
|
+
if (!projectName) {
|
|
28
|
+
projectName = await promptProjectName();
|
|
29
|
+
} else {
|
|
30
|
+
const validation = validateProjectName(projectName);
|
|
31
|
+
if (!validation.valid) {
|
|
32
|
+
console.error(red(`✖ ${validation.message}`));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Step 2: Get template
|
|
38
|
+
let template = options.template;
|
|
39
|
+
if (!template) {
|
|
40
|
+
template = await promptTemplate();
|
|
41
|
+
} else {
|
|
42
|
+
// Validate template
|
|
43
|
+
if (!validateTemplate(template)) {
|
|
44
|
+
console.error(red(`✖ Template "${template}" not found`));
|
|
45
|
+
console.error(`Available templates: ${getTemplateNames().join(', ')}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 3: Get package manager
|
|
51
|
+
let packageManager = options.packageManager || 'npm';
|
|
52
|
+
if (!options.packageManager && !options.yes) {
|
|
53
|
+
packageManager = await promptPackageManager();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Step 4: Check directory and handle conflicts
|
|
57
|
+
const projectPath = resolve(process.cwd(), projectName);
|
|
58
|
+
if (existsSync(projectPath) && !options.yes) {
|
|
59
|
+
const shouldOverwrite = await promptOverwrite(projectName);
|
|
60
|
+
if (!shouldOverwrite) {
|
|
61
|
+
console.log(red('✖ Operation cancelled'));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Step 5: Git initialization preference
|
|
67
|
+
let gitInit = true;
|
|
68
|
+
if (!options.git && !options.yes) {
|
|
69
|
+
gitInit = await promptGitInit();
|
|
70
|
+
} else if (options.git === false) {
|
|
71
|
+
gitInit = false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Step 6: Create the project
|
|
75
|
+
console.log();
|
|
76
|
+
console.log(`Creating Ripple app in ${green(projectPath)}...`);
|
|
77
|
+
console.log();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await createProject({
|
|
81
|
+
projectName,
|
|
82
|
+
projectPath,
|
|
83
|
+
template,
|
|
84
|
+
packageManager,
|
|
85
|
+
typescript: true,
|
|
86
|
+
gitInit
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
showNextSteps(projectName, packageManager);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(red('✖ Failed to create project:'));
|
|
92
|
+
console.error(error.message);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Show next steps to the user
|
|
99
|
+
* @param {string} projectName - The created project name
|
|
100
|
+
* @param {string} packageManager - Package manager used
|
|
101
|
+
*/
|
|
102
|
+
function showNextSteps(projectName, packageManager) {
|
|
103
|
+
const installCommand = getInstallCommand(packageManager);
|
|
104
|
+
const devCommand = getDevCommand(packageManager);
|
|
105
|
+
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(green('🎉 Success! Your Ripple app is ready to go.'));
|
|
108
|
+
console.log();
|
|
109
|
+
console.log('Next steps:');
|
|
110
|
+
console.log(` ${dim('cd')} ${projectName}`);
|
|
111
|
+
console.log(` ${dim(installCommand)}`);
|
|
112
|
+
console.log(` ${dim(devCommand)}`);
|
|
113
|
+
console.log();
|
|
114
|
+
console.log('Happy coding! 🌊');
|
|
115
|
+
console.log();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get install command for package manager
|
|
120
|
+
* @param {string} packageManager - Package manager name
|
|
121
|
+
* @returns {string} - Install command
|
|
122
|
+
*/
|
|
123
|
+
function getInstallCommand(packageManager) {
|
|
124
|
+
const commands = {
|
|
125
|
+
npm: 'npm install',
|
|
126
|
+
yarn: 'yarn install',
|
|
127
|
+
pnpm: 'pnpm install'
|
|
128
|
+
};
|
|
129
|
+
return commands[packageManager] || 'npm install';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get dev command for package manager
|
|
134
|
+
* @param {string} packageManager - Package manager name
|
|
135
|
+
* @returns {string} - Dev command
|
|
136
|
+
*/
|
|
137
|
+
function getDevCommand(packageManager) {
|
|
138
|
+
const commands = {
|
|
139
|
+
npm: 'npm run dev',
|
|
140
|
+
yarn: 'yarn dev',
|
|
141
|
+
pnpm: 'pnpm dev'
|
|
142
|
+
};
|
|
143
|
+
return commands[packageManager] || 'npm run dev';
|
|
144
|
+
}
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = dirname(__filename);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Available templates configuration
|
|
9
|
+
*/
|
|
10
|
+
export const TEMPLATES = [
|
|
11
|
+
{
|
|
12
|
+
name: 'basic',
|
|
13
|
+
display: 'Basic Ripple App',
|
|
14
|
+
description: 'A minimal Ripple application with Vite and TypeScript'
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Get the root directory of the monorepo
|
|
19
|
+
export const REPO_ROOT = resolve(__dirname, '../../../');
|
|
20
|
+
export const TEMPLATES_DIR = join(REPO_ROOT, 'templates');
|
package/src/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { red } from 'kleur/colors';
|
|
8
|
+
import { createCommand } from './commands/create.js';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
14
|
+
const program = new Command();
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.name('create-ripple-app')
|
|
18
|
+
.description('Interactive CLI tool for creating Ripple applications')
|
|
19
|
+
.version(packageJson.version)
|
|
20
|
+
.helpOption('-h, --help', 'Display help for command');
|
|
21
|
+
|
|
22
|
+
program
|
|
23
|
+
.argument('[project-name]', 'Name of the project to create')
|
|
24
|
+
.option('-t, --template <template>', 'Template to use (default: basic)')
|
|
25
|
+
.option('-p, --package-manager <pm>', 'Package manager to use (npm, yarn, pnpm)', 'npm')
|
|
26
|
+
.option('--no-git', 'Skip Git repository initialization')
|
|
27
|
+
.option('-y, --yes', 'Skip all prompts and use defaults')
|
|
28
|
+
.action(async (projectName, options) => {
|
|
29
|
+
try {
|
|
30
|
+
await createCommand(projectName, options);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(red('✖ Unexpected error:'));
|
|
33
|
+
console.error(error.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Handle unhandled promise rejections
|
|
39
|
+
process.on('unhandledRejection', (err) => {
|
|
40
|
+
console.error(red('✖ Unhandled error:'));
|
|
41
|
+
console.error(err);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Handle SIGINT (Ctrl+C)
|
|
46
|
+
process.on('SIGINT', () => {
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(red('✖ Operation cancelled'));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
program.parse();
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { green, dim } from 'kleur/colors';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
|
|
7
|
+
import { getTemplatePath } from './templates.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a new Ripple project
|
|
11
|
+
* @param {object} options - Project creation options
|
|
12
|
+
* @param {string} options.projectName - Name of the project
|
|
13
|
+
* @param {string} options.projectPath - Absolute path where project will be created
|
|
14
|
+
* @param {string} options.template - Template to use
|
|
15
|
+
* @param {string} options.packageManager - Package manager to use
|
|
16
|
+
* @param {boolean} options.typescript - Whether to use TypeScript
|
|
17
|
+
* @param {boolean} options.gitInit - Whether to initialize Git
|
|
18
|
+
*/
|
|
19
|
+
export async function createProject({
|
|
20
|
+
projectName,
|
|
21
|
+
projectPath,
|
|
22
|
+
template,
|
|
23
|
+
packageManager = 'npm',
|
|
24
|
+
typescript = true,
|
|
25
|
+
gitInit = true
|
|
26
|
+
}) {
|
|
27
|
+
console.log(dim(`Creating project: ${projectName}`));
|
|
28
|
+
console.log(dim(`Template: ${template}`));
|
|
29
|
+
console.log(dim(`Package manager: ${packageManager}`));
|
|
30
|
+
console.log();
|
|
31
|
+
|
|
32
|
+
const templatePath = getTemplatePath(template);
|
|
33
|
+
|
|
34
|
+
if (!existsSync(templatePath)) {
|
|
35
|
+
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Step 1: Create project directory
|
|
39
|
+
const spinner1 = ora('Creating project directory...').start();
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(projectPath, { recursive: true });
|
|
42
|
+
spinner1.succeed('Project directory created');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
spinner1.fail('Failed to create project directory');
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Step 2: Copy template files
|
|
49
|
+
const spinner2 = ora('Copying template files...').start();
|
|
50
|
+
try {
|
|
51
|
+
cpSync(templatePath, projectPath, {
|
|
52
|
+
recursive: true,
|
|
53
|
+
filter: (src) => {
|
|
54
|
+
// Skip node_modules and any lock files from template
|
|
55
|
+
const relativePath = src.replace(templatePath, '');
|
|
56
|
+
return (
|
|
57
|
+
!relativePath.includes('node_modules') &&
|
|
58
|
+
!relativePath.includes('package-lock.json') &&
|
|
59
|
+
!relativePath.includes('yarn.lock') &&
|
|
60
|
+
!relativePath.includes('pnpm-lock.yaml')
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
spinner2.succeed('Template files copied');
|
|
65
|
+
} catch (error) {
|
|
66
|
+
spinner2.fail('Failed to copy template files');
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Step 3: Update package.json
|
|
71
|
+
const spinner3 = ora('Configuring package.json...').start();
|
|
72
|
+
try {
|
|
73
|
+
updatePackageJson(projectPath, projectName, packageManager, typescript);
|
|
74
|
+
spinner3.succeed('Package.json configured');
|
|
75
|
+
} catch (error) {
|
|
76
|
+
spinner3.fail('Failed to configure package.json');
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Step 4: Initialize Git (if requested)
|
|
81
|
+
if (gitInit) {
|
|
82
|
+
const spinner4 = ora('Initializing Git repository...').start();
|
|
83
|
+
try {
|
|
84
|
+
execSync('git init', { cwd: projectPath, stdio: 'ignore' });
|
|
85
|
+
execSync('git add .', { cwd: projectPath, stdio: 'ignore' });
|
|
86
|
+
execSync('git commit -m "Initial commit"', { cwd: projectPath, stdio: 'ignore' });
|
|
87
|
+
spinner4.succeed('Git repository initialized');
|
|
88
|
+
} catch (error) {
|
|
89
|
+
spinner4.warn('Git initialization failed (optional)');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(green('✓ Project created successfully!'));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update package.json with project-specific configurations
|
|
99
|
+
* @param {string} projectPath - Path to the project
|
|
100
|
+
* @param {string} projectName - Name of the project
|
|
101
|
+
* @param {string} packageManager - Package manager being used
|
|
102
|
+
* @param {boolean} typescript - Whether TypeScript is enabled
|
|
103
|
+
*/
|
|
104
|
+
function updatePackageJson(projectPath, projectName, packageManager, typescript) {
|
|
105
|
+
const packageJsonPath = join(projectPath, 'package.json');
|
|
106
|
+
|
|
107
|
+
if (!existsSync(packageJsonPath)) {
|
|
108
|
+
throw new Error('package.json not found in template');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
112
|
+
|
|
113
|
+
// Update package name
|
|
114
|
+
packageJson.name = projectName;
|
|
115
|
+
|
|
116
|
+
// Remove version if it exists (since this is a new project)
|
|
117
|
+
if (packageJson.version === '0.0.0') {
|
|
118
|
+
packageJson.version = '1.0.0';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Update description
|
|
122
|
+
packageJson.description = `A Ripple application created with create-ripple-app`;
|
|
123
|
+
|
|
124
|
+
// Add package manager field if not npm
|
|
125
|
+
if (packageManager !== 'npm') {
|
|
126
|
+
packageJson.packageManager = getPackageManagerVersion(packageManager);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Ensure we're using the latest versions
|
|
130
|
+
updateDependencyVersions(packageJson);
|
|
131
|
+
|
|
132
|
+
// Update scripts based on package manager
|
|
133
|
+
updateScripts(packageJson, packageManager);
|
|
134
|
+
|
|
135
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Update dependency versions to latest
|
|
140
|
+
* @param {object} packageJson - Package.json object
|
|
141
|
+
*/
|
|
142
|
+
function updateDependencyVersions(packageJson) {
|
|
143
|
+
// Use the latest versions for Ripple packages
|
|
144
|
+
const latestVersions = {
|
|
145
|
+
ripple: '^0.2.35',
|
|
146
|
+
'vite-plugin-ripple': '^0.2.29',
|
|
147
|
+
'prettier-plugin-ripple': '^0.2.29'
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Update dependencies
|
|
151
|
+
if (packageJson.dependencies) {
|
|
152
|
+
for (const [pkg, version] of Object.entries(latestVersions)) {
|
|
153
|
+
if (packageJson.dependencies[pkg]) {
|
|
154
|
+
packageJson.dependencies[pkg] = version;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Update devDependencies
|
|
160
|
+
if (packageJson.devDependencies) {
|
|
161
|
+
for (const [pkg, version] of Object.entries(latestVersions)) {
|
|
162
|
+
if (packageJson.devDependencies[pkg]) {
|
|
163
|
+
packageJson.devDependencies[pkg] = version;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Update scripts based on package manager
|
|
171
|
+
* @param {object} packageJson - Package.json object
|
|
172
|
+
* @param {string} packageManager - Package manager being used
|
|
173
|
+
*/
|
|
174
|
+
function updateScripts(packageJson, packageManager) {
|
|
175
|
+
if (!packageJson.scripts) return;
|
|
176
|
+
|
|
177
|
+
// Add package manager specific scripts
|
|
178
|
+
const pmCommands = {
|
|
179
|
+
npm: 'npm run',
|
|
180
|
+
yarn: 'yarn',
|
|
181
|
+
pnpm: 'pnpm'
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const pm = pmCommands[packageManager] || 'npm run';
|
|
185
|
+
|
|
186
|
+
// Update format scripts to use the correct package manager
|
|
187
|
+
if (packageJson.scripts.format) {
|
|
188
|
+
packageJson.scripts.format = 'prettier --write .';
|
|
189
|
+
}
|
|
190
|
+
if (packageJson.scripts['format:check']) {
|
|
191
|
+
packageJson.scripts['format:check'] = 'prettier --check .';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get package manager version string
|
|
197
|
+
* @param {string} packageManager - Package manager name
|
|
198
|
+
* @returns {string} - Package manager with version
|
|
199
|
+
*/
|
|
200
|
+
function getPackageManagerVersion(packageManager) {
|
|
201
|
+
const versions = {
|
|
202
|
+
yarn: 'yarn@4.0.0',
|
|
203
|
+
pnpm: 'pnpm@9.0.0'
|
|
204
|
+
};
|
|
205
|
+
return versions[packageManager] || packageManager;
|
|
206
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import { validateProjectName } from './validation.js';
|
|
3
|
+
import { getTemplateChoices } from './templates.js';
|
|
4
|
+
import { red } from 'kleur/colors';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prompt for project name
|
|
8
|
+
* @param {string} defaultName - Default project name
|
|
9
|
+
* @returns {Promise<string>} - Project name
|
|
10
|
+
*/
|
|
11
|
+
export async function promptProjectName(defaultName = 'my-ripple-app') {
|
|
12
|
+
const response = await prompts({
|
|
13
|
+
type: 'text',
|
|
14
|
+
name: 'projectName',
|
|
15
|
+
message: 'What is your project named?',
|
|
16
|
+
initial: defaultName,
|
|
17
|
+
validate: (value) => {
|
|
18
|
+
const validation = validateProjectName(value);
|
|
19
|
+
return validation.valid || validation.message;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (!response.projectName) {
|
|
24
|
+
console.log(red('✖ Operation cancelled'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return response.projectName;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Prompt for template selection
|
|
33
|
+
* @returns {Promise<string>} - Selected template name
|
|
34
|
+
*/
|
|
35
|
+
export async function promptTemplate() {
|
|
36
|
+
const response = await prompts({
|
|
37
|
+
type: 'select',
|
|
38
|
+
name: 'template',
|
|
39
|
+
message: 'Which template would you like to use?',
|
|
40
|
+
choices: getTemplateChoices(),
|
|
41
|
+
initial: 0
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.template) {
|
|
45
|
+
console.log(red('✖ Operation cancelled'));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return response.template;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Prompt for directory overwrite confirmation
|
|
54
|
+
* @param {string} projectName - The project name
|
|
55
|
+
* @returns {Promise<boolean>} - Whether to overwrite
|
|
56
|
+
*/
|
|
57
|
+
export async function promptOverwrite(projectName) {
|
|
58
|
+
const response = await prompts({
|
|
59
|
+
type: 'confirm',
|
|
60
|
+
name: 'overwrite',
|
|
61
|
+
message: `Directory "${projectName}" already exists. Continue anyway?`,
|
|
62
|
+
initial: false
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (response.overwrite === undefined) {
|
|
66
|
+
console.log(red('✖ Operation cancelled'));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return response.overwrite;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Prompt for package manager selection
|
|
75
|
+
* @returns {Promise<string>} - Selected package manager
|
|
76
|
+
*/
|
|
77
|
+
export async function promptPackageManager() {
|
|
78
|
+
const response = await prompts({
|
|
79
|
+
type: 'select',
|
|
80
|
+
name: 'packageManager',
|
|
81
|
+
message: 'Which package manager would you like to use?',
|
|
82
|
+
choices: [
|
|
83
|
+
{ title: 'npm', value: 'npm', description: 'Use npm for dependency management' },
|
|
84
|
+
{ title: 'yarn', value: 'yarn', description: 'Use Yarn for dependency management' },
|
|
85
|
+
{ title: 'pnpm', value: 'pnpm', description: 'Use pnpm for dependency management' }
|
|
86
|
+
],
|
|
87
|
+
initial: 0
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!response.packageManager) {
|
|
91
|
+
console.log(red('✖ Operation cancelled'));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return response.packageManager;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Prompt for TypeScript usage
|
|
100
|
+
* @returns {Promise<boolean>} - Whether to use TypeScript
|
|
101
|
+
*/
|
|
102
|
+
export async function promptTypeScript() {
|
|
103
|
+
const response = await prompts({
|
|
104
|
+
type: 'confirm',
|
|
105
|
+
name: 'typescript',
|
|
106
|
+
message: 'Would you like to use TypeScript?',
|
|
107
|
+
initial: true
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (response.typescript === undefined) {
|
|
111
|
+
console.log(red('✖ Operation cancelled'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return response.typescript;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Prompt for Git initialization
|
|
120
|
+
* @returns {Promise<boolean>} - Whether to initialize Git
|
|
121
|
+
*/
|
|
122
|
+
export async function promptGitInit() {
|
|
123
|
+
const response = await prompts({
|
|
124
|
+
type: 'confirm',
|
|
125
|
+
name: 'gitInit',
|
|
126
|
+
message: 'Initialize a new Git repository?',
|
|
127
|
+
initial: true
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (response.gitInit === undefined) {
|
|
131
|
+
console.log(red('✖ Operation cancelled'));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return response.gitInit;
|
|
136
|
+
}
|