Initial Commit

This commit is contained in:
2026-01-15 21:52:12 +01:00
committed by erik
parent 3ed42cdeb6
commit 5a70f775f1
6702 changed files with 1389544 additions and 0 deletions

BIN
node_modules/app-builder-lib/certs/root_certs.keychain generated vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,2 @@
tidelift: "npm/brace-expansion"
patreon: juliangruber

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Julian Gruber <julian@juliangruber.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.

View File

@@ -0,0 +1,135 @@
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## Sponsors
This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.

View File

@@ -0,0 +1,203 @@
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
// but a{},b}c will be expanded to [a}c,abc].
// One could argue that this is a bug in Bash, but since the goal of
// this module is to match Bash's rules, we escape a leading {}
if (str.substr(0, 2) === '{}') {
str = '\\{\\}' + str.substr(2);
}
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m) return [str];
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
if (/\$$/.test(m.pre)) {
for (var k = 0; k < post.length; k++) {
var expansion = pre+ '{' + m.body + '}' + post[k];
expansions.push(expansion);
}
} else {
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,(?!,).*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = [];
for (var j = 0; j < n.length; j++) {
N.push.apply(N, expand(n[j], false));
}
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
}
return expansions;
}

View File

@@ -0,0 +1,49 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "2.0.2",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh",
"bench": "matcha test/perf/bench.js"
},
"dependencies": {
"balanced-match": "^1.0.0"
},
"devDependencies": {
"@c4312/matcha": "^1.3.1",
"tape": "^4.6.0"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"publishConfig": {
"tag": "2.x"
}
}

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.

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

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

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

View File

@@ -0,0 +1,7 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
copy: u(require('./copy')),
copySync: require('./copy-sync')
}

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
}

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
}

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
}

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
}

View File

@@ -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
}

View File

@@ -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
}

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
}

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'
)
}

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')
}

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

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
}

View File

@@ -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

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

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
}

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
})
}

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
}
}
}

View File

@@ -0,0 +1,7 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
move: u(require('./move')),
moveSync: require('./move-sync')
}

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

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

View File

@@ -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
}

View File

@@ -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
}

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
}

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

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
}

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
}

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
}

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.

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>

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
}

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"
}
}

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 }

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,259 @@
# minimatch
A minimal matching utility.
[![Build Status](https://travis-ci.org/isaacs/minimatch.svg?branch=master)](http://travis-ci.org/isaacs/minimatch)
This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp`
objects.
## Usage
```javascript
var minimatch = require("minimatch")
minimatch("bar.foo", "*.foo") // true!
minimatch("bar.foo", "*.bar") // false!
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
```
## Features
Supports these glob features:
* Brace Expansion
* Extended glob matching
* "Globstar" `**` matching
See:
* `man sh`
* `man bash`
* `man 3 fnmatch`
* `man 5 gitignore`
## Windows
**Please only use forward-slashes in glob expressions.**
Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes in patterns
will always be interpreted as escape characters, not path separators.
Note that `\` or `/` _will_ be interpreted as path separators in paths on
Windows, and will match against `/` in glob expressions.
So just always use `/` in patterns.
## Minimatch Class
Create a minimatch object by instantiating the `minimatch.Minimatch` class.
```javascript
var Minimatch = require("minimatch").Minimatch
var mm = new Minimatch(pattern, options)
```
### Properties
* `pattern` The original pattern the minimatch object represents.
* `options` The options supplied to the constructor.
* `set` A 2-dimensional array of regexp or string expressions.
Each row in the
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
`{a,b/c}/d` would expand to a set of patterns like:
[ [ a, d ]
, [ b, c, d ] ]
If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.
* `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
* `negate` True if the pattern is negated.
* `comment` True if the pattern is a comment.
* `empty` True if the pattern is `""`.
### Methods
* `makeRe` Generate the `regexp` member if necessary, and return it.
Will return `false` if the pattern is invalid.
* `match(fname)` Return true if the filename matches the pattern, or
false otherwise.
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
method is mainly for internal use, but is exposed so that it can be
used by a glob-walker that needs to avoid excessive filesystem calls.
All other methods are internal, and will be called as necessary.
### minimatch(path, pattern, options)
Main export. Tests a path against the pattern using the options.
```javascript
var isJS = minimatch(file, "*.js", { matchBase: true })
```
### minimatch.filter(pattern, options)
Returns a function that tests its
supplied argument, suitable for use with `Array.filter`. Example:
```javascript
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
```
### minimatch.match(list, pattern, options)
Match against the list of
files, in the style of fnmatch or glob. If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.
```javascript
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true})
```
### minimatch.makeRe(pattern, options)
Make a regular expression object from the pattern.
## Options
All options are `false` by default.
### debug
Dump a ton of stuff to stderr.
### nobrace
Do not expand `{a,b}` and `{1..3}` brace sets.
### noglobstar
Disable `**` matching against multiple folder names.
### dot
Allow patterns to match filenames starting with a period, even if
the pattern does not explicitly have a period in that spot.
Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
is set.
### noext
Disable "extglob" style patterns like `+(a|b)`.
### nocase
Perform a case-insensitive match.
### nonull
When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set. When not set, an empty list
is returned if there are no matches.
### matchBase
If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes. For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
### nocomment
Suppress the behavior of treating `#` at the start of a pattern as a
comment.
### nonegate
Suppress the behavior of treating a leading `!` character as negation.
### flipNegate
Returns from negate expressions the same as if they were not negated.
(Ie, true on a hit, false on a miss.)
### partial
Compare a partial path to a pattern. As long as the parts of the path that
are present are not contradicted by the pattern, it will be treated as a
match. This is useful in applications where you're walking through a
folder structure, and don't yet have the full path, but want to ensure that
you do not walk down paths that can never be a match.
For example,
```js
minimatch('/a/b', '/a/*/c/d', { partial: true }) // true, might be /a/b/c/d
minimatch('/a/b', '/**/d', { partial: true }) // true, might be /a/b/.../d
minimatch('/x/y/z', '/a/**/z', { partial: true }) // false, because x !== a
```
### windowsPathsNoEscape
Use `\\` as a path separator _only_, and _never_ as an escape
character. If set, all `\\` characters are replaced with `/` in
the pattern. Note that this makes it **impossible** to match
against paths containing literal glob pattern characters, but
allows matching with patterns constructed using `path.join()` and
`path.resolve()` on Windows platforms, mimicking the (buggy!)
behavior of earlier versions on Windows. Please use with
caution, and be mindful of [the caveat about Windows
paths](#windows).
For legacy reasons, this is also set if
`options.allowWindowsEscape` is set to the exact value `false`.
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between minimatch and other
implementations, and are intentional.
If the pattern starts with a `!` character, then it is negated. Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.
If a pattern starts with `#`, then it is treated as a comment, and
will not match anything. Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
Note that `fnmatch(3)` in libc is an extremely naive string comparison
matcher, which does not do anything special for slashes. This library is
designed to be used in glob searching and file walkers, and so it does do
special things with `/`. Thus, `foo*` will not match `foo/bar` in this
library, even though it would in `fnmatch(3)`.

View File

@@ -0,0 +1,4 @@
const isWindows = typeof process === 'object' &&
process &&
process.platform === 'win32'
module.exports = isWindows ? { sep: '\\' } : { sep: '/' }

View File

@@ -0,0 +1,944 @@
const minimatch = module.exports = (p, pattern, options = {}) => {
assertValidPattern(pattern)
// shortcut: comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
return false
}
return new Minimatch(pattern, options).match(p)
}
module.exports = minimatch
const path = require('./lib/path.js')
minimatch.sep = path.sep
const GLOBSTAR = Symbol('globstar **')
minimatch.GLOBSTAR = GLOBSTAR
const expand = require('brace-expansion')
const plTypes = {
'!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
'?': { open: '(?:', close: ')?' },
'+': { open: '(?:', close: ')+' },
'*': { open: '(?:', close: ')*' },
'@': { open: '(?:', close: ')' }
}
// any single thing other than /
// don't need to escape / when using new RegExp()
const qmark = '[^/]'
// * => any number of characters
const star = qmark + '*?'
// ** when dots are allowed. Anything goes, except .. and .
// not (^ or / followed by one or two dots followed by $ or /),
// followed by anything, any number of times.
const twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
// not a ^ or / followed by a dot,
// followed by anything, any number of times.
const twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
// "abc" -> { a:true, b:true, c:true }
const charSet = s => s.split('').reduce((set, c) => {
set[c] = true
return set
}, {})
// characters that need to be escaped in RegExp.
const reSpecials = charSet('().*{}+?[]^$\\!')
// characters that indicate we have to add the pattern start
const addPatternStartSet = charSet('[.(')
// normalizes slashes.
const slashSplit = /\/+/
minimatch.filter = (pattern, options = {}) =>
(p, i, list) => minimatch(p, pattern, options)
const ext = (a, b = {}) => {
const t = {}
Object.keys(a).forEach(k => t[k] = a[k])
Object.keys(b).forEach(k => t[k] = b[k])
return t
}
minimatch.defaults = def => {
if (!def || typeof def !== 'object' || !Object.keys(def).length) {
return minimatch
}
const orig = minimatch
const m = (p, pattern, options) => orig(p, pattern, ext(def, options))
m.Minimatch = class Minimatch extends orig.Minimatch {
constructor (pattern, options) {
super(pattern, ext(def, options))
}
}
m.Minimatch.defaults = options => orig.defaults(ext(def, options)).Minimatch
m.filter = (pattern, options) => orig.filter(pattern, ext(def, options))
m.defaults = options => orig.defaults(ext(def, options))
m.makeRe = (pattern, options) => orig.makeRe(pattern, ext(def, options))
m.braceExpand = (pattern, options) => orig.braceExpand(pattern, ext(def, options))
m.match = (list, pattern, options) => orig.match(list, pattern, ext(def, options))
return m
}
// Brace expansion:
// a{b,c}d -> abd acd
// a{b,}c -> abc ac
// a{0..3}d -> a0d a1d a2d a3d
// a{b,c{d,e}f}g -> abg acdfg acefg
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
//
// Invalid sets are not expanded.
// a{2..}b -> a{2..}b
// a{b}c -> a{b}c
minimatch.braceExpand = (pattern, options) => braceExpand(pattern, options)
const braceExpand = (pattern, options = {}) => {
assertValidPattern(pattern)
// Thanks to Yeting Li <https://github.com/yetingli> for
// improving this regexp to avoid a ReDOS vulnerability.
if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
// shortcut. no need to expand.
return [pattern]
}
return expand(pattern)
}
const MAX_PATTERN_LENGTH = 1024 * 64
const assertValidPattern = pattern => {
if (typeof pattern !== 'string') {
throw new TypeError('invalid pattern')
}
if (pattern.length > MAX_PATTERN_LENGTH) {
throw new TypeError('pattern is too long')
}
}
// parse a component of the expanded set.
// At this point, no pattern may contain "/" in it
// so we're going to return a 2d array, where each entry is the full
// pattern, split on '/', and then turned into a regular expression.
// A regexp is made at the end which joins each array with an
// escaped /, and another full one which joins each regexp with |.
//
// Following the lead of Bash 4.1, note that "**" only has special meaning
// when it is the *only* thing in a path portion. Otherwise, any series
// of * is equivalent to a single *. Globstar behavior is enabled by
// default, and can be disabled by setting options.noglobstar.
const SUBPARSE = Symbol('subparse')
minimatch.makeRe = (pattern, options) =>
new Minimatch(pattern, options || {}).makeRe()
minimatch.match = (list, pattern, options = {}) => {
const mm = new Minimatch(pattern, options)
list = list.filter(f => mm.match(f))
if (mm.options.nonull && !list.length) {
list.push(pattern)
}
return list
}
// replace stuff like \* with *
const globUnescape = s => s.replace(/\\(.)/g, '$1')
const charUnescape = s => s.replace(/\\([^-\]])/g, '$1')
const regExpEscape = s => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
const braExpEscape = s => s.replace(/[[\]\\]/g, '\\$&')
class Minimatch {
constructor (pattern, options) {
assertValidPattern(pattern)
if (!options) options = {}
this.options = options
this.set = []
this.pattern = pattern
this.windowsPathsNoEscape = !!options.windowsPathsNoEscape ||
options.allowWindowsEscape === false
if (this.windowsPathsNoEscape) {
this.pattern = this.pattern.replace(/\\/g, '/')
}
this.regexp = null
this.negate = false
this.comment = false
this.empty = false
this.partial = !!options.partial
// make the set of regexps etc.
this.make()
}
debug () {}
make () {
const pattern = this.pattern
const options = this.options
// empty patterns and comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
this.comment = true
return
}
if (!pattern) {
this.empty = true
return
}
// step 1: figure out negation, etc.
this.parseNegate()
// step 2: expand braces
let set = this.globSet = this.braceExpand()
if (options.debug) this.debug = (...args) => console.error(...args)
this.debug(this.pattern, set)
// step 3: now we have a set, so turn each one into a series of path-portion
// matching patterns.
// These will be regexps, except in the case of "**", which is
// set to the GLOBSTAR object for globstar behavior,
// and will not contain any / characters
set = this.globParts = set.map(s => s.split(slashSplit))
this.debug(this.pattern, set)
// glob --> regexps
set = set.map((s, si, set) => s.map(this.parse, this))
this.debug(this.pattern, set)
// filter out everything that didn't compile properly.
set = set.filter(s => s.indexOf(false) === -1)
this.debug(this.pattern, set)
this.set = set
}
parseNegate () {
if (this.options.nonegate) return
const pattern = this.pattern
let negate = false
let negateOffset = 0
for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
negate = !negate
negateOffset++
}
if (negateOffset) this.pattern = pattern.slice(negateOffset)
this.negate = negate
}
// set partial to true to test if, for example,
// "/a/b" matches the start of "/*/b/*/d"
// Partial means, if you run out of file before you run
// out of pattern, then that's fine, as long as all
// the parts match.
matchOne (file, pattern, partial) {
var options = this.options
this.debug('matchOne',
{ 'this': this, file: file, pattern: pattern })
this.debug('matchOne', file.length, pattern.length)
for (var fi = 0,
pi = 0,
fl = file.length,
pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++) {
this.debug('matchOne loop')
var p = pattern[pi]
var f = file[fi]
this.debug(pattern, p, f)
// should be impossible.
// some invalid regexp stuff in the set.
/* istanbul ignore if */
if (p === false) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
/* istanbul ignore if */
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
var hit
if (typeof p === 'string') {
hit = f === p
this.debug('string match', p, f, hit)
} else {
hit = f.match(p)
this.debug('pattern match', p, f, hit)
}
if (!hit) return false
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
// an exact hit!
return true
} else if (fi === fl) {
// ran out of file, but still had pattern left.
// this is ok if we're doing the match as part of
// a glob fs traversal.
return partial
} else /* istanbul ignore else */ if (pi === pl) {
// ran out of pattern, still have file left.
// this is only acceptable if we're on the very last
// empty segment of a file with a trailing slash.
// a/* should match a/b/
return (fi === fl - 1) && (file[fi] === '')
}
// should be unreachable.
/* istanbul ignore next */
throw new Error('wtf?')
}
braceExpand () {
return braceExpand(this.pattern, this.options)
}
parse (pattern, isSub) {
assertValidPattern(pattern)
const options = this.options
// shortcuts
if (pattern === '**') {
if (!options.noglobstar)
return GLOBSTAR
else
pattern = '*'
}
if (pattern === '') return ''
let re = ''
let hasMagic = false
let escaping = false
// ? => one single character
const patternListStack = []
const negativeLists = []
let stateChar
let inClass = false
let reClassStart = -1
let classStart = -1
let cs
let pl
let sp
// . and .. never match anything that doesn't start with .,
// even when options.dot is set. However, if the pattern
// starts with ., then traversal patterns can match.
let dotTravAllowed = pattern.charAt(0) === '.'
let dotFileAllowed = options.dot || dotTravAllowed
const patternStart = () =>
dotTravAllowed
? ''
: dotFileAllowed
? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))'
: '(?!\\.)'
const subPatternStart = (p) =>
p.charAt(0) === '.'
? ''
: options.dot
? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))'
: '(?!\\.)'
const clearStateChar = () => {
if (stateChar) {
// we had some state-tracking character
// that wasn't consumed by this pass.
switch (stateChar) {
case '*':
re += star
hasMagic = true
break
case '?':
re += qmark
hasMagic = true
break
default:
re += '\\' + stateChar
break
}
this.debug('clearStateChar %j %j', stateChar, re)
stateChar = false
}
}
for (let i = 0, c; (i < pattern.length) && (c = pattern.charAt(i)); i++) {
this.debug('%s\t%s %s %j', pattern, i, re, c)
// skip over any that are escaped.
if (escaping) {
/* istanbul ignore next - completely not allowed, even escaped. */
if (c === '/') {
return false
}
if (reSpecials[c]) {
re += '\\'
}
re += c
escaping = false
continue
}
switch (c) {
/* istanbul ignore next */
case '/': {
// Should already be path-split by now.
return false
}
case '\\':
if (inClass && pattern.charAt(i + 1) === '-') {
re += c
continue
}
clearStateChar()
escaping = true
continue
// the various stateChar values
// for the "extglob" stuff.
case '?':
case '*':
case '+':
case '@':
case '!':
this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
// all of those are literals inside a class, except that
// the glob [!a] means [^a] in regexp
if (inClass) {
this.debug(' in class')
if (c === '!' && i === classStart + 1) c = '^'
re += c
continue
}
// if we already have a stateChar, then it means
// that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one.
this.debug('call clearStateChar %j', stateChar)
clearStateChar()
stateChar = c
// if extglob is disabled, then +(asdf|foo) isn't a thing.
// just clear the statechar *now*, rather than even diving into
// the patternList stuff.
if (options.noext) clearStateChar()
continue
case '(': {
if (inClass) {
re += '('
continue
}
if (!stateChar) {
re += '\\('
continue
}
const plEntry = {
type: stateChar,
start: i - 1,
reStart: re.length,
open: plTypes[stateChar].open,
close: plTypes[stateChar].close,
}
this.debug(this.pattern, '\t', plEntry)
patternListStack.push(plEntry)
// negation is (?:(?!(?:js)(?:<rest>))[^/]*)
re += plEntry.open
// next entry starts with a dot maybe?
if (plEntry.start === 0 && plEntry.type !== '!') {
dotTravAllowed = true
re += subPatternStart(pattern.slice(i + 1))
}
this.debug('plType %j %j', stateChar, re)
stateChar = false
continue
}
case ')': {
const plEntry = patternListStack[patternListStack.length - 1]
if (inClass || !plEntry) {
re += '\\)'
continue
}
patternListStack.pop()
// closing an extglob
clearStateChar()
hasMagic = true
pl = plEntry
// negation is (?:(?!js)[^/]*)
// The others are (?:<pattern>)<type>
re += pl.close
if (pl.type === '!') {
negativeLists.push(Object.assign(pl, { reEnd: re.length }))
}
continue
}
case '|': {
const plEntry = patternListStack[patternListStack.length - 1]
if (inClass || !plEntry) {
re += '\\|'
continue
}
clearStateChar()
re += '|'
// next subpattern can start with a dot?
if (plEntry.start === 0 && plEntry.type !== '!') {
dotTravAllowed = true
re += subPatternStart(pattern.slice(i + 1))
}
continue
}
// these are mostly the same in regexp and glob
case '[':
// swallow any state-tracking char before the [
clearStateChar()
if (inClass) {
re += '\\' + c
continue
}
inClass = true
classStart = i
reClassStart = re.length
re += c
continue
case ']':
// a right bracket shall lose its special
// meaning and represent itself in
// a bracket expression if it occurs
// first in the list. -- POSIX.2 2.8.3.2
if (i === classStart + 1 || !inClass) {
re += '\\' + c
continue
}
// split where the last [ was, make sure we don't have
// an invalid re. if so, re-walk the contents of the
// would-be class to re-translate any characters that
// were passed through as-is
// TODO: It would probably be faster to determine this
// without a try/catch and a new RegExp, but it's tricky
// to do safely. For now, this is safe and works.
cs = pattern.substring(classStart + 1, i)
try {
RegExp('[' + braExpEscape(charUnescape(cs)) + ']')
// looks good, finish up the class.
re += c
} catch (er) {
// out of order ranges in JS are errors, but in glob syntax,
// they're just a range that matches nothing.
re = re.substring(0, reClassStart) + '(?:$.)' // match nothing ever
}
hasMagic = true
inClass = false
continue
default:
// swallow any state char that wasn't consumed
clearStateChar()
if (reSpecials[c] && !(c === '^' && inClass)) {
re += '\\'
}
re += c
break
} // switch
} // for
// handle the case where we left a class open.
// "[abc" is valid, equivalent to "\[abc"
if (inClass) {
// split where the last [ was, and escape it
// this is a huge pita. We now have to re-walk
// the contents of the would-be class to re-translate
// any characters that were passed through as-is
cs = pattern.slice(classStart + 1)
sp = this.parse(cs, SUBPARSE)
re = re.substring(0, reClassStart) + '\\[' + sp[0]
hasMagic = hasMagic || sp[1]
}
// handle the case where we had a +( thing at the *end*
// of the pattern.
// each pattern list stack adds 3 chars, and we need to go through
// and escape any | chars that were passed through as-is for the regexp.
// Go through and escape them, taking care not to double-escape any
// | chars that were already escaped.
for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
let tail
tail = re.slice(pl.reStart + pl.open.length)
this.debug('setting tail', re, pl)
// maybe some even number of \, then maybe 1 \, followed by a |
tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, (_, $1, $2) => {
/* istanbul ignore else - should already be done */
if (!$2) {
// the | isn't already escaped, so escape it.
$2 = '\\'
}
// need to escape all those slashes *again*, without escaping the
// one that we need for escaping the | character. As it works out,
// escaping an even number of slashes can be done by simply repeating
// it exactly after itself. That's why this trick works.
//
// I am sorry that you have to see this.
return $1 + $1 + $2 + '|'
})
this.debug('tail=%j\n %s', tail, tail, pl, re)
const t = pl.type === '*' ? star
: pl.type === '?' ? qmark
: '\\' + pl.type
hasMagic = true
re = re.slice(0, pl.reStart) + t + '\\(' + tail
}
// handle trailing things that only matter at the very end.
clearStateChar()
if (escaping) {
// trailing \\
re += '\\\\'
}
// only need to apply the nodot start if the re starts with
// something that could conceivably capture a dot
const addPatternStart = addPatternStartSet[re.charAt(0)]
// Hack to work around lack of negative lookbehind in JS
// A pattern like: *.!(x).!(y|z) needs to ensure that a name
// like 'a.xyz.yz' doesn't match. So, the first negative
// lookahead, has to look ALL the way ahead, to the end of
// the pattern.
for (let n = negativeLists.length - 1; n > -1; n--) {
const nl = negativeLists[n]
const nlBefore = re.slice(0, nl.reStart)
const nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
let nlAfter = re.slice(nl.reEnd)
const nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + nlAfter
// Handle nested stuff like *(*.js|!(*.json)), where open parens
// mean that we should *not* include the ) in the bit that is considered
// "after" the negated section.
const closeParensBefore = nlBefore.split(')').length
const openParensBefore = nlBefore.split('(').length - closeParensBefore
let cleanAfter = nlAfter
for (let i = 0; i < openParensBefore; i++) {
cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
}
nlAfter = cleanAfter
const dollar = nlAfter === '' && isSub !== SUBPARSE ? '(?:$|\\/)' : ''
re = nlBefore + nlFirst + nlAfter + dollar + nlLast
}
// if the re is not "" at this point, then we need to make sure
// it doesn't match against an empty path part.
// Otherwise a/* will match a/, which it should not.
if (re !== '' && hasMagic) {
re = '(?=.)' + re
}
if (addPatternStart) {
re = patternStart() + re
}
// parsing just a piece of a larger pattern.
if (isSub === SUBPARSE) {
return [re, hasMagic]
}
// if it's nocase, and the lcase/uppercase don't match, it's magic
if (options.nocase && !hasMagic) {
hasMagic = pattern.toUpperCase() !== pattern.toLowerCase()
}
// skip the regexp for non-magical patterns
// unescape anything in it, though, so that it'll be
// an exact match against a file etc.
if (!hasMagic) {
return globUnescape(pattern)
}
const flags = options.nocase ? 'i' : ''
try {
return Object.assign(new RegExp('^' + re + '$', flags), {
_glob: pattern,
_src: re,
})
} catch (er) /* istanbul ignore next - should be impossible */ {
// If it was an invalid regular expression, then it can't match
// anything. This trick looks for a character after the end of
// the string, which is of course impossible, except in multi-line
// mode, but it's not a /m regex.
return new RegExp('$.')
}
}
makeRe () {
if (this.regexp || this.regexp === false) return this.regexp
// at this point, this.set is a 2d array of partial
// pattern strings, or "**".
//
// It's better to use .match(). This function shouldn't
// be used, really, but it's pretty convenient sometimes,
// when you just want to work with a regex.
const set = this.set
if (!set.length) {
this.regexp = false
return this.regexp
}
const options = this.options
const twoStar = options.noglobstar ? star
: options.dot ? twoStarDot
: twoStarNoDot
const flags = options.nocase ? 'i' : ''
// coalesce globstars and regexpify non-globstar patterns
// if it's the only item, then we just do one twoStar
// if it's the first, and there are more, prepend (\/|twoStar\/)? to next
// if it's the last, append (\/twoStar|) to previous
// if it's in the middle, append (\/|\/twoStar\/) to previous
// then filter out GLOBSTAR symbols
let re = set.map(pattern => {
pattern = pattern.map(p =>
typeof p === 'string' ? regExpEscape(p)
: p === GLOBSTAR ? GLOBSTAR
: p._src
).reduce((set, p) => {
if (!(set[set.length - 1] === GLOBSTAR && p === GLOBSTAR)) {
set.push(p)
}
return set
}, [])
pattern.forEach((p, i) => {
if (p !== GLOBSTAR || pattern[i-1] === GLOBSTAR) {
return
}
if (i === 0) {
if (pattern.length > 1) {
pattern[i+1] = '(?:\\\/|' + twoStar + '\\\/)?' + pattern[i+1]
} else {
pattern[i] = twoStar
}
} else if (i === pattern.length - 1) {
pattern[i-1] += '(?:\\\/|' + twoStar + ')?'
} else {
pattern[i-1] += '(?:\\\/|\\\/' + twoStar + '\\\/)' + pattern[i+1]
pattern[i+1] = GLOBSTAR
}
})
return pattern.filter(p => p !== GLOBSTAR).join('/')
}).join('|')
// must match entire pattern
// ending in a * or ** will make it less strict.
re = '^(?:' + re + ')$'
// can match anything, as long as it's not this.
if (this.negate) re = '^(?!' + re + ').*$'
try {
this.regexp = new RegExp(re, flags)
} catch (ex) /* istanbul ignore next - should be impossible */ {
this.regexp = false
}
return this.regexp
}
match (f, partial = this.partial) {
this.debug('match', f, this.pattern)
// short-circuit in the case of busted things.
// comments, etc.
if (this.comment) return false
if (this.empty) return f === ''
if (f === '/' && partial) return true
const options = this.options
// windows: need to use /, not \
if (path.sep !== '/') {
f = f.split(path.sep).join('/')
}
// treat the test path as a set of pathparts.
f = f.split(slashSplit)
this.debug(this.pattern, 'split', f)
// just ONE of the pattern sets in this.set needs to match
// in order for it to be valid. If negating, then just one
// match means that we have failed.
// Either way, return on the first hit.
const set = this.set
this.debug(this.pattern, 'set', set)
// Find the basename of the path by looking for the last non-empty segment
let filename
for (let i = f.length - 1; i >= 0; i--) {
filename = f[i]
if (filename) break
}
for (let i = 0; i < set.length; i++) {
const pattern = set[i]
let file = f
if (options.matchBase && pattern.length === 1) {
file = [filename]
}
const hit = this.matchOne(file, pattern, partial)
if (hit) {
if (options.flipNegate) return true
return !this.negate
}
}
// didn't get any hits. this is success if it's a negative
// pattern, failure otherwise.
if (options.flipNegate) return false
return this.negate
}
static defaults (def) {
return minimatch.defaults(def).Minimatch
}
}
minimatch.Minimatch = Minimatch

