Skip to content

Commit 46dbfe1

Browse files
committed
feat: remove support of build in the application package.json
BREAKING CHANGE: `build` is allowed since 3.0 only in the development package.json Closes #251
1 parent eadd09b commit 46dbfe1

File tree

16 files changed

+146
-98
lines changed

16 files changed

+146
-98
lines changed

.idea/runConfigurations/linuxPackagerTest.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Why the two package.json structure is ideal and how it solves a lot of issues
3838
See [options](./docs/options.md), but consider to follow simple 4-step guide outlined below at first.
3939

4040
## In short
41-
1. Ensure that required fields are specified in the application `package.json`:
41+
1. Ensure that required fields are specified in the development `package.json`:
4242

4343
Standard `name`, `description`, `version` and `author`.
4444

@@ -54,7 +54,7 @@ See [options](./docs/options.md), but consider to follow simple 4-step guide out
5454

5555
2. Create directory `build` in the root of the project and put your `background.png` (OS X DMG background), `icon.icns` (OS X app icon) and `icon.ico` (Windows app icon).
5656

57-
Linux icon set will be generated automatically on the fly from the OS X `icns` file (or you can put them into the `build/icons` directory — filename must contains size (e.g. `32x32.png`)).
57+
<a id="user-content-linuxIcon" class="anchor" href="#linuxIcon" aria-hidden="true"></a>Linux icon set will be generated automatically on the fly from the OS X `icns` file (or you can put them into the `build/icons` directory — filename must contains size (e.g. `32x32.png`)).
5858

