Skip to content

Commit

Permalink
feat: remove support of build in the application package.json
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `build` is allowed since 3.0 only in the development package.json

Closes #251
  • Loading branch information
develar committed Mar 18, 2016
1 parent eadd09b commit 46dbfe1
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .idea/runConfigurations/linuxPackagerTest.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

## In short
1. Ensure that required fields are specified in the application `package.json`:
1. Ensure that required fields are specified in the development `package.json`:

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

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

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).

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`)).
<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`)).

3. Add [scripts](https://docs.npmjs.com/cli/run-script) to the development `package.json`:
```json
Expand Down
7 changes: 6 additions & 1 deletion src/errorMessages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const buildIsMissed = `Please specify 'build' configuration in the application package.json ('%s'), at least
export const buildIsMissed = `Please specify 'build' configuration in the development package.json ('%s'), at least
build: {
"app-bundle-id": "your.id",
Expand All @@ -16,4 +16,9 @@ See https://docs.npmjs.com/files/package.json#people-fields-author-contributors
It is required to set Linux .deb package maintainer. Or you can set maintainer in the custom linux options.
(see https://github.com/loopline-systems/electron-builder#distributable-format-configuration).
`

export const buildInAppSpecified = `'build' in the application package.json ('%s') is not supported since 3.0 anymore
Please move 'build' into the development package.json ('%s')
`
2 changes: 1 addition & 1 deletion src/install-app-deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ const devPackageFile = path.join(process.cwd(), "package.json")
const appDir = args.appDir || DEFAULT_APP_DIR_NAME

readPackageJson(devPackageFile)
.then(it => installDependencies(path.join(process.cwd(), appDir), getElectronVersion(it, devPackageFile), args.arch))
.then(async (it) => installDependencies(path.join(process.cwd(), appDir), await getElectronVersion(it, devPackageFile), args.arch))
.catch(printErrorAndExit)
59 changes: 31 additions & 28 deletions src/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,42 @@
export interface Metadata {
readonly repository: string | RepositoryInfo
}

/*
Application `package.json`
*/
export interface AppMetadata extends Metadata {
readonly version: string

/**
* The application name
**/
The application name.
*/
readonly name: string

/**
* As {@link 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]{@link https://docs.npmjs.com/files/package.json#name}.
As [name](#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}).
*/
readonly productName?: string

readonly description: string
readonly author: AuthorMetadata

readonly build: BuildMetadata
}

export function getProductName(metadata: AppMetadata) {
return metadata.build.productName || metadata.productName || metadata.name
readonly author: AuthorMetadata
}

/*
Development `package.json`
*/
export interface DevMetadata extends Metadata {
readonly build?: DevBuildMetadata
readonly build?: BuildMetadata

readonly directories?: MetadataDirectories
}

export interface BuildMetadata {
readonly "app-bundle-id": string
readonly "app-category-type": string

readonly iconUrl: string

/**
* See {@link AppMetadata#productName}.
*/
readonly productName?: string
}

export interface RepositoryInfo {
readonly url: string
}

export interface Metadata {
readonly repository: string | RepositoryInfo
}

export interface AuthorMetadata {
readonly name: string
readonly email: string
Expand All @@ -57,7 +46,17 @@ export interface MetadataDirectories {
readonly buildResources?: string
}

export interface DevBuildMetadata {
export interface BuildMetadata {
readonly "app-bundle-id": string
readonly "app-category-type": string

readonly iconUrl: string

/**
See [AppMetadata.productName](#AppMetadata-productName).
*/
readonly productName?: string

readonly osx?: appdmg.Specification
readonly win?: any,
readonly linux?: any
Expand Down Expand Up @@ -90,4 +89,8 @@ export class Platform {

throw new Error("Unknown platform: " + name)
}
}

export function getProductName(metadata: AppMetadata, devMetadata: DevMetadata) {
return devMetadata.build.productName || metadata.productName || metadata.name
}
42 changes: 22 additions & 20 deletions src/packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import WinPackager from "./winPackager"
import * as errorMessages from "./errorMessages"
import * as util from "util"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("./awaiter")
Array.isArray(__awaiter)

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

readonly eventEmitter = new EventEmitter()

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

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

this.electronVersion = getElectronVersion(this.devMetadata, buildPackageFile)
})
const metadataList = await BluebirdPromise.map(Array.from(new Set([devPackageFile, appPackageFile])), readPackageJson)
this.metadata = metadataList[metadataList.length - 1]
this.devMetadata = metadataList[0]
this.checkMetadata(appPackageFile, devPackageFile, platforms)

this.electronVersion = await getElectronVersion(this.devMetadata, devPackageFile)

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

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

const metadata = this.metadata
if (metadata.name == null) {
const appMetadata = this.metadata
if (appMetadata.name == null) {
reportError("name")
}
else if (metadata.description == null) {
else if (appMetadata.description == null) {
reportError("description")
}
else if (metadata.version == null) {
else if (appMetadata.version == null) {
reportError("version")
}
else if (metadata.build == null) {
throw new Error(util.format(errorMessages.buildIsMissed, appPackageFile))
else if (appMetadata !== this.devMetadata && (<any>appMetadata).build != null) {
throw new Error(util.format(errorMessages.buildInAppSpecified, appPackageFile, devAppPackageFile))
}
else if (this.devMetadata.build == null) {
throw new Error(util.format(errorMessages.buildIsMissed, devAppPackageFile))
}
else {
const author = metadata.author
const author = appMetadata.author
if (author == null) {
reportError("author")
}
Expand All @@ -169,7 +171,7 @@ export class Packager implements BuildInfo {
}
else {
log("Skipping app dependencies installation because dev and app dependencies are not separated")
return BluebirdPromise.resolve(null)
return BluebirdPromise.resolve()
}
}
}
Expand Down
26 changes: 20 additions & 6 deletions src/platformPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
const buildMetadata: any = info.devMetadata.build
this.customBuildOptions = buildMetadata == null ? buildMetadata : buildMetadata[this.platform.buildConfigurationKey]

this.appName = getProductName(this.metadata)
this.appName = getProductName(this.metadata, this.devMetadata)
}

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

const electronPackagerOptions = this.devMetadata.build
checkConflictingOptions(electronPackagerOptions)

const options = Object.assign({
dir: this.info.appDir,
out: outDir,
Expand All @@ -104,15 +107,18 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
overwrite: true,
"app-version": version,
"build-version": buildVersion,
tmpdir: false,
"version-string": {
CompanyName: this.metadata.author.name,
FileDescription: this.metadata.description,
ProductVersion: version,
FileVersion: buildVersion,
ProductName: this.appName,
InternalName: this.appName,
}
}, this.metadata.build, {"tmpdir": false})
}, electronPackagerOptions)

delete options.osx
delete options.win
delete options.linux

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

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

abstract packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any>
}

