From c81c0a96e35899b0bdc1b8af51cbb64e7cb3b577 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Tue, 13 Aug 2024 15:43:19 +0200 Subject: [PATCH 1/4] Enhancement: Deprecation eslint rule --- .eslintrc.cjs | 6 ++-- npm-shrinkwrap.json | 15 ++++++++++ src/AuthServer.ts | 2 +- src/Command.ts | 2 +- src/cli/cli.ts | 4 +-- .../entra/commands/m365group/m365group-add.ts | 2 +- .../entra/commands/m365group/m365group-get.ts | 2 +- .../commands/m365group/m365group-list.ts | 2 +- .../entra/commands/m365group/m365group-set.ts | 2 +- src/m365/file/commands/convert/convert-pdf.ts | 7 ++--- src/m365/file/commands/file-add.ts | 11 ++++--- .../spfx/commands/package/package-generate.ts | 2 +- src/m365/spfx/commands/project/JsonRule.ts | 4 +-- ...016004_TS_property_pane_property_import.ts | 4 +-- src/m365/spfx/commands/spfx-doctor.ts | 2 +- src/m365/spo/commands/file/file-add.ts | 2 +- .../file/file-retentionlabel-ensure.ts | 3 +- .../file/file-retentionlabel-remove.ts | 3 +- .../folder/folder-retentionlabel-ensure.ts | 3 +- .../folder/folder-retentionlabel-remove.ts | 3 +- .../commands/list/list-webhook-add.spec.ts | 2 +- .../listitem-retentionlabel-ensure.ts | 3 +- .../listitem-retentionlabel-remove.ts | 3 +- src/m365/spo/commands/page/page-add.ts | 2 +- src/m365/spo/commands/site/site-set.ts | 2 +- src/m365/teams/commands/user/user-app-list.ts | 2 +- src/m365/todo/commands/list/list-remove.ts | 3 +- src/m365/todo/commands/list/list-set.ts | 3 +- src/m365/todo/commands/task/task-add.ts | 3 +- src/m365/todo/commands/task/task-get.ts | 3 +- src/m365/todo/commands/task/task-list.ts | 3 +- src/m365/todo/commands/task/task-remove.ts | 3 +- src/m365/todo/commands/task/task-set.ts | 2 +- src/utils/spo.ts | 9 +++--- src/utils/urlUtil.ts | 30 ++++++++----------- 35 files changed, 83 insertions(+), 71 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d70e3f6dcc7..c8fecd9c5a5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -150,7 +150,8 @@ module.exports = { "plugins": [ "@typescript-eslint", "cli-microsoft365", - "mocha" + "mocha", + "deprecation" ], "ignorePatterns": [ "**/package-generate/assets/**", @@ -239,7 +240,8 @@ module.exports = { ], "@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }], "mocha/no-identical-title": "error", - "@typescript-eslint/no-floating-promises": "error" + "@typescript-eslint/no-floating-promises": "error", + "deprecation/deprecation": "error" }, "overrides": [ { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 44ef29e9164..a6ed26a5d34 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -2434,6 +2434,21 @@ "resolved": "eslint-rules", "link": true }, + "node_modules/eslint-plugin-deprecation": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-3.0.0.tgz", + "integrity": "sha512-JuVLdNg/uf0Adjg2tpTyYoYaMbwQNn/c78P1HcccokvhtRphgnRjZDKmhlxbxYptppex03zO76f97DD/yQHv7A==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^7.0.0", + "ts-api-utils": "^1.3.0", + "tslib": "^2.3.1" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "typescript": "^4.2.4 || ^5.0.0" + } + }, "node_modules/eslint-plugin-mocha": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", diff --git a/src/AuthServer.ts b/src/AuthServer.ts index 9603eeca873..0c1249c9357 100644 --- a/src/AuthServer.ts +++ b/src/AuthServer.ts @@ -38,7 +38,7 @@ export class AuthServer { }; private httpListener = async (): Promise => { - const requestState = Math.random().toString(16).substr(2, 20); + const requestState = Math.random().toString(16).substring(2, 20); const address = this.httpServer.address() as AddressInfo; this.generatedServerUrl = `http://localhost:${address.port}`; const url = `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.connection.cloudType)}/${this.connection.tenant}/oauth2/authorize?response_type=code&client_id=${this.connection.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`; diff --git a/src/Command.ts b/src/Command.ts index aa905ebf7ee..fb692272461 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -350,7 +350,7 @@ export default abstract class Command { pos = pos1; } - commandName = commandName.substr(0, pos).trim(); + commandName = commandName.substring(0, pos).trim(); } return commandName; diff --git a/src/cli/cli.ts b/src/cli/cli.ts index cb6d6848fbf..31151eec3f2 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -830,7 +830,7 @@ function printAvailableCommands(): void { commandsToPrint[commandName] = command; } else { - const subCommandsGroup: string = commandName.substr(0, pos); + const subCommandsGroup: string = commandName.substring(0, pos); if (!commandGroupsToPrint[subCommandsGroup]) { commandGroupsToPrint[subCommandsGroup] = 0; } @@ -1034,7 +1034,7 @@ function loadOptionValuesFromFiles(args: { options: yargs.Arguments }): void { return; } - const filePath: string = value.substr(1); + const filePath: string = value.substring(1); // if the file doesn't exist, leave as-is, if it exists replace with // contents from the file if (fs.existsSync(filePath)) { diff --git a/src/m365/entra/commands/m365group/m365group-add.ts b/src/m365/entra/commands/m365group/m365group-add.ts index 0edb70595ee..ac8a266abd4 100644 --- a/src/m365/entra/commands/m365group/m365group-add.ts +++ b/src/m365/entra/commands/m365group/m365group-add.ts @@ -333,7 +333,7 @@ class EntraM365GroupAddCommand extends GraphCommand { } private getImageContentType(imagePath: string): string { - const extension: string = imagePath.substr(imagePath.lastIndexOf('.')).toLowerCase(); + const extension: string = imagePath.substring(imagePath.lastIndexOf('.')).toLowerCase(); switch (extension) { case '.png': diff --git a/src/m365/entra/commands/m365group/m365group-get.ts b/src/m365/entra/commands/m365group/m365group-get.ts index 9d7611b37b3..713dfa21c02 100644 --- a/src/m365/entra/commands/m365group/m365group-get.ts +++ b/src/m365/entra/commands/m365group/m365group-get.ts @@ -84,7 +84,7 @@ class EntraM365GroupGetCommand extends GraphCommand { }; const res = await request.get<{ webUrl: string }>(requestOptions); - group.siteUrl = res.webUrl ? res.webUrl.substr(0, res.webUrl.lastIndexOf('/')) : ''; + group.siteUrl = res.webUrl ? res.webUrl.substring(0, res.webUrl.lastIndexOf('/')) : ''; } await logger.log(group); diff --git a/src/m365/entra/commands/m365group/m365group-list.ts b/src/m365/entra/commands/m365group/m365group-list.ts index 00019feb2c1..f2367ef09c0 100644 --- a/src/m365/entra/commands/m365group/m365group-list.ts +++ b/src/m365/entra/commands/m365group/m365group-list.ts @@ -129,7 +129,7 @@ class EntraM365GroupListCommand extends GraphCommand { const res = await request.get<{ webUrl: string }>(requestOptions); return { id: groupId, - url: res.webUrl ? res.webUrl.substr(0, res.webUrl.lastIndexOf('/')) : '' + url: res.webUrl ? res.webUrl.substring(0, res.webUrl.lastIndexOf('/')) : '' }; } } diff --git a/src/m365/entra/commands/m365group/m365group-set.ts b/src/m365/entra/commands/m365group/m365group-set.ts index cb4a19e09c1..56ab8578260 100644 --- a/src/m365/entra/commands/m365group/m365group-set.ts +++ b/src/m365/entra/commands/m365group/m365group-set.ts @@ -337,7 +337,7 @@ class EntraM365GroupSetCommand extends GraphCommand { } private getImageContentType(imagePath: string): string { - const extension: string = imagePath.substr(imagePath.lastIndexOf('.')).toLowerCase(); + const extension: string = imagePath.substring(imagePath.lastIndexOf('.')).toLowerCase(); switch (extension) { case '.png': diff --git a/src/m365/file/commands/convert/convert-pdf.ts b/src/m365/file/commands/convert/convert-pdf.ts index 668ecfc3bc5..eb982f12828 100644 --- a/src/m365/file/commands/convert/convert-pdf.ts +++ b/src/m365/file/commands/convert/convert-pdf.ts @@ -2,7 +2,6 @@ import { AxiosResponse } from 'axios'; import fs from 'fs'; import os from 'os'; import path from 'path'; -import url from 'url'; import { v4 } from 'uuid'; import auth from '../../../../Auth.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -243,13 +242,13 @@ class FileConvertPdfCommand extends GraphCommand { return fileGraphUrl; } - const _url = url.parse(fileWebUrl); + const _url = new URL(fileWebUrl); let siteId: string = ''; let driveRelativeFileUrl: string = ''; - const siteInfo = await this.getGraphSiteInfoFromFullUrl(_url.host as string, _url.path as string); + const siteInfo = await this.getGraphSiteInfoFromFullUrl(_url.hostname, _url.pathname); siteId = siteInfo.id; - let siteRelativeFileUrl: string = (_url.path as string).replace(siteInfo.serverRelativeUrl, ''); + let siteRelativeFileUrl: string = _url.pathname.replace(siteInfo.serverRelativeUrl, ''); // normalize site-relative URLs for root site collections and root sites if (!siteRelativeFileUrl.startsWith('/')) { siteRelativeFileUrl = '/' + siteRelativeFileUrl; diff --git a/src/m365/file/commands/file-add.ts b/src/m365/file/commands/file-add.ts index 2636f70e0c0..c92714a91e3 100644 --- a/src/m365/file/commands/file-add.ts +++ b/src/m365/file/commands/file-add.ts @@ -1,6 +1,5 @@ import fs from 'fs'; import path from 'path'; -import url from 'url'; import { Logger } from '../../../cli/Logger.js'; import GlobalOptions from '../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../request.js'; @@ -73,7 +72,7 @@ class FileAddCommand extends GraphCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { let folderUrlWithoutTrailingSlash = args.options.folderUrl; if (folderUrlWithoutTrailingSlash.endsWith('/')) { - folderUrlWithoutTrailingSlash = folderUrlWithoutTrailingSlash.substr(0, folderUrlWithoutTrailingSlash.length - 1); + folderUrlWithoutTrailingSlash = folderUrlWithoutTrailingSlash.substring(0, folderUrlWithoutTrailingSlash.length - 1); } try { @@ -148,15 +147,15 @@ class FileAddCommand extends GraphCommand { await logger.logToStderr(`Resolving Graph drive item URL for ${fileWebUrl}`); } - const _fileWebUrl = url.parse(fileWebUrl); - const _siteUrl = url.parse(siteUrl || fileWebUrl); + const _fileWebUrl = new URL(fileWebUrl); + const _siteUrl = new URL(siteUrl || fileWebUrl); const isSiteUrl = typeof siteUrl !== 'undefined'; let siteId: string = ''; let driveRelativeFileUrl: string = ''; - const siteInfo = await this.getGraphSiteInfoFromFullUrl(_siteUrl.host as string, _siteUrl.path as string, isSiteUrl); + const siteInfo = await this.getGraphSiteInfoFromFullUrl(_siteUrl.host, _siteUrl.pathname, isSiteUrl); siteId = siteInfo.id; - let siteRelativeFileUrl: string = (_fileWebUrl.path as string).replace(siteInfo.serverRelativeUrl, ''); + let siteRelativeFileUrl: string = _fileWebUrl.pathname.replace(siteInfo.serverRelativeUrl, ''); // normalize site-relative URLs for root site collections and root sites if (!siteRelativeFileUrl.startsWith('/')) { diff --git a/src/m365/spfx/commands/package/package-generate.ts b/src/m365/spfx/commands/package/package-generate.ts index 1430c824deb..07a07c6aff2 100644 --- a/src/m365/spfx/commands/package/package-generate.ts +++ b/src/m365/spfx/commands/package/package-generate.ts @@ -254,7 +254,7 @@ class SpfxPackageGenerateCommand extends AnonymousCommand { } private static getWebPartAlias(webPartName: string): string { - return 'AutoWP' + webPartName.replace(/[^a-zA-Z0-9]/g, '').substr(0, 40); + return 'AutoWP' + webPartName.replace(/[^a-zA-Z0-9]/g, '').substring(0, 40); } private static generateNewId = (): string => { diff --git a/src/m365/spfx/commands/project/JsonRule.ts b/src/m365/spfx/commands/project/JsonRule.ts index abf3a68b94b..7188258a56e 100644 --- a/src/m365/spfx/commands/project/JsonRule.ts +++ b/src/m365/spfx/commands/project/JsonRule.ts @@ -65,9 +65,9 @@ export abstract class JsonRule extends Rule { isArray = true; const pos = currentProperty.indexOf('[') + 1; // get array element from the property name - arrayElement = currentProperty.substr(pos, currentProperty.length - pos - 1); + arrayElement = currentProperty.substring(pos, currentProperty.length - 1); // remove array element from the property name - currentProperty = currentProperty.substr(0, pos - 1); + currentProperty = currentProperty.substring(0, pos - 1); } for (let i = 0; i < node.children.length; i++) { diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts index bc953d0f369..7c1955ceb72 100644 --- a/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts @@ -54,8 +54,8 @@ export class FN016004_TS_property_pane_property_import extends TsRule { obj.forEach(n => { const resource: string = n.getText(); - const importsText: string = resource.replace(/\s/g, '').substr(resource.indexOf('{')); - const imports: string[] = importsText.substr(0, importsText.indexOf('}')).split(','); + const importsText: string = resource.replace(/\s/g, '').substring(resource.indexOf('{')); + const imports: string[] = importsText.substring(0, importsText.indexOf('}')).split(','); const importsToStay: string[] = []; const importsToBeMoved: string[] = []; diff --git a/src/m365/spfx/commands/spfx-doctor.ts b/src/m365/spfx/commands/spfx-doctor.ts index 384202e7626..1c620c5979c 100644 --- a/src/m365/spfx/commands/spfx-doctor.ts +++ b/src/m365/spfx/commands/spfx-doctor.ts @@ -1035,7 +1035,7 @@ class SpfxDoctorCommand extends BaseProjectCommand { } private getNodeVersion(): string { - return process.version.substr(1); + return process.version.substring(1); } private async checkStatus(what: string, versionFound: string, versionCheck: VersionCheck): Promise { diff --git a/src/m365/spo/commands/file/file-add.ts b/src/m365/spo/commands/file/file-add.ts index 0140f102e55..5f218bd81fc 100644 --- a/src/m365/spo/commands/file/file-add.ts +++ b/src/m365/spo/commands/file/file-add.ts @@ -449,7 +449,7 @@ class SpoFileAddCommand extends SpoCommand { const isLastChunk: boolean = info.Position >= info.Size; if (isLastChunk) { // trim buffer for last chunk - fileBuffer = fileBuffer.slice(0, readCount); + fileBuffer = fileBuffer.subarray(0, readCount); } const requestOptions: CliRequestOptions = { diff --git a/src/m365/spo/commands/file/file-retentionlabel-ensure.ts b/src/m365/spo/commands/file/file-retentionlabel-ensure.ts index 95a6d9f622d..734fc036777 100644 --- a/src/m365/spo/commands/file/file-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/file/file-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -100,7 +99,7 @@ class SpoFileRetentionLabelEnsureCommand extends SpoCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { try { const fileProperties = await this.getFileProperties(logger, args); - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, fileProperties.listServerRelativeUrl); diff --git a/src/m365/spo/commands/file/file-retentionlabel-remove.ts b/src/m365/spo/commands/file/file-retentionlabel-remove.ts index c157b0e4ec0..2063dfc190d 100644 --- a/src/m365/spo/commands/file/file-retentionlabel-remove.ts +++ b/src/m365/spo/commands/file/file-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; @@ -111,7 +110,7 @@ class SpoFileRetentionLabelRemoveCommand extends SpoCommand { private async removeFileRetentionLabel(logger: Logger, args: CommandArgs): Promise { try { const fileProperties = await this.getFileProperties(logger, args); - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, fileProperties.listServerRelativeUrl); diff --git a/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts b/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts index c3b0f8a87ed..198ec747504 100644 --- a/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import GlobalOptions from '../../../../GlobalOptions.js'; import { Logger } from '../../../../cli/Logger.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -97,7 +96,7 @@ class SpoFolderRetentionLabelEnsureCommand extends SpoCommand { const folderProperties = await this.getFolderProperties(logger, args); if (folderProperties.ListItemAllFields) { - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, folderProperties.ListItemAllFields.ParentList.RootFolder.ServerRelativeUrl); diff --git a/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts b/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts index 8120f97d890..dbad1ad577b 100644 --- a/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts +++ b/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import GlobalOptions from '../../../../GlobalOptions.js'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -113,7 +112,7 @@ class SpoFolderRetentionLabelRemoveCommand extends SpoCommand { const folderProperties = await this.getFolderProperties(logger, args); if (folderProperties.ListItemAllFields) { - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, folderProperties.ListItemAllFields.ParentList.RootFolder.ServerRelativeUrl); diff --git a/src/m365/spo/commands/list/list-webhook-add.spec.ts b/src/m365/spo/commands/list/list-webhook-add.spec.ts index 398fac6f58e..71d5ca4a219 100644 --- a/src/m365/spo/commands/list/list-webhook-add.spec.ts +++ b/src/m365/spo/commands/list/list-webhook-add.spec.ts @@ -383,7 +383,7 @@ describe(commands.LIST_WEBHOOK_ADD, () => { const currentDate: Date = new Date(); currentDate.setMonth(currentDate.getMonth() + 4); currentDate.setDate(1); - const dateString: string = currentDate.toISOString().substr(0, 10); + const dateString: string = currentDate.toISOString().substring(0, 10); const actual = await command.validate({ options: diff --git a/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts b/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts index 5da14157c77..3b92944aedf 100644 --- a/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -161,7 +160,7 @@ class SpoListItemRetentionLabelEnsureCommand extends SpoCommand { } private async getListAbsoluteUrl(options: Options, logger: Logger): Promise { - const parsedUrl = url.parse(options.webUrl); + const parsedUrl = new URL(options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (options.listUrl) { diff --git a/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts b/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts index e9a7a35ce11..8e212f0a91f 100644 --- a/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts +++ b/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; @@ -127,7 +126,7 @@ class SpoListItemRetentionLabelRemoveCommand extends SpoCommand { } private async getListAbsoluteUrl(options: Options, logger: Logger): Promise { - const parsedUrl = url.parse(options.webUrl); + const parsedUrl = new URL(options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (options.listUrl) { diff --git a/src/m365/spo/commands/page/page-add.ts b/src/m365/spo/commands/page/page-add.ts index aa948a192a8..92f82853de4 100644 --- a/src/m365/spo/commands/page/page-add.ts +++ b/src/m365/spo/commands/page/page-add.ts @@ -128,7 +128,7 @@ class SpoPageAddCommand extends SpoCommand { let bannerImageUrl: string = ''; let canvasContent1: string = ''; let layoutWebpartsContent: string = ''; - const pageTitle: string = args.options.title ? args.options.title : (args.options.name.indexOf('.aspx') > -1 ? args.options.name.substr(0, args.options.name.indexOf('.aspx')) : args.options.name); + const pageTitle: string = args.options.title ? args.options.title : (args.options.name.indexOf('.aspx') > -1 ? args.options.name.substring(0, args.options.name.indexOf('.aspx')) : args.options.name); let pageId: number | null = null; const pageDescription: string = args.options.description || ""; diff --git a/src/m365/spo/commands/site/site-set.ts b/src/m365/spo/commands/site/site-set.ts index 7ee9c417616..446f997f577 100644 --- a/src/m365/spo/commands/site/site-set.ts +++ b/src/m365/spo/commands/site/site-set.ts @@ -678,7 +678,7 @@ class SpoSiteSetCommand extends SpoCommand { headers: { 'X-RequestDigest': this.context.FormDigestValue }, - data: `${payload.join('')}` + data: `${payload.join('')}` }; response = await request.post(requestOptions); diff --git a/src/m365/teams/commands/user/user-app-list.ts b/src/m365/teams/commands/user/user-app-list.ts index 73dfb2dc1bc..901cd44029c 100644 --- a/src/m365/teams/commands/user/user-app-list.ts +++ b/src/m365/teams/commands/user/user-app-list.ts @@ -84,7 +84,7 @@ class TeamsUserAppListCommand extends GraphCommand { const items = await odata.getAllItems(endpoint); items.forEach(i => { const userAppId: string = Buffer.from(i.id as string, 'base64').toString('ascii'); - const appId: string = userAppId.substr(userAppId.indexOf("##") + 2, userAppId.length - userId.length - 2); + const appId: string = userAppId.substring(userAppId.indexOf("##") + 2, userAppId.length); (i as any).appId = appId; }); diff --git a/src/m365/todo/commands/list/list-remove.ts b/src/m365/todo/commands/list/list-remove.ts index f047bb4e7f9..fefaf8d8a07 100644 --- a/src/m365/todo/commands/list/list-remove.ts +++ b/src/m365/todo/commands/list/list-remove.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -79,7 +80,7 @@ class TodoListRemoveCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.name!)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.name!)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/list/list-set.ts b/src/m365/todo/commands/list/list-set.ts index 66a749de11c..a6edee4b594 100644 --- a/src/m365/todo/commands/list/list-set.ts +++ b/src/m365/todo/commands/list/list-set.ts @@ -1,6 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -95,7 +96,7 @@ class TodoListSetCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${endpoint}/me/todo/lists?$filter=displayName eq '${escape(args.options.name as string)}'`, + url: `${endpoint}/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.name!)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/task/task-add.ts b/src/m365/todo/commands/task/task-add.ts index 12e47f44ad9..edeb26cd683 100644 --- a/src/m365/todo/commands/task/task-add.ts +++ b/src/m365/todo/commands/task/task-add.ts @@ -1,6 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import { validation } from '../../../../utils/validation.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -206,7 +207,7 @@ class TodoTaskAddCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-get.ts b/src/m365/todo/commands/task/task-get.ts index acb81dbc4a2..c4964bfd3fa 100644 --- a/src/m365/todo/commands/task/task-get.ts +++ b/src/m365/todo/commands/task/task-get.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; import { ToDoTask } from '../../ToDoTask.js'; @@ -70,7 +71,7 @@ class TodoTaskGetCommand extends DelegatedGraphCommand { } const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-list.ts b/src/m365/todo/commands/task/task-list.ts index add477c383f..9e7d96e154b 100644 --- a/src/m365/todo/commands/task/task-list.ts +++ b/src/m365/todo/commands/task/task-list.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import { odata } from '../../../../utils/odata.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -67,7 +68,7 @@ class TodoTaskListCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-remove.ts b/src/m365/todo/commands/task/task-remove.ts index c0d2d2bd7bf..df76db0e939 100644 --- a/src/m365/todo/commands/task/task-remove.ts +++ b/src/m365/todo/commands/task/task-remove.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -81,7 +82,7 @@ class TodoTaskRemoveCommand extends DelegatedGraphCommand { if (options.listName) { // Search list by its name const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(options.listName)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/task/task-set.ts b/src/m365/todo/commands/task/task-set.ts index a730036376a..179a0d93717 100644 --- a/src/m365/todo/commands/task/task-set.ts +++ b/src/m365/todo/commands/task/task-set.ts @@ -185,7 +185,7 @@ class TodoTaskSetCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/utils/spo.ts b/src/utils/spo.ts index 2c415f9dd4f..1fb036faeab 100644 --- a/src/utils/spo.ts +++ b/src/utils/spo.ts @@ -1,5 +1,4 @@ import os from 'os'; -import url from 'url'; import { urlUtil } from "./urlUtil.js"; import { validation } from "./validation.js"; import auth from '../Auth.js'; @@ -271,8 +270,10 @@ export const spo = { * @param siteAccessToken a valid access token for the site specified in the webFullUrl param */ async ensureFolder(webFullUrl: string, folderToEnsure: string, logger: Logger, debug: boolean): Promise { - const webUrl = url.parse(webFullUrl); - if (!webUrl.protocol || !webUrl.hostname) { + try { + new URL(webFullUrl); + } + catch { throw new Error('webFullUrl is not a valid URL'); } @@ -1286,7 +1287,7 @@ export const spo = { headers: { 'X-RequestDigest': context.FormDigestValue }, - data: `${payload.join('')}` + data: `${payload.join('')}` }; diff --git a/src/utils/urlUtil.ts b/src/utils/urlUtil.ts index 7e1f0f752cf..358b4477795 100644 --- a/src/utils/urlUtil.ts +++ b/src/utils/urlUtil.ts @@ -1,5 +1,3 @@ -import url from 'url'; - export const urlUtil = { /** * Returns server relative path. @@ -16,13 +14,7 @@ export const urlUtil = { * urlUtil.getServerRelativePath("/sites/team1/", "/Shared Documents"); */ getServerRelativePath(webUrl: string, folderRelativePath: string): string { - const tenantUrl: string = `${url.parse(webUrl).protocol}//${url.parse(webUrl).hostname}`; - // if webUrl is a server-relative URL then tenantUrl will resolve to null//null - // in which case we should keep webUrl - let webRelativePath: string = tenantUrl !== 'null//null' ? webUrl.substr(tenantUrl.length) : webUrl; - - // will be used to remove relative path from the folderRelativePath - // in case the web relative url is included + let webRelativePath = this.getUrlRelativePath(webUrl); let relativePathToRemove: string = webRelativePath; // add '/' at 0 @@ -105,14 +97,9 @@ export const urlUtil = { * Utils.getWebRelativePath("/sites/team1/", "/sites/team1/Shared Documents"); */ getWebRelativePath(webUrl: string, folderUrl: string): string { - + let webRelativePath = this.getUrlRelativePath(webUrl); let folderWebRelativePath: string = ''; - const tenantUrl: string = `${url.parse(webUrl).protocol}//${url.parse(webUrl).hostname}`; - // if webUrl is a server-relative URL then tenantUrl will resolve to null//null - // in which case we should keep webUrl - let webRelativePath: string = tenantUrl !== 'null//null' ? webUrl.substr(tenantUrl.length) : webUrl; - // will be used to remove relative path from the folderRelativePath // in case the web relative url is included let relativePathToRemove: string = webRelativePath; @@ -168,8 +155,8 @@ export const urlUtil = { * urlUtil.getAbsoluteUrl("https://contoso.sharepoint.com/sites/team1/", "/sites/team1/Lists/MyList"); */ getAbsoluteUrl(webUrl: string, serverRelativeUrl: string): string { - const uri: url.UrlWithStringQuery = url.parse(webUrl); - const tenantUrl: string = `${uri.protocol}//${uri.hostname}`; + const parsedUrl = new URL(webUrl); + const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (serverRelativeUrl[0] !== '/') { serverRelativeUrl = `/${serverRelativeUrl}`; } @@ -242,5 +229,14 @@ export const urlUtil = { */ removeTrailingSlashes(url: string): string { return url.replace(/\/+$/, ''); + }, + + getUrlRelativePath(url: string): string { + if (url.indexOf('://') > 0 || url.indexOf('//') === 0) { + const parsedUrl = new URL(url); + const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; + return url.substring(tenantUrl.length); + } + return url; } }; \ No newline at end of file From 2b3414346133ecacde929fdc6c2d00ebd1d738c6 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Thu, 15 Aug 2024 13:33:45 +0200 Subject: [PATCH 2/4] Enhancement: Deprecation eslint rule --- .eslintrc.cjs | 6 ++---- npm-shrinkwrap.json | 15 --------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c8fecd9c5a5..d70e3f6dcc7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -150,8 +150,7 @@ module.exports = { "plugins": [ "@typescript-eslint", "cli-microsoft365", - "mocha", - "deprecation" + "mocha" ], "ignorePatterns": [ "**/package-generate/assets/**", @@ -240,8 +239,7 @@ module.exports = { ], "@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }], "mocha/no-identical-title": "error", - "@typescript-eslint/no-floating-promises": "error", - "deprecation/deprecation": "error" + "@typescript-eslint/no-floating-promises": "error" }, "overrides": [ { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index a6ed26a5d34..44ef29e9164 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -2434,21 +2434,6 @@ "resolved": "eslint-rules", "link": true }, - "node_modules/eslint-plugin-deprecation": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-3.0.0.tgz", - "integrity": "sha512-JuVLdNg/uf0Adjg2tpTyYoYaMbwQNn/c78P1HcccokvhtRphgnRjZDKmhlxbxYptppex03zO76f97DD/yQHv7A==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^7.0.0", - "ts-api-utils": "^1.3.0", - "tslib": "^2.3.1" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "typescript": "^4.2.4 || ^5.0.0" - } - }, "node_modules/eslint-plugin-mocha": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", From 20f1895a98790cb4ad5a18cc9a796212bd252b0d Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Fri, 18 Oct 2024 08:05:10 +0200 Subject: [PATCH 3/4] Enhancement: Deprecation eslint rule --- src/AuthServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AuthServer.ts b/src/AuthServer.ts index 0c1249c9357..cdbc53da6e1 100644 --- a/src/AuthServer.ts +++ b/src/AuthServer.ts @@ -38,7 +38,7 @@ export class AuthServer { }; private httpListener = async (): Promise => { - const requestState = Math.random().toString(16).substring(2, 20); + const requestState = Math.random().toString(16).substring(2); const address = this.httpServer.address() as AddressInfo; this.generatedServerUrl = `http://localhost:${address.port}`; const url = `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.connection.cloudType)}/${this.connection.tenant}/oauth2/authorize?response_type=code&client_id=${this.connection.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`; From ffb6408c44853dc18233afc3f4214056b80361b6 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Mon, 11 Nov 2024 08:18:24 +0100 Subject: [PATCH 4/4] Enhancement: Deprecation eslint rule --- src/AuthServer.ts | 2 +- src/utils/urlUtil.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/AuthServer.ts b/src/AuthServer.ts index cdbc53da6e1..9a708554276 100644 --- a/src/AuthServer.ts +++ b/src/AuthServer.ts @@ -38,7 +38,7 @@ export class AuthServer { }; private httpListener = async (): Promise => { - const requestState = Math.random().toString(16).substring(2); + const requestState = Math.random().toString(16).substring(2, 22); const address = this.httpServer.address() as AddressInfo; this.generatedServerUrl = `http://localhost:${address.port}`; const url = `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.connection.cloudType)}/${this.connection.tenant}/oauth2/authorize?response_type=code&client_id=${this.connection.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`; diff --git a/src/utils/urlUtil.ts b/src/utils/urlUtil.ts index 358b4477795..0da5077e62a 100644 --- a/src/utils/urlUtil.ts +++ b/src/utils/urlUtil.ts @@ -156,11 +156,10 @@ export const urlUtil = { */ getAbsoluteUrl(webUrl: string, serverRelativeUrl: string): string { const parsedUrl = new URL(webUrl); - const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (serverRelativeUrl[0] !== '/') { serverRelativeUrl = `/${serverRelativeUrl}`; } - return `${tenantUrl}${serverRelativeUrl}`; + return `${parsedUrl.origin}${serverRelativeUrl}`; }, /** @@ -232,10 +231,9 @@ export const urlUtil = { }, getUrlRelativePath(url: string): string { - if (url.indexOf('://') > 0 || url.indexOf('//') === 0) { + if (url.includes('://')) { const parsedUrl = new URL(url); - const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; - return url.substring(tenantUrl.length); + return url.substring(parsedUrl.origin.length); } return url; }