diff --git a/web/docusaurus.config.ts b/web/docusaurus.config.ts
index 2ffae62e..5cec5cfc 100644
--- a/web/docusaurus.config.ts
+++ b/web/docusaurus.config.ts
@@ -45,7 +45,7 @@ const config: Config = {
rules: [
{
test: /\.yaml$/,
- use: "yaml-loader"
+ use: "raw-loader"
}
]
}
diff --git a/web/package.json b/web/package.json
index cae7a5e0..23f5b004 100644
--- a/web/package.json
+++ b/web/package.json
@@ -30,13 +30,13 @@
"react-dom": "^18.0.0",
"react-markdown": "^9.0.1",
"yaml": "^2.3.4",
- "yaml-loader": "^0.8.0",
"yaml-template": "^1.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.0.1",
"@docusaurus/tsconfig": "^3.0.1",
"@docusaurus/types": "^3.0.1",
+ "raw-loader": "^4.0.2",
"typescript": "~5.2.2"
},
"browserslist": {
diff --git a/web/spec/type/base.mdx b/web/spec/type/base.mdx
index 50fb6272..a1d11ce3 100644
--- a/web/spec/type/base.mdx
+++ b/web/spec/type/base.mdx
@@ -141,28 +141,28 @@ A type reference is an object containing the single `"id"` field. This field
must be a string or a number.
+ schema={{ id: "schema:ethdebug/format/type/base" }}
+ pointer="#/$defs/TypeReference" />
#### Type wrapper schema
- typeof item === "object" &&
- !("$schema" in item) &&
- item["$ref"] === "#/$defs/Type"}
- transform={
- ({ $ref, ...rest }, root) => ({
- ...rest,
- type: "object",
- title: root.$defs.Type.title + " [RECURSIVE]",
- description: "The root Type schema"
- })
- }
- />
+ schema={{ id: "schema:ethdebug/format/type/base" }}
+ pointer="#/$defs/TypeWrapper"
+ detect={
+ (item) =>
+ typeof item === "object" &&
+ !("$schema" in item) &&
+ item["$ref"] === "#/$defs/Type"}
+ transform={
+ ({ $ref, ...rest }, root) => ({
+ ...rest,
+ type: "object",
+ title: root.$defs.Type.title + " [RECURSIVE]",
+ description: "The root Type schema"
+ })
+ }
+ />
### ComplexType's `"contains"` field
@@ -175,99 +175,95 @@ As described [above](#type-wrappers-and-type-references), complex types compose
other types. This composition occurs inside the `"contains"` field for all
complex types.
-#### Example complex types to show different forms
+
+ Example complex types to show different forms
+
+
+ This is an example array type, which composes exactly one other type.
-
-
- This is an example array type, which composes exactly one other type.
-
- ```json
- {
- "kind": "array",
- "contains": {
- "type": {
- "kind": "uint",
- "bits": 256
- }
- }
- }
- ```
-
-
- This is an example array type, which composes an ordered list of member
- types.
-
- ```json
- {
- "kind": "struct",
- "contains": [{
- "member": "balance",
- "type": {
- "kind": "uint",
- "bits": 256
- }
- }, {
- "member": "scoreSheet",
- "type": {
- "id": ""
- }
- }]
- }
- ```
-
- In this example, please note how this struct type represents member names
- with a `"member"` field alongside the `"type"` field, and note how the
- value of `"type"` can be either a complete representation or a reference
- object in the form of `{ id }`.
-
-
- This is an example mapping type, which composes an object mapping of types
- by key.
- ```json
- {
- "kind": "mapping",
- "contains": {
- "key": {
+ ```json
+ {
+ "kind": "array",
+ "contains": {
"type": {
- "kind": "address"
+ "kind": "uint",
+ "bits": 256
}
- },
- "value": {
+ }
+ }
+ ```
+
+
+ This is an example array type, which composes an ordered list of member
+ types.
+
+ ```json
+ {
+ "kind": "struct",
+ "contains": [{
+ "member": "balance",
"type": {
"kind": "uint",
"bits": 256
}
+ }, {
+ "member": "scoreSheet",
+ "type": {
+ "id": ""
+ }
+ }]
+ }
+ ```
+
+ In this example, please note how this struct type represents member names
+ with a `"member"` field alongside the `"type"` field, and note how the
+ value of `"type"` can be either a complete representation or a reference
+ object in the form of `{ id }`.
+
+
+ This is an example mapping type, which composes an object mapping of types
+ by key.
+ ```json
+ {
+ "kind": "mapping",
+ "contains": {
+ "key": {
+ "type": {
+ "kind": "address"
+ }
+ },
+ "value": {
+ "type": {
+ "kind": "uint",
+ "bits": 256
+ }
+ }
}
}
- }
- ```
-
-
+ ```
+
+
+
## Full base schema
- typeof item === "object" &&
- !("$schema" in item) &&
- item["$ref"] === "schema:ethdebug/format/type/base"}
- transform={
- ({ $ref, ...rest }, root) => ({
- ...rest,
- type: "object",
- title: root.title + " [RECURSIVE]",
- description: "The root Type schema"
- })
- }
- />
+ schema={{ id: "schema:ethdebug/format/type/base" }}
+ detect={
+ (item) =>
+ typeof item === "object" &&
+ !("$schema" in item) &&
+ item["$ref"] === "schema:ethdebug/format/type/base"}
+ transform={
+ ({ $ref, ...rest }, root) => ({
+ ...rest,
+ type: "object",
+ title: root.title + " [RECURSIVE]",
+ description: "The root Type schema"
+ })
+ }
+ />
+
## Example schema extensions for particular types
These examples show valid schemas that extend **ethdebug/format/types/base**
diff --git a/web/spec/type/type.mdx b/web/spec/type/type.mdx
index a3a7d210..ccab15d8 100644
--- a/web/spec/type/type.mdx
+++ b/web/spec/type/type.mdx
@@ -4,7 +4,6 @@ sidebar_position: 2
import SchemaViewer from "@site/src/components/SchemaViewer";
import { Collapsible, CreateTypes } from "@theme/JSONSchemaViewer/components";
-import { schemas } from "@site/src/loadSchema";
# ethdebug/format/type [placeholder]
diff --git a/web/src/components/SchemaListing.tsx b/web/src/components/SchemaListing.tsx
new file mode 100644
index 00000000..b76ae1a3
--- /dev/null
+++ b/web/src/components/SchemaListing.tsx
@@ -0,0 +1,66 @@
+import YAML from "yaml";
+import {
+ type DescribeSchemaOptions,
+ describeSchema
+} from "@site/src/schemas";
+import CodeBlock from "@theme/CodeBlock";
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
+export interface SchemaListingProps extends DescribeSchemaOptions {
+}
+
+export default function SchemaListing(props: SchemaListingProps): JSX.Element {
+ const {
+ id: qualifiedId,
+ pointer,
+ schema,
+ yaml
+ } = describeSchema(props);
+
+ const id =
+ qualifiedId
+ ? qualifiedId.startsWith("schema:")
+ ? qualifiedId.slice(7)
+ : qualifiedId
+ : undefined;
+
+ const reference = id && pointer
+ ? `${id}${pointer}`
+ : id
+ ? id
+ : undefined;
+
+ return (
+
+
+ {
+ yaml
+ }
+
+
+ {
+ JSON.stringify(schema, undefined, 2)
+ }
+
+
+ );
+}
diff --git a/web/src/components/SchemaViewer.tsx b/web/src/components/SchemaViewer.tsx
index 08cbc7ef..12dc20ff 100644
--- a/web/src/components/SchemaViewer.tsx
+++ b/web/src/components/SchemaViewer.tsx
@@ -1,16 +1,19 @@
import JSONSchemaViewer from "@theme/JSONSchemaViewer";
import CodeBlock from "@theme/CodeBlock";
-import { loadSchema } from "@site/src/loadSchema";
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import {
+ type DescribeSchemaOptions,
+ describeSchema
+} from "@site/src/schemas";
import ReactMarkdown from "react-markdown";
+import SchemaListing from "./SchemaListing";
-export interface SchemaViewerProps {
- schema: string | object;
- pointer?: string;
+export interface SchemaViewerProps extends DescribeSchemaOptions {
detect?: (item: object) => boolean;
transform?: (item: object, root: object) => object;
}
-
export const transformObject = (
obj: object,
predicate: (item: object) => boolean,
@@ -39,58 +42,70 @@ export const transformObject = (
return process(obj);
}
-export default function SchemaViewer({
- schema: schemaName,
- pointer = "",
- detect = () => false,
- transform = (x) => x
-}: SchemaViewerProps): JSX.Element {
- const rawSchema = typeof schemaName === "string"
- ? loadSchema(schemaName)
- : schemaName;
- const schema = transformObject(
+export default function SchemaViewer(props: SchemaViewerProps): JSX.Element {
+ const {
+ schema: rawSchema,
+ yaml,
+ pointer
+ } = describeSchema(props);
+
+ const {
+ detect = () => false,
+ transform = (x) => x
+ } = props;
+
+ const transformedSchema = transformObject(
rawSchema,
detect,
transform
);
return (
- {
- const schema = loadSchema(uri.toString());
- return schema;
-
+
+
+ {
+ const { schema } = describeSchema({
+ schema: {
+ id: uri.toString()
+ }
+ });
+ return schema;
+ }
+ }
}
- }
- }
- }}
- viewerOptions={{
- showExamples: true,
- ValueComponent: ({ value }) => {
- // deal with simple types first
- if ([
- "string",
- "number",
- "bigint",
- "boolean"
- ].includes(typeof value)) {
- return {
- (value as string | number | bigint | boolean).toString()
- }
;
- }
+ }}
+ viewerOptions={{
+ showExamples: true,
+ ValueComponent: ({ value }) => {
+ // deal with simple types first
+ if ([
+ "string",
+ "number",
+ "bigint",
+ "boolean"
+ ].includes(typeof value)) {
+ return {
+ (value as string | number | bigint | boolean).toString()
+ }
;
+ }
- // for complex types use a whole CodeBlock
- return {`${
- JSON.stringify(value, undefined, 2)
- }`};
- },
- DescriptionComponent: ({description}) =>
-
- }} />
+ // for complex types use a whole CodeBlock
+ return {`${
+ JSON.stringify(value, undefined, 2)
+ }`};
+ },
+ DescriptionComponent: ({description}) =>
+
+ }} />
+
+
+
+
+
);
}
diff --git a/web/src/css/custom.css b/web/src/css/custom.css
index ab6677e9..f8ab5609 100644
--- a/web/src/css/custom.css
+++ b/web/src/css/custom.css
@@ -29,6 +29,11 @@
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
+.schema-listing pre {
+ max-height: 500px;
+ overflow: auto;
+}
+
.navbar .draft-warning {
font-weight: bold;
text-decoration: underline;
diff --git a/web/src/loadSchema.ts b/web/src/loadSchema.ts
deleted file mode 100644
index 7dcff92f..00000000
--- a/web/src/loadSchema.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import typeBaseSchemaYaml from "../../schemas/type/base.schema.yaml";
-
-
-export const schemas = [
- typeBaseSchemaYaml,
-].map(schema => ({
- [schema.$id]: schema
-})).reduce((a, b) => ({ ...a, ...b }), {});
-
-export const loadSchema = (id) => {
- const schema = schemas[id];
- return schema;
-}
diff --git a/web/src/schemas.ts b/web/src/schemas.ts
new file mode 100644
index 00000000..f22bb04b
--- /dev/null
+++ b/web/src/schemas.ts
@@ -0,0 +1,194 @@
+import YAML from "yaml";
+
+import typeBaseSchemaYaml from "../../schemas/type/base.schema.yaml";
+
+export const schemaYamls = [
+ typeBaseSchemaYaml,
+].map(schema => ({
+ [YAML.parse(schema).$id]: schema
+})).reduce((a, b) => ({ ...a, ...b }), {});
+
+export interface DescribeSchemaOptions<
+ S extends SchemaReference = SchemaReference
+> {
+ schema: S;
+ pointer?: SchemaPointer;
+};
+
+export interface DescribedSchema {
+ id?: string; // root ID only
+ pointer: SchemaPointer; // normalized from root ID
+ schema: object;
+ yaml: string;
+}
+
+export function describeSchema({
+ schema,
+ pointer
+}: DescribeSchemaOptions): DescribedSchema {
+ if (typeof pointer === "string" && !pointer.startsWith("#")) {
+ throw new Error("`pointer` option must start with '#'");
+ }
+
+ return referencesId(schema)
+ ? describeSchemaById({ schema, pointer })
+ : referencesYaml(schema)
+ ? describeSchemaByYaml({ schema, pointer })
+ : describeSchemaByObject({ schema, pointer });
+}
+
+function describeSchemaById({
+ schema: { id: referencedId },
+ pointer: relativePointer
+}: DescribeSchemaOptions): DescribedSchema {
+ // we need to handle the case where the schema is referenced by an ID
+ // with a pointer specified, possibly with a separate `pointer` field too
+ const [id, rawReferencedPointer] = referencedId.split("#");
+
+ const pointer = rawReferencedPointer
+ ? joinSchemaPointers([`#${rawReferencedPointer}`, relativePointer])
+ : relativePointer;
+
+ const rootYaml = schemaYamls[id]
+ if (!rootYaml) {
+ throw new Error(`Unknown schema with $id "${id}"`);
+ }
+
+ const yaml = pointToYaml(rootYaml, pointer);
+
+ const schema = YAML.parse(yaml);
+
+ return {
+ id,
+ pointer,
+ yaml,
+ schema
+ }
+}
+
+function describeSchemaByYaml({
+ schema: { yaml: referencedYaml },
+ pointer
+}: DescribeSchemaOptions): DescribedSchema {
+ const yaml = pointToYaml(referencedYaml, pointer);
+
+ const schema = YAML.parse(yaml);
+
+ const id = schema.$id;
+
+ if (id) {
+ return {
+ id,
+ pointer,
+ yaml,
+ schema
+ }
+ } else {
+ return {
+ pointer,
+ yaml,
+ schema
+ }
+ }
+}
+
+function describeSchemaByObject({
+ schema: referencedSchema,
+ pointer
+}: DescribeSchemaOptions