@dotenvx/dotenvx 2.8.0 → 2.9.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/ls.js +58 -6
- package/src/cli/commands/ext.js +1 -0
- package/src/cli/dotenvx.js +1 -0
- package/src/lib/main.d.ts +4 -2
- package/src/lib/main.js +2 -2
- package/src/lib/resolvers/ls.js +31 -3
- package/src/lib/services/prebuild.js +1 -1
- package/src/lib/services/precommit.js +1 -1
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/v2.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.9.0...main)
|
|
6
|
+
|
|
7
|
+
## [2.9.0](https://github.com/dotenvx/dotenvx/compare/v2.8.0...v2.9.0) (2026-07-14)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* BREAKING: `ls` from `@dotenvx/dotenvx` is now async/await.
|
|
6
12
|
|
|
7
13
|
## [2.8.0](https://github.com/dotenvx/dotenvx/compare/v2.7.3...v2.8.0) (2026-07-14)
|
|
8
14
|
|
package/package.json
CHANGED
package/src/cli/actions/ls.js
CHANGED
|
@@ -1,24 +1,76 @@
|
|
|
1
1
|
const { objectTreeify: treeify } = require('@dotenvx/tooling')
|
|
2
|
+
const path = require('path')
|
|
2
3
|
|
|
3
4
|
const { logger } = require('./../../shared/logger')
|
|
4
5
|
|
|
5
6
|
const main = require('./../../lib/main')
|
|
6
7
|
const ArrayToTree = require('./../../lib/helpers/arrayToTree')
|
|
8
|
+
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
9
|
+
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
7
10
|
|
|
8
|
-
function ls (directory) {
|
|
11
|
+
async function ls (directory) {
|
|
9
12
|
// debug args
|
|
10
13
|
logger.debug(`directory: ${directory}`)
|
|
11
14
|
|
|
12
15
|
const options = this.opts()
|
|
16
|
+
let spinnerOptions
|
|
17
|
+
if (typeof this.optsWithGlobals === 'function') {
|
|
18
|
+
spinnerOptions = this.optsWithGlobals()
|
|
19
|
+
} else {
|
|
20
|
+
spinnerOptions = options
|
|
21
|
+
}
|
|
22
|
+
const spinner = await createSpinner({ ...spinnerOptions, ...options, text: 'traversing' })
|
|
23
|
+
const startedAt = Date.now()
|
|
24
|
+
let directoryCount = 1
|
|
13
25
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
try {
|
|
28
|
+
const filepaths = await main.ls(directory, options.envFile, options.excludeEnvFile, (filepath) => {
|
|
29
|
+
directoryCount += 1
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
if (spinner) {
|
|
32
|
+
const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1000)
|
|
33
|
+
let directoryLabel = 'directories'
|
|
34
|
+
if (directoryCount === 1) {
|
|
35
|
+
directoryLabel = 'directory'
|
|
36
|
+
}
|
|
20
37
|
|
|
21
|
-
|
|
38
|
+
spinner.text = `traversing ${directoryCount.toLocaleString()} ${directoryLabel} (${elapsedSeconds}s) — ${filepath}`
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
logger.debug(`filepaths: ${JSON.stringify(filepaths)}`)
|
|
42
|
+
|
|
43
|
+
if (spinner) spinner.stop()
|
|
44
|
+
|
|
45
|
+
if (options.json) {
|
|
46
|
+
const cwd = path.resolve(directory || '.')
|
|
47
|
+
const absoluteFilepaths = filepaths.map(filepath => path.resolve(cwd, filepath))
|
|
48
|
+
console.log(JSON.stringify(absoluteFilepaths, null, 2))
|
|
49
|
+
} else {
|
|
50
|
+
const tree = new ArrayToTree(filepaths).run()
|
|
51
|
+
logger.debug(`tree: ${JSON.stringify(tree)}`)
|
|
52
|
+
logger.info(treeify(tree))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!spinnerOptions.quiet && !options.quiet) {
|
|
56
|
+
const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1000)
|
|
57
|
+
const matchedDirectoryCount = new Set(filepaths.map(filepath => path.dirname(filepath))).size
|
|
58
|
+
let fileLabel = 'files'
|
|
59
|
+
if (filepaths.length === 1) {
|
|
60
|
+
fileLabel = 'file'
|
|
61
|
+
}
|
|
62
|
+
let directoryLabel = 'directories'
|
|
63
|
+
if (matchedDirectoryCount === 1) {
|
|
64
|
+
directoryLabel = 'directory'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.error(`▣ found ${filepaths.length.toLocaleString()} .env ${fileLabel} across ${matchedDirectoryCount.toLocaleString()} ${directoryLabel} of ${directoryCount.toLocaleString()} scanned in ${elapsedSeconds}s`)
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (spinner) spinner.stop()
|
|
71
|
+
catchAndLog(error)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
22
74
|
}
|
|
23
75
|
|
|
24
76
|
module.exports = ls
|
package/src/cli/commands/ext.js
CHANGED
|
@@ -24,6 +24,7 @@ ext.command('ls')
|
|
|
24
24
|
.argument('[directory]', 'directory to list .env files from', '.')
|
|
25
25
|
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
26
26
|
.option('-ef, --exclude-env-file <excludeFilenames...>', 'path(s) to exclude from your env file(s) (default: none)')
|
|
27
|
+
.option('--json', 'output a JSON array of absolute filepaths')
|
|
27
28
|
.action(function (...args) {
|
|
28
29
|
return require('./../actions/ls').apply(this, args)
|
|
29
30
|
})
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -176,6 +176,7 @@ program.command('ls')
|
|
|
176
176
|
.argument('[directory]', 'directory to list .env files from', '.')
|
|
177
177
|
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
178
178
|
.option('-ef, --exclude-env-file <excludeFilenames...>', 'path(s) to exclude from your env file(s) (default: none)')
|
|
179
|
+
.option('--json', 'output a JSON array of absolute filepaths')
|
|
179
180
|
.action(function (...args) {
|
|
180
181
|
return require('./actions/ls').apply(this, args)
|
|
181
182
|
})
|
package/src/lib/main.d.ts
CHANGED
|
@@ -391,9 +391,11 @@ export function get(
|
|
|
391
391
|
* @param directory - current working directory
|
|
392
392
|
* @param envFile - glob pattern to match env files
|
|
393
393
|
* @param excludeEnvFile - glob pattern to exclude env files
|
|
394
|
+
* @param onDirectory - called as each directory is traversed
|
|
394
395
|
*/
|
|
395
396
|
export function ls(
|
|
396
397
|
directory: string,
|
|
397
398
|
envFile: string | string[],
|
|
398
|
-
excludeEnvFile: string | string[]
|
|
399
|
-
|
|
399
|
+
excludeEnvFile: string | string[],
|
|
400
|
+
onDirectory?: (directory: string) => void
|
|
401
|
+
): Promise<string[]>;
|
package/src/lib/main.js
CHANGED
|
@@ -389,8 +389,8 @@ const get = async function (key, options = {}) {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
/** @type {import('./main').ls} */
|
|
392
|
-
const ls = function (directory, envFile, excludeEnvFile) {
|
|
393
|
-
return lsResolver({ directory, envFile, excludeEnvFile })
|
|
392
|
+
const ls = async function (directory, envFile, excludeEnvFile, onDirectory) {
|
|
393
|
+
return await lsResolver({ directory, envFile, excludeEnvFile, onDirectory })
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
function resolveNoArmor (options = {}) {
|
package/src/lib/resolvers/ls.js
CHANGED
|
@@ -2,6 +2,14 @@ const { Fdir } = require('@dotenvx/tooling')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const { match } = require('@dotenvx/primitives')
|
|
4
4
|
|
|
5
|
+
const DEFAULT_EXCLUDED_DIRECTORY_EXTENSIONS = new Set([
|
|
6
|
+
'.app',
|
|
7
|
+
'.key',
|
|
8
|
+
'.numbers',
|
|
9
|
+
'.pages',
|
|
10
|
+
'.photoslibrary'
|
|
11
|
+
])
|
|
12
|
+
|
|
5
13
|
function patternsFor (value) {
|
|
6
14
|
if (!Array.isArray(value)) {
|
|
7
15
|
return [`**/${value}`]
|
|
@@ -18,24 +26,44 @@ function excludePatternsFor (value) {
|
|
|
18
26
|
return value.map(part => `**/${part}`)
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
function
|
|
29
|
+
function crawler (options = {}) {
|
|
22
30
|
const ignore = ['node_modules/**', '**/node_modules/**', '.git/**', '**/.git/**']
|
|
23
31
|
const cwd = path.resolve(options.directory || './')
|
|
24
32
|
const envFile = options.envFile || ['.env*']
|
|
25
33
|
const excludeEnvFile = options.excludeEnvFile || []
|
|
26
34
|
const excludePatterns = excludePatternsFor(excludeEnvFile)
|
|
27
|
-
|
|
35
|
+
let excludes
|
|
36
|
+
if (excludePatterns.length > 0) {
|
|
37
|
+
excludes = ignore.concat(excludePatterns)
|
|
38
|
+
} else {
|
|
39
|
+
excludes = ignore
|
|
40
|
+
}
|
|
28
41
|
const exclude = match(excludes, { dot: true })
|
|
29
42
|
const include = match(patternsFor(envFile), {
|
|
30
43
|
dot: true,
|
|
31
44
|
ignore: excludes
|
|
32
45
|
})
|
|
46
|
+
const onDirectory = options.onDirectory || (() => {})
|
|
33
47
|
|
|
34
48
|
return new Fdir()
|
|
35
49
|
.withRelativePaths()
|
|
50
|
+
.exclude((dirname, directory) => {
|
|
51
|
+
if (dirname === 'node_modules' || dirname === '.git') return true
|
|
52
|
+
if (DEFAULT_EXCLUDED_DIRECTORY_EXTENSIONS.has(path.extname(dirname).toLowerCase())) return true
|
|
53
|
+
|
|
54
|
+
onDirectory(path.relative(cwd, directory) || '.')
|
|
55
|
+
return false
|
|
56
|
+
})
|
|
36
57
|
.filter((filepath) => !exclude(filepath) && include(filepath))
|
|
37
58
|
.crawl(cwd)
|
|
38
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function ls (options = {}) {
|
|
62
|
+
return await crawler(options).withPromise()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ls.sync = function (options = {}) {
|
|
66
|
+
return crawler(options).sync()
|
|
39
67
|
}
|
|
40
68
|
|
|
41
69
|
module.exports = ls
|