Initial commit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dinlo
2026-05-31 18:44:04 +08:00
commit 436a9631fc
8616 changed files with 1389957 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
(The MIT License)
Copyright (c) 2011-2017 JP Richardson
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+262
View File
@@ -0,0 +1,262 @@
Node.js: fs-extra
=================
`fs-extra` adds file system methods that aren't included in the native `fs` module and adds promise support to the `fs` methods. It also uses [`graceful-fs`](https://github.com/isaacs/node-graceful-fs) to prevent `EMFILE` errors. It should be a drop in replacement for `fs`.
[![npm Package](https://img.shields.io/npm/v/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![License](https://img.shields.io/npm/l/fs-extra.svg)](https://github.com/jprichardson/node-fs-extra/blob/master/LICENSE)
[![build status](https://img.shields.io/github/workflow/status/jprichardson/node-fs-extra/Node.js%20CI/master)](https://github.com/jprichardson/node-fs-extra/actions/workflows/ci.yml?query=branch%3Amaster)
[![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
Why?
----
I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects.
Installation
------------
npm install fs-extra
Usage
-----
`fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed.
You don't ever need to include the original `fs` module again:
```js
const fs = require('fs') // this is no longer necessary
```
you can now do this:
```js
const fs = require('fs-extra')
```
or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want
to name your `fs` variable `fse` like so:
```js
const fse = require('fs-extra')
```
you can also keep both, but it's redundant:
```js
const fs = require('fs')
const fse = require('fs-extra')
```
Sync vs Async vs Async/Await
-------------
Most methods are async by default. All async methods will return a promise if the callback isn't passed.
Sync methods on the other hand will throw if an error occurs.
Also Async/Await will throw an error if one occurs.
Example:
```js
const fs = require('fs-extra')
// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
if (err) return console.error(err)
console.log('success!')
})
// Sync:
try {
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
// Async/Await:
async function copyFiles () {
try {
await fs.copy('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
}
copyFiles()
```
Methods
-------
### Async
- [copy](docs/copy.md)
- [emptyDir](docs/emptyDir.md)
- [ensureFile](docs/ensureFile.md)
- [ensureDir](docs/ensureDir.md)
- [ensureLink](docs/ensureLink.md)
- [ensureSymlink](docs/ensureSymlink.md)
- [mkdirp](docs/ensureDir.md)
- [mkdirs](docs/ensureDir.md)
- [move](docs/move.md)
- [outputFile](docs/outputFile.md)
- [outputJson](docs/outputJson.md)
- [pathExists](docs/pathExists.md)
- [readJson](docs/readJson.md)
- [remove](docs/remove.md)
- [writeJson](docs/writeJson.md)
### Sync
- [copySync](docs/copy-sync.md)
- [emptyDirSync](docs/emptyDir-sync.md)
- [ensureFileSync](docs/ensureFile-sync.md)
- [ensureDirSync](docs/ensureDir-sync.md)
- [ensureLinkSync](docs/ensureLink-sync.md)
- [ensureSymlinkSync](docs/ensureSymlink-sync.md)
- [mkdirpSync](docs/ensureDir-sync.md)
- [mkdirsSync](docs/ensureDir-sync.md)
- [moveSync](docs/move-sync.md)
- [outputFileSync](docs/outputFile-sync.md)
- [outputJsonSync](docs/outputJson-sync.md)
- [pathExistsSync](docs/pathExists-sync.md)
- [readJsonSync](docs/readJson-sync.md)
- [removeSync](docs/remove-sync.md)
- [writeJsonSync](docs/writeJson-sync.md)
**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()`, `fs.write()`, & `fs.writev()`](docs/fs-read-write-writev.md)
### What happened to `walk()` and `walkSync()`?
They were removed from `fs-extra` in v2.0.0. If you need the functionality, `walk` and `walkSync` are available as separate packages, [`klaw`](https://github.com/jprichardson/node-klaw) and [`klaw-sync`](https://github.com/manidlou/node-klaw-sync).
Third Party
-----------
### CLI
[fse-cli](https://www.npmjs.com/package/@atao60/fse-cli) allows you to run `fs-extra` from a console or from [npm](https://www.npmjs.com) scripts.
### TypeScript
If you like TypeScript, you can use `fs-extra` with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra
### File / Directory Watching
If you want to watch for changes to files or directories, then you should use [chokidar](https://github.com/paulmillr/chokidar).
### Obtain Filesystem (Devices, Partitions) Information
[fs-filesystem](https://github.com/arthurintelligence/node-fs-filesystem) allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system.
### Misc.
- [fs-extra-debug](https://github.com/jdxcode/fs-extra-debug) - Send your fs-extra calls to [debug](https://npmjs.org/package/debug).
- [mfs](https://github.com/cadorn/mfs) - Monitor your fs-extra calls.
Hacking on fs-extra
-------------------
Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project
uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you,
you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`.
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
What's needed?
- First, take a look at existing issues. Those are probably going to be where the priority lies.
- More tests for edge cases. Specifically on different platforms. There can never be enough tests.
- Improve test coverage.
Note: If you make any big changes, **you should definitely file an issue for discussion first.**
### Running the Test Suite
fs-extra contains hundreds of tests.
- `npm run lint`: runs the linter ([standard](http://standardjs.com/))
- `npm run unit`: runs the unit tests
- `npm test`: runs both the linter and the tests
### Windows
If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's
because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
However, I didn't have much luck doing this.
Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command:
net use z: "\\vmware-host\Shared Folders"
I can then navigate to my `fs-extra` directory and run the tests.
Naming
------
I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here:
* https://github.com/jprichardson/node-fs-extra/issues/2
* https://github.com/flatiron/utile/issues/11
* https://github.com/ryanmcgrath/wrench-js/issues/29
* https://github.com/substack/node-mkdirp/issues/17
First, I believe that in as many cases as possible, the [Node.js naming schemes](http://nodejs.org/api/fs.html) should be chosen. However, there are problems with the Node.js own naming schemes.
For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *File* and the **d** is not capitalized in *dir*. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: `fs.mkdir()`, `fs.rmdir()`, `fs.chown()`, etc.
We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`?
My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`.
Credit
------
`fs-extra` wouldn't be possible without using the modules from the following authors:
- [Isaac Shlueter](https://github.com/isaacs)
- [Charlie McConnel](https://github.com/avianflu)
- [James Halliday](https://github.com/substack)
- [Andrew Kelley](https://github.com/andrewrk)
License
-------
Licensed under MIT
Copyright (c) 2011-2017 [JP Richardson](https://github.com/jprichardson)
[1]: http://nodejs.org/docs/latest/api/fs.html
[jsonfile]: https://github.com/jprichardson/node-jsonfile
+169
View File
@@ -0,0 +1,169 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirsSync = require('../mkdirs').mkdirsSync
const utimesMillisSync = require('../util/utimes').utimesMillisSync
const stat = require('../util/stat')
function copySync (src, dest, opts) {
if (typeof opts === 'function') {
opts = { filter: opts }
}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
process.emitWarning(
'Using the preserveTimestamps option in 32-bit node is not recommended;\n\n' +
'\tsee https://github.com/jprichardson/node-fs-extra/issues/269',
'Warning', 'fs-extra-WARN0002'
)
}
const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy', opts)
stat.checkParentPathsSync(src, srcStat, dest, 'copy')
return handleFilterAndCopy(destStat, src, dest, opts)
}
function handleFilterAndCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
const destParent = path.dirname(dest)
if (!fs.existsSync(destParent)) mkdirsSync(destParent)
return getStats(destStat, src, dest, opts)
}
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(destStat, src, dest, opts)
}
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? fs.statSync : fs.lstatSync
const srcStat = statSync(src)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
else if (srcStat.isSocket()) throw new Error(`Cannot copy a socket file: ${src}`)
else if (srcStat.isFIFO()) throw new Error(`Cannot copy a FIFO pipe: ${src}`)
throw new Error(`Unknown file: ${src}`)
}
function onFile (srcStat, destStat, src, dest, opts) {
if (!destStat) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)
}
function mayCopyFile (srcStat, src, dest, opts) {
if (opts.overwrite) {
fs.unlinkSync(dest)
return copyFile(srcStat, src, dest, opts)
} else if (opts.errorOnExist) {
throw new Error(`'${dest}' already exists`)
}
}
function copyFile (srcStat, src, dest, opts) {
fs.copyFileSync(src, dest)
if (opts.preserveTimestamps) handleTimestamps(srcStat.mode, src, dest)
return setDestMode(dest, srcStat.mode)
}
function handleTimestamps (srcMode, src, dest) {
// Make sure the file is writable before setting the timestamp
// otherwise open fails with EPERM when invoked with 'r+'
// (through utimes call)
if (fileIsNotWritable(srcMode)) makeFileWritable(dest, srcMode)
return setDestTimestamps(src, dest)
}
function fileIsNotWritable (srcMode) {
return (srcMode & 0o200) === 0
}
function makeFileWritable (dest, srcMode) {
return setDestMode(dest, srcMode | 0o200)
}
function setDestMode (dest, srcMode) {
return fs.chmodSync(dest, srcMode)
}
function setDestTimestamps (src, dest) {
// The initial srcStat.atime cannot be trusted
// because it is modified by the read(2) system call
// (See https://nodejs.org/api/fs.html#fs_stat_time_values)
const updatedSrcStat = fs.statSync(src)
return utimesMillisSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime)
}
function onDir (srcStat, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts)
return copyDir(src, dest, opts)
}
function mkDirAndCopy (srcMode, src, dest, opts) {
fs.mkdirSync(dest)
copyDir(src, dest, opts)
return setDestMode(dest, srcMode)
}
function copyDir (src, dest, opts) {
fs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts))
}
function copyDirItem (item, src, dest, opts) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
const { destStat } = stat.checkPathsSync(srcItem, destItem, 'copy', opts)
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (destStat, src, dest, opts) {
let resolvedSrc = fs.readlinkSync(src)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest
try {
resolvedDest = fs.readlinkSync(dest)
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)
}
return copyLink(resolvedSrc, dest)
}
}
function copyLink (resolvedSrc, dest) {
fs.unlinkSync(dest)
return fs.symlinkSync(resolvedSrc, dest)
}
module.exports = copySync
+235
View File
@@ -0,0 +1,235 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirs = require('../mkdirs').mkdirs
const pathExists = require('../path-exists').pathExists
const utimesMillis = require('../util/utimes').utimesMillis
const stat = require('../util/stat')
function copy (src, dest, opts, cb) {
if (typeof opts === 'function' && !cb) {
cb = opts
opts = {}
} else if (typeof opts === 'function') {
opts = { filter: opts }
}
cb = cb || function () {}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
process.emitWarning(
'Using the preserveTimestamps option in 32-bit node is not recommended;\n\n' +
'\tsee https://github.com/jprichardson/node-fs-extra/issues/269',
'Warning', 'fs-extra-WARN0001'
)
}
stat.checkPaths(src, dest, 'copy', opts, (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'copy', err => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
})
})
}
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path.dirname(dest)
pathExists(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return getStats(destStat, src, dest, opts, cb)
mkdirs(destParent, err => {
if (err) return cb(err)
return getStats(destStat, src, dest, opts, cb)
})
})
}
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) return onInclude(destStat, src, dest, opts, cb)
return cb()
}, error => cb(error))
}
function startCopy (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb)
return getStats(destStat, src, dest, opts, cb)
}
function getStats (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? fs.stat : fs.lstat
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb)
else if (srcStat.isSocket()) return cb(new Error(`Cannot copy a socket file: ${src}`))
else if (srcStat.isFIFO()) return cb(new Error(`Cannot copy a FIFO pipe: ${src}`))
return cb(new Error(`Unknown file: ${src}`))
})
}
function onFile (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return copyFile(srcStat, src, dest, opts, cb)
return mayCopyFile(srcStat, src, dest, opts, cb)
}
function mayCopyFile (srcStat, src, dest, opts, cb) {
if (opts.overwrite) {
fs.unlink(dest, err => {
if (err) return cb(err)
return copyFile(srcStat, src, dest, opts, cb)
})
} else if (opts.errorOnExist) {
return cb(new Error(`'${dest}' already exists`))
} else return cb()
}
function copyFile (srcStat, src, dest, opts, cb) {
fs.copyFile(src, dest, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) return handleTimestampsAndMode(srcStat.mode, src, dest, cb)
return setDestMode(dest, srcStat.mode, cb)
})
}
function handleTimestampsAndMode (srcMode, src, dest, cb) {
// Make sure the file is writable before setting the timestamp
// otherwise open fails with EPERM when invoked with 'r+'
// (through utimes call)
if (fileIsNotWritable(srcMode)) {
return makeFileWritable(dest, srcMode, err => {
if (err) return cb(err)
return setDestTimestampsAndMode(srcMode, src, dest, cb)
})
}
return setDestTimestampsAndMode(srcMode, src, dest, cb)
}
function fileIsNotWritable (srcMode) {
return (srcMode & 0o200) === 0
}
function makeFileWritable (dest, srcMode, cb) {
return setDestMode(dest, srcMode | 0o200, cb)
}
function setDestTimestampsAndMode (srcMode, src, dest, cb) {
setDestTimestamps(src, dest, err => {
if (err) return cb(err)
return setDestMode(dest, srcMode, cb)
})
}
function setDestMode (dest, srcMode, cb) {
return fs.chmod(dest, srcMode, cb)
}
function setDestTimestamps (src, dest, cb) {
// The initial srcStat.atime cannot be trusted
// because it is modified by the read(2) system call
// (See https://nodejs.org/api/fs.html#fs_stat_time_values)
fs.stat(src, (err, updatedSrcStat) => {
if (err) return cb(err)
return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, cb)
})
}
function onDir (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts, cb)
return copyDir(src, dest, opts, cb)
}
function mkDirAndCopy (srcMode, src, dest, opts, cb) {
fs.mkdir(dest, err => {
if (err) return cb(err)
copyDir(src, dest, opts, err => {
if (err) return cb(err)
return setDestMode(dest, srcMode, cb)
})
})
}
function copyDir (src, dest, opts, cb) {
fs.readdir(src, (err, items) => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
}
function copyDirItems (items, src, dest, opts, cb) {
const item = items.pop()
if (!item) return cb()
return copyDirItem(items, item, src, dest, opts, cb)
}
function copyDirItem (items, item, src, dest, opts, cb) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
stat.checkPaths(srcItem, destItem, 'copy', opts, (err, stats) => {
if (err) return cb(err)
const { destStat } = stats
startCopy(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
})
}
function onLink (destStat, src, dest, opts, cb) {
fs.readlink(src, (err, resolvedSrc) => {
if (err) return cb(err)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlink(resolvedSrc, dest, cb)
} else {
fs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))
}
return copyLink(resolvedSrc, dest, cb)
})
}
})
}
function copyLink (resolvedSrc, dest, cb) {
fs.unlink(dest, err => {
if (err) return cb(err)
return fs.symlink(resolvedSrc, dest, cb)
})
}
module.exports = copy
+7
View File
@@ -0,0 +1,7 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
copy: u(require('./copy')),
copySync: require('./copy-sync')
}
+39
View File
@@ -0,0 +1,39 @@
'use strict'
const u = require('universalify').fromPromise
const fs = require('../fs')
const path = require('path')
const mkdir = require('../mkdirs')
const remove = require('../remove')
const emptyDir = u(async function emptyDir (dir) {
let items
try {
items = await fs.readdir(dir)
} catch {
return mkdir.mkdirs(dir)
}
return Promise.all(items.map(item => remove.remove(path.join(dir, item))))
})
function emptyDirSync (dir) {
let items
try {
items = fs.readdirSync(dir)
} catch {
return mkdir.mkdirsSync(dir)
}
items.forEach(item => {
item = path.join(dir, item)
remove.removeSync(item)
})
}
module.exports = {
emptyDirSync,
emptydirSync: emptyDirSync,
emptyDir,
emptydir: emptyDir
}
+69
View File
@@ -0,0 +1,69 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
function createFile (file, callback) {
function makeFile () {
fs.writeFile(file, '', err => {
if (err) return callback(err)
callback()
})
}
fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
if (!err && stats.isFile()) return callback()
const dir = path.dirname(file)
fs.stat(dir, (err, stats) => {
if (err) {
// if the directory doesn't exist, make it
if (err.code === 'ENOENT') {
return mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeFile()
})
}
return callback(err)
}
if (stats.isDirectory()) makeFile()
else {
// parent is not a directory
// This is just to cause an internal ENOTDIR error to be thrown
fs.readdir(dir, err => {
if (err) return callback(err)
})
}
})
})
}
function createFileSync (file) {
let stats
try {
stats = fs.statSync(file)
} catch {}
if (stats && stats.isFile()) return
const dir = path.dirname(file)
try {
if (!fs.statSync(dir).isDirectory()) {
// parent is not a directory
// This is just to cause an internal ENOTDIR error to be thrown
fs.readdirSync(dir)
}
} catch (err) {
// If the stat call above failed because the directory doesn't exist, create it
if (err && err.code === 'ENOENT') mkdir.mkdirsSync(dir)
else throw err
}
fs.writeFileSync(file, '')
}
module.exports = {
createFile: u(createFile),
createFileSync
}
+23
View File
@@ -0,0 +1,23 @@
'use strict'
const { createFile, createFileSync } = require('./file')
const { createLink, createLinkSync } = require('./link')
const { createSymlink, createSymlinkSync } = require('./symlink')
module.exports = {
// file
createFile,
createFileSync,
ensureFile: createFile,
ensureFileSync: createFileSync,
// link
createLink,
createLinkSync,
ensureLink: createLink,
ensureLinkSync: createLinkSync,
// symlink
createSymlink,
createSymlinkSync,
ensureSymlink: createSymlink,
ensureSymlinkSync: createSymlinkSync
}
+64
View File
@@ -0,0 +1,64 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
const { areIdentical } = require('../util/stat')
function createLink (srcpath, dstpath, callback) {
function makeLink (srcpath, dstpath) {
fs.link(srcpath, dstpath, err => {
if (err) return callback(err)
callback(null)
})
}
fs.lstat(dstpath, (_, dstStat) => {
fs.lstat(srcpath, (err, srcStat) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureLink')
return callback(err)
}
if (dstStat && areIdentical(srcStat, dstStat)) return callback(null)
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeLink(srcpath, dstpath)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeLink(srcpath, dstpath)
})
})
})
})
}
function createLinkSync (srcpath, dstpath) {
let dstStat
try {
dstStat = fs.lstatSync(dstpath)
} catch {}
try {
const srcStat = fs.lstatSync(srcpath)
if (dstStat && areIdentical(srcStat, dstStat)) return
} catch (err) {
err.message = err.message.replace('lstat', 'ensureLink')
throw err
}
const dir = path.dirname(dstpath)
const dirExists = fs.existsSync(dir)
if (dirExists) return fs.linkSync(srcpath, dstpath)
mkdir.mkdirsSync(dir)
return fs.linkSync(srcpath, dstpath)
}
module.exports = {
createLink: u(createLink),
createLinkSync
}
@@ -0,0 +1,99 @@
'use strict'
const path = require('path')
const fs = require('graceful-fs')
const pathExists = require('../path-exists').pathExists
/**
* Function that returns two types of paths, one relative to symlink, and one
* relative to the current working directory. Checks if path is absolute or
* relative. If the path is relative, this function checks if the path is
* relative to symlink or relative to current working directory. This is an
* initiative to find a smarter `srcpath` to supply when building symlinks.
* This allows you to determine which path to use out of one of three possible
* types of source paths. The first is an absolute path. This is detected by
* `path.isAbsolute()`. When an absolute path is provided, it is checked to
* see if it exists. If it does it's used, if not an error is returned
* (callback)/ thrown (sync). The other two options for `srcpath` are a
* relative url. By default Node's `fs.symlink` works by creating a symlink
* using `dstpath` and expects the `srcpath` to be relative to the newly
* created symlink. If you provide a `srcpath` that does not exist on the file
* system it results in a broken symlink. To minimize this, the function
* checks to see if the 'relative to symlink' source file exists, and if it
* does it will use it. If it does not, it checks if there's a file that
* exists that is relative to the current working directory, if does its used.
* This preserves the expectations of the original fs.symlink spec and adds
* the ability to pass in `relative to current working direcotry` paths.
*/
function symlinkPaths (srcpath, dstpath, callback) {
if (path.isAbsolute(srcpath)) {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
toCwd: srcpath,
toDst: srcpath
})
})
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
return pathExists(relativeToDst, (err, exists) => {
if (err) return callback(err)
if (exists) {
return callback(null, {
toCwd: relativeToDst,
toDst: srcpath
})
} else {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
toCwd: srcpath,
toDst: path.relative(dstdir, srcpath)
})
})
}
})
}
}
function symlinkPathsSync (srcpath, dstpath) {
let exists
if (path.isAbsolute(srcpath)) {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('absolute srcpath does not exist')
return {
toCwd: srcpath,
toDst: srcpath
}
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
exists = fs.existsSync(relativeToDst)
if (exists) {
return {
toCwd: relativeToDst,
toDst: srcpath
}
} else {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('relative srcpath does not exist')
return {
toCwd: srcpath,
toDst: path.relative(dstdir, srcpath)
}
}
}
}
module.exports = {
symlinkPaths,
symlinkPathsSync
}
@@ -0,0 +1,31 @@
'use strict'
const fs = require('graceful-fs')
function symlinkType (srcpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
if (type) return callback(null, type)
fs.lstat(srcpath, (err, stats) => {
if (err) return callback(null, 'file')
type = (stats && stats.isDirectory()) ? 'dir' : 'file'
callback(null, type)
})
}
function symlinkTypeSync (srcpath, type) {
let stats
if (type) return type
try {
stats = fs.lstatSync(srcpath)
} catch {
return 'file'
}
return (stats && stats.isDirectory()) ? 'dir' : 'file'
}
module.exports = {
symlinkType,
symlinkTypeSync
}
+82
View File
@@ -0,0 +1,82 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('../fs')
const _mkdirs = require('../mkdirs')
const mkdirs = _mkdirs.mkdirs
const mkdirsSync = _mkdirs.mkdirsSync
const _symlinkPaths = require('./symlink-paths')
const symlinkPaths = _symlinkPaths.symlinkPaths
const symlinkPathsSync = _symlinkPaths.symlinkPathsSync
const _symlinkType = require('./symlink-type')
const symlinkType = _symlinkType.symlinkType
const symlinkTypeSync = _symlinkType.symlinkTypeSync
const pathExists = require('../path-exists').pathExists
const { areIdentical } = require('../util/stat')
function createSymlink (srcpath, dstpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
fs.lstat(dstpath, (err, stats) => {
if (!err && stats.isSymbolicLink()) {
Promise.all([
fs.stat(srcpath),
fs.stat(dstpath)
]).then(([srcStat, dstStat]) => {
if (areIdentical(srcStat, dstStat)) return callback(null)
_createSymlink(srcpath, dstpath, type, callback)
})
} else _createSymlink(srcpath, dstpath, type, callback)
})
}
function _createSymlink (srcpath, dstpath, type, callback) {
symlinkPaths(srcpath, dstpath, (err, relative) => {
if (err) return callback(err)
srcpath = relative.toDst
symlinkType(relative.toCwd, type, (err, type) => {
if (err) return callback(err)
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return fs.symlink(srcpath, dstpath, type, callback)
mkdirs(dir, err => {
if (err) return callback(err)
fs.symlink(srcpath, dstpath, type, callback)
})
})
})
})
}
function createSymlinkSync (srcpath, dstpath, type) {
let stats
try {
stats = fs.lstatSync(dstpath)
} catch {}
if (stats && stats.isSymbolicLink()) {
const srcStat = fs.statSync(srcpath)
const dstStat = fs.statSync(dstpath)
if (areIdentical(srcStat, dstStat)) return
}
const relative = symlinkPathsSync(srcpath, dstpath)
srcpath = relative.toDst
type = symlinkTypeSync(relative.toCwd, type)
const dir = path.dirname(dstpath)
const exists = fs.existsSync(dir)
if (exists) return fs.symlinkSync(srcpath, dstpath, type)
mkdirsSync(dir)
return fs.symlinkSync(srcpath, dstpath, type)
}
module.exports = {
createSymlink: u(createSymlink),
createSymlinkSync
}
+128
View File
@@ -0,0 +1,128 @@
'use strict'
// This is adapted from https://github.com/normalize/mz
// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const api = [
'access',
'appendFile',
'chmod',
'chown',
'close',
'copyFile',
'fchmod',
'fchown',
'fdatasync',
'fstat',
'fsync',
'ftruncate',
'futimes',
'lchmod',
'lchown',
'link',
'lstat',
'mkdir',
'mkdtemp',
'open',
'opendir',
'readdir',
'readFile',
'readlink',
'realpath',
'rename',
'rm',
'rmdir',
'stat',
'symlink',
'truncate',
'unlink',
'utimes',
'writeFile'
].filter(key => {
// Some commands are not available on some systems. Ex:
// fs.opendir was added in Node.js v12.12.0
// fs.rm was added in Node.js v14.14.0
// fs.lchown is not available on at least some Linux
return typeof fs[key] === 'function'
})
// Export cloned fs:
Object.assign(exports, fs)
// Universalify async methods:
api.forEach(method => {
exports[method] = u(fs[method])
})
// We differ from mz/fs in that we still ship the old, broken, fs.exists()
// since we are a drop-in replacement for the native module
exports.exists = function (filename, callback) {
if (typeof callback === 'function') {
return fs.exists(filename, callback)
}
return new Promise(resolve => {
return fs.exists(filename, resolve)
})
}
// fs.read(), fs.write(), & fs.writev() need special treatment due to multiple callback args
exports.read = function (fd, buffer, offset, length, position, callback) {
if (typeof callback === 'function') {
return fs.read(fd, buffer, offset, length, position, callback)
}
return new Promise((resolve, reject) => {
fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
if (err) return reject(err)
resolve({ bytesRead, buffer })
})
})
}
// Function signature can be
// fs.write(fd, buffer[, offset[, length[, position]]], callback)
// OR
// fs.write(fd, string[, position[, encoding]], callback)
// We need to handle both cases, so we use ...args
exports.write = function (fd, buffer, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.write(fd, buffer, ...args)
}
return new Promise((resolve, reject) => {
fs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => {
if (err) return reject(err)
resolve({ bytesWritten, buffer })
})
})
}
// fs.writev only available in Node v12.9.0+
if (typeof fs.writev === 'function') {
// Function signature is
// s.writev(fd, buffers[, position], callback)
// We need to handle the optional arg, so we use ...args
exports.writev = function (fd, buffers, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.writev(fd, buffers, ...args)
}
return new Promise((resolve, reject) => {
fs.writev(fd, buffers, ...args, (err, bytesWritten, buffers) => {
if (err) return reject(err)
resolve({ bytesWritten, buffers })
})
})
}
}
// fs.realpath.native sometimes not available if fs is monkey-patched
if (typeof fs.realpath.native === 'function') {
exports.realpath.native = u(fs.realpath.native)
} else {
process.emitWarning(
'fs.realpath.native is not a function. Is fs being monkey-patched?',
'Warning', 'fs-extra-WARN0003'
)
}
+16
View File
@@ -0,0 +1,16 @@
'use strict'
module.exports = {
// Export promiseified graceful-fs:
...require('./fs'),
// Export extra methods:
...require('./copy'),
...require('./empty'),
...require('./ensure'),
...require('./json'),
...require('./mkdirs'),
...require('./move'),
...require('./output-file'),
...require('./path-exists'),
...require('./remove')
}
+16
View File
@@ -0,0 +1,16 @@
'use strict'
const u = require('universalify').fromPromise
const jsonFile = require('./jsonfile')
jsonFile.outputJson = u(require('./output-json'))
jsonFile.outputJsonSync = require('./output-json-sync')
// aliases
jsonFile.outputJSON = jsonFile.outputJson
jsonFile.outputJSONSync = jsonFile.outputJsonSync
jsonFile.writeJSON = jsonFile.writeJson
jsonFile.writeJSONSync = jsonFile.writeJsonSync
jsonFile.readJSON = jsonFile.readJson
jsonFile.readJSONSync = jsonFile.readJsonSync
module.exports = jsonFile
+11
View File
@@ -0,0 +1,11 @@
'use strict'
const jsonFile = require('jsonfile')
module.exports = {
// jsonfile exports
readJson: jsonFile.readFile,
readJsonSync: jsonFile.readFileSync,
writeJson: jsonFile.writeFile,
writeJsonSync: jsonFile.writeFileSync
}
@@ -0,0 +1,12 @@
'use strict'
const { stringify } = require('jsonfile/utils')
const { outputFileSync } = require('../output-file')
function outputJsonSync (file, data, options) {
const str = stringify(data, options)
outputFileSync(file, str, options)
}
module.exports = outputJsonSync
+12
View File
@@ -0,0 +1,12 @@
'use strict'
const { stringify } = require('jsonfile/utils')
const { outputFile } = require('../output-file')
async function outputJson (file, data, options = {}) {
const str = stringify(data, options)
await outputFile(file, str, options)
}
module.exports = outputJson
+14
View File
@@ -0,0 +1,14 @@
'use strict'
const u = require('universalify').fromPromise
const { makeDir: _makeDir, makeDirSync } = require('./make-dir')
const makeDir = u(_makeDir)
module.exports = {
mkdirs: makeDir,
mkdirsSync: makeDirSync,
// alias
mkdirp: makeDir,
mkdirpSync: makeDirSync,
ensureDir: makeDir,
ensureDirSync: makeDirSync
}
+27
View File
@@ -0,0 +1,27 @@
'use strict'
const fs = require('../fs')
const { checkPath } = require('./utils')
const getMode = options => {
const defaults = { mode: 0o777 }
if (typeof options === 'number') return options
return ({ ...defaults, ...options }).mode
}
module.exports.makeDir = async (dir, options) => {
checkPath(dir)
return fs.mkdir(dir, {
mode: getMode(options),
recursive: true
})
}
module.exports.makeDirSync = (dir, options) => {
checkPath(dir)
return fs.mkdirSync(dir, {
mode: getMode(options),
recursive: true
})
}
+21
View File
@@ -0,0 +1,21 @@
// Adapted from https://github.com/sindresorhus/make-dir
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict'
const path = require('path')
// https://github.com/nodejs/node/issues/8987
// https://github.com/libuv/libuv/pull/1088
module.exports.checkPath = function checkPath (pth) {
if (process.platform === 'win32') {
const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, ''))
if (pathHasInvalidWinCharacters) {
const error = new Error(`Path contains invalid characters: ${pth}`)
error.code = 'EINVAL'
throw error
}
}
}
+7
View File
@@ -0,0 +1,7 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
move: u(require('./move')),
moveSync: require('./move-sync')
}
+54
View File
@@ -0,0 +1,54 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copySync = require('../copy').copySync
const removeSync = require('../remove').removeSync
const mkdirpSync = require('../mkdirs').mkdirpSync
const stat = require('../util/stat')
function moveSync (src, dest, opts) {
opts = opts || {}
const overwrite = opts.overwrite || opts.clobber || false
const { srcStat, isChangingCase = false } = stat.checkPathsSync(src, dest, 'move', opts)
stat.checkParentPathsSync(src, srcStat, dest, 'move')
if (!isParentRoot(dest)) mkdirpSync(path.dirname(dest))
return doRename(src, dest, overwrite, isChangingCase)
}
function isParentRoot (dest) {
const parent = path.dirname(dest)
const parsedPath = path.parse(parent)
return parsedPath.root === parent
}
function doRename (src, dest, overwrite, isChangingCase) {
if (isChangingCase) return rename(src, dest, overwrite)
if (overwrite) {
removeSync(dest)
return rename(src, dest, overwrite)
}
if (fs.existsSync(dest)) throw new Error('dest already exists.')
return rename(src, dest, overwrite)
}
function rename (src, dest, overwrite) {
try {
fs.renameSync(src, dest)
} catch (err) {
if (err.code !== 'EXDEV') throw err
return moveAcrossDevice(src, dest, overwrite)
}
}
function moveAcrossDevice (src, dest, overwrite) {
const opts = {
overwrite,
errorOnExist: true
}
copySync(src, dest, opts)
return removeSync(src)
}
module.exports = moveSync
+75
View File
@@ -0,0 +1,75 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copy = require('../copy').copy
const remove = require('../remove').remove
const mkdirp = require('../mkdirs').mkdirp
const pathExists = require('../path-exists').pathExists
const stat = require('../util/stat')
function move (src, dest, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
opts = opts || {}
const overwrite = opts.overwrite || opts.clobber || false
stat.checkPaths(src, dest, 'move', opts, (err, stats) => {
if (err) return cb(err)
const { srcStat, isChangingCase = false } = stats
stat.checkParentPaths(src, srcStat, dest, 'move', err => {
if (err) return cb(err)
if (isParentRoot(dest)) return doRename(src, dest, overwrite, isChangingCase, cb)
mkdirp(path.dirname(dest), err => {
if (err) return cb(err)
return doRename(src, dest, overwrite, isChangingCase, cb)
})
})
})
}
function isParentRoot (dest) {
const parent = path.dirname(dest)
const parsedPath = path.parse(parent)
return parsedPath.root === parent
}
function doRename (src, dest, overwrite, isChangingCase, cb) {
if (isChangingCase) return rename(src, dest, overwrite, cb)
if (overwrite) {
return remove(dest, err => {
if (err) return cb(err)
return rename(src, dest, overwrite, cb)
})
}
pathExists(dest, (err, destExists) => {
if (err) return cb(err)
if (destExists) return cb(new Error('dest already exists.'))
return rename(src, dest, overwrite, cb)
})
}
function rename (src, dest, overwrite, cb) {
fs.rename(src, dest, err => {
if (!err) return cb()
if (err.code !== 'EXDEV') return cb(err)
return moveAcrossDevice(src, dest, overwrite, cb)
})
}
function moveAcrossDevice (src, dest, overwrite, cb) {
const opts = {
overwrite,
errorOnExist: true
}
copy(src, dest, opts, err => {
if (err) return cb(err)
return remove(src, cb)
})
}
module.exports = move
@@ -0,0 +1,40 @@
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function outputFile (file, data, encoding, callback) {
if (typeof encoding === 'function') {
callback = encoding
encoding = 'utf8'
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return fs.writeFile(file, data, encoding, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
fs.writeFile(file, data, encoding, callback)
})
})
}
function outputFileSync (file, ...args) {
const dir = path.dirname(file)
if (fs.existsSync(dir)) {
return fs.writeFileSync(file, ...args)
}
mkdir.mkdirsSync(dir)
fs.writeFileSync(file, ...args)
}
module.exports = {
outputFile: u(outputFile),
outputFileSync
}
@@ -0,0 +1,12 @@
'use strict'
const u = require('universalify').fromPromise
const fs = require('../fs')
function pathExists (path) {
return fs.access(path).then(() => true).catch(() => false)
}
module.exports = {
pathExists: u(pathExists),
pathExistsSync: fs.existsSync
}
+22
View File
@@ -0,0 +1,22 @@
'use strict'
const fs = require('graceful-fs')
const u = require('universalify').fromCallback
const rimraf = require('./rimraf')
function remove (path, callback) {
// Node 14.14.0+
if (fs.rm) return fs.rm(path, { recursive: true, force: true }, callback)
rimraf(path, callback)
}
function removeSync (path) {
// Node 14.14.0+
if (fs.rmSync) return fs.rmSync(path, { recursive: true, force: true })
rimraf.sync(path)
}
module.exports = {
remove: u(remove),
removeSync
}
+302
View File
@@ -0,0 +1,302 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const assert = require('assert')
const isWindows = (process.platform === 'win32')
function defaults (options) {
const methods = [
'unlink',
'chmod',
'stat',
'lstat',
'rmdir',
'readdir'
]
methods.forEach(m => {
options[m] = options[m] || fs[m]
m = m + 'Sync'
options[m] = options[m] || fs[m]
})
options.maxBusyTries = options.maxBusyTries || 3
}
function rimraf (p, options, cb) {
let busyTries = 0
if (typeof options === 'function') {
cb = options
options = {}
}
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert.strictEqual(typeof cb, 'function', 'rimraf: callback function required')
assert(options, 'rimraf: invalid options argument provided')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
defaults(options)
rimraf_(p, options, function CB (er) {
if (er) {
if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
busyTries < options.maxBusyTries) {
busyTries++
const time = busyTries * 100
// try again, with the same exact callback as this one.
return setTimeout(() => rimraf_(p, options, CB), time)
}
// already gone
if (er.code === 'ENOENT') er = null
}
cb(er)
})
}
// Two possible strategies.
// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong. However, there
// are likely far more normal files in the world than directories. This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow. But until then, YAGNI.
function rimraf_ (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
// sunos lets the root user unlink directories, which is... weird.
// so we have to lstat here and make sure it's not a dir.
options.lstat(p, (er, st) => {
if (er && er.code === 'ENOENT') {
return cb(null)
}
// Windows can EPERM on stat. Life is suffering.
if (er && er.code === 'EPERM' && isWindows) {
return fixWinEPERM(p, options, er, cb)
}
if (st && st.isDirectory()) {
return rmdir(p, options, er, cb)
}
options.unlink(p, er => {
if (er) {
if (er.code === 'ENOENT') {
return cb(null)
}
if (er.code === 'EPERM') {
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
}
if (er.code === 'EISDIR') {
return rmdir(p, options, er, cb)
}
}
return cb(er)
})
})
}
function fixWinEPERM (p, options, er, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.chmod(p, 0o666, er2 => {
if (er2) {
cb(er2.code === 'ENOENT' ? null : er)
} else {
options.stat(p, (er3, stats) => {
if (er3) {
cb(er3.code === 'ENOENT' ? null : er)
} else if (stats.isDirectory()) {
rmdir(p, options, er, cb)
} else {
options.unlink(p, cb)
}
})
}
})
}
function fixWinEPERMSync (p, options, er) {
let stats
assert(p)
assert(options)
try {
options.chmodSync(p, 0o666)
} catch (er2) {
if (er2.code === 'ENOENT') {
return
} else {
throw er
}
}
try {
stats = options.statSync(p)
} catch (er3) {
if (er3.code === 'ENOENT') {
return
} else {
throw er
}
}
if (stats.isDirectory()) {
rmdirSync(p, options, er)
} else {
options.unlinkSync(p)
}
}
function rmdir (p, options, originalEr, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
// try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
// if we guessed wrong, and it's not a directory, then
// raise the original error.
options.rmdir(p, er => {
if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
rmkids(p, options, cb)
} else if (er && er.code === 'ENOTDIR') {
cb(originalEr)
} else {
cb(er)
}
})
}
function rmkids (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.readdir(p, (er, files) => {
if (er) return cb(er)
let n = files.length
let errState
if (n === 0) return options.rmdir(p, cb)
files.forEach(f => {
rimraf(path.join(p, f), options, er => {
if (errState) {
return
}
if (er) return cb(errState = er)
if (--n === 0) {
options.rmdir(p, cb)
}
})
})
})
}
// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
function rimrafSync (p, options) {
let st
options = options || {}
defaults(options)
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert(options, 'rimraf: missing options')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
try {
st = options.lstatSync(p)
} catch (er) {
if (er.code === 'ENOENT') {
return
}
// Windows can EPERM on stat. Life is suffering.
if (er.code === 'EPERM' && isWindows) {
fixWinEPERMSync(p, options, er)
}
}
try {
// sunos lets the root user unlink directories, which is... weird.
if (st && st.isDirectory()) {
rmdirSync(p, options, null)
} else {
options.unlinkSync(p)
}
} catch (er) {
if (er.code === 'ENOENT') {
return
} else if (er.code === 'EPERM') {
return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
} else if (er.code !== 'EISDIR') {
throw er
}
rmdirSync(p, options, er)
}
}
function rmdirSync (p, options, originalEr) {
assert(p)
assert(options)
try {
options.rmdirSync(p)
} catch (er) {
if (er.code === 'ENOTDIR') {
throw originalEr
} else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
rmkidsSync(p, options)
} else if (er.code !== 'ENOENT') {
throw er
}
}
}
function rmkidsSync (p, options) {
assert(p)
assert(options)
options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
if (isWindows) {
// We only end up here once we got ENOTEMPTY at least once, and
// at this point, we are guaranteed to have removed all the kids.
// So, we know that it won't be ENOENT or ENOTDIR or anything else.
// try really hard to delete stuff on windows, because it has a
// PROFOUNDLY annoying habit of not closing handles promptly when
// files are deleted, resulting in spurious ENOTEMPTY errors.
const startTime = Date.now()
do {
try {
const ret = options.rmdirSync(p, options)
return ret
} catch {}
} while (Date.now() - startTime < 500) // give up after 500ms
} else {
const ret = options.rmdirSync(p, options)
return ret
}
}
module.exports = rimraf
rimraf.sync = rimrafSync
+154
View File
@@ -0,0 +1,154 @@
'use strict'
const fs = require('../fs')
const path = require('path')
const util = require('util')
function getStats (src, dest, opts) {
const statFunc = opts.dereference
? (file) => fs.stat(file, { bigint: true })
: (file) => fs.lstat(file, { bigint: true })
return Promise.all([
statFunc(src),
statFunc(dest).catch(err => {
if (err.code === 'ENOENT') return null
throw err
})
]).then(([srcStat, destStat]) => ({ srcStat, destStat }))
}
function getStatsSync (src, dest, opts) {
let destStat
const statFunc = opts.dereference
? (file) => fs.statSync(file, { bigint: true })
: (file) => fs.lstatSync(file, { bigint: true })
const srcStat = statFunc(src)
try {
destStat = statFunc(dest)
} catch (err) {
if (err.code === 'ENOENT') return { srcStat, destStat: null }
throw err
}
return { srcStat, destStat }
}
function checkPaths (src, dest, funcName, opts, cb) {
util.callbackify(getStats)(src, dest, opts, (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
if (destStat) {
if (areIdentical(srcStat, destStat)) {
const srcBaseName = path.basename(src)
const destBaseName = path.basename(dest)
if (funcName === 'move' &&
srcBaseName !== destBaseName &&
srcBaseName.toLowerCase() === destBaseName.toLowerCase()) {
return cb(null, { srcStat, destStat, isChangingCase: true })
}
return cb(new Error('Source and destination must not be the same.'))
}
if (srcStat.isDirectory() && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
if (!srcStat.isDirectory() && destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`))
}
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return cb(null, { srcStat, destStat })
})
}
function checkPathsSync (src, dest, funcName, opts) {
const { srcStat, destStat } = getStatsSync(src, dest, opts)
if (destStat) {
if (areIdentical(srcStat, destStat)) {
const srcBaseName = path.basename(src)
const destBaseName = path.basename(dest)
if (funcName === 'move' &&
srcBaseName !== destBaseName &&
srcBaseName.toLowerCase() === destBaseName.toLowerCase()) {
return { srcStat, destStat, isChangingCase: true }
}
throw new Error('Source and destination must not be the same.')
}
if (srcStat.isDirectory() && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
}
if (!srcStat.isDirectory() && destStat.isDirectory()) {
throw new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`)
}
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
throw new Error(errMsg(src, dest, funcName))
}
return { srcStat, destStat }
}
// recursively check if dest parent is a subdirectory of src.
// It works for all file types including symlinks since it
// checks the src and dest inodes. It starts from the deepest
// parent and stops once it reaches the src parent or the root path.
function checkParentPaths (src, srcStat, dest, funcName, cb) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return cb()
fs.stat(destParent, { bigint: true }, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb()
return cb(err)
}
if (areIdentical(srcStat, destStat)) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return checkParentPaths(src, srcStat, destParent, funcName, cb)
})
}
function checkParentPathsSync (src, srcStat, dest, funcName) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return
let destStat
try {
destStat = fs.statSync(destParent, { bigint: true })
} catch (err) {
if (err.code === 'ENOENT') return
throw err
}
if (areIdentical(srcStat, destStat)) {
throw new Error(errMsg(src, dest, funcName))
}
return checkParentPathsSync(src, srcStat, destParent, funcName)
}
function areIdentical (srcStat, destStat) {
return destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev
}
// return true if dest is a subdir of src, otherwise false.
// It only checks the path strings.
function isSrcSubdir (src, dest) {
const srcArr = path.resolve(src).split(path.sep).filter(i => i)
const destArr = path.resolve(dest).split(path.sep).filter(i => i)
return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true)
}
function errMsg (src, dest, funcName) {
return `Cannot ${funcName} '${src}' to a subdirectory of itself, '${dest}'.`
}
module.exports = {
checkPaths,
checkPathsSync,
checkParentPaths,
checkParentPathsSync,
isSrcSubdir,
areIdentical
}
+26
View File
@@ -0,0 +1,26 @@
'use strict'
const fs = require('graceful-fs')
function utimesMillis (path, atime, mtime, callback) {
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
fs.open(path, 'r+', (err, fd) => {
if (err) return callback(err)
fs.futimes(fd, atime, mtime, futimesErr => {
fs.close(fd, closeErr => {
if (callback) callback(futimesErr || closeErr)
})
})
})
}
function utimesMillisSync (path, atime, mtime) {
const fd = fs.openSync(path, 'r+')
fs.futimesSync(fd, atime, mtime)
return fs.closeSync(fd)
}
module.exports = {
utimesMillis,
utimesMillisSync
}
+67
View File
@@ -0,0 +1,67 @@
{
"name": "fs-extra",
"version": "10.1.0",
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.",
"engines": {
"node": ">=12"
},
"homepage": "https://github.com/jprichardson/node-fs-extra",
"repository": {
"type": "git",
"url": "https://github.com/jprichardson/node-fs-extra"
},
"keywords": [
"fs",
"file",
"file system",
"copy",
"directory",
"extra",
"mkdirp",
"mkdir",
"mkdirs",
"recursive",
"json",
"read",
"write",
"extra",
"delete",
"remove",
"touch",
"create",
"text",
"output",
"move",
"promise"
],
"author": "JP Richardson <jprichardson@gmail.com>",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"devDependencies": {
"at-least-node": "^1.0.0",
"klaw": "^2.1.1",
"klaw-sync": "^3.0.2",
"minimist": "^1.1.1",
"mocha": "^5.0.5",
"nyc": "^15.0.0",
"proxyquire": "^2.0.1",
"read-dir-files": "^0.1.1",
"standard": "^16.0.3"
},
"main": "./lib/index.js",
"files": [
"lib/",
"!lib/**/__tests__/"
],
"scripts": {
"lint": "standard",
"test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha",
"test": "npm run lint && npm run unit",
"unit": "nyc node test.js"
},
"sideEffects": false
}
+15
View File
@@ -0,0 +1,15 @@
(The MIT License)
Copyright (c) 2012-2015, JP Richardson <jprichardson@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+230
View File
@@ -0,0 +1,230 @@
Node.js - jsonfile
================
Easily read/write JSON files in Node.js. _Note: this module cannot be used in the browser._
[![npm Package](https://img.shields.io/npm/v/jsonfile.svg?style=flat-square)](https://www.npmjs.org/package/jsonfile)
[![linux build status](https://img.shields.io/github/actions/workflow/status/jprichardson/node-jsonfile/ci.yml?branch=master)](https://github.com/jprichardson/node-jsonfile/actions?query=branch%3Amaster)
[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-jsonfile/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-jsonfile/branch/master)
<a href="https://github.com/feross/standard"><img src="https://cdn.rawgit.com/feross/standard/master/sticker.svg" alt="Standard JavaScript" width="100"></a>
Why?
----
Writing `JSON.stringify()` and then `fs.writeFile()` and `JSON.parse()` with `fs.readFile()` enclosed in `try/catch` blocks became annoying.
Installation
------------
npm install --save jsonfile
API
---
* [`readFile(filename, [options], callback)`](#readfilefilename-options-callback)
* [`readFileSync(filename, [options])`](#readfilesyncfilename-options)
* [`writeFile(filename, obj, [options], callback)`](#writefilefilename-obj-options-callback)
* [`writeFileSync(filename, obj, [options])`](#writefilesyncfilename-obj-options)
----
### readFile(filename, [options], callback)
`options` (`object`, default `undefined`): Pass in any [`fs.readFile`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback) options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
- `throws` (`boolean`, default: `true`). If `JSON.parse` throws an error, pass this error to the callback.
If `false`, returns `null` for the object.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
jsonfile.readFile(file, function (err, obj) {
if (err) console.error(err)
console.dir(obj)
})
```
You can also use this method with promises. The `readFile` method will return a promise if you do not pass a callback function.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
jsonfile.readFile(file)
.then(obj => console.dir(obj))
.catch(error => console.error(error))
```
----
### readFileSync(filename, [options])
`options` (`object`, default `undefined`): Pass in any [`fs.readFileSync`](https://nodejs.org/api/fs.html#fs_fs_readfilesync_path_options) options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
- `throws` (`boolean`, default: `true`). If an error is encountered reading or parsing the file, throw the error. If `false`, returns `null` for the object.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
console.dir(jsonfile.readFileSync(file))
```
----
### writeFile(filename, obj, [options], callback)
`options`: Pass in any [`fs.writeFile`](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces`, or override `EOL` string or set `finalEOL` flag as `false` to not save the file with `EOL` at the end.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj, function (err) {
if (err) console.error(err)
})
```
Or use with promises as follows:
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj)
.then(res => {
console.log('Write complete')
})
.catch(error => console.error(error))
```
**formatting with spaces:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj, { spaces: 2 }, function (err) {
if (err) console.error(err)
})
```
**overriding EOL:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj, { spaces: 2, EOL: '\r\n' }, function (err) {
if (err) console.error(err)
})
```
**disabling the EOL at the end of file:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj, { spaces: 2, finalEOL: false }, function (err) {
if (err) console.log(err)
})
```
**appending to an existing JSON file:**
You can use `fs.writeFile` option `{ flag: 'a' }` to achieve this.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/mayAlreadyExistedData.json'
const obj = { name: 'JP' }
jsonfile.writeFile(file, obj, { flag: 'a' }, function (err) {
if (err) console.error(err)
})
```
----
### writeFileSync(filename, obj, [options])
`options`: Pass in any [`fs.writeFileSync`](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options) options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces`, or override `EOL` string or set `finalEOL` flag as `false` to not save the file with `EOL` at the end.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFileSync(file, obj)
```
**formatting with spaces:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFileSync(file, obj, { spaces: 2 })
```
**overriding EOL:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFileSync(file, obj, { spaces: 2, EOL: '\r\n' })
```
**disabling the EOL at the end of file:**
```js
const jsonfile = require('jsonfile')
const file = '/tmp/data.json'
const obj = { name: 'JP' }
jsonfile.writeFileSync(file, obj, { spaces: 2, finalEOL: false })
```
**appending to an existing JSON file:**
You can use `fs.writeFileSync` option `{ flag: 'a' }` to achieve this.
```js
const jsonfile = require('jsonfile')
const file = '/tmp/mayAlreadyExistedData.json'
const obj = { name: 'JP' }
jsonfile.writeFileSync(file, obj, { flag: 'a' })
```
License
-------
(MIT License)
Copyright 2012-2016, JP Richardson <jprichardson@gmail.com>
+88
View File
@@ -0,0 +1,88 @@
let _fs
try {
_fs = require('graceful-fs')
} catch (_) {
_fs = require('fs')
}
const universalify = require('universalify')
const { stringify, stripBom } = require('./utils')
async function _readFile (file, options = {}) {
if (typeof options === 'string') {
options = { encoding: options }
}
const fs = options.fs || _fs
const shouldThrow = 'throws' in options ? options.throws : true
let data = await universalify.fromCallback(fs.readFile)(file, options)
data = stripBom(data)
let obj
try {
obj = JSON.parse(data, options ? options.reviver : null)
} catch (err) {
if (shouldThrow) {
err.message = `${file}: ${err.message}`
throw err
} else {
return null
}
}
return obj
}
const readFile = universalify.fromPromise(_readFile)
function readFileSync (file, options = {}) {
if (typeof options === 'string') {
options = { encoding: options }
}
const fs = options.fs || _fs
const shouldThrow = 'throws' in options ? options.throws : true
try {
let content = fs.readFileSync(file, options)
content = stripBom(content)
return JSON.parse(content, options.reviver)
} catch (err) {
if (shouldThrow) {
err.message = `${file}: ${err.message}`
throw err
} else {
return null
}
}
}
async function _writeFile (file, obj, options = {}) {
const fs = options.fs || _fs
const str = stringify(obj, options)
await universalify.fromCallback(fs.writeFile)(file, str, options)
}
const writeFile = universalify.fromPromise(_writeFile)
function writeFileSync (file, obj, options = {}) {
const fs = options.fs || _fs
const str = stringify(obj, options)
// not sure if fs.writeFileSync returns anything, but just in case
return fs.writeFileSync(file, str, options)
}
// NOTE: do not change this export format; required for ESM compat
// see https://github.com/jprichardson/node-jsonfile/pull/162 for details
module.exports = {
readFile,
readFileSync,
writeFile,
writeFileSync
}
+40
View File
@@ -0,0 +1,40 @@
{
"name": "jsonfile",
"version": "6.2.0",
"description": "Easily read/write JSON files.",
"repository": {
"type": "git",
"url": "git@github.com:jprichardson/node-jsonfile.git"
},
"keywords": [
"read",
"write",
"file",
"json",
"fs",
"fs-extra"
],
"author": "JP Richardson <jprichardson@gmail.com>",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
},
"devDependencies": {
"mocha": "^8.2.0",
"rimraf": "^2.4.0",
"standard": "^16.0.1"
},
"main": "index.js",
"files": [
"index.js",
"utils.js"
],
"scripts": {
"lint": "standard",
"test": "npm run lint && npm run unit",
"unit": "mocha"
}
}
+14
View File
@@ -0,0 +1,14 @@
function stringify (obj, { EOL = '\n', finalEOL = true, replacer = null, spaces } = {}) {
const EOF = finalEOL ? EOL : ''
const str = JSON.stringify(obj, replacer, spaces)
return str.replace(/\n/g, EOL) + EOF
}
function stripBom (content) {
// we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified
if (Buffer.isBuffer(content)) content = content.toString('utf8')
return content.replace(/^\uFEFF/, '')
}
module.exports = { stringify, stripBom }
+20
View File
@@ -0,0 +1,20 @@
(The MIT License)
Copyright (c) 2017, Ryan Zimmerman <opensrc@ryanzim.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+76
View File
@@ -0,0 +1,76 @@
# universalify
![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/RyanZim/universalify/ci.yml?branch=master)
![Coveralls github branch](https://img.shields.io/coveralls/github/RyanZim/universalify/master.svg)
![npm](https://img.shields.io/npm/dm/universalify.svg)
![npm](https://img.shields.io/npm/l/universalify.svg)
Make a callback- or promise-based function support both promises and callbacks.
Uses the native promise implementation.
## Installation
```bash
npm install universalify
```
## API
### `universalify.fromCallback(fn)`
Takes a callback-based function to universalify, and returns the universalified function.
Function must take a callback as the last parameter that will be called with the signature `(error, result)`. `universalify` does not support calling the callback with three or more arguments, and does not ensure that the callback is only called once.
```js
function callbackFn (n, cb) {
setTimeout(() => cb(null, n), 15)
}
const fn = universalify.fromCallback(callbackFn)
// Works with Promises:
fn('Hello World!')
.then(result => console.log(result)) // -> Hello World!
.catch(error => console.error(error))
// Works with Callbacks:
fn('Hi!', (error, result) => {
if (error) return console.error(error)
console.log(result)
// -> Hi!
})
```
### `universalify.fromPromise(fn)`
Takes a promise-based function to universalify, and returns the universalified function.
Function must return a valid JS promise. `universalify` does not ensure that a valid promise is returned.
```js
function promiseFn (n) {
return new Promise(resolve => {
setTimeout(() => resolve(n), 15)
})
}
const fn = universalify.fromPromise(promiseFn)
// Works with Promises:
fn('Hello World!')
.then(result => console.log(result)) // -> Hello World!
.catch(error => console.error(error))
// Works with Callbacks:
fn('Hi!', (error, result) => {
if (error) return console.error(error)
console.log(result)
// -> Hi!
})
```
## License
MIT
+24
View File
@@ -0,0 +1,24 @@
'use strict'
exports.fromCallback = function (fn) {
return Object.defineProperty(function (...args) {
if (typeof args[args.length - 1] === 'function') fn.apply(this, args)
else {
return new Promise((resolve, reject) => {
args.push((err, res) => (err != null) ? reject(err) : resolve(res))
fn.apply(this, args)
})
}
}, 'name', { value: fn.name })
}
exports.fromPromise = function (fn) {
return Object.defineProperty(function (...args) {
const cb = args[args.length - 1]
if (typeof cb !== 'function') return fn.apply(this, args)
else {
args.pop()
fn.apply(this, args).then(r => cb(null, r), cb)
}
}, 'name', { value: fn.name })
}
+34
View File
@@ -0,0 +1,34 @@
{
"name": "universalify",
"version": "2.0.1",
"description": "Make a callback- or promise-based function support both promises and callbacks.",
"keywords": [
"callback",
"native",
"promise"
],
"homepage": "https://github.com/RyanZim/universalify#readme",
"bugs": "https://github.com/RyanZim/universalify/issues",
"license": "MIT",
"author": "Ryan Zimmerman <opensrc@ryanzim.com>",
"files": [
"index.js"
],
"repository": {
"type": "git",
"url": "git+https://github.com/RyanZim/universalify.git"
},
"scripts": {
"test": "standard && nyc --reporter text --reporter lcovonly tape test/*.js | colortape"
},
"devDependencies": {
"colortape": "^0.1.2",
"coveralls": "^3.0.1",
"nyc": "^15.0.0",
"standard": "^14.3.1",
"tape": "^5.0.1"
},
"engines": {
"node": ">= 10.0.0"
}
}
+48
View File
@@ -0,0 +1,48 @@
import { DmgOptions, Target } from "app-builder-lib";
import { MacPackager } from "app-builder-lib/out/macPackager";
import { Arch } from "builder-util";
export interface DmgBuildConfig {
title: string;
icon?: string | null;
"badge-icon"?: string | null;
background?: string | null;
"background-color"?: string | null;
"icon-size"?: number | null;
"text-size"?: number | null;
window?: {
position?: {
x?: number;
y?: number;
};
size?: {
width?: number;
height?: number;
};
};
format?: string;
size?: string | null;
shrink?: boolean;
filesystem?: string;
"compression-level"?: number | null;
license?: string | null;
contents?: Array<{
path: string;
x: number;
y: number;
name?: string;
type?: "file" | "link" | "position";
hide_extension?: boolean;
hidden?: boolean;
}>;
}
export declare class DmgTarget extends Target {
private readonly packager;
readonly outDir: string;
readonly options: DmgOptions;
isAsyncSupported: boolean;
constructor(packager: MacPackager, outDir: string);
build(appPath: string, arch: Arch): Promise<void>;
private signDmg;
computeVolumeName(arch: Arch, custom?: string | null): string;
computeDmgOptions(appPath: string): Promise<DmgOptions>;
}
+158
View File
@@ -0,0 +1,158 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DmgTarget = void 0;
const app_builder_lib_1 = require("app-builder-lib");
const macCodeSign_1 = require("app-builder-lib/out/codeSign/macCodeSign");
const differentialUpdateInfoBuilder_1 = require("app-builder-lib/out/targets/differentialUpdateInfoBuilder");
const builder_util_1 = require("builder-util");
const filename_1 = require("builder-util/out/filename");
const os_1 = require("os");
const path = require("path");
const dmgLicense_1 = require("./dmgLicense");
const dmgUtil_1 = require("./dmgUtil");
const hdiuil_1 = require("./hdiuil");
class DmgTarget extends app_builder_lib_1.Target {
constructor(packager, outDir) {
super("dmg");
this.packager = packager;
this.outDir = outDir;
this.options = this.packager.config.dmg || Object.create(null);
this.isAsyncSupported = false;
}
async build(appPath, arch) {
const packager = this.packager;
// tslint:disable-next-line:no-invalid-template-strings
const artifactName = packager.expandArtifactNamePattern(this.options, "dmg", arch, "${productName}-" + (packager.platformSpecificBuildOptions.bundleShortVersion || "${version}") + "-${arch}.${ext}", true, packager.platformSpecificBuildOptions.defaultArch);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.emitArtifactBuildStarted({
targetPresentableName: "DMG",
file: artifactPath,
arch,
});
const volumeName = (0, filename_1.sanitizeFileName)(this.computeVolumeName(arch, this.options.title));
const specification = await this.computeDmgOptions(appPath);
if (!(await (0, dmgUtil_1.customizeDmg)({ appPath, artifactPath, volumeName, specification, packager }))) {
return;
}
if (this.options.internetEnabled && parseInt((0, os_1.release)().split(".")[0], 10) < 19) {
await (0, hdiuil_1.hdiUtil)(addLogLevel(["internet-enable"]).concat(artifactPath));
}
const licenseData = await (0, dmgLicense_1.addLicenseToDmg)(packager, artifactPath);
if (packager.packagerOptions.effectiveOptionComputed != null) {
await packager.packagerOptions.effectiveOptionComputed({ licenseData });
}
if (this.options.sign === true) {
await this.signDmg(artifactPath);
}
const safeArtifactName = packager.computeSafeArtifactName(artifactName, "dmg");
const updateInfo = this.options.writeUpdateInfo === false ? null : await (0, differentialUpdateInfoBuilder_1.createBlockmap)(artifactPath, this, packager, safeArtifactName);
await packager.info.emitArtifactBuildCompleted({
file: artifactPath,
safeArtifactName,
target: this,
arch,
packager,
isWriteUpdateInfo: updateInfo != null,
updateInfo,
});
}
async signDmg(artifactPath) {
if (!(0, macCodeSign_1.isSignAllowed)(false)) {
return;
}
const packager = this.packager;
const qualifier = packager.platformSpecificBuildOptions.identity;
// explicitly disabled if set to null
if (qualifier === null) {
// macPackager already somehow handle this situation, so, here just return
return;
}
const keychainFile = (await packager.codeSigningInfo.value).keychainFile;
const certificateType = "Developer ID Application";
let identity = await (0, macCodeSign_1.findIdentity)(certificateType, qualifier, keychainFile);
if (identity == null) {
identity = await (0, macCodeSign_1.findIdentity)("Mac Developer", qualifier, keychainFile);
if (identity == null) {
return;
}
}
const args = ["--sign", identity.hash];
if (keychainFile != null) {
args.push("--keychain", keychainFile);
}
args.push(artifactPath);
await (0, builder_util_1.exec)("codesign", args);
}
computeVolumeName(arch, custom) {
const appInfo = this.packager.appInfo;
const shortVersion = this.packager.platformSpecificBuildOptions.bundleShortVersion || appInfo.version;
const archString = (0, builder_util_1.getArchSuffix)(arch, this.packager.platformSpecificBuildOptions.defaultArch);
if (custom == null) {
return `${appInfo.productFilename} ${shortVersion}${archString}`;
}
return custom
.replace(/\${arch}/g, archString)
.replace(/\${shortVersion}/g, shortVersion)
.replace(/\${version}/g, appInfo.version)
.replace(/\${name}/g, appInfo.name)
.replace(/\${productName}/g, appInfo.productName);
}
// public to test
async computeDmgOptions(appPath) {
const packager = this.packager;
const specification = { ...this.options };
if (specification.icon == null && specification.icon !== null) {
specification.icon = await packager.getIconPath();
}
if (specification.icon != null && (0, builder_util_1.isEmptyOrSpaces)(specification.icon)) {
throw new builder_util_1.InvalidConfigurationError("dmg.icon cannot be specified as empty string");
}
const background = specification.background;
if (specification.backgroundColor != null) {
if (background != null) {
throw new builder_util_1.InvalidConfigurationError("Both dmg.backgroundColor and dmg.background are specified — please set the only one");
}
}
else if (background == null) {
specification.background = await (0, dmgUtil_1.computeBackground)(packager);
}
else {
specification.background = await packager.getResource(background);
}
if (specification.format == null) {
if (process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL != null) {
specification.format = "UDZO";
}
else if (packager.compression === "store") {
specification.format = "UDRO";
}
else {
specification.format = packager.compression === "maximum" ? "UDBZ" : "UDZO";
}
}
if (specification.contents == null) {
specification.contents = [
{
x: 130,
y: 220,
path: appPath,
type: "file",
name: `${packager.appInfo.productFilename}.app`,
},
{
x: 410,
y: 220,
type: "link",
path: "/Applications",
},
];
}
return specification;
}
}
exports.DmgTarget = DmgTarget;
function addLogLevel(args, isVerbose = process.env.DEBUG_DMG === "true") {
args.push(isVerbose ? "-verbose" : "-quiet");
return args;
}
//# sourceMappingURL=dmg.js.map
+1
View File
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
import { PlatformPackager } from "app-builder-lib";
type LicenseConfig = {
$schema: string;
body: any[];
labels: any[];
};
export declare function addLicenseToDmg(packager: PlatformPackager<any>, dmgPath: string): Promise<LicenseConfig | null>;
export {};
+47
View File
@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addLicenseToDmg = addLicenseToDmg;
const license_1 = require("app-builder-lib/out/util/license");
const builder_util_1 = require("builder-util");
const dmg_license_1 = require("dmg-license");
const fs_extra_1 = require("fs-extra");
const js_yaml_1 = require("js-yaml");
const licenseButtons_1 = require("./licenseButtons");
async function addLicenseToDmg(packager, dmgPath) {
const licenseFiles = await (0, license_1.getLicenseFiles)(packager);
if (licenseFiles.length === 0) {
return null;
}
const licenseButtonFiles = await (0, licenseButtons_1.getLicenseButtonsFile)(packager);
packager.debugLogger.add("dmg.licenseFiles", licenseFiles);
packager.debugLogger.add("dmg.licenseButtons", licenseButtonFiles);
const jsonFile = {
$schema: "https://github.com/argv-minus-one/dmg-license/raw/master/schema.json",
// defaultLang: '',
body: [],
labels: [],
};
for (const file of licenseFiles) {
jsonFile.body.push({
file: file.file,
lang: file.langWithRegion.replace("_", "-"),
});
}
for (const button of licenseButtonFiles) {
const filepath = button.file;
const label = filepath.endsWith(".yml") ? (0, js_yaml_1.load)(await (0, fs_extra_1.readFile)(filepath, "utf-8")) : await (0, fs_extra_1.readJson)(filepath);
if (label.description) {
// to support original button file format
label.message = label.description;
delete label.description;
}
jsonFile.labels.push(Object.assign({
lang: button.langWithRegion.replace("_", "-"),
}, label));
}
await (0, dmg_license_1.dmgLicenseFromJSON)(dmgPath, jsonFile, {
onNonFatalError: builder_util_1.log.warn.bind(builder_util_1.log),
});
return jsonFile;
}
//# sourceMappingURL=dmgLicense.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dmgLicense.js","sourceRoot":"","sources":["../src/dmgLicense.ts"],"names":[],"mappings":";;AAgBA,0CA+CC;AA9DD,8DAAkE;AAClE,+CAAkC;AAClC,6CAAgD;AAChD,uCAA6C;AAC7C,qCAA8B;AAC9B,qDAAwD;AAUjD,KAAK,UAAU,eAAe,CAAC,QAA+B,EAAE,OAAe;IACpF,MAAM,YAAY,GAAG,MAAM,IAAA,yBAAe,EAAC,QAAQ,CAAC,CAAA;IACpD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAM,IAAA,sCAAqB,EAAC,QAAQ,CAAC,CAAA;IAChE,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAA;IAElE,MAAM,QAAQ,GAAkB;QAC9B,OAAO,EAAE,sEAAsE;QAC/E,mBAAmB;QACnB,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,EAAE;KACX,CAAA;IAED,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;SAC5C,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAA;QAC5B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAA,cAAI,EAAC,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAA,mBAAQ,EAAC,QAAQ,CAAC,CAAA;QAC5G,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,yCAAyC;YACzC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,WAAW,CAAA;YACjC,OAAO,KAAK,CAAC,WAAW,CAAA;QAC1B,CAAC;QACD,QAAQ,CAAC,MAAM,CAAC,IAAI,CAClB,MAAM,CAAC,MAAM,CACX;YACE,IAAI,EAAE,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;SAC9C,EACD,KAAK,CACN,CACF,CAAA;IACH,CAAC;IAED,MAAM,IAAA,gCAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE;QAC1C,eAAe,EAAE,kBAAG,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAG,CAAC;KACpC,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import { PlatformPackager } from \"app-builder-lib\"\nimport { getLicenseFiles } from \"app-builder-lib/out/util/license\"\nimport { log } from \"builder-util\"\nimport { dmgLicenseFromJSON } from \"dmg-license\"\nimport { readFile, readJson } from \"fs-extra\"\nimport { load } from \"js-yaml\"\nimport { getLicenseButtonsFile } from \"./licenseButtons\"\n\n// License Specifications\n// https://github.com/argv-minus-one/dmg-license/blob/HEAD/docs/License%20Specifications.md\ntype LicenseConfig = {\n $schema: string\n body: any[]\n labels: any[]\n}\n\nexport async function addLicenseToDmg(packager: PlatformPackager<any>, dmgPath: string): Promise<LicenseConfig | null> {\n const licenseFiles = await getLicenseFiles(packager)\n if (licenseFiles.length === 0) {\n return null\n }\n\n const licenseButtonFiles = await getLicenseButtonsFile(packager)\n packager.debugLogger.add(\"dmg.licenseFiles\", licenseFiles)\n packager.debugLogger.add(\"dmg.licenseButtons\", licenseButtonFiles)\n\n const jsonFile: LicenseConfig = {\n $schema: \"https://github.com/argv-minus-one/dmg-license/raw/master/schema.json\",\n // defaultLang: '',\n body: [],\n labels: [],\n }\n\n for (const file of licenseFiles) {\n jsonFile.body.push({\n file: file.file,\n lang: file.langWithRegion.replace(\"_\", \"-\"),\n })\n }\n\n for (const button of licenseButtonFiles) {\n const filepath = button.file\n const label = filepath.endsWith(\".yml\") ? load(await readFile(filepath, \"utf-8\")) : await readJson(filepath)\n if (label.description) {\n // to support original button file format\n label.message = label.description\n delete label.description\n }\n jsonFile.labels.push(\n Object.assign(\n {\n lang: button.langWithRegion.replace(\"_\", \"-\"),\n },\n label\n )\n )\n }\n\n await dmgLicenseFromJSON(dmgPath, jsonFile, {\n onNonFatalError: log.warn.bind(log),\n })\n\n return jsonFile\n}\n"]}
+20
View File
@@ -0,0 +1,20 @@
import { DmgOptions, MacPackager, PlatformPackager } from "app-builder-lib";
import { TmpDir } from "builder-util";
export { DmgTarget } from "./dmg";
export declare function getDmgTemplatePath(): string;
export declare function attachAndExecute(dmgPath: string, readWrite: boolean, forceDetach: boolean, task: (devicePath: string) => Promise<any>): Promise<any>;
export declare function detach(name: string, alwaysForce: boolean): Promise<string | null>;
export declare function computeBackground(packager: PlatformPackager<any>): Promise<string>;
type DmgBuilderConfig = {
appPath: string;
artifactPath: string;
volumeName: string;
specification: DmgOptions;
packager: MacPackager;
};
export declare function customizeDmg({ appPath, artifactPath, volumeName, specification, packager }: DmgBuilderConfig): Promise<boolean>;
export declare function transformBackgroundFileIfNeed(file: string, tmpDir: TmpDir): Promise<string>;
export declare function getImageSizeUsingSips(background: string): Promise<{
width: number;
height: number;
}>;
+235
View File
@@ -0,0 +1,235 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DmgTarget = void 0;
exports.getDmgTemplatePath = getDmgTemplatePath;
exports.attachAndExecute = attachAndExecute;
exports.detach = detach;
exports.computeBackground = computeBackground;
exports.serializeString = serializeString;
exports.customizeDmg = customizeDmg;
exports.transformBackgroundFileIfNeed = transformBackgroundFileIfNeed;
exports.getImageSizeUsingSips = getImageSizeUsingSips;
const binDownload_1 = require("app-builder-lib/out/binDownload");
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const hdiuil_1 = require("./hdiuil");
var dmg_1 = require("./dmg");
Object.defineProperty(exports, "DmgTarget", { enumerable: true, get: function () { return dmg_1.DmgTarget; } });
const root = path.join(__dirname, "..");
function getDmgTemplatePath() {
return path.join(root, "templates");
}
async function getDmgVendorPath() {
var _a;
const customDmgbuildPath = (_a = process.env.CUSTOM_DMGBUILD_PATH) === null || _a === void 0 ? void 0 : _a.trim();
if (customDmgbuildPath) {
return path.resolve(customDmgbuildPath);
}
// https://github.com/electron-userland/electron-builder-binaries/releases/tag/dmg-builder%401.2.0
const releaseVersion = "75c8a6c";
const arch = process.arch === "arm64" ? "arm64" : "x86_64";
const config = {
"dmgbuild-bundle-arm64-75c8a6c.tar.gz": "a785f2a385c8c31996a089ef8e26361904b40c772d5ea65a36001212f1fc25e0",
"dmgbuild-bundle-x86_64-75c8a6c.tar.gz": "87b3bb72148b11451ee90ede79cc8d59305c9173b68b0f2b50a3bea51fc4a4e2",
};
const filename = `dmgbuild-bundle-${arch}-${releaseVersion}.tar.gz`;
const file = await (0, binDownload_1.downloadArtifact)({
releaseName: "dmg-builder@1.2.0",
filenameWithExt: filename,
checksums: config,
githubOrgRepo: "electron-userland/electron-builder-binaries",
});
return path.resolve(file, "dmgbuild");
}
async function attachAndExecute(dmgPath, readWrite, forceDetach, task) {
//noinspection SpellCheckingInspection
const args = ["attach", "-noverify", "-noautoopen"];
if (readWrite) {
args.push("-readwrite");
}
args.push(dmgPath);
const attachResult = await (0, hdiuil_1.hdiUtil)(args);
const deviceResult = attachResult == null ? null : /^(\/dev\/\w+)/.exec(attachResult);
const device = deviceResult == null || deviceResult.length !== 2 ? null : deviceResult[1];
if (device == null) {
throw new Error(`Cannot mount: ${attachResult}`);
}
const volumePath = await findMountPath(path.basename(device));
if (volumePath == null) {
throw new Error(`Cannot find volume mount path for device: ${device}`);
}
return await (0, builder_util_1.executeFinally)(task(volumePath), () => detach(device, forceDetach));
}
/**
* Find the mount path for a specific device from `hdiutil info`.
*/
async function findMountPath(devName, index = 1) {
const info = await (0, hdiuil_1.hdiUtil)(["info"]);
const lines = info.split("\n");
const regex = new RegExp(`^/dev/${devName}(s\\d+)?\\s+\\S+\\s+(/Volumes/.+)$`);
const matches = [];
for (const line of lines) {
const result = regex.exec(line);
if (result && result.length >= 3) {
matches.push(result[2]);
}
}
return matches.length >= index ? matches[index - 1] : null;
}
async function detach(name, alwaysForce) {
return (0, hdiuil_1.hdiUtil)(["detach", "-quiet", name]).catch(async (e) => {
if (hdiuil_1.hdiutilTransientExitCodes.has(e.code) || alwaysForce) {
// Delay then force unmount with verbose output
await new Promise(resolve => setTimeout(resolve, 3000));
return (0, hdiuil_1.hdiUtil)(["detach", "-force", name]);
}
throw e;
});
}
async function computeBackground(packager) {
const resourceList = await packager.resourceList;
if (resourceList.includes("background.tiff")) {
return path.join(packager.buildResourcesDir, "background.tiff");
}
else if (resourceList.includes("background.png")) {
return path.join(packager.buildResourcesDir, "background.png");
}
else {
return path.join(getDmgTemplatePath(), "background.tiff");
}
}
/** @internal */
function serializeString(data) {
return (' $"' +
data
.match(/.{1,32}/g)
.map(it => it.match(/.{1,4}/g).join(" "))
.join('"\n $"') +
'"');
}
async function customizeDmg({ appPath, artifactPath, volumeName, specification, packager }) {
var _a, _b, _c, _d, _e;
const isValidIconTextSize = !!specification.iconTextSize && specification.iconTextSize >= 10 && specification.iconTextSize <= 16;
const iconTextSize = isValidIconTextSize ? specification.iconTextSize : 12;
const volumePath = path.join("/Volumes", volumeName);
// https://github.com/electron-userland/electron-builder/issues/2115
const settings = {
title: path.basename(volumePath),
"icon-size": specification.iconSize,
"text-size": iconTextSize,
"compression-level": Number(process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL || "9"),
// filesystem: specification.filesystem || "HFS+",
format: specification.format,
size: specification.size,
shrink: specification.shrink,
contents: ((_a = specification.contents) === null || _a === void 0 ? void 0 : _a.map(c => ({
path: c.path || appPath, // path is required, when ommitted, appPath is used (backward compatibility
x: c.x,
y: c.y,
name: c.name,
type: c.type === "dir" ? "file" : c.type, // appdmg expects "file" for directories
// hide_extension: c.hideExtension,
}))) || [],
};
if (specification.badgeIcon) {
let badgeIcon = await packager.getResource(specification.badgeIcon);
if (badgeIcon && badgeIcon.toLowerCase().endsWith(".icon")) {
badgeIcon = await packager.generateIcnsFromIcon(badgeIcon);
}
settings["badge-icon"] = badgeIcon;
}
else {
settings.icon = await packager.getResource(specification.icon);
}
if (specification.backgroundColor != null || specification.background == null) {
settings["background-color"] = specification.backgroundColor || "#ffffff";
const window = specification.window;
if (window != null) {
settings.window = {
position: {
x: (_b = window.x) !== null && _b !== void 0 ? _b : 100,
y: (_c = window.y) !== null && _c !== void 0 ? _c : 400,
},
size: {
width: (_d = window.width) !== null && _d !== void 0 ? _d : 540,
height: (_e = window.height) !== null && _e !== void 0 ? _e : 300,
},
};
}
}
else {
settings.background = specification.background == null ? null : await transformBackgroundFileIfNeed(specification.background, packager.info.tempDirManager);
}
if (!(0, builder_util_1.isEmptyOrSpaces)(settings.background)) {
const size = await getImageSizeUsingSips(settings.background);
settings.window = { position: { x: 400, y: Math.round((1440 - size.height) / 2) }, size, ...settings.window };
}
const settingsFile = await packager.getTempFile(".json");
await (0, fs_extra_1.writeFile)(settingsFile, JSON.stringify(settings, null, 2));
const dmgbuild = await getDmgVendorPath();
await (0, builder_util_1.exec)(dmgbuild, ["-s", settingsFile, path.basename(volumePath), artifactPath], {
env: {
...process.env,
PYTHONIOENCODING: "utf8",
},
});
// effectiveOptionComputed, when present, is purely for verifying result during test execution
return (packager.packagerOptions.effectiveOptionComputed == null ||
(await attachAndExecute(artifactPath, false, true, async (volumePath) => {
var _a;
return !(await packager.packagerOptions.effectiveOptionComputed({
volumePath,
specification: {
...specification,
// clean up `contents` for test snapshot verification since app path is absolute to a unique tmp dir
contents: (_a = specification.contents) === null || _a === void 0 ? void 0 : _a.map((c) => {
var _a;
return ({
...c,
path: path.extname((_a = c.path) !== null && _a !== void 0 ? _a : "") === ".app" ? path.relative(packager.projectDir, c.path) : c.path,
});
}),
},
packager,
}));
})));
}
async function transformBackgroundFileIfNeed(file, tmpDir) {
if (path.extname(file.toLowerCase()) === ".tiff") {
return file;
}
const retinaFile = file.replace(/\.([a-z]+)$/, "@2x.$1");
if (await (0, builder_util_1.exists)(retinaFile)) {
const tiffFile = await tmpDir.getTempFile({ suffix: ".tiff" });
await (0, builder_util_1.exec)("tiffutil", ["-cathidpicheck", file, retinaFile, "-out", tiffFile]);
return tiffFile;
}
return file;
}
async function getImageSizeUsingSips(background) {
const stdout = await (0, builder_util_1.exec)("sips", ["-g", "pixelHeight", "-g", "pixelWidth", background]);
let width = 0;
let height = 0;
const re = /([a-zA-Z]+):\s*(\d+)/;
const lines = stdout.split("\n");
for (const line of lines) {
const match = re.exec(line);
if (!match) {
continue;
}
const key = match[1];
const value = parseInt(match[2], 10);
if (isNaN(value)) {
throw new Error(`Failed to parse number from line: "${line}"`);
}
if (key === "pixelWidth") {
width = value;
}
else if (key === "pixelHeight") {
height = value;
}
}
return { width, height };
}
//# sourceMappingURL=dmgUtil.js.map
+1
View File
File diff suppressed because one or more lines are too long
+18
View File
@@ -0,0 +1,18 @@
/**
* Table of hdiutil error codes that are transient and can be retried.
* These codes are typically related to resource availability or temporary issues.
*
| Code | Meaning | Why Retry? |
| ------- | -------------------------------- | ---------------------------------------------------- |
| `1` | Generic error | Can occur from brief race conditions or temp issues. |
| `16` | **Resource busy** | Volume is in use — wait and retry often works. |
| `35` | **Operation timed out** | System delay or timeout — retry after a short delay. |
| `256` | Volume in use or unmount failure | Same as 16 — usually resolves after retry. |
| `49153` | Volume not mounted yet | Attach may be too fast — retry after delay. |
| `-5341` | Disk image too small | Retry *after fixing* with a larger `-size`. |
| `-5342` | Specified size too small | Same as above — retry if size is corrected. |
*
*/
export declare const hdiutilTransientExitCodes: Set<number>;
export declare function explainHdiutilError(errorCode: number): string;
export declare function hdiUtil(args: string[]): Promise<string | null>;
+62
View File
@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hdiutilTransientExitCodes = void 0;
exports.explainHdiutilError = explainHdiutilError;
exports.hdiUtil = hdiUtil;
const builder_util_1 = require("builder-util");
/**
* Table of hdiutil error codes that are transient and can be retried.
* These codes are typically related to resource availability or temporary issues.
*
| Code | Meaning | Why Retry? |
| ------- | -------------------------------- | ---------------------------------------------------- |
| `1` | Generic error | Can occur from brief race conditions or temp issues. |
| `16` | **Resource busy** | Volume is in use — wait and retry often works. |
| `35` | **Operation timed out** | System delay or timeout — retry after a short delay. |
| `256` | Volume in use or unmount failure | Same as 16 — usually resolves after retry. |
| `49153` | Volume not mounted yet | Attach may be too fast — retry after delay. |
| `-5341` | Disk image too small | Retry *after fixing* with a larger `-size`. |
| `-5342` | Specified size too small | Same as above — retry if size is corrected. |
*
*/
exports.hdiutilTransientExitCodes = new Set([1, 16, 35, 256, 49153]);
function explainHdiutilError(errorCode) {
var _a;
const code = errorCode.toString();
const messages = {
"0": "Success: The hdiutil command completed without error.",
"1": "Generic error: The operation failed, but the reason is not specific. Check command syntax or permissions.",
"2": "No such file or directory: Check if the specified path exists.",
"6": "Disk image to resize is not currently attached or not recognized as a valid block device by macOS.",
"8": "Exec format error: The file might not be a valid disk image.",
"16": "Resource busy: The volume is in use. Try closing files or processes and retry.",
"22": "Invalid argument: One or more arguments passed to hdiutil are incorrect.",
"35": "Operation timed out: The system was too slow or unresponsive. Try again.",
"36": "I/O error: There was a problem reading or writing to disk. Check disk health.",
"100": "Image-related error: The disk image may be corrupted or invalid.",
"256": "Volume is busy or could not be unmounted. Try again after closing files.",
"49153": "Volume not mounted yet: The image may not have been fully attached.",
"-5341": "Disk image too small: hdiutil could not fit the contents. Increase the image size.",
"-5342": "Specified size too small: Disk image creation failed due to insufficient size.",
};
return (_a = messages[code]) !== null && _a !== void 0 ? _a : `Unknown error (code ${code}): Refer to hdiutil documentation or run with -verbose for details by rerunning with env var DEBUG_DEMB=true.`;
}
const shouldRetry = (args) => (error) => {
var _a, _b, _c;
const code = (_a = error.code) !== null && _a !== void 0 ? _a : -1;
const stderr = ((_b = error.stderr) === null || _b === void 0 ? void 0 : _b.toString()) || "";
const stdout = ((_c = error.stdout) === null || _c === void 0 ? void 0 : _c.toString()) || "";
const output = `${stdout} ${stderr}`.trim();
const willRetry = exports.hdiutilTransientExitCodes.has(code.toString());
builder_util_1.log.warn({ willRetry, args, code, output }, `hdiutil error: ${explainHdiutilError(code)}`);
return willRetry;
};
async function hdiUtil(args) {
return (0, builder_util_1.retry)(() => (0, builder_util_1.exec)("hdiutil", args), {
retries: 5,
interval: 5000,
backoff: 2000,
shouldRetry: shouldRetry(args),
});
}
//# sourceMappingURL=hdiuil.js.map
+1
View File
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
import { PlatformPackager } from "app-builder-lib";
export declare function getLicenseButtonsFile(packager: PlatformPackager<any>): Promise<Array<LicenseButtonsFile>>;
export interface LicenseButtonsFile {
file: string;
lang: string;
langWithRegion: string;
langName: string;
}
export declare function getLicenseButtons(licenseButtonFiles: Array<LicenseButtonsFile>, langWithRegion: string, id: number, name: string): Promise<string>;
+141
View File
@@ -0,0 +1,141 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLicenseButtonsFile = getLicenseButtonsFile;
exports.getLicenseButtons = getLicenseButtons;
const license_1 = require("app-builder-lib/out/util/license");
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const iconv = require("iconv-lite");
const js_yaml_1 = require("js-yaml");
const dmgUtil_1 = require("./dmgUtil");
const licenseDefaultButtons_1 = require("./licenseDefaultButtons");
async function getLicenseButtonsFile(packager) {
return (0, license_1.getLicenseAssets)((await packager.resourceList).filter(it => {
const name = it.toLowerCase();
// noinspection SpellCheckingInspection
return name.startsWith("licensebuttons_") && (name.endsWith(".json") || name.endsWith(".yml"));
}), packager);
}
async function getLicenseButtons(licenseButtonFiles, langWithRegion, id, name) {
let data = (0, licenseDefaultButtons_1.getDefaultButtons)(langWithRegion, id, name);
for (const item of licenseButtonFiles) {
if (item.langWithRegion !== langWithRegion) {
continue;
}
try {
const fileData = (0, js_yaml_1.load)(await (0, fs_extra_1.readFile)(item.file, "utf-8"));
const buttonsStr = labelToHex(fileData.lang, item.lang, item.langWithRegion) +
labelToHex(fileData.agree, item.lang, item.langWithRegion) +
labelToHex(fileData.disagree, item.lang, item.langWithRegion) +
labelToHex(fileData.print, item.lang, item.langWithRegion) +
labelToHex(fileData.save, item.lang, item.langWithRegion) +
labelToHex(fileData.description, item.lang, item.langWithRegion);
data = `data 'STR#' (${id}, "${name}") {\n`;
data += (0, dmgUtil_1.serializeString)("0006" + buttonsStr);
data += `\n};`;
builder_util_1.log.debug({ lang: item.langName, data }, `overwriting license buttons`);
return data;
}
catch (e) {
builder_util_1.log.debug({ error: e }, "cannot overwrite license buttons");
return data;
}
}
return data;
}
function labelToHex(label, lang, langWithRegion) {
const lbl = hexEncode(label, lang, langWithRegion).toString().toUpperCase();
const len = numberToHex(lbl.length / 2);
return len + lbl;
}
function numberToHex(nb) {
return ("0" + nb.toString(16)).slice(-2);
}
function hexEncode(str, lang, langWithRegion) {
const macCodePages = getMacCodePage(lang, langWithRegion);
let result = "";
for (let i = 0; i < str.length; i++) {
try {
let hex = getMacHexCode(str, i, macCodePages);
if (hex === undefined) {
hex = "3F"; //?
}
result += hex;
}
catch (e) {
builder_util_1.log.debug({ error: e, char: str[i] }, "cannot convert");
result += "3F"; //?
}
}
return result;
}
function getMacCodePage(lang, langWithRegion) {
switch (lang) {
case "ja": //japanese
return ["euc-jp"]; //Apple Japanese
case "zh": //chinese
if (langWithRegion === "zh_CN") {
return ["gb2312"]; //Apple Simplified Chinese (GB 2312)
}
return ["big5"]; //Apple Traditional Chinese (Big5)
case "ko": //korean
return ["euc-kr"]; //Apple Korean
case "ar": //arabic
case "ur": //urdu
return ["macarabic"]; //Apple Arabic
case "he": //hebrew
return ["machebrew"]; //Apple Hebrew
case "el": //greek
case "elc": //greek
return ["macgreek"]; //Apple Greek
case "ru": //russian
case "be": //belarussian
case "sr": //serbian
case "bg": //bulgarian
case "uz": //uzbek
return ["maccyrillic"]; //Apple Macintosh Cyrillic
case "ro": //romanian
return ["macromania"]; //Apple Romanian
case "uk": //ukrainian
return ["macukraine"]; //Apple Ukrainian
case "th": //thai
return ["macthai"]; //Apple Thai
case "et": //estonian
case "lt": //lithuanian
case "lv": //latvian
case "pl": //polish
case "hu": //hungarian
case "cs": //czech
case "sk": //slovak
return ["maccenteuro"]; //Apple Macintosh Central Europe
case "is": //icelandic
case "fo": //faroese
return ["maciceland"]; //Apple Icelandic
case "tr": //turkish
return ["macturkish"]; //Apple Turkish
case "hr": //croatian
case "sl": //slovenian
return ["maccroatian"]; //Apple Croatian
default:
return ["macroman"]; //Apple Macintosh Roman
}
}
function getMacHexCode(str, i, macCodePages) {
const code = str.charCodeAt(i);
if (code < 128) {
return code.toString(16);
}
else if (code < 256) {
return iconv.encode(str[i], "macroman").toString("hex");
}
else {
for (let i = 0; i < macCodePages.length; i++) {
const result = iconv.encode(str[i], macCodePages[i]).toString("hex");
if (result !== undefined) {
return result;
}
}
}
return code;
}
//# sourceMappingURL=licenseButtons.js.map
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
export declare function getDefaultButtons(langWithRegion: string, id: number, name: string): string;
+260
View File
@@ -0,0 +1,260 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefaultButtons = getDefaultButtons;
function getDefaultButtons(langWithRegion, id, name) {
switch (langWithRegion) {
case "de_DE":
return `data 'STR#' (${id}, "${name}") {
$"0006 0744 6575 7473 6368 0B41 6B7A 6570"
$"7469 6572 656E 0841 626C 6568 6E65 6E07"
$"4472 7563 6B65 6E0A 5369 6368 6572 6E2E"
$"2E2E E74B 6C69 636B 656E 2053 6965 2069"
$"6E20 D241 6B7A 6570 7469 6572 656E D32C"
$"2077 656E 6E20 5369 6520 6D69 7420 6465"
$"6E20 4265 7374 696D 6D75 6E67 656E 2064"
$"6573 2053 6F66 7477 6172 652D 4C69 7A65"
$"6E7A 7665 7274 7261 6773 2065 696E 7665"
$"7273 7461 6E64 656E 2073 696E 642E 2046"
$"616C 6C73 206E 6963 6874 2C20 6269 7474"
$"6520 D241 626C 6568 6E65 6ED3 2061 6E6B"
$"6C69 636B 656E 2E20 5369 6520 6B9A 6E6E"
$"656E 2064 6965 2053 6F66 7477 6172 6520"
$"6E75 7220 696E 7374 616C 6C69 6572 656E"
$"2C20 7765 6E6E 2053 6965 20D2 416B 7A65"
$"7074 6965 7265 6ED3 2061 6E67 656B 6C69"
$"636B 7420 6861 6265 6E2E"
};`;
case "fr_FR":
return `data 'STR#' (${id}, "${name}") {
$"0006 0846 7261 6E8D 6169 7308 4163 6365"
$"7074 6572 0752 6566 7573 6572 0849 6D70"
$"7269 6D65 720E 456E 7265 6769 7374 7265"
$"722E 2E2E BA53 6920 766F 7573 2061 6363"
$"6570 7465 7A20 6C65 7320 7465 726D 6573"
$"2064 6520 6C61 2070 728E 7365 6E74 6520"
$"6C69 6365 6E63 652C 2063 6C69 7175 657A"
$"2073 7572 2022 4163 6365 7074 6572 2220"
$"6166 696E 2064 2769 6E73 7461 6C6C 6572"
$"206C 6520 6C6F 6769 6369 656C 2E20 5369"
$"2076 6F75 7320 6E27 9074 6573 2070 6173"
$"2064 2761 6363 6F72 6420 6176 6563 206C"
$"6573 2074 6572 6D65 7320 6465 206C 6120"
$"6C69 6365 6E63 652C 2063 6C69 7175 657A"
$"2073 7572 2022 5265 6675 7365 7222 2E"
};`;
case "fr_CA":
return `data 'STR#' (${id}, "${name}") {
$"0006 1146 7261 6E8D 6169 7320 6361 6E61"
$"6469 656E 0841 6363 6570 7465 7207 5265"
$"6675 7365 7208 496D 7072 696D 6572 0E45"
$"6E72 6567 6973 7472 6572 2E2E 2EBA 5369"
$"2076 6F75 7320 6163 6365 7074 657A 206C"
$"6573 2074 6572 6D65 7320 6465 206C 6120"
$"7072 8E73 656E 7465 206C 6963 656E 6365"
$"2C20 636C 6971 7565 7A20 7375 7220 2241"
$"6363 6570 7465 7222 2061 6669 6E20 6427"
$"696E 7374 616C 6C65 7220 6C65 206C 6F67"
$"6963 6965 6C2E 2053 6920 766F 7573 206E"
$"2790 7465 7320 7061 7320 6427 6163 636F"
$"7264 2061 7665 6320 6C65 7320 7465 726D"
$"6573 2064 6520 6C61 206C 6963 656E 6365"
$"2C20 636C 6971 7565 7A20 7375 7220 2252"
$"6566 7573 6572 222E"
};`;
case "es_ES":
return `data 'STR#' (${id}, "${name}") {
$"0006 0745 7370 6196 6F6C 0741 6365 7074"
$"6172 0A4E 6F20 6163 6570 7461 7208 496D"
$"7072 696D 6972 0A47 7561 7264 6172 2E2E"
$"2EC0 5369 2065 7374 8720 6465 2061 6375"
$"6572 646F 2063 6F6E 206C 6F73 2074 8E72"
$"6D69 6E6F 7320 6465 2065 7374 6120 6C69"
$"6365 6E63 6961 2C20 7075 6C73 6520 2241"
$"6365 7074 6172 2220 7061 7261 2069 6E73"
$"7461 6C61 7220 656C 2073 6F66 7477 6172"
$"652E 2045 6E20 656C 2073 7570 7565 7374"
$"6F20 6465 2071 7565 206E 6F20 6573 748E"
$"2064 6520 6163 7565 7264 6F20 636F 6E20"
$"6C6F 7320 748E 726D 696E 6F73 2064 6520"
$"6573 7461 206C 6963 656E 6369 612C 2070"
$"756C 7365 2022 4E6F 2061 6365 7074 6172"
$"2E22"
};`;
case "it_IT":
return `data 'STR#' (${id}, "${name}") {
$"0006 0849 7461 6C69 616E 6F07 4163 6365"
$"7474 6F07 5269 6669 7574 6F06 5374 616D"
$"7061 0B52 6567 6973 7472 612E 2E2E 7F53"
$"6520 6163 6365 7474 6920 6C65 2063 6F6E"
$"6469 7A69 6F6E 6920 6469 2071 7565 7374"
$"6120 6C69 6365 6E7A 612C 2066 6169 2063"
$"6C69 6320 7375 2022 4163 6365 7474 6F22"
$"2070 6572 2069 6E73 7461 6C6C 6172 6520"
$"696C 2073 6F66 7477 6172 652E 2041 6C74"
$"7269 6D65 6E74 6920 6661 6920 636C 6963"
$"2073 7520 2252 6966 6975 746F 222E"
};`;
case "ja_JP":
return `data 'STR#' (${id}, "${name}") {
$"0006 084A 6170 616E 6573 650A 93AF 88D3"
$"82B5 82DC 82B7 0C93 AF88 D382 B582 DC82"
$"B982 F108 88F3 8DFC 82B7 82E9 0795 DB91"
$"B62E 2E2E B496 7B83 5C83 7483 6783 4583"
$"4783 418E 6797 708B 9691 F88C 5F96 F182"
$"CC8F F08C 8F82 C993 AF88 D382 B382 EA82"
$"E98F EA8D 8782 C982 CD81 4183 5C83 7483"
$"6783 4583 4783 4182 F083 4383 9383 5883"
$"6781 5B83 8B82 B782 E982 BD82 DF82 C981"
$"7593 AF88 D382 B582 DC82 B781 7682 F089"
$"9F82 B582 C482 AD82 BE82 B382 A281 4281"
$"4093 AF88 D382 B382 EA82 C882 A28F EA8D"
$"8782 C982 CD81 4181 7593 AF88 D382 B582"
$"DC82 B982 F181 7682 F089 9F82 B582 C482"
$"AD82 BE82 B382 A281 42"
};`;
case "nl_NL":
return `data 'STR#' (${id}, "${name}") {
$"0006 0A4E 6564 6572 6C61 6E64 7302 4A61"
$"034E 6565 0550 7269 6E74 0942 6577 6161"
$"722E 2E2E A449 6E64 6965 6E20 7520 616B"
$"6B6F 6F72 6420 6761 6174 206D 6574 2064"
$"6520 766F 6F72 7761 6172 6465 6E20 7661"
$"6E20 6465 7A65 206C 6963 656E 7469 652C"
$"206B 756E 7420 7520 6F70 2027 4A61 2720"
$"6B6C 696B 6B65 6E20 6F6D 2064 6520 7072"
$"6F67 7261 6D6D 6174 7575 7220 7465 2069"
$"6E73 7461 6C6C 6572 656E 2E20 496E 6469"
$"656E 2075 206E 6965 7420 616B 6B6F 6F72"
$"6420 6761 6174 2C20 6B6C 696B 7420 7520"
$"6F70 2027 4E65 6527 2E"
};`;
case "sv_SE":
return `data 'STR#' (${id}, "${name}") {
$"0006 0653 7665 6E73 6B08 476F 646B 8A6E"
$"6E73 0641 7662 9A6A 7308 536B 7269 7620"
$"7574 0853 7061 7261 2E2E 2E93 4F6D 2044"
$"7520 676F 646B 8A6E 6E65 7220 6C69 6365"
$"6E73 7669 6C6C 6B6F 7265 6E20 6B6C 6963"
$"6B61 2070 8C20 2247 6F64 6B8A 6E6E 7322"
$"2066 9A72 2061 7474 2069 6E73 7461 6C6C"
$"6572 6120 7072 6F67 7261 6D70 726F 6475"
$"6B74 656E 2E20 4F6D 2044 7520 696E 7465"
$"2067 6F64 6B8A 6E6E 6572 206C 6963 656E"
$"7376 696C 6C6B 6F72 656E 2C20 6B6C 6963"
$"6B61 2070 8C20 2241 7662 9A6A 7322 2E"
};`;
case "br_FR":
return `data 'STR#' (${id}, "${name}") {
$"0006 1150 6F72 7475 6775 9073 2C20 4272"
$"6173 696C 0943 6F6E 636F 7264 6172 0944"
$"6973 636F 7264 6172 0849 6D70 7269 6D69"
$"7209 5361 6C76 6172 2E2E 2E8C 5365 2065"
$"7374 8720 6465 2061 636F 7264 6F20 636F"
$"6D20 6F73 2074 6572 6D6F 7320 6465 7374"
$"6120 6C69 6365 6E8D 612C 2070 7265 7373"
$"696F 6E65 2022 436F 6E63 6F72 6461 7222"
$"2070 6172 6120 696E 7374 616C 6172 206F"
$"2073 6F66 7477 6172 652E 2053 6520 6E8B"
$"6F20 6573 7487 2064 6520 6163 6F72 646F"
$"2C20 7072 6573 7369 6F6E 6520 2244 6973"
$"636F 7264 6172 222E"
};`;
case "zh_TW":
return `data 'STR#' (${id}, "${name}") {
$"0006 1354 7261 6469 7469 6F6E 616C 2043"
$"6869 6E65 7365 04A6 50B7 4E06 A4A3 A650"
$"B74E 04A6 43A6 4C06 C078 A673 A14B 50A6"
$"70AA 47B1 7AA6 50B7 4EA5 BBB3 5CA5 69C3"
$"D2B8 CCAA BAB1 F8B4 DAA1 41BD D0AB F6A1"
$"A7A6 50B7 4EA1 A8A5 48A6 77B8 CBB3 6EC5"
$"E9A1 43A6 70AA 47A4 A3A6 50B7 4EA1 41BD"
$"D0AB F6A1 A7A4 A3A6 50B7 4EA1 A8A1 43"
};`;
case "zh_CN":
return `data 'STR#' (${id}, "${name}") {
$"0006 1253 696D 706C 6966 6965 6420 4368"
$"696E 6573 6504 CDAC D2E2 06B2 BBCD ACD2"
$"E204 B4F2 D3A1 06B4 E6B4 A2A1 AD54 C8E7"
$"B9FB C4FA CDAC D2E2 B1BE D0ED BFC9 D0AD"
$"D2E9 B5C4 CCF5 BFEE A3AC C7EB B0B4 A1B0"
$"CDAC D2E2 A1B1 C0B4 B0B2 D7B0 B4CB C8ED"
$"BCFE A1A3 C8E7 B9FB C4FA B2BB CDAC D2E2"
$"A3AC C7EB B0B4 A1B0 B2BB CDAC D2E2 A1B1"
$"A1A3"
};`;
case "da_DK":
return `data 'STR#' (${id}, "${name}") {
$"0006 0544 616E 736B 0445 6E69 6705 5565"
$"6E69 6707 5564 736B 7269 760A 4172 6B69"
$"7665 722E 2E2E 9848 7669 7320 6475 2061"
$"6363 6570 7465 7265 7220 6265 7469 6E67"
$"656C 7365 726E 6520 6920 6C69 6365 6E73"
$"6166 7461 6C65 6E2C 2073 6B61 6C20 6475"
$"206B 6C69 6B6B 6520 708C 20D2 456E 6967"
$"D320 666F 7220 6174 2069 6E73 7461 6C6C"
$"6572 6520 736F 6674 7761 7265 6E2E 204B"
$"6C69 6B20 708C 20D2 5565 6E69 67D3 2066"
$"6F72 2061 7420 616E 6E75 6C6C 6572 6520"
$"696E 7374 616C 6C65 7269 6E67 656E 2E"
};`;
case "fi_FI":
return `data 'STR#' (${id}, "${name}") {
$"0006 0553 756F 6D69 0848 7976 8A6B 7379"
$"6E0A 456E 2068 7976 8A6B 7379 0754 756C"
$"6F73 7461 0954 616C 6C65 6E6E 61C9 6F48"
$"7976 8A6B 7379 206C 6973 656E 7373 6973"
$"6F70 696D 756B 7365 6E20 6568 646F 7420"
$"6F73 6F69 7474 616D 616C 6C61 20D5 4879"
$"768A 6B73 79D5 2E20 4A6F 7320 6574 2068"
$"7976 8A6B 7379 2073 6F70 696D 756B 7365"
$"6E20 6568 746F 6A61 2C20 6F73 6F69 7461"
$"20D5 456E 2068 7976 8A6B 7379 D52E"
};`;
case "ko_KR":
return `data 'STR#' (${id}, "${name}") {
$"0006 064B 6F72 6561 6E04 B5BF C0C7 09B5"
$"BFC0 C720 BEC8 C7D4 06C7 C1B8 B0C6 AE07"
$"C0FA C0E5 2E2E 2E7E BBE7 BFEB 20B0 E8BE"
$"E0BC ADC0 C720 B3BB BFEB BFA1 20B5 BFC0"
$"C7C7 CFB8 E92C 2022 B5BF C0C7 2220 B4DC"
$"C3DF B8A6 20B4 ADB7 AF20 BCD2 C7C1 C6AE"
$"BFFE BEEE B8A6 20BC B3C4 A1C7 CFBD CABD"
$"C3BF C02E 20B5 BFC0 C7C7 CFC1 F620 BECA"
$"B4C2 B4D9 B8E9 2C20 22B5 BFC0 C720 BEC8"
$"C7D4 2220 B4DC C3DF B8A6 20B4 A9B8 A3BD"
$"CABD C3BF C02E"
};`;
case "nb_NO":
return `data 'STR#' (${id}, "${name}") {
$"0006 054E 6F72 736B 0445 6E69 6709 496B"
$"6B65 2065 6E69 6708 536B 7269 7620 7574"
$"0A41 726B 6976 6572 2E2E 2EA3 4876 6973"
$"2044 6520 6572 2065 6E69 6720 6920 6265"
$"7374 656D 6D65 6C73 656E 6520 6920 6465"
$"6E6E 6520 6C69 7365 6E73 6176 7461 6C65"
$"6E2C 206B 6C69 6B6B 6572 2044 6520 708C"
$"2022 456E 6967 222D 6B6E 6170 7065 6E20"
$"666F 7220 8C20 696E 7374 616C 6C65 7265"
$"2070 726F 6772 616D 7661 7265 6E2E 2048"
$"7669 7320 4465 2069 6B6B 6520 6572 2065"
$"6E69 672C 206B 6C69 6B6B 6572 2044 6520"
$"708C 2022 496B 6B65 2065 6E69 6722 2E"
};`;
default:
// en_US
return `data 'STR#' (${id}, "${name}") {
$"0006 0745 6E67 6C69 7368 0541 6772 6565"
$"0844 6973 6167 7265 6505 5072 696E 7407"
$"5361 7665 2E2E 2E7A 4966 2079 6F75 2061"
$"6772 6565 2077 6974 6820 7468 6520 7465"
$"726D 7320 6F66 2074 6869 7320 6C69 6365"
$"6E73 652C 2070 7265 7373 20D2 4167 7265"
$"65D3 2074 6F20 696E 7374 616C 6C20 7468"
$"6520 736F 6674 7761 7265 2E20 4966 2079"
$"6F75 2064 6F20 6E6F 7420 6167 7265 652C"
$"2070 7265 7373 20D2 4469 7361 6772 6565"
$"D32E"
};`;
}
}
//# sourceMappingURL=licenseDefaultButtons.js.map
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
{
"name": "dmg-builder",
"version": "26.8.1",
"main": "out/dmgUtil.js",
"author": "Vladimir Krivosheev",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/electron-userland/electron-builder.git",
"directory": "packages/dmg-builder"
},
"bugs": "https://github.com/electron-userland/electron-builder/issues",
"homepage": "https://github.com/electron-userland/electron-builder",
"files": [
"out",
"templates"
],
"dependencies": {
"fs-extra": "^10.1.0",
"iconv-lite": "^0.6.2",
"js-yaml": "^4.1.0",
"app-builder-lib": "26.8.1",
"builder-util": "26.8.1"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
},
"devDependencies": {
"@types/fs-extra": "9.0.13",
"@types/js-yaml": "4.0.3",
"temp-file": "3.4.0"
},
"typings": "./out/dmg.d.ts"
}
+3
View File
@@ -0,0 +1,3 @@
# dmg-builder
Utilities to build DMG. Used by [electron-builder](https://github.com/electron-userland/electron-builder).
Binary file not shown.