Skip to content
This repository has been archived by the owner on Dec 19, 2024. It is now read-only.

Support automatically checking for the node_modules version of Flow #53

Merged
merged 4 commits into from
Dec 6, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 56 additions & 50 deletions lib/flowMain.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,56 @@
/* @flow */

/*
Copyright (c) 2015-present, Facebook, Inc.
All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
the root directory of this source tree.
*/

// Necessary to get the regenerator runtime, which transpiled async functions need
import * as _ from 'regenerator-runtime/runtime';

import * as vscode from 'vscode';

import {CompletionSupport} from './flowCompletion';
import {HoverSupport} from './flowHover'
import {DeclarationSupport} from './flowDeclaration';
import {setupDiagnostics} from './flowDiagnostics';

import {checkNode, checkFlow, isFlowEnabled} from './utils'

type Context = {
subscriptions: Array<vscode.Disposable>
}

const languages = [
{ language: 'javascript', scheme: 'file' },
'javascriptreact'
]

export function activate(context: Context): void {
//User can disable flow for some projects that previously used flow, but it's not have actual typing
if (!isFlowEnabled()) {
return
}
global.vscode = vscode
checkNode()
checkFlow()
// Language features
languages.forEach(lang => {
context.subscriptions.push(vscode.languages.registerHoverProvider(lang, new HoverSupport));
context.subscriptions.push(vscode.languages.registerDefinitionProvider(lang, new DeclarationSupport()));
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(lang, new CompletionSupport(), '.'));
})
// https://github.com/Microsoft/vscode/issues/7031 Workaround for language scoring for language and in-memory. Check in nearest Insiders build
// context.subscriptions.push(vscode.languages.registerCompletionItemProvider({ language: 'javascript' }, new CompletionSupport(), '.'));
// Diagnostics
setupDiagnostics(context.subscriptions);
}
/* @flow */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, bah, git was complaining about line ends - didn't think it would do this


/*
Copyright (c) 2015-present, Facebook, Inc.
All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
the root directory of this source tree.
*/

// Necessary to get the regenerator runtime, which transpiled async functions need
import * as _ from 'regenerator-runtime/runtime';

import * as vscode from 'vscode';

import {CompletionSupport} from './flowCompletion';
import {HoverSupport} from './flowHover'
import {DeclarationSupport} from './flowDeclaration';
import {setupDiagnostics} from './flowDiagnostics';

import {checkNode, checkFlow, isFlowEnabled} from './utils'
import {clearWorkspaceCaches} from './pkg/flow-base/lib/FlowHelpers'

type Context = {
subscriptions: Array<vscode.Disposable>
}

const languages = [
{ language: 'javascript', scheme: 'file' },
'javascriptreact'
]

export function activate(context: Context): void {
//User can disable flow for some projects that previously used flow, but it's not have actual typing
if (!isFlowEnabled()) {
return
}
global.vscode = vscode
checkNode()
checkFlow()

// Language features
languages.forEach(lang => {
context.subscriptions.push(vscode.languages.registerHoverProvider(lang, new HoverSupport));
context.subscriptions.push(vscode.languages.registerDefinitionProvider(lang, new DeclarationSupport()));
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(lang, new CompletionSupport(), '.'));
})
// https://github.com/Microsoft/vscode/issues/7031 Workaround for language scoring for language and in-memory. Check in nearest Insiders build
// context.subscriptions.push(vscode.languages.registerCompletionItemProvider({ language: 'javascript' }, new CompletionSupport(), '.'));
// Diagnostics
setupDiagnostics(context.subscriptions);
}

vscode.workspace.onDidChangeConfiguration(params => {
clearWorkspaceCaches();
});
48 changes: 42 additions & 6 deletions lib/pkg/flow-base/lib/FlowHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,14 @@ function isOptional(param: string): boolean {
return lastChar === '?';
}

function clearWorkspaceCaches() {
flowPathCache.reset();
flowConfigDirCache.reset();
global.cachedPathToFlowBin = undefined;
}

