Skip to content

Implement support for ReScript language - #295

Open
TomiS wants to merge 5 commits into
1Password:mainfrom
TomiS:rescript-support
Open

TomiS wants to merge 5 commits into
1Password:mainfrom
TomiS:rescript-support

Conversation

@TomiS

@TomiS TomiS commented Aug 28, 2026 •

Copy link
Copy Markdown

This PR adds initial support for ReScript language, including almost all of the tests.

@TomiS TomiS changed the title Implement support for ReScript language and add devcontainer config Implement support for ReScript language Aug 28, 2026
…development easier

Implement support for ReScript language

rescript support

devcontainer lock

integer types snapshot

decoded variable name snapshot

type alias snapshot and fix

enum alias snapshot

simple enum case

serialize as string

serialized as

serde url

serde 8601

serde default struct

optional type alias

char

fix double optional

fix type override

comment unused code

default decorators

cleanup

Improve ReScript support and add tests

Add 2 rescript tests

one more test

one more test

add test

new test

change Vec<u8> conversion

remove unnecessary file

add namespacing to anonymous structs

add mention of ReScript into readme

remove devcontainer config
@TomiS
TomiS force-pushed the rescript-support branch from 51c1176 to eec36e3 Compare August 28, 2026 16:03
Comment on lines +281 to +287
let combined_decorators: Vec<String> = self
.default_decorators
.iter()
.chain(rescript_decs.iter())
.cloned()
.collect();
combined_decorators.join(" ")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use Itertools::join

            self.default_decorators
                .iter()
                .chain(rescript_decs.iter())
                .join(" ")

Comment on lines +100 to +103
// if self.custom_translations(mapped).is_some() {
// self.types_for_custom_json_translation
// .insert(mapped.to_string(), BTreeSet::new());
// }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a leftover or it's supposed to be uncommented?

Comment on lines +123 to +128
let _hashtype = match rtype1.as_ref() {
RustType::Simple { id } if generic_types.contains(id) => {
return Err(RustTypeFormatError::GenericKeyForbiddenInTS(id.clone()));
}
_ => self.format_type(rtype1, generic_types)?,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A doubt: in Rescript Dict is "only" with string keys, right? Leave alone the madness about using numbers that are automagically converted into strings, and forget about symbols, the intrinsic logic here forbids the use of arbitrary types for rtype1 or that should be actually checked to be some kind of string?

Comment on lines +179 to +199
writeln!(
w,
"type {}{} = {}\n",
rescript_keyword_aware_rename(ty.id.renamed.to_camel_case()),
if !ty.generic_types.is_empty() {
format!(
"<{}>",
ty.generic_types
.iter()
.map(|s| format!("'{}", s.to_camel_case()))
.join(", ")
)
} else {
Default::default()
},
if ty.r#type.is_optional() {
format!("option<{}>", r#type)
} else {
r#type
},
)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could split the two last formatted string into separate expression in order to make the code a bit more clear. I also optimized a bit to avoid string allocation, but feel free to do something in between to keep the code more simple.

let generics = fmt::from_fn(|f| {
    if !ty.generic_types.is_empty() {
        f.write_char('<')?;
        fn format_generic(generic_type: &String) -> impl fmt::Display + use<'_> {
            fmt::from_fn(move |f| write!(f, "'{}", generic_type.to_camel_case()))
        }
        let mut generic_types = ty.generic_types.iter();
        if let Some(generic_type) = generic_types.next() {
            write!(f, "{}", format_generic(generic_type))?;
            for generic_type in generic_types {
                write!(f, ", {}", format_generic(generic_type))?;
            }
        }
        f.write_char('>')?;
    }
    Ok(())
});
let type_writer = fmt::from_fn(|f| {
    if ty.r#type.is_optional() {
        write!(f, "option<{}>", r#type)
    } else {
        f.write_str(&r#type)
    }
});
writeln!(
    w,
    "type {}{} = {}\n",
    rescript_keyword_aware_rename(ty.id.renamed.to_camel_case()),
    generics,
    type_writer,
)?;

rescript_keyword_aware_rename(type_name),
generic_parameters
)?;
// writeln!(w, "type t = {{")?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover

.iter()
.try_for_each(|f| self.write_field(w, f, rs.generic_types.as_slice()))?;

// writeln!(w, "}}\n")?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover

Comment on lines +528 to +537
if RESCRIPT_KEYWORDS.contains(&name.as_ref()) {
Cow::Owned(format!("\\\"{name}\""))
} else {
// If name contains hyphen
if name.contains('-') {
Cow::Owned(format!("\\\"{name}\""))
} else {
name
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be simplified to

    if RESCRIPT_KEYWORDS.contains(&name.as_ref()) || name.contains('-') {
        Cow::Owned(format!("\\\"{name}\""))
    } else {
        name
    }

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants