generator-bitloops 0.3.23 → 0.3.26

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.23",
3
+ "version": "0.3.26",
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,17 +111,24 @@ 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
- createNextAppCommand.push('--use-npm');
131
+ createNextAppCommand.push('--use-pnpm');
125
132
  createNextAppCommand.push('--eslint');
126
133
 
127
134
  if (this.options.typescript) {
@@ -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
+ )} && pnpm add ${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
+ 'pnpm',
211
+ ['add', '-D', '@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('pnpm', ['add', '-D', '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
+ 'pnpm',
230
+ [
231
+ 'add',
232
+ '-D',
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
+ 'pnpm',
249
+ ['add', '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,58 +311,38 @@ 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
 
294
338
  if (this.options.typescript) {
295
- this.log('Updating tsconfig.json paths...');
296
- const tsconfigPath = this.destinationPath('tsconfig.json');
297
- if (this.fs.exists(tsconfigPath)) {
298
- const tsconfigContent = this.fs.read(tsconfigPath);
299
- const tsconfig = JSON.parse(tsconfigContent);
300
-
301
- // Initialize compilerOptions if it doesn't exist
302
- if (!tsconfig.compilerOptions) {
303
- tsconfig.compilerOptions = {};
304
- }
305
-
306
- // Initialize paths if it doesn't exist
307
- if (!tsconfig.compilerOptions.paths) {
308
- tsconfig.compilerOptions.paths = {};
309
- }
310
-
311
- // Add or merge the path aliases
312
- tsconfig.compilerOptions.paths = {
313
- ...tsconfig.compilerOptions.paths,
314
- "@/primitives": ["./platform-next/src"],
315
- "@/assets": ["./src/assets"]
316
- };
317
-
318
- deleteFileIfExists(tsconfigPath);
319
- this.fs.write(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n');
320
- this.log('tsconfig.json paths updated!');
321
- }
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!');
322
346
  }
323
347
 
324
348
  if (this.options.bitloops) {
@@ -327,36 +351,35 @@ export default class extends Generator {
327
351
  'src/components/bitloops/unsupported/Unsupported.tsx';
328
352
  this.fs.copyTpl(
329
353
  this.templatePath(unsupportedPath),
330
- this.destinationPath(unsupportedPath)
354
+ this.destinationPath(unsupportedPath),
331
355
  );
332
356
  const buttonPath = 'src/components/bitloops/button/Button.tsx';
333
357
  this.fs.copyTpl(
334
358
  this.templatePath(buttonPath),
335
- this.destinationPath(buttonPath)
359
+ this.destinationPath(buttonPath),
336
360
  );
337
361
  if (this.options.storybook) {
338
362
  const unsupportedPath =
339
363
  'src/components/bitloops/unsupported/Unsupported.stories.tsx';
340
364
  this.fs.copyTpl(
341
365
  this.templatePath(unsupportedPath),
342
- this.destinationPath(unsupportedPath)
366
+ this.destinationPath(unsupportedPath),
343
367
  );
344
368
  const buttonPath =
345
369
  'src/components/bitloops/button/Button.stories.tsx';
346
370
  this.fs.copyTpl(
347
371
  this.templatePath(buttonPath),
348
- this.destinationPath(buttonPath)
372
+ this.destinationPath(buttonPath),
349
373
  );
350
374
  }
351
375
  if (this.options.cypress) {
352
376
  const path = 'cypress/helpers/index.ts';
353
377
  this.fs.copyTpl(this.templatePath(path), this.destinationPath(path));
354
378
  }
355
- spawnSync('npm', [
356
- 'install',
357
- '--save-dev',
358
- 'react-aria-components',
359
- ], { stdio: 'inherit', cwd: this.destinationRoot() });
379
+ spawnSync('pnpm', ['add', '-D', 'react-aria-components'], {
380
+ stdio: 'inherit',
381
+ cwd: this.destinationRoot(),
382
+ });
360
383
  }
361
384
  };
362
385
 
@@ -365,8 +388,8 @@ export default class extends Generator {
365
388
  await new Promise((resolve) => {
366
389
  exec(
367
390
  `cd ${toKebabCase(
368
- this.options.project
369
- )} && git add . && git commit -m "Initial setup"`
391
+ this.options.project,
392
+ )} && git add . && git commit -m "Initial setup"`,
370
393
  ).on('exit', (code) => {
371
394
  if (code !== 0) {
372
395
  this.log('Error committing changes to git! ', code);
@@ -383,30 +406,31 @@ export default class extends Generator {
383
406
  // Check if the project name and --nextjs flag are provided
384
407
  if (!this.options.project) {
385
408
  this.log(
386
- 'Error: --project option is required to specify the project name.'
409
+ 'Error: --project option is required to specify the project name.',
387
410
  );
388
411
  process.exit(1);
389
412
  }
390
413
 
391
414
  if (!this.options.nextjs) {
392
415
  this.log(
393
- 'Error: --nextjs option is currently required to scaffold a project.'
416
+ 'Error: --nextjs option is currently required to scaffold a project.',
394
417
  );
395
418
  process.exit(1);
396
419
  }
397
420
 
398
421
  this.log(
399
422
  `Initializing project ${toKebabCase(
400
- this.options.project
401
- )} with the selected options...`
423
+ this.options.project,
424
+ )} with the selected options...`,
402
425
  );
403
426
  }
404
427
 
405
428
  async main() {
406
429
  await this.installNextJS();
407
- this.installStorybook();
408
430
  this.installCypress();
431
+ this.installI18n();
409
432
  this.installPrimitives();
433
+ this.installStorybook();
410
434
  await this.patchFiles();
411
435
  if (this.options.git) {
412
436
  await this.commitChanges();
@@ -416,19 +440,19 @@ export default class extends Generator {
416
440
  end() {
417
441
  this.log(
418
442
  `Your Bitloops project '${toKebabCase(
419
- this.options.project
420
- )}' setup is complete! 🎉🎉🎉`
443
+ this.options.project,
444
+ )}' setup is complete! 🎉🎉🎉`,
421
445
  );
422
446
  this.log('');
423
447
  this.log('Use the following commands to start:');
424
- this.log('- `npm run dev` to start the Next.js app.');
448
+ this.log('- `pnpm dev` to start the Next.js app.');
425
449
  if (this.options.storybook)
426
- this.log('- `npm run storybook` to start Storybook.');
450
+ this.log('- `pnpm storybook` to start Storybook.');
427
451
  if (this.options.cypress)
428
452
  this.log('- `npx cypress open --e2e --browser chrome` to open Cypress.');
429
453
  if (this.options.cypress)
430
454
  this.log(
431
- '- `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.',
432
456
  );
433
457
  }
434
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
+ }