mnfst-run 1.0.13 → 1.0.14
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/package.json +1 -1
- package/serve.mjs +80 -3
package/package.json
CHANGED
package/serve.mjs
CHANGED
|
@@ -115,6 +115,60 @@ const LIVE_RELOAD_SCRIPT = `<script>
|
|
|
115
115
|
})();
|
|
116
116
|
\x3c/script>`;
|
|
117
117
|
|
|
118
|
+
// Read a dotenv-style file from the project root and return a plain object.
|
|
119
|
+
// Used to populate `window.env` in served HTML so manifest.json's `${VAR}`
|
|
120
|
+
// placeholders resolve at runtime — matching the documented developer-facing
|
|
121
|
+
// behaviour without requiring a build step. Returns {} when the file is
|
|
122
|
+
// absent or fails to parse.
|
|
123
|
+
//
|
|
124
|
+
// Supported subset (intentionally minimal, no expansion / multiline / etc.):
|
|
125
|
+
// - KEY=value (whitespace around `=` ok)
|
|
126
|
+
// - KEY="quoted" / KEY='…' (surrounding quotes stripped)
|
|
127
|
+
// - # comments and blank lines ignored
|
|
128
|
+
// - lines without `=` ignored
|
|
129
|
+
//
|
|
130
|
+
// Production note: env injection happens ONLY through this dev server.
|
|
131
|
+
// Static deploys (Netlify/Vercel/Cloudflare Pages/S3/etc.) serve manifest.json
|
|
132
|
+
// verbatim, so any `${VAR}` placeholder that needs a value in production must
|
|
133
|
+
// be hardcoded in manifest.json, baked in at prerender time, or substituted
|
|
134
|
+
// by the host. See the Appwrite setup doc for the full pattern.
|
|
135
|
+
function loadEnvFile(rootDir) {
|
|
136
|
+
const envPath = join(rootDir, '.env');
|
|
137
|
+
if (!existsSync(envPath)) return {};
|
|
138
|
+
const env = {};
|
|
139
|
+
try {
|
|
140
|
+
const text = readFileSync(envPath, 'utf8');
|
|
141
|
+
for (const line of text.split(/\r?\n/)) {
|
|
142
|
+
const trimmed = line.trim();
|
|
143
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
144
|
+
const eqIdx = trimmed.indexOf('=');
|
|
145
|
+
if (eqIdx === -1) continue;
|
|
146
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
147
|
+
if (!key) continue;
|
|
148
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
149
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
150
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
151
|
+
value = value.slice(1, -1);
|
|
152
|
+
}
|
|
153
|
+
env[key] = value;
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.warn('[mnfst-run] Failed to parse .env:', error.message);
|
|
157
|
+
}
|
|
158
|
+
return env;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Build a `<script>window.env = {…};</script>` tag from a parsed env map.
|
|
162
|
+
// Returns '' when there are no vars (so the injection is a no-op for projects
|
|
163
|
+
// without a .env). Escapes any `</script` substring inside string values so an
|
|
164
|
+
// env value can't break out of the script tag.
|
|
165
|
+
function buildEnvInjectScript(envVars) {
|
|
166
|
+
const keys = Object.keys(envVars);
|
|
167
|
+
if (keys.length === 0) return '';
|
|
168
|
+
const json = JSON.stringify(envVars).replace(/<\/script/gi, '<\\/script');
|
|
169
|
+
return `<script>window.env = ${json};</script>`;
|
|
170
|
+
}
|
|
171
|
+
|
|
118
172
|
// --- CLI args ---
|
|
119
173
|
const args = process.argv.slice(2);
|
|
120
174
|
let dir = '.';
|
|
@@ -253,6 +307,17 @@ if (listMode) {
|
|
|
253
307
|
|
|
254
308
|
const root = resolve(process.cwd(), dir);
|
|
255
309
|
|
|
310
|
+
// Load .env from the serving root (if present) and pre-build the inject
|
|
311
|
+
// script. Kept as a single string so serveFile doesn't re-stringify on every
|
|
312
|
+
// HTML response. Empty string when no .env exists — the injection step
|
|
313
|
+
// becomes a no-op for projects that don't use env vars.
|
|
314
|
+
const envVars = loadEnvFile(root);
|
|
315
|
+
const envInjectScript = buildEnvInjectScript(envVars);
|
|
316
|
+
const envCount = Object.keys(envVars).length;
|
|
317
|
+
if (envCount > 0) {
|
|
318
|
+
console.log(`Loaded ${envCount} env var(s) from .env into window.env`);
|
|
319
|
+
}
|
|
320
|
+
|
|
256
321
|
// Dedup: if a server is already serving this exact root, point the user at
|
|
257
322
|
// it (and open the browser, since that's what they were going to do anyway).
|
|
258
323
|
const existing = await findRunningServer(root);
|
|
@@ -377,9 +442,21 @@ function serveFile(res, filePath) {
|
|
|
377
442
|
// Only inject into full HTML documents — not component fragments
|
|
378
443
|
const isFullDoc = /<!doctype\s/i.test(html) || /<html[\s>]/i.test(html);
|
|
379
444
|
if (isFullDoc) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
445
|
+
// 1) Inject window.env into <head> (when .env present) so the
|
|
446
|
+
// framework's manifest.json env-var substitution can resolve
|
|
447
|
+
// `${VAR}` placeholders before any plugin reads the manifest.
|
|
448
|
+
// Must come BEFORE framework scripts execute — <head> insertion
|
|
449
|
+
// guarantees that ordering regardless of where script tags sit.
|
|
450
|
+
let injected = html;
|
|
451
|
+
if (envInjectScript) {
|
|
452
|
+
injected = injected.includes('</head>')
|
|
453
|
+
? injected.replace('</head>', envInjectScript + '</head>')
|
|
454
|
+
: envInjectScript + injected;
|
|
455
|
+
}
|
|
456
|
+
// 2) Inject the live-reload script before </body> (or at end).
|
|
457
|
+
injected = injected.includes('</body>')
|
|
458
|
+
? injected.replace('</body>', LIVE_RELOAD_SCRIPT + '</body>')
|
|
459
|
+
: injected + LIVE_RELOAD_SCRIPT;
|
|
383
460
|
body = Buffer.from(injected, 'utf8');
|
|
384
461
|
}
|
|
385
462
|
}
|