1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-12 00:54:02 -05:00

refactor(cli): runtime compiler APIs consolidated to Deno.emit() (#8799)

Closes: #4752
This commit is contained in:
Kitson Kelly 2021-01-01 08:43:54 +11:00 committed by GitHub
parent 5f4e1767fe
commit 012f99bd9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 592 additions and 516 deletions

View file

@ -44,11 +44,10 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"UnixListenOptions", "UnixListenOptions",
"WritePermissionDescriptor", "WritePermissionDescriptor",
"applySourceMap", "applySourceMap",
"bundle",
"compile",
"connect", "connect",
"consoleSize", "consoleSize",
"createHttpClient", "createHttpClient",
"emit",
"formatDiagnostics", "formatDiagnostics",
"futime", "futime",
"futimeSync", "futimeSync",
@ -77,7 +76,6 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"symlinkSync", "symlinkSync",
"systemMemoryInfo", "systemMemoryInfo",
"systemCpuInfo", "systemCpuInfo",
"transpileOnly",
"umask", "umask",
"utime", "utime",
"utimeSync", "utimeSync",

View file

@ -290,6 +290,8 @@ declare namespace Deno {
/** Base directory to resolve non-relative module names. Defaults to /** Base directory to resolve non-relative module names. Defaults to
* `undefined`. */ * `undefined`. */
baseUrl?: string; baseUrl?: string;
/** The character set of the input files. Defaults to `"utf8"`. */
charset?: string;
/** Report errors in `.js` files. Use in conjunction with `allowJs`. Defaults /** Report errors in `.js` files. Use in conjunction with `allowJs`. Defaults
* to `false`. */ * to `false`. */
checkJs?: boolean; checkJs?: boolean;
@ -338,9 +340,6 @@ declare namespace Deno {
/** Emit the source alongside the source maps within a single file; requires /** Emit the source alongside the source maps within a single file; requires
* `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */ * `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */
inlineSources?: boolean; inlineSources?: boolean;
/** Perform additional checks to ensure that transpile only would be safe.
* Defaults to `true`. */
isolatedModules?: boolean;
/** Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`. /** Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`.
* Defaults to `"react"`. */ * Defaults to `"react"`. */
jsx?: "react" | "preserve" | "react-native"; jsx?: "react" | "preserve" | "react-native";
@ -393,12 +392,17 @@ declare namespace Deno {
/** Do not emit `"use strict"` directives in module output. Defaults to /** Do not emit `"use strict"` directives in module output. Defaults to
* `false`. */ * `false`. */
noImplicitUseStrict?: boolean; noImplicitUseStrict?: boolean;
/** Do not include the default library file (`lib.d.ts`). Defaults to
* `false`. */
noLib?: boolean;
/** Do not add triple-slash references or module import targets to the list of /** Do not add triple-slash references or module import targets to the list of
* compiled files. Defaults to `false`. */ * compiled files. Defaults to `false`. */
noResolve?: boolean; noResolve?: boolean;
/** Disable strict checking of generic signatures in function types. Defaults /** Disable strict checking of generic signatures in function types. Defaults
* to `false`. */ * to `false`. */
noStrictGenericChecks?: boolean; noStrictGenericChecks?: boolean;
/** Include 'undefined' in index signature results. Defaults to `false`. */
noUncheckedIndexedAccess?: boolean;
/** Report errors on unused locals. Defaults to `false`. */ /** Report errors on unused locals. Defaults to `false`. */
noUnusedLocals?: boolean; noUnusedLocals?: boolean;
/** Report errors on unused parameters. Defaults to `false`. */ /** Report errors on unused parameters. Defaults to `false`. */
@ -487,122 +491,78 @@ declare namespace Deno {
useDefineForClassFields?: boolean; useDefineForClassFields?: boolean;
} }
/** **UNSTABLE**: new API, yet to be vetted. interface ImportMap {
* imports: Record<string, string>;
* The results of a transpile only command, where the `source` contains the scopes?: Record<string, Record<string, string>>;
* emitted source, and `map` optionally contains the source map. */
export interface TranspileOnlyResult {
source: string;
map?: string;
} }
/** **UNSTABLE**: new API, yet to be vetted. interface EmitOptions {
/** Indicate that the source code should be emitted to a single file
* JavaScript bundle that is an ES module (`"esm"`). */
bundle?: "esm";
/** If `true` then the sources will be typed checked, returning any
* diagnostic errors in the result. If `false` type checking will be
* skipped. Defaults to `true`.
* *
* Takes a set of TypeScript sources and resolves to a map where the key was * *Note* by default, only TypeScript will be type checked, just like on
* the original file name provided in sources and the result contains the * the command line. Use the `compilerOptions` options of `checkJs` to
* `source` and optionally the `map` from the transpile operation. This does no * enable type checking of JavaScript. */
* type checking and validation, it effectively "strips" the types from the check?: boolean;
* file. /** A set of options that are aligned to TypeScript compiler options that
* * are supported by Deno. */
* ```ts compilerOptions?: CompilerOptions;
* const results = await Deno.transpileOnly({ /** An [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps)
* "foo.ts": `const foo: string = "foo";` * which will be applied to the imports. */
* }); importMap?: ImportMap;
* ``` /** An absolute path to an [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps).
* * Required to be specified if an `importMap` is specified to be able to
* @param sources A map where the key is the filename and the value is the text * determine resolution of relative paths. If a `importMap` is not
* to transpile. The filename is only used in the transpile and * specified, then it will assumed the file path points to an import map on
* not resolved, for example to fill in the source name in the * disk and will be attempted to be loaded based on current runtime
* source map. * permissions.
* @param options An option object of options to send to the compiler. This is
* a subset of ts.CompilerOptions which can be supported by Deno.
* If unsupported option is passed then the API will throw an error.
*/ */
export function transpileOnly( importMapPath?: string;
sources: Record<string, string>, /** A record of sources to use when doing the emit. If provided, Deno will
options?: CompilerOptions, * use these sources instead of trying to resolve the modules externally. */
): Promise<Record<string, TranspileOnlyResult>>; sources?: Record<string, string>;
}
/** **UNSTABLE**: new API, yet to be vetted. interface EmitResult {
* /** Diagnostic messages returned from the type checker (`tsc`). */
* Takes a root module name, and optionally a record set of sources. Resolves diagnostics: Diagnostic[];
* with a compiled set of modules and possibly diagnostics if the compiler /** Any emitted files. If bundled, then the JavaScript will have the
* encountered any issues. If just a root name is provided, the modules * key of `deno:///bundle.js` with an optional map (based on
* will be resolved as if the root module had been passed on the command line. * `compilerOptions`) in `deno:///bundle.js.map`. */
* files: Record<string, string>;
* If sources are passed, all modules will be resolved out of this object, where /** An optional array of any compiler options that were ignored by Deno. */
* the key is the module name and the value is the content. The extension of ignoredOptions?: string[];
* the module name will be used to determine the media type of the module. /** An array of internal statistics related to the emit, for diagnostic
* * purposes. */
* ```ts stats: Array<[string, number]>;
* const [ maybeDiagnostics1, output1 ] = await Deno.compile("foo.ts"); }
*
* const [ maybeDiagnostics2, output2 ] = await Deno.compile("/foo.ts", {
* "/foo.ts": `export * from "./bar.ts";`,
* "/bar.ts": `export const bar = "bar";`
* });
* ```
*
* @param rootName The root name of the module which will be used as the
* "starting point". If no `sources` is specified, Deno will
* resolve the module externally as if the `rootName` had been
* specified on the command line.
* @param sources An optional key/value map of sources to be used when resolving
* modules, where the key is the module name, and the value is
* the source content. The extension of the key will determine
* the media type of the file when processing. If supplied,
* Deno will not attempt to resolve any modules externally.
* @param options An optional object of options to send to the compiler. This is
* a subset of ts.CompilerOptions which can be supported by Deno.
*/
export function compile(
rootName: string,
sources?: Record<string, string>,
options?: CompilerOptions,
): Promise<[Diagnostic[] | undefined, Record<string, string>]>;
/** **UNSTABLE**: new API, yet to be vetted. /**
* **UNSTABLE**: new API, yet to be vetted.
* *
* `bundle()` is part the compiler API. A full description of this functionality * Similar to the command line functionality of `deno run` or `deno cache`,
* can be found in the [manual](https://deno.land/manual/runtime/compiler_apis#denobundle). * `Deno.emit()` provides a way to provide Deno arbitrary JavaScript
* or TypeScript and have it return JavaScript based on the options and
* settings provided. The source code can either be provided or the modules
* can be fetched and resolved in line with the behavior of the command line.
* *
* Takes a root module name, and optionally a record set of sources. Resolves * Requires `allow-read` and/or `allow-net` if sources are not provided.
* with a single JavaScript string (and bundle diagnostics if issues arise with
* the bundling) that is like the output of a `deno bundle` command. If just
* a root name is provided, the modules will be resolved as if the root module
* had been passed on the command line.
* *
* If sources are passed, all modules will be resolved out of this object, where * @param rootSpecifier The specifier that will be used as the entry point.
* the key is the module name and the value is the content. The extension of the * If no sources are provided, then the specifier would
* module name will be used to determine the media type of the module. * be the same as if you typed it on the command line for
* * `deno run`. If sources are provided, it should match
* ```ts * one of the names of the sources.
* // equivalent to "deno bundle foo.ts" from the command line * @param options A set of options to be used with the emit.
* const [ maybeDiagnostics1, output1 ] = await Deno.bundle("foo.ts");
*
* const [ maybeDiagnostics2, output2 ] = await Deno.bundle("/foo.ts", {
* "/foo.ts": `export * from "./bar.ts";`,
* "/bar.ts": `export const bar = "bar";`
* });
* ```
*
* @param rootName The root name of the module which will be used as the
* "starting point". If no `sources` is specified, Deno will
* resolve the module externally as if the `rootName` had been
* specified on the command line.
* @param sources An optional key/value map of sources to be used when resolving
* modules, where the key is the module name, and the value is
* the source content. The extension of the key will determine
* the media type of the file when processing. If supplied,
* Deno will not attempt to resolve any modules externally.
* @param options An optional object of options to send to the compiler. This is
* a subset of ts.CompilerOptions which can be supported by Deno.
*/ */
export function bundle( export function emit(
rootName: string, rootSpecifier: string | URL,
sources?: Record<string, string>, options?: EmitOptions,
options?: CompilerOptions, ): Promise<EmitResult>;
): Promise<[Diagnostic[] | undefined, string]>;
/** **UNSTABLE**: Should not have same name as `window.location` type. */ /** **UNSTABLE**: Should not have same name as `window.location` type. */
interface Location { interface Location {

View file

@ -497,18 +497,27 @@ impl Module {
} }
#[derive(Clone, Debug, Default, Eq, PartialEq)] #[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Stats(pub Vec<(String, u128)>); pub struct Stats(pub Vec<(String, u32)>);
impl<'de> Deserialize<'de> for Stats { impl<'de> Deserialize<'de> for Stats {
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let items: Vec<(String, u128)> = Deserialize::deserialize(deserializer)?; let items: Vec<(String, u32)> = Deserialize::deserialize(deserializer)?;
Ok(Stats(items)) Ok(Stats(items))
} }
} }
impl Serialize for Stats {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Serialize::serialize(&self.0, serializer)
}
}
impl fmt::Display for Stats { impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Compilation statistics:")?; writeln!(f, "Compilation statistics:")?;
@ -620,6 +629,10 @@ impl Default for BundleType {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct EmitOptions { pub struct EmitOptions {
/// If true, then code will be type checked, otherwise type checking will be
/// skipped. If false, then swc will be used for the emit, otherwise tsc will
/// be used.
pub check: bool,
/// Indicate the form the result of the emit should take. /// Indicate the form the result of the emit should take.
pub bundle_type: BundleType, pub bundle_type: BundleType,
/// If `true` then debug logging will be output from the isolate. /// If `true` then debug logging will be output from the isolate.
@ -769,8 +782,8 @@ impl Graph {
let s = self.emit_bundle(&root_specifier, &ts_config.into())?; let s = self.emit_bundle(&root_specifier, &ts_config.into())?;
let stats = Stats(vec![ let stats = Stats(vec![
("Files".to_string(), self.modules.len() as u128), ("Files".to_string(), self.modules.len() as u32),
("Total time".to_string(), start.elapsed().as_millis()), ("Total time".to_string(), start.elapsed().as_millis() as u32),
]); ]);
Ok((s, stats, maybe_ignored_options)) Ok((s, stats, maybe_ignored_options))
@ -918,18 +931,22 @@ impl Graph {
/// emitting single modules as well as bundles, using Deno module resolution /// emitting single modules as well as bundles, using Deno module resolution
/// or supplied sources. /// or supplied sources.
pub fn emit( pub fn emit(
self, mut self,
options: EmitOptions, options: EmitOptions,
) -> Result<(HashMap<String, String>, ResultInfo), AnyError> { ) -> Result<(HashMap<String, String>, ResultInfo), AnyError> {
let mut config = TsConfig::new(json!({ let mut config = TsConfig::new(json!({
"allowJs": true, "allowJs": true,
"checkJs": false,
// TODO(@kitsonk) consider enabling this by default // TODO(@kitsonk) consider enabling this by default
// see: https://github.com/denoland/deno/issues/7732 // see: https://github.com/denoland/deno/issues/7732
"emitDecoratorMetadata": false, "emitDecoratorMetadata": false,
"esModuleInterop": true, "esModuleInterop": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"inlineSourceMap": false,
"isolatedModules": true, "isolatedModules": true,
"jsx": "react", "jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"lib": TypeLib::DenoWindow, "lib": TypeLib::DenoWindow,
"module": "esnext", "module": "esnext",
"strict": true, "strict": true,
@ -937,11 +954,7 @@ impl Graph {
})); }));
let opts = match options.bundle_type { let opts = match options.bundle_type {
BundleType::Esm => json!({ BundleType::Esm => json!({
"checkJs": false,
"inlineSourceMap": false,
"noEmit": true, "noEmit": true,
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
}), }),
BundleType::None => json!({ BundleType::None => json!({
"outDir": "deno://", "outDir": "deno://",
@ -957,11 +970,19 @@ impl Graph {
None None
}; };
if !options.check && config.get_declaration() {
return Err(anyhow!("The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported."));
}
if options.bundle_type != BundleType::None && config.get_declaration() {
return Err(anyhow!("The bundle option is set, but the compiler option of `declaration` is true which is not currently supported."));
}
let mut emitted_files = HashMap::new();
if options.check {
let root_names = self.get_root_names(!config.get_check_js()); let root_names = self.get_root_names(!config.get_check_js());
let hash_data = let hash_data =
vec![config.as_bytes(), version::deno().as_bytes().to_owned()]; vec![config.as_bytes(), version::deno().as_bytes().to_owned()];
let graph = Arc::new(Mutex::new(self)); let graph = Arc::new(Mutex::new(self));
let response = tsc::exec( let response = tsc::exec(
js::compiler_isolate_init(), js::compiler_isolate_init(),
tsc::Request { tsc::Request {
@ -974,7 +995,6 @@ impl Graph {
}, },
)?; )?;
let mut emitted_files = HashMap::new();
let graph = graph.lock().unwrap(); let graph = graph.lock().unwrap();
match options.bundle_type { match options.bundle_type {
BundleType::Esm => { BundleType::Esm => {
@ -1025,6 +1045,63 @@ impl Graph {
stats: response.stats, stats: response.stats,
}, },
)) ))
} else {
let start = Instant::now();
let mut emit_count = 0_u32;
match options.bundle_type {
BundleType::Esm => {
assert_eq!(
self.roots.len(),
1,
"Only a single root module supported."
);
let specifier = &self.roots[0];
let s = self.emit_bundle(specifier, &config.into())?;
emit_count += 1;
emitted_files.insert("deno:///bundle.js".to_string(), s);
}
BundleType::None => {
let emit_options: ast::EmitOptions = config.into();
for (_, module_slot) in self.modules.iter_mut() {
if let ModuleSlot::Module(module) = module_slot {
if !(emit_options.check_js
|| module.media_type == MediaType::JSX
|| module.media_type == MediaType::TSX
|| module.media_type == MediaType::TypeScript)
{
emitted_files
.insert(module.specifier.to_string(), module.source.clone());
}
let parsed_module = module.parse()?;
let (code, maybe_map) = parsed_module.transpile(&emit_options)?;
emit_count += 1;
emitted_files.insert(format!("{}.js", module.specifier), code);
if let Some(map) = maybe_map {
emitted_files
.insert(format!("{}.js.map", module.specifier), map);
}
}
}
self.flush()?;
}
}
let stats = Stats(vec![
("Files".to_string(), self.modules.len() as u32),
("Emitted".to_string(), emit_count),
("Total time".to_string(), start.elapsed().as_millis() as u32),
]);
Ok((
emitted_files,
ResultInfo {
diagnostics: Default::default(),
loadable_modules: self.get_loadable_modules(),
maybe_ignored_options,
stats,
},
))
}
} }
/// Shared between `bundle()` and `emit()`. /// Shared between `bundle()` and `emit()`.
@ -1566,10 +1643,9 @@ impl Graph {
let maybe_ignored_options = let maybe_ignored_options =
ts_config.merge_tsconfig(options.maybe_config_path)?; ts_config.merge_tsconfig(options.maybe_config_path)?;
let emit_options: ast::EmitOptions = ts_config.clone().into();
let mut emit_count: u128 = 0;
let config = ts_config.as_bytes(); let config = ts_config.as_bytes();
let emit_options: ast::EmitOptions = ts_config.into();
let mut emit_count = 0_u32;
for (_, module_slot) in self.modules.iter_mut() { for (_, module_slot) in self.modules.iter_mut() {
if let ModuleSlot::Module(module) = module_slot { if let ModuleSlot::Module(module) = module_slot {
// TODO(kitsonk) a lot of this logic should be refactored into `Module` as // TODO(kitsonk) a lot of this logic should be refactored into `Module` as
@ -1604,9 +1680,9 @@ impl Graph {
self.flush()?; self.flush()?;
let stats = Stats(vec![ let stats = Stats(vec![
("Files".to_string(), self.modules.len() as u128), ("Files".to_string(), self.modules.len() as u32),
("Emitted".to_string(), emit_count), ("Emitted".to_string(), emit_count),
("Total time".to_string(), start.elapsed().as_millis()), ("Total time".to_string(), start.elapsed().as_millis() as u32),
]); ]);
Ok(ResultInfo { Ok(ResultInfo {
@ -2270,6 +2346,7 @@ pub mod tests {
.await; .await;
let (emitted_files, result_info) = graph let (emitted_files, result_info) = graph
.emit(EmitOptions { .emit(EmitOptions {
check: true,
bundle_type: BundleType::None, bundle_type: BundleType::None,
debug: false, debug: false,
maybe_user_config: None, maybe_user_config: None,
@ -2310,6 +2387,7 @@ pub mod tests {
.await; .await;
let (emitted_files, result_info) = graph let (emitted_files, result_info) = graph
.emit(EmitOptions { .emit(EmitOptions {
check: true,
bundle_type: BundleType::Esm, bundle_type: BundleType::Esm,
debug: false, debug: false,
maybe_user_config: None, maybe_user_config: None,
@ -2347,6 +2425,7 @@ pub mod tests {
user_config.insert("declaration".to_string(), json!(true)); user_config.insert("declaration".to_string(), json!(true));
let (emitted_files, result_info) = graph let (emitted_files, result_info) = graph
.emit(EmitOptions { .emit(EmitOptions {
check: true,
bundle_type: BundleType::None, bundle_type: BundleType::None,
debug: false, debug: false,
maybe_user_config: Some(user_config), maybe_user_config: Some(user_config),

View file

@ -1,8 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::ast; use crate::import_map::ImportMap;
use crate::colors;
use crate::media_type::MediaType;
use crate::module_graph::BundleType; use crate::module_graph::BundleType;
use crate::module_graph::EmitOptions; use crate::module_graph::EmitOptions;
use crate::module_graph::GraphBuilder; use crate::module_graph::GraphBuilder;
@ -10,11 +8,10 @@ use crate::program_state::ProgramState;
use crate::specifier_handler::FetchHandler; use crate::specifier_handler::FetchHandler;
use crate::specifier_handler::MemoryHandler; use crate::specifier_handler::MemoryHandler;
use crate::specifier_handler::SpecifierHandler; use crate::specifier_handler::SpecifierHandler;
use crate::tsc_config;
use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::error::Context; use deno_core::error::Context;
use deno_core::serde::Serialize;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::serde_json::Value; use deno_core::serde_json::Value;
@ -30,37 +27,46 @@ use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
pub fn init(rt: &mut deno_core::JsRuntime) { pub fn init(rt: &mut deno_core::JsRuntime) {
super::reg_json_async(rt, "op_compile", op_compile); super::reg_json_async(rt, "op_emit", op_emit);
super::reg_json_async(rt, "op_transpile", op_transpile);
} }
#[derive(Deserialize, Debug)] #[derive(Debug, Deserialize)]
enum RuntimeBundleType {
#[serde(rename = "esm")]
Esm,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CompileArgs { struct EmitArgs {
root_name: String, bundle: Option<RuntimeBundleType>,
check: Option<bool>,
compiler_options: Option<HashMap<String, Value>>,
import_map: Option<Value>,
import_map_path: Option<String>,
root_specifier: String,
sources: Option<HashMap<String, String>>, sources: Option<HashMap<String, String>>,
bundle: bool,
options: Option<String>,
} }
async fn op_compile( async fn op_emit(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: Value, args: Value,
_data: BufVec, _data: BufVec,
) -> Result<Value, AnyError> { ) -> Result<Value, AnyError> {
let args: CompileArgs = serde_json::from_value(args)?; deno_runtime::ops::check_unstable2(&state, "Deno.emit");
if args.bundle { let args: EmitArgs = serde_json::from_value(args)?;
deno_runtime::ops::check_unstable2(&state, "Deno.bundle");
} else {
deno_runtime::ops::check_unstable2(&state, "Deno.compile");
}
let program_state = state.borrow().borrow::<Arc<ProgramState>>().clone(); let program_state = state.borrow().borrow::<Arc<ProgramState>>().clone();
let runtime_permissions = { let runtime_permissions = {
let state = state.borrow(); let state = state.borrow();
state.borrow::<Permissions>().clone() state.borrow::<Permissions>().clone()
}; };
// when we are actually resolving modules without provided sources, we should
// treat the root module as a dynamic import so that runtime permissions are
// applied.
let mut is_dynamic = false;
let handler: Arc<Mutex<dyn SpecifierHandler>> = let handler: Arc<Mutex<dyn SpecifierHandler>> =
if let Some(sources) = args.sources { if let Some(sources) = args.sources {
is_dynamic = true;
Arc::new(Mutex::new(MemoryHandler::new(sources))) Arc::new(Mutex::new(MemoryHandler::new(sources)))
} else { } else {
Arc::new(Mutex::new(FetchHandler::new( Arc::new(Mutex::new(FetchHandler::new(
@ -68,93 +74,44 @@ async fn op_compile(
runtime_permissions, runtime_permissions,
)?)) )?))
}; };
let mut builder = GraphBuilder::new(handler, None, None); let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
let specifier = ModuleSpecifier::resolve_url_or_path(&args.root_name) let import_map_specifier =
.context("The root specifier is invalid.")?; ModuleSpecifier::resolve_url_or_path(&import_map_str).context(
builder.add(&specifier, false).await?; format!("Bad file path (\"{}\") for import map.", import_map_str),
let graph = builder.get_graph(); )?;
let bundle_type = if args.bundle { let import_map_url = import_map_specifier.as_url();
BundleType::Esm let import_map = if let Some(value) = args.import_map {
ImportMap::from_json(&import_map_url.to_string(), &value.to_string())?
} else { } else {
BundleType::None ImportMap::load(&import_map_str)?
}; };
let debug = program_state.flags.log_level == Some(log::Level::Debug); Some(import_map)
let maybe_user_config: Option<HashMap<String, Value>> = } else if args.import_map.is_some() {
if let Some(options) = args.options { return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required."));
Some(serde_json::from_str(&options)?)
} else { } else {
None None
}; };
let (emitted_files, result_info) = graph.emit(EmitOptions { let mut builder = GraphBuilder::new(handler, maybe_import_map, None);
let root_specifier =
ModuleSpecifier::resolve_url_or_path(&args.root_specifier)?;
builder.add(&root_specifier, is_dynamic).await?;
let bundle_type = match args.bundle {
Some(RuntimeBundleType::Esm) => BundleType::Esm,
_ => BundleType::None,
};
let graph = builder.get_graph();
let debug = program_state.flags.log_level == Some(log::Level::Debug);
let (files, result_info) = graph.emit(EmitOptions {
bundle_type, bundle_type,
check: args.check.unwrap_or(true),
debug, debug,
maybe_user_config, maybe_user_config: args.compiler_options,
})?; })?;
Ok(json!({ Ok(json!({
"emittedFiles": emitted_files,
"diagnostics": result_info.diagnostics, "diagnostics": result_info.diagnostics,
"files": files,
"ignoredOptions": result_info.maybe_ignored_options,
"stats": result_info.stats,
})) }))
} }
#[derive(Deserialize, Debug)]
struct TranspileArgs {
sources: HashMap<String, String>,
options: Option<String>,
}
#[derive(Debug, Serialize)]
struct RuntimeTranspileEmit {
source: String,
map: Option<String>,
}
async fn op_transpile(
state: Rc<RefCell<OpState>>,
args: Value,
_data: BufVec,
) -> Result<Value, AnyError> {
deno_runtime::ops::check_unstable2(&state, "Deno.transpileOnly");
let args: TranspileArgs = serde_json::from_value(args)?;
let mut compiler_options = tsc_config::TsConfig::new(json!({
"checkJs": true,
"emitDecoratorMetadata": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"inlineSourceMap": false,
}));
let user_options: HashMap<String, Value> = if let Some(options) = args.options
{
serde_json::from_str(&options)?
} else {
HashMap::new()
};
let maybe_ignored_options =
compiler_options.merge_user_config(&user_options)?;
// TODO(@kitsonk) these really should just be passed back to the caller
if let Some(ignored_options) = maybe_ignored_options {
info!("{}: {}", colors::yellow("warning"), ignored_options);
}
let emit_options: ast::EmitOptions = compiler_options.into();
let mut emit_map = HashMap::new();
for (specifier, source) in args.sources {
let media_type = MediaType::from(&specifier);
let parsed_module = ast::parse(&specifier, &source, &media_type)?;
let (source, maybe_source_map) = parsed_module.transpile(&emit_options)?;
emit_map.insert(
specifier.to_string(),
RuntimeTranspileEmit {
source,
map: maybe_source_map,
},
);
}
let result = serde_json::to_value(emit_map)?;
Ok(result)
}

View file

@ -6,15 +6,21 @@ import {
} from "../../std/testing/asserts.ts"; } from "../../std/testing/asserts.ts";
Deno.test({ Deno.test({
name: "Deno.compile() - sources provided", name: "Deno.emit() - sources provided",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.compile("/foo.ts", { const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
sources: {
"/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`, "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
"/bar.ts": `export const bar = "bar";\n`, "/bar.ts": `export const bar = "bar";\n`,
}); },
assert(diagnostics == null); },
assert(actual); );
const keys = Object.keys(actual).sort(); assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/bar.ts.js")); assert(keys[0].endsWith("/bar.ts.js"));
assert(keys[1].endsWith("/bar.ts.js.map")); assert(keys[1].endsWith("/bar.ts.js.map"));
assert(keys[2].endsWith("/foo.ts.js")); assert(keys[2].endsWith("/foo.ts.js"));
@ -23,12 +29,15 @@ Deno.test({
}); });
Deno.test({ Deno.test({
name: "Deno.compile() - no sources provided", name: "Deno.emit() - no sources provided",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.compile("./subdir/mod1.ts"); const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
assert(diagnostics == null); "./subdir/mod1.ts",
assert(actual); );
const keys = Object.keys(actual).sort(); assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys.length, 6); assertEquals(keys.length, 6);
assert(keys[0].endsWith("cli/tests/subdir/mod1.ts.js")); assert(keys[0].endsWith("cli/tests/subdir/mod1.ts.js"));
assert(keys[1].endsWith("cli/tests/subdir/mod1.ts.js.map")); assert(keys[1].endsWith("cli/tests/subdir/mod1.ts.js.map"));
@ -36,156 +45,217 @@ Deno.test({
}); });
Deno.test({ Deno.test({
name: "Deno.compile() - compiler options effects emit", name: "Deno.emit() - compiler options effects emit",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.compile( const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts", "/foo.ts",
{ {
"/foo.ts": `export const foo = "foo";`, compilerOptions: {
},
{
module: "amd", module: "amd",
sourceMap: false, sourceMap: false,
}, },
sources: { "/foo.ts": `export const foo = "foo";` },
},
); );
assert(diagnostics == null); assertEquals(diagnostics.length, 0);
assert(actual); assert(!ignoredOptions);
const keys = Object.keys(actual); assertEquals(stats.length, 12);
const keys = Object.keys(files);
assertEquals(keys.length, 1); assertEquals(keys.length, 1);
const key = keys[0]; const key = keys[0];
assert(key.endsWith("/foo.ts.js")); assert(key.endsWith("/foo.ts.js"));
assert(actual[key].startsWith("define(")); assert(files[key].startsWith("define("));
}, },
}); });
Deno.test({ Deno.test({
name: "Deno.compile() - pass lib in compiler options", name: "Deno.emit() - pass lib in compiler options",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.compile( const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///foo.ts", "file:///foo.ts",
{ {
compilerOptions: {
lib: ["dom", "es2018", "deno.ns"],
},
sources: {
"file:///foo.ts": `console.log(document.getElementById("foo")); "file:///foo.ts": `console.log(document.getElementById("foo"));
console.log(Deno.args);`, console.log(Deno.args);`,
}, },
{
lib: ["dom", "es2018", "deno.ns"],
}, },
); );
assert(diagnostics == null); assertEquals(diagnostics.length, 0);
assert(actual); assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys, ["file:///foo.ts.js", "file:///foo.ts.js.map"]);
},
});
Deno.test({
name: "Deno.emit() - import maps",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///a.ts",
{
importMap: {
imports: {
"b": "./b.ts",
},
},
importMapPath: "file:///import-map.json",
sources: {
"file:///a.ts": `import * as b from "b"
console.log(b);`,
"file:///b.ts": `export const b = "b";`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals( assertEquals(
Object.keys(actual).sort(), keys,
["file:///foo.ts.js", "file:///foo.ts.js.map"], [
"file:///a.ts.js",
"file:///a.ts.js.map",
"file:///b.ts.js",
"file:///b.ts.js.map",
],
); );
}, },
}); });
// TODO(@kitsonk) figure the "right way" to restore support for types
// Deno.test({
// name: "Deno.compile() - properly handles .d.ts files",
// async fn() {
// const [diagnostics, actual] = await Deno.compile(
// "/foo.ts",
// {
// "/foo.ts": `console.log(Foo.bar);`,
// "/foo_types.d.ts": `declare namespace Foo {
// const bar: string;
// }`,
// },
// {
// types: ["/foo_types.d.ts"],
// },
// );
// assert(diagnostics == null);
// assert(actual);
// assertEquals(
// Object.keys(actual).sort(),
// ["file:///foo.ts.js", "file:///file.ts.js.map"],
// );
// },
// });
Deno.test({ Deno.test({
name: "Deno.transpileOnly()", name: "Deno.emit() - no check",
async fn() { async fn() {
const actual = await Deno.transpileOnly({ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"foo.ts": `export enum Foo { Foo, Bar, Baz };\n`, "/foo.ts",
});
assert(actual);
assertEquals(Object.keys(actual), ["foo.ts"]);
assert(actual["foo.ts"].source.startsWith("export var Foo;"));
assert(actual["foo.ts"].map);
},
});
Deno.test({
name: "Deno.transpileOnly() - config effects commit",
async fn() {
const actual = await Deno.transpileOnly(
{ {
"foo.ts": `/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`, check: false,
sources: {
"/foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
}, },
{
removeComments: true,
}, },
); );
assert(actual); assertEquals(diagnostics.length, 0);
assertEquals(Object.keys(actual), ["foo.ts"]); assert(!ignoredOptions);
assert(!actual["foo.ts"].source.includes("This is JSDoc")); assertEquals(stats.length, 3);
assert(actual["foo.ts"].map); const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
assert(files[keys[0]].startsWith("export var Foo;"));
}, },
}); });
Deno.test({ Deno.test({
name: "Deno.bundle() - sources passed", name: "Deno.emit() - no check - config effects emit",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.bundle("/foo.ts", { const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
check: false,
compilerOptions: { removeComments: true },
sources: {
"/foo.ts":
`/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 3);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
assert(!files[keys[0]].includes("This is JSDoc"));
},
});
Deno.test({
name: "Deno.emit() - bundle esm - with sources",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
bundle: "esm",
sources: {
"/foo.ts": `export * from "./bar.ts";\n`, "/foo.ts": `export * from "./bar.ts";\n`,
"/bar.ts": `export const bar = "bar";\n`, "/bar.ts": `export const bar = "bar";\n`,
}); },
assert(diagnostics == null); },
assert(actual.includes(`const bar = "bar"`)); );
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
assertEquals(Object.keys(files), ["deno:///bundle.js"]);
assert(files["deno:///bundle.js"].includes(`const bar = "bar"`));
}, },
}); });
Deno.test({ Deno.test({
name: "Deno.bundle() - no sources passed", name: "Deno.emit() - bundle esm - no sources",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.bundle("./subdir/mod1.ts"); const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
assert(diagnostics == null); "./subdir/mod1.ts",
assert(actual.length); {
bundle: "esm",
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
assertEquals(Object.keys(files), ["deno:///bundle.js"]);
assert(files["deno:///bundle.js"].length);
}, },
}); });
Deno.test({ Deno.test({
name: "Deno.bundle() - JS Modules included", name: "Deno.emit() - bundle esm - include js modules",
async fn() { async fn() {
const [diagnostics, actual] = await Deno.bundle("/foo.js", { const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.js",
{
bundle: "esm",
sources: {
"/foo.js": `export * from "./bar.js";\n`, "/foo.js": `export * from "./bar.js";\n`,
"/bar.js": `export const bar = "bar";\n`, "/bar.js": `export const bar = "bar";\n`,
}); },
assert(diagnostics == null); },
assert(actual.includes(`const bar = "bar"`)); );
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
assertEquals(Object.keys(files), ["deno:///bundle.js"]);
assert(files["deno:///bundle.js"].includes(`const bar = "bar"`));
}, },
}); });
Deno.test({ Deno.test({
name: "runtime compiler APIs diagnostics", name: "Deno.emit() - generates diagnostics",
async fn() { async fn() {
const [diagnostics] = await Deno.compile("/foo.ts", { const { diagnostics, files } = await Deno.emit(
"/foo.ts",
{
sources: {
"/foo.ts": `document.getElementById("foo");`, "/foo.ts": `document.getElementById("foo");`,
}); },
assert(Array.isArray(diagnostics)); },
assert(diagnostics.length === 1); );
assertEquals(diagnostics.length, 1);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
}, },
}); });
// See https://github.com/denoland/deno/issues/6908 // See https://github.com/denoland/deno/issues/6908
Deno.test({ Deno.test({
name: "Deno.compile() - SWC diagnostics", name: "Deno.emit() - invalid syntax does not panic",
async fn() { async fn() {
await assertThrowsAsync(async () => { await assertThrowsAsync(async () => {
await Deno.compile("/main.js", { await Deno.emit("/main.js", {
sources: {
"/main.js": ` "/main.js": `
export class Foo { export class Foo {
constructor() { constructor() {
@ -195,24 +265,26 @@ Deno.test({
console.log("bar"); console.log("bar");
} }
}`, }`,
},
}); });
}); });
}, },
}); });
Deno.test({ Deno.test({
name: `Deno.compile() - Allows setting of "importsNotUsedAsValues"`, name: 'Deno.emit() - allows setting of "importsNotUsedAsValues"',
async fn() { async fn() {
const [diagnostics] = await Deno.compile("/a.ts", { const { diagnostics } = await Deno.emit("/a.ts", {
sources: {
"/a.ts": `import { B } from "./b.ts"; "/a.ts": `import { B } from "./b.ts";
const b: B = { b: "b" }; const b: B = { b: "b" };`,
`,
"/b.ts": `export interface B { "/b.ts": `export interface B {
b:string; b:string;
}; };`,
`, },
}, { compilerOptions: {
importsNotUsedAsValues: "error", importsNotUsedAsValues: "error",
},
}); });
assert(diagnostics); assert(diagnostics);
assertEquals(diagnostics.length, 1); assertEquals(diagnostics.length, 1);

View file

@ -1,14 +1,16 @@
const [errors, program] = await Deno.compile( const { diagnostics, files } = await Deno.emit(
"/main.ts", "/main.ts",
{ {
sources: {
"/main.ts": "/main.ts":
`/// <reference lib="dom" />\n\ndocument.getElementById("foo");\nDeno.args;`, `/// <reference lib="dom" />\n\ndocument.getElementById("foo");\nDeno.args;`,
}, },
{ compilerOptions: {
target: "es2018", target: "es2018",
lib: ["es2018", "deno.ns"], lib: ["es2018", "deno.ns"],
}, },
},
); );
console.log(errors); console.log(diagnostics);
console.log(Object.keys(program).sort()); console.log(Object.keys(files).sort());

View file

@ -1,2 +1,2 @@
undefined []
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ] [ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]

View file

@ -1,12 +1,14 @@
const [errors, program] = await Deno.compile( const { diagnostics, files } = await Deno.emit(
"/main.ts", "/main.ts",
{ {
sources: {
"/main.ts": `document.getElementById("foo");`, "/main.ts": `document.getElementById("foo");`,
}, },
{ compilerOptions: {
lib: ["dom", "esnext"], lib: ["dom", "esnext"],
}, },
},
); );
console.log(errors); console.log(diagnostics);
console.log(Object.keys(program).sort()); console.log(Object.keys(files).sort());

View file

@ -1,2 +1,2 @@
undefined []
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ] [ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]

View file

@ -1,5 +1,5 @@
console.log(Deno.permissions.query); console.log(Deno.permissions.query);
console.log(Deno.compile); console.log(Deno.emit);
self.onmessage = () => { self.onmessage = () => {
self.close(); self.close();
}; };

View file

@ -1,2 +1,2 @@
[Function: query] [Function: query]
[AsyncFunction: compile] [Function: emit]

View file

@ -49,6 +49,15 @@ impl fmt::Display for IgnoredCompilerOptions {
} }
} }
impl Serialize for IgnoredCompilerOptions {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Serialize::serialize(&self.items, serializer)
}
}
/// A static slice of all the compiler options that should be ignored that /// A static slice of all the compiler options that should be ignored that
/// either have no effect on the compilation or would cause the emit to not work /// either have no effect on the compilation or would cause the emit to not work
/// in Deno. /// in Deno.
@ -64,7 +73,6 @@ pub const IGNORED_COMPILER_OPTIONS: &[&str] = &[
"importHelpers", "importHelpers",
"inlineSourceMap", "inlineSourceMap",
"inlineSources", "inlineSources",
"isolatedModules",
"module", "module",
"noEmitHelpers", "noEmitHelpers",
"noLib", "noLib",
@ -97,6 +105,7 @@ pub const IGNORED_RUNTIME_COMPILER_OPTIONS: &[&str] = &[
"help", "help",
"incremental", "incremental",
"init", "init",
"isolatedModules",
"listEmittedFiles", "listEmittedFiles",
"listFiles", "listFiles",
"mapRoot", "mapRoot",
@ -246,6 +255,14 @@ impl TsConfig {
} }
} }
pub fn get_declaration(&self) -> bool {
if let Some(declaration) = self.0.get("declaration") {
declaration.as_bool().unwrap_or(false)
} else {
false
}
}
/// Merge a serde_json value into the configuration. /// Merge a serde_json value into the configuration.
pub fn merge(&mut self, value: &Value) { pub fn merge(&mut self, value: &Value) {
json_merge(&mut self.0, value); json_merge(&mut self.0, value);

View file

@ -1,97 +1,88 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
// @ts-check
// This file contains the runtime APIs which will dispatch work to the internal // This file contains the runtime APIs which will dispatch work to the internal
// compiler within Deno. // compiler within Deno.
((window) => { ((window) => {
const core = window.Deno.core; const core = window.Deno.core;
const util = window.__bootstrap.util; const util = window.__bootstrap.util;
function opCompile(request) { /**
return core.jsonOpAsync("op_compile", request); * @typedef {object} ImportMap
} * @property {Record<string, string>} imports
* @property {Record<string, Record<string, string>>=} scopes
function opTranspile( */
request,
) { /**
return core.jsonOpAsync("op_transpile", request); * @typedef {object} OpEmitRequest
* @property {"esm"=} bundle
* @property {boolean=} check
* @property {Record<string, any>=} compilerOptions
* @property {ImportMap=} importMap
* @property {string=} importMapPath
* @property {string} rootSpecifier
* @property {Record<string, string>=} sources
*/
/**
* @typedef OpEmitResponse
* @property {any[]} diagnostics
* @property {Record<string, string>} files
* @property {string[]=} ignoredOptions
* @property {Array<[string, number]>} stats
*/
/**
* @param {OpEmitRequest} request
* @returns {Promise<OpEmitResponse>}
*/
function opEmit(request) {
return core.jsonOpAsync("op_emit", request);
} }
/**
* @param {string} specifier
* @returns {string}
*/
function checkRelative(specifier) { function checkRelative(specifier) {
return specifier.match(/^([\.\/\\]|https?:\/{2}|file:\/{2})/) return specifier.match(/^([\.\/\\]|https?:\/{2}|file:\/{2})/)
? specifier ? specifier
: `./${specifier}`; : `./${specifier}`;
} }
// TODO(bartlomieju): change return type to interface? /**
function transpileOnly( * @typedef {object} EmitOptions
sources, * @property {"esm"=} bundle
options = {}, * @property {boolean=} check
) { * @property {Record<string, any>=} compilerOptions
util.log("Deno.transpileOnly", { sources: Object.keys(sources), options }); * @property {ImportMap=} importMap
const payload = { * @property {string=} importMapPath
sources, * @property {Record<string, string>=} sources
options: JSON.stringify(options), */
};
return opTranspile(payload); /**
* @param {string | URL} rootSpecifier
* @param {EmitOptions=} options
* @returns {Promise<OpEmitResponse>}
*/
function emit(rootSpecifier, options = {}) {
util.log(`Deno.emit`, { rootSpecifier });
if (!rootSpecifier) {
return Promise.reject(
new TypeError("A root specifier must be supplied."),
);
} }
if (!(typeof rootSpecifier === "string")) {
// TODO(bartlomieju): change return type to interface? rootSpecifier = rootSpecifier.toString();
async function compile(
rootName,
sources,
options = {},
) {
const payload = {
rootName: sources ? rootName : checkRelative(rootName),
sources,
options: JSON.stringify(options),
bundle: false,
};
util.log("Deno.compile", {
rootName: payload.rootName,
sources: !!sources,
options,
});
/** @type {{ emittedFiles: Record<string, string>, diagnostics: any[] }} */
const result = await opCompile(payload);
util.assert(result.emittedFiles);
const maybeDiagnostics = result.diagnostics.length === 0
? undefined
: result.diagnostics;
return [maybeDiagnostics, result.emittedFiles];
} }
if (!options.sources) {
// TODO(bartlomieju): change return type to interface? rootSpecifier = checkRelative(rootSpecifier);
async function bundle( }
rootName, return opEmit({ rootSpecifier, ...options });
sources,
options = {},
) {
const payload = {
rootName: sources ? rootName : checkRelative(rootName),
sources,
options: JSON.stringify(options),
bundle: true,
};
util.log("Deno.bundle", {
rootName: payload.rootName,
sources: !!sources,
options,
});
/** @type {{ emittedFiles: Record<string, string>, diagnostics: any[] }} */
const result = await opCompile(payload);
const output = result.emittedFiles["deno:///bundle.js"];
util.assert(output);
const maybeDiagnostics = result.diagnostics.length === 0
? undefined
: result.diagnostics;
return [maybeDiagnostics, output];
} }
window.__bootstrap.compilerApi = { window.__bootstrap.compilerApi = {
bundle, emit,
compile,
transpileOnly,
}; };
})(this); })(this);

View file

@ -94,9 +94,7 @@
signals: __bootstrap.signals.signals, signals: __bootstrap.signals.signals,
Signal: __bootstrap.signals.Signal, Signal: __bootstrap.signals.Signal,
SignalStream: __bootstrap.signals.SignalStream, SignalStream: __bootstrap.signals.SignalStream,
transpileOnly: __bootstrap.compilerApi.transpileOnly, emit: __bootstrap.compilerApi.emit,
compile: __bootstrap.compilerApi.compile,
bundle: __bootstrap.compilerApi.bundle,
permissions: __bootstrap.permissions.permissions, permissions: __bootstrap.permissions.permissions,
Permissions: __bootstrap.permissions.Permissions, Permissions: __bootstrap.permissions.Permissions,
PermissionStatus: __bootstrap.permissions.PermissionStatus, PermissionStatus: __bootstrap.permissions.PermissionStatus,