Skip to content

Commit

Permalink
feat: prune submodule package.json dependencies
Browse files Browse the repository at this point in the history
Close #1539
  • Loading branch information
develar committed Jun 12, 2017
1 parent 731f4ac commit 1145cb9
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 65 deletions.
19 changes: 11 additions & 8 deletions docs/Auto Update.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,17 @@ Emitted on progress. Only supported over Windows build, since `Squirrel.Mac` [do
**Kind**: class of [<code>electron-updater</code>](#module_electron-updater)
**Extends**: <code>internal:EventEmitter</code>
**Properties**
* <a name="AppUpdater-autoDownload"></a>`autoDownload` = `true` Boolean - Whether to automatically download an update when it is found.
* <a name="AppUpdater-allowPrerelease"></a>`allowPrerelease` = `false` Boolean - *GitHub provider only.* Whether to allow update to pre-release versions. Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`.
If `true`, downgrade will be allowed (`allowDowngrade` will be set to `true`).
* <a name="AppUpdater-allowDowngrade"></a>`allowDowngrade` = `false` Boolean - Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel).
* <a name="AppUpdater-requestHeaders"></a>`requestHeaders` [RequestHeaders](electron-builder-http#RequestHeaders) - The request headers.
* <a name="AppUpdater-logger"></a>`logger` [Logger](electron-updater#Logger) - The logger. You can pass [electron-log](/~https://github.com/megahertz/electron-log), [winston](/~https://github.com/winstonjs/winston) or another logger with the following interface: `{ info(), warn(), error() }`. Set it to `null` if you would like to disable a logging feature.
* <a name="AppUpdater-signals"></a>`signals` = `new UpdaterSignal(this)` [UpdaterSignal](#UpdaterSignal) - For type safety you can use signals, e.g. `autoUpdater.signals.updateDownloaded(() => {})` instead of `autoUpdater.on('update-available', () => {})`
| Name | Type | Description |
| --- | --- | --- |
| autoDownload = <code>true</code>| <code>Boolean</code> | <a name="AppUpdater-autoDownload"></a>Whether to automatically download an update when it is found. |
| allowPrerelease| <code>Boolean</code> | <a name="AppUpdater-allowPrerelease"></a>*GitHub provider only.* Whether to allow update to pre-release versions. Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`.<br><br>If `true`, downgrade will be allowed (`allowDowngrade` will be set to `true`). |
| allowDowngrade| <code>Boolean</code> | <a name="AppUpdater-allowDowngrade"></a>Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel). |
| requestHeaders| <code>[RequestHeaders](electron-builder-http#RequestHeaders)</code> \| <code>null</code> | <a name="AppUpdater-requestHeaders"></a>The request headers. |
| logger| <code>[Logger](electron-updater#Logger)</code> \| <code>null</code> | <a name="AppUpdater-logger"></a>The logger. You can pass [electron-log](/~https://github.com/megahertz/electron-log), [winston](/~https://github.com/winstonjs/winston) or another logger with the following interface: `{ info(), warn(), error() }`. Set it to `null` if you would like to disable a logging feature. |
| signals = <code>new UpdaterSignal(this)</code>| <code>[UpdaterSignal](#UpdaterSignal)</code> | <a name="AppUpdater-signals"></a>For type safety you can use signals, e.g. `autoUpdater.signals.updateDownloaded(() => {})` instead of `autoUpdater.on('update-available', () => {})` |
* [.AppUpdater](#AppUpdater) ⇐ <code>internal:EventEmitter</code>
* [`.checkForUpdates()`](#module_electron-updater.AppUpdater+checkForUpdates) ⇒ <code>Promise&lt;[UpdateCheckResult](electron-updater#UpdateCheckResult)&gt;</code>
* [`.downloadUpdate(cancellationToken)`](#module_electron-updater.AppUpdater+downloadUpdate) ⇒ <code>Promise&lt;any&gt;</code>
Expand Down
17 changes: 8 additions & 9 deletions docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,7 @@ Most of the options accept `null` — for example, to explicitly set that DMG ic

Hidden files are not ignored by default, but all files that should be ignored, are [ignored by default](#default-file-pattern).

Development dependencies are never copied in any case. You don't need to ignore it explicitly.

[Multiple patterns](#multiple-glob-patterns) are supported. If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory.

Remember that default pattern `**/*` **is not added to your custom** if some of your patterns is not ignore (i.e. not starts with `!`). If you have production dependencies, you need to ensure you add `node_modules/**/*`.
`package.json` is added to your custom in any case. All default ignores are added in any case — you don't need to repeat it if you configure own patterns.

May be specified in the platform options (e.g. in the [mac](#MacOptions)).
If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory.

### Multiple Glob Patterns
```js
Expand All @@ -59,7 +52,7 @@ May be specified in the platform options (e.g. in the [mac](#MacOptions)).
]
```

### Excluding directories
### Excluding Directories

Remember that `!doNotCopyMe/**/*` would match the files *in* the `doNotCopyMe` directory, but not the directory itself, so the [empty directory](/~https://github.com/gulpjs/gulp/issues/165#issuecomment-32613179) would be created.
Solution — use macro `${/*}`, e.g. `!doNotCopyMe${/*}`.
Expand Down Expand Up @@ -107,6 +100,12 @@ Configuration Options
* <a name="Config-copyright"></a>`copyright` = `Copyright © year ${author}` String - The human-readable copyright line for the app.
* <a name="Config-productName"></a>`productName` String - As [name](#Metadata-name), but allows you to specify a product name for your executable which contains spaces and other special characters not allowed in the [name property](https://docs.npmjs.com/files/package.json#name}).
* <a name="Config-files"></a>`files` Array&lt;String&gt; | String - A [glob patterns](#file-patterns) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package.

Development dependencies are never copied in any case. You don't need to ignore it explicitly.

Default pattern `**/*` **is not added to your custom** if some of your patterns is not ignore (i.e. not starts with `!`). `package.json` and `**/node_modules/**/*` (only production dependencies will be copied) is added to your custom in any case. All [default ignores](#default-file-pattern) are added in any case — you don't need to repeat it if you configure own patterns.

May be specified in the platform options (e.g. in the [mac](#MacOptions)).
* <a name="Config-extraResources"></a>`extraResources` Array&lt;String | [FilePattern](#FilePattern)&gt; | [FilePattern](#FilePattern) | String<a name="FilePattern"></a> - A [glob patterns](#file-patterns) relative to the project directory, when specified, copy the file or directory with matching names directly into the app's resources directory (`Contents/Resources` for MacOS, `resources` for Linux/Windows).

Glob rules the same as for [files](#multiple-glob-patterns).
Expand Down
2 changes: 1 addition & 1 deletion jsdoc/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function renderMemberListDescription(text, indent) {
return dmdHelper.inlineLinks(text)
.replace(/<br>/g, "\n")
.replace(/\n/g, "\n" + indent)
.replace('"**\\/*"', '"**/*"')
.replace(new RegExp("\\*{2}\\\\/", "g"), "**/")
}

function getInlinedChild(types) {
Expand Down
7 changes: 4 additions & 3 deletions packages/electron-builder-util/src/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ export async function exists(file: string): Promise<boolean> {
}
}

export async function walk(initialDirPath: string, filter?: Filter | null, consumer?: (file: string, stat: Stats, parent: string) => any): Promise<Array<string>> {
export async function walk(initialDirPath: string, filter?: Filter | null, consumer?: (file: string, stat: Stats, parent: string, extraIgnoredFiles: Set<string>) => any): Promise<Array<string>> {
const result: Array<string> = []
const queue: Array<string> = [initialDirPath]
let addDirToResult = false
const extraIgnoredFiles = new Set<string>()
while (queue.length > 0) {
const dirPath = queue.pop()!
if (addDirToResult) {
Expand All @@ -63,11 +64,11 @@ export async function walk(initialDirPath: string, filter?: Filter | null, consu
const filePath = dirPath + path.sep + name
return lstat(filePath)
.then(stat => {
if (filter != null && !filter(filePath, stat)) {
if (extraIgnoredFiles.has(filePath) || (filter != null && !filter(filePath, stat))) {
return null
}

const consumerResult = consumer == null ? null : consumer(filePath, stat, dirPath)
const consumerResult = consumer == null ? null : consumer(filePath, stat, dirPath, extraIgnoredFiles)
if (consumerResult == null || !("then" in consumerResult)) {
if (stat.isDirectory()) {
dirs.push(name)
Expand Down
3 changes: 1 addition & 2 deletions packages/electron-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
"bin": {
"build": "./out/cli/build-cli.js",
"install-app-deps": "./out/cli/install-app-deps.js",
"node-gyp-rebuild": "./out/cli/node-gyp-rebuild.js",
"create-self-signed-cert": "./out/cli/create-self-signed-cert"
"node-gyp-rebuild": "./out/cli/node-gyp-rebuild.js"
},
"repository": "electron-userland/electron-builder",
"engines": {
Expand Down
52 changes: 34 additions & 18 deletions packages/electron-builder/src/asarUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as path from "path"
import { AsarFilesystem, Node, readAsar } from "./asar"
import { createElectronCompilerHost } from "./fileTransformer"
import { AsarOptions } from "./metadata"
import { dependencies } from "./readInstalled"

const isBinaryFile: any = BluebirdPromise.promisify(require("isbinaryfile"))
const pickle = require ("chromium-pickle-js")
Expand Down Expand Up @@ -56,29 +57,44 @@ export class AsarPackager {
// sort files to minimize file change (i.e. asar file is not changed dramatically on small change)
async pack(filter: Filter, isElectronCompile: boolean) {
const metadata = this.metadata
let files = await walk(this.src, filter, (file, fileStat) => {
const nodeModulesSystemDependentSuffix = `${path.sep}node_modules`
let files = await walk(this.src, filter, (file, fileStat, parent, extraIgnoredFiles) => {
metadata.set(file, fileStat)
if (fileStat.isSymbolicLink()) {
return readlink(file)
.then((linkTarget): any => {
// http://unix.stackexchange.com/questions/105637/is-symlinks-target-relative-to-the-destinations-parent-directory-and-if-so-wh
const resolved = path.resolve(path.dirname(file), linkTarget)
const link = path.relative(this.src, linkTarget)
if (link.startsWith("..")) {
// outside of project, linked module (/~https://github.com/electron-userland/electron-builder/issues/675)
return stat(resolved)
.then(targetFileStat => {
metadata.set(file, targetFileStat)
return targetFileStat
})
}
else {
(<any>fileStat).relativeLink = link

// /~https://github.com/electron-userland/electron-builder/issues/1539
// but do not filter if we inside node_modules dir
if (fileStat.isDirectory() && file.endsWith(nodeModulesSystemDependentSuffix) && !parent.includes("node_modules")) {
return dependencies(parent, extraIgnoredFiles)
.then(it => {
if (debug.enabled) {
debug(`Dev or extraneous dependencies in the ${parent}: ${Array.from(extraIgnoredFiles).filter(it => it.startsWith(file)).map(it => path.relative(file, it)).join(", ")}`)
}
return null
})
}
return null

if (!fileStat.isSymbolicLink()) {
return null
}

return readlink(file)
.then((linkTarget): any => {
// http://unix.stackexchange.com/questions/105637/is-symlinks-target-relative-to-the-destinations-parent-directory-and-if-so-wh
const resolved = path.resolve(path.dirname(file), linkTarget)
const link = path.relative(this.src, linkTarget)
if (link.startsWith("..")) {
// outside of project, linked module (/~https://github.com/electron-userland/electron-builder/issues/675)
return stat(resolved)
.then(targetFileStat => {
metadata.set(file, targetFileStat)
return targetFileStat
})
}
else {
(<any>fileStat).relativeLink = link
}
return null
})
})

// transform before electron-compile to avoid filtering (cache files in any case should be not transformed)
Expand Down
7 changes: 4 additions & 3 deletions packages/electron-builder/src/fileMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,18 @@ export function createFileMatcher(info: BuildInfo, appDir: string, resourcesPath

const relativeBuildResourceDir = path.relative(matcher.from, buildResourceDir)
const ignoreBuildResourceDirPattern = (relativeBuildResourceDir.length !== 0 && !relativeBuildResourceDir.startsWith(".")) ? `!${relativeBuildResourceDir}{,/**/*}` : null

if (matcher.isEmpty() || matcher.containsOnlyIgnore()) {
matcher.addAllPattern()
if (ignoreBuildResourceDirPattern != null) {
matcher.addPattern(ignoreBuildResourceDirPattern)
}
matcher.prependPattern("**/*")
}
else {
if (ignoreBuildResourceDirPattern != null) {
matcher.prependPattern(ignoreBuildResourceDirPattern)
}
// prependPattern - user pattern should be after to be able to override
matcher.prependPattern("**/node_modules/**/*")
matcher.addPattern("package.json")
}
matcher.addPattern("!**/node_modules/*/{CHANGELOG.md,ChangeLog,changelog.md,README.md,README,readme.md,readme,test,__tests__,tests,powered-test,example,examples,*.d.ts}")
Expand Down Expand Up @@ -191,7 +192,7 @@ export function copyFiles(patterns: Array<FileMatcher> | null): Promise<any> {
}

if (pattern.isEmpty() || pattern.containsOnlyIgnore()) {
pattern.addAllPattern()
pattern.prependPattern("**/*")
}
if (debug.enabled) {
debug(`Copying files using pattern: ${pattern}`)
Expand Down
8 changes: 8 additions & 0 deletions packages/electron-builder/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ export interface Config extends PlatformSpecificBuildOptions {

/**
* A [glob patterns](#file-patterns) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package.
*
* Development dependencies are never copied in any case. You don't need to ignore it explicitly.
*
* Default pattern `**\/*` **is not added to your custom** if some of your patterns is not ignore (i.e. not starts with `!`).
* `package.json` and `**\/node_modules/**\/*` (only production dependencies will be copied) is added to your custom in any case.
* All [default ignores](#default-file-pattern) are added in any case — you don't need to repeat it if you configure own patterns.
*
* May be specified in the platform options (e.g. in the [mac](#MacOptions)).
*/
readonly files?: Array<string> | string | null

Expand Down
28 changes: 7 additions & 21 deletions packages/electron-builder/src/platformPackager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computeData } from "asar-integrity"
import BluebirdPromise from "bluebird-lst"
import { Arch, getArchSuffix, Platform, Target, TargetSpecificOptions } from "electron-builder-core"
import { asArray, debug, isEmptyOrSpaces, Lazy, use } from "electron-builder-util"
import { asArray, isEmptyOrSpaces, Lazy, use } from "electron-builder-util"
import { deepAssign } from "electron-builder-util/out/deepAssign"
import { copyDir, statOrNull, unlinkIfExists } from "electron-builder-util/out/fs"
import { log, warn } from "electron-builder-util/out/log"
Expand All @@ -15,7 +15,7 @@ import { createTransformer, isElectronCompileUsed } from "./fileTransformer"
import { AsarOptions, Config, FileAssociation, PlatformSpecificBuildOptions } from "./metadata"
import { unpackElectron, unpackMuon } from "./packager/dirPackager"
import { BuildInfo, PackagerOptions } from "./packagerApi"
import { readInstalled } from "./readInstalled"
import { dependencies } from "./readInstalled"

export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions> {
readonly packagerOptions: PackagerOptions
Expand Down Expand Up @@ -152,15 +152,10 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
await unpackElectron(this, appOutDir, platformName, Arch[arch], this.info.electronVersion)
}
else {
// prune dev or not listed dependencies
await BluebirdPromise.all([
dependencies(appDir, ignoreFiles),
isElectron ? unpackElectron(this, appOutDir, platformName, Arch[arch], this.info.electronVersion) : unpackMuon(this, appOutDir, platformName, Arch[arch], muonVersion!),
])

if (debug.enabled) {
const nodeModulesDir = path.join(appDir, "node_modules")
debug(`Dev or extraneous dependencies: ${Array.from(ignoreFiles).slice(2).map(it => path.relative(nodeModulesDir, it)).join(", ")}`)
await (isElectron ? unpackElectron(this, appOutDir, platformName, Arch[arch], this.info.electronVersion) : unpackMuon(this, appOutDir, platformName, Arch[arch], muonVersion!))

if (asarOptions == null) {
await dependencies(appDir, ignoreFiles)
}
}

Expand Down Expand Up @@ -523,13 +518,4 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
// remove leading dot
export function normalizeExt(ext: string) {
return ext.startsWith(".") ? ext.substring(1) : ext
}

async function dependencies(dir: string, result: Set<string>): Promise<void> {
const pathToDep = await readInstalled(dir)
for (const dep of pathToDep.values()) {
if (dep.extraneous) {
result.add(dep.path)
}
}
}
}
9 changes: 9 additions & 0 deletions packages/electron-builder/src/readInstalled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,13 @@ async function readScopedDir(dir: string) {

result.sort()
return result
}

export async function dependencies(dir: string, result: Set<string>): Promise<void> {
const pathToDep = await readInstalled(dir)
for (const dep of pathToDep.values()) {
if (dep.extraneous) {
result.add(dep.path)
}
}
}

0 comments on commit 1145cb9

Please sign in to comment.