Skip to content

Commit c5e76b4

Browse files
committed
feat: Modularize and update dependencies
1 parent ac3693f commit c5e76b4

24 files changed

+4257
-1936
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 21 deletions
This file was deleted.

.github/workflows/codeql-analysis.yml

Lines changed: 0 additions & 71 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,55 @@
11
name: Release
2+
23
on:
4+
merge_group:
5+
branches:
6+
- main
37
push:
48
branches:
5-
- master
9+
- main
10+
workflow_dispatch:
11+
12+
# Needed for semantic-release to create GitHub releases and publish to npm via OIDC
13+
permissions:
14+
contents: write
15+
issues: write
16+
pull-requests: write
17+
id-token: write
18+
19+
concurrency:
20+
group: Release
21+
cancel-in-progress: false
22+
623
jobs:
724
release:
8-
name: Release
25+
name: Semantic Release
26+
environment: Release
27+
# Ensure releases run only when code reaches main via GitHub Merge Queue, or when manually dispatched
928
runs-on: ubuntu-latest
1029
steps:
11-
- uses: actions/checkout@v4
12-
- uses: actions/setup-node@v4
30+
- name: Checkout
31+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
32+
with:
33+
fetch-depth: 0
34+
- name: Setup Node.js
35+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
1336
with:
1437
node-version-file: '.nvmrc'
38+
cache: npm
39+
registry-url: 'https://registry.npmjs.org'
40+
- name: Update npm
41+
run: npm install -g npm@latest
1542
- name: Install dependencies
1643
run: npm ci
17-
- name: Release
44+
- name: Run ESLint
45+
run: npm run lint
46+
- name: Run unit tests
47+
run: npm run test:ci
48+
- name: Semantic Release
49+
if: ${{ github.event_name == 'push' }}
50+
uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0
1851
env:
1952
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
21-
run: npx semantic-release
53+
NPM_CONFIG_PROVENANCE: true
54+
- name: Publish to NPM
55+
run: npm publish --provenance

.github/workflows/verify-pr.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Verify PR
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
lint:
8+
name: Lint
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
13+
- name: Setup Node.js
14+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
15+
with:
16+
node-version-file: '.nvmrc'
17+
cache: npm
18+
- name: Install dependencies
19+
run: npm ci
20+
- name: Run ESLint
21+
run: npm run lint
22+
23+
test:
24+
name: Test
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
29+
- name: Setup Node.js
30+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
31+
with:
32+
node-version-file: '.nvmrc'
33+
cache: npm
34+
- name: Install dependencies
35+
run: npm ci
36+
- name: Run unit tests
37+
run: npm run test:ci

.npmignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
*
2-
!index.js
1+
.idea
2+
scripts/
3+
tests/
4+
**/*.test.js

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
22
1+
24.11.1

.releaserc.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"branches": [
3+
"main"
4+
],
5+
"plugins": [
6+
[
7+
"@semantic-release/commit-analyzer",
8+
{
9+
"preset": "conventionalcommits",
10+
"releaseRules": [
11+
{
12+
"type": "feat",
13+
"release": "minor"
14+
},
15+
{
16+
"type": "*",
17+
"release": "patch"
18+
}
19+
],
20+
"parserOpts": {
21+
"noteKeywords": [
22+
"BREAKING CHANGE",
23+
"BREAKING CHANGES"
24+
]
25+
}
26+
}
27+
],
28+
"@semantic-release/release-notes-generator",
29+
[
30+
"@semantic-release/npm",
31+
{
32+
"npmPublish": false
33+
}
34+
],
35+
[
36+
"@semantic-release/git",
37+
{
38+
"assets": [
39+
"package.json",
40+
"package-lock.json"
41+
]
42+
}
43+
],
44+
"@semantic-release/github"
45+
]
46+
}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
Dynamically loads environment variables from modules and then spawns a child process with them
44

5-
It's like [dotenv](https://github.com/motdotla/dotenv), but instead of .env files, you require modules!
5+
It's like [dotenv](https://github.com/motdotla/dotenv), but instead of .env files, you require modules!
66

77
## Usage
88

99
Add `dynenv` as a dev dependency using your particular package manager:
10-
```json
10+
```sh
1111
npm install dynenv --save-dev
1212
yarn add dynenv --dev
1313
```

e2e/readmeExamples.test.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3+
import { tmpdir } from 'os';
4+
import { join, dirname, resolve } from 'path';
5+
import { fileURLToPath } from 'url';
6+
import { spawnSync } from 'child_process';
7+
8+
const __dirname = dirname(fileURLToPath(import.meta.url));
9+
const projectRoot = resolve(__dirname, '..');
10+
const cliEntry = resolve(projectRoot, 'index.js');
11+
12+
function makeTempDir(prefix = 'dynenv-it-') {
13+
return mkdtempSync(join(tmpdir(), prefix));
14+
}
15+
16+
function ensureDir(path) {
17+
mkdirSync(path, { recursive: true });
18+
}
19+
20+
function write(path, content) {
21+
ensureDir(dirname(path));
22+
writeFileSync(path, content);
23+
}
24+
25+
function runDynenv(args, options = {}) {
26+
// Use the current Node executable to run the CLI entry
27+
const res = spawnSync(process.execPath, [cliEntry, ...args], {
28+
cwd: options.cwd,
29+
env: options.env || process.env,
30+
stdio: 'ignore',
31+
encoding: 'utf-8',
32+
});
33+
return res;
34+
}
35+
36+
describe('README integration examples', () => {
37+
let cwd;
38+
39+
beforeEach(() => {
40+
cwd = makeTempDir();
41+
});
42+
43+
afterEach(() => {
44+
if (cwd) {
45+
try {
46+
rmSync(cwd, { recursive: true, force: true });
47+
}
48+
catch {
49+
// No directory to clean up (test may have failed before it could get to this point)
50+
}
51+
}
52+
});
53+
54+
it('runs with barrier example: dynenv ./snippet.js -- <command>', () => {
55+
// snippet.js from README exporting a function that returns env vars
56+
write(join(cwd, 'snippet.js'), `module.exports = function() { return { REACT_APP_SAY_WHAAAAAT: 'WORLD!' }; };`);
57+
// child script that asserts env is set
58+
write(join(cwd, 'child.js'), `process.exit(process.env.REACT_APP_SAY_WHAAAAAT==='WORLD!' ? 0 : 1);`);
59+
60+
const res = runDynenv(['./snippet.js', '--', 'node', 'child.js'], { cwd });
61+
62+
expect(res.status).toBe(0);
63+
});
64+
65+
it('auto-discovers dynenv entries from node_modules packages (including scoped)', () => {
66+
// Unscoped package pkg1
67+
const pkg1Root = join(cwd, 'node_modules', 'pkg1');
68+
write(join(pkg1Root, 'package.json'), JSON.stringify({ name: 'pkg1', version: '1.0.0', dynenv: 'env/a.js' }, null, 2));
69+
write(join(pkg1Root, 'env', 'a.js'), `module.exports = () => ({ AUTO_DISCOVERED: 'YES' });`);
70+
71+
// Scoped package @scope/pkg2
72+
const pkg2Root = join(cwd, 'node_modules', '@scope', 'pkg2');
73+
write(join(pkg2Root, 'package.json'), JSON.stringify({ name: '@scope/pkg2', version: '1.0.0', dynenv: 'env/b.js' }, null, 2));
74+
write(join(pkg2Root, 'env', 'b.js'), `module.exports = () => ({ SCOPED: 'OK' });`);
75+
76+
// child script checks both env vars injected
77+
write(join(cwd, 'verify.js'), `const ok = process.env.AUTO_DISCOVERED==='YES' && process.env.SCOPED==='OK'; process.exit(ok?0:1);`);
78+
79+
const res = runDynenv(['node', 'verify.js'], { cwd });
80+
81+
expect(res.status).toBe(0);
82+
});
83+
});

eslint.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import js from '@eslint/js';
2+
import { defineConfig } from 'eslint/config';
3+
import globals from 'globals';
4+
5+
export default defineConfig([
6+
{
7+
files: ['**/*.{js,mjs,cjs}'],
8+
plugins: { js },
9+
extends: ['js/recommended'],
10+
languageOptions: { globals: globals.node },
11+
},
12+
]);

0 commit comments

Comments
 (0)