Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64cbc83faf | ||
|
|
691e4ef31d | ||
|
|
d902197267 | ||
|
|
72a3f83d27 | ||
|
|
3cc1365561 | ||
|
|
3a30fe989b | ||
|
|
01dfb8a963 | ||
|
|
3bd173d61a | ||
|
|
479e80d6a9 | ||
|
|
2c3c1a60a0 | ||
|
|
cdcbe58dee | ||
|
|
38ab1c3559 | ||
|
|
9f86e1dd2b | ||
|
|
a626463c95 | ||
|
|
36b58a84f3 | ||
|
|
fe8d99e31d | ||
|
|
d336231787 | ||
|
|
6cd85d89aa | ||
|
|
7b1c610963 | ||
|
|
060a299188 | ||
|
|
64420e4c32 | ||
|
|
2b411ce98b | ||
|
|
e80eed7f69 | ||
|
|
6053796432 | ||
|
|
0a1d0f916c | ||
|
|
1d4e198ba5 | ||
|
|
2f06fcab5f | ||
|
|
e7d57dd1e5 | ||
|
|
d9b1b4104f | ||
|
|
b445fa1974 | ||
|
|
f265d1f5e2 | ||
|
|
a05a5e6db8 | ||
|
|
8c55e5b4f3 | ||
|
|
477a52e779 | ||
|
|
107823fc2c | ||
|
|
621083fe1f | ||
|
|
0770238718 |
@@ -20,7 +20,7 @@ version: 2.1
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
macos:
|
macos:
|
||||||
xcode: "12.2.0"
|
xcode: "13.4.1"
|
||||||
<<: *steps-test
|
<<: *steps-test
|
||||||
|
|
||||||
release:
|
release:
|
||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- *step-restore-cache
|
- *step-restore-cache
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn --frozen-lockfile
|
||||||
|
- run: npx @continuous-auth/circleci-oidc-github-auth@1.0.5
|
||||||
- run: npx semantic-release
|
- run: npx semantic-release
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
@@ -42,5 +43,6 @@ workflows:
|
|||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- main
|
||||||
|
context: cfa-release
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"@semantic-release/release-notes-generator",
|
"@semantic-release/release-notes-generator",
|
||||||
"@continuous-auth/semantic-release-npm",
|
"@continuous-auth/semantic-release-npm",
|
||||||
"@semantic-release/github"
|
"@semantic-release/github"
|
||||||
]
|
],
|
||||||
|
"branches": [ "main" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Create universal macOS Electron applications
|
> Create universal macOS Electron applications
|
||||||
|
|
||||||
[](https://circleci.com/gh/electron/universal)
|
[](https://circleci.com/gh/electron/universal)
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
24
package.json
24
package.json
@@ -10,6 +10,10 @@
|
|||||||
"apple silicon",
|
"apple silicon",
|
||||||
"universal"
|
"universal"
|
||||||
],
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/electron/universal.git"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
@@ -27,8 +31,11 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@continuous-auth/semantic-release-npm": "^2.0.0",
|
"@continuous-auth/semantic-release-npm": "^2.0.0",
|
||||||
|
"@types/debug": "^4.1.5",
|
||||||
"@types/fs-extra": "^9.0.4",
|
"@types/fs-extra": "^9.0.4",
|
||||||
|
"@types/minimatch": "^3.0.5",
|
||||||
"@types/node": "^14.14.7",
|
"@types/node": "^14.14.7",
|
||||||
|
"@types/plist": "^3.0.2",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"lint-staged": "^10.5.1",
|
"lint-staged": "^10.5.1",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
@@ -36,9 +43,22 @@
|
|||||||
"typescript": "^4.0.5"
|
"typescript": "^4.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@electron/asar": "^3.2.1",
|
||||||
"@malept/cross-spawn-promise": "^1.1.0",
|
"@malept/cross-spawn-promise": "^1.1.0",
|
||||||
"asar": "^3.0.3",
|
"debug": "^4.3.1",
|
||||||
"dir-compare": "^2.4.0",
|
"dir-compare": "^2.4.0",
|
||||||
"fs-extra": "^9.0.1"
|
"fs-extra": "^9.0.1",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"plist": "^3.0.4"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.ts": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
206
src/asar-utils.ts
Normal file
206
src/asar-utils.ts
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import * as asar from '@electron/asar';
|
||||||
|
import { execFileSync } from 'child_process';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as fs from 'fs-extra';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as minimatch from 'minimatch';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { d } from './debug';
|
||||||
|
|
||||||
|
const LIPO = 'lipo';
|
||||||
|
|
||||||
|
export enum AsarMode {
|
||||||
|
NO_ASAR,
|
||||||
|
HAS_ASAR,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MergeASARsOptions = {
|
||||||
|
x64AsarPath: string;
|
||||||
|
arm64AsarPath: string;
|
||||||
|
outputAsarPath: string;
|
||||||
|
|
||||||
|
singleArchFiles?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// See: https://github.com/apple-opensource-mirror/llvmCore/blob/0c60489d96c87140db9a6a14c6e82b15f5e5d252/include/llvm/Object/MachOFormat.h#L108-L112
|
||||||
|
const MACHO_MAGIC = new Set([
|
||||||
|
// 32-bit Mach-O
|
||||||
|
0xfeedface,
|
||||||
|
0xcefaedfe,
|
||||||
|
|
||||||
|
// 64-bit Mach-O
|
||||||
|
0xfeedfacf,
|
||||||
|
0xcffaedfe,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const detectAsarMode = async (appPath: string) => {
|
||||||
|
d('checking asar mode of', appPath);
|
||||||
|
const asarPath = path.resolve(appPath, 'Contents', 'Resources', 'app.asar');
|
||||||
|
|
||||||
|
if (!(await fs.pathExists(asarPath))) {
|
||||||
|
d('determined no asar');
|
||||||
|
return AsarMode.NO_ASAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
d('determined has asar');
|
||||||
|
return AsarMode.HAS_ASAR;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateAsarIntegrity = (asarPath: string) => {
|
||||||
|
return {
|
||||||
|
algorithm: 'SHA256' as const,
|
||||||
|
hash: crypto
|
||||||
|
.createHash('SHA256')
|
||||||
|
.update(asar.getRawHeader(asarPath).headerString)
|
||||||
|
.digest('hex'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function toRelativePath(file: string): string {
|
||||||
|
return file.replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDirectory(a: string, file: string): boolean {
|
||||||
|
return Boolean('files' in asar.statFile(a, file));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSingleArch(archive: string, file: string, allowList?: string): void {
|
||||||
|
if (allowList === undefined || !minimatch(file, allowList, { matchBase: true })) {
|
||||||
|
throw new Error(
|
||||||
|
`Detected unique file "${file}" in "${archive}" not covered by ` +
|
||||||
|
`allowList rule: "${allowList}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mergeASARs = async ({
|
||||||
|
x64AsarPath,
|
||||||
|
arm64AsarPath,
|
||||||
|
outputAsarPath,
|
||||||
|
singleArchFiles,
|
||||||
|
}: MergeASARsOptions): Promise<void> => {
|
||||||
|
d(`merging ${x64AsarPath} and ${arm64AsarPath}`);
|
||||||
|
|
||||||
|
const x64Files = new Set(asar.listPackage(x64AsarPath).map(toRelativePath));
|
||||||
|
const arm64Files = new Set(asar.listPackage(arm64AsarPath).map(toRelativePath));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Build set of unpacked directories and files
|
||||||
|
//
|
||||||
|
|
||||||
|
const unpackedFiles = new Set<string>();
|
||||||
|
|
||||||
|
function buildUnpacked(a: string, fileList: Set<string>): void {
|
||||||
|
for (const file of fileList) {
|
||||||
|
const stat = asar.statFile(a, file);
|
||||||
|
|
||||||
|
if (!('unpacked' in stat) || !stat.unpacked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('files' in stat) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unpackedFiles.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildUnpacked(x64AsarPath, x64Files);
|
||||||
|
buildUnpacked(arm64AsarPath, arm64Files);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Build list of files/directories unique to each asar
|
||||||
|
//
|
||||||
|
|
||||||
|
for (const file of x64Files) {
|
||||||
|
if (!arm64Files.has(file)) {
|
||||||
|
checkSingleArch(x64AsarPath, file, singleArchFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const arm64Unique = [];
|
||||||
|
for (const file of arm64Files) {
|
||||||
|
if (!x64Files.has(file)) {
|
||||||
|
checkSingleArch(arm64AsarPath, file, singleArchFiles);
|
||||||
|
arm64Unique.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find common bindings with different content
|
||||||
|
//
|
||||||
|
|
||||||
|
const commonBindings = [];
|
||||||
|
for (const file of x64Files) {
|
||||||
|
if (!arm64Files.has(file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip directories
|
||||||
|
if (isDirectory(x64AsarPath, file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x64Content = asar.extractFile(x64AsarPath, file);
|
||||||
|
const arm64Content = asar.extractFile(arm64AsarPath, file);
|
||||||
|
|
||||||
|
if (x64Content.compare(arm64Content) === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MACHO_MAGIC.has(x64Content.readUInt32LE(0))) {
|
||||||
|
throw new Error(`Can't reconcile two non-macho files ${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
commonBindings.push(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Extract both
|
||||||
|
//
|
||||||
|
|
||||||
|
const x64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'x64-'));
|
||||||
|
const arm64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'arm64-'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
d(`extracting ${x64AsarPath} to ${x64Dir}`);
|
||||||
|
asar.extractAll(x64AsarPath, x64Dir);
|
||||||
|
|
||||||
|
d(`extracting ${arm64AsarPath} to ${arm64Dir}`);
|
||||||
|
asar.extractAll(arm64AsarPath, arm64Dir);
|
||||||
|
|
||||||
|
for (const file of arm64Unique) {
|
||||||
|
const source = path.resolve(arm64Dir, file);
|
||||||
|
const destination = path.resolve(x64Dir, file);
|
||||||
|
|
||||||
|
if (isDirectory(arm64AsarPath, file)) {
|
||||||
|
d(`creating unique directory: ${file}`);
|
||||||
|
await fs.mkdirp(destination);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
d(`xopying unique file: ${file}`);
|
||||||
|
await fs.mkdirp(path.dirname(destination));
|
||||||
|
await fs.copy(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const binding of commonBindings) {
|
||||||
|
const source = await fs.realpath(path.resolve(arm64Dir, binding));
|
||||||
|
const destination = await fs.realpath(path.resolve(x64Dir, binding));
|
||||||
|
|
||||||
|
d(`merging binding: ${binding}`);
|
||||||
|
execFileSync(LIPO, [source, destination, '-create', '-output', destination]);
|
||||||
|
}
|
||||||
|
|
||||||
|
d(`creating archive at ${outputAsarPath}`);
|
||||||
|
|
||||||
|
const resolvedUnpack = Array.from(unpackedFiles).map((file) => path.join(x64Dir, file));
|
||||||
|
|
||||||
|
await asar.createPackageWithOptions(x64Dir, outputAsarPath, {
|
||||||
|
unpack: `{${resolvedUnpack.join(',')}}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
d('done merging');
|
||||||
|
} finally {
|
||||||
|
await Promise.all([fs.remove(x64Dir), fs.remove(arm64Dir)]);
|
||||||
|
}
|
||||||
|
};
|
||||||
3
src/debug.ts
Normal file
3
src/debug.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import * as debug from 'debug';
|
||||||
|
|
||||||
|
export const d = debug('electron-universal');
|
||||||
73
src/file-utils.ts
Normal file
73
src/file-utils.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { spawn, ExitCodeError } from '@malept/cross-spawn-promise';
|
||||||
|
import * as fs from 'fs-extra';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const MACHO_PREFIX = 'Mach-O ';
|
||||||
|
|
||||||
|
export enum AppFileType {
|
||||||
|
MACHO,
|
||||||
|
PLAIN,
|
||||||
|
INFO_PLIST,
|
||||||
|
SNAPSHOT,
|
||||||
|
APP_CODE,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppFile = {
|
||||||
|
relativePath: string;
|
||||||
|
type: AppFileType;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param appPath Path to the application
|
||||||
|
*/
|
||||||
|
export const getAllAppFiles = async (appPath: string): Promise<AppFile[]> => {
|
||||||
|
const files: AppFile[] = [];
|
||||||
|
|
||||||
|
const visited = new Set<string>();
|
||||||
|
const traverse = async (p: string) => {
|
||||||
|
p = await fs.realpath(p);
|
||||||
|
if (visited.has(p)) return;
|
||||||
|
visited.add(p);
|
||||||
|
|
||||||
|
const info = await fs.stat(p);
|
||||||
|
if (info.isSymbolicLink()) return;
|
||||||
|
if (info.isFile()) {
|
||||||
|
let fileType = AppFileType.PLAIN;
|
||||||
|
|
||||||
|
var fileOutput = '';
|
||||||
|
try {
|
||||||
|
fileOutput = await spawn('file', ['--brief', '--no-pad', p]);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ExitCodeError) {
|
||||||
|
/* silently accept error codes from "file" */
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p.includes('app.asar')) {
|
||||||
|
fileType = AppFileType.APP_CODE;
|
||||||
|
} else if (fileOutput.startsWith(MACHO_PREFIX)) {
|
||||||
|
fileType = AppFileType.MACHO;
|
||||||
|
} else if (p.endsWith('.bin')) {
|
||||||
|
fileType = AppFileType.SNAPSHOT;
|
||||||
|
} else if (path.basename(p) === 'Info.plist') {
|
||||||
|
fileType = AppFileType.INFO_PLIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push({
|
||||||
|
relativePath: path.relative(appPath, p),
|
||||||
|
type: fileType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.isDirectory()) {
|
||||||
|
for (const child of await fs.readdir(p)) {
|
||||||
|
await traverse(path.resolve(p, child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await traverse(appPath);
|
||||||
|
|
||||||
|
return files;
|
||||||
|
};
|
||||||
316
src/index.ts
316
src/index.ts
@@ -1,14 +1,19 @@
|
|||||||
import { spawn } from '@malept/cross-spawn-promise';
|
import { spawn } from '@malept/cross-spawn-promise';
|
||||||
import * as asar from 'asar';
|
import * as asar from '@electron/asar';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
|
import * as minimatch from 'minimatch';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as plist from 'plist';
|
||||||
import * as dircompare from 'dir-compare';
|
import * as dircompare from 'dir-compare';
|
||||||
|
|
||||||
const MACHO_PREFIX = 'Mach-O ';
|
import { AppFile, AppFileType, getAllAppFiles } from './file-utils';
|
||||||
|
import { AsarMode, detectAsarMode, generateAsarIntegrity, mergeASARs } from './asar-utils';
|
||||||
|
import { sha } from './sha';
|
||||||
|
import { d } from './debug';
|
||||||
|
|
||||||
type MakeUniversalOpts = {
|
export type MakeUniversalOpts = {
|
||||||
/**
|
/**
|
||||||
* Absolute file system path to the x64 version of your application. E.g. /Foo/bar/MyApp_x64.app
|
* Absolute file system path to the x64 version of your application. E.g. /Foo/bar/MyApp_x64.app
|
||||||
*/
|
*/
|
||||||
@@ -27,80 +32,26 @@ type MakeUniversalOpts = {
|
|||||||
* Forcefully overwrite any existing files that are in the way of generating the universal application
|
* Forcefully overwrite any existing files that are in the way of generating the universal application
|
||||||
*/
|
*/
|
||||||
force: boolean;
|
force: boolean;
|
||||||
|
/**
|
||||||
|
* Merge x64 and arm64 ASARs into one.
|
||||||
|
*/
|
||||||
|
mergeASARs?: boolean;
|
||||||
|
/**
|
||||||
|
* Minimatch pattern of paths that are allowed to be present in one of the ASAR files, but not in the other.
|
||||||
|
*/
|
||||||
|
singleArchFiles?: string;
|
||||||
|
/**
|
||||||
|
* Minimatch pattern of binaries that are expected to be the same x64 binary in both of the ASAR files.
|
||||||
|
*/
|
||||||
|
x64ArchFiles?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AsarMode {
|
const dupedFiles = (files: AppFile[]) =>
|
||||||
NO_ASAR,
|
files.filter((f) => f.type !== AppFileType.SNAPSHOT && f.type !== AppFileType.APP_CODE);
|
||||||
HAS_ASAR,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const detectAsarMode = async (appPath: string) => {
|
|
||||||
const asarPath = path.resolve(appPath, 'Contents', 'Resources', 'app.asar');
|
|
||||||
|
|
||||||
if (!(await fs.pathExists(asarPath))) return AsarMode.NO_ASAR;
|
|
||||||
|
|
||||||
return AsarMode.HAS_ASAR;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AppFileType {
|
|
||||||
MACHO,
|
|
||||||
PLAIN,
|
|
||||||
SNAPSHOT,
|
|
||||||
APP_CODE,
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppFile = {
|
|
||||||
relativePath: string;
|
|
||||||
type: AppFileType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAllFiles = async (appPath: string): Promise<AppFile[]> => {
|
|
||||||
const files: AppFile[] = [];
|
|
||||||
|
|
||||||
const visited = new Set<string>();
|
|
||||||
const traverse = async (p: string) => {
|
|
||||||
p = await fs.realpath(p);
|
|
||||||
if (visited.has(p)) return;
|
|
||||||
visited.add(p);
|
|
||||||
|
|
||||||
const info = await fs.stat(p);
|
|
||||||
if (info.isSymbolicLink()) return;
|
|
||||||
if (info.isFile()) {
|
|
||||||
let fileType = AppFileType.PLAIN;
|
|
||||||
|
|
||||||
const fileOutput = await spawn('file', ['--brief', '--no-pad', p]);
|
|
||||||
if (p.includes('app.asar')) {
|
|
||||||
fileType = AppFileType.APP_CODE;
|
|
||||||
} else if (fileOutput.startsWith(MACHO_PREFIX)) {
|
|
||||||
fileType = AppFileType.MACHO;
|
|
||||||
} else if (p.endsWith('.bin')) {
|
|
||||||
fileType = AppFileType.SNAPSHOT;
|
|
||||||
}
|
|
||||||
|
|
||||||
files.push({
|
|
||||||
relativePath: path.relative(appPath, p),
|
|
||||||
type: fileType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.isDirectory()) {
|
|
||||||
for (const child of await fs.readdir(p)) {
|
|
||||||
await traverse(path.resolve(p, child));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await traverse(appPath);
|
|
||||||
|
|
||||||
return files;
|
|
||||||
};
|
|
||||||
|
|
||||||
const dupedFiles = (files: AppFile[]) => files.filter(f => f.type !== AppFileType.SNAPSHOT && f.type !== AppFileType.APP_CODE);
|
|
||||||
|
|
||||||
const sha = async (filePath: string) => {
|
|
||||||
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> => {
|
export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> => {
|
||||||
|
d('making a universal app with options', opts);
|
||||||
|
|
||||||
if (process.platform !== 'darwin')
|
if (process.platform !== 'darwin')
|
||||||
throw new Error('@electron/universal is only supported on darwin platforms');
|
throw new Error('@electron/universal is only supported on darwin platforms');
|
||||||
if (!opts.x64AppPath || !path.isAbsolute(opts.x64AppPath))
|
if (!opts.x64AppPath || !path.isAbsolute(opts.x64AppPath))
|
||||||
@@ -111,17 +62,21 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
throw new Error('Expected opts.outAppPath to be an absolute path but it was not');
|
throw new Error('Expected opts.outAppPath to be an absolute path but it was not');
|
||||||
|
|
||||||
if (await fs.pathExists(opts.outAppPath)) {
|
if (await fs.pathExists(opts.outAppPath)) {
|
||||||
|
d('output path exists already');
|
||||||
if (!opts.force) {
|
if (!opts.force) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The out path "${opts.outAppPath}" already exists and force is not set to true`,
|
`The out path "${opts.outAppPath}" already exists and force is not set to true`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
d('overwriting existing application because force == true');
|
||||||
await fs.remove(opts.outAppPath);
|
await fs.remove(opts.outAppPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const x64AsarMode = await detectAsarMode(opts.x64AppPath);
|
const x64AsarMode = await detectAsarMode(opts.x64AppPath);
|
||||||
const arm64AsarMode = await detectAsarMode(opts.arm64AppPath);
|
const arm64AsarMode = await detectAsarMode(opts.arm64AppPath);
|
||||||
|
d('detected x64AsarMode =', x64AsarMode);
|
||||||
|
d('detected arm64AsarMode =', arm64AsarMode);
|
||||||
|
|
||||||
if (x64AsarMode !== arm64AsarMode)
|
if (x64AsarMode !== arm64AsarMode)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -129,23 +84,28 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-universal-'));
|
const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-universal-'));
|
||||||
|
d('building universal app in', tmpDir);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
d('copying x64 app as starter template');
|
||||||
const tmpApp = path.resolve(tmpDir, 'Tmp.app');
|
const tmpApp = path.resolve(tmpDir, 'Tmp.app');
|
||||||
await spawn('cp', ['-R', opts.x64AppPath, tmpApp]);
|
await spawn('cp', ['-R', opts.x64AppPath, tmpApp]);
|
||||||
|
|
||||||
const uniqueToX64: string[] = [];
|
const uniqueToX64: string[] = [];
|
||||||
const uniqueToArm64: string[] = [];
|
const uniqueToArm64: string[] = [];
|
||||||
const x64Files = await getAllFiles(await fs.realpath(tmpApp));
|
const x64Files = await getAllAppFiles(await fs.realpath(tmpApp));
|
||||||
const arm64Files = await getAllFiles(opts.arm64AppPath);
|
const arm64Files = await getAllAppFiles(await fs.realpath(opts.arm64AppPath));
|
||||||
|
|
||||||
for (const file of dupedFiles(x64Files)) {
|
for (const file of dupedFiles(x64Files)) {
|
||||||
if (!arm64Files.some(f => f.relativePath === file.relativePath)) uniqueToX64.push(file.relativePath);
|
if (!arm64Files.some((f) => f.relativePath === file.relativePath))
|
||||||
|
uniqueToX64.push(file.relativePath);
|
||||||
}
|
}
|
||||||
for (const file of dupedFiles(arm64Files)) {
|
for (const file of dupedFiles(arm64Files)) {
|
||||||
if (!x64Files.some(f => f.relativePath === file.relativePath)) uniqueToArm64.push(file.relativePath);
|
if (!x64Files.some((f) => f.relativePath === file.relativePath))
|
||||||
|
uniqueToArm64.push(file.relativePath);
|
||||||
}
|
}
|
||||||
if (uniqueToX64.length !== 0 || uniqueToArm64.length !== 0) {
|
if (uniqueToX64.length !== 0 || uniqueToArm64.length !== 0) {
|
||||||
|
d('some files were not in both builds, aborting');
|
||||||
console.error({
|
console.error({
|
||||||
uniqueToX64,
|
uniqueToX64,
|
||||||
uniqueToArm64,
|
uniqueToArm64,
|
||||||
@@ -155,73 +115,227 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const file of x64Files.filter(f => f.type === AppFileType.PLAIN)) {
|
for (const file of x64Files.filter((f) => f.type === AppFileType.PLAIN)) {
|
||||||
const x64Sha = await sha(path.resolve(opts.x64AppPath, file.relativePath));
|
const x64Sha = await sha(path.resolve(opts.x64AppPath, file.relativePath));
|
||||||
const arm64Sha = await sha(path.resolve(opts.arm64AppPath, file.relativePath));
|
const arm64Sha = await sha(path.resolve(opts.arm64AppPath, file.relativePath));
|
||||||
if (x64Sha !== arm64Sha) {
|
if (x64Sha !== arm64Sha) {
|
||||||
console.error(`${x64Sha} !== ${arm64Sha}`);
|
d('SHA for file', file.relativePath, `does not match across builds ${x64Sha}!=${arm64Sha}`);
|
||||||
throw new Error(`Expected all non-binary files to have identical SHAs when creating a universal build but "${file.relativePath}" did not`);
|
// The MainMenu.nib files generated by Xcode13 are deterministic in effect but not deterministic in generated sequence
|
||||||
|
if (path.basename(path.dirname(file.relativePath)) === 'MainMenu.nib') {
|
||||||
|
// The mismatch here is OK so we just move on to the next one
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Expected all non-binary files to have identical SHAs when creating a universal build but "${file.relativePath}" did not`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const machOFile of x64Files.filter(f => f.type === AppFileType.MACHO)) {
|
for (const machOFile of x64Files.filter((f) => f.type === AppFileType.MACHO)) {
|
||||||
|
const first = await fs.realpath(path.resolve(tmpApp, machOFile.relativePath));
|
||||||
|
const second = await fs.realpath(path.resolve(opts.arm64AppPath, machOFile.relativePath));
|
||||||
|
|
||||||
|
const x64Sha = await sha(path.resolve(opts.x64AppPath, machOFile.relativePath));
|
||||||
|
const arm64Sha = await sha(path.resolve(opts.arm64AppPath, machOFile.relativePath));
|
||||||
|
if (x64Sha === arm64Sha) {
|
||||||
|
if (
|
||||||
|
opts.x64ArchFiles === undefined ||
|
||||||
|
!minimatch(machOFile.relativePath, opts.x64ArchFiles, { matchBase: true })
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Detected file "${machOFile.relativePath}" that's the same in both x64 and arm64 builds and not covered by the ` +
|
||||||
|
`x64ArchFiles rule: "${opts.x64ArchFiles}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
d(
|
||||||
|
'SHA for Mach-O file',
|
||||||
|
machOFile.relativePath,
|
||||||
|
`matches across builds ${x64Sha}===${arm64Sha}, skipping lipo`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
d('joining two MachO files with lipo', {
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
});
|
||||||
await spawn('lipo', [
|
await spawn('lipo', [
|
||||||
await fs.realpath(path.resolve(tmpApp, machOFile.relativePath)),
|
first,
|
||||||
await fs.realpath(path.resolve(opts.arm64AppPath, machOFile.relativePath)),
|
second,
|
||||||
'-create',
|
'-create',
|
||||||
'-output',
|
'-output',
|
||||||
await fs.realpath(path.resolve(tmpApp, machOFile.relativePath)),
|
await fs.realpath(path.resolve(tmpApp, machOFile.relativePath)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we don't have an ASAR we need to check if the two "app" folders are identical, if
|
||||||
|
* they are then we can just leave one there and call it a day. If the app folders for x64
|
||||||
|
* and arm64 are different though we need to rename each folder and create a new fake "app"
|
||||||
|
* entrypoint to dynamically load the correct app folder
|
||||||
|
*/
|
||||||
if (x64AsarMode === AsarMode.NO_ASAR) {
|
if (x64AsarMode === AsarMode.NO_ASAR) {
|
||||||
const comparison = dircompare.compareSync(path.resolve(tmpApp, 'Contents', 'Resources', 'app'), path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'), { compareSize: true, compareContent: true });
|
d('checking if the x64 and arm64 app folders are identical');
|
||||||
|
const comparison = await dircompare.compare(
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app'),
|
||||||
|
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'),
|
||||||
|
{ compareSize: true, compareContent: true },
|
||||||
|
);
|
||||||
|
|
||||||
if (!comparison.same) {
|
if (!comparison.same) {
|
||||||
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64'));
|
d('x64 and arm64 app folders are different, creating dynamic entry ASAR');
|
||||||
await fs.copy(path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64'));
|
await fs.move(
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app'),
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64'),
|
||||||
|
);
|
||||||
|
await fs.copy(
|
||||||
|
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'),
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64'),
|
||||||
|
);
|
||||||
|
|
||||||
const entryAsar = path.resolve(tmpDir, 'entry-asar');
|
const entryAsar = path.resolve(tmpDir, 'entry-asar');
|
||||||
await fs.mkdir(entryAsar);
|
await fs.mkdir(entryAsar);
|
||||||
await fs.copy(path.resolve(__dirname, '..', '..', 'entry-asar', 'no-asar.js'), path.resolve(entryAsar, 'index.js'));
|
await fs.copy(
|
||||||
let pj = await fs.readJson(path.resolve(opts.x64AppPath, 'Contents', 'Resources', 'app', 'package.json'));
|
path.resolve(__dirname, '..', '..', 'entry-asar', 'no-asar.js'),
|
||||||
|
path.resolve(entryAsar, 'index.js'),
|
||||||
|
);
|
||||||
|
let pj = await fs.readJson(
|
||||||
|
path.resolve(opts.x64AppPath, 'Contents', 'Resources', 'app', 'package.json'),
|
||||||
|
);
|
||||||
pj.main = 'index.js';
|
pj.main = 'index.js';
|
||||||
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
||||||
await asar.createPackage(entryAsar, path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
|
await asar.createPackage(
|
||||||
|
entryAsar,
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
d('x64 and arm64 app folders are the same');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x64AsarMode === AsarMode.HAS_ASAR) {
|
const generatedIntegrity: Record<string, { algorithm: 'SHA256'; hash: string }> = {};
|
||||||
|
let didSplitAsar = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we have an ASAR we just need to check if the two "app.asar" files have the same hash,
|
||||||
|
* if they are, same as above, we can leave one there and call it a day. If they're different
|
||||||
|
* we have to make a dynamic entrypoint. There is an assumption made here that every file in
|
||||||
|
* app.asar.unpacked is a native node module. This assumption _may_ not be true so we should
|
||||||
|
* look at codifying that assumption as actual logic.
|
||||||
|
*/
|
||||||
|
// FIXME: Codify the assumption that app.asar.unpacked only contains native modules
|
||||||
|
if (x64AsarMode === AsarMode.HAS_ASAR && opts.mergeASARs) {
|
||||||
|
d('merging x64 and arm64 asars');
|
||||||
|
const output = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar');
|
||||||
|
await mergeASARs({
|
||||||
|
x64AsarPath: path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
arm64AsarPath: path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
outputAsarPath: output,
|
||||||
|
singleArchFiles: opts.singleArchFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(output);
|
||||||
|
} else if (x64AsarMode === AsarMode.HAS_ASAR) {
|
||||||
|
d('checking if the x64 and arm64 asars are identical');
|
||||||
const x64AsarSha = await sha(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
|
const x64AsarSha = await sha(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
|
||||||
const arm64AsarSha = await sha(path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'));
|
const arm64AsarSha = await sha(
|
||||||
|
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
);
|
||||||
|
|
||||||
if (x64AsarSha !== arm64AsarSha) {
|
if (x64AsarSha !== arm64AsarSha) {
|
||||||
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar'));
|
didSplitAsar = true;
|
||||||
|
d('x64 and arm64 asars are different');
|
||||||
|
const x64AsarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar');
|
||||||
|
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'), x64AsarPath);
|
||||||
const x64Unpacked = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar.unpacked');
|
const x64Unpacked = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar.unpacked');
|
||||||
if (await fs.pathExists(x64Unpacked)) {
|
if (await fs.pathExists(x64Unpacked)) {
|
||||||
await fs.move(x64Unpacked, path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar.unpacked'));
|
await fs.move(
|
||||||
|
x64Unpacked,
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar.unpacked'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.copy(path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar'));
|
const arm64AsarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar');
|
||||||
const arm64Unpacked = path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar.unpacked');
|
await fs.copy(
|
||||||
|
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
arm64AsarPath,
|
||||||
|
);
|
||||||
|
const arm64Unpacked = path.resolve(
|
||||||
|
opts.arm64AppPath,
|
||||||
|
'Contents',
|
||||||
|
'Resources',
|
||||||
|
'app.asar.unpacked',
|
||||||
|
);
|
||||||
if (await fs.pathExists(arm64Unpacked)) {
|
if (await fs.pathExists(arm64Unpacked)) {
|
||||||
await fs.copy(arm64Unpacked, path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar.unpacked'));
|
await fs.copy(
|
||||||
|
arm64Unpacked,
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar.unpacked'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entryAsar = path.resolve(tmpDir, 'entry-asar');
|
const entryAsar = path.resolve(tmpDir, 'entry-asar');
|
||||||
await fs.mkdir(entryAsar);
|
await fs.mkdir(entryAsar);
|
||||||
await fs.copy(path.resolve(__dirname, '..', '..', 'entry-asar', 'has-asar.js'), path.resolve(entryAsar, 'index.js'));
|
await fs.copy(
|
||||||
let pj = JSON.parse((await asar.extractFile(path.resolve(opts.x64AppPath, 'Contents', 'Resources', 'app.asar'), 'package.json')).toString('utf8'));
|
path.resolve(__dirname, '..', '..', 'entry-asar', 'has-asar.js'),
|
||||||
|
path.resolve(entryAsar, 'index.js'),
|
||||||
|
);
|
||||||
|
let pj = JSON.parse(
|
||||||
|
(
|
||||||
|
await asar.extractFile(
|
||||||
|
path.resolve(opts.x64AppPath, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
'package.json',
|
||||||
|
)
|
||||||
|
).toString('utf8'),
|
||||||
|
);
|
||||||
pj.main = 'index.js';
|
pj.main = 'index.js';
|
||||||
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
||||||
await asar.createPackage(entryAsar, path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
|
const asarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar');
|
||||||
|
await asar.createPackage(entryAsar, asarPath);
|
||||||
|
|
||||||
|
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(asarPath);
|
||||||
|
generatedIntegrity['Resources/app-x64.asar'] = generateAsarIntegrity(x64AsarPath);
|
||||||
|
generatedIntegrity['Resources/app-arm64.asar'] = generateAsarIntegrity(arm64AsarPath);
|
||||||
|
} else {
|
||||||
|
d('x64 and arm64 asars are the same');
|
||||||
|
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const snapshotsFile of arm64Files.filter(f => f.type === AppFileType.SNAPSHOT)) {
|
const plistFiles = x64Files.filter((f) => f.type === AppFileType.INFO_PLIST);
|
||||||
await fs.copy(path.resolve(opts.arm64AppPath, snapshotsFile.relativePath), path.resolve(tmpApp, snapshotsFile.relativePath));
|
for (const plistFile of plistFiles) {
|
||||||
|
const x64PlistPath = path.resolve(opts.x64AppPath, plistFile.relativePath);
|
||||||
|
const arm64PlistPath = path.resolve(opts.arm64AppPath, plistFile.relativePath);
|
||||||
|
|
||||||
|
const { ElectronAsarIntegrity: x64Integrity, ...x64Plist } = plist.parse(
|
||||||
|
await fs.readFile(x64PlistPath, 'utf8'),
|
||||||
|
) as any;
|
||||||
|
const { ElectronAsarIntegrity: arm64Integrity, ...arm64Plist } = plist.parse(
|
||||||
|
await fs.readFile(arm64PlistPath, 'utf8'),
|
||||||
|
) as any;
|
||||||
|
if (JSON.stringify(x64Plist) !== JSON.stringify(arm64Plist)) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected all Info.plist files to be identical when ignoring integrity when creating a universal build but "${plistFile.relativePath}" was not`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mergedPlist = { ...x64Plist, ElectronAsarIntegrity: generatedIntegrity };
|
||||||
|
|
||||||
|
await fs.writeFile(path.resolve(tmpApp, plistFile.relativePath), plist.build(mergedPlist));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const snapshotsFile of arm64Files.filter((f) => f.type === AppFileType.SNAPSHOT)) {
|
||||||
|
d('copying snapshot file', snapshotsFile.relativePath, 'to target application');
|
||||||
|
await fs.copy(
|
||||||
|
path.resolve(opts.arm64AppPath, snapshotsFile.relativePath),
|
||||||
|
path.resolve(tmpApp, snapshotsFile.relativePath),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
d('moving final universal app to target destination');
|
||||||
|
await fs.mkdirp(path.dirname(opts.outAppPath));
|
||||||
await spawn('mv', [tmpApp, opts.outAppPath]);
|
await spawn('mv', [tmpApp, opts.outAppPath]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
16
src/sha.ts
Normal file
16
src/sha.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import * as fs from 'fs-extra';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import { d } from './debug';
|
||||||
|
|
||||||
|
export const sha = async (filePath: string) => {
|
||||||
|
d('hashing', filePath);
|
||||||
|
const hash = crypto.createHash('sha256');
|
||||||
|
hash.setEncoding('hex');
|
||||||
|
const fileStream = fs.createReadStream(filePath);
|
||||||
|
fileStream.pipe(hash);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
fileStream.on('end', () => resolve());
|
||||||
|
fileStream.on('error', (err) => reject(err));
|
||||||
|
});
|
||||||
|
return hash.read();
|
||||||
|
};
|
||||||
208
yarn.lock
208
yarn.lock
@@ -48,6 +48,18 @@
|
|||||||
read-pkg "^4.0.0"
|
read-pkg "^4.0.0"
|
||||||
registry-auth-token "^3.3.1"
|
registry-auth-token "^3.3.1"
|
||||||
|
|
||||||
|
"@electron/asar@^3.2.1":
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.1.tgz#c4143896f3dd43b59a80a9c9068d76f77efb62ea"
|
||||||
|
integrity sha512-hE2cQMZ5+4o7+6T2lUaVbxIzrOjZZfX7dB02xuapyYFJZEAiWTelq6J3mMoxzd0iONDvYLPVKecB5tyjIoVDVA==
|
||||||
|
dependencies:
|
||||||
|
chromium-pickle-js "^0.2.0"
|
||||||
|
commander "^5.0.0"
|
||||||
|
glob "^7.1.6"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
optionalDependencies:
|
||||||
|
"@types/glob" "^7.1.1"
|
||||||
|
|
||||||
"@iarna/cli@^1.2.0":
|
"@iarna/cli@^1.2.0":
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641"
|
resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641"
|
||||||
@@ -269,6 +281,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||||
|
|
||||||
|
"@types/debug@^4.1.5":
|
||||||
|
version "4.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
|
||||||
|
integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
|
||||||
|
|
||||||
"@types/fs-extra@^9.0.4":
|
"@types/fs-extra@^9.0.4":
|
||||||
version "9.0.4"
|
version "9.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.4.tgz#12553138cf0438db9a31cdc8b0a3aa9332eb67aa"
|
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.4.tgz#12553138cf0438db9a31cdc8b0a3aa9332eb67aa"
|
||||||
@@ -289,6 +306,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||||
|
|
||||||
|
"@types/minimatch@^3.0.5":
|
||||||
|
version "3.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
|
||||||
|
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
|
||||||
|
|
||||||
"@types/minimist@^1.2.0":
|
"@types/minimist@^1.2.0":
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
|
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
|
||||||
@@ -309,6 +331,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
|
"@types/plist@^3.0.2":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01"
|
||||||
|
integrity sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
xmlbuilder ">=11.0.1"
|
||||||
|
|
||||||
"@types/retry@^0.12.0":
|
"@types/retry@^0.12.0":
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||||
@@ -492,18 +522,6 @@ asap@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||||
|
|
||||||
asar@^3.0.3:
|
|
||||||
version "3.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b"
|
|
||||||
integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==
|
|
||||||
dependencies:
|
|
||||||
chromium-pickle-js "^0.2.0"
|
|
||||||
commander "^5.0.0"
|
|
||||||
glob "^7.1.6"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
optionalDependencies:
|
|
||||||
"@types/glob" "^7.1.1"
|
|
||||||
|
|
||||||
asn1@~0.2.3:
|
asn1@~0.2.3:
|
||||||
version "0.2.4"
|
version "0.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
||||||
@@ -554,6 +572,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-js@^1.5.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
bcrypt-pbkdf@^1.0.0:
|
bcrypt-pbkdf@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||||
@@ -1143,6 +1166,13 @@ debug@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
|
debug@^4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||||
|
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debuglog@^1.0.1:
|
debuglog@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||||
@@ -1632,7 +1662,7 @@ fs-extra@^9.0.0, fs-extra@^9.0.1:
|
|||||||
jsonfile "^6.0.1"
|
jsonfile "^6.0.1"
|
||||||
universalify "^1.0.0"
|
universalify "^1.0.0"
|
||||||
|
|
||||||
fs-minipass@^1.2.5:
|
fs-minipass@^1.2.7:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
|
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
|
||||||
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
|
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
|
||||||
@@ -1767,9 +1797,9 @@ git-log-parser@^1.2.0:
|
|||||||
traverse "~0.6.6"
|
traverse "~0.6.6"
|
||||||
|
|
||||||
glob-parent@^5.1.0:
|
glob-parent@^5.1.0:
|
||||||
version "5.1.1"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
@@ -1832,9 +1862,9 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
|||||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||||
|
|
||||||
handlebars@^4.7.6:
|
handlebars@^4.7.6:
|
||||||
version "4.7.6"
|
version "4.7.7"
|
||||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e"
|
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
|
||||||
integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==
|
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
neo-async "^2.6.0"
|
neo-async "^2.6.0"
|
||||||
@@ -1894,9 +1924,9 @@ hook-std@^2.0.0:
|
|||||||
integrity sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==
|
integrity sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==
|
||||||
|
|
||||||
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.8:
|
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.8:
|
||||||
version "2.8.8"
|
version "2.8.9"
|
||||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||||
|
|
||||||
hosted-git-info@^3.0.0, hosted-git-info@^3.0.6:
|
hosted-git-info@^3.0.0, hosted-git-info@^3.0.6:
|
||||||
version "3.0.7"
|
version "3.0.7"
|
||||||
@@ -2063,9 +2093,9 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1,
|
|||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||||
version "1.3.5"
|
version "1.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||||
|
|
||||||
init-package-json@^1.10.3:
|
init-package-json@^1.10.3:
|
||||||
version "1.10.3"
|
version "1.10.3"
|
||||||
@@ -2708,9 +2738,9 @@ lodash.without@~4.4.0:
|
|||||||
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
|
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
|
||||||
|
|
||||||
lodash@^4.17.15, lodash@^4.17.4:
|
lodash@^4.17.15, lodash@^4.17.4:
|
||||||
version "4.17.20"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
log-symbols@^4.0.0:
|
log-symbols@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@@ -2908,11 +2938,11 @@ minimist-options@4.1.0:
|
|||||||
kind-of "^6.0.3"
|
kind-of "^6.0.3"
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.5:
|
minimist@^1.2.0, minimist@^1.2.5:
|
||||||
version "1.2.5"
|
version "1.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||||
|
|
||||||
minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
|
minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0:
|
||||||
version "2.9.0"
|
version "2.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
|
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
|
||||||
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
|
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
|
||||||
@@ -2920,7 +2950,7 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
|
|||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
yallist "^3.0.0"
|
yallist "^3.0.0"
|
||||||
|
|
||||||
minizlib@^1.2.1:
|
minizlib@^1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
|
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
|
||||||
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
|
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
|
||||||
@@ -2943,7 +2973,7 @@ mississippi@^3.0.0:
|
|||||||
stream-each "^1.1.0"
|
stream-each "^1.1.0"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0:
|
mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0:
|
||||||
version "0.5.5"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||||
@@ -3014,9 +3044,11 @@ node-fetch-npm@^2.0.2:
|
|||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
node-fetch@^2.6.1:
|
node-fetch@^2.6.1:
|
||||||
version "2.6.1"
|
version "2.6.7"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-gyp@^5.0.2, node-gyp@^5.1.0:
|
node-gyp@^5.0.2, node-gyp@^5.1.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
@@ -3069,9 +3101,9 @@ normalize-path@^3.0.0:
|
|||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
|
|
||||||
normalize-url@^4.0.0:
|
normalize-url@^4.0.0:
|
||||||
version "4.5.0"
|
version "4.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
|
||||||
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
|
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
||||||
|
|
||||||
normalize-url@^5.0.0:
|
normalize-url@^5.0.0:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
@@ -3641,9 +3673,9 @@ path-key@^3.0.0, path-key@^3.1.0:
|
|||||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
path-type@^2.0.0:
|
path-type@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@@ -3699,6 +3731,14 @@ please-upgrade-node@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver-compare "^1.0.0"
|
semver-compare "^1.0.0"
|
||||||
|
|
||||||
|
plist@^3.0.4:
|
||||||
|
version "3.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987"
|
||||||
|
integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.5.1"
|
||||||
|
xmlbuilder "^9.0.7"
|
||||||
|
|
||||||
prepend-http@^1.0.1:
|
prepend-http@^1.0.1:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
@@ -4114,7 +4154,7 @@ rxjs@^6.6.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.0"
|
tslib "^1.9.0"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@@ -4392,9 +4432,9 @@ sshpk@^1.7.0:
|
|||||||
tweetnacl "~0.14.0"
|
tweetnacl "~0.14.0"
|
||||||
|
|
||||||
ssri@^6.0.0, ssri@^6.0.1:
|
ssri@^6.0.0, ssri@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
|
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
|
||||||
integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
|
integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
figgy-pudding "^3.5.1"
|
figgy-pudding "^3.5.1"
|
||||||
|
|
||||||
@@ -4599,17 +4639,17 @@ supports-hyperlinks@^2.1.0:
|
|||||||
supports-color "^7.0.0"
|
supports-color "^7.0.0"
|
||||||
|
|
||||||
tar@^4.4.10, tar@^4.4.12, tar@^4.4.13:
|
tar@^4.4.10, tar@^4.4.12, tar@^4.4.13:
|
||||||
version "4.4.13"
|
version "4.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"
|
||||||
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
|
integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr "^1.1.1"
|
chownr "^1.1.4"
|
||||||
fs-minipass "^1.2.5"
|
fs-minipass "^1.2.7"
|
||||||
minipass "^2.8.6"
|
minipass "^2.9.0"
|
||||||
minizlib "^1.2.1"
|
minizlib "^1.3.3"
|
||||||
mkdirp "^0.5.0"
|
mkdirp "^0.5.5"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.2.1"
|
||||||
yallist "^3.0.3"
|
yallist "^3.1.1"
|
||||||
|
|
||||||
temp-dir@^2.0.0:
|
temp-dir@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@@ -4688,20 +4728,25 @@ tough-cookie@~2.5.0:
|
|||||||
psl "^1.1.28"
|
psl "^1.1.28"
|
||||||
punycode "^2.1.1"
|
punycode "^2.1.1"
|
||||||
|
|
||||||
|
tr46@~0.0.3:
|
||||||
|
version "0.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||||
|
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
|
||||||
|
|
||||||
traverse@~0.6.6:
|
traverse@~0.6.6:
|
||||||
version "0.6.6"
|
version "0.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
|
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
|
||||||
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=
|
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=
|
||||||
|
|
||||||
trim-newlines@^3.0.0:
|
trim-newlines@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30"
|
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
|
||||||
integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==
|
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
|
||||||
|
|
||||||
trim-off-newlines@^1.0.0:
|
trim-off-newlines@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
|
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1"
|
||||||
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
integrity sha512-kh6Tu6GbeSNMGfrrZh6Bb/4ZEHV1QlB4xNDBeog8Y9/QwFlKTRyWvY3Fs9tRDAMZliVUwieMgEdIeL/FtqjkJg==
|
||||||
|
|
||||||
tslib@^1.9.0:
|
tslib@^1.9.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
@@ -4756,9 +4801,9 @@ typescript@^4.0.5:
|
|||||||
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
|
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
|
||||||
|
|
||||||
uglify-js@^3.1.4:
|
uglify-js@^3.1.4:
|
||||||
version "3.11.5"
|
version "3.13.5"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.5.tgz#d6788bc83cf35ff18ea78a65763e480803409bc6"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113"
|
||||||
integrity sha512-btvv/baMqe7HxP7zJSF7Uc16h1mSfuuSplT0/qdjxseesDU+yYzH33eHBH+eMdeRXwujXspaCTooWHQVVBh09w==
|
integrity sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==
|
||||||
|
|
||||||
uid-number@0.0.6:
|
uid-number@0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
@@ -4923,6 +4968,19 @@ wcwidth@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
defaults "^1.0.3"
|
defaults "^1.0.3"
|
||||||
|
|
||||||
|
webidl-conversions@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
|
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
||||||
|
|
||||||
|
whatwg-url@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
|
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
|
||||||
|
dependencies:
|
||||||
|
tr46 "~0.0.3"
|
||||||
|
webidl-conversions "^3.0.0"
|
||||||
|
|
||||||
which-module@^2.0.0:
|
which-module@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
@@ -5025,15 +5083,25 @@ xdg-basedir@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||||
|
|
||||||
|
xmlbuilder@>=11.0.1:
|
||||||
|
version "15.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
|
||||||
|
integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
|
||||||
|
|
||||||
|
xmlbuilder@^9.0.7:
|
||||||
|
version "9.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||||
|
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
|
||||||
|
|
||||||
xtend@~4.0.1:
|
xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
y18n@^3.2.1:
|
y18n@^3.2.1:
|
||||||
version "3.2.1"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
|
||||||
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
|
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@@ -5045,7 +5113,7 @@ yallist@^2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||||
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
|
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
|
||||||
|
|
||||||
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
|
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||||
|
|||||||
Reference in New Issue
Block a user