reorgnize imports and remove the file: utilities (#48)
* reorgnize imports and remove the file: utilities * remove useless code * close no-unused-param tsconfig setting for now * correct import typo
This commit is contained in:
Родитель
f26f00be4f
Коммит
40a636bce2
|
@ -1,25 +1,17 @@
|
|||
"use strict";
|
||||
|
||||
import * as path from "path";
|
||||
import { clearInterval, setInterval, setTimeout } from "timers";
|
||||
import { commands, MessageItem, OutputChannel, window } from "vscode";
|
||||
import * as nls from "vscode-nls";
|
||||
import { AzureAccount, AzureSession, AzureSubscription } from "./azure-account.api";
|
||||
import {
|
||||
delay, Errors, getStorageAccountKey, getUserSettings,
|
||||
provisionConsole, resetConsole, runInTerminal,
|
||||
} from "./cloudConsoleLauncher";
|
||||
import { TerminalType } from "./shared";
|
||||
import { CSTerminal, exec, IExecResult } from "./utilities";
|
||||
import { isNodeVersionValid } from "./utils/nodeUtils";
|
||||
import { openUrlHint } from "./utils/uiUtils";
|
||||
|
||||
import { SubscriptionClient } from "azure-arm-resource";
|
||||
import { TIMEOUT } from "dns";
|
||||
import { PassThrough } from "stream";
|
||||
import { clearInterval, setInterval, setTimeout } from "timers";
|
||||
import { commands, env, MessageItem, OutputChannel, Terminal, window } from "vscode";
|
||||
import { configure } from "vscode/lib/testrunner";
|
||||
|
||||
import * as cp from "child_process";
|
||||
import * as fsExtra from "fs-extra";
|
||||
import * as opn from "opn";
|
||||
import * as path from "path";
|
||||
import * as semver from "semver";
|
||||
import * as nls from "vscode-nls";
|
||||
import * as ws from "ws";
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export interface IOS {
|
||||
|
@ -52,23 +44,14 @@ export function openCloudConsole(
|
|||
outputChannel.show();
|
||||
outputChannel.appendLine("Attempting to open CloudConsole - Connecting to cloudshell");
|
||||
/* tslint:disable:semicolon */
|
||||
const progress = delayedInterval(() => { outputChannel.append("..")}, 500);
|
||||
const progress = delayedInterval(() => { outputChannel.append("..") }, 500);
|
||||
/* tslint:enable:semicolon */
|
||||
|
||||
const isWindows = process.platform === "win32";
|
||||
if (isWindows) {
|
||||
// See below
|
||||
try {
|
||||
const { stdout } = await exec("node.exe --version");
|
||||
const version = stdout[0] === "v" && stdout.substr(1).trim();
|
||||
if (version && semver.valid(version) && semver.lt(version, "6.0.0")) {
|
||||
progress.cancel();
|
||||
return requiresNode();
|
||||
}
|
||||
} catch (err) {
|
||||
progress.cancel();
|
||||
return requiresNode();
|
||||
}
|
||||
if (isWindows && !await isNodeVersionValid()) {
|
||||
progress.cancel();
|
||||
openUrlHint("Opening a Cloud Shell currently requires Node.js 6 or later being installed.", "https://nodejs.org");
|
||||
return;
|
||||
}
|
||||
|
||||
// Loging in to Azure using the azure account extension
|
||||
|
@ -82,7 +65,8 @@ export function openCloudConsole(
|
|||
const result = await findUserSettings(tokens);
|
||||
if (!result) {
|
||||
progress.cancel();
|
||||
return requiresSetUp();
|
||||
openUrlHint("First launch of Cloud Shell requires setup in the Azure portal.", "https://portal.azure.com");
|
||||
return;
|
||||
}
|
||||
|
||||
outputChannel.append("\nUsersettings obtained from Tokens - proceeding\n");
|
||||
|
@ -110,7 +94,7 @@ export function openCloudConsole(
|
|||
storageAccount.storageAccountName).then((keys) => {
|
||||
outputChannel.appendLine("Storage key obtained ");
|
||||
storageAccountKey = keys.body.keys[0].value;
|
||||
});
|
||||
});
|
||||
|
||||
// Getting the console URI
|
||||
let consoleUri: string;
|
||||
|
@ -170,7 +154,7 @@ export function openCloudConsole(
|
|||
await delay(500);
|
||||
|
||||
terminal.show();
|
||||
return [ terminal, response, storageAccount.storageAccountName, storageAccountKey, fileShareName, storageAccount.resourceGroup];
|
||||
return [terminal, response, storageAccount.storageAccountName, storageAccountKey, fileShareName, storageAccount.resourceGroup];
|
||||
|
||||
})().catch((err) => {
|
||||
outputChannel.append("\nConnecting to CloudShell failed with error: \n" + err);
|
||||
|
@ -202,16 +186,16 @@ async function acquireToken(session: AzureSession) {
|
|||
const environment: any = session.environment;
|
||||
credentials.context.acquireToken(environment.activeDirectoryResourceId,
|
||||
credentials.username, credentials.clientId, (err: any, result: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({
|
||||
session,
|
||||
accessToken: result.accessToken,
|
||||
refreshToken: result.refreshToken,
|
||||
});
|
||||
}
|
||||
});
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({
|
||||
session,
|
||||
accessToken: result.accessToken,
|
||||
refreshToken: result.refreshToken,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -225,27 +209,6 @@ async function findUserSettings(tokens: IToken[]) {
|
|||
}
|
||||
}
|
||||
|
||||
async function requiresSetUp() {
|
||||
const open: MessageItem = { title: "Open instruction" };
|
||||
const close: MessageItem = { title: "Close", isCloseAffordance: true };
|
||||
const message = "First launch of Cloud Shell requires setup in the Azure portal (https://portal.azure.com).";
|
||||
const response = await window.showInformationMessage(message, open, close);
|
||||
if (response === open) {
|
||||
opn("https://docs.microsoft.com/en-us/azure/cloud-shell/overview");
|
||||
}
|
||||
}
|
||||
|
||||
async function requiresNode() {
|
||||
|
||||
const open: MessageItem = { title: "Open" };
|
||||
const close: MessageItem = { title: "Close", isCloseAffordance: true };
|
||||
const message = "Opening a Cloud Shell currently requires Node.js 6 or later being installed (https://nodejs.org).";
|
||||
const response = await window.showInformationMessage(message, open, close);
|
||||
if (response === open) {
|
||||
opn("https://nodejs.org");
|
||||
}
|
||||
}
|
||||
|
||||
function delayed(func: () => void, timerDelay: number) {
|
||||
const handle = setTimeout(func, timerDelay);
|
||||
return {
|
||||
|
@ -259,7 +222,3 @@ function delayedInterval(func: () => void, interval: number) {
|
|||
cancel: () => clearInterval(handle),
|
||||
};
|
||||
}
|
||||
|
||||
function escapeFile(data: string): string {
|
||||
return data.replace(/"/g, '\\"');
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
import * as fsExtra from "fs-extra";
|
||||
import * as request from "request-promise";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as azureStorage from "azure-storage";
|
||||
"use strict";
|
||||
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { FileSystem } from "./shared";
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
import * as fsExtra from "fs-extra";
|
||||
import * as ost from "os";
|
||||
import * as path from "path";
|
||||
import { clearInterval, setInterval } from "timers";
|
||||
import * as vscode from "vscode";
|
||||
import { MessageItem, Terminal} from "vscode";
|
||||
import * as ws from "ws";
|
||||
import { AzureAccount, AzureSubscription } from "./azure-account.api";
|
||||
import { BaseShell } from "./baseShell";
|
||||
import { openCloudConsole, OSes } from "./cloudConsole";
|
||||
|
@ -6,24 +14,6 @@ import { delay } from "./cloudConsoleLauncher";
|
|||
import { aciConfig, Constants, exportContainerCmd, exportTestScript } from "./constants";
|
||||
import { azFileDelete, azFilePush, escapeFile, TerminalType, TFTerminal } from "./shared";
|
||||
|
||||
import { CSTerminal } from "./utilities";
|
||||
|
||||
import { Message } from "_debugger";
|
||||
import { escape } from "querystring";
|
||||
import { cursorTo } from "readline";
|
||||
import { PassThrough } from "stream";
|
||||
import { clearInterval, setInterval } from "timers";
|
||||
import { ConfigurationTarget, MessageItem, Terminal, ThemeColor } from "vscode";
|
||||
import { configure } from "vscode/lib/testrunner";
|
||||
|
||||
import * as fsExtra from "fs-extra";
|
||||
import * as ost from "os";
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import * as ws from "ws";
|
||||
|
||||
import * as extension from "./extension";
|
||||
|
||||
const tempFile = path.join(ost.tmpdir(), "cloudshell" + vscode.env.sessionId + ".log");
|
||||
|
||||
export class CloudShell extends BaseShell {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as vscode from "vscode";
|
||||
import { azFilePush } from "./shared";
|
||||
"use strict";
|
||||
|
||||
export class Constants {
|
||||
public static ExtensionId = "vscode-terraform-azure";
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
"use strict";
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { commands, Disposable, extensions, GlobPattern, MessageItem, window } from "vscode";
|
||||
|
||||
import { AzureServiceClient, BaseResource } from "ms-rest-azure";
|
||||
import { join } from "path";
|
||||
|
||||
import { AzureAccount, AzureSession, AzureSubscription } from "./azure-account.api";
|
||||
import { BaseShell } from "./baseShell";
|
||||
import { CloudShell } from "./cloudShell";
|
||||
import { Constants } from "./constants";
|
||||
import { IntegratedShell } from "./integratedShell";
|
||||
import { azFilePush } from "./shared";
|
||||
import { TestOption } from "./utilities";
|
||||
import { TestOption } from "./shared";
|
||||
import { isDotInstalled } from "./utils/dotUtils";
|
||||
|
||||
let cs: CloudShell;
|
||||
let is: IntegratedShell;
|
||||
let outputChannel: vscode.OutputChannel;
|
||||
let fileWatcher: vscode.FileSystemWatcher;
|
||||
let isFirstPush = true;
|
||||
// tslint:disable-next-line:variable-name prefer-const
|
||||
let _disposable: Disposable;
|
||||
|
||||
function getShell(): BaseShell {
|
||||
let activeShell = null;
|
||||
|
@ -36,28 +24,20 @@ function getShell(): BaseShell {
|
|||
return activeShell;
|
||||
}
|
||||
|
||||
function init(): void {
|
||||
cs = new CloudShell(outputChannel);
|
||||
is = new IntegratedShell(outputChannel);
|
||||
initFileWatcher();
|
||||
outputChannel.show();
|
||||
}
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(ctx: vscode.ExtensionContext) {
|
||||
|
||||
outputChannel = vscode.window.createOutputChannel("VSCode extension for Azure Terraform");
|
||||
|
||||
init();
|
||||
|
||||
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel("VSCode extension for Azure Terraform");
|
||||
outputChannel.appendLine("Loading extension");
|
||||
|
||||
cs = new CloudShell(outputChannel);
|
||||
is = new IntegratedShell(outputChannel);
|
||||
initFileWatcher(ctx);
|
||||
|
||||
ctx.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration("tf-azure.files")) {
|
||||
// dispose of current file watcher and re-init
|
||||
fileWatcher.dispose();
|
||||
initFileWatcher();
|
||||
initFileWatcher(ctx);
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -94,7 +74,7 @@ export function activate(ctx: vscode.ExtensionContext) {
|
|||
ctx.subscriptions.push(vscode.commands.registerCommand("vscode-terraform-azure.exectest", async () => {
|
||||
console.log("Testing current module");
|
||||
const pick: string = await vscode.window.showQuickPick(
|
||||
[ TestOption.lint, TestOption.e2enossh, TestOption.e2ewithssh, TestOption.custom ],
|
||||
[TestOption.lint, TestOption.e2enossh, TestOption.e2ewithssh, TestOption.custom],
|
||||
{ placeHolder: "Select the type of test that you want to run" },
|
||||
);
|
||||
if (pick) {
|
||||
|
@ -109,8 +89,7 @@ export function activate(ctx: vscode.ExtensionContext) {
|
|||
cs.pushFiles(tfFiles, true);
|
||||
});
|
||||
} else {
|
||||
vscode.window.showErrorMessage("Push function only available when using cloudshell.")
|
||||
.then(() => { return; });
|
||||
vscode.window.showErrorMessage("Push function only available when using cloudshell.");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -118,43 +97,25 @@ export function terminalSetToCloudshell(): boolean {
|
|||
return (vscode.workspace.getConfiguration("tf-azure").get("terminal") === "cloudshell") as boolean;
|
||||
}
|
||||
|
||||
export function filesGlobSetting(): GlobPattern {
|
||||
return vscode.workspace.getConfiguration("tf-azure").get("files") as GlobPattern;
|
||||
export function filesGlobSetting(): vscode.GlobPattern {
|
||||
return vscode.workspace.getConfiguration("tf-azure").get("files") as vscode.GlobPattern;
|
||||
}
|
||||
|
||||
export function workspaceSyncEnabled(): boolean {
|
||||
return vscode.workspace.getConfiguration("tf-azure").get("syncEnabled") as boolean;
|
||||
}
|
||||
|
||||
export async function TFLogin(api: AzureAccount) {
|
||||
outputChannel.appendLine("Attempting - TFLogin");
|
||||
if (!(await api.waitForLogin())) {
|
||||
return commands.executeCommand("azure-account.askForLogin");
|
||||
}
|
||||
outputChannel.appendLine("Succeeded - TFLogin");
|
||||
}
|
||||
|
||||
function initFileWatcher(): void {
|
||||
// TODO: Implement filewatcher handlers and config to automatically sync changes in workspace to cloudshell.
|
||||
const subscriptions: Disposable[] = [];
|
||||
|
||||
fileWatcher = vscode.workspace.createFileSystemWatcher(filesGlobSetting());
|
||||
|
||||
// enable file watcher to detect file changes.
|
||||
function initFileWatcher(ctx: vscode.ExtensionContext): void {
|
||||
fileWatcher = vscode.workspace.createFileSystemWatcher(filesGlobSetting());
|
||||
ctx.subscriptions.push(
|
||||
fileWatcher.onDidDelete((deletedUri) => {
|
||||
if (terminalSetToCloudshell() && workspaceSyncEnabled()) {
|
||||
|
||||
cs.deleteFiles([deletedUri]);
|
||||
}
|
||||
}, this, subscriptions);
|
||||
fileWatcher.onDidCreate((createdUri) => {
|
||||
pushHelper(createdUri);
|
||||
}, this, subscriptions);
|
||||
fileWatcher.onDidChange((changedUri) => {
|
||||
pushHelper(changedUri);
|
||||
}, this, subscriptions);
|
||||
|
||||
this._disposable = Disposable.from(...subscriptions);
|
||||
}),
|
||||
fileWatcher.onDidCreate((createdUri) => pushHelper(createdUri)),
|
||||
fileWatcher.onDidChange((changedUri) => pushHelper(changedUri)),
|
||||
);
|
||||
}
|
||||
|
||||
function pushHelper(uri: vscode.Uri) {
|
||||
|
@ -172,8 +133,7 @@ function pushHelper(uri: vscode.Uri) {
|
|||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
this._disposable.dispose();
|
||||
if (fileWatcher) {
|
||||
fileWatcher.dispose();
|
||||
fileWatcher.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import * as vscode from "vscode";
|
|||
import { commands, Uri, ViewColumn, workspace } from "vscode";
|
||||
import { BaseShell } from "./baseShell";
|
||||
import { Constants } from "./constants";
|
||||
import { TerminalType, TFTerminal } from "./shared";
|
||||
import { TestOption } from "./utilities";
|
||||
import { TerminalType, TestOption, TFTerminal } from "./shared";
|
||||
import { isServicePrincipalSetInEnv } from "./utils/azureUtils";
|
||||
import { executeCommand } from "./utils/cpUtils";
|
||||
import { isDockerInstalled, runE2EInDocker, runLintInDocker } from "./utils/dockerUtils";
|
||||
|
|
|
@ -3,15 +3,9 @@
|
|||
import * as azureStorage from "azure-storage";
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { FileService } from "azure-storage";
|
||||
import { WebResource } from "ms-rest";
|
||||
import { AzureAccount, AzureLoginStatus, AzureSession, AzureSubscription } from "./azure-account.api";
|
||||
import { CloudFile } from "./cloudFile";
|
||||
import { IExecResult } from "./utilities";
|
||||
|
||||
export class TFTerminal {
|
||||
|
||||
public type: TerminalType;
|
||||
public terminal: vscode.Terminal;
|
||||
public name: string;
|
||||
|
@ -27,6 +21,13 @@ export class TFTerminal {
|
|||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:max-classes-per-file
|
||||
export class CSTerminal {
|
||||
public accessToken: string;
|
||||
public consoleURI: string;
|
||||
public terminal: vscode.Terminal;
|
||||
}
|
||||
|
||||
export enum TerminalType {
|
||||
Integrated = "integrated",
|
||||
CloudShell = "cloudshell",
|
||||
|
@ -38,6 +39,13 @@ export enum FileSystem {
|
|||
cloudshell = "cloudshell",
|
||||
}
|
||||
|
||||
export enum TestOption {
|
||||
lint = "lint",
|
||||
e2enossh = "e2e - no ssh",
|
||||
e2ewithssh = "e2e - with ssh",
|
||||
custom = "custom",
|
||||
}
|
||||
|
||||
export function escapeFile(data: string): string {
|
||||
return data.replace(/"/g, '\\"').replace(/\$/g, "\\\$");
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ import * as assert from "assert";
|
|||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from "vscode";
|
||||
import * as myExtension from "../extension";
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite("Extension Tests", () => {
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
import * as child_process from "child_process";
|
||||
import * as vscode from "vscode";
|
||||
import { Constants } from "./constants";
|
||||
|
||||
export enum TestOption {
|
||||
lint = "lint",
|
||||
e2enossh = "e2e - no ssh",
|
||||
e2ewithssh = "e2e - with ssh",
|
||||
custom = "custom",
|
||||
}
|
||||
|
||||
export class CSTerminal {
|
||||
public accessToken: string;
|
||||
public consoleURI: string;
|
||||
public terminal: vscode.Terminal;
|
||||
}
|
||||
|
||||
export interface IExecResult {
|
||||
error: Error | null;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}
|
||||
|
||||
export async function exec(command: string) {
|
||||
return new Promise<IExecResult>((resolve, reject) => {
|
||||
child_process.exec(command, (error, stdout, stderr) => {
|
||||
(error || stderr ? reject : resolve)({ error, stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import { openUrlHint } from "./uiUtils";
|
||||
|
||||
export function isServicePrincipalSetInEnv(): boolean {
|
||||
if (!(process.env.ARM_SUBSCRIPTION_ID && process.env.ARM_CLIENT_ID && process.env.ARM_CLIENT_SECRET &&
|
||||
process.env.ARM_TENANT_ID && process.env.ARM_TEST_LOCATION)) {
|
||||
vscode.window.showErrorMessage("Azure Service Principal is not set. (See documentation at: https://www.terraform.io/docs/providers/azurerm/authenticating_via_azure_cli.html)");
|
||||
openUrlHint(
|
||||
"Azure Service Principal is not set.",
|
||||
"https://www.terraform.io/docs/providers/azurerm/authenticating_via_azure_cli.html",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
import * as vscode from "vscode";
|
||||
import { executeCommand } from "./cpUtils";
|
||||
import { openUrlHint } from "./uiUtils";
|
||||
|
||||
export async function isDockerInstalled(): Promise<boolean> {
|
||||
try {
|
||||
await executeCommand("docker", ["-v"], { shell: true });
|
||||
return true;
|
||||
} catch (error) {
|
||||
openUrlHint("Docker is not installed, please install Docker to continue.", "https://www.docker.com");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
import * as vscode from "vscode";
|
||||
import { executeCommand } from "./cpUtils";
|
||||
import { openUrlHint } from "./uiUtils";
|
||||
|
||||
export async function isDotInstalled(): Promise<boolean> {
|
||||
try {
|
||||
await executeCommand("dot", ["-V"], { shell: true });
|
||||
return true;
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage("GraphViz - Dot is not installed, please install GraphViz to continue (https://www.graphviz.org).");
|
||||
openUrlHint("GraphViz - Dot is not installed, please install GraphViz to continue.", "https://www.graphviz.org");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
"use strict";
|
||||
|
||||
import * as semver from "semver";
|
||||
import { executeCommand } from "./cpUtils";
|
||||
|
||||
export async function isNodeVersionValid(): Promise<boolean> {
|
||||
try {
|
||||
const output: string = await executeCommand("node", ["-v"], { shell: true });
|
||||
const version = output.trim()[0] === "v" && output.substr(1).trim();
|
||||
if (version && semver.valid(version) && semver.gte(version, "6.0.0")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
"use strict";
|
||||
|
||||
import * as opn from "opn";
|
||||
import * as vscode from "vscode";
|
||||
|
||||
export async function openUrlHint(message: string, url: string): Promise<void> {
|
||||
const open: vscode.MessageItem = { title: "Open" };
|
||||
const close: vscode.MessageItem = { title: "Close", isCloseAffordance: true };
|
||||
const response = await vscode.window.showInformationMessage(`${message} (See: ${url})`, open, close);
|
||||
if (response === open && url) {
|
||||
opn(url);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,9 @@
|
|||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src"
|
||||
"rootDir": "src",
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitThis": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
|
Загрузка…
Ссылка в новой задаче