Resolve lint issues due to new default rules

We could turn these rules off, but it seems that they indicate some important inconsistencies.

A few cases have been ignored inline because they should be investigated in more detail, which is out of scope for this PR:
- Metadata representation components compare `BrowseByDataType` to a `ResourceType`. Could be related to #2949.
- We assume that dynamic form dates are always represented by strings, but they can also be `Date` or `object` according to the library (see da1742ce05/projects/ng-dynamic-forms/core/src/lib/model/dynamic-date-control.model.ts (L5))
This commit is contained in:
Yury Bondarenko
2024-04-18 10:11:56 +02:00
parent 14a19b2000
commit dc1053e3f9
33 changed files with 64 additions and 42 deletions

View File

@@ -16,6 +16,9 @@
"dspace-angular-ts",
"dspace-angular-html"
],
"ignorePatterns": [
"lint/test/fixture"
],
"overrides": [
{
"files": [
@@ -216,6 +219,15 @@
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/no-base-to-string": [
"error",
{
"ignoredTypeNames": [
"ResourceType",
"Error"
]
}
],
"deprecation/deprecation": "warn",

View File

@@ -43,7 +43,7 @@ function isAngularComponentDecorator(node: ts.Node) {
const method = decorator.expression as ts.CallExpression;
if (method.expression.kind === ts.SyntaxKind.Identifier) {
return (method.expression as Identifier).escapedText === 'Component';
return (method.expression as Identifier).text === 'Component';
}
}
}
@@ -60,7 +60,7 @@ function findImportDeclaration(source: ts.SourceFile, identifierName: string): t
const namedImports = importDeclaration.importClause?.namedBindings as ts.NamedImports;
for (const element of namedImports.elements) {
if (element.name.escapedText === identifierName) {
if (element.name.text === identifierName) {
return importDeclaration;
}
}

View File

@@ -52,7 +52,7 @@ export function findUsages(context: AnyRuleContext, localNode: TSESTree.Identifi
const usages: TSESTree.Identifier[] = [];
for (const token of source.ast.tokens) {
if (token.type === 'Identifier' && token.value === localNode.name && !match(token.range, localNode.range)) {
if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === localNode.name && !match(token.range, localNode.range)) {
const node = source.getNodeByRangeIndex(token.range[0]);
// todo: in some cases, the resulting node can actually be the whole program (!)
if (node !== null) {
@@ -70,7 +70,7 @@ export function findUsagesByName(context: AnyRuleContext, identifier: string): T
const usages: TSESTree.Identifier[] = [];
for (const token of source.ast.tokens) {
if (token.type === 'Identifier' && token.value === identifier) {
if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === identifier) {
const node = source.getNodeByRangeIndex(token.range[0]);
// todo: in some cases, the resulting node can actually be the whole program (!)
if (node !== null) {
@@ -83,11 +83,11 @@ export function findUsagesByName(context: AnyRuleContext, identifier: string): T
}
export function isPartOfTypeExpression(node: TSESTree.Identifier): boolean {
return node.parent?.type?.startsWith('TSType');
return node.parent?.type?.valueOf().startsWith('TSType');
}
export function isPartOfClassDeclaration(node: TSESTree.Identifier): boolean {
return node.parent?.type === 'ClassDeclaration';
return node.parent?.type === TSESTree.AST_NODE_TYPES.ClassDeclaration;
}
function fromSrc(path: string): string {
@@ -134,7 +134,7 @@ export function findImportSpecifier(context: AnyRuleContext, identifier: string)
const usages: TSESTree.Identifier[] = [];
for (const token of source.ast.tokens) {
if (token.type === 'Identifier' && token.value === identifier) {
if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === identifier) {
const node = source.getNodeByRangeIndex(token.range[0]);
// todo: in some cases, the resulting node can actually be the whole program (!)
if (node && node.parent && node.parent.type === TSESTree.AST_NODE_TYPES.ImportSpecifier) {

View File

@@ -15,6 +15,7 @@ import { NotificationsService } from 'src/app/shared/notifications/notifications
import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service';
import { RemoteData } from '../../../../../../core/data/remote-data';
import { RequestEntryState } from '../../../../../../core/data/request-entry-state.model';
import { Group } from '../../../../../../core/eperson/models/group.model';
import { SupervisionOrder } from '../../../../../../core/supervision-order/models/supervision-order.model';
import { SupervisionOrderDataService } from '../../../../../../core/supervision-order/supervision-order-data.service';
@@ -95,7 +96,7 @@ export class SupervisionOrderGroupSelectorComponent {
this.supervisionOrderDataService.create(supervisionDataObject, this.itemUUID, this.selectedGroup.uuid, this.selectedOrderType).pipe(
getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<SupervisionOrder>) => {
if (rd.state === 'Success') {
if (rd.state === RequestEntryState.Success) {
this.notificationsService.success(this.translateService.get('supervision-group-selector.notification.create.success.title', { name: this.dsoNameService.getName(this.selectedGroup) }));
this.create.emit(rd.payload);
this.close();

View File

@@ -91,5 +91,5 @@ export const oneAuthorizationMatchesFeature = (featureID: FeatureID) =>
return observableOf([]);
}
}),
map((features: Feature[]) => features.filter((feature: Feature) => feature.id === featureID).length > 0),
map((features: Feature[]) => features.filter((feature: Feature) => feature.id === featureID.valueOf()).length > 0),
);

View File

@@ -151,7 +151,7 @@ export class DspaceRestService {
return form;
}
protected handleHttpError(err: unknown): RequestError | unknown {
protected handleHttpError(err: unknown): unknown {
if (err instanceof HttpErrorResponse) {
const error = new RequestError(
(isNotEmpty(err?.error?.message)) ? err.error.message : err.message,

View File

@@ -145,7 +145,7 @@ export class ItemEditBitstreamComponent implements OnChanges, OnDestroy, OnInit
* Check if a user should be allowed to cancel the update to this field
*/
canUndo(): boolean {
return this.fieldUpdate.changeType >= 0;
return this.fieldUpdate.changeType?.valueOf() >= 0;
}
}

View File

@@ -199,6 +199,6 @@ export class EditRelationshipComponent implements OnChanges {
* Check if a user should be allowed to cancel the update to this field
*/
canUndo(): boolean {
return this.fieldUpdate.changeType >= 0;
return this.fieldUpdate.changeType?.valueOf() >= 0;
}
}

View File

@@ -117,6 +117,8 @@ export class MetadataValuesComponent implements OnChanges {
*/
getQueryParams(value) {
const queryParams = { startsWith: value };
// todo: should compare with type instead?
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
if (this.browseDefinition.getRenderType() === VALUE_LIST_BROWSE_DEFINITION.value) {
return { value: value };
}

View File

@@ -155,7 +155,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() formModel: DynamicFormControlModel[];
@Input() asBootstrapFormGroup = false;
@Input() bindId = true;
@Input() context: any | null = null;
@Input() context: any = null;
@Input() group: UntypedFormGroup;
@Input() hostClass: string[];
@Input() hasErrorMessaging = false;

View File

@@ -96,6 +96,8 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement
this.initialDay = now.getUTCDate();
if (this.model && this.model.value !== null) {
// todo: model value could object or Date according to its type annotation
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const values = this.model.value.toString().split(DS_DATE_PICKER_SEPARATOR);
if (values.length > 0) {
this.initialYear = parseInt(values[0], 10);

View File

@@ -44,7 +44,7 @@ export class ConcatFieldParser extends FieldParser {
this.secondPlaceholder = secondPlaceholder;
}
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const id: string = this.configData.selectableMetadata[0].metadata;
const clsInput = {

View File

@@ -9,7 +9,7 @@ import { FieldParser } from './field-parser';
export class DateFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
let malformedDate = false;
const inputDateModelConfig: DynamicDsDateControlModelConfig = this.initModel(null, false, true);
inputDateModelConfig.legend = this.configData.label;
@@ -18,6 +18,8 @@ export class DateFieldParser extends FieldParser {
this.setValues(inputDateModelConfig as any, fieldValue);
// Init Data and validity check
if (isNotEmpty(inputDateModelConfig.value)) {
// todo: model value could be object or Date according to its type annotation
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const value = inputDateModelConfig.value.toString();
if (value.length >= 4) {
const valuesArray = value.split(DS_DATE_PICKER_SEPARATOR);

View File

@@ -10,7 +10,7 @@ import { FieldParser } from './field-parser';
*/
export class DisabledFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const emptyModelConfig: DsDynamicDisabledModelConfig = this.initModel(null, label);
this.setValues(emptyModelConfig, fieldValue, true);
return new DynamicDisabledModel(emptyModelConfig);

View File

@@ -28,7 +28,7 @@ export class DropdownFieldParser extends FieldParser {
super(submissionId, configData, initFormValues, parserOptions);
}
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const dropdownModelConfig: DynamicScrollableDropdownModelConfig = this.initModel(null, label);
let layout: DynamicFormControlLayout;

View File

@@ -68,8 +68,8 @@ export abstract class FieldParser {
public parse() {
if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable))
&& (this.configData.input.type !== ParserType.List)
&& (this.configData.input.type !== ParserType.Tag)
&& (this.configData.input.type !== ParserType.List.valueOf())
&& (this.configData.input.type !== ParserType.Tag.valueOf())
) {
let arrayCounter = 0;
let fieldArrayCounter = 0;
@@ -81,7 +81,7 @@ export abstract class FieldParser {
}
let isDraggable = true;
if (this.configData.input.type === ParserType.Onebox && this.configData?.selectableMetadata?.length > 1) {
if (this.configData.input.type === ParserType.Onebox.valueOf() && this.configData?.selectableMetadata?.length > 1) {
isDraggable = false;
}
const config = {
@@ -344,12 +344,12 @@ export abstract class FieldParser {
&& isNotEmpty(fieldScope)
&& isNotEmpty(visibility)
&& ((
submissionScope === SubmissionScopeType.WorkspaceItem
submissionScope === SubmissionScopeType.WorkspaceItem.valueOf()
&& visibility.main === VisibilityType.READONLY
)
||
(visibility.other === VisibilityType.READONLY
&& submissionScope === SubmissionScopeType.WorkflowItem
&& submissionScope === SubmissionScopeType.WorkflowItem.valueOf()
)
);
}

View File

@@ -6,7 +6,7 @@ import { FieldParser } from './field-parser';
export class ListFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const listModelConfig = this.initModel(null, label);
listModelConfig.repeatable = this.configData.repeatable;

View File

@@ -7,7 +7,7 @@ import { FieldParser } from './field-parser';
export class LookupFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
if (this.configData.selectableMetadata[0].controlledVocabulary) {
const lookupModelConfig: DynamicLookupModelConfig = this.initModel(null, label);

View File

@@ -7,7 +7,7 @@ import { FieldParser } from './field-parser';
export class LookupNameFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
if (this.configData.selectableMetadata[0].controlledVocabulary) {
const lookupModelConfig: DynamicLookupNameModelConfig = this.initModel(null, label);

View File

@@ -24,7 +24,7 @@ import { FieldParser } from './field-parser';
export class OneboxFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
if (this.configData.selectableMetadata.length > 1) {
// Case Qualdrop Model
const clsGroup = {

View File

@@ -11,7 +11,7 @@ import { FieldParser } from './field-parser';
export class RelationGroupFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean) {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean) {
const modelConfiguration: DynamicRelationGroupModelConfig = this.initModel(null, label);
modelConfiguration.submissionId = this.submissionId;

View File

@@ -154,9 +154,9 @@ export class RowParser {
&& (
isEmpty(visibility)
&& (
submissionScope === SubmissionScopeType.WorkspaceItem && scope !== SubmissionFieldScopeType.WorkspaceItem
submissionScope === SubmissionScopeType.WorkspaceItem.valueOf() && scope !== SubmissionFieldScopeType.WorkspaceItem.valueOf()
||
submissionScope === SubmissionScopeType.WorkflowItem && scope !== SubmissionFieldScopeType.WorkflowItem
submissionScope === SubmissionScopeType.WorkflowItem.valueOf() && scope !== SubmissionFieldScopeType.WorkflowItem.valueOf()
)
);
}

View File

@@ -7,7 +7,7 @@ import { FieldParser } from './field-parser';
export class TagFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const tagModelConfig: DynamicTagModelConfig = this.initModel(null, label);
if (this.configData.selectableMetadata[0].controlledVocabulary
&& this.configData.selectableMetadata[0].controlledVocabulary.length > 0) {

View File

@@ -8,7 +8,7 @@ import { FieldParser } from './field-parser';
export class TextareaFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
const textAreaModelConfig: DsDynamicTextAreaModelConfig = this.initModel(null, label);
const layout = {

View File

@@ -20,7 +20,7 @@ export function types(): string[] {
export function type<T>(label: T | ''): T {
if (typeCache[label as string]) {
throw new Error(`Action type "${label}" is not unique"`);
throw new Error(`Action type "${label as string}" is not unique"`);
}
typeCache[label as string] = true;

View File

@@ -22,6 +22,8 @@ export class BrowseLinkMetadataListElementComponent extends MetadataRepresentati
*/
getQueryParams() {
const queryParams = { startsWith: this.mdRepresentation.getValue() };
// todo: should compare with type instead?
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
if (this.mdRepresentation.browseDefinition.getRenderType() === VALUE_LIST_BROWSE_DEFINITION.value) {
return { value: this.mdRepresentation.getValue() };
}

View File

@@ -22,6 +22,8 @@ export class PlainTextMetadataListElementComponent extends MetadataRepresentatio
*/
getQueryParams() {
const queryParams = { startsWith: this.mdRepresentation.getValue() };
// todo: should compare with type instead?
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
if (this.mdRepresentation.browseDefinition.getRenderType() === VALUE_LIST_BROWSE_DEFINITION.value) {
return { value: this.mdRepresentation.getValue() };
}

View File

@@ -77,7 +77,7 @@ export class SubscriptionViewComponent {
*/
getPageRoutePrefix(): string {
let routePrefix;
switch (this.dso.type.toString()) {
switch (this.dso.type.value) {
case 'community':
routePrefix = getCommunityModuleRoute();
break;

View File

@@ -166,7 +166,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
*/
ngOnInit() {
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', 'collection');
this.available$ = this.sectionsService.isSectionTypeAvailable(this.submissionId, SectionsType.collection);
this.available$ = this.sectionsService.isSectionTypeAvailable(this.submissionId, SectionsType.Collection);
}
/**

View File

@@ -501,7 +501,7 @@ function getForm(forms, currentState, sectionId) {
* Whether notifications are enabled
*/
function filterErrors(sectionForm: FormState, sectionErrors: SubmissionSectionError[], sectionType: string, notify: boolean): SubmissionSectionError[] {
if (notify || sectionType !== SectionsType.SubmissionForm) {
if (notify || sectionType !== SectionsType.SubmissionForm.valueOf()) {
return sectionErrors;
}
if (!sectionForm || !sectionForm.touched) {

View File

@@ -92,7 +92,7 @@ export class SectionFormOperationsService {
* @return number
* the array index is part of array, zero otherwise
*/
public getArrayIndexFromEvent(event: DynamicFormControlEvent | any): number {
public getArrayIndexFromEvent(event: any): number {
let fieldIndex: number;
if (isNotEmpty(event)) {
@@ -110,7 +110,7 @@ export class SectionFormOperationsService {
} else {
// This is the case of a custom event which contains indexes information
fieldIndex = event.index as any;
fieldIndex = event?.index as any;
}
}

View File

@@ -310,10 +310,10 @@ export class SubmissionSectionFormComponent extends SectionModelComponent {
})?.fields?.[0]?.scope;
switch (scope) {
case SubmissionScopeType.WorkspaceItem: {
case SubmissionScopeType.WorkspaceItem.valueOf(): {
return (this.submissionObject as any).type === WorkspaceItem.type.value;
}
case SubmissionScopeType.WorkflowItem: {
case SubmissionScopeType.WorkflowItem.valueOf(): {
return (this.submissionObject as any).type === WorkflowItem.type.value;
}
default: {
@@ -340,8 +340,8 @@ export class SubmissionSectionFormComponent extends SectionModelComponent {
);
const sectionMetadata = this.sectionService.computeSectionConfiguredMetadata(this.formConfig);
this.sectionService.updateSectionData(this.submissionId, this.sectionData.id, sectionData, errorsToShow, serverValidationErrors, sectionMetadata);
} catch (e) {
const msg: string = this.translate.instant('error.submission.sections.init-form-error') + e.toString();
} catch (e: unknown) {
const msg: string = this.translate.instant('error.submission.sections.init-form-error') + (e as Error).toString();
const sectionError: SubmissionSectionError = {
message: msg,
path: '/sections/' + this.sectionData.id,

View File

@@ -4,7 +4,6 @@ export enum SectionsType {
Upload = 'upload',
License = 'license',
CcLicense = 'cclicense',
collection = 'collection',
AccessesCondition = 'accessCondition',
SherpaPolicies = 'sherpaPolicy',
Identifiers = 'identifiers',