envlock-next 0.5.0 → 0.6.1
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/index.js +164 -17
- package/dist/index.cjs +45 -0
- package/dist/index.d.cts +10 -0
- package/dist/postinstall.cjs +64 -0
- package/package.json +12 -5
package/dist/cli/index.js
CHANGED
|
@@ -4,18 +4,149 @@
|
|
|
4
4
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5
5
|
import { realpathSync } from "fs";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
// ../../node_modules/.pnpm/envlock-core@0.6.0/node_modules/envlock-core/dist/index.js
|
|
9
|
+
import { spawnSync } from "child_process";
|
|
10
|
+
import { execFileSync } from "child_process";
|
|
11
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
12
|
+
import { createServer } from "net";
|
|
13
|
+
var verbose = false;
|
|
14
|
+
function setVerbose(flag) {
|
|
15
|
+
verbose = flag;
|
|
16
|
+
}
|
|
17
|
+
var log = {
|
|
18
|
+
debug: (msg) => {
|
|
19
|
+
if (verbose) process.stderr.write(`[envlock:debug] ${msg}
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
info: (msg) => {
|
|
23
|
+
process.stderr.write(`[envlock] ${msg}
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
warn: (msg) => {
|
|
27
|
+
process.stderr.write(`[envlock] Warning: ${msg}
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
error: (msg) => {
|
|
31
|
+
process.stderr.write(`[envlock] Error: ${msg}
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var WHICH = process.platform === "win32" ? "where" : "which";
|
|
36
|
+
function hasBinary(name) {
|
|
37
|
+
try {
|
|
38
|
+
execFileSync(WHICH, [name], { stdio: "pipe" });
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function checkBinary(name, installHint) {
|
|
45
|
+
if (!hasBinary(name)) {
|
|
46
|
+
throw new Error(`[envlock] '${name}' not found in PATH.
|
|
47
|
+
${installHint}`);
|
|
48
|
+
}
|
|
49
|
+
log.debug(`Binary check: ${name} found`);
|
|
50
|
+
}
|
|
51
|
+
function runWithSecrets(options) {
|
|
52
|
+
const { envFile, environment, onePasswordEnvId, command, args } = options;
|
|
53
|
+
checkBinary(
|
|
54
|
+
"dotenvx",
|
|
55
|
+
"Install dotenvx: npm install -g @dotenvx/dotenvx\nOr add it as a dev dependency."
|
|
56
|
+
);
|
|
57
|
+
const privateKeyVar = `DOTENV_PRIVATE_KEY_${environment.toUpperCase()}`;
|
|
58
|
+
const keyAlreadyInjected = !!process.env[privateKeyVar];
|
|
59
|
+
let result;
|
|
60
|
+
if (keyAlreadyInjected) {
|
|
61
|
+
log.debug(`Spawning: dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
|
|
62
|
+
result = spawnSync(
|
|
63
|
+
"dotenvx",
|
|
64
|
+
["run", "-f", envFile, "--", command, ...args],
|
|
65
|
+
{ stdio: "inherit" }
|
|
66
|
+
);
|
|
67
|
+
if (result.error) {
|
|
68
|
+
throw new Error(`[envlock] Failed to spawn 'dotenvx': ${result.error.message}`);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
checkBinary(
|
|
72
|
+
"op",
|
|
73
|
+
"Install 1Password CLI: brew install --cask 1password-cli@beta\nThen sign in: op signin"
|
|
74
|
+
);
|
|
75
|
+
log.debug(`Spawning: op run --environment ${onePasswordEnvId} -- dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
|
|
76
|
+
result = spawnSync(
|
|
77
|
+
"op",
|
|
78
|
+
[
|
|
79
|
+
"run",
|
|
80
|
+
"--environment",
|
|
81
|
+
onePasswordEnvId,
|
|
82
|
+
"--",
|
|
83
|
+
"dotenvx",
|
|
84
|
+
"run",
|
|
85
|
+
"-f",
|
|
86
|
+
envFile,
|
|
87
|
+
"--",
|
|
88
|
+
command,
|
|
89
|
+
...args
|
|
90
|
+
],
|
|
91
|
+
{ stdio: "inherit" }
|
|
92
|
+
);
|
|
93
|
+
if (result.error) {
|
|
94
|
+
throw new Error(`[envlock] Failed to spawn 'op': ${result.error.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
process.exit(result.status ?? 1);
|
|
98
|
+
}
|
|
99
|
+
var OP_ENV_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
100
|
+
function validateOnePasswordEnvId(id) {
|
|
101
|
+
if (!id || !OP_ENV_ID_PATTERN.test(id)) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`[envlock] Invalid onePasswordEnvId: "${id}". Must be a lowercase alphanumeric string (hyphens allowed), e.g. 'ca6uypwvab5mevel44gqdc2zae'.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function validateEnvFilePath(envFile, cwd) {
|
|
108
|
+
if (envFile.includes("\0")) {
|
|
109
|
+
throw new Error(`[envlock] Invalid env file path: null bytes are not allowed.`);
|
|
110
|
+
}
|
|
111
|
+
const resolved = resolve(cwd, envFile);
|
|
112
|
+
const base = resolve(cwd);
|
|
113
|
+
const rel = relative(base, resolved);
|
|
114
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`[envlock] Invalid env file path: "${envFile}" resolves outside the project directory.`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
var ENVIRONMENTS = {
|
|
121
|
+
development: "development",
|
|
122
|
+
staging: "staging",
|
|
123
|
+
production: "production"
|
|
124
|
+
};
|
|
125
|
+
function isPortFree(port) {
|
|
126
|
+
return new Promise((resolve22) => {
|
|
127
|
+
const server = createServer();
|
|
128
|
+
server.once("error", () => resolve22(false));
|
|
129
|
+
server.once("listening", () => server.close(() => resolve22(true)));
|
|
130
|
+
server.listen(port, "127.0.0.1");
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async function findFreePort(preferred) {
|
|
134
|
+
for (let port = preferred; port <= preferred + 10; port++) {
|
|
135
|
+
if (await isPortFree(port)) return port;
|
|
136
|
+
}
|
|
137
|
+
throw new Error(
|
|
138
|
+
`[envlock] No free port found in range ${preferred}\u2013${preferred + 10}.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
9
141
|
|
|
10
142
|
// src/cli/resolve-config.ts
|
|
11
143
|
import { existsSync } from "fs";
|
|
12
|
-
import { resolve } from "path";
|
|
144
|
+
import { resolve as resolve2 } from "path";
|
|
13
145
|
import { pathToFileURL } from "url";
|
|
14
|
-
|
|
15
|
-
var CONFIG_CANDIDATES = ["next.config.js", "next.config.mjs"];
|
|
146
|
+
var CONFIG_CANDIDATES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
16
147
|
async function resolveConfig(cwd) {
|
|
17
148
|
for (const candidate of CONFIG_CANDIDATES) {
|
|
18
|
-
const fullPath =
|
|
149
|
+
const fullPath = resolve2(cwd, candidate);
|
|
19
150
|
if (!existsSync(fullPath)) continue;
|
|
20
151
|
try {
|
|
21
152
|
const mod = await import(pathToFileURL(fullPath).href);
|
|
@@ -62,16 +193,31 @@ var ARGUMENT_FLAGS = {
|
|
|
62
193
|
async function runNextCommand(subcommand, environment, passthroughArgs) {
|
|
63
194
|
const config = await resolveConfig(process.cwd());
|
|
64
195
|
const envFile = config.envFiles?.[environment] ?? DEFAULT_ENV_FILES[environment];
|
|
65
|
-
log2.debug(`Environment: ${environment}`);
|
|
66
|
-
log2.debug(`Env file: ${envFile}`);
|
|
67
|
-
log2.debug(`Command: next ${subcommand} ${passthroughArgs.join(" ")}`);
|
|
68
196
|
validateEnvFilePath(envFile, process.cwd());
|
|
197
|
+
let finalArgs = [...passthroughArgs];
|
|
198
|
+
if (subcommand === "dev") {
|
|
199
|
+
const portFlagIndex = finalArgs.findIndex(
|
|
200
|
+
(a) => a === "--port" || a === "-p"
|
|
201
|
+
);
|
|
202
|
+
const requestedPort = portFlagIndex !== -1 ? parseInt(finalArgs[portFlagIndex + 1] ?? "3000", 10) : 3e3;
|
|
203
|
+
const freePort = await findFreePort(requestedPort);
|
|
204
|
+
if (freePort !== requestedPort) {
|
|
205
|
+
log.warn(`Port ${requestedPort} in use, switching to ${freePort}`);
|
|
206
|
+
}
|
|
207
|
+
if (portFlagIndex !== -1) {
|
|
208
|
+
finalArgs.splice(portFlagIndex, 2);
|
|
209
|
+
}
|
|
210
|
+
finalArgs = ["-p", String(freePort), ...finalArgs];
|
|
211
|
+
}
|
|
212
|
+
log.debug(`Environment: ${environment}`);
|
|
213
|
+
log.debug(`Env file: ${envFile}`);
|
|
214
|
+
log.debug(`Command: next ${subcommand} ${finalArgs.join(" ")}`);
|
|
69
215
|
runWithSecrets({
|
|
70
216
|
envFile,
|
|
71
217
|
environment,
|
|
72
218
|
onePasswordEnvId: config.onePasswordEnvId,
|
|
73
219
|
command: "next",
|
|
74
|
-
args: [subcommand, ...
|
|
220
|
+
args: [subcommand, ...finalArgs]
|
|
75
221
|
});
|
|
76
222
|
}
|
|
77
223
|
function addEnvFlags(cmd) {
|
|
@@ -96,12 +242,12 @@ async function handleRunCommand(cmd, cmdArgs, opts) {
|
|
|
96
242
|
"[envlock] No onePasswordEnvId found. Set it in envlock.config.js or via ENVLOCK_OP_ENV_ID env var."
|
|
97
243
|
);
|
|
98
244
|
}
|
|
99
|
-
|
|
245
|
+
validateOnePasswordEnvId(onePasswordEnvId);
|
|
100
246
|
const envFile = config.envFiles?.[environment] ?? DEFAULT_ENV_FILES[environment];
|
|
101
247
|
validateEnvFilePath(envFile, process.cwd());
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
248
|
+
log.debug(`Environment: ${environment}`);
|
|
249
|
+
log.debug(`Env file: ${envFile}`);
|
|
250
|
+
log.debug(`Command: ${cmd} ${cmdArgs.join(" ")}`);
|
|
105
251
|
runWithSecrets({
|
|
106
252
|
envFile,
|
|
107
253
|
environment,
|
|
@@ -130,7 +276,7 @@ addEnvFlags(runCmd).action(
|
|
|
130
276
|
try {
|
|
131
277
|
await handleRunCommand(cmd, cmdArgs, opts);
|
|
132
278
|
} catch (err) {
|
|
133
|
-
|
|
279
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
134
280
|
process.exit(1);
|
|
135
281
|
}
|
|
136
282
|
}
|
|
@@ -147,9 +293,10 @@ if (import.meta.url === pathToFileURL2(_resolvedArgv1Next).href) {
|
|
|
147
293
|
if (process.argv.includes("--debug") || process.argv.includes("-d")) {
|
|
148
294
|
setVerbose(true);
|
|
149
295
|
}
|
|
150
|
-
|
|
296
|
+
log.debug(`Resolved argv[1]: ${_resolvedArgv1Next}`);
|
|
151
297
|
program.parse(process.argv);
|
|
152
298
|
}
|
|
153
299
|
export {
|
|
154
|
-
handleRunCommand
|
|
300
|
+
handleRunCommand,
|
|
301
|
+
runNextCommand
|
|
155
302
|
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
withEnvlock: () => withEnvlock
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/plugin.ts
|
|
28
|
+
var import_envlock_core = require("envlock-core");
|
|
29
|
+
function withEnvlock(nextConfig, options) {
|
|
30
|
+
if (!options?.onePasswordEnvId) {
|
|
31
|
+
console.warn(
|
|
32
|
+
"[envlock] No onePasswordEnvId provided to withEnvlock(). Set it to your 1Password Environment ID for automatic secret injection. Alternatively, set ENVLOCK_OP_ENV_ID in your environment."
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
(0, import_envlock_core.validateOnePasswordEnvId)(options.onePasswordEnvId);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
...nextConfig,
|
|
39
|
+
__envlock: options ?? { onePasswordEnvId: "" }
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
43
|
+
0 && (module.exports = {
|
|
44
|
+
withEnvlock
|
|
45
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NextConfig } from 'next';
|
|
2
|
+
import { EnvlockOptions } from 'envlock-core';
|
|
3
|
+
export { EnvlockOptions } from 'envlock-core';
|
|
4
|
+
|
|
5
|
+
type EnvlockNextConfig = NextConfig & {
|
|
6
|
+
__envlock: EnvlockOptions;
|
|
7
|
+
};
|
|
8
|
+
declare function withEnvlock(nextConfig: NextConfig, options?: EnvlockOptions): EnvlockNextConfig;
|
|
9
|
+
|
|
10
|
+
export { type EnvlockNextConfig, withEnvlock };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/postinstall.ts
|
|
21
|
+
var postinstall_exports = {};
|
|
22
|
+
__export(postinstall_exports, {
|
|
23
|
+
rewriteScripts: () => rewriteScripts
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(postinstall_exports);
|
|
26
|
+
var import_node_fs = require("fs");
|
|
27
|
+
var import_node_path = require("path");
|
|
28
|
+
var REWRITES = [
|
|
29
|
+
{ from: /(?<![envlock\s])next dev/, to: "envlock dev", cmd: "dev" },
|
|
30
|
+
{ from: /(?<![envlock\s])next build/, to: "envlock build", cmd: "build" },
|
|
31
|
+
{ from: /(?<![envlock\s])next start/, to: "envlock start", cmd: "start" }
|
|
32
|
+
];
|
|
33
|
+
function rewriteScripts(projectRoot2) {
|
|
34
|
+
const pkgPath = (0, import_node_path.join)(projectRoot2, "package.json");
|
|
35
|
+
if (!(0, import_node_fs.existsSync)(pkgPath)) return;
|
|
36
|
+
let pkg;
|
|
37
|
+
try {
|
|
38
|
+
pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!pkg.scripts) return;
|
|
43
|
+
let changed = false;
|
|
44
|
+
for (const { from, to, cmd } of REWRITES) {
|
|
45
|
+
const script = pkg.scripts[cmd];
|
|
46
|
+
if (!script) continue;
|
|
47
|
+
if (script.includes("envlock")) continue;
|
|
48
|
+
if (!from.test(script)) continue;
|
|
49
|
+
pkg.scripts[cmd] = script.replace(from, to);
|
|
50
|
+
console.log(`[envlock] Updated scripts.${cmd}: "${script}" \u2192 "${pkg.scripts[cmd]}"`);
|
|
51
|
+
changed = true;
|
|
52
|
+
}
|
|
53
|
+
if (changed) {
|
|
54
|
+
(0, import_node_fs.writeFileSync)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var projectRoot = process.env["INIT_CWD"];
|
|
58
|
+
if (projectRoot) {
|
|
59
|
+
rewriteScripts(projectRoot);
|
|
60
|
+
}
|
|
61
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
62
|
+
0 && (module.exports = {
|
|
63
|
+
rewriteScripts
|
|
64
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envlock-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Next.js plugin and CLI for envlock",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,10 +22,12 @@
|
|
|
22
22
|
},
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|
|
25
|
-
"
|
|
26
|
-
"
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"require": "./dist/index.cjs",
|
|
27
|
+
"import": "./dist/index.js"
|
|
27
28
|
}
|
|
28
29
|
},
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
29
31
|
"files": [
|
|
30
32
|
"dist"
|
|
31
33
|
],
|
|
@@ -33,15 +35,20 @@
|
|
|
33
35
|
"build": "tsup",
|
|
34
36
|
"dev": "tsup --watch",
|
|
35
37
|
"test": "vitest run",
|
|
36
|
-
"test:watch": "vitest"
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"postinstall": "node -e \"const f='./dist/postinstall.cjs';require('fs').existsSync(f)&&require(f)\""
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"envlock-core": "^0.
|
|
42
|
+
"envlock-core": "^0.6.0",
|
|
40
43
|
"commander": "^12.0.0"
|
|
41
44
|
},
|
|
42
45
|
"peerDependencies": {
|
|
46
|
+
"@dotenvx/dotenvx": ">=1.0.0",
|
|
43
47
|
"next": ">=14.0.0"
|
|
44
48
|
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@dotenvx/dotenvx": { "optional": false }
|
|
51
|
+
},
|
|
45
52
|
"devDependencies": {
|
|
46
53
|
"envlock-core": "workspace:*",
|
|
47
54
|
"@types/node": "^20.14.10",
|