Skip to content

Commit 3a8e10a

Browse files
committed
feat(serve): Recompile and relaunch Electron on background file change
1 parent eb4c884 commit 3a8e10a

File tree

2 files changed

+119
-62
lines changed

2 files changed

+119
-62
lines changed

__tests__/commands.spec.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ const Application = require('spectron').Application
1212
jest.mock('@vue/cli-service/lib/commands/build')
1313
jest.mock('fs-extra')
1414
jest.mock('electron-builder')
15-
jest.mock('execa', () => jest.fn(() => ({ on: jest.fn() })))
15+
const mockExeca = {
16+
on: jest.fn(),
17+
removeAllListeners: jest.fn(),
18+
kill: jest.fn()
19+
}
20+
jest.mock('execa', () => jest.fn(() => mockExeca))
1621
jest.mock('@vue/cli-service/lib/commands/serve', () => ({
1722
serve: jest.fn().mockResolvedValue({ url: 'serveUrl' })
1823
}))
@@ -284,6 +289,43 @@ describe('serve:electron', () => {
284289
// Both .js and .ts are resolved
285290
expect(mainConfig.resolve.extensions).toEqual(['.js', '.ts'])
286291
})
292+
293+
test('Main process is recompiled and Electron is relaunched on main process file change', async () => {
294+
process.exit = jest.fn()
295+
let watchCb
296+
fs.watchFile.mockImplementation((file, cb) => {
297+
// Set callback to be called later
298+
watchCb = cb
299+
})
300+
await runCommand('serve:electron', {
301+
pluginOptions: {
302+
electronBuilder: {
303+
mainProcessFile: 'customBackground'
304+
}
305+
}
306+
})
307+
308+
// Proper file is watched
309+
expect(fs.watchFile.mock.calls[0][0]).toBe('projectPath/customBackground')
310+
// Child has not yet been killed or unwatched
311+
expect(mockExeca.kill).not.toBeCalled()
312+
expect(mockExeca.removeAllListeners).not.toBeCalled()
313+
// Main process was bundled and Electron was launched initially
314+
expect(webpack).toHaveBeenCalledTimes(1)
315+
expect(execa).toHaveBeenCalledTimes(1)
316+
317+
// Mock change of background file
318+
watchCb()
319+
// Electron was killed and listeners removed
320+
expect(mockExeca.kill).toHaveBeenCalledTimes(1)
321+
expect(mockExeca.removeAllListeners).toHaveBeenCalledTimes(1)
322+
// Process did not exit on Electron close
323+
expect(process.exit).not.toBeCalled()
324+
// Main process file was recompiled
325+
expect(webpack).toHaveBeenCalledTimes(2)
326+
// Electron was re-launched
327+
expect(execa).toHaveBeenCalledTimes(2)
328+
})
287329
})
288330

