1use crate::modules::IntoModuleCodeString;
3use crate::modules::ModuleCodeString;
4use crate::ops::OpMetadata;
5use crate::runtime::bindings;
6use crate::FastStaticString;
7use crate::OpState;
8use anyhow::Context as _;
9use anyhow::Error;
10use std::borrow::Cow;
11use std::marker::PhantomData;
12use std::sync::Arc;
13use v8::fast_api::FastFunction;
14use v8::MapFnTo;
15
16#[derive(Clone)]
17pub enum ExtensionFileSourceCode {
18 #[deprecated = "Use ExtensionFileSource::new"]
23 IncludedInBinary(FastStaticString),
24
25 LoadedFromFsDuringSnapshot(&'static str), LoadedFromMemoryDuringSnapshot(FastStaticString),
34
35 Computed(Arc<str>),
37}
38
39#[allow(deprecated)]
40impl std::fmt::Debug for ExtensionFileSourceCode {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match *self {
43 Self::IncludedInBinary(..) => write!(f, "IncludedInBinary(..)"),
44 Self::LoadedFromFsDuringSnapshot(path) => {
45 write!(f, "LoadedFromFsDuringSnapshot({path})")
46 }
47 Self::LoadedFromMemoryDuringSnapshot(..) => {
48 write!(f, "LoadedFromMemoryDuringSnapshot(..)")
49 }
50 Self::Computed(..) => write!(f, "Computed(..)"),
51 }
52 }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum ExtensionSourceType {
57 LazyEsm,
58 Js,
59 Esm,
60}
61
62#[derive(Clone, Debug)]
63pub struct ExtensionFileSource {
64 pub specifier: &'static str,
65 pub code: ExtensionFileSourceCode,
66 _unconstructable_use_new: PhantomData<()>,
67}
68
69impl ExtensionFileSource {
70 pub const fn new(specifier: &'static str, code: FastStaticString) -> Self {
71 #[allow(deprecated)]
72 Self {
73 specifier,
74 code: ExtensionFileSourceCode::IncludedInBinary(code),
75 _unconstructable_use_new: PhantomData,
76 }
77 }
78
79 pub const fn new_computed(specifier: &'static str, code: Arc<str>) -> Self {
80 #[allow(deprecated)]
81 Self {
82 specifier,
83 code: ExtensionFileSourceCode::Computed(code),
84 _unconstructable_use_new: PhantomData,
85 }
86 }
87
88 pub const fn loaded_during_snapshot(
89 specifier: &'static str,
90 path: &'static str,
91 ) -> Self {
92 #[allow(deprecated)]
93 Self {
94 specifier,
95 code: ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path),
96 _unconstructable_use_new: PhantomData,
97 }
98 }
99
100 pub const fn loaded_from_memory_during_snapshot(
101 specifier: &'static str,
102 code: FastStaticString,
103 ) -> Self {
104 #[allow(deprecated)]
105 Self {
106 specifier,
107 code: ExtensionFileSourceCode::LoadedFromMemoryDuringSnapshot(code),
108 _unconstructable_use_new: PhantomData,
109 }
110 }
111
112 fn find_non_ascii(s: &str) -> String {
113 s.chars().filter(|c| !c.is_ascii()).collect::<String>()
114 }
115
116 #[allow(deprecated)]
117 pub fn load(&self) -> Result<ModuleCodeString, Error> {
118 match &self.code {
119 ExtensionFileSourceCode::LoadedFromMemoryDuringSnapshot(code)
120 | ExtensionFileSourceCode::IncludedInBinary(code) => {
121 debug_assert!(
122 code.is_ascii(),
123 "Extension code must be 7-bit ASCII: {} (found {})",
124 self.specifier,
125 Self::find_non_ascii(code)
126 );
127 Ok(IntoModuleCodeString::into_module_code(*code))
128 }
129 ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => {
130 let msg = || format!("Failed to read \"{}\"", path);
131 let s = std::fs::read_to_string(path).with_context(msg)?;
132 debug_assert!(
133 s.is_ascii(),
134 "Extension code must be 7-bit ASCII: {} (found {})",
135 self.specifier,
136 Self::find_non_ascii(&s)
137 );
138 Ok(s.into())
139 }
140 ExtensionFileSourceCode::Computed(code) => {
141 debug_assert!(
142 code.is_ascii(),
143 "Extension code must be 7-bit ASCII: {} (found {})",
144 self.specifier,
145 Self::find_non_ascii(code)
146 );
147 Ok(ModuleCodeString::from(code.clone()))
148 }
149 }
150 }
151}
152
153pub type OpFnRef = v8::FunctionCallback;
154pub type OpMiddlewareFn = dyn Fn(OpDecl) -> OpDecl;
155pub type OpStateFn = dyn FnOnce(&mut OpState);
156pub trait Op {
158 const NAME: &'static str;
159 const DECL: OpDecl;
160}
161pub type GlobalTemplateMiddlewareFn =
162 for<'s> fn(
163 &mut v8::HandleScope<'s, ()>,
164 v8::Local<'s, v8::ObjectTemplate>,
165 ) -> v8::Local<'s, v8::ObjectTemplate>;
166pub type GlobalObjectMiddlewareFn =
167 for<'s> fn(&mut v8::HandleScope<'s>, v8::Local<'s, v8::Object>);
168
169extern "C" fn noop() {}
170
171#[derive(Clone, Copy)]
172pub struct OpDecl {
173 pub name: &'static str,
174 pub(crate) name_fast: FastStaticString,
175 pub is_async: bool,
176 pub is_reentrant: bool,
177 pub arg_count: u8,
178 pub(crate) slow_fn: OpFnRef,
180 pub(crate) slow_fn_with_metrics: OpFnRef,
182 pub(crate) fast_fn: Option<FastFunction>,
184 pub(crate) fast_fn_with_metrics: Option<FastFunction>,
186 pub metadata: OpMetadata,
188}
189
190impl OpDecl {
191 #[doc(hidden)]
193 #[allow(clippy::too_many_arguments)]
194 pub const fn new_internal_op2(
195 name: (&'static str, FastStaticString),
196 is_async: bool,
197 is_reentrant: bool,
198 arg_count: u8,
199 slow_fn: OpFnRef,
200 slow_fn_with_metrics: OpFnRef,
201 fast_fn: Option<FastFunction>,
202 fast_fn_with_metrics: Option<FastFunction>,
203 metadata: OpMetadata,
204 ) -> Self {
205 #[allow(deprecated)]
206 Self {
207 name: name.0,
208 name_fast: name.1,
209 is_async,
210 is_reentrant,
211 arg_count,
212 slow_fn,
213 slow_fn_with_metrics,
214 fast_fn,
215 fast_fn_with_metrics,
216 metadata,
217 }
218 }
219
220 pub fn disable(self) -> Self {
223 Self {
224 slow_fn: bindings::op_disabled_fn.map_fn_to(),
225 slow_fn_with_metrics: bindings::op_disabled_fn.map_fn_to(),
226 fast_fn: if self.fast_fn.is_some() {
231 Some(FastFunction {
232 args: &[],
233 function: noop as _,
234 repr: v8::fast_api::Int64Representation::Number,
235 return_type: v8::fast_api::CType::Void,
236 })
237 } else {
238 None
239 },
240 fast_fn_with_metrics: if self.fast_fn_with_metrics.is_some() {
241 Some(FastFunction {
242 args: &[],
243 function: noop as _,
244 repr: v8::fast_api::Int64Representation::Number,
245 return_type: v8::fast_api::CType::Void,
246 })
247 } else {
248 None
249 },
250 ..self
251 }
252 }
253
254 pub const fn with_implementation_from(mut self, from: &Self) -> Self {
257 self.slow_fn = from.slow_fn;
258 self.slow_fn_with_metrics = from.slow_fn_with_metrics;
259 self.fast_fn = from.fast_fn;
260 self.fast_fn_with_metrics = from.fast_fn_with_metrics;
261 self
262 }
263
264 #[doc(hidden)]
265 pub const fn fast_fn(&self) -> FastFunction {
266 let Some(f) = self.fast_fn else {
267 panic!("Not a fast function");
268 };
269 f
270 }
271
272 #[doc(hidden)]
273 pub const fn fast_fn_with_metrics(&self) -> FastFunction {
274 let Some(f) = self.fast_fn_with_metrics else {
275 panic!("Not a fast function");
276 };
277 f
278 }
279}
280
281#[macro_export]
319macro_rules! ops {
320 ($name:ident, parameters = [ $( $param:ident : $type:ident ),+ ], ops = [ $( $(#[$m:meta])* $( $op:ident )::+ $( < $op_param:ident > )? ),+ $(,)? ]) => {
321 pub(crate) fn $name < $( $param : $type + 'static ),+ > () -> ::std::vec::Vec<$crate::OpDecl> {
322 vec![
323 $(
324 $( #[ $m ] )*
325 $( $op )::+ :: decl $( :: <$op_param> )? () ,
326 )+
327 ]
328 }
329 };
330 ($name:ident, [ $( $(#[$m:meta])* $( $op:ident )::+ ),+ $(,)? ] ) => {
331 pub(crate) fn $name() -> ::std::Vec<$crate::OpDecl> {
332 use $crate::Op;
333 vec![
334 $( $( #[ $m ] )* $( $op )::+ :: DECL, )+
335 ]
336 }
337 }
338}
339
340#[macro_export]
342macro_rules! or {
343 ($e:expr, $fallback:expr) => {
344 $e
345 };
346 (, $fallback:expr) => {
347 $fallback
348 };
349}
350
351#[macro_export]
386macro_rules! extension {
387 (
388 $name:ident
389 $(, deps = [ $( $dep:ident ),* ] )?
390 $(, parameters = [ $( $param:ident : $type:ident ),+ ] )?
391 $(, bounds = [ $( $bound:path : $bound_type:ident ),+ ] )?
392 $(, ops_fn = $ops_symbol:ident $( < $ops_param:ident > )? )?
393 $(, ops = [ $( $(#[$m:meta])* $( $op:ident )::+ $( < $( $op_param:ident ),* > )? ),+ $(,)? ] )?
394 $(, esm_entry_point = $esm_entry_point:expr )?
395 $(, esm = [ $($esm:tt)* ] )?
396 $(, lazy_loaded_esm = [ $($lazy_loaded_esm:tt)* ] )?
397 $(, js = [ $($js:tt)* ] )?
398 $(, options = { $( $options_id:ident : $options_type:ty ),* $(,)? } )?
399 $(, middleware = $middleware_fn:expr )?
400 $(, state = $state_fn:expr )?
401 $(, global_template_middleware = $global_template_middleware_fn:expr )?
402 $(, global_object_middleware = $global_object_middleware_fn:expr )?
403 $(, external_references = [ $( $external_reference:expr ),* $(,)? ] )?
404 $(, customizer = $customizer_fn:expr )?
405 $(, docs = $($docblocks:expr),+)?
406 $(,)?
407 ) => {
408 $( $(#[doc = $docblocks])+ )?
409 #[doc = concat!("let mut extensions = vec![", stringify!($name), "::init_ops_and_esm()];")]
417 #[allow(non_camel_case_types)]
424 pub struct $name {
425 }
426
427 impl $name {
428 fn ext $( < $( $param : $type + 'static ),+ > )?() -> $crate::Extension {
429 #[allow(unused_imports)]
430 use $crate::Op;
431 $crate::Extension {
432 name: ::std::stringify!($name),
434 deps: &[ $( $( ::std::stringify!($dep) ),* )? ],
435 js_files: {
438 const JS: &'static [$crate::ExtensionFileSource] = &$crate::include_js_files!( $name $($($js)*)? );
439 ::std::borrow::Cow::Borrowed(JS)
440 },
441 esm_files: {
442 const JS: &'static [$crate::ExtensionFileSource] = &$crate::include_js_files!( $name $($($esm)*)? );
443 ::std::borrow::Cow::Borrowed(JS)
444 },
445 lazy_loaded_esm_files: {
446 const JS: &'static [$crate::ExtensionFileSource] = &$crate::include_lazy_loaded_js_files!( $name $($($lazy_loaded_esm)*)? );
447 ::std::borrow::Cow::Borrowed(JS)
448 },
449 esm_entry_point: {
450 const V: ::std::option::Option<&'static ::std::primitive::str> = $crate::or!($(::std::option::Option::Some($esm_entry_point))?, ::std::option::Option::None);
451 V
452 },
453 ops: ::std::borrow::Cow::Borrowed(&[$($(
454 $( #[ $m ] )*
455 $( $op )::+ $( :: < $($op_param),* > )? :: DECL
456 ),+)?]),
457 external_references: ::std::borrow::Cow::Borrowed(&[ $( $external_reference ),* ]),
458 global_template_middleware: ::std::option::Option::None,
459 global_object_middleware: ::std::option::Option::None,
460 op_state_fn: ::std::option::Option::None,
462 middleware_fn: ::std::option::Option::None,
463 enabled: true,
464 }
465 }
466
467 #[inline(always)]
469 #[allow(unused_variables)]
470 fn with_ops_fn $( < $( $param : $type + 'static ),+ > )?(ext: &mut $crate::Extension)
471 $( where $( $bound : $bound_type ),+ )?
472 {
473 $crate::extension!(! __ops__ ext $( $ops_symbol $( < $ops_param > )? )? __eot__);
475 }
476
477 #[inline(always)]
479 #[allow(unused_variables)]
480 fn with_state_and_middleware$( < $( $param : $type + 'static ),+ > )?(ext: &mut $crate::Extension, $( $( $options_id : $options_type ),* )? )
481 $( where $( $bound : $bound_type ),+ )?
482 {
483 $crate::extension!(! __config__ ext $( parameters = [ $( $param : $type ),* ] )? $( config = { $( $options_id : $options_type ),* } )? $( state_fn = $state_fn )? );
484
485 $(
486 ext.global_template_middleware = ::std::option::Option::Some($global_template_middleware_fn);
487 )?
488
489 $(
490 ext.global_object_middleware = ::std::option::Option::Some($global_object_middleware_fn);
491 )?
492
493 $(
494 ext.middleware_fn = ::std::option::Option::Some(::std::boxed::Box::new($middleware_fn));
495 )?
496 }
497
498 #[inline(always)]
499 #[allow(unused_variables)]
500 #[allow(clippy::redundant_closure_call)]
501 fn with_customizer(ext: &mut $crate::Extension) {
502 $( ($customizer_fn)(ext); )?
503 }
504
505 #[allow(dead_code)]
506 pub fn init_ops_and_esm $( < $( $param : $type + 'static ),+ > )? ( $( $( $options_id : $options_type ),* )? ) -> $crate::Extension
514 $( where $( $bound : $bound_type ),+ )?
515 {
516 let mut ext = Self::ext $( ::< $( $param ),+ > )?();
517 Self::with_ops_fn $( ::< $( $param ),+ > )?(&mut ext);
518 Self::with_state_and_middleware $( ::< $( $param ),+ > )?(&mut ext, $( $( $options_id , )* )? );
519 Self::with_customizer(&mut ext);
520 ext
521 }
522
523 #[allow(dead_code)]
524 pub fn init_ops $( < $( $param : $type + 'static ),+ > )? ( $( $( $options_id : $options_type ),* )? ) -> $crate::Extension
532 $( where $( $bound : $bound_type ),+ )?
533 {
534 let mut ext = Self::ext $( ::< $( $param ),+ > )?();
535 Self::with_ops_fn $( ::< $( $param ),+ > )?(&mut ext);
536 Self::with_state_and_middleware $( ::< $( $param ),+ > )?(&mut ext, $( $( $options_id , )* )? );
537 Self::with_customizer(&mut ext);
538 ext.js_files = ::std::borrow::Cow::Borrowed(&[]);
539 ext.esm_files = ::std::borrow::Cow::Borrowed(&[]);
540 ext.esm_entry_point = ::std::option::Option::None;
541 ext
542 }
543 }
544 };
545
546 (! __config__ $ext:ident $( parameters = [ $( $param:ident : $type:ident ),+ ] )? config = { $( $options_id:ident : $options_type:ty ),* } $( state_fn = $state_fn:expr )? ) => {
548 {
549 #[doc(hidden)]
550 struct Config $( < $( $param : $type + 'static ),+ > )? {
551 $( pub $options_id : $options_type , )*
552 $( __phantom_data: ::std::marker::PhantomData<($( $param ),+)>, )?
553 }
554 let config = Config {
555 $( $options_id , )*
556 $( __phantom_data: ::std::marker::PhantomData::<($( $param ),+)>::default() )?
557 };
558
559 let state_fn: fn(&mut $crate::OpState, Config $( < $( $param ),+ > )? ) = $( $state_fn )?;
560 $ext.op_state_fn = ::std::option::Option::Some(::std::boxed::Box::new(move |state: &mut $crate::OpState| {
561 state_fn(state, config);
562 }));
563 }
564 };
565
566 (! __config__ $ext:ident $( parameters = [ $( $param:ident : $type:ident ),+ ] )? $( state_fn = $state_fn:expr )? ) => {
567 $( $ext.op_state_fn = ::std::option::Option::Some(::std::boxed::Box::new($state_fn)); )?
568 };
569
570 (! __ops__ $ext:ident __eot__) => {
571 };
572
573 (! __ops__ $ext:ident $ops_symbol:ident __eot__) => {
574 $ext.ops.to_mut().extend($ops_symbol())
575 };
576
577 (! __ops__ $ext:ident $ops_symbol:ident < $ops_param:ident > __eot__) => {
578 $ext.ops.to_mut().extend($ops_symbol::<$ops_param>())
579 };
580}
581
582pub struct Extension {
583 pub name: &'static str,
584 pub deps: &'static [&'static str],
585 pub js_files: Cow<'static, [ExtensionFileSource]>,
586 pub esm_files: Cow<'static, [ExtensionFileSource]>,
587 pub lazy_loaded_esm_files: Cow<'static, [ExtensionFileSource]>,
588 pub esm_entry_point: Option<&'static str>,
589 pub ops: Cow<'static, [OpDecl]>,
590 pub external_references: Cow<'static, [v8::ExternalReference<'static>]>,
591 pub global_template_middleware: Option<GlobalTemplateMiddlewareFn>,
592 pub global_object_middleware: Option<GlobalObjectMiddlewareFn>,
593 pub op_state_fn: Option<Box<OpStateFn>>,
594 pub middleware_fn: Option<Box<OpMiddlewareFn>>,
595 pub enabled: bool,
596}
597
598impl Extension {
599 pub(crate) fn for_warmup(&self) -> Extension {
603 Self {
604 op_state_fn: None,
605 middleware_fn: None,
606 name: self.name,
607 deps: self.deps,
608 js_files: Cow::Borrowed(&[]),
609 esm_files: Cow::Borrowed(&[]),
610 lazy_loaded_esm_files: Cow::Borrowed(&[]),
611 esm_entry_point: None,
612 ops: self.ops.clone(),
613 external_references: self.external_references.clone(),
614 global_template_middleware: self.global_template_middleware,
615 global_object_middleware: self.global_object_middleware,
616 enabled: self.enabled,
617 }
618 }
619}
620
621impl Default for Extension {
622 fn default() -> Self {
623 Self {
624 name: "DEFAULT",
625 deps: &[],
626 js_files: Cow::Borrowed(&[]),
627 esm_files: Cow::Borrowed(&[]),
628 lazy_loaded_esm_files: Cow::Borrowed(&[]),
629 esm_entry_point: None,
630 ops: Cow::Borrowed(&[]),
631 external_references: Cow::Borrowed(&[]),
632 global_template_middleware: None,
633 global_object_middleware: None,
634 op_state_fn: None,
635 middleware_fn: None,
636 enabled: true,
637 }
638 }
639}
640
641impl Extension {
644 pub fn check_dependencies(&self, previous_exts: &[Extension]) {
648 'dep_loop: for dep in self.deps {
649 if dep == &self.name {
650 panic!("Extension '{}' is either depending on itself or there is another extension with the same name", self.name);
651 }
652
653 for ext in previous_exts {
654 if dep == &ext.name {
655 continue 'dep_loop;
656 }
657 }
658
659 panic!("Extension '{}' is missing dependency '{dep}'", self.name);
660 }
661 }
662
663 pub fn get_js_sources(&self) -> &[ExtensionFileSource] {
666 &self.js_files
667 }
668
669 pub fn get_esm_sources(&self) -> &[ExtensionFileSource] {
670 &self.esm_files
671 }
672
673 pub fn get_lazy_loaded_esm_sources(&self) -> &[ExtensionFileSource] {
674 &self.lazy_loaded_esm_files
675 }
676
677 pub fn get_esm_entry_point(&self) -> Option<&'static str> {
678 self.esm_entry_point
679 }
680
681 pub fn op_count(&self) -> usize {
682 self.ops.len()
683 }
684
685 pub fn init_ops(&mut self) -> &[OpDecl] {
687 if !self.enabled {
688 for op in self.ops.to_mut() {
689 op.disable();
690 }
691 }
692 self.ops.as_ref()
693 }
694
695 pub fn take_state(&mut self, state: &mut OpState) {
697 if let Some(op_fn) = self.op_state_fn.take() {
698 op_fn(state);
699 }
700 }
701
702 pub fn take_middleware(&mut self) -> Option<Box<OpMiddlewareFn>> {
704 self.middleware_fn.take()
705 }
706
707 pub fn get_global_template_middleware(
708 &mut self,
709 ) -> Option<GlobalTemplateMiddlewareFn> {
710 self.global_template_middleware
711 }
712
713 pub fn get_global_object_middleware(
714 &mut self,
715 ) -> Option<GlobalObjectMiddlewareFn> {
716 self.global_object_middleware
717 }
718
719 pub fn get_external_references(
720 &mut self,
721 ) -> &[v8::ExternalReference<'static>] {
722 self.external_references.as_ref()
723 }
724
725 pub fn enabled(self, enabled: bool) -> Self {
726 Self { enabled, ..self }
727 }
728
729 pub fn disable(self) -> Self {
730 self.enabled(false)
731 }
732}
733
734#[macro_export]
783macro_rules! include_js_files {
784 ($name:ident $( dir $dir:literal, )? $(
790 $s1:literal
791 $(with_specifier $s2:literal)?
792 $(= $config:tt)?
793 ),* $(,)?) => {
794 $crate::__extension_include_js_files_detect!(name=$name, dir=$crate::__extension_root_dir!($($dir)?), $([
795 $s1 $(with_specifier $s2)? $(= $config)?
797 ]),*)
798 };
799}
800
801#[macro_export]
810macro_rules! include_lazy_loaded_js_files {
811 ($name:ident $( dir $dir:literal, )? $(
812 $s1:literal
813 $(with_specifier $s2:literal)?
814 $(= $config:tt)?
815 ),* $(,)?) => {
816 $crate::__extension_include_js_files_inner!(mode=included, name=$name, dir=$crate::__extension_root_dir!($($dir)?), $([
817 $s1 $(with_specifier $s2)? $(= $config)?
819 ]),*)
820 };
821}
822
823#[doc(hidden)]
825#[macro_export]
826macro_rules! include_js_files_doctest {
827 ($name:ident $( dir $dir:literal, )? $(
828 $s1:literal
829 $(with_specifier $s2:literal)?
830 $(= $config:tt)?
831 ),* $(,)?) => {
832 $crate::__extension_include_js_files_inner!(mode=loaded, name=$name, dir=$crate::__extension_root_dir!($($dir)?), $([
833 $s1 $(with_specifier $s2)? $(= $config)?
834 ]),*)
835 };
836}
837
838#[cfg(not(feature = "include_js_files_for_snapshotting"))]
843#[doc(hidden)]
844#[macro_export]
845macro_rules! __extension_include_js_files_detect {
846 ($($rest:tt)*) => { $crate::__extension_include_js_files_inner!(mode=included, $($rest)*) };
847}
848
849#[cfg(feature = "include_js_files_for_snapshotting")]
854#[doc(hidden)]
855#[macro_export]
856macro_rules! __extension_include_js_files_detect {
857 ($($rest:tt)*) => { $crate::__extension_include_js_files_inner!(mode=loaded, $($rest)*) };
858}
859
860#[doc(hidden)]
864#[macro_export]
865macro_rules! __extension_include_js_files_inner {
866 (mode=$mode:ident, name=$name:ident, dir=$dir:expr, $([
868 $s1:literal
869 $(with_specifier $s2:literal)?
870 $(= $config:tt)?
871 ]),*) => {
872 [
873 $(
874 $crate::__extension_include_js_files_inner!(
875 @parse_item
876 mode=$mode,
877 name=$name,
878 dir=$dir,
879 $s1 $(with_specifier $s2)? $(= $config)?
880 )
881 ),*
882 ]
883 };
884
885 (@parse_item mode=$mode:ident, name=$name:ident, dir=$dir:expr, $file:literal) => {
889 $crate::__extension_include_js_files_inner!(@item mode=$mode, dir=$dir, specifier=concat!("ext:", stringify!($name), "/", $file), file=$file)
890 };
891 (@parse_item mode=$mode:ident, name=$name:ident, dir=$dir:expr, $file:literal with_specifier $specifier:literal) => {
893 {
894 #[deprecated="When including JS files 'file with_specifier specifier' is deprecated: use 'specifier = file' instead"]
895 struct WithSpecifierIsDeprecated {}
896 _ = WithSpecifierIsDeprecated {};
897 $crate::__extension_include_js_files_inner!(@item mode=$mode, dir=$dir, specifier=$specifier, file=$file)
898 }
899 };
900 (@parse_item mode=$mode:ident, name=$name:ident, dir=$dir:expr, $specifier:literal = $file:literal) => {
902 $crate::__extension_include_js_files_inner!(@item mode=$mode, dir=$dir, specifier=$specifier, file=$file)
903 };
904 (@parse_item mode=$mode:ident, name=$name:ident, dir=$dir:expr, $specifier:literal = { source = $source:literal }) => {
906 $crate::__extension_include_js_files_inner!(@item mode=$mode, specifier=$specifier, source=$source)
907 };
908
909 (@item mode=loaded, specifier=$specifier:expr, source=$source:expr) => {
913 $crate::ExtensionFileSource::loaded_from_memory_during_snapshot($specifier, $crate::ascii_str!($source))
914 };
915 (@item mode=loaded, dir=$dir:expr, specifier=$specifier:expr, file=$file:literal) => {
917 $crate::ExtensionFileSource::loaded_during_snapshot($specifier, concat!($dir, "/", $file))
918 };
919 (@item mode=included, specifier=$specifier:expr, source=$source:expr) => {
921 $crate::ExtensionFileSource::new($specifier, $crate::ascii_str!($source))
922 };
923 (@item mode=included, dir=$dir:expr, specifier=$specifier:expr, file=$file:literal) => {
925 $crate::ExtensionFileSource::new($specifier, $crate::ascii_str_include!(concat!($dir, "/", $file)))
926 };
927}
928
929#[doc(hidden)]
931#[macro_export]
932macro_rules! __extension_root_dir {
933 () => {
934 env!("CARGO_MANIFEST_DIR")
935 };
936 ($dir:expr) => {
937 concat!(env!("CARGO_MANIFEST_DIR"), "/", $dir)
938 };
939}
940
941#[cfg(test)]
942mod tests {
943 #[test]
944 fn test_include_js() {
945 let files = include_js_files!(prefix "00_infra.js", "01_core.js",);
946 assert_eq!("ext:prefix/00_infra.js", files[0].specifier);
947 assert_eq!("ext:prefix/01_core.js", files[1].specifier);
948
949 let files = include_js_files!(prefix dir ".", "00_infra.js", "01_core.js",);
950 assert_eq!("ext:prefix/00_infra.js", files[0].specifier);
951 assert_eq!("ext:prefix/01_core.js", files[1].specifier);
952
953 let files = include_js_files!(prefix
954 "a" = { source = "b" }
955 );
956 assert_eq!("a", files[0].specifier);
957 }
958}