generator-bitloops 0.3.22 → 0.3.25

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
@@ -6,4 +6,4 @@ Nonetheless, you can use it independently to setup your next Next.js project wit
6
6
 
7
7
  ## How to run it
8
8
 
9
- `npx -y generator-bitloops setup --project="Your Project Name" --nextjs --typescript --tailwind --storybook --cypress`
9
+ `npx -y generator-bitloops setup --project="Your Project Name" --nextjs --i18n --typescript --tailwind --storybook --cypress`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generator-bitloops",
3
- "version": "0.3.22",
3
+ "version": "0.3.25",
4
4
  "description": "Next.js with TypeScript, Tailwind, Storybook and Cypress generator by Bitloops",
5
5
  "license": "MIT",
6
6
  "author": "Bitloops S.A.",
package/setup/index.js CHANGED
@@ -111,14 +111,21 @@ export default class extends Generator {
111
111
  default: false,
112
112
  });
113
113
 
114
+ this.option('i18n', {
115
+ type: Boolean,
116
+ description:
117
+ 'Add i18n internationalization support (i18next, i18next-icu, react-i18next)',
118
+ default: false,
119
+ });
120
+
114
121
  this.installNextJS = async function () {
115
122
  // Clone Next.js template with Tailwind if specified, using the project name
116
- const createNextAppCommand = ['-y', 'create-next-app@15.3.3'];
123
+ const createNextAppCommand = ['-y', 'create-next-app@latest'];
117
124
  createNextAppCommand.push(toKebabCase(this.options.project)); // Use the project name for the directory
125
+ createNextAppCommand.push('--yes');
118
126
  createNextAppCommand.push('--app');
119
127
  createNextAppCommand.push('--empty');
120
128
  createNextAppCommand.push('--src-dir');
121
- createNextAppCommand.push('--turbopack'); // when we go to Next.js 15
122
129
  createNextAppCommand.push('--import-alias');
123
130
  createNextAppCommand.push('@/*');
124
131
  createNextAppCommand.push('--use-npm');
@@ -140,11 +147,11 @@ export default class extends Generator {
140
147
  await new Promise((resolve, error) => {
141
148
  exec(
142
149
  `npx ${createNextAppCommand.join(' ')} && cd ${toKebabCase(
143
- this.options.project
144
- )} && npm install ${additionalPackages}`
150
+ this.options.project,
151
+ )} && npm install ${additionalPackages}`,
145
152
  ).on('exit', (code) => {
146
153
  this.destinationRoot(
147
- this.destinationPath(toKebabCase(this.options.project))
154
+ this.destinationPath(toKebabCase(this.options.project)),
148
155
  );
149
156
  resolve();
150
157
  });
@@ -160,33 +167,50 @@ export default class extends Generator {
160
167
  });
161
168
  const versions = JSON.parse(versionsRaw);
162
169
 
163
- // Filter for stable 9.0.x versions (exclude alpha/beta)
170
+ // Filter for stable 10.x versions (exclude alpha/beta/rc)
164
171
  const stableVersions = versions
165
- .filter(version => version.startsWith('9.0.'))
166
- .filter(version => !version.includes('-')); // Exclude pre-releases like -alpha or -beta
172
+ .filter((version) => version.startsWith('10.'))
173
+ .filter((version) => !version.includes('-')); // Exclude pre-releases like -alpha or -beta
167
174
 
168
175
  // Sort descending and get the latest
169
- const latest90 = stableVersions
170
- .sort((a, b) => {
171
- // Split version strings like '9.0.9' into [9, 0, 9]
172
- const aParts = a.split(DOT).map(Number);
173
- const bParts = b.split(DOT).map(Number);
174
- // Compare major, then minor, then patch
175
- if (aParts[0] !== bParts[0]) return bParts[0] - aParts[0];
176
- if (aParts[1] !== bParts[1]) return bParts[1] - aParts[1];
177
- return bParts[2] - aParts[2];
178
- })[0];
179
-
180
- if (!latest90) {
181
- throw new Error('No stable 9.0.x versions found.');
176
+ const latest10 = stableVersions.sort((a, b) => {
177
+ // Split version strings like '10.0.9' into [10, 0, 9]
178
+ const aParts = a.split(DOT).map(Number);
179
+ const bParts = b.split(DOT).map(Number);
180
+ // Compare major, then minor, then patch
181
+ if (aParts[0] !== bParts[0]) return bParts[0] - aParts[0];
182
+ if (aParts[1] !== bParts[1]) return bParts[1] - aParts[1];
183
+ return bParts[2] - aParts[2];
184
+ })[0];
185
+
186
+ if (!latest10) {
187
+ throw new Error('No stable 10.x versions found.');
182
188
  }
183
189
 
184
- this.log(`Latest stable 9.0 version: ${latest90}`);
185
- //Initializing sb with nextjs+vite
186
- spawnSync('npx', ['-y', 'storybook@latest', 'init', '--no-dev', '--yes', '--type', 'nextjs', '--builder', 'vite'], { stdio: 'inherit', cwd: this.destinationRoot() });
190
+ this.log(`Latest stable 10.x version: ${latest10}`);
191
+ // Initializing storybook with nextjs+vite
192
+ spawnSync(
193
+ 'npx',
194
+ [
195
+ '-y',
196
+ `storybook@${latest10}`,
197
+ 'init',
198
+ '--no-dev',
199
+ '--yes',
200
+ '--type',
201
+ 'nextjs',
202
+ '--builder',
203
+ 'vite',
204
+ ],
205
+ { stdio: 'inherit', cwd: this.destinationRoot() },
206
+ );
187
207
  this.log('Storybook installed!');
188
- //Verifies the correct nextjs-vite framework is used
189
- spawnSync('npm', ['install', '--save-dev', '@storybook/nextjs-vite@^9'], { stdio: 'inherit', cwd: this.destinationRoot() });
208
+ // Verifies the correct nextjs-vite framework is used
209
+ spawnSync(
210
+ 'npm',
211
+ ['install', '--save-dev', '@storybook/nextjs-vite@^10'],
212
+ { stdio: 'inherit', cwd: this.destinationRoot() },
213
+ );
190
214
  this.log('@storybook/nextjs-vite installed!');
191
215
  }
192
216
  };
@@ -195,44 +219,64 @@ export default class extends Generator {
195
219
  // Conditionally add Cypress
196
220
  if (this.options.cypress) {
197
221
  this.log('Installing Cypress...');
198
- spawnSync('npm', ['install', '--save-dev', 'cypress'], { stdio: 'inherit', cwd: this.destinationRoot() });
222
+ spawnSync('npm', ['install', '--save-dev', 'cypress'], {
223
+ stdio: 'inherit',
224
+ cwd: this.destinationRoot(),
225
+ });
199
226
  this.log('Cypress installed!');
200
227
  if (this.options.bitloops) {
201
- spawnSync('npm', [
202
- 'install',
203
- '--save-dev',
204
- 'mochawesome',
205
- 'mochawesome-merge',
206
- 'mochawesome-report-generator',
207
- ], { stdio: 'inherit', cwd: this.destinationRoot() });
228
+ spawnSync(
229
+ 'npm',
230
+ [
231
+ 'install',
232
+ '--save-dev',
233
+ 'mochawesome',
234
+ 'mochawesome-merge',
235
+ 'mochawesome-report-generator',
236
+ ],
237
+ { stdio: 'inherit', cwd: this.destinationRoot() },
238
+ );
208
239
  }
209
240
  }
210
241
  };
211
242
 
243
+ this.installI18n = function () {
244
+ // Conditionally add i18n packages
245
+ if (this.options.i18n) {
246
+ this.log('Installing i18n packages...');
247
+ spawnSync(
248
+ 'npm',
249
+ ['install', 'i18next', 'i18next-icu', 'react-i18next'],
250
+ { stdio: 'inherit', cwd: this.destinationRoot() },
251
+ );
252
+ this.log('i18n packages installed!');
253
+ }
254
+ };
255
+
212
256
  this.installPrimitives = function () {
213
257
  // Conditionally add Primitives
214
258
  if (this.options.primitives) {
215
259
  this.log('Installing Primitives...');
216
-
260
+
217
261
  const platformNextIndexPath = `${PLATFORM_NEXT_SRC_FOLDER}/index.ts`;
218
262
  deleteFileIfExists(this.destinationPath(platformNextIndexPath));
219
263
  this.fs.copyTpl(
220
264
  this.templatePath(platformNextIndexPath),
221
- this.destinationPath(platformNextIndexPath)
265
+ this.destinationPath(platformNextIndexPath),
222
266
  );
223
267
 
224
268
  const platformNextImgPath = `${PLATFORM_NEXT_SRC_FOLDER}/Img.tsx`;
225
269
  deleteFileIfExists(this.destinationPath(platformNextImgPath));
226
270
  this.fs.copyTpl(
227
271
  this.templatePath(platformNextImgPath),
228
- this.destinationPath(platformNextImgPath)
272
+ this.destinationPath(platformNextImgPath),
229
273
  );
230
274
 
231
275
  const platformNextTypesPath = `${PLATFORM_NEXT_SRC_FOLDER}/types.ts`;
232
276
  deleteFileIfExists(this.destinationPath(platformNextTypesPath));
233
277
  this.fs.copyTpl(
234
278
  this.templatePath(platformNextTypesPath),
235
- this.destinationPath(platformNextTypesPath)
279
+ this.destinationPath(platformNextTypesPath),
236
280
  );
237
281
 
238
282
  this.log('Primitives installed!');
@@ -248,7 +292,7 @@ export default class extends Generator {
248
292
  this.log('Setting up Tailwind CSS with Storybook...');
249
293
  this.fs.copyTpl(
250
294
  this.templatePath('storybook.preview.ts'),
251
- this.destinationPath('.storybook/preview.ts')
295
+ this.destinationPath('.storybook/preview.ts'),
252
296
  );
253
297
  }
254
298
  this.log('Removing default Storybook stories...');
@@ -267,66 +311,75 @@ export default class extends Generator {
267
311
  this.log('Adding Cypress config...');
268
312
  this.fs.copyTpl(
269
313
  this.templatePath('cypress.config.ts'),
270
- this.destinationPath('cypress.config.ts')
314
+ this.destinationPath('cypress.config.ts'),
271
315
  );
272
316
  }
273
317
 
274
318
  deleteFileIfExists(this.destinationPath('src/app/page.tsx'));
275
319
  this.fs.copyTpl(
276
320
  this.templatePath('next.app.page.tsx'),
277
- this.destinationPath('src/app/page.tsx')
321
+ this.destinationPath('src/app/page.tsx'),
278
322
  );
279
323
 
280
324
  deleteFileIfExists(this.destinationPath('src/app/layout.tsx'));
281
325
  this.fs.copyTpl(
282
326
  this.templatePath('next.app.layout.tsx'),
283
327
  this.destinationPath('src/app/layout.tsx'),
284
- { projectName: this.options.project }
328
+ { projectName: this.options.project },
285
329
  );
286
330
 
287
331
  this.log('Adding Meyer reset in global.css...');
288
332
  deleteFileIfExists(this.destinationPath('src/app/globals.css'));
289
333
  this.fs.copyTpl(
290
334
  this.templatePath('globals.css'),
291
- this.destinationPath('src/app/globals.css')
335
+ this.destinationPath('src/app/globals.css'),
292
336
  );
293
337
 
338
+ if (this.options.typescript) {
339
+ this.log('Replacing tsconfig.json with Bitloops template...');
340
+ deleteFileIfExists(this.destinationPath('tsconfig.json'));
341
+ this.fs.copyTpl(
342
+ this.templatePath('tsconfig.json'),
343
+ this.destinationPath('tsconfig.json'),
344
+ );
345
+ this.log('tsconfig.json updated!');
346
+ }
347
+
294
348
  if (this.options.bitloops) {
295
349
  this.log('Adding Bitloops support components...');
296
350
  const unsupportedPath =
297
351
  'src/components/bitloops/unsupported/Unsupported.tsx';
298
352
  this.fs.copyTpl(
299
353
  this.templatePath(unsupportedPath),
300
- this.destinationPath(unsupportedPath)
354
+ this.destinationPath(unsupportedPath),
301
355
  );
302
356
  const buttonPath = 'src/components/bitloops/button/Button.tsx';
303
357
  this.fs.copyTpl(
304
358
  this.templatePath(buttonPath),
305
- this.destinationPath(buttonPath)
359
+ this.destinationPath(buttonPath),
306
360
  );
307
361
  if (this.options.storybook) {
308
362
  const unsupportedPath =
309
363
  'src/components/bitloops/unsupported/Unsupported.stories.tsx';
310
364
  this.fs.copyTpl(
311
365
  this.templatePath(unsupportedPath),
312
- this.destinationPath(unsupportedPath)
366
+ this.destinationPath(unsupportedPath),
313
367
  );
314
368
  const buttonPath =
315
369
  'src/components/bitloops/button/Button.stories.tsx';
316
370
  this.fs.copyTpl(
317
371
  this.templatePath(buttonPath),
318
- this.destinationPath(buttonPath)
372
+ this.destinationPath(buttonPath),
319
373
  );
320
374
  }
321
375
  if (this.options.cypress) {
322
376
  const path = 'cypress/helpers/index.ts';
323
377
  this.fs.copyTpl(this.templatePath(path), this.destinationPath(path));
324
378
  }
325
- spawnSync('npm', [
326
- 'install',
327
- '--save-dev',
328
- 'react-aria-components',
329
- ], { stdio: 'inherit', cwd: this.destinationRoot() });
379
+ spawnSync('npm', ['install', '--save-dev', 'react-aria-components'], {
380
+ stdio: 'inherit',
381
+ cwd: this.destinationRoot(),
382
+ });
330
383
  }
331
384
  };
332
385
 
@@ -335,8 +388,8 @@ export default class extends Generator {
335
388
  await new Promise((resolve) => {
336
389
  exec(
337
390
  `cd ${toKebabCase(
338
- this.options.project
339
- )} && git add . && git commit -m "Initial setup"`
391
+ this.options.project,
392
+ )} && git add . && git commit -m "Initial setup"`,
340
393
  ).on('exit', (code) => {
341
394
  if (code !== 0) {
342
395
  this.log('Error committing changes to git! ', code);
@@ -353,30 +406,31 @@ export default class extends Generator {
353
406
  // Check if the project name and --nextjs flag are provided
354
407
  if (!this.options.project) {
355
408
  this.log(
356
- 'Error: --project option is required to specify the project name.'
409
+ 'Error: --project option is required to specify the project name.',
357
410
  );
358
411
  process.exit(1);
359
412
  }
360
413
 
361
414
  if (!this.options.nextjs) {
362
415
  this.log(
363
- 'Error: --nextjs option is currently required to scaffold a project.'
416
+ 'Error: --nextjs option is currently required to scaffold a project.',
364
417
  );
365
418
  process.exit(1);
366
419
  }
367
420
 
368
421
  this.log(
369
422
  `Initializing project ${toKebabCase(
370
- this.options.project
371
- )} with the selected options...`
423
+ this.options.project,
424
+ )} with the selected options...`,
372
425
  );
373
426
  }
374
427
 
375
428
  async main() {
376
429
  await this.installNextJS();
377
- this.installStorybook();
378
430
  this.installCypress();
431
+ this.installI18n();
379
432
  this.installPrimitives();
433
+ this.installStorybook();
380
434
  await this.patchFiles();
381
435
  if (this.options.git) {
382
436
  await this.commitChanges();
@@ -386,8 +440,8 @@ export default class extends Generator {
386
440
  end() {
387
441
  this.log(
388
442
  `Your Bitloops project '${toKebabCase(
389
- this.options.project
390
- )}' setup is complete! 🎉🎉🎉`
443
+ this.options.project,
444
+ )}' setup is complete! 🎉🎉🎉`,
391
445
  );
392
446
  this.log('');
393
447
  this.log('Use the following commands to start:');
@@ -398,7 +452,7 @@ export default class extends Generator {
398
452
  this.log('- `npx cypress open --e2e --browser chrome` to open Cypress.');
399
453
  if (this.options.cypress)
400
454
  this.log(
401
- '- `npx cypress run --e2e --browser chrome` to run Cypress on the terminal.'
455
+ '- `npx cypress run --e2e --browser chrome` to run Cypress on the terminal.',
402
456
  );
403
457
  }
404
458
  }
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "baseUrl": ".",
22
+ "paths": {
23
+ "@/*": ["src/*"],
24
+ "@/components/*": ["src/components/*"],
25
+ "@/assets/*": ["src/assets/*"],
26
+ "@/primitives": ["platform-next/src"],
27
+ "@/primitives/*": ["platform-next/src/*"],
28
+ "@/hooks/*": ["src/hooks/*"],
29
+ "@/lib/*": ["src/lib/*"],
30
+ "@/types/*": ["src/types/*"],
31
+ "@/utils/*": ["src/utils/*"]
32
+ }
33
+ },
34
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
35
+ "exclude": ["node_modules"]
36
+ }