1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3use std::os::raw::{c_char, c_void};
4use std::sync::Arc;
5use std::{convert, fmt, slice, str};
6
7use libc::{ptrdiff_t, size_t};
8
9use crate::bridge::{
10 get_allocation_pointer, FfiapiBuildOptions, FfiapiEngine, FfiapiEntryPoint,
11 FfiapiGoStringGoSlice, FfiapiLoader, FfiapiMapStringStringEntry, FfiapiTransformOptions,
12 GoString,
13};
14
15#[inline(always)]
16fn transform<I, S: IntoIterator<Item = I>, O, T: Fn(I) -> O>(src: S, mapper: T) -> Vec<O> {
17 src.into_iter().map(mapper).collect::<Vec<O>>()
18}
19
20pub struct SliceContainer<T> {
23 pub(crate) ptr: *mut T,
24 pub(crate) len: usize,
25}
26
27impl<T> SliceContainer<T> {
28 pub fn as_slice(&self) -> &[T] {
29 unsafe { slice::from_raw_parts(self.ptr, self.len) }
30 }
31}
32
33unsafe impl<T> Send for SliceContainer<T> {}
34
35unsafe impl<T> Sync for SliceContainer<T> {}
36
37impl<T> convert::AsRef<[T]> for SliceContainer<T> {
38 fn as_ref(&self) -> &[T] {
39 self.as_slice()
40 }
41}
42
43impl<T> Drop for SliceContainer<T> {
44 fn drop(&mut self) {
45 unsafe {
46 for i in 0..self.len {
47 drop(self.ptr.offset(i as isize));
48 }
49 libc::free(self.ptr as *mut c_void);
51 };
52 }
53}
54
55#[repr(C)]
58pub struct StrContainer {
59 len: size_t,
60 data: *mut c_char,
61}
62
63impl StrContainer {
64 pub fn as_str(&self) -> &str {
65 unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.data as *mut u8, self.len)) }
66 }
67}
68
69unsafe impl Send for StrContainer {}
70
71unsafe impl Sync for StrContainer {}
72
73impl convert::AsRef<str> for StrContainer {
74 fn as_ref(&self) -> &str {
75 self.as_str()
76 }
77}
78
79impl Display for StrContainer {
80 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
81 self.as_ref().fmt(f)
82 }
83}
84
85impl Drop for StrContainer {
86 fn drop(&mut self) {
87 unsafe {
88 libc::free(self.data as *mut c_void);
90 };
91 }
92}
93
94#[repr(C)]
96pub struct Message {
97 pub file: StrContainer,
98 pub line: ptrdiff_t,
99 pub column: ptrdiff_t,
100 pub length: ptrdiff_t,
101 pub text: StrContainer,
102}
103
104impl Display for Message {
105 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
106 write!(
107 f,
108 "{} [{}:{}:{}]",
109 self.text.as_str(),
110 self.file.as_str(),
111 self.line,
112 self.column
113 )
114 }
115}
116
117#[repr(C)]
119pub struct OutputFile {
120 pub path: StrContainer,
121 pub data: StrContainer,
122}
123
124#[derive(Copy, Clone)]
125pub enum Charset {
126 Default,
127 ASCII,
128 UTF8,
129}
130
131#[derive(Copy, Clone)]
132pub enum EngineName {
133 Chrome,
134 Edge,
135 Firefox,
136 IOS,
137 Node,
138 Safari,
139}
140
141#[derive(Copy, Clone)]
142pub enum Format {
143 Default,
144 IIFE,
145 CommonJS,
146 ESModule,
147}
148
149#[derive(Copy, Clone)]
150pub enum JSXMode {
151 Transform,
152 Preserve,
153}
154
155#[derive(Copy, Clone)]
156pub enum LegalComments {
157 Default,
158 None,
159 Inline,
160 EndOfFile,
161 Linked,
162 External,
163}
164
165#[derive(Copy, Clone)]
166pub enum Loader {
167 None,
168 JS,
169 JSX,
170 TS,
171 TSX,
172 JSON,
173 Text,
174 Base64,
175 DataURL,
176 File,
177 Binary,
178 CSS,
179 Default,
180}
181
182#[derive(Copy, Clone)]
183pub enum Platform {
184 Browser,
185 Node,
186 Neutral,
187}
188
189#[derive(Copy, Clone)]
190pub enum SourceMap {
191 None,
192 Inline,
193 Linked,
194 External,
195 InlineAndExternal,
196}
197
198#[derive(Copy, Clone)]
199pub enum SourcesContent {
200 Include,
201 Exclude,
202}
203
204#[derive(Copy, Clone)]
205pub enum Target {
206 Default,
207 ESNext,
208 ES5,
209 ES2015,
210 ES2016,
211 ES2017,
212 ES2018,
213 ES2019,
214 ES2020,
215 ES2021,
216}
217
218#[derive(Copy, Clone)]
219pub enum TreeShaking {
220 Default,
221 False,
222 True,
223}
224
225#[derive(Clone)]
226pub struct Engine {
227 pub name: EngineName,
228 pub version: String,
229}
230
231#[derive(Clone)]
232pub struct EntryPoint {
233 pub input_path: String,
234 pub output_path: String,
235}
236
237#[derive(Clone)]
243pub struct BuildOptionsBuilder {
244 pub source_map: SourceMap,
245 pub source_root: String,
246 pub sources_content: SourcesContent,
247
248 pub target: Target,
249 pub engines: Vec<Engine>,
250
251 pub minify_whitespace: bool,
252 pub minify_identifiers: bool,
253 pub minify_syntax: bool,
254 pub charset: Charset,
255 pub tree_shaking: TreeShaking,
256 pub ignore_annotations: bool,
257 pub legal_comments: LegalComments,
258
259 pub jsx_mode: JSXMode,
260 pub jsx_factory: String,
261 pub jsx_fragment: String,
262
263 pub define: HashMap<String, String>,
264 pub pure: Vec<String>,
265 pub keep_names: bool,
266
267 pub global_name: String,
268 pub bundle: bool,
269 pub preserve_symlinks: bool,
270 pub splitting: bool,
271 pub outfile: String,
272 pub metafile: bool,
273 pub outdir: String,
274 pub outbase: String,
275 pub abs_working_dir: String,
276 pub platform: Platform,
277 pub format: Format,
278 pub external: Vec<String>,
279 pub main_fields: Vec<String>,
280 pub conditions: Vec<String>,
281 pub loader: HashMap<String, Loader>,
282 pub resolve_extensions: Vec<String>,
283 pub tsconfig: String,
284 pub out_extensions: HashMap<String, String>,
285 pub public_path: String,
286 pub inject: Vec<String>,
287 pub banner: HashMap<String, String>,
288 pub footer: HashMap<String, String>,
289 pub node_paths: Vec<String>,
290
291 pub entry_names: String,
292 pub chunk_names: String,
293 pub asset_names: String,
294
295 pub entry_points: Vec<String>,
296 pub entry_points_advanced: Vec<EntryPoint>,
297
298 pub write: bool,
299 pub allow_overwrite: bool,
300 pub incremental: bool,
301}
302
303pub struct BuildOptions {
304 source_root: String,
306 engines: Vec<FfiapiEngine>,
307 jsx_factory: String,
308 jsx_fragment: String,
309 define: Vec<FfiapiMapStringStringEntry>,
310 pure: Vec<GoString>,
311 global_name: String,
312 outfile: String,
313 outdir: String,
314 outbase: String,
315 abs_working_dir: String,
316 external: Vec<GoString>,
317 main_fields: Vec<GoString>,
318 conditions: Vec<GoString>,
319 loader: Vec<FfiapiLoader>,
320 resolve_extensions: Vec<GoString>,
321 tsconfig: String,
322 out_extensions: Vec<FfiapiMapStringStringEntry>,
323 public_path: String,
324 inject: Vec<GoString>,
325 banner: Vec<FfiapiMapStringStringEntry>,
326 footer: Vec<FfiapiMapStringStringEntry>,
327 node_paths: Vec<GoString>,
328 entry_names: String,
329 chunk_names: String,
330 asset_names: String,
331 entry_points: Vec<GoString>,
332 entry_points_advanced: Vec<FfiapiEntryPoint>,
333 pub(crate) ffiapi_ptr: *const FfiapiBuildOptions,
334}
335
336unsafe impl Send for BuildOptions {}
337
338unsafe impl Sync for BuildOptions {}
339
340impl Drop for BuildOptions {
341 fn drop(&mut self) {
342 unsafe {
343 let _: Box<FfiapiBuildOptions> = Box::from_raw(self.ffiapi_ptr as _);
344 };
345 }
346}
347
348impl BuildOptionsBuilder {
349 pub fn new() -> BuildOptionsBuilder {
350 BuildOptionsBuilder {
351 source_map: SourceMap::None,
352 source_root: "".to_string(),
353 sources_content: SourcesContent::Include,
354 target: Target::Default,
355 engines: vec![],
356 minify_whitespace: false,
357 minify_identifiers: false,
358 minify_syntax: false,
359 charset: Charset::Default,
360 tree_shaking: TreeShaking::Default,
361 ignore_annotations: false,
362 legal_comments: LegalComments::Default,
363 jsx_mode: JSXMode::Transform,
364 jsx_factory: "".to_string(),
365 jsx_fragment: "".to_string(),
366 define: Default::default(),
367 pure: vec![],
368 keep_names: false,
369 global_name: "".to_string(),
370 bundle: false,
371 preserve_symlinks: false,
372 splitting: false,
373 outfile: "".to_string(),
374 metafile: false,
375 outdir: "".to_string(),
376 outbase: "".to_string(),
377 abs_working_dir: "".to_string(),
378 platform: Platform::Browser,
379 format: Format::Default,
380 external: vec![],
381 main_fields: vec![],
382 conditions: vec![],
383 loader: Default::default(),
384 resolve_extensions: vec![],
385 tsconfig: "".to_string(),
386 out_extensions: Default::default(),
387 public_path: "".to_string(),
388 inject: vec![],
389 banner: Default::default(),
390 footer: Default::default(),
391 node_paths: vec![],
392 entry_names: "".to_string(),
393 chunk_names: "".to_string(),
394 asset_names: "".to_string(),
395 entry_points: vec![],
396 entry_points_advanced: vec![],
397 write: false,
398 allow_overwrite: false,
399 incremental: false,
400 }
401 }
402
403 pub fn build(self) -> Arc<BuildOptions> {
404 let mut res = Arc::new(BuildOptions {
405 source_root: self.source_root,
408 engines: transform(self.engines, FfiapiEngine::from_engine),
409 jsx_factory: self.jsx_factory,
410 jsx_fragment: self.jsx_fragment,
411 define: transform(self.define, FfiapiMapStringStringEntry::from_map_entry),
412 pure: transform(self.pure, GoString::from_string),
413 global_name: self.global_name,
414 outfile: self.outfile,
415 outdir: self.outdir,
416 outbase: self.outbase,
417 abs_working_dir: self.abs_working_dir,
418 external: transform(self.external, GoString::from_string),
419 main_fields: transform(self.main_fields, GoString::from_string),
420 conditions: transform(self.conditions, GoString::from_string),
421 loader: transform(self.loader, FfiapiLoader::from_map_entry),
422 resolve_extensions: transform(self.resolve_extensions, GoString::from_string),
423 tsconfig: self.tsconfig,
424 out_extensions: transform(
425 self.out_extensions,
426 FfiapiMapStringStringEntry::from_map_entry,
427 ),
428 public_path: self.public_path,
429 inject: transform(self.inject, GoString::from_string),
430 banner: transform(self.banner, FfiapiMapStringStringEntry::from_map_entry),
431 footer: transform(self.footer, FfiapiMapStringStringEntry::from_map_entry),
432 node_paths: transform(self.node_paths, GoString::from_string),
433 entry_names: self.entry_names,
434 chunk_names: self.chunk_names,
435 asset_names: self.asset_names,
436 entry_points: transform(self.entry_points, GoString::from_string),
437 entry_points_advanced: transform(
438 self.entry_points_advanced,
439 FfiapiEntryPoint::from_entry_point,
440 ),
441 ffiapi_ptr: std::ptr::null(),
442 });
443
444 unsafe {
445 let ffiapi_ptr = Box::into_raw(Box::new(FfiapiBuildOptions {
446 source_map: self.source_map as u8,
447 source_root: GoString::from_bytes_unmanaged(res.source_root.as_bytes()),
448 sources_content: self.sources_content as u8,
449
450 target: self.target as u8,
451 engines: get_allocation_pointer(&res.engines),
452 engines_len: res.engines.len(),
453
454 minify_whitespace: self.minify_whitespace,
455 minify_identifiers: self.minify_identifiers,
456 minify_syntax: self.minify_syntax,
457 charset: self.charset as u8,
458 tree_shaking: self.tree_shaking as u8,
459 ignore_annotations: self.ignore_annotations,
460 legal_comments: self.legal_comments as u8,
461
462 jsx_mode: self.jsx_mode as u8,
463 jsx_factory: GoString::from_bytes_unmanaged(res.jsx_factory.as_bytes()),
464 jsx_fragment: GoString::from_bytes_unmanaged(res.jsx_fragment.as_bytes()),
465
466 define: get_allocation_pointer(&res.define),
467 define_len: res.define.len(),
468 pure: FfiapiGoStringGoSlice::from_vec_unamanged(&res.pure),
469 keep_names: self.keep_names,
470
471 global_name: GoString::from_bytes_unmanaged(res.global_name.as_bytes()),
472 bundle: self.bundle,
473 preserve_symlinks: self.preserve_symlinks,
474 splitting: self.splitting,
475 outfile: GoString::from_bytes_unmanaged(res.outfile.as_bytes()),
476 metafile: self.metafile,
477 outdir: GoString::from_bytes_unmanaged(res.outdir.as_bytes()),
478 outbase: GoString::from_bytes_unmanaged(res.outbase.as_bytes()),
479 abs_working_dir: GoString::from_bytes_unmanaged(res.abs_working_dir.as_bytes()),
480 platform: self.platform as u8,
481 format: self.format as u8,
482 external: FfiapiGoStringGoSlice::from_vec_unamanged(&res.external),
483 main_fields: FfiapiGoStringGoSlice::from_vec_unamanged(&res.main_fields),
484 conditions: FfiapiGoStringGoSlice::from_vec_unamanged(&res.conditions),
485 loader: get_allocation_pointer(&res.loader),
486 loader_len: res.loader.len(),
487 resolve_extensions: FfiapiGoStringGoSlice::from_vec_unamanged(
488 &res.resolve_extensions,
489 ),
490 tsconfig: GoString::from_bytes_unmanaged(res.tsconfig.as_bytes()),
491 out_extensions: get_allocation_pointer(&res.out_extensions),
492 out_extensions_len: res.out_extensions.len(),
493 public_path: GoString::from_bytes_unmanaged(res.public_path.as_bytes()),
494 inject: FfiapiGoStringGoSlice::from_vec_unamanged(&res.inject),
495 banner: get_allocation_pointer(&res.banner),
496 banner_len: res.banner.len(),
497 footer: get_allocation_pointer(&res.footer),
498 footer_len: res.footer.len(),
499 node_paths: FfiapiGoStringGoSlice::from_vec_unamanged(&res.node_paths),
500
501 entry_names: GoString::from_bytes_unmanaged(res.entry_names.as_bytes()),
502 chunk_names: GoString::from_bytes_unmanaged(res.chunk_names.as_bytes()),
503 asset_names: GoString::from_bytes_unmanaged(res.asset_names.as_bytes()),
504
505 entry_points: FfiapiGoStringGoSlice::from_vec_unamanged(&res.entry_points),
506 entry_points_advanced: get_allocation_pointer(&res.entry_points_advanced),
507 entry_points_advanced_len: res.entry_points_advanced.len(),
508
509 write: self.write,
510 allow_overwrite: self.allow_overwrite,
511 incremental: self.incremental,
512 }));
513 Arc::get_mut(&mut res).unwrap().ffiapi_ptr = ffiapi_ptr;
514 };
515
516 res
517 }
518}
519
520pub struct BuildResult {
521 pub metafile: StrContainer,
522 pub output_files: SliceContainer<OutputFile>,
523 pub errors: SliceContainer<Message>,
524 pub warnings: SliceContainer<Message>,
525}
526
527#[derive(Clone)]
528pub struct TransformOptionsBuilder {
529 pub source_map: SourceMap,
530 pub source_root: String,
531 pub sources_content: SourcesContent,
532
533 pub target: Target,
534 pub format: Format,
535 pub global_name: String,
536 pub engines: Vec<Engine>,
537
538 pub minify_whitespace: bool,
539 pub minify_identifiers: bool,
540 pub minify_syntax: bool,
541 pub charset: Charset,
542 pub tree_shaking: TreeShaking,
543 pub ignore_annotations: bool,
544 pub legal_comments: LegalComments,
545
546 pub jsx_mode: JSXMode,
547 pub jsx_factory: String,
548 pub jsx_fragment: String,
549
550 pub tsconfig_raw: String,
551 pub footer: String,
552 pub banner: String,
553
554 pub define: HashMap<String, String>,
555 pub pure: Vec<String>,
556 pub keep_names: bool,
557
558 pub source_file: String,
559 pub loader: Loader,
560}
561
562pub struct TransformOptions {
563 source_root: String,
565 global_name: String,
566 engines: Vec<FfiapiEngine>,
567 jsx_factory: String,
568 jsx_fragment: String,
569 tsconfig_raw: String,
570 footer: String,
571 banner: String,
572 define: Vec<FfiapiMapStringStringEntry>,
573 pure: Vec<GoString>,
574 source_file: String,
575 pub(crate) ffiapi_ptr: *const FfiapiTransformOptions,
576}
577
578unsafe impl Send for TransformOptions {}
579
580unsafe impl Sync for TransformOptions {}
581
582impl Drop for TransformOptions {
583 fn drop(&mut self) {
584 unsafe {
585 let _: Box<FfiapiTransformOptions> = Box::from_raw(self.ffiapi_ptr as _);
586 };
587 }
588}
589
590impl TransformOptionsBuilder {
591 pub fn new() -> TransformOptionsBuilder {
592 TransformOptionsBuilder {
593 source_map: SourceMap::None,
594 source_root: "".to_string(),
595 sources_content: SourcesContent::Include,
596 target: Target::Default,
597 format: Format::Default,
598 global_name: "".to_string(),
599 engines: vec![],
600 minify_whitespace: false,
601 minify_identifiers: false,
602 minify_syntax: false,
603 charset: Charset::Default,
604 tree_shaking: TreeShaking::Default,
605 ignore_annotations: false,
606 legal_comments: LegalComments::Default,
607 jsx_mode: JSXMode::Transform,
608 jsx_factory: "".to_string(),
609 jsx_fragment: "".to_string(),
610 tsconfig_raw: "".to_string(),
611 footer: "".to_string(),
612 banner: "".to_string(),
613 define: Default::default(),
614 pure: vec![],
615 keep_names: false,
616 source_file: "".to_string(),
617 loader: Loader::None,
618 }
619 }
620
621 pub fn build(self) -> Arc<TransformOptions> {
622 let mut res = Arc::new(TransformOptions {
623 source_root: self.source_root,
626 global_name: self.global_name,
627 engines: transform(self.engines, FfiapiEngine::from_engine),
628 jsx_factory: self.jsx_factory,
629 jsx_fragment: self.jsx_fragment,
630 tsconfig_raw: self.tsconfig_raw,
631 footer: self.footer,
632 banner: self.banner,
633 define: transform(self.define, FfiapiMapStringStringEntry::from_map_entry),
634 pure: transform(self.pure, GoString::from_string),
635 source_file: self.source_file,
636 ffiapi_ptr: std::ptr::null(),
637 });
638
639 unsafe {
640 let ffiapi_ptr = Box::into_raw(Box::new(FfiapiTransformOptions {
641 source_map: self.source_map as u8,
642 source_root: GoString::from_bytes_unmanaged(res.source_root.as_bytes()),
643 sources_content: self.sources_content as u8,
644
645 target: self.target as u8,
646 format: self.format as u8,
647 global_name: GoString::from_bytes_unmanaged(res.global_name.as_bytes()),
648 engines: get_allocation_pointer(&res.engines),
649 engines_len: res.engines.len(),
650
651 minify_whitespace: self.minify_whitespace,
652 minify_identifiers: self.minify_identifiers,
653 minify_syntax: self.minify_syntax,
654 charset: self.charset as u8,
655 tree_shaking: self.tree_shaking as u8,
656 ignore_annotations: self.ignore_annotations,
657 legal_comments: self.legal_comments as u8,
658
659 jsx_mode: self.jsx_mode as u8,
660 jsx_factory: GoString::from_bytes_unmanaged(res.jsx_factory.as_bytes()),
661 jsx_fragment: GoString::from_bytes_unmanaged(res.jsx_fragment.as_bytes()),
662 tsconfig_raw: GoString::from_bytes_unmanaged(res.tsconfig_raw.as_bytes()),
663 footer: GoString::from_bytes_unmanaged(res.footer.as_bytes()),
664 banner: GoString::from_bytes_unmanaged(res.banner.as_bytes()),
665
666 define: get_allocation_pointer(&res.define),
667 define_len: res.define.len(),
668 pure: FfiapiGoStringGoSlice::from_vec_unamanged(&res.pure),
669 keep_names: self.keep_names,
670
671 source_file: GoString::from_bytes_unmanaged(res.source_file.as_bytes()),
672 loader: self.loader as u8,
673 }));
674 Arc::get_mut(&mut res).unwrap().ffiapi_ptr = ffiapi_ptr;
675 };
676
677 res
678 }
679}
680
681pub struct TransformResult {
682 pub code: StrContainer,
683 pub map: StrContainer,
684 pub errors: SliceContainer<Message>,
685 pub warnings: SliceContainer<Message>,
686}