Create common module

pull/373/head
Kelvin Schoofs 3 years ago
parent 83cf22abbe
commit 85f7a69c46

@ -6,6 +6,7 @@
"**/node_modules/": true, "**/node_modules/": true,
"dist/": true, "dist/": true,
"util/": true, "util/": true,
"common/out": true,
"webview/build/": true, "webview/build/": true,
".yarn/": true, ".yarn/": true,
".yarnrc.yml": true, ".yarnrc.yml": true,

32
.vscode/tasks.json vendored

@ -6,9 +6,10 @@
{ {
"label": "Extension - Watch all", "label": "Extension - Watch all",
"group": "build", "group": "build",
"dependsOrder": "sequence",
"dependsOn": [ "dependsOn": [
"Extension - Watch", "Extension Common - Watch",
"Extension WebView - Watch" "Extension - Watch non-Common"
], ],
"problemMatcher": [], "problemMatcher": [],
"isBackground": true, "isBackground": true,
@ -16,6 +17,33 @@
"runOn": "folderOpen" "runOn": "folderOpen"
} }
}, },
{
"label": "Extension - Watch non-Common",
"group": "build",
"dependsOrder": "parallel",
"dependsOn": [
"Extension - Watch",
"Extension WebView - Watch"
],
"problemMatcher": [],
"isBackground": true,
},
{
"type": "shell",
"label": "Extension Common - Watch",
"command": "yarn watch",
"options": {
"cwd": "./common"
},
"group": "build",
"problemMatcher": {
"base": "$tsc-watch",
"source": "tsc-watch",
"owner": "tsc-watch",
"applyTo": "allDocuments"
},
"isBackground": true
},
{ {
"type": "npm", "type": "npm",
"label": "Extension - Watch", "label": "Extension - Watch",

@ -2272,7 +2272,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/ssh2@npm:^0.5.41, @types/ssh2@npm:^0.5.47": "@types/ssh2@npm:^0.5.41":
version: 0.5.47 version: 0.5.47
resolution: "@types/ssh2@npm:0.5.47" resolution: "@types/ssh2@npm:0.5.47"
dependencies: dependencies:
@ -3714,6 +3714,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"common@workspace:*, common@workspace:common":
version: 0.0.0-use.local
resolution: "common@workspace:common"
dependencies:
"@types/node": ^12.7.12
"@types/ssh2": ^0.5.41
typescript: ~4.5.5
languageName: unknown
linkType: soft
"commondir@npm:^1.0.1": "commondir@npm:^1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "commondir@npm:1.0.1" resolution: "commondir@npm:1.0.1"
@ -9505,6 +9515,7 @@ resolve@^2.0.0-next.3:
"@types/vscode": ~1.49.0 "@types/vscode": ~1.49.0
"@types/webpack": ^4.4.25 "@types/webpack": ^4.4.25
"@types/winreg": ^1.2.30 "@types/winreg": ^1.2.30
common: "workspace:*"
event-stream: ^4.0.1 event-stream: ^4.0.1
jsonc-parser: ^2.0.0 jsonc-parser: ^2.0.0
semver: ^7.3.5 semver: ^7.3.5
@ -9518,7 +9529,6 @@ resolve@^2.0.0-next.3:
vsce: ^2.5.1 vsce: ^2.5.1
webpack: ^5.69.1 webpack: ^5.69.1
webpack-cli: ^4.7.2 webpack-cli: ^4.7.2
webview: "workspace:*"
winreg: ^1.2.4 winreg: ^1.2.4
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -9717,7 +9727,7 @@ resolve@^2.0.0-next.3:
languageName: node languageName: node
linkType: hard linkType: hard
"webview@workspace:*, webview@workspace:webview": "webview@workspace:webview":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "webview@workspace:webview" resolution: "webview@workspace:webview"
dependencies: dependencies:
@ -9727,12 +9737,12 @@ resolve@^2.0.0-next.3:
"@types/react": ^17.0.18 "@types/react": ^17.0.18
"@types/react-dom": ^17.0.9 "@types/react-dom": ^17.0.9
"@types/react-redux": ^7.1.7 "@types/react-redux": ^7.1.7
"@types/ssh2": ^0.5.47
"@typescript-eslint/eslint-plugin": ^4.5.0 "@typescript-eslint/eslint-plugin": ^4.5.0
"@typescript-eslint/parser": ^4.5.0 "@typescript-eslint/parser": ^4.5.0
babel-eslint: ^10.1.0 babel-eslint: ^10.1.0
babel-loader: 8.1.0 babel-loader: 8.1.0
babel-preset-react-app: 10.0.0 babel-preset-react-app: 10.0.0
common: "workspace:*"
css-loader: 4.3.0 css-loader: 4.3.0
css-minimizer-webpack-plugin: ^3.0.2 css-minimizer-webpack-plugin: ^3.0.2
dotenv: 8.2.0 dotenv: 8.2.0

@ -23,6 +23,7 @@
- All dependencies are upgraded within their (package.json-specified) ranges, to get latest patches - All dependencies are upgraded within their (package.json-specified) ranges, to get latest patches
- Update to Yarn 3.1.1 and TypeScript ~4.5.5 - Update to Yarn 3.1.1 and TypeScript ~4.5.5
- Also ditched `@yarnpkg/plugin-version` which wasn't even really used in the first place - Also ditched `@yarnpkg/plugin-version` which wasn't even really used in the first place
- Created a `common` module which now holds `fileSystemConfig.ts` and `webviewMessages.ts`
## v1.24.1 (2021-12-07) ## v1.24.1 (2021-12-07)

3
common/.gitignore vendored

@ -0,0 +1,3 @@
# Build output
out/

@ -0,0 +1,25 @@
{
"name": "common",
"version": "0.1.0",
"private": true,
"exports": {
"./*": "./out/*"
},
"typesVersions": {
">=0": {
"*": [
"out/*"
]
}
},
"scripts": {
"watch": "tsc -w",
"build": "tsc"
},
"devDependencies": {
"@types/node": "^12.7.12",
"@types/ssh2": "^0.5.41",
"typescript": "~4.5.5"
},
"packageManager": "yarn@3.1.1"
}

@ -0,0 +1,20 @@
{
"compilerOptions": {
"strictNullChecks": true,
"module": "esnext",
"moduleResolution": "node",
"target": "ES2019",
"outDir": "out",
"lib": [
"ES2019"
],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"rootDir": "src"
},
"compileOnSave": true,
"include": [
"src"
]
}

@ -402,7 +402,7 @@
} }
}, },
"scripts": { "scripts": {
"vscode:prepublish": "yarn workspaces foreach -vip -j 2 run build", "vscode:prepublish": "yarn workspaces foreach -tvip -j 2 run build",
"build": "webpack --mode production", "build": "webpack --mode production",
"compile": "webpack --mode development", "compile": "webpack --mode development",
"watch": "webpack --mode development --watch" "watch": "webpack --mode development --watch"
@ -424,19 +424,20 @@
"webpack-cli": "^4.7.2" "webpack-cli": "^4.7.2"
}, },
"dependencies": { "dependencies": {
"common": "workspace:*",
"event-stream": "^4.0.1", "event-stream": "^4.0.1",
"jsonc-parser": "^2.0.0", "jsonc-parser": "^2.0.0",
"semver": "^7.3.5", "semver": "^7.3.5",
"socks": "^2.2.0", "socks": "^2.2.0",
"ssh2": "^0.8.9", "ssh2": "^0.8.9",
"ssh2-streams": "^0.4.10", "ssh2-streams": "^0.4.10",
"webview": "workspace:*",
"winreg": "^1.2.4" "winreg": "^1.2.4"
}, },
"resolutions": { "resolutions": {
"ssh2-streams": "Timmmm/ssh2-streams#patch-1" "ssh2-streams": "Timmmm/ssh2-streams#patch-1"
}, },
"workspaces": [ "workspaces": [
"./common",
"./webview" "./webview"
], ],
"packageManager": "yarn@3.1.1" "packageManager": "yarn@3.1.1"

