Skip to content

Commit

Permalink
Merge pull request #247 from electron/feat/local-version-intellisense
Browse files Browse the repository at this point in the history
feat: intellisense for local builds
  • Loading branch information
felixrieseberg authored Aug 14, 2019
2 parents 7035be3 + 66ba7b7 commit 1f9db42
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 41 deletions.
79 changes: 58 additions & 21 deletions src/renderer/fetch-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,38 @@ import * as fsType from 'fs-extra';
import * as MonacoType from 'monaco-editor';
import * as path from 'path';

import { ElectronVersion, ElectronVersionSource } from '../interfaces';
import { callIn } from '../utils/call-in';
import { fancyImport } from '../utils/import';
import { USER_DATA_PATH } from './constants';

const definitionPath = path.join(USER_DATA_PATH, 'electron-typedef');


/**
* Fetch TypeScript definitions for the current version of Electron (online)
*
* @param {string} version
* @returns {Promise<string>}
*/
export function fetchTypeDefinitions(version: string): Promise<string> {
export async function fetchTypeDefinitions(version: string): Promise<string> {
const url = `https://unpkg.com/electron@${version}/electron.d.ts`;

return window.fetch(url)
.then((response) => response.text())
.catch((error) => {
console.warn(`Fetch Types: Could not fetch definitions`, error);
return '';
});
let text: string;
try {
const response = await window.fetch(url);
text = await response.text();
} catch (error) {
console.warn(`Fetch Types: Could not fetch definitions`, error);
return '';
}

// for invalid packa
if (text.includes('Cannot find package')) {
console.warn(`Fetch Types: ${text}`);
return '';
} else {
return text;
}
}

/**
Expand All @@ -48,27 +58,25 @@ export async function getOfflineTypeDefinitions(version: string): Promise<boolea
}

/**
* Get TypeScript defintions for a version of Electron. If none can't be
* Get TypeScript definitions for a version of Electron. If none can be
* found, returns null.
*
* @param {string} version
* @returns {void}
*/
export async function getTypeDefinitions(version: string): Promise<string | null> {
export async function getDownloadedVersionTypeDefs(version: ElectronVersion): Promise<string | null> {
const fs = await fancyImport<typeof fsType>('fs-extra');
await fs.mkdirp(definitionPath);
await fs.mkdirp(definitionPath);
const offlinePath = getOfflineTypeDefinitionPath(version.version);

const offlinePath = getOfflineTypeDefinitionPath(version);

if (await getOfflineTypeDefinitions(version)) {
if (await getOfflineTypeDefinitions(version.version)) {
try {
return await fs.readFile(offlinePath, 'utf-8');
} catch (error) {
return null;
}
} else {
const typeDefs = await fetchTypeDefinitions(version);

const typeDefs = await fetchTypeDefinitions(version.version);
if (typeDefs && typeDefs.length > 0) {
try {
await fs.outputFile(offlinePath, typeDefs);
Expand All @@ -79,17 +87,27 @@ export async function getTypeDefinitions(version: string): Promise<string | null
return typeDefs;
}
}

return null;
}
}

export async function getLocalVersionTypeDefs(version: ElectronVersion) {
if (version.source === ElectronVersionSource.local && !!version.localPath) {
const fs = await fancyImport<typeof fsType>('fs-extra');
const typesPath = getLocalTypePathForVersion(version);
if (!!typesPath && fs.existsSync(typesPath)) {
return fs.readFile(typesPath, 'utf-8');
}
}
return null;
}

/**
* Tries to update the editor with type definitions.
*
* @param {string} version
*/
export async function updateEditorTypeDefinitions(version: string, i: number = 0): Promise<void> {
export async function updateEditorTypeDefinitions(version: ElectronVersion, i: number = 0): Promise<void> {
const defer = async (): Promise<void> => {
if (i > 10) {
console.warn(`Fetch Types: Failed, dependencies do not exist`);
Expand All @@ -106,17 +124,36 @@ export async function updateEditorTypeDefinitions(version: string, i: number = 0
const { app } = window.ElectronFiddle;
const monaco: typeof MonacoType = app.monaco!;
const typeDefDisposable: MonacoType.IDisposable = app.typeDefDisposable!;
const typeDefs = await getTypeDefinitions(version);

const getTypeDefs = (version.source === ElectronVersionSource.local) ?
getLocalVersionTypeDefs : getDownloadedVersionTypeDefs;

const typeDefs = await getTypeDefs(version);

if (typeDefDisposable) {
typeDefDisposable.dispose();
}

if (typeDefs) {
console.log(`Fetch Types: Updating Monaco types with electron.d.ts@${version}`);
console.log(`Fetch Types: Updating Monaco types with electron.d.ts@${version.version}`);
const disposable = monaco.languages.typescript.javascriptDefaults.addExtraLib(typeDefs);
window.ElectronFiddle.app.typeDefDisposable = disposable;
} else {
console.log(`Fetch Types: No type definitions for ${version} 😢`);
console.log(`Fetch Types: No type definitions for ${version.version} 😢`);
}
}

export function getLocalTypePathForVersion(version: ElectronVersion) {
if (version.localPath) {
return path.join(
version.localPath,
'gen',
'electron',
'tsc',
'typings',
'electron.d.ts'
);
} else {
return null;
}
}
25 changes: 23 additions & 2 deletions src/renderer/state.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as fsType from 'fs-extra';
import { action, autorun, computed, observable, when } from 'mobx';
import { MosaicNode } from 'react-mosaic-component';

Expand All @@ -19,14 +20,16 @@ import { arrayToStringMap } from '../utils/array-to-stringmap';
import { EditorBackup, getEditorBackup } from '../utils/editor-backup';
import { createMosaicArrangement, getVisibleMosaics } from '../utils/editors-mosaic-arrangement';
import { getName } from '../utils/get-title';
import { fancyImport } from '../utils/import';
import { normalizeVersion } from '../utils/normalize-version';
import { isEditorBackup, isEditorId, isPanelId } from '../utils/type-checks';
import { BinaryManager } from './binary';
import { DEFAULT_MOSAIC_ARRANGEMENT } from './constants';
import { getContent, isContentUnchanged } from './content';
import { updateEditorTypeDefinitions } from './fetch-types';
import { getLocalTypePathForVersion, updateEditorTypeDefinitions } from './fetch-types';
import { ipcRendererManager } from './ipc';
import { activateTheme } from './themes';

import {
addLocalVersion,
ElectronReleaseChannel,
Expand Down Expand Up @@ -96,6 +99,7 @@ export class AppState {
@observable public mosaicArrangement: MosaicNode<MosaicId> | null = DEFAULT_MOSAIC_ARRANGEMENT;
@observable public templateName: string | undefined;
@observable public currentDocsDemoPage: DocsDemoPage = DocsDemoPage.DEFAULT;
@observable public localTypeWatcher: fsType.FSWatcher | undefined;

// -- Various "isShowing" settings ------------------
@observable public isConsoleShowing: boolean = false;
Expand Down Expand Up @@ -405,7 +409,24 @@ export class AppState {
}

// Update TypeScript definitions
updateEditorTypeDefinitions(version);
const versionObject = this.versions[version];

if (versionObject.source === ElectronVersionSource.local) {
const fs = await fancyImport<typeof fsType>('fs-extra');
const typePath = getLocalTypePathForVersion(versionObject);
console.info(`TypeDefs: Watching file for local version ${version} at path ${typePath}`);
this.localTypeWatcher = fs.watch(typePath!, async () => {
console.info(`TypeDefs: Noticed file change at ${typePath}. Updating editor typedefs.`);
await updateEditorTypeDefinitions(versionObject);
});
} else {
if (!!this.localTypeWatcher) {
console.info(`TypeDefs: Switched to downloaded version ${version}. Unwatching local typedefs.`);
this.localTypeWatcher.close();
this.localTypeWatcher = undefined;
}
}
await updateEditorTypeDefinitions(versionObject);

// Fetch new binaries, maybe?
await this.downloadVersion(version);
Expand Down
2 changes: 1 addition & 1 deletion tests/renderer/app-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe('Editors component', () => {
html: 'html-value',
main: 'main-value',
});

expect((window as any).ElectronFiddle.editors.renderer.setValue)
.not.toHaveBeenCalled();
});
Expand Down
6 changes: 3 additions & 3 deletions tests/renderer/binary-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BinaryManager } from '../../src/renderer/binary';
import { overridePlatform, resetPlatform } from '../utils';
import { USER_DATA_PATH } from '../../src/renderer/constants';
import { overridePlatform, resetPlatform } from '../utils';

import * as path from 'path';

Expand Down Expand Up @@ -80,7 +80,7 @@ describe('binary', () => {

await binaryManager.remove('v3.0.0');
expect(binaryManager.removeTypeDefsForVersion).toHaveBeenCalledTimes(4);

});
});

Expand All @@ -103,7 +103,7 @@ describe('binary', () => {

try {
await binaryManager.removeTypeDefsForVersion('v3.0.0');
} catch(e) {
} catch (e) {
expect(e).toEqual(new Error('Bwap bwap'));
}
});
Expand Down
2 changes: 1 addition & 1 deletion tests/renderer/components/settings-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('Settings component', () => {
expect(wrapper).toMatchSnapshot();
});

it.only('closes upon pressing Escape key', () => {
it('closes upon pressing Escape key', () => {
expect(store.isSettingsShowing).toBe(true);
// mock event listener API
const map: any = {};
Expand Down
Loading

0 comments on commit 1f9db42

Please sign in to comment.