289331
describe('testWithSpectron', async () => {
@@ -303,7 +345,7 @@ describe('testWithSpectron', async () => {
303345
}
304346
})
305347
const testPromise = testWithSpectron(spectronOptions)
306-
// Mock console.log from serve:elctron
348+
// Mock console.log from serve:electron
307349
if (launchOptions.customLog) await sendData(launchOptions.customLog)
308350
await sendData(`$outputDir=${launchOptions.outputDir || 'dist_electron'}`)
309351
await sendData(

index.js

Lines changed: 75 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
22
const webpack = require('webpack')
33
const Config = require('webpack-chain')
44
const merge = require('lodash.merge')
5+
const fs = require('fs-extra')
56

67
module.exports = (api, options) => {
78
// If plugin options are provided in vue.config.js, those will be used. Otherwise it is empty object
@@ -34,7 +35,6 @@ module.exports = (api, options) => {
3435
},
3536
async (args, rawArgs) => {
3637
const buildRenderer = require('@vue/cli-service/lib/commands/build').build
37-
const fs = require('fs-extra')
3838
const builder = require('electron-builder')
3939
const yargs = require('yargs')
4040
// Import the yargs options from electron-builder
@@ -257,73 +257,88 @@ module.exports = (api, options) => {
257257
mainConfig
258258
.plugin('env')
259259
.tap(args => [{ ...args, WEBPACK_DEV_SERVER_URL: server.url }])
260-
// Build the main process
261-
const bundle = webpack(mainProcessChain(mainConfig).toConfig())
262-
console.log('Bundling main process:\n')
263-
bundle.run((err, stats) => {
264-
if (err) {
265-
console.error(err.stack || err)
266-
if (err.details) {
267-
console.error(err.details)
268-
}
269-
process.exit(1)
260+
// Electron process
261+
let child
262+
// Function to bundle main process and start Electron
263+
const startElectron = () => {
264+
if (child) {
265+
// Prevent self exit on Electron process death
266+
child.removeAllListeners()
267+
// Kill old Electron process
268+
child.kill()
270269
}
270+
// Build the main process
271+
const bundle = webpack(mainProcessChain(mainConfig).toConfig())
272+
console.log('Bundling main process:\n')
273+
bundle.run((err, stats) => {
274+
if (err) {
275+
console.error(err.stack || err)
276+
if (err.details) {
277+
console.error(err.details)
278+
}
279+
process.exit(1)
280+
}
271281

272-
const info = stats.toJson()
282+
const info = stats.toJson()
273283

274-
if (stats.hasErrors()) {
275-
console.error(info.errors)
276-
process.exit(1)
277-
}
284+
if (stats.hasErrors()) {
285+
console.error(info.errors)
286+
process.exit(1)
287+
}
278288

279-
if (stats.hasWarnings()) {
280-
console.warn(info.warnings)
281-
}
289+
if (stats.hasWarnings()) {
290+
console.warn(info.warnings)
291+
}
282292

283-
console.log(
284-
stats.toString({
285-
chunks: false,
286-
colors: true
287-
})
288-
)
289-
if (args.debug) {
290-
// Do not launch electron and provide instructions on launching through debugger
291-
console.log(
292-
'\nNot launching electron as debug argument was passed. You must launch electron though your debugger.'
293-
)
294293
console.log(
295-
`If you are using Spectron, make sure to set the IS_TEST env variable to true.`
294+
stats.toString({
295+
chunks: false,
296+
colors: true
297+
})
296298
)
297-
console.log(
298-
'Learn more about debugging the main process at https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/testingAndDebugging.html#debugging.'
299-
)
300-
} else if (args.headless) {
301-
// Log information for spectron
302-
console.log(`$outputDir=${outputDir}`)
303-
console.log(`$WEBPACK_DEV_SERVER_URL=${server.url}`)
304-
} else {
305-
// Launch electron with execa
306-
console.log('\nLaunching Electron...')
307-
const child = execa(
308-
'./node_modules/.bin/electron',
309-
// Have it load the main process file built with webpack
310-
[`${outputDir}/background.js`],
311-
{
312-
cwd: api.resolve('.'),
313-
stdio: 'inherit',
314-
env: {
315-
...process.env,
316-
// Disable electron security warnings
317-
ELECTRON_DISABLE_SECURITY_WARNINGS: true
299+
if (args.debug) {
300+
// Do not launch electron and provide instructions on launching through debugger
301+
console.log(
302+
'\nNot launching electron as debug argument was passed. You must launch electron though your debugger.'
303+
)
304+
console.log(
305+
`If you are using Spectron, make sure to set the IS_TEST env variable to true.`
306+
)
307+
console.log(
308+
'Learn more about debugging the main process at https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/testingAndDebugging.html#debugging.'
309+
)
310+
} else if (args.headless) {
311+
// Log information for spectron
312+
console.log(`$outputDir=${outputDir}`)
313+
console.log(`$WEBPACK_DEV_SERVER_URL=${server.url}`)
314+
} else {
315+
// Launch electron with execa
316+
console.log('\nLaunching Electron...')
317+
child = execa(
318+
require('electron'),
319+
// Have it load the main process file built with webpack
320+
[`${outputDir}/background.js`],
321+
{
322+
cwd: api.resolve('.'),
323+
stdio: 'inherit',
324+
env: {
325+
...process.env,
326+
// Disable electron security warnings
327+
ELECTRON_DISABLE_SECURITY_WARNINGS: true
328+
}
318329
}
319-
}
320-
)
321-
child.on('exit', () => {
322-
// Exit when electron is closed
323-
process.exit(0)
324-
})
325-
}
326-
})
330+
)
331+
child.on('exit', () => {
332+
// Exit when electron is closed
333+
process.exit(0)
334+
})
335+
}
336+
})
337+
}
338+
// Initial start of Electron
339+
startElectron()
340+
// Restart on main process file change
341+
fs.watchFile(api.resolve(mainProcessFile), startElectron)
327342
})
328343
}
329344
)

0 commit comments

Comments
 (0)