@ -1,11 +1,11 @@
import { ConfigLocation, FileSystemConfig, invalidConfigName, parseConnectionString } from 'common/fileSystemConfig';
import { readFile, writeFile } from 'fs'; import { readFile, writeFile } from 'fs';
import { parse as parseJsonc, ParseError } from 'jsonc-parser'; import { parse as parseJsonc, ParseError } from 'jsonc-parser';
import * as semver from 'semver';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { ConfigLocation, FileSystemConfig, invalidConfigName, parseConnectionString } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import { toPromise } from './utils'; import { toPromise } from './utils';
import * as semver from 'semver';
// Logger scope with default warning/error options (which enables stacktraces) disabled // Logger scope with default warning/error options (which enables stacktraces) disabled
const logging = Logging.scope(undefined, false); const logging = Logging.scope(undefined, false);

@ -1,3 +1,4 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import { readFile } from 'fs'; import { readFile } from 'fs';
import { Socket } from 'net'; import { Socket } from 'net';
import { userInfo } from 'os'; import { userInfo } from 'os';
@ -5,7 +6,6 @@ import { Client, ClientChannel, ConnectConfig, SFTPWrapper as SFTPWrapperReal }
import { SFTPStream } from 'ssh2-streams'; import { SFTPStream } from 'ssh2-streams';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfig, getFlagBoolean } from './config'; import { getConfig, getFlagBoolean } from './config';
import type { FileSystemConfig } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import type { PuttySession } from './putty'; import type { PuttySession } from './putty';
import { toPromise, validatePort } from './utils'; import { toPromise, validatePort } from './utils';

