Skip to content

Commit 78bddc7

Browse files
committed
feat: Ability to customize the output directory
Closes #272
1 parent 422a032 commit 78bddc7

File tree

9 files changed

+48
-34
lines changed

9 files changed

+48
-34
lines changed

docs/options.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Here documented only `electron-builder` specific options:
3333
# Application `package.json`
3434
| Name | Description
3535
| --- | ---
36-
| name | <a name="AppMetadata-name"></a>The application name.
36+
| **name** | <a name="AppMetadata-name"></a>The application name.
3737
| productName | <a name="AppMetadata-productName"></a><p>As [name](#AppMetadata-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}).</p>
3838

3939
<a name="DevMetadata"></a>
@@ -42,16 +42,16 @@ Here documented only `electron-builder` specific options:
4242
| --- | ---
4343
| homepage | <a name="DevMetadata-homepage"></a><p>The url to the project [homepage](https://docs.npmjs.com/files/package.json#homepage) (NuGet Package <code>projectUrl</code> (optional) or Linux Package URL (required)).</p> <p>If not specified and your project repository is public on GitHub, it will be <code>https://github.com/${user}/${project}</code> by default.</p>
4444
| license | <a name="DevMetadata-license"></a>*linux-only.* The [license](https://docs.npmjs.com/files/package.json#license) name for this package.
45-
| build | <a name="DevMetadata-build"></a>See [.build](#BuildMetadata).
45+
| **build** | <a name="DevMetadata-build"></a>See [.build](#BuildMetadata).
4646
| directories | <a name="DevMetadata-directories"></a>See [.directories](#MetadataDirectories)
4747

4848
<a name="BuildMetadata"></a>
4949
## `.build`
5050
| Name | Description
5151
| --- | ---
52-
| app-bundle-id | <a name="BuildMetadata-app-bundle-id"></a>The bundle identifier to use in the application's plist.
53-
| app-category-type | <a name="BuildMetadata-app-category-type"></a><p>The application category type, as shown in the Finder via *View -&gt; Arrange by Application Category* when viewing the Applications directory.</p> <p>For example, <code>app-category-type=public.app-category.developer-tools</code> will set the application category to *Developer Tools*.</p> <p>Valid values are listed in [Apple’s documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8).</p>
54-
| iconUrl | <a name="BuildMetadata-iconUrl"></a><p>*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel &gt; Programs and Features). Defaults to the Atom icon.</p> <p>Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.</p> <ul> <li>If you don’t plan to build windows installer, you can omit it.</li> <li>If your project repository is public on GitHub, it will be <code>https://raw.githubusercontent.com/${user}/${project}/master/build/icon.ico</code> by default.</li> </ul>
52+
| **app-bundle-id** | <a name="BuildMetadata-app-bundle-id"></a>The bundle identifier to use in the application's plist.
53+
| **app-category-type** | <a name="BuildMetadata-app-category-type"></a><p>The application category type, as shown in the Finder via *View -&gt; Arrange by Application Category* when viewing the Applications directory.</p> <p>For example, <code>app-category-type=public.app-category.developer-tools</code> will set the application category to *Developer Tools*.</p> <p>Valid values are listed in [Apple’s documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8).</p>
54+
| **iconUrl** | <a name="BuildMetadata-iconUrl"></a><p>*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel &gt; Programs and Features). Defaults to the Electron icon.</p> <p>Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.</p> <ul> <li>If you don’t plan to build windows installer, you can omit it.</li> <li>If your project repository is public on GitHub, it will be <code>https://raw.githubusercontent.com/${user}/${project}/master/build/icon.ico</code> by default.</li> </ul>
5555
| productName | <a name="BuildMetadata-productName"></a>See [AppMetadata.productName](#AppMetadata-productName).
5656
| extraResources | <a name="BuildMetadata-extraResources"></a><p>A [glob expression](https://www.npmjs.com/package/glob#glob-primer), when specified, copy the file or directory with matching names directly into the app’s directory (<code>Contents/Resources</code> for OS X).</p> <p>You can use <code>${os}</code> (expanded to osx, linux or win according to current platform) and <code>${arch}</code> in the pattern.</p> <p>If directory matched, all contents are copied. So, you can just specify <code>foo</code> to copy <code>&lt;project_dir&gt;/foo</code> directory.</p> <p>May be specified in the platform options (i.e. in the <code>build.osx</code>).</p>
5757
| osx | <a name="BuildMetadata-osx"></a>See [.build.osx](#OsXBuildOptions).
@@ -73,12 +73,13 @@ See all [appdmg options](https://www.npmjs.com/package/appdmg#json-specification
7373
### `.build.linux`
7474
| Name | Description
7575
| --- | ---
76-
| compression | <a name="LinuxBuildOptions-compression"></a>*deb-only.* The compression type, one of `gz`, `bzip2`, `xz`. (default: `xz`).
76+
| compression | <a name="LinuxBuildOptions-compression"></a>*deb-only.* The compression type, one of `gz`, `bzip2`, `xz` (default: `xz`).
7777

7878
<a name="MetadataDirectories"></a>
7979
## `.directories`
8080
| Name | Description
8181
| --- | ---
8282
| buildResources | <a name="MetadataDirectories-buildResources"></a>The path to build resources, default `build`.
83+
| output | <a name="MetadataDirectories-output"></a>The output directory, default `dist`.
8384

8485
<!-- end of generated block -->

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@
8686
"pre-commit": "^1.1.2",
8787
"semantic-release": "^4.3.5",
8888
"should": "^8.3.0",
89-
"ts-babel": "^0.6.9",
89+
"ts-babel": "^0.7.0",
9090
"tsconfig-glob": "^0.4.3",
9191
"tslint": "next",
92-
"typescript": "^1.9.0-dev.20160408",
92+
"typescript": "1.9.0-dev.20160409",
9393
"validate-commit-msg": "^2.5.0"
9494
},
9595
"babel": {

src/linuxPackager.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from "path"
22
import { Promise as BluebirdPromise } from "bluebird"
3-
import { PlatformPackager, BuildInfo } from "./platformPackager"
3+
import { PlatformPackager, BuildInfo, use } from "./platformPackager"
44
import { Platform, LinuxBuildOptions } from "./metadata"
55
import { dir as _tpmDir, TmpOptions } from "tmp"
66
import { exec, log } from "./util"
@@ -199,12 +199,6 @@ Icon=${this.metadata.name}
199199
}
200200
}
201201

202-
function use<T>(value: T, task: (it: T) => void) {
203-
if (value != null) {
204-
task(value)
205-
}
206-
}
207-
208202
async function writeConfigFile(tempDir: string, templatePath: string, options: any): Promise<string> {
209203
const config = template(await readFile(templatePath, "utf8"),
210204
{

src/metadata.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export interface BuildMetadata {
7878
readonly "app-category-type": string
7979

8080
/*
81-
*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Atom icon.
81+
*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Electron icon.
8282
8383
Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.
8484
@@ -157,7 +157,7 @@ export interface LinuxBuildOptions {
157157
afterRemove?: string
158158

159159
/*
160-
*deb-only.* The compression type, one of `gz`, `bzip2`, `xz`. (default: `xz`).
160+
*deb-only.* The compression type, one of `gz`, `bzip2`, `xz` (default: `xz`).
161161
*/
162162
readonly compression?: string
163163
}
@@ -170,6 +170,11 @@ export interface MetadataDirectories {
170170
The path to build resources, default `build`.
171171
*/
172172
readonly buildResources?: string
173+
174+
/*
175+
The output directory, default `dist`.
176+
*/
177+
readonly output?: string
173178
}
174179

175180
export interface PlatformSpecificBuildOptions {

src/packager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { EventEmitter } from "events"
66
import { Promise as BluebirdPromise } from "bluebird"
77
import { InfoRetriever } from "./repositoryInfo"
88
import { AppMetadata, DevMetadata } from "./metadata"
9-
import { PackagerOptions, PlatformPackager, BuildInfo, ArtifactCreated } from "./platformPackager"
9+
import { PackagerOptions, PlatformPackager, BuildInfo, ArtifactCreated, use } from "./platformPackager"
1010
import MacPackager from "./macPackager"
1111
import WinPackager from "./winPackager"
1212
import * as errorMessages from "./errorMessages"
@@ -65,12 +65,12 @@ export class Packager implements BuildInfo {
6565

6666
private async doBuild(platforms: Array<string>, cleanupTasks: Array<() => Promise<any>>): Promise<any> {
6767
const distTasks: Array<Promise<any>> = []
68+
const outDir = path.resolve(this.projectDir, use(this.devMetadata.directories, it => it.output) || "dist")
69+
6870
for (let platform of platforms) {
6971
const helper = this.createHelper(platform, cleanupTasks)
7072
for (let arch of normalizeArchs(platform, this.options.arch)) {
7173
await this.installAppDependencies(arch)
72-
73-
const outDir = path.join(this.projectDir, "dist")
7474
// electron-packager uses productName in the directory name
7575
const appOutDir = path.join(outDir, helper.appName + "-" + platform + "-" + arch)
7676
await helper.pack(outDir, appOutDir, arch)

src/platformPackager.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,13 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
6969

7070
this.buildResourcesDir = path.resolve(this.projectDir, this.relativeBuildResourcesDirname)
7171

72-
const buildMetadata: any = info.devMetadata.build
73-
this.customBuildOptions = buildMetadata == null ? buildMetadata : buildMetadata[this.platform.buildConfigurationKey]
72+
this.customBuildOptions = (<any>info.devMetadata.build)[this.platform.buildConfigurationKey]
7473

7574
this.appName = getProductName(this.metadata, this.devMetadata)
7675
}
7776

7877
protected get relativeBuildResourcesDirname() {
79-
const directories = this.devMetadata.directories
80-
return (directories == null ? null : directories.buildResources) || "build"
78+
return use(this.devMetadata.directories, it => it.buildResources) || "build"
8179
}
8280

8381
protected dispatchArtifactCreated(file: string, artifactName?: string) {
@@ -197,4 +195,8 @@ export interface ArtifactCreated {
197195
readonly artifactName?: string
198196

199197
readonly platform: Platform
198+
}
199+
200+
export function use<T, R>(value: T, task: (it: T) => R): R {
201+
return value == null ? null : task(value)
200202
}

test/src/BuildTest.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import test from "./helpers/avaEx"
2-
import { assertPack, modifyPackageJson } from "./helpers/packTester"
2+
import { assertPack, modifyPackageJson, outDirName } from "./helpers/packTester"
33
import { move, outputFile, outputJson } from "fs-extra-p"
44
import { Promise as BluebirdPromise } from "bluebird"
55
import * as path from "path"
@@ -20,6 +20,17 @@ test("custom buildResources dir", () => assertPack("test-app-one", allPlatformsA
2020
])
2121
}))
2222

23+
test("custom output dir", () => assertPack("test-app-one", allPlatformsAndCurrentArch(false), {
24+
tempDirCreated: projectDir => modifyPackageJson(projectDir, data => {
25+
data.directories = {
26+
output: "customDist"
27+
}
28+
}),
29+
packed: async (projectDir) => {
30+
await assertThat(path.join(projectDir, "customDist")).isDirectory()
31+
}
32+
}))
33+
2334
test("productName with space", () => assertPack("test-app-one", allPlatformsAndCurrentArch(), {
2435
tempDirCreated: projectDir => modifyPackageJson(projectDir, data => {
2536
data.productName = "Test App"
@@ -87,8 +98,8 @@ test("copy extra resource", async () => {
8798
outputFile(path.join(projectDir, "ignoreMe.txt"), "ignoreMe"),
8899
])
89100
},
90-
packed: async(projectDir) => {
91-
let resourcesDir = path.join(projectDir, "dist", "TestApp-" + platform + "-" + process.arch)
101+
packed: async (projectDir) => {
102+
let resourcesDir = path.join(projectDir, outDirName, "TestApp-" + platform + "-" + process.arch)
92103
if (platform === "darwin") {
93104
resourcesDir = path.join(resourcesDir, "TestApp.app", "Contents", "Resources")
94105
}
@@ -135,9 +146,10 @@ test("copy extra resource", async () => {
135146
}
136147
})
137148

138-
function allPlatformsAndCurrentArch(): PackagerOptions {
149+
function allPlatformsAndCurrentArch(dist: boolean = true): PackagerOptions {
139150
return {
140151
platform: getPossiblePlatforms(),
152+
dist: dist
141153
}
142154
}
143155

test/src/helpers/fileAssert.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { stat } from "fs-extra-p"
2-
import { AssertionError } from "assert"
32

43
//noinspection JSUnusedLocalSymbols
54
const __awaiter = require("out/awaiter")
@@ -22,7 +21,7 @@ class FileAssertions {
2221
async isDirectory() {
2322
const info = await stat(this.path)
2423
if (!info.isDirectory()) {
25-
throw new Error(`Path ${this.path} is not a file`)
24+
throw new Error(`Path ${this.path} is not a directory`)
2625
}
2726
}
2827

test/src/helpers/packTester.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { exec } from "out/util"
1111
import pathSorter = require("path-sort")
1212
import { tmpdir } from "os"
1313
import DecompressZip = require("decompress-zip")
14-
import { Promise as BluebirdPromise } from "bluebird"
1514

1615
//noinspection JSUnusedLocalSymbols
1716
const __awaiter = require("out/awaiter")
@@ -24,6 +23,8 @@ if (process.env.TRAVIS !== "true") {
2423
process.env.CIRCLE_BUILD_NUM = 42
2524
}
2625

26+
export const outDirName = "dist"
27+
2728
interface AssertPackOptions {
2829
readonly tempDirCreated?: (projectDir: string) => Promise<any>
2930
readonly packed?: (projectDir: string) => Promise<any>
@@ -47,7 +48,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
4748
await copy(projectDir, dir, {
4849
filter: it => {
4950
const basename = path.basename(it)
50-
return basename !== "dist" && basename !== "node_modules" && basename[0] !== "."
51+
return basename !== outDirName && basename !== "node_modules" && basename[0] !== "."
5152
}
5253
})
5354
projectDir = dir
@@ -131,10 +132,10 @@ async function checkLinuxResult(projectDir: string, packager: Packager, packager
131132
// console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb", productName), null, 2))
132133
// console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb", productName), null, 2))
133134

134-
const packageFile = projectDir + "/dist/TestApp-1.1.0-amd64.deb"
135+
const packageFile = `${projectDir}/${outDirName}/TestApp-1.1.0-amd64.deb`
135136
assertThat(await getContents(packageFile, productName)).deepEqual(expectedContents)
136137
if (packagerOptions.arch === "all" || packagerOptions.arch === "ia32") {
137-
assertThat(await getContents(projectDir + "/dist/TestApp-1.1.0-i386.deb", productName)).deepEqual(expectedContents)
138+
assertThat(await getContents(`${projectDir}/${outDirName}/TestApp-1.1.0-i386.deb`, productName)).deepEqual(expectedContents)
138139
}
139140

140141
const regexp = /^ *(\w+): *(.+)$/gm

0 commit comments

Comments
 (0)