├── .cargo └── config ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── bundler ├── Cargo.toml ├── README.md ├── examples │ └── bundle.rs ├── fixtures │ ├── 01_main.ts │ ├── 02_global.ts │ ├── 03_yoga_graphql.ts │ ├── base.ts │ └── yoga.ts ├── src │ ├── config.json │ ├── config.rs │ ├── hook.rs │ ├── lib.rs │ ├── loader.rs │ ├── minify.rs │ ├── options.rs │ ├── output.rs │ └── resolver.rs └── templates │ └── layout.j2 ├── deny.toml ├── fixtures └── testdata │ ├── 001_hello.js │ ├── 999_bundle.js │ ├── callable.ts │ ├── circular1.js │ ├── circular2.js │ ├── esm_imports_a.js │ ├── esm_imports_b.js │ └── server.js ├── runtime ├── Cargo.toml ├── README.md ├── build.rs ├── colors.rs ├── errors.rs ├── examples │ ├── base.js │ ├── hello_runtime.js │ └── hello_runtime.rs ├── fs_util.rs ├── inspector_server.rs ├── js.rs ├── lib.rs ├── ops │ ├── fs.rs │ ├── fs_events.rs │ ├── http.rs │ ├── io.rs │ ├── mod.rs │ ├── os.rs │ ├── permissions.rs │ ├── process.rs │ ├── runtime.rs │ ├── signal.rs │ ├── spawn.rs │ ├── tty.rs │ ├── utils.rs │ ├── web_worker.rs │ ├── web_worker │ │ └── sync_fetch.rs │ └── worker_host.rs ├── patch.rs ├── permissions.rs ├── tokio_util.rs ├── web_worker.rs ├── worker.rs └── worker_bootstrap.rs ├── snapshot ├── Cargo.toml ├── README.md ├── js │ ├── 01_build.js │ ├── 01_errors.js │ ├── 01_version.js │ ├── 01_web_util.js │ ├── 06_util.js │ ├── 10_permissions.js │ ├── 11_workers.js │ ├── 12_io.js │ ├── 13_buffer.js │ ├── 30_fs.js │ ├── 30_os.js │ ├── 40_diagnostics.js │ ├── 40_files.js │ ├── 40_fs_events.js │ ├── 40_http.js │ ├── 40_process.js │ ├── 40_read_file.js │ ├── 40_signals.js │ ├── 40_spawn.js │ ├── 40_testing.js │ ├── 40_tty.js │ ├── 40_write_file.js │ ├── 41_prompt.js │ ├── 90_deno_ns.js │ ├── 99_main.js │ └── README.md └── src │ ├── builder.rs │ ├── lib.rs │ └── permissions.rs ├── transpiler ├── Cargo.toml ├── README.md ├── fixtures │ ├── code.js │ └── code.ts └── src │ ├── compile.rs │ ├── lib.rs │ └── minify.rs └── utils ├── Cargo.toml ├── README.md └── src ├── compressible.rs ├── data_channel.rs ├── fs_util.rs ├── lib.rs ├── loader ├── mod.rs └── universal_loader.rs ├── store ├── fs_store.rs └── mod.rs ├── tokio_util.rs └── unstable_checker.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "./target" 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build-rust: 13 | strategy: 14 | matrix: 15 | platform: [ubuntu-latest] 16 | runs-on: ${{ matrix.platform }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Cache cargo registry 20 | uses: actions/cache@v1 21 | with: 22 | path: ~/.cargo/registry 23 | key: ${{ runner.os }}-cargo-registry 24 | - name: Cache cargo index 25 | uses: actions/cache@v1 26 | with: 27 | path: ~/.cargo/git 28 | key: ${{ runner.os }}-cargo-index 29 | - name: Cache cargo build 30 | uses: actions/cache@v1 31 | with: 32 | path: target 33 | key: ${{ runner.os }}-cargo-build-target 34 | - name: Install stable 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | profile: minimal 38 | toolchain: stable 39 | override: true 40 | - name: install nextest 41 | run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin 42 | - name: Check code format 43 | run: cargo fmt -- --check 44 | - name: Check the package for errors 45 | run: cargo check --all 46 | - name: Lint rust sources 47 | run: cargo clippy --all-targets --tests --benches -- -D warnings 48 | - name: Run tests 49 | run: cargo nextest run 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | */Cargo.lock 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: false 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v2.3.0 5 | hooks: 6 | - id: check-byte-order-marker 7 | - id: check-case-conflict 8 | - id: check-merge-conflict 9 | - id: check-symlinks 10 | - id: check-yaml 11 | - id: end-of-file-fixer 12 | - id: mixed-line-ending 13 | - id: trailing-whitespace 14 | - repo: https://github.com/psf/black 15 | rev: 19.3b0 16 | hooks: 17 | - id: black 18 | - repo: local 19 | hooks: 20 | - id: cargo-fmt 21 | name: cargo fmt 22 | description: Format files with rustfmt. 23 | entry: bash -c 'cargo fmt -- --check' 24 | language: rust 25 | files: \.rs$ 26 | args: [] 27 | # - id: cargo-deny 28 | # name: cargo deny check 29 | # description: Check cargo depencencies 30 | # entry: bash -c 'cargo deny check' 31 | # language: rust 32 | # files: \.rs$ 33 | # args: [] 34 | - id: cargo-check 35 | name: cargo check 36 | description: Check the package for errors. 37 | entry: bash -c 'cargo check --all' 38 | language: rust 39 | files: \.rs$ 40 | pass_filenames: false 41 | - id: cargo-clippy 42 | name: cargo clippy 43 | description: Lint rust sources 44 | entry: bash -c 'cargo clippy --all-targets --tests --benches -- -D warnings' 45 | language: rust 46 | files: \.rs$ 47 | pass_filenames: false 48 | - id: cargo-test 49 | name: cargo test 50 | description: unit test for the project 51 | entry: bash -c 'cargo nextest run' 52 | language: rust 53 | files: \.rs$ 54 | pass_filenames: false 55 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "bundler", 4 | "runtime", 5 | "snapshot", 6 | "transpiler", 7 | "utils", 8 | ] 9 | resolver = "2" 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright <2021> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deno utility functions 2 | 3 | Mainly provided: 4 | 5 | 1. universal module loader which could load modules from local file system, 6 | remote HTTP server, and data protocol. 7 | 2. typescript transpiler. 8 | 3. code bundler. 9 | 4. snapshot utility functions. 10 | -------------------------------------------------------------------------------- /bundler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno-bundler" 3 | version = "0.6.0" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/deno-bundler" 7 | repository = "https://github.com/tyrchen/deno-utils" 8 | homepage = "https://github.com/tyrchen/deno-utils" 9 | description = """ 10 | javascript/typescript bundler for deno. 11 | """ 12 | readme = "README.md" 13 | keywords = ["bundler", "deno", "v8"] 14 | categories = ["development-tools::build-utils"] 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | askama = "0.11.1" 20 | base64 = "0.13.0" 21 | deno_ast = { version = "0.17.0", features = ["bundler"] } 22 | deno_core = "0.147.0" 23 | deno_graph = "0.30.0" 24 | derive_builder = "0.11.2" 25 | futures = "0.3.23" 26 | serde = { version = "1.0.143", features = ["derive"] } 27 | serde_json = "1.0.83" 28 | swc_ecma_minifier = "0.136.1" 29 | 30 | deno-utils = { version = "0.7.0", path = "../utils", features = ["bundle", "transpile"] } 31 | 32 | 33 | [dev-dependencies] 34 | tokio = { version = "1.20.1", features = ["full"] } 35 | tracing-subscriber = "0.3.15" 36 | -------------------------------------------------------------------------------- /bundler/README.md: -------------------------------------------------------------------------------- 1 | # Deno bundler 2 | 3 | An easy to use bundler for Deno. 4 | -------------------------------------------------------------------------------- /bundler/examples/bundle.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use deno_bundler::{BundleOptionsBuilder, BundleType}; 4 | use deno_core::resolve_url_or_path; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | tracing_subscriber::fmt::init(); 9 | let options = BundleOptionsBuilder::default() 10 | .bundle_type(BundleType::Classic) 11 | .build() 12 | .unwrap(); 13 | let f = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures/03_yoga_graphql.ts"); 14 | let f = f.to_string_lossy().to_string(); 15 | let m = resolve_url_or_path(&f).unwrap(); 16 | let (bundle, _) = deno_bundler::bundle(m, options).await.unwrap(); 17 | 18 | println!("{}", bundle); 19 | } 20 | -------------------------------------------------------------------------------- /bundler/fixtures/01_main.ts: -------------------------------------------------------------------------------- 1 | import { serve } from 'https://cdn.jsdelivr.net/gh/denoland/deno_std@main/http/server.ts'; 2 | import { delay } from 'https://cdn.jsdelivr.net/gh/deno-delay/delay@main/src/delay.ts'; 3 | 4 | async function handler(req: Request): Promise { 5 | await delay(100); 6 | const body = `Your user-agent is: ${ 7 | req.headers.get('user-agent') ?? 'Unknown' 8 | }`; 9 | return new Response(body, { 10 | status: 200, 11 | }); 12 | } 13 | 14 | await serve(handler, { port: 8080 }); 15 | -------------------------------------------------------------------------------- /bundler/fixtures/02_global.ts: -------------------------------------------------------------------------------- 1 | import { hello } from './base.ts'; 2 | 3 | async function handler(name: string): Promise { 4 | return await hello(name); 5 | } 6 | 7 | export { handler }; 8 | -------------------------------------------------------------------------------- /bundler/fixtures/03_yoga_graphql.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from './yoga.ts'; 2 | 3 | const yoga = createServer({ 4 | schema: { 5 | typeDefs: /* GraphQL */ ` 6 | scalar File 7 | type Query { 8 | hello: String 9 | } 10 | type Mutation { 11 | getFileName(file: File!): String 12 | } 13 | type Subscription { 14 | countdown(from: Int!): Int! 15 | } 16 | `, 17 | resolvers: { 18 | Query: { 19 | hello: () => 'world', 20 | }, 21 | Mutation: { 22 | getFileName: (_root: any, { file }: { file: File }) => file.name, 23 | }, 24 | Subscription: { 25 | countdown: { 26 | async *subscribe(_: any, { from }: any) { 27 | for (let i = from; i >= 0; i--) { 28 | await new Promise((resolve) => setTimeout(resolve, 1000)); 29 | yield { countdown: i }; 30 | } 31 | }, 32 | }, 33 | }, 34 | }, 35 | }, 36 | logging: false, 37 | }); 38 | 39 | console.log(yoga); 40 | -------------------------------------------------------------------------------- /bundler/fixtures/base.ts: -------------------------------------------------------------------------------- 1 | async function hello(name: string): Promise { 2 | return `Hello ${name}`; 3 | } 4 | 5 | export { hello }; 6 | -------------------------------------------------------------------------------- /bundler/fixtures/yoga.ts: -------------------------------------------------------------------------------- 1 | export * from 'https://cdn.skypack.dev/-/@graphql-yoga/common@v2.8.0-AHugADxPCaAm4fjrSRdN/dist=es2019,mode=imports/optimized/@graphql-yoga/common.js'; 2 | export { default } from 'https://cdn.skypack.dev/-/@graphql-yoga/common@v2.8.0-AHugADxPCaAm4fjrSRdN/dist=es2019,mode=imports/optimized/@graphql-yoga/common.js'; 3 | 4 | // export * from 'https://cdn.skypack.dev/@graphql-yoga/common?dts'; 5 | -------------------------------------------------------------------------------- /bundler/src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mangle": { 3 | "toplevel": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /bundler/src/hook.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::swc::{ 2 | bundler::{Hook, ModuleRecord}, 3 | common::Span, 4 | }; 5 | use deno_core::error::AnyError; 6 | 7 | /// This contains the logic for Deno to rewrite the `import.meta` when bundling. 8 | pub struct BundleHook; 9 | 10 | impl Hook for BundleHook { 11 | fn get_import_meta_props( 12 | &self, 13 | span: Span, 14 | module_record: &ModuleRecord, 15 | ) -> Result, AnyError> { 16 | use deno_ast::swc::ast; 17 | 18 | Ok(vec![ 19 | ast::KeyValueProp { 20 | key: ast::PropName::Ident(ast::Ident::new("url".into(), span)), 21 | value: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { 22 | span, 23 | value: module_record.file_name.to_string().into(), 24 | raw: None, 25 | }))), 26 | }, 27 | ast::KeyValueProp { 28 | key: ast::PropName::Ident(ast::Ident::new("main".into(), span)), 29 | value: Box::new(if module_record.is_entry { 30 | ast::Expr::Member(ast::MemberExpr { 31 | span, 32 | obj: Box::new(ast::Expr::MetaProp(ast::MetaPropExpr { 33 | span, 34 | kind: ast::MetaPropKind::ImportMeta, 35 | })), 36 | prop: ast::MemberProp::Ident(ast::Ident::new("main".into(), span)), 37 | }) 38 | } else { 39 | ast::Expr::Lit(ast::Lit::Bool(ast::Bool { span, value: false })) 40 | }), 41 | }, 42 | ]) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bundler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod hook; 3 | mod loader; 4 | mod minify; 5 | mod options; 6 | mod output; 7 | mod resolver; 8 | 9 | use askama::Template; 10 | use config::TsConfig; 11 | use deno_ast::swc::{ 12 | self, 13 | bundler::Bundler, 14 | common::{FileName, FilePathMapping, Globals, SourceMap, GLOBALS}, 15 | }; 16 | use deno_core::{anyhow::Context, error::AnyError, ModuleSpecifier}; 17 | use deno_utils::{ModuleStore, UniversalModuleLoader}; 18 | use derive_builder::Builder; 19 | use hook::BundleHook; 20 | use loader::BundleLoader; 21 | use minify::minify; 22 | use output::gen_code; 23 | use resolver::BundleResolver; 24 | use std::{collections::HashMap, rc::Rc, sync::Arc}; 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 27 | pub enum BundleType { 28 | MainModule, 29 | /// Return the emitted contents of the program as a single "flattened" ES 30 | /// module. 31 | Module, 32 | /// Return the emitted contents of the program as a single script that 33 | /// executes the program using an immediately invoked function execution 34 | /// (IIFE). 35 | Classic, 36 | } 37 | 38 | #[derive(Builder, Clone)] 39 | #[builder(default, pattern = "owned")] 40 | pub struct BundleOptions { 41 | pub bundle_type: BundleType, 42 | pub ts_config: TsConfig, 43 | pub emit_ignore_directives: bool, 44 | pub module_store: Option>, 45 | pub minify: bool, 46 | } 47 | 48 | #[derive(Template)] 49 | #[template(path = "layout.j2", escape = "none")] 50 | struct BundledJs { 51 | body: String, 52 | bundle_type: BundleType, 53 | } 54 | 55 | /// Given a module graph, generate and return a bundle of the graph and 56 | /// optionally its source map. Unlike emitting with `check_and_maybe_emit` and 57 | /// `emit`, which store the emitted modules in the cache, this function simply 58 | /// returns the output. 59 | pub async fn bundle( 60 | // graph: &ModuleGraph, 61 | root: ModuleSpecifier, 62 | options: BundleOptions, 63 | ) -> Result<(String, Option), AnyError> { 64 | let mut loader = UniversalModuleLoader::new(options.module_store, false); 65 | let graph_owned = deno_graph::create_graph( 66 | vec![(root, deno_graph::ModuleKind::Esm)], 67 | false, 68 | None, 69 | &mut loader, 70 | None, 71 | None, 72 | None, 73 | None, 74 | ) 75 | .await; 76 | let graph = &graph_owned; 77 | 78 | let globals = Globals::new(); 79 | GLOBALS.set(&globals, || { 80 | let emit_options: deno_ast::EmitOptions = options.ts_config.into(); 81 | 82 | let cm = Rc::new(SourceMap::new(FilePathMapping::empty())); 83 | let loader = BundleLoader::new(cm.clone(), &emit_options, graph); 84 | let resolver = BundleResolver(graph); 85 | let config = swc::bundler::Config { 86 | module: options.bundle_type.into(), 87 | disable_fixer: options.minify, 88 | disable_hygiene: options.minify, 89 | ..Default::default() 90 | }; 91 | // This hook will rewrite the `import.meta` when bundling to give a consistent 92 | // behavior between bundled and unbundled code. 93 | let hook = Box::new(BundleHook); 94 | let mut bundler = Bundler::new(&globals, cm.clone(), loader, resolver, config, hook); 95 | let mut entries = HashMap::new(); 96 | entries.insert( 97 | "bundle".to_string(), 98 | FileName::Url(graph.roots[0].0.clone()), 99 | ); 100 | let mut modules = bundler 101 | .bundle(entries) 102 | .context("Unable to output during bundling.")?; 103 | 104 | if options.minify { 105 | modules = minify(cm.clone(), modules); 106 | } 107 | 108 | let (mut code, may_map) = gen_code( 109 | cm, 110 | &modules[0], 111 | &emit_options, 112 | options.emit_ignore_directives, 113 | options.minify, 114 | )?; 115 | 116 | let tpl = BundledJs { 117 | body: code, 118 | bundle_type: options.bundle_type, 119 | }; 120 | code = tpl.render()?; 121 | Ok((code, may_map)) 122 | }) 123 | } 124 | 125 | #[cfg(test)] 126 | mod tests { 127 | use std::path::Path; 128 | 129 | use deno_core::resolve_url_or_path; 130 | 131 | use super::*; 132 | 133 | #[tokio::test] 134 | async fn bundle_code_main_module_should_work() { 135 | let options = BundleOptions { 136 | bundle_type: BundleType::MainModule, 137 | ..BundleOptions::default() 138 | }; 139 | let f = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures/01_main.ts"); 140 | let f = f.to_string_lossy().to_string(); 141 | let m = resolve_url_or_path(&f).unwrap(); 142 | let ret = bundle(m.clone(), options.clone()).await; 143 | assert!(ret.is_ok()); 144 | } 145 | 146 | #[tokio::test] 147 | async fn bundle_code_module_should_work() { 148 | let options = BundleOptions::default(); 149 | let f = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures/01_main.ts"); 150 | let f = f.to_string_lossy().to_string(); 151 | let m = resolve_url_or_path(&f).unwrap(); 152 | let (_bundle, _) = bundle(m.clone(), options.clone()).await.unwrap(); 153 | let store = options.module_store.unwrap(); 154 | let ret = store.get(m.as_str()).await; 155 | assert!(ret.is_ok()); 156 | let imported = resolve_url_or_path( 157 | "https://cdn.jsdelivr.net/gh/denoland/deno_std@main/http/server.ts", 158 | ) 159 | .unwrap(); 160 | let _ret = store.get(imported.as_str()).await.unwrap(); 161 | } 162 | 163 | #[tokio::test] 164 | async fn bundle_code_classic_should_work() { 165 | let options = BundleOptions { 166 | bundle_type: BundleType::Classic, 167 | ..BundleOptions::default() 168 | }; 169 | let f = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures/02_global.ts"); 170 | let f = f.to_string_lossy().to_string(); 171 | let m = resolve_url_or_path(&f).unwrap(); 172 | let ret = bundle(m.clone(), options.clone()).await; 173 | assert!(ret.is_ok()); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /bundler/src/loader.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::{ 2 | get_syntax, 3 | swc::{ 4 | self, 5 | common::{comments::SingleThreadedComments, FileName, Mark, SourceMap, Spanned}, 6 | parser::{error::Error as SwcError, lexer::Lexer, StringInput}, 7 | }, 8 | Diagnostic, LineAndColumnDisplay, MediaType, SourceRangedForSpanned, 9 | }; 10 | 11 | use deno_core::{anyhow::anyhow, error::AnyError, ModuleSpecifier}; 12 | use deno_graph::ModuleGraph; 13 | use std::rc::Rc; 14 | 15 | /// A module loader for swc which does the appropriate retrieval and transpiling 16 | /// of modules from the graph. 17 | pub struct BundleLoader<'a> { 18 | cm: Rc, 19 | emit_options: &'a deno_ast::EmitOptions, 20 | graph: &'a ModuleGraph, 21 | } 22 | 23 | impl<'a> BundleLoader<'a> { 24 | pub fn new( 25 | cm: Rc, 26 | emit_options: &'a deno_ast::EmitOptions, 27 | graph: &'a ModuleGraph, 28 | ) -> Self { 29 | Self { 30 | cm, 31 | emit_options, 32 | graph, 33 | } 34 | } 35 | } 36 | 37 | impl swc::bundler::Load for BundleLoader<'_> { 38 | fn load( 39 | &self, 40 | file_name: &swc::common::FileName, 41 | ) -> Result { 42 | match file_name { 43 | swc::common::FileName::Url(specifier) => { 44 | if let Some(m) = self.graph.get(specifier) { 45 | let (fm, module) = transpile_module( 46 | specifier, 47 | m.maybe_source.as_ref().map(|s| s.as_ref()).unwrap_or(""), 48 | m.media_type, 49 | self.emit_options, 50 | self.cm.clone(), 51 | )?; 52 | Ok(swc::bundler::ModuleData { 53 | fm, 54 | module, 55 | helpers: Default::default(), 56 | }) 57 | } else { 58 | Err(anyhow!( 59 | "Module \"{}\" unexpectedly missing when bundling.", 60 | specifier 61 | )) 62 | } 63 | } 64 | _ => unreachable!( 65 | "Received a request for unsupported filename {:?}", 66 | file_name 67 | ), 68 | } 69 | } 70 | } 71 | 72 | /// Transpiles a source module into an swc SourceFile. 73 | fn transpile_module( 74 | specifier: &ModuleSpecifier, 75 | source: &str, 76 | media_type: MediaType, 77 | options: &deno_ast::EmitOptions, 78 | cm: Rc, 79 | ) -> Result<(Rc, swc::ast::Module), AnyError> { 80 | let source = strip_bom(source); 81 | let source = if media_type == MediaType::Json { 82 | format!( 83 | "export default JSON.parse(`{}`);", 84 | source.replace("${", "\\${").replace('`', "\\`") 85 | ) 86 | } else { 87 | source.to_string() 88 | }; 89 | let source_file = cm.new_source_file(FileName::Url(specifier.clone()), source); 90 | let input = StringInput::from(&*source_file); 91 | let comments = SingleThreadedComments::default(); 92 | let syntax = if media_type == MediaType::Json { 93 | get_syntax(MediaType::JavaScript) 94 | } else { 95 | get_syntax(media_type) 96 | }; 97 | let lexer = Lexer::new(syntax, deno_ast::ES_VERSION, input, Some(&comments)); 98 | let mut parser = swc::parser::Parser::new_from(lexer); 99 | let module = parser 100 | .parse_module() 101 | .map_err(|e| swc_err_to_diagnostic(&cm, specifier, e))?; 102 | let diagnostics = parser 103 | .take_errors() 104 | .into_iter() 105 | .map(|e| swc_err_to_diagnostic(&cm, specifier, e)) 106 | .collect::>(); 107 | 108 | let top_level_mark = Mark::fresh(Mark::root()); 109 | let program = deno_ast::fold_program( 110 | swc::ast::Program::Module(module), 111 | options, 112 | cm, 113 | &comments, 114 | top_level_mark, 115 | &diagnostics, 116 | )?; 117 | let module = match program { 118 | swc::ast::Program::Module(module) => module, 119 | _ => unreachable!(), 120 | }; 121 | 122 | Ok((source_file, module)) 123 | } 124 | 125 | const BOM_CHAR: char = '\u{FEFF}'; 126 | /// Strips the byte order mark from the provided text if it exists. 127 | fn strip_bom(text: &str) -> &str { 128 | if text.starts_with(BOM_CHAR) { 129 | &text[BOM_CHAR.len_utf8()..] 130 | } else { 131 | text 132 | } 133 | } 134 | 135 | fn swc_err_to_diagnostic( 136 | source_map: &SourceMap, 137 | specifier: &ModuleSpecifier, 138 | err: SwcError, 139 | ) -> Diagnostic { 140 | let location = source_map.lookup_char_pos(err.span().lo); 141 | Diagnostic { 142 | specifier: specifier.to_string(), 143 | range: err.range(), 144 | display_position: LineAndColumnDisplay { 145 | line_number: location.line, 146 | column_number: location.col_display + 1, 147 | }, 148 | kind: err.into_kind(), 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /bundler/src/minify.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::swc; 2 | use swc::{ 3 | bundler::Bundle, 4 | common::{sync::Lrc, Mark, SourceMap}, 5 | transforms::fixer, 6 | visit::VisitMutWith, 7 | }; 8 | use swc_ecma_minifier::{ 9 | optimize, 10 | option::{ExtraOptions, MinifyOptions}, 11 | }; 12 | 13 | const MINIFY_CONFIG: &str = include_str!("config.json"); 14 | 15 | pub fn minify(cm: Lrc, modules: Vec) -> Vec { 16 | let options: MinifyOptions = serde_json::from_str(MINIFY_CONFIG).unwrap(); 17 | modules 18 | .into_iter() 19 | .map(|mut b| { 20 | b.module = optimize( 21 | b.module.into(), 22 | cm.clone(), 23 | None, 24 | None, 25 | &options, 26 | &ExtraOptions { 27 | unresolved_mark: Mark::fresh(Mark::root()), 28 | top_level_mark: Mark::fresh(Mark::root()), 29 | }, 30 | ) 31 | .expect_module(); 32 | b.module.visit_mut_with(&mut fixer(None)); 33 | b 34 | }) 35 | .collect() 36 | } 37 | -------------------------------------------------------------------------------- /bundler/src/options.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use deno_ast::swc; 4 | use deno_utils::FsModuleStore; 5 | 6 | use crate::{ 7 | config::{get_ts_config, ConfigType}, 8 | BundleOptions, BundleType, 9 | }; 10 | 11 | impl Default for BundleType { 12 | fn default() -> Self { 13 | BundleType::Module 14 | } 15 | } 16 | 17 | impl From for swc::bundler::ModuleType { 18 | fn from(bundle_type: BundleType) -> Self { 19 | match bundle_type { 20 | BundleType::Classic => Self::Iife, 21 | BundleType::Module => Self::Es, 22 | BundleType::MainModule => Self::Es, 23 | } 24 | } 25 | } 26 | 27 | impl Default for BundleOptions { 28 | fn default() -> Self { 29 | Self { 30 | bundle_type: BundleType::Module, 31 | ts_config: get_ts_config(ConfigType::Bundle).unwrap(), 32 | emit_ignore_directives: false, 33 | module_store: Some(Arc::new(FsModuleStore::default())), 34 | minify: true, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bundler/src/output.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::{ 2 | swc::{self, bundler::Bundle, common::sync::Lrc, common::SourceMap}, 3 | EmitOptions, 4 | }; 5 | use deno_core::{anyhow::Context, error::AnyError}; 6 | 7 | const IGNORE_DIRECTIVES: &[&str] = &[ 8 | "// deno-fmt-ignore-file", 9 | "// deno-lint-ignore-file", 10 | "// This code was bundled using `deno-bundler` and it's not recommended to edit it manually", 11 | "", 12 | ]; 13 | 14 | pub fn gen_code( 15 | cm: Lrc, 16 | bundle: &Bundle, 17 | emit_options: &EmitOptions, 18 | ignore_directive: bool, 19 | minify: bool, 20 | ) -> Result<(String, Option), AnyError> { 21 | let source_map_config = deno_ast::SourceMapConfig { 22 | inline_sources: emit_options.inline_sources, 23 | }; 24 | let mut buf = Vec::new(); 25 | let mut srcmap = Vec::new(); 26 | { 27 | let cfg = swc::codegen::Config { 28 | minify, 29 | ..Default::default() 30 | }; 31 | let mut wr = Box::new(swc::codegen::text_writer::JsWriter::new( 32 | cm.clone(), 33 | "\n", 34 | &mut buf, 35 | Some(&mut srcmap), 36 | )); 37 | 38 | if ignore_directive { 39 | // write leading comments in bundled file 40 | use swc::codegen::text_writer::WriteJs; 41 | let cmt = IGNORE_DIRECTIVES.join("\n") + "\n"; 42 | wr.write_comment(&cmt)?; 43 | } 44 | 45 | let mut emitter = swc::codegen::Emitter { 46 | cfg, 47 | cm: cm.clone(), 48 | comments: None, 49 | wr, 50 | }; 51 | emitter 52 | .emit_module(&bundle.module) 53 | .context("Unable to emit during bundling.")?; 54 | } 55 | let mut code = String::from_utf8(buf).context("Emitted code is an invalid string.")?; 56 | 57 | let mut maybe_map: Option = None; 58 | if emit_options.source_map || emit_options.inline_source_map { 59 | let mut buf = Vec::new(); 60 | cm.build_source_map_with_config(&mut srcmap, None, source_map_config) 61 | .to_writer(&mut buf)?; 62 | if emit_options.inline_source_map { 63 | let encoded_map = format!( 64 | "//# sourceMappingURL=data:application/json;base64,{}\n", 65 | base64::encode(buf) 66 | ); 67 | code.push_str(&encoded_map); 68 | } else if emit_options.source_map { 69 | maybe_map = Some(String::from_utf8(buf)?); 70 | } 71 | } 72 | 73 | Ok((code, maybe_map)) 74 | } 75 | -------------------------------------------------------------------------------- /bundler/src/resolver.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::swc; 2 | use deno_core::{anyhow::anyhow, error::AnyError}; 3 | use deno_graph::ModuleGraph; 4 | 5 | /// A resolver implementation for swc that resolves specifiers from the graph. 6 | pub struct BundleResolver<'a>(pub &'a ModuleGraph); 7 | 8 | impl swc::bundler::Resolve for BundleResolver<'_> { 9 | fn resolve( 10 | &self, 11 | referrer: &swc::common::FileName, 12 | specifier: &str, 13 | ) -> Result { 14 | let referrer = if let swc::common::FileName::Url(referrer) = referrer { 15 | referrer 16 | } else { 17 | unreachable!( 18 | "An unexpected referrer was passed when bundling: {:?}", 19 | referrer 20 | ); 21 | }; 22 | if let Some(specifier) = self.0.resolve_dependency(specifier, referrer, false) { 23 | Ok(deno_ast::swc::common::FileName::Url(specifier.clone())) 24 | } else { 25 | Err(anyhow!( 26 | "Cannot resolve \"{}\" from \"{}\".", 27 | specifier, 28 | referrer 29 | )) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bundler/templates/layout.j2: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | {% if bundle_type == BundleType::MainModule %} 3 | ((window) => { 4 | async function mainModule() { 5 | {{ body }} 6 | } 7 | window.mainModule = mainModule; 8 | })(globalThis); 9 | {% else if bundle_type == BundleType::Classic %} 10 | globalThis.mainModule = {{ body }} 11 | {% else %} 12 | {{ body }} 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /fixtures/testdata/001_hello.js: -------------------------------------------------------------------------------- 1 | console.log('Hello World'); 2 | let resp = await fetch('https://jsonplaceholder.typicode.com/todos/1'); 3 | let json = await resp.json(); 4 | console.log(resp); 5 | -------------------------------------------------------------------------------- /fixtures/testdata/callable.ts: -------------------------------------------------------------------------------- 1 | function process(data: string): string { 2 | return data; 3 | } 4 | 5 | async function async_process(url: string): Promise { 6 | const data = await fetch(url); 7 | return data.status; 8 | } 9 | 10 | globalThis.process = process; 11 | globalThis.async_process = async_process; 12 | -------------------------------------------------------------------------------- /fixtures/testdata/circular1.js: -------------------------------------------------------------------------------- 1 | import "./circular2.js"; 2 | console.log("circular1"); 3 | -------------------------------------------------------------------------------- /fixtures/testdata/circular2.js: -------------------------------------------------------------------------------- 1 | import "./circular1.js"; 2 | console.log("circular2"); 3 | -------------------------------------------------------------------------------- /fixtures/testdata/esm_imports_a.js: -------------------------------------------------------------------------------- 1 | import { retb } from "./esm_imports_b.js"; 2 | 3 | if (retb() != "b") throw Error(); 4 | -------------------------------------------------------------------------------- /fixtures/testdata/esm_imports_b.js: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore-file 2 | export function retb() { 3 | return "b"; 4 | } 5 | -------------------------------------------------------------------------------- /fixtures/testdata/server.js: -------------------------------------------------------------------------------- 1 | const server = Deno.listen({ port: 8080 }); 2 | // console.log(`HTTP webserver running. Access it at: http://localhost:8080/`); 3 | 4 | for await (const conn of server) { 5 | // console.log(`Connection from ${JSON.stringify(conn.remoteAddr)}`); 6 | serveHttp(conn); 7 | } 8 | 9 | async function serveHttp(conn) { 10 | const httpConn = Deno.serveHttp(conn); 11 | for await (const requestEvent of httpConn) { 12 | const body = `Your user-agent is:\n\n${ 13 | requestEvent.request.headers.get('user-agent') ?? 'Unknown' 14 | }`; 15 | 16 | await requestEvent.respondWith( 17 | new Response(body, { 18 | status: 200, 19 | }) 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | [package] 4 | name = "deno_simple_runtime" 5 | version = "0.73.2" 6 | authors = ["the Deno authors"] 7 | edition = "2021" 8 | license = "MIT" 9 | repository = "https://github.com/denoland/deno" 10 | description = "Provides the deno runtime library" 11 | 12 | [features] 13 | # "fake" feature that allows to generate docs on docs.rs 14 | docsrs = [] 15 | ext_webgpu = ["deno_webgpu"] 16 | 17 | 18 | [lib] 19 | name = "deno_simple_runtime" 20 | path = "lib.rs" 21 | 22 | [[example]] 23 | name = "hello_runtime" 24 | path = "examples/hello_runtime.rs" 25 | 26 | [build-dependencies] 27 | deno-snapshot = { version = "0.6.1", features = ["build"], path = "../snapshot" } 28 | 29 | [target.'cfg(windows)'.build-dependencies] 30 | winres = "0.1.12" 31 | winapi = "0.3.9" 32 | 33 | [dependencies] 34 | derive_builder = "0.11.2" 35 | 36 | deno_broadcast_channel = "0.59.0" 37 | deno_console = "0.65.0" 38 | deno_core = "0.147.0" 39 | deno_crypto = "0.79.0" 40 | deno_fetch = "0.88.0" 41 | deno_http = "0.59.0" 42 | deno_net = "0.57.0" 43 | deno_node = " 0.2.0" 44 | deno_tls = "0.52.0" 45 | deno_url = "0.65.0" 46 | deno_web = "0.96.0" 47 | deno_webgpu = { version = "0.66.0", optional = true } 48 | deno_webidl = "0.65.0" 49 | deno_websocket = "0.70.0" 50 | deno_webstorage = "0.60.0" 51 | 52 | atty = "0.2.14" 53 | # dlopen = { version = "0.1.8", optional = true } 54 | encoding_rs = "0.8.31" 55 | filetime = "0.2.17" 56 | fs3 = "0.5.0" 57 | http = "0.2.8" 58 | hyper = { version = "0.14.20", features = ["server", "stream", "http1", "http2", "runtime"] } 59 | libc = "0.2.132" 60 | log = "0.4.17" 61 | netif = "0.1.3" 62 | notify = "=5.0.0-pre.15" 63 | once_cell = "1.13.1" 64 | regex = "1.6.0" 65 | ring = "0.16.20" 66 | serde = { version = "1.0.143", features = ["derive"] } 67 | signal-hook-registry = "1.4.0" 68 | sys-info = "0.9.1" 69 | termcolor = "1.1.3" 70 | tokio = { version = "1.20.1", features = ["full"] } 71 | uuid = { version = "1.1.2", features = ["v4"] } 72 | 73 | deno-snapshot = { version = "0.6.1", path = "../snapshot" } 74 | 75 | [target.'cfg(windows)'.dependencies] 76 | fwdansi = "1.1.0" 77 | winapi = { version = "0.3.9", features = ["commapi", "knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2","winuser"] } 78 | 79 | [target.'cfg(unix)'.dependencies] 80 | nix = "0.25.0" 81 | 82 | [package.metadata.docs.rs] 83 | features = ["docsrs"] 84 | -------------------------------------------------------------------------------- /runtime/README.md: -------------------------------------------------------------------------------- 1 | # `deno_runtime` crate 2 | 3 | [![crates](https://img.shields.io/crates/v/deno_runtime.svg)](https://crates.io/crates/deno_runtime) 4 | [![docs](https://docs.rs/deno_runtime/badge.svg)](https://docs.rs/deno_runtime) 5 | 6 | This is a slim version of the Deno CLI which removes typescript integration and 7 | various tooling (like lint and doc). Basically only JavaScript execution with 8 | Deno's operating system bindings (ops). 9 | 10 | ## Stability 11 | 12 | This crate is built using battle-tested modules that were originally in `deno` 13 | crate, however the API of this crate is subject to rapid and breaking changes. 14 | 15 | ## `MainWorker` 16 | 17 | The main API of this crate is `MainWorker`. `MainWorker` is a structure 18 | encapsulating `deno_core::JsRuntime` with a set of ops used to implement `Deno` 19 | namespace. 20 | 21 | When creating a `MainWorker` implementors must call `MainWorker::bootstrap` to 22 | prepare JS runtime for use. 23 | 24 | `MainWorker` is highly configurable and allows to customize many of the 25 | runtime's properties: 26 | 27 | - module loading implementation 28 | - error formatting 29 | - support for source maps 30 | - support for V8 inspector and Chrome Devtools debugger 31 | - HTTP client user agent, CA certificate 32 | - random number generator seed 33 | 34 | ## `Worker` Web API 35 | 36 | `deno_runtime` comes with support for `Worker` Web API. The `Worker` API is 37 | implemented using `WebWorker` structure. 38 | 39 | When creating a new instance of `MainWorker` implementors must provide a 40 | callback function that is used when creating a new instance of `Worker`. 41 | 42 | All `WebWorker` instances are descendents of `MainWorker` which is responsible 43 | for setting up communication with child worker. Each `WebWorker` spawns a new OS 44 | thread that is dedicated solely to that worker. 45 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use std::env; 4 | #[allow(unused_imports)] 5 | use std::path::Path; 6 | use std::path::PathBuf; 7 | 8 | // This is a shim that allows to generate documentation on docs.rs 9 | #[cfg(not(feature = "docsrs"))] 10 | mod not_docs { 11 | use deno_snapshot::create_snapshot; 12 | use std::path::PathBuf; 13 | 14 | pub fn build_snapshot(filename: PathBuf) { 15 | let data = create_snapshot(vec![], &[]).unwrap(); 16 | std::fs::write(filename, data).unwrap(); 17 | } 18 | } 19 | 20 | fn main() { 21 | // To debug snapshot issues uncomment: 22 | // op_fetch_asset::trace_serializer(); 23 | 24 | println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); 25 | println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); 26 | let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 27 | 28 | // Main snapshot 29 | let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin"); 30 | 31 | // If we're building on docs.rs we just create 32 | // and empty snapshot file and return, because `rusty_v8` 33 | // doesn't actually compile on docs.rs 34 | if env::var_os("DOCS_RS").is_some() { 35 | let snapshot_slice = &[]; 36 | std::fs::write(&runtime_snapshot_path, snapshot_slice).unwrap(); 37 | } 38 | 39 | #[cfg(not(feature = "docsrs"))] 40 | not_docs::build_snapshot(runtime_snapshot_path) 41 | } 42 | -------------------------------------------------------------------------------- /runtime/colors.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use atty; 4 | use once_cell::sync::Lazy; 5 | use std::fmt; 6 | use std::io::Write; 7 | use termcolor::Color::{Ansi256, Black, Blue, Cyan, Green, Red, White, Yellow}; 8 | use termcolor::{Ansi, ColorSpec, WriteColor}; 9 | 10 | #[cfg(windows)] 11 | use termcolor::{BufferWriter, ColorChoice}; 12 | 13 | static NO_COLOR: Lazy = Lazy::new(|| std::env::var_os("NO_COLOR").is_some()); 14 | 15 | static IS_TTY: Lazy = Lazy::new(|| atty::is(atty::Stream::Stdout)); 16 | 17 | pub fn is_tty() -> bool { 18 | *IS_TTY 19 | } 20 | 21 | pub fn use_color() -> bool { 22 | !(*NO_COLOR) 23 | } 24 | 25 | #[cfg(windows)] 26 | pub fn enable_ansi() { 27 | BufferWriter::stdout(ColorChoice::AlwaysAnsi); 28 | } 29 | 30 | fn style>(s: S, colorspec: ColorSpec) -> impl fmt::Display { 31 | if !use_color() { 32 | return String::from(s.as_ref()); 33 | } 34 | let mut v = Vec::new(); 35 | let mut ansi_writer = Ansi::new(&mut v); 36 | ansi_writer.set_color(&colorspec).unwrap(); 37 | ansi_writer.write_all(s.as_ref().as_bytes()).unwrap(); 38 | ansi_writer.reset().unwrap(); 39 | String::from_utf8_lossy(&v).into_owned() 40 | } 41 | 42 | pub fn red_bold>(s: S) -> impl fmt::Display { 43 | let mut style_spec = ColorSpec::new(); 44 | style_spec.set_fg(Some(Red)).set_bold(true); 45 | style(s, style_spec) 46 | } 47 | 48 | pub fn green_bold>(s: S) -> impl fmt::Display { 49 | let mut style_spec = ColorSpec::new(); 50 | style_spec.set_fg(Some(Green)).set_bold(true); 51 | style(s, style_spec) 52 | } 53 | 54 | pub fn italic>(s: S) -> impl fmt::Display { 55 | let mut style_spec = ColorSpec::new(); 56 | style_spec.set_italic(true); 57 | style(s, style_spec) 58 | } 59 | 60 | pub fn italic_gray>(s: S) -> impl fmt::Display { 61 | let mut style_spec = ColorSpec::new(); 62 | style_spec.set_fg(Some(Ansi256(8))).set_italic(true); 63 | style(s, style_spec) 64 | } 65 | 66 | pub fn italic_bold>(s: S) -> impl fmt::Display { 67 | let mut style_spec = ColorSpec::new(); 68 | style_spec.set_bold(true).set_italic(true); 69 | style(s, style_spec) 70 | } 71 | 72 | pub fn white_on_red>(s: S) -> impl fmt::Display { 73 | let mut style_spec = ColorSpec::new(); 74 | style_spec.set_bg(Some(Red)).set_fg(Some(White)); 75 | style(s, style_spec) 76 | } 77 | 78 | pub fn black_on_green>(s: S) -> impl fmt::Display { 79 | let mut style_spec = ColorSpec::new(); 80 | style_spec.set_bg(Some(Green)).set_fg(Some(Black)); 81 | style(s, style_spec) 82 | } 83 | 84 | pub fn yellow>(s: S) -> impl fmt::Display { 85 | let mut style_spec = ColorSpec::new(); 86 | style_spec.set_fg(Some(Yellow)); 87 | style(s, style_spec) 88 | } 89 | 90 | pub fn cyan>(s: S) -> impl fmt::Display { 91 | let mut style_spec = ColorSpec::new(); 92 | style_spec.set_fg(Some(Cyan)); 93 | style(s, style_spec) 94 | } 95 | 96 | pub fn red>(s: S) -> impl fmt::Display { 97 | let mut style_spec = ColorSpec::new(); 98 | style_spec.set_fg(Some(Red)); 99 | style(s, style_spec) 100 | } 101 | 102 | pub fn green>(s: S) -> impl fmt::Display { 103 | let mut style_spec = ColorSpec::new(); 104 | style_spec.set_fg(Some(Green)); 105 | style(s, style_spec) 106 | } 107 | 108 | pub fn bold>(s: S) -> impl fmt::Display { 109 | let mut style_spec = ColorSpec::new(); 110 | style_spec.set_bold(true); 111 | style(s, style_spec) 112 | } 113 | 114 | pub fn gray>(s: S) -> impl fmt::Display { 115 | let mut style_spec = ColorSpec::new(); 116 | style_spec.set_fg(Some(Ansi256(245))); 117 | style(s, style_spec) 118 | } 119 | 120 | pub fn intense_blue>(s: S) -> impl fmt::Display { 121 | let mut style_spec = ColorSpec::new(); 122 | style_spec.set_fg(Some(Blue)).set_intense(true); 123 | style(s, style_spec) 124 | } 125 | 126 | pub fn white_bold_on_red>(s: S) -> impl fmt::Display { 127 | let mut style_spec = ColorSpec::new(); 128 | style_spec 129 | .set_bold(true) 130 | .set_bg(Some(Red)) 131 | .set_fg(Some(White)); 132 | style(s, style_spec) 133 | } 134 | -------------------------------------------------------------------------------- /runtime/errors.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | //! There are many types of errors in Deno: 4 | //! - AnyError: a generic wrapper that can encapsulate any type of error. 5 | //! - JsError: a container for the error message and stack trace for exceptions 6 | //! thrown in JavaScript code. We use this to pretty-print stack traces. 7 | //! - Diagnostic: these are errors that originate in TypeScript's compiler. 8 | //! They're similar to JsError, in that they have line numbers. But 9 | //! Diagnostics are compile-time type errors, whereas JsErrors are runtime 10 | //! exceptions. 11 | 12 | use deno_core::error::AnyError; 13 | use deno_core::serde_json; 14 | use deno_core::url; 15 | use deno_core::ModuleResolutionError; 16 | use deno_fetch::reqwest; 17 | use std::env; 18 | use std::error::Error; 19 | use std::io; 20 | use std::sync::Arc; 21 | 22 | #[cfg(feature = "ext_ffi")] 23 | fn get_dlopen_error_class(error: &dlopen::Error) -> &'static str { 24 | use dlopen::Error::*; 25 | match error { 26 | NullCharacter(_) => "InvalidData", 27 | OpeningLibraryError(ref e) => get_io_error_class(e), 28 | SymbolGettingError(ref e) => get_io_error_class(e), 29 | AddrNotMatchingDll(ref e) => get_io_error_class(e), 30 | NullSymbol => "NotFound", 31 | } 32 | } 33 | 34 | fn get_env_var_error_class(error: &env::VarError) -> &'static str { 35 | use env::VarError::*; 36 | match error { 37 | NotPresent => "NotFound", 38 | NotUnicode(..) => "InvalidData", 39 | } 40 | } 41 | 42 | fn get_io_error_class(error: &io::Error) -> &'static str { 43 | use io::ErrorKind::*; 44 | match error.kind() { 45 | NotFound => "NotFound", 46 | PermissionDenied => "PermissionDenied", 47 | ConnectionRefused => "ConnectionRefused", 48 | ConnectionReset => "ConnectionReset", 49 | ConnectionAborted => "ConnectionAborted", 50 | NotConnected => "NotConnected", 51 | AddrInUse => "AddrInUse", 52 | AddrNotAvailable => "AddrNotAvailable", 53 | BrokenPipe => "BrokenPipe", 54 | AlreadyExists => "AlreadyExists", 55 | InvalidInput => "TypeError", 56 | InvalidData => "InvalidData", 57 | TimedOut => "TimedOut", 58 | Interrupted => "Interrupted", 59 | WriteZero => "WriteZero", 60 | UnexpectedEof => "UnexpectedEof", 61 | Other => "Error", 62 | WouldBlock => unreachable!(), 63 | // Non-exhaustive enum - might add new variants 64 | // in the future 65 | _ => "Error", 66 | } 67 | } 68 | 69 | fn get_module_resolution_error_class(_: &ModuleResolutionError) -> &'static str { 70 | "URIError" 71 | } 72 | 73 | fn get_notify_error_class(error: ¬ify::Error) -> &'static str { 74 | use notify::ErrorKind::*; 75 | match error.kind { 76 | Generic(_) => "Error", 77 | Io(ref e) => get_io_error_class(e), 78 | PathNotFound => "NotFound", 79 | WatchNotFound => "NotFound", 80 | InvalidConfig(_) => "InvalidData", 81 | MaxFilesWatch => "Error", 82 | } 83 | } 84 | 85 | fn get_regex_error_class(error: ®ex::Error) -> &'static str { 86 | use regex::Error::*; 87 | match error { 88 | Syntax(_) => "SyntaxError", 89 | CompiledTooBig(_) => "RangeError", 90 | _ => "Error", 91 | } 92 | } 93 | 94 | fn get_request_error_class(error: &reqwest::Error) -> &'static str { 95 | error 96 | .source() 97 | .and_then(|inner_err| { 98 | (inner_err 99 | .downcast_ref::() 100 | .map(get_io_error_class)) 101 | .or_else(|| { 102 | inner_err 103 | .downcast_ref::() 104 | .map(get_serde_json_error_class) 105 | }) 106 | .or_else(|| { 107 | inner_err 108 | .downcast_ref::() 109 | .map(get_url_parse_error_class) 110 | }) 111 | }) 112 | .unwrap_or("Http") 113 | } 114 | 115 | fn get_serde_json_error_class(error: &serde_json::error::Error) -> &'static str { 116 | use deno_core::serde_json::error::*; 117 | match error.classify() { 118 | Category::Io => error 119 | .source() 120 | .and_then(|e| e.downcast_ref::()) 121 | .map(get_io_error_class) 122 | .unwrap(), 123 | Category::Syntax => "SyntaxError", 124 | Category::Data => "InvalidData", 125 | Category::Eof => "UnexpectedEof", 126 | } 127 | } 128 | 129 | fn get_url_parse_error_class(_error: &url::ParseError) -> &'static str { 130 | "URIError" 131 | } 132 | 133 | fn get_hyper_error_class(_error: &hyper::Error) -> &'static str { 134 | "Http" 135 | } 136 | 137 | #[cfg(unix)] 138 | pub fn get_nix_error_class(error: &nix::Error) -> &'static str { 139 | match error { 140 | nix::Error::ECHILD => "NotFound", 141 | nix::Error::EINVAL => "TypeError", 142 | nix::Error::ENOENT => "NotFound", 143 | nix::Error::ENOTTY => "BadResource", 144 | nix::Error::EPERM => "PermissionDenied", 145 | nix::Error::ESRCH => "NotFound", 146 | nix::Error::UnknownErrno => "Error", 147 | &nix::Error::ENOTSUP => unreachable!(), 148 | _ => "Error", 149 | } 150 | } 151 | 152 | pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { 153 | deno_core::error::get_custom_error_class(e) 154 | .or_else(|| deno_webgpu_error_class_name(e)) 155 | .or_else(|| deno_web::get_error_class_name(e)) 156 | .or_else(|| deno_webstorage::get_not_supported_error_class_name(e)) 157 | .or_else(|| deno_websocket::get_network_error_class_name(e)) 158 | // .or_else(|| { 159 | // e.downcast_ref::() 160 | // .map(get_dlopen_error_class) 161 | // }) 162 | .or_else(|| e.downcast_ref::().map(get_hyper_error_class)) 163 | .or_else(|| { 164 | e.downcast_ref::>() 165 | .map(|e| get_hyper_error_class(&**e)) 166 | }) 167 | .or_else(|| { 168 | e.downcast_ref::().map(|e| { 169 | let io_err: io::Error = e.to_owned().into(); 170 | get_io_error_class(&io_err) 171 | }) 172 | }) 173 | .or_else(|| { 174 | e.downcast_ref::() 175 | .map(get_env_var_error_class) 176 | }) 177 | .or_else(|| e.downcast_ref::().map(get_io_error_class)) 178 | .or_else(|| { 179 | e.downcast_ref::() 180 | .map(get_module_resolution_error_class) 181 | }) 182 | .or_else(|| { 183 | e.downcast_ref::() 184 | .map(get_notify_error_class) 185 | }) 186 | .or_else(|| { 187 | e.downcast_ref::() 188 | .map(get_request_error_class) 189 | }) 190 | .or_else(|| e.downcast_ref::().map(get_regex_error_class)) 191 | .or_else(|| { 192 | e.downcast_ref::() 193 | .map(get_serde_json_error_class) 194 | }) 195 | .or_else(|| { 196 | e.downcast_ref::() 197 | .map(get_url_parse_error_class) 198 | }) 199 | .or_else(|| { 200 | #[cfg(unix)] 201 | let maybe_get_nix_error_class = 202 | || e.downcast_ref::().map(get_nix_error_class); 203 | #[cfg(not(unix))] 204 | let maybe_get_nix_error_class = || Option::<&'static str>::None; 205 | (maybe_get_nix_error_class)() 206 | }) 207 | } 208 | 209 | #[cfg(feature = "ext_webgpu")] 210 | fn deno_webgpu_error_class_name(e: &AnyError) -> Option<&'static str> { 211 | deno_webgpu::error::get_error_class_name(e) 212 | } 213 | 214 | #[cfg(not(feature = "ext_webgpu"))] 215 | fn deno_webgpu_error_class_name(_e: &AnyError) -> Option<&'static str> { 216 | None 217 | } 218 | -------------------------------------------------------------------------------- /runtime/examples/base.js: -------------------------------------------------------------------------------- 1 | function hello(name) { 2 | return `Hello ${name}`; 3 | } 4 | 5 | export { hello }; 6 | -------------------------------------------------------------------------------- /runtime/examples/hello_runtime.js: -------------------------------------------------------------------------------- 1 | import { hello } from './base.js'; 2 | 3 | console.log(hello('Tyr')); 4 | -------------------------------------------------------------------------------- /runtime/examples/hello_runtime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use deno_core::error::AnyError; 4 | use deno_simple_runtime::permissions::Permissions; 5 | use deno_simple_runtime::worker::MainWorker; 6 | use deno_simple_runtime::WorkerOptionsBuilder; 7 | use std::path::Path; 8 | 9 | fn get_error_class_name(e: &AnyError) -> &'static str { 10 | deno_simple_runtime::errors::get_error_class_name(e).unwrap_or("Error") 11 | } 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<(), AnyError> { 15 | let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/hello_runtime.js"); 16 | let permissions = Permissions::allow_all(); 17 | 18 | let options = WorkerOptionsBuilder::default() 19 | .main_module(Some(&js_path.to_string_lossy())) 20 | .permissions(permissions) 21 | .get_error_class_fn(Some(&get_error_class_name)) 22 | .build() 23 | .unwrap(); 24 | 25 | let mut worker = MainWorker::bootstrap_from_options(options, vec![]); 26 | worker.execute_main_module().await?; 27 | worker.run_event_loop(false).await?; 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /runtime/fs_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use deno_core::anyhow::Context; 4 | use deno_core::error::AnyError; 5 | pub use deno_core::normalize_path; 6 | use std::env::current_dir; 7 | use std::io::Error; 8 | use std::path::{Path, PathBuf}; 9 | 10 | /// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows. 11 | pub fn canonicalize_path(path: &Path) -> Result { 12 | let mut canonicalized_path = path.canonicalize()?; 13 | if cfg!(windows) { 14 | canonicalized_path = PathBuf::from( 15 | canonicalized_path 16 | .display() 17 | .to_string() 18 | .trim_start_matches("\\\\?\\"), 19 | ); 20 | } 21 | Ok(canonicalized_path) 22 | } 23 | 24 | pub fn resolve_from_cwd(path: &Path) -> Result { 25 | let resolved_path = if path.is_absolute() { 26 | path.to_owned() 27 | } else { 28 | let cwd = current_dir().context("Failed to get current working directory")?; 29 | cwd.join(path) 30 | }; 31 | 32 | Ok(normalize_path(&resolved_path)) 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn resolve_from_cwd_child() { 41 | let cwd = current_dir().unwrap(); 42 | assert_eq!(resolve_from_cwd(Path::new("a")).unwrap(), cwd.join("a")); 43 | } 44 | 45 | #[test] 46 | fn resolve_from_cwd_dot() { 47 | let cwd = current_dir().unwrap(); 48 | assert_eq!(resolve_from_cwd(Path::new(".")).unwrap(), cwd); 49 | } 50 | 51 | #[test] 52 | fn resolve_from_cwd_parent() { 53 | let cwd = current_dir().unwrap(); 54 | assert_eq!(resolve_from_cwd(Path::new("a/..")).unwrap(), cwd); 55 | } 56 | 57 | #[test] 58 | fn test_normalize_path() { 59 | assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b")); 60 | assert_eq!(normalize_path(Path::new("a/./b/")), PathBuf::from("a/b/")); 61 | assert_eq!( 62 | normalize_path(Path::new("a/./b/../c")), 63 | PathBuf::from("a/c") 64 | ); 65 | 66 | if cfg!(windows) { 67 | assert_eq!( 68 | normalize_path(Path::new("C:\\a\\.\\b\\..\\c")), 69 | PathBuf::from("C:\\a\\c") 70 | ); 71 | } 72 | } 73 | 74 | // TODO: Get a good expected value here for Windows. 75 | #[cfg(not(windows))] 76 | #[test] 77 | fn resolve_from_cwd_absolute() { 78 | let expected = Path::new("/a"); 79 | assert_eq!(resolve_from_cwd(expected).unwrap(), expected); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /runtime/js.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | use deno_core::Snapshot; 3 | use log::debug; 4 | use once_cell::sync::Lazy; 5 | 6 | pub static CLI_SNAPSHOT: Lazy> = Lazy::new( 7 | #[allow(clippy::uninit_vec)] 8 | #[cold] 9 | #[inline(never)] 10 | || { 11 | static COMPRESSED_CLI_SNAPSHOT: &[u8] = 12 | include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin")); 13 | 14 | deno_snapshot::decode(COMPRESSED_CLI_SNAPSHOT) 15 | }, 16 | ); 17 | 18 | pub fn deno_isolate_init() -> Snapshot { 19 | debug!("Deno isolate init with snapshots."); 20 | Snapshot::Static(&*CLI_SNAPSHOT) 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn cli_snapshot() { 29 | let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions { 30 | startup_snapshot: Some(deno_isolate_init()), 31 | ..Default::default() 32 | }); 33 | js_runtime 34 | .execute_script( 35 | "", 36 | r#" 37 | if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) { 38 | throw Error("bad"); 39 | } 40 | console.log("we have console.log!!!"); 41 | "#, 42 | ) 43 | .unwrap(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /runtime/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | #![allow(clippy::derive_partial_eq_without_eq)] 3 | 4 | pub use deno_broadcast_channel; 5 | pub use deno_console; 6 | pub use deno_core; 7 | pub use deno_crypto; 8 | pub use deno_fetch; 9 | pub use deno_http; 10 | pub use deno_net; 11 | pub use deno_node; 12 | pub use deno_tls; 13 | pub use deno_url; 14 | pub use deno_web; 15 | #[cfg(feature = "ext_webgpu")] 16 | pub use deno_webgpu; 17 | pub use deno_webidl; 18 | pub use deno_websocket; 19 | pub use deno_webstorage; 20 | 21 | pub mod colors; 22 | pub mod errors; 23 | pub mod fs_util; 24 | pub mod inspector_server; 25 | pub mod js; 26 | pub mod ops; 27 | pub mod permissions; 28 | pub mod tokio_util; 29 | pub mod web_worker; 30 | pub mod worker; 31 | 32 | mod patch; 33 | mod worker_bootstrap; 34 | pub use worker_bootstrap::BootstrapOptions; 35 | 36 | pub use patch::StartSnapshot; 37 | pub use worker::{MainWorker, WorkerOptions, WorkerOptionsBuilder, WorkerOptionsBuilderError}; 38 | 39 | #[cfg(test)] 40 | pub mod test_util { 41 | use crate::{permissions::Permissions, MainWorker, WorkerOptionsBuilder}; 42 | use std::path::PathBuf; 43 | 44 | pub fn testdata_path(name: &str) -> String { 45 | let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 46 | let path = path.join(format!("../fixtures/testdata/{}", name)); 47 | path.to_string_lossy().into() 48 | } 49 | 50 | pub fn create_test_worker(file: impl AsRef) -> MainWorker { 51 | let options = WorkerOptionsBuilder::default() 52 | .main_module(Some(file.as_ref())) 53 | .permissions(Permissions::allow_all()) 54 | .build() 55 | .unwrap(); 56 | 57 | MainWorker::bootstrap_from_options(options, vec![]) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /runtime/ops/fs_events.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::permissions::Permissions; 4 | use deno_core::error::AnyError; 5 | use deno_core::parking_lot::Mutex; 6 | use deno_core::AsyncRefCell; 7 | use deno_core::CancelFuture; 8 | use deno_core::CancelHandle; 9 | use deno_core::OpState; 10 | use deno_core::RcRef; 11 | use deno_core::Resource; 12 | use deno_core::ResourceId; 13 | 14 | use deno_core::op; 15 | 16 | use deno_core::Extension; 17 | use notify::event::Event as NotifyEvent; 18 | use notify::Error as NotifyError; 19 | use notify::EventKind; 20 | use notify::RecommendedWatcher; 21 | use notify::RecursiveMode; 22 | use notify::Watcher; 23 | use serde::Deserialize; 24 | use serde::Serialize; 25 | use std::borrow::Cow; 26 | use std::cell::RefCell; 27 | use std::convert::From; 28 | use std::path::PathBuf; 29 | use std::rc::Rc; 30 | use tokio::sync::mpsc; 31 | 32 | pub fn init() -> Extension { 33 | Extension::builder() 34 | .ops(vec![op_fs_events_open::decl(), op_fs_events_poll::decl()]) 35 | .build() 36 | } 37 | 38 | struct FsEventsResource { 39 | #[allow(unused)] 40 | watcher: RecommendedWatcher, 41 | receiver: AsyncRefCell>>, 42 | cancel: CancelHandle, 43 | } 44 | 45 | impl Resource for FsEventsResource { 46 | fn name(&self) -> Cow { 47 | "fsEvents".into() 48 | } 49 | 50 | fn close(self: Rc) { 51 | self.cancel.cancel(); 52 | } 53 | } 54 | 55 | /// Represents a file system event. 56 | /// 57 | /// We do not use the event directly from the notify crate. We flatten 58 | /// the structure into this simpler structure. We want to only make it more 59 | /// complex as needed. 60 | /// 61 | /// Feel free to expand this struct as long as you can add tests to demonstrate 62 | /// the complexity. 63 | #[derive(Serialize, Debug)] 64 | struct FsEvent { 65 | kind: &'static str, 66 | paths: Vec, 67 | flag: Option<&'static str>, 68 | } 69 | 70 | impl From for FsEvent { 71 | fn from(e: NotifyEvent) -> Self { 72 | let kind = match e.kind { 73 | EventKind::Any => "any", 74 | EventKind::Access(_) => "access", 75 | EventKind::Create(_) => "create", 76 | EventKind::Modify(_) => "modify", 77 | EventKind::Remove(_) => "remove", 78 | EventKind::Other => "other", 79 | }; 80 | let flag = e.flag().map(|f| match f { 81 | notify::event::Flag::Rescan => "rescan", 82 | }); 83 | FsEvent { 84 | kind, 85 | paths: e.paths, 86 | flag, 87 | } 88 | } 89 | } 90 | 91 | #[derive(Deserialize)] 92 | pub struct OpenArgs { 93 | recursive: bool, 94 | paths: Vec, 95 | } 96 | 97 | #[op] 98 | fn op_fs_events_open(state: &mut OpState, args: OpenArgs) -> Result { 99 | let (sender, receiver) = mpsc::channel::>(16); 100 | let sender = Mutex::new(sender); 101 | let mut watcher: RecommendedWatcher = 102 | Watcher::new(move |res: Result| { 103 | let res2 = res.map(FsEvent::from).map_err(AnyError::from); 104 | let sender = sender.lock(); 105 | // Ignore result, if send failed it means that watcher was already closed, 106 | // but not all messages have been flushed. 107 | let _ = sender.try_send(res2); 108 | })?; 109 | let recursive_mode = if args.recursive { 110 | RecursiveMode::Recursive 111 | } else { 112 | RecursiveMode::NonRecursive 113 | }; 114 | for path in &args.paths { 115 | let path = PathBuf::from(path); 116 | state.borrow_mut::().read.check(&path)?; 117 | watcher.watch(&path, recursive_mode)?; 118 | } 119 | let resource = FsEventsResource { 120 | watcher, 121 | receiver: AsyncRefCell::new(receiver), 122 | cancel: Default::default(), 123 | }; 124 | let rid = state.resource_table.add(resource); 125 | Ok(rid) 126 | } 127 | 128 | #[op] 129 | async fn op_fs_events_poll( 130 | state: Rc>, 131 | rid: ResourceId, 132 | ) -> Result, AnyError> { 133 | let resource = state.borrow().resource_table.get::(rid)?; 134 | let mut receiver = RcRef::map(&resource, |r| &r.receiver).borrow_mut().await; 135 | let cancel = RcRef::map(resource, |r| &r.cancel); 136 | let maybe_result = receiver.recv().or_cancel(cancel).await?; 137 | match maybe_result { 138 | Some(Ok(value)) => Ok(Some(value)), 139 | Some(Err(err)) => Err(err), 140 | None => Ok(None), 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /runtime/ops/http.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use deno_core::error::bad_resource_id; 5 | use deno_core::error::custom_error; 6 | use deno_core::error::AnyError; 7 | use deno_core::op; 8 | use deno_core::Extension; 9 | use deno_core::OpState; 10 | use deno_core::RcRef; 11 | use deno_core::ResourceId; 12 | use deno_core::ZeroCopyBuf; 13 | use deno_http::http_create_conn_resource; 14 | use deno_http::HttpRequestReader; 15 | use deno_http::HttpStreamResource; 16 | use deno_net::io::TcpStreamResource; 17 | use deno_net::ops_tls::TlsStream; 18 | use deno_net::ops_tls::TlsStreamResource; 19 | use hyper::upgrade::Parts; 20 | use serde::Serialize; 21 | use tokio::net::TcpStream; 22 | 23 | #[cfg(unix)] 24 | use deno_net::io::UnixStreamResource; 25 | #[cfg(unix)] 26 | use tokio::net::UnixStream; 27 | 28 | pub fn init() -> Extension { 29 | Extension::builder() 30 | .ops(vec![op_http_start::decl(), op_http_upgrade::decl()]) 31 | .build() 32 | } 33 | 34 | #[op] 35 | fn op_http_start(state: &mut OpState, tcp_stream_rid: ResourceId) -> Result { 36 | if let Ok(resource_rc) = state 37 | .resource_table 38 | .take::(tcp_stream_rid) 39 | { 40 | let resource = 41 | Rc::try_unwrap(resource_rc).expect("Only a single use of this resource should happen"); 42 | let (read_half, write_half) = resource.into_inner(); 43 | let tcp_stream = read_half.reunite(write_half)?; 44 | let addr = tcp_stream.local_addr()?; 45 | return http_create_conn_resource(state, tcp_stream, addr, "http"); 46 | } 47 | 48 | if let Ok(resource_rc) = state 49 | .resource_table 50 | .take::(tcp_stream_rid) 51 | { 52 | let resource = 53 | Rc::try_unwrap(resource_rc).expect("Only a single use of this resource should happen"); 54 | let (read_half, write_half) = resource.into_inner(); 55 | let tls_stream = read_half.reunite(write_half); 56 | let addr = tls_stream.get_ref().0.local_addr()?; 57 | return http_create_conn_resource(state, tls_stream, addr, "https"); 58 | } 59 | 60 | #[cfg(unix)] 61 | if let Ok(resource_rc) = state 62 | .resource_table 63 | .take::(tcp_stream_rid) 64 | { 65 | super::check_unstable(state, "Deno.serveHttp"); 66 | 67 | let resource = 68 | Rc::try_unwrap(resource_rc).expect("Only a single use of this resource should happen"); 69 | let (read_half, write_half) = resource.into_inner(); 70 | let unix_stream = read_half.reunite(write_half)?; 71 | let addr = unix_stream.local_addr()?; 72 | return http_create_conn_resource(state, unix_stream, addr, "http+unix"); 73 | } 74 | 75 | Err(bad_resource_id()) 76 | } 77 | 78 | #[derive(Serialize)] 79 | #[serde(rename_all = "camelCase")] 80 | pub struct HttpUpgradeResult { 81 | conn_rid: ResourceId, 82 | conn_type: &'static str, 83 | read_buf: ZeroCopyBuf, 84 | } 85 | 86 | #[op] 87 | async fn op_http_upgrade( 88 | state: Rc>, 89 | rid: ResourceId, 90 | _: (), 91 | ) -> Result { 92 | let stream = state 93 | .borrow_mut() 94 | .resource_table 95 | .get::(rid)?; 96 | let mut rd = RcRef::map(&stream, |r| &r.rd).borrow_mut().await; 97 | 98 | let request = match &mut *rd { 99 | HttpRequestReader::Headers(request) => request, 100 | _ => { 101 | return Err(custom_error( 102 | "Http", 103 | "cannot upgrade because request body was used", 104 | )) 105 | } 106 | }; 107 | 108 | let transport = hyper::upgrade::on(request).await?; 109 | let transport = match transport.downcast::() { 110 | Ok(Parts { 111 | io: tcp_stream, 112 | read_buf, 113 | .. 114 | }) => { 115 | return Ok(HttpUpgradeResult { 116 | conn_type: "tcp", 117 | conn_rid: state 118 | .borrow_mut() 119 | .resource_table 120 | .add(TcpStreamResource::new(tcp_stream.into_split())), 121 | read_buf: read_buf.to_vec().into(), 122 | }); 123 | } 124 | Err(transport) => transport, 125 | }; 126 | #[cfg(unix)] 127 | let transport = match transport.downcast::() { 128 | Ok(Parts { 129 | io: unix_stream, 130 | read_buf, 131 | .. 132 | }) => { 133 | return Ok(HttpUpgradeResult { 134 | conn_type: "unix", 135 | conn_rid: state 136 | .borrow_mut() 137 | .resource_table 138 | .add(UnixStreamResource::new(unix_stream.into_split())), 139 | read_buf: read_buf.to_vec().into(), 140 | }); 141 | } 142 | Err(transport) => transport, 143 | }; 144 | match transport.downcast::() { 145 | Ok(Parts { 146 | io: tls_stream, 147 | read_buf, 148 | .. 149 | }) => Ok(HttpUpgradeResult { 150 | conn_type: "tls", 151 | conn_rid: state 152 | .borrow_mut() 153 | .resource_table 154 | .add(TlsStreamResource::new(tls_stream.into_split())), 155 | read_buf: read_buf.to_vec().into(), 156 | }), 157 | Err(_) => Err(custom_error( 158 | "Http", 159 | "encountered unsupported transport while upgrading", 160 | )), 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /runtime/ops/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub mod fs; 4 | pub mod fs_events; 5 | pub mod http; 6 | pub mod io; 7 | pub mod os; 8 | pub mod permissions; 9 | pub mod process; 10 | pub mod runtime; 11 | pub mod signal; 12 | pub mod spawn; 13 | pub mod tty; 14 | mod utils; 15 | pub mod web_worker; 16 | pub mod worker_host; 17 | 18 | use deno_core::OpState; 19 | use std::cell::RefCell; 20 | use std::rc::Rc; 21 | 22 | /// `UnstableChecker` is a struct so it can be placed inside `GothamState`; 23 | /// using type alias for a bool could work, but there's a high chance 24 | /// that there might be another type alias pointing to a bool, which 25 | /// would override previously used alias. 26 | pub struct UnstableChecker { 27 | pub unstable: bool, 28 | } 29 | 30 | impl UnstableChecker { 31 | /// Quits the process if the --unstable flag was not provided. 32 | /// 33 | /// This is intentionally a non-recoverable check so that people cannot probe 34 | /// for unstable APIs from stable programs. 35 | // NOTE(bartlomieju): keep in sync with `cli/program_state.rs` 36 | pub fn check_unstable(&self, api_name: &str) { 37 | if !self.unstable { 38 | eprintln!( 39 | "Unstable API '{}'. The --unstable flag must be provided.", 40 | api_name 41 | ); 42 | std::process::exit(70); 43 | } 44 | } 45 | } 46 | /// Helper for checking unstable features. Used for sync ops. 47 | pub fn check_unstable(state: &OpState, api_name: &str) { 48 | state.borrow::().check_unstable(api_name) 49 | } 50 | 51 | /// Helper for checking unstable features. Used for async ops. 52 | pub fn check_unstable2(state: &Rc>, api_name: &str) { 53 | let state = state.borrow(); 54 | state.borrow::().check_unstable(api_name) 55 | } 56 | 57 | pub struct TestingFeaturesEnabled(pub bool); 58 | -------------------------------------------------------------------------------- /runtime/ops/permissions.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::permissions::Permissions; 4 | use deno_core::error::custom_error; 5 | use deno_core::error::uri_error; 6 | use deno_core::error::AnyError; 7 | use deno_core::op; 8 | use deno_core::url; 9 | use deno_core::Extension; 10 | use deno_core::OpState; 11 | use serde::Deserialize; 12 | use std::path::Path; 13 | 14 | pub fn init() -> Extension { 15 | Extension::builder() 16 | .ops(vec![ 17 | op_query_permission::decl(), 18 | op_revoke_permission::decl(), 19 | op_request_permission::decl(), 20 | ]) 21 | .build() 22 | } 23 | 24 | #[derive(Deserialize)] 25 | pub struct PermissionArgs { 26 | name: String, 27 | path: Option, 28 | host: Option, 29 | variable: Option, 30 | command: Option, 31 | } 32 | 33 | #[op] 34 | pub fn op_query_permission(state: &mut OpState, args: PermissionArgs) -> Result { 35 | let permissions = state.borrow::(); 36 | let path = args.path.as_deref(); 37 | let perm = match args.name.as_ref() { 38 | "read" => permissions.read.query(path.map(Path::new)), 39 | "write" => permissions.write.query(path.map(Path::new)), 40 | "net" => permissions.net.query( 41 | match args.host.as_deref() { 42 | None => None, 43 | Some(h) => Some(parse_host(h)?), 44 | } 45 | .as_ref(), 46 | ), 47 | "env" => permissions.env.query(args.variable.as_deref()), 48 | "run" => permissions.run.query(args.command.as_deref()), 49 | "ffi" => permissions.ffi.query(args.path.as_deref().map(Path::new)), 50 | "hrtime" => permissions.hrtime.query(), 51 | n => { 52 | return Err(custom_error( 53 | "ReferenceError", 54 | format!("No such permission name: {}", n), 55 | )) 56 | } 57 | }; 58 | Ok(perm.to_string()) 59 | } 60 | 61 | #[op] 62 | pub fn op_revoke_permission(state: &mut OpState, args: PermissionArgs) -> Result { 63 | let permissions = state.borrow_mut::(); 64 | let path = args.path.as_deref(); 65 | let perm = match args.name.as_ref() { 66 | "read" => permissions.read.revoke(path.map(Path::new)), 67 | "write" => permissions.write.revoke(path.map(Path::new)), 68 | "net" => permissions.net.revoke( 69 | match args.host.as_deref() { 70 | None => None, 71 | Some(h) => Some(parse_host(h)?), 72 | } 73 | .as_ref(), 74 | ), 75 | "env" => permissions.env.revoke(args.variable.as_deref()), 76 | "run" => permissions.run.revoke(args.command.as_deref()), 77 | "ffi" => permissions.ffi.revoke(args.path.as_deref().map(Path::new)), 78 | "hrtime" => permissions.hrtime.revoke(), 79 | n => { 80 | return Err(custom_error( 81 | "ReferenceError", 82 | format!("No such permission name: {}", n), 83 | )) 84 | } 85 | }; 86 | Ok(perm.to_string()) 87 | } 88 | 89 | #[op] 90 | pub fn op_request_permission( 91 | state: &mut OpState, 92 | args: PermissionArgs, 93 | ) -> Result { 94 | let permissions = state.borrow_mut::(); 95 | let path = args.path.as_deref(); 96 | let perm = match args.name.as_ref() { 97 | "read" => permissions.read.request(path.map(Path::new)), 98 | "write" => permissions.write.request(path.map(Path::new)), 99 | "net" => permissions.net.request( 100 | match args.host.as_deref() { 101 | None => None, 102 | Some(h) => Some(parse_host(h)?), 103 | } 104 | .as_ref(), 105 | ), 106 | "env" => permissions.env.request(args.variable.as_deref()), 107 | "run" => permissions.run.request(args.command.as_deref()), 108 | "ffi" => permissions.ffi.request(args.path.as_deref().map(Path::new)), 109 | "hrtime" => permissions.hrtime.request(), 110 | n => { 111 | return Err(custom_error( 112 | "ReferenceError", 113 | format!("No such permission name: {}", n), 114 | )) 115 | } 116 | }; 117 | Ok(perm.to_string()) 118 | } 119 | 120 | fn parse_host(host_str: &str) -> Result<(String, Option), AnyError> { 121 | let url = 122 | url::Url::parse(&format!("http://{}/", host_str)).map_err(|_| uri_error("Invalid host"))?; 123 | if url.path() != "/" { 124 | return Err(uri_error("Invalid host")); 125 | } 126 | let hostname = url.host_str().unwrap(); 127 | Ok((hostname.to_string(), url.port())) 128 | } 129 | -------------------------------------------------------------------------------- /runtime/ops/runtime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::permissions::Permissions; 4 | use deno_core::anyhow::Context; 5 | use deno_core::error::AnyError; 6 | use deno_core::op; 7 | use deno_core::Extension; 8 | use deno_core::ModuleSpecifier; 9 | use deno_core::OpState; 10 | 11 | pub fn init(main_module: ModuleSpecifier) -> Extension { 12 | Extension::builder() 13 | .ops(vec![op_main_module::decl()]) 14 | .state(move |state| { 15 | state.put::(main_module.clone()); 16 | Ok(()) 17 | }) 18 | .build() 19 | } 20 | 21 | #[op] 22 | fn op_main_module(state: &mut OpState) -> Result { 23 | let main = state.borrow::().to_string(); 24 | let main_url = deno_core::resolve_url_or_path(&main)?; 25 | if main_url.scheme() == "file" { 26 | let main_path = std::env::current_dir() 27 | .context("Failed to get current working directory")? 28 | .join(main_url.to_string()); 29 | state 30 | .borrow_mut::() 31 | .read 32 | .check_blind(&main_path, "main_module")?; 33 | } 34 | Ok(main) 35 | } 36 | 37 | pub fn ppid() -> i64 { 38 | #[cfg(windows)] 39 | { 40 | // Adopted from rustup: 41 | // https://github.com/rust-lang/rustup/blob/1.21.1/src/cli/self_update.rs#L1036 42 | // Copyright Diggory Blake, the Mozilla Corporation, and rustup contributors. 43 | // Licensed under either of 44 | // - Apache License, Version 2.0 45 | // - MIT license 46 | use std::mem; 47 | use winapi::shared::minwindef::DWORD; 48 | use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; 49 | use winapi::um::processthreadsapi::GetCurrentProcessId; 50 | use winapi::um::tlhelp32::{ 51 | CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, 52 | TH32CS_SNAPPROCESS, 53 | }; 54 | // SAFETY: winapi calls 55 | unsafe { 56 | // Take a snapshot of system processes, one of which is ours 57 | // and contains our parent's pid 58 | let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 59 | if snapshot == INVALID_HANDLE_VALUE { 60 | return -1; 61 | } 62 | 63 | let mut entry: PROCESSENTRY32 = mem::zeroed(); 64 | entry.dwSize = mem::size_of::() as DWORD; 65 | 66 | // Iterate over system processes looking for ours 67 | let success = Process32First(snapshot, &mut entry); 68 | if success == 0 { 69 | CloseHandle(snapshot); 70 | return -1; 71 | } 72 | 73 | let this_pid = GetCurrentProcessId(); 74 | while entry.th32ProcessID != this_pid { 75 | let success = Process32Next(snapshot, &mut entry); 76 | if success == 0 { 77 | CloseHandle(snapshot); 78 | return -1; 79 | } 80 | } 81 | CloseHandle(snapshot); 82 | 83 | // FIXME: Using the process ID exposes a race condition 84 | // wherein the parent process already exited and the OS 85 | // reassigned its ID. 86 | let parent_id = entry.th32ParentProcessID; 87 | parent_id.into() 88 | } 89 | } 90 | #[cfg(not(windows))] 91 | { 92 | use std::os::unix::process::parent_id; 93 | parent_id().into() 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /runtime/ops/spawn.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use super::io::ChildStderrResource; 4 | use super::io::ChildStdinResource; 5 | use super::io::ChildStdoutResource; 6 | use super::process::Stdio; 7 | use super::process::StdioOrRid; 8 | use crate::permissions::Permissions; 9 | use deno_core::error::AnyError; 10 | use deno_core::op; 11 | use deno_core::Extension; 12 | use deno_core::OpState; 13 | use deno_core::Resource; 14 | use deno_core::ResourceId; 15 | use deno_core::ZeroCopyBuf; 16 | use serde::Deserialize; 17 | use serde::Serialize; 18 | use std::borrow::Cow; 19 | use std::cell::RefCell; 20 | use std::process::ExitStatus; 21 | use std::rc::Rc; 22 | 23 | #[cfg(unix)] 24 | use std::os::unix::prelude::ExitStatusExt; 25 | #[cfg(unix)] 26 | use std::os::unix::process::CommandExt; 27 | 28 | pub fn init() -> Extension { 29 | Extension::builder() 30 | .ops(vec![ 31 | op_spawn_child::decl(), 32 | op_spawn_wait::decl(), 33 | op_spawn_sync::decl(), 34 | ]) 35 | .build() 36 | } 37 | 38 | struct ChildResource(tokio::process::Child); 39 | 40 | impl Resource for ChildResource { 41 | fn name(&self) -> Cow { 42 | "child".into() 43 | } 44 | } 45 | 46 | #[derive(Deserialize)] 47 | #[serde(rename_all = "camelCase")] 48 | pub struct SpawnArgs { 49 | cmd: String, 50 | args: Vec, 51 | cwd: Option, 52 | clear_env: bool, 53 | env: Vec<(String, String)>, 54 | #[cfg(unix)] 55 | gid: Option, 56 | #[cfg(unix)] 57 | uid: Option, 58 | 59 | #[serde(flatten)] 60 | stdio: ChildStdio, 61 | } 62 | 63 | #[derive(Deserialize)] 64 | #[serde(rename_all = "camelCase")] 65 | pub struct ChildStdio { 66 | stdin: Stdio, 67 | stdout: Stdio, 68 | stderr: Stdio, 69 | } 70 | 71 | #[derive(Serialize)] 72 | #[serde(rename_all = "camelCase")] 73 | pub struct ChildStatus { 74 | success: bool, 75 | code: i32, 76 | signal: Option, 77 | } 78 | 79 | impl TryFrom for ChildStatus { 80 | type Error = AnyError; 81 | 82 | fn try_from(status: ExitStatus) -> Result { 83 | let code = status.code(); 84 | #[cfg(unix)] 85 | let signal = status.signal(); 86 | #[cfg(not(unix))] 87 | let signal: Option = None; 88 | 89 | let status = if let Some(signal) = signal { 90 | ChildStatus { 91 | success: false, 92 | code: 128 + signal, 93 | #[cfg(unix)] 94 | signal: Some(crate::ops::signal::signal_int_to_str(signal)?.to_string()), 95 | #[cfg(not(unix))] 96 | signal: None, 97 | } 98 | } else { 99 | let code = code.expect("Should have either an exit code or a signal."); 100 | 101 | ChildStatus { 102 | success: code == 0, 103 | code, 104 | signal: None, 105 | } 106 | }; 107 | 108 | Ok(status) 109 | } 110 | } 111 | 112 | #[derive(Serialize)] 113 | #[serde(rename_all = "camelCase")] 114 | pub struct SpawnOutput { 115 | status: ChildStatus, 116 | stdout: Option, 117 | stderr: Option, 118 | } 119 | 120 | fn create_command(state: &mut OpState, args: SpawnArgs) -> Result { 121 | super::check_unstable(state, "Deno.spawn"); 122 | state.borrow_mut::().run.check(&args.cmd)?; 123 | 124 | let mut command = std::process::Command::new(args.cmd); 125 | command.args(args.args); 126 | 127 | if let Some(cwd) = args.cwd { 128 | command.current_dir(cwd); 129 | } 130 | 131 | if args.clear_env { 132 | command.env_clear(); 133 | } 134 | command.envs(args.env); 135 | 136 | #[cfg(unix)] 137 | if let Some(gid) = args.gid { 138 | super::check_unstable(state, "Deno.spawn.gid"); 139 | command.gid(gid); 140 | } 141 | #[cfg(unix)] 142 | if let Some(uid) = args.uid { 143 | super::check_unstable(state, "Deno.spawn.uid"); 144 | command.uid(uid); 145 | } 146 | #[cfg(unix)] 147 | // TODO(bartlomieju): 148 | #[allow(clippy::undocumented_unsafe_blocks)] 149 | unsafe { 150 | command.pre_exec(|| { 151 | libc::setgroups(0, std::ptr::null()); 152 | Ok(()) 153 | }); 154 | } 155 | 156 | command.stdin(args.stdio.stdin.as_stdio()); 157 | command.stdout(match args.stdio.stdout { 158 | Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?, 159 | value => value.as_stdio(), 160 | }); 161 | command.stderr(match args.stdio.stderr { 162 | Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?, 163 | value => value.as_stdio(), 164 | }); 165 | 166 | Ok(command) 167 | } 168 | 169 | #[derive(Serialize)] 170 | #[serde(rename_all = "camelCase")] 171 | struct Child { 172 | rid: ResourceId, 173 | pid: u32, 174 | stdin_rid: Option, 175 | stdout_rid: Option, 176 | stderr_rid: Option, 177 | } 178 | 179 | #[op] 180 | fn op_spawn_child(state: &mut OpState, args: SpawnArgs) -> Result { 181 | let mut command = tokio::process::Command::from(create_command(state, args)?); 182 | // TODO(@crowlkats): allow detaching processes. 183 | // currently deno will orphan a process when exiting with an error or Deno.exit() 184 | // We want to kill child when it's closed 185 | command.kill_on_drop(true); 186 | 187 | let mut child = command.spawn()?; 188 | let pid = child.id().expect("Process ID should be set."); 189 | 190 | let stdin_rid = child 191 | .stdin 192 | .take() 193 | .map(|stdin| state.resource_table.add(ChildStdinResource::from(stdin))); 194 | 195 | let stdout_rid = child 196 | .stdout 197 | .take() 198 | .map(|stdout| state.resource_table.add(ChildStdoutResource::from(stdout))); 199 | 200 | let stderr_rid = child 201 | .stderr 202 | .take() 203 | .map(|stderr| state.resource_table.add(ChildStderrResource::from(stderr))); 204 | 205 | let child_rid = state.resource_table.add(ChildResource(child)); 206 | 207 | Ok(Child { 208 | rid: child_rid, 209 | pid, 210 | stdin_rid, 211 | stdout_rid, 212 | stderr_rid, 213 | }) 214 | } 215 | 216 | #[op] 217 | async fn op_spawn_wait( 218 | state: Rc>, 219 | rid: ResourceId, 220 | ) -> Result { 221 | let resource = state 222 | .borrow_mut() 223 | .resource_table 224 | .take::(rid)?; 225 | Rc::try_unwrap(resource) 226 | .ok() 227 | .unwrap() 228 | .0 229 | .wait() 230 | .await? 231 | .try_into() 232 | } 233 | 234 | #[op] 235 | fn op_spawn_sync(state: &mut OpState, args: SpawnArgs) -> Result { 236 | let stdout = matches!(args.stdio.stdout, Stdio::Piped); 237 | let stderr = matches!(args.stdio.stderr, Stdio::Piped); 238 | let output = create_command(state, args)?.output()?; 239 | 240 | Ok(SpawnOutput { 241 | status: output.status.try_into()?, 242 | stdout: if stdout { 243 | Some(output.stdout.into()) 244 | } else { 245 | None 246 | }, 247 | stderr: if stderr { 248 | Some(output.stderr.into()) 249 | } else { 250 | None 251 | }, 252 | }) 253 | } 254 | -------------------------------------------------------------------------------- /runtime/ops/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | use deno_core::error::custom_error; 3 | use deno_core::error::AnyError; 4 | 5 | /// A utility function to map OsStrings to Strings 6 | pub fn into_string(s: std::ffi::OsString) -> Result { 7 | s.into_string().map_err(|s| { 8 | let message = format!("File name or path {:?} is not valid UTF-8", s); 9 | custom_error("InvalidData", message) 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /runtime/ops/web_worker.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | mod sync_fetch; 4 | 5 | use crate::web_worker::WebWorkerInternalHandle; 6 | use crate::web_worker::WebWorkerType; 7 | use deno_core::error::AnyError; 8 | use deno_core::op; 9 | 10 | use deno_core::CancelFuture; 11 | use deno_core::Extension; 12 | use deno_core::OpState; 13 | use deno_web::JsMessageData; 14 | use std::cell::RefCell; 15 | use std::rc::Rc; 16 | 17 | use self::sync_fetch::op_worker_sync_fetch; 18 | 19 | pub fn init() -> Extension { 20 | Extension::builder() 21 | .ops(vec![ 22 | op_worker_post_message::decl(), 23 | op_worker_recv_message::decl(), 24 | // Notify host that guest worker closes. 25 | op_worker_close::decl(), 26 | op_worker_get_type::decl(), 27 | op_worker_sync_fetch::decl(), 28 | ]) 29 | .build() 30 | } 31 | 32 | #[op] 33 | fn op_worker_post_message(state: &mut OpState, data: JsMessageData) -> Result<(), AnyError> { 34 | let handle = state.borrow::().clone(); 35 | handle.port.send(state, data)?; 36 | Ok(()) 37 | } 38 | 39 | #[op] 40 | async fn op_worker_recv_message( 41 | state: Rc>, 42 | ) -> Result, AnyError> { 43 | let handle = { 44 | let state = state.borrow(); 45 | state.borrow::().clone() 46 | }; 47 | handle 48 | .port 49 | .recv(state.clone()) 50 | .or_cancel(handle.cancel) 51 | .await? 52 | } 53 | 54 | #[op] 55 | fn op_worker_close(state: &mut OpState) { 56 | // Notify parent that we're finished 57 | let mut handle = state.borrow_mut::().clone(); 58 | 59 | handle.terminate(); 60 | } 61 | 62 | #[op] 63 | fn op_worker_get_type(state: &mut OpState) -> WebWorkerType { 64 | let handle = state.borrow::().clone(); 65 | handle.worker_type 66 | } 67 | -------------------------------------------------------------------------------- /runtime/ops/web_worker/sync_fetch.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::web_worker::WebWorkerInternalHandle; 4 | use crate::web_worker::WebWorkerType; 5 | use deno_core::error::type_error; 6 | use deno_core::error::AnyError; 7 | use deno_core::op; 8 | use deno_core::url::Url; 9 | use deno_core::OpState; 10 | use deno_fetch::data_url::DataUrl; 11 | use deno_fetch::reqwest; 12 | use deno_web::BlobStore; 13 | use deno_websocket::DomExceptionNetworkError; 14 | use hyper::body::Bytes; 15 | use serde::{Deserialize, Serialize}; 16 | use tokio::task::JoinHandle; 17 | 18 | // TODO(andreubotella) Properly parse the MIME type 19 | fn mime_type_essence(mime_type: &str) -> String { 20 | let essence = match mime_type.split_once(';') { 21 | Some((essence, _)) => essence, 22 | None => mime_type, 23 | }; 24 | essence.trim().to_ascii_lowercase() 25 | } 26 | 27 | #[derive(Serialize, Deserialize)] 28 | #[serde(rename_all = "camelCase")] 29 | pub struct SyncFetchScript { 30 | url: String, 31 | script: String, 32 | } 33 | 34 | #[op] 35 | pub fn op_worker_sync_fetch( 36 | state: &mut OpState, 37 | scripts: Vec, 38 | mut loose_mime_checks: bool, 39 | ) -> Result, AnyError> { 40 | let handle = state.borrow::().clone(); 41 | assert_eq!(handle.worker_type, WebWorkerType::Classic); 42 | 43 | let client = state.borrow::().clone(); 44 | 45 | // TODO(andreubotella) It's not good to throw an exception related to blob 46 | // URLs when none of the script URLs use the blob scheme. 47 | // Also, in which contexts are blob URLs not supported? 48 | let blob_store = state 49 | .try_borrow::() 50 | .ok_or_else(|| type_error("Blob URLs are not supported in this context."))? 51 | .clone(); 52 | 53 | // TODO(andreubotella): make the below thread into a resource that can be 54 | // re-used. This would allow parallel fecthing of multiple scripts. 55 | 56 | let thread = std::thread::spawn(move || { 57 | let runtime = tokio::runtime::Builder::new_current_thread() 58 | .enable_io() 59 | .enable_time() 60 | .build() 61 | .unwrap(); 62 | 63 | let handles: Vec<_> = scripts 64 | .into_iter() 65 | .map(|script| -> JoinHandle> { 66 | let client = client.clone(); 67 | let blob_store = blob_store.clone(); 68 | runtime.spawn(async move { 69 | let script_url = 70 | Url::parse(&script).map_err(|_| type_error("Invalid script URL"))?; 71 | 72 | let (body, mime_type, res_url) = match script_url.scheme() { 73 | "http" | "https" => { 74 | let resp = client.get(script_url).send().await?.error_for_status()?; 75 | 76 | let res_url = resp.url().to_string(); 77 | 78 | // TODO(andreubotella) Properly run fetch's "extract a MIME type". 79 | let mime_type = resp 80 | .headers() 81 | .get("Content-Type") 82 | .and_then(|v| v.to_str().ok()) 83 | .map(mime_type_essence); 84 | 85 | // Always check the MIME type with HTTP(S). 86 | loose_mime_checks = false; 87 | 88 | let body = resp.bytes().await?; 89 | 90 | (body, mime_type, res_url) 91 | } 92 | "data" => { 93 | let data_url = DataUrl::process(&script) 94 | .map_err(|e| type_error(format!("{:?}", e)))?; 95 | 96 | let mime_type = { 97 | let mime = data_url.mime_type(); 98 | format!("{}/{}", mime.type_, mime.subtype) 99 | }; 100 | 101 | let (body, _) = data_url 102 | .decode_to_vec() 103 | .map_err(|e| type_error(format!("{:?}", e)))?; 104 | 105 | (Bytes::from(body), Some(mime_type), script) 106 | } 107 | "blob" => { 108 | let blob = blob_store 109 | .get_object_url(script_url)? 110 | .ok_or_else(|| type_error("Blob for the given URL not found."))?; 111 | 112 | let mime_type = mime_type_essence(&blob.media_type); 113 | 114 | let body = blob.read_all().await?; 115 | 116 | (Bytes::from(body), Some(mime_type), script) 117 | } 118 | _ => { 119 | return Err(type_error(format!( 120 | "Classic scripts with scheme {}: are not supported in workers.", 121 | script_url.scheme() 122 | ))) 123 | } 124 | }; 125 | 126 | if !loose_mime_checks { 127 | // TODO(andreubotella) Check properly for a Javascript MIME type. 128 | match mime_type.as_deref() { 129 | Some("application/javascript" | "text/javascript") => {} 130 | Some(mime_type) => { 131 | return Err(DomExceptionNetworkError { 132 | msg: format!("Invalid MIME type {:?}.", mime_type), 133 | } 134 | .into()) 135 | } 136 | None => { 137 | return Err( 138 | DomExceptionNetworkError::new("Missing MIME type.").into() 139 | ) 140 | } 141 | } 142 | } 143 | 144 | let (text, _) = encoding_rs::UTF_8.decode_with_bom_removal(&body); 145 | 146 | Ok(SyncFetchScript { 147 | url: res_url, 148 | script: text.into_owned(), 149 | }) 150 | }) 151 | }) 152 | .collect(); 153 | 154 | let mut ret = Vec::with_capacity(handles.len()); 155 | for handle in handles { 156 | let script = runtime.block_on(handle)??; 157 | ret.push(script); 158 | } 159 | Ok(ret) 160 | }); 161 | 162 | thread.join().unwrap() 163 | } 164 | -------------------------------------------------------------------------------- /runtime/tokio_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn create_basic_runtime() -> tokio::runtime::Runtime { 4 | tokio::runtime::Builder::new_current_thread() 5 | .enable_io() 6 | .enable_time() 7 | // This limits the number of threads for blocking operations (like for 8 | // synchronous fs ops) or CPU bound tasks like when we run dprint in 9 | // parallel for deno fmt. 10 | // The default value is 512, which is an unhelpfully large thread pool. We 11 | // don't ever want to have more than a couple dozen threads. 12 | .max_blocking_threads(32) 13 | .build() 14 | .unwrap() 15 | } 16 | 17 | pub fn run_local(future: F) -> R 18 | where 19 | F: std::future::Future, 20 | { 21 | let rt = create_basic_runtime(); 22 | let local = tokio::task::LocalSet::new(); 23 | local.block_on(&rt, future) 24 | } 25 | -------------------------------------------------------------------------------- /runtime/worker_bootstrap.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::runtime::ppid; 2 | use deno_core::serde_json; 3 | use deno_core::serde_json::json; 4 | use deno_core::ModuleSpecifier; 5 | 6 | /// Common bootstrap options for MainWorker & WebWorker 7 | #[derive(Clone)] 8 | pub struct BootstrapOptions { 9 | /// Sets `Deno.args` in JS runtime. 10 | pub args: Vec, 11 | pub cpu_count: usize, 12 | pub debug_flag: bool, 13 | pub enable_testing_features: bool, 14 | pub location: Option, 15 | /// Sets `Deno.noColor` in JS runtime. 16 | pub no_color: bool, 17 | pub is_tty: bool, 18 | /// Sets `Deno.version.deno` in JS runtime. 19 | pub runtime_version: String, 20 | /// Sets `Deno.version.typescript` in JS runtime. 21 | pub ts_version: String, 22 | pub unstable: bool, 23 | pub user_agent: String, 24 | } 25 | 26 | impl BootstrapOptions { 27 | pub fn as_json(&self) -> String { 28 | let payload = json!({ 29 | // Shared bootstrap args 30 | "args": self.args, 31 | "cpuCount": self.cpu_count, 32 | "debugFlag": self.debug_flag, 33 | "denoVersion": self.runtime_version, 34 | "location": self.location, 35 | "noColor": self.no_color, 36 | "isTty": self.is_tty, 37 | "tsVersion": self.ts_version, 38 | "unstableFlag": self.unstable, 39 | // Web worker only 40 | "enableTestingFeaturesFlag": self.enable_testing_features, 41 | // Env values 42 | "pid": std::process::id(), 43 | "ppid": ppid(), 44 | "target": env!("TARGET"), 45 | "v8Version": deno_core::v8_version(), 46 | "userAgent": self.user_agent, 47 | }); 48 | serde_json::to_string_pretty(&payload).unwrap() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /snapshot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno-snapshot" 3 | version = "0.6.1" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/deno-snapshot" 7 | repository = "https://github.com/tyrchen/deno-utils" 8 | homepage = "https://github.com/tyrchen/deno-utils" 9 | description = """ 10 | Generate snapshot for deno. Extracted some logic from main deno repo for better reusability. 11 | """ 12 | readme = "README.md" 13 | keywords = ["snapshot", "deno", "v8"] 14 | categories = ["development-tools"] 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | deno_core = "0.147.0" 20 | 21 | deno_broadcast_channel = { version = "0.59.0", optional = true } 22 | deno_console = { version = "0.65.0", optional = true } 23 | deno_crypto = { version = "0.79.0", optional = true } 24 | deno_fetch = { version = "0.88.0", optional = true } 25 | deno_http = { version = "0.59.0", optional = true } 26 | deno_net = { version = "0.57.0", optional = true } 27 | deno_tls = { version = "0.52.0", optional = true } 28 | deno_url = { version = "0.65.0", optional = true } 29 | deno_web = { version = "0.96.0", optional = true } 30 | deno_webgpu = { version = "0.66.0", optional = true } 31 | deno_webidl = { version = "0.65.0", optional = true } 32 | deno_websocket = { version = "0.70.0", optional = true } 33 | deno_webstorage = { version = "0.60.0", optional = true } 34 | glob = { version = "0.3.0", optional = true } 35 | zstd = "0.11.2" 36 | 37 | [target.'cfg(windows)'.build-dependencies] 38 | winres = "0.1.12" 39 | winapi = "0.3.9" 40 | 41 | [features] 42 | build = ["deno_broadcast_channel", "deno_console", "deno_crypto", "deno_fetch", "deno_http", "deno_net", "deno_tls", "deno_url", "deno_web", "deno_webidl", "deno_websocket", "deno_webstorage", "glob"] 43 | build_webgpu = ["deno_webgpu"] 44 | -------------------------------------------------------------------------------- /snapshot/README.md: -------------------------------------------------------------------------------- 1 | # Deno snapshot 2 | 3 | Generate snapshot for deno. Extracted some logic from main deno repo for better reusability. 4 | -------------------------------------------------------------------------------- /snapshot/js/01_build.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const { ObjectFreeze, StringPrototypeSplit } = window.__bootstrap.primordials; 6 | 7 | const build = { 8 | target: "unknown", 9 | arch: "unknown", 10 | os: "unknown", 11 | vendor: "unknown", 12 | env: undefined, 13 | }; 14 | 15 | function setBuildInfo(target) { 16 | const [arch, vendor, os, env] = StringPrototypeSplit(target, "-", 4); 17 | build.target = target; 18 | build.arch = arch; 19 | build.vendor = vendor; 20 | build.os = os; 21 | build.env = env; 22 | ObjectFreeze(build); 23 | } 24 | 25 | window.__bootstrap.build = { 26 | build, 27 | setBuildInfo, 28 | }; 29 | })(this); 30 | -------------------------------------------------------------------------------- /snapshot/js/01_errors.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const { Error } = window.__bootstrap.primordials; 7 | const { BadResource, Interrupted } = core; 8 | 9 | class NotFound extends Error { 10 | constructor(msg) { 11 | super(msg); 12 | this.name = "NotFound"; 13 | } 14 | } 15 | 16 | class PermissionDenied extends Error { 17 | constructor(msg) { 18 | super(msg); 19 | this.name = "PermissionDenied"; 20 | } 21 | } 22 | 23 | class ConnectionRefused extends Error { 24 | constructor(msg) { 25 | super(msg); 26 | this.name = "ConnectionRefused"; 27 | } 28 | } 29 | 30 | class ConnectionReset extends Error { 31 | constructor(msg) { 32 | super(msg); 33 | this.name = "ConnectionReset"; 34 | } 35 | } 36 | 37 | class ConnectionAborted extends Error { 38 | constructor(msg) { 39 | super(msg); 40 | this.name = "ConnectionAborted"; 41 | } 42 | } 43 | 44 | class NotConnected extends Error { 45 | constructor(msg) { 46 | super(msg); 47 | this.name = "NotConnected"; 48 | } 49 | } 50 | 51 | class AddrInUse extends Error { 52 | constructor(msg) { 53 | super(msg); 54 | this.name = "AddrInUse"; 55 | } 56 | } 57 | 58 | class AddrNotAvailable extends Error { 59 | constructor(msg) { 60 | super(msg); 61 | this.name = "AddrNotAvailable"; 62 | } 63 | } 64 | 65 | class BrokenPipe extends Error { 66 | constructor(msg) { 67 | super(msg); 68 | this.name = "BrokenPipe"; 69 | } 70 | } 71 | 72 | class AlreadyExists extends Error { 73 | constructor(msg) { 74 | super(msg); 75 | this.name = "AlreadyExists"; 76 | } 77 | } 78 | 79 | class InvalidData extends Error { 80 | constructor(msg) { 81 | super(msg); 82 | this.name = "InvalidData"; 83 | } 84 | } 85 | 86 | class TimedOut extends Error { 87 | constructor(msg) { 88 | super(msg); 89 | this.name = "TimedOut"; 90 | } 91 | } 92 | 93 | class WriteZero extends Error { 94 | constructor(msg) { 95 | super(msg); 96 | this.name = "WriteZero"; 97 | } 98 | } 99 | 100 | class UnexpectedEof extends Error { 101 | constructor(msg) { 102 | super(msg); 103 | this.name = "UnexpectedEof"; 104 | } 105 | } 106 | 107 | class Http extends Error { 108 | constructor(msg) { 109 | super(msg); 110 | this.name = "Http"; 111 | } 112 | } 113 | 114 | class Busy extends Error { 115 | constructor(msg) { 116 | super(msg); 117 | this.name = "Busy"; 118 | } 119 | } 120 | 121 | class NotSupported extends Error { 122 | constructor(msg) { 123 | super(msg); 124 | this.name = "NotSupported"; 125 | } 126 | } 127 | 128 | const errors = { 129 | NotFound, 130 | PermissionDenied, 131 | ConnectionRefused, 132 | ConnectionReset, 133 | ConnectionAborted, 134 | NotConnected, 135 | AddrInUse, 136 | AddrNotAvailable, 137 | BrokenPipe, 138 | AlreadyExists, 139 | InvalidData, 140 | TimedOut, 141 | Interrupted, 142 | WriteZero, 143 | UnexpectedEof, 144 | BadResource, 145 | Http, 146 | Busy, 147 | NotSupported, 148 | }; 149 | 150 | window.__bootstrap.errors = { 151 | errors, 152 | }; 153 | })(this); 154 | -------------------------------------------------------------------------------- /snapshot/js/01_version.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const { ObjectFreeze } = window.__bootstrap.primordials; 6 | 7 | const version = { 8 | deno: "", 9 | v8: "", 10 | typescript: "", 11 | }; 12 | 13 | function setVersions( 14 | denoVersion, 15 | v8Version, 16 | tsVersion, 17 | ) { 18 | version.deno = denoVersion; 19 | version.v8 = v8Version; 20 | version.typescript = tsVersion; 21 | 22 | ObjectFreeze(version); 23 | } 24 | 25 | window.__bootstrap.version = { 26 | version, 27 | setVersions, 28 | }; 29 | })(this); 30 | -------------------------------------------------------------------------------- /snapshot/js/01_web_util.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const { TypeError, Symbol } = window.__bootstrap.primordials; 6 | const illegalConstructorKey = Symbol("illegalConstructorKey"); 7 | 8 | function requiredArguments( 9 | name, 10 | length, 11 | required, 12 | ) { 13 | if (length < required) { 14 | const errMsg = `${name} requires at least ${required} argument${ 15 | required === 1 ? "" : "s" 16 | }, but only ${length} present`; 17 | throw new TypeError(errMsg); 18 | } 19 | } 20 | 21 | window.__bootstrap.webUtil = { 22 | illegalConstructorKey, 23 | requiredArguments, 24 | }; 25 | })(this); 26 | -------------------------------------------------------------------------------- /snapshot/js/06_util.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const { 6 | decodeURIComponent, 7 | ObjectPrototypeIsPrototypeOf, 8 | Promise, 9 | SafeArrayIterator, 10 | StringPrototypeReplace, 11 | TypeError, 12 | } = window.__bootstrap.primordials; 13 | const { build } = window.__bootstrap.build; 14 | const { URLPrototype } = window.__bootstrap.url; 15 | let logDebug = false; 16 | let logSource = "JS"; 17 | 18 | function setLogDebug(debug, source) { 19 | logDebug = debug; 20 | if (source) { 21 | logSource = source; 22 | } 23 | } 24 | 25 | function log(...args) { 26 | if (logDebug) { 27 | // if we destructure `console` off `globalThis` too early, we don't bind to 28 | // the right console, therefore we don't log anything out. 29 | globalThis.console.log( 30 | `DEBUG ${logSource} -`, 31 | ...new SafeArrayIterator(args), 32 | ); 33 | } 34 | } 35 | 36 | function createResolvable() { 37 | let resolve; 38 | let reject; 39 | const promise = new Promise((res, rej) => { 40 | resolve = res; 41 | reject = rej; 42 | }); 43 | promise.resolve = resolve; 44 | promise.reject = reject; 45 | return promise; 46 | } 47 | 48 | // Keep in sync with `fromFileUrl()` in `std/path/win32.ts`. 49 | function pathFromURLWin32(url) { 50 | let p = StringPrototypeReplace( 51 | url.pathname, 52 | /^\/*([A-Za-z]:)(\/|$)/, 53 | "$1/", 54 | ); 55 | p = StringPrototypeReplace( 56 | p, 57 | /\//g, 58 | "\\", 59 | ); 60 | p = StringPrototypeReplace( 61 | p, 62 | /%(?![0-9A-Fa-f]{2})/g, 63 | "%25", 64 | ); 65 | let path = decodeURIComponent(p); 66 | if (url.hostname != "") { 67 | // Note: The `URL` implementation guarantees that the drive letter and 68 | // hostname are mutually exclusive. Otherwise it would not have been valid 69 | // to append the hostname and path like this. 70 | path = `\\\\${url.hostname}${path}`; 71 | } 72 | return path; 73 | } 74 | 75 | // Keep in sync with `fromFileUrl()` in `std/path/posix.ts`. 76 | function pathFromURLPosix(url) { 77 | if (url.hostname !== "") { 78 | throw new TypeError(`Host must be empty.`); 79 | } 80 | 81 | return decodeURIComponent( 82 | StringPrototypeReplace(url.pathname, /%(?![0-9A-Fa-f]{2})/g, "%25"), 83 | ); 84 | } 85 | 86 | function pathFromURL(pathOrUrl) { 87 | if (ObjectPrototypeIsPrototypeOf(URLPrototype, pathOrUrl)) { 88 | if (pathOrUrl.protocol != "file:") { 89 | throw new TypeError("Must be a file URL."); 90 | } 91 | 92 | return build.os == "windows" 93 | ? pathFromURLWin32(pathOrUrl) 94 | : pathFromURLPosix(pathOrUrl); 95 | } 96 | return pathOrUrl; 97 | } 98 | 99 | window.__bootstrap.internals = { 100 | ...window.__bootstrap.internals ?? {}, 101 | pathFromURL, 102 | }; 103 | 104 | function writable(value) { 105 | return { 106 | value, 107 | writable: true, 108 | enumerable: true, 109 | configurable: true, 110 | }; 111 | } 112 | 113 | function nonEnumerable(value) { 114 | return { 115 | value, 116 | writable: true, 117 | enumerable: false, 118 | configurable: true, 119 | }; 120 | } 121 | 122 | function readOnly(value) { 123 | return { 124 | value, 125 | enumerable: true, 126 | writable: false, 127 | configurable: true, 128 | }; 129 | } 130 | 131 | function getterOnly(getter) { 132 | return { 133 | get: getter, 134 | enumerable: true, 135 | configurable: true, 136 | }; 137 | } 138 | 139 | window.__bootstrap.util = { 140 | log, 141 | setLogDebug, 142 | createResolvable, 143 | pathFromURL, 144 | writable, 145 | nonEnumerable, 146 | readOnly, 147 | getterOnly, 148 | }; 149 | })(this); 150 | -------------------------------------------------------------------------------- /snapshot/js/10_permissions.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const { 6 | Event, 7 | EventTarget, 8 | Deno: { core: { ops } }, 9 | __bootstrap: { webUtil: { illegalConstructorKey } }, 10 | } = window; 11 | const { pathFromURL } = window.__bootstrap.util; 12 | const { 13 | ArrayIsArray, 14 | ArrayPrototypeIncludes, 15 | ArrayPrototypeMap, 16 | ArrayPrototypeSlice, 17 | Map, 18 | MapPrototypeGet, 19 | MapPrototypeHas, 20 | MapPrototypeSet, 21 | FunctionPrototypeCall, 22 | PromiseResolve, 23 | PromiseReject, 24 | ReflectHas, 25 | SymbolFor, 26 | TypeError, 27 | } = window.__bootstrap.primordials; 28 | 29 | /** 30 | * @typedef StatusCacheValue 31 | * @property {PermissionState} state 32 | * @property {PermissionStatus} status 33 | */ 34 | 35 | /** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "run" | "ffi" | "hrtime">} */ 36 | const permissionNames = [ 37 | "read", 38 | "write", 39 | "net", 40 | "env", 41 | "run", 42 | "ffi", 43 | "hrtime", 44 | ]; 45 | 46 | /** 47 | * @param {Deno.PermissionDescriptor} desc 48 | * @returns {Deno.PermissionState} 49 | */ 50 | function opQuery(desc) { 51 | return ops.op_query_permission(desc); 52 | } 53 | 54 | /** 55 | * @param {Deno.PermissionDescriptor} desc 56 | * @returns {Deno.PermissionState} 57 | */ 58 | function opRevoke(desc) { 59 | return ops.op_revoke_permission(desc); 60 | } 61 | 62 | /** 63 | * @param {Deno.PermissionDescriptor} desc 64 | * @returns {Deno.PermissionState} 65 | */ 66 | function opRequest(desc) { 67 | return ops.op_request_permission(desc); 68 | } 69 | 70 | class PermissionStatus extends EventTarget { 71 | /** @type {{ state: Deno.PermissionState }} */ 72 | #state; 73 | 74 | /** @type {((this: PermissionStatus, event: Event) => any) | null} */ 75 | onchange = null; 76 | 77 | /** @returns {Deno.PermissionState} */ 78 | get state() { 79 | return this.#state.state; 80 | } 81 | 82 | /** 83 | * @param {{ state: Deno.PermissionState }} state 84 | * @param {unknown} key 85 | */ 86 | constructor(state = null, key = null) { 87 | if (key != illegalConstructorKey) { 88 | throw new TypeError("Illegal constructor."); 89 | } 90 | super(); 91 | this.#state = state; 92 | } 93 | 94 | /** 95 | * @param {Event} event 96 | * @returns {boolean} 97 | */ 98 | dispatchEvent(event) { 99 | let dispatched = super.dispatchEvent(event); 100 | if (dispatched && this.onchange) { 101 | FunctionPrototypeCall(this.onchange, this, event); 102 | dispatched = !event.defaultPrevented; 103 | } 104 | return dispatched; 105 | } 106 | 107 | [SymbolFor("Deno.privateCustomInspect")](inspect) { 108 | return `${this.constructor.name} ${ 109 | inspect({ state: this.state, onchange: this.onchange }) 110 | }`; 111 | } 112 | } 113 | 114 | /** @type {Map} */ 115 | const statusCache = new Map(); 116 | 117 | /** 118 | * @param {Deno.PermissionDescriptor} desc 119 | * @param {Deno.PermissionState} state 120 | * @returns {PermissionStatus} 121 | */ 122 | function cache(desc, state) { 123 | let { name: key } = desc; 124 | if ( 125 | (desc.name === "read" || desc.name === "write") && 126 | ReflectHas(desc, "path") 127 | ) { 128 | key += `-${desc.path}`; 129 | } else if (desc.name === "net" && desc.host) { 130 | key += `-${desc.host}`; 131 | } 132 | if (MapPrototypeHas(statusCache, key)) { 133 | const status = MapPrototypeGet(statusCache, key); 134 | if (status.state !== state) { 135 | status.state = state; 136 | status.status.dispatchEvent(new Event("change", { cancelable: false })); 137 | } 138 | return status.status; 139 | } 140 | /** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */ 141 | const status = { state }; 142 | status.status = new PermissionStatus(status, illegalConstructorKey); 143 | MapPrototypeSet(statusCache, key, status); 144 | return status.status; 145 | } 146 | 147 | /** 148 | * @param {unknown} desc 149 | * @returns {desc is Deno.PermissionDescriptor} 150 | */ 151 | function isValidDescriptor(desc) { 152 | return typeof desc === "object" && desc !== null && 153 | ArrayPrototypeIncludes(permissionNames, desc.name); 154 | } 155 | 156 | class Permissions { 157 | constructor(key = null) { 158 | if (key != illegalConstructorKey) { 159 | throw new TypeError("Illegal constructor."); 160 | } 161 | } 162 | 163 | query(desc) { 164 | if (!isValidDescriptor(desc)) { 165 | return PromiseReject( 166 | new TypeError( 167 | `The provided value "${desc?.name}" is not a valid permission name.`, 168 | ), 169 | ); 170 | } 171 | 172 | if ( 173 | desc.name === "read" || desc.name === "write" || desc.name === "ffi" 174 | ) { 175 | desc.path = pathFromURL(desc.path); 176 | } else if (desc.name === "run") { 177 | desc.command = pathFromURL(desc.command); 178 | } 179 | 180 | const state = opQuery(desc); 181 | return PromiseResolve(cache(desc, state)); 182 | } 183 | 184 | revoke(desc) { 185 | if (!isValidDescriptor(desc)) { 186 | return PromiseReject( 187 | new TypeError( 188 | `The provided value "${desc?.name}" is not a valid permission name.`, 189 | ), 190 | ); 191 | } 192 | 193 | if (desc.name === "read" || desc.name === "write") { 194 | desc.path = pathFromURL(desc.path); 195 | } else if (desc.name === "run") { 196 | desc.command = pathFromURL(desc.command); 197 | } 198 | 199 | const state = opRevoke(desc); 200 | return PromiseResolve(cache(desc, state)); 201 | } 202 | 203 | request(desc) { 204 | if (!isValidDescriptor(desc)) { 205 | return PromiseReject( 206 | new TypeError( 207 | `The provided value "${desc?.name}" is not a valid permission name.`, 208 | ), 209 | ); 210 | } 211 | 212 | if (desc.name === "read" || desc.name === "write") { 213 | desc.path = pathFromURL(desc.path); 214 | } else if (desc.name === "run") { 215 | desc.command = pathFromURL(desc.command); 216 | } 217 | 218 | const state = opRequest(desc); 219 | return PromiseResolve(cache(desc, state)); 220 | } 221 | } 222 | 223 | const permissions = new Permissions(illegalConstructorKey); 224 | 225 | /** Converts all file URLs in FS allowlists to paths. */ 226 | function serializePermissions(permissions) { 227 | if (typeof permissions == "object" && permissions != null) { 228 | const serializedPermissions = {}; 229 | for (const key of ["read", "write", "run", "ffi"]) { 230 | if (ArrayIsArray(permissions[key])) { 231 | serializedPermissions[key] = ArrayPrototypeMap( 232 | permissions[key], 233 | (path) => pathFromURL(path), 234 | ); 235 | } else { 236 | serializedPermissions[key] = permissions[key]; 237 | } 238 | } 239 | for (const key of ["env", "hrtime", "net"]) { 240 | if (ArrayIsArray(permissions[key])) { 241 | serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]); 242 | } else { 243 | serializedPermissions[key] = permissions[key]; 244 | } 245 | } 246 | return serializedPermissions; 247 | } 248 | return permissions; 249 | } 250 | 251 | window.__bootstrap.permissions = { 252 | serializePermissions, 253 | permissions, 254 | Permissions, 255 | PermissionStatus, 256 | }; 257 | })(this); 258 | -------------------------------------------------------------------------------- /snapshot/js/11_workers.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { 8 | Error, 9 | ObjectPrototypeIsPrototypeOf, 10 | StringPrototypeStartsWith, 11 | String, 12 | SymbolIterator, 13 | SymbolToStringTag, 14 | } = window.__bootstrap.primordials; 15 | const webidl = window.__bootstrap.webidl; 16 | const { URL } = window.__bootstrap.url; 17 | const { getLocationHref } = window.__bootstrap.location; 18 | const { serializePermissions } = window.__bootstrap.permissions; 19 | const { log } = window.__bootstrap.util; 20 | const { defineEventHandler } = window.__bootstrap.event; 21 | const { 22 | deserializeJsMessageData, 23 | serializeJsMessageData, 24 | MessagePortPrototype, 25 | } = window.__bootstrap.messagePort; 26 | 27 | function createWorker( 28 | specifier, 29 | hasSourceCode, 30 | sourceCode, 31 | permissions, 32 | name, 33 | workerType, 34 | ) { 35 | return ops.op_create_worker({ 36 | hasSourceCode, 37 | name, 38 | permissions: serializePermissions(permissions), 39 | sourceCode, 40 | specifier, 41 | workerType, 42 | }); 43 | } 44 | 45 | function hostTerminateWorker(id) { 46 | ops.op_host_terminate_worker(id); 47 | } 48 | 49 | function hostPostMessage(id, data) { 50 | ops.op_host_post_message(id, data); 51 | } 52 | 53 | function hostRecvCtrl(id) { 54 | return core.opAsync("op_host_recv_ctrl", id); 55 | } 56 | 57 | function hostRecvMessage(id) { 58 | return core.opAsync("op_host_recv_message", id); 59 | } 60 | 61 | class Worker extends EventTarget { 62 | #id = 0; 63 | #name = ""; 64 | 65 | // "RUNNING" | "CLOSED" | "TERMINATED" 66 | // "TERMINATED" means that any controls or messages received will be 67 | // discarded. "CLOSED" means that we have received a control 68 | // indicating that the worker is no longer running, but there might 69 | // still be messages left to receive. 70 | #status = "RUNNING"; 71 | 72 | constructor(specifier, options = {}) { 73 | super(); 74 | specifier = String(specifier); 75 | const { 76 | deno, 77 | name, 78 | type = "classic", 79 | } = options; 80 | 81 | const workerType = webidl.converters["WorkerType"](type); 82 | 83 | if ( 84 | StringPrototypeStartsWith(specifier, "./") || 85 | StringPrototypeStartsWith(specifier, "../") || 86 | StringPrototypeStartsWith(specifier, "/") || workerType === "classic" 87 | ) { 88 | const baseUrl = getLocationHref(); 89 | if (baseUrl != null) { 90 | specifier = new URL(specifier, baseUrl).href; 91 | } 92 | } 93 | 94 | this.#name = name; 95 | let hasSourceCode, sourceCode; 96 | if (workerType === "classic") { 97 | hasSourceCode = true; 98 | sourceCode = `importScripts("#");`; 99 | } else { 100 | hasSourceCode = false; 101 | sourceCode = ""; 102 | } 103 | 104 | const id = createWorker( 105 | specifier, 106 | hasSourceCode, 107 | sourceCode, 108 | deno?.permissions, 109 | name, 110 | workerType, 111 | ); 112 | this.#id = id; 113 | this.#pollControl(); 114 | this.#pollMessages(); 115 | } 116 | 117 | #handleError(e) { 118 | const event = new ErrorEvent("error", { 119 | cancelable: true, 120 | message: e.message, 121 | lineno: e.lineNumber ? e.lineNumber : undefined, 122 | colno: e.columnNumber ? e.columnNumber : undefined, 123 | filename: e.fileName, 124 | error: null, 125 | }); 126 | 127 | this.dispatchEvent(event); 128 | // Don't bubble error event to window for loader errors (`!e.fileName`). 129 | // TODO(nayeemrmn): It's not correct to use `e.fileName` to detect user 130 | // errors. It won't be there for non-awaited async ops for example. 131 | if (e.fileName && !event.defaultPrevented) { 132 | window.dispatchEvent(event); 133 | } 134 | 135 | return event.defaultPrevented; 136 | } 137 | 138 | #pollControl = async () => { 139 | while (this.#status === "RUNNING") { 140 | const [type, data] = await hostRecvCtrl(this.#id); 141 | 142 | // If terminate was called then we ignore all messages 143 | if (this.#status === "TERMINATED") { 144 | return; 145 | } 146 | 147 | switch (type) { 148 | case 1: { // TerminalError 149 | this.#status = "CLOSED"; 150 | } /* falls through */ 151 | case 2: { // Error 152 | if (!this.#handleError(data)) { 153 | throw new Error("Unhandled error in child worker."); 154 | } 155 | break; 156 | } 157 | case 3: { // Close 158 | log(`Host got "close" message from worker: ${this.#name}`); 159 | this.#status = "CLOSED"; 160 | return; 161 | } 162 | default: { 163 | throw new Error(`Unknown worker event: "${type}"`); 164 | } 165 | } 166 | } 167 | }; 168 | 169 | #pollMessages = async () => { 170 | while (this.#status !== "TERMINATED") { 171 | const data = await hostRecvMessage(this.#id); 172 | if (this.#status === "TERMINATED" || data === null) { 173 | return; 174 | } 175 | let message, transferables; 176 | try { 177 | const v = deserializeJsMessageData(data); 178 | message = v[0]; 179 | transferables = v[1]; 180 | } catch (err) { 181 | const event = new MessageEvent("messageerror", { 182 | cancelable: false, 183 | data: err, 184 | }); 185 | this.dispatchEvent(event); 186 | return; 187 | } 188 | const event = new MessageEvent("message", { 189 | cancelable: false, 190 | data: message, 191 | ports: transferables.filter((t) => 192 | ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t) 193 | ), 194 | }); 195 | this.dispatchEvent(event); 196 | } 197 | }; 198 | 199 | postMessage(message, transferOrOptions = {}) { 200 | const prefix = "Failed to execute 'postMessage' on 'MessagePort'"; 201 | webidl.requiredArguments(arguments.length, 1, { prefix }); 202 | message = webidl.converters.any(message); 203 | let options; 204 | if ( 205 | webidl.type(transferOrOptions) === "Object" && 206 | transferOrOptions !== undefined && 207 | transferOrOptions[SymbolIterator] !== undefined 208 | ) { 209 | const transfer = webidl.converters["sequence"]( 210 | transferOrOptions, 211 | { prefix, context: "Argument 2" }, 212 | ); 213 | options = { transfer }; 214 | } else { 215 | options = webidl.converters.StructuredSerializeOptions( 216 | transferOrOptions, 217 | { 218 | prefix, 219 | context: "Argument 2", 220 | }, 221 | ); 222 | } 223 | const { transfer } = options; 224 | const data = serializeJsMessageData(message, transfer); 225 | if (this.#status === "RUNNING") { 226 | hostPostMessage(this.#id, data); 227 | } 228 | } 229 | 230 | terminate() { 231 | if (this.#status !== "TERMINATED") { 232 | this.#status = "TERMINATED"; 233 | hostTerminateWorker(this.#id); 234 | } 235 | } 236 | 237 | [SymbolToStringTag] = "Worker"; 238 | } 239 | 240 | defineEventHandler(Worker.prototype, "error"); 241 | defineEventHandler(Worker.prototype, "message"); 242 | defineEventHandler(Worker.prototype, "messageerror"); 243 | 244 | webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [ 245 | "classic", 246 | "module", 247 | ]); 248 | 249 | window.__bootstrap.worker = { 250 | Worker, 251 | }; 252 | })(this); 253 | -------------------------------------------------------------------------------- /snapshot/js/12_io.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | // Interfaces 100% copied from Go. 4 | // Documentation liberally lifted from them too. 5 | // Thank you! We love Go! <3 6 | "use strict"; 7 | 8 | ((window) => { 9 | const core = window.Deno.core; 10 | const ops = core.ops; 11 | const { 12 | Uint8Array, 13 | ArrayPrototypePush, 14 | MathMin, 15 | TypedArrayPrototypeSubarray, 16 | TypedArrayPrototypeSet, 17 | } = window.__bootstrap.primordials; 18 | 19 | const DEFAULT_BUFFER_SIZE = 32 * 1024; 20 | // Seek whence values. 21 | // https://golang.org/pkg/io/#pkg-constants 22 | const SeekMode = { 23 | 0: "Start", 24 | 1: "Current", 25 | 2: "End", 26 | 27 | Start: 0, 28 | Current: 1, 29 | End: 2, 30 | }; 31 | 32 | async function copy( 33 | src, 34 | dst, 35 | options, 36 | ) { 37 | let n = 0; 38 | const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; 39 | const b = new Uint8Array(bufSize); 40 | let gotEOF = false; 41 | while (gotEOF === false) { 42 | const result = await src.read(b); 43 | if (result === null) { 44 | gotEOF = true; 45 | } else { 46 | let nwritten = 0; 47 | while (nwritten < result) { 48 | nwritten += await dst.write( 49 | TypedArrayPrototypeSubarray(b, nwritten, result), 50 | ); 51 | } 52 | n += nwritten; 53 | } 54 | } 55 | return n; 56 | } 57 | 58 | async function* iter( 59 | r, 60 | options, 61 | ) { 62 | const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; 63 | const b = new Uint8Array(bufSize); 64 | while (true) { 65 | const result = await r.read(b); 66 | if (result === null) { 67 | break; 68 | } 69 | 70 | yield TypedArrayPrototypeSubarray(b, 0, result); 71 | } 72 | } 73 | 74 | function* iterSync( 75 | r, 76 | options, 77 | ) { 78 | const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; 79 | const b = new Uint8Array(bufSize); 80 | while (true) { 81 | const result = r.readSync(b); 82 | if (result === null) { 83 | break; 84 | } 85 | 86 | yield TypedArrayPrototypeSubarray(b, 0, result); 87 | } 88 | } 89 | 90 | function readSync(rid, buffer) { 91 | if (buffer.length === 0) { 92 | return 0; 93 | } 94 | 95 | const nread = ops.op_read_sync(rid, buffer); 96 | 97 | return nread === 0 ? null : nread; 98 | } 99 | 100 | async function read(rid, buffer) { 101 | if (buffer.length === 0) { 102 | return 0; 103 | } 104 | 105 | const nread = await core.read(rid, buffer); 106 | 107 | return nread === 0 ? null : nread; 108 | } 109 | 110 | function writeSync(rid, data) { 111 | return ops.op_write_sync(rid, data); 112 | } 113 | 114 | function write(rid, data) { 115 | return core.write(rid, data); 116 | } 117 | 118 | const READ_PER_ITER = 64 * 1024; // 64kb 119 | 120 | function readAll(r) { 121 | return readAllInner(r); 122 | } 123 | async function readAllInner(r, options) { 124 | const buffers = []; 125 | const signal = options?.signal ?? null; 126 | while (true) { 127 | signal?.throwIfAborted(); 128 | const buf = new Uint8Array(READ_PER_ITER); 129 | const read = await r.read(buf); 130 | if (typeof read == "number") { 131 | ArrayPrototypePush(buffers, new Uint8Array(buf.buffer, 0, read)); 132 | } else { 133 | break; 134 | } 135 | } 136 | signal?.throwIfAborted(); 137 | 138 | return concatBuffers(buffers); 139 | } 140 | 141 | function readAllSync(r) { 142 | const buffers = []; 143 | 144 | while (true) { 145 | const buf = new Uint8Array(READ_PER_ITER); 146 | const read = r.readSync(buf); 147 | if (typeof read == "number") { 148 | ArrayPrototypePush(buffers, buf.subarray(0, read)); 149 | } else { 150 | break; 151 | } 152 | } 153 | 154 | return concatBuffers(buffers); 155 | } 156 | 157 | function concatBuffers(buffers) { 158 | let totalLen = 0; 159 | for (const buf of buffers) { 160 | totalLen += buf.byteLength; 161 | } 162 | 163 | const contents = new Uint8Array(totalLen); 164 | 165 | let n = 0; 166 | for (const buf of buffers) { 167 | TypedArrayPrototypeSet(contents, buf, n); 168 | n += buf.byteLength; 169 | } 170 | 171 | return contents; 172 | } 173 | 174 | function readAllSyncSized(r, size) { 175 | const buf = new Uint8Array(size + 1); // 1B to detect extended files 176 | let cursor = 0; 177 | 178 | while (cursor < size) { 179 | const sliceEnd = MathMin(size + 1, cursor + READ_PER_ITER); 180 | const slice = buf.subarray(cursor, sliceEnd); 181 | const read = r.readSync(slice); 182 | if (typeof read == "number") { 183 | cursor += read; 184 | } else { 185 | break; 186 | } 187 | } 188 | 189 | // Handle truncated or extended files during read 190 | if (cursor > size) { 191 | // Read remaining and concat 192 | return concatBuffers([buf, readAllSync(r)]); 193 | } else { // cursor == size 194 | return buf.subarray(0, cursor); 195 | } 196 | } 197 | 198 | async function readAllInnerSized(r, size, options) { 199 | const buf = new Uint8Array(size + 1); // 1B to detect extended files 200 | let cursor = 0; 201 | const signal = options?.signal ?? null; 202 | while (cursor < size) { 203 | signal?.throwIfAborted(); 204 | const sliceEnd = MathMin(size + 1, cursor + READ_PER_ITER); 205 | const slice = buf.subarray(cursor, sliceEnd); 206 | const read = await r.read(slice); 207 | if (typeof read == "number") { 208 | cursor += read; 209 | } else { 210 | break; 211 | } 212 | } 213 | signal?.throwIfAborted(); 214 | 215 | // Handle truncated or extended files during read 216 | if (cursor > size) { 217 | // Read remaining and concat 218 | return concatBuffers([buf, await readAllInner(r, options)]); 219 | } else { 220 | return buf.subarray(0, cursor); 221 | } 222 | } 223 | 224 | window.__bootstrap.io = { 225 | iterSync, 226 | iter, 227 | copy, 228 | SeekMode, 229 | read, 230 | readSync, 231 | write, 232 | writeSync, 233 | readAll, 234 | readAllInner, 235 | readAllSync, 236 | readAllSyncSized, 237 | readAllInnerSized, 238 | }; 239 | })(this); 240 | -------------------------------------------------------------------------------- /snapshot/js/13_buffer.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | // This code has been ported almost directly from Go's src/bytes/buffer.go 4 | // Copyright 2009 The Go Authors. All rights reserved. BSD license. 5 | // https://github.com/golang/go/blob/master/LICENSE 6 | "use strict"; 7 | 8 | ((window) => { 9 | const { assert } = window.__bootstrap.infra; 10 | const { 11 | TypedArrayPrototypeSubarray, 12 | TypedArrayPrototypeSlice, 13 | TypedArrayPrototypeSet, 14 | MathFloor, 15 | MathMin, 16 | PromiseResolve, 17 | Uint8Array, 18 | Error, 19 | } = window.__bootstrap.primordials; 20 | 21 | // MIN_READ is the minimum ArrayBuffer size passed to a read call by 22 | // buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond 23 | // what is required to hold the contents of r, readFrom() will not grow the 24 | // underlying buffer. 25 | const MIN_READ = 32 * 1024; 26 | const MAX_SIZE = 2 ** 32 - 2; 27 | 28 | // `off` is the offset into `dst` where it will at which to begin writing values 29 | // from `src`. 30 | // Returns the number of bytes copied. 31 | function copyBytes(src, dst, off = 0) { 32 | const r = dst.byteLength - off; 33 | if (src.byteLength > r) { 34 | src = TypedArrayPrototypeSubarray(src, 0, r); 35 | } 36 | TypedArrayPrototypeSet(dst, src, off); 37 | return src.byteLength; 38 | } 39 | 40 | class Buffer { 41 | #buf = null; // contents are the bytes buf[off : len(buf)] 42 | #off = 0; // read at buf[off], write at buf[buf.byteLength] 43 | 44 | constructor(ab) { 45 | if (ab == null) { 46 | this.#buf = new Uint8Array(0); 47 | return; 48 | } 49 | 50 | this.#buf = new Uint8Array(ab); 51 | } 52 | 53 | bytes(options = { copy: true }) { 54 | if (options.copy === false) { 55 | return TypedArrayPrototypeSubarray(this.#buf, this.#off); 56 | } 57 | return TypedArrayPrototypeSlice(this.#buf, this.#off); 58 | } 59 | 60 | empty() { 61 | return this.#buf.byteLength <= this.#off; 62 | } 63 | 64 | get length() { 65 | return this.#buf.byteLength - this.#off; 66 | } 67 | 68 | get capacity() { 69 | return this.#buf.buffer.byteLength; 70 | } 71 | 72 | truncate(n) { 73 | if (n === 0) { 74 | this.reset(); 75 | return; 76 | } 77 | if (n < 0 || n > this.length) { 78 | throw Error("bytes.Buffer: truncation out of range"); 79 | } 80 | this.#reslice(this.#off + n); 81 | } 82 | 83 | reset() { 84 | this.#reslice(0); 85 | this.#off = 0; 86 | } 87 | 88 | #tryGrowByReslice(n) { 89 | const l = this.#buf.byteLength; 90 | if (n <= this.capacity - l) { 91 | this.#reslice(l + n); 92 | return l; 93 | } 94 | return -1; 95 | } 96 | 97 | #reslice(len) { 98 | assert(len <= this.#buf.buffer.byteLength); 99 | this.#buf = new Uint8Array(this.#buf.buffer, 0, len); 100 | } 101 | 102 | readSync(p) { 103 | if (this.empty()) { 104 | // Buffer is empty, reset to recover space. 105 | this.reset(); 106 | if (p.byteLength === 0) { 107 | // this edge case is tested in 'bufferReadEmptyAtEOF' test 108 | return 0; 109 | } 110 | return null; 111 | } 112 | const nread = copyBytes( 113 | TypedArrayPrototypeSubarray(this.#buf, this.#off), 114 | p, 115 | ); 116 | this.#off += nread; 117 | return nread; 118 | } 119 | 120 | read(p) { 121 | const rr = this.readSync(p); 122 | return PromiseResolve(rr); 123 | } 124 | 125 | writeSync(p) { 126 | const m = this.#grow(p.byteLength); 127 | return copyBytes(p, this.#buf, m); 128 | } 129 | 130 | write(p) { 131 | const n = this.writeSync(p); 132 | return PromiseResolve(n); 133 | } 134 | 135 | #grow(n) { 136 | const m = this.length; 137 | // If buffer is empty, reset to recover space. 138 | if (m === 0 && this.#off !== 0) { 139 | this.reset(); 140 | } 141 | // Fast: Try to grow by means of a reslice. 142 | const i = this.#tryGrowByReslice(n); 143 | if (i >= 0) { 144 | return i; 145 | } 146 | const c = this.capacity; 147 | if (n <= MathFloor(c / 2) - m) { 148 | // We can slide things down instead of allocating a new 149 | // ArrayBuffer. We only need m+n <= c to slide, but 150 | // we instead let capacity get twice as large so we 151 | // don't spend all our time copying. 152 | copyBytes(TypedArrayPrototypeSubarray(this.#buf, this.#off), this.#buf); 153 | } else if (c + n > MAX_SIZE) { 154 | throw new Error("The buffer cannot be grown beyond the maximum size."); 155 | } else { 156 | // Not enough space anywhere, we need to allocate. 157 | const buf = new Uint8Array(MathMin(2 * c + n, MAX_SIZE)); 158 | copyBytes(TypedArrayPrototypeSubarray(this.#buf, this.#off), buf); 159 | this.#buf = buf; 160 | } 161 | // Restore this.#off and len(this.#buf). 162 | this.#off = 0; 163 | this.#reslice(MathMin(m + n, MAX_SIZE)); 164 | return m; 165 | } 166 | 167 | grow(n) { 168 | if (n < 0) { 169 | throw Error("Buffer.grow: negative count"); 170 | } 171 | const m = this.#grow(n); 172 | this.#reslice(m); 173 | } 174 | 175 | async readFrom(r) { 176 | let n = 0; 177 | const tmp = new Uint8Array(MIN_READ); 178 | while (true) { 179 | const shouldGrow = this.capacity - this.length < MIN_READ; 180 | // read into tmp buffer if there's not enough room 181 | // otherwise read directly into the internal buffer 182 | const buf = shouldGrow 183 | ? tmp 184 | : new Uint8Array(this.#buf.buffer, this.length); 185 | 186 | const nread = await r.read(buf); 187 | if (nread === null) { 188 | return n; 189 | } 190 | 191 | // write will grow if needed 192 | if (shouldGrow) { 193 | this.writeSync(TypedArrayPrototypeSubarray(buf, 0, nread)); 194 | } else this.#reslice(this.length + nread); 195 | 196 | n += nread; 197 | } 198 | } 199 | 200 | readFromSync(r) { 201 | let n = 0; 202 | const tmp = new Uint8Array(MIN_READ); 203 | while (true) { 204 | const shouldGrow = this.capacity - this.length < MIN_READ; 205 | // read into tmp buffer if there's not enough room 206 | // otherwise read directly into the internal buffer 207 | const buf = shouldGrow 208 | ? tmp 209 | : new Uint8Array(this.#buf.buffer, this.length); 210 | 211 | const nread = r.readSync(buf); 212 | if (nread === null) { 213 | return n; 214 | } 215 | 216 | // write will grow if needed 217 | if (shouldGrow) { 218 | this.writeSync(TypedArrayPrototypeSubarray(buf, 0, nread)); 219 | } else this.#reslice(this.length + nread); 220 | 221 | n += nread; 222 | } 223 | } 224 | } 225 | 226 | async function readAll(r) { 227 | const buf = new Buffer(); 228 | await buf.readFrom(r); 229 | return buf.bytes(); 230 | } 231 | 232 | function readAllSync(r) { 233 | const buf = new Buffer(); 234 | buf.readFromSync(r); 235 | return buf.bytes(); 236 | } 237 | 238 | async function writeAll(w, arr) { 239 | let nwritten = 0; 240 | while (nwritten < arr.length) { 241 | nwritten += await w.write(TypedArrayPrototypeSubarray(arr, nwritten)); 242 | } 243 | } 244 | 245 | function writeAllSync(w, arr) { 246 | let nwritten = 0; 247 | while (nwritten < arr.length) { 248 | nwritten += w.writeSync(TypedArrayPrototypeSubarray(arr, nwritten)); 249 | } 250 | } 251 | 252 | window.__bootstrap.buffer = { 253 | writeAll, 254 | writeAllSync, 255 | readAll, 256 | readAllSync, 257 | Buffer, 258 | }; 259 | })(this); 260 | -------------------------------------------------------------------------------- /snapshot/js/30_os.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { 8 | Error, 9 | SymbolFor, 10 | } = window.__bootstrap.primordials; 11 | 12 | const windowDispatchEvent = window.dispatchEvent.bind(window); 13 | 14 | function loadavg() { 15 | return ops.op_loadavg(); 16 | } 17 | 18 | function hostname() { 19 | return ops.op_hostname(); 20 | } 21 | 22 | function osRelease() { 23 | return ops.op_os_release(); 24 | } 25 | 26 | function systemMemoryInfo() { 27 | return ops.op_system_memory_info(); 28 | } 29 | 30 | function networkInterfaces() { 31 | return ops.op_network_interfaces(); 32 | } 33 | 34 | function getGid() { 35 | return ops.op_getgid(); 36 | } 37 | 38 | function getUid() { 39 | return ops.op_getuid(); 40 | } 41 | 42 | // This is an internal only method used by the test harness to override the 43 | // behavior of exit when the exit sanitizer is enabled. 44 | let exitHandler = null; 45 | function setExitHandler(fn) { 46 | exitHandler = fn; 47 | } 48 | 49 | function exit(code) { 50 | // Set exit code first so unload event listeners can override it. 51 | if (typeof code === "number") { 52 | ops.op_set_exit_code(code); 53 | } else { 54 | code = 0; 55 | } 56 | 57 | // Dispatches `unload` only when it's not dispatched yet. 58 | if (!window[SymbolFor("isUnloadDispatched")]) { 59 | // Invokes the `unload` hooks before exiting 60 | // ref: https://github.com/denoland/deno/issues/3603 61 | windowDispatchEvent(new Event("unload")); 62 | } 63 | 64 | if (exitHandler) { 65 | exitHandler(code); 66 | return; 67 | } 68 | 69 | ops.op_exit(); 70 | throw new Error("Code not reachable"); 71 | } 72 | 73 | function setEnv(key, value) { 74 | ops.op_set_env(key, value); 75 | } 76 | 77 | function getEnv(key) { 78 | return ops.op_get_env(key) ?? undefined; 79 | } 80 | 81 | function deleteEnv(key) { 82 | ops.op_delete_env(key); 83 | } 84 | 85 | const env = { 86 | get: getEnv, 87 | toObject() { 88 | return ops.op_env(); 89 | }, 90 | set: setEnv, 91 | delete: deleteEnv, 92 | }; 93 | 94 | function execPath() { 95 | return ops.op_exec_path(); 96 | } 97 | 98 | window.__bootstrap.os = { 99 | env, 100 | execPath, 101 | exit, 102 | getGid, 103 | getUid, 104 | hostname, 105 | loadavg, 106 | networkInterfaces, 107 | osRelease, 108 | setExitHandler, 109 | systemMemoryInfo, 110 | }; 111 | })(this); 112 | -------------------------------------------------------------------------------- /snapshot/js/40_diagnostics.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | // Diagnostic provides an abstraction for advice/errors received from a 4 | // compiler, which is strongly influenced by the format of TypeScript 5 | // diagnostics. 6 | "use strict"; 7 | 8 | ((window) => { 9 | const DiagnosticCategory = { 10 | 0: "Warning", 11 | 1: "Error", 12 | 2: "Suggestion", 13 | 3: "Message", 14 | 15 | Warning: 0, 16 | Error: 1, 17 | Suggestion: 2, 18 | Message: 3, 19 | }; 20 | 21 | window.__bootstrap.diagnostics = { 22 | DiagnosticCategory, 23 | }; 24 | })(this); 25 | -------------------------------------------------------------------------------- /snapshot/js/40_files.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { read, readSync, write, writeSync } = window.__bootstrap.io; 8 | const { ftruncate, ftruncateSync, fstat, fstatSync } = window.__bootstrap.fs; 9 | const { pathFromURL } = window.__bootstrap.util; 10 | const { writableStreamForRid } = window.__bootstrap.streamUtils; 11 | const { readableStreamForRid } = window.__bootstrap.streams; 12 | const { 13 | ArrayPrototypeFilter, 14 | Error, 15 | ObjectValues, 16 | } = window.__bootstrap.primordials; 17 | 18 | function seekSync( 19 | rid, 20 | offset, 21 | whence, 22 | ) { 23 | return ops.op_seek_sync({ rid, offset, whence }); 24 | } 25 | 26 | function seek( 27 | rid, 28 | offset, 29 | whence, 30 | ) { 31 | return core.opAsync("op_seek_async", { rid, offset, whence }); 32 | } 33 | 34 | function openSync( 35 | path, 36 | options = { read: true }, 37 | ) { 38 | checkOpenOptions(options); 39 | const mode = options?.mode; 40 | const rid = ops.op_open_sync( 41 | { path: pathFromURL(path), options, mode }, 42 | ); 43 | 44 | return new FsFile(rid); 45 | } 46 | 47 | async function open( 48 | path, 49 | options = { read: true }, 50 | ) { 51 | checkOpenOptions(options); 52 | const mode = options?.mode; 53 | const rid = await core.opAsync( 54 | "op_open_async", 55 | { path: pathFromURL(path), options, mode }, 56 | ); 57 | 58 | return new FsFile(rid); 59 | } 60 | 61 | function createSync(path) { 62 | return openSync(path, { 63 | read: true, 64 | write: true, 65 | truncate: true, 66 | create: true, 67 | }); 68 | } 69 | 70 | function create(path) { 71 | return open(path, { 72 | read: true, 73 | write: true, 74 | truncate: true, 75 | create: true, 76 | }); 77 | } 78 | 79 | class FsFile { 80 | #rid = 0; 81 | 82 | #readable; 83 | #writable; 84 | 85 | constructor(rid) { 86 | this.#rid = rid; 87 | } 88 | 89 | get rid() { 90 | return this.#rid; 91 | } 92 | 93 | write(p) { 94 | return write(this.rid, p); 95 | } 96 | 97 | writeSync(p) { 98 | return writeSync(this.rid, p); 99 | } 100 | 101 | truncate(len) { 102 | return ftruncate(this.rid, len); 103 | } 104 | 105 | truncateSync(len) { 106 | return ftruncateSync(this.rid, len); 107 | } 108 | 109 | read(p) { 110 | return read(this.rid, p); 111 | } 112 | 113 | readSync(p) { 114 | return readSync(this.rid, p); 115 | } 116 | 117 | seek(offset, whence) { 118 | return seek(this.rid, offset, whence); 119 | } 120 | 121 | seekSync(offset, whence) { 122 | return seekSync(this.rid, offset, whence); 123 | } 124 | 125 | stat() { 126 | return fstat(this.rid); 127 | } 128 | 129 | statSync() { 130 | return fstatSync(this.rid); 131 | } 132 | 133 | close() { 134 | core.close(this.rid); 135 | } 136 | 137 | get readable() { 138 | if (this.#readable === undefined) { 139 | this.#readable = readableStreamForRid(this.rid); 140 | } 141 | return this.#readable; 142 | } 143 | 144 | get writable() { 145 | if (this.#writable === undefined) { 146 | this.#writable = writableStreamForRid(this.rid); 147 | } 148 | return this.#writable; 149 | } 150 | } 151 | 152 | class Stdin { 153 | #readable; 154 | 155 | constructor() { 156 | } 157 | 158 | get rid() { 159 | return 0; 160 | } 161 | 162 | read(p) { 163 | return read(this.rid, p); 164 | } 165 | 166 | readSync(p) { 167 | return readSync(this.rid, p); 168 | } 169 | 170 | close() { 171 | core.close(this.rid); 172 | } 173 | 174 | get readable() { 175 | if (this.#readable === undefined) { 176 | this.#readable = readableStreamForRid(this.rid); 177 | } 178 | return this.#readable; 179 | } 180 | } 181 | 182 | class Stdout { 183 | #writable; 184 | 185 | constructor() { 186 | } 187 | 188 | get rid() { 189 | return 1; 190 | } 191 | 192 | write(p) { 193 | return write(this.rid, p); 194 | } 195 | 196 | writeSync(p) { 197 | return writeSync(this.rid, p); 198 | } 199 | 200 | close() { 201 | core.close(this.rid); 202 | } 203 | 204 | get writable() { 205 | if (this.#writable === undefined) { 206 | this.#writable = writableStreamForRid(this.rid); 207 | } 208 | return this.#writable; 209 | } 210 | } 211 | 212 | class Stderr { 213 | #writable; 214 | 215 | constructor() { 216 | } 217 | 218 | get rid() { 219 | return 2; 220 | } 221 | 222 | write(p) { 223 | return write(this.rid, p); 224 | } 225 | 226 | writeSync(p) { 227 | return writeSync(this.rid, p); 228 | } 229 | 230 | close() { 231 | core.close(this.rid); 232 | } 233 | 234 | get writable() { 235 | if (this.#writable === undefined) { 236 | this.#writable = writableStreamForRid(this.rid); 237 | } 238 | return this.#writable; 239 | } 240 | } 241 | 242 | const stdin = new Stdin(); 243 | const stdout = new Stdout(); 244 | const stderr = new Stderr(); 245 | 246 | function checkOpenOptions(options) { 247 | if ( 248 | ArrayPrototypeFilter( 249 | ObjectValues(options), 250 | (val) => val === true, 251 | ).length === 0 252 | ) { 253 | throw new Error("OpenOptions requires at least one option to be true"); 254 | } 255 | 256 | if (options.truncate && !options.write) { 257 | throw new Error("'truncate' option requires 'write' option"); 258 | } 259 | 260 | const createOrCreateNewWithoutWriteOrAppend = 261 | (options.create || options.createNew) && 262 | !(options.write || options.append); 263 | 264 | if (createOrCreateNewWithoutWriteOrAppend) { 265 | throw new Error( 266 | "'create' or 'createNew' options require 'write' or 'append' option", 267 | ); 268 | } 269 | } 270 | 271 | window.__bootstrap.files = { 272 | stdin, 273 | stdout, 274 | stderr, 275 | File: FsFile, 276 | FsFile, 277 | create, 278 | createSync, 279 | open, 280 | openSync, 281 | seek, 282 | seekSync, 283 | }; 284 | })(this); 285 | -------------------------------------------------------------------------------- /snapshot/js/40_fs_events.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { BadResourcePrototype, InterruptedPrototype } = core; 8 | const { 9 | ArrayIsArray, 10 | ObjectPrototypeIsPrototypeOf, 11 | PromiseResolve, 12 | SymbolAsyncIterator, 13 | } = window.__bootstrap.primordials; 14 | class FsWatcher { 15 | #rid = 0; 16 | 17 | constructor(paths, options) { 18 | const { recursive } = options; 19 | this.#rid = ops.op_fs_events_open({ recursive, paths }); 20 | } 21 | 22 | get rid() { 23 | return this.#rid; 24 | } 25 | 26 | async next() { 27 | try { 28 | const value = await core.opAsync("op_fs_events_poll", this.rid); 29 | return value 30 | ? { value, done: false } 31 | : { value: undefined, done: true }; 32 | } catch (error) { 33 | if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { 34 | return { value: undefined, done: true }; 35 | } else if ( 36 | ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error) 37 | ) { 38 | return { value: undefined, done: true }; 39 | } 40 | throw error; 41 | } 42 | } 43 | 44 | // TODO(kt3k): This is deprecated. Will be removed in v2.0. 45 | // See https://github.com/denoland/deno/issues/10577 for details 46 | return(value) { 47 | core.close(this.rid); 48 | return PromiseResolve({ value, done: true }); 49 | } 50 | 51 | close() { 52 | core.close(this.rid); 53 | } 54 | 55 | [SymbolAsyncIterator]() { 56 | return this; 57 | } 58 | } 59 | 60 | function watchFs( 61 | paths, 62 | options = { recursive: true }, 63 | ) { 64 | return new FsWatcher(ArrayIsArray(paths) ? paths : [paths], options); 65 | } 66 | 67 | window.__bootstrap.fsEvents = { 68 | watchFs, 69 | }; 70 | })(this); 71 | -------------------------------------------------------------------------------- /snapshot/js/40_http.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.__bootstrap.core; 6 | const ops = core.ops; 7 | const { HttpConn } = window.__bootstrap.http; 8 | 9 | function serveHttp(conn) { 10 | const rid = ops.op_http_start(conn.rid); 11 | return new HttpConn(rid, conn.remoteAddr, conn.localAddr); 12 | } 13 | 14 | window.__bootstrap.http.serveHttp = serveHttp; 15 | })(globalThis); 16 | -------------------------------------------------------------------------------- /snapshot/js/40_process.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { FsFile } = window.__bootstrap.files; 8 | const { readAll } = window.__bootstrap.io; 9 | const { pathFromURL } = window.__bootstrap.util; 10 | const { assert } = window.__bootstrap.infra; 11 | const { 12 | ArrayPrototypeMap, 13 | ArrayPrototypeSlice, 14 | TypeError, 15 | ObjectEntries, 16 | String, 17 | } = window.__bootstrap.primordials; 18 | 19 | function opKill(pid, signo) { 20 | ops.op_kill(pid, signo); 21 | } 22 | 23 | function opRunStatus(rid) { 24 | return core.opAsync("op_run_status", rid); 25 | } 26 | 27 | function opRun(request) { 28 | assert(request.cmd.length > 0); 29 | return ops.op_run(request); 30 | } 31 | 32 | async function runStatus(rid) { 33 | const res = await opRunStatus(rid); 34 | 35 | if (res.gotSignal) { 36 | const signal = res.exitSignal; 37 | return { success: false, code: 128 + signal, signal }; 38 | } else if (res.exitCode != 0) { 39 | return { success: false, code: res.exitCode }; 40 | } else { 41 | return { success: true, code: 0 }; 42 | } 43 | } 44 | 45 | class Process { 46 | constructor(res) { 47 | this.rid = res.rid; 48 | this.pid = res.pid; 49 | 50 | if (res.stdinRid && res.stdinRid > 0) { 51 | this.stdin = new FsFile(res.stdinRid); 52 | } 53 | 54 | if (res.stdoutRid && res.stdoutRid > 0) { 55 | this.stdout = new FsFile(res.stdoutRid); 56 | } 57 | 58 | if (res.stderrRid && res.stderrRid > 0) { 59 | this.stderr = new FsFile(res.stderrRid); 60 | } 61 | } 62 | 63 | status() { 64 | return runStatus(this.rid); 65 | } 66 | 67 | async output() { 68 | if (!this.stdout) { 69 | throw new TypeError("stdout was not piped"); 70 | } 71 | try { 72 | return await readAll(this.stdout); 73 | } finally { 74 | this.stdout.close(); 75 | } 76 | } 77 | 78 | async stderrOutput() { 79 | if (!this.stderr) { 80 | throw new TypeError("stderr was not piped"); 81 | } 82 | try { 83 | return await readAll(this.stderr); 84 | } finally { 85 | this.stderr.close(); 86 | } 87 | } 88 | 89 | close() { 90 | core.close(this.rid); 91 | } 92 | 93 | kill(signo) { 94 | opKill(this.pid, signo); 95 | } 96 | } 97 | 98 | function run({ 99 | cmd, 100 | cwd = undefined, 101 | clearEnv = false, 102 | env = {}, 103 | gid = undefined, 104 | uid = undefined, 105 | stdout = "inherit", 106 | stderr = "inherit", 107 | stdin = "inherit", 108 | }) { 109 | if (cmd[0] != null) { 110 | cmd = [pathFromURL(cmd[0]), ...ArrayPrototypeSlice(cmd, 1)]; 111 | } 112 | const res = opRun({ 113 | cmd: ArrayPrototypeMap(cmd, String), 114 | cwd, 115 | clearEnv, 116 | env: ObjectEntries(env), 117 | gid, 118 | uid, 119 | stdin, 120 | stdout, 121 | stderr, 122 | }); 123 | return new Process(res); 124 | } 125 | 126 | window.__bootstrap.process = { 127 | run, 128 | Process, 129 | kill: opKill, 130 | }; 131 | })(this); 132 | -------------------------------------------------------------------------------- /snapshot/js/40_read_file.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { pathFromURL } = window.__bootstrap.util; 8 | const { abortSignal } = window.__bootstrap; 9 | 10 | function readFileSync(path) { 11 | return ops.op_readfile_sync(pathFromURL(path)); 12 | } 13 | 14 | async function readFile(path, options) { 15 | let cancelRid; 16 | let abortHandler; 17 | if (options?.signal) { 18 | options.signal.throwIfAborted(); 19 | cancelRid = ops.op_cancel_handle(); 20 | abortHandler = () => core.tryClose(cancelRid); 21 | options.signal[abortSignal.add](abortHandler); 22 | } 23 | 24 | try { 25 | const read = await core.opAsync( 26 | "op_readfile_async", 27 | pathFromURL(path), 28 | cancelRid, 29 | ); 30 | return read; 31 | } finally { 32 | if (options?.signal) { 33 | options.signal[abortSignal.remove](abortHandler); 34 | 35 | // always throw the abort error when aborted 36 | options.signal.throwIfAborted(); 37 | } 38 | } 39 | } 40 | 41 | function readTextFileSync(path) { 42 | return ops.op_readfile_text_sync(pathFromURL(path)); 43 | } 44 | 45 | async function readTextFile(path, options) { 46 | let cancelRid; 47 | let abortHandler; 48 | if (options?.signal) { 49 | options.signal.throwIfAborted(); 50 | cancelRid = ops.op_cancel_handle(); 51 | abortHandler = () => core.tryClose(cancelRid); 52 | options.signal[abortSignal.add](abortHandler); 53 | } 54 | 55 | try { 56 | const read = await core.opAsync( 57 | "op_readfile_text_async", 58 | pathFromURL(path), 59 | cancelRid, 60 | ); 61 | return read; 62 | } finally { 63 | if (options?.signal) { 64 | options.signal[abortSignal.remove](abortHandler); 65 | 66 | // always throw the abort error when aborted 67 | options.signal.throwIfAborted(); 68 | } 69 | } 70 | } 71 | 72 | window.__bootstrap.readFile = { 73 | readFile, 74 | readFileSync, 75 | readTextFileSync, 76 | readTextFile, 77 | }; 78 | })(this); 79 | -------------------------------------------------------------------------------- /snapshot/js/40_signals.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { 8 | Set, 9 | SymbolFor, 10 | TypeError, 11 | } = window.__bootstrap.primordials; 12 | 13 | function bindSignal(signo) { 14 | return ops.op_signal_bind(signo); 15 | } 16 | 17 | function pollSignal(rid) { 18 | const promise = core.opAsync("op_signal_poll", rid); 19 | core.unrefOp(promise[SymbolFor("Deno.core.internalPromiseId")]); 20 | return promise; 21 | } 22 | 23 | function unbindSignal(rid) { 24 | ops.op_signal_unbind(rid); 25 | } 26 | 27 | // Stores signal listeners and resource data. This has type of 28 | // `Record void> }` 29 | const signalData = {}; 30 | 31 | /** Gets the signal handlers and resource data of the given signal */ 32 | function getSignalData(signo) { 33 | return signalData[signo] ?? 34 | (signalData[signo] = { rid: undefined, listeners: new Set() }); 35 | } 36 | 37 | function checkSignalListenerType(listener) { 38 | if (typeof listener !== "function") { 39 | throw new TypeError( 40 | `Signal listener must be a function. "${typeof listener}" is given.`, 41 | ); 42 | } 43 | } 44 | 45 | function addSignalListener(signo, listener) { 46 | checkSignalListenerType(listener); 47 | 48 | const sigData = getSignalData(signo); 49 | sigData.listeners.add(listener); 50 | 51 | if (!sigData.rid) { 52 | // If signal resource doesn't exist, create it. 53 | // The program starts listening to the signal 54 | sigData.rid = bindSignal(signo); 55 | loop(sigData); 56 | } 57 | } 58 | 59 | function removeSignalListener(signo, listener) { 60 | checkSignalListenerType(listener); 61 | 62 | const sigData = getSignalData(signo); 63 | sigData.listeners.delete(listener); 64 | 65 | if (sigData.listeners.size === 0 && sigData.rid) { 66 | unbindSignal(sigData.rid); 67 | sigData.rid = undefined; 68 | } 69 | } 70 | 71 | async function loop(sigData) { 72 | while (sigData.rid) { 73 | if (await pollSignal(sigData.rid)) { 74 | return; 75 | } 76 | for (const listener of sigData.listeners) { 77 | listener(); 78 | } 79 | } 80 | } 81 | 82 | window.__bootstrap.signals = { 83 | addSignalListener, 84 | removeSignalListener, 85 | }; 86 | })(this); 87 | -------------------------------------------------------------------------------- /snapshot/js/40_spawn.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | const { pathFromURL } = window.__bootstrap.util; 8 | const { illegalConstructorKey } = window.__bootstrap.webUtil; 9 | const { add, remove } = window.__bootstrap.abortSignal; 10 | const { 11 | ArrayPrototypeMap, 12 | ObjectEntries, 13 | String, 14 | TypeError, 15 | Uint8Array, 16 | PromiseAll, 17 | SymbolFor, 18 | } = window.__bootstrap.primordials; 19 | const { readableStreamForRid, writableStreamForRid } = 20 | window.__bootstrap.streamUtils; 21 | 22 | const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); 23 | 24 | function spawnChild(command, { 25 | args = [], 26 | cwd = undefined, 27 | clearEnv = false, 28 | env = {}, 29 | uid = undefined, 30 | gid = undefined, 31 | stdin = "null", 32 | stdout = "piped", 33 | stderr = "piped", 34 | signal = undefined, 35 | } = {}) { 36 | const child = ops.op_spawn_child({ 37 | cmd: pathFromURL(command), 38 | args: ArrayPrototypeMap(args, String), 39 | cwd: pathFromURL(cwd), 40 | clearEnv, 41 | env: ObjectEntries(env), 42 | uid, 43 | gid, 44 | stdin, 45 | stdout, 46 | stderr, 47 | }); 48 | return new Child(illegalConstructorKey, { 49 | ...child, 50 | signal, 51 | }); 52 | } 53 | 54 | async function collectOutput(readableStream) { 55 | if (!(readableStream instanceof ReadableStream)) { 56 | return null; 57 | } 58 | 59 | const bufs = []; 60 | let size = 0; 61 | for await (const chunk of readableStream) { 62 | bufs.push(chunk); 63 | size += chunk.byteLength; 64 | } 65 | 66 | const buffer = new Uint8Array(size); 67 | let offset = 0; 68 | for (const chunk of bufs) { 69 | buffer.set(chunk, offset); 70 | offset += chunk.byteLength; 71 | } 72 | 73 | return buffer; 74 | } 75 | 76 | class Child { 77 | #rid; 78 | #waitPromiseId; 79 | #unrefed = false; 80 | 81 | #pid; 82 | get pid() { 83 | return this.#pid; 84 | } 85 | 86 | #stdin = null; 87 | get stdin() { 88 | if (this.#stdin == null) { 89 | throw new TypeError("stdin is not piped"); 90 | } 91 | return this.#stdin; 92 | } 93 | 94 | #stdoutPromiseId; 95 | #stdoutRid; 96 | #stdout = null; 97 | get stdout() { 98 | if (this.#stdout == null) { 99 | throw new TypeError("stdout is not piped"); 100 | } 101 | return this.#stdout; 102 | } 103 | 104 | #stderrPromiseId; 105 | #stderrRid; 106 | #stderr = null; 107 | get stderr() { 108 | if (this.#stderr == null) { 109 | throw new TypeError("stderr is not piped"); 110 | } 111 | return this.#stderr; 112 | } 113 | 114 | constructor(key = null, { 115 | signal, 116 | rid, 117 | pid, 118 | stdinRid, 119 | stdoutRid, 120 | stderrRid, 121 | } = null) { 122 | if (key !== illegalConstructorKey) { 123 | throw new TypeError("Illegal constructor."); 124 | } 125 | 126 | this.#rid = rid; 127 | this.#pid = pid; 128 | 129 | if (stdinRid !== null) { 130 | this.#stdin = writableStreamForRid(stdinRid); 131 | } 132 | 133 | if (stdoutRid !== null) { 134 | this.#stdoutRid = stdoutRid; 135 | this.#stdout = readableStreamForRid(stdoutRid, (promise) => { 136 | this.#stdoutPromiseId = promise[promiseIdSymbol]; 137 | if (this.#unrefed) core.unrefOp(this.#stdoutPromiseId); 138 | }); 139 | } 140 | 141 | if (stderrRid !== null) { 142 | this.#stderrRid = stderrRid; 143 | this.#stderr = readableStreamForRid(stderrRid, (promise) => { 144 | this.#stderrPromiseId = promise[promiseIdSymbol]; 145 | if (this.#unrefed) core.unrefOp(this.#stderrPromiseId); 146 | }); 147 | } 148 | 149 | const onAbort = () => this.kill("SIGTERM"); 150 | signal?.[add](onAbort); 151 | 152 | const waitPromise = core.opAsync("op_spawn_wait", this.#rid); 153 | this.#waitPromiseId = waitPromise[promiseIdSymbol]; 154 | this.#status = waitPromise.then((res) => { 155 | this.#rid = null; 156 | signal?.[remove](onAbort); 157 | return res; 158 | }); 159 | } 160 | 161 | #status; 162 | get status() { 163 | return this.#status; 164 | } 165 | 166 | async output() { 167 | if (this.#stdout?.locked) { 168 | throw new TypeError( 169 | "Can't collect output because stdout is locked", 170 | ); 171 | } 172 | if (this.#stderr?.locked) { 173 | throw new TypeError( 174 | "Can't collect output because stderr is locked", 175 | ); 176 | } 177 | 178 | const [status, stdout, stderr] = await PromiseAll([ 179 | this.#status, 180 | collectOutput(this.#stdout), 181 | collectOutput(this.#stderr), 182 | ]); 183 | 184 | return { 185 | success: status.success, 186 | code: status.code, 187 | signal: status.signal, 188 | get stdout() { 189 | if (stdout == null) { 190 | throw new TypeError("stdout is not piped"); 191 | } 192 | return stdout; 193 | }, 194 | get stderr() { 195 | if (stderr == null) { 196 | throw new TypeError("stderr is not piped"); 197 | } 198 | return stderr; 199 | }, 200 | }; 201 | } 202 | 203 | kill(signo = "SIGTERM") { 204 | if (this.#rid === null) { 205 | throw new TypeError("Child process has already terminated."); 206 | } 207 | ops.op_kill(this.#pid, signo); 208 | } 209 | 210 | ref() { 211 | this.#unrefed = false; 212 | core.refOp(this.#waitPromiseId); 213 | if (this.#stdoutPromiseId) core.refOp(this.#stdoutPromiseId); 214 | if (this.#stderrPromiseId) core.refOp(this.#stderrPromiseId); 215 | } 216 | 217 | unref() { 218 | this.#unrefed = true; 219 | core.unrefOp(this.#waitPromiseId); 220 | if (this.#stdoutPromiseId) core.unrefOp(this.#stdoutPromiseId); 221 | if (this.#stderrPromiseId) core.unrefOp(this.#stderrPromiseId); 222 | } 223 | } 224 | 225 | function spawn(command, options) { 226 | if (options?.stdin === "piped") { 227 | throw new TypeError( 228 | "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", 229 | ); 230 | } 231 | return spawnChild(command, options).output(); 232 | } 233 | 234 | function spawnSync(command, { 235 | args = [], 236 | cwd = undefined, 237 | clearEnv = false, 238 | env = {}, 239 | uid = undefined, 240 | gid = undefined, 241 | stdin = "null", 242 | stdout = "piped", 243 | stderr = "piped", 244 | } = {}) { 245 | if (stdin === "piped") { 246 | throw new TypeError( 247 | "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", 248 | ); 249 | } 250 | const result = ops.op_spawn_sync({ 251 | cmd: pathFromURL(command), 252 | args: ArrayPrototypeMap(args, String), 253 | cwd: pathFromURL(cwd), 254 | clearEnv, 255 | env: ObjectEntries(env), 256 | uid, 257 | gid, 258 | stdin, 259 | stdout, 260 | stderr, 261 | }); 262 | return { 263 | success: result.status.success, 264 | code: result.status.code, 265 | signal: result.status.signal, 266 | get stdout() { 267 | if (result.stdout == null) { 268 | throw new TypeError("stdout is not piped"); 269 | } 270 | return result.stdout; 271 | }, 272 | get stderr() { 273 | if (result.stderr == null) { 274 | throw new TypeError("stderr is not piped"); 275 | } 276 | return result.stderr; 277 | }, 278 | }; 279 | } 280 | 281 | window.__bootstrap.spawn = { 282 | Child, 283 | spawnChild, 284 | spawn, 285 | spawnSync, 286 | }; 287 | })(this); 288 | -------------------------------------------------------------------------------- /snapshot/js/40_tty.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const ops = core.ops; 7 | 8 | function consoleSize(rid) { 9 | return ops.op_console_size(rid); 10 | } 11 | 12 | function isatty(rid) { 13 | return ops.op_isatty(rid); 14 | } 15 | 16 | const DEFAULT_SET_RAW_OPTIONS = { 17 | cbreak: false, 18 | }; 19 | 20 | function setRaw(rid, mode, options = {}) { 21 | const rOptions = { ...DEFAULT_SET_RAW_OPTIONS, ...options }; 22 | ops.op_set_raw({ rid, mode, options: rOptions }); 23 | } 24 | 25 | window.__bootstrap.tty = { 26 | consoleSize, 27 | isatty, 28 | setRaw, 29 | }; 30 | })(this); 31 | -------------------------------------------------------------------------------- /snapshot/js/40_write_file.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | ((window) => { 4 | const core = window.__bootstrap.core; 5 | const ops = core.ops; 6 | const { abortSignal } = window.__bootstrap; 7 | const { pathFromURL } = window.__bootstrap.util; 8 | 9 | function writeFileSync( 10 | path, 11 | data, 12 | options = {}, 13 | ) { 14 | options.signal?.throwIfAborted(); 15 | ops.op_write_file_sync({ 16 | path: pathFromURL(path), 17 | data, 18 | mode: options.mode, 19 | append: options.append ?? false, 20 | create: options.create ?? true, 21 | }); 22 | } 23 | 24 | async function writeFile( 25 | path, 26 | data, 27 | options = {}, 28 | ) { 29 | let cancelRid; 30 | let abortHandler; 31 | if (options.signal) { 32 | options.signal.throwIfAborted(); 33 | cancelRid = ops.op_cancel_handle(); 34 | abortHandler = () => core.tryClose(cancelRid); 35 | options.signal[abortSignal.add](abortHandler); 36 | } 37 | try { 38 | await core.opAsync("op_write_file_async", { 39 | path: pathFromURL(path), 40 | data, 41 | mode: options.mode, 42 | append: options.append ?? false, 43 | create: options.create ?? true, 44 | cancelRid, 45 | }); 46 | } finally { 47 | if (options.signal) { 48 | options.signal[abortSignal.remove](abortHandler); 49 | 50 | // always throw the abort error when aborted 51 | options.signal.throwIfAborted(); 52 | } 53 | } 54 | } 55 | 56 | function writeTextFileSync( 57 | path, 58 | data, 59 | options = {}, 60 | ) { 61 | const encoder = new TextEncoder(); 62 | return writeFileSync(path, encoder.encode(data), options); 63 | } 64 | 65 | function writeTextFile( 66 | path, 67 | data, 68 | options = {}, 69 | ) { 70 | const encoder = new TextEncoder(); 71 | return writeFile(path, encoder.encode(data), options); 72 | } 73 | 74 | window.__bootstrap.writeFile = { 75 | writeTextFile, 76 | writeTextFileSync, 77 | writeFile, 78 | writeFileSync, 79 | }; 80 | })(this); 81 | -------------------------------------------------------------------------------- /snapshot/js/41_prompt.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | ((window) => { 4 | const { stdin } = window.__bootstrap.files; 5 | const { ArrayPrototypePush, StringPrototypeCharCodeAt, Uint8Array } = 6 | window.__bootstrap.primordials; 7 | const { isatty } = window.__bootstrap.tty; 8 | const LF = StringPrototypeCharCodeAt("\n", 0); 9 | const CR = StringPrototypeCharCodeAt("\r", 0); 10 | const core = window.Deno.core; 11 | 12 | function alert(message = "Alert") { 13 | if (!isatty(stdin.rid)) { 14 | return; 15 | } 16 | 17 | core.print(`${message} [Enter] `, false); 18 | 19 | readLineFromStdinSync(); 20 | } 21 | 22 | function confirm(message = "Confirm") { 23 | if (!isatty(stdin.rid)) { 24 | return false; 25 | } 26 | 27 | core.print(`${message} [y/N] `, false); 28 | 29 | const answer = readLineFromStdinSync(); 30 | 31 | return answer === "Y" || answer === "y"; 32 | } 33 | 34 | function prompt(message = "Prompt", defaultValue) { 35 | defaultValue ??= null; 36 | 37 | if (!isatty(stdin.rid)) { 38 | return null; 39 | } 40 | 41 | core.print(`${message} `, false); 42 | 43 | if (defaultValue) { 44 | core.print(`[${defaultValue}] `, false); 45 | } 46 | 47 | return readLineFromStdinSync() || defaultValue; 48 | } 49 | 50 | function readLineFromStdinSync() { 51 | const c = new Uint8Array(1); 52 | const buf = []; 53 | 54 | while (true) { 55 | const n = stdin.readSync(c); 56 | if (n === null || n === 0) { 57 | break; 58 | } 59 | if (c[0] === CR) { 60 | const n = stdin.readSync(c); 61 | if (c[0] === LF) { 62 | break; 63 | } 64 | ArrayPrototypePush(buf, CR); 65 | if (n === null || n === 0) { 66 | break; 67 | } 68 | } 69 | if (c[0] === LF) { 70 | break; 71 | } 72 | ArrayPrototypePush(buf, c[0]); 73 | } 74 | return core.decode(new Uint8Array(buf)); 75 | } 76 | 77 | window.__bootstrap.prompt = { 78 | alert, 79 | confirm, 80 | prompt, 81 | }; 82 | })(this); 83 | -------------------------------------------------------------------------------- /snapshot/js/90_deno_ns.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | "use strict"; 3 | 4 | ((window) => { 5 | const core = window.Deno.core; 6 | const __bootstrap = window.__bootstrap; 7 | __bootstrap.denoNs = { 8 | metrics: core.metrics, 9 | test: __bootstrap.testing.test, 10 | bench: __bootstrap.testing.bench, 11 | Process: __bootstrap.process.Process, 12 | run: __bootstrap.process.run, 13 | isatty: __bootstrap.tty.isatty, 14 | writeFileSync: __bootstrap.writeFile.writeFileSync, 15 | writeFile: __bootstrap.writeFile.writeFile, 16 | writeTextFileSync: __bootstrap.writeFile.writeTextFileSync, 17 | writeTextFile: __bootstrap.writeFile.writeTextFile, 18 | readTextFile: __bootstrap.readFile.readTextFile, 19 | readTextFileSync: __bootstrap.readFile.readTextFileSync, 20 | readFile: __bootstrap.readFile.readFile, 21 | readFileSync: __bootstrap.readFile.readFileSync, 22 | watchFs: __bootstrap.fsEvents.watchFs, 23 | chmodSync: __bootstrap.fs.chmodSync, 24 | chmod: __bootstrap.fs.chmod, 25 | chown: __bootstrap.fs.chown, 26 | chownSync: __bootstrap.fs.chownSync, 27 | copyFileSync: __bootstrap.fs.copyFileSync, 28 | cwd: __bootstrap.fs.cwd, 29 | makeTempDirSync: __bootstrap.fs.makeTempDirSync, 30 | makeTempDir: __bootstrap.fs.makeTempDir, 31 | makeTempFileSync: __bootstrap.fs.makeTempFileSync, 32 | makeTempFile: __bootstrap.fs.makeTempFile, 33 | memoryUsage: core.memoryUsage, 34 | mkdirSync: __bootstrap.fs.mkdirSync, 35 | mkdir: __bootstrap.fs.mkdir, 36 | chdir: __bootstrap.fs.chdir, 37 | copyFile: __bootstrap.fs.copyFile, 38 | readDirSync: __bootstrap.fs.readDirSync, 39 | readDir: __bootstrap.fs.readDir, 40 | readLinkSync: __bootstrap.fs.readLinkSync, 41 | readLink: __bootstrap.fs.readLink, 42 | realPathSync: __bootstrap.fs.realPathSync, 43 | realPath: __bootstrap.fs.realPath, 44 | removeSync: __bootstrap.fs.removeSync, 45 | remove: __bootstrap.fs.remove, 46 | renameSync: __bootstrap.fs.renameSync, 47 | rename: __bootstrap.fs.rename, 48 | version: __bootstrap.version.version, 49 | build: __bootstrap.build.build, 50 | statSync: __bootstrap.fs.statSync, 51 | lstatSync: __bootstrap.fs.lstatSync, 52 | stat: __bootstrap.fs.stat, 53 | lstat: __bootstrap.fs.lstat, 54 | truncateSync: __bootstrap.fs.truncateSync, 55 | truncate: __bootstrap.fs.truncate, 56 | ftruncateSync: __bootstrap.fs.ftruncateSync, 57 | ftruncate: __bootstrap.fs.ftruncate, 58 | errors: __bootstrap.errors.errors, 59 | // TODO(kt3k): Remove this export at v2 60 | // See https://github.com/denoland/deno/issues/9294 61 | customInspect: __bootstrap.console.customInspect, 62 | inspect: __bootstrap.console.inspect, 63 | env: __bootstrap.os.env, 64 | exit: __bootstrap.os.exit, 65 | execPath: __bootstrap.os.execPath, 66 | Buffer: __bootstrap.buffer.Buffer, 67 | readAll: __bootstrap.buffer.readAll, 68 | readAllSync: __bootstrap.buffer.readAllSync, 69 | writeAll: __bootstrap.buffer.writeAll, 70 | writeAllSync: __bootstrap.buffer.writeAllSync, 71 | copy: __bootstrap.io.copy, 72 | iter: __bootstrap.io.iter, 73 | iterSync: __bootstrap.io.iterSync, 74 | SeekMode: __bootstrap.io.SeekMode, 75 | read: __bootstrap.io.read, 76 | readSync: __bootstrap.io.readSync, 77 | write: __bootstrap.io.write, 78 | writeSync: __bootstrap.io.writeSync, 79 | File: __bootstrap.files.File, 80 | FsFile: __bootstrap.files.FsFile, 81 | open: __bootstrap.files.open, 82 | openSync: __bootstrap.files.openSync, 83 | create: __bootstrap.files.create, 84 | createSync: __bootstrap.files.createSync, 85 | stdin: __bootstrap.files.stdin, 86 | stdout: __bootstrap.files.stdout, 87 | stderr: __bootstrap.files.stderr, 88 | seek: __bootstrap.files.seek, 89 | seekSync: __bootstrap.files.seekSync, 90 | connect: __bootstrap.net.connect, 91 | listen: __bootstrap.net.listen, 92 | connectTls: __bootstrap.tls.connectTls, 93 | listenTls: __bootstrap.tls.listenTls, 94 | startTls: __bootstrap.tls.startTls, 95 | shutdown: __bootstrap.net.shutdown, 96 | fstatSync: __bootstrap.fs.fstatSync, 97 | fstat: __bootstrap.fs.fstat, 98 | fsyncSync: __bootstrap.fs.fsyncSync, 99 | fsync: __bootstrap.fs.fsync, 100 | fdatasyncSync: __bootstrap.fs.fdatasyncSync, 101 | fdatasync: __bootstrap.fs.fdatasync, 102 | symlink: __bootstrap.fs.symlink, 103 | symlinkSync: __bootstrap.fs.symlinkSync, 104 | link: __bootstrap.fs.link, 105 | linkSync: __bootstrap.fs.linkSync, 106 | permissions: __bootstrap.permissions.permissions, 107 | Permissions: __bootstrap.permissions.Permissions, 108 | PermissionStatus: __bootstrap.permissions.PermissionStatus, 109 | serveHttp: __bootstrap.http.serveHttp, 110 | resolveDns: __bootstrap.net.resolveDns, 111 | upgradeWebSocket: __bootstrap.http.upgradeWebSocket, 112 | upgradeHttp: __bootstrap.http.upgradeHttp, 113 | kill: __bootstrap.process.kill, 114 | addSignalListener: __bootstrap.signals.addSignalListener, 115 | removeSignalListener: __bootstrap.signals.removeSignalListener, 116 | }; 117 | 118 | __bootstrap.denoNsUnstable = { 119 | setRaw: __bootstrap.tty.setRaw, 120 | consoleSize: __bootstrap.tty.consoleSize, 121 | DiagnosticCategory: __bootstrap.diagnostics.DiagnosticCategory, 122 | loadavg: __bootstrap.os.loadavg, 123 | hostname: __bootstrap.os.hostname, 124 | osRelease: __bootstrap.os.osRelease, 125 | systemMemoryInfo: __bootstrap.os.systemMemoryInfo, 126 | networkInterfaces: __bootstrap.os.networkInterfaces, 127 | getGid: __bootstrap.os.getGid, 128 | getUid: __bootstrap.os.getUid, 129 | listen: __bootstrap.netUnstable.listen, 130 | connect: __bootstrap.netUnstable.connect, 131 | listenDatagram: __bootstrap.netUnstable.listenDatagram, 132 | Listener: __bootstrap.netUnstable.Listener, 133 | umask: __bootstrap.fs.umask, 134 | futime: __bootstrap.fs.futime, 135 | futimeSync: __bootstrap.fs.futimeSync, 136 | utime: __bootstrap.fs.utime, 137 | utimeSync: __bootstrap.fs.utimeSync, 138 | HttpClient: __bootstrap.fetch.HttpClient, 139 | createHttpClient: __bootstrap.fetch.createHttpClient, 140 | http: __bootstrap.http, 141 | // dlopen: __bootstrap.ffi.dlopen, 142 | // UnsafeCallback: __bootstrap.ffi.UnsafeCallback, 143 | // UnsafePointer: __bootstrap.ffi.UnsafePointer, 144 | // UnsafePointerView: __bootstrap.ffi.UnsafePointerView, 145 | // UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer, 146 | flock: __bootstrap.fs.flock, 147 | flockSync: __bootstrap.fs.flockSync, 148 | funlock: __bootstrap.fs.funlock, 149 | funlockSync: __bootstrap.fs.funlockSync, 150 | refTimer: __bootstrap.timers.refTimer, 151 | unrefTimer: __bootstrap.timers.unrefTimer, 152 | Child: __bootstrap.spawn.Child, 153 | spawnChild: __bootstrap.spawn.spawnChild, 154 | spawn: __bootstrap.spawn.spawn, 155 | spawnSync: __bootstrap.spawn.spawnSync, 156 | }; 157 | })(this); 158 | -------------------------------------------------------------------------------- /snapshot/js/README.md: -------------------------------------------------------------------------------- 1 | # Runtime JavaScript Code 2 | 3 | This directory contains Deno runtime code written in plain JavaScript. 4 | 5 | Each file is a plain, old **script**, not ES modules. The reason is that 6 | snapshotting ES modules is much harder, especially if one needs to manipulate 7 | global scope (like in case of Deno). 8 | 9 | Each file is prefixed with a number, telling in which order scripts should be 10 | loaded into V8 isolate. This is temporary solution and we're striving not to 11 | require specific order (though it's not 100% obvious if that's feasible). 12 | 13 | ## Deno Web APIs 14 | 15 | This directory facilities Web APIs that are available in Deno. 16 | 17 | Please note, that some implementations might not be completely aligned with 18 | specification. 19 | 20 | Some Web APIs are using ops under the hood, eg. `console`, `performance`. 21 | 22 | ## Implemented Web APIs 23 | 24 | - [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob): for 25 | representing opaque binary data. 26 | - [Console](https://developer.mozilla.org/en-US/docs/Web/API/Console): for 27 | logging purposes. 28 | - [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent), 29 | [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) 30 | and 31 | [EventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventListener): 32 | to work with DOM events. 33 | - **Implementation notes:** There is no DOM hierarchy in Deno, so there is no 34 | tree for Events to bubble/capture through. 35 | - [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch), 36 | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request), 37 | [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response), 38 | [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body) and 39 | [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers): modern 40 | Promise-based HTTP Request API. 41 | - [location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) 42 | and [Location](https://developer.mozilla.org/en-US/docs/Web/API/Location). 43 | - [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData): access 44 | to a `multipart/form-data` serialization. 45 | - [Performance](https://developer.mozilla.org/en-US/docs/Web/API/Performance): 46 | retrieving current time with a high precision. 47 | - [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout), 48 | [setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval), 49 | [clearTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout): 50 | scheduling callbacks in future and 51 | [clearInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval). 52 | - [Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) for 53 | creating, composing, and consuming streams of data. 54 | - [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) and 55 | [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams): 56 | to construct and parse URLSs. 57 | - [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker): executing 58 | additional code in a separate thread. 59 | - **Implementation notes:** Blob URLs are not supported, object ownership 60 | cannot be transferred, posted data is serialized to JSON instead of 61 | [structured cloning](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). 62 | -------------------------------------------------------------------------------- /snapshot/src/builder.rs: -------------------------------------------------------------------------------- 1 | use deno_broadcast_channel::InMemoryBroadcastChannel; 2 | use deno_core::{anyhow::Result, Extension, JsRuntime, RuntimeOptions}; 3 | use deno_web::BlobStore; 4 | use std::{env, path::PathBuf}; 5 | 6 | use crate::permissions::Permissions; 7 | 8 | const JS_PATHS: &[&str] = &["js/**/*.js"]; 9 | 10 | pub fn create_snapshot_with_main_module( 11 | exts: Vec, 12 | files: &[PathBuf], 13 | code: Option, 14 | ) -> Result> { 15 | _create_snapshot(exts, files, code, false) 16 | } 17 | 18 | pub fn create_snapshot(exts: Vec, files: &[PathBuf]) -> Result> { 19 | _create_snapshot(exts, files, None, true) 20 | } 21 | 22 | pub fn _create_snapshot( 23 | exts: Vec, 24 | files: &[PathBuf], 25 | code: Option, 26 | build: bool, 27 | ) -> Result> { 28 | // Order matters! 29 | let mut extensions = vec![ 30 | deno_webidl::init(), 31 | deno_console::init(), 32 | deno_url::init(), 33 | deno_web::init::(BlobStore::default(), Default::default()), 34 | deno_fetch::init::(Default::default()), 35 | deno_websocket::init::("".to_owned(), None, None), 36 | deno_webstorage::init(None), 37 | deno_crypto::init(None), 38 | #[cfg(feature = "build_webgpu")] 39 | deno_webgpu::init(false), 40 | deno_broadcast_channel::init(InMemoryBroadcastChannel::default(), false), 41 | deno_tls::init(), 42 | deno_net::init::( 43 | None, false, // No --unstable. 44 | None, 45 | ), 46 | deno_http::init(), 47 | ]; 48 | extensions.extend(exts); 49 | 50 | let rt = JsRuntime::new(RuntimeOptions { 51 | will_snapshot: true, 52 | extensions, 53 | ..Default::default() 54 | }); 55 | _gen_snapshot(rt, files, code, build) 56 | } 57 | 58 | pub fn get_js_files(base_dir: &str, paths: &[&str]) -> Vec { 59 | let mut files: Vec<_> = paths 60 | .iter() 61 | .flat_map(|p| { 62 | glob::glob(format!("{}/{}", base_dir, p).as_str()) 63 | .unwrap() 64 | .filter_map(Result::ok) 65 | .collect::>() 66 | }) 67 | .collect(); 68 | 69 | files.sort(); 70 | files 71 | } 72 | 73 | fn _gen_snapshot( 74 | mut rt: JsRuntime, 75 | files: &[PathBuf], 76 | code: Option, 77 | build: bool, 78 | ) -> Result> { 79 | let base_dir = env!("CARGO_MANIFEST_DIR"); 80 | // let display_root = Path::new(base_dir).parent().unwrap(); 81 | let mut all_files = get_js_files(base_dir, JS_PATHS); 82 | all_files.extend_from_slice(files); 83 | for file in all_files { 84 | if build { 85 | println!("cargo:rerun-if-changed={}", file.display()); 86 | } 87 | // let display_path = file.strip_prefix(display_root).unwrap(); 88 | let display_path_str = file.display().to_string(); 89 | rt.execute_script( 90 | &("deno:".to_string() + &display_path_str.replace('\\', "/")), 91 | &std::fs::read_to_string(&file).unwrap(), 92 | )?; 93 | } 94 | if let Some(v) = code { 95 | rt.execute_script("deno:main", &v)?; 96 | } 97 | 98 | let snapshot = rt.snapshot(); 99 | let snapshot_slice: &[u8] = &*snapshot; 100 | 101 | Ok(zstd::encode_all(snapshot_slice, 7)?) 102 | } 103 | -------------------------------------------------------------------------------- /snapshot/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "build")] 2 | mod builder; 3 | #[cfg(feature = "build")] 4 | mod permissions; 5 | 6 | #[cfg(feature = "build")] 7 | pub use builder::{create_snapshot, create_snapshot_with_main_module, get_js_files}; 8 | 9 | pub fn decode(compressed: &[u8]) -> Box<[u8]> { 10 | zstd::decode_all(compressed).unwrap().into_boxed_slice() 11 | } 12 | -------------------------------------------------------------------------------- /snapshot/src/permissions.rs: -------------------------------------------------------------------------------- 1 | use deno_net::NetPermissions; 2 | use deno_web::TimersPermission; 3 | use deno_websocket::WebSocketPermissions; 4 | use std::path::Path; 5 | 6 | pub struct Permissions; 7 | 8 | impl deno_fetch::FetchPermissions for Permissions { 9 | fn check_net_url( 10 | &mut self, 11 | _url: &deno_core::url::Url, 12 | ) -> Result<(), deno_core::error::AnyError> { 13 | unreachable!("snapshotting!") 14 | } 15 | 16 | fn check_read(&mut self, _p: &Path) -> Result<(), deno_core::error::AnyError> { 17 | unreachable!("snapshotting!") 18 | } 19 | } 20 | 21 | impl WebSocketPermissions for Permissions { 22 | fn check_net_url( 23 | &mut self, 24 | _url: &deno_core::url::Url, 25 | ) -> Result<(), deno_core::error::AnyError> { 26 | unreachable!("snapshotting!") 27 | } 28 | } 29 | 30 | impl TimersPermission for Permissions { 31 | fn allow_hrtime(&mut self) -> bool { 32 | unreachable!("snapshotting!") 33 | } 34 | 35 | fn check_unstable(&self, _state: &deno_core::OpState, _api_name: &'static str) { 36 | unreachable!("snapshotting!") 37 | } 38 | } 39 | 40 | impl NetPermissions for Permissions { 41 | fn check_net>( 42 | &mut self, 43 | _host: &(T, Option), 44 | ) -> Result<(), deno_core::error::AnyError> { 45 | unreachable!("snapshotting!") 46 | } 47 | 48 | fn check_read(&mut self, _p: &Path) -> Result<(), deno_core::error::AnyError> { 49 | unreachable!("snapshotting!") 50 | } 51 | 52 | fn check_write(&mut self, _p: &Path) -> Result<(), deno_core::error::AnyError> { 53 | unreachable!("snapshotting!") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /transpiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno-transpiler" 3 | version = "0.4.0" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/deno-transpiler" 7 | repository = "https://github.com/tyrchen/deno-utils" 8 | homepage = "https://github.com/tyrchen/deno-utils" 9 | description = """ 10 | A typescript transpiler using deno core and deno ast. 11 | """ 12 | readme = "README.md" 13 | keywords = ["typescript", "transpiler", "deno", "v8"] 14 | categories = ["development-tools::build-utils"] 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | # deno_ast = { version = "0.15.0", features = ["minifier", "module_specifier", "transpiling"] } 20 | deno_ast = { version = "0.17.0", features = ["bundler", "transpiling"] } 21 | deno_core = "0.147.0" 22 | swc_ecma_minifier = "0.136.1" 23 | 24 | [dev-dependencies] 25 | regex = "1.6.0" 26 | -------------------------------------------------------------------------------- /transpiler/README.md: -------------------------------------------------------------------------------- 1 | # Deno transpiler 2 | 3 | A simple transpiler to transpile typescript to javascript. 4 | -------------------------------------------------------------------------------- /transpiler/fixtures/code.js: -------------------------------------------------------------------------------- 1 | import { serve } from 'https://deno.land/std@0.134.0/http/server.ts'; 2 | import { delay } from 'https://deno.land/x/delay@v0.2.0/mod.ts'; 3 | async function handler(req) { 4 | await delay(100); 5 | const body = `Your user-agent is: ${ 6 | req.headers.get('user-agent') ?? 'Unknown' 7 | }`; 8 | console.log('body: ', body); 9 | return new Response(body, { status: 200 }); 10 | } 11 | await serve(handler, { port: 8080 }); 12 | -------------------------------------------------------------------------------- /transpiler/fixtures/code.ts: -------------------------------------------------------------------------------- 1 | import { serve } from 'https://deno.land/std@0.134.0/http/server.ts'; 2 | import { delay } from 'https://deno.land/x/delay@v0.2.0/mod.ts'; 3 | 4 | async function handler(req: Request): Promise { 5 | await delay(100); 6 | const body = `Your user-agent is: ${ 7 | req.headers.get('user-agent') ?? 'Unknown' 8 | }`; 9 | console.log('body: ', body); 10 | return new Response(body, { status: 200 }); 11 | } 12 | 13 | await serve(handler, { port: 8080 }); 14 | -------------------------------------------------------------------------------- /transpiler/src/compile.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::{EmitOptions, MediaType, ParseParams, SourceTextInfo}; 2 | use deno_core::{error::AnyError, ModuleSpecifier}; 3 | 4 | use crate::minify::minify_module; 5 | 6 | pub fn compile(m: &ModuleSpecifier, code: String, minify: bool) -> Result { 7 | let media_type = MediaType::from(m); 8 | let params = ParseParams { 9 | specifier: m.to_string(), 10 | text_info: SourceTextInfo::from_string(code), 11 | media_type, 12 | capture_tokens: false, 13 | scope_analysis: false, 14 | maybe_syntax: None, 15 | }; 16 | let parsed = if !minify { 17 | deno_ast::parse_module(params)? 18 | } else { 19 | deno_ast::parse_module_with_post_process(params, minify_module)? 20 | }; 21 | let options = EmitOptions { 22 | source_map: false, 23 | inline_source_map: false, 24 | ..Default::default() 25 | }; 26 | Ok(parsed.transpile(&options)?.text) 27 | } 28 | 29 | #[allow(dead_code)] 30 | fn need_transpile(media_type: MediaType) -> bool { 31 | match media_type { 32 | MediaType::JavaScript | MediaType::Mjs | MediaType::Cjs | MediaType::Json => false, 33 | MediaType::Jsx 34 | | MediaType::TypeScript 35 | | MediaType::Mts 36 | | MediaType::Cts 37 | | MediaType::Dts 38 | | MediaType::Dmts 39 | | MediaType::Dcts 40 | | MediaType::Tsx => true, 41 | _ => false, 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use deno_core::resolve_url_or_path; 48 | 49 | use super::*; 50 | 51 | #[test] 52 | fn compile_should_work() { 53 | use regex::Regex; 54 | let re = Regex::new(r"\s+").unwrap(); 55 | 56 | let ts_code = include_str!("../fixtures/code.ts"); 57 | let js_code = include_str!("../fixtures/code.js"); 58 | let m = resolve_url_or_path("foo.ts").unwrap(); 59 | let res = compile(&m, ts_code.to_string(), false).unwrap(); 60 | assert_eq!(re.replace_all(&res, ""), re.replace_all(js_code, "")); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /transpiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod compile; 2 | mod minify; 3 | 4 | pub use compile::compile; 5 | -------------------------------------------------------------------------------- /transpiler/src/minify.rs: -------------------------------------------------------------------------------- 1 | use deno_ast::swc::{ 2 | ast::Module, 3 | common::{Mark, SourceMap}, 4 | }; 5 | use std::rc::Rc; 6 | use swc_ecma_minifier::{ 7 | optimize, 8 | option::{ExtraOptions, MangleOptions, MinifyOptions}, 9 | }; 10 | 11 | pub(crate) fn minify_module(program: Module) -> Module { 12 | let top_level_mark = Mark::fresh(Mark::root()); 13 | let unresolved_mark = Mark::fresh(Mark::root()); 14 | let cm = Rc::new(SourceMap::default()); 15 | let options = MinifyOptions { 16 | compress: Some(Default::default()), 17 | mangle: Some(MangleOptions { 18 | top_level: true, 19 | ..Default::default() 20 | }), 21 | ..Default::default() 22 | }; 23 | optimize( 24 | program.into(), 25 | cm, 26 | None, 27 | None, 28 | &options, 29 | &ExtraOptions { 30 | top_level_mark, 31 | unresolved_mark, 32 | }, 33 | ) 34 | .expect_module() 35 | } 36 | -------------------------------------------------------------------------------- /utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno-utils" 3 | version = "0.7.0" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/deno-utils" 7 | repository = "https://github.com/tyrchen/deno-utils" 8 | homepage = "https://github.com/tyrchen/deno-utils" 9 | description = """ 10 | Utility functions for deno. 11 | """ 12 | readme = "README.md" 13 | keywords = ["loader", "deno", "v8"] 14 | categories = ["development-tools"] 15 | 16 | [features] 17 | transpile = ["deno-transpiler"] 18 | bundle = ["deno_graph"] 19 | 20 | [dependencies] 21 | async-trait = "0.1.57" 22 | data-url = "0.1.1" 23 | deno_core = "0.147.0" 24 | deno_graph = { version = "0.30.0", optional = true } 25 | dirs = "4.0.0" 26 | futures = "0.3.23" 27 | mime = "0.3.16" 28 | phf = { version = "0.11.1", features = ["macros"] } 29 | pin-project = "1.0.12" 30 | reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } 31 | tokio = { version = "1.20.1", features = ["rt", "rt-multi-thread", "fs", "sync"] } 32 | 33 | deno-transpiler = { version = "0.4.0", path = "../transpiler", optional = true } 34 | 35 | [dev-dependencies] 36 | tokio = { version = "1.20.1", features = ["rt", "macros"] } 37 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # Deno Utility functions 2 | 3 | Provide utility functions like universal module loader for Deno. 4 | -------------------------------------------------------------------------------- /utils/src/data_channel.rs: -------------------------------------------------------------------------------- 1 | use std::task::Poll; 2 | 3 | use deno_core::error::AnyError; 4 | use futures::future::poll_fn; 5 | use tokio::sync::oneshot::{self, error::TryRecvError}; 6 | 7 | pub struct ClientChannel { 8 | rx: Option>, 9 | tx: Option>, 10 | } 11 | 12 | pub struct ServerChannel { 13 | rx: Option>, 14 | tx: Option>, 15 | } 16 | 17 | pub fn create_client_server_channel() -> (ClientChannel, ServerChannel) 18 | { 19 | let (tx, rx) = oneshot::channel(); 20 | let (tx2, rx2) = oneshot::channel(); 21 | ( 22 | ClientChannel { 23 | rx: Some(rx), 24 | tx: Some(tx2), 25 | }, 26 | ServerChannel { 27 | rx: Some(rx2), 28 | tx: Some(tx), 29 | }, 30 | ) 31 | } 32 | 33 | impl ClientChannel { 34 | pub fn send(&mut self, req: Req) -> Result<(), AnyError> { 35 | let tx = self 36 | .tx 37 | .take() 38 | .ok_or_else(|| AnyError::msg("client tx already taken"))?; 39 | tx.send(req) 40 | .map_err(|_e| AnyError::msg("Error: failed to send request"))?; 41 | Ok(()) 42 | } 43 | 44 | pub async fn recv(&mut self) -> Result { 45 | if let Some(rx) = self.rx.as_mut() { 46 | poll_fn(move |_cx| match rx.try_recv() { 47 | Ok(res) => Poll::Ready(Ok(res)), 48 | Err(TryRecvError::Empty) => Poll::Pending, 49 | _ => Poll::Ready(Err(AnyError::msg("Error: failed to receive response"))), 50 | }) 51 | .await 52 | } else { 53 | Err(AnyError::msg("Error: client rx already taken")) 54 | } 55 | } 56 | } 57 | 58 | impl ServerChannel { 59 | pub fn take_tx(&mut self) -> Result, AnyError> { 60 | let tx = self 61 | .tx 62 | .take() 63 | .ok_or_else(|| AnyError::msg("server tx already taken"))?; 64 | Ok(tx) 65 | } 66 | 67 | pub fn send(&mut self, res: Res) -> Result<(), AnyError> { 68 | let tx = self.take_tx()?; 69 | tx.send(res) 70 | .map_err(|_e| AnyError::msg("Error: failed to send response"))?; 71 | Ok(()) 72 | } 73 | 74 | pub async fn recv(&mut self) -> Result { 75 | let rx = self 76 | .rx 77 | .take() 78 | .ok_or_else(|| AnyError::msg("server rx already taken"))?; 79 | 80 | Ok(rx.await?) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /utils/src/fs_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | use deno_core::anyhow::Context; 4 | use deno_core::error::AnyError; 5 | use std::env::current_dir; 6 | use std::hash::{Hash, Hasher}; 7 | use std::io::Error; 8 | use std::path::{Component, Path, PathBuf}; 9 | 10 | /// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows. 11 | pub fn canonicalize_path(path: &Path) -> Result { 12 | let mut canonicalized_path = path.canonicalize()?; 13 | if cfg!(windows) { 14 | canonicalized_path = PathBuf::from( 15 | canonicalized_path 16 | .display() 17 | .to_string() 18 | .trim_start_matches("\\\\?\\"), 19 | ); 20 | } 21 | Ok(canonicalized_path) 22 | } 23 | 24 | pub fn resolve_from_cwd(path: &Path) -> Result { 25 | let resolved_path = if path.is_absolute() { 26 | path.to_owned() 27 | } else { 28 | let cwd = current_dir().context("Failed to get current working directory")?; 29 | cwd.join(path) 30 | }; 31 | 32 | Ok(normalize_path(&resolved_path)) 33 | } 34 | 35 | /// Normalize all intermediate components of the path (ie. remove "./" and "../" components). 36 | /// Similar to `fs::canonicalize()` but doesn't resolve symlinks. 37 | /// 38 | /// Taken from Cargo 39 | /// 40 | pub fn normalize_path>(path: P) -> PathBuf { 41 | let mut components = path.as_ref().components().peekable(); 42 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { 43 | components.next(); 44 | PathBuf::from(c.as_os_str()) 45 | } else { 46 | PathBuf::new() 47 | }; 48 | 49 | for component in components { 50 | match component { 51 | Component::Prefix(..) => unreachable!(), 52 | Component::RootDir => { 53 | ret.push(component.as_os_str()); 54 | } 55 | Component::CurDir => {} 56 | Component::ParentDir => { 57 | ret.pop(); 58 | } 59 | Component::Normal(c) => { 60 | ret.push(c); 61 | } 62 | } 63 | } 64 | ret 65 | } 66 | 67 | pub fn to_hash_path(base: &Path, key: &str) -> PathBuf { 68 | let hash = get_hash_from_key(key); 69 | base.join(format!("{}/{}/{}", &hash[..2], &hash[2..4], &hash[4..])) 70 | } 71 | 72 | fn get_hash_from_key(key: &str) -> String { 73 | let mut hasher = std::collections::hash_map::DefaultHasher::new(); 74 | key.hash(&mut hasher); 75 | format!("{:x}", hasher.finish()) 76 | } 77 | 78 | #[cfg(test)] 79 | mod tests { 80 | use super::*; 81 | 82 | #[test] 83 | fn resolve_from_cwd_child() { 84 | let cwd = current_dir().unwrap(); 85 | assert_eq!(resolve_from_cwd(Path::new("a")).unwrap(), cwd.join("a")); 86 | } 87 | 88 | #[test] 89 | fn resolve_from_cwd_dot() { 90 | let cwd = current_dir().unwrap(); 91 | assert_eq!(resolve_from_cwd(Path::new(".")).unwrap(), cwd); 92 | } 93 | 94 | #[test] 95 | fn resolve_from_cwd_parent() { 96 | let cwd = current_dir().unwrap(); 97 | assert_eq!(resolve_from_cwd(Path::new("a/..")).unwrap(), cwd); 98 | } 99 | 100 | #[test] 101 | fn test_normalize_path() { 102 | assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b")); 103 | assert_eq!(normalize_path(Path::new("a/./b/")), PathBuf::from("a/b/")); 104 | assert_eq!( 105 | normalize_path(Path::new("a/./b/../c")), 106 | PathBuf::from("a/c") 107 | ); 108 | 109 | if cfg!(windows) { 110 | assert_eq!( 111 | normalize_path(Path::new("C:\\a\\.\\b\\..\\c")), 112 | PathBuf::from("C:\\a\\c") 113 | ); 114 | } 115 | } 116 | 117 | // TODO: Get a good expected value here for Windows. 118 | #[cfg(not(windows))] 119 | #[test] 120 | fn resolve_from_cwd_absolute() { 121 | let expected = Path::new("/a"); 122 | assert_eq!(resolve_from_cwd(expected).unwrap(), expected); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod compressible; 2 | mod data_channel; 3 | mod fs_util; 4 | mod loader; 5 | mod store; 6 | mod tokio_util; 7 | mod unstable_checker; 8 | 9 | pub use compressible::*; 10 | pub use data_channel::*; 11 | pub use fs_util::*; 12 | pub use loader::*; 13 | pub use store::*; 14 | pub use tokio_util::*; 15 | pub use unstable_checker::*; 16 | 17 | use async_trait::async_trait; 18 | use deno_core::error::AnyError; 19 | use std::fmt; 20 | #[async_trait] 21 | pub trait ModuleStore: fmt::Debug + Send + Sync { 22 | async fn get(&self, specifier: &str) -> Result, AnyError>; 23 | async fn put(&self, specifier: String, code: &[u8]) -> Result<(), AnyError>; 24 | } 25 | 26 | #[cfg(test)] 27 | pub mod test_util { 28 | use std::path::PathBuf; 29 | 30 | pub fn testdata_path(name: &str) -> String { 31 | let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 32 | let path = path.join(format!("../fixtures/testdata/{}", name)); 33 | path.to_string_lossy().into() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /utils/src/loader/mod.rs: -------------------------------------------------------------------------------- 1 | mod universal_loader; 2 | 3 | use crate::ModuleStore; 4 | use data_url::DataUrl; 5 | use deno_core::{anyhow::bail, error::AnyError, ModuleSpecifier}; 6 | use std::{sync::Arc, time::Duration}; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct UniversalModuleLoader { 10 | store: Option>, 11 | #[allow(dead_code)] 12 | compile: bool, 13 | } 14 | 15 | pub async fn get_source_code(m: &ModuleSpecifier) -> Result { 16 | let code = match m.scheme() { 17 | "http" | "https" => { 18 | let client = reqwest::Client::builder() 19 | .timeout(Duration::from_millis(5000)) 20 | .build()?; 21 | let res = client.get(m.to_owned()).send().await?; 22 | // TODO: The HTML spec says to fail if the status is not 23 | // 200-299, but `error_for_status()` fails if the status is 24 | // 400-599. 25 | let res = res.error_for_status()?; 26 | res.text().await? 27 | } 28 | "file" => { 29 | let path = match m.to_file_path() { 30 | Ok(path) => path, 31 | Err(_) => bail!("Invalid file URL."), 32 | }; 33 | tokio::fs::read_to_string(path).await? 34 | } 35 | "data" => { 36 | let url = match DataUrl::process(m.as_str()) { 37 | Ok(url) => url, 38 | Err(_) => bail!("Not a valid data URL."), 39 | }; 40 | let bytes = match url.decode_to_vec() { 41 | Ok((bytes, _)) => bytes, 42 | Err(_) => bail!("Not a valid data URL."), 43 | }; 44 | match String::from_utf8(bytes) { 45 | Ok(s) => s, 46 | Err(_) => bail!("Not a valid data URL code."), 47 | } 48 | } 49 | schema => bail!("Invalid schema {}", schema), 50 | }; 51 | Ok(code) 52 | } 53 | -------------------------------------------------------------------------------- /utils/src/loader/universal_loader.rs: -------------------------------------------------------------------------------- 1 | use deno_core::anyhow::bail; 2 | use deno_core::error::AnyError; 3 | use deno_core::futures::FutureExt; 4 | use deno_core::resolve_import; 5 | use deno_core::ModuleLoader; 6 | use deno_core::ModuleSource; 7 | use deno_core::ModuleSourceFuture; 8 | use deno_core::ModuleSpecifier; 9 | use deno_core::ModuleType; 10 | #[cfg(feature = "bundle")] 11 | use deno_graph::source::{LoadFuture, LoadResponse, Loader}; 12 | #[cfg(feature = "transpile")] 13 | use deno_transpiler::compile; 14 | use std::path::PathBuf; 15 | use std::pin::Pin; 16 | use std::sync::Arc; 17 | 18 | use crate::FsModuleStore; 19 | use crate::{get_source_code, ModuleStore, UniversalModuleLoader}; 20 | 21 | impl Default for UniversalModuleLoader { 22 | fn default() -> Self { 23 | Self { 24 | store: Some(Arc::new(FsModuleStore::default())), 25 | compile: true, 26 | } 27 | } 28 | } 29 | 30 | impl UniversalModuleLoader { 31 | pub fn new(module_store: Option>, compile: bool) -> Self { 32 | Self { 33 | store: module_store, 34 | compile, 35 | } 36 | } 37 | 38 | pub async fn get_and_update_source( 39 | self, 40 | m: &ModuleSpecifier, 41 | #[allow(unused_variables)] minify: bool, 42 | ) -> Result { 43 | #[allow(unused_mut)] 44 | let mut code = get_source_code(m).await?; 45 | #[cfg(feature = "transpile")] 46 | if self.compile { 47 | code = compile(m, code, minify)?; 48 | } 49 | if let Some(store) = self.store.as_ref() { 50 | store.put(m.to_string(), code.as_bytes()).await?; 51 | } 52 | Ok(code) 53 | } 54 | } 55 | 56 | impl ModuleLoader for UniversalModuleLoader { 57 | fn resolve( 58 | &self, 59 | specifier: &str, 60 | referrer: &str, 61 | _is_main: bool, 62 | ) -> Result { 63 | Ok(resolve_import(specifier, referrer)?) 64 | } 65 | 66 | fn load( 67 | &self, 68 | module_specifier: &ModuleSpecifier, 69 | _maybe_referrer: Option, 70 | _is_dyn_import: bool, 71 | ) -> Pin> { 72 | let m = module_specifier.clone(); 73 | let string_specifier = m.to_string(); 74 | 75 | let loader = self.clone(); 76 | async move { 77 | let module_type = get_module_type(&m)?; 78 | if let Some(store) = loader.store.as_ref() { 79 | if let Ok(code) = store.get(&string_specifier).await { 80 | return Ok(ModuleSource { 81 | code, 82 | module_type, 83 | module_url_specified: string_specifier.clone(), 84 | module_url_found: string_specifier, 85 | }); 86 | } 87 | } 88 | let code = loader.get_and_update_source(&m, false).await?; 89 | 90 | Ok(ModuleSource { 91 | code: code.into_bytes().into_boxed_slice(), 92 | module_type, 93 | module_url_specified: string_specifier.clone(), 94 | module_url_found: string_specifier, 95 | }) 96 | } 97 | .boxed_local() 98 | } 99 | } 100 | 101 | #[cfg(feature = "bundle")] 102 | impl Loader for UniversalModuleLoader { 103 | fn load(&mut self, specifier: &ModuleSpecifier, _is_dynamic: bool) -> LoadFuture { 104 | let loader = self.clone(); 105 | let m = specifier.clone(); 106 | async move { 107 | let code = loader.get_and_update_source(&m, false).await?; 108 | Ok(Some(LoadResponse::Module { 109 | content: code.into(), 110 | specifier: m, 111 | maybe_headers: None, 112 | })) 113 | } 114 | .boxed_local() 115 | } 116 | } 117 | 118 | fn get_module_type(m: &ModuleSpecifier) -> Result { 119 | let path = if let Ok(path) = m.to_file_path() { 120 | path 121 | } else { 122 | PathBuf::from(m.path()) 123 | }; 124 | match path.extension() { 125 | Some(ext) => { 126 | let lowercase_str = ext.to_str().map(|s| s.to_lowercase()); 127 | match lowercase_str.as_deref() { 128 | Some("json") => Ok(ModuleType::Json), 129 | None => bail!("Unknown extension"), 130 | _ => Ok(ModuleType::JavaScript), 131 | } 132 | } 133 | None => bail!("Unknown media type {:?}", path), 134 | } 135 | } 136 | 137 | #[cfg(test)] 138 | mod tests { 139 | use deno_core::resolve_url_or_path; 140 | 141 | use super::*; 142 | use crate::test_util::testdata_path; 143 | #[tokio::test] 144 | async fn universal_loader_should_work() { 145 | let p = testdata_path("esm_imports_a.js"); 146 | let m = resolve_url_or_path(&p).unwrap(); 147 | let store = FsModuleStore::default(); 148 | let loader = UniversalModuleLoader::new(Some(Arc::new(store.clone())), false); 149 | let content = loader.get_and_update_source(&m, false).await.unwrap(); 150 | let expected = include_str!("../../../fixtures/testdata/esm_imports_a.js"); 151 | assert_eq!(content, expected); 152 | 153 | let cache = store.get(m.as_str()).await.unwrap(); 154 | assert_eq!(cache, expected.as_bytes().to_vec().into_boxed_slice()); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /utils/src/store/fs_store.rs: -------------------------------------------------------------------------------- 1 | use crate::{to_hash_path, FsModuleStore, ModuleStore}; 2 | use async_trait::async_trait; 3 | use deno_core::{anyhow::bail, error::AnyError}; 4 | use dirs::home_dir; 5 | use std::{ 6 | fs, 7 | io::{Read, Write}, 8 | path::PathBuf, 9 | }; 10 | 11 | impl Default for FsModuleStore { 12 | fn default() -> Self { 13 | let base = home_dir().unwrap().join(".cache/deno_fs_store"); 14 | fs::create_dir_all(&base).unwrap(); 15 | FsModuleStore { base } 16 | } 17 | } 18 | 19 | impl FsModuleStore { 20 | pub fn new(base: impl Into) -> Self { 21 | let base = base.into(); 22 | fs::create_dir_all(&base).unwrap(); 23 | FsModuleStore { base } 24 | } 25 | } 26 | 27 | #[async_trait] 28 | impl ModuleStore for FsModuleStore { 29 | async fn get(&self, key: &str) -> Result, AnyError> { 30 | let path = to_hash_path(&self.base, key); 31 | if !path.exists() { 32 | bail!("Module not found: {}", key); 33 | } 34 | let mut file = fs::File::open(&path)?; 35 | let mut contents = Vec::new(); 36 | file.read_to_end(&mut contents)?; 37 | Ok(contents.into_boxed_slice()) 38 | } 39 | 40 | async fn put(&self, key: String, value: &[u8]) -> Result<(), AnyError> { 41 | let path = to_hash_path(&self.base, &key); 42 | fs::create_dir_all(path.parent().unwrap())?; 43 | let mut file = fs::File::create(&path)?; 44 | file.write_all(value)?; 45 | Ok(()) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use crate::{FsModuleStore, ModuleStore}; 52 | use std::path::PathBuf; 53 | 54 | #[tokio::test] 55 | async fn module_store_should_work() { 56 | let base = PathBuf::from("/tmp/deno_fs_store"); 57 | let store = FsModuleStore::new(base); 58 | store.put("foo".to_string(), b"bar").await.unwrap(); 59 | let contents = store.get("foo").await.unwrap(); 60 | assert_eq!(&contents[..], b"bar"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /utils/src/store/mod.rs: -------------------------------------------------------------------------------- 1 | mod fs_store; 2 | 3 | use std::path::PathBuf; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct FsModuleStore { 7 | base: PathBuf, 8 | } 9 | -------------------------------------------------------------------------------- /utils/src/tokio_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn create_basic_runtime() -> tokio::runtime::Runtime { 4 | tokio::runtime::Builder::new_current_thread() 5 | .enable_io() 6 | .enable_time() 7 | // This limits the number of threads for blocking operations (like for 8 | // synchronous fs ops) or CPU bound tasks like when we run dprint in 9 | // parallel for deno fmt. 10 | // The default value is 512, which is an unhelpfully large thread pool. We 11 | // don't ever want to have more than a couple dozen threads. 12 | .max_blocking_threads(32) 13 | .build() 14 | .unwrap() 15 | } 16 | 17 | // TODO(ry) rename to run_local ? 18 | pub fn run_basic(future: F) -> R 19 | where 20 | F: std::future::Future, 21 | { 22 | let rt = create_basic_runtime(); 23 | let local = tokio::task::LocalSet::new(); 24 | local.block_on(&rt, future) 25 | } 26 | -------------------------------------------------------------------------------- /utils/src/unstable_checker.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use deno_core::OpState; 4 | 5 | /// `UnstableChecker` is a struct so it can be placed inside `GothamState`; 6 | /// using type alias for a bool could work, but there's a high chance 7 | /// that there might be another type alias pointing to a bool, which 8 | /// would override previously used alias. 9 | pub struct UnstableChecker { 10 | pub unstable: bool, 11 | } 12 | 13 | impl UnstableChecker { 14 | /// Quits the process if the --unstable flag was not provided. 15 | /// 16 | /// This is intentionally a non-recoverable check so that people cannot probe 17 | /// for unstable APIs from stable programs. 18 | // NOTE(bartlomieju): keep in sync with `cli/program_state.rs` 19 | pub fn check_unstable(&self, api_name: &str) { 20 | if !self.unstable { 21 | eprintln!( 22 | "Unstable API '{}'. The --unstable flag must be provided.", 23 | api_name 24 | ); 25 | std::process::exit(70); 26 | } 27 | } 28 | } 29 | 30 | /// Helper for checking unstable features. Used for sync ops. 31 | pub fn check_unstable(state: &OpState, api_name: &str) { 32 | state.borrow::().check_unstable(api_name) 33 | } 34 | 35 | /// Helper for checking unstable features. Used for async ops. 36 | pub fn check_unstable2(state: &Rc>, api_name: &str) { 37 | let state = state.borrow(); 38 | state.borrow::().check_unstable(api_name) 39 | } 40 | 41 | pub struct TestingFeaturesEnabled(pub bool); 42 | --------------------------------------------------------------------------------