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) {
ts: ((process.platform === 'win32') ? 'node_modules\\.bin\\tsc.cmd' : './node_modules/.bin/tsc') + ' --project tsconfig.json',
'yarn-install': 'yarn install'
},
tslint: {
eslint: {
options: {
configuration: 'tslint.json',
force: false
configFile: 'eslintrc.js'
},
files: {
src: [
......@@ -575,7 +574,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-exec');
grunt.loadNpmTasks('grunt-tslint');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-stylelint');
grunt.loadNpmTasks('grunt-lintspaces');
grunt.loadNpmTasks('grunt-contrib-imagemin');
......@@ -595,11 +594,11 @@ module.exports = function (grunt) {
* call "$ grunt lint"
*
* this task does the following things:
* - tslint
* - eslint
* - stylelint
* - lintspaces
*/
grunt.registerTask('lint', ['tslint', 'stylelint', 'lintspaces']);
grunt.registerTask('lint', ['eslint', 'stylelint', 'lintspaces']);
/**
* grunt format
......@@ -641,11 +640,11 @@ module.exports = function (grunt) {
* call "$ grunt scripts"
*
* 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
* - 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
......
......@@ -338,7 +338,7 @@ namespace TYPO3 {
}
private initializeEvents(): void {
this.trigger.addEventListener('click', (event: MouseEvent) => {
this.trigger.addEventListener('click', () => {
this.adminPanel.removeBackdrop();
if (this.isActive()) {
this.disable();
......
......@@ -19,7 +19,7 @@ import {AbstractAction} from './AbstractAction';
class ImmediateAction extends AbstractAction {
protected callback: () => void;
public execute(el: HTMLElement): Promise<any> {
public execute(): Promise<any> {
return this.executeCallback();
}
......
......@@ -51,11 +51,7 @@ class ContextMenuActions {
);
}
/**
* @param {string} table
* @param {number} uid
*/
public static viewRecord(table: string, uid: number): void {
public static viewRecord(): void {
const $viewUrl = $(this).data('preview-url');
if ($viewUrl) {
const previewWin = window.open($viewUrl, 'newTYPO3frontendWindow');
......@@ -91,11 +87,7 @@ class ContextMenuActions {
);
}
/**
* @param {string} table
* @param {number} uid
*/
public static newContentWizard(table: string, uid: number): void {
public static newContentWizard(): void {
const $me = $(this);
let $wizardUrl = $me.data('new-wizard-url');
if ($wizardUrl) {
......@@ -139,22 +131,14 @@ class ContextMenuActions {
ModuleMenu.App.showModule('web_list', 'id=' + pageId);
}
/**
* @param {string} table
* @param {number} uid
*/
public static pagesSort(table: string, uid: number): void {
public static pagesSort(): void {
const pagesSortUrl = $(this).data('pages-sort-url');
if (pagesSortUrl) {
Viewport.ContentContainer.setUrl(pagesSortUrl);
}
}
/**
* @param {string} table
* @param {number} uid
*/
public static pagesNewMultiple(table: string, uid: number): void {
public static pagesNewMultiple(): void {
const pagesSortUrl = $(this).data('pages-new-multiple-url');
if (pagesSortUrl) {
Viewport.ContentContainer.setUrl(pagesSortUrl);
......
......@@ -18,12 +18,6 @@ class DocumentSaveActions {
private static instance: DocumentSaveActions = null;
private preSubmitCallbacks: Array<Function> = [];
private constructor() {
$((): void => {
this.initializeSaveHandling();
});
}
public static getInstance(): DocumentSaveActions {
if (DocumentSaveActions.instance === null) {
DocumentSaveActions.instance = new DocumentSaveActions();
......@@ -32,6 +26,12 @@ class DocumentSaveActions {
return DocumentSaveActions.instance;
}
private constructor() {
$((): void => {
this.initializeSaveHandling();
});
}
/**
* Adds a callback being executed before submit
*
......
......@@ -68,6 +68,12 @@ interface DragUploaderOptions {
outputColor?: string;
}
interface FileConflict {
original: UploadedFile;
uploaded: InternalFile;
action: Action;
}
class DragUploaderPlugin {
public irreObjectUid: number;
public $fileList: JQuery;
......@@ -81,17 +87,17 @@ class DragUploaderPlugin {
/**
* 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 $body: JQuery;
private $element: JQuery;
private $dropzone: JQuery;
private $dropzoneMask: JQuery;
private fileInput: HTMLInputElement;
private readonly $element: JQuery;
private readonly $dropzone: JQuery;
private readonly $dropzoneMask: JQuery;
private readonly fileInput: HTMLInputElement;
private browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean };
private dropZoneInsertBefore: boolean;
private readonly dropZoneInsertBefore: boolean;
private queueLength: number;
private defaultAction: string;
......@@ -256,7 +262,7 @@ class DragUploaderPlugin {
// Check for each file if is already exist before adding it to the queue
const ajaxCalls: JQueryXHR[] = [];
$.each(files, (i: string, file) => {
$.each(files, (i: string, file: InternalFile) => {
ajaxCalls[parseInt(i, 10)] = $.ajax({
url: TYPO3.settings.ajaxUrls.file_exists,
data: {
......@@ -274,8 +280,7 @@ class DragUploaderPlugin {
});
NProgress.inc(this.percentagePerFile);
} else {
// Unused var _ is necessary as "no-unused-expression" is active
const _ = new FileQueueItem(this, file, Action.SKIP);
new FileQueueItem(this, file, Action.SKIP);
}
},
});
......@@ -320,7 +325,7 @@ class DragUploaderPlugin {
$.ajax({
url: TYPO3.settings.ajaxUrls.flashmessages_render,
cache: false,
success: (data) => {
success: (data: any) => {
$.each(data, (index: number, flashMessage: { title: string, message: string, severity: number }) => {
Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
});
......@@ -429,7 +434,7 @@ class DragUploaderPlugin {
if (value !== '') {
// 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),
index = parseInt($select.data('override'), 10);
$select.val(value).prop('disabled', 'disabled');
......@@ -447,15 +452,14 @@ class DragUploaderPlugin {
uploader.askForOverride = [];
Modal.dismiss();
} 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) {
DragUploader.addFileToIrre(
uploader.irreObjectUid,
fileInfo.original,
);
} else if (fileInfo.action !== Action.SKIP) {
// Unused var _ is necessary as "no-unused-expression" is active
const _ = new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
}
});
uploader.askForOverride = [];
......@@ -740,7 +744,6 @@ class DragUploader {
$('.t3js-drag-uploader').dragUploader(opts);
});
}
}
/**
......@@ -767,7 +770,7 @@ export const initialize = function (): void {
&& 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']
) {
$.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]);
},
);
......
......@@ -186,7 +186,7 @@ class InlineControlContainer {
* @param {UniqueDefinitionCollection} hashmap
*/
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 {
......
......@@ -103,10 +103,10 @@ export abstract class AbstractSortableSelectItems {
return;
}
aside.addEventListener('click', (e): void => {
aside.addEventListener('click', (e: Event): void => {
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')) {
target = <HTMLAnchorElement>e.target;
}
......
......@@ -65,7 +65,7 @@ class SelectBoxFilter {
this.selectElement.innerHTML = '';
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)) {
this.selectElement.appendChild(el);
}
......
......@@ -83,7 +83,7 @@ class SlugElement {
}
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]);
// Listen on 'listenerFieldNames' for new pages. This is typically the 'title' field
// of a page to create slugs from the title when title is set / changed.
......@@ -97,7 +97,7 @@ class SlugElement {
}
// Clicking the recreate button makes new slug proposal created from 'title' field
$(this.$fullElement).on('click', Selectors.recreateButton, (e): void => {
$(this.$fullElement).on('click', Selectors.recreateButton, (e: JQueryEventObject): void => {
e.preventDefault();
if (this.$readOnlyField.hasClass('hidden')) {
// Switch to readonly version - similar to 'new' page where field is
......@@ -122,7 +122,7 @@ class SlugElement {
// Clicking the toggle button toggles the read only field and the input field.
// Also set the value of either the read only or the input field to the hidden field
// and update the value of the read only field after manual change of the input field.
$(this.$fullElement).on('click', Selectors.toggleButton, (e): void => {
$(this.$fullElement).on('click', Selectors.toggleButton, (e: JQueryEventObject): void => {
e.preventDefault();
const showReadOnlyField = this.$readOnlyField.hasClass('hidden');
this.$readOnlyField.toggleClass('hidden', !showReadOnlyField);
......
......@@ -86,7 +86,7 @@ export class AjaxDispatcher {
// TODO: This is subject to be removed
if (json.scriptCall && json.scriptCall.length > 0) {
$.each(json.scriptCall, (index: number, value: string): void => {
// tslint:disable-next-line:no-eval
// eslint-disable-next-line no-eval
eval(value);
});
}
......
......@@ -111,7 +111,7 @@ class FlexFormElement {
this.createSortable();
// allow delete of a single section
this.$el.off('click').on('click', this.opts.deleteIconSelector, (evt) => {
this.$el.off('click').on('click', this.opts.deleteIconSelector, (evt: JQueryEventObject) => {
evt.preventDefault();
const confirmTitle = TYPO3.lang['flexform.section.delete.title'] || 'Are you sure?';
......@@ -129,7 +129,7 @@ class FlexFormElement {
});
// allow the toggle open/close of the main selection
this.$el.on('click', this.opts.sectionToggleButtonSelector, (evt) => {
this.$el.on('click', this.opts.sectionToggleButtonSelector, (evt: JQueryEventObject) => {
evt.preventDefault();
const $sectionEl = $(evt.currentTarget).closest(this.opts.sectionSelector);
this.toggleSection($sectionEl);
......@@ -225,7 +225,7 @@ class FlexFormElement {
$.fn.t3FormEngineFlexFormElement = function(options: FlexFormElementOptions): JQuery {
// apply all util functions to ourself (for use in templates, etc.)
return this.each(function(this: HTMLElement): void {
const _ = new FlexFormElement(this, options);
new FlexFormElement(this, options);
});
};
......@@ -259,7 +259,7 @@ $(function(): void {
$('.t3-flex-container').t3FormEngineFlexFormElement();
if (response.scriptCall && response.scriptCall.length > 0) {
$.each(response.scriptCall, function(index: number, value: string): void {
/* tslint:disable-next-line:no-eval */
// eslint-disable-next-line no-eval
eval(value);
});
}
......
......@@ -55,11 +55,13 @@ class Icons {
* @param {MarkupIdentifiers} markupIdentifier
* @returns {JQueryPromise<any>}
*/
public getIcon(identifier: string,
public getIcon(
identifier: string,
size: Sizes,
overlayIdentifier?: string,
state?: string,
markupIdentifier?: MarkupIdentifiers): JQueryPromise<any> {
markupIdentifier?: MarkupIdentifiers,
): JQueryPromise<any> {
/**
* Icon keys:
......
......@@ -276,7 +276,7 @@ class ImageManipulation {
title: modalTitle,
});
this.currentModal.on('hide.bs.modal', (e: JQueryEventObject): void => {
this.currentModal.on('hide.bs.modal', (): void => {
this.destroy();
});
// do not dismiss the modal when clicking beside it to avoid data loss
......
......@@ -60,10 +60,10 @@ class DragDrop {
// addClasses: 'active-drag',
revert: 'invalid',
zIndex: 100,
start: (evt: JQueryEventObject, ui: DroppableEventUIParam): void => {
start: (evt: JQueryEventObject): void => {
DragDrop.onDragStart($(evt.target));
},
stop: (evt: JQueryEventObject, ui: DroppableEventUIParam): void => {
stop: (evt: JQueryEventObject): void => {
DragDrop.onDragStop($(evt.target));
},
});
......
......@@ -158,7 +158,7 @@ class BackendLogin {
}
(<NodeListOf<HTMLInputElement>>document.querySelectorAll('.t3js-clearable')).forEach(
clearableField => clearableField.clearable(),
(clearableField: HTMLInputElement) => clearableField.clearable(),
);
// carousel news height transition
......
......@@ -21,7 +21,7 @@ enum MarkupIdentifiers {
}
// hack is required, because the Notification definition is wrong
declare var Notification: any;
declare let Notification: any;
/**
* Module: TYPO3/CMS/Backend/LoginRefresh
......@@ -396,7 +396,7 @@ class LoginRefresh {
url: $form.attr('action'),
method: 'POST',
data: postData,
success: (response) => {
success: (response: { [key: string ]: any }) => {
if (response.login.success) {
// User is logged in
this.hideLoginForm();
......@@ -439,7 +439,7 @@ class LoginRefresh {
* and opens a dialog.
*/
protected checkActiveSession = (): void => {
$.getJSON(TYPO3.settings.ajaxUrls.login_timedout, [], (response) => {
$.getJSON(TYPO3.settings.ajaxUrls.login_timedout, [], (response: { [key: string ]: any }) => {
if (response.login.locked) {
if (!this.backendIsLocked) {
this.backendIsLocked = true;
......
......@@ -148,11 +148,13 @@ class Modal {
* @param {Array<string>} additionalCssClasses Additional css classes to add to the modal
* @returns {JQuery}
*/
public confirm(title: string,
public confirm(
title: string,
content: string | JQuery,
severity: SeverityEnum = SeverityEnum.warning,
buttons: Array<Object> = [],
additionalCssClasses?: Array<string>): JQuery {
additionalCssClasses?: Array<string>,
): JQuery {
if (buttons.length === 0) {
buttons.push(
{
......@@ -199,7 +201,8 @@ class Modal {
* @param {string} target
* @returns {JQuery}
*/
public loadUrl(title: string,
public loadUrl(
title: string,
severity: SeverityEnum = SeverityEnum.info,
buttons: Array<Object>,
url: string,
......@@ -226,11 +229,13 @@ class Modal {
* @param {Array<string>} additionalCssClasses
* @returns {JQuery}
*/
public show(title: string,
public show(
title: string,
content: string | JQuery,
severity: SeverityEnum = SeverityEnum.info,
buttons?: Array<Object>,
additionalCssClasses?: Array<string>): JQuery {
additionalCssClasses?: Array<string>,
): JQuery {
return this.advanced({
type: Types.default,
title,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment