@wchen.ai/env-from-example 1.0.1 → 1.0.3

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.
@@ -1,236 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import { getRootDirFromArgv, parseEnvExample, getExistingEnvVersion, getExistingEnvVariables, serializeEnvExample, polishEnvExample, bumpSemver, updateEnvSchemaVersion, } from '../../setup-env.js';
5
- const FIXTURES_DIR = path.resolve(__dirname, '../fixtures');
6
- describe('getRootDirFromArgv', () => {
7
- const originalArgv = process.argv;
8
- afterEach(() => {
9
- process.argv = originalArgv;
10
- });
11
- it('returns process.cwd() when --cwd is not present', () => {
12
- process.argv = ['node', 'setup-env.js'];
13
- expect(getRootDirFromArgv()).toBe(process.cwd());
14
- });
15
- it('returns resolved path when --cwd is present with value', () => {
16
- process.argv = ['node', 'setup-env.js', '--cwd', '/some/project'];
17
- expect(getRootDirFromArgv()).toBe(path.resolve('/some/project'));
18
- });
19
- it('returns process.cwd() when --cwd is last (no value)', () => {
20
- process.argv = ['node', 'setup-env.js', '--yes', '--cwd'];
21
- expect(getRootDirFromArgv()).toBe(process.cwd());
22
- });
23
- it('returns resolved path for relative --cwd', () => {
24
- process.argv = ['node', 'setup-env.js', '--cwd', './fixtures/full'];
25
- const result = getRootDirFromArgv();
26
- expect(path.isAbsolute(result)).toBe(true);
27
- expect(result).toMatch(/fixtures[\\/]full$/);
28
- });
29
- });
30
- describe('parseEnvExample', () => {
31
- it('throws when .env.example does not exist', () => {
32
- expect(() => parseEnvExample('/nonexistent/dir')).toThrow(/.env.example not found at/);
33
- });
34
- it('parses full fixture: version, sections, required, commented-out', () => {
35
- const rootDir = path.join(FIXTURES_DIR, 'full');
36
- const { version, variables } = parseEnvExample(rootDir);
37
- expect(version).toBe('1.0');
38
- const keys = variables.map((v) => v.key);
39
- expect(keys).toContain('DATABASE_URL');
40
- expect(keys).toContain('DATABASE_POOL_SIZE');
41
- expect(keys).toContain('API_KEY');
42
- expect(keys).toContain('API_SECRET');
43
- expect(keys).toContain('API_BASE_URL');
44
- expect(keys).toContain('NODE_ENV');
45
- expect(keys).toContain('SESSION_SECRET');
46
- expect(keys).toContain('FEATURE_BETA');
47
- expect(keys).toContain('PORT');
48
- const databaseUrl = variables.find((v) => v.key === 'DATABASE_URL');
49
- expect(databaseUrl.defaultValue).toBe('postgres://localhost:5432/myapp');
50
- expect(databaseUrl.required).toBe(true);
51
- expect(databaseUrl.isCommentedOut).toBe(false);
52
- expect(databaseUrl.comment).toMatch(/Postgres|REQUIRED/);
53
- const apiBaseUrl = variables.find((v) => v.key === 'API_BASE_URL');
54
- expect(apiBaseUrl.defaultValue).toBe('https://api.example.com/v1');
55
- const featureBeta = variables.find((v) => v.key === 'FEATURE_BETA');
56
- expect(featureBeta.isCommentedOut).toBe(true);
57
- expect(featureBeta.defaultValue).toBe('false');
58
- const port = variables.find((v) => v.key === 'PORT');
59
- expect(port.isCommentedOut).toBe(true);
60
- expect(port.defaultValue).toBe('3000');
61
- });
62
- it('parses minimal fixture with version', () => {
63
- const rootDir = path.join(FIXTURES_DIR, 'minimal');
64
- const { version, variables } = parseEnvExample(rootDir);
65
- expect(version).toBe('2.0');
66
- expect(variables).toHaveLength(2);
67
- const nodeEnv = variables.find((v) => v.key === 'NODE_ENV');
68
- expect(nodeEnv.defaultValue).toBe('development');
69
- expect(nodeEnv.required).toBe(false);
70
- const someKey = variables.find((v) => v.key === 'SOME_KEY');
71
- expect(someKey.defaultValue).toBe('default_value');
72
- });
73
- it('parses required-only fixture', () => {
74
- const rootDir = path.join(FIXTURES_DIR, 'required-only');
75
- const { version, variables } = parseEnvExample(rootDir);
76
- expect(version).toBe('1.0');
77
- expect(variables).toHaveLength(1);
78
- expect(variables[0].key).toBe('REQUIRED_VAR');
79
- expect(variables[0].required).toBe(true);
80
- expect(variables[0].defaultValue).toBe('');
81
- });
82
- it('parses no-version fixture: version is null', () => {
83
- const rootDir = path.join(FIXTURES_DIR, 'no-version');
84
- const { version, variables } = parseEnvExample(rootDir);
85
- expect(version).toBeNull();
86
- expect(variables).toHaveLength(2);
87
- expect(variables.find((v) => v.key === 'FOO')?.defaultValue).toBe('bar');
88
- expect(variables.find((v) => v.key === 'BAZ')?.defaultValue).toBe('qux');
89
- });
90
- it('strips inline comments from values', () => {
91
- const rootDir = path.join(FIXTURES_DIR, 'full');
92
- const { variables } = parseEnvExample(rootDir);
93
- // API_BASE_URL has no inline comment in fixture; DATABASE_URL might have comment in some examples
94
- // Check that quoted values are unquoted
95
- const apiBase = variables.find((v) => v.key === 'API_BASE_URL');
96
- expect(apiBase.defaultValue).toBe('https://api.example.com/v1');
97
- });
98
- it('preserves section headers in comment', () => {
99
- const rootDir = path.join(FIXTURES_DIR, 'full');
100
- const { variables } = parseEnvExample(rootDir);
101
- const dbUrl = variables.find((v) => v.key === 'DATABASE_URL');
102
- expect(dbUrl.comment).toMatch(/------/);
103
- });
104
- });
105
- describe('getExistingEnvVersion', () => {
106
- it('returns null for content without version', () => {
107
- expect(getExistingEnvVersion('FOO=bar')).toBeNull();
108
- expect(getExistingEnvVersion('')).toBeNull();
109
- });
110
- it('returns version from quoted ENV_SCHEMA_VERSION', () => {
111
- const content = '# ENV_SCHEMA_VERSION="1.0"\nFOO=bar';
112
- expect(getExistingEnvVersion(content)).toBe('1.0');
113
- });
114
- it('returns version from unquoted ENV_SCHEMA_VERSION', () => {
115
- const content = '# ENV_SCHEMA_VERSION=2.0\n';
116
- expect(getExistingEnvVersion(content)).toBe('2.0');
117
- });
118
- it('returns first match when multiple version-like lines exist', () => {
119
- const content = '# ENV_SCHEMA_VERSION="1.0"\n# ENV_SCHEMA_VERSION="2.0"';
120
- expect(getExistingEnvVersion(content)).toBe('1.0');
121
- });
122
- });
123
- describe('getExistingEnvVariables', () => {
124
- it('returns empty object when file does not exist', () => {
125
- const result = getExistingEnvVariables(path.join(FIXTURES_DIR, 'nonexistent.env'));
126
- expect(result).toEqual({});
127
- });
128
- it('parses existing .env file', () => {
129
- const envPath = path.join(FIXTURES_DIR, 'full', '.env.example');
130
- const result = getExistingEnvVariables(envPath);
131
- expect(result).toBeDefined();
132
- expect(typeof result).toBe('object');
133
- expect(result.DATABASE_URL).toBe('postgres://localhost:5432/myapp');
134
- expect(result.NODE_ENV).toBe('development');
135
- });
136
- it('returns empty object for empty or comment-only file', () => {
137
- const tmpDir = path.join(FIXTURES_DIR, 'full');
138
- const commentOnlyPath = path.join(tmpDir, '.env.comment-only');
139
- fs.writeFileSync(commentOnlyPath, '# only comments\n\n', 'utf-8');
140
- try {
141
- const result = getExistingEnvVariables(commentOnlyPath);
142
- expect(result).toEqual({});
143
- }
144
- finally {
145
- try {
146
- fs.unlinkSync(commentOnlyPath);
147
- }
148
- catch {
149
- // ignore
150
- }
151
- }
152
- });
153
- });
154
- describe('serializeEnvExample', () => {
155
- it('outputs version line and variables with sections', () => {
156
- const variables = [
157
- {
158
- key: 'FOO',
159
- defaultValue: 'bar',
160
- comment: '# ------ Section ------\nDescription',
161
- required: false,
162
- isCommentedOut: false,
163
- },
164
- {
165
- key: 'BAZ',
166
- defaultValue: 'qux',
167
- comment: '',
168
- required: false,
169
- isCommentedOut: true,
170
- },
171
- ];
172
- const out = serializeEnvExample('1.0', variables);
173
- expect(out).toMatch(/# ENV_SCHEMA_VERSION="1.0"/);
174
- expect(out).toMatch(/# ------ Section ------/);
175
- expect(out).toMatch(/FOO=bar/);
176
- expect(out).toMatch(/# BAZ=qux/);
177
- });
178
- it('outputs no version line when version is null', () => {
179
- const variables = [
180
- { key: 'X', defaultValue: 'y', comment: '', required: false, isCommentedOut: false },
181
- ];
182
- const out = serializeEnvExample(null, variables);
183
- expect(out).not.toMatch(/ENV_SCHEMA_VERSION/);
184
- expect(out).toMatch(/^X=y/);
185
- });
186
- });
187
- describe('bumpSemver', () => {
188
- it('bumps patch', () => {
189
- expect(bumpSemver('1.0.0', 'patch')).toBe('1.0.1');
190
- expect(bumpSemver('1.0', 'patch')).toBe('1.0.1');
191
- });
192
- it('bumps minor', () => {
193
- expect(bumpSemver('1.0.0', 'minor')).toBe('1.1.0');
194
- expect(bumpSemver('2.1', 'minor')).toBe('2.2.0');
195
- });
196
- it('bumps major', () => {
197
- expect(bumpSemver('1.0.0', 'major')).toBe('2.0.0');
198
- expect(bumpSemver('3.2.1', 'major')).toBe('4.0.0');
199
- });
200
- });
201
- describe('polishEnvExample', () => {
202
- it('overwrites .env.example with normalized content and dedupes keys', () => {
203
- const fixtureDir = path.join(FIXTURES_DIR, 'full');
204
- const envPath = path.join(fixtureDir, '.env.example');
205
- const before = fs.readFileSync(envPath, 'utf-8');
206
- polishEnvExample(fixtureDir);
207
- const after = fs.readFileSync(envPath, 'utf-8');
208
- expect(after).toMatch(/# ENV_SCHEMA_VERSION="1.0"/);
209
- expect(after).toMatch(/DATABASE_URL=postgres:\/\/localhost:5432\/myapp/);
210
- expect(after).toMatch(/# ------ Database ------/);
211
- // Restore original so other tests and fixtures are unchanged
212
- fs.writeFileSync(envPath, before, 'utf-8');
213
- });
214
- it('throws when .env.example does not exist', () => {
215
- expect(() => polishEnvExample('/nonexistent/dir')).toThrow(/.env.example not found/);
216
- });
217
- });
218
- describe('updateEnvSchemaVersion', () => {
219
- it('updates ENV_SCHEMA_VERSION in .env.example', () => {
220
- const fixtureDir = path.join(FIXTURES_DIR, 'minimal');
221
- const envPath = path.join(fixtureDir, '.env.example');
222
- const before = fs.readFileSync(envPath, 'utf-8');
223
- try {
224
- updateEnvSchemaVersion(fixtureDir, '3.0.0');
225
- const after = fs.readFileSync(envPath, 'utf-8');
226
- expect(after).toMatch(/# ENV_SCHEMA_VERSION="3.0.0"/);
227
- expect(after).toMatch(/NODE_ENV=development/);
228
- }
229
- finally {
230
- fs.writeFileSync(envPath, before, 'utf-8');
231
- }
232
- });
233
- it('throws when .env.example does not exist', () => {
234
- expect(() => updateEnvSchemaVersion('/nonexistent', '1.0.0')).toThrow(/.env.example not found/);
235
- });
236
- });
@@ -1,15 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
- import { resolve } from "path";
3
- export default defineConfig({
4
- test: {
5
- include: ["test/**/*.test.ts"],
6
- environment: "node",
7
- globals: true,
8
- fileParallelism: false,
9
- },
10
- resolve: {
11
- alias: {
12
- "env-from-example": resolve(__dirname, "env-from-example.ts"),
13
- },
14
- },
15
- });