diff --git a/package.json b/package.json index 47840b1..5ba611d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "build": "npm run build --workspaces", "lint": "npm run lint:eslint && npm run lint:misc --check", - "lint:eslint": "eslint . --cache --ext js,ts", + "lint:eslint": "npm run lint:eslint --workspace=site && npm run lint:eslint --workspace=@xmtp/snap", "lint:fix": "npm run lint:eslint --fix && npm run lint:misc --write", "lint:misc": "prettier '**/*.md' '!**/CHANGELOG.md' '**/*.yml' --ignore-path .gitignore", "start": "concurrently \"npm start --workspace=site\" \"npm start --workspace=@xmtp/snap\"", diff --git a/packages/site/.eslintrc.js b/packages/site/.eslintrc.js index 99910b3..268a79f 100644 --- a/packages/site/.eslintrc.js +++ b/packages/site/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { extends: ['../../.eslintrc.js'], - + parserOptions: { + project: './tsconfig.eslint.json', + }, overrides: [ { files: ['**/*.{ts,tsx}'], @@ -9,6 +11,5 @@ module.exports = { }, }, ], - ignorePatterns: ['!.eslintrc.js', 'build/'], }; diff --git a/packages/site/gatsby-config.ts b/packages/site/gatsby-config.ts index 7af8bf1..59d192a 100644 --- a/packages/site/gatsby-config.ts +++ b/packages/site/gatsby-config.ts @@ -1,8 +1,9 @@ -import { GatsbyConfig } from 'gatsby'; import dotenv from 'dotenv'; +import type { GatsbyConfig } from 'gatsby'; dotenv.config({ - path: `.env.${process.env.NODE_ENV}`, + // eslint-disable-next-line no-restricted-globals, @typescript-eslint/no-non-null-assertion + path: `.env.${process.env.NODE_ENV!}`, }); const config: GatsbyConfig = { @@ -17,7 +18,9 @@ const config: GatsbyConfig = { options: { name: 'Template Snap', icon: 'src/assets/logo.svg', + // eslint-disable-next-line @typescript-eslint/naming-convention theme_color: '#6F4CFF', + // eslint-disable-next-line @typescript-eslint/naming-convention background_color: '#FFFFFF', display: 'standalone', }, diff --git a/packages/site/src/config/snap.ts b/packages/site/src/config/snap.ts index bfd349f..9ffb011 100644 --- a/packages/site/src/config/snap.ts +++ b/packages/site/src/config/snap.ts @@ -3,9 +3,11 @@ * Will default to the local hosted snap if no value is provided in environment. */ export const defaultSnapOrigin = + // eslint-disable-next-line no-restricted-globals process.env.SNAP_ORIGIN ?? `local:http://localhost:8080`; export const getSnapParams = () => { + // eslint-disable-next-line no-restricted-globals const envSnapVersion = process.env.SNAP_VERSION; if (defaultSnapOrigin.startsWith('npm:') && envSnapVersion) { return { version: envSnapVersion }; diff --git a/packages/site/src/polyfills.ts b/packages/site/src/polyfills.ts index bc106d4..441ee1e 100644 --- a/packages/site/src/polyfills.ts +++ b/packages/site/src/polyfills.ts @@ -1,5 +1,8 @@ +// eslint-disable-next-line import/no-nodejs-modules import { Buffer } from 'buffer'; +// eslint-disable-next-line no-restricted-globals if (typeof window !== 'undefined') { + // eslint-disable-next-line no-restricted-globals window.Buffer = window.Buffer ?? Buffer; } diff --git a/packages/site/src/utils/localStorage.ts b/packages/site/src/utils/localStorage.ts index 7bff069..390c4dc 100644 --- a/packages/site/src/utils/localStorage.ts +++ b/packages/site/src/utils/localStorage.ts @@ -4,6 +4,7 @@ * @returns The value stored at the key provided if the key exists. */ export const getLocalStorage = (key: string) => { + // eslint-disable-next-line no-restricted-globals const { localStorage: ls } = window; if (ls !== null) { @@ -20,6 +21,7 @@ export const getLocalStorage = (key: string) => { * @param value - The value to set. */ export const setLocalStorage = (key: string, value: string) => { + // eslint-disable-next-line no-restricted-globals const { localStorage: ls } = window; if (ls !== null) { diff --git a/packages/site/src/utils/metamask.ts b/packages/site/src/utils/metamask.ts index 05f2f52..0eefe01 100644 --- a/packages/site/src/utils/metamask.ts +++ b/packages/site/src/utils/metamask.ts @@ -4,6 +4,7 @@ import { ethers } from 'ethers'; * @returns True if the MetaMask version is Flask, false otherwise. */ export const isFlask = async () => { + // eslint-disable-next-line no-restricted-globals const provider = window.ethereum; try { const clientVersion = await provider?.request({ @@ -19,9 +20,11 @@ export const isFlask = async () => { }; export const getSigner = async () => { + // eslint-disable-next-line no-restricted-globals const provider = new ethers.BrowserProvider(window.ethereum); // Request account access if needed + // eslint-disable-next-line no-restricted-globals await window.ethereum.request({ method: 'eth_requestAccounts' }); // Getting the signer from provider diff --git a/packages/site/src/utils/snap.ts b/packages/site/src/utils/snap.ts index 352280a..5554c21 100644 --- a/packages/site/src/utils/snap.ts +++ b/packages/site/src/utils/snap.ts @@ -7,6 +7,7 @@ import type { GetSnapsResponse, Snap } from '../types'; * @returns The snaps installed in MetaMask. */ export const getSnaps = async (): Promise => { + // eslint-disable-next-line no-restricted-globals return (await window.ethereum.request({ method: 'wallet_getSnaps', })) as unknown as GetSnapsResponse; @@ -21,6 +22,7 @@ export const connectSnap = async ( snapId: string = defaultSnapOrigin, params: Record<'version' | string, unknown> = {}, ) => { + // eslint-disable-next-line no-restricted-globals await window.ethereum.request({ method: 'wallet_requestSnaps', params: { @@ -42,8 +44,8 @@ export const getSnap = async (version?: string): Promise => { (snap) => snap.id === defaultSnapOrigin && (!version || snap.version === version), ); - } catch (e) { - console.log('Failed to obtain installed snap', e); + } catch (error) { + console.log('Failed to obtain installed snap', error); return undefined; } }; diff --git a/packages/site/src/utils/theme.ts b/packages/site/src/utils/theme.ts index 859fa70..b4d761a 100644 --- a/packages/site/src/utils/theme.ts +++ b/packages/site/src/utils/theme.ts @@ -6,10 +6,12 @@ import { getLocalStorage, setLocalStorage } from './localStorage'; * @returns True if the theme is "dark" otherwise, false. */ export const getThemePreference = () => { + // eslint-disable-next-line no-restricted-globals if (typeof window === 'undefined') { return false; } + // eslint-disable-next-line no-restricted-globals const darkModeSystem = window?.matchMedia( '(prefers-color-scheme: dark)', ).matches; diff --git a/packages/site/tsconfig.eslint.json b/packages/site/tsconfig.eslint.json new file mode 100644 index 0000000..ec845b8 --- /dev/null +++ b/packages/site/tsconfig.eslint.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": [".", ".eslintrc.js"], + "exclude": ["public", "node_modules"] +} diff --git a/packages/snap/.eslintrc.js b/packages/snap/.eslintrc.js index 73938b6..a8937d0 100644 --- a/packages/snap/.eslintrc.js +++ b/packages/snap/.eslintrc.js @@ -1,5 +1,7 @@ module.exports = { extends: ['../../.eslintrc.js'], - ignorePatterns: ['!.eslintrc.js', 'dist/'], + parserOptions: { + project: './tsconfig.eslint.json', + }, }; diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 0964531..cd8cdb3 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/xmtp/snap.git" }, "source": { - "shasum": "r9PCkPbktkhs3W1SdRYzRRLbbBvVviJwF8NWaMOAPkU=", + "shasum": "5SJLhbSi9AD3AfeY+aKYpHlMuqj7sWpYM27NePYm1Wo=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/authorizer.test.ts b/packages/snap/src/authorizer.test.ts index f096d14..ece5c36 100644 --- a/packages/snap/src/authorizer.test.ts +++ b/packages/snap/src/authorizer.test.ts @@ -1,7 +1,8 @@ -import { XmtpEnv } from '@xmtp/xmtp-js'; +import type { XmtpEnv } from '@xmtp/xmtp-js'; + import { Authorizer } from './authorizer'; -import { sleep } from './testHelpers'; import { AUTHORIZATION_EXPIRY_MS } from './config'; +import { sleep } from './testHelpers'; const WALLET_ADDRESS = '0x1234'; const ENV = 'dev' as XmtpEnv; @@ -32,6 +33,7 @@ describe('Authorizer', () => { throw new Error('unknown operation'); }); + // eslint-disable-next-line no-restricted-globals (global as any).snap = { request: mockRequest, }; diff --git a/packages/snap/src/authorizer.ts b/packages/snap/src/authorizer.ts index a74d03a..7e9f291 100644 --- a/packages/snap/src/authorizer.ts +++ b/packages/snap/src/authorizer.ts @@ -34,11 +34,7 @@ export class Authorizer { this.cache = noCache ? undefined : new Map(); } - private async getAuthRecord( - walletAddress: string, - env: XmtpEnv, - origin: string, - ) { + async getAuthRecord(walletAddress: string, env: XmtpEnv, origin: string) { const key = buildKey(walletAddress, env, origin); // If the record exists in the cache, return it if (this.cache?.has(key)) { diff --git a/packages/snap/src/handlers.test.ts b/packages/snap/src/handlers.test.ts index 4023c3f..d9d1c87 100644 --- a/packages/snap/src/handlers.test.ts +++ b/packages/snap/src/handlers.test.ts @@ -1,6 +1,8 @@ -import { PrivateKeyBundleV1, XmtpEnv } from '@xmtp/xmtp-js'; -import { keystore } from '@xmtp/proto'; import { installSnap } from '@metamask/snaps-jest'; +import { keystore } from '@xmtp/proto'; +import type { XmtpEnv } from '@xmtp/xmtp-js'; +import { PrivateKeyBundleV1 } from '@xmtp/xmtp-js'; + import { buildRpcRequest, newWallet } from './testHelpers'; import { base64Encode } from './utils'; @@ -29,6 +31,7 @@ describe('onRPCRequest', () => { env: ENV, }; + // eslint-disable-next-line @typescript-eslint/unbound-method const { request } = await installSnap(); // Check the status of a fresh instance @@ -105,6 +108,7 @@ describe('onRPCRequest', () => { it('can return the public key', async () => { const wallet = newWallet(); const bundle = await PrivateKeyBundleV1.generate(wallet); + // eslint-disable-next-line @typescript-eslint/unbound-method const { request } = await installSnap(); await initKeystore(bundle, request); const meta = { @@ -123,6 +127,7 @@ describe('onRPCRequest', () => { it('returns an error if unknown handler is called', async () => { const wallet = newWallet(); const bundle = await PrivateKeyBundleV1.generate(wallet); + // eslint-disable-next-line @typescript-eslint/unbound-method const { request } = await installSnap(); await initKeystore(bundle, request); const meta = { @@ -139,6 +144,7 @@ describe('onRPCRequest', () => { it('prompts for authorization', async () => { const wallet = newWallet(); const bundle = await PrivateKeyBundleV1.generate(wallet); + // eslint-disable-next-line @typescript-eslint/unbound-method const { request } = await installSnap(); await initKeystore(bundle, request); const meta = { @@ -165,6 +171,7 @@ describe('onRPCRequest', () => { it('throws errors on rejected authorization', async () => { const wallet = newWallet(); const bundle = await PrivateKeyBundleV1.generate(wallet); + // eslint-disable-next-line @typescript-eslint/unbound-method const { request } = await installSnap(); await initKeystore(bundle, request); const meta = { diff --git a/packages/snap/src/handlers.ts b/packages/snap/src/handlers.ts index 7cb590f..b76542e 100644 --- a/packages/snap/src/handlers.ts +++ b/packages/snap/src/handlers.ts @@ -33,9 +33,9 @@ export type SnapResponse = { res: string | string[]; }; -type Codec = { - decode(input: Reader | Uint8Array, length?: number): T; - encode(message: T, writer?: Writer): Writer; +type Codec = { + decode(input: Reader | Uint8Array, length?: number): MessageType; + encode(message: MessageType, writer?: Writer): Writer; }; export type SnapRPC = { @@ -54,7 +54,7 @@ export async function processProtoRequest( } if (typeof request.req !== 'string') { - throw new Error(`Expected string request. Got: ${request.req}`); + throw new Error(`Expected string request. Got: ${typeof request.req}`); } const decodedRequest = rpc.req.decode(b64Decode(request.req)); @@ -62,7 +62,10 @@ export async function processProtoRequest( return serializeResponse(rpc.res, result); } -function serializeResponse(codec: Codec, res: T) { +function serializeResponse( + codec: Codec, + res: MessageType, +) { const responseBytes = codec.encode(res).finish(); return { res: b64Encode(responseBytes, 0, responseBytes.length) }; } @@ -139,10 +142,10 @@ export async function getKeystoreStatus( return { status: KeystoreStatus.KEYSTORE_STATUS_INITIALIZED, }; - } catch (e) { + } catch (error) { // Only swallow KeyNotFoundError and turn into a negative response - if (!(e instanceof KeyNotFoundError)) { - throw e; + if (!(error instanceof KeyNotFoundError)) { + throw error; } return { status: KeystoreStatus.KEYSTORE_STATUS_UNINITIALIZED, @@ -152,7 +155,7 @@ export async function getKeystoreStatus( ); } -export function KeystoreHandler(backingKeystore: InMemoryKeystore) { +export function keystoreHandler(backingKeystore: InMemoryKeystore) { const out: any = {}; for (const [method, apiDef] of Object.entries(keystoreApiDefs)) { if (!(method in backingKeystore)) { diff --git a/packages/snap/src/snapPersistence.ts b/packages/snap/src/snapPersistence.ts index db9226c..b5308d8 100644 --- a/packages/snap/src/snapPersistence.ts +++ b/packages/snap/src/snapPersistence.ts @@ -5,7 +5,7 @@ import storage from './storage'; const ENCODING = 'binary'; // Wraps the snap storage in an interface compatible with the XMTP persistence interface -// Main difference is that XMTP persistence asssumes all values are Uint8Arrays +// Main difference is that XMTP persistence assumes all values are Uint8Arrays // and expects implementations to handle serialization export default class SnapPersistence implements Persistence { async getItem(key: string): Promise { @@ -13,10 +13,12 @@ export default class SnapPersistence implements Persistence { if (typeof value !== 'string') { return null; } + // eslint-disable-next-line no-restricted-globals return value ? Uint8Array.from(Buffer.from(value, ENCODING)) : null; } async setItem(key: string, value: Uint8Array): Promise { + // eslint-disable-next-line no-restricted-globals await storage.setItem(key, Buffer.from(value).toString(ENCODING)); } } diff --git a/packages/snap/src/utils.ts b/packages/snap/src/utils.ts index e4da115..9aafb3d 100644 --- a/packages/snap/src/utils.ts +++ b/packages/snap/src/utils.ts @@ -8,13 +8,13 @@ import { import type { XmtpEnv, Persistence } from '@xmtp/xmtp-js'; import { KeyNotFoundError } from './errors'; -import { type SnapRequest, KeystoreHandler } from './handlers'; +import { type SnapRequest, keystoreHandler } from './handlers'; import SnapPersistence from './snapPersistence'; const { b64Encode } = fetcher; // Mapping of keystore identifiers ($walletAddress/$env) to handlers -const handlers = new Map>(); +const handlers = new Map>(); // Gets the keys from provided persistence and converts to a class export async function getKeys(persistence: Persistence) { @@ -47,7 +47,7 @@ export async function getHandler(address: string, env: XmtpEnv) { // This will throw if keys do not exist const keys = await getKeys(persistence); const keyStore = await InMemoryKeystore.create(keys, persistence); - handlers.set(key, KeystoreHandler(keyStore)); + handlers.set(key, keystoreHandler(keyStore)); } return handlers.get(key); diff --git a/packages/snap/tsconfig.eslint.json b/packages/snap/tsconfig.eslint.json new file mode 100644 index 0000000..52f8922 --- /dev/null +++ b/packages/snap/tsconfig.eslint.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": [".", ".eslintrc.js"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/snap/tsconfig.json b/packages/snap/tsconfig.json index 596e2cf..73bfee2 100644 --- a/packages/snap/tsconfig.json +++ b/packages/snap/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src", "snap.config.ts"] }