View File

@@ -0,0 +1,35 @@
{
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
"name": "minimatch",
"description": "a glob matcher in javascript",
"publishConfig": {
"tag": "legacy-v5"
},
"version": "5.1.6",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"
},
"main": "minimatch.js",
"scripts": {
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags"
},
"engines": {
"node": ">=10"
},
"dependencies": {
"brace-expansion": "^2.0.1"
},
"devDependencies": {
"tap": "^16.3.2"
},
"license": "ISC",
"files": [
"minimatch.js",
"lib"
]
}

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.

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

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 })
}

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"
}
}

36
node_modules/app-builder-lib/out/Framework.d.ts generated vendored Normal file
View File

@@ -0,0 +1,36 @@
import { FileTransformer } from "builder-util/out/fs";
import { AsarIntegrity } from "./asar/integrity";
import { Platform, PlatformPackager, ElectronPlatformName, AfterPackContext } from "./index";
export interface Framework {
readonly name: string;
readonly version: string;
readonly distMacOsAppName: string;
readonly macOsDefaultTargets: Array<string>;
readonly defaultAppIdPrefix: string;
readonly isNpmRebuildRequired: boolean;
readonly isCopyElevateHelper: boolean;
getDefaultIcon?(platform: Platform): string | null;
getMainFile?(platform: Platform): string | null;
getExcludedDependencies?(platform: Platform): Array<string> | null;
prepareApplicationStageDirectory(options: PrepareApplicationStageDirectoryOptions): Promise<any>;
beforeCopyExtraFiles?(options: BeforeCopyExtraFilesOptions): Promise<any>;
afterPack?(context: AfterPackContext): Promise<any>;
createTransformer?(): FileTransformer | null;
}
export interface BeforeCopyExtraFilesOptions {
packager: PlatformPackager<any>;
appOutDir: string;
asarIntegrity: AsarIntegrity | null;
platformName: string;
}
export interface PrepareApplicationStageDirectoryOptions {
readonly packager: PlatformPackager<any>;
/**
* Platform doesn't process application output directory in any way. Unpack implementation must create or empty dir if need.
*/
readonly appOutDir: string;
readonly platformName: ElectronPlatformName;
readonly arch: string;
readonly version: string;
}
export declare function isElectronBased(framework: Framework): boolean;

8
node_modules/app-builder-lib/out/Framework.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isElectronBased = void 0;
function isElectronBased(framework) {
return framework.name === "electron";
}
exports.isElectronBased = isElectronBased;
//# sourceMappingURL=Framework.js.map

1
node_modules/app-builder-lib/out/Framework.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"Framework.js","sourceRoot":"","sources":["../src/Framework.ts"],"names":[],"mappings":";;;AAmDA,SAAgB,eAAe,CAAC,SAAoB;IAClD,OAAO,SAAS,CAAC,IAAI,KAAK,UAAU,CAAA;AACtC,CAAC;AAFD,0CAEC","sourcesContent":["import { FileTransformer } from \"builder-util/out/fs\"\nimport { AsarIntegrity } from \"./asar/integrity\"\nimport { Platform, PlatformPackager, ElectronPlatformName, AfterPackContext } from \"./index\"\n\nexport interface Framework {\n readonly name: string\n readonly version: string\n readonly distMacOsAppName: string\n readonly macOsDefaultTargets: Array<string>\n readonly defaultAppIdPrefix: string\n\n readonly isNpmRebuildRequired: boolean\n\n readonly isCopyElevateHelper: boolean\n\n getDefaultIcon?(platform: Platform): string | null\n\n getMainFile?(platform: Platform): string | null\n\n getExcludedDependencies?(platform: Platform): Array<string> | null\n\n prepareApplicationStageDirectory(options: PrepareApplicationStageDirectoryOptions): Promise<any>\n\n beforeCopyExtraFiles?(options: BeforeCopyExtraFilesOptions): Promise<any>\n\n afterPack?(context: AfterPackContext): Promise<any>\n\n createTransformer?(): FileTransformer | null\n}\n\nexport interface BeforeCopyExtraFilesOptions {\n packager: PlatformPackager<any>\n appOutDir: string\n\n asarIntegrity: AsarIntegrity | null\n\n // ElectronPlatformName\n platformName: string\n}\n\nexport interface PrepareApplicationStageDirectoryOptions {\n readonly packager: PlatformPackager<any>\n /**\n * Platform doesn't process application output directory in any way. Unpack implementation must create or empty dir if need.\n */\n readonly appOutDir: string\n readonly platformName: ElectronPlatformName\n readonly arch: string\n readonly version: string\n}\n\nexport function isElectronBased(framework: Framework): boolean {\n return framework.name === \"electron\"\n}\n"]}

10
node_modules/app-builder-lib/out/ProtonFramework.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import { FileTransformer } from "builder-util/out/fs";
import { Platform } from "./core";
import { LibUiFramework } from "./frameworks/LibUiFramework";
export declare class ProtonFramework extends LibUiFramework {
readonly name = "proton";
readonly defaultAppIdPrefix = "com.proton-native.";
constructor(version: string, distMacOsAppName: string, isUseLaunchUi: boolean);
getDefaultIcon(platform: Platform): string;
createTransformer(): FileTransformer | null;
}

95
node_modules/app-builder-lib/out/ProtonFramework.js generated vendored Normal file
View File

@@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtonFramework = void 0;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const core_1 = require("./core");
const fileTransformer_1 = require("./fileTransformer");
const LibUiFramework_1 = require("./frameworks/LibUiFramework");
const pathManager_1 = require("./util/pathManager");
class ProtonFramework extends LibUiFramework_1.LibUiFramework {
constructor(version, distMacOsAppName, isUseLaunchUi) {
super(version, distMacOsAppName, isUseLaunchUi);
this.name = "proton";
// noinspection JSUnusedGlobalSymbols
this.defaultAppIdPrefix = "com.proton-native.";
}
getDefaultIcon(platform) {
if (platform === core_1.Platform.WINDOWS) {
return (0, pathManager_1.getTemplatePath)("icons/proton-native/proton-native.ico");
}
else if (platform === core_1.Platform.LINUX) {
return (0, pathManager_1.getTemplatePath)("icons/proton-native/linux");
}
else {
return (0, pathManager_1.getTemplatePath)("icons/proton-native/proton-native.icns");
}
}
createTransformer() {
let babel;
const babelOptions = { ast: false, sourceMaps: "inline" };
if (process.env.TEST_SET_BABEL_PRESET === "true") {
babel = require("@babel/core");
// eslint-disable-next-line @typescript-eslint/no-use-before-define
babel = testOnlyBabel(babel, babelOptions, this.version);
}
else {
try {
babel = require("babel-core");
}
catch (e) {
// babel isn't installed
builder_util_1.log.debug(null, "don't transpile source code using Babel");
return null;
}
}
builder_util_1.log.info({
options: (0, builder_util_runtime_1.safeStringifyJson)(babelOptions, new Set(["presets"])),
}, "transpile source code using Babel");
return (file) => {
if (!(file.endsWith(".js") || file.endsWith(".jsx")) || file.includes(fileTransformer_1.NODE_MODULES_PATTERN)) {
return null;
}
return new Promise((resolve, reject) => {
return babel.transformFile(file, babelOptions, (error, result) => {
if (error == null) {
resolve(result.code);
}
else {
reject(error);
}
});
});
};
}
}
exports.ProtonFramework = ProtonFramework;
function testOnlyBabel(babel, babelOptions, nodeVersion) {
// out test dir can be located outside of electron-builder node_modules and babel cannot resolve string names of preset
babelOptions.presets = [[require("@babel/preset-env").default, { targets: { node: nodeVersion } }], require("@babel/preset-react")];
babelOptions.plugins = [
// stage 0
require("@babel/plugin-proposal-function-bind").default,
// stage 1
require("@babel/plugin-proposal-export-default-from").default,
require("@babel/plugin-proposal-logical-assignment-operators").default,
[require("@babel/plugin-proposal-optional-chaining").default, { loose: false }],
[require("@babel/plugin-proposal-pipeline-operator").default, { proposal: "minimal" }],
[require("@babel/plugin-proposal-nullish-coalescing-operator").default, { loose: false }],
require("@babel/plugin-proposal-do-expressions").default,
// stage 2
[require("@babel/plugin-proposal-decorators").default, { legacy: true }],
require("@babel/plugin-proposal-function-sent").default,
require("@babel/plugin-proposal-export-namespace-from").default,
require("@babel/plugin-proposal-numeric-separator").default,
require("@babel/plugin-proposal-throw-expressions").default,
// stage 3
require("@babel/plugin-syntax-dynamic-import").default,
require("@babel/plugin-syntax-import-meta").default,
[require("@babel/plugin-proposal-class-properties").default, { loose: false }],
require("@babel/plugin-proposal-json-strings").default,
];
babelOptions.babelrc = false;
return babel;
}
//# sourceMappingURL=ProtonFramework.js.map

