Skip to content

Commit

Permalink
feat: react-cra detection, shareable config support — extends
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Jun 15, 2017
1 parent fda6ee9 commit 28f0266
Show file tree
Hide file tree
Showing 19 changed files with 215 additions and 153 deletions.
6 changes: 5 additions & 1 deletion docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,11 @@ Configuration Options
* <a name="Config-rpm"></a>`rpm` [LinuxTargetSpecificOptions](electron-builder#LinuxTargetSpecificOptions)
* <a name="Config-freebsd"></a>`freebsd` [LinuxTargetSpecificOptions](electron-builder#LinuxTargetSpecificOptions)
* <a name="Config-p5p"></a>`p5p` [LinuxTargetSpecificOptions](electron-builder#LinuxTargetSpecificOptions)
* <a name="Config-apk"></a>`apk` [LinuxTargetSpecificOptions](electron-builder#LinuxTargetSpecificOptions)<a name="Metadata"></a>
* <a name="Config-apk"></a>`apk` [LinuxTargetSpecificOptions](electron-builder#LinuxTargetSpecificOptions)
* <a name="Config-extends"></a>`extends` String - The name of a built-in configuration preset. Currently, only `react-cra` is supported.

If `react-scripts` in the app dev dependencies, `react-cra` will be set automatically. Set to `null` to disable automatic detection.
* <a name="Config-extraMetadata"></a>`extraMetadata` any<a name="Metadata"></a>

## `Metadata`
Some standard fields should be defined in the `package.json`.
Expand Down
4 changes: 2 additions & 2 deletions docs/api/electron-builder-util.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@
## electron-builder-util/out/deepAssign
<a name="module_electron-builder-util/out/deepAssign.deepAssign"></a>

### `electron-builder-util/out/deepAssign.deepAssign(target, objects)` ⇒ <code>any</code>
### `electron-builder-util/out/deepAssign.deepAssign(target, objects)` ⇒ <code>module:electron-builder-util/out/deepAssign.T</code>
**Kind**: method of [<code>electron-builder-util/out/deepAssign</code>](#module_electron-builder-util/out/deepAssign)

| Param | Type |
| --- | --- |
| target | <code>any</code> |
| target | <code>module:electron-builder-util/out/deepAssign.T</code> |
| objects | <code>Array&lt;any&gt;</code> |

<a name="module_electron-builder-util/out/fs"></a>
Expand Down
2 changes: 1 addition & 1 deletion docs/api/electron-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,7 @@

| Name | Type |
| --- | --- |
| options = <code>this.packager.config.dmg</code>| <code>undefined</code> \| <code>null</code> \| <code>[DmgOptions](Options#DmgOptions)</code> |
| options = <code>this.packager.config.dmg || Object.create(null)</code>| <code>[DmgOptions](Options#DmgOptions)</code> |


* [.DmgTarget](#DmgTarget) ⇐ <code>[Target](electron-builder-core#Target)</code>
Expand Down
17 changes: 11 additions & 6 deletions packages/electron-builder-util/src/deepAssign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ function isObject(x: any) {
return type === "object" || type === "function"
}

function assignKey(to: any, from: any, key: string) {
function assignKey(target: any, from: any, key: string) {
const value = from[key]
// https://github.com/electron-userland/electron-builder/pull/562
if (value === undefined) {
return
}

const prevValue = to[key]
if (prevValue == null || value === null || !isObject(prevValue) || !isObject(value)) {
to[key] = value
const prevValue = target[key]
if (prevValue === null) {
// if explicitly set to null, it means that we want to not use default or inherited value
return
}

if (prevValue == null || value == null || !isObject(prevValue) || !isObject(value)) {
target[key] = value
}
else {
to[key] = assign(prevValue, value)
target[key] = assign(prevValue, value)
}
}

Expand All @@ -32,7 +37,7 @@ function assign(to: any, from: any) {
return to
}

export function deepAssign(target: any, ...objects: Array<any>) {
export function deepAssign<T>(target: T, ...objects: Array<any>): T {
for (const o of objects) {
if (o != null) {
assign(target, o)
Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder/src/appInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class AppInfo {
if (copyright != null) {
return copyright
}
return `Copyright © ${new Date().getFullYear()} ${this.metadata.author!.name || this.productName}`
return `Copyright © ${new Date().getFullYear()} ${this.companyName || this.productName}`
}

async computePackageUrl(): Promise<string | null> {
Expand Down
34 changes: 30 additions & 4 deletions packages/electron-builder/src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { normalizePlatforms, Packager } from "./packager"
import { PackagerOptions } from "./packagerApi"
import { PublishManager } from "./publish/PublishManager"

/** @internal */
export interface BuildOptions extends PackagerOptions, PublishOptions {
}

Expand All @@ -29,12 +30,11 @@ export interface CliOptions extends PackagerOptions, PublishOptions {
platform?: string

project?: string

extraMetadata?: any
}

/**
* @private
* @internal
*/
/** @internal */
export function normalizeOptions(args: CliOptions): BuildOptions {
if (args.targets != null) {
return args
Expand Down Expand Up @@ -140,6 +140,7 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
delete r._
delete r.version
delete r.help
delete r.c

delete result.ia32
delete result.x64
Expand All @@ -149,6 +150,31 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
result.projectDir = result.project
}
delete result.project

let config = result.config
if (config != null && !(typeof config === "string")) {
if (typeof config.asar === "string") {
(<any>config).asar = config.asar === "true"
}
}

if (result.extraMetadata != null) {
if (typeof config === "string") {
// transform to object and specify path to config as extends
config = {
extends: config,
extraMetadata: result.extraMetadata,
};
(<any>result).config = config
}
else if (config == null) {
config = {};
(<any>result).config = config
}
(<any>config).extraMetadata = result.extraMetadata
}
delete result.extraMetadata

return result
}

Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { Packager, BuildResult } from "./packager"
export { PackagerOptions, ArtifactCreated, BuildInfo } from "./packagerApi"
export { getArchSuffix, Platform, Arch, archFromString, Target, DIR_TARGET } from "electron-builder-core"
export { BuildOptions, build, CliOptions, createTargets } from "./builder"
export { build, CliOptions, createTargets } from "./builder"
export { Metadata, Config, AfterPackContext, MetadataDirectories, Protocol, FileAssociation, PlatformSpecificBuildOptions, AuthorMetadata, RepositoryInfo, AsarOptions, FilePattern } from "./metadata"
export { MacOptions, DmgOptions, MasBuildOptions, MacOsTargetName, PkgOptions, DmgContent, DmgWindow } from "./options/macOptions"
export { WinBuildOptions, NsisOptions, SquirrelWindowsOptions, AppXOptions, NsisWebOptions, PortableOptions, CommonNsisOptions } from "./options/winOptions"
Expand Down
5 changes: 3 additions & 2 deletions packages/electron-builder/src/macPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ export default class MacPackager extends PlatformPackager<MacOptions> {

const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (<any>this.config).mas)
if (targetName === "mas-dev") {
deepAssign(masBuildOptions, (<any>this.config)[targetName])
masBuildOptions.type = "development"
deepAssign(masBuildOptions, (<any>this.config)[targetName], {
type: "development",
})
}

const targetOutDir = path.join(outDir, targetName)
Expand Down
9 changes: 9 additions & 0 deletions packages/electron-builder/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ export interface Config extends PlatformSpecificBuildOptions {
* @private
*/
readonly icon?: string | null

/**
* The name of a built-in configuration preset. Currently, only `react-cra` is supported.
*
* If `react-scripts` in the app dev dependencies, `react-cra` will be set automatically. Set to `null` to disable automatic detection.
*/
readonly extends?: string | null

readonly extraMetadata?: any
}

export interface AfterPackContext {
Expand Down
55 changes: 32 additions & 23 deletions packages/electron-builder/src/packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import MacPackager from "./macPackager"
import { AfterPackContext, Config, Metadata } from "./metadata"
import { ArtifactCreated, BuildInfo, PackagerOptions } from "./packagerApi"
import { PlatformPackager } from "./platformPackager"
import { reactCra } from "./presets/rect-cra"
import { getRepositoryInfo } from "./repositoryInfo"
import { computeArchToTargetNamesMap, createTargets, NoOpTarget } from "./targets/targetFactory"
import { doLoadConfig, getElectronVersion, loadConfig, readPackageJson, validateConfig } from "./util/readPackageJson"
Expand Down Expand Up @@ -71,6 +72,13 @@ export class Packager implements BuildInfo {

//noinspection JSUnusedGlobalSymbols
constructor(readonly options: PackagerOptions, readonly cancellationToken: CancellationToken) {
if ("devMetadata" in options) {
throw new Error("devMetadata in the options is deprecated, please use config instead")
}
if ("extraMetadata" in options) {
throw new Error("extraMetadata in the options is deprecated, please use config.extraMetadata instead")
}

this.projectDir = options.projectDir == null ? process.cwd() : path.resolve(options.projectDir)

this.prepackaged = options.prepackaged == null ? null : path.resolve(this.projectDir, options.prepackaged)
Expand Down Expand Up @@ -100,42 +108,28 @@ export class Packager implements BuildInfo {
}

async build(): Promise<BuildResult> {
//noinspection JSDeprecatedSymbols
const devMetadataFromOptions = this.options.devMetadata
if (devMetadataFromOptions != null) {
warn("devMetadata is deprecated, please use config instead")
}

let configPath: string | null = null
let configFromOptions = this.options.config
if (typeof configFromOptions === "string") {
// it is a path to config file
configPath = configFromOptions
configFromOptions = null
}

if (devMetadataFromOptions != null) {
if (configFromOptions != null) {
throw new Error("devMetadata and config cannot be used in conjunction")
}
configFromOptions = devMetadataFromOptions.build
else if (configFromOptions != null && configFromOptions.extends != null && configFromOptions.extends.includes(".")) {
configPath = configFromOptions.extends
}

const projectDir = this.projectDir
const fileOrPackageConfig = await (configPath == null ? loadConfig(projectDir) : doLoadConfig(path.resolve(projectDir, configPath), projectDir))
const config = deepAssign({}, fileOrPackageConfig, configFromOptions)
const config: Config = deepAssign({}, fileOrPackageConfig, configFromOptions)

const extraMetadata = this.options.extraMetadata
const extraMetadata = config.extraMetadata
if (extraMetadata != null) {
const extraBuildMetadata = extraMetadata.build
if (extraBuildMetadata != null) {
deepAssign(config, extraBuildMetadata)
delete extraMetadata.build
if (extraMetadata.build != null) {
throw new Error(`--em.build is deprecated, please specify as -c"`)
}
if (extraMetadata.directories != null) {
warn(`--em.directories is deprecated, please specify as --em.build.directories"`)
deepAssign(config, {directories: extraMetadata.directories})
delete extraMetadata.directories
throw new Error(`--em.directories is deprecated, please specify as -c.directories"`)
}
}

Expand All @@ -152,8 +146,7 @@ export class Packager implements BuildInfo {
await this.readProjectMetadata(appPackageFile, extraMetadata)

if (this.isTwoPackageJsonProjectLayoutUsed) {
this.devMetadata = deepAssign(await readPackageJson(devPackageFile), devMetadataFromOptions)

this.devMetadata = await readPackageJson(devPackageFile)
debug(`Two package.json structure is used (dev: ${devPackageFile}, app: ${appPackageFile})`)
}
else {
Expand All @@ -166,6 +159,22 @@ export class Packager implements BuildInfo {
}
}

if (config.extends == null && config.extends !== null) {
const devDependencies = (<any>this.devMetadata).devDependencies
if (devDependencies != null && "react-scripts" in devDependencies) {
(<any>config).extends = "react-cra"
}
}

if (config.extends === "react-cra") {
await reactCra(config, this.projectDir)

// apply extraMetadata again to metadata because other code expects that appInfo.metadata it is effective metadata, not as on disk (e.g. application entry check)
if (config.extraMetadata != null) {
deepAssign(this.metadata, config.extraMetadata)
}
}

this.checkMetadata(appPackageFile, devPackageFile)

if (debug.enabled) {
Expand Down
7 changes: 0 additions & 7 deletions packages/electron-builder/src/packagerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ export interface PackagerOptions {

platformPackagerFactory?: ((info: BuildInfo, platform: Platform, cleanupTasks: Array<() => Promise<any>>) => PlatformPackager<any>) | null

/**
* @deprecated Use {@link PackagerOptions#config} instead.
*/
readonly devMetadata?: Metadata

readonly config?: Config | string | null

/**
Expand All @@ -35,8 +30,6 @@ export interface PackagerOptions {

readonly effectiveOptionComputed?: (options: any) => Promise<boolean>

readonly extraMetadata?: any

readonly prepackaged?: string
}

Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder/src/platformPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
const transformer = await createTransformer(appDir, isElectronCompile ? Object.assign({
originalMain: this.info.metadata.main,
main: ELECTRON_COMPILE_SHIM_FILENAME,
}, this.packagerOptions.extraMetadata) : this.packagerOptions.extraMetadata)
}, this.config.extraMetadata) : this.config.extraMetadata)
let promise
if (this.info.isPrepackedAppAsar) {
promise = copyDir(appDir, path.join(resourcesPath), filter, transformer)
Expand Down
22 changes: 22 additions & 0 deletions packages/electron-builder/src/presets/rect-cra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { deepAssign } from "electron-builder-util/out/deepAssign"
import { statOrNull } from "electron-builder-util/out/fs"
import { warn } from "electron-builder-util/out/log"
import * as path from "path"
import { Config } from "../metadata"

/** @internal */
export async function reactCra(config: Config, projectDir: string) {
if ((await statOrNull(path.join(projectDir, "public", "electron.js"))) == null) {
warn("public/electron.js not found. Please see https://medium.com/@kitze/%EF%B8%8F-from-react-to-an-electron-app-ready-for-production-a0468ecb1da3")
}

deepAssign(config, <Config>{
directories: {
buildResources: "assets"
},
files: ["build/**/*"],
extraMetadata: {
main: "build/electron.js"
}
})
}
Loading

0 comments on commit 28f0266

Please sign in to comment.