From 7b88a8f2c75190eb6fe18c451b105919261e305f Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 27 Oct 2021 18:20:56 -0400 Subject: [PATCH] Rearchitect declaration internals to support UDVTs - Remove old declarations system that relied heavily on tuple signature recomputation - Replace with new declarations systems with its own IR-like format: - Introduce new `Type` type/namespace to encapsulate logic around ABI JSON parameter `type`s ("ufixed160x10" e.g.), including type guards to match array types vs. struct types, etc. (importantly this doesn't include any handling for UDVTs, which can only be detected via the nonstandard `internalType`) - Introduce explicit "Identifier" type/namespace. Identifiers belong to a class (interface, struct, udvt), have a name, and (except for interfaces) may have a container. Identifiers are string-serializable by way of Identifier.toReference() - Introduce explicit "Kind" type/namespace for elementary, struct, array, and interface params, including type guards and identifier/UDVT detection via nonstandard `internalType`. - Organize identifiers and kinds into Declarations data structure, which stores all kinds with necessary indexes. Declarations are generic to whether or not they are guaranteed by the type system to have all bindings. Declarations comprise: - all unnamed struct kinds, organized by tuple signature - named kind lookup by identifier reference - container indexes for non-global kinds - index for all kinds defined globally - Structure declarations collection as two step process: 1. Gather all references to declared types ("kinds"), inferring identifiers when present in optional `internalType` fields (produces `Declarations`) 2. Bind identifiers to all kinds that are missing identifiers (converts to `Declarations`) - Define search logic to find the correct kind to match a given ABI JSON parameter inside a fully bound set of declarations - Update Solidity generation to rely on new indexes and search, eliminating architectural coupling with tuple signature based lookup - Probably make a mess with a bunch of things? - Redo interface for version features, replacing mandatory triple-equal checks with more semantic sounding methods like `.supported()`, `.missing()`, and `.consistently(value)` - Do some unrelated things: - Add madge - Upgrade TypeScript to ^4.9.5 - Change lib from es2017 to es2019 - Use newer Solidiy for fast-check tests - Add test for output settings behavior --- .gitignore | 1 + CHANGELOG.md | 6 + packages/abi-to-sol/.madgerc | 13 + packages/abi-to-sol/README.md | 4 - packages/abi-to-sol/bin/abi-to-sol.ts | 8 +- packages/abi-to-sol/package.json | 10 +- packages/abi-to-sol/src/declarations.test.ts | 151 ----- packages/abi-to-sol/src/declarations.ts | 166 ----- .../abi-to-sol/src/declarations/bind.test.ts | 55 ++ packages/abi-to-sol/src/declarations/bind.ts | 158 +++++ .../abi-to-sol/src/declarations/collect.ts | 84 +++ packages/abi-to-sol/src/declarations/find.ts | 132 ++++ .../src/declarations/fromParameter.test.ts | 129 ++++ .../src/declarations/fromParameter.ts | 174 +++++ .../abi-to-sol/src/declarations/identifier.ts | 110 +++ .../abi-to-sol/src/declarations/index.test.ts | 113 ++++ packages/abi-to-sol/src/declarations/index.ts | 27 + packages/abi-to-sol/src/declarations/kind.ts | 164 +++++ packages/abi-to-sol/src/declarations/types.ts | 214 ++++++ packages/abi-to-sol/src/index.ts | 12 +- packages/abi-to-sol/src/parameter.ts | 188 ++++++ packages/abi-to-sol/src/solidity.test.ts | 82 --- packages/abi-to-sol/src/solidity.ts | 554 --------------- .../{abi-features.ts => solidity/analyze.ts} | 32 +- .../abi-to-sol/src/{ => solidity}/defaults.ts | 0 packages/abi-to-sol/src/solidity/features.ts | 133 ++++ packages/abi-to-sol/src/solidity/generate.ts | 505 ++++++++++++++ .../abi-to-sol/src/solidity/index.test.ts | 185 +++++ packages/abi-to-sol/src/solidity/index.ts | 64 ++ .../abi-to-sol/src/{ => solidity}/options.ts | 1 - packages/abi-to-sol/src/solidity/print.ts | 150 +++++ packages/abi-to-sol/src/type.test.ts | 15 + packages/abi-to-sol/src/type.ts | 305 +++++++++ packages/abi-to-sol/src/version-features.ts | 82 --- packages/abi-to-sol/src/visitor.ts | 4 +- packages/abi-to-sol/test/custom-example.ts | 20 - packages/abi-to-sol/tsconfig.json | 4 +- packages/web-ui/package.json | 4 +- packages/web-ui/src/abi/Examples.ts | 6 + .../web-ui/src/abi/examples/UDVTs.abi.json | 25 + .../web-ui/src/solidity/OptionsControls.tsx | 1 + yarn.lock | 633 ++++++++++++++++-- 42 files changed, 3558 insertions(+), 1166 deletions(-) create mode 100644 packages/abi-to-sol/.madgerc delete mode 100644 packages/abi-to-sol/src/declarations.test.ts delete mode 100644 packages/abi-to-sol/src/declarations.ts create mode 100644 packages/abi-to-sol/src/declarations/bind.test.ts create mode 100644 packages/abi-to-sol/src/declarations/bind.ts create mode 100644 packages/abi-to-sol/src/declarations/collect.ts create mode 100644 packages/abi-to-sol/src/declarations/find.ts create mode 100644 packages/abi-to-sol/src/declarations/fromParameter.test.ts create mode 100644 packages/abi-to-sol/src/declarations/fromParameter.ts create mode 100644 packages/abi-to-sol/src/declarations/identifier.ts create mode 100644 packages/abi-to-sol/src/declarations/index.test.ts create mode 100644 packages/abi-to-sol/src/declarations/index.ts create mode 100644 packages/abi-to-sol/src/declarations/kind.ts create mode 100644 packages/abi-to-sol/src/declarations/types.ts create mode 100644 packages/abi-to-sol/src/parameter.ts delete mode 100644 packages/abi-to-sol/src/solidity.test.ts delete mode 100644 packages/abi-to-sol/src/solidity.ts rename packages/abi-to-sol/src/{abi-features.ts => solidity/analyze.ts} (68%) rename packages/abi-to-sol/src/{ => solidity}/defaults.ts (100%) create mode 100644 packages/abi-to-sol/src/solidity/features.ts create mode 100644 packages/abi-to-sol/src/solidity/generate.ts create mode 100644 packages/abi-to-sol/src/solidity/index.test.ts create mode 100644 packages/abi-to-sol/src/solidity/index.ts rename packages/abi-to-sol/src/{ => solidity}/options.ts (99%) create mode 100644 packages/abi-to-sol/src/solidity/print.ts create mode 100644 packages/abi-to-sol/src/type.test.ts create mode 100644 packages/abi-to-sol/src/type.ts delete mode 100644 packages/abi-to-sol/src/version-features.ts create mode 100644 packages/web-ui/src/abi/examples/UDVTs.abi.json diff --git a/.gitignore b/.gitignore index c2658d7..e22271d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +deps.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a24f0..3b82d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## vNext +### New features + +- Rearchitect declaration internals to support UDVTs + ([#114](https://github.com/gnidan/abi-to-sol/pull/114) by + [@gnidan](https://github.com/gnidan)) + ### Project updates - Fix web UI deployment ([#112](https://github.com/gnidan/abi-to-sol/pull/112) diff --git a/packages/abi-to-sol/.madgerc b/packages/abi-to-sol/.madgerc new file mode 100644 index 0000000..5888d3c --- /dev/null +++ b/packages/abi-to-sol/.madgerc @@ -0,0 +1,13 @@ +{ + "excludeRegExp": [ + "\\.test\\.ts$", + "\/test\/" + ], + "fileExtensions": ["js","ts"], + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + }, + "tsConfig": "tsconfig.json" +} diff --git a/packages/abi-to-sol/README.md b/packages/abi-to-sol/README.md index fdca8ff..c1d3d4e 100644 --- a/packages/abi-to-sol/README.md +++ b/packages/abi-to-sol/README.md @@ -98,10 +98,6 @@ interface ENS { ``` -## Currently unsupported (PRs welcome! :wink:) - -- [User defined value types](https://blog.soliditylang.org/2021/09/27/user-defined-value-types/) - ## Is this project useful to you? Feel free to donate to diff --git a/packages/abi-to-sol/bin/abi-to-sol.ts b/packages/abi-to-sol/bin/abi-to-sol.ts index 9d19e26..8f79bb4 100644 --- a/packages/abi-to-sol/bin/abi-to-sol.ts +++ b/packages/abi-to-sol/bin/abi-to-sol.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node +import "source-map-support/register"; const neodoc = require("neodoc"); import {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; @@ -6,8 +7,11 @@ import * as abiSchema from "@truffle/contract-schema/spec/abi.spec.json"; import betterAjvErrors from "better-ajv-errors"; import Ajv from "ajv"; -import {generateSolidity, GenerateSolidityMode} from "../src"; -import * as defaults from "../src/defaults"; +import { + generateSolidity, + defaults, + GenerateSolidityMode +} from "../src"; const usage = ` abi-to-sol diff --git a/packages/abi-to-sol/package.json b/packages/abi-to-sol/package.json index d9e365f..dc80487 100644 --- a/packages/abi-to-sol/package.json +++ b/packages/abi-to-sol/package.json @@ -1,6 +1,6 @@ { "name": "abi-to-sol", - "version": "0.7.1", + "version": "0.8.0-0", "description": "Compile ABI JSON to Solidity interface", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -15,6 +15,7 @@ "scripts": { "abi-to-sol": "ts-node ./bin/abi-to-sol.ts", "prepare": "tsc", + "madge": "madge ./src --image deps.pdf", "test": "jest src/**", "test:test": "jest test/**", "test:dist": "yarn prepare && jest dist/src", @@ -34,16 +35,17 @@ "@types/semver": "^7.3.7", "change-case": "^4.1.1", "faker": "^5.1.0", - "fast-check": "^3.1.1", + "fast-check": "3.1.1", "husky": ">=4", "jest": "^26.4.2", "jest-fast-check": "^0.0.1", "jest-json-schema": "^2.1.0", "lint-staged": ">=10", - "solc": "^0.8.6", + "madge": "^5.0.2", + "solc": "^0.8.20", "ts-jest": "^26.4.0", "ts-node": "^9.0.0", - "typescript": "4.5.2" + "typescript": "^4.9.5" }, "dependencies": { "@truffle/abi-utils": "^1.0.0", diff --git a/packages/abi-to-sol/src/declarations.test.ts b/packages/abi-to-sol/src/declarations.test.ts deleted file mode 100644 index 876e074..0000000 --- a/packages/abi-to-sol/src/declarations.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as fc from "fast-check"; -import {testProp} from "jest-fast-check"; -import {Arbitrary} from "@truffle/abi-utils"; -import * as Example from "../test/custom-example"; - -import {collectDeclarations} from "./declarations"; - -describe("collectDeclarations", () => { - describe("arbitrary examples", () => { - describe("for non-tuple parameters / event parameters", () => { - testProp( - "are empty", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(!parameter.type.startsWith("tuple")); - - expect(collectDeclarations(parameter)).toEqual({ - signatureDeclarations: {}, - containerSignatures: {} - }); - } - ); - }); - - describe("for tuple parameters with non-tuple components", () => { - testProp( - "have length 1", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - fc.pre( - parameter.components.every( - (component: any) => !component.type.startsWith("tuple") - ) - ); - - const declarations = collectDeclarations(parameter); - expect(Object.keys(declarations.signatureDeclarations)).toHaveLength(1); - - const [declaration] = Object.values(declarations.signatureDeclarations); - expect(declaration).toHaveProperty("components"); - - const {components} = declaration; - expect(components).toHaveLength(parameter.components.length); - - for (const [index, component] of components.entries()) { - expect(component.name).toEqual(parameter.components[index].name); - } - } - ); - }); - - describe("for tuple parameters with exactly one tuple component", () => { - testProp( - "have length 2", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - - // find exactly one tuple-based component - const tupleComponents = parameter.components.filter( - (component: any) => component.type.startsWith("tuple") - ); - - fc.pre(tupleComponents.length === 1); - - const [tupleComponent] = tupleComponents; - - fc.pre( - tupleComponent.components.every( - (component: any) => !component.type.startsWith("tuple") - ) - ); - - const declarations = collectDeclarations(parameter); - expect(Object.keys(declarations.signatureDeclarations)).toHaveLength(2); - } - ); - }); - - testProp( - "produce only valid references to each other", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - - const components = parameter.components || []; - - const declarations = collectDeclarations(parameter); - - for (const {components} of Object.values(declarations.signatureDeclarations)) { - for (const {signature} of components) { - if (signature) { - expect(declarations.signatureDeclarations).toHaveProperty(signature); - } - } - } - } - ); - }); - - describe("custom example", () => { - const declarations = collectDeclarations(Example.abi); - - for (const [structName, signature] of Object.entries( - Example.expectedSignatures - )) { - describe(`struct ${structName}`, () => { - it("exists in declarations", () => { - expect(declarations.signatureDeclarations).toHaveProperty(signature); - }); - - const expectedComponents = (Example.expectedDeclarations as any)[ - structName - ]; - const declaration = declarations.signatureDeclarations[signature]; - - for (const [componentName, component] of Object.entries( - expectedComponents - )) { - describe(`component ${componentName}`, () => { - it("exists in declarations", () => { - const names = declaration.components.map(({name}) => name); - expect(names).toContain(componentName); - }); - - const expectedComponent = (expectedComponents as any)[ - componentName - ]; - - const component: any = declaration.components.find( - ({name}) => name === componentName - ); - - it("has correct type", () => { - expect(component.type).toEqual(expectedComponent.type); - }); - - if (component.signature) { - it("has correct signature", () => { - expect(component.signature).toEqual( - expectedComponent.signature - ); - }); - } - }); - } - }); - } - }); -}); diff --git a/packages/abi-to-sol/src/declarations.ts b/packages/abi-to-sol/src/declarations.ts deleted file mode 100644 index c14b376..0000000 --- a/packages/abi-to-sol/src/declarations.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; -import type * as Abi from "@truffle/abi-utils"; -import { abiTupleSignature } from "@truffle/abi-utils"; - -import {Visitor, VisitOptions, dispatch, Node} from "./visitor"; - -export interface Component { - name: string; - type: string; - signature?: string; -} - -export interface Declaration { - identifier?: string; - components: Component[]; -} - -export interface Declarations { - signatureDeclarations: { - [signature: string]: Declaration; - }; - containerSignatures: { - [container: string]: string[]; - } -} - -export class DeclarationsCollector implements Visitor { - visitAbi({node: nodes}: VisitOptions): Declarations { - return nodes - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitEventEntry({node: entry}: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitErrorEntry({node: entry}: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitFunctionEntry({ - node: entry, - }: VisitOptions): Declarations { - return [...entry.inputs, ...(entry.outputs || [])] - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitConstructorEntry({ - node: entry, - }: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitFallbackEntry({ - node: entry, - }: VisitOptions): Declarations { - return emptyDeclarations(); - } - - visitReceiveEntry({ - node: entry, - }: VisitOptions): Declarations { - return emptyDeclarations(); - } - - visitParameter({node: parameter}: VisitOptions): Declarations { - if (!parameter.type.startsWith("tuple")) { - return emptyDeclarations(); - } - - let container = ""; - const components = parameter.components || []; - const signature = abiTupleSignature(components); - const declaration: Declaration = { - components: components.map(({name, type, components}) => - !components - ? {name, type} - : { - name, - type, - signature: abiTupleSignature(components), - } - ), - }; - - if ("internalType" in parameter && parameter.internalType) { - const match = parameter.internalType.match(/struct ([^\[]+).*/); - if (match) { - const possiblyQualifiedIdentifier = match[1]; - const parts = possiblyQualifiedIdentifier.split("."); - if (parts.length === 1) { - declaration.identifier = parts[0]; - } else if (parts.length === 2) { - container = parts[0]; - declaration.identifier = parts[1]; - } - } - } - - const declarations = { - signatureDeclarations: { - [signature]: declaration - }, - containerSignatures: { - [container]: [signature] - } - }; - - const componentDeclarations: Declarations = components - .map((component: Abi.Parameter) => - this.visitParameter({node: component}) - ) - .reduce(mergeDeclarations, emptyDeclarations()) - - - return mergeDeclarations(declarations, componentDeclarations); - } -} - -export const collectDeclarations = (node: SchemaAbi | Node) => - dispatch({ - node, - visitor: new DeclarationsCollector(), - }); - -function mergeDeclarations( - a: Declarations, - b: Declarations -): Declarations { - const declarations: Declarations = { - signatureDeclarations: { - ...a.signatureDeclarations, - ...b.signatureDeclarations - }, - containerSignatures: { - ...a.containerSignatures, - // add b iteratively separately to merge arrays - } - }; - - for (const [container, signatures] of Object.entries(b.containerSignatures)) { - const mergedSignatures = new Set([ - ...(declarations.containerSignatures[container] || []), - ...signatures - ]) - - declarations.containerSignatures[container] = [...mergedSignatures]; - } - - return declarations; -} - -function emptyDeclarations(): Declarations { - return { - signatureDeclarations: {}, - containerSignatures: {} - }; -} diff --git a/packages/abi-to-sol/src/declarations/bind.test.ts b/packages/abi-to-sol/src/declarations/bind.test.ts new file mode 100644 index 0000000..5f99397 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/bind.test.ts @@ -0,0 +1,55 @@ +import * as Abi from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; +import { fromParameter } from "./fromParameter"; + +import { bind } from "./bind"; + +describe("bind", () => { + it("should re-use the same identifier for the same unnamed struct", () => { + const internalComponent = { + name: "u", + type: "uint256" + }; + + const parameter = { + name: "a", + type: "tuple", + internalType: "struct A", + components: [{ + name: "b1", + type: "tuple", + components: [{ ...internalComponent }] + }, { + name: "b2", + type: "tuple", + components: [{ ...internalComponent }] + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + if (!("identifier" in parameterKind) || !parameterKind.identifier) { + throw new Error("Expected parameterKind to have identifier"); + } + + const { identifier } = parameterKind; + + const declarationsWithBindings = bind(declarations); + + const outerStructKind = declarationsWithBindings.byIdentifierReference[ + Identifier.toReference(identifier) + ]; + + if (!("members" in outerStructKind)) { + throw new Error("Expected outer struct to have `members`"); + } + + const { members: [b1, b2] } = outerStructKind; + + if (!("identifier" in b1.kind) || !("identifier" in b2.kind)) { + throw new Error("Inner struct is missing identifier"); + } + + expect(b1.kind.identifier).toEqual(b2.kind.identifier); + }); +}); diff --git a/packages/abi-to-sol/src/declarations/bind.ts b/packages/abi-to-sol/src/declarations/bind.ts new file mode 100644 index 0000000..0b402d1 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/bind.ts @@ -0,0 +1,158 @@ +import { Identifier } from "./identifier"; +import { Kind, Bindings, HasBindings, MissingDeepBindings } from "./kind"; + +import { Declarations, merge } from "./types"; + +export const bind = ( + declarations: Declarations +): Declarations => { + const { + newDeclarations, + identifierBySignature, + }: { + newDeclarations: Declarations; + identifierBySignature: { + [signature: string]: Identifier + } + } = Object.entries(declarations.unnamedBySignature || {}) + .map(([signature, unidentifiedKind], index) => { + const identifier: Identifier = { + class: "struct", + name: `S_${index}` + }; + + const kind = { + ...unidentifiedKind, + identifier + }; + + return { + kind, + signature + }; + }) + .reduce(( + { + newDeclarations: { + byIdentifierReference, + globalIdentifiers + }, + identifierBySignature + }, + { + kind, + signature + } + ) => ({ + newDeclarations: { + byIdentifierReference: { + ...byIdentifierReference, + [Identifier.toReference(kind.identifier)]: kind + }, + unnamedBySignature: {}, + globalIdentifiers: new Set([ + ...globalIdentifiers, + Identifier.toReference(kind.identifier) + ]), + identifiersByContainer: {}, + }, + identifierBySignature: { + ...identifierBySignature, + [signature]: kind.identifier + } + }), { + newDeclarations: { + byIdentifierReference: {}, + unnamedBySignature: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {} + }, + identifierBySignature: {} + }); + + const declarationsMissingDeepBindings = merge( + declarations as Declarations, + newDeclarations + ); + + return { + byIdentifierReference: Object.entries( + declarationsMissingDeepBindings.byIdentifierReference + ) + .map(([identifierReference, kind]) => ({ + [identifierReference]: Kind.isStruct(kind) + ? bindStruct(kind, identifierBySignature) + : kind + })) + .reduce((a, b) => ({ ...a, ...b }), {}), + unnamedBySignature: {}, + globalIdentifiers: declarationsMissingDeepBindings.globalIdentifiers, + identifiersByContainer: declarationsMissingDeepBindings.identifiersByContainer + }; +}; + +const bindKind = ( + kind: Kind, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind => { + if (Kind.isElementary(kind)) { + return kind; + } + + if (Kind.isUserDefinedValueType(kind)) { + return kind; + } + + if (Kind.isStruct(kind)) { + return bindStruct(kind, identifierBySignature); + } + + if (Kind.isArray(kind)) { + return bindArray(kind, identifierBySignature); + } + + throw new Error("Could not recognize kind"); +} + +const bindStruct = ( + kind: Kind.Struct, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind.Struct => { + const { + signature, + identifier = identifierBySignature[signature] + } = kind; + + const members = kind.members.map(({ name, kind }) => ({ + name, + kind: bindKind(kind, identifierBySignature) + })); + + return { + signature, + members, + identifier + } +}; + +const bindArray = ( + kind: Kind.Array, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind.Array => { + const itemKind = bindKind(kind.itemKind, identifierBySignature); + const length = kind.length; + + return { + itemKind, + ...("length" in kind + ? { length } + : {} + ) + }; +}; diff --git a/packages/abi-to-sol/src/declarations/collect.ts b/packages/abi-to-sol/src/declarations/collect.ts new file mode 100644 index 0000000..88b956e --- /dev/null +++ b/packages/abi-to-sol/src/declarations/collect.ts @@ -0,0 +1,84 @@ +import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; +import type * as Abi from "@truffle/abi-utils"; + +import {Visitor, VisitOptions, dispatch, Node} from "../visitor"; + +import { Declarations, empty, merge } from "./types"; +import { fromParameter } from "./fromParameter"; + +export const collectWithoutBindings = (node: Node) => + dispatch({ node, visitor: new DeclarationsCollector() }); + +interface Context { +} + +type Visit = VisitOptions; + + +class DeclarationsCollector + implements Visitor +{ + visitAbi({ + node: abi, + context + }: Visit): Declarations { + return abi + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitEventEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitErrorEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitFunctionEntry({ + node: entry, + context + }: Visit): Declarations { + return [...entry.inputs, ...(entry.outputs || [])] + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitConstructorEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitFallbackEntry({ + node: entry + }: Visit): Declarations { + return empty(); + } + + visitReceiveEntry({ + node: entry, + }: Visit): Declarations { + return empty(); + } + + visitParameter({ + node: parameter + }: Visit): Declarations { + const { declarations } = fromParameter(parameter); + return declarations; + } +} diff --git a/packages/abi-to-sol/src/declarations/find.ts b/packages/abi-to-sol/src/declarations/find.ts new file mode 100644 index 0000000..34b2541 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/find.ts @@ -0,0 +1,132 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Parameter, isParameter } from "../parameter"; + +import { Identifier } from "./identifier"; +import { Kind, HasBindings } from "./kind"; +import { Declarations } from "./types"; + +export const find = ( + parameter: Abi.Parameter, + declarations: Declarations +): Kind => { + const { type } = parameter; + + if (!isParameter(parameter)) { + throw new Error( + `Parameter type \`${parameter.type}\` is not a valid ABI type` + ); + } + + if (Parameter.isElementary(parameter)) { + return findElementary(parameter, declarations); + } + + if (Parameter.isArray(parameter)) { + return findArray(parameter, declarations); + } + + if (Parameter.isTuple(parameter)) { + return findTuple(parameter, declarations); + } + + throw new Error(`Unknown type ${type}`); +} + +const findElementary = ( + parameter: Parameter.Elementary, + declarations: Declarations +): Kind => { + if (!Parameter.isUserDefinedValueType(parameter)) { + const { type, internalType } = parameter; + return { + type, + ...( + internalType + ? { hints: { internalType } } + : {} + ) + } + } + + const { name, scope } = Parameter.UserDefinedValueType.recognize( + parameter + ); + const identifier = Identifier.UserDefinedValueType.create({ name, scope }); + const reference = Identifier.toReference(identifier); + + const kind = declarations.byIdentifierReference[reference]; + + if (!kind) { + throw new Error( + `Unknown declaration with identifier reference ${reference}` + ); + } + + return kind; +}; + +const findArray = ( + parameter: Parameter.Array, + declarations: Declarations +): Kind => { + const itemParameter = Parameter.Array.item(parameter); + + const itemKind = find(itemParameter, declarations); + + return { + itemKind, + ...( + Parameter.Array.isStatic(parameter) + ? { length: Parameter.Array.Static.length(parameter) } + : {} + ) + }; +} + +const findTuple = ( + parameter: Parameter.Tuple, + declarations: Declarations +): Kind => { + const { + signature, + name, + scope + } = Parameter.Tuple.recognize(parameter); + + const identifier = name + ? Identifier.Struct.create({ name, scope }) + : undefined; + + if (identifier) { + const reference = Identifier.toReference(identifier); + + const kind = declarations.byIdentifierReference[reference]; + + if (!kind) { + throw new Error( + `Unknown declaration with identifier reference ${reference}` + ); + } + + return kind; + } + + // reaching here guarantees no internalType specified for `parameter` + // so only match declarations that also have no internalType + + const kind = Object.values(declarations.byIdentifierReference) + .find(kind => + Kind.isStruct(kind) && + kind.signature === signature && + !kind.hints?.internalType + ) + + if (!kind) { + throw new Error( + `Unknown declaration with tuple signature ${signature}` + ); + } + + return kind; +} diff --git a/packages/abi-to-sol/src/declarations/fromParameter.test.ts b/packages/abi-to-sol/src/declarations/fromParameter.test.ts new file mode 100644 index 0000000..87baef5 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/fromParameter.test.ts @@ -0,0 +1,129 @@ +import type * as Abi from "@truffle/abi-utils"; +import { abiTupleSignature } from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; + +import { fromParameter } from "./fromParameter"; + +describe("fromParameter", () => { + it("builds declarations from a single elementary type", () => { + const parameter: Abi.Parameter = { + type: "uint256", + name: "u", + internalType: "uint256" + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + expect(declarations.byIdentifierReference).toEqual({}); + expect(declarations.unnamedBySignature).toEqual({}); + }); + + it("builds a reference to an unnamed struct", () => { + const parameter: Abi.Parameter = { + type: "tuple", + name: "s", + components: [ + { + type: "uint256", + name: "u" + } + ] + }; + + const expectedSignature = abiTupleSignature( + // default to satisfy type-checker (Abi.Parameter includes non-tuples) + parameter.components || [] + ); + + const { declarations, parameterKind } = fromParameter(parameter); + + expect(declarations.byIdentifierReference).toEqual({}); + expect(declarations.unnamedBySignature).toHaveProperty("(uint256)"); + + const unnamedDeclaration = declarations.unnamedBySignature["(uint256)"]; + if (!unnamedDeclaration) { + throw new Error("Expected unnamed reference"); + } + + expect(unnamedDeclaration.signature).toEqual(expectedSignature); + expect(unnamedDeclaration.identifier).toEqual(undefined); + expect(unnamedDeclaration.members).toHaveLength(1); + + const [member] = unnamedDeclaration.members; + + expect(member.name).toEqual("u"); + expect(member.kind).toEqual({ + type: "uint256" + }); + }); + + it("should deduplicate unnamed structs", () => { + const internalComponent = { + name: "u", + type: "uint256" + }; + + const parameter = { + name: "a", + type: "tuple", + components: [{ + name: "b1", + type: "tuple", + components: [{ ...internalComponent }] + }, { + name: "b2", + type: "tuple", + components: [{ ...internalComponent }] + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + // outer struct + expect(declarations.unnamedBySignature).toHaveProperty("((uint256),(uint256))"); + const outerStruct = declarations.unnamedBySignature["((uint256),(uint256))"]; + + // inner struct + expect(declarations.unnamedBySignature).toHaveProperty("(uint256)"); + }); + + it("should include identifiers when given internalType", () => { + const parameter: Abi.Parameter = { + name: "a", + type: "tuple", + internalType: "struct A", + components: [{ + name: "u", + type: "uint256" + }, { + name: "f", + type: "address", + internalType: "contract Foo" + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + if (!("identifier" in parameterKind)) { + throw new Error("Expected `identifier` to exist on parameterKind"); + } + + const { identifier } = parameterKind; + + if (!identifier) { + throw new Error("Expected identifier to be defined"); + } + + expect(declarations.byIdentifierReference).toHaveProperty( + Identifier.toReference(identifier) + ); + + const kind = declarations.byIdentifierReference[ + Identifier.toReference(identifier) + ]; + + expect(parameterKind).toEqual(kind); + }); +}); + diff --git a/packages/abi-to-sol/src/declarations/fromParameter.ts b/packages/abi-to-sol/src/declarations/fromParameter.ts new file mode 100644 index 0000000..907df78 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/fromParameter.ts @@ -0,0 +1,174 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Parameter, isParameter } from "../parameter"; + +import { Identifier } from "./identifier"; +import { Kind, MissingBindings } from "./kind"; +import { Declarations, empty, merge, from } from "./types"; + +export interface FromParameterResult { + parameterKind: Kind; + declarations: Declarations; +} + +export const fromParameter = ( + parameter: Abi.Parameter +): FromParameterResult => { + if (!isParameter(parameter)) { + throw new Error( + `Parameter type \`${parameter.type}\` is not a valid ABI type` + ); + } + + if (Parameter.isElementary(parameter)) { + return fromElementaryParameter(parameter); + } + + if (Parameter.isArray(parameter)) { + return fromArrayParameter(parameter); + } + + if (Parameter.isTuple(parameter)) { + return fromTupleParameter(parameter); + } + + throw new Error(`Unexpectedly could not convert Abi.Parameter to Kind`); +}; + +const fromElementaryParameter = ( + parameter: Parameter.Elementary +): FromParameterResult => { + if (Parameter.isUserDefinedValueType(parameter)) { + const { name, scope } = Parameter.UserDefinedValueType.recognize( + parameter + ); + const identifier = Identifier.UserDefinedValueType.create({ name, scope }); + + const { type, internalType } = parameter; + + const parameterKind: Kind.UserDefinedValueType = { + type, + hints: { internalType }, + identifier + }; + + return { + parameterKind, + declarations: from(parameterKind) + } + } + + const { type, internalType } = parameter; + + const parameterKind: Kind.Elementary = { + type, + ...( + internalType + ? { hints: { internalType } } + : {} + ) + }; + + return { + parameterKind, + declarations: from(parameterKind) + } +} + +const fromArrayParameter = ( + parameter: Parameter.Array +): FromParameterResult => { + const itemParameter = Parameter.Array.item(parameter); + + const { + parameterKind: itemKind, + declarations + } = fromParameter(itemParameter); + + const parameterKind: Kind.Array = { + itemKind, + ...( + Parameter.Array.isStatic(parameter) + ? { length: Parameter.Array.Static.length(parameter) } + : {} + ) + }; + + return { + declarations, + parameterKind + }; +}; + +const fromTupleParameter = ( + parameter: Parameter.Tuple +): FromParameterResult => { + const { + internalType, + components + } = parameter; + + const { + signature, + name, + scope + } = Parameter.Tuple.recognize(parameter); + + const identifier = name + ? Identifier.Struct.create({ name, scope }) + : undefined; + + const memberResults: { + member: Kind.Struct.Member; + declarations: Declarations + }[] = components.map(component => { + const { name } = component; + + const { + parameterKind: kind, + declarations + } = fromParameter(component); + + return { + member: { + kind, + ...( + name + ? { name } + : {} + ) + }, + declarations + } + }); + + const members = memberResults.map(({ member }) => member); + const membersDeclarations = memberResults + .map(({ declarations }) => declarations) + .reduce(merge, empty()); + + const parameterKind = { + signature, + members, + ...( + internalType + ? { hints: { internalType } } + : {} + ), + ...( + identifier + ? { identifier } + : {} + ) + }; + + const declarations = merge( + membersDeclarations, + from(parameterKind) + ); + + return { + declarations, + parameterKind + }; +} diff --git a/packages/abi-to-sol/src/declarations/identifier.ts b/packages/abi-to-sol/src/declarations/identifier.ts new file mode 100644 index 0000000..a2e0eac --- /dev/null +++ b/packages/abi-to-sol/src/declarations/identifier.ts @@ -0,0 +1,110 @@ +export type Identifier = + | Identifier.Interface + | Identifier.Struct + | Identifier.UserDefinedValueType; + +export namespace Identifier { + export interface Properties { + name: string; + scope?: string; + } + + export type Class = + | Struct.Class + | Interface.Class + | UserDefinedValueType.Class; + + export interface Interface { + class: Interface.Class; + name: string; + container?: never; + } + + export namespace Interface { + export type Class = "interface"; + + export const create = ({ + name + }: Omit): Identifier.Interface => ({ + class: "interface", + name + }); + + export type Reference = `${ + Identifier["class"] + }--${ + Identifier["name"] + }`; + } + + export interface Struct { + class: Struct.Class; + name: string; + container?: Interface; + } + + export namespace Struct { + export type Class = "struct"; + + export const create = ({ + name, + scope + }: Properties): Identifier.Struct => ({ + class: "struct", + name, + ...( + scope + ? { container: Identifier.Interface.create({ name: scope }) } + : {} + ) + }); + } + + export interface UserDefinedValueType { + class: UserDefinedValueType.Class; + name: string; + container?: Interface; + } + + + export namespace UserDefinedValueType { + export type Class = "udvt"; + + export const create = ({ + name, + scope + }: Properties): Identifier.UserDefinedValueType => ({ + class: "udvt", + name, + ...( + scope + ? { container: Identifier.Interface.create({ name: scope }) } + : {} + ) + }); + } + + export type Reference = + | `${ + Identifier["class"] + }--${ + Identifier["name"] + }` + | `${ + Identifier["class"] + }--${ + Identifier["name"] + }--${ + Exclude["name"] + }`; + + export const toReference = ( + identifier: Identifier + ): Reference => { + if (identifier.container) { + return `${identifier.class}--${identifier.name}--${identifier.container.name}`; + } + + return `${identifier.class}--${identifier.name}`; + } +} diff --git a/packages/abi-to-sol/src/declarations/index.test.ts b/packages/abi-to-sol/src/declarations/index.test.ts new file mode 100644 index 0000000..9515fd6 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/index.test.ts @@ -0,0 +1,113 @@ +import * as fc from "fast-check"; +import { testProp } from "jest-fast-check"; +import { Arbitrary } from "@truffle/abi-utils"; + +import { Declarations } from "."; +import { Kind } from "./kind"; +import { Identifier } from "./identifier"; + +describe("Declarations.collect", () => { + describe("arbitrary examples", () => { + describe("for non-tuple parameters / event parameters", () => { + testProp( + "are empty", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(!parameter.type.startsWith("tuple")); + + expect(Declarations.collect(parameter)).toEqual({ + byIdentifierReference: {}, + unnamedBySignature: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + }); + } + ); + }); + + describe("for tuple parameters with non-tuple components", () => { + testProp( + "have length 1", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + fc.pre( + parameter.components.every( + (component: any) => !component.type.startsWith("tuple") + ) + ); + + const declarations = Declarations.collect(parameter); + expect(Object.keys(declarations.byIdentifierReference)).toHaveLength(1); + + const [kind] = Object.values(declarations.byIdentifierReference); + if (!("members" in kind)) { + throw new Error("Expected kind to be a struct with members"); + } + + const { members } = kind; + expect(members).toHaveLength(parameter.components.length); + + for (const [index, member] of members.entries()) { + expect(member.name).toEqual(parameter.components[index].name); + } + } + ); + }); + + describe("for tuple parameters with exactly one tuple component", () => { + testProp( + "have length 2", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + + // find exactly one tuple-based component + const tupleComponents = parameter.components.filter( + (component: any) => component.type.startsWith("tuple") + ); + + fc.pre(tupleComponents.length === 1); + + const [tupleComponent] = tupleComponents; + + fc.pre( + tupleComponent.components.every( + (component: any) => !component.type.startsWith("tuple") + ) + ); + + const declarations = Declarations.collect(parameter); + expect(Object.keys(declarations.byIdentifierReference)).toHaveLength(2); + } + ); + }); + + testProp( + "produce only valid references to each other", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + + const components = parameter.components || []; + + const declarations = Declarations.collect(parameter); + + for (const kind of Object.values(declarations.byIdentifierReference)) { + if ("members" in kind) { + for (const member of kind.members) { + if (Kind.isStruct(member.kind)) { + if (!("identifier" in member.kind)) { + throw new Error("Expected identifier"); + } + expect(declarations.byIdentifierReference).toHaveProperty( + Identifier.toReference(member.kind.identifier) + ); + } + } + } + } + } + ); + }); +}); diff --git a/packages/abi-to-sol/src/declarations/index.ts b/packages/abi-to-sol/src/declarations/index.ts new file mode 100644 index 0000000..f51651b --- /dev/null +++ b/packages/abi-to-sol/src/declarations/index.ts @@ -0,0 +1,27 @@ +import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; +import * as Abi from "@truffle/abi-utils"; + +import type { Declarations as _Declarations } from "./types"; +import { find as _find } from "./find"; +import { bind } from "./bind"; +import { HasBindings } from "./kind"; +import { collectWithoutBindings } from "./collect"; + +export { Identifier } from "./identifier"; +export { Kind } from "./kind"; + +export namespace Declarations { + export const collect = ( + abi: Abi.Abi | SchemaAbi + ): _Declarations => { + const unboundDeclarations = collectWithoutBindings(abi); + + const declarations = bind(unboundDeclarations); + + return declarations; + }; + + export const find = _find; +} + +export type Declarations = _Declarations; diff --git a/packages/abi-to-sol/src/declarations/kind.ts b/packages/abi-to-sol/src/declarations/kind.ts new file mode 100644 index 0000000..5f68068 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/kind.ts @@ -0,0 +1,164 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; + +import type { Type } from "../type"; + +export type MissingBindings = "missing-bindings"; +export type MissingDeepBindings = "missing-deep-bindings"; +export type HasBindings = "has-bindings"; + +export type Bindings = + | MissingBindings + | MissingDeepBindings + | HasBindings; + +export type Kind = + | Kind.Interface + | Kind.Elementary + | Kind.Array + | Kind.Struct; + +export namespace Kind { + /* + * Elementary + */ + + export interface Elementary { + type: Type.Elementary; + hints?: { + internalType?: string; + } + } + + export const isElementary = ( + kind: Kind + ): kind is Elementary => "type" in kind; + + /* + * UserDefinedValueType (extends Elementary) + */ + + export interface UserDefinedValueType extends Elementary { + // UDVTs are meaningless without identifier (they'd just be elementary) + identifier: Identifier + + // UDVTs are a Solidity feature that depend on `internalType`, so require + // the corresponding hint + hints: { + internalType: string; + } + } + + export const isUserDefinedValueType = ( + kind: Kind + ): kind is UserDefinedValueType => + isElementary(kind) && "identifier" in kind; + + /* + * Array + */ + + export interface Array { + itemKind: Kind; + length?: number; + hints?: {}; // don't use internalType for arrays, for safety + } + + export const isArray = ( + kind: Kind + ): kind is Array => + "itemKind" in kind; + + export namespace Array { + export interface Static< + B extends Bindings + > extends Array { + length: number; + } + + export const isStatic = ( + kind: Kind + ): kind is Static => + isArray(kind) && typeof kind.length === "number"; + + export interface Dynamic< + B extends Bindings + > extends Array { + length: never; + } + + export const isDynamic = ( + kind: Kind + ): kind is Dynamic => + isArray(kind) && typeof kind.length !== "number"; + } + + /* + * Struct + */ + + export type Struct = + & { + signature: string; + hints?: { + internalType?: string; + } + } + & ( + B extends HasBindings + ? { + identifier: Identifier; + members: Struct.Member[]; + } + : B extends MissingDeepBindings + ? { + identifier: Identifier; + members: Struct.Member[]; + } + : B extends MissingBindings + ? { + identifier?: Identifier; + members: Struct.Member[]; + } + : { + identifier?: Identifier; + members: Struct.Member[]; + } + ); + + export const isStruct = ( + kind: Kind + ): kind is Struct => + "members" in kind; + + export namespace Struct { + export interface Member { + name?: string; + kind: Kind; + }; + } + + export type Interface = + B extends HasBindings + ? { + identifier: Identifier; + } + : B extends MissingDeepBindings + ? { + identifier: Identifier; + } + : B extends MissingBindings + ? { + identifier?: Identifier; + } + : { + identifier?: Identifier; + }; + + export const isInterface = ( + kind: Kind + ): kind is Interface => + // only has key identifier + Object.keys(kind).filter(key => key !== "identifier").length === 0 +} diff --git a/packages/abi-to-sol/src/declarations/types.ts b/packages/abi-to-sol/src/declarations/types.ts new file mode 100644 index 0000000..c0d765f --- /dev/null +++ b/packages/abi-to-sol/src/declarations/types.ts @@ -0,0 +1,214 @@ +import { Identifier } from "./identifier"; +import { Bindings, HasBindings, MissingDeepBindings, MissingBindings, Kind } from "./kind"; + +export type Declarations = { + byIdentifierReference: { + [reference: Identifier.Reference]: + | Kind.Interface + | Kind.UserDefinedValueType + | Kind.Struct + }; + unnamedBySignature: UnnamedBySignature; + globalIdentifiers: Set; + identifiersByContainer: { + [reference: Identifier.Interface.Reference]: Set; + }; +} + +export type UnnamedBySignature = + B extends HasBindings + ? { + [signature: string]: never; + } + : B extends MissingDeepBindings + ? { + [signature: string]: undefined; + } + : B extends MissingBindings + ? { + [signature: string]: Kind.Struct; // udvts are inherently named + } + : { + [signature: string]: Kind.Struct; // udvts are inherently named + }; + +/** + * Initialize an empty set of declarations + */ +export const empty = (): Declarations => ({ + byIdentifierReference: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + unnamedBySignature: {} as UnnamedBySignature +}); + +/** + * Merge two sets of declarations + */ +export const merge = ( + a: Declarations, + b: Declarations +): Declarations => ({ + byIdentifierReference: { + ...a.byIdentifierReference, + ...b.byIdentifierReference + }, + unnamedBySignature: { + ...a.unnamedBySignature, + ...b.unnamedBySignature + }, + globalIdentifiers: new Set([ + ...a.globalIdentifiers, + ...b.globalIdentifiers + ]), + identifiersByContainer: mergeIdentifiersByContainer( + a.identifiersByContainer, + b.identifiersByContainer + ) +}); + +/** + * Generate declarations to include a single Kind. + * Note! This does not recurse; e.g. it returns empty() for arrays always + */ +export const from = ( + kind: Kind +): Declarations => { + if (Kind.isInterface(kind)) { + return fromInterface(kind); + } + + if (Kind.isStruct(kind)) { + return fromStruct(kind); + } + + if (Kind.isUserDefinedValueType(kind)) { + return fromUserDefinedValueType(kind); + }; + + return empty(); +} + +const fromUserDefinedValueType = ( + kind: Kind.UserDefinedValueType +): Declarations => { + const { identifier } = kind; + const reference = Identifier.toReference(identifier); + const { container } = identifier; + + // globally-defined case + if (!container) { + return { + byIdentifierReference: { + [reference]: kind + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; + } + + // defined inside containing contract/interface + const containerDeclarations = fromInterface({ identifier: container }); + const containerReference = Identifier.toReference(container); + + return merge(containerDeclarations, { + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: { + [containerReference]: new Set([reference]) + } + }); +} + +const fromStruct = ( + kind: Kind.Struct +): Declarations => { + const { identifier } = kind; + + // unnamed case + if (!identifier) { + const { signature } = kind; + + return { + byIdentifierReference: {}, + unnamedBySignature: { + [signature]: kind + } as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + }; + } + + const reference = Identifier.toReference(identifier); + const { container } = identifier; + + // globally-defined case + if (!container) { + return { + byIdentifierReference: { + [reference]: kind + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; + } + + // defined inside containing contract/interface + const containerDeclarations = fromInterface({ identifier: container }); + + const containerReference = Identifier.toReference(container); + + return merge(containerDeclarations, { + // defined inside interface case + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: { + [containerReference]: new Set([reference]) + } + }); +} + +const fromInterface = ( + kind: Kind.Interface +): Declarations => { + const { identifier } = kind; + if (!identifier) { + return empty(); + } + + const reference = Identifier.toReference(identifier); + + return { + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; +}; + +const mergeIdentifiersByContainer = ( + a: Declarations["identifiersByContainer"], + b: Declarations["identifiersByContainer"] +) => + ([...new Set([ + ...Object.keys(a), + ...Object.keys(b) + ])] as Identifier.Interface.Reference[]) + .map((containerReference: Identifier.Interface.Reference) => ({ + [containerReference]: new Set([ + ...(a[containerReference] || []), + ...(b[containerReference] || []) + ]) + })) + .reduce((a, b) => ({ ...a, ...b }), {}) + diff --git a/packages/abi-to-sol/src/index.ts b/packages/abi-to-sol/src/index.ts index 0c1f3c2..f1c5adc 100644 --- a/packages/abi-to-sol/src/index.ts +++ b/packages/abi-to-sol/src/index.ts @@ -1,6 +1,6 @@ -import "source-map-support/register"; - -export * as defaults from "./defaults"; -export {generateSolidity} from "./solidity"; -export {GenerateSolidityOptions, GenerateSolidityMode} from "./options"; - +export { + generateSolidity, + GenerateSolidityOptions, + GenerateSolidityMode, + defaults +} from "./solidity"; diff --git a/packages/abi-to-sol/src/parameter.ts b/packages/abi-to-sol/src/parameter.ts new file mode 100644 index 0000000..a5765d0 --- /dev/null +++ b/packages/abi-to-sol/src/parameter.ts @@ -0,0 +1,188 @@ +import * as Abi from "@truffle/abi-utils"; +import { abiTupleSignature } from "@truffle/abi-utils"; + +import { Type, isType } from "./type"; + +export type Parameter = Abi.Parameter & { + type: Type +}; + +export const isParameter = ( + parameter: Abi.Parameter +): parameter is Parameter => isType(parameter.type); + +export namespace Parameter { + export type Elementary = Abi.Parameter & { + type: Type.Elementary + }; + + export const isElementary = ( + parameter: Parameter + ): parameter is Elementary => Type.isElementary(parameter.type); + + export type UserDefinedValueType = Elementary & { + internalType: string + } + + export const isUserDefinedValueType = ( + parameter: Parameter + ): parameter is UserDefinedValueType => + isElementary(parameter) && + !!parameter.internalType && + parameter.internalType !== parameter.type && + UserDefinedValueType.internalTypePattern.test(parameter.internalType); + + export namespace UserDefinedValueType { + export const internalTypePattern = new RegExp( + /^(([a-zA-Z$_][a-zA-Z0-9$_]*)\.)?([a-zA-Z$_][a-zA-Z0-9$_]*)$/ + ); + + export type RecognizeResult