async function isFlowInstalled(): Promise<boolean> {
const flowPath = getPathToFlow();
const flowPath = await getPathToFlow();
if (!flowPathCache.has(flowPath)) {
flowPathCache.set(flowPath, await canFindFlow(flowPath));
}
Expand All @@ -144,12 +150,41 @@ async function canFindFlow(flowPath: string): Promise<boolean> {
}

/**
* @return The path to Flow on the user's machine. It is recommended not to cache the result of this
* function in case the user updates his or her preferences in Atom, in which case the return
* value will be stale.
* @return The path to Flow on the user's machine. First using the the user's
* config, then looking into the node_modules for the project.
*
* It is cached, so it is expected that changing the users settings will
* trigger a call to `clearWorkspaceCaches`.
*/

async function getPathToFlow(): Promise<string> {
if (global.cachedPathToFlowBin) { return global.cachedPathToFlowBin }

const config = global.vscode.workspace.getConfiguration('flow');
const shouldUseNodeModule = config.get('useNPMPackagedFlow');

if (shouldUseNodeModule && await canFindFlow(nodeModuleFlowLocation())){
global.cachedPathToFlowBin = nodeModuleFlowLocation();
return global.cachedPathToFlowBin
}

const userPath = config.get('pathToFlow');
if (await canFindFlow(userPath)) {
global.cachedPathToFlowBin = userPath
return global.cachedPathToFlowBin
}

// Final fallback, mainly so we complete the promise<string> implmentation
return "flow"
}

/**
* @returnThe potential path to Flow on the user's machine if they are using NPM/Yarn to manage
* their installs of flow.
*/
function getPathToFlow(): string {
return global.vscode.workspace.getConfiguration('flow').get('pathToFlow')
function nodeModuleFlowLocation(): string {
const flowBin = process.platform === 'win32' ? 'flow.cmd' : 'flow'
return `${global.vscode.workspace.rootPath}/node_modules/.bin/${flowBin}`
}

function getStopFlowOnExit(): boolean {
Expand Down Expand Up @@ -192,4 +227,5 @@ module.exports = {
processAutocompleteItem,
groupParamNames,
flowCoordsToAtomCoords,
clearWorkspaceCaches
};
7 changes: 4 additions & 3 deletions lib/pkg/flow-base/lib/FlowProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ export class FlowProcess {
// don't want to log because it just means the server is busy and we don't want to wait.
if (!couldRetry && !suppressErrors) {
// not sure what happened, but we'll let the caller deal with it
logger.error(`Flow failed: flow ${args.join(' ')}. Error: ${JSON.stringify(e)}`);
const pathToFlow = await getPathToFlow();
logger.error(`Flow failed: ${pathToFlow} ${args.join(' ')}. Error: ${JSON.stringify(e)}`);
}
throw e;
}
Expand All @@ -153,7 +154,7 @@ export class FlowProcess {

/** Starts a Flow server in the current root */
async _startFlowServer(): Promise<void> {
const pathToFlow = getPathToFlow();
const pathToFlow = await getPathToFlow();
// `flow server` will start a server in the foreground. asyncExecute
// will not resolve the promise until the process exits, which in this
// case is never. We need to use spawn directly to get access to the
Expand Down Expand Up @@ -342,7 +343,7 @@ export class FlowProcess {
...args,
'--from', 'nuclide',
];
const pathToFlow = getPathToFlow();
const pathToFlow = await getPathToFlow();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much slower do you think this call is? I mainly worry about things like autocomplete, where adding 100ms can really degrade performance.

Copy link
Contributor Author

@orta orta Dec 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the difference is a fileExists check for which I don't expect to be a slow it's a which lookup, so it would be - however, I can instead opt to cache the values, I think it's pretty easy to get a callback from VS Code when your settings changes and so the stored path can be reset then - will take a look

const ret = await asyncExecute(pathToFlow, args, options);
if (ret.exitCode !== 0) {
// TODO: bubble up the exit code via return value instead
Expand Down
10 changes: 4 additions & 6 deletions lib/utils/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

import spawn from 'cross-spawn';
import {window, workspace} from 'vscode'
import {getPathToFlow} from "../pkg/flow-base/lib/FlowHelpers"

const NODE_NOT_FOUND = '[Flow] Cannot find node in PATH. The simpliest way to resolve it is install node globally'
const FLOW_NOT_FOUND = '[Flow] Cannot find flow in PATH. Try to install it by npm install flow-bin -g'

function getPathToFlow(): string {
return workspace.getConfiguration('flow').get('pathToFlow')
}

export function isFlowEnabled() {
return workspace.getConfiguration('flow').get('enabled')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this would have different behaviour now, I made them share the code instead

}
Expand All @@ -36,9 +33,10 @@ export function checkNode() {
}
}

export function checkFlow() {
export async function checkFlow() {
const path = await getPathToFlow()
try {
const check = spawn(process.platform === 'win32' ? 'where' : 'which', [getPathToFlow()])
const check = spawn(process.platform === 'win32' ? 'where' : 'which', [path])
let
flowOutput = "",
flowOutputError = ""
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"type": "boolean",
"default": true,
"description": "Stop Flow on Exit"
},
"flow.useNPMPackagedFlow": {
"type": "boolean",
"default": false,
"description": "Support using flow through your node_modules folder, WARNING: Checking this box is a security risk. When you open a project we will immediately run code contained within it."
}
}
}
Expand Down Expand Up @@ -77,4 +82,4 @@
"bugs": {
"url": "https://github.com/flowtype/flow-for-vscode/issues"
}
}
}