diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14a2524b..7c935296 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,12 +15,12 @@ jobs: - name: dependencies e2e test working-directory: e2e/dependencies/consumer run: | - cargo t - tsc bindings/* --noEmit --noUnusedLocals --strict + cargo t --features ts-rs/export + tsc bindings/*.ts --noEmit --noUnusedLocals --strict - name: dependencies e2e test with default export env working-directory: e2e/dependencies/consumer run: | - TS_RS_EXPORT_DIR=custom-bindings cargo t + TS_RS_EXPORT_DIR=custom-bindings cargo t --features ts-rs/export shopt -s globstar tsc custom-bindings/**/*.ts --noEmit --noUnusedLocals --strict e2e-workspace: @@ -37,14 +37,14 @@ jobs: - name: workspace e2e test working-directory: e2e/workspace run: | - cargo t + cargo t --features ts-rs/export shopt -s globstar tsc parent/bindings/**/*.ts --noEmit --noUnusedLocals --strict rm -rf parent/bindings - name: workspace e2e with default export env working-directory: e2e/workspace run: | - TS_RS_EXPORT_DIR=custom-bindings cargo t + TS_RS_EXPORT_DIR=custom-bindings cargo t --features ts-rs/export shopt -s globstar tsc parent/custom-bindings/**/*.ts --noEmit --noUnusedLocals --strict rm -rf parent/custom-bindings @@ -62,12 +62,12 @@ jobs: - name: example e2e test working-directory: example run: | - cargo t - tsc bindings/* --noEmit + cargo t --features ts-rs/export + tsc bindings/*.ts --noEmit - name: example e2e with default export env working-directory: example run: | - TS_RS_EXPORT_DIR=custom-bindings cargo t + TS_RS_EXPORT_DIR=custom-bindings cargo t --features ts-rs/export shopt -s globstar tsc custom-bindings/**/*.ts --noEmit --noUnusedLocals --strict @@ -121,7 +121,7 @@ jobs: - name: Test working-directory: ts-rs run: | - TS_RS_EXPORT_DIR=output cargo test --no-default-features + TS_RS_EXPORT_DIR=output cargo test --no-default-features --features ts-rs/export shopt -s globstar tsc output/**/*.ts --noEmit --noUnusedLocals --strict rm -rf output @@ -159,7 +159,7 @@ jobs: - name: Test working-directory: ts-rs run: | - cargo test --no-default-features + cargo test --no-default-features --features ts-rs/export shopt -s globstar tsc bindings/**/*.ts --noEmit --noUnusedLocals rm -rf bindings diff --git a/.gitignore b/.gitignore index f5541831..f8bdb731 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target/ tsconfig.json /ts-rs/tests-out +ts_rs.meta \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6662c493..90636dd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -70,6 +70,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.89" @@ -85,20 +134,20 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -117,9 +166,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "better_scoped_tls" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de" +checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" dependencies = [ "scoped-tls", ] @@ -207,11 +256,19 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cargo-ts" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", +] + [[package]] name = "cc" -version = "1.1.21" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "shlex", ] @@ -243,6 +300,79 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -316,7 +446,7 @@ checksum = "f3ab0dd2bedc109d25f0d21afb09b7d329f6c6fa83b095daf31d2d967e091548" dependencies = [ "anyhow", "bumpalo", - "hashbrown", + "hashbrown 0.14.5", "indexmap", "rustc-hash", "serde", @@ -387,6 +517,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -404,7 +544,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -426,9 +566,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hash32" @@ -449,6 +589,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heapless" version = "0.8.0" @@ -459,6 +605,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -471,7 +623,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", "phf", @@ -512,14 +664,20 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", "serde", ] @@ -532,9 +690,15 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.11" @@ -543,9 +707,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b" dependencies = [ "wasm-bindgen", ] @@ -558,9 +722,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libm" @@ -632,28 +796,34 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "ordered-float" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" +checksum = "44d501f1a72f71d3c063a6bbc8f7271fa73aa09fe5d6283b6571e2ed176a2537" dependencies = [ "num-traits", ] +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -690,7 +860,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -725,9 +895,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -788,9 +958,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -800,9 +970,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -811,9 +981,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" @@ -871,7 +1041,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -887,6 +1057,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -942,7 +1121,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -960,9 +1139,15 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.77", + "syn 2.0.79", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "swc_atoms" version = "0.6.7" @@ -1048,18 +1233,18 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "swc_macros_common" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +checksum = "27e18fbfe83811ffae2bb23727e45829a0d19c6870bced7c0f545cc99ad248dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1082,7 +1267,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1098,9 +1283,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1133,22 +1318,32 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", ] [[package]] @@ -1226,7 +1421,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1236,13 +1431,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] name = "triomphe" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" dependencies = [ "serde", "stable_deref_trait", @@ -1278,7 +1495,7 @@ version = "10.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "termcolor", ] @@ -1290,9 +1507,9 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-id-start" @@ -1317,9 +1534,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "url" @@ -1333,6 +1550,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.10.0" @@ -1343,6 +1566,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.5" @@ -1357,9 +1586,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887" dependencies = [ "cfg-if", "once_cell", @@ -1368,24 +1597,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1393,22 +1622,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9" [[package]] name = "winapi-util" @@ -1416,7 +1645,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1428,6 +1657,15 @@ dependencies = [ "windows-targets", ] +[[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-sys" version = "0.59.0" @@ -1528,5 +1766,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] diff --git a/Cargo.toml b/Cargo.toml index 3afd3eec..2269bbc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] -members = ["macros", "ts-rs", "example"] +members = ["macros", "ts-rs", "example", "cli"] resolver = "2" diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 00000000..1236df2a --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo-ts" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4", features = ["derive"]} +color-eyre = "0.6" diff --git a/cli/src/args.rs b/cli/src/args.rs new file mode 100644 index 00000000..86cde5f8 --- /dev/null +++ b/cli/src/args.rs @@ -0,0 +1,48 @@ +use std::path::PathBuf; + +use clap::Parser; + +use crate::metadata::FILE_NAME; + +#[derive(Parser, Debug)] +#[allow(clippy::struct_excessive_bools)] +pub struct Args { + /// Defines where your TS bindings will be saved by setting TS_RS_EXPORT_DIR + #[arg(long, short)] + pub output_directory: PathBuf, + + /// Disables warnings caused by using serde attributes that ts-rs cannot process + #[arg(long)] + pub no_warnings: bool, + + /// Adds the ".js" extension to import paths + #[arg(long)] + pub esm_imports: bool, + + /// Formats the generated TypeScript files + #[arg(long)] + pub format: bool, + + /// Generates an index.ts file in your --output-directory that re-exports all + /// types generated by ts-rs + #[arg(long = "index")] + pub generate_index_ts: bool, + + /// Generates only a single index.ts file in your --output-directory that + /// contains all exported types + #[arg(long = "merge")] + pub merge_files: bool, + + /// Do not capture `cargo test`'s output, and pass --nocapture to the test binary + #[arg(long = "nocapture")] + pub no_capture: bool, +} + +// Args is in scope for the entirety of the main function, so this will only +// be executed when the program is finished running. This helps prevent us +// from forgetting to do cleanup if some code branch early returns from main +impl Drop for Args { + fn drop(&mut self) { + _ = std::fs::remove_file(self.output_directory.join(FILE_NAME)); + } +} diff --git a/cli/src/cargo.rs b/cli/src/cargo.rs new file mode 100644 index 00000000..a9695f24 --- /dev/null +++ b/cli/src/cargo.rs @@ -0,0 +1,51 @@ +use std::process::{Command, Stdio}; + +use color_eyre::Result; + +use crate::{args::Args, path}; + +macro_rules! feature { + ($cargo_invocation: expr, $args: expr, { $($field: ident => $feature: literal),* $(,)? }) => { + $( + if $args.$field { + $cargo_invocation + .arg("--features") + .arg(format!("ts-rs/{}", $feature)); + } + )* + }; +} + +pub fn invoke(args: &Args) -> Result<()> { + let mut cargo_invocation = Command::new("cargo"); + + cargo_invocation + .arg("test") + .arg("export_bindings_") + .arg("--features") + .arg("ts-rs/export") + .arg("--features") + .arg("ts-rs/generate-metadata") + .stdout(if args.no_capture { + Stdio::inherit() + } else { + Stdio::piped() + }) + .env("TS_RS_EXPORT_DIR", path::absolute(&args.output_directory)?); + + feature!(cargo_invocation, args, { + no_warnings => "no-serde-warnings", + esm_imports => "import-esm", + format => "format", + }); + + if args.no_capture { + cargo_invocation.arg("--").arg("--nocapture"); + } else { + cargo_invocation.arg("--quiet"); + } + + cargo_invocation.spawn()?.wait()?; + + Ok(()) +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 00000000..005684c5 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,107 @@ +#![warn(clippy::pedantic, clippy::nursery)] + +use std::{ + fs::{self, OpenOptions}, + io::{Read, Write}, +}; + +use clap::Parser; +use color_eyre::{owo_colors::OwoColorize, Result}; + +mod args; +mod cargo; +mod metadata; +mod path; + +use args::Args; +use metadata::{Metadata, FILE_NAME}; + +const BLANK_LINE: [u8; 2] = [b'\n', b'\n']; +const NOTE: &[u8; 109] = b"// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n"; + +fn main() -> Result<()> { + color_eyre::install()?; + + let args = Args::parse(); + + let metadata_path = args.output_directory.join(FILE_NAME); + if metadata_path.exists() { + fs::remove_file(&metadata_path)?; + } + + if args.merge_files && args.generate_index_ts { + eprintln!( + "{} --index is not compatible with --merge", + "Error:".red().bold() + ); + + return Ok(()); + } + + cargo::invoke(&args)?; + + let metadata_content = fs::read_to_string(&metadata_path)?; + let metadata = Metadata::try_from(&*metadata_content)?; + + let demand_unique_names = args.merge_files || args.generate_index_ts; + + if !demand_unique_names || metadata.is_empty() { + return Ok(()); + } + + if metadata.has_naming_collisions() { + metadata.report_naming_collisions(); + + eprintln!( + "{} due to the naming collisions listed above, generating an index.ts file is not possible", + "Error:".red().bold() + ); + + return Ok(()); + } + + let index_path = args.output_directory.join("index.ts"); + + if index_path.exists() { + fs::remove_file(&index_path)?; + } + + let mut index = OpenOptions::new() + .create(true) + .append(true) + .open(index_path)?; + + index.write_all(NOTE)?; + + if args.generate_index_ts { + for path in metadata.export_paths() { + index.write_fmt(format_args!("\nexport * from {path:?};"))?; + } + + return Ok(()); + } + + if args.merge_files { + for path in metadata.export_paths() { + let path = path::absolute(args.output_directory.join(path))?; + let mut file = OpenOptions::new().read(true).open(&path)?; + + let mut buf = Vec::with_capacity(file.metadata()?.len().try_into()?); + file.read_to_end(&mut buf)?; + + let Some((i, _)) = buf.windows(2).enumerate().find(|(_, w)| w == &BLANK_LINE) else { + continue; + }; + + index.write_all(&buf[i + 1..])?; + + fs::remove_file(path)?; + } + + path::remove_empty_subdirectories(&args.output_directory)?; + + return Ok(()); + } + + Ok(()) +} diff --git a/cli/src/metadata.rs b/cli/src/metadata.rs new file mode 100644 index 00000000..e04cc2bf --- /dev/null +++ b/cli/src/metadata.rs @@ -0,0 +1,104 @@ +use std::{ + collections::{HashMap, HashSet}, + path::Path, +}; + +use color_eyre::{ + eyre::{Error, OptionExt}, + owo_colors::OwoColorize, + Result, +}; + +pub const FILE_NAME: &str = "ts_rs.meta"; + +pub struct Metadata<'a> { + entries: std::collections::HashMap<&'a str, HashSet>>, +} + +impl<'a> TryFrom<&'a str> for Metadata<'a> { + type Error = Error; + + fn try_from(value: &'a str) -> Result { + Ok(Self { + entries: value.lines().try_fold( + HashMap::<&str, HashSet<_>>::default(), + |mut acc, cur| { + let (key, value) = cur.split_once(',').ok_or_eyre("Invalid metadata file")?; + let value = Entry::try_from(value)?; + + acc.entry(key).or_default().insert(value); + + Ok::<_, Error>(acc) + }, + )?, + }) + } +} + +impl<'a> Metadata<'a> { + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + pub fn has_naming_collisions(&self) -> bool { + self.entries.values().any(|x| x.len() > 1) + } + + pub fn report_naming_collisions(&self) { + self.entries + .iter() + .filter(|(_, x)| x.len() > 1) + .for_each(|(ty, entry)| name_collision_warning(ty, entry)); + } + + pub fn export_paths(&self) -> impl Iterator { + self.entries.values().flatten().map(|x| x.export_path) + } +} + +#[derive(PartialEq, Eq, Hash)] +struct Entry<'a> { + rust_name: &'a str, + export_path: &'a Path, +} + +impl<'a> TryFrom<&'a str> for Entry<'a> { + type Error = Error; + + fn try_from(value: &'a str) -> Result { + let (rust_name, export_path) = + value.split_once(',').ok_or_eyre("Invalid metadata entry")?; + + Ok(Self { + rust_name, + export_path: Path::new(export_path), + }) + } +} + +fn name_collision_warning(ts_type: &str, metadata: &HashSet) { + eprintln!( + "{} Multiple types being exported with the name \"{}\"", + "Warning:".yellow().bold(), + ts_type.green().bold() + ); + + for entry in metadata { + eprintln!( + " {} {} {}", + "-".blue().bold(), + "Type:".bold(), + entry.rust_name.cyan(), + ); + + eprintln!( + " {} {}", + "Path:".bold(), + entry.export_path.to_string_lossy() + ); + + eprintln!(); + } + + eprintln!(); +} diff --git a/cli/src/path.rs b/cli/src/path.rs new file mode 100644 index 00000000..6e4255e3 --- /dev/null +++ b/cli/src/path.rs @@ -0,0 +1,64 @@ +use std::{ + fs, + io::ErrorKind, + path::{Component as C, Path, PathBuf}, +}; + +use color_eyre::{eyre::OptionExt, Result}; + +pub fn absolute>(path: T) -> Result { + let path = path.as_ref(); + + if path.is_absolute() { + return Ok(path.to_owned()); + } + + let path = std::env::current_dir()?.join(path); + + let mut out = Vec::new(); + for comp in path.components() { + match comp { + C::CurDir => (), + C::ParentDir => { + out.pop().ok_or_eyre("Invalid path")?; + } + comp => out.push(comp), + } + } + + Ok(if out.is_empty() { + PathBuf::from(".") + } else { + out.iter().collect() + }) +} + +pub fn remove_empty_subdirectories>(path: T) -> Result<()> { + let path = path.as_ref(); + + for entry in path.read_dir()? { + let entry = entry?; + + let path = entry.path(); + + if !path.is_dir() { + continue; + } + + remove_empty_subdirectories(&path)?; + if let Err(e) = fs::remove_dir(path) { + // The other possible error kinds are either not available + // in stable rust (DirectoryNotEmpty), not possible due + // to the logic of this function (NotFound) or both (NotADirectory) + // + // The correct check would be `!matches!(e.kind(), ErrorKind::DirectoryNotEmpty)` + // as that is the only error we actually WANT to ignore... the others, + // although impossible, should be returned if they somehow happen + if matches!(e.kind(), ErrorKind::PermissionDenied) { + return Err(e.into()); + } + } + } + + Ok(()) +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 62532719..ee41ec30 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/Aleph-Alpha/ts-rs" [features] serde-compat = ["termcolor"] no-serde-warnings = [] +export = [] [lib] proc-macro = true diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 4be1ce53..75360fdc 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -35,9 +35,8 @@ struct DerivedTS { impl DerivedTS { fn into_impl(mut self, rust_ty: Ident, generics: Generics) -> TokenStream { - let export = self - .export - .then(|| self.generate_export_test(&rust_ty, &generics)); + let allow_export = cfg!(feature = "export") && self.export; + let export = allow_export.then(|| self.generate_export_test(&rust_ty, &generics)); let output_path_fn = { let path = match self.export_to.as_deref() { diff --git a/macros/src/utils.rs b/macros/src/utils.rs index 445487ff..e118a1f2 100644 --- a/macros/src/utils.rs +++ b/macros/src/utils.rs @@ -208,7 +208,7 @@ mod warning { let mut buffer = writer.buffer(); buffer.set_color(&yellow_bold)?; - write!(&mut buffer, "warning")?; + write!(&mut buffer, "Warning")?; buffer.set_color(&white_bold)?; writeln!(&mut buffer, ": {}", title)?; diff --git a/ts-rs/Cargo.toml b/ts-rs/Cargo.toml index a6592441..e2fa9edf 100644 --- a/ts-rs/Cargo.toml +++ b/ts-rs/Cargo.toml @@ -35,6 +35,8 @@ smol_str-impl = ["smol_str"] serde-json-impl = ["serde_json"] no-serde-warnings = ["ts-rs-macros/no-serde-warnings"] import-esm = [] +export = ["ts-rs-macros/export"] +generate-metadata = [] tokio-impl = ["tokio"] [dev-dependencies] diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index 7b0d08fd..a69a3b92 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -125,14 +125,14 @@ pub(crate) fn export_to>( std::fs::create_dir_all(parent)?; } - export_and_merge(path, type_name, buffer)?; + export_and_merge::(path, type_name, buffer)?; Ok(()) } /// Exports the type to a new file if the file hasn't yet been written to. /// Otherwise, finds its place in the already existing file and inserts it. -fn export_and_merge( +fn export_and_merge( path: PathBuf, type_name: String, generated_type: String, @@ -141,6 +141,24 @@ fn export_and_merge( let mut lock = EXPORT_PATHS.lock().unwrap(); + if cfg!(feature = "generate-metadata") { + let relative_path = T::output_path() + .ok_or_else(std::any::type_name::) + .map_err(ExportError::CannotBeExported)? + .to_string_lossy(); + + let type_ts_name = T::ident(); + let type_rs_name = std::any::type_name::().split('<').next().unwrap(); + + std::fs::OpenOptions::new() + .append(true) + .create(true) + .open(default_out_dir().join("ts_rs.meta"))? + .write_fmt(format_args!( + "{type_ts_name},{type_rs_name},./{relative_path}\n" + ))?; + } + let Some(entry) = lock.get_mut(&path) else { // The file hasn't been written to yet, so it must be // overwritten diff --git a/ts-rs/tests/integration/issue_308.rs b/ts-rs/tests/integration/issue_308.rs index 41fc5e79..be402b20 100644 --- a/ts-rs/tests/integration/issue_308.rs +++ b/ts-rs/tests/integration/issue_308.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use ts_rs::{Dependency, ExportError, TypeVisitor, TS}; +use ts_rs::{TypeVisitor, Dependency, ExportError, TS}; #[rustfmt::skip] trait Malicious { diff --git a/ts-rs/tests/integration/path_bug.rs b/ts-rs/tests/integration/path_bug.rs index 12ade1bf..4c48c6ba 100644 --- a/ts-rs/tests/integration/path_bug.rs +++ b/ts-rs/tests/integration/path_bug.rs @@ -2,7 +2,7 @@ use ts_rs::TS; #[derive(TS)] -#[ts(export, export_to = "path_bug/aaa/")] +#[ts(export_to = "path_bug/aaa/")] struct Foo { bar: Bar, } @@ -15,7 +15,7 @@ struct Bar { #[test] fn path_bug() { - export_bindings_foo(); + Foo::export_all().unwrap(); assert!(Foo::default_output_path().unwrap().is_file()); assert!(Bar::default_output_path().unwrap().is_file()); diff --git a/ts-rs/tests/integration/recursion_limit.rs b/ts-rs/tests/integration/recursion_limit.rs index 16e1a2d2..fb9738c7 100644 --- a/ts-rs/tests/integration/recursion_limit.rs +++ b/ts-rs/tests/integration/recursion_limit.rs @@ -1,3 +1,5 @@ +#![allow(clippy::upper_case_acronyms)] + use std::any::TypeId; use ts_rs::{TypeVisitor, TS};