create-ripple 0.1.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ripple",
3
- "version": "0.1.3",
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);
@@ -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,17 +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
- // We should not automatically commit without asking user, so I have currently commented the code:
114
- // execSync('git add .', { cwd: projectPath, stdio: 'ignore' });
115
- // execSync('git commit -m "Initial commit"', { cwd: projectPath, stdio: 'ignore' });
116
- spinner5.succeed('Git repository initialized');
128
+ spinner6.succeed('Git repository initialized');
117
129
  } catch (error) {
118
- spinner5.warn('Git initialization failed (optional)');
130
+ spinner6.warn('Git initialization failed (optional)');
119
131
  }
120
132
  }
121
133
 
@@ -137,9 +149,9 @@ export async function createProject({
137
149
  * @param {string} projectPath - Path to the project
138
150
  * @param {string} projectName - Name of the project
139
151
  * @param {string} packageManager - Package manager being used
140
- * @param {boolean} typescript - Whether TypeScript is enabled
152
+ * @param {string} stylingFramework - Styling framework being used
141
153
  */
142
- function updatePackageJson(projectPath, projectName, packageManager, typescript) {
154
+ function updatePackageJson(projectPath, projectName, packageManager, stylingFramework) {
143
155
  const packageJsonPath = join(projectPath, 'package.json');
144
156
 
145
157
  if (!existsSync(packageJsonPath)) {
@@ -164,6 +176,20 @@ function updatePackageJson(projectPath, projectName, packageManager, typescript)
164
176
  packageJson.packageManager = getPackageManagerVersion(packageManager);
165
177
  }
166
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
+
167
193
  // Ensure we're using the latest versions
168
194
  updateDependencyVersions(packageJson);
169
195
 
@@ -173,6 +199,52 @@ function updatePackageJson(projectPath, projectName, packageManager, typescript)
173
199
  writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
174
200
  }
175
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
+
176
248
  /**
177
249
  * Update dependency versions to latest
178
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
+ }
@@ -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
@@ -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
  });