2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2021-11-16 09:02:28 -05:00
use crate ::OpState ;
2023-02-20 21:45:34 +01:00
use anyhow ::Context as _ ;
2021-11-16 09:02:28 -05:00
use anyhow ::Error ;
2023-01-14 20:18:58 -08:00
use std ::cell ::RefCell ;
2023-02-20 21:45:34 +01:00
use std ::path ::PathBuf ;
2023-01-14 20:18:58 -08:00
use std ::rc ::Rc ;
use std ::task ::Context ;
2022-08-21 17:37:53 +05:30
use v8 ::fast_api ::FastFunction ;
2021-04-28 18:41:50 +02:00
2023-02-20 01:11:56 +01:00
#[ derive(Clone, Debug) ]
pub enum ExtensionFileSourceCode {
/// Source code is included in the binary produced. Either by being defined
/// inline, or included using `include_str!()`. If you are snapshotting, this
/// will result in two copies of the source code being included - one in the
/// snapshot, the other the static string in the `Extension`.
IncludedInBinary ( & 'static str ) ,
2023-02-20 21:45:34 +01:00
// Source code is loaded from a file on disk. It's meant to be used if the
// embedder is creating snapshots. Files will be loaded from the filesystem
// during the build time and they will only be present in the V8 snapshot.
LoadedFromFsDuringSnapshot ( PathBuf ) ,
}
impl ExtensionFileSourceCode {
pub fn load ( & self ) -> Result < String , Error > {
match self {
ExtensionFileSourceCode ::IncludedInBinary ( code ) = > Ok ( code . to_string ( ) ) ,
ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot ( path ) = > {
let msg = format! ( " Failed to read \" {} \" " , path . display ( ) ) ;
let code = std ::fs ::read_to_string ( path ) . context ( msg ) ? ;
Ok ( code )
}
}
}
2023-02-20 01:11:56 +01:00
}
2023-02-08 22:40:18 +01:00
#[ derive(Clone, Debug) ]
2023-02-08 00:21:43 +01:00
pub struct ExtensionFileSource {
pub specifier : String ,
2023-02-20 01:11:56 +01:00
pub code : ExtensionFileSourceCode ,
2023-02-08 00:21:43 +01:00
}
2022-03-14 23:14:15 +05:30
pub type OpFnRef = v8 ::FunctionCallback ;
2022-03-15 23:43:17 +01:00
pub type OpMiddlewareFn = dyn Fn ( OpDecl ) -> OpDecl ;
2023-03-17 16:15:27 -06:00
pub type OpStateFn = dyn FnOnce ( & mut OpState ) ;
2022-06-28 12:23:36 +03:00
pub type OpEventLoopFn = dyn Fn ( Rc < RefCell < OpState > > , & mut Context ) -> bool ;
2021-04-28 18:41:50 +02:00
2022-03-15 23:43:17 +01:00
pub struct OpDecl {
pub name : & 'static str ,
pub v8_fn_ptr : OpFnRef ,
2022-03-22 16:39:58 +01:00
pub enabled : bool ,
2022-08-21 17:37:53 +05:30
pub is_async : bool ,
2022-04-02 00:09:21 +02:00
pub is_unstable : bool ,
2022-05-12 19:06:42 +02:00
pub is_v8 : bool ,
2022-08-21 17:37:53 +05:30
pub fast_fn : Option < Box < dyn FastFunction > > ,
2022-03-22 16:39:58 +01:00
}
impl OpDecl {
pub fn enabled ( self , enabled : bool ) -> Self {
Self { enabled , .. self }
}
pub fn disable ( self ) -> Self {
self . enabled ( false )
}
2022-03-15 23:43:17 +01:00
}
2023-03-17 12:22:15 -06:00
/// Declares a block of Deno `#[op]`s. The first parameter determines the name of the
/// op declaration block, and is usually `deno_ops`. This block generates a function that
/// returns a [`Vec<OpDecl>`].
///
/// This can be either a compact form like:
///
/// ```no_compile
/// # use deno_core::*;
/// #[op]
/// fn op_xyz() {}
///
/// deno_core::ops!(deno_ops, [
/// op_xyz
/// ]);
///
/// // Use the ops:
/// deno_ops()
/// ```
///
/// ... or a parameterized form like so that allows passing a number of type parameters
/// to each `#[op]`:
///
/// ```no_compile
/// # use deno_core::*;
/// #[op]
/// fn op_xyz<P>() where P: Clone {}
///
/// deno_core::ops!(deno_ops,
/// parameters = [P: Clone],
/// ops = [
/// op_xyz<P>
/// ]
/// );
///
/// // Use the ops, with `String` as the parameter `P`:
/// deno_ops::<String>()
/// ```
#[ macro_export ]
macro_rules ! ops {
( $name :ident , parameters = [ $( $param :ident : $type :ident ) , + ] , ops = [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ $( < $op_param :ident > ) ? ) , + $(, ) ? ] ) = > {
pub ( crate ) fn $name < $( $param : $type + 'static ) , + > ( ) -> Vec < $crate ::OpDecl > {
vec! [
$(
$( #[ $m ] ) *
$( $op ) ::+ :: decl $( :: < $op_param > ) ? ( ) ,
) +
]
}
} ;
( $name :ident , [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ ) , + $(, ) ? ] ) = > {
pub ( crate ) fn $name ( ) -> Vec < $crate ::OpDecl > {
vec! [
$( $( #[ $m ] ) * $( $op ) ::+ :: decl ( ) , ) +
]
}
}
}
/// Defines a Deno extension. The first parameter is the name of the extension symbol namespace to create. This is the symbol you
/// will use to refer to the extension.
///
/// Most extensions will define a combination of ops and ESM files, like so:
///
/// ```no_compile
/// #[op]
/// fn op_xyz() {
/// }
///
/// deno_core::extension!(
/// my_extension,
/// ops = [ op_xyz ],
/// esm = [ "my_script.js" ],
/// );
/// ```
///
/// The following options are available for the [`extension`] macro:
///
/// * deps: a comma-separated list of module dependencies, eg: `deps = [ my_other_extension ]`
/// * parameters: a comma-separated list of parameters and base traits, eg: `parameters = [ P: MyTrait ]`
/// * ops: a comma-separated list of [`OpDecl`]s to provide, eg: `ops = [ op_foo, op_bar ]`
/// * esm: a comma-separated list of ESM module filenames (see [`include_js_files`]), eg: `esm = [ dir "dir", "my_file.js" ]`
/// * esm_setup_script: see [`ExtensionBuilder::esm_setup_script`]
/// * js: a comma-separated list of JS filenames (see [`include_js_files`]), eg: `js = [ dir "dir", "my_file.js" ]`
/// * config: a structure-like definition for configuration parameters which will be required when initializing this extension, eg: `config = { my_param: Option<usize> }`
/// * middleware: an [`OpDecl`] middleware function with the signature `fn (OpDecl) -> OpDecl`
/// * state: a state initialization function, with the signature `fn (&mut OpState, ...) -> ()`, where `...` are parameters matching the fields of the config struct
/// * event_loop_middleware: an event-loop middleware function (see [`ExtensionBuilder::event_loop_middleware`])
#[ macro_export ]
macro_rules ! extension {
(
$name :ident
$(, deps = [ $( $dep :ident ) , * ] ) ?
$(, parameters = [ $( $param :ident : $type :ident ) , + ] ) ?
$(, ops_fn = $ops_symbol :ident $( < $ops_param :ident > ) ? ) ?
$(, ops = [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ $( < $op_param :ident > ) ? ) , + $(, ) ? ] ) ?
$(, esm_entry_point = $esm_entry_point :literal ) ?
$(, esm = [ $( dir $dir_esm :literal , ) ? $( $esm :literal ) , * $(, ) ? ] ) ?
$(, esm_setup_script = $esm_setup_script :expr ) ?
$(, js = [ $( dir $dir_js :literal , ) ? $( $js :literal ) , * $(, ) ? ] ) ?
2023-03-17 16:15:27 -06:00
$(, options = { $( $options_id :ident : $options_type :ty ) , * $(, ) ? } ) ?
2023-03-17 12:22:15 -06:00
$(, middleware = $middleware_fn :expr ) ?
$(, state = $state_fn :expr ) ?
$(, event_loop_middleware = $event_loop_middleware_fn :ident ) ?
$(, customizer = $customizer_fn :expr ) ?
$(, ) ?
) = > {
/// Extension struct for
#[ doc = stringify!($name) ]
/// .
#[ allow(non_camel_case_types) ]
pub struct $name {
}
impl $name {
#[ inline(always) ]
fn ext ( ) -> $crate ::ExtensionBuilder {
$crate ::Extension ::builder_with_deps ( stringify! ( $name ) , & [ $( $( stringify! ( $dep ) ) , * ) ? ] )
}
/// If ESM or JS was specified, add those files to the extension.
#[ inline(always) ]
#[ allow(unused_variables) ]
fn with_js ( ext : & mut $crate ::ExtensionBuilder ) {
$( ext . esm (
$crate ::include_js_files! ( $( dir $dir_esm , ) ? $( $esm , ) * )
) ; ) ?
$(
ext . esm ( vec! [ ExtensionFileSource {
specifier : " ext:setup " . to_string ( ) ,
code : ExtensionFileSourceCode ::IncludedInBinary ( $esm_setup_script ) ,
} ] ) ;
) ?
$(
ext . esm_entry_point ( $esm_entry_point ) ;
) ?
$( ext . js (
$crate ::include_js_files! ( $( dir $dir_js , ) ? $( $js , ) * )
) ; ) ?
}
// If ops were specified, add those ops to the extension.
#[ inline(always) ]
#[ allow(unused_variables) ]
fn with_ops $( < $( $param : $type + Clone + 'static ) , + > ) ? ( ext : & mut $crate ::ExtensionBuilder ) {
// If individual ops are specified, roll them up into a vector and apply them
$(
let v = vec! [
$(
$( #[ $m ] ) *
$( $op ) ::+ :: decl $( :: < $op_param > ) ? ( )
) , +
] ;
ext . ops ( v ) ;
) ?
// Otherwise use the ops_fn, if provided
$crate ::extension! ( ! __ops__ ext $( $ops_symbol $( < $ops_param > ) ? ) ? __eot__ ) ;
}
// Includes the state and middleware functions, if defined.
#[ inline(always) ]
#[ allow(unused_variables) ]
2023-03-17 16:15:27 -06:00
fn with_state_and_middleware $( < $( $param : $type + Clone + 'static ) , + > ) ? ( ext : & mut $crate ::ExtensionBuilder , $( $( $options_id : $options_type ) , * ) ? ) {
2023-03-17 12:22:15 -06:00
#[ allow(unused_variables) ]
2023-03-17 16:15:27 -06:00
let config = $crate ::extension! ( ! __config__ $( parameters = [ $( $param : $type ) , * ] ) ? $( config = { $( $options_id : $options_type ) , * } ) ? ) ;
2023-03-17 12:22:15 -06:00
$(
ext . state ( move | state : & mut $crate ::OpState | {
2023-03-17 16:15:27 -06:00
config . call_callback ( state , $state_fn )
2023-03-17 12:22:15 -06:00
} ) ;
) ?
$(
ext . event_loop_middleware ( $event_loop_middleware_fn ) ;
) ?
$(
ext . middleware ( $middleware_fn ) ;
) ?
}
#[ inline(always) ]
#[ allow(unused_variables) ]
fn with_customizer ( ext : & mut $crate ::ExtensionBuilder ) {
$( ( $customizer_fn ) ( ext ) ; ) ?
}
#[ allow(dead_code) ]
pub fn init_js_only $( < $( $param : $type + Clone + 'static ) , + > ) ? ( ) -> $crate ::Extension {
let mut ext = Self ::ext ( ) ;
// If esm or JS was specified, add JS files
Self ::with_js ( & mut ext ) ;
Self ::with_ops $( ::< ( $( $param ) , + ) > ) ? ( & mut ext ) ;
Self ::with_customizer ( & mut ext ) ;
2023-03-17 16:15:27 -06:00
ext . take ( )
2023-03-17 12:22:15 -06:00
}
#[ allow(dead_code) ]
2023-03-17 16:15:27 -06:00
pub fn init_ops_and_esm $( < $( $param : $type + Clone + 'static ) , + > ) ? ( $( $( $options_id : $options_type ) , * ) ? ) -> $crate ::Extension {
2023-03-17 12:22:15 -06:00
let mut ext = Self ::ext ( ) ;
// If esm or JS was specified, add JS files
Self ::with_js ( & mut ext ) ;
Self ::with_ops $( ::< ( $( $param ) , + ) > ) ? ( & mut ext ) ;
2023-03-17 16:15:27 -06:00
Self ::with_state_and_middleware $( ::< ( $( $param ) , + ) > ) ? ( & mut ext , $( $( $options_id , ) * ) ? ) ;
2023-03-17 12:22:15 -06:00
Self ::with_customizer ( & mut ext ) ;
2023-03-17 16:15:27 -06:00
ext . take ( )
2023-03-17 12:22:15 -06:00
}
#[ allow(dead_code) ]
2023-03-17 16:15:27 -06:00
pub fn init_ops $( < $( $param : $type + Clone + 'static ) , + > ) ? ( $( $( $options_id : $options_type ) , * ) ? ) -> $crate ::Extension {
2023-03-17 12:22:15 -06:00
let mut ext = Self ::ext ( ) ;
Self ::with_ops $( ::< ( $( $param ) , + ) > ) ? ( & mut ext ) ;
2023-03-17 16:15:27 -06:00
Self ::with_state_and_middleware $( ::< ( $( $param ) , + ) > ) ? ( & mut ext , $( $( $options_id , ) * ) ? ) ;
2023-03-17 12:22:15 -06:00
Self ::with_customizer ( & mut ext ) ;
2023-03-17 16:15:27 -06:00
ext . take ( )
2023-03-17 12:22:15 -06:00
}
}
} ;
2023-03-17 16:15:27 -06:00
// This branch of the macro generates a config object that calls the state function with itself.
( ! __config__ $( parameters = [ $( $param :ident : $type :ident ) , + ] ) ? config = { $( $options_id :ident : $options_type :ty ) , * } ) = > {
2023-03-17 12:22:15 -06:00
{
#[ doc(hidden) ]
struct Config $( < $( $param : $type + Clone + 'static ) , + > ) ? {
2023-03-17 16:15:27 -06:00
$( pub $options_id : $options_type , ) *
2023-03-17 12:22:15 -06:00
$( __phantom_data : ::std ::marker ::PhantomData < ( $( $param ) , + ) > , ) ?
}
impl $( < $( $param : $type + Clone + 'static ) , + > ) ? Config $( < $( $param ) , + > ) ? {
2023-03-17 16:15:27 -06:00
/// Call a function of |state, cfg| using this configuration structure.
2023-03-17 12:22:15 -06:00
#[ allow(dead_code) ]
#[ doc(hidden) ]
#[ inline(always) ]
2023-03-17 16:15:27 -06:00
fn call_callback < F : Fn ( & mut $crate ::OpState , Self ) > ( self , state : & mut $crate ::OpState , f : F ) {
f ( state , self )
2023-03-17 12:22:15 -06:00
}
}
Config {
2023-03-17 16:15:27 -06:00
$( $options_id , ) *
2023-03-17 12:22:15 -06:00
$( __phantom_data : ::std ::marker ::PhantomData ::< ( $( $param ) , + ) > ::default ( ) ) ?
}
}
} ;
2023-03-17 16:15:27 -06:00
// This branch of the macro generates an empty config object that doesn't actually make any callbacks on the state function.
( ! __config__ $( parameters = [ $( $param :ident : $type :ident ) , + ] ) ? ) = > {
{
#[ doc(hidden) ]
struct Config {
}
impl Config {
/// Call a function of |state| using the fields of this configuration structure.
#[ allow(dead_code) ]
#[ doc(hidden) ]
#[ inline(always) ]
fn call_callback < F : Fn ( & mut $crate ::OpState ) > ( self , state : & mut $crate ::OpState , f : F ) {
f ( state )
}
}
Config { }
}
} ;
2023-03-17 12:22:15 -06:00
( ! __ops__ $ext :ident __eot__ ) = > {
} ;
( ! __ops__ $ext :ident $ops_symbol :ident __eot__ ) = > {
$ext . ops ( $ops_symbol ( ) )
} ;
( ! __ops__ $ext :ident $ops_symbol :ident < $ops_param :ident > __eot__ ) = > {
$ext . ops ( $ops_symbol ::< $ops_param > ( ) )
} ;
}
2021-04-28 18:41:50 +02:00
#[ derive(Default) ]
pub struct Extension {
2023-02-08 00:21:43 +01:00
js_files : Option < Vec < ExtensionFileSource > > ,
esm_files : Option < Vec < ExtensionFileSource > > ,
2023-02-14 00:43:53 +01:00
esm_entry_point : Option < & 'static str > ,
2022-07-22 19:06:32 +05:30
ops : Option < Vec < OpDecl > > ,
2021-04-28 18:41:50 +02:00
opstate_fn : Option < Box < OpStateFn > > ,
middleware_fn : Option < Box < OpMiddlewareFn > > ,
2022-03-08 20:10:34 +05:30
event_loop_middleware : Option < Box < OpEventLoopFn > > ,
2021-04-28 18:41:50 +02:00
initialized : bool ,
2022-03-22 16:39:58 +01:00
enabled : bool ,
2023-01-08 23:48:46 +01:00
name : & 'static str ,
2023-03-09 08:10:54 -04:00
deps : Option < & 'static [ & 'static str ] > ,
2021-04-28 18:41:50 +02:00
}
// Note: this used to be a trait, but we "downgraded" it to a single concrete type
// for the initial iteration, it will likely become a trait in the future
impl Extension {
2023-01-08 23:48:46 +01:00
pub fn builder ( name : & 'static str ) -> ExtensionBuilder {
ExtensionBuilder {
name ,
.. Default ::default ( )
}
}
2023-03-09 08:10:54 -04:00
pub fn builder_with_deps (
name : & 'static str ,
deps : & 'static [ & 'static str ] ,
) -> ExtensionBuilder {
ExtensionBuilder {
name ,
deps ,
.. Default ::default ( )
}
}
2023-01-08 23:48:46 +01:00
/// Check if dependencies have been loaded, and errors if either:
/// - The extension is depending on itself or an extension with the same name.
/// - A dependency hasn't been loaded yet.
2023-03-09 20:22:27 -04:00
pub fn check_dependencies ( & self , previous_exts : & [ Extension ] ) {
2023-03-09 08:10:54 -04:00
if let Some ( deps ) = self . deps {
2023-01-08 23:48:46 +01:00
' dep_loop : for dep in deps {
if dep = = & self . name {
panic! ( " Extension ' {} ' is either depending on itself or there is another extension with the same name " , self . name ) ;
}
for ext in previous_exts {
if dep = = & ext . name {
continue 'dep_loop ;
}
}
panic! ( " Extension ' {} ' is missing dependency ' {dep} ' " , self . name ) ;
}
}
2021-04-29 00:16:45 +02:00
}
2021-04-28 18:41:50 +02:00
/// returns JS source code to be loaded into the isolate (either at snapshotting,
/// or at startup). as a vector of a tuple of the file name, and the source code.
2023-03-09 20:22:27 -04:00
pub fn get_js_sources ( & self ) -> Option < & Vec < ExtensionFileSource > > {
self . js_files . as_ref ( )
2021-04-28 18:41:50 +02:00
}
2023-03-09 20:22:27 -04:00
pub fn get_esm_sources ( & self ) -> Option < & Vec < ExtensionFileSource > > {
self . esm_files . as_ref ( )
2023-02-07 20:22:46 +01:00
}
2023-02-14 00:43:53 +01:00
pub fn get_esm_entry_point ( & self ) -> Option < & 'static str > {
self . esm_entry_point
}
2021-04-28 18:41:50 +02:00
/// Called at JsRuntime startup to initialize ops in the isolate.
2022-03-15 23:43:17 +01:00
pub fn init_ops ( & mut self ) -> Option < Vec < OpDecl > > {
2021-04-28 18:41:50 +02:00
// TODO(@AaronO): maybe make op registration idempotent
if self . initialized {
panic! ( " init_ops called twice: not idempotent or correct " ) ;
}
self . initialized = true ;
2022-03-22 16:39:58 +01:00
let mut ops = self . ops . take ( ) ? ;
for op in ops . iter_mut ( ) {
op . enabled = self . enabled & & op . enabled ;
}
Some ( ops )
2021-04-28 18:41:50 +02:00
}
/// Allows setting up the initial op-state of an isolate at startup.
2023-03-17 16:15:27 -06:00
pub fn init_state ( & mut self , state : & mut OpState ) {
if let Some ( op_fn ) = self . opstate_fn . take ( ) {
2023-03-07 17:37:37 -04:00
op_fn ( state ) ;
2021-04-28 18:41:50 +02:00
}
}
/// init_middleware lets us middleware op registrations, it's called before init_ops
2021-05-07 15:45:07 +02:00
pub fn init_middleware ( & mut self ) -> Option < Box < OpMiddlewareFn > > {
2021-04-28 18:41:50 +02:00
self . middleware_fn . take ( )
}
2022-03-08 20:10:34 +05:30
pub fn init_event_loop_middleware ( & mut self ) -> Option < Box < OpEventLoopFn > > {
self . event_loop_middleware . take ( )
}
pub fn run_event_loop_middleware (
& self ,
2022-06-28 12:23:36 +03:00
op_state_rc : Rc < RefCell < OpState > > ,
2022-03-08 20:10:34 +05:30
cx : & mut Context ,
) -> bool {
self
. event_loop_middleware
. as_ref ( )
2022-06-28 12:23:36 +03:00
. map ( | f | f ( op_state_rc , cx ) )
2022-03-08 20:10:34 +05:30
. unwrap_or ( false )
}
2022-03-22 16:39:58 +01:00
pub fn enabled ( self , enabled : bool ) -> Self {
Self { enabled , .. self }
}
pub fn disable ( self ) -> Self {
self . enabled ( false )
}
2021-04-28 18:41:50 +02:00
}
2021-04-29 00:16:45 +02:00
// Provides a convenient builder pattern to declare Extensions
#[ derive(Default) ]
pub struct ExtensionBuilder {
2023-02-08 00:21:43 +01:00
js : Vec < ExtensionFileSource > ,
esm : Vec < ExtensionFileSource > ,
2023-02-14 00:43:53 +01:00
esm_entry_point : Option < & 'static str > ,
2022-03-15 23:43:17 +01:00
ops : Vec < OpDecl > ,
2021-04-29 00:16:45 +02:00
state : Option < Box < OpStateFn > > ,
middleware : Option < Box < OpMiddlewareFn > > ,
2022-03-08 20:10:34 +05:30
event_loop_middleware : Option < Box < OpEventLoopFn > > ,
2023-01-08 23:48:46 +01:00
name : & 'static str ,
2023-03-09 08:10:54 -04:00
deps : & 'static [ & 'static str ] ,
2021-04-29 00:16:45 +02:00
}
impl ExtensionBuilder {
2023-02-08 00:21:43 +01:00
pub fn js ( & mut self , js_files : Vec < ExtensionFileSource > ) -> & mut Self {
let js_files =
2023-02-20 01:11:56 +01:00
// TODO(bartlomieju): if we're automatically remapping here, then we should
// use a different result struct that `ExtensionFileSource` as it's confusing
// when (and why) the remapping happens.
2023-02-08 00:21:43 +01:00
js_files . into_iter ( ) . map ( | file_source | ExtensionFileSource {
2023-03-08 07:44:54 -04:00
specifier : format ! ( " ext:{}/{} " , self . name , file_source . specifier ) ,
2023-02-08 00:21:43 +01:00
code : file_source . code ,
} ) ;
2021-04-29 00:16:45 +02:00
self . js . extend ( js_files ) ;
self
}
2023-02-08 00:21:43 +01:00
pub fn esm ( & mut self , esm_files : Vec < ExtensionFileSource > ) -> & mut Self {
2023-02-20 01:11:56 +01:00
let esm_files = esm_files
. into_iter ( )
// TODO(bartlomieju): if we're automatically remapping here, then we should
// use a different result struct that `ExtensionFileSource` as it's confusing
// when (and why) the remapping happens.
. map ( | file_source | ExtensionFileSource {
2023-03-08 07:44:54 -04:00
specifier : format ! ( " ext:{}/{} " , self . name , file_source . specifier ) ,
2023-02-20 01:11:56 +01:00
code : file_source . code ,
} ) ;
2023-02-07 22:09:50 +01:00
self . esm . extend ( esm_files ) ;
2023-02-07 20:22:46 +01:00
self
}
2023-02-14 00:43:53 +01:00
pub fn esm_entry_point ( & mut self , entry_point : & 'static str ) -> & mut Self {
self . esm_entry_point = Some ( entry_point ) ;
self
}
2022-03-15 23:43:17 +01:00
pub fn ops ( & mut self , ops : Vec < OpDecl > ) -> & mut Self {
2021-04-29 00:16:45 +02:00
self . ops . extend ( ops ) ;
self
}
pub fn state < F > ( & mut self , opstate_fn : F ) -> & mut Self
where
2023-03-17 16:15:27 -06:00
F : FnOnce ( & mut OpState ) + 'static ,
2021-04-29 00:16:45 +02:00
{
self . state = Some ( Box ::new ( opstate_fn ) ) ;
self
}
pub fn middleware < F > ( & mut self , middleware_fn : F ) -> & mut Self
where
2022-03-15 23:43:17 +01:00
F : Fn ( OpDecl ) -> OpDecl + 'static ,
2021-04-29 00:16:45 +02:00
{
self . middleware = Some ( Box ::new ( middleware_fn ) ) ;
self
}
2022-03-08 20:10:34 +05:30
pub fn event_loop_middleware < F > ( & mut self , middleware_fn : F ) -> & mut Self
where
2022-06-28 12:23:36 +03:00
F : Fn ( Rc < RefCell < OpState > > , & mut Context ) -> bool + 'static ,
2022-03-08 20:10:34 +05:30
{
self . event_loop_middleware = Some ( Box ::new ( middleware_fn ) ) ;
self
}
2023-03-17 16:15:27 -06:00
/// Consume the [`ExtensionBuilder`] and return an [`Extension`].
pub fn take ( self ) -> Extension {
let js_files = Some ( self . js ) ;
let esm_files = Some ( self . esm ) ;
let ops = Some ( self . ops ) ;
let deps = Some ( self . deps ) ;
Extension {
js_files ,
esm_files ,
esm_entry_point : self . esm_entry_point ,
ops ,
opstate_fn : self . state ,
middleware_fn : self . middleware ,
event_loop_middleware : self . event_loop_middleware ,
initialized : false ,
enabled : true ,
name : self . name ,
deps ,
}
}
2021-04-29 00:16:45 +02:00
pub fn build ( & mut self ) -> Extension {
let js_files = Some ( std ::mem ::take ( & mut self . js ) ) ;
2023-02-07 20:22:46 +01:00
let esm_files = Some ( std ::mem ::take ( & mut self . esm ) ) ;
2021-04-29 00:16:45 +02:00
let ops = Some ( std ::mem ::take ( & mut self . ops ) ) ;
2023-01-08 23:48:46 +01:00
let deps = Some ( std ::mem ::take ( & mut self . deps ) ) ;
2021-04-29 00:16:45 +02:00
Extension {
js_files ,
2023-02-07 20:22:46 +01:00
esm_files ,
2023-02-14 00:43:53 +01:00
esm_entry_point : self . esm_entry_point . take ( ) ,
2021-04-29 00:16:45 +02:00
ops ,
opstate_fn : self . state . take ( ) ,
middleware_fn : self . middleware . take ( ) ,
2022-03-08 20:10:34 +05:30
event_loop_middleware : self . event_loop_middleware . take ( ) ,
2021-04-29 00:16:45 +02:00
initialized : false ,
2022-03-22 16:39:58 +01:00
enabled : true ,
2023-01-08 23:48:46 +01:00
name : self . name ,
deps ,
2021-04-29 00:16:45 +02:00
}
}
}
2023-02-08 22:40:18 +01:00
/// Helps embed JS files in an extension. Returns a vector of
2023-02-20 01:11:56 +01:00
/// `ExtensionFileSource`, that represent the filename and source code. All
2023-03-08 07:44:54 -04:00
/// specified files are rewritten into "ext:<extension_name>/<file_name>".
2021-04-28 18:41:50 +02:00
///
2023-02-20 01:11:56 +01:00
/// An optional "dir" option can be specified to prefix all files with a
/// directory name.
///
/// Example (for "my_extension"):
2021-04-28 18:41:50 +02:00
/// ```ignore
/// include_js_files!(
/// "01_hello.js",
/// "02_goodbye.js",
/// )
2023-02-20 01:11:56 +01:00
/// // Produces following specifiers:
2023-03-08 07:44:54 -04:00
/// - "ext:my_extension/01_hello.js"
/// - "ext:my_extension/02_goodbye.js"
2023-02-08 22:40:18 +01:00
///
2023-02-20 01:11:56 +01:00
/// /// Example with "dir" option (for "my_extension"):
2023-02-08 22:40:18 +01:00
/// ```ignore
2023-02-20 01:11:56 +01:00
/// include_js_files!(
/// dir "js",
2023-02-08 22:40:18 +01:00
/// "01_hello.js",
/// "02_goodbye.js",
/// )
2023-02-20 01:11:56 +01:00
/// // Produces following specifiers:
2023-03-08 07:44:54 -04:00
/// - "ext:my_extension/js/01_hello.js"
/// - "ext:my_extension/js/02_goodbye.js"
2023-02-08 22:40:18 +01:00
/// ```
2023-02-20 21:45:34 +01:00
#[ cfg(not(feature = " include_js_files_for_snapshotting " )) ]
2023-02-08 22:40:18 +01:00
#[ macro_export ]
2023-02-20 01:11:56 +01:00
macro_rules ! include_js_files {
2023-02-08 22:40:18 +01:00
( dir $dir :literal , $( $file :literal , ) + ) = > {
vec! [
$( $crate ::ExtensionFileSource {
refactor(core): include_js_files! 'dir' option doesn't change specifiers (#18019)
This commit changes "include_js_files!" macro from "deno_core"
in a way that "dir" option doesn't cause specifiers to be rewritten
to include it.
Example:
```
include_js_files! {
dir "js",
"hello.js",
}
```
The above definition required embedders to use:
`import ... from "internal:<ext_name>/js/hello.js"`.
But with this change, the "js" directory in which the files are stored
is an implementation detail, which for embedders results in:
`import ... from "internal:<ext_name>/hello.js"`.
The directory the files are stored in, is an implementation detail and
in some cases might result in a significant size difference for the
snapshot. As an example, in "deno_node" extension, we store the
source code in "polyfills" directory; which resulted in each specifier
to look like "internal:deno_node/polyfills/<module_name>", but with
this change it's "internal:deno_node/<module_name>".
Given that "deno_node" has over 100 files, many of them having
several import specifiers to the same extension, this change removes
10 characters from each import specifier.
2023-03-04 22:31:38 -04:00
specifier : concat ! ( $file ) . to_string ( ) ,
2023-02-20 01:11:56 +01:00
code : $crate ::ExtensionFileSourceCode ::IncludedInBinary (
include_str! ( concat! ( $dir , " / " , $file )
) ) ,
} , ) +
]
} ;
( $( $file :literal , ) + ) = > {
vec! [
$( $crate ::ExtensionFileSource {
specifier : $file . to_string ( ) ,
code : $crate ::ExtensionFileSourceCode ::IncludedInBinary (
include_str! ( $file )
) ,
2023-02-08 22:40:18 +01:00
} , ) +
]
} ;
}
2023-02-20 21:45:34 +01:00
#[ cfg(feature = " include_js_files_for_snapshotting " ) ]
#[ macro_export ]
macro_rules ! include_js_files {
( dir $dir :literal , $( $file :literal , ) + ) = > {
vec! [
$( $crate ::ExtensionFileSource {
refactor(core): include_js_files! 'dir' option doesn't change specifiers (#18019)
This commit changes "include_js_files!" macro from "deno_core"
in a way that "dir" option doesn't cause specifiers to be rewritten
to include it.
Example:
```
include_js_files! {
dir "js",
"hello.js",
}
```
The above definition required embedders to use:
`import ... from "internal:<ext_name>/js/hello.js"`.
But with this change, the "js" directory in which the files are stored
is an implementation detail, which for embedders results in:
`import ... from "internal:<ext_name>/hello.js"`.
The directory the files are stored in, is an implementation detail and
in some cases might result in a significant size difference for the
snapshot. As an example, in "deno_node" extension, we store the
source code in "polyfills" directory; which resulted in each specifier
to look like "internal:deno_node/polyfills/<module_name>", but with
this change it's "internal:deno_node/<module_name>".
Given that "deno_node" has over 100 files, many of them having
several import specifiers to the same extension, this change removes
10 characters from each import specifier.
2023-03-04 22:31:38 -04:00
specifier : concat ! ( $file ) . to_string ( ) ,
2023-02-20 21:45:34 +01:00
code : $crate ::ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot (
std ::path ::PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( $dir ) . join ( $file )
) ,
} , ) +
]
} ;
( $( $file :literal , ) + ) = > {
vec! [
$( $crate ::ExtensionFileSource {
specifier : $file . to_string ( ) ,
code : $crate ::ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot (
std ::path ::PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( $file )
) ,
} , ) +
]
} ;
}