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.
- package/dist/cli/commands/dev.js +21 -5
- package/dist/utils/loadConfig.js +31 -33
- package/package.json +22 -14
- package/templates/react/index.html +1 -1
- package/templates/react-ssr/index.html +1 -1
- package/templates/react-ssr-ts/index.html +1 -1
- package/templates/react-tailwind/index.html +1 -1
- package/templates/react-tailwind-ts/index.html +1 -1
- package/templates/react-ts/index.html +1 -1
package/dist/cli/commands/dev.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
|
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️⃣
|
|
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️⃣
|
|
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";
|
package/dist/utils/loadConfig.js
CHANGED
|
@@ -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
|
|
46
|
-
*
|
|
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
|
-
//
|
|
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.
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
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
|
|
75
|
+
console.log(chalk_1.default.gray('ℹ️ No react-client.config found, using defaults.'));
|
|
80
76
|
return {};
|
|
81
77
|
}
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
//
|
|
85
|
-
if (
|
|
86
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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(`❌
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
22
23
|
"require": "./dist/index.js",
|
|
23
|
-
"
|
|
24
|
+
"import": "./dist/index.js"
|
|
24
25
|
},
|
|
25
26
|
"./config": {
|
|
26
|
-
"
|
|
27
|
+
"types": "./dist/config.d.ts",
|
|
27
28
|
"require": "./dist/config.js",
|
|
28
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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='/
|
|
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='/
|
|
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='/
|
|
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='/
|
|
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='/
|
|
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='/
|
|
1
|
+
<!doctype html><html><head><meta charset='utf-8'></head><body><div id='root'></div><script type='module' src='/main.js'></script></body></html>
|