(core) Configure more comprehensive eslint rules for Typescript

Summary:
- Update rules to be more like we've had with tslint
- Switch tsserver plugin to eslint (tsserver makes for a much faster way to lint in editors)
- Apply suggested auto-fixes
- Fix all lint errors and warnings in core/, app/, test/

Test Plan: Some behavior may change subtly (e.g. added missing awaits), relying on existing tests to catch problems.

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D2785
This commit is contained in:
Dmitry S
2021-04-26 17:54:09 -04:00
parent 91fdef58ac
commit 526b0ad33e
56 changed files with 147 additions and 125 deletions

View File

@@ -241,9 +241,9 @@ AceEditor.prototype._getContentHeight = function() {
let _RangeConstructor = null; //singleton, load it lazily
AceEditor.makeRange = function(a,b,c,d) {
AceEditor.makeRange = function(a, b, c, d) {
_RangeConstructor = _RangeConstructor || ace.acequire('ace/range').Range;
return new _RangeConstructor(a,b,c,d);
return new _RangeConstructor(a, b, c, d);
};
module.exports = AceEditor;

View File

@@ -262,7 +262,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
if (this._loaded || !this._gristDoc) { return; }
this._loading(true);
// Returned actions are ordered with earliest actions first.
const result = await this._gristDoc!.docComm.getActionSummaries();
const result = await this._gristDoc.docComm.getActionSummaries();
this._loading(false);
this._loaded = true;
// Add the actions to our action log.

View File

@@ -15,7 +15,7 @@ import * as ko from 'knockout';
import noop = require('lodash/noop');
// To simplify diff (avoid rearranging methods to satisfy private/public order).
// tslint:disable:member-ordering
/* eslint-disable @typescript-eslint/member-ordering */
type AceEditor = any;
@@ -130,7 +130,7 @@ export class ColumnTransform extends Disposable {
this.transformColumn = this.field.column();
this.transformColumn.origColRef(this.origColumn.getRowId());
this._setTransforming(true);
return await this.postAddTransformColumn();
return this.postAddTransformColumn();
} finally {
this.isCallPending(false);
}
@@ -167,7 +167,7 @@ export class ColumnTransform extends Disposable {
/**
* A derived class can override to do some processing after this.transformColumn has been set.
*/
protected postAddTransformColumn() {
protected postAddTransformColumn(): void {
// Nothing in base class.
}
@@ -207,7 +207,7 @@ export class ColumnTransform extends Disposable {
} finally {
// Wait until the change completed to set column back, to avoid value flickering.
field.colRef(origRef);
tableData.sendTableAction(['RemoveColumn', transformColId]);
void tableData.sendTableAction(['RemoveColumn', transformColId]);
this.dispose();
}
}

View File

@@ -350,11 +350,11 @@ GridView.prototype.fillSelectionDown = function() {
}).filter(colId => colId);
var colInfo = _.object(colIds, colIds.map(colId => {
var val = this.tableModel.tableData.getValue(rowIds[0],colId);
var val = this.tableModel.tableData.getValue(rowIds[0], colId);
return rowIds.map(() => val);
}));
this.tableModel.sendTableAction(["BulkUpdateRecord",rowIds,colInfo]);
this.tableModel.sendTableAction(["BulkUpdateRecord", rowIds, colInfo]);
};

View File

@@ -153,7 +153,7 @@ export class Importer extends Disposable {
} else if (item.kind === "url") {
uploadResult = await fetchURL(this._docComm, item.url);
} else {
throw new Error(`Import source of kind ${item!.kind} are not yet supported!`);
throw new Error(`Import source of kind ${(item as any).kind} are not yet supported!`);
}
}
}
@@ -391,7 +391,7 @@ export class Importer extends Disposable {
}
function getSourceDescription(sourceInfo: SourceInfo, upload: UploadResult) {
const origName = upload!.files[sourceInfo.uploadFileIndex].origName;
const origName = upload.files[sourceInfo.uploadFileIndex].origName;
return sourceInfo.origTableName ? origName + ' - ' + sourceInfo.origTableName : origName;
}

View File

