mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Renable Strict TSLint (#5018)
* removes more builder references * remove builder from profiler * formatting * fix profiler dailog * remove builder from oatuhdialog * remove the rest of builder references * formatting * add more strict null checks to base * enable strict tslint rules * fix formatting * fix compile error * fix the rest of the hygeny issues and add pipeline step * fix pipeline files
This commit is contained in:
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as adal from 'adal-node';
|
||||
import * as azdata from 'azdata';
|
||||
import * as request from 'request';
|
||||
@@ -54,8 +52,8 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
|
||||
/**
|
||||
* Clears all tokens that belong to the given account from the token cache
|
||||
* @param {"data".AccountKey} accountKey Key identifying the account to delete tokens for
|
||||
* @returns {Thenable<void>} Promise to clear requested tokens from the token cache
|
||||
* @param accountKey Key identifying the account to delete tokens for
|
||||
* @returns Promise to clear requested tokens from the token cache
|
||||
*/
|
||||
public clear(accountKey: azdata.AccountKey): Thenable<void> {
|
||||
return this.doIfInitialized(() => this.clearAccountTokens(accountKey));
|
||||
@@ -63,7 +61,7 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
|
||||
/**
|
||||
* Clears the entire token cache. Invoked by command palette action.
|
||||
* @returns {Thenable<void>} Promise to clear the token cache
|
||||
* @returns Promise to clear the token cache
|
||||
*/
|
||||
public clearTokenCache(): Thenable<void> {
|
||||
return this._tokenCache.clear();
|
||||
@@ -92,13 +90,13 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
// NOTE: Based on ADAL implementation, getting tokens should use the refresh token if necessary
|
||||
let task = this.getAccessTokens(account, azdata.AzureResource.ResourceManagement)
|
||||
.then(
|
||||
() => {
|
||||
return account;
|
||||
},
|
||||
() => {
|
||||
account.isStale = true;
|
||||
return account;
|
||||
}
|
||||
() => {
|
||||
return account;
|
||||
},
|
||||
() => {
|
||||
account.isStale = true;
|
||||
return account;
|
||||
}
|
||||
);
|
||||
rehydrationTasks.push(task);
|
||||
}
|
||||
@@ -321,10 +319,10 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
* Retrieves a token for the given user ID for the specific tenant ID. If the token can, it
|
||||
* will be retrieved from the cache as per the ADAL API. AFAIK, the ADAL API will also utilize
|
||||
* the refresh token if there aren't any unexpired tokens to use.
|
||||
* @param {string} userId ID of the user to get a token for
|
||||
* @param {string} tenantId Tenant to get the token for
|
||||
* @param {string} resourceId ID of the resource the token will be good for
|
||||
* @returns {Thenable<TokenResponse>} Promise to return a token. Rejected if retrieving the token fails.
|
||||
* @param userId ID of the user to get a token for
|
||||
* @param tenantId Tenant to get the token for
|
||||
* @param resourceId ID of the resource the token will be good for
|
||||
* @returns Promise to return a token. Rejected if retrieving the token fails.
|
||||
*/
|
||||
private getToken(userId: string, tenantId: string, resourceId: string): Thenable<adal.TokenResponse> {
|
||||
let self = this;
|
||||
@@ -346,9 +344,9 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
|
||||
/**
|
||||
* Performs a web request using the provided bearer token
|
||||
* @param {TokenResponse} accessToken Bearer token for accessing the provided URI
|
||||
* @param {string} uri URI to access
|
||||
* @returns {Thenable<any>} Promise to return the deserialized body of the request. Rejected if error occurred.
|
||||
* @param accessToken Bearer token for accessing the provided URI
|
||||
* @param uri URI to access
|
||||
* @returns Promise to return the deserialized body of the request. Rejected if error occurred.
|
||||
*/
|
||||
private makeWebRequest(accessToken: adal.TokenResponse, uri: string): Thenable<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
|
||||
@@ -77,14 +77,14 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(
|
||||
() => {
|
||||
let message = localize('clearTokenCacheSuccess', 'Token cache successfully cleared');
|
||||
vscode.window.showInformationMessage(`${constants.extensionName}: ${message}`);
|
||||
},
|
||||
err => {
|
||||
let message = localize('clearTokenCacheFailure', 'Failed to clear token cache');
|
||||
vscode.window.showErrorMessage(`${constants.extensionName}: ${message}: ${err}`);
|
||||
});
|
||||
() => {
|
||||
let message = localize('clearTokenCacheSuccess', 'Token cache successfully cleared');
|
||||
vscode.window.showInformationMessage(`${constants.extensionName}: ${message}`);
|
||||
},
|
||||
err => {
|
||||
let message = localize('clearTokenCacheFailure', 'Failed to clear token cache');
|
||||
vscode.window.showErrorMessage(`${constants.extensionName}: ${message}: ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
private onDidChangeConfiguration(): void {
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as adal from 'adal-node';
|
||||
import * as azdata from 'azdata';
|
||||
import * as crypto from 'crypto';
|
||||
@@ -34,8 +32,8 @@ export default class TokenCache implements adal.TokenCache {
|
||||
.then(cache => self.addToCache(cache, entries))
|
||||
.then(updatedCache => self.writeCache(updatedCache))
|
||||
.then(
|
||||
() => callback(null, false),
|
||||
(err) => callback(err, true)
|
||||
() => callback(null, false),
|
||||
(err) => callback(err, true)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -70,8 +68,8 @@ export default class TokenCache implements adal.TokenCache {
|
||||
);
|
||||
})
|
||||
.then(
|
||||
results => callback(null, results),
|
||||
(err) => callback(err, null)
|
||||
results => callback(null, results),
|
||||
(err) => callback(err, null)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -79,7 +77,7 @@ export default class TokenCache implements adal.TokenCache {
|
||||
/**
|
||||
* Wrapper to make callback-based find method into a thenable method
|
||||
* @param query Partial object to use to look up tokens. Ideally should be partial of adal.TokenResponse
|
||||
* @returns {Thenable<any[]>} Promise to return the matching adal.TokenResponse objects.
|
||||
* @returns Promise to return the matching adal.TokenResponse objects.
|
||||
* Rejected if an error was sent in the callback
|
||||
*/
|
||||
public findThenable(query: any): Thenable<any[]> {
|
||||
@@ -104,16 +102,16 @@ export default class TokenCache implements adal.TokenCache {
|
||||
.then(cache => self.removeFromCache(cache, entries))
|
||||
.then(updatedCache => self.writeCache(updatedCache))
|
||||
.then(
|
||||
() => callback(null, null),
|
||||
(err) => callback(err, null)
|
||||
() => callback(null, null),
|
||||
(err) => callback(err, null)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to make callback-based remove method into a thenable method
|
||||
* @param {TokenResponse[]} entries Array of entries to remove from the token cache
|
||||
* @returns {Thenable<void>} Promise to remove the given tokens from the token cache
|
||||
* @param entries Array of entries to remove from the token cache
|
||||
* @returns Promise to remove the given tokens from the token cache
|
||||
* Rejected if an error was sent in the callback
|
||||
*/
|
||||
public removeThenable(entries: adal.TokenResponse[]): Thenable<void> {
|
||||
@@ -186,8 +184,8 @@ export default class TokenCache implements adal.TokenCache {
|
||||
if (splitValues.length === 2 && splitValues[0] && splitValues[1]) {
|
||||
try {
|
||||
return <EncryptionParams>{
|
||||
key: new Buffer(splitValues[0], 'hex'),
|
||||
initializationVector: new Buffer(splitValues[1], 'hex')
|
||||
key: Buffer.from(splitValues[0], 'hex'),
|
||||
initializationVector: Buffer.from(splitValues[1], 'hex')
|
||||
};
|
||||
} catch (e) {
|
||||
// Swallow the error and fall through to generate new params
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
@@ -15,7 +13,6 @@ import * as constants from './constants';
|
||||
* this API from our code
|
||||
*
|
||||
* @export
|
||||
* @class ApiWrapper
|
||||
*/
|
||||
export class ApiWrapper {
|
||||
// Data APIs
|
||||
|
||||
@@ -16,8 +16,7 @@ import { AzureResourceResourceTreeNode } from '../../resourceTreeNode';
|
||||
|
||||
export function registerAzureResourceDatabaseCommands(appContext: AppContext): void {
|
||||
appContext.apiWrapper.registerCommand('azure.resource.connectsqldb', async (node?: TreeNode) => {
|
||||
if (!node)
|
||||
{
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ import { AzureResourceResourceTreeNode } from '../../resourceTreeNode';
|
||||
|
||||
export function registerAzureResourceDatabaseServerCommands(appContext: AppContext): void {
|
||||
appContext.apiWrapper.registerCommand('azure.resource.connectsqlserver', async (node?: TreeNode) => {
|
||||
if (!node)
|
||||
{
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ export class AzureResourceDatabaseServerService implements IAzureResourceDatabas
|
||||
|
||||
svrs.forEach((svr) => databaseServers.push({
|
||||
name: svr.name,
|
||||
fullName: svr.fullyQualifiedDomainName,
|
||||
loginName: svr.administratorLogin,
|
||||
defaultDatabaseName: 'master'
|
||||
fullName: svr.fullyQualifiedDomainName,
|
||||
loginName: svr.administratorLogin,
|
||||
defaultDatabaseName: 'master'
|
||||
}));
|
||||
|
||||
return databaseServers;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
|
||||
import { azureResource } from '../../azure-resource';
|
||||
import { AzureResourceDatabaseServer } from './models';
|
||||
|
||||
@@ -17,10 +17,10 @@ export class AzureResourceCacheService implements IAzureResourceCacheService {
|
||||
}
|
||||
|
||||
public generateKey(id: string): string {
|
||||
return `${AzureResourceCacheService.cacheKeyPrefix}.${id}`;
|
||||
}
|
||||
return `${AzureResourceCacheService.cacheKeyPrefix}.${id}`;
|
||||
}
|
||||
|
||||
public get<T>(key: string): T | undefined {
|
||||
public get<T>(key: string): T | undefined {
|
||||
return this._context.workspaceState.get(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { azureResource } from '../azure-resource';
|
||||
import { IAzureResourceSubscriptionFilterService, IAzureResourceCacheService } from '../interfaces';
|
||||
|
||||
interface AzureResourceSelectedSubscriptionsCache {
|
||||
selectedSubscriptions: { [accountId: string]: azureResource.AzureResourceSubscription[]};
|
||||
selectedSubscriptions: { [accountId: string]: azureResource.AzureResourceSubscription[] };
|
||||
}
|
||||
|
||||
export class AzureResourceSubscriptionFilterService implements IAzureResourceSubscriptionFilterService {
|
||||
@@ -36,7 +36,7 @@ export class AzureResourceSubscriptionFilterService implements IAzureResourceSub
|
||||
}
|
||||
|
||||
public async saveSelectedSubscriptions(account: Account, selectedSubscriptions: azureResource.AzureResourceSubscription[]): Promise<void> {
|
||||
let selectedSubscriptionsCache: { [accountId: string]: azureResource.AzureResourceSubscription[]} = {};
|
||||
let selectedSubscriptionsCache: { [accountId: string]: azureResource.AzureResourceSubscription[] } = {};
|
||||
|
||||
const cache = this._cacheService.get<AzureResourceSelectedSubscriptionsCache>(this._cacheKey);
|
||||
if (cache) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
|
||||
|
||||
if (this._isClearingCache) {
|
||||
try {
|
||||
const tokens = await this.appContext.apiWrapper.getSecurityToken(this.account, AzureResource.ResourceManagement);
|
||||
const tokens = await this.appContext.apiWrapper.getSecurityToken(this.account, AzureResource.ResourceManagement);
|
||||
|
||||
for (const tenant of this.account.properties.tenants) {
|
||||
const token = tokens[tenant.id].token;
|
||||
|
||||
@@ -44,8 +44,8 @@ export abstract class AzureResourceContainerTreeNodeBase extends AzureResourceTr
|
||||
}
|
||||
|
||||
protected setCacheKey(id: string): void {
|
||||
this._cacheKey = this._cacheService.generateKey(id);
|
||||
}
|
||||
this._cacheKey = this._cacheService.generateKey(id);
|
||||
}
|
||||
|
||||
protected updateCache<T>(cache: T): void {
|
||||
this._cacheService.update<T>(this._cacheKey, cache);
|
||||
|
||||
@@ -86,7 +86,7 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return this._id;
|
||||
return this._id;
|
||||
}
|
||||
|
||||
private _id: string = undefined;
|
||||
|
||||
@@ -45,7 +45,7 @@ export abstract class TreeNode {
|
||||
if (children) {
|
||||
for (let child of children) {
|
||||
if (filter && filter(child)) {
|
||||
let childNode = await this.findNode(child, condition, filter, expandIfNeeded);
|
||||
let childNode = await this.findNode(child, condition, filter, expandIfNeeded);
|
||||
if (childNode) {
|
||||
return childNode;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export function getErrorMessage(error: Error | string): string {
|
||||
return (error instanceof Error) ? error.message : error;
|
||||
return (error instanceof Error) ? error.message : error;
|
||||
}
|
||||
|
||||
export class AzureResourceErrorMessageUtil {
|
||||
@@ -19,78 +19,78 @@ export class AzureResourceErrorMessageUtil {
|
||||
}
|
||||
|
||||
export function generateGuid(): string {
|
||||
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||||
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
||||
let oct: string = '';
|
||||
let tmp: number;
|
||||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||||
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
||||
let oct: string = '';
|
||||
let tmp: number;
|
||||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
||||
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
||||
/* tslint:enable:no-bitwise */
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
||||
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
||||
/* tslint:enable:no-bitwise */
|
||||
}
|
||||
|
||||
export function equals(one: any, other: any): boolean {
|
||||
if (one === other) {
|
||||
return true;
|
||||
}
|
||||
if (one === null || one === undefined || other === null || other === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== typeof other) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== 'object') {
|
||||
return false;
|
||||
}
|
||||
if ((Array.isArray(one)) !== (Array.isArray(other))) {
|
||||
return false;
|
||||
}
|
||||
if (one === other) {
|
||||
return true;
|
||||
}
|
||||
if (one === null || one === undefined || other === null || other === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== typeof other) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== 'object') {
|
||||
return false;
|
||||
}
|
||||
if ((Array.isArray(one)) !== (Array.isArray(other))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let i: number;
|
||||
let key: string;
|
||||
let i: number;
|
||||
let key: string;
|
||||
|
||||
if (Array.isArray(one)) {
|
||||
if (one.length !== other.length) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < one.length; i++) {
|
||||
if (!equals(one[i], other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const oneKeys: string[] = [];
|
||||
if (Array.isArray(one)) {
|
||||
if (one.length !== other.length) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < one.length; i++) {
|
||||
if (!equals(one[i], other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const oneKeys: string[] = [];
|
||||
|
||||
for (key in one) {
|
||||
oneKeys.push(key);
|
||||
}
|
||||
oneKeys.sort();
|
||||
const otherKeys: string[] = [];
|
||||
for (key in other) {
|
||||
otherKeys.push(key);
|
||||
}
|
||||
otherKeys.sort();
|
||||
if (!equals(oneKeys, otherKeys)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < oneKeys.length; i++) {
|
||||
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
for (key in one) {
|
||||
oneKeys.push(key);
|
||||
}
|
||||
oneKeys.sort();
|
||||
const otherKeys: string[] = [];
|
||||
for (key in other) {
|
||||
otherKeys.push(key);
|
||||
}
|
||||
otherKeys.sort();
|
||||
if (!equals(oneKeys, otherKeys)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < oneKeys.length; i++) {
|
||||
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
IAzureResourceAccountService,
|
||||
IAzureResourceSubscriptionService,
|
||||
IAzureResourceSubscriptionFilterService,
|
||||
IAzureResourceTenantService } from '../azureResource/interfaces';
|
||||
IAzureResourceTenantService
|
||||
} from '../azureResource/interfaces';
|
||||
import { AzureResourceServiceNames } from '../azureResource/constants';
|
||||
import { AzureResourceTreeProvider } from '../azureResource/tree/treeProvider';
|
||||
import { registerAzureResourceCommands } from '../azureResource/commands';
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
'use strict';
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
@@ -23,7 +26,7 @@ let controllers: ControllerBase[] = [];
|
||||
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
|
||||
// work for now because the extension is running in different process.
|
||||
export function getAppDataPath() {
|
||||
var platform = process.platform;
|
||||
let platform = process.platform;
|
||||
switch (platform) {
|
||||
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
|
||||
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
|
||||
|
||||
Reference in New Issue
Block a user