Commit 07971b1e authored by Andreas Fernandez's avatar Andreas Fernandez Committed by Frank Nägler
Browse files

[TASK] Use ESLint as base for linting

TSLint is in a deprecation phase, recommending to migrate to ESLint
instead. This patch removes all TSLint packages and supplies a proper
ESLint configuration.

Also, rule violations are fixed in this patch.

Executed commands:

  yarn add --dev typescript-eslint \
    @typescript-eslint/parser \
    @typescript-eslint/eslint-plugin \
    eslint grunt-eslint

  yarn remove tslint grunt-tslint

  ./node_modules/.bin/eslint -c eslintrc.js --fix --ext .ts \
    ./Sources/TypeScript/

Resolves: #89232
Releases: master
Change-Id: I3bd4a1c30ecc27f8c334951547aff5e9352629da
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/61784


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
parent cf40e083
...@@ -185,10 +185,9 @@ module.exports = function (grunt) { ...@@ -185,10 +185,9 @@ module.exports = function (grunt) {
ts: ((process.platform === 'win32') ? 'node_modules\\.bin\\tsc.cmd' : './node_modules/.bin/tsc') + ' --project tsconfig.json', ts: ((process.platform === 'win32') ? 'node_modules\\.bin\\tsc.cmd' : './node_modules/.bin/tsc') + ' --project tsconfig.json',
'yarn-install': 'yarn install' 'yarn-install': 'yarn install'
}, },
tslint: { eslint: {
options: { options: {
configuration: 'tslint.json', configFile: 'eslintrc.js'
force: false
}, },
files: { files: {
src: [ src: [
...@@ -575,7 +574,7 @@ module.exports = function (grunt) { ...@@ -575,7 +574,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-postcss'); grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-exec');
grunt.loadNpmTasks('grunt-tslint'); grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-stylelint'); grunt.loadNpmTasks('grunt-stylelint');
grunt.loadNpmTasks('grunt-lintspaces'); grunt.loadNpmTasks('grunt-lintspaces');
grunt.loadNpmTasks('grunt-contrib-imagemin'); grunt.loadNpmTasks('grunt-contrib-imagemin');
...@@ -595,11 +594,11 @@ module.exports = function (grunt) { ...@@ -595,11 +594,11 @@ module.exports = function (grunt) {
* call "$ grunt lint" * call "$ grunt lint"
* *
* this task does the following things: * this task does the following things:
* - tslint * - eslint
* - stylelint * - stylelint
* - lintspaces * - lintspaces
*/ */
grunt.registerTask('lint', ['tslint', 'stylelint', 'lintspaces']); grunt.registerTask('lint', ['eslint', 'stylelint', 'lintspaces']);
/** /**
* grunt format * grunt format
...@@ -641,11 +640,11 @@ module.exports = function (grunt) { ...@@ -641,11 +640,11 @@ module.exports = function (grunt) {
* call "$ grunt scripts" * call "$ grunt scripts"
* *
* this task does the following things: * this task does the following things:
* - 1) Check all TypeScript files (*.ts) with TSLint which are located in sysext/<EXTKEY>/Resources/Private/TypeScript/*.ts * - 1) Check all TypeScript files (*.ts) with ESLint which are located in sysext/<EXTKEY>/Resources/Private/TypeScript/*.ts
* - 2) Compiles all TypeScript files (*.ts) which are located in sysext/<EXTKEY>/Resources/Private/TypeScript/*.ts * - 2) Compiles all TypeScript files (*.ts) which are located in sysext/<EXTKEY>/Resources/Private/TypeScript/*.ts
* - 3) Copy all generated JavaScript and Map files to public folders * - 3) Copy all generated JavaScript and Map files to public folders
*/ */
grunt.registerTask('scripts', ['tsconfig', 'tslint', 'tsclean', 'exec:ts', 'copy:ts_files', 'terser:typescript']); grunt.registerTask('scripts', ['tsconfig', 'eslint', 'tsclean', 'exec:ts', 'copy:ts_files', 'terser:typescript']);
/** /**
* grunt tsclean task * grunt tsclean task
......
...@@ -55,7 +55,7 @@ namespace TYPO3 { ...@@ -55,7 +55,7 @@ namespace TYPO3 {
}, },
); );
this.contentSettings = this.querySelectorAll(AdminPanelSelectors.contentSettingsTriggerRole).map( this.contentSettings = this.querySelectorAll(AdminPanelSelectors.contentSettingsTriggerRole).map(
(contentSettingTrigger: HTMLElement) => { (contentSettingTrigger: HTMLElement) => {
const contentSettingElement = contentSettingTrigger const contentSettingElement = contentSettingTrigger
.closest(AdminPanelSelectors.contentParentClass) .closest(AdminPanelSelectors.contentParentClass)
.querySelector(AdminPanelSelectors.contentSettingsParentClass); .querySelector(AdminPanelSelectors.contentSettingsParentClass);
...@@ -193,16 +193,16 @@ namespace TYPO3 { ...@@ -193,16 +193,16 @@ namespace TYPO3 {
private addBackdropListener(): void { private addBackdropListener(): void {
this.querySelectorAll('.' + AdminPanelClasses.backdrop) this.querySelectorAll('.' + AdminPanelClasses.backdrop)
.forEach((elm: HTMLElement) => { .forEach((elm: HTMLElement) => {
elm.addEventListener('click', () => { elm.addEventListener('click', () => {
this.removeBackdrop(); this.removeBackdrop();
this this
.querySelectorAll(AdminPanelSelectors.moduleTriggerRole) .querySelectorAll(AdminPanelSelectors.moduleTriggerRole)
.forEach((innerElm: HTMLElement) => { .forEach((innerElm: HTMLElement) => {
innerElm.closest(AdminPanelSelectors.moduleParentClass) innerElm.closest(AdminPanelSelectors.moduleParentClass)
.classList.remove(AdminPanelClasses.activeModule); .classList.remove(AdminPanelClasses.activeModule);
}); });
});
}); });
});
} }
} }
...@@ -338,7 +338,7 @@ namespace TYPO3 { ...@@ -338,7 +338,7 @@ namespace TYPO3 {
} }
private initializeEvents(): void { private initializeEvents(): void {
this.trigger.addEventListener('click', (event: MouseEvent) => { this.trigger.addEventListener('click', () => {
this.adminPanel.removeBackdrop(); this.adminPanel.removeBackdrop();
if (this.isActive()) { if (this.isActive()) {
this.disable(); this.disable();
......
...@@ -19,7 +19,7 @@ import {AbstractAction} from './AbstractAction'; ...@@ -19,7 +19,7 @@ import {AbstractAction} from './AbstractAction';
class ImmediateAction extends AbstractAction { class ImmediateAction extends AbstractAction {
protected callback: () => void; protected callback: () => void;
public execute(el: HTMLElement): Promise<any> { public execute(): Promise<any> {
return this.executeCallback(); return this.executeCallback();
} }
......
...@@ -51,11 +51,7 @@ class ContextMenuActions { ...@@ -51,11 +51,7 @@ class ContextMenuActions {
); );
} }
/** public static viewRecord(): void {
* @param {string} table
* @param {number} uid
*/
public static viewRecord(table: string, uid: number): void {
const $viewUrl = $(this).data('preview-url'); const $viewUrl = $(this).data('preview-url');
if ($viewUrl) { if ($viewUrl) {
const previewWin = window.open($viewUrl, 'newTYPO3frontendWindow'); const previewWin = window.open($viewUrl, 'newTYPO3frontendWindow');
...@@ -91,11 +87,7 @@ class ContextMenuActions { ...@@ -91,11 +87,7 @@ class ContextMenuActions {
); );
} }
/** public static newContentWizard(): void {
* @param {string} table
* @param {number} uid
*/
public static newContentWizard(table: string, uid: number): void {
const $me = $(this); const $me = $(this);
let $wizardUrl = $me.data('new-wizard-url'); let $wizardUrl = $me.data('new-wizard-url');
if ($wizardUrl) { if ($wizardUrl) {
...@@ -139,22 +131,14 @@ class ContextMenuActions { ...@@ -139,22 +131,14 @@ class ContextMenuActions {
ModuleMenu.App.showModule('web_list', 'id=' + pageId); ModuleMenu.App.showModule('web_list', 'id=' + pageId);
} }
/** public static pagesSort(): void {
* @param {string} table
* @param {number} uid
*/
public static pagesSort(table: string, uid: number): void {
const pagesSortUrl = $(this).data('pages-sort-url'); const pagesSortUrl = $(this).data('pages-sort-url');
if (pagesSortUrl) { if (pagesSortUrl) {
Viewport.ContentContainer.setUrl(pagesSortUrl); Viewport.ContentContainer.setUrl(pagesSortUrl);
} }
} }
/** public static pagesNewMultiple(): void {
* @param {string} table
* @param {number} uid
*/
public static pagesNewMultiple(table: string, uid: number): void {
const pagesSortUrl = $(this).data('pages-new-multiple-url'); const pagesSortUrl = $(this).data('pages-new-multiple-url');
if (pagesSortUrl) { if (pagesSortUrl) {
Viewport.ContentContainer.setUrl(pagesSortUrl); Viewport.ContentContainer.setUrl(pagesSortUrl);
...@@ -305,7 +289,7 @@ class ContextMenuActions { ...@@ -305,7 +289,7 @@ class ContextMenuActions {
}, },
error: (): void => { error: (): void => {
Notification.error( Notification.error(
'Clearing page caches went wrong on the server side.', 'Clearing page caches went wrong on the server side.',
); );
}, },
}); });
......
...@@ -18,12 +18,6 @@ class DocumentSaveActions { ...@@ -18,12 +18,6 @@ class DocumentSaveActions {
private static instance: DocumentSaveActions = null; private static instance: DocumentSaveActions = null;
private preSubmitCallbacks: Array<Function> = []; private preSubmitCallbacks: Array<Function> = [];
private constructor() {
$((): void => {
this.initializeSaveHandling();
});
}
public static getInstance(): DocumentSaveActions { public static getInstance(): DocumentSaveActions {
if (DocumentSaveActions.instance === null) { if (DocumentSaveActions.instance === null) {
DocumentSaveActions.instance = new DocumentSaveActions(); DocumentSaveActions.instance = new DocumentSaveActions();
...@@ -32,6 +26,12 @@ class DocumentSaveActions { ...@@ -32,6 +26,12 @@ class DocumentSaveActions {
return DocumentSaveActions.instance; return DocumentSaveActions.instance;
} }
private constructor() {
$((): void => {
this.initializeSaveHandling();
});
}
/** /**
* Adds a callback being executed before submit * Adds a callback being executed before submit
* *
......
...@@ -68,6 +68,12 @@ interface DragUploaderOptions { ...@@ -68,6 +68,12 @@ interface DragUploaderOptions {
outputColor?: string; outputColor?: string;
} }
interface FileConflict {
original: UploadedFile;
uploaded: InternalFile;
action: Action;
}
class DragUploaderPlugin { class DragUploaderPlugin {
public irreObjectUid: number; public irreObjectUid: number;
public $fileList: JQuery; public $fileList: JQuery;
...@@ -81,17 +87,17 @@ class DragUploaderPlugin { ...@@ -81,17 +87,17 @@ class DragUploaderPlugin {
/** /**
* Array of files which are asked for being overridden * Array of files which are asked for being overridden
*/ */
private askForOverride: Array<{ original: UploadedFile, uploaded: InternalFile, action: Action }> = []; private askForOverride: Array<FileConflict> = [];
private percentagePerFile: number = 1; private percentagePerFile: number = 1;
private $body: JQuery; private $body: JQuery;
private $element: JQuery; private readonly $element: JQuery;
private $dropzone: JQuery; private readonly $dropzone: JQuery;
private $dropzoneMask: JQuery; private readonly $dropzoneMask: JQuery;
private fileInput: HTMLInputElement; private readonly fileInput: HTMLInputElement;
private browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean }; private browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean };
private dropZoneInsertBefore: boolean; private readonly dropZoneInsertBefore: boolean;
private queueLength: number; private queueLength: number;
private defaultAction: string; private defaultAction: string;
...@@ -256,7 +262,7 @@ class DragUploaderPlugin { ...@@ -256,7 +262,7 @@ class DragUploaderPlugin {
// Check for each file if is already exist before adding it to the queue // Check for each file if is already exist before adding it to the queue
const ajaxCalls: JQueryXHR[] = []; const ajaxCalls: JQueryXHR[] = [];
$.each(files, (i: string, file) => { $.each(files, (i: string, file: InternalFile) => {
ajaxCalls[parseInt(i, 10)] = $.ajax({ ajaxCalls[parseInt(i, 10)] = $.ajax({
url: TYPO3.settings.ajaxUrls.file_exists, url: TYPO3.settings.ajaxUrls.file_exists,
data: { data: {
...@@ -274,8 +280,7 @@ class DragUploaderPlugin { ...@@ -274,8 +280,7 @@ class DragUploaderPlugin {
}); });
NProgress.inc(this.percentagePerFile); NProgress.inc(this.percentagePerFile);
} else { } else {
// Unused var _ is necessary as "no-unused-expression" is active new FileQueueItem(this, file, Action.SKIP);
const _ = new FileQueueItem(this, file, Action.SKIP);
} }
}, },
}); });
...@@ -320,7 +325,7 @@ class DragUploaderPlugin { ...@@ -320,7 +325,7 @@ class DragUploaderPlugin {
$.ajax({ $.ajax({
url: TYPO3.settings.ajaxUrls.flashmessages_render, url: TYPO3.settings.ajaxUrls.flashmessages_render,
cache: false, cache: false,
success: (data) => { success: (data: any) => {
$.each(data, (index: number, flashMessage: { title: string, message: string, severity: number }) => { $.each(data, (index: number, flashMessage: { title: string, message: string, severity: number }) => {
Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity); Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
}); });
...@@ -355,8 +360,8 @@ class DragUploaderPlugin { ...@@ -355,8 +360,8 @@ class DragUploaderPlugin {
const $record = $('<tr />').append( const $record = $('<tr />').append(
$('<td />').append( $('<td />').append(
(this.askForOverride[i].original.thumbUrl !== '' (this.askForOverride[i].original.thumbUrl !== ''
? $('<img />', {src: this.askForOverride[i].original.thumbUrl, height: 40}) ? $('<img />', {src: this.askForOverride[i].original.thumbUrl, height: 40})
: $(this.askForOverride[i].original.icon) : $(this.askForOverride[i].original.icon)
), ),
), ),
$('<td />').html( $('<td />').html(
...@@ -429,7 +434,7 @@ class DragUploaderPlugin { ...@@ -429,7 +434,7 @@ class DragUploaderPlugin {
if (value !== '') { if (value !== '') {
// mass action was selected, apply action to every file // mass action was selected, apply action to every file
$modal.find('.t3js-actions').each((i, select) => { $modal.find('.t3js-actions').each((i: number, select: HTMLSelectElement) => {
const $select = $(select), const $select = $(select),
index = parseInt($select.data('override'), 10); index = parseInt($select.data('override'), 10);
$select.val(value).prop('disabled', 'disabled'); $select.val(value).prop('disabled', 'disabled');
...@@ -447,15 +452,14 @@ class DragUploaderPlugin { ...@@ -447,15 +452,14 @@ class DragUploaderPlugin {
uploader.askForOverride = []; uploader.askForOverride = [];
Modal.dismiss(); Modal.dismiss();
} else if ((<HTMLInputElement>(e.target)).name === 'continue') { } else if ((<HTMLInputElement>(e.target)).name === 'continue') {
$.each(uploader.askForOverride, (key, fileInfo) => { $.each(uploader.askForOverride, (key: number, fileInfo: FileConflict) => {
if (fileInfo.action === Action.USE_EXISTING) { if (fileInfo.action === Action.USE_EXISTING) {
DragUploader.addFileToIrre( DragUploader.addFileToIrre(
uploader.irreObjectUid, uploader.irreObjectUid,
fileInfo.original, fileInfo.original,
); );
} else if (fileInfo.action !== Action.SKIP) { } else if (fileInfo.action !== Action.SKIP) {
// Unused var _ is necessary as "no-unused-expression" is active new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
const _ = new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
} }
}); });
uploader.askForOverride = []; uploader.askForOverride = [];
...@@ -740,7 +744,6 @@ class DragUploader { ...@@ -740,7 +744,6 @@ class DragUploader {
$('.t3js-drag-uploader').dragUploader(opts); $('.t3js-drag-uploader').dragUploader(opts);
}); });
} }
} }
/** /**
...@@ -767,7 +770,7 @@ export const initialize = function (): void { ...@@ -767,7 +770,7 @@ export const initialize = function (): void {
&& 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'] && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']
) { ) {
$.each( $.each(
TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], (pos, moduleName) => { TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], (pos: number, moduleName: string) => {
require([moduleName]); require([moduleName]);
}, },
); );
......
...@@ -129,8 +129,8 @@ class InlineControlContainer { ...@@ -129,8 +129,8 @@ class InlineControlContainer {
private static registerInfoButton(e: Event): void { private static registerInfoButton(e: Event): void {
let target: HTMLElement; let target: HTMLElement;
if ((target = InlineControlContainer.getDelegatedEventTarget( if ((target = InlineControlContainer.getDelegatedEventTarget(
e.target, e.target,
Selectors.infoWindowButton) Selectors.infoWindowButton)
) === null) { ) === null) {
return; return;
} }
...@@ -186,7 +186,7 @@ class InlineControlContainer { ...@@ -186,7 +186,7 @@ class InlineControlContainer {
* @param {UniqueDefinitionCollection} hashmap * @param {UniqueDefinitionCollection} hashmap
*/ */
private static getValuesFromHashMap(hashmap: UniqueDefinitionCollection): Array<any> { private static getValuesFromHashMap(hashmap: UniqueDefinitionCollection): Array<any> {
return Object.keys(hashmap).map(key => hashmap[key]); return Object.keys(hashmap).map((key: string) => hashmap[key]);
} }
private static selectOptionValueExists(selectElement: HTMLSelectElement, value: string): boolean { private static selectOptionValueExists(selectElement: HTMLSelectElement, value: string): boolean {
...@@ -374,7 +374,7 @@ class InlineControlContainer { ...@@ -374,7 +374,7 @@ class InlineControlContainer {
*/ */
private handlePostMessage = (e: MessageEvent): void => { private handlePostMessage = (e: MessageEvent): void => {
if (!MessageUtility.verifyOrigin(e.origin)) { if (!MessageUtility.verifyOrigin(e.origin)) {
throw 'Denied message sent by ' + e.origin; throw 'Denied message sent by ' + e.origin;
} }
if (typeof e.data.objectGroup === 'undefined') { if (typeof e.data.objectGroup === 'undefined') {
...@@ -448,8 +448,8 @@ class InlineControlContainer { ...@@ -448,8 +448,8 @@ class InlineControlContainer {
private registerEnableDisableButton(e: Event): void { private registerEnableDisableButton(e: Event): void {
let target: HTMLElement; let target: HTMLElement;
if ((target = InlineControlContainer.getDelegatedEventTarget( if ((target = InlineControlContainer.getDelegatedEventTarget(
e.target, e.target,
Selectors.enableDisableRecordButtonSelector) Selectors.enableDisableRecordButtonSelector)
) === null) { ) === null) {
return; return;
} }
...@@ -492,8 +492,8 @@ class InlineControlContainer { ...@@ -492,8 +492,8 @@ class InlineControlContainer {
private registerDeleteButton(e: Event): void { private registerDeleteButton(e: Event): void {
let target: HTMLElement; let target: HTMLElement;
if ((target = InlineControlContainer.getDelegatedEventTarget( if ((target = InlineControlContainer.getDelegatedEventTarget(
e.target, e.target,
Selectors.deleteRecordButtonSelector) Selectors.deleteRecordButtonSelector)
) === null) { ) === null) {
return; return;
} }
......
...@@ -103,10 +103,10 @@ export abstract class AbstractSortableSelectItems { ...@@ -103,10 +103,10 @@ export abstract class AbstractSortableSelectItems {
return; return;
} }
aside.addEventListener('click', (e): void => { aside.addEventListener('click', (e: Event): void => {
let target: HTMLAnchorElement; let target: HTMLAnchorElement;
if ((target = <HTMLAnchorElement>(<Element>e.target).closest('.t3js-btn-option')) === null) { if ((target = (<Element>e.target).closest('.t3js-btn-option')) === null) {
if ((<Element>e.target).matches('.t3js-btn-option')) { if ((<Element>e.target).matches('.t3js-btn-option')) {
target = <HTMLAnchorElement>e.target; target = <HTMLAnchorElement>e.target;
} }
......
...@@ -65,7 +65,7 @@ class SelectBoxFilter { ...@@ -65,7 +65,7 @@ class SelectBoxFilter {
this.selectElement.innerHTML = ''; this.selectElement.innerHTML = '';
const matchFilter = new RegExp(filterText, 'i'); const matchFilter = new RegExp(filterText, 'i');
this.$availableOptions.each((i, el): void => { this.$availableOptions.each((i: number, el: HTMLElement): void => {
if (filterText.length === 0 || el.textContent.match(matchFilter)) { if (filterText.length === 0 || el.textContent.match(matchFilter)) {
this.selectElement.appendChild(el); this.selectElement.appendChild(el);
} }
......
...@@ -83,7 +83,7 @@ class SlugElement { ...@@ -83,7 +83,7 @@ class SlugElement {
} }
private registerEvents(): void { private registerEvents(): void {
const fieldsToListenOnList = Object.keys(this.getAvailableFieldsForProposalGeneration()).map(k => this.fieldsToListenOn[k]); const fieldsToListenOnList = Object.keys(this.getAvailableFieldsForProposalGeneration()).map((k: string) => this.fieldsToListenOn[k]);