Skip to content

Commit 17668d3

Browse files
committed
feat(serve): add vue devtools extension, closes #35
1 parent 3a8e10a commit 17668d3

File tree

9 files changed

+226
-6
lines changed

9 files changed

+226
-6
lines changed

generator/template/src/background.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import { app, protocol, BrowserWindow } from 'electron'
44
import * as path from 'path'
55
import { format as formatUrl } from 'url'
6-
import createProtocol from 'vue-cli-plugin-electron-builder/lib/createProtocol.js'
7-
6+
import {
7+
createProtocol,
8+
installVueDevtools
9+
} from 'vue-cli-plugin-electron-builder/lib'
810
const isDevelopment = process.env.NODE_ENV !== 'production'
911

1012
// global reference to mainWindow (necessary to prevent window from being garbage collected)
@@ -61,6 +63,10 @@ app.on('activate', () => {
6163
})
6264

6365
// create main BrowserWindow when electron is ready
64-
app.on('ready', () => {
66+
app.on('ready', async () => {
67+
if (isDevelopment && !process.env.IS_TEST) {
68+
// Install Vue Devtools
69+
await installVueDevtools()
70+
}
6571
mainWindow = createMainWindow()
6672
})

lib/createProtocol.d.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

lib/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export function createProtocol(scheme: string): void
2+
export function installVueDevtools(forceDownload?: boolean): Promise<string>

lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as installVueDevtools } from './installVueDevtools'
2+
export { default as createProtocol } from './createProtocol'
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
import rimraf from 'rimraf'
4+
import unzip from 'unzip-crx'
5+
6+
import { getPath, downloadFile, changePermissions } from './utils'
7+
8+
const downloadChromeExtension = (
9+
chromeStoreID,
10+
forceDownload,
11+
attempts = 5
12+
) => {
13+
const extensionsStore = getPath()
14+
if (!fs.existsSync(extensionsStore)) {
15+
fs.mkdirSync(extensionsStore)
16+
}
17+
const extensionFolder = path.resolve(`${extensionsStore}/${chromeStoreID}`)
18+
return new Promise((resolve, reject) => {
19+
if (!fs.existsSync(extensionFolder) || forceDownload) {
20+
if (fs.existsSync(extensionFolder)) {
21+
rimraf.sync(extensionFolder)
22+
}
23+
const fileURL = `https://clients2.google.com/service/update2/crx?response=redirect&x=id%3D${chromeStoreID}%26uc&prodversion=32` // eslint-disable-line
24+
const filePath = path.resolve(`${extensionFolder}.crx`)
25+
downloadFile(fileURL, filePath)
26+
.then(() => {
27+
unzip(filePath, extensionFolder).then(err => {
28+
if (
29+
err &&
30+
!fs.existsSync(path.resolve(extensionFolder, 'manifest.json'))
31+
) {
32+
return reject(err)
33+
}
34+
changePermissions(extensionFolder, 755)
35+
resolve(extensionFolder)
36+
})
37+
})
38+
.catch(err => {
39+
console.log(
40+
`Failed to fetch extension, trying ${attempts - 1} more times`
41+
) // eslint-disable-line
42+
if (attempts <= 1) {
43+
return reject(err)
44+
}
45+
setTimeout(() => {
46+
downloadChromeExtension(chromeStoreID, forceDownload, attempts - 1)
47+
.then(resolve)
48+
.catch(reject)
49+
}, 200)
50+
})
51+
} else {
52+
resolve(extensionFolder)
53+
}
54+
})
55+
}
56+
57+
export default downloadChromeExtension

lib/installVueDevtools/index.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import electron, { remote } from 'electron'
2+
import fs from 'fs'
3+
import path from 'path'
4+
5+
import downloadChromeExtension from './downloadChromeExtension'
6+
import { getPath } from './utils'
7+
8+
const { BrowserWindow } = remote || electron
9+
10+
let IDMap = {}
11+
const IDMapPath = path.resolve(getPath(), 'IDMap.json')
12+
if (fs.existsSync(IDMapPath)) {
13+
try {
14+
IDMap = JSON.parse(fs.readFileSync(IDMapPath, 'utf8'))
15+
} catch (err) {
16+
console.error(
17+
'electron-devtools-installer: Invalid JSON present in the IDMap file'
18+
)
19+
}
20+
}
21+
22+
const install = (forceDownload = false) => {
23+
return new Promise(resolve => {
24+
const chromeStoreID = 'nhdogjmejiglipccpnnnanhbledajbpd'
25+
const extensionName = IDMap[chromeStoreID]
26+
const extensionInstalled =
27+
extensionName &&
28+
BrowserWindow.getDevToolsExtensions &&
29+
BrowserWindow.getDevToolsExtensions()[extensionName]
30+
if (!forceDownload && extensionInstalled) {
31+
return Promise.resolve(IDMap[chromeStoreID])
32+
}
33+
downloadChromeExtension(chromeStoreID, forceDownload).then(
34+
extensionFolder => {
35+
// Use forceDownload, but already installed
36+
if (extensionInstalled) {
37+
BrowserWindow.removeDevToolsExtension(extensionName)
38+
}
39+
const name = BrowserWindow.addDevToolsExtension(extensionFolder) // eslint-disable-line
40+
fs.writeFileSync(
41+
IDMapPath,
42+
JSON.stringify(
43+
Object.assign(IDMap, {
44+
[chromeStoreID]: name
45+
})
46+
)
47+
)
48+
resolve(extensionFolder)
49+
}
50+
)
51+
})
52+
}
53+
54+
export default install

lib/installVueDevtools/utils.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import electron, { remote } from 'electron'
2+
import fs from 'fs'
3+
import path from 'path'
4+
import https from 'https'
5+
6+
export const getPath = () => {
7+
const savePath = (remote || electron).app.getPath('userData')
8+
return path.resolve(`${savePath}/extensions`)
9+
}
10+
11+
// Use https.get fallback for Electron < 1.4.5
12+
const { net } = remote || electron
13+
const request = net ? net.request : https.get
14+
15+
export const downloadFile = (from, to) =>
16+
new Promise((resolve, reject) => {
17+
const req = request(from)
18+
req.on('response', res => {
19+
// Shouldn't handle redirect with `electron.net`, this is for https.get fallback
20+
if (
21+
res.statusCode >= 300 &&
22+
res.statusCode < 400 &&
23+
res.headers.location
24+
) {
25+
return downloadFile(res.headers.location, to)
26+
.then(resolve)
27+
.catch(reject)
28+
}
29+
res.pipe(fs.createWriteStream(to)).on('close', resolve)
30+
})
31+
req.on('error', reject)
32+
req.end()
33+
})
34+
35+
export const changePermissions = (dir, mode) => {
36+
const files = fs.readdirSync(dir)
37+
files.forEach(file => {
38+
const filePath = path.join(dir, file)
39+
fs.chmodSync(filePath, parseInt(mode, 8))
40+
if (fs.statSync(filePath).isDirectory()) {
41+
changePermissions(filePath, mode)
42+
}
43+
})
44+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
"private": false,
2929
"dependencies": {
3030
"@vue/cli-service": "https://github.com/nklayman/vue-cli-plugin-electron-builder/raw/master/vue-cli-service-custom-1.tgz",
31+
"electron": "^2.0.5",
3132
"electron-builder": "^20.24.4",
3233
"execa": "^0.10.0",
3334
"fs-extra": "^7.0.0",
3435
"lodash.merge": "^4.6.1",
3536
"spectron": "^3.8.0",
3637
"uglifyjs-webpack-plugin": "^1.2.7",
38+
"unzip-crx": "^0.2.0",
3739
"webpack": "^4.16.1",
3840
"webpack-chain": "^4.8.0",
3941
"webpack-target-electron-renderer": "^0.4.0"
@@ -46,7 +48,6 @@
4648
"@vue/cli-plugin-unit-jest": "^3.0.0-rc.5",
4749
"@vue/cli-test-utils": "^3.0.0-rc.5",
4850
"@vue/eslint-config-typescript": "^3.0.0-rc.5",
49-
"electron": "^2.0.5",
5051
"eslint-config-standard": "^11.0.0",
5152
"eslint-plugin-import": "^2.13.0",
5253
"eslint-plugin-node": "^7.0.1",

yarn.lock

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3397,6 +3397,10 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3, core-js@^2.5.7:
33973397
version "2.5.7"
33983398
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
33993399

3400+
core-js@~2.3.0:
3401+
version "2.3.0"
3402+
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
3403+
34003404
[email protected], core-util-is@~1.0.0:
34013405
version "1.0.2"
34023406
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -4349,6 +4353,10 @@ es6-promise@^4.0.3, es6-promise@^4.0.5, es6-promise@^4.1.0:
43494353
version "4.2.4"
43504354
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
43514355

4356+
es6-promise@~3.0.2:
4357+
version "3.0.2"
4358+
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
4359+
43524360
es6-promisify@^5.0.0:
43534361
version "5.0.0"
43544362
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
@@ -5865,6 +5873,10 @@ immediate@^3.2.3:
58655873
version "3.2.3"
58665874
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c"
58675875

5876+
immediate@~3.0.5:
5877+
version "3.0.6"
5878+
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
5879+
58685880
import-global@^0.1.0:
58695881
version "0.1.0"
58705882
resolved "https://registry.yarnpkg.com/import-global/-/import-global-0.1.0.tgz#97b38fd444114eec16824a935f8da575b57aa1ce"
@@ -6904,6 +6916,16 @@ jsprim@^1.2.2:
69046916
json-schema "0.2.3"
69056917
verror "1.10.0"
69066918

6919+
jszip@^3.1.0:
6920+
version "3.1.5"
6921+
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37"
6922+
dependencies:
6923+
core-js "~2.3.0"
6924+
es6-promise "~3.0.2"
6925+
lie "~3.1.0"
6926+
pako "~1.0.2"
6927+
readable-stream "~2.0.6"
6928+
69076929
keygrip@~1.0.2:
69086930
version "1.0.2"
69096931
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
@@ -7102,6 +7124,12 @@ levn@^0.3.0, levn@~0.3.0:
71027124
prelude-ls "~1.1.2"
71037125
type-check "~0.3.2"
71047126

7127+
lie@~3.1.0:
7128+
version "3.1.1"
7129+
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
7130+
dependencies:
7131+
immediate "~3.0.5"
7132+
71057133
linkify-it@^2.0.0:
71067134
version "2.0.3"
71077135
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
@@ -8379,7 +8407,7 @@ pako@~0.2.0:
83798407
version "0.2.9"
83808408
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
83818409

8382-
pako@~1.0.5:
8410+
pako@~1.0.2, pako@~1.0.5:
83838411
version "1.0.6"
83848412
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
83858413

@@ -8989,6 +9017,10 @@ private@^0.1.6, private@^0.1.8, private@~0.1.5:
89899017
version "0.1.8"
89909018
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
89919019

9020+
process-nextick-args@~1.0.6:
9021+
version "1.0.7"
9022+
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
9023+
89929024
process-nextick-args@~2.0.0:
89939025
version "2.0.0"
89949026
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -9306,6 +9338,17 @@ [email protected], readable-stream@~1.1.9:
93069338
isarray "0.0.1"
93079339
string_decoder "~0.10.x"
93089340

9341+
readable-stream@~2.0.6:
9342+
version "2.0.6"
9343+
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
9344+
dependencies:
9345+
core-util-is "~1.0.0"
9346+
inherits "~2.0.1"
9347+
isarray "~1.0.0"
9348+
process-nextick-args "~1.0.6"
9349+
string_decoder "~0.10.x"
9350+
util-deprecate "~1.0.1"
9351+
93099352
readdirp@^2.0.0:
93109353
version "2.1.0"
93119354
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
@@ -10958,6 +11001,14 @@ unset-value@^1.0.0:
1095811001
has-value "^0.3.1"
1095911002
isobject "^3.0.0"
1096011003

11004+
unzip-crx@^0.2.0:
11005+
version "0.2.0"
11006+
resolved "https://registry.yarnpkg.com/unzip-crx/-/unzip-crx-0.2.0.tgz#4c0baa8bdac756256754beca7843c13d7b858c18"
11007+
dependencies:
11008+
jszip "^3.1.0"
11009+
mkdirp "^0.5.1"
11010+
yaku "^0.16.6"
11011+
1096111012
unzip-response@^2.0.1:
1096211013
version "2.0.1"
1096311014
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
@@ -11915,6 +11966,10 @@ y18n@^3.2.1:
1191511966
version "4.0.0"
1191611967
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
1191711968

11969+
yaku@^0.16.6:
11970+
version "0.16.7"
11971+
resolved "https://registry.yarnpkg.com/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e"
11972+
1191811973
yallist@^2.1.2:
1191911974
version "2.1.2"
1192011975
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"

0 commit comments

Comments
 (0)