@ -1,9 +1,9 @@
import type { EnvironmentVariable, FileSystemConfig } from 'common/fileSystemConfig';
import { posix as path } from 'path'; import { posix as path } from 'path';
import * as readline from 'readline'; import * as readline from 'readline';
import type { Client, ClientChannel } from 'ssh2'; import type { Client, ClientChannel } from 'ssh2';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { configMatches, getFlagBoolean, loadConfigs } from './config'; import { configMatches, getFlagBoolean, loadConfigs } from './config';
import type { EnvironmentVariable, FileSystemConfig } from './fileSystemConfig';
import { Logging, LOGGING_NO_STACKTRACE } from './logging'; import { Logging, LOGGING_NO_STACKTRACE } from './logging';
import type { SSHPseudoTerminal } from './pseudoTerminal'; import type { SSHPseudoTerminal } from './pseudoTerminal';
import { calculateShellConfig, ShellConfig, tryEcho } from './shellConfig'; import { calculateShellConfig, ShellConfig, tryEcho } from './shellConfig';

@ -1,8 +1,8 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { loadConfigs } from './config'; import { loadConfigs } from './config';
import type { Connection } from './connection'; import type { Connection } from './connection';
import type { FileSystemConfig } from './fileSystemConfig';
import { FileSystemRouter } from './fileSystemRouter'; import { FileSystemRouter } from './fileSystemRouter';
import { Logging, setDebug } from './logging'; import { Logging, setDebug } from './logging';
import { Manager } from './manager'; import { Manager } from './manager';

@ -1,5 +1,5 @@
import { FileSystemConfig, isFileSystemConfig } from 'common/fileSystemConfig';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { FileSystemConfig, isFileSystemConfig } from './fileSystemConfig';
// Since the Extension Development Host runs with debugger, we can use this to detect if we're debugging. // Since the Extension Development Host runs with debugger, we can use this to detect if we're debugging.
// The only things it currently does is copying Logging messages to the console, while also enabling // The only things it currently does is copying Logging messages to the console, while also enabling