@@ -25,8 +25,8 @@ export function makeSearchToolbarGroup(gristDoc: GristDoc) {
// Active normally.
const commandGroup = createGroup({
find: () => { input.focus(); },
findNext: () => { searcher.findNext(); }, // tslint:disable-line:no-floating-promises TODO
findPrev: () => { searcher.findPrev(); }, // tslint:disable-line:no-floating-promises TODO
findNext: () => { searcher.findNext(); }, // eslint-disable-line @typescript-eslint/no-floating-promises
findPrev: () => { searcher.findPrev(); }, // eslint-disable-line @typescript-eslint/no-floating-promises
}, null, true);
// Return an array of one item (for a toolbar group of a single item). The item is an array of
@@ -49,7 +49,7 @@ export function makeSearchToolbarGroup(gristDoc: GristDoc) {
// the searchbox is created so early that the actions like accept/cancel get overridden).
dom.on('keydown', (e: KeyboardEvent) => {
switch (e.keyCode) {
case 13: searcher.findNext(); break; // tslint:disable-line:no-floating-promises TODO
case 13: searcher.findNext(); break; // eslint-disable-line @typescript-eslint/no-floating-promises
case 27: input.blur(); break;
}
})

View File

@@ -264,7 +264,7 @@ CellSelector.prototype.rowCount = function() {
return this.rowUpper() - this.rowLower() + 1;
};
CellSelector.prototype.selectArea = function(rowStartIdx,colStartIdx,rowEndIdx,colEndIdx) {
CellSelector.prototype.selectArea = function(rowStartIdx, colStartIdx, rowEndIdx, colEndIdx) {
this.row.start(rowStartIdx);
this.col.start(colStartIdx);
this.row.end(rowEndIdx);

View File

@@ -22,7 +22,7 @@ import isEmpty = require('lodash/isEmpty');
import pickBy = require('lodash/pickBy');
// To simplify diff (avoid rearranging methods to satisfy private/public order).
// tslint:disable:member-ordering
/* eslint-disable @typescript-eslint/member-ordering */
/**
* Creates an instance of TypeTransform for a single field. Extends ColumnTransform.

View File

@@ -58,7 +58,7 @@ function ViewConfigTab(options) {
}) || self.viewSectionData.at(0);
}));
this.isDetail = this.autoDispose(ko.computed(function() {
return ['detail','single'].includes(this.viewModel.activeSection().parentKey());
return ['detail', 'single'].includes(this.viewModel.activeSection().parentKey());
}, this));
this.isChart = this.autoDispose(ko.computed(function() {
return this.viewModel.activeSection().parentKey() === 'chart';}, this));

View File

@@ -35,7 +35,6 @@ declare module "app/client/components/BaseView" {
import {Cursor, CursorPos} from 'app/client/components/Cursor';
import {GristDoc} from 'app/client/components/GristDoc';
import {Disposable} from 'app/client/lib/dispose';
import {KoArray} from "app/client/lib/koArray";
import * as BaseRowModel from "app/client/models/BaseRowModel";
import {DataRowModel} from 'app/client/models/DataRowModel';
import {LazyArrayModel} from "app/client/models/DataTableModel";
@@ -72,10 +71,8 @@ declare module "app/client/components/BaseView" {
}
declare module "app/client/components/RefSelect" {
import {GristDoc, TabContent} from 'app/client/components/GristDoc';
import {Disposable} from 'app/client/lib/dispose';
import {ColumnRec} from "app/client/models/DocModel";
import {DomArg} from 'grainjs';
import {DocModel} from "app/client/models/DocModel";
import {FieldBuilder} from "app/client/widgets/FieldBuilder";
@@ -161,11 +158,11 @@ declare module "app/client/models/BaseRowModel" {
class BaseRowModel extends Disposable {
public id: ko.Computed<number>;
public _index: ko.Observable<number|null>;
public getRowId(): number;
public updateColValues(colValues: ColValues): Promise<void>;
public _table: TableModel;
protected _rowId: number | 'new' | null;
protected _fields: string[];
public getRowId(): number;
public updateColValues(colValues: ColValues): Promise<void>;
}
export = BaseRowModel;
}
@@ -286,10 +283,9 @@ declare module "app/client/models/DataTableModel" {
import * as BaseRowModel from "app/client/models/BaseRowModel";
import {DocModel, TableRec} from "app/client/models/DocModel";
import {TableQuerySets} from 'app/client/models/QuerySet';
import {RowSource, SortedRowSet} from "app/client/models/rowset";
import {SortedRowSet} from "app/client/models/rowset";
import {TableData} from "app/client/models/TableData";
import * as TableModel from "app/client/models/TableModel";
import {CellValue} from "app/common/DocActions";
namespace DataTableModel {
interface LazyArrayModel<T> extends KoArray<T | null> {

View File

@@ -1,7 +1,7 @@
/**
* Implements an autocomplete dropdown.
*/
import {createPopper, Instance as Popper, Modifier, Options as PopperOptions} from '@popperjs/core';
import {createPopper, Modifier, Instance as Popper, Options as PopperOptions} from '@popperjs/core';
import {ACItem, ACResults, HighlightFunc} from 'app/client/lib/ACIndex';
import {reportError} from 'app/client/models/errors';
import {Disposable, dom, EventCB, IDisposable} from 'grainjs';

View File

@@ -174,7 +174,7 @@ export class BillingModelImpl extends Disposable implements BillingModel {
await this._billingAPI.updateAddress(newAddr || undefined, newSettings || undefined);
}
// If there is an org update, re-initialize the org in the client.
if (newSettings) { await this._appModel.topAppModel.initialize(); }
if (newSettings) { this._appModel.topAppModel.initialize(); }
} else {
throw new Error('BillingPage _submit error: no task in progress');
}

View File

@@ -27,13 +27,6 @@ const ROW_ID_SKIP = -1;
* This should be the only part of the code that knows that.
*/
export class ExtraRows {
readonly leftTableDelta?: TableDelta;
readonly rightTableDelta?: TableDelta;
readonly rightAddRows: Set<number>;
readonly rightRemoveRows: Set<number>;
readonly leftAddRows: Set<number>;
readonly leftRemoveRows: Set<number>;
/**
* Map back from a possibly synthetic row id to an original strictly-positive row id.
*/
@@ -44,7 +37,14 @@ export class ExtraRows {
return { type: 'local-remove', id: -(rowId + 2) / 2 };
}
public constructor(readonly tableId: string, readonly comparison?: DocStateComparisonDetails) {
public readonly leftTableDelta?: TableDelta;
public readonly rightTableDelta?: TableDelta;
public readonly rightAddRows: Set<number>;
public readonly rightRemoveRows: Set<number>;
public readonly leftAddRows: Set<number>;
public readonly leftRemoveRows: Set<number>;
public constructor(public readonly tableId: string, public readonly comparison?: DocStateComparisonDetails) {
const remoteTableId = getRemoteTableId(tableId, comparison);
this.leftTableDelta = this.comparison?.leftChanges?.tableDeltas[tableId];
if (remoteTableId) {

View File

@@ -100,7 +100,7 @@ export class DocData extends BaseDocData {
this._nextDesc = options.description;
this._lastActionNum = null;
this._triggerBundleFinalize = triggerFinalize;
await prepareResolve(options.prepare());
prepareResolve(options.prepare());
this._shouldIncludeInBundle = options.shouldIncludeInBundle;
await triggerFinalizePromise;
@@ -162,7 +162,7 @@ export class DocData extends BaseDocData {
public sendActions(actions: UserAction[], optDesc?: string): Promise<any[]> {
// Some old code relies on this promise being a bluebird Promise.
// TODO Remove bluebird and this cast.
return bluebird.Promise.resolve(this._sendActionsImpl(actions, optDesc)) as any;
return bluebird.Promise.resolve(this._sendActionsImpl(actions, optDesc)) as unknown as Promise<any[]>;
}
/**

View File

@@ -94,7 +94,8 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
public readonly isReadonly = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isReadonly : false);
public readonly isPrefork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isPreFork : false);
public readonly isFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isFork : false);
public readonly isRecoveryMode = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isRecoveryMode : false);
public readonly isRecoveryMode = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isRecoveryMode : false);
public readonly userOverride = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.userOverride : null);
public readonly isBareFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isBareFork : false);
public readonly isSample = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isSample : false);
@@ -133,8 +134,9 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
if (!urlId) {
this._openerHolder.clear();
} else {
FlowRunner.create(this._openerHolder, (flow: AsyncFlow) => this._openDoc(flow, urlId, urlOpenMode,
state.params?.compare, linkParameters))
FlowRunner.create(this._openerHolder,
(flow: AsyncFlow) => this._openDoc(flow, urlId, urlOpenMode, state.params?.compare, linkParameters)
)
.resultPromise.catch(err => this._onOpenError(err));
}
}
@@ -305,8 +307,10 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
menuIcon("Widget"), "Add Widget to Page", testId('dp-add-widget-to-page'),
dom.cls('disabled', isReadonly)
),
menuItem(() => gristDoc.addEmptyTable().catch(reportError), menuIcon("TypeTable"), "Add Empty Table", testId('dp-empty-table'),
dom.cls('disabled', isReadonly)),
menuItem(() => gristDoc.addEmptyTable().catch(reportError),
menuIcon("TypeTable"), "Add Empty Table", testId('dp-empty-table'),
dom.cls('disabled', isReadonly)
),
menuDivider(),
...importSources.map((importSource, i) =>
menuItem(importSource.action,

View File

@@ -314,7 +314,7 @@ function convertQueryToRefs(docModel: DocModel, query: Query): QueryRefs {
const tableRec: any = docModel.dataTables[query.tableId].tableMetaRow;
const colRefsByColId: {[colId: string]: number} = {};
for (const col of (tableRec as any).columns.peek().peek()) {
for (const col of tableRec.columns.peek().peek()) {
colRefsByColId[col.colId.peek()] = col.getRowId();
}

View File

@@ -84,7 +84,7 @@ export class SearchModelImpl extends Disposable implements SearchModel {
// Listen to input value changes (debounced) to activate searching.
const findFirst = debounce((_value: string) => this._findFirst(_value), 100);
this.autoDispose(this.value.addListener(v => { findFirst(v); }));
this.autoDispose(this.value.addListener(v => { void findFirst(v); }));
}
public async findNext() {

View File

@@ -7,7 +7,7 @@ import {DocData} from 'app/client/models/DocData';
import {DocAction, ReplaceTableData, TableDataAction, UserAction} from 'app/common/DocActions';
import {isRaisedException} from 'app/common/gristTypes';
import {countIf} from 'app/common/gutil';
import {ColTypeMap, TableData as BaseTableData} from 'app/common/TableData';
import {TableData as BaseTableData, ColTypeMap} from 'app/common/TableData';
import {BaseFormatter} from 'app/common/ValueFormatter';
import {Emitter} from 'grainjs';

View File

@@ -153,7 +153,7 @@ class BillingPaymentForm extends BillingSubForm {
}) {
super();
const autofill = this._options.autofill;
const stripeAPIKey = (G.window as any).gristConfig.stripeAPIKey;
const stripeAPIKey = G.window.gristConfig.stripeAPIKey;
try {
this._stripe = G.Stripe(stripeAPIKey);
this._elements = this._stripe.elements();
@@ -462,7 +462,7 @@ function checkRequired(propertyName: string) {
// if the current observable value is valid.
function createValidated(
owner: IDisposableOwnerT<any>,
checkValidity: (value: string) => void
checkValidity: (value: string) => void|Promise<void>,
): IValidated<string> {
const value = Observable.create(owner, '');
const isInvalid = Observable.create<boolean>(owner, false);

View File

@@ -352,7 +352,7 @@ function getFieldNewPosition(fields: KoArray<ViewFieldRec>, item: IField,
return tableUtil.fieldInsertPositions(fields, index, 1)[0];
}
function getItemIndex<T>(collection: KoArray<ViewFieldRec>, item: ViewFieldRec|null): number {
function getItemIndex(collection: KoArray<ViewFieldRec>, item: ViewFieldRec|null): number {
if (item !== null) {
return collection.peek().indexOf(item);
}

View File

@@ -194,8 +194,8 @@ export const cssHideForNarrowScreen = styled('div', `
* Attaches the global css properties to the document's root to them available in the page.
*/
export function attachCssRootVars(productFlavor: ProductFlavor, varsOnly: boolean = false) {
dom.update(document.documentElement!, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars));
document.documentElement!.classList.add(cssRoot.className);
dom.update(document.documentElement, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars));
document.documentElement.classList.add(cssRoot.className);
document.body.classList.add(cssBody.className);
const theme = getTheme(productFlavor);
if (theme.bodyClassName) {

View File

@@ -4,7 +4,7 @@ import { CellValue } from 'app/common/DocActions';
import { isVersions } from 'app/common/gristTypes';
import { inlineStyle } from 'app/common/gutil';
import { BaseFormatter } from 'app/common/ValueFormatter';
import { Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch as DiffMatchPatch, DIFF_EQUAL } from 'diff-match-patch';
import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
import { Computed, dom } from 'grainjs';
/**

View File

@@ -27,7 +27,7 @@ import * as gristTypes from 'app/common/gristTypes';
import * as gutil from 'app/common/gutil';
import { CellValue } from 'app/plugin/GristData';
import { delay } from 'bluebird';
import { Computed, Disposable, dom as grainjsDom, fromKo, Holder, IDisposable, makeTestId } from 'grainjs';
import { Computed, Disposable, fromKo, dom as grainjsDom, Holder, IDisposable, makeTestId } from 'grainjs';
import * as ko from 'knockout';
import * as _ from 'underscore';
@@ -77,7 +77,7 @@ export class FieldBuilder extends Disposable {
private readonly widgetCons: ko.Computed<{create: (...args: any[]) => NewAbstractWidget}>;
private readonly docModel: DocModel;
public constructor(readonly gristDoc: GristDoc, readonly field: ViewFieldRec,
public constructor(public readonly gristDoc: GristDoc, public readonly field: ViewFieldRec,
private _cursor: Cursor) {
super();