forked from Uniswap/widgets
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: compile ajv validators at build instead of runtime (Uniswap#134)
* wip: build validator functions before runtime * set esm to true * make sure folder sticks around * eslint * PR feedback * return the right values * update error message * style(lint): lint action with ESLint Co-authored-by: Lint Action <[email protected]>
- Loading branch information
Showing
7 changed files
with
74 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* eslint-disable */ | ||
|
||
const fs = require('fs') | ||
const path = require('path') | ||
const Ajv = require('ajv') | ||
const standaloneCode = require('ajv/dist/standalone').default | ||
const addFormats = require('ajv-formats') | ||
const schema = require('@uniswap/token-lists/dist/tokenlist.schema.json') | ||
|
||
const ajv = new Ajv({ code: { source: true, esm: true } }) | ||
addFormats(ajv) | ||
const validate = ajv.compile(schema) | ||
let moduleCode = standaloneCode(ajv, validate) | ||
fs.writeFileSync(path.join(__dirname, '../src/__generated__/validateTokenList.js'), moduleCode) | ||
|
||
const ajv2 = new Ajv({ code: { source: true, esm: true } }) | ||
addFormats(ajv2) | ||
const validate2 = ajv2.compile({ ...schema, required: ['tokens'] }) | ||
let moduleCode2 = standaloneCode(ajv2, validate2) | ||
fs.writeFileSync(path.join(__dirname, '../src/__generated__/validateTokens.js'), moduleCode2) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,62 @@ | ||
import type { TokenInfo, TokenList } from '@uniswap/token-lists' | ||
import type { Ajv, ValidateFunction } from 'ajv' | ||
import type { ValidateFunction } from 'ajv' | ||
|
||
enum ValidationSchema { | ||
LIST = 'list', | ||
TOKENS = 'tokens', | ||
} | ||
|
||
const validator = new Promise<Ajv>(async (resolve) => { | ||
const [ajv, schema] = await Promise.all([import('ajv'), import('@uniswap/token-lists/src/tokenlist.schema.json')]) | ||
const validator = new ajv.default({ allErrors: true }) | ||
.addSchema(schema, ValidationSchema.LIST) | ||
// Adds a meta scheme of Pick<TokenList, 'tokens'> | ||
.addSchema( | ||
{ | ||
...schema, | ||
$id: schema.$id + '#tokens', | ||
required: ['tokens'], | ||
}, | ||
ValidationSchema.TOKENS | ||
) | ||
resolve(validator) | ||
}) | ||
|
||
function getValidationErrors(validate: ValidateFunction | undefined): string { | ||
return ( | ||
validate?.errors?.map((error) => [error.dataPath, error.message].filter(Boolean).join(' ')).join('; ') ?? | ||
validate?.errors?.map((error) => [error.instancePath, error.message].filter(Boolean).join(' ')).join('; ') ?? | ||
'unknown error' | ||
) | ||
} | ||
|
||
async function validate(schema: ValidationSchema, data: unknown): Promise<unknown> { | ||
let validatorImport | ||
switch (schema) { | ||
case ValidationSchema.LIST: | ||
validatorImport = import('__generated__/validateTokenList') | ||
break | ||
case ValidationSchema.TOKENS: | ||
validatorImport = import('__generated__/validateTokens') | ||
break | ||
default: | ||
throw new Error('No validation function specified for schema') | ||
break | ||
} | ||
|
||
const [, validatorModule] = await Promise.all([import('ajv'), validatorImport]) | ||
const validator = (await validatorModule.default) as ValidateFunction | ||
if (validator?.(data)) { | ||
return data | ||
} | ||
throw new Error(getValidationErrors(validator)) | ||
} | ||
|
||
/** | ||
* Validates an array of tokens. | ||
* @param json the TokenInfo[] to validate | ||
*/ | ||
export async function validateTokens(json: TokenInfo[]): Promise<TokenInfo[]> { | ||
const validate = (await validator).getSchema(ValidationSchema.TOKENS) | ||
if (validate?.({ tokens: json })) { | ||
try { | ||
await validate(ValidationSchema.TOKENS, { tokens: json }) | ||
return json | ||
} catch (err) { | ||
throw new Error(`Tokens failed validation: ${err.message}`) | ||
} | ||
throw new Error(`Token list failed validation: ${getValidationErrors(validate)}`) | ||
} | ||
|
||
/** | ||
* Validates a token list. | ||
* @param json the TokenList to validate | ||
*/ | ||
export default async function validateTokenList(json: TokenList): Promise<TokenList> { | ||
const validate = (await validator).getSchema(ValidationSchema.LIST) | ||
if (validate?.(json)) { | ||
try { | ||
await validate(ValidationSchema.LIST, json) | ||
return json | ||
} catch (err) { | ||
throw new Error(`Token list failed validation: ${err.message}`) | ||
} | ||
throw new Error(`Token list failed validation: ${getValidationErrors(validate)}`) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters