envelope-env 1.0.0
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/LICENSE.md +13 -0
- package/README.md +191 -0
- package/dist/index.cjs +174 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +238 -0
- package/package.json +39 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
2
|
+
Version 2, December 2004
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
|
5
|
+
|
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
|
7
|
+
copies of this license document, and changing it is allowed as long
|
|
8
|
+
as the name is changed.
|
|
9
|
+
|
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
12
|
+
|
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Envelope 📨
|
|
2
|
+
|
|
3
|
+
Envelope is a CLI tool that compiles a `.env` from a set of
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Global Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g envelope
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Local Development
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone <repository-url>
|
|
17
|
+
cd envelope-node
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
Envelope expects your project to have the following structure:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
your-project/
|
|
28
|
+
├── env/ # Environment configuration directory
|
|
29
|
+
│ ├── .env # Common environment variables (shared across all envs)
|
|
30
|
+
│ ├── development/
|
|
31
|
+
│ │ └── .env # Development-specific variables
|
|
32
|
+
│ ├── staging/
|
|
33
|
+
│ │ └── .env # Staging-specific variables
|
|
34
|
+
│ ├── production/
|
|
35
|
+
│ │ └── .env # Production-specific variables
|
|
36
|
+
│ └── some-custom-env/
|
|
37
|
+
│ └── .env # Production-specific variables
|
|
38
|
+
├── .env # Compiled by running `envelope use <environment name>`
|
|
39
|
+
└── ...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Alternatively you can use the `.env.<name>` syntax:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
your-project/
|
|
46
|
+
├── env/ # Environment configuration directory
|
|
47
|
+
│ ├── .env # Common environment variables (shared across all envs)
|
|
48
|
+
│ ├── .env.development # Development-specific variables
|
|
49
|
+
│ ├── .env.staging # Staging environment
|
|
50
|
+
│ ├── .env.production # Production environment
|
|
51
|
+
│ └── .env.some-custom-env # Production environment
|
|
52
|
+
├── .env # Compiled by running `envelope use <environment name>`
|
|
53
|
+
└── ...
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Commands
|
|
59
|
+
|
|
60
|
+
#### `envelope list`
|
|
61
|
+
|
|
62
|
+
List all available environments in your project.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
envelope list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Output:**
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Available environments: development, staging, production
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### `envelope get <environment>`
|
|
75
|
+
|
|
76
|
+
Display the compiled environment variables for a specific environment without writing to file.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
envelope get development
|
|
80
|
+
envelope get production --silent
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Options:**
|
|
84
|
+
|
|
85
|
+
- `--silent, -s` - Suppress status messages
|
|
86
|
+
|
|
87
|
+
#### `envelope use <environment>`
|
|
88
|
+
|
|
89
|
+
Compile and write environment variables for a specific environment to your project's `.env` file.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
envelope use development
|
|
93
|
+
envelope use production --silent
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Options:**
|
|
97
|
+
|
|
98
|
+
- `--silent, -s` - Suppress status messages
|
|
99
|
+
|
|
100
|
+
### Examples
|
|
101
|
+
|
|
102
|
+
#### Setting up a new environment
|
|
103
|
+
|
|
104
|
+
1. Create your environment directory:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
mkdir -p env/staging
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
2. Create environment-specific variables:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# env/staging/.env
|
|
114
|
+
DATABASE_URL=postgresql://staging:pass@localhost:5432/staging_db
|
|
115
|
+
API_URL=https://staging-api.example.com
|
|
116
|
+
LOG_LEVEL=debug
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. Use the environment:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
envelope use staging
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Viewing environment variables
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# View development environment variables
|
|
129
|
+
envelope get development
|
|
130
|
+
|
|
131
|
+
# View production environment variables silently
|
|
132
|
+
envelope get production --silent
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Environment File Format
|
|
136
|
+
|
|
137
|
+
Environment files follow standard `.env` format:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Comments start with #
|
|
141
|
+
DATABASE_URL=postgresql://localhost:5432/mydb
|
|
142
|
+
API_KEY=your-secret-key
|
|
143
|
+
DEBUG=true
|
|
144
|
+
|
|
145
|
+
# Empty lines are allowed
|
|
146
|
+
|
|
147
|
+
# Variables can contain spaces and special characters
|
|
148
|
+
COMPLEX_VALUE="This is a complex value with spaces"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Environment Variable Precedence
|
|
152
|
+
|
|
153
|
+
When compiling environment variables, Envelope follows this order:
|
|
154
|
+
|
|
155
|
+
1. **Base variables** - `ENVELOPE_ENV` and `ENVELOPE_DIR` are always set
|
|
156
|
+
2. **Common variables** - From `env/.env` (if it exists)
|
|
157
|
+
3. **Environment-specific variables** - From `env/<environment>/.env`
|
|
158
|
+
|
|
159
|
+
Environment-specific variables will override common variables with the same name.
|
|
160
|
+
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
### Building
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npm run build
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Development Mode
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm run dev
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Testing
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm test
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
|
|
183
|
+
1. Fork the repository
|
|
184
|
+
2. Create a feature branch
|
|
185
|
+
3. Make your changes
|
|
186
|
+
4. Add tests if applicable
|
|
187
|
+
5. Submit a pull request
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
WTFPL
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/main.ts
|
|
27
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
28
|
+
var import_path = __toESM(require("path"), 1);
|
|
29
|
+
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
30
|
+
var import_citty = require("citty");
|
|
31
|
+
|
|
32
|
+
// package.json
|
|
33
|
+
var version = "1.0.0";
|
|
34
|
+
|
|
35
|
+
// src/main.ts
|
|
36
|
+
var import_consola = require("consola");
|
|
37
|
+
var log = (0, import_consola.createConsola)({
|
|
38
|
+
level: 3,
|
|
39
|
+
fancy: true
|
|
40
|
+
});
|
|
41
|
+
function getConfDir() {
|
|
42
|
+
let currentDir = process.cwd();
|
|
43
|
+
while (true) {
|
|
44
|
+
const configDir = import_path.default.join(currentDir, "config");
|
|
45
|
+
if (import_fs.default.existsSync(configDir) && import_fs.default.statSync(configDir).isDirectory()) {
|
|
46
|
+
return configDir;
|
|
47
|
+
}
|
|
48
|
+
const parentDir = import_path.default.dirname(currentDir);
|
|
49
|
+
if (parentDir === currentDir) {
|
|
50
|
+
throw new Error("Could not find config directory");
|
|
51
|
+
}
|
|
52
|
+
currentDir = parentDir;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getEnvDir(env) {
|
|
56
|
+
const confDir = getConfDir();
|
|
57
|
+
const envDir = import_path.default.join(confDir, env);
|
|
58
|
+
if (import_fs.default.existsSync(envDir) && import_fs.default.statSync(envDir).isDirectory()) {
|
|
59
|
+
return envDir;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Could not find directory ${envDir}`);
|
|
62
|
+
}
|
|
63
|
+
function validateEnvString(envVars) {
|
|
64
|
+
const lines = envVars.split("\n");
|
|
65
|
+
const validLineRegex = /^(\s*#.*|\s*$|[A-Za-z_][A-Za-z0-9_]*\s*=\s*.*)$/;
|
|
66
|
+
for (let i = 0; i < lines.length; i++) {
|
|
67
|
+
const line = lines[i].trim();
|
|
68
|
+
if (!validLineRegex.test(line)) {
|
|
69
|
+
throw new Error(`invalid .env format at line ${i + 1}: ${line}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function getCompiledEnv(env) {
|
|
75
|
+
const envDir = getEnvDir(env);
|
|
76
|
+
const confDir = getConfDir();
|
|
77
|
+
const commonEnvPath = import_path.default.join(confDir, ".env");
|
|
78
|
+
const envEnvPath = import_path.default.join(envDir, ".env");
|
|
79
|
+
let envVars = "";
|
|
80
|
+
if (import_fs.default.existsSync(commonEnvPath)) {
|
|
81
|
+
envVars += import_fs.default.readFileSync(commonEnvPath, "utf-8");
|
|
82
|
+
}
|
|
83
|
+
if (import_fs.default.existsSync(envEnvPath)) {
|
|
84
|
+
envVars += "\n" + import_fs.default.readFileSync(envEnvPath, "utf-8");
|
|
85
|
+
}
|
|
86
|
+
validateEnvString(envVars);
|
|
87
|
+
return import_dotenv.default.parse(envVars);
|
|
88
|
+
}
|
|
89
|
+
function getEnvDetails(env) {
|
|
90
|
+
const compiledEnv = getCompiledEnv(env);
|
|
91
|
+
return Object.entries(compiledEnv).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
92
|
+
}
|
|
93
|
+
function applyEnv(env) {
|
|
94
|
+
const compiledEnv = getCompiledEnv(env);
|
|
95
|
+
const envDir = getEnvDir(env);
|
|
96
|
+
process.env.ENV_DIR = envDir;
|
|
97
|
+
Object.entries(compiledEnv).forEach(([key, value]) => {
|
|
98
|
+
process.env[key] = value;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
var main = (0, import_citty.defineCommand)({
|
|
102
|
+
meta: {
|
|
103
|
+
name: "envelope",
|
|
104
|
+
version,
|
|
105
|
+
description: "\u{1F4E8}"
|
|
106
|
+
},
|
|
107
|
+
subCommands: {
|
|
108
|
+
use: (0, import_citty.defineCommand)({
|
|
109
|
+
meta: {
|
|
110
|
+
name: "use",
|
|
111
|
+
description: "use an environment"
|
|
112
|
+
},
|
|
113
|
+
args: {
|
|
114
|
+
environment: {
|
|
115
|
+
type: "positional",
|
|
116
|
+
description: "the environment name"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
async run({ args }) {
|
|
120
|
+
try {
|
|
121
|
+
applyEnv(args.environment);
|
|
122
|
+
log.success(`Using environment: ${args.environment}`);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
log.error(error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}),
|
|
128
|
+
get: (0, import_citty.defineCommand)({
|
|
129
|
+
meta: {
|
|
130
|
+
name: "get",
|
|
131
|
+
description: "prints environment details"
|
|
132
|
+
},
|
|
133
|
+
args: {
|
|
134
|
+
environment: {
|
|
135
|
+
type: "positional",
|
|
136
|
+
description: "the environment name",
|
|
137
|
+
required: false
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
async run({ args }) {
|
|
141
|
+
if (args.environment === void 0) {
|
|
142
|
+
const usage = await (0, import_citty.renderUsage)(this);
|
|
143
|
+
return log.info(usage);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const details = getEnvDetails(args.environment);
|
|
147
|
+
log.info(details);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
log.error(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}),
|
|
153
|
+
list: (0, import_citty.defineCommand)({
|
|
154
|
+
meta: {
|
|
155
|
+
name: "list",
|
|
156
|
+
description: "List all dev environment variables"
|
|
157
|
+
},
|
|
158
|
+
run() {
|
|
159
|
+
console.log("Listing all dev environment variables");
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
},
|
|
163
|
+
async run(opts) {
|
|
164
|
+
if (opts.args.help || opts.cmd === void 0) {
|
|
165
|
+
const usage = await (0, import_citty.renderUsage)(this);
|
|
166
|
+
log.info(usage);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// src/index.ts
|
|
173
|
+
var import_citty2 = require("citty");
|
|
174
|
+
(0, import_citty2.runMain)(main);
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/main.ts
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
import { defineCommand, renderUsage } from "citty";
|
|
8
|
+
|
|
9
|
+
// package.json
|
|
10
|
+
var version = "1.0.0";
|
|
11
|
+
|
|
12
|
+
// src/main.ts
|
|
13
|
+
import { createConsola } from "consola";
|
|
14
|
+
var log = createConsola({
|
|
15
|
+
level: 3,
|
|
16
|
+
fancy: true
|
|
17
|
+
});
|
|
18
|
+
function getRootDir() {
|
|
19
|
+
let currentDir = process.cwd();
|
|
20
|
+
while (true) {
|
|
21
|
+
const configDir = path.join(currentDir, "env");
|
|
22
|
+
if (fs.existsSync(configDir) && fs.statSync(configDir).isDirectory()) {
|
|
23
|
+
return currentDir;
|
|
24
|
+
}
|
|
25
|
+
const parentDir = path.dirname(currentDir);
|
|
26
|
+
if (parentDir === currentDir) {
|
|
27
|
+
throw new Error("Could not find env directory");
|
|
28
|
+
}
|
|
29
|
+
currentDir = parentDir;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getRootEnvDir() {
|
|
33
|
+
const rootDir = getRootDir();
|
|
34
|
+
return path.join(rootDir, "env");
|
|
35
|
+
}
|
|
36
|
+
function validateEnvString(envVars) {
|
|
37
|
+
const lines = envVars.split("\n");
|
|
38
|
+
const validLineRegex = /^(\s*#.*|\s*$|[A-Za-z_][A-Za-z0-9_]*\s*=\s*.*)$/;
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
const line = lines[i].trim();
|
|
41
|
+
if (!validLineRegex.test(line)) {
|
|
42
|
+
throw new Error(`invalid .env format at line ${i + 1}: ${line}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function assertSingleEnvMode(rootEnvDir) {
|
|
48
|
+
const entries = fs.readdirSync(rootEnvDir, { withFileTypes: true });
|
|
49
|
+
const hasSubdirs = entries.some(
|
|
50
|
+
(e) => e.isDirectory() && e.name !== "node_modules"
|
|
51
|
+
);
|
|
52
|
+
const hasFlatFiles = entries.some(
|
|
53
|
+
(e) => e.isFile() && /^\.env\../.test(e.name)
|
|
54
|
+
);
|
|
55
|
+
if (hasSubdirs && hasFlatFiles) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Incompatible environment modes detected in '${rootEnvDir}': found both subdirectories (directory mode) and .env.* files (flat mode). Use one layout or the other, not both.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function getCompiledEnv(env, opts) {
|
|
62
|
+
const rootEnvDir = getRootEnvDir();
|
|
63
|
+
assertSingleEnvMode(rootEnvDir);
|
|
64
|
+
const envSubDir = path.join(rootEnvDir, env);
|
|
65
|
+
const flatEnvFile = path.join(rootEnvDir, `.env.${env}`);
|
|
66
|
+
const isDirectoryMode = fs.existsSync(envSubDir) && fs.statSync(envSubDir).isDirectory();
|
|
67
|
+
const isFlatMode = !isDirectoryMode && fs.existsSync(flatEnvFile);
|
|
68
|
+
if (!isDirectoryMode && !isFlatMode) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Could not find environment '${env}': no directory '${envSubDir}' or file '${flatEnvFile}'`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const envDir = isDirectoryMode ? envSubDir : rootEnvDir;
|
|
74
|
+
let envVars = "";
|
|
75
|
+
envVars += `ENVELOPE_ENV=${env}
|
|
76
|
+
`;
|
|
77
|
+
envVars += `ENVELOPE_DIR=${envDir}
|
|
78
|
+
`;
|
|
79
|
+
const envFiles = [
|
|
80
|
+
{ path: path.join(rootEnvDir, ".env"), name: "common" },
|
|
81
|
+
{
|
|
82
|
+
path: isDirectoryMode ? path.join(envSubDir, ".env") : flatEnvFile,
|
|
83
|
+
name: env
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
for (const file of envFiles) {
|
|
87
|
+
if (fs.existsSync(file.path)) {
|
|
88
|
+
if (opts?.silent !== true)
|
|
89
|
+
log.info(`Reading ${file.name} environment variables from ${file.path}`);
|
|
90
|
+
envVars += "\n" + fs.readFileSync(file.path, "utf-8");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
validateEnvString(envVars);
|
|
94
|
+
return dotenv.parse(envVars);
|
|
95
|
+
}
|
|
96
|
+
async function compileDotEnv(env, silent) {
|
|
97
|
+
const compiledEnv = await getCompiledEnv(env, {
|
|
98
|
+
silent
|
|
99
|
+
});
|
|
100
|
+
return Object.entries(compiledEnv).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
101
|
+
}
|
|
102
|
+
var main = defineCommand({
|
|
103
|
+
meta: {
|
|
104
|
+
name: "envelope",
|
|
105
|
+
version,
|
|
106
|
+
description: "\u{1F4E8}"
|
|
107
|
+
},
|
|
108
|
+
subCommands: {
|
|
109
|
+
get: defineCommand({
|
|
110
|
+
meta: {
|
|
111
|
+
name: "get",
|
|
112
|
+
description: "Print environment variables to console"
|
|
113
|
+
},
|
|
114
|
+
args: {
|
|
115
|
+
silent: {
|
|
116
|
+
type: "boolean",
|
|
117
|
+
description: "Do not log status messages",
|
|
118
|
+
alias: ["s"]
|
|
119
|
+
},
|
|
120
|
+
environment: {
|
|
121
|
+
type: "positional",
|
|
122
|
+
description: "The environment name",
|
|
123
|
+
required: true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
async run({ args }) {
|
|
127
|
+
try {
|
|
128
|
+
const silent = !!args.silent;
|
|
129
|
+
const env = await compileDotEnv(args.environment, silent);
|
|
130
|
+
log.box(env);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
log.error(`Error in 'get' command: ${error.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}),
|
|
136
|
+
use: defineCommand({
|
|
137
|
+
meta: {
|
|
138
|
+
name: "use",
|
|
139
|
+
description: "Compile a .env file for the given environment"
|
|
140
|
+
},
|
|
141
|
+
args: {
|
|
142
|
+
silent: {
|
|
143
|
+
type: "boolean",
|
|
144
|
+
description: "Do not log status messages",
|
|
145
|
+
alias: ["s"]
|
|
146
|
+
},
|
|
147
|
+
environment: {
|
|
148
|
+
type: "positional",
|
|
149
|
+
description: "The environment name",
|
|
150
|
+
required: true
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
async run({ args }) {
|
|
154
|
+
const silent = !!args.silent;
|
|
155
|
+
try {
|
|
156
|
+
if (!silent) {
|
|
157
|
+
log.info(`Compiling environment variables for ${args.environment}`);
|
|
158
|
+
}
|
|
159
|
+
const env = await compileDotEnv(args.environment, silent);
|
|
160
|
+
const rootDir = getRootDir();
|
|
161
|
+
if (!silent) {
|
|
162
|
+
log.success(
|
|
163
|
+
`Compiled environment variables for ${args.environment}: to ${rootDir}/.env`
|
|
164
|
+
// TODO: this is not correct
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
fs.writeFileSync(path.join(rootDir, ".env"), env, "utf-8");
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (!silent) log.error(error);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}),
|
|
174
|
+
list: defineCommand({
|
|
175
|
+
meta: {
|
|
176
|
+
name: "list",
|
|
177
|
+
description: "List all available environments"
|
|
178
|
+
},
|
|
179
|
+
run() {
|
|
180
|
+
try {
|
|
181
|
+
const rootEnvDir = getRootEnvDir();
|
|
182
|
+
assertSingleEnvMode(rootEnvDir);
|
|
183
|
+
const entries = fs.readdirSync(rootEnvDir, { withFileTypes: true });
|
|
184
|
+
const environments = [
|
|
185
|
+
...entries.filter((e) => e.isDirectory() && e.name !== "node_modules").map((e) => e.name),
|
|
186
|
+
...entries.filter((e) => e.isFile() && /^\.env\../.test(e.name)).map((e) => e.name.slice(5))
|
|
187
|
+
];
|
|
188
|
+
log.info("Available environments: " + environments.join(", "));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
log.error(`Error listing environments: ${error.message}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}),
|
|
195
|
+
current: defineCommand({
|
|
196
|
+
meta: {
|
|
197
|
+
name: "current",
|
|
198
|
+
description: "Print current environment"
|
|
199
|
+
},
|
|
200
|
+
run() {
|
|
201
|
+
try {
|
|
202
|
+
const rootDir = getRootDir();
|
|
203
|
+
const envFilePath = path.join(rootDir, ".env");
|
|
204
|
+
if (!fs.existsSync(envFilePath)) {
|
|
205
|
+
throw new Error("Oopsie");
|
|
206
|
+
}
|
|
207
|
+
const envString = fs.readFileSync(envFilePath, "utf-8");
|
|
208
|
+
validateEnvString(envString);
|
|
209
|
+
const parsed = dotenv.parse(envString);
|
|
210
|
+
if (!parsed.ENVELOPE_ENV) {
|
|
211
|
+
throw new Error("Could not parse ENVELOPE_ENV");
|
|
212
|
+
}
|
|
213
|
+
log.info(`The current environment is: ${parsed.ENVELOPE_ENV}`);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
log.error(`Error getting current environment: ${error.message}`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
},
|
|
221
|
+
args: {
|
|
222
|
+
help: {
|
|
223
|
+
type: "boolean",
|
|
224
|
+
description: "show help"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
async run(opts) {
|
|
228
|
+
if (opts.args.help || opts.rawArgs.length === 0) {
|
|
229
|
+
const usage = await renderUsage(this);
|
|
230
|
+
log.info(usage);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// src/index.ts
|
|
237
|
+
import { runMain } from "citty";
|
|
238
|
+
await runMain(main);
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "envelope-env",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsup src/index.ts --format esm --dts --target es2022",
|
|
7
|
+
"start": "node dist/index.js",
|
|
8
|
+
"dev": "tsup src/index.ts --format esm,cjs --watch --onSuccess \"node dist/index.js\"",
|
|
9
|
+
"test": "vitest"
|
|
10
|
+
},
|
|
11
|
+
"keywords": ["env", "environment variables", "dx"],
|
|
12
|
+
"author": "Maximillian George",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"description": "Organise multiple environments in /env and compile into root .env by name",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"c12": "^1.11.2",
|
|
17
|
+
"citty": "^0.1.6",
|
|
18
|
+
"consola": "^3.2.3",
|
|
19
|
+
"defu": "^6.1.4",
|
|
20
|
+
"dotenv": "^16.4.5",
|
|
21
|
+
"glob": "^11.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/dotenv": "^6.1.1",
|
|
25
|
+
"@types/node": "^22.5.5",
|
|
26
|
+
"memfs": "^4.12.0",
|
|
27
|
+
"tsup": "^8.3.0",
|
|
28
|
+
"typescript": "^5.6.2",
|
|
29
|
+
"vite": "^5.4.7",
|
|
30
|
+
"vitest": "^2.1.1"
|
|
31
|
+
},
|
|
32
|
+
"bin": {
|
|
33
|
+
"envelope": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"type": "module"
|
|
39
|
+
}
|