feat(ast_tools): output typescript to a separate package (#6755)

Part of #6347.

Moves typescript logic from derive_estree into a new ast_tools generator.
This commit is contained in:
ottomated 2024-10-24 13:08:57 +00:00
parent 1c27a2c36c
commit 1145341a92
30 changed files with 2406 additions and 1071 deletions

View file

@ -32,5 +32,6 @@ src:
- 'crates/oxc_ast/src/generated/ast_builder.rs'
- 'crates/oxc_ast/src/generated/visit.rs'
- 'crates/oxc_ast/src/generated/visit_mut.rs'
- 'npm/oxc-types/src/generated/types.d.ts'
- 'tasks/ast_tools/src/**'
- '.github/.generated_ast_watch_list.yml'

View file

@ -125,18 +125,20 @@ jobs:
with:
cache-key: wasm
save-cache: ${{ github.ref_name == 'main' }}
tools: wasm-pack
tools: wasm-pack,just
- name: Check
run: |
rustup target add wasm32-unknown-unknown
cargo check -p oxc_wasm --target wasm32-unknown-unknown
- uses: ./.github/actions/pnpm
- name: Build
run: |
wasm-pack build --target web --dev ./crates/oxc_wasm
wasm-pack build --target web --dev ./wasm/parser
- uses: ./.github/actions/pnpm
just build-wasm debug
cd ./wasm/parser && pnpm build
- name: Check output types
run: npx -y -p typescript tsc --lib es2020,dom crates/oxc_wasm/pkg/oxc_wasm.d.ts
run: |
pnpm install
npx -y -p typescript tsc --lib es2020,dom npm/oxc-wasm/oxc_wasm.d.ts
typos:
name: Spell Check
@ -262,9 +264,17 @@ jobs:
if: steps.filter.outputs.src == 'true'
with:
components: rustfmt
tools: dprint
cache-key: ast_changes
save-cache: ${{ github.ref_name == 'main' }}
- name: Restore dprint plugin cache
id: cache-restore
uses: actions/cache/restore@v4
with:
key: dprint-autofix-ci-${{ runner.os }}-${{ hashFiles('dprint.json') }}
path: ~/.cache/dprint
- name: Check AST Changes
if: steps.filter.outputs.src == 'true'
run: |
@ -272,6 +282,14 @@ jobs:
git diff --exit-code ||
(echo 'AST changes caused the "generated" code to get outdated. Have you forgotten to run the `just ast` command and/or commit generated codes?' && exit 1)
- name: Save dprint plugin cache
if: ${{ github.ref_name == 'main' }}
id: cache-save
uses: actions/cache/save@v4
with:
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
path: ~/.cache/dprint
napi:
name: Test NAPI
runs-on: ubuntu-latest

54
.github/workflows/release_types.yml vendored Normal file
View file

@ -0,0 +1,54 @@
name: Release @oxc/types
on:
workflow_dispatch:
push:
branches:
- main
paths:
- npm/oxc-types/package.json # Please only commit this file, so we don't need to wait for all the other CI jobs to finish.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Check version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
version_changed: ${{ steps.version.outputs.changed }}
steps:
- uses: taiki-e/checkout-action@v1
- name: Check version changes
uses: EndBug/version-check@v2
id: version
with:
static-checking: localIsNew
file-url: https://unpkg.com/@oxc/types/package.json
file-name: npm/oxc-types/package.json
- name: Set version name
if: steps.version.outputs.changed == 'true'
run: |
echo "Version change found! New version: ${{ steps.version.outputs.version }} (${{ steps.version.outputs.version_type }})"
build:
needs: check
if: needs.check.outputs.version_changed == 'true'
name: Release @oxc/types
runs-on: ubuntu-latest
permissions:
id-token: write # for `pnpm publish --provenance`
steps:
- uses: taiki-e/checkout-action@v1
- uses: ./.github/actions/pnpm
- name: Publish
working-directory: npm/oxc-types
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: pnpm publish --provenance --access public

3
Cargo.lock generated
View file

@ -1453,7 +1453,6 @@ dependencies = [
"oxc_syntax",
"serde",
"serde_json",
"wasm-bindgen",
]
[[package]]
@ -1865,7 +1864,6 @@ dependencies = [
"rustc-hash",
"serde",
"unicode-id-start",
"wasm-bindgen",
]
[[package]]
@ -1937,7 +1935,6 @@ dependencies = [
"oxc_estree",
"schemars",
"serde",
"wasm-bindgen",
]
[[package]]

View file

@ -31,14 +31,12 @@ num-bigint = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true, optional = true }
wasm-bindgen = { workspace = true, optional = true }
[features]
default = []
serialize = [
"dep:serde",
"dep:serde_json",
"dep:wasm-bindgen",
"oxc_allocator/serialize",
"oxc_regular_expression/serialize",
"oxc_span/serialize",

View file

@ -190,26 +190,3 @@ bitflags! {
const V = 1 << 7;
}
}
#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type RegExpFlags = {
/** Global flag */
G: 1,
/** Ignore case flag */
I: 2,
/** Multiline flag */
M: 4,
/** DotAll flag */
S: 8,
/** Unicode flag */
U: 16,
/** Sticky flag */
Y: 32,
/** Indices flag */
D: 64,
/** Unicode sets flag */
V: 128
};
"#;

