@netlify/cache-utils 4.1.5-rc → 4.1.6-rc
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/lib/dir.js +8 -0
- package/lib/expire.js +15 -0
- package/lib/fs.js +60 -0
- package/lib/hash.js +36 -0
- package/lib/list.js +29 -0
- package/lib/main.js +103 -0
- package/lib/manifest.js +71 -0
- package/lib/path.js +107 -0
- package/lib/utils/cwd.js +23 -0
- package/package.json +1 -1
package/lib/dir.js
ADDED
package/lib/expire.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Retrieve the expiration date when caching a file
|
|
2
|
+
export const getExpires = function (ttl) {
|
|
3
|
+
if (!Number.isInteger(ttl) || ttl < 1) {
|
|
4
|
+
return
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return Date.now() + ttl * SECS_TO_MSECS
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const SECS_TO_MSECS = 1e3
|
|
11
|
+
|
|
12
|
+
// Check if a file about to be restored is expired
|
|
13
|
+
export const checkExpires = function (expires) {
|
|
14
|
+
return expires !== undefined && Date.now() > expires
|
|
15
|
+
}
|
package/lib/fs.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { basename, dirname } from 'path'
|
|
3
|
+
|
|
4
|
+
import cpy from 'cpy'
|
|
5
|
+
import { globby } from 'globby'
|
|
6
|
+
import { isNotJunk } from 'junk'
|
|
7
|
+
import { moveFile } from 'move-file'
|
|
8
|
+
|
|
9
|
+
// Move or copy a cached file/directory from/to a local one
|
|
10
|
+
export const moveCacheFile = async function (src, dest, move) {
|
|
11
|
+
// Moving is faster but removes the source files locally
|
|
12
|
+
if (move) {
|
|
13
|
+
return moveFile(src, dest, { overwrite: false })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { srcGlob, cwd } = await getSrcGlob(src)
|
|
17
|
+
return cpy(srcGlob, dirname(dest), { cwd, parents: true, overwrite: false })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Non-existing files and empty directories are always skipped
|
|
21
|
+
export const hasFiles = async function (src) {
|
|
22
|
+
const { srcGlob, cwd, isDir } = await getSrcGlob(src)
|
|
23
|
+
return srcGlob !== undefined && !(await isEmptyDir({ srcGlob, cwd, isDir }))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Replicates what `cpy` is doing under the hood.
|
|
27
|
+
const isEmptyDir = async function ({ srcGlob, cwd, isDir }) {
|
|
28
|
+
if (!isDir) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const files = await globby(srcGlob, { cwd })
|
|
33
|
+
const filteredFiles = files.filter((file) => isNotJunk(basename(file)))
|
|
34
|
+
return filteredFiles.length === 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get globbing pattern with files to move/copy
|
|
38
|
+
const getSrcGlob = async function (src) {
|
|
39
|
+
const srcStat = await getStat(src)
|
|
40
|
+
|
|
41
|
+
if (srcStat === undefined) {
|
|
42
|
+
return {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isDir = srcStat.isDirectory()
|
|
46
|
+
const srcBasename = basename(src)
|
|
47
|
+
const cwd = dirname(src)
|
|
48
|
+
|
|
49
|
+
if (isDir) {
|
|
50
|
+
return { srcGlob: `${srcBasename}/**`, cwd, isDir }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { srcGlob: srcBasename, cwd, isDir }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const getStat = async function (src) {
|
|
57
|
+
try {
|
|
58
|
+
return await fs.stat(src)
|
|
59
|
+
} catch {}
|
|
60
|
+
}
|
package/lib/hash.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createHash } from 'crypto'
|
|
2
|
+
import { createReadStream } from 'fs'
|
|
3
|
+
|
|
4
|
+
import getStream from 'get-stream'
|
|
5
|
+
import { locatePath } from 'locate-path'
|
|
6
|
+
|
|
7
|
+
// Caching a big directory like `node_modules` is slow. However those can
|
|
8
|
+
// sometime be represented by a digest file such as `package-lock.json`. If this
|
|
9
|
+
// has not changed, we don't need to save cache again.
|
|
10
|
+
export const getHash = async function (digests, move) {
|
|
11
|
+
// Moving files is faster than computing hashes
|
|
12
|
+
if (move || digests.length === 0) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const digestPath = await locatePath(digests)
|
|
17
|
+
if (digestPath === undefined) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hash = await hashFile(digestPath)
|
|
22
|
+
return hash
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Hash a file's contents
|
|
26
|
+
const hashFile = async function (path) {
|
|
27
|
+
const contentStream = createReadStream(path, 'utf8')
|
|
28
|
+
const hashStream = createHash(HASH_ALGO, { encoding: 'hex' })
|
|
29
|
+
contentStream.pipe(hashStream)
|
|
30
|
+
const hash = await getStream(hashStream)
|
|
31
|
+
return hash
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// We need a hashing algoritm that's as fast as possible.
|
|
35
|
+
// Userland CRC32 implementations are actually slower than Node.js SHA1.
|
|
36
|
+
const HASH_ALGO = 'sha1'
|
package/lib/list.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
|
|
3
|
+
import readdirp from 'readdirp'
|
|
4
|
+
|
|
5
|
+
import { getCacheDir } from './dir.js'
|
|
6
|
+
import { isManifest } from './manifest.js'
|
|
7
|
+
import { getBases } from './path.js'
|
|
8
|
+
|
|
9
|
+
// List all cached files/directories, at the top-level
|
|
10
|
+
export const list = async function ({ cacheDir, cwd: cwdOpt, depth = DEFAULT_DEPTH } = {}) {
|
|
11
|
+
const bases = await getBases(cwdOpt)
|
|
12
|
+
const cacheDirA = getCacheDir({ cacheDir, cwd: cwdOpt })
|
|
13
|
+
const files = await Promise.all(bases.map(({ name, base }) => listBase({ name, base, cacheDir: cacheDirA, depth })))
|
|
14
|
+
const filesA = files.flat()
|
|
15
|
+
return filesA
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DEFAULT_DEPTH = 1
|
|
19
|
+
|
|
20
|
+
// TODO: the returned paths are missing the Windows drive
|
|
21
|
+
const listBase = async function ({ name, base, cacheDir, depth }) {
|
|
22
|
+
const files = await readdirp.promise(`${cacheDir}/${name}`, { fileFilter, depth, type: 'files_directories' })
|
|
23
|
+
const filesA = files.map(({ path }) => join(base, path))
|
|
24
|
+
return filesA
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const fileFilter = function ({ basename }) {
|
|
28
|
+
return !isManifest(basename)
|
|
29
|
+
}
|
package/lib/main.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import del from 'del'
|
|
2
|
+
|
|
3
|
+
import { getCacheDir } from './dir.js'
|
|
4
|
+
import { moveCacheFile, hasFiles } from './fs.js'
|
|
5
|
+
import { list } from './list.js'
|
|
6
|
+
import { getManifestInfo, writeManifest, removeManifest, isExpired } from './manifest.js'
|
|
7
|
+
import { parsePath } from './path.js'
|
|
8
|
+
|
|
9
|
+
export { getCacheDir } from './dir.js'
|
|
10
|
+
export { list } from './list.js'
|
|
11
|
+
|
|
12
|
+
// Cache a file
|
|
13
|
+
const saveOne = async function (
|
|
14
|
+
path,
|
|
15
|
+
{ move = DEFAULT_MOVE, ttl = DEFAULT_TTL, digests = [], cacheDir, cwd: cwdOpt } = {},
|
|
16
|
+
) {
|
|
17
|
+
const { srcPath, cachePath } = await parsePath({ path, cacheDir, cwdOpt })
|
|
18
|
+
|
|
19
|
+
if (!(await hasFiles(srcPath))) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { manifestInfo, identical } = await getManifestInfo({ cachePath, move, ttl, digests })
|
|
24
|
+
if (identical) {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await del(cachePath, { force: true })
|
|
29
|
+
await moveCacheFile(srcPath, cachePath, move)
|
|
30
|
+
await writeManifest(manifestInfo)
|
|
31
|
+
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Restore a cached file
|
|
36
|
+
const restoreOne = async function (path, { move = DEFAULT_MOVE, cacheDir, cwd: cwdOpt } = {}) {
|
|
37
|
+
const { srcPath, cachePath } = await parsePath({ path, cacheDir, cwdOpt })
|
|
38
|
+
|
|
39
|
+
if (!(await hasFiles(cachePath))) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (await isExpired(cachePath)) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await del(srcPath, { force: true })
|
|
48
|
+
await moveCacheFile(cachePath, srcPath, move)
|
|
49
|
+
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Remove the cache of a file
|
|
54
|
+
const removeOne = async function (path, { cacheDir, cwd: cwdOpt } = {}) {
|
|
55
|
+
const { cachePath } = await parsePath({ path, cacheDir, cwdOpt })
|
|
56
|
+
|
|
57
|
+
if (!(await hasFiles(cachePath))) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await del(cachePath, { force: true })
|
|
62
|
+
await removeManifest(cachePath)
|
|
63
|
+
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if a file is cached
|
|
68
|
+
const hasOne = async function (path, { cacheDir, cwd: cwdOpt } = {}) {
|
|
69
|
+
const { cachePath } = await parsePath({ path, cacheDir, cwdOpt })
|
|
70
|
+
|
|
71
|
+
return (await hasFiles(cachePath)) && !(await isExpired(cachePath))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const DEFAULT_MOVE = false
|
|
75
|
+
const DEFAULT_TTL = undefined
|
|
76
|
+
|
|
77
|
+
// Allow each of the main functions to take either a single path or an array of
|
|
78
|
+
// paths as arguments
|
|
79
|
+
const allowMany = async function (func, paths, ...args) {
|
|
80
|
+
if (!Array.isArray(paths)) {
|
|
81
|
+
return func(paths, ...args)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const results = await Promise.all(paths.map((path) => func(path, ...args)))
|
|
85
|
+
return results.some(Boolean)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const save = allowMany.bind(null, saveOne)
|
|
89
|
+
export const restore = allowMany.bind(null, restoreOne)
|
|
90
|
+
export const remove = allowMany.bind(null, removeOne)
|
|
91
|
+
export const has = allowMany.bind(null, hasOne)
|
|
92
|
+
|
|
93
|
+
// Change `opts` default values
|
|
94
|
+
export const bindOpts = function (opts) {
|
|
95
|
+
return {
|
|
96
|
+
save: (paths, optsA) => save(paths, { ...opts, ...optsA }),
|
|
97
|
+
restore: (paths, optsA) => restore(paths, { ...opts, ...optsA }),
|
|
98
|
+
remove: (paths, optsA) => remove(paths, { ...opts, ...optsA }),
|
|
99
|
+
has: (paths, optsA) => has(paths, { ...opts, ...optsA }),
|
|
100
|
+
list: (optsA) => list({ ...opts, ...optsA }),
|
|
101
|
+
getCacheDir: (optsA) => getCacheDir({ ...opts, ...optsA }),
|
|
102
|
+
}
|
|
103
|
+
}
|
package/lib/manifest.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { dirname } from 'path'
|
|
3
|
+
|
|
4
|
+
import del from 'del'
|
|
5
|
+
import { pathExists } from 'path-exists'
|
|
6
|
+
|
|
7
|
+
import { getExpires, checkExpires } from './expire.js'
|
|
8
|
+
import { getHash } from './hash.js'
|
|
9
|
+
|
|
10
|
+
// Retrieve cache manifest of a file to cache, which contains the file/directory
|
|
11
|
+
// contents hash and the `expires` date.
|
|
12
|
+
export const getManifestInfo = async function ({ cachePath, move, ttl, digests }) {
|
|
13
|
+
const manifestPath = getManifestPath(cachePath)
|
|
14
|
+
const expires = getExpires(ttl)
|
|
15
|
+
const hash = await getHash(digests, move)
|
|
16
|
+
const manifest = { expires, hash }
|
|
17
|
+
const manifestString = `${JSON.stringify(manifest, null, 2)}\n`
|
|
18
|
+
const identical = await isIdentical({ hash, manifestPath, manifestString })
|
|
19
|
+
return { manifestInfo: { manifestPath, manifestString }, identical }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Whether the cache manifest has changed
|
|
23
|
+
const isIdentical = async function ({ hash, manifestPath, manifestString }) {
|
|
24
|
+
if (hash === undefined || !(await pathExists(manifestPath))) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const oldManifestString = await fs.readFile(manifestPath, 'utf8')
|
|
29
|
+
return oldManifestString === manifestString
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Persist the cache manifest to filesystem
|
|
33
|
+
export const writeManifest = async function ({ manifestPath, manifestString }) {
|
|
34
|
+
await fs.mkdir(dirname(manifestPath), { recursive: true })
|
|
35
|
+
await fs.writeFile(manifestPath, manifestString)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Remove the cache manifest from filesystem
|
|
39
|
+
export const removeManifest = async function (cachePath) {
|
|
40
|
+
const manifestPath = getManifestPath(cachePath)
|
|
41
|
+
await del(manifestPath, { force: true })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Retrieve the cache manifest filepath
|
|
45
|
+
const getManifestPath = function (cachePath) {
|
|
46
|
+
return `${cachePath}${CACHE_EXTENSION}`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const isManifest = function (filePath) {
|
|
50
|
+
return filePath.endsWith(CACHE_EXTENSION)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const CACHE_EXTENSION = '.netlify.cache.json'
|
|
54
|
+
|
|
55
|
+
// Check whether a file/directory is expired by checking its cache manifest
|
|
56
|
+
export const isExpired = async function (cachePath) {
|
|
57
|
+
const manifestPath = getManifestPath(cachePath)
|
|
58
|
+
if (!(await pathExists(manifestPath))) {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { expires } = await readManifest(cachePath)
|
|
63
|
+
return checkExpires(expires)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const readManifest = async function (cachePath) {
|
|
67
|
+
const manifestPath = getManifestPath(cachePath)
|
|
68
|
+
const manifestString = await fs.readFile(manifestPath)
|
|
69
|
+
const manifest = JSON.parse(manifestString)
|
|
70
|
+
return manifest
|
|
71
|
+
}
|
package/lib/path.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { homedir } from 'os'
|
|
2
|
+
import { resolve, isAbsolute, join, sep } from 'path'
|
|
3
|
+
|
|
4
|
+
import { getCacheDir } from './dir.js'
|
|
5
|
+
import { safeGetCwd } from './utils/cwd.js'
|
|
6
|
+
|
|
7
|
+
// Find the paths of the file before/after caching
|
|
8
|
+
export const parsePath = async function ({ path, cacheDir, cwdOpt }) {
|
|
9
|
+
const srcPath = await getSrcPath(path, cwdOpt)
|
|
10
|
+
const cachePath = await getCachePath({ srcPath, cacheDir, cwdOpt })
|
|
11
|
+
return { srcPath, cachePath }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Retrieve absolute path to the local file to cache/restore
|
|
15
|
+
const getSrcPath = async function (path, cwdOpt) {
|
|
16
|
+
const cwd = await safeGetCwd(cwdOpt)
|
|
17
|
+
const srcPath = resolvePath(path, cwd)
|
|
18
|
+
checkSrcPath(srcPath, cwd)
|
|
19
|
+
return srcPath
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const resolvePath = function (path, cwd) {
|
|
23
|
+
if (isAbsolute(path)) {
|
|
24
|
+
return resolve(path)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (cwd !== '') {
|
|
28
|
+
return resolve(cwd, path)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw new Error(`Current directory does not exist: ${cwd}`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Caching the whole repository creates many issues:
|
|
35
|
+
// - It caches many directories that are not related to Gatsby but take lots of
|
|
36
|
+
// space, such as node_modules
|
|
37
|
+
// - It caches directories that are not meant to restored across builds. For
|
|
38
|
+
// example .git (beside being big).
|
|
39
|
+
// - It undoes any build operations inside the repository that might have
|
|
40
|
+
// happened before this plugin starts restoring the cache, leading to
|
|
41
|
+
// conflicts with other plugins, Netlify Build or the buildbot.
|
|
42
|
+
const checkSrcPath = function (srcPath, cwd) {
|
|
43
|
+
if (cwd !== '' && isParentPath(srcPath, cwd)) {
|
|
44
|
+
throw new Error(`Cannot cache ${srcPath} because it is the current directory (${cwd}) or a parent directory`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Note: srcPath and cwd are already normalized and absolute
|
|
49
|
+
const isParentPath = function (srcPath, cwd) {
|
|
50
|
+
return `${cwd}${sep}`.startsWith(`${srcPath}${sep}`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getCachePath = async function ({ srcPath, cacheDir, cwdOpt }) {
|
|
54
|
+
const cacheDirA = getCacheDir({ cacheDir, cwd: cwdOpt })
|
|
55
|
+
const { name, relPath } = await findBase(srcPath, cwdOpt)
|
|
56
|
+
const cachePath = join(cacheDirA, name, relPath)
|
|
57
|
+
return cachePath
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The cached path is the path relative to the base which can be either the
|
|
61
|
+
// current directory, the home directory or the root directory. Each is tried
|
|
62
|
+
// in order.
|
|
63
|
+
const findBase = async function (srcPath, cwdOpt) {
|
|
64
|
+
const bases = await getBases(cwdOpt)
|
|
65
|
+
const srcPathA = normalizeWindows(srcPath)
|
|
66
|
+
return bases.map(({ name, base }) => parseBase(name, base, srcPathA)).find(Boolean)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Windows drives are problematic:
|
|
70
|
+
// - they cannot be used in `relPath` since directories cannot be called `C:`
|
|
71
|
+
// for example.
|
|
72
|
+
// - they make comparing between drives harder
|
|
73
|
+
// For the moment, we simply strip them. This does mean two files with the same
|
|
74
|
+
// paths but inside different drives would be cached together, which is not
|
|
75
|
+
// correct.
|
|
76
|
+
// This also means `cache.list()` always assumes the source files are on the
|
|
77
|
+
// current drive.
|
|
78
|
+
// TODO: fix it.
|
|
79
|
+
const normalizeWindows = function (srcPath) {
|
|
80
|
+
return srcPath.replace(WINDOWS_DRIVE_REGEX, '\\')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const WINDOWS_DRIVE_REGEX = /^[a-zA-Z]:\\/
|
|
84
|
+
|
|
85
|
+
// This logic works when `base` and `path` are on different Windows drives
|
|
86
|
+
const parseBase = function (name, base, srcPath) {
|
|
87
|
+
if (srcPath === base || !srcPath.startsWith(base)) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const relPath = srcPath.replace(base, '')
|
|
92
|
+
return { name, relPath }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const getBases = async function (cwdOpt) {
|
|
96
|
+
const cwdBase = await getCwdBase(cwdOpt)
|
|
97
|
+
return [...cwdBase, { name: 'home', base: homedir() }, { name: 'root', base: sep }]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getCwdBase = async function (cwdOpt) {
|
|
101
|
+
const cwd = await safeGetCwd(cwdOpt)
|
|
102
|
+
if (cwd === '') {
|
|
103
|
+
return []
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return [{ name: 'cwd', base: cwd }]
|
|
107
|
+
}
|
package/lib/utils/cwd.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { normalize } from 'path'
|
|
2
|
+
import process from 'process'
|
|
3
|
+
|
|
4
|
+
import { pathExists } from 'path-exists'
|
|
5
|
+
|
|
6
|
+
// Like `process.cwd()` but safer when current directory is wrong
|
|
7
|
+
export const safeGetCwd = async function (cwdOpt) {
|
|
8
|
+
try {
|
|
9
|
+
const cwd = getCwdValue(cwdOpt)
|
|
10
|
+
|
|
11
|
+
if (!(await pathExists(cwd))) {
|
|
12
|
+
return ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return cwd
|
|
16
|
+
} catch {
|
|
17
|
+
return ''
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const getCwdValue = function (cwdOpt = process.cwd()) {
|
|
22
|
+
return normalize(cwdOpt)
|
|
23
|
+
}
|