File diff suppressed because one or more lines are too long

30
node_modules/app-builder-lib/out/appInfo.d.ts generated vendored Normal file
View File

@@ -0,0 +1,30 @@
import { PlatformSpecificBuildOptions } from "./options/PlatformSpecificBuildOptions";
import { Packager } from "./packager";
export declare function smarten(s: string): string;
export declare class AppInfo {
private readonly info;
private readonly platformSpecificOptions;
readonly description: string;
readonly version: string;
readonly type: string | undefined;
readonly shortVersion: string | undefined;
readonly shortVersionWindows: string | undefined;
readonly buildNumber: string | undefined;
readonly buildVersion: string;
readonly productName: string;
readonly sanitizedProductName: string;
readonly productFilename: string;
constructor(info: Packager, buildVersion: string | null | undefined, platformSpecificOptions?: PlatformSpecificBuildOptions | null, normalizeNfd?: boolean);
get channel(): string | null;
getVersionInWeirdWindowsForm(isSetBuildNumber?: boolean): string;
private get notNullDevMetadata();
get companyName(): string | null;
get id(): string;
get macBundleIdentifier(): string;
get name(): string;
get linuxPackageName(): string;
get sanitizedName(): string;
get updaterCacheDirName(): string;
get copyright(): string;
computePackageUrl(): Promise<string | null>;
}

152
node_modules/app-builder-lib/out/appInfo.js generated vendored Normal file
View File

