UNPKG

@varlock/nextjs-integration

Version:

drop-in replacement for @next/env that uses varlock to load .env files with validation and extra security features

240 lines (235 loc) 8.88 kB
'use strict'; var fs = require('fs'); var path = require('path'); var child_process = require('child_process'); var env = require('varlock/env'); var patchConsole = require('varlock/patch-console'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var fs__namespace = /*#__PURE__*/_interopNamespace(fs); var path__namespace = /*#__PURE__*/_interopNamespace(path); // src/next-env-compat.ts exports.initialEnv = void 0; var lastReloadAt; var varlockLoadedEnv; var combinedEnv; var parsedEnv; var loadedEnvFiles = []; var rootDir; function getVarlockSourcesAsLoadedEnvFiles() { const envFiles = varlockLoadedEnv.sources.filter((s) => s.enabled && s.label !== "process.env").map((s) => ({ path: s.label, contents: "", env: {} })); if (envFiles.length) { envFiles.push({ path: "\n \u2728 loaded by varlock \u2728", contents: "", env: {} }); } return envFiles; } var IS_WORKER = !!process.env.NEXT_PRIVATE_WORKER; function debug(...args) { if (!process.env.DEBUG_VARLOCK_NEXT_INTEGRATION) return; console.log( IS_WORKER ? "worker -- " : "server -- ", ...args ); } debug("\u2728 LOADED @next/env module!"); var extraWatcherEnabled = false; var NEXT_WATCHED_ENV_FILES = [".env", ".env.local", ".env.development", ".env.development.local"]; function enableExtraFileWatchers() { if (extraWatcherEnabled || IS_WORKER) return; extraWatcherEnabled = true; if (!rootDir) throw new Error("expected rootDir to be set"); const envSchemaPath = path__namespace.join(rootDir, ".env.schema"); let envFilePathToUpdate = null; for (const envFileName of NEXT_WATCHED_ENV_FILES) { const filePath = path__namespace.join(rootDir, envFileName); if (fs__namespace.existsSync(filePath)) { envFilePathToUpdate = filePath; break; } } let destroyFile = false; if (!envFilePathToUpdate) { envFilePathToUpdate ||= path__namespace.join(rootDir, ".env"); destroyFile = true; } debug("set up extra file watchers", envFilePathToUpdate, destroyFile); fs__namespace.watchFile(envSchemaPath, { interval: 500 }, (_curr, _prev) => { debug(".env.schema changed", envFilePathToUpdate, destroyFile); if (destroyFile) { fs__namespace.writeFileSync(envFilePathToUpdate, "# trigger reload", "utf-8"); setTimeout(() => { fs__namespace.unlinkSync(envFilePathToUpdate); }, 500); } else { const currentContents = fs__namespace.readFileSync(envFilePathToUpdate, "utf-8"); fs__namespace.writeFileSync(envFilePathToUpdate, currentContents, "utf-8"); } }); } function detectOpenNextCloudflareBuild() { try { const pppid = parseInt(child_process.execSync(`ps -o ppid= -p ${process.ppid}`).toString().trim()); const commandName = child_process.execSync(`ps -p ${pppid} -o command`).toString().split("\n")[1]; if (commandName.endsWith(".bin/opennextjs-cloudflare build")) { return true; } } catch (err) { } return false; } function writeResolvedEnvFile() { const dotEnvStrLines = []; for (const [itemKey, itemInfo] of Object.entries(varlockLoadedEnv.config)) { if (itemInfo.value !== void 0) dotEnvStrLines.push(`${itemKey}=${JSON.stringify(itemInfo.value)}`); } if (detectOpenNextCloudflareBuild() || process.env.VERCEL || process.env.WORKERS_CI || process.env._VARLOCK_EXPORT_RESOLVED_ENV_FILE) { dotEnvStrLines.unshift(` # \u{1F6D1} DO NOT CHECK THIS FILE INTO VERSION CONTROL \u{1F6D1} # This file was automatically generated by @varlock/nextjs-integration # It contains a _fully resolved env_ to pass to platforms (ex: vercel, cloudflare, etc) # that are doing their own magic when booting up nextjs in certain scenarios # # It likely contains sensitive config data and should be deleted after use # # @disable # tells varlock to ignore this file # --- `); dotEnvStrLines.push(`__VARLOCK_ENV=${JSON.stringify(varlockLoadedEnv)}`); let resolvedEnvFileName = ".env.production.local"; if (process.env._VARLOCK_EXPORT_RESOLVED_ENV_FILE && !["true", "1"].includes(process.env._VARLOCK_EXPORT_RESOLVED_ENV_FILE)) { resolvedEnvFileName = process.env._VARLOCK_EXPORT_RESOLVED_ENV_FILE; } if (!rootDir) throw new Error("expected rootDir to be set"); fs__namespace.writeFileSync(path__namespace.resolve(rootDir, resolvedEnvFileName), dotEnvStrLines.join("\n"), "utf-8"); } } function updateInitialEnv(newEnv) { if (Object.keys(newEnv).length) { debug("updateInitialEnv", newEnv); Object.assign(exports.initialEnv || {}, newEnv); } } function replaceProcessEnv(sourceEnv) { Object.keys(process.env).forEach((key) => { if (!key.startsWith("__NEXT_PRIVATE")) { if (sourceEnv[key] === void 0 || sourceEnv[key] === "") { delete process.env[key]; } } }); Object.entries(sourceEnv).forEach(([key, value]) => { process.env[key] = value; }); } function processEnv(_loadedEnvFiles, _dir, _log = console, _forceReload = false, _onReload) { return [process.env]; } function resetEnv() { if (exports.initialEnv) { replaceProcessEnv(exports.initialEnv); } } function loadEnvConfig(dir, dev, _log = console, forceReload = false, _onReload) { exports.initialEnv ||= { ...process.env }; debug("loadEnvConfig!", "forceReload = ", forceReload); rootDir ||= dir; if (rootDir !== dir) throw new Error("root directory changed"); if (dev) enableExtraFileWatchers(); let useCachedEnv = !!process.env.__VARLOCK_ENV; if (forceReload) { if (!lastReloadAt) { lastReloadAt = /* @__PURE__ */ new Date(); } else if (lastReloadAt.getTime() < Date.now() - 1e3) { lastReloadAt = /* @__PURE__ */ new Date(); useCachedEnv = false; } } if (useCachedEnv) { if (!varlockLoadedEnv) { varlockLoadedEnv = JSON.parse(process.env.__VARLOCK_ENV || "{}"); parsedEnv = Object.fromEntries( Object.entries(varlockLoadedEnv.config).map(([key, value]) => [key, value.value]) ); env.resetRedactionMap(varlockLoadedEnv); debug("patching console with varlock redactor"); patchConsole.patchGlobalConsole(); } combinedEnv = { ...exports.initialEnv, ...parsedEnv }; debug(">> USING CACHED ENV"); return { combinedEnv, parsedEnv, loadedEnvFiles }; } lastReloadAt = /* @__PURE__ */ new Date(); debug(">> RELOADING ENV"); replaceProcessEnv(exports.initialEnv); let envFromNextCommand = dev ? "development" : "production"; if (process.env.NODE_ENV === "test") envFromNextCommand = "test"; debug("Inferred env mode (to match @next/env):", envFromNextCommand); try { const varlockLoadedEnvBuf = child_process.execSync(`varlock load --format json-full --env ${envFromNextCommand}`, { env: exports.initialEnv }); varlockLoadedEnv = JSON.parse(varlockLoadedEnvBuf.toString()); } catch (err) { const { stdout, stderr } = err; const stdoutStr = stdout?.toString() || ""; const stderrStr = stderr?.toString() || ""; if (stderrStr.includes("command not found")) { console.error([ "", "\u274C ERROR: varlock not found", "varlock is a required peer dependency of @varlock/nextjs-integration", "", "Please add varlock as a dependency to your project (e.g., `npm install varlock`)" ].join("\n")); throw new Error("missing peer dependency - varlock"); } if (stdoutStr) console.log(stdoutStr); if (stderrStr) console.error(stderrStr); if (!dev) process.exit(1); process.env.__VARLOCK_ENV = JSON.stringify({ sources: [], config: {}, settings: {} }); return { combinedEnv: {}, parsedEnv: {}, loadedEnvFiles: [] }; } parsedEnv = {}; for (const [itemKey, itemInfo] of Object.entries(varlockLoadedEnv.config)) { parsedEnv[itemKey] = itemInfo.value; } debug("LOADED ENV:", parsedEnv); process.env.__VARLOCK_ENV = JSON.stringify(varlockLoadedEnv); env.initVarlockEnv(); env.resetRedactionMap(varlockLoadedEnv); debug("patching console with varlock redactor"); patchConsole.patchGlobalConsole(); combinedEnv = { ...exports.initialEnv, ...parsedEnv }; loadedEnvFiles = getVarlockSourcesAsLoadedEnvFiles(); if (!dev) writeResolvedEnvFile(); return { combinedEnv, parsedEnv, loadedEnvFiles }; } exports.loadEnvConfig = loadEnvConfig; exports.processEnv = processEnv; exports.resetEnv = resetEnv; exports.updateInitialEnv = updateInitialEnv; //# sourceMappingURL=next-env-compat.js.map //# sourceMappingURL=next-env-compat.js.map