@ -1,13 +1,13 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import type { Navigation } from 'common/webviewMessages';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfig, getFlagBoolean, loadConfigsRaw } from './config'; import { getConfig, getFlagBoolean, loadConfigsRaw } from './config';
import { Connection, ConnectionManager } from './connection'; import { Connection, ConnectionManager } from './connection';
import type { FileSystemConfig } from './fileSystemConfig';
import { Logging, LOGGING_NO_STACKTRACE } from './logging'; import { Logging, LOGGING_NO_STACKTRACE } from './logging';
import { isSSHPseudoTerminal, replaceVariables, replaceVariablesRecursive } from './pseudoTerminal'; import { isSSHPseudoTerminal, replaceVariables, replaceVariablesRecursive } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem'; import type { SSHFileSystem } from './sshFileSystem';
import { catchingPromise, joinCommands } from './utils'; import { catchingPromise, joinCommands } from './utils';
import type { Navigation } from './webviewMessages';
function commandArgumentToName(arg?: string | FileSystemConfig | Connection): string { function commandArgumentToName(arg?: string | FileSystemConfig | Connection): string {
if (!arg) return 'undefined'; if (!arg) return 'undefined';

@ -1,8 +1,8 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import * as dns from 'dns'; import * as dns from 'dns';
import { request } from 'http'; import { request } from 'http';
import { SocksClient } from 'socks'; import { SocksClient } from 'socks';
import type { FileSystemConfig } from './fileSystemConfig';
import { Logging } from './logging'; import { Logging } from './logging';
import { toPromise, validatePort } from './utils'; import { toPromise, validatePort } from './utils';

@ -1,9 +1,9 @@
import type { EnvironmentVariable, FileSystemConfig } from "common/fileSystemConfig";
import * as path from 'path'; import * as path from 'path';
import type { ClientChannel, PseudoTtyOptions } from "ssh2"; import type { ClientChannel, PseudoTtyOptions } from "ssh2";
import * as vscode from "vscode"; import * as vscode from "vscode";
import { getFlagBoolean } from './config'; import { getFlagBoolean } from './config';
import type { Connection } from './connection'; import type { Connection } from './connection';
import type { EnvironmentVariable, FileSystemConfig } from "./fileSystemConfig";
import { Logging, LOGGING_NO_STACKTRACE } from "./logging"; import { Logging, LOGGING_NO_STACKTRACE } from "./logging";
import { environmentToExportString, joinCommands, mergeEnvironment, toPromise } from './utils'; import { environmentToExportString, joinCommands, mergeEnvironment, toPromise } from './utils';

@ -1,10 +1,10 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import * as path from 'path'; import * as path from 'path';
import type * as ssh2 from 'ssh2'; import type * as ssh2 from 'ssh2';
import type * as ssh2s from 'ssh2-streams'; import type * as ssh2s from 'ssh2-streams';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getFlagBoolean } from './config'; import { getFlagBoolean } from './config';
import type { FileSystemConfig } from './fileSystemConfig';
import { Logger, Logging, LOGGING_NO_STACKTRACE, LOGGING_SINGLE_LINE_STACKTRACE, withStacktraceOffset } from './logging'; import { Logger, Logging, LOGGING_NO_STACKTRACE, LOGGING_SINGLE_LINE_STACKTRACE, withStacktraceOffset } from './logging';
// This makes it report a single line of the stacktrace of where the e.g. logger.info() call happened // This makes it report a single line of the stacktrace of where the e.g. logger.info() call happened

@ -1,8 +1,8 @@
import { FileSystemConfig, getGroups } from 'common/fileSystemConfig';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfigs, UPDATE_LISTENERS } from './config'; import { getConfigs, UPDATE_LISTENERS } from './config';
import type { Connection, ConnectionManager } from './connection'; import type { Connection, ConnectionManager } from './connection';
import { FileSystemConfig, getGroups } from './fileSystemConfig';
import type { SSHPseudoTerminal } from './pseudoTerminal'; import type { SSHPseudoTerminal } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem'; import type { SSHFileSystem } from './sshFileSystem';
import { formatItem } from './ui-utils'; import { formatItem } from './ui-utils';

@ -1,8 +1,8 @@
import { FileSystemConfig, parseConnectionString } from 'common/fileSystemConfig';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfigs } from './config'; import { getConfigs } from './config';
import type { Connection, ConnectionManager } from './connection'; import type { Connection, ConnectionManager } from './connection';
import { FileSystemConfig, parseConnectionString } from './fileSystemConfig';
import type { Manager } from './manager'; import type { Manager } from './manager';
import type { SSHPseudoTerminal } from './pseudoTerminal'; import type { SSHPseudoTerminal } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem'; import type { SSHFileSystem } from './sshFileSystem';

@ -1,5 +1,5 @@
import type { EnvironmentVariable } from "./fileSystemConfig"; import type { EnvironmentVariable } from 'common/fileSystemConfig';
import { DEBUG } from "./logging"; import { DEBUG } from './logging';
function prepareStackTraceDefault(error: Error, stackTraces: NodeJS.CallSite[]): string { function prepareStackTraceDefault(error: Error, stackTraces: NodeJS.CallSite[]): string {
return stackTraces.reduce((s, c) => `${s}\n\tat ${c} (${c.getFunction()})`, `${error.name || "Error"}: ${error.message || ""}`); return stackTraces.reduce((s, c) => `${s}\n\tat ${c} (${c.getFunction()})`, `${error.name || "Error"}: ${error.message || ""}`);

@ -1,12 +1,12 @@
import { getLocations } from 'common/fileSystemConfig';
import type { Message, Navigation } from 'common/webviewMessages';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { deleteConfig, loadConfigsRaw, updateConfig } from './config'; import { deleteConfig, loadConfigsRaw, updateConfig } from './config';
import { getLocations } from './fileSystemConfig';
import { DEBUG, Logging as _Logging, LOGGING_NO_STACKTRACE } from './logging'; import { DEBUG, Logging as _Logging, LOGGING_NO_STACKTRACE } from './logging';
import { toPromise } from './utils'; import { toPromise } from './utils';
import type { Message, Navigation } from './webviewMessages';
const Logging = _Logging.scope('WebView'); const Logging = _Logging.scope('WebView');

@ -14,9 +14,5 @@
"compileOnSave": true, "compileOnSave": true,
"include": [ "include": [
"src" "src"
],
"exclude": [
"node_modules",
".vscode-test"
] ]
} }

