react-client 1.0.8 → 1.0.9

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.
@@ -32,6 +32,7 @@ async function dev() {
32
32
  process.exit(1);
33
33
  }
34
34
  await fs_extra_1.default.ensureDir(outDir);
35
+ // 🧠 Detect open port
35
36
  const availablePort = await (0, detect_port_1.default)(defaultPort);
36
37
  const port = availablePort;
37
38
  if (availablePort !== defaultPort) {
@@ -83,13 +84,25 @@ async function dev() {
83
84
  await ctx.watch();
84
85
  // 🌐 connect server
85
86
  const app = (0, connect_1.default)();
86
- // 1️⃣ Serve react-refresh runtime
87
+ // 🛡 Security headers
88
+ app.use((_req, res, next) => {
89
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
90
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
91
+ next();
92
+ });
93
+ // 1️⃣ Serve react-refresh runtime with browser-safe shim
87
94
  app.use('/@react-refresh', async (_req, res) => {
88
95
  const runtime = await fs_extra_1.default.readFile(reactRefreshRuntime, 'utf8');
96
+ const shim = `
97
+ // React Refresh browser shims
98
+ window.process = window.process || { env: { NODE_ENV: 'development' } };
99
+ window.module = { exports: {} };
100
+ window.global = window; // ensure global scope for refresh
101
+ `;
89
102
  res.setHeader('Content-Type', 'application/javascript');
90
- res.end(runtime);
103
+ res.end(shim + '\n' + runtime);
91
104
  });
92
- // 2️⃣ Serve PrismJS for highlighting
105
+ // 2️⃣ Serve PrismJS (for code frame overlay)
93
106
  app.use('/@prismjs', async (_req, res) => {
94
107
  const prismPath = require.resolve('prismjs/prism.js');
95
108
  const css = await fs_extra_1.default.readFile(require.resolve('prismjs/themes/prism-tomorrow.css'), 'utf8');
@@ -104,7 +117,7 @@ async function dev() {
104
117
  })();
105
118
  `);
106
119
  });
107
- // 3️⃣ Serve source map resolver
120
+ // 3️⃣ Source map resolver (for overlay stack trace)
108
121
  app.use('/@source-map', async (req, res) => {
109
122
  const url = new URL(req.url ?? '', `http://localhost:${port}`);
110
123
  const file = url.searchParams.get('file');
@@ -152,7 +165,7 @@ async function dev() {
152
165
  res.end(JSON.stringify({ error: msg }));
153
166
  }
154
167
  });
155
- // 4️⃣ Inject overlay + HMR
168
+ // 4️⃣ Serve HTML and inject overlay + HMR
156
169
  app.use(async (req, res, next) => {
157
170
  if (req.url === '/' || req.url === '/index.html') {
158
171
  if (!fs_extra_1.default.existsSync(indexHtml)) {
@@ -161,7 +174,10 @@ async function dev() {
161
174
  return;
162
175
  }
163
176
  let html = await fs_extra_1.default.readFile(indexHtml, 'utf8');
177
+ // Ensure main entry reference
178
+ html = html.replace(/<script[^>]*src="\/bundle\.js"[^>]*><\/script>/, '');
164
179
  html = html.replace('</body>', `
180
+ <script type="module" src="/main.js"></script>
165
181
  <script type="module">
166
182
  import "/@react-refresh";
167
183
  import "/@prismjs";
@@ -41,71 +41,69 @@ const path_1 = __importDefault(require("path"));
41
41
  const fs_extra_1 = __importDefault(require("fs-extra"));
42
42
  const url_1 = require("url");
43
43
  const chalk_1 = __importDefault(require("chalk"));
44
+ const esbuild_1 = require("esbuild");
44
45
  /**
45
- * Dynamically load react-client.config.(js|mjs|ts)
46
- * from the user project, not the CLI’s own folder.
46
+ * Dynamically loads react-client.config.(ts|js|mjs)
47
+ * Compiles .ts and .js configs to .mjs temporarily for import.
47
48
  */
48
49
  async function loadReactClientConfig(cwd) {
49
50
  let projectRoot = cwd;
50
51
  try {
51
- // 🧭 Detect if running inside react-client source repo
52
+ // Detect if running inside react-client repo for local testing
52
53
  const pkgPath = path_1.default.join(cwd, 'package.json');
53
- if (fs_extra_1.default.existsSync(pkgPath)) {
54
- const pkgJson = JSON.parse(await fs_extra_1.default.readFile(pkgPath, 'utf8'));
55
- if (pkgJson.name === 'react-client') {
56
- // Running CLI locally auto-switch into example project (if found)
57
- const myappPath = path_1.default.join(cwd, 'myapp');
58
- if (fs_extra_1.default.existsSync(myappPath)) {
59
- console.log(chalk_1.default.gray('🧩 Detected local CLI environment, using ./myapp as root.'));
60
- projectRoot = myappPath;
61
- }
54
+ if (await fs_extra_1.default.pathExists(pkgPath)) {
55
+ const pkg = JSON.parse(await fs_extra_1.default.readFile(pkgPath, 'utf8'));
56
+ if (pkg.name === 'react-client' && (await fs_extra_1.default.pathExists(path_1.default.join(cwd, 'myapp')))) {
57
+ console.log(chalk_1.default.gray('🧩 Detected local CLI environment, using ./myapp as root.'));
58
+ projectRoot = path_1.default.join(cwd, 'myapp');
62
59
  }
63
60
  }
64
- // 🔍 Possible config file names (prefer .js for Node compatibility)
65
61
  const filenames = [
66
- 'react-client.config.js',
67
- 'react-client.config.mjs',
68
62
  'react-client.config.ts',
63
+ 'react-client.config.mjs',
64
+ 'react-client.config.js',
69
65
  ];
70
66
  let configFile = null;
71
67
  for (const name of filenames) {
72
68
  const file = path_1.default.join(projectRoot, name);
73
- if (fs_extra_1.default.existsSync(file)) {
69
+ if (await fs_extra_1.default.pathExists(file)) {
74
70
  configFile = file;
75
71
  break;
76
72
  }
77
73
  }
78
74
  if (!configFile) {
79
- console.log(chalk_1.default.gray('ℹ️ No react-client.config.js found, using defaults.'));
75
+ console.log(chalk_1.default.gray('ℹ️ No react-client.config found, using defaults.'));
80
76
  return {};
81
77
  }
82
- // 🧩 Import dynamically using file://
83
- const fileUrl = (0, url_1.pathToFileURL)(configFile).href;
84
- // If TypeScript file, try to compile it temporarily using esbuild
85
- if (configFile.endsWith('.ts')) {
86
- const esbuild = await Promise.resolve().then(() => __importStar(require('esbuild')));
87
- const outFile = path_1.default.join(projectRoot, '.react-client.temp.config.js');
88
- await esbuild.build({
78
+ const ext = path_1.default.extname(configFile);
79
+ const tempFile = path_1.default.join(projectRoot, `.react-client.temp-${Date.now()}.mjs`);
80
+ // 🧠 Always compile .ts or .js .mjs for safe ESM import
81
+ if (ext === '.ts' || ext === '.js') {
82
+ await (0, esbuild_1.build)({
89
83
  entryPoints: [configFile],
90
- outfile: outFile,
91
- format: 'esm',
84
+ outfile: tempFile,
92
85
  platform: 'node',
86
+ format: 'esm',
87
+ target: 'node18',
93
88
  bundle: true,
94
89
  write: true,
90
+ logLevel: 'silent',
95
91
  });
96
- const mod = await Promise.resolve(`${(0, url_1.pathToFileURL)(outFile).href}`).then(s => __importStar(require(s)));
97
- await fs_extra_1.default.remove(outFile);
98
- console.log(chalk_1.default.green(`🧩 Loaded config from ${path_1.default.basename(configFile)}`));
99
- return mod.default || mod;
100
92
  }
101
- // Normal .js or .mjs import
93
+ else {
94
+ await fs_extra_1.default.copyFile(configFile, tempFile);
95
+ }
96
+ // Import via file:// URL
97
+ const fileUrl = (0, url_1.pathToFileURL)(tempFile).href;
102
98
  const mod = await Promise.resolve(`${fileUrl}`).then(s => __importStar(require(s)));
99
+ await fs_extra_1.default.remove(tempFile);
100
+ const config = mod.default || mod;
103
101
  console.log(chalk_1.default.green(`🧩 Loaded config from ${path_1.default.basename(configFile)}`));
104
- return mod.default || mod;
102
+ return config;
105
103
  }
106
104
  catch (err) {
107
105
  const msg = err instanceof Error ? err.message : String(err);
108
- console.error(chalk_1.default.red(`❌ Failed to load react-client.config: ${msg}`));
106
+ console.error(chalk_1.default.red(`❌ Could not load config (${path_1.default.join(cwd, 'react-client.config.js')}): ${msg}`));
109
107
  return {};
110
108
  }
111
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
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.",
5
5
  "license": "MIT",
6
6
  "author": "Venkatesh Sundaram",
@@ -11,21 +11,22 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/venkateshsundaram/react-client/issues"
13
13
  },
14
- "main": "dist/index.js",
15
- "types": "dist/index.d.ts",
14
+ "homepage": "https://github.com/venkateshsundaram/react-client#readme",
16
15
  "bin": {
17
16
  "react-client": "dist/cli/index.js"
18
17
  },
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
19
20
  "exports": {
20
21
  ".": {
21
- "import": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
22
23
  "require": "./dist/index.js",
23
- "types": "./dist/index.d.ts"
24
+ "import": "./dist/index.js"
24
25
  },
25
26
  "./config": {
26
- "import": "./dist/config.js",
27
+ "types": "./dist/config.d.ts",
27
28
  "require": "./dist/config.js",
28
- "types": "./dist/config.d.ts"
29
+ "import": "./dist/config.js"
29
30
  }
30
31
  },
31
32
  "typesVersions": {
@@ -54,25 +55,28 @@
54
55
  "esbuild",
55
56
  "tooling",
56
57
  "vite-alternative",
57
- "fast-refresh"
58
+ "fast-refresh",
59
+ "react-refresh",
60
+ "tailwind",
61
+ "developer-experience"
58
62
  ],
59
63
  "scripts": {
60
- "clean": "rm -rf dist",
64
+ "clean": "rm -rf dist .turbo .react-client.temp.config*",
61
65
  "compile": "tsc -p tsconfig.build.json",
62
66
  "build": "npm run clean && npm run compile",
63
67
  "build:cli": "node dist/cli/index.js build",
64
68
  "build:ssr": "node dist/cli/index.js build:ssr",
65
69
  "dev": "node dist/cli/index.js dev",
66
70
  "prepare": "npm run compile",
67
- "precommit": "lint-staged",
68
- "lint": "eslint . --ext .ts",
71
+ "lint": "eslint . --ext .ts --max-warnings 0",
69
72
  "format": "prettier --write \"src/**/*.ts\"",
70
73
  "format:check": "prettier --check \"src/**/*.ts\"",
71
74
  "release": "standard-version",
72
75
  "postrelease": "git push --follow-tags",
73
- "prepublishOnly": "npm run compile",
76
+ "prepublishOnly": "npm run lint && npm run format:check && npm run build && npm test",
74
77
  "test": "jest",
75
- "typecheck": "tsc --noEmit"
78
+ "typecheck": "tsc --noEmit",
79
+ "verify": "bash scripts/local-verify.sh"
76
80
  },
77
81
  "lint-staged": {
78
82
  "*.ts": [
@@ -82,7 +86,8 @@
82
86
  },
83
87
  "husky": {
84
88
  "hooks": {
85
- "pre-commit": "lint-staged"
89
+ "pre-commit": "lint-staged",
90
+ "pre-push": "npm run lint && npm run typecheck"
86
91
  }
87
92
  },
88
93
  "commitlint": {
@@ -130,5 +135,8 @@
130
135
  "standard-version": "^9.5.0",
131
136
  "ts-jest": "^29.0.0",
132
137
  "typescript": "^5.3.0"
138
+ },
139
+ "engines": {
140
+ "node": ">=18.0.0"
133
141
  }
134
142
  }
@@ -1 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
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 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
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 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8' /></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
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 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
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 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
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 +1 @@
1
- <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/client/bundle.js'></script></body></html>
1
+ <!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>