From 49a9cff1b3c795e3ba00eba4cac9651923f1a0f2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sat, 29 Jun 2024 14:38:19 +0900 Subject: [PATCH] feat(oxc-rsc): rsc transform in oxc (#47) --- .github/workflows/ci.yml | 11 + oxc-rsc/.gitignore | 2 + oxc-rsc/Cargo.lock | 1032 +++++++++++++++++ oxc-rsc/Cargo.toml | 17 + oxc-rsc/README.md | 7 + oxc-rsc/package.json | 10 + oxc-rsc/rust-toolchain.toml | 3 + oxc-rsc/src/hoist.rs | 384 ++++++ oxc-rsc/src/lib.rs | 1 + oxc-rsc/tests/hoist/arrow.js | 11 + oxc-rsc/tests/hoist/arrow.snap | 14 + oxc-rsc/tests/hoist/bind.js | 37 + oxc-rsc/tests/hoist/bind.snap | 36 + oxc-rsc/tests/hoist/function-declaration.js | 12 + oxc-rsc/tests/hoist/function-declaration.snap | 16 + oxc-rsc/tests/hoist/function-expression.js | 12 + oxc-rsc/tests/hoist/function-expression.snap | 14 + oxc-rsc/tests/hoist/top.js | 25 + oxc-rsc/tests/hoist/top.snap | 23 + 19 files changed, 1667 insertions(+) create mode 100644 oxc-rsc/.gitignore create mode 100644 oxc-rsc/Cargo.lock create mode 100644 oxc-rsc/Cargo.toml create mode 100644 oxc-rsc/README.md create mode 100644 oxc-rsc/package.json create mode 100644 oxc-rsc/rust-toolchain.toml create mode 100644 oxc-rsc/src/hoist.rs create mode 100644 oxc-rsc/src/lib.rs create mode 100644 oxc-rsc/tests/hoist/arrow.js create mode 100644 oxc-rsc/tests/hoist/arrow.snap create mode 100644 oxc-rsc/tests/hoist/bind.js create mode 100644 oxc-rsc/tests/hoist/bind.snap create mode 100644 oxc-rsc/tests/hoist/function-declaration.js create mode 100644 oxc-rsc/tests/hoist/function-declaration.snap create mode 100644 oxc-rsc/tests/hoist/function-expression.js create mode 100644 oxc-rsc/tests/hoist/function-expression.snap create mode 100644 oxc-rsc/tests/hoist/top.js create mode 100644 oxc-rsc/tests/hoist/top.snap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d143649..fb46d24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,3 +87,14 @@ jobs: - run: pnpm test-e2e - run: pnpm build - run: pnpm test-e2e-preview + + oxc-rsc: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./oxc-rsc + steps: + - uses: actions/checkout@v4 + - run: rustup show + - run: cargo fmt --check + - run: cargo test diff --git a/oxc-rsc/.gitignore b/oxc-rsc/.gitignore new file mode 100644 index 0000000..a0cefe4 --- /dev/null +++ b/oxc-rsc/.gitignore @@ -0,0 +1,2 @@ +target +*.debug diff --git a/oxc-rsc/Cargo.lock b/oxc-rsc/Cargo.lock new file mode 100644 index 0000000..5595c12 --- /dev/null +++ b/oxc-rsc/Cargo.lock @@ -0,0 +1,1032 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "assert-unchecked" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7330592adf847ee2e3513587b4db2db410a0d751378654e7e993d9adcbe5c795" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys", +] + +[[package]] +name = "daachorse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b7ef7a4be509357f4804d0a22e830daddb48f19fd604e4ad32ddce04a94c36" + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "insta" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" +dependencies = [ + "console", + "globset", + "lazy_static", + "linked-hash-map", + "similar", + "walkdir", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "cfg-if", + "miette-derive", + "owo-colors", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + +[[package]] +name = "oxc" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b98ed0ccd09e8f5cfc5805c3eda59e1582296e25e8e9b56b078ac1350f473c" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_codegen", + "oxc_diagnostics", + "oxc_index", + "oxc_parser", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_transformer", +] + +[[package]] +name = "oxc-browserslist" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d67676f247b63c0f6a8ed20f6c1faa39ff5e0e4af923284a0857623eec5ab14" +dependencies = [ + "nom", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "time", +] + +[[package]] +name = "oxc-rsc" +version = "0.0.0" +dependencies = [ + "base64", + "insta", + "oxc", + "oxc_traverse", +] + +[[package]] +name = "oxc_allocator" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef6ad8a6770a741b0b703ddc2fc01f470b55129ce0afc45cf2c06aa5e6d4c45" +dependencies = [ + "allocator-api2", + "bumpalo", +] + +[[package]] +name = "oxc_ast" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb409cbd3bd77a493fd504c5db2b5e8ebfc9007a93c2d754684587d9e7ef5d9d" +dependencies = [ + "bitflags", + "num-bigint", + "oxc_allocator", + "oxc_ast_macros", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_ast_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8098332429fb072c7e256152820257afc1b7c33a331966263275150d0a47d0e1" + +[[package]] +name = "oxc_cfg" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf812e39e51f854990d9c55020db4135bc075d07a1550285ad846603c6d458a2" +dependencies = [ + "bitflags", + "itertools", + "oxc_syntax", + "petgraph", + "rustc-hash", +] + +[[package]] +name = "oxc_codegen" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cbe74adc40504a7b5ca703e825647467e60ebfd0b90533155fc091bc09a7d8e" +dependencies = [ + "bitflags", + "daachorse", + "once_cell", + "oxc_allocator", + "oxc_ast", + "oxc_sourcemap", + "oxc_span", + "oxc_syntax", + "rustc-hash", +] + +[[package]] +name = "oxc_diagnostics" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48093d74f54f4bf5088c5c06474dc4cac6b56aaad6edf78f39bfdc9e9a78166f" +dependencies = [ + "miette", + "owo-colors", + "textwrap", + "unicode-width", +] + +[[package]] +name = "oxc_index" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c463a51782cbc6f7accfae849a3ba4018792d76010c0c0719b2b97e495484c" + +[[package]] +name = "oxc_parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9936b21a3b353950bf3f82a01feaef676969b0b22d39e8977477c9eb7e0e2037" +dependencies = [ + "assert-unchecked", + "bitflags", + "memchr", + "num-bigint", + "num-traits", + "oxc_allocator", + "oxc_ast", + "oxc_diagnostics", + "oxc_span", + "oxc_syntax", + "rustc-hash", + "seq-macro", +] + +[[package]] +name = "oxc_semantic" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db799530957d0492885fc4e81ff0261edbece5426311f177478b0c4a5f029af0" +dependencies = [ + "indexmap", + "itertools", + "oxc_allocator", + "oxc_ast", + "oxc_cfg", + "oxc_diagnostics", + "oxc_index", + "oxc_span", + "oxc_syntax", + "phf", + "rustc-hash", +] + +[[package]] +name = "oxc_sourcemap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "748747fd08f167c39b47722b09e6df1e5aff41028e804aa342174f6cd103b602" +dependencies = [ + "base64-simd", + "cfg-if", + "rustc-hash", + "serde", + "serde_json", +] + +[[package]] +name = "oxc_span" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c7b464bb6139857ef6327801a35216d94434f07aaddc8bdb1677bcd8212ec18" +dependencies = [ + "compact_str", + "miette", +] + +[[package]] +name = "oxc_syntax" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261dfc1ff75d4eeafeaffd550380c61ae910aa4f5aa1e5690c12fe625000b249" +dependencies = [ + "bitflags", + "dashmap", + "oxc_index", + "oxc_span", + "phf", + "rustc-hash", + "ryu-js", + "unicode-id-start", +] + +[[package]] +name = "oxc_transformer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9cfc000e99a78e67126dab8087ef439f868d97ef18fc218f850b9cf0d1686a" +dependencies = [ + "dashmap", + "indexmap", + "oxc-browserslist", + "oxc_allocator", + "oxc_ast", + "oxc_diagnostics", + "oxc_span", + "oxc_syntax", + "oxc_traverse", + "ropey", + "rustc-hash", + "serde", + "serde_json", +] + +[[package]] +name = "oxc_traverse" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b042af1eeddde5afe385a43e9def84f9eedaf1f5d8e339d82cd25a4d8be751" +dependencies = [ + "compact_str", + "memoffset", + "oxc_allocator", + "oxc_ast", + "oxc_semantic", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seq-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_indices" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c" + +[[package]] +name = "syn" +version = "2.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "unicode-id-start" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f73150333cb58412db36f2aca8f2875b013049705cc77b94ded70a1ab1f5da" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/oxc-rsc/Cargo.toml b/oxc-rsc/Cargo.toml new file mode 100644 index 0000000..5988de7 --- /dev/null +++ b/oxc-rsc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "oxc-rsc" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +oxc = { version = "0.16.0", features = ["transformer", "codegen", "semantic"] } +oxc_traverse = { version = "0.16.0" } + +[dev-dependencies] +base64 = "0.22.1" +insta = { version = "1.39.0", features = ["glob"] } + +[profile.dev.package] +insta.opt-level = 3 +similar.opt-level = 3 diff --git a/oxc-rsc/README.md b/oxc-rsc/README.md new file mode 100644 index 0000000..586dfd3 --- /dev/null +++ b/oxc-rsc/README.md @@ -0,0 +1,7 @@ +# oxc-rsc-transform + +Porting estree-based transforms from [`@hiogawa/transforms`](https://github.com/hi-ogawa/vite-plugins/tree/main/packages/transforms) into [Oxc](https://github.com/oxc-project/oxc) + +```sh +cargo test +``` diff --git a/oxc-rsc/package.json b/oxc-rsc/package.json new file mode 100644 index 0000000..fe4a366 --- /dev/null +++ b/oxc-rsc/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "scripts": { + "lint": "cargo fmt", + "lint-check": "cargo fmt --check", + "test": "cargo test", + "snapshot": "cargo insta test --review" + }, + "packageManager": "pnpm@9.3.0+sha512.ee7b93e0c2bd11409c6424f92b866f31d3ea1bef5fbe47d3c7500cdc3c9668833d2e55681ad66df5b640c61fa9dc25d546efa54d76d7f8bf54b13614ac293631" +} diff --git a/oxc-rsc/rust-toolchain.toml b/oxc-rsc/rust-toolchain.toml new file mode 100644 index 0000000..7ae2b72 --- /dev/null +++ b/oxc-rsc/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.79.0" +profile = "default" diff --git a/oxc-rsc/src/hoist.rs b/oxc-rsc/src/hoist.rs new file mode 100644 index 0000000..542baea --- /dev/null +++ b/oxc-rsc/src/hoist.rs @@ -0,0 +1,384 @@ +use std::collections::BTreeSet; + +use oxc::{ + ast::ast::{ + Argument, BindingIdentifier, Declaration, Expression, FormalParameterKind, + FormalParameters, FunctionBody, FunctionType, NullLiteral, Statement, + }, + semantic::{Reference, ScopeId}, + span::{Span, SPAN}, +}; +use oxc_traverse::Traverse; + +// porting +// https://github.com/hi-ogawa/vite-plugins/blob/main/packages/transforms/src/hoist.ts + +pub struct HoistTransformer<'a> { + directive: &'a str, + runtime: &'a str, + id: &'a str, + hoisted_functions: Vec>, +} + +impl<'a> HoistTransformer<'a> { + pub fn new(directive: &'a str, runtime: &'a str, id: &'a str) -> Self { + Self { + directive, + runtime, + id, + hoisted_functions: vec![], + } + } +} + +// collect references which are neither at top nor in own scope +fn get_bind_vars<'a>(ctx: &mut oxc_traverse::TraverseCtx<'a>, span: Span) -> Vec { + let mut bind_vars: Vec = vec![]; + let ancestors: BTreeSet = ctx.scopes().ancestors(ctx.current_scope_id()).collect(); + for reference in &ctx.symbols().references { + // pick reference used inside + // TODO: probably relying on "span" is not robust. + let ref_span = reference.span(); + if !(span.start <= ref_span.start && ref_span.end <= span.end) { + continue; + } + if let Some(symbol_id) = reference.symbol_id() { + // pick symbol defined outside except top level one + let scope_id = ctx.symbols().get_scope_id(symbol_id); + let scope_flags = ctx.scopes().get_flags(scope_id); + if !scope_flags.is_top() && ancestors.contains(&scope_id) { + bind_vars.push(reference.clone()); + } + } + } + bind_vars +} + +fn has_directive(body: &FunctionBody, directive: &str) -> bool { + body.directives + .iter() + .any(|e| e.expression.value == directive) +} + +// create action register and bind expression +// $$register($$hoist, "", "$$hoist").bind(null, ) +fn ast_register_bind_expression<'a>( + ctx: &mut oxc_traverse::TraverseCtx<'a>, + id: &str, + runtime: &str, + name: &str, + bind_vars: &Vec, +) -> Expression<'a> { + // $$register(...) + let mut expr = ctx.ast.call_expression( + SPAN, + ctx.ast + .identifier_reference_expression(ctx.ast.identifier_reference(SPAN, runtime)), + ctx.ast.new_vec_from_iter([ + Argument::from( + ctx.ast + .identifier_reference_expression(ctx.ast.identifier_reference(SPAN, name)), + ), + Argument::from( + ctx.ast + .literal_string_expression(ctx.ast.string_literal(SPAN, id)), + ), + Argument::from( + ctx.ast + .literal_string_expression(ctx.ast.string_literal(SPAN, name)), + ), + ]), + false, + None, + ); + + if bind_vars.len() > 0 { + // $$register(...).bind(...) + let mut arguments = ctx.ast.new_vec_single(Argument::from( + ctx.ast.literal_null_expression(NullLiteral::new(SPAN)), + )); + for var in bind_vars { + arguments.push(Argument::from(ctx.ast.identifier_reference_expression( + ctx.ast.identifier_reference(SPAN, var.name()), + ))) + } + expr = ctx.ast.call_expression( + SPAN, + ctx.ast.static_member_expression( + SPAN, + expr, + ctx.ast.identifier_name(SPAN, "bind"), + false, + ), + arguments, + false, + None, + ); + } + expr +} + +// create hoisted function declarations +// export function $$hoist(...) { ... } +fn ast_hoist_declaration<'a>( + ctx: &mut oxc_traverse::TraverseCtx<'a>, + span: Span, + name: &str, + params: &FormalParameters<'a>, + body: &FunctionBody<'a>, + bind_vars: &Vec, +) -> Statement<'a> { + let mut new_param_items = ctx.ast.new_vec_from_iter(bind_vars.iter().map(|var| { + ctx.ast.formal_parameter( + SPAN, + ctx.ast.binding_pattern( + ctx.ast.binding_pattern_identifier(BindingIdentifier::new( + SPAN, + ctx.ast.new_atom(var.name()), + )), + None, + false, + ), + None, + false, + false, + ctx.ast.new_vec(), + ) + })); + new_param_items.extend(ctx.ast.copy(¶ms.items)); + + let new_func = ctx.ast.function( + FunctionType::FunctionDeclaration, + span, + Some(BindingIdentifier::new(SPAN, ctx.ast.new_atom(name))), + false, + true, + false, + None, + ctx.ast.formal_parameters( + params.span, + FormalParameterKind::FormalParameter, + new_param_items, + None, + ), + Some(ctx.ast.function_body( + body.span, + ctx.ast.new_vec(), + // TODO: move? + ctx.ast.copy(&body.statements), + )), + None, + None, + ); + Statement::ExportNamedDeclaration(ctx.ast.plain_export_named_declaration_declaration( + SPAN, + Declaration::FunctionDeclaration(new_func), + )) +} + +impl<'a> Traverse<'a> for HoistTransformer<'a> { + fn exit_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut oxc_traverse::TraverseCtx<'a>, + ) { + match expr { + Expression::ArrowFunctionExpression(node) => { + if has_directive(&node.body, &self.directive) { + // replace function definition with action register and bind + // $$register($$hoist, "", "$$hoist").bind(null, ) + let new_name = format!("$$hoist_{}", self.hoisted_functions.len()); + let bind_vars = get_bind_vars(ctx, node.span); + let new_expr = ast_register_bind_expression( + ctx, + &self.id, + &self.runtime, + &new_name, + &bind_vars, + ); + + // save functions to be hoisted + // export function $$hoist(...) { ... } + self.hoisted_functions.push(ast_hoist_declaration( + ctx, + node.span, + &new_name, + &node.params, + &node.body, + &bind_vars, + )); + + *expr = new_expr; + } + } + Expression::FunctionExpression(node) => { + if let Some(body) = &node.body { + if has_directive(&body, &self.directive) { + let new_name = format!("$$hoist_{}", self.hoisted_functions.len()); + let bind_vars = get_bind_vars(ctx, node.span); + let new_expr = ast_register_bind_expression( + ctx, + &self.id, + &self.runtime, + &new_name, + &bind_vars, + ); + + self.hoisted_functions.push(ast_hoist_declaration( + ctx, + node.span, + &new_name, + &node.params, + node.body.as_ref().unwrap(), + &bind_vars, + )); + + *expr = new_expr; + } + } + } + _ => {} + } + } + + fn exit_statement( + &mut self, + stmt: &mut Statement<'a>, + ctx: &mut oxc_traverse::TraverseCtx<'a>, + ) { + match stmt { + Statement::FunctionDeclaration(node) => { + if let (Some(body), Some(name)) = (&node.body, &node.id) { + if has_directive(&body, &self.directive) { + let new_name = format!("$$hoist_{}", self.hoisted_functions.len()); + let bind_vars = get_bind_vars(ctx, node.span); + let new_expr = ast_register_bind_expression( + ctx, + &self.id, + &self.runtime, + &new_name, + &bind_vars, + ); + + // const = $$register(...) + let new_stmt = + Statement::VariableDeclaration(ctx.ast.variable_declaration( + SPAN, + oxc::ast::ast::VariableDeclarationKind::Const, + ctx.ast.new_vec_single(ctx.ast.variable_declarator( + SPAN, + oxc::ast::ast::VariableDeclarationKind::Const, + ctx.ast.binding_pattern( + ctx.ast.binding_pattern_identifier(name.clone()), + None, + false, + ), + Some(new_expr), + true, + )), + false, + )); + + self.hoisted_functions.push(ast_hoist_declaration( + ctx, + node.span, + &new_name, + &node.params, + node.body.as_ref().unwrap(), + &bind_vars, + )); + + *stmt = new_stmt; + } + } + } + _ => {} + } + } + + fn exit_program( + &mut self, + program: &mut oxc::ast::ast::Program<'a>, + ctx: &mut oxc_traverse::TraverseCtx<'a>, + ) { + for stmt in &mut self.hoisted_functions { + program.body.push(ctx.ast.move_statement(stmt)); + } + } +} + +#[cfg(test)] +mod tests { + use std::fs; + + use base64::{engine::general_purpose::STANDARD, Engine as _}; + use oxc::{ + allocator::Allocator, + codegen::{CodeGenerator, CodegenReturn}, + parser::Parser, + span::SourceType, + }; + use oxc_traverse::traverse_mut; + + use super::HoistTransformer; + + #[test] + fn test_hoist_snapshot() { + insta::glob!("../tests/hoist", "*.js", |path| { + let source_text = fs::read_to_string(path).unwrap(); + let name = path.file_stem().unwrap().to_str().unwrap(); + + let allocator = Allocator::default(); + let source_type = SourceType::default().with_module(true); + let parser = Parser::new(&allocator, &source_text, source_type); + let parser_ret = parser.parse(); + assert_eq!(parser_ret.errors.len(), 0); + let mut program = parser_ret.program; + let mut traverser = HoistTransformer::new("use server", "$$register", ""); + traverse_mut( + &mut traverser, + &mut program, + &source_text, + source_type, + &allocator, + ); + let codegen_ret = CodeGenerator::new() + .enable_source_map("test.js", &source_text) + .build(&program); + + if std::env::var("DEBUG_SOURCEMAP").is_ok() { + let output = to_source_map_debug(&codegen_ret); + fs::write(path.parent().unwrap().join(format!("{name}.debug")), output).unwrap(); + } + + insta::with_settings!({ + prepend_module_to_snapshot => false, + omit_expression => true, + snapshot_suffix => "", + snapshot_path => path.parent().unwrap() + }, { + insta::assert_snapshot!(name, codegen_ret.source_text); + }); + }); + } + + fn to_source_map_debug(result: &CodegenReturn) -> String { + // https://github.com/oxc-project/oxc/blob/a6487482bc053797f7f1a42f5793fafbd9a47114/crates/oxc_codegen/examples/sourcemap.rs#L34-L44 + let source_map = result.source_map.as_ref().unwrap(); + let source_map_json = source_map.to_json_string().unwrap(); + let evanw_hash = STANDARD.encode(format!( + "{}\0{}{}\0{}", + result.source_text.len(), + result.source_text, + source_map_json.len(), + source_map_json + )); + let evanw_url = format!("https://evanw.github.io/source-map-visualization/#{evanw_hash}"); + return format!( + "{}\n// {}\n//# sourceMappingURL={}\n", + &result.source_text, + &evanw_url, + source_map.to_data_url().unwrap() + ); + } +} diff --git a/oxc-rsc/src/lib.rs b/oxc-rsc/src/lib.rs new file mode 100644 index 0000000..39206d6 --- /dev/null +++ b/oxc-rsc/src/lib.rs @@ -0,0 +1 @@ +pub mod hoist; diff --git a/oxc-rsc/tests/hoist/arrow.js b/oxc-rsc/tests/hoist/arrow.js new file mode 100644 index 0000000..6352f5f --- /dev/null +++ b/oxc-rsc/tests/hoist/arrow.js @@ -0,0 +1,11 @@ +let count = 0; + +export function Counter() { + return { + type: "form", + action: (formData) => { + "use server"; + count += Number(formData.get("name")); + }, + }; +} diff --git a/oxc-rsc/tests/hoist/arrow.snap b/oxc-rsc/tests/hoist/arrow.snap new file mode 100644 index 0000000..0b2dd1a --- /dev/null +++ b/oxc-rsc/tests/hoist/arrow.snap @@ -0,0 +1,14 @@ +--- +source: src/hoist.rs +input_file: tests/hoist/arrow.js +--- +let count = 0; +export function Counter() { + return { + type: 'form', + action: $$register($$hoist_0, '', '$$hoist_0') + }; +} +export async function $$hoist_0(formData) { + count += Number(formData.get('name')); +} diff --git a/oxc-rsc/tests/hoist/bind.js b/oxc-rsc/tests/hoist/bind.js new file mode 100644 index 0000000..3a136cd --- /dev/null +++ b/oxc-rsc/tests/hoist/bind.js @@ -0,0 +1,37 @@ +let count = 0; + +export function Counter() { + const outer1 = 0; + const outer2 = 0; + + return { + type: "form", + action: (formData) => { + "use server"; + const inner = 0; + count += Number(formData.get("name")); + count += inner; + count += outer1; + count += outer2; + }, + }; +} + +export function Counter2() { + const outer1 = 0; + const outer2 = 0; + + function action(formData) { + "use server"; + const inner = 0; + count += Number(formData.get("name")); + count += inner; + count += outer1; + count += outer2; + } + + return { + type: "form", + action, + }; +} diff --git a/oxc-rsc/tests/hoist/bind.snap b/oxc-rsc/tests/hoist/bind.snap new file mode 100644 index 0000000..d6982f6 --- /dev/null +++ b/oxc-rsc/tests/hoist/bind.snap @@ -0,0 +1,36 @@ +--- +source: src/hoist.rs +input_file: tests/hoist/bind.js +--- +let count = 0; +export function Counter() { + const outer1 = 0; + const outer2 = 0; + return { + type: 'form', + action: $$register($$hoist_0, '', '$$hoist_0').bind(null, outer1, outer2) + }; +} +export function Counter2() { + const outer1 = 0; + const outer2 = 0; + const action = $$register($$hoist_1, '', '$$hoist_1').bind(null, outer1, outer2); + return { + type: 'form', + action + }; +} +export async function $$hoist_0(outer1, outer2, formData) { + const inner = 0; + count += Number(formData.get('name')); + count += inner; + count += outer1; + count += outer2; +} +export async function $$hoist_1(outer1, outer2, formData) { + const inner = 0; + count += Number(formData.get('name')); + count += inner; + count += outer1; + count += outer2; +} diff --git a/oxc-rsc/tests/hoist/function-declaration.js b/oxc-rsc/tests/hoist/function-declaration.js new file mode 100644 index 0000000..0124a2e --- /dev/null +++ b/oxc-rsc/tests/hoist/function-declaration.js @@ -0,0 +1,12 @@ +let count = 0; + +export function Counter() { + function action(formData) { + "use server"; + count += Number(formData.get("name")); + }; + return { + type: "form", + action, + }; +} diff --git a/oxc-rsc/tests/hoist/function-declaration.snap b/oxc-rsc/tests/hoist/function-declaration.snap new file mode 100644 index 0000000..e0611b4 --- /dev/null +++ b/oxc-rsc/tests/hoist/function-declaration.snap @@ -0,0 +1,16 @@ +--- +source: src/hoist.rs +input_file: tests/hoist/function-declaration.js +--- +let count = 0; +export function Counter() { + const action = $$register($$hoist_0, '', '$$hoist_0'); + ; + return { + type: 'form', + action + }; +} +export async function $$hoist_0(formData) { + count += Number(formData.get('name')); +} diff --git a/oxc-rsc/tests/hoist/function-expression.js b/oxc-rsc/tests/hoist/function-expression.js new file mode 100644 index 0000000..06453af --- /dev/null +++ b/oxc-rsc/tests/hoist/function-expression.js @@ -0,0 +1,12 @@ +let count = 0; + +export function Counter() { + return { + type: "form", + // TODO: FunctionExpression + action: function (formData) { + "use server"; + count += Number(formData.get("name")); + }, + }; +} diff --git a/oxc-rsc/tests/hoist/function-expression.snap b/oxc-rsc/tests/hoist/function-expression.snap new file mode 100644 index 0000000..9d56aaa --- /dev/null +++ b/oxc-rsc/tests/hoist/function-expression.snap @@ -0,0 +1,14 @@ +--- +source: src/hoist.rs +input_file: tests/hoist/function-expression.js +--- +let count = 0; +export function Counter() { + return { + type: 'form', + action: $$register($$hoist_0, '', '$$hoist_0') + }; +} +export async function $$hoist_0(formData) { + count += Number(formData.get('name')); +} diff --git a/oxc-rsc/tests/hoist/top.js b/oxc-rsc/tests/hoist/top.js new file mode 100644 index 0000000..e271b78 --- /dev/null +++ b/oxc-rsc/tests/hoist/top.js @@ -0,0 +1,25 @@ +const x = "x"; + +const f = async () => { + "use server"; + return x; +}; + +async function f2() { + "use server"; + return x; +} + +const g = async () => {}; + +async function g2() {} + +const h = async (formData) => { + "use server"; + return formData.get(x); +}; + +async function h2(formData) { + "use server"; + return formData.get(x); +} diff --git a/oxc-rsc/tests/hoist/top.snap b/oxc-rsc/tests/hoist/top.snap new file mode 100644 index 0000000..b24a2da --- /dev/null +++ b/oxc-rsc/tests/hoist/top.snap @@ -0,0 +1,23 @@ +--- +source: src/hoist.rs +input_file: tests/hoist/top.js +--- +const x = 'x'; +const f = $$register($$hoist_0, '', '$$hoist_0'); +const f2 = $$register($$hoist_1, '', '$$hoist_1'); +const g = async () => {}; +async function g2() {} +const h = $$register($$hoist_2, '', '$$hoist_2'); +const h2 = $$register($$hoist_3, '', '$$hoist_3'); +export async function $$hoist_0() { + return x; +} +export async function $$hoist_1() { + return x; +} +export async function $$hoist_2(formData) { + return formData.get(x); +} +export async function $$hoist_3(formData) { + return formData.get(x); +}