@ -13,7 +13,6 @@
"@types/react": "^17.0.18", "@types/react": "^17.0.18",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"@types/react-redux": "^7.1.7", "@types/react-redux": "^7.1.7",
"@types/ssh2": "^0.5.47",
"@typescript-eslint/eslint-plugin": "^4.5.0", "@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0", "@typescript-eslint/parser": "^4.5.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
@ -68,5 +67,8 @@
"presets": [ "presets": [
"react-app" "react-app"
] ]
},
"dependencies": {
"common": "workspace:*"
} }
} }

@ -1,6 +1,6 @@
import { getGroups } from 'common/fileSystemConfig';
import { FieldDropdownWithInput } from '../FieldTypes/dropdownwithinput'; import { FieldDropdownWithInput } from '../FieldTypes/dropdownwithinput';
import { connect } from '../redux'; import { connect } from '../redux';
import { getGroups } from '../types/fileSystemConfig';
export interface StateProps { export interface StateProps {
values: string[]; values: string[];

@ -1,3 +1,4 @@
import { FileSystemConfig, invalidConfigName } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import { FieldCheckbox } from '../FieldTypes/checkbox'; import { FieldCheckbox } from '../FieldTypes/checkbox';
import { FieldDropdown } from '../FieldTypes/dropdown'; import { FieldDropdown } from '../FieldTypes/dropdown';
@ -6,7 +7,6 @@ import { FieldNumber } from '../FieldTypes/number';
import { FieldPath } from '../FieldTypes/path'; import { FieldPath } from '../FieldTypes/path';
import { FieldString } from '../FieldTypes/string'; import { FieldString } from '../FieldTypes/string';
import { FieldUmask } from '../FieldTypes/umask'; import { FieldUmask } from '../FieldTypes/umask';
import { FileSystemConfig, invalidConfigName } from '../types/fileSystemConfig';
import FieldConfigGroup from './configGroupField'; import FieldConfigGroup from './configGroupField';
import { PROXY_FIELD } from './proxyFields'; import { PROXY_FIELD } from './proxyFields';

@ -1,7 +1,7 @@
import { FileSystemConfig, formatConfigLocation } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import { FieldGroup } from '../FieldTypes/group'; import { FieldGroup } from '../FieldTypes/group';
import { connect, pickProperties } from '../redux'; import { connect, pickProperties } from '../redux';
import { FileSystemConfig, formatConfigLocation } from '../types/fileSystemConfig';
import type { IConfigEditorState } from '../view'; import type { IConfigEditorState } from '../view';
import { configEditorSetNewConfig, configEditorSetStatusMessage, openStartScreen } from '../view/actions'; import { configEditorSetNewConfig, configEditorSetStatusMessage, openStartScreen } from '../view/actions';
import { deleteConfig, saveConfig } from '../vscode'; import { deleteConfig, saveConfig } from '../vscode';

@ -1,9 +1,9 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import { FieldConfig } from '../FieldTypes/config'; import { FieldConfig } from '../FieldTypes/config';
import { FieldDropdown } from '../FieldTypes/dropdown'; import { FieldDropdown } from '../FieldTypes/dropdown';
import { FieldNumber } from '../FieldTypes/number'; import { FieldNumber } from '../FieldTypes/number';
import { FieldString } from '../FieldTypes/string'; import { FieldString } from '../FieldTypes/string';
import { FileSystemConfig } from '../types/fileSystemConfig';
import type { FieldFactory, FSCChanged, FSCChangedMultiple } from './fields'; import type { FieldFactory, FSCChanged, FSCChangedMultiple } from './fields';
function hostAndPort(config: FileSystemConfig, onChange: FSCChanged<'proxy'>): React.ReactElement { function hostAndPort(config: FileSystemConfig, onChange: FSCChanged<'proxy'>): React.ReactElement {

@ -1,6 +1,6 @@
import type { FileSystemConfig } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import { connect } from '../redux'; import { connect } from '../redux';
import type { FileSystemConfig } from '../types/fileSystemConfig';
import { openConfigEditor } from '../view/actions'; import { openConfigEditor } from '../view/actions';
import './index.css'; import './index.css';

@ -1,7 +1,7 @@
import { FileSystemConfig, formatConfigLocation } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import ConfigList from './ConfigList'; import ConfigList from './ConfigList';
import { connect, pickProperties } from './redux'; import { connect, pickProperties } from './redux';
import { FileSystemConfig, formatConfigLocation } from './types/fileSystemConfig';
import type { IConfigLocatorState } from './view'; import type { IConfigLocatorState } from './view';
function displayName(config: FileSystemConfig) { function displayName(config: FileSystemConfig) {

@ -1,5 +1,5 @@
import { FileSystemConfig, formatConfigLocation } from 'common/fileSystemConfig';
import { connect } from '../redux'; import { connect } from '../redux';
import { FileSystemConfig, formatConfigLocation } from '../types/fileSystemConfig';
import type { Props as FieldBaseProps } from './base'; import type { Props as FieldBaseProps } from './base';
import { FieldDropdown, Props as FieldDropdownProps } from './dropdown'; import { FieldDropdown, Props as FieldDropdownProps } from './dropdown';

@ -1,9 +1,9 @@
import { ConfigLocation, formatConfigLocation, invalidConfigName } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import { FieldDropdown } from './FieldTypes/dropdown'; import { FieldDropdown } from './FieldTypes/dropdown';
import { FieldGroup } from './FieldTypes/group'; import { FieldGroup } from './FieldTypes/group';
import { FieldString } from './FieldTypes/string'; import { FieldString } from './FieldTypes/string';
import { connect, pickProperties, State } from './redux'; import { connect, pickProperties, State } from './redux';
import { ConfigLocation, formatConfigLocation, invalidConfigName } from './types/fileSystemConfig';
import type { INewConfigState } from './view'; import type { INewConfigState } from './view';
import { newConfigSetLocation, newConfigSetName, openConfigEditor, openStartScreen } from './view/actions'; import { newConfigSetLocation, newConfigSetName, openConfigEditor, openStartScreen } from './view/actions';
import { createConfig } from './vscode'; import { createConfig } from './vscode';

@ -1,8 +1,8 @@
import { ConfigLocation, FileSystemConfig, formatConfigLocation, groupByGroup, groupByLocation } from 'common/fileSystemConfig';
import * as React from 'react'; import * as React from 'react';
import ConfigList from './ConfigList'; import ConfigList from './ConfigList';
import { receivedData } from './data/actions'; import { receivedData } from './data/actions';
import { connect, pickProperties } from './redux'; import { connect, pickProperties } from './redux';
import { ConfigLocation, FileSystemConfig, formatConfigLocation, groupByGroup, groupByLocation } from './types/fileSystemConfig';
import type { IStartScreenState } from './view'; import type { IStartScreenState } from './view';
import { openNewConfig, openStartScreen } from './view/actions'; import { openNewConfig, openStartScreen } from './view/actions';
import { API } from './vscode'; import { API } from './vscode';

@ -1,4 +1,4 @@
import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; import type { ConfigLocation, FileSystemConfig } from 'common/fileSystemConfig';
export enum ActionType { export enum ActionType {
RECEIVED_DATA = 'RECEIVED_DATA', RECEIVED_DATA = 'RECEIVED_DATA',

@ -1,4 +1,4 @@
import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; import type { ConfigLocation, FileSystemConfig } from 'common/fileSystemConfig';
export interface IState { export interface IState {
configs: FileSystemConfig[]; configs: FileSystemConfig[];

@ -1,161 +0,0 @@
import type { ConnectConfig } from 'ssh2';
export interface ProxyConfig {
type: 'socks4' | 'socks5' | 'http';
host: string;
port: number;
}
export type ConfigLocation = number | string;
/** Might support conditional stuff later, although ssh2/OpenSSH might not support that natively */
export interface EnvironmentVariable {
key: string;
value: string;
}
export function formatConfigLocation(location?: ConfigLocation): string {
if (!location) return 'Unknown location';
if (typeof location === 'number') {
return `${[, 'Global', 'Workspace', 'WorkspaceFolder'][location] || 'Unknown'} settings.json`;
}
return location;
}
export function getLocations(configs: FileSystemConfig[]): ConfigLocation[] {
const res: ConfigLocation[] = [1, 2 /*, 3*/]; // No WorkspaceFolder support (for now)
// TODO: Suggest creating sshfs.jsonc etc in current workspace folder(s) (UI feature?)
for (const { _location } of configs) {
if (!_location) continue;
if (!res.find(l => l === _location)) {
res.push(_location);
}
}
return res;
}
export function getGroups(configs: FileSystemConfig[], expanded = false): string[] {
const res: string[] = [];
function addGroup(group: string) {
if (!res.find(l => l === group)) {
res.push(group);
}
}
for (const { group } of configs) {
if (!group) continue;
const groups = expanded ? group.split('.') : [group];
groups.forEach((g, i) => addGroup([...groups.slice(0, i), g].join('.')));
}
return res;
}
export function groupByLocation(configs: FileSystemConfig[]): [ConfigLocation, FileSystemConfig[]][] {
const res: [ConfigLocation, FileSystemConfig[]][] = [];
function getForLoc(loc: ConfigLocation = 'Unknown') {
let found = res.find(([l]) => l === loc);
if (found) return found;
found = [loc, []];
res.push(found);
return found;
}
for (const config of configs) {
getForLoc(config._location!)[1].push(config);
}
return res;
}
export function groupByGroup(configs: FileSystemConfig[]): [string, FileSystemConfig[]][] {
const res: [string, FileSystemConfig[]][] = [];
function getForGroup(group: string = '') {
let found = res.find(([l]) => l === group);
if (found) return found;
found = [group, []];
res.push(found);
return found;
}
for (const config of configs) {
getForGroup(config.group)[1].push(config);
}
return res;
}
export interface FileSystemConfig extends ConnectConfig {
/** Name of the config. Can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@ */
name: string;
/** Optional label to display in some UI places (e.g. popups) */
label?: string;
/** Optional group for this config, to group configs together in some UI places. Allows subgroups, in the format "Group1.SubGroup1.Subgroup2" */
group?: string;
/** Whether to merge this "lower" config (e.g. from workspace settings) into higher configs (e.g. from global settings) */
merge?: boolean;
/** Path on the remote server that should be opened by default when creating a terminal or using the `Add as Workspace folder` command/button. Defaults to `/` */
root?: string;
/** A name of a PuTTY session, or `true` to find the PuTTY session from the host address */
putty?: string | boolean;
/** Optional object defining a proxy to use */
proxy?: ProxyConfig;
/** Optional path to a private keyfile to authenticate with */
privateKeyPath?: string;
/** A name of another config to use as a hop */
hop?: string;
/** The command to run on the remote SSH session to start a SFTP session (defaults to sftp subsystem) */
sftpCommand?: string;
/** Whether to use a sudo shell (and for which user) to run the sftpCommand in (sftpCommand defaults to /usr/lib/openssh/sftp-server if missing) */
sftpSudo?: string | boolean;
/** The command(s) to run when a new SSH terminal gets created. Defaults to `$SHELL`. Internally the command `cd ...` is run first */
terminalCommand?: string | string[];
/** The command(s) to run when a `ssh-shell` task gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first */
taskCommand?: string | string[];
/** An object with environment variables to add to the SSH connection. Affects the whole connection thus all terminals */
environment?: EnvironmentVariable[] | Record<string, string>;
/** The filemode to assign to new files created using VS Code, not the terminal. Similar to umask. Defaults to `rw-rw-r--` (regardless of server config, whether you are root, ...) */
newFileMode?: number | string;
/** Whether this config was created from an instant connection string. Enables fuzzy matching for e.g. PuTTY, config-by-host, ... */
instantConnection?: boolean;
/** List of special flags to enable/disable certain fixes/features. Flags are usually used for issues or beta testing. Flags can disappear/change anytime! */
flags?: string[];
/** Internal property saying where this config comes from. Undefined if this config is merged or something */
_location?: ConfigLocation;
/** Internal property keeping track of where this config comes from (including merges) */
_locations: ConfigLocation[];
/** Internal property keeping track of whether this config is an actually calculated one, and if so, which config it originates from (normally itself) */
_calculated?: FileSystemConfig;
}
export function invalidConfigName(name: string) {
if (!name) return 'Missing a name for this SSH FS';
if (name.match(/^[\w_\\/.@\-+]+$/)) return null;
return `A SSH FS name can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@`;
}
/**
* https://regexr.com/5m3gl (mostly based on https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04)
* Supports several formats, the first one being the "full" format, with others being partial:
* - `user;abc=def,a-b=1-5@server.example.com:22/some/file.ext`
* - `user@server.example.com/directory`
* - `server:22/directory`
* - `test-user@server`
* - `server`
* - `@server/path` - Unlike OpenSSH, we allow a @ (and connection parameters) without a username
*
* The resulting FileSystemConfig will have as name basically the input, but without the path. If there is no
* username given, the name will start with `@`, as to differentiate between connection strings and config names.
*/
const CONNECTION_REGEX = /^((?<user>[\w\-._]+)?(;[\w-]+=[\w\d-]+(,[\w\d-]+=[\w\d-]+)*)?@)?(?<host>[^\s@\\/:,=]+)(:(?<port>\d+))?(?<path>\/\S*)?$/;
export function parseConnectionString(input: string): [config: FileSystemConfig, path?: string] | string {
input = input.trim();
const match = input.match(CONNECTION_REGEX);
if (!match) return 'Invalid format, expected something like "user@example.com:22/some/path"';
const { user, host, path } = match.groups!;
const portStr = match.groups!.port;
const port = portStr ? Number.parseInt(portStr) : undefined;
if (portStr && (!port || port < 1 || port > 65535)) return `The string '${port}' is not a valid port number`;
const name = `${user || ''}@${host}${port ? `:${port}` : ''}${path || ''}`;
return [{
name, host, port,
instantConnection: true,
username: user || '$USERNAME',
_locations: [],
}, path];
}

@ -1,70 +0,0 @@
import type { ConfigLocation, FileSystemConfig } from './fileSystemConfig';
/* Type of messages*/
export interface RequestDataMessage {
type: 'requestData';
}
export interface ResponseDataMessage {
type: 'responseData';
configs: FileSystemConfig[];
locations: ConfigLocation[];
}
export interface SaveConfigMessage {
type: 'saveConfig';
config: FileSystemConfig;
name?: string;
uniqueId?: string;
remove?: boolean;
}
export interface SaveConfigResultMessage {
type: 'saveConfigResult';
error?: string;
config: FileSystemConfig;
uniqueId?: string;
}
export interface PromptPathMessage {
type: 'promptPath';
uniqueId?: string;
}
export interface PromptPathResultMessage {
type: 'promptPathResult';
error?: string;
path?: string;
uniqueId?: string;
}
export interface NavigateMessage {
type: 'navigate';
navigation: Navigation;
}
export interface NavigatedMessage {
type: 'navigated';
view: string;
}
export interface MessageTypes {
requestData: RequestDataMessage;
responseData: ResponseDataMessage;
saveConfig: SaveConfigMessage;
saveConfigResult: SaveConfigResultMessage;
promptPath: PromptPathMessage;
promptPathResult: PromptPathResultMessage;
navigate: NavigateMessage;
navigated: NavigatedMessage;
}
export type Message = MessageTypes[keyof MessageTypes];
/* Types related to NavigateMessage */
export interface NewConfigNavigation {
type: 'newconfig';
}
export interface EditConfigNavigation {
type: 'editconfig';
config: FileSystemConfig | FileSystemConfig[];
}
export type Navigation = NewConfigNavigation | EditConfigNavigation;

@ -1,4 +1,4 @@
import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; import type { ConfigLocation, FileSystemConfig } from 'common/fileSystemConfig';
export enum ActionType { export enum ActionType {
// Startscreen // Startscreen

@ -1,4 +1,4 @@
import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; import type { ConfigLocation, FileSystemConfig } from 'common/fileSystemConfig';
interface IViewState<V extends string> { interface IViewState<V extends string> {
view: V; view: V;

@ -1,6 +1,6 @@
import type { ConfigLocation, FileSystemConfig } from './types/fileSystemConfig'; import type { ConfigLocation, FileSystemConfig } from 'common/fileSystemConfig';
import type { Message, MessageTypes, PromptPathResultMessage, SaveConfigResultMessage } from './types/webviewMessages'; import type { Message, MessageTypes, PromptPathResultMessage, SaveConfigResultMessage } from 'common/webviewMessages';
interface VSCodeAPI { interface VSCodeAPI {
postMessage(msg: Message): void; postMessage(msg: Message): void;

Loading…
Cancel
Save