32
crates/oxc_ast/src/ast/types.d.ts vendored Normal file
View file

@ -0,0 +1,32 @@
export interface FormalParameterRest extends Span {
type: 'RestElement';
argument: BindingPatternKind;
typeAnnotation: TSTypeAnnotation | null;
optional: boolean;
}
export type RegExpFlags = {
/** Global flag */
G: 1;
/** Ignore case flag */
I: 2;
/** Multiline flag */
M: 4;
/** DotAll flag */
S: 8;
/** Unicode flag */
U: 16;
/** Sticky flag */
Y: 32;
/** Indices flag */
D: 64;
/** Unicode sets flag */
V: 128;
};
export type JSXElementName =
| JSXIdentifier
| JSXNamespacedName
| JSXMemberExpression;
export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;

View file

@ -5,13 +5,6 @@ use oxc_span::Atom;
use crate::ast::*;
#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression;
export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;
"#;
// 1.2 JSX Elements
impl<'a> fmt::Display for JSXIdentifier<'a> {

File diff suppressed because it is too large Load diff

View file

@ -156,17 +156,6 @@ impl<'a> Serialize for FormalParameters<'a> {
}
}
#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type FormalParameterRest = ({
type: "RestElement",
argument: BindingPatternKind,
typeAnnotation: TSTypeAnnotation | null,
optional: boolean,
}) & Span;
"#;
#[derive(Serialize)]
#[serde(tag = "type", rename = "FormalParameters")]
struct SerFormalParameters<'a, 'b> {

View file

@ -31,8 +31,7 @@ rustc-hash = { workspace = true }
unicode-id-start = { workspace = true }
serde = { workspace = true, optional = true }
wasm-bindgen = { workspace = true, optional = true }
[features]
default = []
serialize = ["dep:serde", "dep:wasm-bindgen", "oxc_allocator/serialize", "oxc_span/serialize"]
serialize = ["dep:serde", "oxc_allocator/serialize", "oxc_span/serialize"]

View file

@ -18,10 +18,6 @@ impl<'a> Serialize for Pattern<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type Pattern = ({\n\ttype: 'Pattern';\n\tbody: Disjunction;\n}) & Span;";
impl<'a> Serialize for Disjunction<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -32,10 +28,6 @@ impl<'a> Serialize for Disjunction<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type Disjunction = ({\n\ttype: 'Disjunction';\n\tbody: Array<Alternative>;\n}) & Span;";
impl<'a> Serialize for Alternative<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -46,10 +38,6 @@ impl<'a> Serialize for Alternative<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type Alternative = ({\n\ttype: 'Alternative';\n\tbody: Array<Term>;\n}) & Span;";
impl<'a> Serialize for Term<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
@ -69,9 +57,6 @@ impl<'a> Serialize for Term<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Term = BoundaryAssertion | LookAroundAssertion | Quantifier | Character | Dot | CharacterClassEscape | UnicodePropertyEscape | CharacterClass | CapturingGroup | IgnoreGroup | IndexedReference | NamedReference;";
impl Serialize for BoundaryAssertion {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -82,9 +67,6 @@ impl Serialize for BoundaryAssertion {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type BoundaryAssertion = ({\n\ttype: 'BoundaryAssertion';\n\tspan: Span;\n\tkind: BoundaryAssertionKind;\n});";
impl Serialize for BoundaryAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -104,10 +86,6 @@ impl Serialize for BoundaryAssertionKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type BoundaryAssertionKind = 'start' | 'end' | 'boundary' | 'negativeBoundary';";
impl<'a> Serialize for LookAroundAssertion<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -119,9 +97,6 @@ impl<'a> Serialize for LookAroundAssertion<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type LookAroundAssertion = ({\n\ttype: 'LookAroundAssertion';\n\tkind: LookAroundAssertionKind;\n\tbody: Disjunction;\n}) & Span;";
impl Serialize for LookAroundAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -145,9 +120,6 @@ impl Serialize for LookAroundAssertionKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type LookAroundAssertionKind = 'lookahead' | 'negativeLookahead' | 'lookbehind' | 'negativeLookbehind';";
impl<'a> Serialize for Quantifier<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -161,9 +133,6 @@ impl<'a> Serialize for Quantifier<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Quantifier = ({\n\ttype: 'Quantifier';\n\tmin: number;\n\tmax: (number) | null;\n\tgreedy: boolean;\n\tbody: Term;\n}) & Span;";
impl Serialize for Character {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -175,9 +144,6 @@ impl Serialize for Character {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Character = ({\n\ttype: 'Character';\n\tkind: CharacterKind;\n\tvalue: number;\n}) & Span;";
impl Serialize for CharacterKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -213,9 +179,6 @@ impl Serialize for CharacterKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterKind = 'controlLetter' | 'hexadecimalEscape' | 'identifier' | 'null' | 'octal1' | 'octal2' | 'octal3' | 'singleEscape' | 'symbol' | 'unicodeEscape';";
impl Serialize for CharacterClassEscape {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -226,9 +189,6 @@ impl Serialize for CharacterClassEscape {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterClassEscape = ({\n\ttype: 'CharacterClassEscape';\n\tkind: CharacterClassEscapeKind;\n}) & Span;";
impl Serialize for CharacterClassEscapeKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -254,9 +214,6 @@ impl Serialize for CharacterClassEscapeKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterClassEscapeKind = 'd' | 'negativeD' | 's' | 'negativeS' | 'w' | 'negativeW';";
impl<'a> Serialize for UnicodePropertyEscape<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -270,9 +227,6 @@ impl<'a> Serialize for UnicodePropertyEscape<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type UnicodePropertyEscape = ({\n\ttype: 'UnicodePropertyEscape';\n\tnegative: boolean;\n\tstrings: boolean;\n\tname: string;\n\tvalue: (string) | null;\n}) & Span;";
impl Serialize for Dot {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -282,9 +236,6 @@ impl Serialize for Dot {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Dot = ({\n\ttype: 'Dot';\n}) & Span;";
impl<'a> Serialize for CharacterClass<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -298,9 +249,6 @@ impl<'a> Serialize for CharacterClass<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterClass = ({\n\ttype: 'CharacterClass';\n\tnegative: boolean;\n\tstrings: boolean;\n\tkind: CharacterClassContentsKind;\n\tbody: Array<CharacterClassContents>;\n}) & Span;";
impl Serialize for CharacterClassContentsKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -319,10 +267,6 @@ impl Serialize for CharacterClassContentsKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type CharacterClassContentsKind = 'union' | 'intersection' | 'subtraction';";
impl<'a> Serialize for CharacterClassContents<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
@ -338,9 +282,6 @@ impl<'a> Serialize for CharacterClassContents<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterClassContents = CharacterClassRange | CharacterClassEscape | UnicodePropertyEscape | Character | CharacterClass | ClassStringDisjunction;";
impl Serialize for CharacterClassRange {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -352,9 +293,6 @@ impl Serialize for CharacterClassRange {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CharacterClassRange = ({\n\ttype: 'CharacterClassRange';\n\tmin: Character;\n\tmax: Character;\n}) & Span;";
impl<'a> Serialize for ClassStringDisjunction<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -366,9 +304,6 @@ impl<'a> Serialize for ClassStringDisjunction<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type ClassStringDisjunction = ({\n\ttype: 'ClassStringDisjunction';\n\tstrings: boolean;\n\tbody: Array<ClassString>;\n}) & Span;";
impl<'a> Serialize for ClassString<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -380,9 +315,6 @@ impl<'a> Serialize for ClassString<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type ClassString = ({\n\ttype: 'ClassString';\n\tstrings: boolean;\n\tbody: Array<Character>;\n}) & Span;";
impl<'a> Serialize for CapturingGroup<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -394,9 +326,6 @@ impl<'a> Serialize for CapturingGroup<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type CapturingGroup = ({\n\ttype: 'CapturingGroup';\n\tname: (string) | null;\n\tbody: Disjunction;\n}) & Span;";
impl<'a> Serialize for IgnoreGroup<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -408,9 +337,6 @@ impl<'a> Serialize for IgnoreGroup<'a> {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type IgnoreGroup = ({\n\ttype: 'IgnoreGroup';\n\tmodifiers: (Modifiers) | null;\n\tbody: Disjunction;\n}) & Span;";
impl Serialize for Modifiers {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -422,9 +348,6 @@ impl Serialize for Modifiers {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Modifiers = ({\n\ttype: 'Modifiers';\n\tenabling: (Modifier) | null;\n\tdisabling: (Modifier) | null;\n}) & Span;";
impl Serialize for Modifier {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -436,9 +359,6 @@ impl Serialize for Modifier {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type Modifier = ({\n\ttype: 'Modifier';\n\tignoreCase: boolean;\n\tmultiline: boolean;\n\tsticky: boolean;\n});";
impl Serialize for IndexedReference {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -449,10 +369,6 @@ impl Serialize for IndexedReference {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type IndexedReference = ({\n\ttype: 'IndexedReference';\n\tindex: number;\n}) & Span;";
impl<'a> Serialize for NamedReference<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -462,7 +378,3 @@ impl<'a> Serialize for NamedReference<'a> {
map.end()
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type NamedReference = ({\n\ttype: 'NamedReference';\n\tname: string;\n}) & Span;";

View file

@ -29,9 +29,8 @@ miette = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
wasm-bindgen = { workspace = true, optional = true }
[features]
default = []
serialize = ["compact_str/serde", "dep:serde", "dep:wasm-bindgen"]
serialize = ["compact_str/serde", "dep:serde"]
schemars = ["dep:schemars"]

View file

@ -20,10 +20,6 @@ impl Serialize for Span {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type Span = ({\n\tstart: number;\n\tend: number;\n});";
impl Serialize for SourceType {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
@ -34,9 +30,6 @@ impl Serialize for SourceType {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type SourceType = ({\n\tlanguage: Language;\n\tmoduleKind: ModuleKind;\n\tvariant: LanguageVariant;\n});";
impl Serialize for Language {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -53,10 +46,6 @@ impl Serialize for Language {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type Language = 'javascript' | 'typescript' | 'typescriptDefinition';";
impl Serialize for ModuleKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -69,10 +58,6 @@ impl Serialize for ModuleKind {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type ModuleKind = 'script' | 'module' | 'unambiguous';";
impl Serialize for LanguageVariant {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -85,6 +70,3 @@ impl Serialize for LanguageVariant {
}
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type LanguageVariant = 'standard' | 'jsx';";

View file

@ -63,9 +63,6 @@ impl Serialize for AssignmentOperator {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type AssignmentOperator = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '<<=' | '>>=' | '>>>=' | '|=' | '^=' | '&=' | '&&=' | '||=' | '??=' | '**=';";
impl Serialize for BinaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -137,9 +134,6 @@ impl Serialize for BinaryOperator {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type BinaryOperator = '==' | '!=' | '===' | '!==' | '<' | '<=' | '>' | '>=' | '<<' | '>>' | '>>>' | '+' | '-' | '*' | '/' | '%' | '|' | '^' | '&' | 'in' | 'instanceof' | '**';";
impl Serialize for LogicalOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -154,9 +148,6 @@ impl Serialize for LogicalOperator {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type LogicalOperator = '||' | '&&' | '??';";
impl Serialize for UnaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -183,10 +174,6 @@ impl Serialize for UnaryOperator {
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str =
"export type UnaryOperator = '-' | '+' | '!' | '~' | 'typeof' | 'void' | 'delete';";
impl Serialize for UpdateOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match *self {
@ -199,6 +186,3 @@ impl Serialize for UpdateOperator {
}
}
}
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = "export type UpdateOperator = '++' | '--';";

View file

@ -0,0 +1,30 @@
{
"name": "oxc_wasm",
"type": "module",
"collaborators": [
"Boshen <boshenc@gmail.com>",
"Oxc contributors"
],
"version": "0.0.0",
"license": "MIT",
"files": [
"oxc_wasm_bg.wasm",
"oxc_wasm.js",
"oxc_wasm.d.ts"
],
"devDependencies": {
"@oxc/types": "workspace:^"
},
"main": "oxc_wasm.js",
"types": "oxc_wasm.d.ts",
"sideEffects": [
"./snippets/*"
],
"keywords": [
"JavaScript",
"TypeScript",
"linter",
"minifier",
"parser"
]
}

View file

@ -32,6 +32,12 @@ use wasm_bindgen::prelude::*;
use crate::options::{OxcOptions, OxcRunOptions};
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
import type { Program, Span } from "@oxc/types";
export * from "@oxc/types";
"#;
#[wasm_bindgen(getter_with_clone)]
#[derive(Default, Tsify)]
#[serde(rename_all = "camelCase")]

View file

@ -136,6 +136,7 @@ watch-wasm:
build-wasm mode="release":
wasm-pack build --out-dir ../../npm/oxc-wasm --target web --{{mode}} --scope oxc crates/oxc_wasm
cp crates/oxc_wasm/package.json npm/oxc-wasm/package.json
# Generate the JavaScript global variables. See `tasks/javascript_globals`
javascript-globals:

3
npm/oxc-types/README.md Normal file
View file

@ -0,0 +1,3 @@
# Oxc Types
Typescript definitions for Oxc AST nodes.

View file

@ -0,0 +1,27 @@
{
"name": "@oxc/types",
"version": "0.32.0",
"description": "Types for Oxc AST nodes",
"keywords": [
"AST",
"Parser"
],
"author": "Boshen and oxc contributors",
"license": "MIT",
"homepage": "https://oxc.rs",
"bugs": "https://github.com/oxc-project/oxc/issues",
"repository": {
"type": "git",
"url": "https://github.com/oxc-project/oxc.git",
"directory": "npm/oxc-types"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"main": "",
"types": "src/index.d.ts",
"files": [
"src/index.d.ts",
"src/generated/types.d.ts"
]
}

2010
npm/oxc-types/src/generated/types.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

1
npm/oxc-types/src/index.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export * from './generated/types';

View file

@ -58,6 +58,26 @@ importers:
napi/transform: {}
npm/oxc-parser: {}
npm/oxc-transform: {}
npm/oxc-types: {}
npm/oxc-wasm:
devDependencies:
'@oxc/types':
specifier: workspace:^
version: link:../oxc-types
npm/oxlint: {}
npm/parser-wasm:
devDependencies:
'@oxc/types':
specifier: workspace:^
version: link:../oxc-types
tasks/benchmark/codspeed:
devDependencies:
axios:
@ -70,7 +90,13 @@ importers:
specifier: ^7.2.0
version: 7.4.3
wasm/parser: {}
wasm/parser:
devDependencies:
'@oxc/types':
specifier: workspace:^
version: link:../../npm/oxc-types
wasm/parser/pkg: {}
packages:

View file

@ -3,6 +3,7 @@ packages:
- 'wasm/**'
- 'editors/**'
- 'tasks/benchmark/codspeed'
- 'npm/**'
catalog:
"@napi-rs/cli": 3.0.0-alpha.61

View file

@ -1,5 +1,4 @@
use convert_case::{Case, Casing};
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::quote;
@ -9,7 +8,7 @@ use crate::{
markers::ESTreeStructAttribute,
schema::{
serialize::{enum_variant_name, get_type_tag},
EnumDef, GetGenerics, GetIdent, StructDef, TypeDef, TypeName,
EnumDef, GetGenerics, GetIdent, StructDef, TypeDef,
},
};
@ -27,19 +26,6 @@ impl Derive for DeriveESTree {
}
fn derive(&mut self, def: &TypeDef, _: &LateCtx) -> TokenStream {
let ts_type_def = match def {
TypeDef::Enum(def) => typescript_enum(def),
TypeDef::Struct(def) => Some(typescript_struct(def)),
};
let ts_type_def = if let Some(ts_type_def) = ts_type_def {
quote! {
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = #ts_type_def;
}
} else {
TokenStream::new()
};
if let TypeDef::Struct(def) = def {
if def
.markers
@ -47,7 +33,7 @@ impl Derive for DeriveESTree {
.as_ref()
.is_some_and(|e| e == &ESTreeStructAttribute::CustomSerialize)
{
return ts_type_def;
return TokenStream::new();
}
}
@ -64,9 +50,6 @@ impl Derive for DeriveESTree {
#body
}
}
///@@line_break
#ts_type_def
}
}
@ -171,72 +154,3 @@ fn serialize_enum(def: &EnumDef) -> TokenStream {
}
}
}
// Untagged enums: "type Expression = BooleanLiteral | NullLiteral"
// Tagged enums: "type PropertyKind = 'init' | 'get' | 'set'"
fn typescript_enum(def: &EnumDef) -> Option<String> {
if def.markers.estree.custom_ts_def {
return None;
}
let union = if def.markers.estree.untagged {
def.all_variants().map(|var| type_to_string(var.fields[0].typ.name())).join(" | ")
} else {
def.all_variants().map(|var| format!("'{}'", enum_variant_name(var, def))).join(" | ")
};
let ident = def.ident();
Some(format!("export type {ident} = {union};"))
}
fn typescript_struct(def: &StructDef) -> String {
let ident = def.ident();
let mut fields = String::new();
let mut extends = vec![];
if let Some(type_tag) = get_type_tag(def) {
fields.push_str(&format!("\n\ttype: '{type_tag}';"));
}
for field in &def.fields {
if field.markers.derive_attributes.estree.skip {
continue;
}
let ty = match &field.markers.derive_attributes.tsify_type {
Some(ty) => ty.clone(),
None => type_to_string(field.typ.name()),
};
if field.markers.derive_attributes.estree.flatten {
extends.push(ty);
continue;
}
let name = match &field.markers.derive_attributes.estree.rename {
Some(rename) => rename.to_string(),
None => field.name.clone().unwrap().to_case(Case::Camel),
};
fields.push_str(&format!("\n\t{name}: {ty};"));
}
let extends =
if extends.is_empty() { String::new() } else { format!(" & {}", extends.join(" & ")) };
format!("export type {ident} = ({{{fields}\n}}){extends};")
}
fn type_to_string(ty: &TypeName) -> String {
match ty {
TypeName::Ident(ident) => match ident.as_str() {
"f64" | "f32" | "usize" | "u64" | "u32" | "u16" | "u8" | "i64" | "i32" | "i16"
| "i8" => "number",
"bool" => "boolean",
"str" | "String" | "Atom" | "CompactStr" => "string",
ty => ty,
}
.to_string(),
TypeName::Vec(type_name) => format!("Array<{}>", type_to_string(type_name)),
TypeName::Box(type_name) | TypeName::Ref(type_name) | TypeName::Complex(type_name) => {
type_to_string(type_name)
}
TypeName::Opt(type_name) => format!("({}) | null", type_to_string(type_name)),
}
}

View file

@ -7,11 +7,13 @@ use crate::codegen::LateCtx;
mod assert_layouts;
mod ast_builder;
mod ast_kind;
mod typescript;
mod visit;
pub use assert_layouts::AssertLayouts;
pub use ast_builder::AstBuilderGenerator;
pub use ast_kind::AstKindGenerator;
pub use typescript::TypescriptGenerator;
pub use visit::{VisitGenerator, VisitMutGenerator};
/// Inserts a newline in the `TokenStream`.
@ -28,15 +30,8 @@ pub trait Generator {
#[derive(Debug, Clone)]
pub enum GeneratorOutput {
Rust {
path: PathBuf,
tokens: TokenStream,
},
#[expect(dead_code)]
Text {
path: PathBuf,
content: String,
},
Rust { path: PathBuf, tokens: TokenStream },
Text { path: PathBuf, content: String },
}
macro_rules! define_generator {

View file

@ -0,0 +1,170 @@
use convert_case::{Case, Casing};
use itertools::Itertools;
use std::{
io::Write,
process::{Command, Stdio},
};
use super::define_generator;
use crate::{
codegen::LateCtx,
output,
schema::{
serialize::{enum_variant_name, get_type_tag},
EnumDef, GetIdent, StructDef, TypeDef, TypeName,
},
Generator, GeneratorOutput,
};
const CUSTOM_TYPESCRIPT: &str = include_str!("../../../../crates/oxc_ast/src/ast/types.d.ts");
define_generator! {
pub struct TypescriptGenerator;
}
impl Generator for TypescriptGenerator {
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
let file = file!().replace('\\', "/");
let mut content = format!(
"\
// To edit this generated file you have to edit `{file}`\n\
// Auto-generated code, DO NOT EDIT DIRECTLY!\n\n\
{CUSTOM_TYPESCRIPT}\n\
"
);
for def in ctx.schema() {
if !def.generates_derive("ESTree") {
continue;
}
let ts_type_def = match def {
TypeDef::Struct(it) => Some(typescript_struct(it)),
TypeDef::Enum(it) => typescript_enum(it),
};
let Some(ts_type_def) = ts_type_def else { continue };
content.push_str(&ts_type_def);
content.push_str("\n\n");
}
GeneratorOutput::Text {
path: output(crate::TYPESCRIPT_PACKAGE, "types.d.ts"),
content: format_typescript(&content),
}
}
}
// Untagged enums: "type Expression = BooleanLiteral | NullLiteral"
// Tagged enums: "type PropertyKind = 'init' | 'get' | 'set'"
fn typescript_enum(def: &EnumDef) -> Option<String> {
if def.markers.estree.custom_ts_def {
return None;
}
let union = if def.markers.estree.untagged {
def.all_variants().map(|var| type_to_string(var.fields[0].typ.name())).join(" | ")
} else {
def.all_variants().map(|var| format!("'{}'", enum_variant_name(var, def))).join(" | ")
};
let ident = def.ident();
Some(format!("export type {ident} = {union};"))
}
fn typescript_struct(def: &StructDef) -> String {
let ident = def.ident();
let mut fields = String::new();
let mut extends = vec![];
if let Some(type_tag) = get_type_tag(def) {
fields.push_str(&format!("\n\ttype: '{type_tag}';"));
}
for field in &def.fields {
if field.markers.derive_attributes.estree.skip {
continue;
}
let ty = match &field.markers.derive_attributes.tsify_type {
Some(ty) => ty.clone(),
None => type_to_string(field.typ.name()),
};
if field.markers.derive_attributes.estree.flatten {
extends.push(ty);
continue;
}
let name = match &field.markers.derive_attributes.estree.rename {
Some(rename) => rename.to_string(),
None => field.name.clone().unwrap().to_case(Case::Camel),
};
fields.push_str(&format!("\n\t{name}: {ty};"));
}
let extends_union = extends.iter().any(|it| it.contains('|'));
if extends_union {
let extends =
if extends.is_empty() { String::new() } else { format!(" & {}", extends.join(" & ")) };
format!("export type {ident} = ({{{fields}\n}}){extends};")
} else {
let extends = if extends.is_empty() {
String::new()
} else {
format!(" extends {}", extends.join(", "))
};
format!("export interface {ident}{extends} {{{fields}\n}}")
}
}
fn type_to_string(ty: &TypeName) -> String {
match ty {
TypeName::Ident(ident) => match ident.as_str() {
"f64" | "f32" | "usize" | "u64" | "u32" | "u16" | "u8" | "i64" | "i32" | "i16"
| "i8" => "number",
"bool" => "boolean",
"str" | "String" | "Atom" | "CompactStr" => "string",
ty => ty,
}
.to_string(),
TypeName::Vec(type_name) => format!("Array<{}>", type_to_string(type_name)),
TypeName::Box(type_name) | TypeName::Ref(type_name) | TypeName::Complex(type_name) => {
type_to_string(type_name)
}
TypeName::Opt(type_name) => format!("({}) | null", type_to_string(type_name)),
}
}
fn format_typescript(source_text: &str) -> String {
let mut dprint = Command::new("dprint")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.args(["fmt", "--stdin", "types.d.ts"])
.spawn()
.expect("Failed to run dprint (is it installed?)");
let stdin = dprint.stdin.as_mut().unwrap();
stdin.write_all(source_text.as_bytes()).unwrap();
stdin.flush().unwrap();
let output = dprint.wait_with_output().unwrap();
String::from_utf8(output.stdout).unwrap()
}
// Unusable until oxc_prettier supports comments
// fn format_typescript(source_text: &str) -> String {
// let allocator = Allocator::default();
// let source_type = SourceType::ts();
// let ret = Parser::new(&allocator, source_text, source_type)
// .with_options(ParseOptions { preserve_parens: false, ..ParseOptions::default() })
// .parse();
// Prettier::new(
// &allocator,
// PrettierOptions {
// semi: true,
// trailing_comma: TrailingComma::All,
// single_quote: true,
// ..PrettierOptions::default()
// },
// )
// .build(&ret.program)
// }

View file

@ -24,7 +24,7 @@ use derives::{
use fmt::cargo_fmt;
use generators::{
AssertLayouts, AstBuilderGenerator, AstKindGenerator, Generator, GeneratorOutput,
VisitGenerator, VisitMutGenerator,
TypescriptGenerator, VisitGenerator, VisitMutGenerator,
};
use passes::{CalcLayout, Linker};
use util::{write_all_to, NormalizeError};
@ -43,6 +43,7 @@ static SOURCE_PATHS: &[&str] = &[
];
const AST_CRATE: &str = "crates/oxc_ast";
const TYPESCRIPT_PACKAGE: &str = "npm/oxc-types";
type Result<R> = std::result::Result<R, String>;
type TypeId = usize;
@ -84,6 +85,7 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
.generate(AstBuilderGenerator)
.generate(VisitGenerator)
.generate(VisitMutGenerator)
.generate(TypescriptGenerator)
.run()?;
if !cli_options.dry_run {

View file

@ -26,6 +26,9 @@
"node",
"web"
],
"devDependencies": {
"@oxc/types": "workspace:^"
},
"scripts": {
"build": "pnpm run build-node && pnpm run build-web && pnpm run copy-files && pnpm run clean-files",
"build-node": "pnpm run build-base --target nodejs --out-dir ../../npm/parser-wasm/node .",

View file

@ -7,6 +7,12 @@ use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::*;
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
import type { Program } from "@oxc/types";
export * from "@oxc/types";
"#;
#[derive(Debug, Default, Clone, Deserialize, Tsify)]
#[tsify(from_wasm_abi)]
#[serde(rename_all = "camelCase")]