Skip to content

Commit

Permalink
refactor: rewrite to typescript (#169)
Browse files Browse the repository at this point in the history
* refactor: rewrite to typescript

* fix: handle older electron versions
  • Loading branch information
MarshallOfSound committed Feb 10, 2021
1 parent 412a088 commit 9fad2da
Show file tree
Hide file tree
Showing 12 changed files with 365 additions and 1,931 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist
xunit.xml
yarn-error.log
32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"description": "An easy way to install Dev Tools extensions into Electron applications",
"main": "dist/index.js",
"scripts": {
"prettier:check": "prettier --check \"src/**/*.js\" \"test/**/*.js\"",
"prettier:write": "prettier --write \"src/**/*.js\" \"test/**/*.js\"",
"compile": "babel src --out-dir dist",
"prettier:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"prettier:write": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"compile": "tsc",
"prepare": "npm run compile",
"pretest-unit": "npm run compile",
"test": "npm run prettier:check && npm run test-unit",
"test-unit": "electron-mocha ./test --recursive --require @babel/register --timeout 60000 -R spec-xunit-file -r test/setup.js"
"test-unit": "electron-mocha ./test/**/*_spec.ts --recursive --require ts-node/register --timeout 60000 -R spec-xunit-file -r test/setup.ts"
},
"keywords": [
"electron",
Expand All @@ -20,7 +20,7 @@
"author": {
"name": "Samuel Attard",
"email": "samuel.r.attard@gmail.com",
"url": "www.samuelattard.com"
"url": "https://www.samuelattard.com"
},
"bugs": {
"url": "https://github.com/MarshallOfSound/electron-devtools-installer/issues"
Expand All @@ -31,31 +31,31 @@
"url": "https://github.com/MarshallOfSound/electron-devtools-installer.git"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.0",
"@babel/polyfill": "^7.10.1",
"@babel/preset-env": "^7.9.0",
"@babel/register": "^7.9.0",
"@continuous-auth/semantic-release-npm": "^2.0.0",
"@types/chai": "^4.2.14",
"@types/chai-as-promised": "^7.1.3",
"@types/chai-fs": "^2.0.2",
"@types/mocha": "^8.2.0",
"@types/node": "^12.0.0",
"@types/rimraf": "^3.0.0",
"@types/semver": "^7.3.4",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-fs": "chaijs/chai-fs",
"electron": "7.2.1",
"electron-mocha": "^8.2.1",
"mocha-testdata": "^1.2.0",
"prettier": "^2.0.4",
"spec-xunit-file": "0.0.1-3"
"spec-xunit-file": "0.0.1-3",
"ts-node": "^9.1.1",
"typescript": "^4.1.4"
},
"dependencies": {
"rimraf": "^3.0.2",
"semver": "^7.2.1",
"tslib": "^2.1.0",
"unzip-crx-3": "^0.2.0"
},
"babel": {
"presets": [
"@babel/preset-env"
]
},
"files": [
"LICENSE",
"README.md",
Expand Down
17 changes: 11 additions & 6 deletions src/downloadChromeExtension.js → src/downloadChromeExtension.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import fs from 'fs';
import path from 'path';
import rimraf from 'rimraf';
import unzip from 'unzip-crx-3';
import * as fs from 'fs';
import * as path from 'path';
import * as rimraf from 'rimraf';

import { getPath, downloadFile, changePermissions } from './utils';

const downloadChromeExtension = (chromeStoreID, forceDownload, attempts = 5) => {
const unzip: any = require('unzip-crx-3');

const downloadChromeExtension = (
chromeStoreID: string,
forceDownload?: boolean,
attempts = 5,
): Promise<string> => {
const extensionsStore = getPath();
if (!fs.existsSync(extensionsStore)) {
fs.mkdirSync(extensionsStore, { recursive: true });
Expand All @@ -25,7 +30,7 @@ const downloadChromeExtension = (chromeStoreID, forceDownload, attempts = 5) =>
changePermissions(extensionFolder, 755);
resolve(extensionFolder);
})
.catch((err) => {
.catch((err: Error) => {
if (!fs.existsSync(path.resolve(extensionFolder, 'manifest.json'))) {
return reject(err);
}
Expand Down
104 changes: 73 additions & 31 deletions src/index.js → src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BrowserWindow, session } from 'electron';
import fs from 'fs';
import path from 'path';
import semver from 'semver';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';

import downloadChromeExtension from './downloadChromeExtension';
import { getPath } from './utils';

let IDMap = {};
let IDMap: Record<string, string> = {};
const getIDMapPath = () => path.resolve(getPath(), 'IDMap.json');
if (fs.existsSync(getIDMapPath())) {
try {
Expand All @@ -16,7 +16,43 @@ if (fs.existsSync(getIDMapPath())) {
}
}

const install = (extensionReference, forceDownload = false) => {
interface ExtensionReference {
/**
* Extension ID
*/
id: string;
/**
* Range of electron versions this extension is supported by
*/
electron: string;
}

interface ExtensionOptions {
/**
* Ignore whether the extension is already downloaded and redownload every time
*/
forceDownload?: boolean;
/**
* Options passed to session.loadExtension
*/
loadExtensionOptions?: Record<any, any>;
}

/**
* @param extensionReference Extension or extensions to install
* @param options Installation options
* @returns A promise resolving with the name or names of the extensions installed
*/
const install = (
extensionReference: ExtensionReference | string | Array<ExtensionReference | string>,
options: ExtensionOptions | boolean = {},
): Promise<string> => {
// Support old forceDownload syntax
if (typeof options === 'boolean') {
options = { forceDownload: options };
}
const { forceDownload, loadExtensionOptions } = options;

if (process.type !== 'browser') {
return Promise.reject(
new Error('electron-devtools-installer can only be used from the main process'),
Expand All @@ -25,11 +61,11 @@ const install = (extensionReference, forceDownload = false) => {

if (Array.isArray(extensionReference)) {
return extensionReference.reduce(
(accum, extension) => accum.then(() => install(extension, forceDownload)),
Promise.resolve(),
(accum, extension) => accum.then(() => install(extension, options)),
Promise.resolve(''),
);
}
let chromeStoreID;
let chromeStoreID: string;
if (typeof extensionReference === 'object' && extensionReference.id) {
chromeStoreID = extensionReference.id;
const electronVersion = process.versions.electron.split('-')[0];
Expand All @@ -48,40 +84,46 @@ const install = (extensionReference, forceDownload = false) => {
);
}
const extensionName = IDMap[chromeStoreID];
let extensionInstalled = extensionName;
let extensionInstalled: boolean;

// For Electron >=9.
if (session.defaultSession.getExtension) {
if ((session.defaultSession as any).getExtension) {
extensionInstalled =
extensionInstalled &&
session.defaultSession.getAllExtensions().find((e) => e.name === extensionName);
!!extensionName &&
(session.defaultSession as any)
.getAllExtensions()
.find((e: { name: string }) => e.name === extensionName);
} else {
extensionInstalled =
extensionInstalled &&
!!extensionName &&
BrowserWindow.getDevToolsExtensions &&
BrowserWindow.getDevToolsExtensions().hasOwnProperty(extensionName);
}

if (!forceDownload && extensionInstalled) {
return Promise.resolve(IDMap[chromeStoreID]);
}
return downloadChromeExtension(chromeStoreID, forceDownload).then((extensionFolder) => {
return downloadChromeExtension(chromeStoreID, forceDownload || false).then((extensionFolder) => {
// Use forceDownload, but already installed
if (extensionInstalled) {
// For Electron >=9.
if (session.defaultSession.removeExtension) {
const extensionId = session.defaultSession.getAllExtensions().find((e) => e.name).id;
session.defaultSession.removeExtension(extensionId);
if ((session.defaultSession as any).removeExtension) {
const extensionId = (session.defaultSession as any)
.getAllExtensions()
.find((e: { name: string }) => e.name).id;
(session.defaultSession as any).removeExtension(extensionId);
} else {
BrowserWindow.removeDevToolsExtension(extensionName);
}
}

// For Electron >=9.
if (session.defaultSession.loadExtension) {
return session.defaultSession.loadExtension(extensionFolder).then((ext) => {
return Promise.resolve(ext.name);
});
if ((session.defaultSession as any).loadExtension) {
return (session.defaultSession as any)
.loadExtension(extensionFolder, loadExtensionOptions)
.then((ext: { name: string }) => {
return Promise.resolve(ext.name);
});
}

const name = BrowserWindow.addDevToolsExtension(extensionFolder); // eslint-disable-line
Expand All @@ -99,43 +141,43 @@ const install = (extensionReference, forceDownload = false) => {
};

export default install;
export const EMBER_INSPECTOR = {
export const EMBER_INSPECTOR: ExtensionReference = {
id: 'bmdblncegkenkacieihfhpjfppoconhi',
electron: '>=1.2.1',
};
export const REACT_DEVELOPER_TOOLS = {
export const REACT_DEVELOPER_TOOLS: ExtensionReference = {
id: 'fmkadmapgofadopljbjfkapdkoienihi',
electron: '>=1.2.1',
};
export const BACKBONE_DEBUGGER = {
export const BACKBONE_DEBUGGER: ExtensionReference = {
id: 'bhljhndlimiafopmmhjlgfpnnchjjbhd',
electron: '>=1.2.1',
};
export const JQUERY_DEBUGGER = {
export const JQUERY_DEBUGGER: ExtensionReference = {
id: 'dbhhnnnpaeobfddmlalhnehgclcmjimi',
electron: '>=1.2.1',
};
export const ANGULARJS_BATARANG = {
export const ANGULARJS_BATARANG: ExtensionReference = {
id: 'ighdmehidhipcmcojjgiloacoafjmpfk',
electron: '>=1.2.1',
};
export const VUEJS_DEVTOOLS = {
export const VUEJS_DEVTOOLS: ExtensionReference = {
id: 'nhdogjmejiglipccpnnnanhbledajbpd',
electron: '>=1.2.1',
};
export const REDUX_DEVTOOLS = {
export const REDUX_DEVTOOLS: ExtensionReference = {
id: 'lmhkpmbekcpmknklioeibfkpmmfibljd',
electron: '>=1.2.1',
};
export const CYCLEJS_DEVTOOL = {
export const CYCLEJS_DEVTOOL: ExtensionReference = {
id: 'dfgplfmhhmdekalbpejekgfegkonjpfp',
electron: '>=1.2.1',
};
export const APOLLO_DEVELOPER_TOOLS = {
export const APOLLO_DEVELOPER_TOOLS: ExtensionReference = {
id: 'jdkknkkbebbapilgoeccciglkfbmbnfm',
electron: '>=1.2.1',
};
export const MOBX_DEVTOOLS = {
export const MOBX_DEVTOOLS: ExtensionReference = {
id: 'pfgnfdagidkfgccljigdamigbcnndkod',
electron: '>=1.2.1',
};
18 changes: 9 additions & 9 deletions src/utils.js → src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { app, net } from 'electron';
import fs from 'fs';
import path from 'path';
import https from 'https';
import * as fs from 'fs';
import * as path from 'path';
import * as https from 'https';

export const getPath = () => {
const savePath = app.getPath('userData');
return path.resolve(`${savePath}/extensions`);
};

// Use https.get fallback for Electron < 1.4.5
const request = net ? net.request : https.get;
const request: typeof https.request = net ? (net.request as any) : https.get;

export const downloadFile = (from, to) => {
return new Promise((resolve, reject) => {
export const downloadFile = (from: string, to: string) => {
return new Promise<void>((resolve, reject) => {
const req = request(from);
req.on('response', (res) => {
// Shouldn't handle redirect with `electron.net`, this is for https.get fallback
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return downloadFile(res.headers.location, to).then(resolve).catch(reject);
}
res.pipe(fs.createWriteStream(to)).on('close', resolve);
Expand All @@ -27,11 +27,11 @@ export const downloadFile = (from, to) => {
});
};

export const changePermissions = (dir, mode) => {
export const changePermissions = (dir: string, mode: string | number) => {
const files = fs.readdirSync(dir);
files.forEach((file) => {
const filePath = path.join(dir, file);
fs.chmodSync(filePath, parseInt(mode, 8));
fs.chmodSync(filePath, parseInt(`${mode}`, 8));
if (fs.statSync(filePath).isDirectory()) {
changePermissions(filePath, mode);
}
Expand Down
10 changes: 5 additions & 5 deletions test/compat_spec.js → test/compat_spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Pre-run
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';

// Actual Test Imports
import installExtension, { REACT_DEVELOPER_TOOLS } from '../src/';
import installExtension, { REACT_DEVELOPER_TOOLS } from '../src';

chai.use(chaiAsPromised);
chai.should();

describe('Extension Compatability Checker', () => {
let currentVersion;
let currentVersion: PropertyDescriptor;

beforeEach(() => {
currentVersion = Object.getOwnPropertyDescriptor(process.versions, 'electron');
currentVersion = Object.getOwnPropertyDescriptor(process.versions, 'electron')!;
});

describe('when using a compatable version of electron', () => {
Expand Down
10 changes: 5 additions & 5 deletions test/download_spec.js → test/download_spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Pre-run
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiFs from 'chai-fs';
import fs from 'fs';
import path from 'path';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as chaiFs from 'chai-fs';
import * as fs from 'fs';
import * as path from 'path';

// Actual Test Imports
import downloadChromeExtension from '../src/downloadChromeExtension';
Expand Down
Loading

0 comments on commit 9fad2da

Please sign in to comment.