diff --git a/package.json b/package.json index 571660248db..7e2df44c451 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "pretty-ms": "^2.1.0", "progress": "^1.1.8", "progress-stream": "^1.2.0", + "read-installed": "^4.0.3", "sanitize-filename": "^1.6.0", "semver": "^5.3.0", "source-map-support": "^0.4.2", diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 0754d7c506e..ccf6e70fd0c 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -11,7 +11,7 @@ import { Minimatch } from "minimatch" import { checkFileInArchive, createAsarArchive } from "./asarUtil" import { warn, log, task } from "./util/log" import { AppInfo } from "./appInfo" -import { listDependencies, createFilter, copyFiltered, hasMagic } from "./util/filter" +import { createFilter, copyFiltered, hasMagic, devDependencies } from "./util/filter" import { ElectronPackagerOptions, pack } from "./packager/dirPackager" //noinspection JSUnusedLocalSymbols @@ -169,22 +169,17 @@ export abstract class PlatformPackager const p = pack(options, appOutDir, platformName, Arch[arch], this.info.electronVersion, async() => { const ignoreFiles = new Set([path.relative(this.info.appDir, outDir), path.relative(this.info.appDir, this.buildResourcesDir)]) if (!this.info.isTwoPackageJsonProjectLayoutUsed) { - const result = await BluebirdPromise.all([listDependencies(this.info.appDir, false), listDependencies(this.info.appDir, true)]) - const productionDepsSet = new Set(result[1]) - + const result = await devDependencies(this.info.appDir) // npm returns real path, so, we should use relative path to avoid any mismatch const realAppDirPath = await realpath(this.info.appDir) - - for (let it of result[0]) { - if (!productionDepsSet.has(it)) { - if (it.startsWith(realAppDirPath)) { - it = it.substring(realAppDirPath.length + 1) - } - else if (it.startsWith(this.info.appDir)) { - it = it.substring(this.info.appDir.length + 1) - } - ignoreFiles.add(it) + for (let it of result) { + if (it.startsWith(realAppDirPath)) { + it = it.substring(realAppDirPath.length + 1) + } + else if (it.startsWith(this.info.appDir)) { + it = it.substring(this.info.appDir.length + 1) } + ignoreFiles.add(it) } } diff --git a/src/util/filter.ts b/src/util/filter.ts index 991a496dc61..62370828253 100644 --- a/src/util/filter.ts +++ b/src/util/filter.ts @@ -1,10 +1,11 @@ import { copy } from "fs-extra-p" import { Minimatch } from "minimatch" -import { exec } from "./util" import * as path from "path" +import { Promise as BluebirdPromise } from "bluebird" //noinspection JSUnusedLocalSymbols const __awaiter = require("./awaiter") +const readInstalled = require("read-installed") // we use relative path to avoid canonical path issue - e.g. /tmp vs /private/tmp export function copyFiltered(src: string, destination: string, filter: (file: string) => boolean, dereference: boolean): Promise { @@ -54,29 +55,43 @@ export function createFilter(src: string, patterns: Array, ignoreFile } } -export async function listDependencies(appDir: string, production: boolean): Promise> { - let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS - const npmExecArgs = ["ls", production ? "--production" : "--dev", "--parseable"] - if (npmExecPath == null) { - npmExecPath = process.platform === "win32" ? "npm.cmd" : "npm" - } - else { - npmExecArgs.unshift(npmExecPath) - npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node" - } +export function devDependencies(dir: string): Promise> { + return new BluebirdPromise((resolve, reject) => { + readInstalled(dir, (error: Error, data: any) => { + if (error) { + reject(error) + } + else { + resolve(flatDependencies(data, new Set())) + } + }) + }) +} - const result = (await exec(npmExecPath, npmExecArgs, { - cwd: appDir, - stdio: "inherit", - maxBuffer: 1024 * 1024, - })).trim().split("\n") - if (result.length > 0 && !result[0].includes("/node_modules/")) { - // first line is a project dir - const lastIndex = result.length - 1 - result[0] = result[lastIndex] - result.length = result.length - 1 +function flatDependencies(data: any, seen: Set): any { + const deps = data.dependencies + if (deps == null) { + return [] } - return result + + return Object.keys(deps).map(function (d) { + if (typeof deps[d] !== "object" || seen.has(deps[d])) { + return null + } + + seen.add(deps[d]) + if (deps[d].extraneous) { + const extra = deps[d] + delete deps[d] + return extra.path + } + return flatDependencies(deps[d], seen) + }) + .filter(it => it !== null) + .reduce(function FLAT(l, r) { + return l.concat(Array.isArray(r) ? r.reduce(FLAT, []) : r) + }, []) + } // https://github.com/joshwnj/minimatch-all/blob/master/index.js diff --git a/test/src/globTest.ts b/test/src/globTest.ts index f8e6a5de5e4..33420d2f6e1 100644 --- a/test/src/globTest.ts +++ b/test/src/globTest.ts @@ -135,6 +135,22 @@ test.ifNotCiOsx("ignore node_modules known dev dep", () => { }) }) +// https://github.com/electron-userland/electron-builder/issues/611 +test.ifDevOrLinuxCi("failed peer dep", () => { + return assertPack("test-app-one", { + targets: Platform.LINUX.createTarget(DIR_TARGET), + }, { + npmInstallBefore: true, + tempDirCreated: projectDir => modifyPackageJson(projectDir, data => { + data.dependencies = { + "rc-datepicker": "4.0.0", + "react": "15.2.1", + "react-dom": "15.2.1" + } + }), + }) +}) + test("extraResources", async () => { for (let platform of getPossiblePlatforms().keys()) { const osName = platform.buildConfigurationKey