Skip to content
Permalink
Browse files

feat(generic): Support setting the Electron app path in start()

The start command allows the caller to specify the directory for the Electron forge project, and then assumes that its main module is the Electron application. However, some projects may contain multiple Electron applications, or may want to specify an html file or a URL as the Electron application (see https://github.com/electron/electron/blob/cf694ef32b78f0904219c6c8ba554d3d5cbea037/default_app/main.js#L338).

So, this adds an optional 'appPath' API option (and corresponding '-p'/'--app-path' CLI option) that is the path to the Electron application relative to the electron forge project (the 'dir' option).

This also fixes the start command-line options parsing, which was passing all arguments through to the Electron application instead of parsing out the start command options.
  • Loading branch information
Ben Demboski authored and MarshallOfSound committed Feb 27, 2017
1 parent fddb40e commit 47c5572e7701aaa80644b063700a3f2ebc9e2d9b
Showing with 150 additions and 9 deletions.
  1. +6 −4 src/api/start.js
  2. +34 −5 src/electron-forge-start.js
  3. +89 −0 test/fast/electron_forge_start_spec.js
  4. +21 −0 test/fast/start_spec.js
@@ -9,7 +9,8 @@ import resolveDir from '../util/resolve-dir';

/**
* @typedef {Object} StartOptions
* @property {string} [dir=process.cwd()] The path to the app to be run
* @property {string} [dir=process.cwd()] The path to the electron forge project to run
* @property {string} [appPath='.'] The path (relative to dir) to the electron app to run relative to the project directory
* @property {boolean} [interactive=false] Whether to use sensible defaults or prompt the user visually
* @property {boolean} [enableLogging=false] Enables advanced internal Electron debug calls
* @property {Array<string>} [args] Arguments to pass through to the launched Electron application
@@ -23,8 +24,9 @@ import resolveDir from '../util/resolve-dir';
*/
export default async (providedOptions = {}) => {
// eslint-disable-next-line prefer-const, no-unused-vars
let { dir, interactive, enableLogging, args } = Object.assign({
let { dir, interactive, enableLogging, appPath, args } = Object.assign({
dir: process.cwd(),
appPath: '.',
interactive: false,
enableLogging: false,
args: [],
@@ -56,9 +58,9 @@ export default async (providedOptions = {}) => {
await asyncOra('Launching Application', async () => {
/* istanbul ignore if */
if (process.platform === 'win32') {
spawned = spawn(path.resolve(dir, 'node_modules/.bin/electron.cmd'), ['.'].concat(args), spawnOpts);
spawned = spawn(path.resolve(dir, 'node_modules/.bin/electron.cmd'), [appPath].concat(args), spawnOpts);
} else {
spawned = spawn(path.resolve(dir, 'node_modules/.bin/electron'), ['.'].concat(args), spawnOpts);
spawned = spawn(path.resolve(dir, 'node_modules/.bin/electron'), [appPath].concat(args), spawnOpts);
}
});

@@ -6,10 +6,21 @@ import './util/terminate';
import { start } from './api';

(async () => {
let commandArgs;
let appArgs;
const tripleDashIndex = process.argv.indexOf('---');
if (tripleDashIndex === -1) {
commandArgs = process.argv;
} else {
commandArgs = process.argv.slice(0, tripleDashIndex);
appArgs = process.argv.slice(tripleDashIndex + 1);
}

let dir = process.cwd();
program
.version(require('../package.json').version)
.arguments('[cwd]')
.option('-p, --app-path <path>', "Override the path to the Electron app to launch (defaults to '.')")
.option('-l, --enable-logging', 'Enable advanced logging. This will log internal Electron things')
.action((cwd) => {
if (!cwd) return;
@@ -19,12 +30,30 @@ import { start } from './api';
dir = path.resolve(dir, cwd);
}
})
.parse(process.argv.slice(0, 2));
.parse(commandArgs);

program.on('--help', () => {
console.log(" Any arguments found after '---' will be passed to the Electron app, e.g.");
console.log('');
console.log(' $ electron-forge /path/to/project -l --- -d -f foo.txt');
console.log('');
console.log(" will pass the arguments '-d -f foo.txt' to the Electron app");
});

await start({
const opts = {
dir,
interactive: true,
enableLogging: program.enableLogging,
args: process.argv.slice(2),
});
};

if (program.appPath) {
opts.appPath = program.appPath;
}
if (program.enableLogging) {
opts.enableLogging = program.enableLogging;
}
if (appArgs) {
opts.args = appArgs;
}

await start(opts);
})();
@@ -0,0 +1,89 @@
import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import { Command } from 'commander';
import path from 'path';

chai.use(chaiAsPromised);

describe('electron-forge start', () => {
let argv;
let startStub;
let runCommand;

beforeEach(() => {
({ argv } = process);

startStub = sinon.stub();
runCommand = async (args = []) => {
process.argv = ['node', 'electron-forge-start'].concat(args);
return proxyquire.noCallThru().load('../../src/electron-forge-start', {
commander: new Command(),
'./api': { start: async opts => startStub(opts) },
});
};
});

afterEach(() => {
process.argv = argv;
});

it('should pass through correct defaults', async () => {
await runCommand();
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: process.cwd(),
interactive: true,
});
});

it('should handle an absolute project directory', async () => {
await runCommand([path.join(process.cwd(), 'test', 'fixture', 'dummy_app')]);
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: path.join(process.cwd(), 'test', 'fixture', 'dummy_app'),
interactive: true,
});
});

it('should handle a relative project directory', async () => {
await runCommand([path.join('test', 'fixture', 'dummy_app')]);
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: path.join(process.cwd(), 'test', 'fixture', 'dummy_app'),
interactive: true,
});
});

it('should handle an app path', async () => {
await runCommand(['-p', path.join('foo', 'electron.js')]);
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: process.cwd(),
appPath: path.join('foo', 'electron.js'),
interactive: true,
});
});

it('should be able to enable logging', async () => {
await runCommand(['-l']);
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: process.cwd(),
enableLogging: true,
interactive: true,
});
});

it('should handle app args', async () => {
await runCommand(['-l', '---', '-a', 'foo', '-l']);
expect(startStub.callCount).to.equal(1);
expect(startStub.firstCall.args[0]).to.deep.equal({
dir: process.cwd(),
enableLogging: true,
interactive: true,
args: ['-a', 'foo', '-l'],
});
});
});
@@ -35,6 +35,27 @@ describe('start', () => {
expect(spawnStub.firstCall.args[2].env).to.not.have.property('ELECTRON_ENABLE_LOGGING');
});

it("should pass electron '.' as the app path if not specified", async () => {
resolveStub.returnsArg(0);
await start({
dir: __dirname,
});
expect(spawnStub.callCount).to.equal(1);
expect(spawnStub.firstCall.args[0]).to.contain('electron');
expect(spawnStub.firstCall.args[1][0]).to.equal('.');
});

it('should pass electron the app path if specified', async () => {
resolveStub.returnsArg(0);
await start({
dir: __dirname,
appPath: '/path/to/app.js',
});
expect(spawnStub.callCount).to.equal(1);
expect(spawnStub.firstCall.args[0]).to.contain('electron');
expect(spawnStub.firstCall.args[1][0]).to.equal('/path/to/app.js');
});

it('should enable electron logging if enableLogging=true', async () => {
resolveStub.returnsArg(0);
await start({

0 comments on commit 47c5572

Please sign in to comment.
You can’t perform that action at this time.