diff --git a/packages/api/core/src/api/index.ts b/packages/api/core/src/api/index.ts index 453bd47284..8d999865c7 100644 --- a/packages/api/core/src/api/index.ts +++ b/packages/api/core/src/api/index.ts @@ -11,6 +11,8 @@ import _package, { PackageOptions } from './package'; import publish, { PublishOptions } from './publish'; import start, { StartOptions } from './start'; +import { fromBuildIdentifier } from '../util/forge-config'; + export class ForgeAPI { /** * Attempt to import a given module directory to the Electron Forge standard. @@ -72,7 +74,21 @@ export class ForgeAPI { } } +export class ForgeUtils { + /** + * Helper for creating a dynamic config value that will get it's real value + * based on the "buildIdentifier" in your forge config. + * + * Usage: + * `fromBuildIdentifier({ stable: 'App', beta: 'App Beta' })` + */ + fromBuildIdentifier(map: { [key: string]: T | undefined }) { + return fromBuildIdentifier(map); + } +} + const api = new ForgeAPI(); +const utils = new ForgeUtils(); export { ForgeMakeResult, @@ -86,4 +102,5 @@ export { PublishOptions, StartOptions, api, + utils, }; diff --git a/packages/api/core/src/util/forge-config.ts b/packages/api/core/src/util/forge-config.ts index 4950245f65..febcd398aa 100644 --- a/packages/api/core/src/util/forge-config.ts +++ b/packages/api/core/src/util/forge-config.ts @@ -8,31 +8,40 @@ import PluginInterface from './plugin-interface'; const underscoreCase = (str: string) => str.replace(/(.)([A-Z][a-z]+)/g, '$1_$2').replace(/([a-z0-9])([A-Z])/g, '$1_$2').toUpperCase(); -const proxify = (object: T, envPrefix: string): T => { - const newObject: T = {} as any; +const proxify = (buildIdentifier: string | (() => string), object: T, envPrefix: string): T => { + let newObject: T = {} as any; + if (Array.isArray(object)) { + newObject = [] as any; + } Object.keys(object).forEach((key) => { - if (typeof (object as any)[key] === 'object' && !Array.isArray((object as any)[key]) && key !== 'pluginInterface') { - (newObject as any)[key] = proxify((object as any)[key], `${envPrefix}_${underscoreCase(key)}`); + if (typeof (object as any)[key] === 'object' && key !== 'pluginInterface') { + (newObject as any)[key] = proxify(buildIdentifier, (object as any)[key], `${envPrefix}_${underscoreCase(key)}`); } else { (newObject as any)[key] = (object as any)[key]; } }); return new Proxy(newObject, { - get(target, name) { + get(target, name, receiver) { // eslint-disable-next-line no-prototype-builtins if (!target.hasOwnProperty(name) && typeof name === 'string') { const envValue = process.env[`${envPrefix}_${underscoreCase(name)}`]; if (envValue) return envValue; } - return (target as any)[name]; + const value = Reflect.get(target, name, receiver); + + if (value && typeof value === 'object' && value.__isMagicBuildIdentifierMap) { + const identifier = typeof buildIdentifier === 'function' ? buildIdentifier() : buildIdentifier; + return value.map[identifier]; + } + return value; }, getOwnPropertyDescriptor(target, name) { const envValue = process.env[`${envPrefix}_${underscoreCase(name as string)}`]; // eslint-disable-next-line no-prototype-builtins if (target.hasOwnProperty(name)) { - return Object.getOwnPropertyDescriptor(target, name); + return Reflect.getOwnPropertyDescriptor(target, name); } else if (envValue) { return { writable: true, enumerable: true, configurable: true, value: envValue }; } @@ -51,6 +60,13 @@ export function setInitialForgeConfig(packageJSON: any) { /* eslint-enable no-param-reassign */ } +export function fromBuildIdentifier(map: { [key: string]: T | undefined }) { + return { + __isMagicBuildIdentifierMap: true, + map, + }; +} + export default async (dir: string) => { const packageJSON = await readPackageJSON(dir); let forgeConfig: ForgeConfig | string = packageJSON.config.forge; @@ -91,5 +107,5 @@ export default async (dir: string) => { forgeConfig.pluginInterface = new PluginInterface(dir, forgeConfig); - return proxify(forgeConfig, 'ELECTRON_FORGE'); + return proxify(forgeConfig.buildIdentifier || '', forgeConfig, 'ELECTRON_FORGE'); }; diff --git a/packages/api/core/test/fast/forge-config_spec.ts b/packages/api/core/test/fast/forge-config_spec.ts index ef93223101..916b5c3e8d 100644 --- a/packages/api/core/test/fast/forge-config_spec.ts +++ b/packages/api/core/test/fast/forge-config_spec.ts @@ -11,7 +11,7 @@ const defaults = { plugins: [], }; -describe('forge-config', () => { +describe.only('forge-config', () => { it('should resolve the object in package.json with defaults if one exists', async () => { const config = await findConfig(path.resolve(__dirname, '../fixture/dummy_app')); delete config.pluginInterface; @@ -54,7 +54,11 @@ describe('forge-config', () => { it('should resolve the JS file exports in config.forge points to a JS file', async () => { const config = JSON.parse(JSON.stringify(await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf')))); delete config.pluginInterface; + delete config.sub; + delete config.topLevelProp; + delete config.topLevelUndef; expect(config).to.be.deep.equal(Object.assign({}, defaults, { + buildIdentifier: 'beta', packagerConfig: { foo: 'bar', baz: {} }, s3: {}, electronReleaseServer: {}, @@ -78,4 +82,31 @@ describe('forge-config', () => { delete process.env.ELECTRON_FORGE_S3_SECRET_ACCESS_KEY; delete process.env.ELECTRON_FORGE_ELECTRON_RELEASE_SERVER_BASE_URL; }); + + it('should resolve values fromBuildIdentifier', async () => { + const conf: any = await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf')); + expect(conf.topLevelProp).to.equal('foo'); + expect(conf.sub).to.deep.equal({ + prop: { + deep: { + prop: 'bar' + }, + inArray: [ + 'arr', + 'natural', + 'array' + ] + }, + }); + }); + + it('should resolve undefined from fromBuildIdentifier if no value is provided', async () => { + const conf: any = await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf')); + expect(conf.topLevelUndef).to.equal(undefined); + }); + + it('should leave arrays intact', async () => { + const conf: any = await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf')); + expect(Array.isArray(conf.sub.prop.inArray)).to.equal(true, 'original array should be recognized as array'); + }); }); diff --git a/packages/api/core/test/fixture/dummy_js_conf/forge.config.js b/packages/api/core/test/fixture/dummy_js_conf/forge.config.js index 2db35a8e21..a1f1f8c5c9 100644 --- a/packages/api/core/test/fixture/dummy_js_conf/forge.config.js +++ b/packages/api/core/test/fixture/dummy_js_conf/forge.config.js @@ -1,8 +1,25 @@ +const { fromBuildIdentifier } = require('../../../src/api'); + module.exports = { + buildIdentifier: 'beta', makers: [], publishers: [], packagerConfig: { foo: 'bar', baz: {} }, s3: {}, electronReleaseServer: {}, magicFn: () => 'magic result', + topLevelProp: fromBuildIdentifier({ beta: 'foo' }), + topLevelUndef: fromBuildIdentifier({ stable: 'heya' }), + sub: { + prop: { + inArray: [ + fromBuildIdentifier({ beta: 'arr' }), + 'natural', + 'array', + ], + deep: { + prop: fromBuildIdentifier({ beta: 'bar' }), + }, + }, + }, };