@@ -0,0 +1,152 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.filterCFBundleIdentifier = exports.AppInfo = exports.smarten = void 0;
const builder_util_1 = require("builder-util");
const semver_1 = require("semver");
const macroExpander_1 = require("./util/macroExpander");
const filename_1 = require("./util/filename");
// fpm bug - rpm build --description is not escaped, well... decided to replace quite to smart quote
// http://leancrew.com/all-this/2010/11/smart-quotes-in-javascript/
function smarten(s) {
// opening singles
s = s.replace(/(^|[-\u2014\s(["])'/g, "$1\u2018");
// closing singles & apostrophes
s = s.replace(/'/g, "\u2019");
// opening doubles
s = s.replace(/(^|[-\u2014/[(\u2018\s])"/g, "$1\u201c");
// closing doubles
s = s.replace(/"/g, "\u201d");
return s;
}
exports.smarten = smarten;
class AppInfo {
constructor(info, buildVersion, platformSpecificOptions = null, normalizeNfd = false) {
var _a;
this.info = info;
this.platformSpecificOptions = platformSpecificOptions;
this.description = smarten(this.info.metadata.description || "");
this.version = info.metadata.version;
this.type = info.metadata.type;
if (buildVersion == null) {
buildVersion = info.config.buildVersion;
}
const buildNumberEnvs = process.env.BUILD_NUMBER ||
process.env.TRAVIS_BUILD_NUMBER ||
process.env.APPVEYOR_BUILD_NUMBER ||
process.env.CIRCLE_BUILD_NUM ||
process.env.BUILD_BUILDNUMBER ||
process.env.CI_PIPELINE_IID;
this.buildNumber = info.config.buildNumber || buildNumberEnvs;
if (buildVersion == null) {
buildVersion = this.version;
if (!(0, builder_util_1.isEmptyOrSpaces)(this.buildNumber)) {
buildVersion += `.${this.buildNumber}`;
}
}
this.buildVersion = buildVersion;
if (info.metadata.shortVersion) {
this.shortVersion = info.metadata.shortVersion;
}
if (info.metadata.shortVersionWindows) {
this.shortVersionWindows = info.metadata.shortVersionWindows;
}
this.productName = info.config.productName || info.metadata.productName || info.metadata.name;
this.sanitizedProductName = (0, filename_1.sanitizeFileName)(this.productName, normalizeNfd);
const executableName = (_a = platformSpecificOptions === null || platformSpecificOptions === void 0 ? void 0 : platformSpecificOptions.executableName) !== null && _a !== void 0 ? _a : info.config.executableName;
this.productFilename = executableName != null ? (0, filename_1.sanitizeFileName)(executableName, normalizeNfd) : this.sanitizedProductName;
}
get channel() {
const prereleaseInfo = (0, semver_1.prerelease)(this.version);
if (prereleaseInfo != null && prereleaseInfo.length > 0) {
return prereleaseInfo[0];
}
return null;
}
getVersionInWeirdWindowsForm(isSetBuildNumber = true) {
const [major, maybe_minor, maybe_patch] = this.version.split(".").map(versionPart => parseInt(versionPart));
// The major component must be present. Here it can be either NaN or undefined, which
// both returns true from isNaN.
if (isNaN(major)) {
throw new Error(`Invalid major number in: ${this.version}`);
}
// Allow missing version parts. Minor and patch can be left out and default to zero
const minor = maybe_minor !== null && maybe_minor !== void 0 ? maybe_minor : 0;
const patch = maybe_patch !== null && maybe_patch !== void 0 ? maybe_patch : 0;
// ... but reject non-integer version parts. '1.a' is not going to fly
if (isNaN(minor) || isNaN(patch)) {
throw new Error(`Invalid minor or patch number in: ${this.version}`);
}
// https://github.com/electron-userland/electron-builder/issues/2635#issuecomment-371792272
let buildNumber = isSetBuildNumber ? this.buildNumber : null;
if (buildNumber == null || !/^\d+$/.test(buildNumber)) {
buildNumber = "0";
}
return `${major}.${minor}.${patch}.${buildNumber}`;
}
get notNullDevMetadata() {
return this.info.devMetadata || {};
}
get companyName() {
const author = this.info.metadata.author || this.notNullDevMetadata.author;
return author == null ? null : author.name;
}
get id() {
let appId = null;
for (const options of [this.platformSpecificOptions, this.info.config]) {
if (options != null && appId == null) {
appId = options.appId;
}
}
const generateDefaultAppId = () => {
const info = this.info;
return `${info.framework.defaultAppIdPrefix}${info.metadata.name.toLowerCase()}`;
};
if (appId != null && (appId === "your.id" || (0, builder_util_1.isEmptyOrSpaces)(appId))) {
const incorrectAppId = appId;
appId = generateDefaultAppId();
builder_util_1.log.warn(`do not use "${incorrectAppId}" as appId, "${appId}" will be used instead`);
}
return appId == null ? generateDefaultAppId() : appId;
}
get macBundleIdentifier() {
return filterCFBundleIdentifier(this.id);
}
get name() {
return this.info.metadata.name;
}
get linuxPackageName() {
const name = this.name;
// https://github.com/electron-userland/electron-builder/issues/2963
return name.startsWith("@") ? this.sanitizedProductName : name;
}
get sanitizedName() {
return (0, filename_1.sanitizeFileName)(this.name);
}
get updaterCacheDirName() {
return this.sanitizedName.toLowerCase() + "-updater";
}
get copyright() {
const copyright = this.info.config.copyright;
if (copyright != null) {
return (0, macroExpander_1.expandMacro)(copyright, null, this);
}
return `Copyright © ${new Date().getFullYear()} ${this.companyName || this.productName}`;
}
async computePackageUrl() {
const url = this.info.metadata.homepage || this.notNullDevMetadata.homepage;
if (url != null) {
return url;
}
const info = await this.info.repositoryInfo;
return info == null || info.type !== "github" ? null : `https://${info.domain}/${info.user}/${info.project}`;
}
}
exports.AppInfo = AppInfo;
/** @internal */
function filterCFBundleIdentifier(identifier) {
// Remove special characters and allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)
// Apple documentation: https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070
return identifier.replace(/ /g, "-").replace(/[^a-zA-Z0-9.-]/g, "");
}
exports.filterCFBundleIdentifier = filterCFBundleIdentifier;
//# sourceMappingURL=appInfo.js.map

1
node_modules/app-builder-lib/out/appInfo.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

41
node_modules/app-builder-lib/out/asar/asar.d.ts generated vendored Normal file
View File

@@ -0,0 +1,41 @@
/// <reference types="node" />
/// <reference types="node" />
import { Stats } from "fs-extra";
export interface ReadAsarHeader {
readonly header: string;
readonly size: number;
}
export interface NodeIntegrity {
algorithm: "SHA256";
hash: string;
blockSize: number;
blocks: Array<string>;
}
export declare class Node {
files?: {
[key: string]: Node;
};
unpacked?: boolean;
size?: number;
offset?: string;
executable?: boolean;
link?: string;
integrity?: NodeIntegrity;
}
export declare class AsarFilesystem {
readonly src: string;
readonly header: Node;
readonly headerSize: number;
private offset;
constructor(src: string, header?: Node, headerSize?: number);
searchNodeFromDirectory(p: string, isCreate: boolean): Node | null;
getOrCreateNode(p: string): Node;
addFileNode(file: string, dirNode: Node, size: number, unpacked: boolean, stat: Stats, integrity?: NodeIntegrity): Node;
getNode(p: string): Node | null;
getFile(p: string, followLinks?: boolean): Node;
readJson(file: string): Promise<any>;
readFile(file: string): Promise<Buffer>;
}
export declare function readAsarHeader(archive: string): Promise<ReadAsarHeader>;
export declare function readAsar(archive: string): Promise<AsarFilesystem>;
export declare function readAsarJson(archive: string, file: string): Promise<any>;

151
node_modules/app-builder-lib/out/asar/asar.js generated vendored Normal file
View File

@@ -0,0 +1,151 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readAsarJson = exports.readAsar = exports.readAsarHeader = exports.AsarFilesystem = exports.Node = void 0;
const chromium_pickle_js_1 = require("chromium-pickle-js");
const fs_extra_1 = require("fs-extra");
const path = require("path");
class Node {
}
exports.Node = Node;
class AsarFilesystem {
constructor(src, header = new Node(), headerSize = -1) {
this.src = src;
this.header = header;
this.headerSize = headerSize;
this.offset = 0;
if (this.header.files == null) {
this.header.files = {};
}
}
searchNodeFromDirectory(p, isCreate) {
let node = this.header;
for (const dir of p.split(path.sep)) {
if (dir !== ".") {
let child = node.files[dir];
if (child == null) {
if (!isCreate) {
return null;
}
child = new Node();
child.files = {};
node.files[dir] = child;
}
node = child;
}
}
return node;
}
getOrCreateNode(p) {
if (p == null || p.length === 0) {
return this.header;
}
const name = path.basename(p);
const dirNode = this.searchNodeFromDirectory(path.dirname(p), true);
if (dirNode.files == null) {
dirNode.files = {};
}
let result = dirNode.files[name];
if (result == null) {
result = new Node();
dirNode.files[name] = result;
}
return result;
}
addFileNode(file, dirNode, size, unpacked, stat, integrity) {
if (size > 4294967295) {
throw new Error(`${file}: file size cannot be larger than 4.2GB`);
}
const node = new Node();
node.size = size;
if (integrity) {
node.integrity = integrity;
}
if (unpacked) {
node.unpacked = true;
}
else {
// electron expects string
node.offset = this.offset.toString();
if (process.platform !== "win32" && stat.mode & 0o100) {
node.executable = true;
}
this.offset += node.size;
}
let children = dirNode.files;
if (children == null) {
children = {};
dirNode.files = children;
}
children[path.basename(file)] = node;
return node;
}
getNode(p) {
const node = this.searchNodeFromDirectory(path.dirname(p), false);
return node.files[path.basename(p)];
}
getFile(p, followLinks = true) {
const info = this.getNode(p);
// if followLinks is false we don't resolve symlinks
return followLinks && info.link != null ? this.getFile(info.link) : info;
}
async readJson(file) {
return JSON.parse((await this.readFile(file)).toString());
}
readFile(file) {
return readFileFromAsar(this, file, this.getFile(file));
}
}
exports.AsarFilesystem = AsarFilesystem;
async function readAsarHeader(archive) {
const fd = await (0, fs_extra_1.open)(archive, "r");
let size;
let headerBuf;
try {
const sizeBuf = Buffer.allocUnsafe(8);
if ((await (0, fs_extra_1.read)(fd, sizeBuf, 0, 8, null)).bytesRead !== 8) {
throw new Error("Unable to read header size");
}
const sizePickle = (0, chromium_pickle_js_1.createFromBuffer)(sizeBuf);
size = sizePickle.createIterator().readUInt32();
headerBuf = Buffer.allocUnsafe(size);
if ((await (0, fs_extra_1.read)(fd, headerBuf, 0, size, null)).bytesRead !== size) {
throw new Error("Unable to read header");
}
}
finally {
await (0, fs_extra_1.close)(fd);
}
const headerPickle = (0, chromium_pickle_js_1.createFromBuffer)(headerBuf);
return { header: headerPickle.createIterator().readString(), size };
}
exports.readAsarHeader = readAsarHeader;
async function readAsar(archive) {
const { header, size } = await readAsarHeader(archive);
return new AsarFilesystem(archive, JSON.parse(header), size);
}
exports.readAsar = readAsar;
async function readAsarJson(archive, file) {
const fs = await readAsar(archive);
return await fs.readJson(file);
}
exports.readAsarJson = readAsarJson;
async function readFileFromAsar(filesystem, filename, info) {
const size = info.size;
const buffer = Buffer.allocUnsafe(size);
if (size <= 0) {
return buffer;
}
if (info.unpacked) {
return await (0, fs_extra_1.readFile)(path.join(`${filesystem.src}.unpacked`, filename));
}
const fd = await (0, fs_extra_1.open)(filesystem.src, "r");
try {
const offset = 8 + filesystem.headerSize + parseInt(info.offset, 10);
await (0, fs_extra_1.read)(fd, buffer, 0, size, offset);
}
finally {
await (0, fs_extra_1.close)(fd);
}
return buffer;
}
//# sourceMappingURL=asar.js.map

1
node_modules/app-builder-lib/out/asar/asar.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkFileInArchive = void 0;
const fs_1 = require("builder-util/out/fs");
const asar_1 = require("./asar");
/** @internal */
async function checkFileInArchive(asarFile, relativeFile, messagePrefix) {
function error(text) {
return new Error(`${messagePrefix} "${relativeFile}" in the "${asarFile}" ${text}`);
}
let fs;
try {
fs = await (0, asar_1.readAsar)(asarFile);
}
catch (e) {
throw error(`is corrupted: ${e}`);
}
let stat;
try {
stat = fs.getFile(relativeFile);
}
catch (e) {
const fileStat = await (0, fs_1.statOrNull)(asarFile);
if (fileStat == null) {
throw error(`does not exist. Seems like a wrong configuration.`);
}
// asar throws error on access to undefined object (info.link)
stat = null;
}
if (stat == null) {
throw error(`does not exist. Seems like a wrong configuration.`);
}
if (stat.size === 0) {
throw error(`is corrupted: size 0`);
}
}
exports.checkFileInArchive = checkFileInArchive;
//# sourceMappingURL=asarFileChecker.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"asarFileChecker.js","sourceRoot":"","sources":["../../src/asar/asarFileChecker.ts"],"names":[],"mappings":";;;AAAA,4CAAgD;AAChD,iCAAuC;AAEvC,gBAAgB;AACT,KAAK,UAAU,kBAAkB,CAAC,QAAgB,EAAE,YAAoB,EAAE,aAAqB;IACpG,SAAS,KAAK,CAAC,IAAY;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,aAAa,KAAK,YAAY,aAAa,QAAQ,KAAK,IAAI,EAAE,CAAC,CAAA;IACrF,CAAC;IAED,IAAI,EAAE,CAAA;IACN,IAAI,CAAC;QACH,EAAE,GAAG,MAAM,IAAA,eAAQ,EAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAA;IACnC,CAAC;IAED,IAAI,IAAiB,CAAA;IACrB,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACjC,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAA,eAAU,EAAC,QAAQ,CAAC,CAAA;QAC3C,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,KAAK,CAAC,mDAAmD,CAAC,CAAA;QAClE,CAAC;QAED,8DAA8D;QAC9D,IAAI,GAAG,IAAI,CAAA;IACb,CAAC;IAED,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,KAAK,CAAC,mDAAmD,CAAC,CAAA;IAClE,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAA;IACrC,CAAC;AACH,CAAC;AA/BD,gDA+BC","sourcesContent":["import { statOrNull } from \"builder-util/out/fs\"\nimport { Node, readAsar } from \"./asar\"\n\n/** @internal */\nexport async function checkFileInArchive(asarFile: string, relativeFile: string, messagePrefix: string) {\n function error(text: string) {\n return new Error(`${messagePrefix} \"${relativeFile}\" in the \"${asarFile}\" ${text}`)\n }\n\n let fs\n try {\n fs = await readAsar(asarFile)\n } catch (e: any) {\n throw error(`is corrupted: ${e}`)\n }\n\n let stat: Node | null\n try {\n stat = fs.getFile(relativeFile)\n } catch (e: any) {\n const fileStat = await statOrNull(asarFile)\n if (fileStat == null) {\n throw error(`does not exist. Seems like a wrong configuration.`)\n }\n\n // asar throws error on access to undefined object (info.link)\n stat = null\n }\n\n if (stat == null) {\n throw error(`does not exist. Seems like a wrong configuration.`)\n }\n if (stat.size === 0) {\n throw error(`is corrupted: size 0`)\n }\n}\n"]}

1
node_modules/app-builder-lib/out/asar/asarUtil.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

241
node_modules/app-builder-lib/out/asar/asarUtil.js generated vendored Normal file
View File

@@ -0,0 +1,241 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsarPackager = void 0;
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_2 = require("fs");
const promises_1 = require("fs/promises");
const path = require("path");
const appFileCopier_1 = require("../util/appFileCopier");
const asar_1 = require("./asar");
const integrity_1 = require("./integrity");
const unpackDetector_1 = require("./unpackDetector");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pickle = require("chromium-pickle-js");
/** @internal */
class AsarPackager {
constructor(src, destination, options, unpackPattern) {
this.src = src;
this.destination = destination;
this.options = options;
this.unpackPattern = unpackPattern;
this.fs = new asar_1.AsarFilesystem(this.src);
this.outFile = path.join(destination, "app.asar");
this.unpackedDest = `${this.outFile}.unpacked`;
}
// sort files to minimize file change (i.e. asar file is not changed dramatically on small change)
async pack(fileSets, packager) {
if (this.options.ordering != null) {
// ordering doesn't support transformed files, but ordering is not used functionality - wait user report to fix it
await order(fileSets[0].files, this.options.ordering, fileSets[0].src);
}
await (0, promises_1.mkdir)(path.dirname(this.outFile), { recursive: true });
const unpackedFileIndexMap = new Map();
for (const fileSet of fileSets) {
unpackedFileIndexMap.set(fileSet, await this.createPackageFromFiles(fileSet, packager.info));
}
await this.writeAsarFile(fileSets, unpackedFileIndexMap);
}
async createPackageFromFiles(fileSet, packager) {
const metadata = fileSet.metadata;
// search auto unpacked dir
const unpackedDirs = new Set();
const rootForAppFilesWithoutAsar = path.join(this.destination, "app");
if (this.options.smartUnpack !== false) {
await (0, unpackDetector_1.detectUnpackedDirs)(fileSet, unpackedDirs, this.unpackedDest, rootForAppFilesWithoutAsar);
}
const dirToCreateForUnpackedFiles = new Set(unpackedDirs);
const correctDirNodeUnpackedFlag = async (filePathInArchive, dirNode) => {
for (const dir of unpackedDirs) {
if (filePathInArchive.length > dir.length + 2 && filePathInArchive[dir.length] === path.sep && filePathInArchive.startsWith(dir)) {
dirNode.unpacked = true;
unpackedDirs.add(filePathInArchive);
// not all dirs marked as unpacked after first iteration - because node module dir can be marked as unpacked after processing node module dir content
// e.g. node-notifier/example/advanced.js processed, but only on process vendor/terminal-notifier.app module will be marked as unpacked
await (0, promises_1.mkdir)(path.join(this.unpackedDest, filePathInArchive), { recursive: true });
break;
}
}
};
const transformedFiles = fileSet.transformedFiles;
const taskManager = new builder_util_1.AsyncTaskManager(packager.cancellationToken);
const fileCopier = new fs_1.FileCopier();
let currentDirNode = null;
let currentDirPath = null;
const unpackedFileIndexSet = new Set();
for (let i = 0, n = fileSet.files.length; i < n; i++) {
const file = fileSet.files[i];
const stat = metadata.get(file);
if (stat == null) {
continue;
}
const pathInArchive = path.relative(rootForAppFilesWithoutAsar, (0, appFileCopier_1.getDestinationPath)(file, fileSet));
if (stat.isSymbolicLink()) {
const s = stat;
this.fs.getOrCreateNode(pathInArchive).link = s.relativeLink;
s.pathInArchive = pathInArchive;
unpackedFileIndexSet.add(i);
continue;
}
let fileParent = path.dirname(pathInArchive);
if (fileParent === ".") {
fileParent = "";
}
if (currentDirPath !== fileParent) {
if (fileParent.startsWith("..")) {
throw new Error(`Internal error: path must not start with "..": ${fileParent}`);
}
currentDirPath = fileParent;
currentDirNode = this.fs.getOrCreateNode(fileParent);
// do not check for root
if (fileParent !== "" && !currentDirNode.unpacked) {
if (unpackedDirs.has(fileParent)) {
currentDirNode.unpacked = true;
}
else {
await correctDirNodeUnpackedFlag(fileParent, currentDirNode);
}
}
}
const dirNode = currentDirNode;
const newData = transformedFiles == null ? undefined : transformedFiles.get(i);
const isUnpacked = dirNode.unpacked || (this.unpackPattern != null && this.unpackPattern(file, stat));
const integrity = newData === undefined ? await (0, integrity_1.hashFile)(file) : (0, integrity_1.hashFileContents)(newData);
this.fs.addFileNode(file, dirNode, newData == undefined ? stat.size : Buffer.byteLength(newData), isUnpacked, stat, integrity);
if (isUnpacked) {
if (!dirNode.unpacked && !dirToCreateForUnpackedFiles.has(fileParent)) {
dirToCreateForUnpackedFiles.add(fileParent);
await (0, promises_1.mkdir)(path.join(this.unpackedDest, fileParent), { recursive: true });
}
const unpackedFile = path.join(this.unpackedDest, pathInArchive);
taskManager.addTask(copyFileOrData(fileCopier, newData, file, unpackedFile, stat));
if (taskManager.tasks.length > fs_1.MAX_FILE_REQUESTS) {
await taskManager.awaitTasks();
}
unpackedFileIndexSet.add(i);
}
}
if (taskManager.tasks.length > 0) {
await taskManager.awaitTasks();
}
return unpackedFileIndexSet;
}
writeAsarFile(fileSets, unpackedFileIndexMap) {
return new Promise((resolve, reject) => {
const headerPickle = pickle.createEmpty();
headerPickle.writeString(JSON.stringify(this.fs.header));
const headerBuf = headerPickle.toBuffer();
const sizePickle = pickle.createEmpty();
sizePickle.writeUInt32(headerBuf.length);
const sizeBuf = sizePickle.toBuffer();
const writeStream = (0, fs_2.createWriteStream)(this.outFile);
writeStream.on("error", reject);
writeStream.on("close", resolve);
writeStream.write(sizeBuf);
let fileSetIndex = 0;
let files = fileSets[0].files;
let metadata = fileSets[0].metadata;
let transformedFiles = fileSets[0].transformedFiles;
let unpackedFileIndexSet = unpackedFileIndexMap.get(fileSets[0]);
const w = (index) => {
while (true) {
if (index >= files.length) {
if (++fileSetIndex >= fileSets.length) {
writeStream.end();
return;
}
else {
files = fileSets[fileSetIndex].files;
metadata = fileSets[fileSetIndex].metadata;
transformedFiles = fileSets[fileSetIndex].transformedFiles;
unpackedFileIndexSet = unpackedFileIndexMap.get(fileSets[fileSetIndex]);
index = 0;
}
}
if (!unpackedFileIndexSet.has(index)) {
break;
}
else {
const stat = metadata.get(files[index]);
if (stat != null && stat.isSymbolicLink()) {
(0, fs_2.symlink)(stat.linkRelativeToFile, path.join(this.unpackedDest, stat.pathInArchive), () => w(index + 1));
return;
}
}
index++;
}
const data = transformedFiles == null ? null : transformedFiles.get(index);
const file = files[index];
if (data !== null && data !== undefined) {
writeStream.write(data, () => w(index + 1));
return;
}
// https://github.com/yarnpkg/yarn/pull/3539
const stat = metadata.get(file);
if (stat != null && stat.size < 2 * 1024 * 1024) {
(0, promises_1.readFile)(file)
.then(it => {
writeStream.write(it, () => w(index + 1));
})
.catch((e) => reject(`Cannot read file ${file}: ${e.stack || e}`));
}
else {
const readStream = (0, fs_2.createReadStream)(file);
readStream.on("error", reject);
readStream.once("end", () => w(index + 1));
readStream.on("open", () => {
readStream.pipe(writeStream, {
end: false,
});
});
}
};
writeStream.write(headerBuf, () => w(0));
});
}
}
exports.AsarPackager = AsarPackager;
async function order(filenames, orderingFile, src) {
const orderingFiles = (await (0, promises_1.readFile)(orderingFile, "utf8")).split("\n").map(line => {
if (line.indexOf(":") !== -1) {
line = line.split(":").pop();
}
line = line.trim();
if (line[0] === "/") {
line = line.slice(1);
}
return line;
});
const ordering = [];
for (const file of orderingFiles) {
const pathComponents = file.split(path.sep);
for (const pathComponent of pathComponents) {
ordering.push(path.join(src, pathComponent));
}
}
const sortedFiles = [];
let missing = 0;
const total = filenames.length;
for (const file of ordering) {
if (!sortedFiles.includes(file) && filenames.includes(file)) {
sortedFiles.push(file);
}
}
for (const file of filenames) {
if (!sortedFiles.includes(file)) {
sortedFiles.push(file);
missing += 1;
}
}
builder_util_1.log.info({ coverage: ((total - missing) / total) * 100 }, "ordering files in ASAR archive");
return sortedFiles;
}
function copyFileOrData(fileCopier, data, source, destination, stats) {
if (data == null) {
return fileCopier.copy(source, destination, stats);
}
else {
return (0, promises_1.writeFile)(destination, data);
}
}
//# sourceMappingURL=asarUtil.js.map

File diff suppressed because one or more lines are too long

16
node_modules/app-builder-lib/out/asar/integrity.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/// <reference types="node" />
import { NodeIntegrity } from "./asar";
export interface AsarIntegrityOptions {
readonly resourcesPath: string;
readonly resourcesRelativePath: string;
}
export interface HeaderHash {
algorithm: "SHA256";
hash: string;
}
export interface AsarIntegrity {
[key: string]: HeaderHash;
}
export declare function computeData({ resourcesPath, resourcesRelativePath }: AsarIntegrityOptions): Promise<AsarIntegrity>;
export declare function hashFile(file: string, blockSize?: number): Promise<NodeIntegrity>;
export declare function hashFileContents(contents: Buffer | string, blockSize?: number): NodeIntegrity;

89
node_modules/app-builder-lib/out/asar/integrity.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hashFileContents = exports.hashFile = exports.computeData = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const crypto_1 = require("crypto");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const path = require("path");
const asar_1 = require("./asar");
async function computeData({ resourcesPath, resourcesRelativePath }) {
// sort to produce constant result
const names = (await (0, promises_1.readdir)(resourcesPath)).filter(it => it.endsWith(".asar")).sort();
const checksums = await bluebird_lst_1.default.map(names, it => hashHeader(path.join(resourcesPath, it)));
const result = {};
for (let i = 0; i < names.length; i++) {
result[path.join(resourcesRelativePath, names[i])] = checksums[i];
}
return result;
}
exports.computeData = computeData;
async function hashHeader(file) {
const hash = (0, crypto_1.createHash)("sha256");
const { header } = await (0, asar_1.readAsarHeader)(file);
hash.update(header);
return {
algorithm: "SHA256",
hash: hash.digest("hex"),
};
}
function hashFile(file, blockSize = 4 * 1024 * 1024) {
return new Promise((resolve, reject) => {
const hash = (0, crypto_1.createHash)("sha256");
const blocks = new Array();
let blockBytes = 0;
let blockHash = (0, crypto_1.createHash)("sha256");
function updateBlockHash(chunk) {
let off = 0;
while (off < chunk.length) {
const toHash = Math.min(blockSize - blockBytes, chunk.length - off);
blockHash.update(chunk.slice(off, off + toHash));
off += toHash;
blockBytes += toHash;
if (blockBytes === blockSize) {
blocks.push(blockHash.digest("hex"));
blockHash = (0, crypto_1.createHash)("sha256");
blockBytes = 0;
}
}
}
(0, fs_1.createReadStream)(file)
.on("data", it => {
// Note that `it` is a Buffer anyway so this cast is a no-op
updateBlockHash(Buffer.from(it));
hash.update(it);
})
.on("error", reject)
.on("end", () => {
if (blockBytes !== 0) {
blocks.push(blockHash.digest("hex"));
}
resolve({
algorithm: "SHA256",
hash: hash.digest("hex"),
blockSize,
blocks,
});
});
});
}
exports.hashFile = hashFile;
function hashFileContents(contents, blockSize = 4 * 1024 * 1024) {
const buffer = Buffer.from(contents);
const hash = (0, crypto_1.createHash)("sha256");
hash.update(buffer);
const blocks = new Array();
for (let off = 0; off < buffer.length; off += blockSize) {
const blockHash = (0, crypto_1.createHash)("sha256");
blockHash.update(buffer.slice(off, off + blockSize));
blocks.push(blockHash.digest("hex"));
}
return {
algorithm: "SHA256",
hash: hash.digest("hex"),
blockSize,
blocks,
};
}
exports.hashFileContents = hashFileContents;
//# sourceMappingURL=integrity.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export declare function isLibOrExe(file: string): boolean;

108
node_modules/app-builder-lib/out/asar/unpackDetector.js generated vendored Normal file
View File

@@ -0,0 +1,108 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectUnpackedDirs = exports.isLibOrExe = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_extra_1 = require("fs-extra");
const isbinaryfile_1 = require("isbinaryfile");
const path = require("path");
const fileTransformer_1 = require("../fileTransformer");
const appFileCopier_1 = require("../util/appFileCopier");
function addValue(map, key, value) {
let list = map.get(key);
if (list == null) {
list = [value];
map.set(key, list);
}
else {
list.push(value);
}
}
function isLibOrExe(file) {
return file.endsWith(".dll") || file.endsWith(".exe") || file.endsWith(".dylib") || file.endsWith(".so");
}
exports.isLibOrExe = isLibOrExe;
/** @internal */
async function detectUnpackedDirs(fileSet, autoUnpackDirs, unpackedDest, rootForAppFilesWithoutAsar) {
const dirToCreate = new Map();
const metadata = fileSet.metadata;
function addParents(child, root) {
child = path.dirname(child);
if (autoUnpackDirs.has(child)) {
return;
}
do {
autoUnpackDirs.add(child);
const p = path.dirname(child);
// create parent dir to be able to copy file later without directory existence check
addValue(dirToCreate, p, path.basename(child));
if (child === root || p === root || autoUnpackDirs.has(p)) {
break;
}
child = p;
} while (true);
autoUnpackDirs.add(root);
}
for (let i = 0, n = fileSet.files.length; i < n; i++) {
const file = fileSet.files[i];
const index = file.lastIndexOf(fileTransformer_1.NODE_MODULES_PATTERN);
if (index < 0) {
continue;
}
let nextSlashIndex = file.indexOf(path.sep, index + fileTransformer_1.NODE_MODULES_PATTERN.length + 1);
if (nextSlashIndex < 0) {
continue;
}
if (file[index + fileTransformer_1.NODE_MODULES_PATTERN.length] === "@") {
nextSlashIndex = file.indexOf(path.sep, nextSlashIndex + 1);
}
if (!metadata.get(file).isFile()) {
continue;
}
const packageDir = file.substring(0, nextSlashIndex);
const packageDirPathInArchive = path.relative(rootForAppFilesWithoutAsar, (0, appFileCopier_1.getDestinationPath)(packageDir, fileSet));
const pathInArchive = path.relative(rootForAppFilesWithoutAsar, (0, appFileCopier_1.getDestinationPath)(file, fileSet));
if (autoUnpackDirs.has(packageDirPathInArchive)) {
// if package dir is unpacked, any file also unpacked
addParents(pathInArchive, packageDirPathInArchive);
continue;
}
// https://github.com/electron-userland/electron-builder/issues/2679
let shouldUnpack = false;
// ffprobe-static and ffmpeg-static are known packages to always unpack
const moduleName = path.basename(packageDir);
if (moduleName === "ffprobe-static" || moduleName === "ffmpeg-static" || isLibOrExe(file)) {
shouldUnpack = true;
}
else if (!file.includes(".", nextSlashIndex)) {
shouldUnpack = !!(0, isbinaryfile_1.isBinaryFileSync)(file);
}
if (!shouldUnpack) {
continue;
}
if (builder_util_1.log.isDebugEnabled) {
builder_util_1.log.debug({ file: pathInArchive, reason: "contains executable code" }, "not packed into asar archive");
}
addParents(pathInArchive, packageDirPathInArchive);
}
if (dirToCreate.size > 0) {
await (0, fs_extra_1.mkdir)(`${unpackedDest + path.sep}node_modules`, { recursive: true });
// child directories should be not created asynchronously - parent directories should be created first
await bluebird_lst_1.default.map(dirToCreate.keys(), async (parentDir) => {
const base = unpackedDest + path.sep + parentDir;
await (0, fs_extra_1.mkdir)(base, { recursive: true });
await bluebird_lst_1.default.each(dirToCreate.get(parentDir), (it) => {
if (dirToCreate.has(parentDir + path.sep + it)) {
// already created
return null;
}
else {
return (0, fs_extra_1.mkdir)(base + path.sep + it, { recursive: true });
}
});
}, fs_1.CONCURRENCY);
}
}
exports.detectUnpackedDirs = detectUnpackedDirs;
//# sourceMappingURL=unpackDetector.js.map

File diff suppressed because one or more lines are too long

4
node_modules/app-builder-lib/out/binDownload.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export declare function download(url: string, output: string, checksum?: string | null): Promise<void>;
export declare function getBinFromCustomLoc(name: string, version: string, binariesLocUrl: string, checksum: string): Promise<string>;
export declare function getBinFromUrl(name: string, version: string, checksum: string): Promise<string>;
export declare function getBin(name: string, url?: string | null, checksum?: string | null): Promise<string>;

64
node_modules/app-builder-lib/out/binDownload.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBin = exports.getBinFromUrl = exports.getBinFromCustomLoc = exports.download = void 0;
const builder_util_1 = require("builder-util");
const versionToPromise = new Map();
function download(url, output, checksum) {
const args = ["download", "--url", url, "--output", output];
if (checksum != null) {
args.push("--sha512", checksum);
}
return (0, builder_util_1.executeAppBuilder)(args);
}
exports.download = download;
function getBinFromCustomLoc(name, version, binariesLocUrl, checksum) {
const dirName = `${name}-${version}`;
return getBin(dirName, binariesLocUrl, checksum);
}
exports.getBinFromCustomLoc = getBinFromCustomLoc;
function getBinFromUrl(name, version, checksum) {
const dirName = `${name}-${version}`;
let url;
if (process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL) {
url = process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL + "/" + dirName + ".7z";
}
else {
const baseUrl = process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_MIRROR ||
process.env.npm_config_electron_builder_binaries_mirror ||
process.env.npm_package_config_electron_builder_binaries_mirror ||
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||
"https://github.com/electron-userland/electron-builder-binaries/releases/download/";
const middleUrl = process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||
process.env.npm_config_electron_builder_binaries_custom_dir ||
process.env.npm_package_config_electron_builder_binaries_custom_dir ||
process.env.ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||
dirName;
const urlSuffix = dirName + ".7z";
url = `${baseUrl}${middleUrl}/${urlSuffix}`;
}
return getBin(dirName, url, checksum);
}
exports.getBinFromUrl = getBinFromUrl;
function getBin(name, url, checksum) {
// Old cache is ignored if cache environment variable changes
const cacheName = `${process.env.ELECTRON_BUILDER_CACHE}${name}`;
let promise = versionToPromise.get(cacheName); // if rejected, we will try to download again
if (promise != null) {
return promise;
}
promise = doGetBin(name, url, checksum);
versionToPromise.set(cacheName, promise);
return promise;
}
exports.getBin = getBin;
function doGetBin(name, url, checksum) {
const args = ["download-artifact", "--name", name];
if (url != null) {
args.push("--url", url);
}
if (checksum != null) {
args.push("--sha512", checksum);
}
return (0, builder_util_1.executeAppBuilder)(args);
}
//# sourceMappingURL=binDownload.js.map

1
node_modules/app-builder-lib/out/binDownload.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"binDownload.js","sourceRoot":"","sources":["../src/binDownload.ts"],"names":[],"mappings":";;;AAAA,+CAAgD;AAEhD,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA2B,CAAA;AAE3D,SAAgB,QAAQ,CAAC,GAAW,EAAE,MAAc,EAAE,QAAwB;IAC5E,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAA;IAC3D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,IAAA,gCAAiB,EAAC,IAAI,CAAiB,CAAA;AAChD,CAAC;AAND,4BAMC;AAED,SAAgB,mBAAmB,CAAC,IAAY,EAAE,OAAe,EAAE,cAAsB,EAAE,QAAgB;IACzG,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,OAAO,EAAE,CAAA;IACpC,OAAO,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAA;AAClD,CAAC;AAHD,kDAGC;AAED,SAAgB,aAAa,CAAC,IAAY,EAAE,OAAe,EAAE,QAAgB;IAC3E,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,OAAO,EAAE,CAAA;IACpC,IAAI,GAAW,CAAA;IACf,IAAI,OAAO,CAAC,GAAG,CAAC,+CAA+C,EAAE,CAAC;QAChE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,+CAA+C,GAAG,GAAG,GAAG,OAAO,GAAG,KAAK,CAAA;IAC3F,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,2CAA2C;YACvD,OAAO,CAAC,GAAG,CAAC,2CAA2C;YACvD,OAAO,CAAC,GAAG,CAAC,mDAAmD;YAC/D,OAAO,CAAC,GAAG,CAAC,gCAAgC;YAC5C,mFAAmF,CAAA;QACrF,MAAM,SAAS,GACb,OAAO,CAAC,GAAG,CAAC,+CAA+C;YAC3D,OAAO,CAAC,GAAG,CAAC,+CAA+C;YAC3D,OAAO,CAAC,GAAG,CAAC,uDAAuD;YACnE,OAAO,CAAC,GAAG,CAAC,oCAAoC;YAChD,OAAO,CAAA;QACT,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,CAAA;QACjC,GAAG,GAAG,GAAG,OAAO,GAAG,SAAS,IAAI,SAAS,EAAE,CAAA;IAC7C,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;AACvC,CAAC;AAvBD,sCAuBC;AAED,SAAgB,MAAM,CAAC,IAAY,EAAE,GAAmB,EAAE,QAAwB;IAChF,6DAA6D;IAC7D,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,IAAI,EAAE,CAAA;IAChE,IAAI,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA,CAAC,6CAA6C;IAE3F,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;IACvC,gBAAgB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACxC,OAAO,OAAO,CAAA;AAChB,CAAC;AAZD,wBAYC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,GAA8B,EAAE,QAAmC;IACjG,MAAM,IAAI,GAAG,CAAC,mBAAmB,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAA;IAClD,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IACzB,CAAC;IACD,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,IAAA,gCAAiB,EAAC,IAAI,CAAC,CAAA;AAChC,CAAC","sourcesContent":["import { executeAppBuilder } from \"builder-util\"\n\nconst versionToPromise = new Map<string, Promise<string>>()\n\nexport function download(url: string, output: string, checksum?: string | null): Promise<void> {\n const args = [\"download\", \"--url\", url, \"--output\", output]\n if (checksum != null) {\n args.push(\"--sha512\", checksum)\n }\n return executeAppBuilder(args) as Promise<any>\n}\n\nexport function getBinFromCustomLoc(name: string, version: string, binariesLocUrl: string, checksum: string): Promise<string> {\n const dirName = `${name}-${version}`\n return getBin(dirName, binariesLocUrl, checksum)\n}\n\nexport function getBinFromUrl(name: string, version: string, checksum: string): Promise<string> {\n const dirName = `${name}-${version}`\n let url: string\n if (process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL) {\n url = process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL + \"/\" + dirName + \".7z\"\n } else {\n const baseUrl =\n process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_MIRROR ||\n process.env.npm_config_electron_builder_binaries_mirror ||\n process.env.npm_package_config_electron_builder_binaries_mirror ||\n process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||\n \"https://github.com/electron-userland/electron-builder-binaries/releases/download/\"\n const middleUrl =\n process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||\n process.env.npm_config_electron_builder_binaries_custom_dir ||\n process.env.npm_package_config_electron_builder_binaries_custom_dir ||\n process.env.ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||\n dirName\n const urlSuffix = dirName + \".7z\"\n url = `${baseUrl}${middleUrl}/${urlSuffix}`\n }\n\n return getBin(dirName, url, checksum)\n}\n\nexport function getBin(name: string, url?: string | null, checksum?: string | null): Promise<string> {\n // Old cache is ignored if cache environment variable changes\n const cacheName = `${process.env.ELECTRON_BUILDER_CACHE}${name}`\n let promise = versionToPromise.get(cacheName) // if rejected, we will try to download again\n\n if (promise != null) {\n return promise\n }\n\n promise = doGetBin(name, url, checksum)\n versionToPromise.set(cacheName, promise)\n return promise\n}\n\nfunction doGetBin(name: string, url: string | undefined | null, checksum: string | null | undefined): Promise<string> {\n const args = [\"download-artifact\", \"--name\", name]\n if (url != null) {\n args.push(\"--url\", url)\n }\n if (checksum != null) {\n args.push(\"--sha512\", checksum)\n }\n return executeAppBuilder(args)\n}\n"]}

View File

@@ -0,0 +1,3 @@
import { TmpDir } from "temp-file";
/** @private */
export declare function importCertificate(cscLink: string, tmpDir: TmpDir, currentDir: string): Promise<string>;

51
node_modules/app-builder-lib/out/codeSign/codesign.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importCertificate = void 0;
const fs_extra_1 = require("fs-extra");
const os_1 = require("os");
const path = require("path");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const binDownload_1 = require("../binDownload");
/** @private */
async function importCertificate(cscLink, tmpDir, currentDir) {
var _a, _b;
cscLink = cscLink.trim();
let file = null;
if ((cscLink.length > 3 && cscLink[1] === ":") || cscLink.startsWith("/") || cscLink.startsWith(".")) {
file = cscLink;
}
else if (cscLink.startsWith("file://")) {
file = cscLink.substring("file://".length);
}
else if (cscLink.startsWith("~/")) {
file = path.join((0, os_1.homedir)(), cscLink.substring("~/".length));
}
else if (cscLink.startsWith("https://")) {
const tempFile = await tmpDir.getTempFile({ suffix: ".p12" });
await (0, binDownload_1.download)(cscLink, tempFile);
return tempFile;
}
else {
const mimeType = (_a = /data:.*;base64,/.exec(cscLink)) === null || _a === void 0 ? void 0 : _a[0];
if (mimeType || cscLink.length > 2048 || cscLink.endsWith("=")) {
const tempFile = await tmpDir.getTempFile({ suffix: ".p12" });
await (0, fs_extra_1.outputFile)(tempFile, Buffer.from(cscLink.substring((_b = mimeType === null || mimeType === void 0 ? void 0 : mimeType.length) !== null && _b !== void 0 ? _b : 0), "base64"));
return tempFile;
}
file = cscLink;
}
file = path.resolve(currentDir, file);
const stat = await (0, fs_1.statOrNull)(file);
if (stat == null) {
throw new builder_util_1.InvalidConfigurationError(`${file} doesn't exist`);
}
else if (!stat.isFile()) {
throw new builder_util_1.InvalidConfigurationError(`${file} not a file`);
}
else {
return file;
}
}
exports.importCertificate = importCertificate;
//# sourceMappingURL=codesign.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"codesign.js","sourceRoot":"","sources":["../../src/codeSign/codesign.ts"],"names":[],"mappings":";;;AAAA,uCAAqC;AACrC,2BAA4B;AAC5B,6BAA4B;AAE5B,+CAAwD;AACxD,4CAAgD;AAChD,gDAAyC;AAEzC,eAAe;AACR,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,MAAc,EAAE,UAAkB;;IACzF,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAExB,IAAI,IAAI,GAAkB,IAAI,CAAA;IAC9B,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrG,IAAI,GAAG,OAAO,CAAA;IAChB,CAAC;SAAM,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;SAAM,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,YAAO,GAAE,EAAE,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7D,CAAC;SAAM,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;QAC7D,MAAM,IAAA,sBAAQ,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;QACjC,OAAO,QAAQ,CAAA;IACjB,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,MAAA,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,0CAAG,CAAC,CAAC,CAAA;QACrD,IAAI,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;YAC7D,MAAM,IAAA,qBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,mCAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;YAC3F,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,IAAI,GAAG,OAAO,CAAA;IAChB,CAAC;IAED,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,MAAM,IAAA,eAAU,EAAC,IAAI,CAAC,CAAA;IACnC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,wCAAyB,CAAC,GAAG,IAAI,gBAAgB,CAAC,CAAA;IAC9D,CAAC;SAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1B,MAAM,IAAI,wCAAyB,CAAC,GAAG,IAAI,aAAa,CAAC,CAAA;IAC3D,CAAC;SAAM,CAAC;QACN,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAjCD,8CAiCC","sourcesContent":["import { outputFile } from \"fs-extra\"\nimport { homedir } from \"os\"\nimport * as path from \"path\"\nimport { TmpDir } from \"temp-file\"\nimport { InvalidConfigurationError } from \"builder-util\"\nimport { statOrNull } from \"builder-util/out/fs\"\nimport { download } from \"../binDownload\"\n\n/** @private */\nexport async function importCertificate(cscLink: string, tmpDir: TmpDir, currentDir: string): Promise<string> {\n cscLink = cscLink.trim()\n\n let file: string | null = null\n if ((cscLink.length > 3 && cscLink[1] === \":\") || cscLink.startsWith(\"/\") || cscLink.startsWith(\".\")) {\n file = cscLink\n } else if (cscLink.startsWith(\"file://\")) {\n file = cscLink.substring(\"file://\".length)\n } else if (cscLink.startsWith(\"~/\")) {\n file = path.join(homedir(), cscLink.substring(\"~/\".length))\n } else if (cscLink.startsWith(\"https://\")) {\n const tempFile = await tmpDir.getTempFile({ suffix: \".p12\" })\n await download(cscLink, tempFile)\n return tempFile\n } else {\n const mimeType = /data:.*;base64,/.exec(cscLink)?.[0]\n if (mimeType || cscLink.length > 2048 || cscLink.endsWith(\"=\")) {\n const tempFile = await tmpDir.getTempFile({ suffix: \".p12\" })\n await outputFile(tempFile, Buffer.from(cscLink.substring(mimeType?.length ?? 0), \"base64\"))\n return tempFile\n }\n file = cscLink\n }\n\n file = path.resolve(currentDir, file)\n const stat = await statOrNull(file)\n if (stat == null) {\n throw new InvalidConfigurationError(`${file} doesn't exist`)\n } else if (!stat.isFile()) {\n throw new InvalidConfigurationError(`${file} not a file`)\n } else {\n return file\n }\n}\n"]}

View File

@@ -0,0 +1,27 @@
import { TmpDir } from "builder-util/out/util";
export declare const appleCertificatePrefixes: string[];
export type CertType = "Developer ID Application" | "Developer ID Installer" | "3rd Party Mac Developer Application" | "3rd Party Mac Developer Installer" | "Mac Developer" | "Apple Development" | "Apple Distribution";
export interface CodeSigningInfo {
keychainFile?: string | null;
}
export declare function isSignAllowed(isPrintWarn?: boolean): boolean;
export declare function reportError(isMas: boolean, certificateTypes: CertType[], qualifier: string | null | undefined, keychainFile: string | null | undefined, isForceCodeSigning: boolean): Promise<void>;
export interface CreateKeychainOptions {
tmpDir: TmpDir;
cscLink: string;
cscKeyPassword: string;
cscILink?: string | null;
cscIKeyPassword?: string | null;
currentDir: string;
}
export declare function removeKeychain(keychainFile: string, printWarn?: boolean): Promise<any>;
export declare function createKeychain({ tmpDir, cscLink, cscKeyPassword, cscILink, cscIKeyPassword, currentDir }: CreateKeychainOptions): Promise<CodeSigningInfo>;
/** @private */
export declare function sign(path: string, name: string, keychain: string): Promise<any>;
export declare let findIdentityRawResult: Promise<Array<string>> | null;
export declare class Identity {
readonly name: string;
readonly hash?: string;
constructor(name: string, hash?: string);
}
export declare function findIdentity(certType: CertType, qualifier?: string | null, keychain?: string | null): Promise<Identity | null>;

View File

@@ -0,0 +1,280 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findIdentity = exports.findIdentityRawResult = exports.sign = exports.createKeychain = exports.removeKeychain = exports.reportError = exports.isSignAllowed = exports.appleCertificatePrefixes = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const util_1 = require("builder-util/out/util");
const fs_1 = require("builder-util/out/fs");
const log_1 = require("builder-util/out/log");
const crypto_1 = require("crypto");
const promises_1 = require("fs/promises");
const lazy_val_1 = require("lazy-val");
const os_1 = require("os");
const path = require("path");
const temp_file_1 = require("temp-file");
const flags_1 = require("../util/flags");
const codesign_1 = require("./codesign");
const util_identities_1 = require("@electron/osx-sign/dist/cjs/util-identities");
exports.appleCertificatePrefixes = ["Developer ID Application:", "Developer ID Installer:", "3rd Party Mac Developer Application:", "3rd Party Mac Developer Installer:"];
function isSignAllowed(isPrintWarn = true) {
if (process.platform !== "darwin") {
if (isPrintWarn) {
util_1.log.warn({ reason: "supported only on macOS" }, "skipped macOS application code signing");
}
return false;
}
const buildForPrWarning = "There are serious security concerns with CSC_FOR_PULL_REQUEST=true (see the CircleCI documentation (https://circleci.com/docs/1.0/fork-pr-builds/) for details)" +
"\nIf you have SSH keys, sensitive env vars or AWS credentials stored in your project settings and untrusted forks can make pull requests against your repo, then this option isn't for you.";
if ((0, util_1.isPullRequest)()) {
if ((0, util_1.isEnvTrue)(process.env.CSC_FOR_PULL_REQUEST)) {
if (isPrintWarn) {
util_1.log.warn(buildForPrWarning);
}
}
else {
if (isPrintWarn) {
// https://github.com/electron-userland/electron-builder/issues/1524
util_1.log.warn("Current build is a part of pull request, code signing will be skipped." + "\nSet env CSC_FOR_PULL_REQUEST to true to force code signing." + `\n${buildForPrWarning}`);
}
return false;
}
}
return true;
}
exports.isSignAllowed = isSignAllowed;
async function reportError(isMas, certificateTypes, qualifier, keychainFile, isForceCodeSigning) {
const logFields = {};
if (qualifier == null) {
logFields.reason = "";
if ((0, flags_1.isAutoDiscoveryCodeSignIdentity)()) {
logFields.reason += `cannot find valid "${certificateTypes.join(", ")}" identity${isMas ? "" : ` or custom non-Apple code signing certificate, it could cause some undefined behaviour, e.g. macOS localized description not visible`}`;
}
logFields.reason += ", see https://electron.build/code-signing";
if (!(0, flags_1.isAutoDiscoveryCodeSignIdentity)()) {
logFields.CSC_IDENTITY_AUTO_DISCOVERY = false;
}
}
else {
logFields.reason = "Identity name is specified, but no valid identity with this name in the keychain";
logFields.identity = qualifier;
}
const args = ["find-identity"];
if (keychainFile != null) {
args.push(keychainFile);
}
if (qualifier != null || (0, flags_1.isAutoDiscoveryCodeSignIdentity)()) {
logFields.allIdentities = (await (0, util_1.exec)("/usr/bin/security", args))
.trim()
.split("\n")
.filter(it => !(it.includes("Policy: X.509 Basic") || it.includes("Matching identities")))
.join("\n");
}
if (isMas || isForceCodeSigning) {
throw new Error(log_1.Logger.createMessage("skipped macOS application code signing", logFields, "error", it => it));
}
else {
util_1.log.warn(logFields, "skipped macOS application code signing");
}
}
exports.reportError = reportError;
// "Note that filename will not be searched to resolve the signing identity's certificate chain unless it is also on the user's keychain search list."
// but "security list-keychains" doesn't support add - we should 1) get current list 2) set new list - it is very bad http://stackoverflow.com/questions/10538942/add-a-keychain-to-search-list
// "overly complicated and introduces a race condition."
// https://github.com/electron-userland/electron-builder/issues/398
const bundledCertKeychainAdded = new lazy_val_1.Lazy(async () => {
// copy to temp and then atomic rename to final path
const cacheDir = getCacheDirectory();
const tmpKeychainPath = path.join(cacheDir, (0, temp_file_1.getTempName)("electron-builder-root-certs"));
const keychainPath = path.join(cacheDir, "electron-builder-root-certs.keychain");
const results = await Promise.all([
listUserKeychains(),
(0, fs_1.copyFile)(path.join(__dirname, "..", "..", "certs", "root_certs.keychain"), tmpKeychainPath).then(() => (0, promises_1.rename)(tmpKeychainPath, keychainPath)),
]);
const list = results[0];
if (!list.includes(keychainPath)) {
await (0, util_1.exec)("/usr/bin/security", ["list-keychains", "-d", "user", "-s", keychainPath].concat(list));
}
});
function getCacheDirectory() {
const env = process.env.ELECTRON_BUILDER_CACHE;
return (0, util_1.isEmptyOrSpaces)(env) ? path.join((0, os_1.homedir)(), "Library", "Caches", "electron-builder") : path.resolve(env);
}
function listUserKeychains() {
return (0, util_1.exec)("/usr/bin/security", ["list-keychains", "-d", "user"]).then(it => it
.split("\n")
.map(it => {
const r = it.trim();
return r.substring(1, r.length - 1);
})
.filter(it => it.length > 0));
}
function removeKeychain(keychainFile, printWarn = true) {
return (0, util_1.exec)("/usr/bin/security", ["delete-keychain", keychainFile]).catch((e) => {
if (printWarn) {
util_1.log.warn({ file: keychainFile, error: e.stack || e }, "cannot delete keychain");
}
return (0, fs_1.unlinkIfExists)(keychainFile);
});
}
exports.removeKeychain = removeKeychain;
async function createKeychain({ tmpDir, cscLink, cscKeyPassword, cscILink, cscIKeyPassword, currentDir }) {
// travis has correct AppleWWDRCA cert
if (process.env.TRAVIS !== "true") {
await bundledCertKeychainAdded.value;
}
// https://github.com/electron-userland/electron-builder/issues/3685
// use constant file
const keychainFile = path.join(process.env.APP_BUILDER_TMP_DIR || (0, os_1.tmpdir)(), `${(0, crypto_1.createHash)("sha256").update(currentDir).update("app-builder").digest("hex")}.keychain`);
// noinspection JSUnusedLocalSymbols
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await removeKeychain(keychainFile, false).catch(_ => {
/* ignore*/
});
const certLinks = [cscLink];
if (cscILink != null) {
certLinks.push(cscILink);
}
const certPaths = new Array(certLinks.length);
const keychainPassword = (0, crypto_1.randomBytes)(32).toString("base64");
const securityCommands = [
["create-keychain", "-p", keychainPassword, keychainFile],
["unlock-keychain", "-p", keychainPassword, keychainFile],
["set-keychain-settings", keychainFile],
];
// https://stackoverflow.com/questions/42484678/codesign-keychain-gets-ignored
// https://github.com/electron-userland/electron-builder/issues/1457
const list = await listUserKeychains();
if (!list.includes(keychainFile)) {
securityCommands.push(["list-keychains", "-d", "user", "-s", keychainFile].concat(list));
}
await Promise.all([
// we do not clear downloaded files - will be removed on tmpDir cleanup automatically. not a security issue since in any case data is available as env variables and protected by password.
bluebird_lst_1.default.map(certLinks, (link, i) => (0, codesign_1.importCertificate)(link, tmpDir, currentDir).then(it => (certPaths[i] = it))),
bluebird_lst_1.default.mapSeries(securityCommands, it => (0, util_1.exec)("/usr/bin/security", it)),
]);
return await importCerts(keychainFile, certPaths, [cscKeyPassword, cscIKeyPassword].filter(it => it != null));
}
exports.createKeychain = createKeychain;
async function importCerts(keychainFile, paths, keyPasswords) {
for (let i = 0; i < paths.length; i++) {
const password = keyPasswords[i];
await (0, util_1.exec)("/usr/bin/security", ["import", paths[i], "-k", keychainFile, "-T", "/usr/bin/codesign", "-T", "/usr/bin/productbuild", "-P", password]);
// https://stackoverflow.com/questions/39868578/security-codesign-in-sierra-keychain-ignores-access-control-settings-and-ui-p
// https://github.com/electron-userland/electron-packager/issues/701#issuecomment-322315996
await (0, util_1.exec)("/usr/bin/security", ["set-key-partition-list", "-S", "apple-tool:,apple:", "-s", "-k", password, keychainFile]);
}
return {
keychainFile,
};
}
/** @private */
function sign(path, name, keychain) {
const args = ["--deep", "--force", "--sign", name, path];
if (keychain != null) {
args.push("--keychain", keychain);
}
return (0, util_1.exec)("/usr/bin/codesign", args);
}
exports.sign = sign;
exports.findIdentityRawResult = null;
async function getValidIdentities(keychain) {
function addKeychain(args) {
if (keychain != null) {
args.push(keychain);
}
return args;
}
let result = exports.findIdentityRawResult;
if (result == null || keychain != null) {
// https://github.com/electron-userland/electron-builder/issues/481
// https://github.com/electron-userland/electron-builder/issues/535
result = Promise.all([
(0, util_1.exec)("/usr/bin/security", addKeychain(["find-identity", "-v"])).then(it => it
.trim()
.split("\n")
.filter(it => {
for (const prefix of exports.appleCertificatePrefixes) {
if (it.includes(prefix)) {
return true;
}
}
return false;
})),
(0, util_1.exec)("/usr/bin/security", addKeychain(["find-identity", "-v", "-p", "codesigning"])).then(it => it.trim().split("\n")),
]).then(it => {
const array = it[0]
.concat(it[1])
.filter(it => !it.includes("(Missing required extension)") && !it.includes("valid identities found") && !it.includes("iPhone ") && !it.includes("com.apple.idms.appleid.prd."))
// remove 1)
.map(it => it.substring(it.indexOf(")") + 1).trim());
return Array.from(new Set(array));
});
if (keychain == null) {
exports.findIdentityRawResult = result;
}
}
return result;
}
async function _findIdentity(type, qualifier, keychain) {
// https://github.com/electron-userland/electron-builder/issues/484
//noinspection SpellCheckingInspection
const lines = await getValidIdentities(keychain);
const namePrefix = `${type}:`;
for (const line of lines) {
if (qualifier != null && !line.includes(qualifier)) {
continue;
}
if (line.includes(namePrefix)) {
return parseIdentity(line);
}
}
if (type === "Developer ID Application") {
// find non-Apple certificate
// https://github.com/electron-userland/electron-builder/issues/458
l: for (const line of lines) {
if (qualifier != null && !line.includes(qualifier)) {
continue;
}
if (line.includes("Mac Developer:")) {
continue;
}
for (const prefix of exports.appleCertificatePrefixes) {
if (line.includes(prefix)) {
continue l;
}
}
return parseIdentity(line);
}
}
return null;
}
function parseIdentity(line) {
const firstQuoteIndex = line.indexOf('"');
const name = line.substring(firstQuoteIndex + 1, line.lastIndexOf('"'));
const hash = line.substring(0, firstQuoteIndex - 1);
return new util_identities_1.Identity(name, hash);
}
function findIdentity(certType, qualifier, keychain) {
let identity = qualifier || process.env.CSC_NAME;
if ((0, util_1.isEmptyOrSpaces)(identity)) {
if ((0, flags_1.isAutoDiscoveryCodeSignIdentity)()) {
return _findIdentity(certType, null, keychain);
}
else {
return Promise.resolve(null);
}
}
else {
identity = identity.trim();
for (const prefix of exports.appleCertificatePrefixes) {
checkPrefix(identity, prefix);
}
return _findIdentity(certType, identity, keychain);
}
}
exports.findIdentity = findIdentity;
function checkPrefix(name, prefix) {
if (name.startsWith(prefix)) {
throw new util_1.InvalidConfigurationError(`Please remove prefix "${prefix}" from the specified name — appropriate certificate will be chosen automatically`);
}
}
//# sourceMappingURL=macCodeSign.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,38 @@
import { WindowsConfiguration } from "../options/winOptions";
import { VmManager } from "../vm/vm";
import { WinPackager } from "../winPackager";
export declare function getSignVendorPath(): Promise<string>;
export type CustomWindowsSign = (configuration: CustomWindowsSignTaskConfiguration, packager?: WinPackager) => Promise<any>;
export interface WindowsSignOptions {
readonly path: string;
readonly name?: string | null;
readonly cscInfo?: FileCodeSigningInfo | CertificateFromStoreInfo | null;
readonly site?: string | null;
readonly options: WindowsConfiguration;
}
export interface WindowsSignTaskConfiguration extends WindowsSignOptions {
resultOutputPath?: string;
hash: string;
isNest: boolean;
}
export interface CustomWindowsSignTaskConfiguration extends WindowsSignTaskConfiguration {
computeSignToolArgs(isWin: boolean): Array<string>;
}
export declare function sign(options: WindowsSignOptions, packager: WinPackager): Promise<boolean>;
export interface FileCodeSigningInfo {
readonly file: string;
readonly password: string | null;
}
export declare function getCertInfo(file: string, password: string): Promise<CertificateInfo>;
export interface CertificateInfo {
readonly commonName: string;
readonly bloodyMicrosoftSubjectDn: string;
}
export interface CertificateFromStoreInfo {
thumbprint: string;
subject: string;
store: string;
isLocalMachineStore: boolean;
}
export declare function getCertificateFromStoreInfo(options: WindowsConfiguration, vm: VmManager): Promise<CertificateFromStoreInfo>;
export declare function doSign(configuration: CustomWindowsSignTaskConfiguration, packager: WinPackager): Promise<void>;

View File

@@ -0,0 +1,253 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isOldWin6 = exports.doSign = exports.getCertificateFromStoreInfo = exports.getCertInfo = exports.sign = exports.getSignVendorPath = void 0;
const util_1 = require("builder-util/out/util");
const binDownload_1 = require("../binDownload");
const appBuilder_1 = require("../util/appBuilder");
const bundledTool_1 = require("../util/bundledTool");
const fs_extra_1 = require("fs-extra");
const os = require("os");
const path = require("path");
const platformPackager_1 = require("../platformPackager");
const flags_1 = require("../util/flags");
const vm_1 = require("../vm/vm");
function getSignVendorPath() {
return (0, binDownload_1.getBin)("winCodeSign");
}
exports.getSignVendorPath = getSignVendorPath;
async function sign(options, packager) {
let hashes = options.options.signingHashAlgorithms;
// msi does not support dual-signing
if (options.path.endsWith(".msi")) {
hashes = [hashes != null && !hashes.includes("sha1") ? "sha256" : "sha1"];
}
else if (options.path.endsWith(".appx")) {
hashes = ["sha256"];
}
else if (hashes == null) {
hashes = ["sha1", "sha256"];
}
else {
hashes = Array.isArray(hashes) ? hashes : [hashes];
}
const executor = (await (0, platformPackager_1.resolveFunction)(packager.appInfo.type, options.options.sign, "sign")) || doSign;
let isNest = false;
for (const hash of hashes) {
const taskConfiguration = { ...options, hash, isNest };
await Promise.resolve(executor({
...taskConfiguration,
computeSignToolArgs: isWin => computeSignToolArgs(taskConfiguration, isWin),
}, packager));
isNest = true;
if (taskConfiguration.resultOutputPath != null) {
await (0, fs_extra_1.rename)(taskConfiguration.resultOutputPath, options.path);
}
}
return true;
}
exports.sign = sign;
async function getCertInfo(file, password) {
let result = null;
const errorMessagePrefix = "Cannot extract publisher name from code signing certificate. As workaround, set win.publisherName. Error: ";
try {
result = await (0, appBuilder_1.executeAppBuilderAsJson)(["certificate-info", "--input", file, "--password", password]);
}
catch (e) {
throw new Error(`${errorMessagePrefix}${e.stack || e}`);
}
if (result.error != null) {
// noinspection ExceptionCaughtLocallyJS
throw new util_1.InvalidConfigurationError(`${errorMessagePrefix}${result.error}`);
}
return result;
}
exports.getCertInfo = getCertInfo;
async function getCertificateFromStoreInfo(options, vm) {
const certificateSubjectName = options.certificateSubjectName;
const certificateSha1 = options.certificateSha1 ? options.certificateSha1.toUpperCase() : options.certificateSha1;
// ExcludeProperty doesn't work, so, we cannot exclude RawData, it is ok
// powershell can return object if the only item
const rawResult = await vm.exec("powershell.exe", [
"-NoProfile",
"-NonInteractive",
"-Command",
"Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint | ConvertTo-Json -Compress",
]);
const certList = rawResult.length === 0 ? [] : (0, util_1.asArray)(JSON.parse(rawResult));
for (const certInfo of certList) {
if ((certificateSubjectName != null && !certInfo.Subject.includes(certificateSubjectName)) ||
(certificateSha1 != null && certInfo.Thumbprint.toUpperCase() !== certificateSha1)) {
continue;
}
const parentPath = certInfo.PSParentPath;
const store = parentPath.substring(parentPath.lastIndexOf("\\") + 1);
util_1.log.debug({ store, PSParentPath: parentPath }, "auto-detect certificate store");
// https://github.com/electron-userland/electron-builder/issues/1717
const isLocalMachineStore = parentPath.includes("Certificate::LocalMachine");
util_1.log.debug(null, "auto-detect using of LocalMachine store");
return {
thumbprint: certInfo.Thumbprint,
subject: certInfo.Subject,
store,
isLocalMachineStore,
};
}
throw new Error(`Cannot find certificate ${certificateSubjectName || certificateSha1}, all certs: ${rawResult}`);
}
exports.getCertificateFromStoreInfo = getCertificateFromStoreInfo;
async function doSign(configuration, packager) {
// https://github.com/electron-userland/electron-builder/pull/1944
const timeout = parseInt(process.env.SIGNTOOL_TIMEOUT, 10) || 10 * 60 * 1000;
// decide runtime argument by cases
let args;
let env = process.env;
let vm;
const vmRequired = configuration.path.endsWith(".appx") || !("file" in configuration.cscInfo); /* certificateSubjectName and other such options */
const isWin = process.platform === "win32" || vmRequired;
const toolInfo = await getToolPath(isWin);
const tool = toolInfo.path;
if (vmRequired) {
vm = await packager.vm.value;
args = computeSignToolArgs(configuration, isWin, vm);
}
else {
vm = new vm_1.VmManager();
args = configuration.computeSignToolArgs(isWin);
if (toolInfo.env != null) {
env = toolInfo.env;
}
}
try {
await vm.exec(tool, args, { timeout, env });
}
catch (e) {
if (e.message.includes("The file is being used by another process") || e.message.includes("The specified timestamp server either could not be reached")) {
util_1.log.warn(`First attempt to code sign failed, another attempt will be made in 15 seconds: ${e.message}`);
await new Promise((resolve, reject) => {
setTimeout(() => {
vm.exec(tool, args, { timeout, env }).then(resolve).catch(reject);
}, 15000);
});
}
throw e;
}
}
exports.doSign = doSign;
// on windows be aware of http://stackoverflow.com/a/32640183/1910191
function computeSignToolArgs(options, isWin, vm = new vm_1.VmManager()) {
const inputFile = vm.toVmFile(options.path);
const outputPath = isWin ? inputFile : getOutputPath(inputFile, options.hash);
if (!isWin) {
options.resultOutputPath = outputPath;
}
const args = isWin ? ["sign"] : ["-in", inputFile, "-out", outputPath];
if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
if (isWin) {
args.push(options.isNest || options.hash === "sha256" ? "/tr" : "/t", options.isNest || options.hash === "sha256" ? options.options.rfc3161TimeStampServer || "http://timestamp.digicert.com" : timestampingServiceUrl);
}
else {
args.push("-t", timestampingServiceUrl);
}
}
const certificateFile = options.cscInfo.file;
if (certificateFile == null) {
const cscInfo = options.cscInfo;
const subjectName = cscInfo.thumbprint;
if (!isWin) {
throw new Error(`${subjectName == null ? "certificateSha1" : "certificateSubjectName"} supported only on Windows`);
}
args.push("/sha1", cscInfo.thumbprint);
args.push("/s", cscInfo.store);
if (cscInfo.isLocalMachineStore) {
args.push("/sm");
}
}
else {
const certExtension = path.extname(certificateFile);
if (certExtension === ".p12" || certExtension === ".pfx") {
args.push(isWin ? "/f" : "-pkcs12", vm.toVmFile(certificateFile));
}
else {
throw new Error(`Please specify pkcs12 (.p12/.pfx) file, ${certificateFile} is not correct`);
}
}
if (!isWin || options.hash !== "sha1") {
args.push(isWin ? "/fd" : "-h", options.hash);
if (isWin && process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
args.push("/td", "sha256");
}
}
if (options.name) {
args.push(isWin ? "/d" : "-n", options.name);
}
if (options.site) {
args.push(isWin ? "/du" : "-i", options.site);
}
// msi does not support dual-signing
if (options.isNest) {
args.push(isWin ? "/as" : "-nest");
}
const password = options.cscInfo == null ? null : options.cscInfo.password;
if (password) {
args.push(isWin ? "/p" : "-pass", password);
}
if (options.options.additionalCertificateFile) {
args.push(isWin ? "/ac" : "-ac", vm.toVmFile(options.options.additionalCertificateFile));
}
const httpsProxyFromEnv = process.env.HTTPS_PROXY;
if (!isWin && httpsProxyFromEnv != null && httpsProxyFromEnv.length) {
args.push("-p", httpsProxyFromEnv);
}
if (isWin) {
// https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610
args.push("/debug");
// must be last argument
args.push(inputFile);
}
return args;
}
function getOutputPath(inputPath, hash) {
const extension = path.extname(inputPath);
return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`);
}
/** @internal */
function isOldWin6() {
const winVersion = os.release();
return winVersion.startsWith("6.") && !winVersion.startsWith("6.3");
}
exports.isOldWin6 = isOldWin6;
function getWinSignTool(vendorPath) {
// use modern signtool on Windows Server 2012 R2 to be able to sign AppX
if (isOldWin6()) {
return path.join(vendorPath, "windows-6", "signtool.exe");
}
else {
return path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
}
}
async function getToolPath(isWin = process.platform === "win32") {
if ((0, flags_1.isUseSystemSigncode)()) {
return { path: "osslsigncode" };
}
const result = process.env.SIGNTOOL_PATH;
if (result) {
return { path: result };
}
const vendorPath = await getSignVendorPath();
if (isWin) {
// use modern signtool on Windows Server 2012 R2 to be able to sign AppX
return { path: getWinSignTool(vendorPath) };
}
else if (process.platform === "darwin") {
const toolDirPath = path.join(vendorPath, process.platform, "10.12");
return {
path: path.join(toolDirPath, "osslsigncode"),
env: (0, bundledTool_1.computeToolEnv)([path.join(toolDirPath, "lib")]),
};
}
else {
return { path: path.join(vendorPath, process.platform, "osslsigncode") };
}
}
//# sourceMappingURL=windowsCodeSign.js.map

File diff suppressed because one or more lines are too long

276
node_modules/app-builder-lib/out/configuration.d.ts generated vendored Normal file
View File

@@ -0,0 +1,276 @@
import { Arch } from "builder-util";
import { BeforeBuildContext, Target } from "./core";
import { ElectronBrandingOptions, ElectronDownloadOptions } from "./electron/ElectronFramework";
import { PrepareApplicationStageDirectoryOptions } from "./Framework";
import { AppXOptions } from "./options/AppXOptions";
import { AppImageOptions, DebOptions, FlatpakOptions, LinuxConfiguration, LinuxTargetSpecificOptions } from "./options/linuxOptions";
import { DmgOptions, MacConfiguration, MasConfiguration } from "./options/macOptions";
import { MsiOptions } from "./options/MsiOptions";
import { MsiWrappedOptions } from "./options/MsiWrappedOptions";
import { PkgOptions } from "./options/pkgOptions";
import { PlatformSpecificBuildOptions } from "./options/PlatformSpecificBuildOptions";
import { SnapOptions } from "./options/SnapOptions";
import { SquirrelWindowsOptions } from "./options/SquirrelWindowsOptions";
import { WindowsConfiguration } from "./options/winOptions";
import { BuildResult } from "./packager";
import { ArtifactBuildStarted, ArtifactCreated } from "./packagerApi";
import { PlatformPackager } from "./platformPackager";
import { NsisOptions, NsisWebOptions, PortableOptions } from "./targets/nsis/nsisOptions";
/**
* Configuration Options
*/
export interface Configuration extends PlatformSpecificBuildOptions {
/**
* The application id. Used as [CFBundleIdentifier](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070) for MacOS and as
* [Application User Model ID](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx) for Windows (NSIS target only, Squirrel.Windows not supported). It is strongly recommended that an explicit ID is set.
* @default com.electron.${name}
*/
readonly appId?: string | null;
/**
* As [name](#Metadata-name), but allows you to specify a product name for your executable which contains spaces and other special characters not allowed in the [name property](https://docs.npmjs.com/files/package.json#name).
* If not specified inside of the `build` configuration, `productName` property defined at the top level of `package.json` is used. If not specified at the top level of `package.json`, [name property](https://docs.npmjs.com/files/package.json#name) is used.
*/
readonly productName?: string | null;
/**
* The human-readable copyright line for the app.
* @default Copyright © year ${author}
*/
readonly copyright?: string | null;
readonly directories?: MetadataDirectories | null;
/**
* Options related to how build macOS targets.
*/
readonly mac?: MacConfiguration | null;
/**
* MAS (Mac Application Store) options.
*/
readonly mas?: MasConfiguration | null;
/**
* MAS (Mac Application Store) development options (`mas-dev` target).
*/
readonly masDev?: MasConfiguration | null;
/**
* macOS DMG options.
*/
readonly dmg?: DmgOptions | null;
/**
* macOS PKG options.
*/
readonly pkg?: PkgOptions | null;
/**
* Options related to how build Windows targets.
*/
readonly win?: WindowsConfiguration | null;
readonly nsis?: NsisOptions | null;
readonly nsisWeb?: NsisWebOptions | null;
readonly portable?: PortableOptions | null;
readonly appx?: AppXOptions | null;
/** @private */
readonly msi?: MsiOptions | null;
/** @private */
readonly msiWrapped?: MsiWrappedOptions | null;
readonly squirrelWindows?: SquirrelWindowsOptions | null;
/**
* Options related to how build Linux targets.
*/
readonly linux?: LinuxConfiguration | null;
/**
* Debian package options.
*/
readonly deb?: DebOptions | null;
/**
* Snap options.
*/
readonly snap?: SnapOptions | null;
/**
* AppImage options.
*/
readonly appImage?: AppImageOptions | null;
/**
* Flatpak options.
*/
readonly flatpak?: FlatpakOptions | null;
readonly pacman?: LinuxTargetSpecificOptions | null;
readonly rpm?: LinuxTargetSpecificOptions | null;
readonly freebsd?: LinuxTargetSpecificOptions | null;
readonly p5p?: LinuxTargetSpecificOptions | null;
readonly apk?: LinuxTargetSpecificOptions | null;
/**
* Whether to include *all* of the submodules node_modules directories
* @default false
*/
includeSubNodeModules?: boolean;
/**
* Whether to build the application native dependencies from source.
* @default false
*/
buildDependenciesFromSource?: boolean;
/**
* Whether to execute `node-gyp rebuild` before starting to package the app.
*
* Don't [use](https://github.com/electron-userland/electron-builder/issues/683#issuecomment-241214075) [npm](http://electron.atom.io/docs/tutorial/using-native-node-modules/#using-npm) (neither `.npmrc`) for configuring electron headers. Use `electron-builder node-gyp-rebuild` instead.
* @default false
*/
readonly nodeGypRebuild?: boolean;
/**
* Additional command line arguments to use when installing app native deps.
*/
readonly npmArgs?: Array<string> | string | null;
/**
* Whether to [rebuild](https://docs.npmjs.com/cli/rebuild) native dependencies before starting to package the app.
* @default true
*/
readonly npmRebuild?: boolean;
/**
* The build number. Maps to the `--iteration` flag for builds using FPM on Linux.
* If not defined, then it will fallback to `BUILD_NUMBER` or `TRAVIS_BUILD_NUMBER` or `APPVEYOR_BUILD_NUMBER` or `CIRCLE_BUILD_NUM` or `BUILD_BUILDNUMBER` or `CI_PIPELINE_IID` env.
*/
readonly buildNumber?: string | null;
/**
* The build version. Maps to the `CFBundleVersion` on macOS, and `FileVersion` metadata property on Windows. Defaults to the `version`.
* If `buildVersion` is not defined and `buildNumber` (or one of the `buildNumber` envs) is defined, it will be used as a build version (`version.buildNumber`).
*/
readonly buildVersion?: string | null;
/**
* Whether to download the alternate FFmpeg library from Electron's release assets and replace the default FFmpeg library prior to signing
*/
readonly downloadAlternateFFmpeg?: boolean;
/**
* Whether to use [electron-compile](http://github.com/electron/electron-compile) to compile app. Defaults to `true` if `electron-compile` in the dependencies. And `false` if in the `devDependencies` or doesn't specified.
*/
readonly electronCompile?: boolean;
/**
* Returns the path to custom Electron build (e.g. `~/electron/out/R`). Zip files must follow the pattern `electron-v${version}-${platformName}-${arch}.zip`, otherwise it will be assumed to be an unpacked Electron app directory
*/
readonly electronDist?: string | ((options: PrepareApplicationStageDirectoryOptions) => string);
/**
* The [electron-download](https://github.com/electron-userland/electron-download#usage) options.
*/
readonly electronDownload?: ElectronDownloadOptions;
/**
* The branding used by Electron's distributables. This is needed if a fork has modified Electron's BRANDING.json file.
*/
readonly electronBranding?: ElectronBrandingOptions;
/**
* The version of electron you are packaging for. Defaults to version of `electron`, `electron-prebuilt` or `electron-prebuilt-compile` dependency.
*/
electronVersion?: string | null;
/**
* The name of a built-in configuration preset (currently, only `react-cra` is supported) or any number of paths to config files (relative to project dir).
*
* The latter allows to mixin a config from multiple other configs, as if you `Object.assign` them, but properly combine `files` glob patterns.
*
* If `react-scripts` in the app dependencies, `react-cra` will be set automatically. Set to `null` to disable automatic detection.
*/
extends?: Array<string> | string | null;
/**
* Inject properties to `package.json`.
*/
readonly extraMetadata?: any;
/**
* Whether to fail if the application is not signed (to prevent unsigned app if code signing configuration is not correct).
* @default false
*/
readonly forceCodeSigning?: boolean;
/**
* *libui-based frameworks only* The version of NodeJS you are packaging for.
* You can set it to `current` to set the Node.js version that you use to run.
*/
readonly nodeVersion?: string | null;
/**
* *libui-based frameworks only* The version of LaunchUI you are packaging for. Applicable for Windows only. Defaults to version suitable for used framework version.
*/
readonly launchUiVersion?: boolean | string | null;
/**
* The framework name. One of `electron`, `proton`, `libui`. Defaults to `electron`.
*/
readonly framework?: string | null;
/**
* The function (or path to file or module id) to be [run before pack](#beforepack)
*/
readonly beforePack?: ((context: BeforePackContext) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be [run after pack](#afterpack) (but before pack into distributable format and sign).
*/
readonly afterPack?: ((context: AfterPackContext) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be [run after pack and sign](#aftersign) (but before pack into distributable format).
*/
readonly afterSign?: ((context: AfterPackContext) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be run on artifact build start.
*/
readonly artifactBuildStarted?: ((context: ArtifactBuildStarted) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be run on artifact build completed.
*/
readonly artifactBuildCompleted?: ((context: ArtifactCreated) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be [run after all artifacts are build](#afterAllArtifactBuild).
*/
readonly afterAllArtifactBuild?: ((context: BuildResult) => Promise<Array<string>> | Array<string>) | string | null;
/**
* MSI project created on disk - not packed into .msi package yet.
*/
readonly msiProjectCreated?: ((path: string) => Promise<any> | any) | string | null;
/**
* Appx manifest created on disk - not packed into .appx package yet.
*/
readonly appxManifestCreated?: ((path: string) => Promise<any> | any) | string | null;
/**
* The function (or path to file or module id) to be [run on each node module](#onnodemodulefile) file. Returning `true`/`false` will determine whether to force include or to use the default copier logic
*/
readonly onNodeModuleFile?: ((path: string) => void | boolean) | string | null;
/**
* The function (or path to file or module id) to be run before dependencies are installed or rebuilt. Works when `npmRebuild` is set to `true`. Resolving to `false` will skip dependencies install or rebuild.
*
* If provided and `node_modules` are missing, it will not invoke production dependencies check.
*/
readonly beforeBuild?: ((context: BeforeBuildContext) => Promise<any>) | string | null;
/**
* Whether to include PDB files.
* @default false
*/
readonly includePdb?: boolean;
/**
* Whether to remove `scripts` field from `package.json` files.
*
* @default true
*/
readonly removePackageScripts?: boolean;
/**
* Whether to remove `keywords` field from `package.json` files.
*
* @default true
*/
readonly removePackageKeywords?: boolean;
}
interface PackContext {
readonly outDir: string;
readonly appOutDir: string;
readonly packager: PlatformPackager<any>;
readonly electronPlatformName: string;
readonly arch: Arch;
readonly targets: Array<Target>;
}
export type AfterPackContext = PackContext;
export type BeforePackContext = PackContext;
export interface MetadataDirectories {
/**
* The path to build resources.
*
* Please note — build resources are not packed into the app. If you need to use some files, e.g. as tray icon, please include required files explicitly: `"files": ["**\/*", "build/icon.*"]`
* @default build
*/
readonly buildResources?: string | null;
/**
* The output directory. [File macros](/file-patterns#file-macros) are supported.
* @default dist
*/
readonly output?: string | null;
/**
* The application directory (containing the application package.json), defaults to `app`, `www` or working directory.
*/
readonly app?: string | null;
}
export {};

3
node_modules/app-builder-lib/out/configuration.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=configuration.js.map

File diff suppressed because one or more lines are too long

60
node_modules/app-builder-lib/out/core.d.ts generated vendored Normal file
View File

@@ -0,0 +1,60 @@
/// <reference types="node" />
import { Arch, ArchType } from "builder-util";
import { AllPublishOptions } from "builder-util-runtime";
export type Publish = AllPublishOptions | Array<AllPublishOptions> | null;
export type TargetConfigType = Array<string | TargetConfiguration> | string | TargetConfiguration | null;
export interface TargetConfiguration {
/**
* The target name. e.g. `snap`.
*/
readonly target: string;
/**
* The arch or list of archs.
*/
readonly arch?: Array<ArchType> | ArchType;
}
export declare class Platform {
name: string;
buildConfigurationKey: string;
nodeName: NodeJS.Platform;
static MAC: Platform;
static LINUX: Platform;
static WINDOWS: Platform;
constructor(name: string, buildConfigurationKey: string, nodeName: NodeJS.Platform);
toString(): string;
createTarget(type?: string | Array<string> | null, ...archs: Array<Arch>): Map<Platform, Map<Arch, Array<string>>>;
static current(): Platform;
static fromString(name: string): Platform;
}
export declare abstract class Target {
readonly name: string;
readonly isAsyncSupported: boolean;
abstract readonly outDir: string;
abstract readonly options: TargetSpecificOptions | null | undefined;
protected constructor(name: string, isAsyncSupported?: boolean);
checkOptions(): Promise<any>;
abstract build(appOutDir: string, arch: Arch): Promise<any>;
finishBuild(): Promise<any>;
}
export interface TargetSpecificOptions {
/**
The [artifact file name template](/configuration/configuration#artifact-file-name-template).
*/
readonly artifactName?: string | null;
publish?: Publish;
}
export declare const DEFAULT_TARGET = "default";
export declare const DIR_TARGET = "dir";
export type CompressionLevel = "store" | "normal" | "maximum";
export interface BeforeBuildContext {
readonly appDir: string;
readonly electronVersion: string;
readonly platform: Platform;
readonly arch: string;
}
export interface SourceRepositoryInfo {
type?: string;
domain?: string;
user: string;
project: string;
}

63
node_modules/app-builder-lib/out/core.js generated vendored Normal file
View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DIR_TARGET = exports.DEFAULT_TARGET = exports.Target = exports.Platform = void 0;
const builder_util_1 = require("builder-util");
class Platform {
constructor(name, buildConfigurationKey, nodeName) {
this.name = name;
this.buildConfigurationKey = buildConfigurationKey;
this.nodeName = nodeName;
}
toString() {
return this.name;
}
createTarget(type, ...archs) {
if (type == null && (archs == null || archs.length === 0)) {
return new Map([[this, new Map()]]);
}
const archToType = new Map();
for (const arch of archs == null || archs.length === 0 ? [(0, builder_util_1.archFromString)(process.arch)] : archs) {
archToType.set(arch, type == null ? [] : Array.isArray(type) ? type : [type]);
}
return new Map([[this, archToType]]);
}
static current() {
return Platform.fromString(process.platform);
}
static fromString(name) {
name = name.toLowerCase();
switch (name) {
case Platform.MAC.nodeName:
case Platform.MAC.name:
return Platform.MAC;
case Platform.WINDOWS.nodeName:
case Platform.WINDOWS.name:
case Platform.WINDOWS.buildConfigurationKey:
return Platform.WINDOWS;
case Platform.LINUX.nodeName:
return Platform.LINUX;
default:
throw new Error(`Unknown platform: ${name}`);
}
}
}
exports.Platform = Platform;
Platform.MAC = new Platform("mac", "mac", "darwin");
Platform.LINUX = new Platform("linux", "linux", "linux");
Platform.WINDOWS = new Platform("windows", "win", "win32");
class Target {
constructor(name, isAsyncSupported = true) {
this.name = name;
this.isAsyncSupported = isAsyncSupported;
}
async checkOptions() {
// ignore
}
finishBuild() {
return Promise.resolve();
}
}
exports.Target = Target;
exports.DEFAULT_TARGET = "default";
exports.DIR_TARGET = "dir";
//# sourceMappingURL=core.js.map

1
node_modules/app-builder-lib/out/core.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import { Configuration } from "../configuration";
import { Framework } from "../Framework";
import { Packager } from "../index";
export type ElectronPlatformName = "darwin" | "linux" | "win32" | "mas";
/**
* Electron distributables branding options.
* @see [Electron BRANDING.json](https://github.com/electron/electron/blob/master/shell/app/BRANDING.json).
*/
export interface ElectronBrandingOptions {
projectName?: string;
productName?: string;
}
export declare function createBrandingOpts(opts: Configuration): Required<ElectronBrandingOptions>;
export interface ElectronDownloadOptions {
version?: string;
/**
* The [cache location](https://github.com/electron-userland/electron-download#cache-location).
*/
cache?: string | null;
/**
* The mirror.
*/
mirror?: string | null;
/** @private */
customDir?: string | null;
/** @private */
customFilename?: string | null;
strictSSL?: boolean;
isVerifyChecksum?: boolean;
platform?: ElectronPlatformName;
arch?: string;
}
export declare function createElectronFrameworkSupport(configuration: Configuration, packager: Packager): Promise<Framework>;

View File

@@ -0,0 +1,171 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createElectronFrameworkSupport = exports.createBrandingOpts = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const index_1 = require("../index");
const pathManager_1 = require("../util/pathManager");
const electronMac_1 = require("./electronMac");
const electronVersion_1 = require("./electronVersion");
const fs = require("fs/promises");
const injectFFMPEG_1 = require("./injectFFMPEG");
function createBrandingOpts(opts) {
var _a, _b;
return {
projectName: ((_a = opts.electronBranding) === null || _a === void 0 ? void 0 : _a.projectName) || "electron",
productName: ((_b = opts.electronBranding) === null || _b === void 0 ? void 0 : _b.productName) || "Electron",
};
}
exports.createBrandingOpts = createBrandingOpts;
function createDownloadOpts(opts, platform, arch, electronVersion) {
return {
platform,
arch,
version: electronVersion,
...opts.electronDownload,
};
}
async function beforeCopyExtraFiles(options) {
const { appOutDir, packager } = options;
const electronBranding = createBrandingOpts(packager.config);
if (packager.platform === index_1.Platform.LINUX) {
const linuxPackager = packager;
const executable = path.join(appOutDir, linuxPackager.executableName);
await (0, fs_extra_1.rename)(path.join(appOutDir, electronBranding.projectName), executable);
}
else if (packager.platform === index_1.Platform.WINDOWS) {
const executable = path.join(appOutDir, `${packager.appInfo.productFilename}.exe`);
await (0, fs_extra_1.rename)(path.join(appOutDir, `${electronBranding.projectName}.exe`), executable);
}
else {
await (0, electronMac_1.createMacApp)(packager, appOutDir, options.asarIntegrity, options.platformName === "mas");
}
await removeUnusedLanguagesIfNeeded(options);
}
async function removeUnusedLanguagesIfNeeded(options) {
const { packager: { config, platformSpecificBuildOptions }, } = options;
const wantedLanguages = (0, builder_util_1.asArray)(platformSpecificBuildOptions.electronLanguages || config.electronLanguages);
if (!wantedLanguages.length) {
return;
}
const { dir, langFileExt } = getLocalesConfig(options);
// noinspection SpellCheckingInspection
await bluebird_lst_1.default.map((0, fs_extra_1.readdir)(dir), file => {
if (!file.endsWith(langFileExt)) {
return;
}
const language = file.substring(0, file.length - langFileExt.length);
if (!wantedLanguages.includes(language)) {
return fs.rm(path.join(dir, file), { recursive: true, force: true });
}
return;
}, fs_1.CONCURRENCY);
function getLocalesConfig(options) {
const { appOutDir, packager } = options;
if (packager.platform === index_1.Platform.MAC) {
return { dir: packager.getResourcesDir(appOutDir), langFileExt: ".lproj" };
}
else {
return { dir: path.join(packager.getResourcesDir(appOutDir), "..", "locales"), langFileExt: ".pak" };
}
}
}
class ElectronFramework {
constructor(name, version, distMacOsAppName) {
this.name = name;
this.version = version;
this.distMacOsAppName = distMacOsAppName;
// noinspection JSUnusedGlobalSymbols
this.macOsDefaultTargets = ["zip", "dmg"];
// noinspection JSUnusedGlobalSymbols
this.defaultAppIdPrefix = "com.electron.";
// noinspection JSUnusedGlobalSymbols
this.isCopyElevateHelper = true;
// noinspection JSUnusedGlobalSymbols
this.isNpmRebuildRequired = true;
}
getDefaultIcon(platform) {
if (platform === index_1.Platform.LINUX) {
return path.join((0, pathManager_1.getTemplatePath)("icons"), "electron-linux");
}
else {
// default icon is embedded into app skeleton
return null;
}
}
async prepareApplicationStageDirectory(options) {
await unpack(options, createDownloadOpts(options.packager.config, options.platformName, options.arch, this.version), this.distMacOsAppName);
if (options.packager.config.downloadAlternateFFmpeg) {
await (0, injectFFMPEG_1.default)(options, this.version);
}
}
beforeCopyExtraFiles(options) {
return beforeCopyExtraFiles(options);
}
}
async function createElectronFrameworkSupport(configuration, packager) {
let version = configuration.electronVersion;
if (version == null) {
// for prepacked app asar no dev deps in the app.asar
if (packager.isPrepackedAppAsar) {
version = await (0, electronVersion_1.getElectronVersionFromInstalled)(packager.projectDir);
if (version == null) {
throw new Error(`Cannot compute electron version for prepacked asar`);
}
}
else {
version = await (0, electronVersion_1.computeElectronVersion)(packager.projectDir);
}
configuration.electronVersion = version;
}
const branding = createBrandingOpts(configuration);
return new ElectronFramework(branding.projectName, version, `${branding.productName}.app`);
}
exports.createElectronFrameworkSupport = createElectronFrameworkSupport;
async function unpack(prepareOptions, options, distMacOsAppName) {
const { packager, appOutDir, platformName } = prepareOptions;
const electronDist = packager.config.electronDist;
let dist = typeof electronDist === "function" ? electronDist(prepareOptions) : electronDist;
if (dist != null) {
const zipFile = `electron-v${options.version}-${platformName}-${options.arch}.zip`;
const resolvedDist = path.isAbsolute(dist) ? dist : path.resolve(packager.projectDir, dist);
if ((await (0, fs_1.statOrNull)(path.join(resolvedDist, zipFile))) != null) {
builder_util_1.log.info({ resolvedDist, zipFile }, "resolved electronDist");
options.cache = resolvedDist;
dist = null;
}
}
let isFullCleanup = false;
if (dist == null) {
await (0, builder_util_1.executeAppBuilder)(["unpack-electron", "--configuration", JSON.stringify([options]), "--output", appOutDir, "--distMacOsAppName", distMacOsAppName]);
}
else {
isFullCleanup = true;
const source = packager.getElectronSrcDir(dist);
const destination = packager.getElectronDestinationDir(appOutDir);
builder_util_1.log.info({ source, destination }, "copying Electron");
await (0, fs_extra_1.emptyDir)(appOutDir);
await (0, fs_1.copyDir)(source, destination, {
isUseHardLink: fs_1.DO_NOT_USE_HARD_LINKS,
});
}
await cleanupAfterUnpack(prepareOptions, distMacOsAppName, isFullCleanup);
}
function cleanupAfterUnpack(prepareOptions, distMacOsAppName, isFullCleanup) {
const out = prepareOptions.appOutDir;
const isMac = prepareOptions.packager.platform === index_1.Platform.MAC;
const resourcesPath = isMac ? path.join(out, distMacOsAppName, "Contents", "Resources") : path.join(out, "resources");
return Promise.all([
isFullCleanup ? (0, fs_1.unlinkIfExists)(path.join(resourcesPath, "default_app.asar")) : Promise.resolve(),
isFullCleanup ? (0, fs_1.unlinkIfExists)(path.join(out, "version")) : Promise.resolve(),
isMac
? Promise.resolve()
: (0, fs_extra_1.rename)(path.join(out, "LICENSE"), path.join(out, "LICENSE.electron.txt")).catch(() => {
/* ignore */
}),
]);
}
//# sourceMappingURL=ElectronFramework.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,269 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMacApp = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const promises_1 = require("fs/promises");
const path = require("path");
const appInfo_1 = require("../appInfo");
const platformPackager_1 = require("../platformPackager");
const appBuilder_1 = require("../util/appBuilder");
const ElectronFramework_1 = require("./ElectronFramework");
function doRename(basePath, oldName, newName) {
return (0, promises_1.rename)(path.join(basePath, oldName), path.join(basePath, newName));
}
function moveHelpers(helperSuffixes, frameworksPath, appName, prefix) {
return bluebird_lst_1.default.map(helperSuffixes, suffix => {
const executableBasePath = path.join(frameworksPath, `${prefix}${suffix}.app`, "Contents", "MacOS");
return doRename(executableBasePath, `${prefix}${suffix}`, appName + suffix).then(() => doRename(frameworksPath, `${prefix}${suffix}.app`, `${appName}${suffix}.app`));
});
}
function getAvailableHelperSuffixes(helperEHPlist, helperNPPlist, helperRendererPlist, helperPluginPlist, helperGPUPlist) {
const result = [" Helper"];
if (helperEHPlist != null) {
result.push(" Helper EH");
}
if (helperNPPlist != null) {
result.push(" Helper NP");
}
if (helperRendererPlist != null) {
result.push(" Helper (Renderer)");
}
if (helperPluginPlist != null) {
result.push(" Helper (Plugin)");
}
if (helperGPUPlist != null) {
result.push(" Helper (GPU)");
}
return result;
}
/** @internal */
async function createMacApp(packager, appOutDir, asarIntegrity, isMas) {
var _a, _b;
const appInfo = packager.appInfo;
// Electon uses the application name (CFBundleName) to resolve helper apps
// https://github.com/electron/electron/blob/main/shell/app/electron_main_delegate_mac.mm
// https://github.com/electron-userland/electron-builder/issues/6962
const appFilename = appInfo.sanitizedProductName;
const electronBranding = (0, ElectronFramework_1.createBrandingOpts)(packager.config);
const contentsPath = path.join(appOutDir, packager.info.framework.distMacOsAppName, "Contents");
const frameworksPath = path.join(contentsPath, "Frameworks");
const loginItemPath = path.join(contentsPath, "Library", "LoginItems");
const appPlistFilename = path.join(contentsPath, "Info.plist");
const helperPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper.app`, "Contents", "Info.plist");
const helperEHPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper EH.app`, "Contents", "Info.plist");
const helperNPPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper NP.app`, "Contents", "Info.plist");
const helperRendererPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper (Renderer).app`, "Contents", "Info.plist");
const helperPluginPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper (Plugin).app`, "Contents", "Info.plist");
const helperGPUPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper (GPU).app`, "Contents", "Info.plist");
const helperLoginPlistFilename = path.join(loginItemPath, `${electronBranding.productName} Login Helper.app`, "Contents", "Info.plist");
const plistContent = await (0, appBuilder_1.executeAppBuilderAsJson)([
"decode-plist",
"-f",
appPlistFilename,
"-f",
helperPlistFilename,
"-f",
helperEHPlistFilename,
"-f",
helperNPPlistFilename,
"-f",
helperRendererPlistFilename,
"-f",
helperPluginPlistFilename,
"-f",
helperGPUPlistFilename,
"-f",
helperLoginPlistFilename,
]);
if (plistContent[0] == null) {
throw new Error("corrupted Electron dist");
}
const appPlist = plistContent[0];
const helperPlist = plistContent[1];
const helperEHPlist = plistContent[2];
const helperNPPlist = plistContent[3];
const helperRendererPlist = plistContent[4];
const helperPluginPlist = plistContent[5];
const helperGPUPlist = plistContent[6];
const helperLoginPlist = plistContent[7];
// if an extend-info file was supplied, copy its contents in first
if (plistContent[8] != null) {
Object.assign(appPlist, plistContent[8]);
}
const buildMetadata = packager.config;
/**
* Configure bundleIdentifier for the generic Electron Helper process
*
* This was the only Helper in Electron 5 and before. Allow users to configure
* the bundleIdentifier for continuity.
*/
const oldHelperBundleId = buildMetadata["helper-bundle-id"];
if (oldHelperBundleId != null) {
builder_util_1.log.warn("build.helper-bundle-id is deprecated, please set as build.mac.helperBundleId");
}
const defaultAppId = packager.platformSpecificBuildOptions.appId;
const cfBundleIdentifier = (0, appInfo_1.filterCFBundleIdentifier)((isMas ? (_a = packager.config.mas) === null || _a === void 0 ? void 0 : _a.appId : defaultAppId) || defaultAppId || appInfo.macBundleIdentifier);
const defaultHelperId = packager.platformSpecificBuildOptions.helperBundleId;
const helperBundleIdentifier = (0, appInfo_1.filterCFBundleIdentifier)((isMas ? (_b = packager.config.mas) === null || _b === void 0 ? void 0 : _b.helperBundleId : defaultHelperId) || defaultHelperId || oldHelperBundleId || `${cfBundleIdentifier}.helper`);
appPlist.CFBundleIdentifier = cfBundleIdentifier;
await packager.applyCommonInfo(appPlist, contentsPath);
// required for electron-updater proxy
if (!isMas) {
configureLocalhostAts(appPlist);
}
helperPlist.CFBundleExecutable = `${appFilename} Helper`;
helperPlist.CFBundleDisplayName = `${appInfo.productName} Helper`;
helperPlist.CFBundleIdentifier = helperBundleIdentifier;
helperPlist.CFBundleVersion = appPlist.CFBundleVersion;
/**
* Configure bundleIdentifier for Electron 5+ Helper processes
*
* In Electron 6, parts of the generic Electron Helper process were split into
* individual helper processes. Allow users to configure the bundleIdentifiers
* for continuity, specifically because macOS keychain access relies on
* bundleIdentifiers not changing (i.e. across versions of Electron).
*/
function configureHelper(helper, postfix, userProvidedBundleIdentifier) {
helper.CFBundleExecutable = `${appFilename} Helper ${postfix}`;
helper.CFBundleDisplayName = `${appInfo.productName} Helper ${postfix}`;
helper.CFBundleIdentifier = userProvidedBundleIdentifier
? (0, appInfo_1.filterCFBundleIdentifier)(userProvidedBundleIdentifier)
: (0, appInfo_1.filterCFBundleIdentifier)(`${helperBundleIdentifier}.${postfix}`);
helper.CFBundleVersion = appPlist.CFBundleVersion;
}
if (helperRendererPlist != null) {
configureHelper(helperRendererPlist, "(Renderer)", packager.platformSpecificBuildOptions.helperRendererBundleId);
}
if (helperPluginPlist != null) {
configureHelper(helperPluginPlist, "(Plugin)", packager.platformSpecificBuildOptions.helperPluginBundleId);
}
if (helperGPUPlist != null) {
configureHelper(helperGPUPlist, "(GPU)", packager.platformSpecificBuildOptions.helperGPUBundleId);
}
if (helperEHPlist != null) {
configureHelper(helperEHPlist, "EH", packager.platformSpecificBuildOptions.helperEHBundleId);
}
if (helperNPPlist != null) {
configureHelper(helperNPPlist, "NP", packager.platformSpecificBuildOptions.helperNPBundleId);
}
if (helperLoginPlist != null) {
helperLoginPlist.CFBundleExecutable = `${appFilename} Login Helper`;
helperLoginPlist.CFBundleDisplayName = `${appInfo.productName} Login Helper`;
// noinspection SpellCheckingInspection
helperLoginPlist.CFBundleIdentifier = `${cfBundleIdentifier}.loginhelper`;
helperLoginPlist.CFBundleVersion = appPlist.CFBundleVersion;
}
const protocols = (0, builder_util_1.asArray)(buildMetadata.protocols).concat((0, builder_util_1.asArray)(packager.platformSpecificBuildOptions.protocols));
if (protocols.length > 0) {
appPlist.CFBundleURLTypes = protocols.map(protocol => {
const schemes = (0, builder_util_1.asArray)(protocol.schemes);
if (schemes.length === 0) {
throw new builder_util_1.InvalidConfigurationError(`Protocol "${protocol.name}": must be at least one scheme specified`);
}
return {
CFBundleURLName: protocol.name,
CFBundleTypeRole: protocol.role || "Editor",
CFBundleURLSchemes: schemes.slice(),
};
});
}
const fileAssociations = packager.fileAssociations;
if (fileAssociations.length > 0) {
const documentTypes = await bluebird_lst_1.default.map(fileAssociations, async (fileAssociation) => {
const extensions = (0, builder_util_1.asArray)(fileAssociation.ext).map(platformPackager_1.normalizeExt);
const customIcon = await packager.getResource((0, builder_util_1.getPlatformIconFileName)(fileAssociation.icon, true), `${extensions[0]}.icns`);
let iconFile = appPlist.CFBundleIconFile;
if (customIcon != null) {
iconFile = path.basename(customIcon);
await (0, fs_1.copyOrLinkFile)(customIcon, path.join(path.join(contentsPath, "Resources"), iconFile));
}
const result = {
CFBundleTypeExtensions: extensions,
CFBundleTypeName: fileAssociation.name || extensions[0],
CFBundleTypeRole: fileAssociation.role || "Editor",
LSHandlerRank: fileAssociation.rank || "Default",
CFBundleTypeIconFile: iconFile,
};
if (fileAssociation.isPackage) {
result.LSTypeIsPackage = true;
}
return result;
});
// `CFBundleDocumentTypes` may be defined in `mac.extendInfo`, so we need to merge it in that case
appPlist.CFBundleDocumentTypes = [...(appPlist.CFBundleDocumentTypes || []), ...documentTypes];
}
if (asarIntegrity != null) {
appPlist.ElectronAsarIntegrity = asarIntegrity;
}
const plistDataToWrite = {
[appPlistFilename]: appPlist,
[helperPlistFilename]: helperPlist,
};
if (helperEHPlist != null) {
plistDataToWrite[helperEHPlistFilename] = helperEHPlist;
}
if (helperNPPlist != null) {
plistDataToWrite[helperNPPlistFilename] = helperNPPlist;
}
if (helperRendererPlist != null) {
plistDataToWrite[helperRendererPlistFilename] = helperRendererPlist;
}
if (helperPluginPlist != null) {
plistDataToWrite[helperPluginPlistFilename] = helperPluginPlist;
}
if (helperGPUPlist != null) {
plistDataToWrite[helperGPUPlistFilename] = helperGPUPlist;
}
if (helperLoginPlist != null) {
plistDataToWrite[helperLoginPlistFilename] = helperLoginPlist;
}
await Promise.all([
(0, appBuilder_1.executeAppBuilderAndWriteJson)(["encode-plist"], plistDataToWrite),
doRename(path.join(contentsPath, "MacOS"), electronBranding.productName, appPlist.CFBundleExecutable),
(0, fs_1.unlinkIfExists)(path.join(appOutDir, "LICENSE")),
(0, fs_1.unlinkIfExists)(path.join(appOutDir, "LICENSES.chromium.html")),
]);
await moveHelpers(getAvailableHelperSuffixes(helperEHPlist, helperNPPlist, helperRendererPlist, helperPluginPlist, helperGPUPlist), frameworksPath, appFilename, electronBranding.productName);
if (helperLoginPlist != null) {
const prefix = electronBranding.productName;
const suffix = " Login Helper";
const executableBasePath = path.join(loginItemPath, `${prefix}${suffix}.app`, "Contents", "MacOS");
await doRename(executableBasePath, `${prefix}${suffix}`, appFilename + suffix).then(() => doRename(loginItemPath, `${prefix}${suffix}.app`, `${appFilename}${suffix}.app`));
}
const appPath = path.join(appOutDir, `${appInfo.productFilename}.app`);
await (0, promises_1.rename)(path.dirname(contentsPath), appPath);
// https://github.com/electron-userland/electron-builder/issues/840
const now = Date.now() / 1000;
await (0, promises_1.utimes)(appPath, now, now);
}
exports.createMacApp = createMacApp;
function configureLocalhostAts(appPlist) {
// https://bencoding.com/2015/07/20/app-transport-security-and-localhost/
let ats = appPlist.NSAppTransportSecurity;
if (ats == null) {
ats = {};
appPlist.NSAppTransportSecurity = ats;
}
ats.NSAllowsLocalNetworking = true;
// https://github.com/electron-userland/electron-builder/issues/3377#issuecomment-446035814
ats.NSAllowsArbitraryLoads = true;
let exceptionDomains = ats.NSExceptionDomains;
if (exceptionDomains == null) {
exceptionDomains = {};
ats.NSExceptionDomains = exceptionDomains;
}
if (exceptionDomains.localhost == null) {
const allowHttp = {
NSTemporaryExceptionAllowsInsecureHTTPSLoads: false,
NSIncludesSubdomains: false,
NSTemporaryExceptionAllowsInsecureHTTPLoads: true,
NSTemporaryExceptionMinimumTLSVersion: "1.0",
NSTemporaryExceptionRequiresForwardSecrecy: false,
};
exceptionDomains.localhost = allowHttp;
exceptionDomains["127.0.0.1"] = allowHttp;
}
}
//# sourceMappingURL=electronMac.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { Lazy } from "lazy-val";
import { Configuration } from "../configuration";
export type MetadataValue = Lazy<{
[key: string]: any;
} | null>;
export declare function getElectronVersion(projectDir: string, config?: Configuration): Promise<string>;
export declare function getElectronVersionFromInstalled(projectDir: string): Promise<string | null>;
export declare function getElectronPackage(projectDir: string): Promise<any>;

Some files were not shown because too many files have changed in this diff Show More