react-client 1.0.9 β†’ 1.0.11

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.
Files changed (38) hide show
  1. package/README.md +0 -22
  2. package/dist/cli/commands/dev.js +79 -71
  3. package/dist/cli/index.js +1 -27
  4. package/package.json +2 -5
  5. package/templates/react/index.html +13 -1
  6. package/templates/react/public/favicon.ico +0 -0
  7. package/templates/react/src/App.jsx +1 -1
  8. package/templates/react/src/main.jsx +6 -1
  9. package/templates/react-tailwind/index.html +13 -1
  10. package/templates/react-tailwind/public/favicon.ico +0 -0
  11. package/templates/react-tailwind/src/App.jsx +3 -1
  12. package/templates/react-tailwind/src/index.css +1 -0
  13. package/templates/react-tailwind/src/main.jsx +9 -1
  14. package/templates/react-tailwind-ts/index.html +13 -1
  15. package/templates/react-tailwind-ts/public/favicon.ico +0 -0
  16. package/templates/react-tailwind-ts/src/App.tsx +3 -1
  17. package/templates/react-tailwind-ts/src/main.tsx +1 -0
  18. package/templates/react-ts/index.html +13 -1
  19. package/templates/react-ts/public/favicon.ico +0 -0
  20. package/templates/react-ts/src/main.tsx +6 -1
  21. package/templates/{react-ssr-ts β†’ react-ts}/tsconfig.json +1 -2
  22. package/dist/cli/commands/build.ssr.js +0 -46
  23. package/dist/cli/commands/generate.js +0 -42
  24. package/templates/react-ssr/index.html +0 -1
  25. package/templates/react-ssr/package.json +0 -14
  26. package/templates/react-ssr/src/entry-client.jsx +0 -1
  27. package/templates/react-ssr/src/entry-server.jsx +0 -1
  28. package/templates/react-ssr/src/pages/index.jsx +0 -1
  29. package/templates/react-ssr-ts/index.html +0 -1
  30. package/templates/react-ssr-ts/package.json +0 -21
  31. package/templates/react-ssr-ts/src/entry-client.js +0 -5
  32. package/templates/react-ssr-ts/src/entry-client.tsx +0 -6
  33. package/templates/react-ssr-ts/src/entry-server.js +0 -4
  34. package/templates/react-ssr-ts/src/entry-server.tsx +0 -5
  35. package/templates/react-ssr-ts/src/pages/index.js +0 -2
  36. package/templates/react-ssr-ts/src/pages/index.tsx +0 -3
  37. package/templates/react-tailwind-ts/src/App.js +0 -2
  38. package/templates/react-tailwind-ts/src/main.js +0 -7
