Move to @pezkuwi/dev 0.86 (eslint 10 toolchain)

- @pezkuwi/dev, dev-test, dev-ts ^0.86.3 in the root and in every
  workspace package (they still asked for 0.84.3)
- tsconfig.base target ES2022, the @pezkuwi/dev default; it carries
  the ErrorOptions type for `cause`
- rethrown errors carry the original as `cause`; rejections are
  normalised to Error
- page/messaging: the typed subscriber is called directly, not as
  `Function`
- chrome mock: constant results through Promise.resolve
- useLedger: drop a construction every branch below repeated
- InputWithLabel: onKeyDown instead of the deprecated onKeyPress
- Account: toggleActions + 1 instead of a pre-increment of a parameter
- optional chains where a guard tested the same value
- specs: mount<React.Component> so the wrappers are not `any`-typed
- AccountsStore.set keeps its async override of KeyringStore.set:
  BaseStore.set handles its own failure, so it never rejects
- extension-chains keeps its ChainProperties assertion: the package's
  own build does not load the type augmentation
- the existing React Compiler findings (37, react-hooks rules only)
  are recorded in eslint-suppressions.json; a new one fails lint
This commit is contained in:
Satoshi Qazi Muhammed authored and SatoshiQaziMuhammed committed 2026-10-09 16:32:46 +04:00
1 parent 0321454dc7
commit 9d769676e4
58 files changed
+2795 -3355

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
{
"packages/extension-ui/src/Popup/Accounts/Account.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Accounts/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/CreateAccount/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Derive/DerivationPath.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Derive/SelectParent.tsx": {
"react-hooks/set-state-in-effect": {
"count": 3
}
},
"packages/extension-ui/src/Popup/ImportLedger.tsx": {
"react-hooks/refs": {
"count": 3
}
},
"packages/extension-ui/src/Popup/ImportSeed/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Signing/Extrinsic.tsx": {
"react-hooks/refs": {
"count": 3
}
},
"packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
},
"react-hooks/static-components": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Signing/Request/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/Signing/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/Popup/index.tsx": {
"react-hooks/set-state-in-effect": {
"count": 2
}
},
"packages/extension-ui/src/components/Address.tsx": {
"react-hooks/set-state-in-effect": {
"count": 3
},
"react-hooks/static-components": {
"count": 2
}
},
"packages/extension-ui/src/components/ErrorBoundary.tsx": {
"react-hooks/set-state-in-effect": {
"count": 2
}
},
"packages/extension-ui/src/components/ValidatedInput.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/hooks/useIsMounted.ts": {
"react-hooks/refs": {
"count": 1
}
},
"packages/extension-ui/src/hooks/useLedger.ts": {
"react-hooks/set-state-in-effect": {
"count": 1
},
"react-hooks/set-state-in-render": {
"count": 4
}
},
"packages/extension-ui/src/hooks/useMetadata.ts": {
"react-hooks/set-state-in-effect": {
"count": 1
}
},
"packages/extension-ui/src/partials/AccountSelection.tsx": {
"react-hooks/set-state-in-effect": {
"count": 2
}
}
}
+2 -2
View File
@@ -11,13 +11,13 @@ export default [
...baseConfig,
{
rules: {
'import/extensions': 'off'
'import-x/extensions': 'off'
}
},
{
files: ['**/*.spec.ts', '**/*.spec.tsx'],
rules: {
'deprecation/deprecation': 'off'
'@typescript-eslint/no-deprecated': 'off'
}
}
];
+1 -1
View File
@@ -47,7 +47,7 @@
"test:one": "EXTENSION_PREFIX='test' pezkuwi-dev-run-test --env browser"
},
"devDependencies": {
"@pezkuwi/dev": "^0.85.12",
"@pezkuwi/dev": "^0.86.3",
"@types/node": "^20.10.5",
"eslint-scope": "^5.1.1",
"i18next-scanner": "^4.4.0",
+1 -1
View File
@@ -39,7 +39,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@pezkuwi/dev-test": "^0.84.3",
"@pezkuwi/dev-test": "^0.86.3",
"@pezkuwi/extension-mocks": "0.62.27"
}
}
@@ -9,8 +9,8 @@ import type { Registry, SignerPayloadJSON, SignerPayloadRaw } from '@pezkuwi/typ
import type { SubjectInfo } from '@pezkuwi/ui-keyring/observable/types';
import type { KeypairType } from '@pezkuwi/util-crypto/types';
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestActiveTabsUrlUpdate, RequestAuthorizeApprove, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, RequestUpdateAuthorizedAccounts, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types.js';
import type { AuthorizedAccountsDiff } from './State.js';
import type State from './State.js';
import type { AuthorizedAccountsDiff } from './State.js';
import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@pezkuwi/extension-base/defaults';
import { metadataExpand } from '@pezkuwi/extension-chains';
@@ -296,7 +296,7 @@ export default class Extension {
try {
keyring.restoreAccount(file, password);
} catch (error) {
throw new Error((error as Error).message);
throw new Error((error as Error).message, { cause: error });
}
}
@@ -304,7 +304,7 @@ export default class Extension {
try {
keyring.restoreAccounts(file, password);
} catch (error) {
throw new Error((error as Error).message);
throw new Error((error as Error).message, { cause: error });
}
}
@@ -320,7 +320,7 @@ export default class Extension {
} as ResponseJsonGetAccountInfo;
} catch (e) {
console.error(e);
throw new Error((e as Error).message);
throw new Error((e as Error).message, { cause: e });
}
}
@@ -570,7 +570,6 @@ export default class Extension {
}
// Weird thought, the eslint override is not needed in Tabs
// eslint-disable-next-line @typescript-eslint/require-await
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], port?: chrome.runtime.Port): Promise<ResponseType<TMessageType>> {
switch (type) {
case 'pri(authorize.approve)':
@@ -30,7 +30,7 @@ interface AuthRequest extends Resolver<AuthResponse> {
url: string;
}
export type AuthorizedAccountsDiff = [url: string, authorizedAccounts: AuthUrlInfo['authorizedAccounts']][]
export type AuthorizedAccountsDiff = [url: string, authorizedAccounts: AuthUrlInfo['authorizedAccounts']][];
interface MetaRequest extends Resolver<boolean> {
id: string;
@@ -50,7 +50,7 @@ type Providers = Record<string, {
// The provider is not running at init, calling this will instantiate the
// provider.
start: () => ProviderInterface;
}>
}>;
interface SignRequest extends Resolver<ResponseSigning> {
account: AccountJson;
@@ -80,7 +80,7 @@ const NORMAL_WINDOW_OPTS: chrome.windows.CreateData = {
export enum NotificationOptions {
None,
Normal,
PopUp,
PopUp
}
const AUTH_URLS_KEY = 'authUrls';
@@ -518,7 +518,7 @@ export default class State {
return parsedUrl.origin;
} catch (e) {
console.error(e);
throw new Error('Invalid URL');
throw new Error('Invalid URL', { cause: e });
}
}
@@ -796,7 +796,7 @@ export default class State {
try {
this.handleSignRequest(url);
} catch (error) {
return Promise.reject(error);
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
}
return new Promise((resolve, reject): void => {
@@ -9,8 +9,8 @@ import type { JsonRpcResponse } from '@pezkuwi/rpc-provider/types';
import type { SignerPayloadJSON, SignerPayloadRaw } from '@pezkuwi/types/types';
import type { SubjectInfo } from '@pezkuwi/ui-keyring/observable/types';
import type { AuthUrlInfo, MessageTypes, RequestAccountList, RequestAccountUnsubscribe, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '../types.js';
import type { AuthResponse } from './State.js';
import type State from './State.js';
import type { AuthResponse } from './State.js';
import { combineLatest, type Subscription } from 'rxjs';
@@ -113,7 +113,7 @@ export default class Tabs {
private accountsUnsubscribe (url: string, { id }: RequestAccountUnsubscribe): boolean {
const sub = this.#accountSubs[id];
if (!sub || sub.url !== url) {
if (sub?.url !== url) {
return false;
}
@@ -1,8 +1,6 @@
// Copyright 2019-2026 @pezkuwi/extension-base authors & contributors
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable no-use-before-define */
import type { InjectedAccount, InjectedMetadataKnown, MetadataDef, ProviderList, ProviderMeta } from '@pezkuwi/extension-inject/types';
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@pezkuwi/keyring/types';
import type { JsonRpcResponse } from '@pezkuwi/rpc-provider/types';
@@ -45,7 +43,7 @@ export interface AccountJson extends KeyringPair$Meta {
export type AccountWithChildren = AccountJson & {
children?: AccountWithChildren[];
}
};
export interface AccountsContext {
accounts: AccountJson[];
@@ -74,7 +72,7 @@ export interface SigningRequest {
url: string;
}
export type ConnectedTabsUrlResponse = string[]
export type ConnectedTabsUrlResponse = string[];
// [MessageType]: [RequestType, ResponseType, SubscriptionMessageType?]
export interface RequestSignatures {
@@ -148,7 +146,7 @@ export type RequestTypes = {
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][0]
};
export type MessageTypesWithNullRequest = NullKeys<RequestTypes>
export type MessageTypesWithNullRequest = NullKeys<RequestTypes>;
export interface TransportRequestMessage<TMessageType extends MessageTypes> {
id: string;
@@ -396,7 +394,7 @@ export type SubscriptionMessageTypes = NoUndefinedValues<{
}>;
export type MessageTypesWithSubscriptions = keyof SubscriptionMessageTypes;
export type MessageTypesWithNoSubscriptions = Exclude<MessageTypes, keyof SubscriptionMessageTypes>
export type MessageTypesWithNoSubscriptions = Exclude<MessageTypes, keyof SubscriptionMessageTypes>;
export interface RequestSign {
readonly payload: SignerPayloadJSON | SignerPayloadRaw;
+4 -5
View File
@@ -30,9 +30,9 @@ const handlers: Handlers = {};
// a generic message sender that creates an event, returning a promise that will
// resolve once the event is resolved (by the response listener just below this)
export function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithNullRequest> (message: TMessageType): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions> (message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithSubscriptions> (message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise<ResponseTypes[TMessageType]> {
return new Promise((resolve, reject): void => {
@@ -79,8 +79,7 @@ export function handleResponse<TMessageType extends MessageTypes> (data: Transpo
}
if (data.subscription) {
// eslint-disable-next-line @typescript-eslint/ban-types
(handler.subscriber as Function)(data.subscription);
handler.subscriber?.(data.subscription);
} else if (data.error) {
handler.reject(new Error(data.error));
} else {
@@ -15,10 +15,13 @@ export default class AccountsStore extends BaseStore<KeyringJson> implements Key
);
}
// KeyringStore.set returns void and the keyring does not wait on it;
// BaseStore.set handles its own failure, so this promise never rejects.
// eslint-disable-next-line @typescript-eslint/no-misused-promises
public override async set (key: string, value: KeyringJson, update?: () => void): Promise<void> {
// shortcut, don't save testing accounts in extension storage
if (key.startsWith('account:') && value.meta && value.meta.isTesting) {
update && update();
if (key.startsWith('account:') && value.meta?.isTesting) {
update?.();
return;
}
+5 -1
View File
@@ -22,7 +22,7 @@ const expanded = new Map<string, Chain>();
export function metadataExpand (definition: MetadataDef, isPartial = false): Chain {
const cached = expanded.get(definition.genesisHash);
if (cached && cached.specVersion === definition.specVersion) {
if (cached?.specVersion === definition.specVersion) {
return cached;
}
@@ -33,6 +33,10 @@ export function metadataExpand (definition: MetadataDef, isPartial = false): Cha
registry.register(types);
}
// Needed in this package's own build, where the type augmentation is not
// loaded and createType returns a plain Codec; the repository-wide lint sees
// it augmented.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
registry.setChainProperties(registry.createType('ChainProperties', {
ss58Format,
tokenDecimals,
+1 -1
View File
@@ -27,7 +27,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@pezkuwi/dev-test": "^0.84.3"
"@pezkuwi/dev-test": "^0.86.3"
},
"peerDependencies": {
"@pezkuwi/api": "*",
+1 -1
View File
@@ -27,7 +27,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@pezkuwi/dev-test": "^0.84.3",
"@pezkuwi/dev-test": "^0.86.3",
"@types/chrome": "^0.0.254",
"@types/firefox-webext-browser": "^111.0.5"
},
+1 -2
View File
@@ -7,7 +7,6 @@ import type { ExtDef } from '@pezkuwi/types/extrinsic/signedExtensions/types';
import type { HexString } from '@pezkuwi/util/types';
import type { KeypairType } from '@pezkuwi/util-crypto/types';
// eslint-disable-next-line no-undef
type This = typeof globalThis;
export type Unsubcall = () => void;
@@ -80,7 +79,7 @@ export interface InjectedMetadata {
provide: (definition: MetadataDef) => Promise<boolean>;
}
export type ProviderList = Record<string, ProviderMeta>
export type ProviderList = Record<string, ProviderMeta>;
export interface InjectedProvider extends ProviderInterface {
listProviders: () => Promise<ProviderList>;
+12 -27
View File
@@ -29,36 +29,21 @@ const messagingFake = new MessagingFake();
chrome.runtime.connect.returns(messagingFake);
chrome.storage.local.get.returns(
new Promise<Record<string, any>>((resolve, reject) => {
try {
const result = {
authUrls: JSON.stringify({
'http://localhost:3000': {
authorizedAccounts: ['5FbSap4BsWfjyRhCchoVdZHkDnmDm3NEgLZ25mesq4aw2WvX'],
count: 0,
id: '11',
origin: 'example.com',
url: 'http://localhost:3000'
}
})
};
resolve(result);
} catch (error) {
reject(error);
}
Promise.resolve<Record<string, any>>({
authUrls: JSON.stringify({
'http://localhost:3000': {
authorizedAccounts: ['5FbSap4BsWfjyRhCchoVdZHkDnmDm3NEgLZ25mesq4aw2WvX'],
count: 0,
id: '11',
origin: 'example.com',
url: 'http://localhost:3000'
}
})
}));
chrome.storage.local.set.returns(
new Promise<void>((resolve, reject) => {
try {
resolve();
} catch (error) {
reject(error);
}
}));
chrome.storage.local.set.returns(Promise.resolve());
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(window as any).chrome = (globalThis as any).chrome = chrome;
export default chrome;
@@ -1,5 +1,4 @@
// Copyright 2019-2026 @pezkuwi/extension-mocks authors & contributors
// SPDX-License-Identifier: Apache-2.0
// eslint-disable-line
module.exports = '';
+1 -1
View File
@@ -56,7 +56,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@pezkuwi/dev-test": "^0.84.3",
"@pezkuwi/dev-test": "^0.86.3",
"@pezkuwi/extension-mocks": "0.62.27",
"@types/enzyme": "^3.10.18",
"@types/enzyme-adapter-react-16": "^1.0.9",
@@ -25,7 +25,7 @@ const { configure, mount } = enzyme;
// ...jest.requireActual('../../messaging')
// }));
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
jest.spyOn(messaging, 'getAllMetadata').mockImplementation(() => Promise.resolve([]));
@@ -45,7 +45,7 @@ function Account ({ address, className, genesisHash, isExternal, isHardware, isH
? selectedAccounts.filter((account) => account !== address)
: [...selectedAccounts, address];
setSelectedAccounts && setSelectedAccounts(newList);
setSelectedAccounts?.(newList);
}, [address, selectedAccounts, setSelectedAccounts]);
const _onChangeGenesis = useCallback(
@@ -57,7 +57,7 @@ function Account ({ address, className, genesisHash, isExternal, isHardware, isH
);
const _toggleEdit = useCallback(
(): void => setEditing(({ toggleActions }) => ({ isEditing: !isEditing, toggleActions: ++toggleActions })),
(): void => setEditing(({ toggleActions }) => ({ isEditing: !isEditing, toggleActions: toggleActions + 1 })),
[isEditing]
);
@@ -4,8 +4,8 @@
// We _could_ reformat, but just keep it as-is, since this is actually
// externally generated and not really user-editable
/* eslint-disable react/jsx-sort-props */
/* eslint-disable react/jsx-max-props-per-line */
/* eslint-disable @stylistic/jsx-sort-props */
/* eslint-disable @stylistic/jsx-max-props-per-line */
import React from 'react';
@@ -15,7 +15,7 @@ interface Props {
}
function AccountManagement ({ className }: Props): React.ReactElement<Props> {
const { url } = useParams<{url: string}>();
const { url } = useParams<{ url: string }>();
const decodedUrl = decodeURIComponent(url);
const { selectedAccounts = [], setSelectedAccounts } = useContext(AccountContext);
const { t } = useTranslation();
@@ -28,7 +28,7 @@ function AccountManagement ({ className }: Props): React.ReactElement<Props> {
return;
}
setSelectedAccounts && setSelectedAccounts(list[decodedUrl].authorizedAccounts);
setSelectedAccounts?.(list[decodedUrl].authorizedAccounts);
})
.catch(console.error);
}, [setSelectedAccounts, decodedUrl]);
@@ -20,7 +20,7 @@ import Request from './Request.js';
const { configure, mount } = enzyme;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
const oneRequest = [{ id: '1', request: { origin: '???' }, url: 'http://pezkuwichain.io' }];
@@ -30,7 +30,7 @@ function Request ({ authId, className, request: { origin }, url }: Props): React
.filter(({ isDefaultAuthSelected }) => !!isDefaultAuthSelected)
.map(({ address }) => address);
setSelectedAccounts && setSelectedAccounts(defaultAccountSelection);
setSelectedAccounts?.(defaultAccountSelection);
}, [accounts, setSelectedAccounts]);
const _onApprove = useCallback(
@@ -29,7 +29,7 @@ const { configure, mount } = enzyme;
// For this file, there are a lot of them
/* eslint-disable @typescript-eslint/no-unsafe-argument */
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
describe('Create Account', () => {
@@ -38,12 +38,11 @@ function CreateAccount ({ className }: Props): React.ReactElement {
setSeed(seed);
})
.catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect((): void => {
if (seed) {
const type = chain && chain.definition.chainType === 'ethereum'
const type = chain?.definition.chainType === 'ethereum'
? 'ethereum'
: DEFAULT_TYPE;
@@ -5,7 +5,7 @@ import '@pezkuwi/extension-mocks/chrome';
import type { ReactWrapper } from 'enzyme';
import type * as _ from '@pezkuwi/dev-test/globals.d.ts';
import type { AccountJson, ResponseDeriveValidate } from '@pezkuwi/extension-base/background/types';
import type { AccountJson } from '@pezkuwi/extension-base/background/types';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import enzyme from 'enzyme';
@@ -32,7 +32,7 @@ const { configure, mount } = enzyme;
// For this file, there are a lot of them
/* eslint-disable @typescript-eslint/no-unsafe-argument */
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
const parentPassword = 'pass';
@@ -57,7 +57,7 @@ describe('Derive', () => {
}> => {
const onActionStub = jest.fn();
const wrapper = mount(
const wrapper = mount<React.Component>(
<MemoryRouter initialEntries={ [`/account/derive/${accounts[account].address}`] }>
<ActionContext.Provider value={onActionStub}>
<AccountContext.Provider
@@ -110,7 +110,7 @@ describe('Derive', () => {
throw new Error('wrong suri');
}
return { address: derivedAddress, suri: defaultDerivation } as ResponseDeriveValidate;
return { address: derivedAddress, suri: defaultDerivation };
});
it('Button is disabled and password field visible, path field is hidden', () => {
@@ -216,7 +216,7 @@ describe('Derive', () => {
expect(button.prop('disabled')).toBe(true);
expect(wrapper.find('.warning-message')).toHaveLength(1);
// eslint-disable-next-line quotes
// eslint-disable-next-line @stylistic/quotes
expect(wrapper.find('.warning-message').first().text()).toEqual("`///password` not supported for derivation");
});
@@ -27,7 +27,7 @@ const { configure, mount } = enzyme;
// ...jest.requireActual('../messaging')
// }));
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
describe('Export component', () => {
+4 -4
View File
@@ -15,7 +15,7 @@ import { styled } from '../styled.js';
const MIN_LENGTH = 6;
interface Props extends RouteComponentProps<{address: string}> {
interface Props extends RouteComponentProps<{ address: string }> {
className?: string;
}
@@ -35,8 +35,8 @@ function Export ({ className, match: { params: { address } } }: Props): React.Re
(password: string) => {
setPass(password);
setError('');
}
, []);
},
[]);
const _onExportButtonClick = useCallback(
(): void => {
@@ -46,7 +46,7 @@ function Export ({ className, match: { params: { address } } }: Props): React.Re
.then(({ exportedJson }) => {
const blob = new Blob([JSON.stringify(exportedJson)], { type: 'application/json; charset=utf-8' });
// eslint-disable-next-line deprecation/deprecation
// eslint-disable-next-line @typescript-eslint/no-deprecated
fileSaver.saveAs(blob, `${address}.json`);
onAction('/');
@@ -36,8 +36,8 @@ function ExportAll ({ className }: Props): React.ReactElement<Props> {
(password: string) => {
setPass(password);
setError('');
}
, []);
},
[]);
const _onExportAllButtonClick = useCallback(
(): void => {
@@ -47,7 +47,7 @@ function ExportAll ({ className }: Props): React.ReactElement<Props> {
.then(({ exportedJson }) => {
const blob = new Blob([JSON.stringify(exportedJson)], { type: 'application/json; charset=utf-8' });
// eslint-disable-next-line deprecation/deprecation
// eslint-disable-next-line @typescript-eslint/no-deprecated
fileSaver.saveAs(blob, `batch_exported_account_${Date.now()}.json`);
onAction('/');
@@ -49,7 +49,7 @@ interface QrScanAddressProps {
// ...jest.requireActual('../messaging')
// }));
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
const typeName = async (wrapper: ReactWrapper, value: string) => {
@@ -77,7 +77,6 @@ describe('ImportQr component', () => {
</MemoryRouter>
);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
(wrapper.find('QrScanAddress').first().prop('onScan') as unknown as QrScanAddressProps['onScan'])(mockedAccount);
@@ -38,7 +38,7 @@ const account = {
// For this file, there are a lot of them
/* eslint-disable @typescript-eslint/no-unsafe-argument */
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
jest.spyOn(messaging, 'getAllMetadata').mockImplementation(() => Promise.resolve([]));
@@ -36,7 +36,7 @@ function ImportSeed (): React.ReactElement {
useEffect((): void => {
setType(
chain && chain.definition.chainType === 'ethereum'
chain?.definition.chainType === 'ethereum'
? 'ethereum'
: DEFAULT_TYPE
);
@@ -120,7 +120,7 @@ function Extrinsic ({ className, payload, request: { blockNumber, genesisHash, m
const chain = useMetadata(genesisHash);
const specVersion = useRef(bnToBn(hexSpec)).current;
const decoded = useMemo(
() => chain && chain.hasMetadata
() => chain?.hasMetadata
? decodeMethod(method, chain, specVersion)
: { args: null, method: null },
[method, chain, specVersion]
@@ -1,7 +1,7 @@
// Copyright 2019-2026 @pezkuwi/extension-ui authors & contributors
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable deprecation/deprecation */
/* eslint-disable @typescript-eslint/no-deprecated */
import type { Chain } from '@pezkuwi/extension-chains/types';
import type { Ledger, LedgerGeneric } from '@pezkuwi/hw-ledger';
@@ -41,7 +41,7 @@ const { configure, mount } = enzyme;
// For this file, there are a lot of them
/* eslint-disable @typescript-eslint/no-unsafe-argument */
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
describe('Signing requests', () => {
@@ -345,7 +345,6 @@ describe('Signing requests', () => {
wrapper.find('FontAwesomeIcon.arrowRight').simulate('click');
await act(flushAllPromises);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => {
emitter.emit('request', [signRequests[0]]);
});
+4
View File
@@ -3,20 +3,24 @@
declare module '*.svg' {
const url: string;
export default url;
}
declare module '*.png' {
const url: string;
export default url;
}
declare module '*.woff' {
const url: string;
export default url;
}
declare module '*.woff2' {
const url: string;
export default url;
}
@@ -20,7 +20,7 @@ import { AccountNamePasswordCreation, Input, InputWithLabel, NextStepButton } fr
const { configure, mount } = enzyme;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
const account = {
@@ -40,7 +40,7 @@ function AccountNamePasswordCreation ({ buttonLabel, isBusy, onBackClick, onCrea
const _onPasswordChange = useCallback(
(password: string | null) => {
onPasswordChange && onPasswordChange(password || '');
onPasswordChange?.(password || '');
setPassword(password);
},
[onPasswordChange]
@@ -50,7 +50,7 @@ function AccountNamePasswordCreation ({ buttonLabel, isBusy, onBackClick, onCrea
() => {
_onNameChange(null);
setPassword(null);
onBackClick && onBackClick();
onBackClick?.();
},
[_onNameChange, onBackClick]
);
@@ -38,7 +38,7 @@ const { configure, mount } = enzyme;
// ...jest.requireActual('../MetadataCache')
// }));
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
interface AccountTestJson extends AccountJson {
@@ -121,7 +121,7 @@ const mountComponent = async (addressComponentProps: AddressComponentProps, cont
const actionStub = jest.fn();
const { actions = actionStub } = addressComponentProps;
const wrapper = mount(
const wrapper = mount<React.Component>(
<AccountContext.Provider
value={{
accounts: contextAccounts,
@@ -25,12 +25,12 @@ function Checkbox ({ checked, className, indeterminate, label, onChange, onClick
}, [indeterminate]);
const _onChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => onChange && onChange(event.target.checked),
(event: React.ChangeEvent<HTMLInputElement>) => onChange?.(event.target.checked),
[onChange]
);
const _onClick = useCallback(
() => onClick && onClick(),
() => onClick?.(),
[onClick]
);
@@ -29,7 +29,7 @@ interface Props {
function Dropdown ({ className, defaultValue, isDisabled, isFocussed, label, onBlur, onChange, options, value }: Props): React.ReactElement<Props> {
const _onChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLSelectElement>) =>
onChange && onChange(value.trim()),
onChange?.(value.trim()),
[onChange]
);
@@ -77,7 +77,7 @@ function InputFile ({ accept, className = '', clearContent, convertHex, isDisabl
const name = file.name;
const data = convertResult(target.result as ArrayBuffer, convertHex);
onChange && onChange(data, name);
onChange?.(data, name);
dropRef && setFile({
name,
size: data.length
@@ -25,7 +25,7 @@ function InputFilter ({ className, onChange, placeholder, value, withReset = fal
const onResetFilter = useCallback(() => {
onChange('');
inputRef?.current && inputRef.current.select();
inputRef?.current?.select();
}, [onChange]);
return (
@@ -48,7 +48,7 @@ function InputWithLabel ({ className, defaultValue, disabled, isError, isFocused
const _onChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
onChange && onChange(value);
onChange?.(value);
},
[onChange]
);
@@ -67,7 +67,7 @@ function InputWithLabel ({ className, defaultValue, disabled, isError, isFocused
onBlur={onBlur}
onChange={_onChange}
onFocus={onFocus}
onKeyPress={_checkKey}
onKeyDown={_checkKey}
placeholder={placeholder}
readOnly={isReadOnly}
spellCheck={false}
@@ -20,7 +20,7 @@ interface Props {
export default function TextAreaWithLabel ({ className, isError, isFocused, isReadOnly, label, onChange, rowsCount, value }: Props): React.ReactElement<Props> {
const _onChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLTextAreaElement>): void => {
onChange && onChange(value);
onChange?.(value);
},
[onChange]
);
@@ -23,7 +23,7 @@ type Props<T extends BasicProps> = T & {
defaultValue?: string;
onValidatedChange: (value: string | null) => void;
validator: Validator<string>;
}
};
function ValidatedInput<T extends Record<string, unknown>> ({ className, component: Input, defaultValue, onValidatedChange, validator, ...props }: Props<T>): React.ReactElement<Props<T>> {
const [value, setValue] = useState(defaultValue || '');
@@ -19,6 +19,6 @@ const MetadataReqContext = React.createContext<MetadataRequest[]>([]);
const SettingsContext = React.createContext<SettingsStruct>(settings.get());
const SigningReqContext = React.createContext<SigningRequest[]>([]);
const ThemeSwitchContext = React.createContext<(theme: Theme) => void>(noop);
const ToastContext = React.createContext<({show: (message: string) => void})>({ show: noop });
const ToastContext = React.createContext<({ show: (message: string) => void })>({ show: noop });
export { AccountContext, ActionContext, AuthorizeReqContext, MediaContext, MetadataReqContext, SettingsContext, SigningReqContext, ThemeSwitchContext, ToastContext };
@@ -14,7 +14,7 @@ export function chooseTheme (): Theme {
: 'light';
}
return window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches
return window.matchMedia?.('(prefers-color-scheme: light)').matches
? 'light'
: 'dark';
}
+2 -6
View File
@@ -3,7 +3,7 @@
// This is to ensure the legacy `class Ledger` doesn't throw linting errors.
//
/* eslint-disable deprecation/deprecation */
/* eslint-disable @typescript-eslint/no-deprecated */
import type { Network } from '@pezkuwi/networks/types';
import type { HexString } from '@pezkuwi/util/types';
@@ -50,7 +50,7 @@ function getState (): StateBase {
}
function retrieveLedger (genesis: string): LedgerGeneric | Ledger {
let ledger: LedgerGeneric | Ledger | null = null;
let ledger: LedgerGeneric | Ledger;
const currApp = settings.get().ledgerApp;
@@ -64,10 +64,6 @@ function retrieveLedger (genesis: string): LedgerGeneric | Ledger {
assert(def.slip44, 'Slip44 is not available for this network, please report an issue to update this chains slip44');
// All chains use the `slip44` from polkadot in their derivation path in ledger.
// This interface is specific to the underlying PolkadotGenericApp.
ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']);
if (currApp === 'generic') {
// All chains use the `slip44` from polkadot in their derivation path in ledger.
// This interface is specific to the underlying PolkadotGenericApp.
@@ -5,7 +5,7 @@ import type { RefObject } from 'react';
import { useCallback, useEffect } from 'react';
export default function useOutsideClick (refs: RefObject<HTMLDivElement|null>[], callback: () => void): void {
export default function useOutsideClick (refs: RefObject<HTMLDivElement | null>[], callback: () => void): void {
const handleClick = useCallback((e: MouseEvent): void => {
refs.every(({ current }) =>
current &&
+1 -1
View File
@@ -5,6 +5,6 @@ import { useContext } from 'react';
import { ToastContext } from '../components/contexts.js';
export default function useToast (): {show: (message: string) => void} {
export default function useToast (): { show: (message: string) => void } {
return useContext(ToastContext);
}
+6 -6
View File
@@ -49,8 +49,7 @@ function onPortMessageHandler (data: Message['data']): void {
}
if (data.subscription) {
// eslint-disable-next-line @typescript-eslint/ban-types
(handler.subscriber as Function)(data.subscription);
handler.subscriber?.(data.subscription);
} else if (data.error) {
handler.reject(new Error(data.error));
} else {
@@ -68,9 +67,10 @@ const portConfig = {
portName: PORT_EXTENSION
};
function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithNullRequest> (message: TMessageType): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions> (message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithSubscriptions> (message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise<ResponseTypes[TMessageType]> {
return new Promise((resolve, reject): void => {
const id = getId();
@@ -82,7 +82,7 @@ function sendMessage<TMessageType extends MessageTypes> (message: TMessageType,
port = connectedPort;
}).catch((error) => {
console.error(`Failed to send message: ${(error as Error).message}`);
reject(error);
reject(error instanceof Error ? error : new Error(String(error)));
});
});
}
@@ -30,12 +30,12 @@ function AccountSelection ({ className, origin, showHidden = false, url, withWar
const allDisplayedAddresses = useMemo(
() => showHidden
? accounts.map(({ address }) => address)
: allVisibleAccounts.map(({ address }) => address)
, [accounts, allVisibleAccounts, showHidden]
: allVisibleAccounts.map(({ address }) => address),
[accounts, allVisibleAccounts, showHidden]
);
const areAllAccountsSelected = useMemo(
() => selectedAccounts.length === allDisplayedAddresses.length
, [allDisplayedAddresses.length, selectedAccounts.length]
() => selectedAccounts.length === allDisplayedAddresses.length,
[allDisplayedAddresses.length, selectedAccounts.length]
);
useEffect(() => {
@@ -54,12 +54,12 @@ function AccountSelection ({ className, origin, showHidden = false, url, withWar
const _onSelectAllToggle = useCallback(() => {
if (areAllAccountsSelected) {
setSelectedAccounts && setSelectedAccounts([]);
setSelectedAccounts?.([]);
return;
}
setSelectedAccounts && setSelectedAccounts(allDisplayedAddresses);
setSelectedAccounts?.(allDisplayedAddresses);
}, [allDisplayedAddresses, areAllAccountsSelected, setSelectedAccounts]
);
@@ -16,7 +16,7 @@ import Settings from './MenuSettings.js';
const { configure, mount } = enzyme;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
configure({ adapter: new Adapter() });
describe('Header component', () => {
@@ -39,8 +39,8 @@ function Header ({ children, className = '', onFilter, showAdd, showBackArrow, s
const addMenuRef = useRef<HTMLDivElement>(null);
const setIconRef = useRef(null);
const setMenuRef = useRef<HTMLDivElement>(null);
const isConnected = useMemo(() => connectedTabsUrl.length >= 1
, [connectedTabsUrl]);
const isConnected = useMemo(() => connectedTabsUrl.length >= 1,
[connectedTabsUrl]);
const onAction = useContext(ActionContext);
useEffect(() => {
@@ -74,7 +74,7 @@ function Header ({ children, className = '', onFilter, showAdd, showBackArrow, s
const _onChangeFilter = useCallback(
(filter: string) => {
setFilter(filter);
onFilter && onFilter(filter);
onFilter?.(filter);
},
[onFilter]
);
@@ -91,8 +91,8 @@ function Header ({ children, className = '', onFilter, showAdd, showBackArrow, s
);
const _onBackArrowClick = useCallback(
() => onAction('../index.js')
, [onAction]);
() => onAction('../index.js'),
[onAction]);
return (
<div className={`${className} ${smallMargin ? 'smallMargin' : ''}`}>
+1 -1
View File
@@ -27,7 +27,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@pezkuwi/dev": "^0.84.3",
"@pezkuwi/dev": "^0.86.3",
"browser-resolve": "^2.0.0",
"buffer": "^6.0.3",
"copy-webpack-plugin": "^11.0.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"extends": "@pezkuwi/dev/config/tsconfig.json",
"compilerOptions": {
"composite": true,
"target": "ES2020",
"target": "ES2022",
/* FIXME Dropzone is problematic with nodenext resolution */
"module": "ESNext",
"moduleResolution": "Bundler",
+2566 -3215
View File
File diff suppressed because it is too large. Load diff