= ( + P extends Parameter.UserDefinedValueType + ? { + name: string; + scope?: string + } + : { + name: string; + scope?: string + } | undefined + ); + + export const recognize =

( + parameter: P + ): RecognizeResult

=> { + const { type, internalType } = parameter; + + if (!internalType || internalType === type) { + return undefined as RecognizeResult

; + } + + const match = internalType.match(internalTypePattern); + if (!match) { + return undefined as RecognizeResult

; + } + + const scope = match[2]; + const name = match[3]; + + return { + name, + ...( + scope + ? { scope } + : {} + ) + } as RecognizeResult

; + }; + } + + export type Array = Parameter & { + type: Type.Array + }; + + export const isArray = ( + parameter: Parameter + ): parameter is Parameter.Array => Type.isArray(parameter.type); + + export namespace Array { + export const item = ( + parameter: Parameter.Array + ): Parameter => { + const type = Type.Array.underlying(parameter.type); + + let internalType; + { + const match = (parameter.internalType || "").match(/^(.+)\[[^\]]*\]$/); + if (match) { + const [_, underlying] = match; + internalType = underlying; + } + } + + return { + ...parameter, + type, + ...( + internalType && internalType !== "tuple" + ? { internalType } + : {} + ) + }; + }; + + export type Static = Parameter.Array & { + type: Type.Array.Static + }; + + export const isStatic = ( + parameter: Parameter.Array + ): parameter is Parameter.Array.Static => + Type.Array.isStatic(parameter.type); + + export namespace Static { + export const length = ( + parameter: Parameter.Array.Static + ): number => Type.Array.length(parameter.type); + } + } + + export type Tuple = Parameter & { + type: Type.Tuple; + components: Exclude; + } + + export const isTuple = ( + parameter: Parameter + ): parameter is Parameter.Tuple => Type.isTuple(parameter.type); + + + export namespace Tuple { + export const internalTypePattern = new RegExp( + /^struct (([a-zA-Z$_][a-zA-Z0-9$_]*)\.)?([a-zA-Z$_][a-zA-Z0-9$_]*)$/ + ); + + export type TupleRecognizeResult = { + signature: string; + name?: string; + scope?: string + } + + export type RecognizeResult

= + P extends Parameter.Tuple + ? TupleRecognizeResult + : TupleRecognizeResult | undefined; + + export const recognize =

( + parameter: P + ): RecognizeResult

=> { + if (!Parameter.isTuple(parameter)) { + return undefined as RecognizeResult

; + } + + const signature = abiTupleSignature(parameter.components); + + if (!parameter.internalType) { + return { signature }; + } + + const match = parameter.internalType.match(internalTypePattern); + if (!match) { + return { signature }; + } + + const scope = match[2]; + const name = match[3]; + + return { + signature, + name, + ...( + scope + ? { scope } + : {} + ) + }; + }; + } +} diff --git a/packages/abi-to-sol/src/solidity.test.ts b/packages/abi-to-sol/src/solidity.test.ts deleted file mode 100644 index 1af80a2..0000000 --- a/packages/abi-to-sol/src/solidity.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as fc from "fast-check"; -import {testProp} from "jest-fast-check"; -import * as Abi from "@truffle/abi-utils"; -import * as Example from "../test/custom-example"; -import {compileAbi} from "../test/compile-abi"; -import {excludesFunctionParameters} from "../test/preflight"; - -import {generateSolidity} from "./solidity"; - -const removeProps = (obj: any, keys: Set) => { - if (obj instanceof Array) { - for (const item of obj) { - removeProps(item, keys); - } - } else if (typeof obj === "object") { - for (const [key, value] of Object.entries(obj)) { - if (keys.has(key)) { - delete obj[key]; - } else { - removeProps(obj[key], keys); - } - } - } - - return obj; -}; - -describe("generateSolidity", () => { - testProp("compiles to input ABI", [Abi.Arbitrary.Abi()], (abi) => { - fc.pre( - abi.every((entry) => "type" in entry && entry.type !== "constructor") - ); - fc.pre(excludesFunctionParameters(abi)); - - fc.pre(abi.length > 0); - - const output = generateSolidity({ - name: "MyInterface", - abi, - solidityVersion: "^0.8.4", - }); - - let resultAbi; - try { - resultAbi = compileAbi(output); - } catch (error) { - console.log("Failed to compile. Solidity:\n%s", output); - throw error; - } - - const compiledAbi = new Set( - removeProps(resultAbi, new Set(["internalType"])) - ); - - const expectedAbi = new Set(Abi.normalize(abi)); - - expect(compiledAbi).toEqual(expectedAbi); - }); - - describe("custom example", () => { - const abiWithoutConstructor = Abi.normalize( - Example.abi.filter(({type}) => type !== "constructor") - ); - - const output = generateSolidity({ - name: "Example", - abi: abiWithoutConstructor, - solidityVersion: "^0.8.4", - }); - - it("generates output", () => { - const compiledAbi = compileAbi(output); - - const expectedAbi = abiWithoutConstructor.map((entry) => ({ - ...entry, - type: entry.type || "function", - })); - - expect(compiledAbi).toEqual(expectedAbi); - }); - }); -}); diff --git a/packages/abi-to-sol/src/solidity.ts b/packages/abi-to-sol/src/solidity.ts deleted file mode 100644 index 352be94..0000000 --- a/packages/abi-to-sol/src/solidity.ts +++ /dev/null @@ -1,554 +0,0 @@ -import type Prettier from "prettier"; -import type * as Abi from "@truffle/abi-utils"; -import { abiTupleSignature } from "@truffle/abi-utils"; -import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; -import { version } from "../package.json"; -import {Visitor, VisitOptions, dispatch, Node} from "./visitor"; -import { forRange, VersionFeatures, mixed } from "./version-features"; -import { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; -import * as defaults from "./defaults"; -import { - Component, - Declaration, - Declarations, - collectDeclarations, -} from "./declarations"; -import { collectAbiFeatures, AbiFeatures } from "./abi-features"; - -let prettier: typeof Prettier -try { - prettier = require("prettier"); -} catch { - // no-op -} - -export const generateSolidity = ({ - abi, - name = defaults.name, - solidityVersion = defaults.solidityVersion, - license = defaults.license, - mode = defaults.mode, - outputAttribution = defaults.outputAttribution, - outputSource = defaults.outputSource, - prettifyOutput = prettier && defaults.prettifyOutput, -}: GenerateSolidityOptions) => { - if (!prettier && prettifyOutput) { - throw new Error("Could not require() prettier"); - } - - const versionFeatures = forRange(solidityVersion); - const abiFeatures = collectAbiFeatures(abi); - const declarations = collectDeclarations(abi); - - const generated = dispatch({ - node: abi, - visitor: new SolidityGenerator({ - name, - solidityVersion, - license, - mode, - outputAttribution, - outputSource, - versionFeatures, - abiFeatures, - declarations, - }), - }); - - if (!prettifyOutput) { - return generated; - } - - try { - return prettier.format(generated, { - plugins: ["prettier-plugin-solidity"], - // @ts-ignore - parser: "solidity-parse", - }); - } catch (error) { - return generated; - } -}; - -interface Context { - interfaceName?: string; - parameterModifiers?: (parameter: Abi.Parameter) => string[]; -} - -type Visit = VisitOptions; - -type ConstructorOptions = { - versionFeatures: VersionFeatures; - abiFeatures: AbiFeatures; - declarations: Declarations; -} & Required< - Omit ->; - -const shimGlobalInterfaceName = "__Structs"; - -class SolidityGenerator implements Visitor { - private name: string; - private license: string; - private mode: GenerateSolidityMode; - private solidityVersion: string; - private outputAttribution: boolean; - private outputSource: boolean; - private versionFeatures: VersionFeatures; - private abiFeatures: AbiFeatures; - private declarations: Declarations; - private identifiers: { - [signature: string]: { - identifier: string; - container?: string; - } - }; - - constructor({ - name, - license, - mode, - outputAttribution, - outputSource, - solidityVersion, - versionFeatures, - abiFeatures, - declarations, - }: ConstructorOptions) { - this.name = name; - this.license = license; - this.mode = mode; - this.solidityVersion = solidityVersion; - this.versionFeatures = versionFeatures; - this.abiFeatures = abiFeatures; - this.declarations = declarations; - this.outputAttribution = outputAttribution; - this.outputSource = outputSource; - - this.identifiers = {}; - let index = 0; - for (const [container, signatures] of Object.entries(declarations.containerSignatures)) { - for (const signature of signatures) { - const { - identifier = `S_${index++}` - } = declarations.signatureDeclarations[signature]; - - if (container === "" && this.versionFeatures["global-structs"] !== true) { - this.identifiers[signature] = { - container: shimGlobalInterfaceName, - identifier - }; - } else if (container === "") { - this.identifiers[signature] = { identifier }; - } else { - this.identifiers[signature] = { - container, - identifier - } - } - } - } - } - - visitAbi({node: abi}: Visit) { - switch (this.mode) { - case GenerateSolidityMode.Normal: { - return [ - this.generateHeader(), - this.generateInterface(abi), - this.generateDeclarations(), - this.generateAutogeneratedNotice(abi), - ].join("\n\n"); - } - case GenerateSolidityMode.Embedded: { - return [ - this.generateInterface(abi), - this.generateDeclarations(), - ].join("\n\n"); - } - } - } - - visitFunctionEntry({node: entry, context}: Visit): string { - const {name, inputs, stateMutability} = entry; - - return [ - `function ${name}(`, - entry.inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => - parameter.type.startsWith("tuple") || - parameter.type.includes("[") || - parameter.type === "bytes" || - parameter.type === "string" - ? [this.generateArrayParameterLocation(parameter)] - : [], - }, - }) - ), - `) external`, - this.generateStateMutability(entry), - entry.outputs && entry.outputs.length > 0 - ? [ - `returns (`, - entry.outputs - .map((node) => - dispatch({ - node, - visitor: this, - context: { - parameterModifiers: (parameter: Abi.Parameter) => - parameter.type.startsWith("tuple") || - parameter.type.includes("[") || - parameter.type === "bytes" || - parameter.type === "string" - ? ["memory"] - : [], - }, - }) - ) - .join(", "), - `)`, - ].join("") - : ``, - `;`, - ].join(" "); - } - - visitConstructorEntry({node: entry}: Visit): string { - // interfaces don't have constructors - return ""; - } - - visitFallbackEntry({ node: entry }: Visit): string { - const servesAsReceive = this.abiFeatures["defines-receive"] && - this.versionFeatures["receive-keyword"] !== true; - - const { stateMutability } = entry; - return `${this.generateFallbackName()} () external ${ - stateMutability === "payable" || servesAsReceive ? "payable" : "" - };`; - } - - visitReceiveEntry() { - // if version has receive, emit as normal - if (this.versionFeatures["receive-keyword"] === true) { - return `receive () external payable;`; - } - - // if this ABI defines a fallback separately, emit nothing, since - // visitFallbackEntry will cover it - if (this.abiFeatures["defines-fallback"]) { - return ""; - } - - // otherwise, explicitly invoke visitFallbackEntry - return this.visitFallbackEntry({ - node: { type: "fallback", stateMutability: "payable" }, - }); - } - - visitEventEntry({node: entry, context}: Visit): string { - const {name, inputs, anonymous} = entry; - - return [ - `event ${name}(`, - inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => - // TODO fix this - (parameter as Abi.EventParameter).indexed ? ["indexed"] : [], - }, - }) - ), - `)`, - `${anonymous ? "anonymous" : ""};`, - ].join(" "); - } - - visitErrorEntry({node: entry, context}: Visit): string { - if (this.versionFeatures["custom-errors"] !== true) { - throw new Error("ABI defines custom errors; use Solidity v0.8.4 or higher"); - } - - const {name, inputs} = entry; - - return [ - `error ${name}(`, - inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => [] - }, - }) - ), - `);`, - ].join(" "); - } - - visitParameter({node: parameter, context}: Visit) { - const type = this.generateType(parameter, context); - - // @ts-ignore - const {parameterModifiers} = context; - - return [type, ...parameterModifiers(parameter), parameter.name].join(" "); - } - - private generateHeader(): string { - const includeExperimentalPragma = - this.abiFeatures["needs-abiencoder-v2"] && - this.versionFeatures["abiencoder-v2"] !== "default"; - - const attribution = - !this.outputAttribution - ? [] - : [this.generateAttribution()] - - return [ - `// SPDX-License-Identifier: ${this.license}`, - ...attribution, - `pragma solidity ${this.solidityVersion};`, - ...( - includeExperimentalPragma - ? [`pragma experimental ABIEncoderV2;`] - : [] - ) - ].join("\n"); - } - - private generateAttribution(): string { - const unit = this.mode === GenerateSolidityMode.Normal - ? "FILE" - : "INTERFACE" - return this.outputSource - ? `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. SEE SOURCE BELOW. !!` - : `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. !!`; - } - - private generateAutogeneratedNotice(abi: Abi.Abi): string { - if (!this.outputSource) { - return ""; - } - - return [ - ``, - `// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON:`, - `/*`, - JSON.stringify(abi), - `*/`, - ].join("\n"); - } - - private generateDeclarations(): string { - if ( - this.versionFeatures["structs-in-interfaces"] !== true && - Object.keys(this.declarations.signatureDeclarations).length > 0 - ) { - throw new Error( - "abi-to-sol does not support custom struct types for this Solidity version" - ); - } - - const externalContainers = Object.keys(this.declarations.containerSignatures) - .filter(container => container !== "" && container !== this.name); - - const externalDeclarations = externalContainers - .map(container => [ - `interface ${container} {`, - this.generateDeclarationsForContainer(container), - `}` - ].join("\n")) - .join("\n\n"); - - const globalSignatures = this.declarations.containerSignatures[""] || []; - if (globalSignatures.length > 0) { - const declarations = this.versionFeatures["global-structs"] === true - ? this.generateDeclarationsForContainer("") - : [ - `interface ${shimGlobalInterfaceName} {`, - this.generateDeclarationsForContainer(""), - `}` - ].join("\n"); - - return [declarations, externalDeclarations].join("\n\n"); - } - - return externalDeclarations; - } - - private generateDeclarationsForContainer(container: string): string { - const signatures = new Set( - this.declarations.containerSignatures[container] - ); - - if (container === "" && this.versionFeatures["global-structs"] !== true) { - container = shimGlobalInterfaceName; - } - - return Object.entries(this.declarations.signatureDeclarations) - .filter(([signature]) => signatures.has(signature)) - .map(([signature, declaration]) => { - const { identifier } = this.identifiers[signature]; - const components = this.generateComponents(declaration, { interfaceName: container }); - - return `struct ${identifier} { ${components} }`; - }) - .join("\n\n"); - } - - private generateComponents( - declaration: Declaration, - context?: Pick - ): string { - return declaration.components - .map((component) => { - const {name} = component; - - return `${this.generateType(component, context)} ${name};`; - }) - .join("\n"); - } - - private generateType( - variable: Abi.Parameter | Component, - context: Pick = {} - ): string { - const signature = this.generateSignature(variable); - - if (!signature) { - return this.generateElementaryType(variable, context); - } - - const { type } = variable; - - const { container, identifier } = this.identifiers[signature]; - - if (container && container !== context.interfaceName) { - return type.replace("tuple", `${container}.${identifier}`); - } - - if (!container && this.versionFeatures["global-structs"] !== true) { - return type.replace("tuple", `${shimGlobalInterfaceName}.${identifier}`); - } - - return type.replace("tuple", identifier); - } - - private generateElementaryType( - variable: Abi.Parameter | Component, - context: Pick = {} - ): string { - // normally we can return the type itself, but functions are a special case - if (variable.type !== "function") { - return variable.type; - } - - // use just the `internalType` field if it exists - if ("internalType" in variable && variable.internalType) { - return variable.internalType; - } - - // otherwise output minimally syntactically-valid syntax with a warning - return [ - "/* warning: the following type may be incomplete. ", - "the receiving contract may expect additional input or output parameters. */ ", - "function() external" - ].join(""); - } - - - private generateSignature( - variable: Abi.Parameter | Component - ): string | undefined { - if ("signature" in variable && variable.signature) { - return variable.signature; - } - - if ("components" in variable && variable.components) { - return abiTupleSignature(variable.components); - } - } - - private generateStateMutability( - entry: - | Abi.FunctionEntry - | Abi.FallbackEntry - | Abi.ConstructorEntry - | Abi.ReceiveEntry - ): string { - if (entry.stateMutability && entry.stateMutability !== "nonpayable") { - return entry.stateMutability; - } - - return ""; - } - - private generateFallbackName(): string { - switch (this.versionFeatures["fallback-keyword"]) { - case true: { - return "fallback"; - } - case false: { - return "function"; - } - case mixed: { - throw new Error( - `Desired Solidity range lacks unambigious fallback syntax.` - ); - } - } - } - - private generateArrayParameterLocation(parameter: Abi.Parameter): string { - switch (this.versionFeatures["array-parameter-location"]) { - case undefined: { - return ""; - } - case mixed: { - throw new Error( - `Desired Solidity range lacks unambiguous location specifier for ` + - `parameter of type "${parameter.type}".` - ); - } - default: { - return this.versionFeatures["array-parameter-location"]; - } - } - } - - private generateInterface(abi: Abi.Abi): string { - return [ - `interface ${this.name} {`, - ...( - this.mode === GenerateSolidityMode.Embedded && this.outputAttribution - ? [this.generateAttribution()] - : [] - ), - this.generateDeclarationsForContainer(this.name), - ``, - ...abi.map((node) => dispatch({ - node, - context: { interfaceName: this.name }, - visitor: this - })), - ...( - this.mode === GenerateSolidityMode.Embedded - ? [this.generateAutogeneratedNotice(abi)] - : [] - ), - `}`, - ].join("\n"); - } -} diff --git a/packages/abi-to-sol/src/abi-features.ts b/packages/abi-to-sol/src/solidity/analyze.ts similarity index 68% rename from packages/abi-to-sol/src/abi-features.ts rename to packages/abi-to-sol/src/solidity/analyze.ts index 7710453..4193a2f 100644 --- a/packages/abi-to-sol/src/abi-features.ts +++ b/packages/abi-to-sol/src/solidity/analyze.ts @@ -1,40 +1,40 @@ import type { Abi as SchemaAbi } from "@truffle/contract-schema/spec"; import type * as Abi from "@truffle/abi-utils"; -import { Visitor, VisitOptions, dispatch, Node } from "./visitor"; +import { Visitor, VisitOptions, dispatch, Node } from "../visitor"; -export const allFeatures = [ +export const observableProperties = [ "defines-receive", "defines-fallback", "needs-abiencoder-v2", "defines-error", ] as const; -export type AbiFeature = typeof allFeatures[number]; -export type AbiFeatures = Partial<{ - [F in AbiFeature]: true +export type AbiProperty = typeof observableProperties[number]; +export type AbiProperties = Partial<{ + [F in AbiProperty]: true }>; -export const collectAbiFeatures = (node: SchemaAbi | Node) => +export const analyze = (node: SchemaAbi | Node) => dispatch({ node, - visitor: new AbiFeaturesCollector(), + visitor: new AbiPropertiesCollector(), }); -export class AbiFeaturesCollector implements Visitor { - visitAbi({ node: nodes }: VisitOptions): AbiFeatures { +export class AbiPropertiesCollector implements Visitor { + visitAbi({ node: nodes }: VisitOptions): AbiProperties { return nodes .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); } - visitEventEntry({ node: entry }: VisitOptions): AbiFeatures { + visitEventEntry({ node: entry }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); } - visitErrorEntry({ node: entry }: VisitOptions): AbiFeatures { + visitErrorEntry({ node: entry }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -42,7 +42,7 @@ export class AbiFeaturesCollector implements Visitor { visitFunctionEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return [...entry.inputs, ...(entry.outputs || [])] .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -50,7 +50,7 @@ export class AbiFeaturesCollector implements Visitor { visitConstructorEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -58,19 +58,19 @@ export class AbiFeaturesCollector implements Visitor { visitFallbackEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return { "defines-fallback": true }; } visitReceiveEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return { "defines-receive": true }; } visitParameter({ node: parameter, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { if ( parameter.type.startsWith("tuple") || // anything with tuples parameter.type.includes("string[") || // arrays of strings diff --git a/packages/abi-to-sol/src/defaults.ts b/packages/abi-to-sol/src/solidity/defaults.ts similarity index 100% rename from packages/abi-to-sol/src/defaults.ts rename to packages/abi-to-sol/src/solidity/defaults.ts diff --git a/packages/abi-to-sol/src/solidity/features.ts b/packages/abi-to-sol/src/solidity/features.ts new file mode 100644 index 0000000..1666572 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/features.ts @@ -0,0 +1,133 @@ +import * as semver from "semver"; + +export const allFeatures = { + "receive-keyword": { + ">=0.6.0": true, + "<0.6.0": false + }, + "fallback-keyword": { + ">=0.6.0": true, + "<0.6.0": false + }, + "array-parameter-location": { + ">=0.7.0": "memory", + "^0.5.0 || ^0.6.0": "calldata", + "<0.5.0": undefined + }, + "abiencoder-v2": { + ">=0.8.0": "default", + "<0.8.0": "experimental", + }, + "global-structs": { + ">=0.6.0": true, + "<0.6.0": false + }, + "structs-in-interfaces": { + ">=0.5.0": true, + "<0.5.0": false + }, + "custom-errors": { + ">=0.8.4": true, + "<0.8.4": false + }, + "user-defined-value-types": { + ">=0.8.8": true, + "<0.8.8": false + } +} as const; + +export type AllFeatures = typeof allFeatures; + +export type Category = keyof AllFeatures; + +export type CategorySpecification = AllFeatures[C]; + +export type CategoryOptionRange = string & { + [K in C]: keyof CategorySpecification +}[C]; + +export type CategoryOption = { + [K in C]: CategorySpecification[CategoryOptionRange] +}[C]; + +export type BooleanCategory = { + [C in Category]: CategoryOption extends boolean + ? C + : never +}[Category]; + +export const isBooleanCategory = ( + category: Category +): category is BooleanCategory => { + return Object.values(allFeatures[category]) + .every(option => option === true || option === false); +}; + +export type VersionsFeatures = { + [C in Category]: VersionsFeature; +}; + +export type VersionsFeature = + C extends BooleanCategory + ? { + supported(): boolean; + missing(): boolean; + varies(): boolean; + } + : { + consistently(option: CategoryOption): boolean; + }; + +export const forRange = (range: string | semver.Range): VersionsFeatures => { + return (Object.keys(allFeatures) as Category[]) + .map((category: C) => { + const specification = allFeatures[category]; + const matchingRanges: CategoryOptionRange[] = + (Object.keys(specification) as CategoryOptionRange[]) + .filter(optionRange => semver.intersects(range, optionRange)); + + const matchingOptions: CategoryOption[] = [...new Set( + matchingRanges.map(range => specification[range]) + )]; + + if (isBooleanCategory(category)) { + return { + [category]: { + supported() { + return ( + matchingOptions.length === 1 && + matchingOptions[0] === true as unknown as CategoryOption + ); + }, + + missing() { + return ( + matchingOptions.indexOf( + true as unknown as CategoryOption + ) === -1 + ) + }, + + varies() { + return matchingOptions.length > 1; + } + } + }; + } + + return { + [category]: { + consistently(option: CategoryOption) { + if (matchingOptions.length !== 1) { + return false; + } + + const [onlyOption] = matchingOptions; + return option === onlyOption; + } + } as VersionsFeature + }; + + }) + .reduce((a, b) => ({ ...a, ...b }), {}) as VersionsFeatures; +} diff --git a/packages/abi-to-sol/src/solidity/generate.ts b/packages/abi-to-sol/src/solidity/generate.ts new file mode 100644 index 0000000..f564a28 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/generate.ts @@ -0,0 +1,505 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { version } from "../../package.json"; +import { Declarations, Identifier, Kind } from "../declarations"; +import { Visitor, VisitOptions, dispatch, Node } from "../visitor"; +import type { VersionsFeatures } from "./features"; +import type { AbiProperties } from "./analyze"; +import { printType } from "./print"; + +import { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; + +interface Context { + interfaceName?: string; + parameterModifiers?: (parameter: Abi.Parameter) => string[]; +} + +type Visit = VisitOptions; + +type ConstructorOptions = { + versionsFeatures: VersionsFeatures; + abiProperties: AbiProperties; + declarations: Declarations; +} & Required< + Omit +>; + +const shimGlobalInterfaceName = "__Structs"; + +export const generateRawSolidity = ( + abi: GenerateSolidityOptions["abi"], + options: ConstructorOptions +) => dispatch({ node: abi, visitor: new SolidityGenerator(options) }); + +class SolidityGenerator implements Visitor { + private name: string; + private license: string; + private mode: GenerateSolidityMode; + private solidityVersion: string; + private outputAttribution: boolean; + private outputSource: boolean; + private versionsFeatures: VersionsFeatures; + private abiProperties: AbiProperties; + private declarations: Declarations; + + constructor({ + name, + license, + mode, + outputAttribution, + outputSource, + solidityVersion, + versionsFeatures, + abiProperties, + declarations + }: ConstructorOptions) { + this.name = name; + this.license = license; + this.mode = mode; + this.solidityVersion = solidityVersion; + this.versionsFeatures = versionsFeatures; + this.abiProperties = abiProperties; + this.declarations = declarations; + this.outputAttribution = outputAttribution; + this.outputSource = outputSource; + } + + visitAbi({node: abi}: Visit) { + switch (this.mode) { + case GenerateSolidityMode.Normal: { + return [ + this.generateHeader(), + this.generateInterface(abi), + this.generateExternals(), + this.generateSourceNotice(abi), + ].join("\n\n"); + } + case GenerateSolidityMode.Embedded: { + return [ + this.generateInterface(abi), + this.generateExternals(), + ].join("\n\n"); + } + } + } + + visitFunctionEntry({node: entry, context}: Visit): string { + const {name, inputs, stateMutability} = entry; + + return [ + `function ${name}(`, + entry.inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => + parameter.type.startsWith("tuple") || + parameter.type.includes("[") || + parameter.type === "bytes" || + parameter.type === "string" + ? [this.generateArrayParameterLocation(parameter)] + : [], + }, + }) + ), + `) external`, + this.generateStateMutability(entry), + entry.outputs && entry.outputs.length > 0 + ? [ + `returns (`, + entry.outputs + .map((node) => + dispatch({ + node, + visitor: this, + context: { + parameterModifiers: (parameter: Abi.Parameter) => + parameter.type.startsWith("tuple") || + parameter.type.includes("[") || + parameter.type === "bytes" || + parameter.type === "string" + ? ["memory"] + : [], + }, + }) + ) + .join(", "), + `)`, + ].join("") + : ``, + `;`, + ].join(" "); + } + + visitConstructorEntry({node: entry}: Visit): string { + // interfaces don't have constructors + return ""; + } + + visitFallbackEntry({ node: entry }: Visit): string { + const servesAsReceive = this.abiProperties["defines-receive"] && + !this.versionsFeatures["receive-keyword"].supported(); + + const { stateMutability } = entry; + return `${this.generateFallbackName()} () external ${ + stateMutability === "payable" || servesAsReceive ? "payable" : "" + };`; + } + + visitReceiveEntry() { + // if version has receive, emit as normal + if (this.versionsFeatures["receive-keyword"].supported()) { + return `receive () external payable;`; + } + + // if this ABI defines a fallback separately, emit nothing, since + // visitFallbackEntry will cover it + if (this.abiProperties["defines-fallback"]) { + return ""; + } + + // otherwise, explicitly invoke visitFallbackEntry + return this.visitFallbackEntry({ + node: { type: "fallback", stateMutability: "payable" }, + }); + } + + visitEventEntry({node: entry, context}: Visit): string { + const {name, inputs, anonymous} = entry; + + return [ + `event ${name}(`, + inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => + // TODO fix this + (parameter as Abi.EventParameter).indexed ? ["indexed"] : [], + }, + }) + ), + `)`, + `${anonymous ? "anonymous" : ""};`, + ].join(" "); + } + + visitErrorEntry({node: entry, context}: Visit): string { + if (!this.versionsFeatures["custom-errors"].supported()) { + throw new Error("ABI defines custom errors; use Solidity v0.8.4 or higher"); + } + + const {name, inputs} = entry; + + return [ + `error ${name}(`, + inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => [] + }, + }) + ), + `);`, + ].join(" "); + } + + visitParameter({ node: parameter, context }: Visit) { + const kind = Declarations.find(parameter, this.declarations); + const type = printType(kind, { + currentInterfaceName: context?.interfaceName, + enableGlobalStructs: + this.versionsFeatures["global-structs"].supported(), + enableUserDefinedValueTypes: + this.versionsFeatures["user-defined-value-types"].supported(), + shimGlobalInterfaceName + }); + + // @ts-ignore + const { parameterModifiers } = context; + + return [type, ...parameterModifiers(parameter), parameter.name].join(" "); + } + + private generateHeader(): string { + const includeExperimentalPragma = + this.abiProperties["needs-abiencoder-v2"] && + !this.versionsFeatures["abiencoder-v2"].consistently("default"); + + const attribution = + !this.outputAttribution + ? [] + : [this.generateAttribution()] + + return [ + `// SPDX-License-Identifier: ${this.license}`, + ...attribution, + `pragma solidity ${this.solidityVersion};`, + ...( + includeExperimentalPragma + ? [`pragma experimental ABIEncoderV2;`] + : [] + ) + ].join("\n"); + } + + private generateAttribution(): string { + const unit = this.mode === GenerateSolidityMode.Normal + ? "FILE" + : "INTERFACE" + return this.outputSource + ? `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. SEE SOURCE BELOW. !!` + : `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. !!`; + } + + private generateSourceNotice(abi: Abi.Abi): string { + if (!this.outputSource) { + return ""; + } + + return [ + ``, + `// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON:`, + `/*`, + JSON.stringify(abi), + `*/`, + ].join("\n"); + } + + private generateExternals(): string { + if ( + !this.versionsFeatures["structs-in-interfaces"].supported() && + Object.values(this.declarations.byIdentifierReference).some( + ({ identifier }) => identifier.class === "struct" + ) + ) { + throw new Error( + "abi-to-sol does not support custom struct types for this Solidity version" + ); + } + + const isDeclarable = + this.versionsFeatures["user-defined-value-types"].supported() + ? (kind: Kind): kind is Kind.Struct | Kind.UserDefinedValueType => Kind.isStruct(kind) || Kind.isUserDefinedValueType(kind) + : Kind.isStruct; + + const hasDifferentContainer = ( + kind: Kind.Struct | Kind.UserDefinedValueType + ) => + !!kind.identifier.container && + kind.identifier.container.name !== this.name; + + const externalDeclarations = Object.entries( + this.declarations.identifiersByContainer + ).flatMap((pair) => { + const [ + containerIdentifierReference, + identifierReferences + ] = pair as [ + Identifier.Interface.Reference, + Set + ]; + + const kinds = new Set( + [...identifierReferences] + .map((identifierReference) => + this.declarations.byIdentifierReference[identifierReference] + ).filter( + (kind): kind is ( + & { identifier: { container: Identifier.Interface } } + & ( + | Kind.Struct + | Kind.UserDefinedValueType + ) + ) => { + return isDeclarable(kind) && hasDifferentContainer(kind); + } + ) + ); + + if (!kinds.size) { + return [] + } + + const { identifier: { container } } = [...kinds][0]; + + return [{ + container, + kinds + }]; + }).map(({ container, kinds }) => [ + `interface ${container.name} {`, + this.generateSiblingDeclarations(kinds, { + interfaceName: container.name + }), + `}` + ].join("\n")).join("\n\n"); + + const globalKinds = new Set([ + ...this.declarations.globalIdentifiers + ].flatMap(reference => { + const kind = this.declarations.byIdentifierReference[reference]; + + if (isDeclarable(kind)) { + return [kind]; + } + + return []; + })); + + if (globalKinds.size > 0) { + const globalDeclarations = this.generateSiblingDeclarations(globalKinds); + + return [ + externalDeclarations, + this.versionsFeatures["global-structs"].supported() + ? globalDeclarations + : [ + `interface ${shimGlobalInterfaceName} {`, + globalDeclarations, + `}` + ].join("\n") + ].join("\n\n"); + } + + return externalDeclarations; + } + + private generateSiblingDeclarations( + kinds: Set, + context: Pick = {} + ): string { + return [...kinds] + .map(kind => { + if (Kind.isStruct(kind)) { + return this.generateStructDeclaration(kind, context) + } + + if ( + this.versionsFeatures["user-defined-value-types"].supported() && + Kind.isUserDefinedValueType(kind) + ) { + return this.generateUserDefinedValueTypeDefinition(kind, context); + } + }) + .join("\n\n"); + } + + private generateStructDeclaration( + kind: Kind.Struct, + context: Pick = {} + ): string { + return [ + `struct ${kind.identifier.name} {`, + ...kind.members.map(({ name, kind: memberKind}) => + `${ + printType(memberKind, { + currentInterfaceName: context.interfaceName, + enableGlobalStructs: + this.versionsFeatures["global-structs"].supported(), + enableUserDefinedValueTypes: + this.versionsFeatures["user-defined-value-types"].supported(), + shimGlobalInterfaceName + }) + } ${name};` + ), + `}` + ].join("\n"); + } + + private generateUserDefinedValueTypeDefinition( + kind: Kind.UserDefinedValueType, + context: Pick = {} + ): string { + return [ + `type ${kind.identifier.name} is ${kind.type};` + ].join("\n"); + } + + + private generateStateMutability( + entry: + | Abi.FunctionEntry + | Abi.FallbackEntry + | Abi.ConstructorEntry + | Abi.ReceiveEntry + ): string { + if (entry.stateMutability && entry.stateMutability !== "nonpayable") { + return entry.stateMutability; + } + + return ""; + } + + private generateFallbackName(): string { + if (this.versionsFeatures["fallback-keyword"].supported()) { + return "fallback"; + } + + if (this.versionsFeatures["fallback-keyword"].missing()) { + return "function"; + } + + throw new Error( + `Desired Solidity range lacks unambigious fallback syntax.` + ); + } + + private generateArrayParameterLocation(parameter: Abi.Parameter): string { + const location = this.versionsFeatures["array-parameter-location"]; + + if (location.consistently(undefined)) { + return ""; + } + + if (location.consistently("memory")) { + return "memory"; + } + + if (location.consistently("calldata")) { + return "calldata"; + } + + throw new Error( + `Desired Solidity range lacks unambiguous location specifier for ` + + `parameter of type "${parameter.type}".` + ); + } + + private generateInterface(abi: Abi.Abi): string { + const kinds = new Set([ + ...(this.declarations.identifiersByContainer[ + Identifier.toReference({ class: "interface", name: this.name }) + ] || []) + ].map(reference => this.declarations.byIdentifierReference[reference])); + + return [ + `interface ${this.name} {`, + ...( + this.mode === GenerateSolidityMode.Embedded && this.outputAttribution + ? [this.generateAttribution()] + : [] + ), + this.generateSiblingDeclarations(kinds, { interfaceName: this.name }), + ``, + ...abi.map((node) => dispatch({ + node, + context: { interfaceName: this.name }, + visitor: this + })), + ...( + this.mode === GenerateSolidityMode.Embedded + ? [this.generateSourceNotice(abi)] + : [] + ), + `}`, + ].join("\n"); + } +} diff --git a/packages/abi-to-sol/src/solidity/index.test.ts b/packages/abi-to-sol/src/solidity/index.test.ts new file mode 100644 index 0000000..fea54fe --- /dev/null +++ b/packages/abi-to-sol/src/solidity/index.test.ts @@ -0,0 +1,185 @@ +import * as fc from "fast-check"; +import { testProp } from "jest-fast-check"; +import * as Abi from "@truffle/abi-utils"; +import * as Example from "../../test/custom-example"; +import { compileAbi } from "../../test/compile-abi"; +import { excludesFunctionParameters } from "../../test/preflight"; + +import { generateSolidity } from "."; +import { GenerateSolidityMode } from "./options"; + +const removeProps = (obj: any, keys: Set) => { + if (obj instanceof Array) { + for (const item of obj) { + removeProps(item, keys); + } + } else if (typeof obj === "object") { + for (const [key, value] of Object.entries(obj)) { + if (keys.has(key)) { + delete obj[key]; + } else { + removeProps(obj[key], keys); + } + } + } + + return obj; +}; + +describe("generateSolidity", () => { + testProp("respects output settings", [ + Abi.Arbitrary.Abi(), + fc.constantFrom(GenerateSolidityMode.Normal, GenerateSolidityMode.Embedded), + fc.boolean(), // outputAttribution + fc.boolean(), // outputSource + ], (abi, mode, outputAttribution, outputSource) => { + fc.pre(abi.length > 0); + + const output = generateSolidity({ + name: "MyInterface", + abi, + solidityVersion: "^0.8.20", + mode, + outputAttribution, + outputSource + }); + + const attributionOutput = output.indexOf("abi-to-sol") !== -1; + const sourceOutput = output.indexOf("FROM THE FOLLOWING ABI JSON:") !== -1; + const modeOutput = output.indexOf("SPDX-License-Identifier") === -1 + ? GenerateSolidityMode.Embedded + : GenerateSolidityMode.Normal; + + expect(mode).toEqual(modeOutput); + expect(outputAttribution).toEqual(attributionOutput); + expect(outputSource).toEqual(sourceOutput); + }); + + testProp("compiles to input ABI", [Abi.Arbitrary.Abi()], (abi) => { + fc.pre( + abi.every((entry) => "type" in entry && entry.type !== "constructor") + ); + fc.pre(excludesFunctionParameters(abi)); + + fc.pre(abi.length > 0); + + const output = generateSolidity({ + name: "MyInterface", + abi, + solidityVersion: "^0.8.20", + }); + + let resultAbi; + try { + resultAbi = compileAbi(output); + } catch (error) { + console.log("Failed to compile. Solidity:\n%s", output); + throw error; + } + + const compiledAbi = new Set( + removeProps(resultAbi, new Set(["internalType"])) + ); + + const expectedAbi = new Set(Abi.normalize(abi)); + + expect(compiledAbi).toEqual(expectedAbi); + }, + ); + + describe("custom example", () => { + const abiWithoutConstructor = Abi.normalize( + Example.abi.filter(({type}) => type !== "constructor") + ); + + const output = generateSolidity({ + name: "Example", + abi: abiWithoutConstructor, + solidityVersion: "^0.8.4", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + + const expectedAbi = abiWithoutConstructor.map((entry) => ({ + ...entry, + type: entry.type || "function", + })); + + expect(compiledAbi).toEqual(expectedAbi); + }); + }); + + describe("function pointers", () => { + const abi: Abi.Abi = [ + { + "inputs": [ + { + "internalType": "function (uint256) external returns (uint256)", + "name": "f", + "type": "function" + }, + { + "internalType": "uint256[]", + "name": "l", + "type": "uint256[]" + } + ], + "name": "map", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ]; + + const output = generateSolidity({ + name: "Example", + abi, + solidityVersion: "^0.8.13", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + expect(compiledAbi).toEqual(abi); + }); + }); + + describe("UDVT support", () => { + const abi: Abi.Abi = [ + { + "inputs": [ + { + "internalType": "Int", + "name": "i", + "type": "int256" + }, + { + "internalType": "Example.Uint", + "name": "u", + "type": "uint256" + }, + { + "internalType": "Other.Bool", + "name": "b", + "type": "bool" + } + ], + "name": "fnoo", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ]; + + const output = generateSolidity({ + name: "Example", + abi, + solidityVersion: "^0.8.13", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + expect(compiledAbi).toEqual(abi); + }); + }); +}); diff --git a/packages/abi-to-sol/src/solidity/index.ts b/packages/abi-to-sol/src/solidity/index.ts new file mode 100644 index 0000000..d92b7a6 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/index.ts @@ -0,0 +1,64 @@ +import type Prettier from "prettier"; + +import { Declarations } from "../declarations"; + +import * as defaults from "./defaults"; +import * as Features from "./features"; +import { GenerateSolidityOptions } from "./options"; +import { generateRawSolidity } from "./generate"; +import { analyze } from "./analyze"; + +export { defaults }; +export { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; + +let prettier: typeof Prettier +try { + prettier = require("prettier"); +} catch { + // no-op +} + +export const generateSolidity = ({ + abi, + name = defaults.name, + solidityVersion = defaults.solidityVersion, + license = defaults.license, + mode = defaults.mode, + outputAttribution = defaults.outputAttribution, + outputSource = defaults.outputSource, + prettifyOutput = prettier && defaults.prettifyOutput, +}: GenerateSolidityOptions) => { + if (!prettier && prettifyOutput) { + throw new Error("Could not require() prettier"); + } + + const versionsFeatures = Features.forRange(solidityVersion); + const abiProperties = analyze(abi); + const declarations = Declarations.collect(abi); + + const raw = generateRawSolidity(abi, { + name, + solidityVersion, + license, + mode, + outputAttribution, + outputSource, + versionsFeatures, + abiProperties, + declarations + }); + + if (!prettifyOutput) { + return raw; + } + + try { + return prettier.format(raw, { + plugins: ["prettier-plugin-solidity"], + // @ts-ignore + parser: "solidity-parse", + }); + } catch (error) { + return raw; + } +}; diff --git a/packages/abi-to-sol/src/options.ts b/packages/abi-to-sol/src/solidity/options.ts similarity index 99% rename from packages/abi-to-sol/src/options.ts rename to packages/abi-to-sol/src/solidity/options.ts index 95d3627..f901ebd 100644 --- a/packages/abi-to-sol/src/options.ts +++ b/packages/abi-to-sol/src/solidity/options.ts @@ -16,4 +16,3 @@ export interface GenerateSolidityOptions { outputSource?: boolean; prettifyOutput?: boolean; } - diff --git a/packages/abi-to-sol/src/solidity/print.ts b/packages/abi-to-sol/src/solidity/print.ts new file mode 100644 index 0000000..207c804 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/print.ts @@ -0,0 +1,150 @@ +import { Kind } from "../declarations"; + +export const printType = ( + kind: Kind, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + const { + currentInterfaceName, + enableUserDefinedValueTypes = false, + enableGlobalStructs = false, + shimGlobalInterfaceName + } = options; + + if (Kind.isUserDefinedValueType(kind)) { + return printUserDefinedValueTypeType(kind, { + currentInterfaceName, + enableUserDefinedValueTypes + }); + } + + if (Kind.isElementary(kind)) { + return printElementaryType(kind); + } + + if (Kind.isStruct(kind)) { + return printStructType(kind, { + currentInterfaceName, + enableGlobalStructs, + shimGlobalInterfaceName + }); + } + + if (Kind.isArray(kind)) { + return printArrayType(kind, options); + } + + throw new Error(`Unexpectedly unsupported kind: ${JSON.stringify(kind)}`); +} + +const printUserDefinedValueTypeType = ( + kind: Kind.UserDefinedValueType, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + } = {} +): string => { + const { + currentInterfaceName, + enableUserDefinedValueTypes = false + } = options; + + const result = ( + kind.identifier.container && + kind.identifier.container.name !== currentInterfaceName + ) + ? `${kind.identifier.container.name}.${kind.identifier.name}` + : kind.identifier.name; + + if (!enableUserDefinedValueTypes) { + return [ + `/* warning: missing UDVT support in source Solidity version; `, + `parameter is \`${result}\`. */ `, + kind.type + ].join(""); + } + + return result; +}; + +const printElementaryType = ( + kind: Kind.Elementary, + options: {} = {} +): string => { + if (kind.type !== "function") { + return kind.type; + } + + // use just the `internalType` field if it exists + if (kind.hints?.internalType) { + return kind.hints.internalType; + } + + // otherwise output minimally syntactically-valid syntax with a warning + return [ + "/* warning: the following type may be incomplete. ", + "the receiving contract may expect additional input or output parameters. */ ", + "function() external" + ].join(""); +} + +const printStructType = ( + kind: Kind.Struct, + options: { + currentInterfaceName?: string, + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + const { + currentInterfaceName, + enableGlobalStructs = false, + shimGlobalInterfaceName + } = options; + + if (!enableGlobalStructs && !shimGlobalInterfaceName) { + throw new Error( + "Option `shimGlobalInterfaceName` is required without global structs" + ) + } + + if ( + kind.identifier.container && + kind.identifier.container.name !== currentInterfaceName + ) { + return `${kind.identifier.container.name}.${kind.identifier.name}`; + } + + if ( + !kind.identifier.container && + currentInterfaceName && + !enableGlobalStructs + ) { + return `${shimGlobalInterfaceName}.${kind.identifier.name}`; + } + + return kind.identifier.name; +} + +const printArrayType = ( + kind: Kind.Array, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + if (Kind.Array.isDynamic(kind)) { + return `${printType(kind.itemKind, options)}[]`; + } + + // static case + return `${printType(kind.itemKind, options)}[${kind.length}]`; +} + diff --git a/packages/abi-to-sol/src/type.test.ts b/packages/abi-to-sol/src/type.test.ts new file mode 100644 index 0000000..a4d3aa3 --- /dev/null +++ b/packages/abi-to-sol/src/type.test.ts @@ -0,0 +1,15 @@ +import { isType } from "./type"; + +describe("isType", () => { + it("recognizes dynamic arrays", () => { + expect(isType("uint256[]")).toEqual(true); + }); + + it("recognizes static arrays", () => { + expect(isType("uint256[1]")).toEqual(true); + }); + + it("recognizes arrays of arrays", () => { + expect(isType("ufixed256x18[][]")).toEqual(true); + }); +}); diff --git a/packages/abi-to-sol/src/type.ts b/packages/abi-to-sol/src/type.ts new file mode 100644 index 0000000..f628fcc --- /dev/null +++ b/packages/abi-to-sol/src/type.ts @@ -0,0 +1,305 @@ +export type Octal = + | "8" + | "16" + | "24" + | "32" + | "48" + | "56" + | "64" + | "72" + | "80" + | "88" + | "96" + | "104" + | "112" + | "120" + | "128" + | "136" + | "144" + | "152" + | "160" + | "168" + | "176" + | "184" + | "192" + | "200" + | "208" + | "216" + | "224" + | "232" + | "240" + | "248" + | "256"; + +export const isOctal = (expression: string): expression is Octal => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer % 8 === 0 && integer >= 8 && integer <= 256; +} + +export type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; + +// gotta do some janky stuff to represent the natural / whole numbers +// even still, `Whole` will disagree with `isWhole` at times (shhhh it's ok) +export type Digits = `${bigint}` & `${Digit}${string}`; +export type Natural = + & Digits + & `${Exclude}${string}`; +export type Whole = Natural | "0"; +export const isWhole = ( + expression: string +): expression is Whole => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer >= 0; +}; + +export type NaturalLessThanEqualToEighty = + | Exclude // 1-9 + | `${Exclude}${Digit}` // 10-79 + | "80"; + +export const isNaturalLessThanEqualToEighty = ( + expression: string +): expression is NaturalLessThanEqualToEighty => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer > 0 && integer <= 80; +} + +export type NaturalLessThanEqualToThirtyTwo = + | Exclude // 1-9 + | `${"1" | "2"}${Digit}` // 10-29 + | "30" + | "31" + | "32"; + +export const isNaturalLessThanEqualToThirtyTwo = ( + expression: string +): expression is NaturalLessThanEqualToThirtyTwo => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer > 0 && integer <= 32; +} + +export namespace Type { + export namespace Elementary { + export type Uint = "uint" | `uint${Octal}`; + export const isUint = ( + expression: string + ): expression is Uint => + expression === "uint" || ( + expression.startsWith("uint") && + isOctal(expression.slice(4)) + ); + + export type Int = "int" | `int${Octal}`; + export const isInt = ( + expression: string + ): expression is Int => + expression === "int" || ( + expression.startsWith("int") && + isOctal(expression.slice(3)) + ); + + export type Address = "address"; + export const isAddress = ( + expression: string + ): expression is Address => + expression === "address"; + + export type Bool = "bool"; + export const isBool = ( + expression: string + ): expression is Bool => + expression === "bool"; + + export type Ufixed = "ufixed" | `ufixed${Octal}x${NaturalLessThanEqualToEighty}`; + export const isUfixed = ( + expression: string + ): expression is Ufixed => { + if (expression === "ufixed") { + return true; + } + + const match = expression.match(/^ufixed([^x]+)x([^x]+)$/); + if (!match) { + return false; + } + + const [_, m, n] = match; + return isOctal(m) && isNaturalLessThanEqualToEighty(n); + } + + export type Fixed = "fixed" | `fixed${Octal}x${NaturalLessThanEqualToEighty}`; + export const isFixed = ( + expression: string + ): expression is Fixed => { + if (expression === "fixed") { + return true; + } + + const match = expression.match(/^fixed([^x]+)x([^x]+)$/); + if (!match) { + return false; + } + + const [_, m, n] = match; + return isOctal(m) && isNaturalLessThanEqualToEighty(n); + } + + export type StaticBytes = `bytes${NaturalLessThanEqualToThirtyTwo}`; + export const isStaticBytes = ( + expression: string + ): expression is StaticBytes => + expression.startsWith("bytes") && + isNaturalLessThanEqualToThirtyTwo(expression.slice(5)); + + export type Bytes = "bytes"; + export const isBytes = ( + expression: string + ): expression is Bytes => + expression === "bytes"; + + export type String = "string"; + export const isString = ( + expression: string + ): expression is String => + expression === "string"; + + export type Function = "function"; + export const isFunction = ( + expression: string + ): expression is Function => + expression === "function"; + } + + export type Elementary = + | Elementary.Uint + | Elementary.Int + | Elementary.Address + | Elementary.Bool + | Elementary.Ufixed + | Elementary.Fixed + | Elementary.StaticBytes + | Elementary.Bytes + | Elementary.String + | Elementary.Function; + + export const isElementary = ( + expression: string + ): expression is Elementary => + Elementary.isUint(expression) || + Elementary.isInt(expression) || + Elementary.isAddress(expression) || + Elementary.isBool(expression) || + Elementary.isUfixed(expression) || + Elementary.isFixed(expression) || + Elementary.isStaticBytes(expression) || + Elementary.isBytes(expression) || + Elementary.isString(expression) || + Elementary.isFunction(expression); + + export namespace Array { + export type Static = `${string}[${Whole}]`; + export const isStatic = ( + expression: string + ): expression is Static => { + const match = expression.match(/^(.+)\[([0-9]+)\]$/); + if (!match) { + return false; + } + + const [_, underlying, length] = match; + return isType(underlying) && isWhole(length); + }; + + export const length = ( + type: Static + ): number => { + const match = type.match(/\[([0-9]+)\]$/); + if (!match) { + throw new Error( + `Unexpected mismatch, type \`${type}\` is not a valid static array` + ); + } + + const [_, length] = match; + return parseInt(length, 10); + } + + export type Dynamic = `${string}[]`; + export const isDynamic = ( + expression: string + ): expression is Dynamic => { + const match = expression.match(/^(.+)\[\]$/); + if (!match) { + return false; + } + + const [_, underlying] = match; + return isType(underlying); + } + + export const underlying = ( + type: Array + ): Type => { + const match = type.match(/^(.+)\[[^\]]*\]$/); + if (!match) { + throw new Error( + `Unexpected mismatch, \`${type}\` is not a valid array type` + ); + } + + const [_, underlying] = match; + if (!isType(underlying)) { + throw new Error( + `Underlying type \`${underlying}\` is not a valid type` + ); + + } + + return underlying; + } + } + + export type Array = + | Array.Static + | Array.Dynamic; + + export const isArray = ( + expression: string + ): expression is Array => + Array.isStatic(expression) || + Array.isDynamic(expression); + + export type Tuple = "tuple"; + export const isTuple = ( + expression: string + ): expression is Tuple => + expression === "tuple"; +} + +export type Type = + | Type.Elementary + | Type.Array + | Type.Tuple; + +export const isType = ( + expression: string +): expression is Type => + Type.isElementary(expression) || + Type.isArray(expression) || + Type.isTuple(expression); diff --git a/packages/abi-to-sol/src/version-features.ts b/packages/abi-to-sol/src/version-features.ts deleted file mode 100644 index 40471d2..0000000 --- a/packages/abi-to-sol/src/version-features.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as semver from "semver"; - -export const mixed: unique symbol = Symbol(); - -export const allFeatures = { - "receive-keyword": { - ">=0.6.0": true, - "<0.6.0": false - }, - "fallback-keyword": { - ">=0.6.0": true, - "<0.6.0": false - }, - "array-parameter-location": { - ">=0.7.0": "memory", - "^0.5.0 || ^0.6.0": "calldata", - "<0.5.0": undefined - }, - "abiencoder-v2": { - ">=0.8.0": "default", - "<0.8.0": "experimental", - }, - "global-structs": { - ">=0.6.0": true, - "<0.6.0": false - }, - "structs-in-interfaces": { - ">=0.5.0": true, - "<0.5.0": false - }, - "custom-errors": { - ">=0.8.4": true, - "<0.8.4": false - }, - "user-defined-value-types": { - ">=0.8.8": true, - "<0.8.8": false - } -} as const; - -export type AllFeatures = typeof allFeatures; - -export type Category = keyof AllFeatures; - -export type CategoryOptions = AllFeatures[C]; - -export type CategoryOptionRange = string & { - [K in C]: keyof CategoryOptions -}[C]; - -export type CategoryOption = { - [K in C]: CategoryOptions[CategoryOptionRange] -}[C]; - -export type VersionFeatures = { - [C in Category]: VersionFeature; -}; - -export type VersionFeature = - CategoryOption | typeof mixed; - -export const forRange = (range: string | semver.Range): VersionFeatures => { - const forCategory = ( - category: C - ): VersionFeature => { - const options = allFeatures[category]; - const matchingRanges: CategoryOptionRange[] = - (Object.keys(options) as CategoryOptionRange[]) - .filter(optionRange => semver.intersects(range, optionRange)); - - if (matchingRanges.length > 1) { - return mixed; - } - - const [matchingRange] = matchingRanges; - return options[matchingRange]; - } - - return (Object.keys(allFeatures) as Category[]) - .map((category: C) => ({ [category]: forCategory(category) })) - .reduce((a, b) => ({ ...a, ...b }), {}) as VersionFeatures; -} diff --git a/packages/abi-to-sol/src/visitor.ts b/packages/abi-to-sol/src/visitor.ts index a3bc07c..aff12b5 100644 --- a/packages/abi-to-sol/src/visitor.ts +++ b/packages/abi-to-sol/src/visitor.ts @@ -18,13 +18,14 @@ export interface Visitor { } export interface DispatchOptions { - node: Node | SchemaAbi; + node: Node; visitor: Visitor; context?: C; } export type Node = | Abi.Abi + | SchemaAbi | Abi.Entry | Abi.FunctionEntry | Abi.ConstructorEntry @@ -70,6 +71,7 @@ const isAbi = (node: Node | SchemaAbi): node is Abi.Abi | SchemaAbi => const isEntry = (node: Node): node is Abi.Entry => typeof node === "object" && "type" in node && + typeof node.type === "string" && ["function", "constructor", "fallback", "receive", "event", "error"].includes( node.type ) && diff --git a/packages/abi-to-sol/test/custom-example.ts b/packages/abi-to-sol/test/custom-example.ts index df70025..14a2fcc 100644 --- a/packages/abi-to-sol/test/custom-example.ts +++ b/packages/abi-to-sol/test/custom-example.ts @@ -99,23 +99,3 @@ export const expectedSignatures: {[name: string]: string} = { Foo: "((uint256,uint256)[],uint256)", Bar: "(uint256,uint256)", }; - -export const expectedDeclarations = { - Foo: { - bars: { - type: "tuple[]", - signature: expectedSignatures.Bar, - }, - c: { - type: "uint256", - }, - }, - Bar: { - a: { - type: "uint256", - }, - b: { - type: "uint256", - }, - }, -}; diff --git a/packages/abi-to-sol/tsconfig.json b/packages/abi-to-sol/tsconfig.json index d29e6e4..90aaa4e 100644 --- a/packages/abi-to-sol/tsconfig.json +++ b/packages/abi-to-sol/tsconfig.json @@ -6,7 +6,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": ["es2017"], /* Specify library files to be included in the compilation. */ + "lib": ["es2019"], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ @@ -28,7 +28,7 @@ "strict": true, /* Enable all strict type-checking options. */ "resolveJsonModule": true, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ + "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 43a457e..0c86f04 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -34,7 +34,7 @@ "@types/react-dom": "^17.0.0", "@typescript-eslint/eslint-plugin": "^4.5.0", "@typescript-eslint/parser": "^4.5.0", - "abi-to-sol": "^0.7.1", + "abi-to-sol": "^0.8.0-0", "add": "^2.0.6", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.0", @@ -94,7 +94,7 @@ "style-loader": "1.3.0", "terser-webpack-plugin": "4.2.3", "ts-pnp": "1.2.0", - "typescript": "4.5.2", + "typescript": "^4.9.5", "unstated-next": "^1.1.0", "url-loader": "4.1.1", "web-vitals": "^1.0.1", diff --git a/packages/web-ui/src/abi/Examples.ts b/packages/web-ui/src/abi/Examples.ts index 00bf66b..e4487b2 100644 --- a/packages/web-ui/src/abi/Examples.ts +++ b/packages/web-ui/src/abi/Examples.ts @@ -8,6 +8,7 @@ import DepositContract from "./examples/DepositContract.abi.json"; import UniswapV2RouterO2 from "./examples/UniswapV2Router02.abi.json"; import AirSwap from "./examples/AirSwap.abi.json"; import BunchaStructs from "./examples/BunchaStructs.abi.json"; +import UDVTs from "./examples/UDVTs.abi.json"; export interface Example { name: string; @@ -40,6 +41,11 @@ export const examples: { [exampleName: string]: Example } = { name: "BunchaStructs", license: "", contents: JSON.stringify(BunchaStructs, undefined, 2) + }, + udvts: { + name: "UDVTs", + license: "", + contents: JSON.stringify(UDVTs, undefined, 2) } }; diff --git a/packages/web-ui/src/abi/examples/UDVTs.abi.json b/packages/web-ui/src/abi/examples/UDVTs.abi.json new file mode 100644 index 0000000..1460fbb --- /dev/null +++ b/packages/web-ui/src/abi/examples/UDVTs.abi.json @@ -0,0 +1,25 @@ +[ + { + "inputs": [ + { + "internalType": "Int", + "name": "i", + "type": "int256" + }, + { + "internalType": "UDVTs.Uint", + "name": "u", + "type": "uint256" + }, + { + "internalType": "Other.Bool", + "name": "b", + "type": "bool" + } + ], + "name": "fnoo", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/packages/web-ui/src/solidity/OptionsControls.tsx b/packages/web-ui/src/solidity/OptionsControls.tsx index 457eb6e..2788926 100644 --- a/packages/web-ui/src/solidity/OptionsControls.tsx +++ b/packages/web-ui/src/solidity/OptionsControls.tsx @@ -62,6 +62,7 @@ export const OptionsControls = () => { setSolidityVersion(event.target.value) }} > + diff --git a/yarn.lock b/yarn.lock index a8f251a..1151533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -287,6 +287,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.0.0": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.5.tgz#821bb520118fd25b982eaf8d37421cf5c64a312b" + integrity sha512-J+IxH2IsxV4HbnTrSWgMAQj0UEo61hDA4Ny8h8PCX0MLXiibqHbqIOVneqdocemSBc22VpBKxt4J6FQzy9HarQ== + "@babel/parser@^7.1.0", "@babel/parser@^7.12.3", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.0": version "7.14.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" @@ -3300,9 +3305,9 @@ "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" @@ -3402,6 +3407,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/lodash.mergewith@4.6.6": version "4.6.6" resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10" @@ -3649,6 +3659,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.4.tgz#41acbd79b5816b7c0dd7530a43d97d020d3aeb42" integrity sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + "@typescript-eslint/typescript-estree@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" @@ -3676,6 +3691,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@^4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/visitor-keys@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" @@ -3691,6 +3719,14 @@ "@typescript-eslint/types" "4.28.4" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -4079,6 +4115,11 @@ anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -4266,6 +4307,16 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-module-types@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" + integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== + +ast-module-types@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-3.0.0.tgz#9a6d8a80f438b6b8fe4995699d700297f398bf81" + integrity sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ== + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -4541,7 +4592,7 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4621,6 +4672,15 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blakejs@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" @@ -4868,6 +4928,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" @@ -5263,6 +5331,11 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-spinners@^2.5.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== + cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -5380,7 +5453,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -5426,12 +5499,7 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== - -commander@^2.20.0: +commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5451,6 +5519,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -6193,6 +6266,13 @@ debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.0.0, debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -6217,9 +6297,9 @@ decimal.js@^10.2.1: integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" @@ -6238,6 +6318,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -6325,6 +6410,17 @@ depd@^1.1.2, depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +dependency-tree@^8.1.1: + version "8.1.2" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-8.1.2.tgz#c9e652984f53bd0239bc8a3e50cbd52f05b2e770" + integrity sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A== + dependencies: + commander "^2.20.3" + debug "^4.3.1" + filing-cabinet "^3.0.1" + precinct "^8.0.0" + typescript "^3.9.7" + deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -6376,6 +6472,90 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" +detective-amd@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357" + integrity sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ== + dependencies: + ast-module-types "^3.0.0" + escodegen "^2.0.0" + get-amd-module-type "^3.0.0" + node-source-walk "^4.2.0" + +detective-cjs@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.3.tgz#50e107d67b37f459b0ec02966ceb7e20a73f268b" + integrity sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +detective-es6@^2.2.0, detective-es6@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.2.tgz#ee5f880981d9fecae9a694007029a2f6f26d8d28" + integrity sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw== + dependencies: + node-source-walk "^4.0.0" + +detective-less@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" + integrity sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA== + dependencies: + debug "^4.0.0" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-postcss@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-4.0.0.tgz#24e69b465e5fefe7a6afd05f7e894e34595dbf51" + integrity sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A== + dependencies: + debug "^4.1.1" + is-url "^1.2.4" + postcss "^8.1.7" + postcss-values-parser "^2.0.1" + +detective-postcss@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-5.1.3.tgz#773314cd017621b7d382be81331eb0c7abbe8cc3" + integrity sha512-Wo7PUpF6wqeT1aRgajdyIdDRjFFJVxlXPRAlT1aankH/RVOgrJuEZFZ4ABxYXdzaRPO5Lkg8rHxsxpLnxdJIYA== + dependencies: + is-url "^1.2.4" + postcss "^8.4.6" + postcss-values-parser "^5.0.0" + +detective-sass@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-3.0.2.tgz#e0f35aac79a4d2f6409c284d95b8f7ecd5973afd" + integrity sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-scss@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.2.tgz#7d2a642616d44bf677963484fa8754d9558b8235" + integrity sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-stylus@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.3.tgz#20a702936c9fd7d4203fd7a903314b5dd43ac713" + integrity sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q== + +detective-typescript@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-7.0.2.tgz#c6e00b4c28764741ef719662250e6b014a5f3c8e" + integrity sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA== + dependencies: + "@typescript-eslint/typescript-estree" "^4.33.0" + ast-module-types "^2.7.1" + node-source-walk "^4.2.0" + typescript "^3.9.10" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -6677,6 +6857,14 @@ enhanced-resolve@^4.3.0: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^5.8.3: + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -7079,7 +7267,7 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= ethereum-bloom-filters@^1.0.6: version "1.0.10" @@ -7340,7 +7528,7 @@ faker@^5.1.0: resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== -fast-check@3.1.1, fast-check@^3.1.1: +fast-check@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.1.1.tgz#72c5ae7022a4e86504762e773adfb8a5b0b01252" integrity sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA== @@ -7441,6 +7629,25 @@ filesize@6.1.0: resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== +filing-cabinet@^3.0.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-3.3.1.tgz#45d87bb273a0e0a7dd6ac6bac9111059186e2e9c" + integrity sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA== + dependencies: + app-module-path "^2.2.0" + commander "^2.20.3" + debug "^4.3.3" + enhanced-resolve "^5.8.3" + is-relative-path "^1.0.2" + module-definition "^3.3.1" + module-lookup-amd "^7.0.1" + resolve "^1.21.0" + resolve-dependency-path "^2.0.0" + sass-lookup "^3.0.0" + stylus-lookup "^3.0.1" + tsconfig-paths "^3.10.1" + typescript "^3.9.7" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -7645,17 +7852,6 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" - integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" - fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -7755,6 +7951,14 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-amd-module-type@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0" + integrity sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.2.2" + get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -7978,7 +8182,14 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: +gonzales-pe@^4.2.3, gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -7988,6 +8199,13 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphviz@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" + integrity sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg== + dependencies: + temp "~0.4.0" + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -8423,7 +8641,7 @@ identity-obj-proxy@3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -8544,7 +8762,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: +ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8742,6 +8960,13 @@ is-core-module@^2.0.0, is-core-module@^2.2.0, is-core-module@^2.4.0: dependencies: has "^1.0.3" +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -8847,6 +9072,11 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -8955,6 +9185,11 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= +is-relative-path@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" + integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== + is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -9018,6 +9253,16 @@ is-upper-case@^1.1.0: dependencies: upper-case "^1.1.0" +is-url-superb@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" + integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -9712,20 +9957,13 @@ json5@2.x, json5@^2.1.2, json5@^2.2.0: dependencies: minimist "^1.2.5" -json5@^1.0.1: +json5@^1.0.1, json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -9771,9 +10009,9 @@ jsprim@^1.2.2: object.assign "^4.1.2" keccak@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + version "3.0.3" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" + integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== dependencies: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" @@ -9808,13 +10046,6 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= - optionalDependencies: - graceful-fs "^4.1.9" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -10175,6 +10406,34 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +madge@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/madge/-/madge-5.0.2.tgz#d34527af7e96de9625e8069902667c4c5a073ada" + integrity sha512-OeqFIgugINbVqh6keLWePD/N3u1EEYS3O9gCTD+EjcuaJa1TH30jcCxr8CEl3+neS1VM8sDCQSYoln/2li3ceg== + dependencies: + chalk "^4.1.1" + commander "^7.2.0" + commondir "^1.0.1" + debug "^4.3.1" + dependency-tree "^8.1.1" + detective-amd "^3.1.0" + detective-cjs "^3.1.1" + detective-es6 "^2.2.0" + detective-less "^1.0.2" + detective-postcss "^5.0.0" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^7.0.0" + graphviz "0.0.9" + ora "^5.4.1" + pluralize "^8.0.0" + precinct "^8.1.0" + pretty-ms "^7.0.1" + rc "^1.2.7" + typescript "^3.9.5" + walkdir "^0.4.1" + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -10521,6 +10780,11 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -10648,6 +10912,25 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +module-definition@^3.3.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.4.0.tgz#953a3861f65df5e43e80487df98bb35b70614c2b" + integrity sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +module-lookup-amd@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" + integrity sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + glob "^7.1.6" + requirejs "^2.3.5" + requirejs-config-file "^4.0.0" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -10728,6 +11011,11 @@ nanoid@^3.1.23: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -10822,9 +11110,9 @@ node-forge@^0.10.0: integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== node-gyp-build@^4.2.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-gyp@^5.0.2: version "5.1.1" @@ -10915,6 +11203,13 @@ node-releases@^1.1.61, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.3.0.tgz#8336b56cfed23ac5180fe98f1e3bb6b11fd5317c" + integrity sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA== + dependencies: + "@babel/parser" "^7.0.0" + nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -11329,6 +11624,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -11567,6 +11877,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + parse-path@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" @@ -11673,7 +11988,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -11711,6 +12026,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -11797,6 +12117,11 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -12468,6 +12793,15 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-values-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-5.0.0.tgz#10c61ac3f488e4de25746b829ea8d8894e9ac3d2" + integrity sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw== + dependencies: + color-name "^1.1.4" + is-url-superb "^4.0.0" + quote-unquote "^1.0.0" + postcss@7.0.36, postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.36" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" @@ -12486,6 +12820,43 @@ postcss@^8.1.0: nanoid "^3.1.23" source-map-js "^0.6.2" +postcss@^8.1.7: + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8.4.6: + version "8.4.24" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" + integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +precinct@^8.0.0, precinct@^8.1.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-8.3.1.tgz#94b99b623df144eed1ce40e0801c86078466f0dc" + integrity sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q== + dependencies: + commander "^2.20.3" + debug "^4.3.3" + detective-amd "^3.1.0" + detective-cjs "^3.1.1" + detective-es6 "^2.2.1" + detective-less "^1.0.2" + detective-postcss "^4.0.0" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^7.0.0" + module-definition "^3.3.1" + node-source-walk "^4.2.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -12556,6 +12927,13 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -12709,9 +13087,9 @@ punycode@^2.1.0, punycode@^2.1.1: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pure-rand@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" - integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== + version "5.0.5" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.5.tgz#bda2a7f6a1fc0f284d78d78ca5902f26f2ad35cf" + integrity sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw== q@^1.1.2, q@^1.5.1: version "1.5.1" @@ -12778,6 +13156,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quote-unquote@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" + integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== + raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -12815,6 +13198,16 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-app-polyfill@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf" @@ -13088,6 +13481,15 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -13270,7 +13672,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.0, require-from-string@^2.0.2: +require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -13280,6 +13682,19 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requirejs-config-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" + integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== + dependencies: + esprima "^4.0.0" + stringify-object "^3.2.1" + +requirejs@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" + integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -13304,6 +13719,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-dependency-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" + integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -13356,6 +13776,15 @@ resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.1 is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.21.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -13418,7 +13847,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13573,6 +14002,13 @@ sass-loader@^10.0.5: schema-utils "^3.0.0" semver "^7.3.2" +sass-lookup@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" + integrity sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg== + dependencies: + commander "^2.16.0" + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -13684,9 +14120,9 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== dependencies: lru-cache "^6.0.0" @@ -13998,18 +14434,16 @@ socks@^2.3.3: ip "^1.1.5" smart-buffer "^4.1.0" -solc@^0.8.6: - version "0.8.6" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.6.tgz#e4341fa6780137df97b94a0cfbd59b3f2037d0e0" - integrity sha512-miiDaWdaUnD7A6Cktb/2ug9f+ajcOCDYRr7vgbPEsMoutSlBtp5rca57oMg8iHSuM7jilwdxePujWI/+rbNftQ== +solc@^0.8.20: + version "0.8.20" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.20.tgz#b49151cf5ecc8de088d3d32b0afb607b3522ba8d" + integrity sha512-fPRnGspIEqmhu63RFO3pc79sLA7ZmzO0Uy0L5l6hEt2wAsq0o7UV6pXkAp3Mfv9IBhg7Px/oTu3a+y4gs3BWrQ== dependencies: command-exists "^1.2.8" - commander "3.0.2" + commander "^8.1.0" follow-redirects "^1.12.1" - fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" - require-from-string "^2.0.0" semver "^5.5.0" tmp "0.0.33" @@ -14049,6 +14483,11 @@ source-map-js@^0.6.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -14422,7 +14861,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.3.0: +stringify-object@^3.2.1, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -14513,6 +14952,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -14552,6 +14996,14 @@ stylis@^4.0.3, stylis@^4.0.6: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg== +stylus-lookup@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" + integrity sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14581,6 +15033,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -14635,6 +15092,11 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + tar@^4.4.12: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -14676,6 +15138,11 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" +temp@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" + integrity sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA== + tempy@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.3.0.tgz#6f6c5b295695a16130996ad5ab01a8bd726e8bf8" @@ -14976,6 +15443,16 @@ ts-pnp@1.2.0, ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +tsconfig-paths@^3.10.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tsconfig-paths@^3.9.0: version "3.10.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" @@ -15108,10 +15585,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +typescript@^3.9.10, typescript@^3.9.5, typescript@^3.9.7: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== uglify-js@^3.1.4: version "3.13.10" @@ -15467,6 +15949,11 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +walkdir@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -15506,7 +15993,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=