create-ripple 0.1.2 → 0.2.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # create-ripple-app
1
+ # create-ripple
2
2
 
3
3
  Interactive CLI tool for creating new Ripple applications.
4
4
 
@@ -7,21 +7,21 @@ Interactive CLI tool for creating new Ripple applications.
7
7
  ### Interactive Mode
8
8
 
9
9
  ```bash
10
- npm create ripple-app
10
+ npm create ripple
11
11
  # or
12
- npx create-ripple-app
12
+ npx create-ripple
13
13
  # or
14
- yarn create ripple-app
14
+ yarn create ripple
15
15
  # or
16
- pnpm create ripple-app
16
+ pnpm create ripple
17
17
  ```
18
18
 
19
19
  ### With Arguments
20
20
 
21
21
  ```bash
22
- npm create ripple-app my-app
22
+ npm create ripple my-app
23
23
  # or
24
- npx create-ripple-app my-app basic
24
+ npx create-ripple my-app
25
25
  ```
26
26
 
27
27
  ## Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ripple",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Interactive CLI tool for creating Ripple applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,7 +8,8 @@ import {
8
8
  promptTemplate,
9
9
  promptOverwrite,
10
10
  promptPackageManager,
11
- promptGitInit
11
+ promptGitInit,
12
+ promptStylingFramework
12
13
  } from '../lib/prompts.js';
13
14
  import { createProject } from '../lib/project-creator.js';
14
15
 
@@ -71,6 +72,11 @@ export async function createCommand(projectName, options) {
71
72
  gitInit = false;
72
73
  }
73
74
 
75
+ let stylingFramework = 'vanilla';
76
+ if (!options.yes) {
77
+ stylingFramework = await promptStylingFramework();
78
+ }
79
+
74
80
  // Step 6: Create the project
75
81
  console.log();
76
82
  console.log(`Creating Ripple app in ${green(projectPath)}...`);
@@ -83,7 +89,8 @@ export async function createCommand(projectName, options) {
83
89
  template,
84
90
  packageManager,
85
91
  typescript: true,
86
- gitInit
92
+ gitInit,
93
+ stylingFramework
87
94
  });
88
95
 
89
96
  showNextSteps(projectName, packageManager);
package/src/index.js CHANGED
@@ -14,7 +14,7 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'),
14
14
  const program = new Command();
15
15
 
16
16
  program
17
- .name('create-ripple-app')
17
+ .name('create-ripple')
18
18
  .description('Interactive CLI tool for creating Ripple applications')
19
19
  .version(packageJson.version)
20
20
  .helpOption('-h, --help', 'Display help for command');
@@ -13,16 +13,16 @@ import { downloadTemplate, getLocalTemplatePath, isLocalDevelopment } from './te
13
13
  * @param {string} options.projectPath - Absolute path where project will be created
14
14
  * @param {string} options.template - Template to use
15
15
  * @param {string} options.packageManager - Package manager to use
16
- * @param {boolean} options.typescript - Whether to use TypeScript
17
16
  * @param {boolean} options.gitInit - Whether to initialize Git
17
+ * @param {string} options.stylingFramework - Styling framework to use
18
18
  */