function checkConflictingOptions(options: any): void {
for (let name of ["all", "out", "tmpdir", "version", "platform", "dir", "arch"]) {
if (name in options) {
throw new Error(`Option ${name} is ignored, do not specify it.`)
}
}
}
21 changes: 17 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Promise as BluebirdPromise } from "bluebird"
import readPackageJsonAsync = require("read-package-json")
import * as os from "os"
import * as path from "path"
import { readJson } from "fs-extra-p"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("./awaiter")

export const log = console.log

Expand Down Expand Up @@ -90,16 +94,25 @@ export function spawn(command: string, args?: string[], options?: SpawnOptions):
})
}

export function getElectronVersion(packageData: any, filePath: string): string {
export async function getElectronVersion(packageData: any, packageJsonPath: string): Promise<string> {
try {
return (await readJson(path.join(path.dirname(packageJsonPath), "node_modules", "electron-prebuilt", "package.json"))).version
}
catch (e) {
// ignore
}

const devDependencies = packageData.devDependencies
let electronPrebuiltDep = devDependencies == null ? null : devDependencies["electron-prebuilt"]
let electronPrebuiltDep: string = devDependencies == null ? null : devDependencies["electron-prebuilt"]
if (electronPrebuiltDep == null) {
const dependencies = packageData.dependencies
electronPrebuiltDep = dependencies == null ? null : dependencies["electron-prebuilt"]
}

if (electronPrebuiltDep == null) {
throw new Error("Cannot find electron-prebuilt dependency to get electron version in the '" + filePath + "'")
throw new Error("Cannot find electron-prebuilt dependency to get electron version in the '" + packageJsonPath + "'")
}
return electronPrebuiltDep.substring(1)

const firstChar = electronPrebuiltDep[0]
return firstChar === "^" || firstChar === "~" ? electronPrebuiltDep.substring(1) : electronPrebuiltDep
}
11 changes: 8 additions & 3 deletions src/winPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions {

readonly icon?: string
readonly iconUrl?: string

/**
Whether to create an MSI installer. Defaults to `true` (MSI is not created).
*/
readonly noMsi?: boolean
}

export default class WinPackager extends PlatformPackager<WinBuildOptions> {
Expand Down Expand Up @@ -60,7 +65,7 @@ export default class WinPackager extends PlatformPackager<WinBuildOptions> {
}

async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
let iconUrl = this.metadata.build.iconUrl
let iconUrl = this.devMetadata.build.iconUrl
if (!iconUrl) {
if (this.customBuildOptions != null) {
iconUrl = this.customBuildOptions.iconUrl
Expand Down Expand Up @@ -98,7 +103,8 @@ export default class WinPackager extends PlatformPackager<WinBuildOptions> {
certificateFile: certificateFile,
certificatePassword: this.options.cscKeyPassword,
fixUpPaths: false,
usePackageJson: false
usePackageJson: false,
noMsi: true,
}, this.customBuildOptions)

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

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

Expand Down
Loading

0 comments on commit 46dbfe1

Please sign in to comment.