@dotenvx/dotenvx 1.65.3 → 1.66.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/CHANGELOG.md +7 -1
- package/package.json +1 -1
- package/src/cli/actions/doctor.js +22 -0
- package/src/cli/dotenvx.js +8 -0
- package/src/lib/main.d.ts +17 -0
- package/src/lib/main.js +6 -0
- package/src/lib/services/doctor.js +93 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.66.0...main)
|
|
6
|
+
|
|
7
|
+
## [1.66.0](https://github.com/dotenvx/dotenvx/compare/v1.65.3...v1.66.0) (2026-05-13)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add `dotenvx doctor` ([#815](https://github.com/dotenvx/dotenvx/pull/815))
|
|
6
12
|
|
|
7
13
|
## [1.65.3](https://github.com/dotenvx/dotenvx/compare/v1.65.2...v1.65.3) (2026-05-13)
|
|
8
14
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { logger } = require('./../../shared/logger')
|
|
2
|
+
const main = require('./../../lib/main')
|
|
3
|
+
|
|
4
|
+
function doctor (directory = '.') {
|
|
5
|
+
logger.debug(`directory: ${directory}`)
|
|
6
|
+
|
|
7
|
+
const findings = main.doctor(directory)
|
|
8
|
+
logger.debug(`findings: ${JSON.stringify(findings)}`)
|
|
9
|
+
|
|
10
|
+
if (findings.length === 0) {
|
|
11
|
+
logger.info('no dotenv loaders found')
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
logger.warn(`found ${findings.length} possible dotenv loader${findings.length === 1 ? '' : 's'}`)
|
|
16
|
+
|
|
17
|
+
for (const finding of findings) {
|
|
18
|
+
logger.info(`│ ${finding.filepath}:${finding.line}: ${finding.code}`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = doctor
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -186,6 +186,14 @@ program.command('ls')
|
|
|
186
186
|
return require('./actions/ls').apply(this, args)
|
|
187
187
|
})
|
|
188
188
|
|
|
189
|
+
// dotenvx doctor
|
|
190
|
+
program.command('doctor', { hidden: true })
|
|
191
|
+
.description('scan for dotenv loaders')
|
|
192
|
+
.argument('[directory]', 'directory to scan', '.')
|
|
193
|
+
.action(function (...args) {
|
|
194
|
+
return require('./actions/doctor').apply(this, args)
|
|
195
|
+
})
|
|
196
|
+
|
|
189
197
|
// dotenvx login
|
|
190
198
|
program.command('login')
|
|
191
199
|
.description('log in to unlock ⛨ ARMORED KEYS ✦ BETA')
|
package/src/lib/main.d.ts
CHANGED
|
@@ -363,6 +363,23 @@ export function ls(
|
|
|
363
363
|
excludeEnvFile: string | string[]
|
|
364
364
|
): string[];
|
|
365
365
|
|
|
366
|
+
export type DoctorFinding = {
|
|
367
|
+
lang: string;
|
|
368
|
+
filepath: string;
|
|
369
|
+
line: number;
|
|
370
|
+
code: string;
|
|
371
|
+
msg: string;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Scan code for dotenv loaders that can conflict with dotenvx.
|
|
376
|
+
*
|
|
377
|
+
* @param directory - directory to scan
|
|
378
|
+
*/
|
|
379
|
+
export function doctor(
|
|
380
|
+
directory: string
|
|
381
|
+
): DoctorFinding[];
|
|
382
|
+
|
|
366
383
|
export type GenExampleOutput = {
|
|
367
384
|
envExampleFile: string;
|
|
368
385
|
envFile: string | string[];
|
package/src/lib/main.js
CHANGED
|
@@ -7,6 +7,7 @@ const { getColor, bold } = require('./../shared/colors')
|
|
|
7
7
|
|
|
8
8
|
// services
|
|
9
9
|
const Ls = require('./services/ls')
|
|
10
|
+
const Doctor = require('./services/doctor')
|
|
10
11
|
const Run = require('./services/run')
|
|
11
12
|
const Sets = require('./services/sets')
|
|
12
13
|
const Get = require('./services/get')
|
|
@@ -298,6 +299,10 @@ const ls = function (directory, envFile, excludeEnvFile) {
|
|
|
298
299
|
return new Ls(directory, envFile, excludeEnvFile).run()
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
const doctor = function (directory) {
|
|
303
|
+
return new Doctor(directory).run()
|
|
304
|
+
}
|
|
305
|
+
|
|
301
306
|
/** @type {import('./main').genexample} */
|
|
302
307
|
const genexample = function (directory, envFile) {
|
|
303
308
|
return new Genexample(directory, envFile).run()
|
|
@@ -326,6 +331,7 @@ module.exports = {
|
|
|
326
331
|
set,
|
|
327
332
|
get,
|
|
328
333
|
ls,
|
|
334
|
+
doctor,
|
|
329
335
|
keypair,
|
|
330
336
|
genexample,
|
|
331
337
|
// expose for libs depending on @dotenvx/dotenvx - like dotenvx-ops
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { fdir: Fdir } = require('fdir')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const picomatch = require('picomatch')
|
|
4
|
+
const fsx = require('./../helpers/fsx')
|
|
5
|
+
|
|
6
|
+
const patterns = [
|
|
7
|
+
{ lang: 'Python', file: /\.py$/, match: /load_dotenv\s*\([^)]*\)/, msg: 'found python-dotenv load call' },
|
|
8
|
+
{ lang: 'Python', file: /\.py$/, match: /dotenv_values\s*\([^)]*\)/, msg: 'found python-dotenv values call' },
|
|
9
|
+
|
|
10
|
+
{ lang: 'Node', file: /\.(js|cjs|mjs|ts)$/, match: /require\(['"]dotenv['"]\)\.config\s*\([^)]*\)/, msg: 'found dotenv config require call' },
|
|
11
|
+
{ lang: 'Node', file: /\.(js|cjs|mjs|ts)$/, match: /import\s+['"]dotenv\/config['"]/, msg: 'found dotenv/config import' },
|
|
12
|
+
{ lang: 'Node', file: /\.(js|cjs|mjs|ts)$/, match: /dotenv\.config\s*\([^)]*\)/, msg: 'found dotenv config call' },
|
|
13
|
+
|
|
14
|
+
{ lang: 'Ruby', file: /\.rb$/, match: /Dotenv\.(load|overload)\s*\([^)]*\)/, msg: 'found ruby dotenv load call' },
|
|
15
|
+
{ lang: 'Go', file: /\.go$/, match: /godotenv\.(Load|Overload)\s*\([^)]*\)/, msg: 'found godotenv load call' },
|
|
16
|
+
{ lang: 'PHP', file: /\.php$/, match: /Dotenv\\Dotenv::create\w*\s*\([^)]*\)/, msg: 'found vlucas/phpdotenv loader' },
|
|
17
|
+
|
|
18
|
+
{ lang: 'Rust', file: /\.rs$/, match: /dotenvy?::dotenv\s*\([^)]*\)/, msg: 'found Rust dotenv load call' },
|
|
19
|
+
{ lang: 'Java', file: /\.java$/, match: /Dotenv\.(load|configure)\s*\([^)]*\)/, msg: 'found java-dotenv load call' },
|
|
20
|
+
{ lang: 'Kotlin', file: /\.kt$/, match: /Dotenv\.(load|configure)\s*\([^)]*\)/, msg: 'found java-dotenv load call' },
|
|
21
|
+
{ lang: '.NET', file: /\.(cs|fs|vb)$/, match: /(?:DotNetEnv\.)?Env\.Load\s*\([^)]*\)/, msg: 'found DotNetEnv load call' }
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
class Doctor {
|
|
25
|
+
constructor (directory = './') {
|
|
26
|
+
this.cwd = path.resolve(directory)
|
|
27
|
+
this.ignore = [
|
|
28
|
+
'**/node_modules/**',
|
|
29
|
+
'**/.git/**',
|
|
30
|
+
'**/vendor/**',
|
|
31
|
+
'**/dist/**',
|
|
32
|
+
'**/build/**',
|
|
33
|
+
'**/coverage/**',
|
|
34
|
+
'**/.next/**'
|
|
35
|
+
]
|
|
36
|
+
this.exclude = picomatch(this.ignore)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
run () {
|
|
40
|
+
const findings = []
|
|
41
|
+
|
|
42
|
+
for (const filepath of this._filepaths()) {
|
|
43
|
+
findings.push(...this._scanFile(filepath))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return findings
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_filepaths () {
|
|
50
|
+
return new Fdir()
|
|
51
|
+
.withRelativePaths()
|
|
52
|
+
.exclude((dir, filepath) => this._ignored(filepath))
|
|
53
|
+
.filter((filepath) => this._patternsFor(filepath).length > 0)
|
|
54
|
+
.crawl(this.cwd)
|
|
55
|
+
.sync()
|
|
56
|
+
.sort()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_scanFile (relativeFilepath) {
|
|
60
|
+
const src = fsx.readFileXSync(path.join(this.cwd, relativeFilepath))
|
|
61
|
+
const lines = src.split(/\r?\n/)
|
|
62
|
+
const findings = []
|
|
63
|
+
|
|
64
|
+
lines.forEach((line, index) => {
|
|
65
|
+
for (const pattern of this._patternsFor(relativeFilepath)) {
|
|
66
|
+
if (pattern.match.test(line)) {
|
|
67
|
+
const match = line.match(pattern.match)
|
|
68
|
+
findings.push({
|
|
69
|
+
lang: pattern.lang,
|
|
70
|
+
filepath: relativeFilepath,
|
|
71
|
+
line: index + 1,
|
|
72
|
+
code: match[0].trim(),
|
|
73
|
+
msg: pattern.msg
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return findings
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_patternsFor (filepath) {
|
|
83
|
+
return patterns.filter(pattern => pattern.file.test(filepath))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_ignored (filepath) {
|
|
87
|
+
return this.exclude(filepath)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Doctor.patterns = patterns
|
|
92
|
+
|
|
93
|
+
module.exports = Doctor
|