1extern crate proc_macro;
2use proc_macro::TokenStream;
3use proc_macro2::{Ident, Span};
4use quote::quote;
5use syn::{Item as SynItem, ItemFn};
6
7#[proc_macro_attribute]
8pub fn plugin_transform(
9 _args: proc_macro::TokenStream,
10 input: proc_macro::TokenStream,
11) -> proc_macro::TokenStream {
12 let token = proc_macro2::TokenStream::from(input);
13 let parsed_results = syn::parse2::<SynItem>(token).expect("Failed to parse tokens");
14 match parsed_results {
15 SynItem::Fn(func) => handle_func(func, Ident::new("Program", Span::call_site())),
16 _ => panic!("Please confirm if plugin macro is specified for the function"),
17 }
18}
19
20#[proc_macro_attribute]
21pub fn css_plugin_transform(
22 _args: proc_macro::TokenStream,
23 input: proc_macro::TokenStream,
24) -> proc_macro::TokenStream {
25 let token = proc_macro2::TokenStream::from(input);
26 let parsed_results = syn::parse2::<SynItem>(token).expect("Failed to parse tokens");
27 match parsed_results {
28 SynItem::Fn(func) => handle_func(func, Ident::new("Stylesheet", Span::call_site())),
29 _ => panic!("Please confirm if plugin macro is specified for the function"),
30 }
31}
32
33#[allow(clippy::redundant_clone)]
34fn handle_func(func: ItemFn, ast_type: Ident) -> TokenStream {
35 let ident = func.sig.ident.clone();
36 let transform_process_impl_ident =
37 Ident::new("__transform_plugin_process_impl", Span::call_site());
38 let transform_core_pkg_diag_ident =
39 Ident::new("__get_transform_plugin_core_pkg_diag", Span::call_site());
40
41 let ret = quote! {
42 #func
43
44 #[cfg(target_arch = "wasm32")] extern "C" {
48 fn __set_transform_result(bytes_ptr: u32, bytes_ptr_len: u32);
49 fn __set_transform_plugin_core_pkg_diagnostics(bytes_ptr: u32, bytes_ptr_len: u32);
50 fn __emit_diagnostics(bytes_ptr: u32, bytes_ptr_len: u32);
51 }
52
53 pub struct PluginDiagnosticsEmitter;
59
60 impl swc_core::common::errors::Emitter for PluginDiagnosticsEmitter {
61 #[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
62 fn emit(&mut self, db: &mut swc_core::common::errors::DiagnosticBuilder<'_>) {
63 let diag = swc_core::common::plugin::serialized::PluginSerializedBytes::try_serialize(&swc_core::common::plugin::serialized::VersionedSerializable::new(*db.diagnostic.clone()))
64 .expect("Should able to serialize Diagnostic");
65 let (ptr, len) = diag.as_ptr();
66
67 #[cfg(target_arch = "wasm32")] unsafe {
69 __emit_diagnostics(ptr as u32, len as u32);
70 }
71 }
72 }
73
74
75 fn send_transform_result_to_host(bytes_ptr: u32, bytes_ptr_len: u32) {
78 #[cfg(target_arch = "wasm32")] unsafe {
80 __set_transform_result(bytes_ptr, bytes_ptr_len);
81 }
82 }
83
84 fn construct_error_ptr(plugin_error: swc_core::common::plugin::serialized::PluginError) -> u32 {
86 let ret = swc_core::common::plugin::serialized::PluginSerializedBytes::try_serialize(&swc_core::common::plugin::serialized::VersionedSerializable::new(plugin_error)).expect("Should able to serialize PluginError");
87 let (ptr, len) = ret.as_ptr();
88
89 send_transform_result_to_host(
90 ptr as _,
91 len as u32
92 );
93 1
94 }
95
96 #[no_mangle]
97 #[allow(clippy::not_unsafe_ptr_arg_deref)]
98 pub fn #transform_core_pkg_diag_ident() -> u32 {
99 let schema_version = swc_core::common::plugin::PLUGIN_TRANSFORM_AST_SCHEMA_VERSION;
100 let core_pkg_diag = swc_core::diagnostics::get_core_engine_diagnostics();
101
102 let result = swc_core::common::plugin::diagnostics::PluginCorePkgDiagnostics {
103 ast_schema_version: schema_version,
104 pkg_version: core_pkg_diag.package_semver,
105 git_sha: core_pkg_diag.git_sha,
106 cargo_features: core_pkg_diag.cargo_features,
107 };
108
109 let serialized_result = swc_core::common::plugin::serialized::PluginSerializedBytes::try_serialize(
110 &swc_core::common::plugin::serialized::VersionedSerializable::new(result)
111 ).expect("Diagnostics should be always serializable");
112
113 let (serialized_result_ptr, serialized_result_ptr_len) = serialized_result.as_ptr();
114
115 #[cfg(target_arch = "wasm32")] unsafe {
117 __set_transform_plugin_core_pkg_diagnostics(serialized_result_ptr as _, serialized_result_ptr_len as u32);
118 }
119 0
120 }
121
122 #[no_mangle]
127 #[allow(clippy::not_unsafe_ptr_arg_deref)]
128 pub fn #transform_process_impl_ident(
129 ast_ptr: *const u8, ast_ptr_len: u32,
130 unresolved_mark: u32, should_enable_comments_proxy: i32) -> u32 {
131 let program = swc_core::common::plugin::serialized::PluginSerializedBytes::from_raw_ptr(ast_ptr, ast_ptr_len.try_into().expect("Should able to convert ptr length")).deserialize();
134 if program.is_err() {
135 let err = swc_core::common::plugin::serialized::PluginError::Deserialize("Failed to deserialize program received from host".to_string());
136 return construct_error_ptr(err);
137 }
138 let program: #ast_type = program.expect("Should be a program").into_inner();
139
140 let handler = swc_core::common::errors::Handler::with_emitter(
142 true,
143 false,
144 Box::new(PluginDiagnosticsEmitter)
145 );
146
147 let plugin_comments_proxy = if should_enable_comments_proxy == 1 { Some(swc_core::plugin::proxies::PluginCommentsProxy) } else { None };
149 let mut metadata = swc_core::plugin::metadata::TransformPluginProgramMetadata {
150 comments: plugin_comments_proxy,
151 source_map: swc_core::plugin::proxies::PluginSourceMapProxy { source_file: swc_core::common::sync::OnceCell::new() },
152 unresolved_mark: swc_core::common::Mark::from_u32(unresolved_mark as u32),
153 };
154
155 let transformed_program = swc_core::common::plugin::serialized::VersionedSerializable::new(swc_core::common::errors::HANDLER.set(&handler, || {
157 #ident(program, metadata)
158 }));
159
160 let serialized_result = swc_core::common::plugin::serialized::PluginSerializedBytes::try_serialize(
162 &transformed_program
163 );
164
165 if serialized_result.is_err() {
166 let err = swc_core::common::plugin::serialized::PluginError::Serialize("Failed to serialize transformed program".to_string());
167 return construct_error_ptr(err);
168 }
169
170 let serialized_result = serialized_result.expect("Should be a realized transformed program");
171 let (serialized_result_ptr, serialized_result_ptr_len) = serialized_result.as_ptr();
172
173 send_transform_result_to_host(serialized_result_ptr as _, serialized_result_ptr_len as u32);
174 0
175 }
176 };
177
178 ret.into()
179}