package/README.md CHANGED
@@ -76,8 +76,6 @@ export default defineConfig({
76
76
  |-----------|-------------|
77
77
  | `react` | JavaScript SPA |
78
78
  | `react-ts` | TypeScript SPA |
79
- | `react-ssr` | JavaScript SSR |
80
- | `react-ssr-ts` | TypeScript SSR |
81
79
  | `react-tailwind` | JS + Tailwind |
82
80
  | `react-tailwind-ts` | TS + Tailwind |
83
81
 
@@ -94,8 +92,6 @@ Each template is pre-configured for esbuild, HMR, and fast bootstrapping.
94
92
  - πŸ’¬ **Auto Port Detection** β€” Prompts when default port 5173 is occupied
95
93
  - 🧠 **Smart Config Loader** β€” Detects project root, compiles `.ts` configs dynamically
96
94
  - 🎨 **PrismJS Highlighting** β€” For pretty overlay code frames
97
- - 🧱 **SSR Runtime Support** β€” For server-side templates (`react-ssr*`)
98
- - 🧩 **Generators** β€” Create components, routes, and tests instantly
99
95
  - πŸ”Œ **Plugin Hook System** β€” Extendable with `configResolved`, `transform`, `buildEnd`
100
96
 
101
97
  ---
@@ -200,21 +196,3 @@ Found an issue or have a feature request?
200
196
  ## πŸͺͺ License
201
197
 
202
198
  **MIT Licensed** Β© [Venkatesh Sundaram](https://github.com/venkateshsundaram)
203
-
204
- ---
205
-
206
- ## πŸ—ΊοΈ Architecture Overview (Bonus)
207
-
208
- ```text
209
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
210
- β”‚ react-client (CLI) β”‚
211
- β”‚ β”œβ”€β”€ esbuild (watch/bundle)
212
- β”‚ β”œβ”€β”€ connect (dev server)
213
- β”‚ β”œβ”€β”€ websocket (HMR)
214
- β”‚ β”œβ”€β”€ prismjs overlay
215
- β”‚ └── chokidar (file watch)
216
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
217
- β”‚
218
- β–Ό
219
- Browser ↔ Live HMR + Overlay
220
- ```
@@ -13,7 +13,6 @@ const detect_port_1 = __importDefault(require("detect-port"));
13
13
  const prompts_1 = __importDefault(require("prompts"));
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const fs_extra_1 = __importDefault(require("fs-extra"));
16
- const trace_mapping_1 = require("@jridgewell/trace-mapping");
17
16
  const loadConfig_1 = require("../../utils/loadConfig");
18
17
  const open_1 = __importDefault(require("open"));
19
18
  const child_process_1 = require("child_process");
@@ -25,12 +24,14 @@ async function dev() {
25
24
  const appRoot = path_1.default.resolve(root, userConfig.root || '.');
26
25
  const defaultPort = userConfig.server?.port || 5173;
27
26
  const outDir = path_1.default.join(appRoot, userConfig.build?.outDir || '.react-client/dev');
28
- const entry = path_1.default.join(appRoot, 'src', 'main.tsx');
29
- const indexHtml = path_1.default.join(appRoot, 'index.html');
30
- if (!fs_extra_1.default.existsSync(entry)) {
31
- console.error(chalk_1.default.red('❌ Entry not found: src/main.tsx'));
27
+ // βœ… Dynamically detect entry (main.tsx or main.jsx)
28
+ const possibleEntries = ['src/main.tsx', 'src/main.jsx'];
29
+ const entry = possibleEntries.map((p) => path_1.default.join(appRoot, p)).find((p) => fs_extra_1.default.existsSync(p));
30
+ if (!entry) {
31
+ console.error(chalk_1.default.red('❌ No entry found: src/main.tsx or src/main.jsx'));
32
32
  process.exit(1);
33
33
  }
34
+ const indexHtml = path_1.default.join(appRoot, 'index.html');
34
35
  await fs_extra_1.default.ensureDir(outDir);
35
36
  // 🧠 Detect open port
36
37
  const availablePort = await (0, detect_port_1.default)(defaultPort);
@@ -80,8 +81,13 @@ async function dev() {
80
81
  outdir: outDir,
81
82
  define: { 'process.env.NODE_ENV': '"development"' },
82
83
  loader: { '.ts': 'ts', '.tsx': 'tsx', '.js': 'jsx', '.jsx': 'jsx' },
84
+ entryNames: '[name]',
85
+ assetNames: 'assets/[name]',
83
86
  });
84
87
  await ctx.watch();
88
+ console.log(chalk_1.default.gray('πŸ“¦ Watching and building dev bundle...'));
89
+ console.log(chalk_1.default.gray(' Output dir:'), chalk_1.default.blue(outDir));
90
+ console.log(chalk_1.default.gray(' Entry file:'), chalk_1.default.yellow(entry));
85
91
  // 🌐 connect server
86
92
  const app = (0, connect_1.default)();
87
93
  // πŸ›‘ Security headers
@@ -90,82 +96,79 @@ async function dev() {
90
96
  res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
91
97
  next();
92
98
  });
93
- // 1️⃣ Serve react-refresh runtime with browser-safe shim
99
+ // 1️⃣ Serve react-refresh runtime with safe browser shim
94
100
  app.use('/@react-refresh', async (_req, res) => {
95
101
  const runtime = await fs_extra_1.default.readFile(reactRefreshRuntime, 'utf8');
96
102
  const shim = `
97
103
  // React Refresh browser shims
98
104
  window.process = window.process || { env: { NODE_ENV: 'development' } };
99
105
  window.module = { exports: {} };
100
- window.global = window; // ensure global scope for refresh
106
+ window.global = window;
107
+ window.require = () => window.module.exports;
101
108
  `;
102
109
  res.setHeader('Content-Type', 'application/javascript');
103
110
  res.end(shim + '\n' + runtime);
104
111
  });
105
- // 2️⃣ Serve PrismJS (for code frame overlay)
106
- app.use('/@prismjs', async (_req, res) => {
107
- const prismPath = require.resolve('prismjs/prism.js');
108
- const css = await fs_extra_1.default.readFile(require.resolve('prismjs/themes/prism-tomorrow.css'), 'utf8');
109
- const js = await fs_extra_1.default.readFile(prismPath, 'utf8');
110
- res.setHeader('Content-Type', 'application/javascript');
111
- res.end(`
112
- (function(){
113
- const style = document.createElement('style');
114
- style.textContent = \`${css}\`;
115
- document.head.appendChild(style);
116
- ${js}
117
- })();
118
- `);
119
- });
120
- // 3️⃣ Source map resolver (for overlay stack trace)
121
- app.use('/@source-map', async (req, res) => {
122
- const url = new URL(req.url ?? '', `http://localhost:${port}`);
123
- const file = url.searchParams.get('file');
124
- const line = Number(url.searchParams.get('line'));
125
- const column = Number(url.searchParams.get('column'));
126
- if (!file) {
127
- res.writeHead(400);
128
- res.end('Missing ?file parameter');
129
- return;
112
+ // 2️⃣ Minimal bare import resolver (/@modules/)
113
+ app.use('/@modules/', async (req, res, next) => {
114
+ const id = req.url?.replace(/^\/@modules\//, '');
115
+ if (!id)
116
+ return next();
117
+ try {
118
+ const entry = require.resolve(id, { paths: [appRoot] });
119
+ const out = await esbuild_1.default.build({
120
+ entryPoints: [entry],
121
+ bundle: true,
122
+ write: false,
123
+ platform: 'browser',
124
+ format: 'esm',
125
+ target: 'es2020',
126
+ });
127
+ res.setHeader('Content-Type', 'application/javascript');
128
+ res.end(out.outputFiles[0].text);
130
129
  }
131
- const mapPath = path_1.default.join(outDir, file + '.map');
132
- if (!fs_extra_1.default.existsSync(mapPath)) {
133
- res.writeHead(404);
134
- res.end('Map not found');
135
- return;
130
+ catch (err) {
131
+ console.error('Failed to resolve module', id, err);
132
+ res.writeHead(500);
133
+ res.end(`// Could not resolve module ${id}`);
136
134
  }
135
+ });
136
+ // 3️⃣ Serve /src/* files (on-the-fly transform)
137
+ app.use(async (req, res, next) => {
138
+ if (!req.url || !req.url.startsWith('/src/'))
139
+ return next();
137
140
  try {
138
- const mapJson = JSON.parse(await fs_extra_1.default.readFile(mapPath, 'utf8'));
139
- const traceMap = new trace_mapping_1.TraceMap(mapJson);
140
- const pos = (0, trace_mapping_1.originalPositionFor)(traceMap, { line, column });
141
- if (!pos.source) {
142
- res.writeHead(404);
143
- res.end('Source not found');
144
- return;
145
- }
146
- const absSource = path_1.default.resolve(outDir, '../', pos.source);
147
- let snippet = '';
148
- if (await fs_extra_1.default.pathExists(absSource)) {
149
- const lines = (await fs_extra_1.default.readFile(absSource, 'utf8')).split('\n');
150
- const start = Math.max((pos.line || 1) - 3, 0);
151
- const end = Math.min(lines.length, (pos.line || 1) + 2);
152
- snippet = lines
153
- .slice(start, end)
154
- .map((l, i) => `<span class="line-number">${start + i + 1}</span> ${l
155
- .replace(/</g, '&lt;')
156
- .replace(/>/g, '&gt;')}`)
157
- .join('\\n');
158
- }
159
- res.setHeader('Content-Type', 'application/json');
160
- res.end(JSON.stringify({ ...pos, snippet }));
141
+ const filePath = path_1.default.join(appRoot, decodeURIComponent(req.url.split('?')[0]));
142
+ if (!(await fs_extra_1.default.pathExists(filePath)))
143
+ return next();
144
+ const code = await fs_extra_1.default.readFile(filePath, 'utf8');
145
+ const ext = path_1.default.extname(filePath).toLowerCase();
146
+ let loader = 'js';
147
+ if (ext === '.ts')
148
+ loader = 'ts';
149
+ else if (ext === '.tsx')
150
+ loader = 'tsx';
151
+ else if (ext === '.jsx')
152
+ loader = 'jsx';
153
+ const transformed = await esbuild_1.default.transform(code, {
154
+ loader,
155
+ sourcemap: 'inline',
156
+ sourcefile: req.url,
157
+ target: 'es2020',
158
+ jsxFactory: 'React.createElement',
159
+ jsxFragment: 'React.Fragment',
160
+ });
161
+ res.setHeader('Content-Type', 'application/javascript');
162
+ res.end(transformed.code);
161
163
  }
162
164
  catch (err) {
163
165
  const msg = err instanceof Error ? err.message : String(err);
166
+ console.error('Error serving src file:', msg);
164
167
  res.writeHead(500);
165
- res.end(JSON.stringify({ error: msg }));
168
+ res.end(`// Error: ${msg}`);
166
169
  }
167
170
  });
168
- // 4️⃣ Serve HTML and inject overlay + HMR
171
+ // 4️⃣ Serve index.html with injected refresh + HMR
169
172
  app.use(async (req, res, next) => {
170
173
  if (req.url === '/' || req.url === '/index.html') {
171
174
  if (!fs_extra_1.default.existsSync(indexHtml)) {
@@ -174,13 +177,9 @@ async function dev() {
174
177
  return;
175
178
  }
176
179
  let html = await fs_extra_1.default.readFile(indexHtml, 'utf8');
177
- // Ensure main entry reference
178
- html = html.replace(/<script[^>]*src="\/bundle\.js"[^>]*><\/script>/, '');
179
180
  html = html.replace('</body>', `
180
- <script type="module" src="/main.js"></script>
181
181
  <script type="module">
182
182
  import "/@react-refresh";
183
- import "/@prismjs";
184
183
  const ws = new WebSocket("ws://" + location.host);
185
184
  ws.onmessage = async (e) => {
186
185
  const msg = JSON.parse(e.data);
@@ -204,9 +203,18 @@ async function dev() {
204
203
  res.setHeader('Content-Type', 'text/html');
205
204
  res.end(html);
206
205
  }
207
- else
208
- next();
206
+ else {
207
+ const filePath = path_1.default.join(outDir, req.url || '');
208
+ if (await fs_extra_1.default.pathExists(filePath)) {
209
+ const content = await fs_extra_1.default.readFile(filePath);
210
+ res.setHeader('Content-Type', 'application/javascript');
211
+ res.end(content);
212
+ }
213
+ else
214
+ next();
215
+ }
209
216
  });
217
+ // πŸ” HMR via WebSocket
210
218
  const server = http_1.default.createServer(app);
211
219
  const wss = new ws_1.WebSocketServer({ server });
212
220
  const broadcast = (data) => {
@@ -230,9 +238,9 @@ async function dev() {
230
238
  });
231
239
  server.listen(port, async () => {
232
240
  const url = `http://localhost:${port}`;
233
- console.log(chalk_1.default.green(`\n⚑ Dev Server running at ${url}`));
234
- if (port !== defaultPort)
235
- console.log(chalk_1.default.yellow(`⚠️ Using alternate port (default ${defaultPort} was occupied).`));
241
+ console.log(chalk_1.default.cyan.bold(`\nπŸš€ React Client Dev Server`));
242
+ console.log(chalk_1.default.gray('───────────────────────────────'));
243
+ console.log(chalk_1.default.green(`⚑ Running at: ${url}`));
236
244
  await (0, open_1.default)(url, { newInstance: true });
237
245
  });
238
246
  process.on('SIGINT', async () => {
package/dist/cli/index.js CHANGED
@@ -9,10 +9,8 @@ const path_1 = __importDefault(require("path"));
9
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const init_1 = __importDefault(require("./commands/init"));
12
- const generate_1 = __importDefault(require("./commands/generate"));
13
12
  const dev_1 = __importDefault(require("./commands/dev"));
14
13
  const build_1 = __importDefault(require("./commands/build"));
15
- const build_ssr_1 = __importDefault(require("./commands/build.ssr"));
16
14
  const preview_1 = __importDefault(require("./commands/preview"));
17
15
  // Load package.json version dynamically
18
16
  const pkgPath = path_1.default.resolve(__dirname, '../../package.json');
@@ -23,7 +21,7 @@ const pkg = fs_extra_1.default.existsSync(pkgPath)
23
21
  function showBanner(cmd) {
24
22
  const title = chalk_1.default.bold.cyan('⚑ React Client');
25
23
  const version = chalk_1.default.gray(`v${pkg.version}`);
26
- const tagline = chalk_1.default.dim('Fast esbuild-based React CLI with HMR, SSR & Overlay');
24
+ const tagline = chalk_1.default.dim('Fast esbuild-based React CLI with HMR & Overlay');
27
25
  const line = chalk_1.default.gray('──────────────────────────────────────────────────────────────');
28
26
  console.log(`\n${title} ${version}`);
29
27
  console.log(tagline);
@@ -32,18 +30,12 @@ function showBanner(cmd) {
32
30
  case 'init':
33
31
  console.log(chalk_1.default.cyan('πŸ“¦ Initializing new React project...\n'));
34
32
  break;
35
- case 'generate':
36
- console.log(chalk_1.default.white('✨ Generating boilerplate files...\n'));
37
- break;
38
33
  case 'dev':
39
34
  console.log(chalk_1.default.green('πŸš€ Starting development server...\n'));
40
35
  break;
41
36
  case 'build':
42
37
  console.log(chalk_1.default.yellow('πŸ—οΈ Building for production...\n'));
43
38
  break;
44
- case 'build:ssr':
45
- console.log(chalk_1.default.magenta('🧱 Building for server-side rendering (SSR)...\n'));
46
- break;
47
39
  case 'preview':
48
40
  console.log(chalk_1.default.blue('🌐 Starting production preview server...\n'));
49
41
  break;
@@ -69,17 +61,6 @@ program
69
61
  showBanner('init');
70
62
  (0, init_1.default)(name, opts);
71
63
  });
72
- program
73
- .command('generate <kind> <name>')
74
- .alias('g')
75
- .option('-p, --path <path>', 'output path')
76
- .option('--no-ts', 'generate JS instead of TS')
77
- .option('-f, --force', 'overwrite if exists')
78
- .description('generate components, pages, or stores')
79
- .action((kind, name, opts) => {
80
- showBanner('generate');
81
- (0, generate_1.default)(kind, name, opts);
82
- });
83
64
  program
84
65
  .command('dev')
85
66
  .description('start dev server (with React Fast Refresh)')
@@ -94,13 +75,6 @@ program
94
75
  showBanner('build');
95
76
  (0, build_1.default)();
96
77
  });
97
- program
98
- .command('build:ssr')
99
- .description('build for server-side rendering (SSR)')
100
- .action(() => {
101
- showBanner('build:ssr');
102
- (0, build_ssr_1.default)();
103
- });
104
78
  program
105
79
  .command('preview')
106
80
  .description('preview production build')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.9",
4
- "description": "react-client is a lightweight CLI and runtime for building React apps with fast iteration. Esbuild-based, Node-native, and modular like Vite/Next.js with SSR, HMR, Tailwind, CSS Modules, and generators.",
3
+ "version": "1.0.11",
4
+ "description": "react-client is a lightweight CLI and runtime for building React apps with fast iteration.",
5
5
  "license": "MIT",
6
6
  "author": "Venkatesh Sundaram",
7
7
  "repository": {
@@ -51,7 +51,6 @@
51
51
  "react-cli",
52
52
  "framework",
53
53
  "hmr",
54
- "ssr",
55
54
  "esbuild",
56
55
  "tooling",
57
56
  "vite-alternative",
@@ -65,7 +64,6 @@
65
64
  "compile": "tsc -p tsconfig.build.json",
66
65
  "build": "npm run clean && npm run compile",
67
66
  "build:cli": "node dist/cli/index.js build",
68
- "build:ssr": "node dist/cli/index.js build:ssr",
69
67
  "dev": "node dist/cli/index.js dev",
70
68
  "prepare": "npm run compile",
71
69
  "lint": "eslint . --ext .ts --max-warnings 0",
@@ -96,7 +94,6 @@
96
94
  ]
97
95
  },
98
96
  "dependencies": {
99
- "@jridgewell/trace-mapping": "^0.3.31",
100
97
  "chalk": "^4.1.2",
101
98
  "chokidar": "^4.0.3",
102
99
  "commander": "^14.0.2",
@@ -1 +1,13 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Client App</title>
7
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id='root'></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
@@ -1 +1 @@
1
- export default ()=> <div>Hello React</div>;
1
+ export default () => <div>Hello React</div>;
@@ -1 +1,6 @@
1
- import React from 'react';import { createRoot } from 'react-dom/client';import App from './App';const root = createRoot(document.getElementById('root'));root.render(<App/>);
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+
5
+ const root = createRoot(document.getElementById('root'));
6
+ root.render(<App />);
@@ -1 +1,13 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Client App</title>
7
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id='root'></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
@@ -1 +1,3 @@
1
- export default ()=> <div className='p-8'><h1 className='text-2xl'>Tailwind</h1></div>;
1
+ export default function App() {
2
+ return <div className='p-8'><h1 className='text-2xl'>Tailwind</h1><p>Welcome to react-tailwind template.</p></div>;
3
+ }
@@ -0,0 +1 @@
1
+ @tailwind base;@tailwind components;@tailwind utilities;
@@ -1 +1,9 @@
1
- import React from 'react';import { createRoot } from 'react-dom/client';import App from './App';import './index.css';const root = createRoot(document.getElementById('root'));root.render(<App/>);
1
+
2
+ import '/@react-refresh-shim';
3
+ import React from 'react';
4
+ import { createRoot } from 'react-dom/client';
5
+ import App from './App';
6
+ import './index.css';
7
+
8
+ const root = createRoot(document.getElementById('root'));
9
+ root.render(<App/>);
@@ -1 +1,13 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Client App</title>
7
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id='root'></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -1,2 +1,4 @@
1
1
 
2
- export default function App(){ return <div className='p-8'><h1 className='text-2xl font-bold'>Tailwind TS</h1><p>Welcome to react-tailwind-ts template.</p></div>; }
2
+ export default function App() {
3
+ return <div className='p-8'><h1 className='text-2xl font-bold'>Tailwind TS</h1><p>Welcome to react-tailwind-ts template.</p></div>;
4
+ }
@@ -4,5 +4,6 @@ import React from 'react';
4
4
  import { createRoot } from 'react-dom/client';
5
5
  import App from './App';
6
6
  import './index.css';
7
+
7
8
  const root = createRoot(document.getElementById('root')!);
8
9
  root.render(<App/>);
@@ -1 +1,13 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Client App</title>
7
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id='root'></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -1 +1,6 @@
1
- import React from 'react';import { createRoot } from 'react-dom/client';import App from './App';const root = createRoot(document.getElementById('root')!);root.render(<App/>);
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+
5
+ const root = createRoot(document.getElementById('root')!);
6
+ root.render(<App/>);
@@ -9,7 +9,6 @@
9
9
  "jsx": "react-jsx",
10
10
  "moduleResolution": "node",
11
11
  "strict": true,
12
- "esModuleInterop": true,
13
- "skipLibCheck": true
12
+ "esModuleInterop": true
14
13
  }
15
14
  }
@@ -1,46 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.default = buildSsr;
7
- const esbuild_1 = __importDefault(require("esbuild"));
8
- const path_1 = __importDefault(require("path"));
9
- const fs_extra_1 = __importDefault(require("fs-extra"));
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const loadConfig_1 = require("../../utils/loadConfig");
12
- async function buildSsr() {
13
- const root = process.cwd();
14
- const config = await (0, loadConfig_1.loadReactClientConfig)(root);
15
- const appRoot = path_1.default.resolve(root, config.root || '.');
16
- const outDir = path_1.default.join(appRoot, config.build?.outDir || '.react-client/ssr');
17
- console.log(chalk_1.default.cyan(`\n🧱 Building SSR bundle...`));
18
- console.log(chalk_1.default.gray(`Root: ${appRoot}`));
19
- console.log(chalk_1.default.gray(`Output: ${outDir}\n`));
20
- const entry = path_1.default.join(appRoot, 'src', 'server.tsx');
21
- if (!fs_extra_1.default.existsSync(entry)) {
22
- console.error(chalk_1.default.red('❌ SSR entry not found: src/server.tsx'));
23
- process.exit(1);
24
- }
25
- await fs_extra_1.default.ensureDir(outDir);
26
- try {
27
- await esbuild_1.default.build({
28
- entryPoints: [entry],
29
- bundle: true,
30
- platform: 'node',
31
- format: 'esm',
32
- target: 'node18',
33
- external: ['react', 'react-dom'],
34
- outdir: outDir,
35
- define: { 'process.env.NODE_ENV': '"production"' },
36
- loader: { '.ts': 'ts', '.tsx': 'tsx' },
37
- });
38
- console.log(chalk_1.default.green(`βœ… SSR build completed successfully!`));
39
- console.log(chalk_1.default.gray(`Output directory: ${outDir}`));
40
- }
41
- catch (err) {
42
- const msg = err instanceof Error ? err.message : String(err);
43
- console.error('❌ SSR build failed:', msg);
44
- process.exit(1);
45
- }
46
- }
@@ -1,42 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.default = generate;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = __importDefault(require("path"));
9
- const string_1 = require("../../utils/string");
10
- async function generate(kind, name, opts = {}) {
11
- const root = process.cwd();
12
- const useTS = opts.ts !== false && opts['no-ts'] !== true;
13
- if (kind === 'component') {
14
- const pascal = (0, string_1.pascalCase)(name);
15
- const dir = path_1.default.join(root, opts.path || 'src/components');
16
- await fs_extra_1.default.ensureDir(dir);
17
- const ext = useTS ? 'tsx' : 'jsx';
18
- const compPath = path_1.default.join(dir, `${pascal}.${ext}`);
19
- const css = path_1.default.join(dir, `${pascal}.module.css`);
20
- await fs_extra_1.default.writeFile(compPath, `import React from 'react';\nimport styles from './${pascal}.module.css';\nexport default function ${pascal}(){ return <div className={styles.root}>${pascal}</div>; }\n`);
21
- await fs_extra_1.default.writeFile(css, `.root{display:block}`);
22
- console.log('Created component', compPath);
23
- return;
24
- }
25
- if (kind === 'route') {
26
- const parts = name.replace(/^\//, '').split('/').filter(Boolean);
27
- const pages = path_1.default.join(root, 'src', 'pages', ...parts.slice(0, -1));
28
- await fs_extra_1.default.ensureDir(pages);
29
- const last = parts[parts.length - 1] || 'index';
30
- const file = path_1.default.join(pages, last + '.' + (useTS ? 'tsx' : 'jsx'));
31
- if (await fs_extra_1.default.pathExists(file)) {
32
- console.error('Route exists');
33
- return;
34
- }
35
- const compName = (0, string_1.pascalCase)(parts.join('-') || 'IndexPage');
36
- const content = `import React from 'react';\nexport default function ${compName}(){ return (<div style={{padding:20}}><h1>${compName}</h1></div>); }\n`;
37
- await fs_extra_1.default.writeFile(file, content, 'utf8');
38
- console.log('Created route', file);
39
- return;
40
- }
41
- console.log('Unknown generator', kind);
42
- }
@@ -1 +0,0 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
@@ -1,14 +0,0 @@
1
- {
2
- "name": "template-react-ssr",
3
- "version": "0.0.1",
4
- "private": true,
5
- "scripts": {
6
- "dev": "react-client dev",
7
- "build": "react-client build:ssr",
8
- "preview": "react-client preview"
9
- },
10
- "dependencies": {
11
- "react": "^18.2.0",
12
- "react-dom": "^18.2.0"
13
- }
14
- }
@@ -1 +0,0 @@
1
- import React from 'react';import { hydrateRoot } from 'react-dom/client';import Home from './pages/index';hydrateRoot(document.getElementById('root'), React.createElement(Home));
@@ -1 +0,0 @@
1
- import React from 'react';import { renderToString } from 'react-dom/server';import Home from './pages/index';exports.render = function(url){ const Page = Home; return Promise.resolve(renderToString(React.createElement(Page))); };
@@ -1 +0,0 @@
1
- import React from 'react';export default function Home(){ return <div>Home SSR</div>; }
@@ -1 +0,0 @@
1
- <!doctype html><html><head><meta charset='utf-8' /></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
@@ -1,21 +0,0 @@
1
- {
2
- "name": "my-react-ssr-ts-app",
3
- "version": "0.0.1",
4
- "private": true,
5
- "scripts": {
6
- "dev": "react-client dev",
7
- "build": "react-client build:ssr",
8
- "preview": "react-client preview"
9
- },
10
- "dependencies": {
11
- "react": "^18.2.0",
12
- "react-dom": "^18.2.0"
13
- },
14
- "devDependencies": {
15
- "@types/react": "^18.0.0",
16
- "@types/react-dom": "^18.0.0",
17
- "esbuild": "^0.25.12",
18
- "react-refresh": "^0.14.0",
19
- "typescript": "^5.0.0"
20
- }
21
- }
@@ -1,5 +0,0 @@
1
- import '/@react-refresh-shim';
2
- import React from 'react';
3
- import { hydrateRoot } from 'react-dom/client';
4
- import Home from './pages/index';
5
- hydrateRoot(document.getElementById('root'), React.createElement(Home));
@@ -1,6 +0,0 @@
1
-
2
- import '/@react-refresh-shim';
3
- import React from 'react';
4
- import { hydrateRoot } from 'react-dom/client';
5
- import Home from './pages/index';
6
- hydrateRoot(document.getElementById('root')!, React.createElement(Home));
@@ -1,4 +0,0 @@
1
- import React from 'react';
2
- import { renderToString } from 'react-dom/server';
3
- import Home from './pages/index';
4
- export async function render(url) { const Page = Home; return renderToString(React.createElement(Page)); }
@@ -1,5 +0,0 @@
1
-
2
- import React from 'react';
3
- import { renderToString } from 'react-dom/server';
4
- import Home from './pages/index';
5
- export async function render(url:string){ const Page = Home; return renderToString(React.createElement(Page)); }
@@ -1,2 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export default function Home() { return _jsxs("div", { style: { padding: 20 }, children: [_jsx("h1", { children: "Home (SSR TS)" }), _jsx("p", { children: "Server-side rendered page." })] }); }
@@ -1,3 +0,0 @@
1
-
2
- import React from 'react';
3
- export default function Home(){ return <div style={{padding:20}}><h1>Home (SSR TS)</h1><p>Server-side rendered page.</p></div>; }
@@ -1,2 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export default function App() { return _jsxs("div", { className: 'p-8', children: [_jsx("h1", { className: 'text-2xl font-bold', children: "Tailwind TS" }), _jsx("p", { children: "Welcome to react-tailwind-ts template." })] }); }
@@ -1,7 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import '/@react-refresh-shim';
3
- import { createRoot } from 'react-dom/client';
4
- import App from './App';
5
- import './index.css';
6
- const root = createRoot(document.getElementById('root'));
7
- root.render(_jsx(App, {}));