19
19
  export async function createProject({
20
20
  projectName,
21
21
  projectPath,
22
22
  template,
23
23
  packageManager = 'npm',
24
- typescript = true,
25
- gitInit = true
24
+ gitInit = true,
25
+ stylingFramework = 'vanilla'
26
26
  }) {
27
27
  console.log(dim(`Creating project: ${projectName}`));
28
28
  console.log(dim(`Template: ${template}`));
@@ -95,7 +95,7 @@ export async function createProject({
95
95
  // Step 4: Update package.json
96
96
  const spinner4 = ora('Configuring package.json...').start();
97
97
  try {
98
- updatePackageJson(projectPath, projectName, packageManager, typescript);
98
+ updatePackageJson(projectPath, projectName, packageManager, stylingFramework);
99
99
  spinner4.succeed('Package.json configured');
100
100
  } catch (error) {
101
101
  spinner4.fail('Failed to configure package.json');
@@ -105,16 +105,29 @@ export async function createProject({
105
105
  throw error;
106
106
  }
107
107
 
108
- // Step 5: Initialize Git (if requested)
108
+ // Step 5: Configure styling
109
+ const spinner5 = ora('Configuring styling framework...').start();
110
+ try {
111
+ configureStyling(projectPath, stylingFramework);
112
+ spinner5.succeed('Styling framework configured');
113
+ } catch (error) {
114
+ spinner5.fail('Failed to configure styling framework');
115
+ if (isTemporary) {
116
+ rmSync(templatePath, { recursive: true, force: true });
117
+ }
118
+ throw error;
119
+ }
120
+
121
+
122
+
123
+ // Step 6: Initialize Git (if requested)
109
124
  if (gitInit) {
110
- const spinner5 = ora('Initializing Git repository...').start();
125
+ const spinner6 = ora('Initializing Git repository...').start();
111
126
  try {
112
127
  execSync('git init', { cwd: projectPath, stdio: 'ignore' });
113
- execSync('git add .', { cwd: projectPath, stdio: 'ignore' });
114
- execSync('git commit -m "Initial commit"', { cwd: projectPath, stdio: 'ignore' });
115
- spinner5.succeed('Git repository initialized');
128
+ spinner6.succeed('Git repository initialized');
116
129
  } catch (error) {
117
- spinner5.warn('Git initialization failed (optional)');
130
+ spinner6.warn('Git initialization failed (optional)');
118
131
  }
119
132
  }
120
133
 
@@ -136,9 +149,9 @@ export async function createProject({
136
149
  * @param {string} projectPath - Path to the project
137
150
  * @param {string} projectName - Name of the project
138
151
  * @param {string} packageManager - Package manager being used
139
- * @param {boolean} typescript - Whether TypeScript is enabled
152
+ * @param {string} stylingFramework - Styling framework being used
140
153
  */
141
- function updatePackageJson(projectPath, projectName, packageManager, typescript) {
154
+ function updatePackageJson(projectPath, projectName, packageManager, stylingFramework) {
142
155
  const packageJsonPath = join(projectPath, 'package.json');
143
156
 
144
157
  if (!existsSync(packageJsonPath)) {
@@ -156,13 +169,27 @@ function updatePackageJson(projectPath, projectName, packageManager, typescript)
156
169
  }
157
170
 
158
171
  // Update description
159
- packageJson.description = `A Ripple application created with create-ripple-app`;
172
+ packageJson.description = `A Ripple application created with create-ripple`;
160
173
 
161
174
  // Add package manager field if not npm
162
175
  if (packageManager !== 'npm') {
163
176
  packageJson.packageManager = getPackageManagerVersion(packageManager);
164
177
  }
165
178
 
179
+ // Add styling dependencies
180
+ if (stylingFramework === 'tailwind') {
181
+ packageJson.devDependencies = {
182
+ ...packageJson.devDependencies,
183
+ 'tailwindcss': '^4.1.12',
184
+ '@tailwindcss/vite': '^4.1.12'
185
+ };
186
+ } else if (stylingFramework === 'bootstrap') {
187
+ packageJson.dependencies = {
188
+ ...packageJson.dependencies,
189
+ 'bootstrap': '^5.3.0'
190
+ };
191
+ }
192
+
166
193
  // Ensure we're using the latest versions
167
194
  updateDependencyVersions(packageJson);
168
195
 
@@ -172,6 +199,52 @@ function updatePackageJson(projectPath, projectName, packageManager, typescript)
172
199
  writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
173
200
  }
174
201
 
202
+ function configureStyling(projectPath, stylingFramework) {
203
+ if (stylingFramework === 'tailwind') {
204
+ const tailwindConfig = `import type { Config } from 'tailwindcss';
205
+ export default {
206
+ content: [
207
+ "./index.html",
208
+ "./src/**/*.{ts,ripple}",
209
+ ],
210
+ theme: {
211
+ extend: {},
212
+ },
213
+ plugins: []
214
+ } satisfies Config
215
+ `;
216
+ writeFileSync(join(projectPath, 'tailwind.config.ts'), tailwindConfig);
217
+ const mainCss = `@import "tailwindcss";
218
+ @config "./tailwind.config.ts";`;
219
+ writeFileSync(join(projectPath, 'src', 'index.css'), mainCss);
220
+
221
+ const mainTs = readFileSync(join(projectPath, 'src', 'index.ts'), 'utf-8');
222
+ const newMainTs = "import './index.css';\n" + mainTs;
223
+ writeFileSync(join(projectPath, 'src', 'index.ts'), newMainTs);
224
+
225
+ if (existsSync(join(projectPath, 'vite.config.js'))) {
226
+ rmSync(join(projectPath, 'vite.config.js'));
227
+ }
228
+ const viteConfig = `import { defineConfig } from 'vite';
229
+ import { ripple } from 'vite-plugin-ripple';
230
+ import tailwindcss from '@tailwindcss/vite';
231
+
232
+ export default defineConfig({
233
+ plugins: [ripple(), tailwindcss()],
234
+ server: {
235
+ port: 3000
236
+ }
237
+ });
238
+ `;
239
+ writeFileSync(join(projectPath, 'vite.config.js'), viteConfig);
240
+
241
+ } else if (stylingFramework === 'bootstrap') {
242
+ const mainTs = readFileSync(join(projectPath, 'src', 'index.ts'), 'utf-8');
243
+ const newMainTs = "import 'bootstrap/dist/css/bootstrap.min.css';\n" + mainTs;
244
+ writeFileSync(join(projectPath, 'src', 'index.ts'), newMainTs);
245
+ }
246
+ }
247
+
175
248
  /**
176
249
  * Update dependency versions to latest
177
250
  * @param {object} packageJson - Package.json object
@@ -134,3 +134,31 @@ export async function promptGitInit() {
134
134
 
135
135
  return response.gitInit;
136
136
  }
137
+
138
+ export async function promptStylingFramework() {
139
+ const response = await prompts({
140
+ type: 'select',
141
+ name: 'stylingFramework',
142
+ message: 'Which styling framework would you like to integrate with Ripple?',
143
+ choices: [{
144
+ title: 'Vanilla CSS',
145
+ value: 'vanilla',
146
+ description: 'Use Vanilla CSS for styling your components'
147
+ }, {
148
+ title: 'Bootstrap',
149
+ value: 'bootstrap',
150
+ description: 'Use Bootstrap classes to style your components'
151
+ }, {
152
+ title: 'TailwindCSS',
153
+ value: 'tailwind',
154
+ description: 'Use TailwindCSS to style your components'
155
+ }]
156
+ })
157
+
158
+ if (response.stylingFramework === undefined) {
159
+ console.log(red('✖ Operation cancelled'));
160
+ process.exit(1);
161
+ }
162
+
163
+ return response.stylingFramework;
164
+ }
@@ -67,7 +67,7 @@ describe('CLI Integration Tests', () => {
67
67
 
68
68
  expect(result.code).toBe(0);
69
69
  expect(result.stdout).toContain('Interactive CLI tool for creating Ripple applications');
70
- expect(result.stdout).toContain('Usage: create-ripple-app');
70
+ expect(result.stdout).toContain('Usage: create-ripple');
71
71
  expect(result.stdout).toContain('Arguments:');
72
72
  expect(result.stdout).toContain('Options:');
73
73
  expect(result.stdout).toContain('--template');
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
- import { createProject } from '../../src/lib/project-creator.js';
5
+ import { createProject, updatePackageJson, configureStyling } from '../../src/lib/project-creator.js';
6
6
  import { getLocalTemplatePath, isLocalDevelopment, validateTemplate } from '../../src/lib/templates.js';
7
7
 
8
8
  // Mock ora for cleaner test output
@@ -116,7 +116,7 @@ describe('createProject integration tests', () => {
116
116
  // Verify package.json was updated
117
117
  const packageJson = JSON.parse(readFileSync(join(projectPath, 'package.json'), 'utf-8'));
118
118
  expect(packageJson.name).toBe('test-project');
119
- expect(packageJson.description).toBe('A Ripple application created with create-ripple-app');
119
+ expect(packageJson.description).toBe('A Ripple application created with create-ripple');
120
120
  expect(packageJson.version).toBe('1.0.0');
121
121
  });
122
122
 
@@ -227,4 +227,45 @@ describe('createProject integration tests', () => {
227
227
  expect(existsSync(join(projectPath, 'package.json'))).toBe(true);
228
228
  expect(existsSync(join(projectPath, 'existing-file.txt'))).toBe(true);
229
229
  });
230
+ it('should configure Tailwind CSS correctly', async () => {
231
+ writeFileSync(join(templatePath, 'src', 'index.ts'), 'console.log("Hello, World!");');
232
+ await createProject({
233
+ projectName: 'test-tailwind-project',
234
+ projectPath,
235
+ template: 'basic',
236
+ packageManager: 'npm',
237
+ typescript: true,
238
+ gitInit: false,
239
+ stylingFramework: 'tailwind'
240
+ });
241
+
242
+ const packageJson = JSON.parse(readFileSync(join(projectPath, 'package.json'), 'utf-8'));
243
+ expect(packageJson.devDependencies).toHaveProperty('tailwindcss');
244
+ expect(packageJson.devDependencies).toHaveProperty('@tailwindcss/vite');
245
+
246
+ expect(existsSync(join(projectPath, 'tailwind.config.ts'))).toBe(true);
247
+ expect(readFileSync(join(projectPath, 'src', 'index.ts'), 'utf-8')).toContain("import './index.css';\n");
248
+ expect(existsSync(join(projectPath, 'src', 'index.css'))).toBe(true);
249
+ expect(readFileSync(join(projectPath, 'src', 'index.css'), 'utf-8')).toContain('@import "tailwindcss"');
250
+ });
251
+
252
+ it('should configure Bootstrap correctly', async () => {
253
+ writeFileSync(join(templatePath, 'src', 'index.ts'), 'console.log("Hello, World!");');
254
+
255
+ await createProject({
256
+ projectName: 'test-bootstrap-project',
257
+ projectPath,
258
+ template: 'basic',
259
+ packageManager: 'npm',
260
+ typescript: true,
261
+ gitInit: false,
262
+ stylingFramework: 'bootstrap'
263
+ });
264
+
265
+ const packageJson = JSON.parse(readFileSync(join(projectPath, 'package.json'), 'utf-8'));
266
+ expect(packageJson.dependencies).toHaveProperty('bootstrap');
267
+
268
+ const mainTsContent = readFileSync(join(projectPath, 'src', 'index.ts'), 'utf-8');
269
+ expect(mainTsContent).toContain("import 'bootstrap/dist/css/bootstrap.min.css';");
270
+ });
230
271
  });
@@ -28,7 +28,8 @@ import {
28
28
  promptOverwrite,
29
29
  promptPackageManager,
30
30
  promptTypeScript,
31
- promptGitInit
31
+ promptGitInit,
32
+ promptStylingFramework
32
33
  } from '../../src/lib/prompts.js';
33
34
 
34
35
  describe('Prompts', () => {
@@ -204,4 +205,36 @@ describe('Prompts', () => {
204
205
  expect(mockExit).toHaveBeenCalledWith(1);
205
206
  });
206
207
  });
208
+ describe('promptStylingFramework', () => {
209
+ it('should return selected styling framework', async () => {
210
+ prompts.default.mockResolvedValue({ stylingFramework: 'tailwind' });
211
+
212
+ const result = await promptStylingFramework();
213
+ expect(result).toBe('tailwind');
214
+ expect(prompts.default).toHaveBeenCalledWith({
215
+ type: 'select',
216
+ name: 'stylingFramework',
217
+ message: 'Which styling framework would you like to integrate with Ripple?',
218
+ choices: [{
219
+ title: 'Vanilla CSS',
220
+ value: 'vanilla',
221
+ description: 'Use Vanilla CSS for styling your components'
222
+ }, {
223
+ title: 'Bootstrap',
224
+ value: 'bootstrap',
225
+ description: 'Use Bootstrap classes to style your components'
226
+ }, {
227
+ title: 'TailwindCSS',
228
+ value: 'tailwind',
229
+ description: 'Use TailwindCSS to style your components'
230
+ }]
231
+ });
232
+ });
233
+
234
+ it('should return undefined when user cancels', async () => {
235
+ prompts.default.mockResolvedValue({ stylingFramework: undefined });
236
+ const result = await promptStylingFramework();
237
+ expect(result).toBeUndefined();
238
+ });
239
+ });
207
240
  });