5959
3. Add [scripts](https://docs.npmjs.com/cli/run-script) to the development `package.json`:
6060
```json

src/errorMessages.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const buildIsMissed = `Please specify 'build' configuration in the application package.json ('%s'), at least
1+
export const buildIsMissed = `Please specify 'build' configuration in the development package.json ('%s'), at least
22
33
build: {
44
"app-bundle-id": "your.id",
@@ -16,4 +16,9 @@ See https://docs.npmjs.com/files/package.json#people-fields-author-contributors
1616
1717
It is required to set Linux .deb package maintainer. Or you can set maintainer in the custom linux options.
1818
(see https://github.com/loopline-systems/electron-builder#distributable-format-configuration).
19+
`
20+
21+
export const buildInAppSpecified = `'build' in the application package.json ('%s') is not supported since 3.0 anymore
22+
23+
Please move 'build' into the development package.json ('%s')
1924
`

src/install-app-deps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ const devPackageFile = path.join(process.cwd(), "package.json")
1414
const appDir = args.appDir || DEFAULT_APP_DIR_NAME
1515

1616
readPackageJson(devPackageFile)
17-
.then(it => installDependencies(path.join(process.cwd(), appDir), getElectronVersion(it, devPackageFile), args.arch))
17+
.then(async (it) => installDependencies(path.join(process.cwd(), appDir), await getElectronVersion(it, devPackageFile), args.arch))
1818
.catch(printErrorAndExit)

src/metadata.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,42 @@
1+
export interface Metadata {
2+
readonly repository: string | RepositoryInfo
3+
}
4+
5+
/*
6+
Application `package.json`
7+
*/
18
export interface AppMetadata extends Metadata {
29
readonly version: string
310

411
/**
5-
* The application name
6-
**/
12+
The application name.
13+
*/
714
readonly name: string
815

916
/**
10-
* As {@link AppMetadata#name}, but allows you to specify a product name for your executable which contains spaces and other special characters
11-
* not allowed in the [name property]{@link https://docs.npmjs.com/files/package.json#name}.
17+
As [name](#name), but allows you to specify a product name for your executable which contains spaces and other special characters
18+
not allowed in the [name property](https://docs.npmjs.com/files/package.json#name}).
1219
*/
1320
readonly productName?: string
1421

1522
readonly description: string
16-
readonly author: AuthorMetadata
17-
18-
readonly build: BuildMetadata
19-
}
2023

21-
export function getProductName(metadata: AppMetadata) {
22-
return metadata.build.productName || metadata.productName || metadata.name
24+
readonly author: AuthorMetadata
2325
}
2426

27+
/*
28+
Development `package.json`
29+
*/
2530
export interface DevMetadata extends Metadata {
26-
readonly build?: DevBuildMetadata
31+
readonly build?: BuildMetadata
2732

2833
readonly directories?: MetadataDirectories
2934
}
3035

31-
export interface BuildMetadata {
32-
readonly "app-bundle-id": string
33-
readonly "app-category-type": string
34-
35-
readonly iconUrl: string
36-
37-
/**
38-
* See {@link AppMetadata#productName}.
39-
*/
40-
readonly productName?: string
41-
}
42-
4336
export interface RepositoryInfo {
4437
readonly url: string
4538
}
4639

47-
export interface Metadata {
48-
readonly repository: string | RepositoryInfo
49-
}
50-
5140
export interface AuthorMetadata {
5241
readonly name: string
5342
readonly email: string
@@ -57,7 +46,17 @@ export interface MetadataDirectories {
5746
readonly buildResources?: string
5847
}
5948

60-
export interface DevBuildMetadata {
49+
export interface BuildMetadata {
50+
readonly "app-bundle-id": string
51+
readonly "app-category-type": string
52+
53+
readonly iconUrl: string
54+
55+
/**
56+
See [AppMetadata.productName](#AppMetadata-productName).
57+
*/
58+
readonly productName?: string
59+
6160
readonly osx?: appdmg.Specification
6261
readonly win?: any,
6362
readonly linux?: any
@@ -90,4 +89,8 @@ export class Platform {
9089

9190
throw new Error("Unknown platform: " + name)
9291
}
92+
}
93+
94+
export function getProductName(metadata: AppMetadata, devMetadata: DevMetadata) {
95+
return devMetadata.build.productName || metadata.productName || metadata.name
9396
}

src/packager.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import WinPackager from "./winPackager"
1212
import * as errorMessages from "./errorMessages"
1313
import * as util from "util"
1414

15+
//noinspection JSUnusedLocalSymbols
1516
const __awaiter = require("./awaiter")
16-
Array.isArray(__awaiter)
1717

1818
function addHandler(emitter: EventEmitter, event: string, handler: Function) {
1919
emitter.on(event, handler)
@@ -32,7 +32,7 @@ export class Packager implements BuildInfo {
3232

3333
readonly eventEmitter = new EventEmitter()
3434

35-
//noinspection JSUnusedLocalSymbols
35+
//noinspection JSUnusedGlobalSymbols
3636
constructor(public options: PackagerOptions, public repositoryInfo: InfoRetriever = null) {
3737
this.projectDir = options.projectDir == null ? process.cwd() : path.resolve(options.projectDir)
3838
this.appDir = this.computeAppDirectory()
@@ -48,17 +48,16 @@ export class Packager implements BuildInfo {
4848
}
4949

5050
async build(): Promise<any> {
51-
const buildPackageFile = this.devPackageFile
52-
const appPackageFile = this.projectDir === this.appDir ? buildPackageFile : path.join(this.appDir, "package.json")
51+
const devPackageFile = this.devPackageFile
52+
const appPackageFile = this.projectDir === this.appDir ? devPackageFile : path.join(this.appDir, "package.json")
5353
const platforms = normalizePlatforms(this.options.platform)
54-
await BluebirdPromise.map(Array.from(new Set([buildPackageFile, appPackageFile])), readPackageJson)
55-
.then(result => {
56-
this.metadata = result[result.length - 1]
57-
this.devMetadata = result[0]
58-
this.checkMetadata(appPackageFile, platforms)
5954

60-
this.electronVersion = getElectronVersion(this.devMetadata, buildPackageFile)
61-
})
55+
const metadataList = await BluebirdPromise.map(Array.from(new Set([devPackageFile, appPackageFile])), readPackageJson)
56+
this.metadata = metadataList[metadataList.length - 1]
57+
this.devMetadata = metadataList[0]
58+
this.checkMetadata(appPackageFile, devPackageFile, platforms)
59+
60+
this.electronVersion = await getElectronVersion(this.devMetadata, devPackageFile)
6261

6362
const cleanupTasks: Array<() => Promise<any>> = []
6463
return executeFinally(this.doBuild(platforms, cleanupTasks), () => all(cleanupTasks.map(it => it())))
@@ -134,26 +133,29 @@ export class Packager implements BuildInfo {
134133
return absoluteAppPath
135134
}
136135

137-
private checkMetadata(appPackageFile: string, platforms: Array<string>): void {
136+
private checkMetadata(appPackageFile: string, devAppPackageFile: string, platforms: Array<string>): void {
138137
const reportError = (missedFieldName: string) => {
139138
throw new Error("Please specify '" + missedFieldName + "' in the application package.json ('" + appPackageFile + "')")
140139
}
141140

142-
const metadata = this.metadata
143-
if (metadata.name == null) {
141+
const appMetadata = this.metadata
142+
if (appMetadata.name == null) {
144143
reportError("name")
145144
}
146-
else if (metadata.description == null) {
145+
else if (appMetadata.description == null) {
147146
reportError("description")
148147
}
149-
else if (metadata.version == null) {
148+
else if (appMetadata.version == null) {
150149
reportError("version")
151150
}
152-
else if (metadata.build == null) {
153-
throw new Error(util.format(errorMessages.buildIsMissed, appPackageFile))
151+
else if (appMetadata !== this.devMetadata && (<any>appMetadata).build != null) {
152+
throw new Error(util.format(errorMessages.buildInAppSpecified, appPackageFile, devAppPackageFile))
153+
}
154+
else if (this.devMetadata.build == null) {
155+
throw new Error(util.format(errorMessages.buildIsMissed, devAppPackageFile))
154156
}
155157
else {
156-
const author = metadata.author
158+
const author = appMetadata.author
157159
if (author == null) {
158160
reportError("author")
159161
}
@@ -169,7 +171,7 @@ export class Packager implements BuildInfo {
169171
}
170172
else {
171173
log("Skipping app dependencies installation because dev and app dependencies are not separated")
172-
return BluebirdPromise.resolve(null)
174+
return BluebirdPromise.resolve()
173175
}
174176
}
175177
}

src/platformPackager.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
7272
const buildMetadata: any = info.devMetadata.build
7373
this.customBuildOptions = buildMetadata == null ? buildMetadata : buildMetadata[this.platform.buildConfigurationKey]
7474

75-
this.appName = getProductName(this.metadata)
75+
this.appName = getProductName(this.metadata, this.devMetadata)
7676
}
7777

7878
protected get relativeBuildResourcesDirname() {
@@ -92,6 +92,9 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
9292
buildVersion += "." + buildNumber
9393
}
9494

95+
const electronPackagerOptions = this.devMetadata.build
96+
checkConflictingOptions(electronPackagerOptions)
97+
9598
const options = Object.assign({
9699
dir: this.info.appDir,
97100
out: outDir,
@@ -104,15 +107,18 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
104107
overwrite: true,
105108
"app-version": version,
106109
"build-version": buildVersion,
110+
tmpdir: false,
107111
"version-string": {
108112
CompanyName: this.metadata.author.name,
109113
FileDescription: this.metadata.description,
110-
ProductVersion: version,
111-
FileVersion: buildVersion,
112114
ProductName: this.appName,
113115
InternalName: this.appName,
114116
}
115-
}, this.metadata.build, {"tmpdir": false})
117+
}, electronPackagerOptions)
118+
119+
delete options.osx
120+
delete options.win
121+
delete options.linux
116122

117123
// this option only for windows-installer
118124
delete options.iconUrl
@@ -128,8 +134,8 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
128134

129135
if (extraResources != null) {
130136
const expandedPatterns = extraResources.map(it => it
131-
.replace(/\$\{arch\}/g, arch)
132-
.replace(/\$\{os\}/g, this.platform.buildConfigurationKey))
137+
.replace(/\$\{arch}/g, arch)
138+
.replace(/\$\{os}/g, this.platform.buildConfigurationKey))
133139
await BluebirdPromise.map(await globby(expandedPatterns, {cwd: this.projectDir}), it => {
134140
let resourcesDir = appOutDir
135141
if (platform === "darwin") {
@@ -141,4 +147,12 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
141147
}
142148

143149
abstract packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any>
150+
}
151+
152+
function checkConflictingOptions(options: any): void {
153+
for (let name of ["all", "out", "tmpdir", "version", "platform", "dir", "arch"]) {
154+
if (name in options) {
155+
throw new Error(`Option ${name} is ignored, do not specify it.`)
156+
}
157+
}
144158
}

src/util.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { Promise as BluebirdPromise } from "bluebird"
33
import readPackageJsonAsync = require("read-package-json")
44
import * as os from "os"
55
import * as path from "path"
6+
import { readJson } from "fs-extra-p"
7+
8+
//noinspection JSUnusedLocalSymbols
9+
const __awaiter = require("./awaiter")
610

711
export const log = console.log
812

@@ -90,16 +94,25 @@ export function spawn(command: string, args?: string[], options?: SpawnOptions):
9094
})
9195
}
9296

93-
export function getElectronVersion(packageData: any, filePath: string): string {
97+
export async function getElectronVersion(packageData: any, packageJsonPath: string): Promise<string> {
98+
try {
99+
return (await readJson(path.join(path.dirname(packageJsonPath), "node_modules", "electron-prebuilt", "package.json"))).version
100+
}
101+
catch (e) {
102+
// ignore
103+
}
104+
94105
const devDependencies = packageData.devDependencies
95-
let electronPrebuiltDep = devDependencies == null ? null : devDependencies["electron-prebuilt"]
106+
let electronPrebuiltDep: string = devDependencies == null ? null : devDependencies["electron-prebuilt"]
96107
if (electronPrebuiltDep == null) {
97108
const dependencies = packageData.dependencies
98109
electronPrebuiltDep = dependencies == null ? null : dependencies["electron-prebuilt"]
99110
}
100111

101112
if (electronPrebuiltDep == null) {
102-
throw new Error("Cannot find electron-prebuilt dependency to get electron version in the '" + filePath + "'")
113+
throw new Error("Cannot find electron-prebuilt dependency to get electron version in the '" + packageJsonPath + "'")
103114
}
104-
return electronPrebuiltDep.substring(1)
115+
116+
const firstChar = electronPrebuiltDep[0]
117+
return firstChar === "^" || firstChar === "~" ? electronPrebuiltDep.substring(1) : electronPrebuiltDep
105118
}

src/winPackager.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions {
1515

1616
readonly icon?: string
1717
readonly iconUrl?: string
18+
19+
/**
20+
Whether to create an MSI installer. Defaults to `true` (MSI is not created).
21+
*/
22+
readonly noMsi?: boolean
1823
}
1924

2025
export default class WinPackager extends PlatformPackager<WinBuildOptions> {
@@ -60,7 +65,7 @@ export default class WinPackager extends PlatformPackager<WinBuildOptions> {
6065
}
6166

6267
async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
63-
let iconUrl = this.metadata.build.iconUrl
68+
let iconUrl = this.devMetadata.build.iconUrl
6469
if (!iconUrl) {
6570
if (this.customBuildOptions != null) {
6671
iconUrl = this.customBuildOptions.iconUrl
@@ -98,7 +103,8 @@ export default class WinPackager extends PlatformPackager<WinBuildOptions> {
98103
certificateFile: certificateFile,
99104
certificatePassword: this.options.cscKeyPassword,
100105
fixUpPaths: false,
101-
usePackageJson: false
106+
usePackageJson: false,
107+
noMsi: true,
102108
}, this.customBuildOptions)
103109

104110
// we use metadata.name instead of appName because appName can contains unsafe chars
@@ -131,7 +137,6 @@ export default class WinPackager extends PlatformPackager<WinBuildOptions> {
131137

132138
async function changeFileNameInTheReleasesFile(): Promise<void> {
133139
const data = (await readFile(releasesFile, "utf8")).replace(new RegExp(" " + nupkgPathOriginal + " ", "g"), " " + nupkgPathWithArch + " ")
134-
debugger
135140
await writeFile(releasesFile, data)
136141
}
137142

0 commit comments

Comments
 (0)