create-hsi-app 0.6.0 → 0.6.1

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,7 +1,7 @@
1
1
  # create-hsi-app
2
2
 
3
- Scaffold a new Vite or Next.js + React + TypeScript app from the frontend
4
- template.
3
+ Scaffold a new Vite or Next.js App Router SPA + React + TypeScript app from
4
+ the frontend template.
5
5
 
6
6
  ## Usage
7
7
 
@@ -24,7 +24,7 @@ import {
24
24
  } from './ui.mjs';
25
25
 
26
26
  const templateRepo = 'https://github.com/Hsiii/frontend-template.git';
27
- const templateTag = 'v0.6.0';
27
+ const templateTag = 'v0.6.1';
28
28
  const defaultAppName = 'my-app';
29
29
  const packageManagers = ['bun', 'npm', 'pnpm', 'yarn'];
30
30
  const nextVersion = '16.2.7';
@@ -82,6 +82,7 @@ async function main() {
82
82
  console.log(`- framework: ${frameworkLabel(selectedFramework)}`);
83
83
  console.log(`- package.json: name, version, scripts, packageManager`);
84
84
  logFrameworkFileChanges();
85
+ console.log(`- .gitignore: framework build artifacts`);
85
86
  console.log(`- README.md: install/dev/check commands`);
86
87
  console.log(`- package manager config: ${packageManagerConfigFile()}`);
87
88
  if (selectedPackageManager === 'bun') {
@@ -89,6 +90,7 @@ async function main() {
89
90
  }
90
91
  updateFrameworkFiles();
91
92
  updateAppText();
93
+ updateGitIgnore();
92
94
  updatePackageManagerFiles();
93
95
  writeAppReadme();
94
96
 
@@ -135,7 +137,7 @@ function updatePackageJson() {
135
137
  if (selectedFramework === 'next') {
136
138
  packageJson.scripts.dev = 'next dev';
137
139
  packageJson.scripts.build = 'next build';
138
- packageJson.scripts.preview = 'next start';
140
+ delete packageJson.scripts.preview;
139
141
  packageJson.scripts.check =
140
142
  'tsc -p tsconfig.json --noEmit && eslint . && prettier . --check && next build';
141
143
  packageJson.dependencies.next = nextVersion;
@@ -191,6 +193,14 @@ function updateFrameworkFiles() {
191
193
  }
192
194
  }
193
195
 
196
+ function updateGitIgnore() {
197
+ if (selectedFramework !== 'next') {
198
+ return;
199
+ }
200
+
201
+ appendGitIgnoreEntries(['.next/', 'next-env.d.ts']);
202
+ }
203
+
194
204
  function updatePackageManagerFiles() {
195
205
  rmSync(join(targetPath, 'bunfig.toml'), { force: true });
196
206
  rmSync(join(targetPath, '.npmrc'), { force: true });
@@ -250,7 +260,7 @@ function writeAppReadme() {
250
260
  const securityNote = securityNoteForPackageManager();
251
261
  const readme = `# ${appName}
252
262
 
253
- Created from the ${frameworkLabel(selectedFramework)} frontend template.
263
+ Created from the ${frameworkDescription(selectedFramework)} frontend template.
254
264
 
255
265
  ## Install
256
266
 
@@ -440,6 +450,30 @@ function replaceInFile(filePath, searchValue, replacement) {
440
450
  writeFileSync(filePath, source.replace(searchValue, replacement.with));
441
451
  }
442
452
 
453
+ function appendGitIgnoreEntries(entries) {
454
+ const gitIgnorePath = join(targetPath, '.gitignore');
455
+
456
+ if (!existsSync(gitIgnorePath)) {
457
+ writeFileSync(gitIgnorePath, `${entries.join('\n')}\n`);
458
+ return;
459
+ }
460
+
461
+ const source = readFileSync(gitIgnorePath, 'utf8');
462
+ const lines = new Set(source.split('\n').filter(Boolean));
463
+ let nextSource = source;
464
+
465
+ for (const entry of entries) {
466
+ if (lines.has(entry)) {
467
+ continue;
468
+ }
469
+
470
+ nextSource += nextSource.endsWith('\n') ? `${entry}\n` : `\n${entry}\n`;
471
+ lines.add(entry);
472
+ }
473
+
474
+ writeFileSync(gitIgnorePath, nextSource);
475
+ }
476
+
443
477
  function toPackageName(value) {
444
478
  const name = value
445
479
  .trim()
@@ -539,9 +573,9 @@ function readNpmBooleanFlag(name) {
539
573
  function logFrameworkFileChanges() {
540
574
  if (selectedFramework === 'next') {
541
575
  console.log(
542
- `- Next app router files: src/app/layout.tsx, src/app/page.tsx`
576
+ `- Next app router files: src/app/layout.tsx, src/app/[[...slug]]/*`
543
577
  );
544
- console.log(`- src/app/global.css: app styles`);
578
+ console.log(`- src/app/global.css: app styles and client bootstrap`);
545
579
  console.log(`- Next config: next.config.mjs, next-env.d.ts`);
546
580
  console.log(
547
581
  `- Vite files removed: index.html, vite.config.mjs, src/main.tsx`
@@ -561,15 +595,18 @@ function writeNextAppFiles() {
561
595
  rmSync(join(targetPath, 'src/global.css'), { force: true });
562
596
 
563
597
  const appPath = join(targetPath, 'src/app');
598
+ const catchAllPath = join(appPath, '[[...slug]]');
564
599
  mkdirSync(appPath, { recursive: true });
600
+ mkdirSync(catchAllPath, { recursive: true });
565
601
 
566
602
  writeFileSync(join(targetPath, 'next-env.d.ts'), nextEnvTypes());
567
603
  writeFileSync(join(targetPath, 'next.config.mjs'), nextConfig());
568
604
  writeFileSync(join(targetPath, 'eslint.config.mjs'), nextEslintConfig());
569
605
  writeFileSync(join(targetPath, 'tsconfig.json'), nextTsconfig());
570
606
  writeFileSync(join(appPath, 'layout.tsx'), nextLayout());
571
- writeFileSync(join(appPath, 'page.tsx'), nextPage());
572
607
  writeFileSync(join(appPath, 'global.css'), nextGlobalCss());
608
+ writeFileSync(join(catchAllPath, 'client.tsx'), nextClientPage());
609
+ writeFileSync(join(catchAllPath, 'page.tsx'), nextPage());
573
610
  }
574
611
 
575
612
  function frameworkLabel(framework) {
@@ -583,6 +620,17 @@ function frameworkLabel(framework) {
583
620
  }
584
621
  }
585
622
 
623
+ function frameworkDescription(framework) {
624
+ switch (framework) {
625
+ case 'vite':
626
+ return 'Vite';
627
+ case 'next':
628
+ return 'Next.js App Router SPA';
629
+ default:
630
+ fail(`Unsupported framework: ${framework}`);
631
+ }
632
+ }
633
+
586
634
  function frameworkTitle(framework) {
587
635
  switch (framework) {
588
636
  case 'vite':
@@ -624,7 +672,10 @@ function nextEnvTypes() {
624
672
 
625
673
  function nextConfig() {
626
674
  return `/** @type {import("next").NextConfig} */
627
- const nextConfig = {};
675
+ const nextConfig = {
676
+ output: 'export',
677
+ distDir: './dist',
678
+ };
628
679
 
629
680
  export default nextConfig;
630
681
  `;
@@ -638,7 +689,7 @@ export default [
638
689
  ...completeConfigBase,
639
690
 
640
691
  {
641
- ignores: ['.next/**', 'node_modules/**'],
692
+ ignores: ['.next/**', 'dist/**', 'node_modules/**'],
642
693
  },
643
694
 
644
695
  {
@@ -667,6 +718,9 @@ export default [
667
718
  {
668
719
  files: ['src/app/**/*.tsx'],
669
720
  rules: {
721
+ 'complete/no-mutable-return': 'off',
722
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
723
+ 'n/file-extension-in-import': 'off',
670
724
  'import-x/no-default-export': 'off',
671
725
  },
672
726
  },
@@ -739,13 +793,37 @@ export default function RootLayout({ children }: RootLayoutProps): JSX.Element {
739
793
  `;
740
794
  }
741
795
 
796
+ function nextClientPage() {
797
+ return `'use client';
798
+
799
+ import type { JSX } from 'react';
800
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
801
+
802
+ import { App } from '@/components/App';
803
+
804
+ const queryClient = new QueryClient();
805
+
806
+ export function ClientOnly(): JSX.Element {
807
+ return (
808
+ <QueryClientProvider client={queryClient}>
809
+ <App />
810
+ </QueryClientProvider>
811
+ );
812
+ }
813
+ `;
814
+ }
815
+
742
816
  function nextPage() {
743
817
  return `import type { JSX } from 'react';
744
818
 
745
- import { App } from '@/components/App';
819
+ import { ClientOnly } from './client';
820
+
821
+ export function generateStaticParams() {
822
+ return [{ slug: [''] }];
823
+ }
746
824
 
747
825
  export default function HomePage(): JSX.Element {
748
- return <App />;
826
+ return <ClientOnly />;
749
827
  }
750
828
  `;
751
829
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-hsi-app",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "Create a new app from the frontend template.",
6
6
  "dependencies": {