23 Commits

Author SHA1 Message Date
Samuel Attard
36b58a84f3 feat: add support for auto-merging ElectronAsarIntegrity values 2021-09-15 12:50:03 -07:00
dependabot[bot]
fe8d99e31d build(deps): bump tar from 4.4.15 to 4.4.19
Bumps [tar](https://github.com/npm/node-tar) from 4.4.15 to 4.4.19.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v4.4.15...v4.4.19)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-02 11:43:24 -07:00
dependabot[bot]
d336231787 build(deps): bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-12 15:49:25 -07:00
Samuel Attard
6cd85d89aa chore: update deps 2021-07-30 01:36:40 -07:00
Charles Kerr
7b1c610963 Merge pull request #25 from electron/dependabot/npm_and_yarn/glob-parent-5.1.2
build(deps): bump glob-parent from 5.1.1 to 5.1.2
2021-06-09 09:24:07 -05:00
Charles Kerr
060a299188 Merge pull request #24 from electron/dependabot/npm_and_yarn/trim-newlines-3.0.1
build(deps): bump trim-newlines from 3.0.0 to 3.0.1
2021-06-09 09:23:58 -05:00
dependabot[bot]
64420e4c32 build(deps): bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-09 14:22:23 +00:00
dependabot[bot]
2b411ce98b build(deps): bump trim-newlines from 3.0.0 to 3.0.1
Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sindresorhus/trim-newlines/releases)
- [Commits](https://github.com/sindresorhus/trim-newlines/commits)

---
updated-dependencies:
- dependency-name: trim-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-09 14:22:22 +00:00
Charles Kerr
e80eed7f69 Merge pull request #23 from electron/dependabot/npm_and_yarn/normalize-url-4.5.1
build(deps): bump normalize-url from 4.5.0 to 4.5.1
2021-06-09 09:21:54 -05:00
dependabot[bot]
6053796432 build(deps): bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-09 02:23:44 +00:00
dependabot[bot]
0a1d0f916c build(deps): bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 22:10:18 -07:00
dependabot[bot]
1d4e198ba5 build(deps): bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 22:10:08 -07:00
dependabot[bot]
2f06fcab5f build(deps): bump handlebars from 4.7.6 to 4.7.7
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 22:09:50 -07:00
Andrew Plotkin
e7d57dd1e5 fix: /usr/bin/file can return errors on MacOS; ignore these errors (#13)
Co-authored-by: Andrew Plotkin <zarf@ZarfLent.local>
2021-05-08 21:14:36 -07:00
dependabot[bot]
d9b1b4104f build(deps): bump ssri from 6.0.1 to 6.0.2 (#19)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-08 21:13:52 -07:00
Charles Kerr
b445fa1974 Merge pull request #15 from electron/dependabot/npm_and_yarn/y18n-3.2.2
build(deps): bump y18n from 3.2.1 to 3.2.2
2021-04-05 23:11:27 -05:00
dependabot[bot]
f265d1f5e2 build(deps): bump y18n from 3.2.1 to 3.2.2
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-01 07:54:28 +00:00
dependabot[bot]
a05a5e6db8 build(deps): bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-14 09:11:26 -08:00
Samuel Attard
8c55e5b4f3 docs: update CircleCI readme badge 2020-11-26 13:42:27 -08:00
Niels Leenheer
477a52e779 fix: use setEncoding() and read() for crypto.createHash instead of digest() (#11) 2020-11-20 02:05:59 -08:00
Samuel Attard
107823fc2c fix: use realpath when scanning app files 2020-11-19 14:43:08 -08:00
Samuel Attard
621083fe1f fix: add debug logging 2020-11-19 10:01:20 -08:00
Samuel Attard
0770238718 chore: cleanup files and split into modules 2020-11-19 09:49:17 -08:00
8 changed files with 402 additions and 156 deletions

View File

@@ -2,7 +2,7 @@
> Create universal macOS Electron applications > Create universal macOS Electron applications
[![CircleCI](https://circleci.com/gh/electron/universal.svg?style=svg)](https://circleci.com/gh/electron/universal) [![CircleCI](https://circleci.com/gh/electron/universal/tree/master.svg?style=svg)](https://circleci.com/gh/electron/universal)
## Usage ## Usage

View File

@@ -27,8 +27,10 @@
}, },
"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/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",
@@ -37,8 +39,20 @@
}, },
"dependencies": { "dependencies": {
"@malept/cross-spawn-promise": "^1.1.0", "@malept/cross-spawn-promise": "^1.1.0",
"asar": "^3.0.3", "asar": "^3.1.0",
"debug": "^4.3.1",
"dir-compare": "^2.4.0", "dir-compare": "^2.4.0",
"fs-extra": "^9.0.1" "fs-extra": "^9.0.1",
"plist": "^3.0.4"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.ts": [
"prettier --write"
]
} }
} }

33
src/asar-utils.ts Normal file
View File

@@ -0,0 +1,33 @@
import * as asar from 'asar';
import * as crypto from 'crypto';
import * as fs from 'fs-extra';
import * as path from 'path';
import { d } from './debug';
export enum AsarMode {
NO_ASAR,
HAS_ASAR,
}
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'),
};
};

3
src/debug.ts Normal file
View File

@@ -0,0 +1,3 @@
import * as debug from 'debug';
export const d = debug('electron-universal');

73
src/file-utils.ts Normal file
View 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;
};

View File

@@ -4,9 +4,13 @@ import * as crypto from 'crypto';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
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 } from './asar-utils';
import { sha } from './sha';
import { d } from './debug';
type MakeUniversalOpts = { type MakeUniversalOpts = {
/** /**
@@ -29,78 +33,12 @@ type MakeUniversalOpts = {
force: boolean; force: boolean;
}; };
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 +49,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 +71,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 +102,190 @@ 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`); 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));
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');
} }
} }
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) { 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
View 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();
};

149
yarn.lock
View File

@@ -269,6 +269,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"
@@ -309,6 +314,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,10 +505,10 @@ 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: asar@^3.1.0:
version "3.0.3" version "3.1.0"
resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b" resolved "https://registry.yarnpkg.com/asar/-/asar-3.1.0.tgz#70b0509449fe3daccc63beb4d3c7d2e24d3c6473"
integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw== integrity sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==
dependencies: dependencies:
chromium-pickle-js "^0.2.0" chromium-pickle-js "^0.2.0"
commander "^5.0.0" commander "^5.0.0"
@@ -554,6 +567,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 +1161,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 +1657,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 +1792,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 +1857,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 +1919,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 +2088,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 +2733,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"
@@ -2912,7 +2937,7 @@ minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
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 +2945,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 +2968,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==
@@ -3069,9 +3094,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 +3666,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 +3724,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.4"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe"
integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==
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 +4147,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 +4425,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 +4632,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"
@@ -4694,9 +4727,9 @@ traverse@~0.6.6:
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.1"
@@ -4756,9 +4789,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"
@@ -5025,15 +5058,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 +5088,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==