Repository: bytecodealliance/wasm-tools
Pull request #1203 (2023-09-14 19:32:56)

Add Serialize and JSON serialization support to the exported structs in wit-parser
This PR is an updated version of #1177 which adds a `Serialize` implementation to all the exported structs of the `wit-parser` crate so that it can output JSON from a fully-resolved WIT file.

This follows the discussion from [bytecodealliance/wit-bindgen#640](https://github.com/bytecodealliance/wit-bindgen/issues/640)

## Usage:

`wasm-tools component wit --json`

## Testing

`cargo test -p wit-parser`

This verifies the serialized JSON against the `*.json` files in the tests/ui directory.

To regenerate the golden JSON files, run:

`BLESS=1 cargo test -p wit-parser`

Thanks to @Mossaka for the initial PR and @alexcrichton for the review!


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 9047ce01..e7c6686a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2397,6 +2397,8 @@ dependencies = [
  "pulldown-cmark",
  "rayon",
  "semver",
+ "serde",
+ "serde_json",
  "unicode-xid",
  "url",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 90738fed..3a243ede 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -170,6 +170,7 @@ component = [
   'wast',
   'wasm-encoder',
   'dep:wasmparser',
+  'serde_json',
 ]
 metadata = ['dep:wasmparser', 'wasm-metadata', 'serde_json']
 wit-smith = ['dep:wit-smith', 'arbitrary']
diff --git a/crates/wit-parser/Cargo.toml b/crates/wit-parser/Cargo.toml
index f426267f..5b3b5a95 100644
--- a/crates/wit-parser/Cargo.toml
+++ b/crates/wit-parser/Cargo.toml
@@ -15,12 +15,14 @@ Tooling for parsing `*.wit` files and working with their contents.
 [dependencies]
 id-arena = "2"
 anyhow = { workspace = true }
-indexmap = { workspace = true }
+indexmap = { workspace = true, features = ["serde"] }
 pulldown-cmark = { version = "0.9.3", default-features = false }
 unicode-xid = "0.2.2"
 log = { workspace = true }
 url = { workspace = true }
 semver = { workspace = true }
+serde.workspace = true
+serde_json = "1.0.105"
 
 [dev-dependencies]
 rayon = "1"
diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs
index 57a1455e..b1bb6370 100644
--- a/crates/wit-parser/src/lib.rs
+++ b/crates/wit-parser/src/lib.rs
@@ -2,6 +2,7 @@ use anyhow::{Context, Result};
 use id_arena::{Arena, Id};
 use indexmap::IndexMap;
 use semver::Version;
+use serde::Serialize;
 use std::borrow::Cow;
 use std::fmt;
 use std::path::Path;
@@ -16,6 +17,10 @@ mod resolve;
 pub use resolve::{Package, PackageId, Remap, Resolve};
 mod live;
 pub use live::LiveTypes;
+mod serde_;
+use serde_::{
+    serialize_anon_result, serialize_id, serialize_id_map, serialize_optional_id, serialize_params,
+};
 
 /// Checks if the given string is a legal identifier in wit.
 pub fn validate_id(s: &str) -> Result<()> {
@@ -101,9 +106,12 @@ pub struct UnresolvedPackage {
     required_resource_types: Vec<(TypeId, Span)>,
 }
 
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum AstItem {
+    #[serde(serialize_with = "serialize_id")]
     Interface(InterfaceId),
+    #[serde(serialize_with = "serialize_id")]
     World(WorldId),
 }
 
@@ -112,7 +120,8 @@ pub enum AstItem {
 ///
 /// This is directly encoded as an "ID" in the binary component representation
 /// with an interfaced tacked on as well.
-#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize)]
+#[serde(into = "String")]
 pub struct PackageName {
     /// A namespace such as `wasi` in `wasi:foo/bar`
     pub namespace: String,
@@ -122,6 +131,12 @@ pub struct PackageName {
     pub version: Option<Version>,
 }
 
+impl From<PackageName> for String {
+    fn from(name: PackageName) -> String {
+        name.to_string()
+    }
+}
+
 impl PackageName {
     /// Returns the ID that this package name would assign the `interface` name
     /// specified.
@@ -231,12 +246,13 @@ impl UnresolvedPackage {
     }
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize)]
 pub struct World {
     /// The WIT identifier name of this world.
     pub name: String,
 
     /// Documentation associated with this world declaration.
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
 
     /// All imported items into this interface, both worlds and functions.
@@ -246,12 +262,15 @@ pub struct World {
     pub exports: IndexMap<WorldKey, WorldItem>,
 
     /// The package that owns this world.
+    #[serde(serialize_with = "serialize_optional_id")]
     pub package: Option<PackageId>,
 
     /// All the included worlds from this world. Empty if this is fully resolved
+    #[serde(skip)]
     pub includes: Vec<WorldId>,
 
     /// All the included worlds names. Empty if this is fully resolved
+    #[serde(skip)]
     pub include_names: Vec<Vec<IncludeName>>,
 }
 
@@ -266,7 +285,8 @@ pub struct IncludeName {
 
 /// The key to the import/export maps of a world. Either a kebab-name or a
 /// unique interface.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
+#[serde(into = "String")]
 pub enum WorldKey {
     /// A kebab-name.
     Name(String),
@@ -274,6 +294,15 @@ pub enum WorldKey {
     Interface(InterfaceId),
 }
 
+impl From<WorldKey> for String {
+    fn from(key: WorldKey) -> String {
+        match key {
+            WorldKey::Name(name) => name,
+            WorldKey::Interface(id) => format!("interface-{}", id.index()),
+        }
+    }
+}
+
 impl WorldKey {
     /// Asserts that this is `WorldKey::Name` and returns the name.
     #[track_caller]
@@ -285,10 +314,12 @@ impl WorldKey {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum WorldItem {
     /// An interface is being imported or exported from a world, indicating that
     /// it's a namespace of functions.
+    #[serde(serialize_with = "serialize_id")]
     Interface(InterfaceId),
 
     /// A function is being directly imported or exported from this world.
@@ -297,10 +328,11 @@ pub enum WorldItem {
     /// A type is being exported from this world.
     ///
     /// Note that types are never imported into worlds at this time.
+    #[serde(serialize_with = "serialize_id")]
     Type(TypeId),
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize)]
 pub struct Interface {
     /// Optionally listed name of this interface.
     ///
@@ -308,30 +340,35 @@ pub struct Interface {
     pub name: Option<String>,
 
     /// Documentation associated with this interface.
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
 
     /// Exported types from this interface.
     ///
     /// Export names are listed within the types themselves. Note that the
     /// export name here matches the name listed in the `TypeDef`.
+    #[serde(serialize_with = "serialize_id_map")]
     pub types: IndexMap<String, TypeId>,
 
     /// Exported functions from this interface.
     pub functions: IndexMap<String, Function>,
 
     /// The package that owns this interface.
+    #[serde(serialize_with = "serialize_optional_id")]
     pub package: Option<PackageId>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct TypeDef {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub kind: TypeDefKind,
     pub name: Option<String>,
     pub owner: TypeOwner,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum TypeDefKind {
     Record(Record),
     Resource,
@@ -379,20 +416,26 @@ impl TypeDefKind {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum TypeOwner {
     /// This type was defined within a `world` block.
+    #[serde(serialize_with = "serialize_id")]
     World(WorldId),
     /// This type was defined within an `interface` block.
+    #[serde(serialize_with = "serialize_id")]
     Interface(InterfaceId),
     /// This type wasn't inherently defined anywhere, such as a `list<T>`, which
     /// doesn't need an owner.
     None,
 }
 
-#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum Handle {
+    #[serde(serialize_with = "serialize_id")]
     Own(TypeId),
+    #[serde(serialize_with = "serialize_id")]
     Borrow(TypeId),
 }
 
@@ -414,7 +457,7 @@ pub enum Type {
     Id(TypeId),
 }
 
-#[derive(PartialEq, Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum Int {
     U8,
     U16,
@@ -422,30 +465,33 @@ pub enum Int {
     U64,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Record {
     pub fields: Vec<Field>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Field {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub name: String,
+    #[serde(rename = "type")]
     pub ty: Type,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Flags {
     pub flags: Vec<Flag>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Flag {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub name: String,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq)]
 pub enum FlagsRepr {
     U8,
     U16,
@@ -473,20 +519,22 @@ impl FlagsRepr {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Tuple {
     pub types: Vec<Type>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Variant {
     pub cases: Vec<Case>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Case {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub name: String,
+    #[serde(rename = "type")]
     pub ty: Option<Type>,
 }
 
@@ -501,13 +549,14 @@ impl Variant {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Enum {
     pub cases: Vec<EnumCase>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct EnumCase {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub name: String,
 }
@@ -523,28 +572,37 @@ impl Enum {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Result_ {
     pub ok: Option<Type>,
     pub err: Option<Type>,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize)]
 pub struct Stream {
     pub element: Option<Type>,
     pub end: Option<Type>,
 }
 
-#[derive(Clone, Default, Debug, PartialEq, Eq)]
+#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize)]
 pub struct Docs {
     pub contents: Option<String>,
 }
 
+impl Docs {
+    pub fn is_empty(&self) -> bool {
+        self.contents.is_none()
+    }
+}
+
 pub type Params = Vec<(String, Type)>;
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
+#[serde(untagged)]
 pub enum Results {
+    #[serde(serialize_with = "serialize_params")]
     Named(Params),
+    #[serde(serialize_with = "serialize_anon_result")]
     Anon(Type),
 }
 
@@ -607,20 +665,26 @@ impl Results {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct Function {
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
     pub name: String,
     pub kind: FunctionKind,
+    #[serde(serialize_with = "serialize_params")]
     pub params: Params,
     pub results: Results,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
+#[serde(rename_all = "lowercase")]
 pub enum FunctionKind {
     Freestanding,
+    #[serde(serialize_with = "serialize_id")]
     Method(TypeId),
+    #[serde(serialize_with = "serialize_id")]
     Static(TypeId),
+    #[serde(serialize_with = "serialize_id")]
     Constructor(TypeId),
 }
 
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index 397b26ae..2bd16e58 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -1,5 +1,6 @@
 use crate::ast::lex::Span;
 use crate::ast::{parse_use_path, AstUsePath};
+use crate::serde_::{serialize_arena, serialize_id_map};
 use crate::{
     AstItem, Docs, Error, Function, FunctionKind, Handle, IncludeName, Interface, InterfaceId,
     PackageName, Results, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, UnresolvedPackage, World,
@@ -8,6 +9,7 @@ use crate::{
 use anyhow::{anyhow, bail, Context, Result};
 use id_arena::{Arena, Id};
 use indexmap::{IndexMap, IndexSet};
+use serde::Serialize;
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::mem;
 use std::path::{Path, PathBuf};
@@ -25,12 +27,17 @@ use std::path::{Path, PathBuf};
 ///
 /// Each item in a `Resolve` has a parent link to trace it back to the original
 /// package as necessary.
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Serialize)]
 pub struct Resolve {
+    #[serde(serialize_with = "serialize_arena")]
     pub worlds: Arena<World>,
+    #[serde(serialize_with = "serialize_arena")]
     pub interfaces: Arena<Interface>,
+    #[serde(serialize_with = "serialize_arena")]
     pub types: Arena<TypeDef>,
+    #[serde(serialize_with = "serialize_arena")]
     pub packages: Arena<Package>,
+    #[serde(skip)]
     pub package_names: IndexMap<PackageName, PackageId>,
 }
 
@@ -39,19 +46,22 @@ pub struct Resolve {
 /// A package is a collection of interfaces and worlds. Packages additionally
 /// have a unique identifier that affects generated components and uniquely
 /// identifiers this particular package.
-#[derive(Clone)]
+#[derive(Clone, Serialize)]
 pub struct Package {
     /// A unique name corresponding to this package.
     pub name: PackageName,
 
     /// Documentation associated with this package.
+    #[serde(skip_serializing_if = "Docs::is_empty")]
     pub docs: Docs,
 
     /// All interfaces contained in this packaged, keyed by the interface's
     /// name.
+    #[serde(serialize_with = "serialize_id_map")]
     pub interfaces: IndexMap<String, InterfaceId>,
 
     /// All worlds contained in this package, keyed by the world's name.
+    #[serde(serialize_with = "serialize_id_map")]
     pub worlds: IndexMap<String, WorldId>,
 }
 
@@ -824,7 +834,7 @@ impl Remap {
     fn process_foreign_interfaces(
         &mut self,
         unresolved: &UnresolvedPackage,
-        interface_to_package: &HashMap<Id<Interface>, (&PackageName, &String, Span)>,
+        interface_to_package: &HashMap<InterfaceId, (&PackageName, &String, Span)>,
         resolve: &mut Resolve,
     ) -> Result<(), anyhow::Error> {
         for (unresolved_iface_id, unresolved_iface) in unresolved.interfaces.iter() {
@@ -872,7 +882,7 @@ impl Remap {
     fn process_foreign_worlds(
         &mut self,
         unresolved: &UnresolvedPackage,
-        world_to_package: &HashMap<Id<World>, (&PackageName, &String, Span)>,
+        world_to_package: &HashMap<WorldId, (&PackageName, &String, Span)>,
         resolve: &mut Resolve,
     ) -> Result<(), anyhow::Error> {
         for (unresolved_world_id, _) in unresolved.worlds.iter() {
diff --git a/crates/wit-parser/src/serde_.rs b/crates/wit-parser/src/serde_.rs
new file mode 100644
index 00000000..64497b3c
--- /dev/null
+++ b/crates/wit-parser/src/serde_.rs
@@ -0,0 +1,101 @@
+use crate::{Params, Type};
+use id_arena::{Arena, Id};
+use indexmap::IndexMap;
+use serde::ser::{SerializeMap, SerializeSeq, Serializer};
+use serde::Serialize;
+
+pub fn serialize_arena<T, S>(arena: &Arena<T>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    T: Serialize,
+    S: Serializer,
+{
+    let mut seq = serializer.serialize_seq(Some(arena.len()))?;
+    for (_, item) in arena.iter() {
+        seq.serialize_element(&item)?;
+    }
+    seq.end()
+}
+
+pub fn serialize_id<T, S>(id: &Id<T>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    serializer.serialize_u64(id.index() as u64)
+}
+
+pub fn serialize_optional_id<T, S>(id: &Option<Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    match id {
+        Some(id) => serialize_id(&id, serializer),
+        None => serializer.serialize_none(),
+    }
+}
+
+pub fn serialize_id_map<K, T, S>(map: &IndexMap<K, Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    K: Serialize,
+    S: Serializer,
+{
+    let mut s = serializer.serialize_map(Some(map.len()))?;
+    for (key, id) in map.iter() {
+        s.serialize_entry(key, &(id.index() as u64))?;
+    }
+    s.end()
+}
+
+impl Serialize for Type {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        match self {
+            Type::Bool => serializer.serialize_str("bool"),
+            Type::U8 => serializer.serialize_str("u8"),
+            Type::U16 => serializer.serialize_str("u16"),
+            Type::U32 => serializer.serialize_str("u32"),
+            Type::U64 => serializer.serialize_str("u64"),
+            Type::S8 => serializer.serialize_str("s8"),
+            Type::S16 => serializer.serialize_str("s16"),
+            Type::S32 => serializer.serialize_str("s32"),
+            Type::S64 => serializer.serialize_str("s64"),
+            Type::Float32 => serializer.serialize_str("float32"),
+            Type::Float64 => serializer.serialize_str("float64"),
+            Type::Char => serializer.serialize_str("char"),
+            Type::String => serializer.serialize_str("string"),
+            Type::Id(type_id) => serializer.serialize_u64(type_id.index() as u64),
+        }
+    }
+}
+
+pub fn serialize_anon_result<S>(typ: &Type, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let params: Params = vec![(String::default(), *typ)];
+    serialize_params(&params, serializer)
+}
+
+pub fn serialize_params<S>(params: &Params, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let mut seq = serializer.serialize_seq(Some(params.len()))?;
+    for (name, typ) in params.iter() {
+        let param = Param {
+            name: name.to_string(),
+            typ: *typ,
+        };
+        seq.serialize_element(&param)?;
+    }
+    seq.end()
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize)]
+struct Param {
+    #[serde(skip_serializing_if = "String::is_empty")]
+    pub name: String,
+    #[serde(rename = "type")]
+    pub typ: Type,
+}
diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs
index 2757e5d4..edbdd097 100644
--- a/src/bin/wasm-tools/component.rs
+++ b/src/bin/wasm-tools/component.rs
@@ -416,6 +416,16 @@ pub struct WitOpts {
     /// options.
     #[clap(long)]
     skip_validation: bool,
+
+    /// Emit the WIT document as JSON instead of text.
+    #[clap(
+        short,
+        long,
+        conflicts_with = "wasm",
+        conflicts_with = "out_dir",
+        conflicts_with = "wat"
+    )]
+    json: bool,
 }
 
 impl WitOpts {
@@ -478,6 +488,10 @@ impl WitOpts {
 
         // Now that the WIT document has been decoded, it's time to emit it.
         // This interprets all of the output options and performs such a task.
+        if self.json {
+            self.emit_json(&decoded)?;
+            return Ok(());
+        }
         if self.wasm || self.wat {
             self.emit_wasm(&decoded)?;
         } else {
@@ -567,6 +581,16 @@ impl WitOpts {
 
         Ok(())
     }
+
+    fn emit_json(&self, decoded: &DecodedWasm) -> Result<()> {
+        assert!(!self.wasm && !self.wat);
+
+        let resolve = decoded.resolve();
+        let output = serde_json::to_string_pretty(&resolve)?;
+        self.output.output(Output::Json(&output))?;
+
+        Ok(())
+    }
 }
 
 /// Tool for verifying whether a component conforms to a world.
diff --git a/src/lib.rs b/src/lib.rs
index d0b74cab..0a558d28 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -94,6 +94,7 @@ pub struct OutputArg {
 pub enum Output<'a> {
     Wat(&'a str),
     Wasm { bytes: &'a [u8], wat: bool },
+    Json(&'a str),
 }
 
 impl InputOutput {
@@ -147,6 +148,7 @@ impl OutputArg {
                 }
                 Ok(())
             }
+            Output::Json(s) => self.output_str(s),
         }
     }
 


Test patch:
diff --git a/crates/wit-parser/tests/all.rs b/crates/wit-parser/tests/all.rs
index c1c1132c..c8830391 100644
--- a/crates/wit-parser/tests/all.rs
+++ b/crates/wit-parser/tests/all.rs
@@ -124,20 +124,34 @@ impl Runner<'_> {
             }
         } else {
             result?;
+            // format json string to human readable
+            let json_result = serde_json::to_string_pretty(&resolve)?;
+            // "foo.wit" => "foo.wit.json"
+            self.read_or_write_to_file(test, &json_result, "json")?;
             return Ok(());
         };
 
         // "foo.wit" => "foo.wit.result"
         // "foo.wit.md" => "foo.wit.md.result"
+        self.read_or_write_to_file(test, &result, "result")?;
+        return Ok(());
+    }
+
+    fn read_or_write_to_file(
+        &mut self,
+        test: &Path,
+        result: &str,
+        extension: &str,
+    ) -> Result<(), anyhow::Error> {
         let result_file = if test.extension() == Some(OsStr::new("md"))
             && test
                 .file_stem()
                 .and_then(|path| Path::new(path).extension())
                 == Some(OsStr::new("wit"))
         {
-            test.with_extension("md.result")
+            test.with_extension(format!("md.{extension}"))
         } else {
-            test.with_extension("wit.result")
+            test.with_extension(format!("wit.{extension}"))
         };
         if env::var_os("BLESS").is_some() {
             fs::write(&result_file, result)?;
@@ -155,14 +169,14 @@ impl Runner<'_> {
             }
         }
         self.bump_ntests();
-        return Ok(());
-
-        fn normalize(s: &str) -> String {
-            s.replace('\\', "/").replace("\r\n", "\n")
-        }
+        Ok(())
     }
 
     fn bump_ntests(&self) {
         self.ntests.fetch_add(1, SeqCst);
     }
 }
+
+fn normalize(s: &str) -> String {
+    s.replace('\\', "/").replace("\r\n", "\n")
+}
diff --git a/crates/wit-parser/tests/ui/comments.wit.json b/crates/wit-parser/tests/ui/comments.wit.json
new file mode 100644
index 00000000..bd49b943
--- /dev/null
+++ b/crates/wit-parser/tests/ui/comments.wit.json
@@ -0,0 +1,46 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "x": 0,
+        "bar": 1
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "x",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "stream": {
+          "element": 0,
+          "end": null
+        }
+      },
+      "name": "bar",
+      "owner": {
+        "interface": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:comments",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/complex-include.wit.json b/crates/wit-parser/tests/ui/complex-include.wit.json
new file mode 100644
index 00000000..2c657bea
--- /dev/null
+++ b/crates/wit-parser/tests/ui/complex-include.wit.json
@@ -0,0 +1,168 @@
+{
+  "worlds": [
+    {
+      "name": "bar-a",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "baz-a",
+      "imports": {
+        "interface-2": {
+          "interface": 2
+        },
+        "interface-3": {
+          "interface": 3
+        }
+      },
+      "exports": {},
+      "package": 1
+    },
+    {
+      "name": "a",
+      "imports": {
+        "interface-4": {
+          "interface": 4
+        },
+        "interface-5": {
+          "interface": 5
+        }
+      },
+      "exports": {},
+      "package": 2
+    },
+    {
+      "name": "b",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "exports": {},
+      "package": 2
+    },
+    {
+      "name": "c",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "exports": {},
+      "package": 2
+    },
+    {
+      "name": "union-world",
+      "imports": {
+        "interface-4": {
+          "interface": 4
+        },
+        "interface-5": {
+          "interface": 5
+        },
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        },
+        "interface-2": {
+          "interface": 2
+        },
+        "interface-3": {
+          "interface": 3
+        }
+      },
+      "exports": {},
+      "package": 2
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "a",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "b",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "a",
+      "types": {},
+      "functions": {},
+      "package": 1
+    },
+    {
+      "name": "b",
+      "types": {},
+      "functions": {},
+      "package": 1
+    },
+    {
+      "name": "ai",
+      "types": {},
+      "functions": {},
+      "package": 2
+    },
+    {
+      "name": "bi",
+      "types": {},
+      "functions": {},
+      "package": 2
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "a": 0,
+        "b": 1
+      },
+      "worlds": {
+        "bar-a": 0
+      }
+    },
+    {
+      "name": "foo:baz",
+      "interfaces": {
+        "a": 2,
+        "b": 3
+      },
+      "worlds": {
+        "baz-a": 1
+      }
+    },
+    {
+      "name": "foo:root",
+      "interfaces": {
+        "ai": 4,
+        "bi": 5
+      },
+      "worlds": {
+        "a": 2,
+        "b": 3,
+        "c": 4,
+        "union-world": 5
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/cross-package-resource.wit.json b/crates/wit-parser/tests/ui/cross-package-resource.wit.json
new file mode 100644
index 00000000..437bc014
--- /dev/null
+++ b/crates/wit-parser/tests/ui/cross-package-resource.wit.json
@@ -0,0 +1,67 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "r": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "types": {
+        "r": 1,
+        "t": 2
+      },
+      "functions": {},
+      "package": 1
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "r",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 1
+        }
+      },
+      "name": "t",
+      "owner": {
+        "interface": 1
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "some:dep",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "foo": 1
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/diamond1.wit.json b/crates/wit-parser/tests/ui/diamond1.wit.json
new file mode 100644
index 00000000..8d7ce082
--- /dev/null
+++ b/crates/wit-parser/tests/ui/diamond1.wit.json
@@ -0,0 +1,55 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "exports": {},
+      "package": 2
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "types",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "types",
+      "types": {},
+      "functions": {},
+      "package": 1
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:dep1",
+      "interfaces": {
+        "types": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:dep2",
+      "interfaces": {
+        "types": 1
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:foo",
+      "interfaces": {},
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json
new file mode 100644
index 00000000..c3d2a8e1
--- /dev/null
+++ b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json
@@ -0,0 +1,107 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "foo": {
+          "interface": 2
+        },
+        "interface-1": {
+          "interface": 1
+        },
+        "bar": {
+          "interface": 3
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "shared1",
+      "types": {
+        "the-type": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "shared2",
+      "types": {
+        "the-type": 1
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {
+        "the-type": 2
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {
+        "the-type": 3
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 3
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:diamond",
+      "interfaces": {
+        "shared1": 0,
+        "shared2": 1
+      },
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/embedded.wit.md.json b/crates/wit-parser/tests/ui/embedded.wit.md.json
new file mode 100644
index 00000000..83a2a44a
--- /dev/null
+++ b/crates/wit-parser/tests/ui/embedded.wit.md.json
@@ -0,0 +1,40 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {},
+      "functions": {
+        "x": {
+          "name": "x",
+          "kind": "freestanding",
+          "params": [],
+          "results": []
+        },
+        "y": {
+          "name": "y",
+          "kind": "freestanding",
+          "params": [],
+          "results": []
+        },
+        "z": {
+          "name": "z",
+          "kind": "freestanding",
+          "params": [],
+          "results": []
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/empty.wit.json b/crates/wit-parser/tests/ui/empty.wit.json
new file mode 100644
index 00000000..092abe5e
--- /dev/null
+++ b/crates/wit-parser/tests/ui/empty.wit.json
@@ -0,0 +1,12 @@
+{
+  "worlds": [],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:empty",
+      "interfaces": {},
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json
new file mode 100644
index 00000000..6a7cd7f1
--- /dev/null
+++ b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json
@@ -0,0 +1,380 @@
+{
+  "worlds": [
+    {
+      "name": "wasi",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {},
+      "package": 5
+    },
+    {
+      "name": "my-world",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 6
+    },
+    {
+      "name": "my-world2",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {
+        "interface-9": {
+          "interface": 9
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 6
+    },
+    {
+      "name": "bars-world",
+      "imports": {
+        "interface-4": {
+          "interface": 4
+        },
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {},
+      "package": 6
+    },
+    {
+      "name": "unionw-world",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        },
+        "interface-9": {
+          "interface": 9
+        }
+      },
+      "package": 6
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "other-interface",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "saas",
+      "types": {},
+      "functions": {},
+      "package": 1
+    },
+    {
+      "name": "i",
+      "types": {},
+      "functions": {},
+      "package": 2
+    },
+    {
+      "name": "the-default",
+      "types": {
+        "some-type": 0
+      },
+      "functions": {},
+      "package": 3
+    },
+    {
+      "name": "the-default",
+      "types": {
+        "from-default": 1
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "some-interface",
+      "types": {
+        "another-type": 2
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "another-interface",
+      "types": {
+        "yet-another-type": 3
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "clocks",
+      "types": {
+        "timestamp": 4
+      },
+      "functions": {},
+      "package": 5
+    },
+    {
+      "name": "filesystem",
+      "types": {
+        "stat": 5
+      },
+      "functions": {},
+      "package": 5
+    },
+    {
+      "name": "foo",
+      "types": {
+        "timestamp": 6,
+        "stat": 7
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "bar",
+      "types": {
+        "from-default": 8,
+        "another-type": 9,
+        "yet-another-type": 10
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "use1",
+      "types": {
+        "some-type": 11
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "use2",
+      "types": {
+        "some-type": 12
+      },
+      "functions": {},
+      "package": 6
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": "string"
+      },
+      "name": "from-default",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "another-type",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": "u8"
+      },
+      "name": "yet-another-type",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": "u64"
+      },
+      "name": "timestamp",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "ino",
+              "type": "u64"
+            }
+          ]
+        }
+      },
+      "name": "stat",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 4
+      },
+      "name": "timestamp",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 5
+      },
+      "name": "stat",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "from-default",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 2
+      },
+      "name": "another-type",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 3
+      },
+      "name": "yet-another-type",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 11
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 12
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:another-pkg",
+      "interfaces": {
+        "other-interface": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:corp",
+      "interfaces": {
+        "saas": 1
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:different-pkg",
+      "interfaces": {
+        "i": 2
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:foreign-pkg",
+      "interfaces": {
+        "the-default": 3
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:some-pkg",
+      "interfaces": {
+        "the-default": 4,
+        "some-interface": 5,
+        "another-interface": 6
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:wasi",
+      "interfaces": {
+        "clocks": 7,
+        "filesystem": 8
+      },
+      "worlds": {
+        "wasi": 0
+      }
+    },
+    {
+      "name": "foo:root",
+      "interfaces": {
+        "foo": 9,
+        "bar": 10,
+        "use1": 11,
+        "use2": 12
+      },
+      "worlds": {
+        "my-world": 1,
+        "my-world2": 2,
+        "bars-world": 3,
+        "unionw-world": 4
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/foreign-deps.wit.json b/crates/wit-parser/tests/ui/foreign-deps.wit.json
new file mode 100644
index 00000000..d75bd5ad
--- /dev/null
+++ b/crates/wit-parser/tests/ui/foreign-deps.wit.json
@@ -0,0 +1,344 @@
+{
+  "worlds": [
+    {
+      "name": "my-world",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 6
+    },
+    {
+      "name": "my-world2",
+      "imports": {
+        "interface-8": {
+          "interface": 8
+        },
+        "interface-7": {
+          "interface": 7
+        }
+      },
+      "exports": {
+        "interface-9": {
+          "interface": 9
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 6
+    },
+    {
+      "name": "bars-world",
+      "imports": {
+        "interface-4": {
+          "interface": 4
+        },
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {},
+      "package": 6
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "other-interface",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "saas",
+      "types": {},
+      "functions": {},
+      "package": 1
+    },
+    {
+      "name": "i",
+      "types": {},
+      "functions": {},
+      "package": 2
+    },
+    {
+      "name": "the-default",
+      "types": {
+        "some-type": 0
+      },
+      "functions": {},
+      "package": 3
+    },
+    {
+      "name": "the-default",
+      "types": {
+        "from-default": 1
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "some-interface",
+      "types": {
+        "another-type": 2
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "another-interface",
+      "types": {
+        "yet-another-type": 3
+      },
+      "functions": {},
+      "package": 4
+    },
+    {
+      "name": "clocks",
+      "types": {
+        "timestamp": 4
+      },
+      "functions": {},
+      "package": 5
+    },
+    {
+      "name": "filesystem",
+      "types": {
+        "stat": 5
+      },
+      "functions": {},
+      "package": 5
+    },
+    {
+      "name": "foo",
+      "types": {
+        "timestamp": 6,
+        "stat": 7
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "bar",
+      "types": {
+        "from-default": 8,
+        "another-type": 9,
+        "yet-another-type": 10
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "use1",
+      "types": {
+        "some-type": 11
+      },
+      "functions": {},
+      "package": 6
+    },
+    {
+      "name": "use2",
+      "types": {
+        "some-type": 12
+      },
+      "functions": {},
+      "package": 6
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": "string"
+      },
+      "name": "from-default",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "another-type",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": "u8"
+      },
+      "name": "yet-another-type",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": "u64"
+      },
+      "name": "timestamp",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "ino",
+              "type": "u64"
+            }
+          ]
+        }
+      },
+      "name": "stat",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 4
+      },
+      "name": "timestamp",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 5
+      },
+      "name": "stat",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "from-default",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 2
+      },
+      "name": "another-type",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 3
+      },
+      "name": "yet-another-type",
+      "owner": {
+        "interface": 10
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 11
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "some-type",
+      "owner": {
+        "interface": 12
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:another-pkg",
+      "interfaces": {
+        "other-interface": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:corp",
+      "interfaces": {
+        "saas": 1
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:different-pkg",
+      "interfaces": {
+        "i": 2
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:foreign-pkg",
+      "interfaces": {
+        "the-default": 3
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:some-pkg",
+      "interfaces": {
+        "the-default": 4,
+        "some-interface": 5,
+        "another-interface": 6
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:wasi",
+      "interfaces": {
+        "clocks": 7,
+        "filesystem": 8
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:root",
+      "interfaces": {
+        "foo": 9,
+        "bar": 10,
+        "use1": 11,
+        "use2": 12
+      },
+      "worlds": {
+        "my-world": 0,
+        "my-world2": 1,
+        "bars-world": 2
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/functions.wit.json b/crates/wit-parser/tests/ui/functions.wit.json
new file mode 100644
index 00000000..0fae5b8b
--- /dev/null
+++ b/crates/wit-parser/tests/ui/functions.wit.json
@@ -0,0 +1,166 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "functions",
+      "types": {},
+      "functions": {
+        "f1": {
+          "name": "f1",
+          "kind": "freestanding",
+          "params": [],
+          "results": []
+        },
+        "f2": {
+          "name": "f2",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ],
+          "results": []
+        },
+        "f3": {
+          "name": "f3",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ],
+          "results": []
+        },
+        "f4": {
+          "name": "f4",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": "u32"
+            }
+          ]
+        },
+        "f6": {
+          "name": "f6",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": 0
+            }
+          ]
+        },
+        "f7": {
+          "name": "f7",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": "float32"
+            },
+            {
+              "name": "b",
+              "type": "float32"
+            }
+          ],
+          "results": [
+            {
+              "type": 0
+            }
+          ]
+        },
+        "f8": {
+          "name": "f8",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "type": 2
+            }
+          ]
+        },
+        "f9": {
+          "name": "f9",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "name": "u",
+              "type": "u32"
+            },
+            {
+              "name": "f",
+              "type": "float32"
+            }
+          ]
+        },
+        "f10": {
+          "name": "f10",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "name": "u",
+              "type": "u32"
+            }
+          ]
+        },
+        "f11": {
+          "name": "f11",
+          "kind": "freestanding",
+          "params": [],
+          "results": []
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            "u32",
+            "u32"
+          ]
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "option": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": "u32",
+          "err": "float32"
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:functions",
+      "interfaces": {
+        "functions": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json
new file mode 100644
index 00000000..1b9d00c3
--- /dev/null
+++ b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json
@@ -0,0 +1,39 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {},
+      "package": 1
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "types",
+      "types": {},
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "types": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:foo",
+      "interfaces": {},
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/include-reps.wit.json b/crates/wit-parser/tests/ui/include-reps.wit.json
new file mode 100644
index 00000000..8217e2ee
--- /dev/null
+++ b/crates/wit-parser/tests/ui/include-reps.wit.json
@@ -0,0 +1,60 @@
+{
+  "worlds": [
+    {
+      "name": "bar",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "a",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "b",
+      "types": {},
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "a": 0,
+        "b": 1
+      },
+      "worlds": {
+        "bar": 0,
+        "foo": 1
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json
new file mode 100644
index 00000000..14c6d0d0
--- /dev/null
+++ b/crates/wit-parser/tests/ui/kebab-name-include-with.wit.json
@@ -0,0 +1,70 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "a": {
+          "function": {
+            "name": "a",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "bar",
+      "imports": {
+        "a": {
+          "function": {
+            "name": "a",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "baz",
+      "imports": {
+        "b": {
+          "function": {
+            "name": "a",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        },
+        "a": {
+          "function": {
+            "name": "a",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {},
+      "worlds": {
+        "foo": 0,
+        "bar": 1,
+        "baz": 2
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/many-names.wit.json b/crates/wit-parser/tests/ui/many-names.wit.json
new file mode 100644
index 00000000..87d111bd
--- /dev/null
+++ b/crates/wit-parser/tests/ui/many-names.wit.json
@@ -0,0 +1,40 @@
+{
+  "worlds": [
+    {
+      "name": "name",
+      "imports": {
+        "name": {
+          "interface": 1
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "x",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {},
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:name",
+      "interfaces": {
+        "x": 0
+      },
+      "worlds": {
+        "name": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/multi-file.wit.json b/crates/wit-parser/tests/ui/multi-file.wit.json
new file mode 100644
index 00000000..c7055676
--- /dev/null
+++ b/crates/wit-parser/tests/ui/multi-file.wit.json
@@ -0,0 +1,298 @@
+{
+  "worlds": [
+    {
+      "name": "more-depends-on-later-things",
+      "imports": {
+        "interface-3": {
+          "interface": 3
+        }
+      },
+      "exports": {
+        "interface-3": {
+          "interface": 3
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "the-world",
+      "imports": {
+        "interface-1": {
+          "interface": 1
+        },
+        "x": {
+          "type": 15
+        },
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": [
+              {
+                "type": 15
+              }
+            ]
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "irrelevant-name",
+      "types": {
+        "a-name": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "depend-on-me",
+      "types": {
+        "x": 1
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "depends-on-later-item",
+      "types": {
+        "x": 2
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "later-interface",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "cycle1",
+      "types": {
+        "t": 3
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "cycle2",
+      "types": {
+        "t": 4
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "cycle3",
+      "types": {
+        "t": 5
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "types": {
+        "x": 6
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "something-else",
+      "types": {
+        "y": 7
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "bar",
+      "types": {
+        "x": 8,
+        "x2": 9,
+        "x3": 10,
+        "x4": 11,
+        "y": 12,
+        "y2": 13,
+        "a-name": 14
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "record": {
+          "fields": []
+        }
+      },
+      "name": "a-name",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "x",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "x",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 3
+      },
+      "name": "t",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 4
+      },
+      "name": "t",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "x",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": "u64"
+      },
+      "name": "y",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 6
+      },
+      "name": "x",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 6
+      },
+      "name": "x2",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 6
+      },
+      "name": "x3",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "x4",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 7
+      },
+      "name": "y",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 7
+      },
+      "name": "y2",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "a-name",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "x",
+      "owner": {
+        "world": 1
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:multi-file",
+      "interfaces": {
+        "irrelevant-name": 0,
+        "depend-on-me": 1,
+        "depends-on-later-item": 2,
+        "later-interface": 3,
+        "cycle1": 4,
+        "cycle2": 5,
+        "cycle3": 6,
+        "foo": 7,
+        "something-else": 8,
+        "bar": 9
+      },
+      "worlds": {
+        "more-depends-on-later-things": 0,
+        "the-world": 1
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json
new file mode 100644
index 00000000..d0bcb7d4
--- /dev/null
+++ b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json
@@ -0,0 +1,89 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "a": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "types": {
+        "a": 1,
+        "t1": 2,
+        "t2": 3,
+        "t3": 4
+      },
+      "functions": {},
+      "package": 1
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "a",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "a",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 1
+        }
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 2
+        }
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 1
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "some:dep",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "foo": 1
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/package-syntax1.wit.json b/crates/wit-parser/tests/ui/package-syntax1.wit.json
new file mode 100644
index 00000000..80af0cbb
--- /dev/null
+++ b/crates/wit-parser/tests/ui/package-syntax1.wit.json
@@ -0,0 +1,12 @@
+{
+  "worlds": [],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {},
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/package-syntax3.wit.json b/crates/wit-parser/tests/ui/package-syntax3.wit.json
new file mode 100644
index 00000000..e0323c51
--- /dev/null
+++ b/crates/wit-parser/tests/ui/package-syntax3.wit.json
@@ -0,0 +1,12 @@
+{
+  "worlds": [],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {},
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/package-syntax4.wit.json b/crates/wit-parser/tests/ui/package-syntax4.wit.json
new file mode 100644
index 00000000..7bacc86f
--- /dev/null
+++ b/crates/wit-parser/tests/ui/package-syntax4.wit.json
@@ -0,0 +1,12 @@
+{
+  "worlds": [],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:bar@2.0.0",
+      "interfaces": {},
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-empty.wit.json b/crates/wit-parser/tests/ui/resources-empty.wit.json
new file mode 100644
index 00000000..ddb0ce92
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-empty.wit.json
@@ -0,0 +1,72 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources-empty",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "t2": {
+          "name": "t2",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": []
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources-empty",
+      "interfaces": {
+        "resources-empty": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-multiple-returns-borrow.wit.json b/crates/wit-parser/tests/ui/resources-multiple-returns-borrow.wit.json
new file mode 100644
index 00000000..3109f6f3
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-multiple-returns-borrow.wit.json
@@ -0,0 +1,74 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources1",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "name": "a",
+              "type": "s32"
+            },
+            {
+              "name": "handle",
+              "type": 1
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources1",
+      "interfaces": {
+        "resources1": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json
new file mode 100644
index 00000000..91d49631
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json
@@ -0,0 +1,83 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources1",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "name": "a",
+              "type": "s32"
+            },
+            {
+              "name": "handle",
+              "type": 2
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources1",
+      "interfaces": {
+        "resources1": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-multiple.wit.json b/crates/wit-parser/tests/ui/resources-multiple.wit.json
new file mode 100644
index 00000000..79674fb6
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-multiple.wit.json
@@ -0,0 +1,281 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources-multiple",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": []
+        },
+        "t2": {
+          "name": "t2",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 5
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f2": {
+          "name": "[method]r1.f2",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            },
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f3": {
+          "name": "[method]r1.f3",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            },
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f4": {
+          "name": "[method]r1.f4",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "type": "u32"
+            }
+          ]
+        },
+        "[method]r1.f6": {
+          "name": "[method]r1.f6",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "type": 2
+            }
+          ]
+        },
+        "[method]r1.f7": {
+          "name": "[method]r1.f7",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            },
+            {
+              "name": "a",
+              "type": "float32"
+            },
+            {
+              "name": "b",
+              "type": "float32"
+            }
+          ],
+          "results": [
+            {
+              "type": 2
+            }
+          ]
+        },
+        "[method]r1.f8": {
+          "name": "[method]r1.f8",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            },
+            {
+              "name": "a",
+              "type": 3
+            }
+          ],
+          "results": [
+            {
+              "type": 4
+            }
+          ]
+        },
+        "[method]r1.f9": {
+          "name": "[method]r1.f9",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "name": "u",
+              "type": "u32"
+            },
+            {
+              "name": "f",
+              "type": "float32"
+            }
+          ]
+        },
+        "[method]r1.f10": {
+          "name": "[method]r1.f10",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "name": "u",
+              "type": "u32"
+            }
+          ]
+        },
+        "[method]r1.f11": {
+          "name": "[method]r1.f11",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": []
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            "u32",
+            "u32"
+          ]
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "option": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": "u32",
+          "err": "float32"
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources-multiple",
+      "interfaces": {
+        "resources-multiple": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-return-borrow.wit.json b/crates/wit-parser/tests/ui/resources-return-borrow.wit.json
new file mode 100644
index 00000000..7e52922f
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-return-borrow.wit.json
@@ -0,0 +1,69 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources1",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "type": 1
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources1",
+      "interfaces": {
+        "resources1": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources-return-own.wit.json b/crates/wit-parser/tests/ui/resources-return-own.wit.json
new file mode 100644
index 00000000..c7e2936c
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources-return-own.wit.json
@@ -0,0 +1,78 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources1",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": [
+            {
+              "type": 2
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources1",
+      "interfaces": {
+        "resources1": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources.wit.json b/crates/wit-parser/tests/ui/resources.wit.json
new file mode 100644
index 00000000..8df576af
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources.wit.json
@@ -0,0 +1,340 @@
+{
+  "worlds": [
+    {
+      "name": "w",
+      "imports": {
+        "a": {
+          "type": 11
+        },
+        "b": {
+          "type": 12
+        },
+        "c": {
+          "type": 13
+        },
+        "[constructor]c": {
+          "function": {
+            "name": "[constructor]c",
+            "kind": {
+              "constructor": 13
+            },
+            "params": [],
+            "results": [
+              {
+                "type": 18
+              }
+            ]
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "a": 0,
+        "b": 1,
+        "c": 2,
+        "d": 3,
+        "e": 4
+      },
+      "functions": {
+        "[constructor]b": {
+          "name": "[constructor]b",
+          "kind": {
+            "constructor": 1
+          },
+          "params": [],
+          "results": [
+            {
+              "type": 14
+            }
+          ]
+        },
+        "[constructor]c": {
+          "name": "[constructor]c",
+          "kind": {
+            "constructor": 2
+          },
+          "params": [
+            {
+              "name": "x",
+              "type": "u32"
+            }
+          ],
+          "results": [
+            {
+              "type": 15
+            }
+          ]
+        },
+        "[constructor]d": {
+          "name": "[constructor]d",
+          "kind": {
+            "constructor": 3
+          },
+          "params": [
+            {
+              "name": "x",
+              "type": "u32"
+            }
+          ],
+          "results": [
+            {
+              "type": 16
+            }
+          ]
+        },
+        "[method]d.a": {
+          "name": "[method]d.a",
+          "kind": {
+            "method": 3
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 5
+            }
+          ],
+          "results": []
+        },
+        "[static]d.b": {
+          "name": "[static]d.b",
+          "kind": {
+            "static": 3
+          },
+          "params": [],
+          "results": []
+        },
+        "[constructor]e": {
+          "name": "[constructor]e",
+          "kind": {
+            "constructor": 4
+          },
+          "params": [
+            {
+              "name": "other",
+              "type": 17
+            },
+            {
+              "name": "other2",
+              "type": 6
+            }
+          ],
+          "results": [
+            {
+              "type": 17
+            }
+          ]
+        },
+        "[method]e.method": {
+          "name": "[method]e.method",
+          "kind": {
+            "method": 4
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 6
+            },
+            {
+              "name": "thing",
+              "type": 17
+            },
+            {
+              "name": "thing2",
+              "type": 6
+            }
+          ],
+          "results": []
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "i",
+      "types": {
+        "a": 7,
+        "t1": 8,
+        "t2": 9,
+        "t3": 10
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "a",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "b",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "c",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "d",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "e",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 3
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 4
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": "resource",
+      "name": "a",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 7
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 7
+        }
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 8
+        }
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "a",
+      "owner": {
+        "world": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "b",
+      "owner": {
+        "world": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "c",
+      "owner": {
+        "world": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 1
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 2
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 3
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 4
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 13
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "foo": 0,
+        "i": 1
+      },
+      "worlds": {
+        "w": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/resources1.wit.json b/crates/wit-parser/tests/ui/resources1.wit.json
new file mode 100644
index 00000000..0945e573
--- /dev/null
+++ b/crates/wit-parser/tests/ui/resources1.wit.json
@@ -0,0 +1,96 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "resources1",
+      "types": {
+        "r1": 0
+      },
+      "functions": {
+        "t1": {
+          "name": "t1",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 1
+            }
+          ],
+          "results": []
+        },
+        "t2": {
+          "name": "t2",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "t3": {
+          "name": "t3",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "a",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "[method]r1.f1": {
+          "name": "[method]r1.f1",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 1
+            }
+          ],
+          "results": []
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "r1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:resources1",
+      "interfaces": {
+        "resources1": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/shared-types.wit.json b/crates/wit-parser/tests/ui/shared-types.wit.json
new file mode 100644
index 00000000..76729dd3
--- /dev/null
+++ b/crates/wit-parser/tests/ui/shared-types.wit.json
@@ -0,0 +1,83 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "foo": {
+          "interface": 0
+        }
+      },
+      "exports": {
+        "bar": {
+          "interface": 1
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": null,
+      "types": {},
+      "functions": {
+        "a": {
+          "name": "a",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": 0
+            }
+          ]
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {},
+      "functions": {
+        "a": {
+          "name": "a",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": 1
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "list": "u8"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            0
+          ]
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:shared-items",
+      "interfaces": {},
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json
new file mode 100644
index 00000000..e7081d15
--- /dev/null
+++ b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json
@@ -0,0 +1,1136 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        },
+        "interface-2": {
+          "interface": 2
+        },
+        "interface-3": {
+          "interface": 3
+        },
+        "interface-4": {
+          "interface": 4
+        },
+        "interface-5": {
+          "interface": 5
+        },
+        "interface-6": {
+          "interface": 6
+        },
+        "interface-7": {
+          "interface": 7
+        },
+        "interface-8": {
+          "interface": 8
+        }
+      },
+      "exports": {
+        "interface-9": {
+          "interface": 9
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "i1",
+      "types": {
+        "t1": 0,
+        "t2": 1,
+        "t3": 2,
+        "t4": 3,
+        "t5": 4,
+        "t6": 5,
+        "t7": 6,
+        "t8": 7,
+        "t9": 8,
+        "t10": 9
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i2",
+      "types": {
+        "t1": 10,
+        "t2": 11,
+        "t3": 12,
+        "t4": 13,
+        "t5": 14,
+        "t6": 15,
+        "t7": 16,
+        "t8": 17,
+        "t9": 18,
+        "t10": 19
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i3",
+      "types": {
+        "t1": 20,
+        "t2": 21,
+        "t3": 22,
+        "t4": 23,
+        "t5": 24,
+        "t6": 25,
+        "t7": 26,
+        "t8": 27,
+        "t9": 28,
+        "t10": 29
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i4",
+      "types": {
+        "t1": 30,
+        "t2": 31,
+        "t3": 32,
+        "t4": 33,
+        "t5": 34,
+        "t6": 35,
+        "t7": 36,
+        "t8": 37,
+        "t9": 38,
+        "t10": 39
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i5",
+      "types": {
+        "t1": 40,
+        "t2": 41,
+        "t3": 42,
+        "t4": 43,
+        "t5": 44,
+        "t6": 45,
+        "t7": 46,
+        "t8": 47,
+        "t9": 48,
+        "t10": 49
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i6",
+      "types": {
+        "t1": 50,
+        "t2": 51,
+        "t3": 52,
+        "t4": 53,
+        "t5": 54,
+        "t6": 55,
+        "t7": 56,
+        "t8": 57,
+        "t9": 58,
+        "t10": 59
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i7",
+      "types": {
+        "t1": 60,
+        "t2": 61,
+        "t3": 62,
+        "t4": 63,
+        "t5": 64,
+        "t6": 65,
+        "t7": 66,
+        "t8": 67,
+        "t9": 68,
+        "t10": 69
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i8",
+      "types": {
+        "t1": 70,
+        "t2": 71,
+        "t3": 72,
+        "t4": 73,
+        "t5": 74,
+        "t6": 75,
+        "t7": 76,
+        "t8": 77,
+        "t9": 78,
+        "t10": 79
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i9",
+      "types": {
+        "t1": 80,
+        "t2": 81,
+        "t3": 82,
+        "t4": 83,
+        "t5": 84,
+        "t6": 85,
+        "t7": 86,
+        "t8": 87,
+        "t9": 88,
+        "t10": 89
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i10",
+      "types": {
+        "t1": 90,
+        "t2": 91,
+        "t3": 92,
+        "t4": 93,
+        "t5": 94,
+        "t6": 95,
+        "t7": 96,
+        "t8": 97,
+        "t9": 98,
+        "t10": 99
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 2
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 3
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 4
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 5
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 6
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 7
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 8
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 9
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 10
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 11
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 12
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 13
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 14
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 15
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 16
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 17
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 18
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 19
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 20
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 21
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 22
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 23
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 24
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 25
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 26
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 27
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 28
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 29
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 3
+      }
+    },
+    {
+      "kind": {
+        "type": 30
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 31
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 32
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 33
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 34
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 35
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 36
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 37
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 38
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 39
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 4
+      }
+    },
+    {
+      "kind": {
+        "type": 40
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 41
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 42
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 43
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 44
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 45
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 46
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 47
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 48
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 49
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 50
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 51
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 52
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 53
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 54
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 55
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 56
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 57
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 58
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 59
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "type": 60
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 61
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 62
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 63
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 64
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 65
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 66
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 67
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 68
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 69
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 7
+      }
+    },
+    {
+      "kind": {
+        "type": 70
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 71
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 72
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 73
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 74
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 75
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 76
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 77
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 78
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 79
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 8
+      }
+    },
+    {
+      "kind": {
+        "type": 80
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 81
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 82
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 83
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 84
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 85
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 86
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 87
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 88
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 9
+      }
+    },
+    {
+      "kind": {
+        "type": 89
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 9
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {
+        "i1": 0,
+        "i2": 1,
+        "i3": 2,
+        "i4": 3,
+        "i5": 4,
+        "i6": 5,
+        "i7": 6,
+        "i8": 7,
+        "i9": 8,
+        "i10": 9
+      },
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/type-then-eof.wit.json b/crates/wit-parser/tests/ui/type-then-eof.wit.json
new file mode 100644
index 00000000..beb0a7ad
--- /dev/null
+++ b/crates/wit-parser/tests/ui/type-then-eof.wit.json
@@ -0,0 +1,32 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {},
+      "functions": {
+        "foo": {
+          "name": "foo",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": "string"
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json
new file mode 100644
index 00000000..46c3452e
--- /dev/null
+++ b/crates/wit-parser/tests/ui/types.wit.json
@@ -0,0 +1,751 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "types",
+      "types": {
+        "t1": 0,
+        "t2": 1,
+        "t3": 2,
+        "t4": 3,
+        "t5": 4,
+        "t6": 5,
+        "t7": 6,
+        "t8": 7,
+        "t9": 8,
+        "t10": 9,
+        "t11": 10,
+        "t12": 11,
+        "t13": 12,
+        "t14": 13,
+        "t15": 14,
+        "t16": 15,
+        "t17": 16,
+        "t18": 17,
+        "t20": 18,
+        "t21": 19,
+        "t22": 20,
+        "t23": 21,
+        "t24": 22,
+        "t25": 23,
+        "record": 24,
+        "t26": 25,
+        "t27": 26,
+        "t28": 27,
+        "t29": 28,
+        "t30": 29,
+        "t31": 30,
+        "t32": 31,
+        "t33": 32,
+        "t34": 33,
+        "t35": 34,
+        "t36": 35,
+        "t37": 37,
+        "t41": 38,
+        "t42": 39,
+        "t43": 40,
+        "t44": 41,
+        "t45": 44,
+        "t46": 45,
+        "t47": 46,
+        "t48": 47,
+        "t49": 48,
+        "t50": 49,
+        "t51": 50,
+        "t52": 51,
+        "t53": 52,
+        "bar": 53,
+        "foo": 54
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u8"
+      },
+      "name": "t1",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u16"
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t3",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u64"
+      },
+      "name": "t4",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "s8"
+      },
+      "name": "t5",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "s16"
+      },
+      "name": "t6",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "s32"
+      },
+      "name": "t7",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "s64"
+      },
+      "name": "t8",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "float32"
+      },
+      "name": "t9",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "float64"
+      },
+      "name": "t10",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "char"
+      },
+      "name": "t11",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "list": "char"
+      },
+      "name": "t12",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "string"
+      },
+      "name": "t13",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "option": "u32"
+      },
+      "name": "t14",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": "u32",
+          "err": "u32"
+        }
+      },
+      "name": "t15",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": null,
+          "err": "u32"
+        }
+      },
+      "name": "t16",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": "u32",
+          "err": null
+        }
+      },
+      "name": "t17",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "result": {
+          "ok": null,
+          "err": null
+        }
+      },
+      "name": "t18",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": []
+        }
+      },
+      "name": "t20",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ]
+        }
+      },
+      "name": "t21",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "a",
+              "type": "u32"
+            }
+          ]
+        }
+      },
+      "name": "t22",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "a",
+              "type": "u32"
+            },
+            {
+              "name": "b",
+              "type": "u64"
+            }
+          ]
+        }
+      },
+      "name": "t23",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "a",
+              "type": "u32"
+            },
+            {
+              "name": "b",
+              "type": "u64"
+            }
+          ]
+        }
+      },
+      "name": "t24",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "x",
+              "type": "u32"
+            }
+          ]
+        }
+      },
+      "name": "t25",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": []
+        }
+      },
+      "name": "record",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": []
+        }
+      },
+      "name": "t26",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            "u32"
+          ]
+        }
+      },
+      "name": "t27",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            "u32"
+          ]
+        }
+      },
+      "name": "t28",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "tuple": {
+          "types": [
+            "u32",
+            "u64"
+          ]
+        }
+      },
+      "name": "t29",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "flags": {
+          "flags": []
+        }
+      },
+      "name": "t30",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "flags": {
+          "flags": [
+            {
+              "name": "a"
+            },
+            {
+              "name": "b"
+            },
+            {
+              "name": "c"
+            }
+          ]
+        }
+      },
+      "name": "t31",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "flags": {
+          "flags": [
+            {
+              "name": "a"
+            },
+            {
+              "name": "b"
+            },
+            {
+              "name": "c"
+            }
+          ]
+        }
+      },
+      "name": "t32",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "a",
+              "type": null
+            }
+          ]
+        }
+      },
+      "name": "t33",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "a",
+              "type": null
+            },
+            {
+              "name": "b",
+              "type": null
+            }
+          ]
+        }
+      },
+      "name": "t34",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "a",
+              "type": null
+            },
+            {
+              "name": "b",
+              "type": null
+            }
+          ]
+        }
+      },
+      "name": "t35",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "a",
+              "type": null
+            },
+            {
+              "name": "b",
+              "type": "u32"
+            }
+          ]
+        }
+      },
+      "name": "t36",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "option": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "a",
+              "type": null
+            },
+            {
+              "name": "b",
+              "type": 36
+            }
+          ]
+        }
+      },
+      "name": "t37",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "enum": {
+          "cases": [
+            {
+              "name": "a"
+            },
+            {
+              "name": "b"
+            },
+            {
+              "name": "c"
+            }
+          ]
+        }
+      },
+      "name": "t41",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "enum": {
+          "cases": [
+            {
+              "name": "a"
+            },
+            {
+              "name": "b"
+            },
+            {
+              "name": "c"
+            }
+          ]
+        }
+      },
+      "name": "t42",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "bool"
+      },
+      "name": "t43",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "string"
+      },
+      "name": "t44",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "list": 31
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "list": 42
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "list": 43
+      },
+      "name": "t45",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 41
+      },
+      "name": "t46",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 41
+      },
+      "name": "t47",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "stream": {
+          "element": "u32",
+          "end": "u32"
+        }
+      },
+      "name": "t48",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "stream": {
+          "element": null,
+          "end": "u32"
+        }
+      },
+      "name": "t49",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "stream": {
+          "element": "u32",
+          "end": null
+        }
+      },
+      "name": "t50",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "stream": {
+          "element": null,
+          "end": null
+        }
+      },
+      "name": "t51",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "future": "u32"
+      },
+      "name": "t52",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "future": null
+      },
+      "name": "t53",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "bar",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 53
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:types",
+      "interfaces": {
+        "types": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/union-fuzz-1.wit.json b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json
new file mode 100644
index 00000000..31c7c826
--- /dev/null
+++ b/crates/wit-parser/tests/ui/union-fuzz-1.wit.json
@@ -0,0 +1,35 @@
+{
+  "worlds": [
+    {
+      "name": "xo",
+      "imports": {},
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "name",
+      "imports": {},
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "x",
+      "imports": {},
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:bar",
+      "interfaces": {},
+      "worlds": {
+        "xo": 0,
+        "name": 1,
+        "x": 2
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/use-chain.wit.json b/crates/wit-parser/tests/ui/use-chain.wit.json
new file mode 100644
index 00000000..f8598bcb
--- /dev/null
+++ b/crates/wit-parser/tests/ui/use-chain.wit.json
@@ -0,0 +1,53 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "foo": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "name",
+      "types": {
+        "foo": 1
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "record": {
+          "fields": []
+        }
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 1
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:name",
+      "interfaces": {
+        "foo": 0,
+        "name": 1
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/use.wit.json b/crates/wit-parser/tests/ui/use.wit.json
new file mode 100644
index 00000000..e37de2c1
--- /dev/null
+++ b/crates/wit-parser/tests/ui/use.wit.json
@@ -0,0 +1,172 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "bar",
+      "types": {
+        "the-type": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "types": {
+        "the-type": 1
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "baz",
+      "types": {
+        "the-type": 2,
+        "test": 3
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "empty",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "use-from-empty",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "use-multiple",
+      "types": {
+        "the-type": 4,
+        "test": 5
+      },
+      "functions": {
+        "some-function": {
+          "name": "some-function",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "x",
+              "type": 4
+            }
+          ],
+          "results": [
+            {
+              "type": 5
+            }
+          ]
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "trailing-comma",
+      "types": {
+        "the-type": 6,
+        "the-foo": 7
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "test",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 2
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 3
+      },
+      "name": "test",
+      "owner": {
+        "interface": 5
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "the-type",
+      "owner": {
+        "interface": 6
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "a",
+              "type": 6
+            }
+          ]
+        }
+      },
+      "name": "the-foo",
+      "owner": {
+        "interface": 6
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "bar": 0,
+        "foo": 1,
+        "baz": 2,
+        "empty": 3,
+        "use-from-empty": 4,
+        "use-multiple": 5,
+        "trailing-comma": 6
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/versions.wit.json b/crates/wit-parser/tests/ui/versions.wit.json
new file mode 100644
index 00000000..aabcc1b6
--- /dev/null
+++ b/crates/wit-parser/tests/ui/versions.wit.json
@@ -0,0 +1,91 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "t": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "foo",
+      "types": {
+        "t": 1
+      },
+      "functions": {},
+      "package": 1
+    },
+    {
+      "name": "foo",
+      "types": {
+        "t": 2,
+        "t2": 3
+      },
+      "functions": {},
+      "package": 2
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "t",
+      "owner": {
+        "interface": 2
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "t2",
+      "owner": {
+        "interface": 2
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "a:a@1.0.0",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {}
+    },
+    {
+      "name": "a:a@2.0.0",
+      "interfaces": {
+        "foo": 1
+      },
+      "worlds": {}
+    },
+    {
+      "name": "foo:versions",
+      "interfaces": {
+        "foo": 2
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/wasi.wit.json b/crates/wit-parser/tests/ui/wasi.wit.json
new file mode 100644
index 00000000..43e6102d
--- /dev/null
+++ b/crates/wit-parser/tests/ui/wasi.wit.json
@@ -0,0 +1,296 @@
+{
+  "worlds": [],
+  "interfaces": [
+    {
+      "name": "wasi",
+      "types": {
+        "clockid": 0,
+        "timestamp": 1,
+        "errno": 2
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "enum": {
+          "cases": [
+            {
+              "name": "realtime"
+            },
+            {
+              "name": "monotonic"
+            }
+          ]
+        }
+      },
+      "name": "clockid",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u64"
+      },
+      "name": "timestamp",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "enum": {
+          "cases": [
+            {
+              "name": "success"
+            },
+            {
+              "name": "toobig"
+            },
+            {
+              "name": "access"
+            },
+            {
+              "name": "addrinuse"
+            },
+            {
+              "name": "addrnotavail"
+            },
+            {
+              "name": "afnosupport"
+            },
+            {
+              "name": "again"
+            },
+            {
+              "name": "already"
+            },
+            {
+              "name": "badf"
+            },
+            {
+              "name": "badmsg"
+            },
+            {
+              "name": "busy"
+            },
+            {
+              "name": "canceled"
+            },
+            {
+              "name": "child"
+            },
+            {
+              "name": "connaborted"
+            },
+            {
+              "name": "connrefused"
+            },
+            {
+              "name": "connreset"
+            },
+            {
+              "name": "deadlk"
+            },
+            {
+              "name": "destaddrreq"
+            },
+            {
+              "name": "dom"
+            },
+            {
+              "name": "dquot"
+            },
+            {
+              "name": "exist"
+            },
+            {
+              "name": "fault"
+            },
+            {
+              "name": "fbig"
+            },
+            {
+              "name": "hostunreach"
+            },
+            {
+              "name": "idrm"
+            },
+            {
+              "name": "ilseq"
+            },
+            {
+              "name": "inprogress"
+            },
+            {
+              "name": "intr"
+            },
+            {
+              "name": "inval"
+            },
+            {
+              "name": "io"
+            },
+            {
+              "name": "isconn"
+            },
+            {
+              "name": "isdir"
+            },
+            {
+              "name": "loop"
+            },
+            {
+              "name": "mfile"
+            },
+            {
+              "name": "mlink"
+            },
+            {
+              "name": "msgsize"
+            },
+            {
+              "name": "multihop"
+            },
+            {
+              "name": "nametoolong"
+            },
+            {
+              "name": "netdown"
+            },
+            {
+              "name": "netreset"
+            },
+            {
+              "name": "netunreach"
+            },
+            {
+              "name": "nfile"
+            },
+            {
+              "name": "nobufs"
+            },
+            {
+              "name": "nodev"
+            },
+            {
+              "name": "noent"
+            },
+            {
+              "name": "noexec"
+            },
+            {
+              "name": "nolck"
+            },
+            {
+              "name": "nolink"
+            },
+            {
+              "name": "nomem"
+            },
+            {
+              "name": "nomsg"
+            },
+            {
+              "name": "noprotoopt"
+            },
+            {
+              "name": "nospc"
+            },
+            {
+              "name": "nosys"
+            },
+            {
+              "name": "notconn"
+            },
+            {
+              "name": "notdir"
+            },
+            {
+              "name": "notempty"
+            },
+            {
+              "name": "notrecoverable"
+            },
+            {
+              "name": "notsock"
+            },
+            {
+              "name": "notsup"
+            },
+            {
+              "name": "notty"
+            },
+            {
+              "name": "nxio"
+            },
+            {
+              "name": "overflow"
+            },
+            {
+              "name": "ownerdead"
+            },
+            {
+              "name": "perm"
+            },
+            {
+              "name": "pipe"
+            },
+            {
+              "name": "proto"
+            },
+            {
+              "name": "protonosupport"
+            },
+            {
+              "name": "prototype"
+            },
+            {
+              "name": "range"
+            },
+            {
+              "name": "rofs"
+            },
+            {
+              "name": "spipe"
+            },
+            {
+              "name": "srch"
+            },
+            {
+              "name": "stale"
+            },
+            {
+              "name": "timedout"
+            },
+            {
+              "name": "txtbsy"
+            },
+            {
+              "name": "xdev"
+            },
+            {
+              "name": "notcapable"
+            }
+          ]
+        }
+      },
+      "name": "errno",
+      "owner": {
+        "interface": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "wasi:filesystem",
+      "interfaces": {
+        "wasi": 0
+      },
+      "worlds": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-diamond.wit b/crates/wit-parser/tests/ui/world-diamond.wit
index 6dd4ce88..c858ed36 100644
--- a/crates/wit-parser/tests/ui/world-diamond.wit
+++ b/crates/wit-parser/tests/ui/world-diamond.wit
@@ -19,4 +19,5 @@ interface i2 {
 world the-world {
   import i1
   import i2
+  import a: func()
 }
diff --git a/crates/wit-parser/tests/ui/world-diamond.wit.json b/crates/wit-parser/tests/ui/world-diamond.wit.json
new file mode 100644
index 00000000..75c8900e
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-diamond.wit.json
@@ -0,0 +1,118 @@
+{
+  "worlds": [
+    {
+      "name": "the-world",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        },
+        "interface-2": {
+          "interface": 2
+        },
+        "a": {
+          "function": {
+            "name": "a",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "shared-items",
+      "types": {
+        "foo": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "i1",
+      "types": {
+        "foo": 1
+      },
+      "functions": {
+        "a": {
+          "name": "a",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": 1
+            }
+          ]
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "i2",
+      "types": {
+        "foo": 2
+      },
+      "functions": {
+        "a": {
+          "name": "a",
+          "kind": "freestanding",
+          "params": [],
+          "results": [
+            {
+              "type": 2
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "foo",
+      "owner": {
+        "interface": 2
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "shared-items": 0,
+        "i1": 1,
+        "i2": 2
+      },
+      "worlds": {
+        "the-world": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json
new file mode 100644
index 00000000..9d705dee
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json
@@ -0,0 +1,66 @@
+{
+  "worlds": [
+    {
+      "name": "bar",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "t": {
+          "type": 1
+        },
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "t": 0
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "t",
+      "owner": {
+        "world": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {
+        "bar": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json
new file mode 100644
index 00000000..03bfab32
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json
@@ -0,0 +1,75 @@
+{
+  "worlds": [
+    {
+      "name": "the-world",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "bar": {
+          "interface": 1
+        },
+        "foo": {
+          "interface": 2
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "a": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {
+        "a": 1
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {},
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "a",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "a",
+      "owner": {
+        "interface": 1
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {
+        "the-world": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json
new file mode 100644
index 00000000..7bd1a871
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json
@@ -0,0 +1,70 @@
+{
+  "worlds": [
+    {
+      "name": "w",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "g": {
+          "type": 1
+        },
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": [
+              {
+                "type": 1
+              }
+            ]
+          }
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "g": 0
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "char"
+      },
+      "name": "g",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "g",
+      "owner": {
+        "world": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {
+        "w": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json
new file mode 100644
index 00000000..3dcbde53
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json
@@ -0,0 +1,71 @@
+{
+  "worlds": [
+    {
+      "name": "w",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "g": {
+          "type": 1
+        }
+      },
+      "exports": {
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": [
+              {
+                "type": 1
+              }
+            ]
+          }
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "foo",
+      "types": {
+        "g": 0
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "char"
+      },
+      "name": "g",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "g",
+      "owner": {
+        "world": 0
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "foo": 0
+      },
+      "worlds": {
+        "w": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-same-fields4.wit.json b/crates/wit-parser/tests/ui/world-same-fields4.wit.json
new file mode 100644
index 00000000..83bb910d
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-same-fields4.wit.json
@@ -0,0 +1,76 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "shared-items": {
+          "interface": 1
+        },
+        "interface-0": {
+          "interface": 0
+        }
+      },
+      "exports": {
+        "a-name": {
+          "interface": 2
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "shared-items",
+      "types": {
+        "a": 0
+      },
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": null,
+      "types": {
+        "a": 1
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "a",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "a",
+      "owner": {
+        "interface": 2
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "shared-items": 0
+      },
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json
new file mode 100644
index 00000000..43401ee3
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-top-level-funcs.wit.json
@@ -0,0 +1,86 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        },
+        "bar": {
+          "function": {
+            "name": "bar",
+            "kind": "freestanding",
+            "params": [
+              {
+                "name": "arg",
+                "type": 0
+              }
+            ],
+            "results": []
+          }
+        }
+      },
+      "exports": {
+        "foo2": {
+          "function": {
+            "name": "foo2",
+            "kind": "freestanding",
+            "params": [],
+            "results": []
+          }
+        },
+        "some-name": {
+          "function": {
+            "name": "some-name",
+            "kind": "freestanding",
+            "params": [],
+            "results": [
+              {
+                "type": 2
+              }
+            ]
+          }
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [],
+  "types": [
+    {
+      "kind": {
+        "list": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "option": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "list": 1
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {},
+      "worlds": {
+        "foo": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json
new file mode 100644
index 00000000..f869fb1a
--- /dev/null
+++ b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json
@@ -0,0 +1,240 @@
+{
+  "worlds": [
+    {
+      "name": "proxy",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "exports": {
+        "interface-1": {
+          "interface": 1
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "types",
+      "types": {
+        "request": 0,
+        "response": 1
+      },
+      "functions": {
+        "[method]request.foo": {
+          "name": "[method]request.foo",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 2
+            }
+          ],
+          "results": []
+        },
+        "[method]request.bar": {
+          "name": "[method]request.bar",
+          "kind": {
+            "method": 0
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 2
+            },
+            {
+              "name": "arg",
+              "type": 3
+            }
+          ],
+          "results": []
+        },
+        "[method]response.foo": {
+          "name": "[method]response.foo",
+          "kind": {
+            "method": 1
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 4
+            }
+          ],
+          "results": []
+        },
+        "[method]response.bar": {
+          "name": "[method]response.bar",
+          "kind": {
+            "method": 1
+          },
+          "params": [
+            {
+              "name": "self",
+              "type": 4
+            },
+            {
+              "name": "arg",
+              "type": 3
+            }
+          ],
+          "results": []
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "handler",
+      "types": {
+        "request": 5,
+        "response": 6
+      },
+      "functions": {
+        "handle": {
+          "name": "handle",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "some",
+              "type": 7
+            }
+          ],
+          "results": [
+            {
+              "type": 8
+            }
+          ]
+        },
+        "handle-owned": {
+          "name": "handle-owned",
+          "kind": "freestanding",
+          "params": [
+            {
+              "name": "some",
+              "type": 9
+            }
+          ],
+          "results": [
+            {
+              "type": 10
+            }
+          ]
+        }
+      },
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": "resource",
+      "name": "request",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": "resource",
+      "name": "response",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 0
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "list": "u32"
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 1
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "request",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "response",
+      "owner": {
+        "interface": 1
+      }
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 5
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "borrow": 6
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 5
+        }
+      },
+      "name": null,
+      "owner": "none"
+    },
+    {
+      "kind": {
+        "handle": {
+          "own": 6
+        }
+      },
+      "name": null,
+      "owner": "none"
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "types": 0,
+        "handler": 1
+      },
+      "worlds": {
+        "proxy": 0
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json
new file mode 100644
index 00000000..7fb8ff0a
--- /dev/null
+++ b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json
@@ -0,0 +1,100 @@
+{
+  "worlds": [
+    {
+      "name": "my-world-a",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-2": {
+          "interface": 2
+        }
+      },
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "my-world-b",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-2": {
+          "interface": 2
+        }
+      },
+      "exports": {},
+      "package": 0
+    },
+    {
+      "name": "union-my-world",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "interface-2": {
+          "interface": 2
+        }
+      },
+      "exports": {},
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "a1",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "a2",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "b1",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "b2",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "c",
+      "types": {},
+      "functions": {},
+      "package": 0
+    },
+    {
+      "name": "d",
+      "types": {},
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "a1": 0,
+        "a2": 1,
+        "b1": 2,
+        "b2": 3,
+        "c": 4,
+        "d": 5
+      },
+      "worlds": {
+        "my-world-a": 0,
+        "my-world-b": 1,
+        "union-my-world": 2
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/worlds-with-types.wit.json b/crates/wit-parser/tests/ui/worlds-with-types.wit.json
new file mode 100644
index 00000000..d4f85282
--- /dev/null
+++ b/crates/wit-parser/tests/ui/worlds-with-types.wit.json
@@ -0,0 +1,202 @@
+{
+  "worlds": [
+    {
+      "name": "foo",
+      "imports": {
+        "a": {
+          "type": 1
+        },
+        "b": {
+          "type": 2
+        }
+      },
+      "exports": {
+        "c": {
+          "function": {
+            "name": "c",
+            "kind": "freestanding",
+            "params": [
+              {
+                "name": "a",
+                "type": 1
+              }
+            ],
+            "results": [
+              {
+                "type": 2
+              }
+            ]
+          }
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "bar",
+      "imports": {
+        "interface-0": {
+          "interface": 0
+        },
+        "t": {
+          "type": 3
+        }
+      },
+      "exports": {
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [],
+            "results": [
+              {
+                "type": 3
+              }
+            ]
+          }
+        }
+      },
+      "package": 0
+    },
+    {
+      "name": "the-test",
+      "imports": {
+        "a": {
+          "type": 4
+        },
+        "b": {
+          "type": 5
+        },
+        "foo": {
+          "function": {
+            "name": "foo",
+            "kind": "freestanding",
+            "params": [
+              {
+                "name": "a",
+                "type": 4
+              }
+            ],
+            "results": [
+              {
+                "type": 5
+              }
+            ]
+          }
+        }
+      },
+      "exports": {
+        "bar": {
+          "function": {
+            "name": "bar",
+            "kind": "freestanding",
+            "params": [
+              {
+                "name": "a",
+                "type": 4
+              }
+            ],
+            "results": [
+              {
+                "type": 5
+              }
+            ]
+          }
+        }
+      },
+      "package": 0
+    }
+  ],
+  "interfaces": [
+    {
+      "name": "disambiguate",
+      "types": {
+        "t": 0
+      },
+      "functions": {},
+      "package": 0
+    }
+  ],
+  "types": [
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "t",
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "kind": {
+        "type": "u32"
+      },
+      "name": "a",
+      "owner": {
+        "world": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 1
+      },
+      "name": "b",
+      "owner": {
+        "world": 0
+      }
+    },
+    {
+      "kind": {
+        "type": 0
+      },
+      "name": "t",
+      "owner": {
+        "world": 1
+      }
+    },
+    {
+      "kind": {
+        "record": {
+          "fields": [
+            {
+              "name": "x",
+              "type": "u32"
+            }
+          ]
+        }
+      },
+      "name": "a",
+      "owner": {
+        "world": 2
+      }
+    },
+    {
+      "kind": {
+        "variant": {
+          "cases": [
+            {
+              "name": "c",
+              "type": 4
+            }
+          ]
+        }
+      },
+      "name": "b",
+      "owner": {
+        "world": 2
+      }
+    }
+  ],
+  "packages": [
+    {
+      "name": "foo:foo",
+      "interfaces": {
+        "disambiguate": 0
+      },
+      "worlds": {
+        "foo": 0,
+        "bar": 1,
+        "the-test": 2
+      }
+    }
+  ]
+}
\ No newline at end of file