summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/diagnostics.rs4
-rw-r--r--cli/dts/lib.deno.unstable.d.ts188
-rw-r--r--cli/module_graph.rs223
-rw-r--r--cli/ops/runtime_compiler.rs153
-rw-r--r--cli/tests/compiler_api_test.ts316
-rw-r--r--cli/tests/lib_ref.ts20
-rw-r--r--cli/tests/lib_ref.ts.out2
-rw-r--r--cli/tests/lib_runtime_api.ts16
-rw-r--r--cli/tests/lib_runtime_api.ts.out2
-rw-r--r--cli/tests/subdir/worker_unstable.ts2
-rw-r--r--cli/tests/unstable_worker.ts.out2
-rw-r--r--cli/tsc_config.rs19
-rw-r--r--runtime/js/40_compiler_api.js139
-rw-r--r--runtime/js/90_deno_ns.js4
14 files changed, 583 insertions, 507 deletions
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index 419d89a97..773ee0b36 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -44,11 +44,10 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"UnixListenOptions",
"WritePermissionDescriptor",
"applySourceMap",
- "bundle",
- "compile",
"connect",
"consoleSize",
"createHttpClient",
+ "emit",
"formatDiagnostics",
"futime",
"futimeSync",
@@ -77,7 +76,6 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"symlinkSync",
"systemMemoryInfo",
"systemCpuInfo",
- "transpileOnly",
"umask",
"utime",
"utimeSync",
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index b3933762e..01d2ff2e1 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -290,6 +290,8 @@ declare namespace Deno {
/** Base directory to resolve non-relative module names. Defaults to
* `undefined`. */
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
* to `false`. */
checkJs?: boolean;
@@ -338,9 +340,6 @@ declare namespace Deno {
/** Emit the source alongside the source maps within a single file; requires
* `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */
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"`.
* Defaults to `"react"`. */
jsx?: "react" | "preserve" | "react-native";
@@ -393,12 +392,17 @@ declare namespace Deno {
/** Do not emit `"use strict"` directives in module output. Defaults to
* `false`. */
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
* compiled files. Defaults to `false`. */
noResolve?: boolean;
/** Disable strict checking of generic signatures in function types. Defaults
* to `false`. */
noStrictGenericChecks?: boolean;
+ /** Include 'undefined' in index signature results. Defaults to `false`. */
+ noUncheckedIndexedAccess?: boolean;
/** Report errors on unused locals. Defaults to `false`. */
noUnusedLocals?: boolean;
/** Report errors on unused parameters. Defaults to `false`. */
@@ -487,122 +491,78 @@ declare namespace Deno {
useDefineForClassFields?: boolean;
}
- /** **UNSTABLE**: new API, yet to be vetted.
- *
- * The results of a transpile only command, where the `source` contains the
- * emitted source, and `map` optionally contains the source map. */
- export interface TranspileOnlyResult {
- source: string;
- map?: string;
+ interface ImportMap {
+ imports: Record<string, string>;
+ scopes?: Record<string, Record<string, string>>;
}
- /** **UNSTABLE**: new API, yet to be vetted.
- *
- * Takes a set of TypeScript sources and resolves to a map where the key was
- * the original file name provided in sources and the result contains the
- * `source` and optionally the `map` from the transpile operation. This does no
- * type checking and validation, it effectively "strips" the types from the
- * file.
- *
- * ```ts
- * const results = await Deno.transpileOnly({
- * "foo.ts": `const foo: string = "foo";`
- * });
- * ```
- *
- * @param sources A map where the key is the filename and the value is the text
- * to transpile. The filename is only used in the transpile and
- * not resolved, for example to fill in the source name in the
- * source map.
- * @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(
- sources: Record<string, string>,
- options?: CompilerOptions,
- ): Promise<Record<string, TranspileOnlyResult>>;
+ 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`.
+ *
+ * *Note* by default, only TypeScript will be type checked, just like on
+ * the command line. Use the `compilerOptions` options of `checkJs` to
+ * enable type checking of JavaScript. */
+ check?: boolean;
+ /** A set of options that are aligned to TypeScript compiler options that
+ * are supported by Deno. */
+ compilerOptions?: CompilerOptions;
+ /** An [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps)
+ * 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
+ * determine resolution of relative paths. If a `importMap` is not
+ * specified, then it will assumed the file path points to an import map on
+ * disk and will be attempted to be loaded based on current runtime
+ * permissions.
+ */
+ importMapPath?: string;
+ /** A record of sources to use when doing the emit. If provided, Deno will
+ * use these sources instead of trying to resolve the modules externally. */
+ sources?: Record<string, string>;
+ }
- /** **UNSTABLE**: new API, yet to be vetted.
- *
- * Takes a root module name, and optionally a record set of sources. Resolves
- * with a compiled set of modules and possibly diagnostics if the compiler
- * encountered any issues. 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
- * the key is the module name and the value is the content. The extension of
- * the module name will be used to determine the media type of the module.
- *
- * ```ts
- * 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>]>;
+ interface EmitResult {
+ /** Diagnostic messages returned from the type checker (`tsc`). */
+ diagnostics: Diagnostic[];
+ /** Any emitted files. If bundled, then the JavaScript will have the
+ * key of `deno:///bundle.js` with an optional map (based on
+ * `compilerOptions`) in `deno:///bundle.js.map`. */
+ files: Record<string, string>;
+ /** An optional array of any compiler options that were ignored by Deno. */
+ ignoredOptions?: string[];
+ /** An array of internal statistics related to the emit, for diagnostic
+ * purposes. */
+ stats: Array<[string, number]>;
+ }
- /** **UNSTABLE**: new API, yet to be vetted.
- *
- * `bundle()` is part the compiler API. A full description of this functionality
- * can be found in the [manual](https://deno.land/manual/runtime/compiler_apis#denobundle).
- *
- * Takes a root module name, and optionally a record set of sources. Resolves
- * 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
- * the key is the module name and the value is the content. The extension of the
- * module name will be used to determine the media type of the module.
- *
- * ```ts
- * // equivalent to "deno bundle foo.ts" from the command line
- * 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.
+ /**
+ * **UNSTABLE**: new API, yet to be vetted.
+ *
+ * Similar to the command line functionality of `deno run` or `deno cache`,
+ * `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.
+ *
+ * Requires `allow-read` and/or `allow-net` if sources are not provided.
+ *
+ * @param rootSpecifier The specifier that will be used as the entry point.
+ * If no sources are provided, then the specifier would
+ * be the same as if you typed it on the command line for
+ * `deno run`. If sources are provided, it should match
+ * one of the names of the sources.
+ * @param options A set of options to be used with the emit.
*/
- export function bundle(
- rootName: string,
- sources?: Record<string, string>,
- options?: CompilerOptions,
- ): Promise<[Diagnostic[] | undefined, string]>;
+ export function emit(
+ rootSpecifier: string | URL,
+ options?: EmitOptions,
+ ): Promise<EmitResult>;
/** **UNSTABLE**: Should not have same name as `window.location` type. */
interface Location {
diff --git a/cli/module_graph.rs b/cli/module_graph.rs
index c8bd3d1f3..d1e51bd4c 100644
--- a/cli/module_graph.rs
+++ b/cli/module_graph.rs
@@ -497,18 +497,27 @@ impl Module {
}
#[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 {
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
- let items: Vec<(String, u128)> = Deserialize::deserialize(deserializer)?;
+ let items: Vec<(String, u32)> = Deserialize::deserialize(deserializer)?;
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Compilation statistics:")?;
@@ -620,6 +629,10 @@ impl Default for BundleType {
#[derive(Debug, Default)]
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.
pub bundle_type: BundleType,
/// 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 stats = Stats(vec![
- ("Files".to_string(), self.modules.len() as u128),
- ("Total time".to_string(), start.elapsed().as_millis()),
+ ("Files".to_string(), self.modules.len() as u32),
+ ("Total time".to_string(), start.elapsed().as_millis() as u32),
]);
Ok((s, stats, maybe_ignored_options))
@@ -918,18 +931,22 @@ impl Graph {
/// emitting single modules as well as bundles, using Deno module resolution
/// or supplied sources.
pub fn emit(
- self,
+ mut self,
options: EmitOptions,
) -> Result<(HashMap<String, String>, ResultInfo), AnyError> {
let mut config = TsConfig::new(json!({
"allowJs": true,
+ "checkJs": false,
// TODO(@kitsonk) consider enabling this by default
// see: https://github.com/denoland/deno/issues/7732
"emitDecoratorMetadata": false,
"esModuleInterop": true,
"experimentalDecorators": true,
+ "inlineSourceMap": false,
"isolatedModules": true,
"jsx": "react",
+ "jsxFactory": "React.createElement",
+ "jsxFragmentFactory": "React.Fragment",
"lib": TypeLib::DenoWindow,
"module": "esnext",
"strict": true,
@@ -937,11 +954,7 @@ impl Graph {
}));
let opts = match options.bundle_type {
BundleType::Esm => json!({
- "checkJs": false,
- "inlineSourceMap": false,
"noEmit": true,
- "jsxFactory": "React.createElement",
- "jsxFragmentFactory": "React.Fragment",
}),
BundleType::None => json!({
"outDir": "deno://",
@@ -957,74 +970,138 @@ impl Graph {
None
};
- let root_names = self.get_root_names(!config.get_check_js());
- let hash_data =
- vec![config.as_bytes(), version::deno().as_bytes().to_owned()];
- let graph = Arc::new(Mutex::new(self));
-
- let response = tsc::exec(
- js::compiler_isolate_init(),
- tsc::Request {
- config: config.clone(),
- debug: options.debug,
- graph: graph.clone(),
- hash_data,
- maybe_tsbuildinfo: None,
- root_names,
- },
- )?;
+ 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();
- let graph = graph.lock().unwrap();
- match options.bundle_type {
- BundleType::Esm => {
- assert!(
- response.emitted_files.is_empty(),
- "No files should have been emitted from tsc."
- );
- assert_eq!(
- graph.roots.len(),
- 1,
- "Only a single root module supported."
- );
- let specifier = &graph.roots[0];
- let s = graph.emit_bundle(specifier, &config.into())?;
- emitted_files.insert("deno:///bundle.js".to_string(), s);
- }
- BundleType::None => {
- for emitted_file in &response.emitted_files {
+ if options.check {
+ let root_names = self.get_root_names(!config.get_check_js());
+ let hash_data =
+ vec![config.as_bytes(), version::deno().as_bytes().to_owned()];
+ let graph = Arc::new(Mutex::new(self));
+ let response = tsc::exec(
+ js::compiler_isolate_init(),
+ tsc::Request {
+ config: config.clone(),
+ debug: options.debug,
+ graph: graph.clone(),
+ hash_data,
+ maybe_tsbuildinfo: None,
+ root_names,
+ },
+ )?;
+
+ let graph = graph.lock().unwrap();
+ match options.bundle_type {
+ BundleType::Esm => {
assert!(
- emitted_file.maybe_specifiers.is_some(),
- "Orphaned file emitted."
+ response.emitted_files.is_empty(),
+ "No files should have been emitted from tsc."
);
- let specifiers = emitted_file.maybe_specifiers.clone().unwrap();
assert_eq!(
- specifiers.len(),
+ graph.roots.len(),
1,
- "An unexpected number of specifiers associated with emitted file."
+ "Only a single root module supported."
);
- let specifier = specifiers[0].clone();
- let extension = match emitted_file.media_type {
- MediaType::JavaScript => ".js",
- MediaType::SourceMap => ".js.map",
- MediaType::Dts => ".d.ts",
- _ => unreachable!(),
- };
- let key = format!("{}{}", specifier, extension);
- emitted_files.insert(key, emitted_file.data.clone());
+ let specifier = &graph.roots[0];
+ let s = graph.emit_bundle(specifier, &config.into())?;
+ emitted_files.insert("deno:///bundle.js".to_string(), s);
+ }
+ BundleType::None => {
+ for emitted_file in &response.emitted_files {
+ assert!(
+ emitted_file.maybe_specifiers.is_some(),
+ "Orphaned file emitted."
+ );
+ let specifiers = emitted_file.maybe_specifiers.clone().unwrap();
+ assert_eq!(
+ specifiers.len(),
+ 1,
+ "An unexpected number of specifiers associated with emitted file."
+ );
+ let specifier = specifiers[0].clone();
+ let extension = match emitted_file.media_type {
+ MediaType::JavaScript => ".js",
+ MediaType::SourceMap => ".js.map",
+ MediaType::Dts => ".d.ts",
+ _ => unreachable!(),
+ };
+ let key = format!("{}{}", specifier, extension);
+ emitted_files.insert(key, emitted_file.data.clone());
+ }
+ }
+ };
+
+ Ok((
+ emitted_files,
+ ResultInfo {
+ diagnostics: response.diagnostics,
+ loadable_modules: graph.get_loadable_modules(),
+ maybe_ignored_options,
+ 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()?;
}
}
- };
- Ok((
- emitted_files,
- ResultInfo {
- diagnostics: response.diagnostics,
- loadable_modules: graph.get_loadable_modules(),
- maybe_ignored_options,
- stats: response.stats,
- },
- ))
+ 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()`.
@@ -1566,10 +1643,9 @@ impl Graph {
let maybe_ignored_options =
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 emit_options: ast::EmitOptions = ts_config.into();
+ let mut emit_count = 0_u32;
for (_, module_slot) in self.modules.iter_mut() {
if let ModuleSlot::Module(module) = module_slot {
// TODO(kitsonk) a lot of this logic should be refactored into `Module` as
@@ -1604,9 +1680,9 @@ impl Graph {
self.flush()?;
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),
- ("Total time".to_string(), start.elapsed().as_millis()),
+ ("Total time".to_string(), start.elapsed().as_millis() as u32),
]);
Ok(ResultInfo {
@@ -2270,6 +2346,7 @@ pub mod tests {
.await;
let (emitted_files, result_info) = graph
.emit(EmitOptions {
+ check: true,
bundle_type: BundleType::None,
debug: false,
maybe_user_config: None,
@@ -2310,6 +2387,7 @@ pub mod tests {
.await;
let (emitted_files, result_info) = graph
.emit(EmitOptions {
+ check: true,
bundle_type: BundleType::Esm,
debug: false,
maybe_user_config: None,
@@ -2347,6 +2425,7 @@ pub mod tests {
user_config.insert("declaration".to_string(), json!(true));
let (emitted_files, result_info) = graph
.emit(EmitOptions {
+ check: true,
bundle_type: BundleType::None,
debug: false,
maybe_user_config: Some(user_config),
diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs
index bb3e47226..774b280ba 100644
--- a/cli/ops/runtime_compiler.rs
+++ b/cli/ops/runtime_compiler.rs
@@ -1,8 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-use crate::ast;
-use crate::colors;
-use crate::media_type::MediaType;
+use crate::import_map::ImportMap;
use crate::module_graph::BundleType;
use crate::module_graph::EmitOptions;
use crate::module_graph::GraphBuilder;
@@ -10,11 +8,10 @@ use crate::program_state::ProgramState;
use crate::specifier_handler::FetchHandler;
use crate::specifier_handler::MemoryHandler;
use crate::specifier_handler::SpecifierHandler;
-use crate::tsc_config;
+use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::error::Context;
-use deno_core::serde::Serialize;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
@@ -30,37 +27,46 @@ use std::sync::Arc;
use std::sync::Mutex;
pub fn init(rt: &mut deno_core::JsRuntime) {
- super::reg_json_async(rt, "op_compile", op_compile);
- super::reg_json_async(rt, "op_transpile", op_transpile);
+ super::reg_json_async(rt, "op_emit", op_emit);
}
-#[derive(Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
+enum RuntimeBundleType {
+ #[serde(rename = "esm")]
+ Esm,
+}
+
+#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
-struct CompileArgs {
- root_name: String,
+struct EmitArgs {
+ 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>>,
- bundle: bool,
- options: Option<String>,
}
-async fn op_compile(
+async fn op_emit(
state: Rc<RefCell<OpState>>,
args: Value,
_data: BufVec,
) -> Result<Value, AnyError> {
- let args: CompileArgs = serde_json::from_value(args)?;
- if args.bundle {
- deno_runtime::ops::check_unstable2(&state, "Deno.bundle");
- } else {
- deno_runtime::ops::check_unstable2(&state, "Deno.compile");
- }
+ deno_runtime::ops::check_unstable2(&state, "Deno.emit");
+ let args: EmitArgs = serde_json::from_value(args)?;
let program_state = state.borrow().borrow::<Arc<ProgramState>>().clone();
let runtime_permissions = {
let state = state.borrow();
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>> =
if let Some(sources) = args.sources {
+ is_dynamic = true;
Arc::new(Mutex::new(MemoryHandler::new(sources)))
} else {
Arc::new(Mutex::new(FetchHandler::new(
@@ -68,93 +74,44 @@ async fn op_compile(
runtime_permissions,
)?))
};
- let mut builder = GraphBuilder::new(handler, None, None);
- let specifier = ModuleSpecifier::resolve_url_or_path(&args.root_name)
- .context("The root specifier is invalid.")?;
- builder.add(&specifier, false).await?;
- let graph = builder.get_graph();
- let bundle_type = if args.bundle {
- BundleType::Esm
+ let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
+ let import_map_specifier =
+ ModuleSpecifier::resolve_url_or_path(&import_map_str).context(
+ format!("Bad file path (\"{}\") for import map.", import_map_str),
+ )?;
+ let import_map_url = import_map_specifier.as_url();
+ let import_map = if let Some(value) = args.import_map {
+ ImportMap::from_json(&import_map_url.to_string(), &value.to_string())?
+ } else {
+ ImportMap::load(&import_map_str)?
+ };
+ Some(import_map)
+ } else if args.import_map.is_some() {
+ return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required."));
} else {
- BundleType::None
+ None
+ };
+ 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 maybe_user_config: Option<HashMap<String, Value>> =
- if let Some(options) = args.options {
- Some(serde_json::from_str(&options)?)
- } else {
- None
- };
- let (emitted_files, result_info) = graph.emit(EmitOptions {
+ let (files, result_info) = graph.emit(EmitOptions {
bundle_type,
+ check: args.check.unwrap_or(true),
debug,
- maybe_user_config,
+ maybe_user_config: args.compiler_options,
})?;
Ok(json!({
- "emittedFiles": emitted_files,
"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)
-}
diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts
index 4535ad6ed..6f58356df 100644
--- a/cli/tests/compiler_api_test.ts
+++ b/cli/tests/compiler_api_test.ts
@@ -6,15 +6,21 @@ import {
} from "../../std/testing/asserts.ts";
Deno.test({
- name: "Deno.compile() - sources provided",
+ name: "Deno.emit() - sources provided",
async fn() {
- const [diagnostics, actual] = await Deno.compile("/foo.ts", {
- "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
- "/bar.ts": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual);
- const keys = Object.keys(actual).sort();
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "/foo.ts",
+ {
+ sources: {
+ "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
+ "/bar.ts": `export const bar = "bar";\n`,
+ },
+ },
+ );
+ 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[1].endsWith("/bar.ts.js.map"));
assert(keys[2].endsWith("/foo.ts.js"));
@@ -23,12 +29,15 @@ Deno.test({
});
Deno.test({
- name: "Deno.compile() - no sources provided",
+ name: "Deno.emit() - no sources provided",
async fn() {
- const [diagnostics, actual] = await Deno.compile("./subdir/mod1.ts");
- assert(diagnostics == null);
- assert(actual);
- const keys = Object.keys(actual).sort();
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "./subdir/mod1.ts",
+ );
+ assertEquals(diagnostics.length, 0);
+ assert(!ignoredOptions);
+ assertEquals(stats.length, 12);
+ const keys = Object.keys(files).sort();
assertEquals(keys.length, 6);
assert(keys[0].endsWith("cli/tests/subdir/mod1.ts.js"));
assert(keys[1].endsWith("cli/tests/subdir/mod1.ts.js.map"));
@@ -36,183 +45,246 @@ Deno.test({
});
Deno.test({
- name: "Deno.compile() - compiler options effects emit",
+ name: "Deno.emit() - compiler options effects emit",
async fn() {
- const [diagnostics, actual] = await Deno.compile(
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
- "/foo.ts": `export const foo = "foo";`,
- },
- {
- module: "amd",
- sourceMap: false,
+ compilerOptions: {
+ module: "amd",
+ sourceMap: false,
+ },
+ sources: { "/foo.ts": `export const foo = "foo";` },
},
);
- assert(diagnostics == null);
- assert(actual);
- const keys = Object.keys(actual);
+ assertEquals(diagnostics.length, 0);
+ assert(!ignoredOptions);
+ assertEquals(stats.length, 12);
+ const keys = Object.keys(files);
assertEquals(keys.length, 1);
const key = keys[0];
assert(key.endsWith("/foo.ts.js"));
- assert(actual[key].startsWith("define("));
+ assert(files[key].startsWith("define("));
},
});
Deno.test({
- name: "Deno.compile() - pass lib in compiler options",
+ name: "Deno.emit() - pass lib in compiler options",
async fn() {
- const [diagnostics, actual] = await Deno.compile(
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///foo.ts",
{
- "file:///foo.ts": `console.log(document.getElementById("foo"));
- console.log(Deno.args);`,
+ compilerOptions: {
+ lib: ["dom", "es2018", "deno.ns"],
+ },
+ sources: {
+ "file:///foo.ts": `console.log(document.getElementById("foo"));
+ console.log(Deno.args);`,
+ },
},
+ );
+ assertEquals(diagnostics.length, 0);
+ 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",
{
- lib: ["dom", "es2018", "deno.ns"],
+ 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";`,
+ },
},
);
- assert(diagnostics == null);
- assert(actual);
+ assertEquals(diagnostics.length, 0);
+ assert(!ignoredOptions);
+ assertEquals(stats.length, 12);
+ const keys = Object.keys(files).sort();
assertEquals(
- Object.keys(actual).sort(),
- ["file:///foo.ts.js", "file:///foo.ts.js.map"],
+ keys,
+ [
+ "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({
- name: "Deno.transpileOnly()",
+ name: "Deno.emit() - no check",
async fn() {
- const actual = await Deno.transpileOnly({
- "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
- });
- assert(actual);
- assertEquals(Object.keys(actual), ["foo.ts"]);
- assert(actual["foo.ts"].source.startsWith("export var Foo;"));
- assert(actual["foo.ts"].map);
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "/foo.ts",
+ {
+ check: false,
+ sources: {
+ "/foo.ts": `export 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]].startsWith("export var Foo;"));
},
});
Deno.test({
- name: "Deno.transpileOnly() - config effects commit",
+ name: "Deno.emit() - no check - config effects emit",
async fn() {
- const actual = await Deno.transpileOnly(
- {
- "foo.ts": `/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`,
- },
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "/foo.ts",
{
- removeComments: true,
+ check: false,
+ compilerOptions: { removeComments: true },
+ sources: {
+ "/foo.ts":
+ `/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`,
+ },
},
);
- assert(actual);
- assertEquals(Object.keys(actual), ["foo.ts"]);
- assert(!actual["foo.ts"].source.includes("This is JSDoc"));
- assert(actual["foo.ts"].map);
+ 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.bundle() - sources passed",
+ name: "Deno.emit() - bundle esm - with sources",
async fn() {
- const [diagnostics, actual] = await Deno.bundle("/foo.ts", {
- "/foo.ts": `export * from "./bar.ts";\n`,
- "/bar.ts": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual.includes(`const bar = "bar"`));
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "/foo.ts",
+ {
+ bundle: "esm",
+ sources: {
+ "/foo.ts": `export * from "./bar.ts";\n`,
+ "/bar.ts": `export const bar = "bar";\n`,
+ },
+ },
+ );
+ 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({
- name: "Deno.bundle() - no sources passed",
+ name: "Deno.emit() - bundle esm - no sources",
async fn() {
- const [diagnostics, actual] = await Deno.bundle("./subdir/mod1.ts");
- assert(diagnostics == null);
- assert(actual.length);
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "./subdir/mod1.ts",
+ {
+ 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({
- name: "Deno.bundle() - JS Modules included",
+ name: "Deno.emit() - bundle esm - include js modules",
async fn() {
- const [diagnostics, actual] = await Deno.bundle("/foo.js", {
- "/foo.js": `export * from "./bar.js";\n`,
- "/bar.js": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual.includes(`const bar = "bar"`));
+ const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
+ "/foo.js",
+ {
+ bundle: "esm",
+ sources: {
+ "/foo.js": `export * from "./bar.js";\n`,
+ "/bar.js": `export const bar = "bar";\n`,
+ },
+ },
+ );
+ 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({
- name: "runtime compiler APIs diagnostics",
+ name: "Deno.emit() - generates diagnostics",
async fn() {
- const [diagnostics] = await Deno.compile("/foo.ts", {
- "/foo.ts": `document.getElementById("foo");`,
- });
- assert(Array.isArray(diagnostics));
- assert(diagnostics.length === 1);
+ const { diagnostics, files } = await Deno.emit(
+ "/foo.ts",
+ {
+ sources: {
+ "/foo.ts": `document.getElementById("foo");`,
+ },
+ },
+ );
+ 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
Deno.test({
- name: "Deno.compile() - SWC diagnostics",
+ name: "Deno.emit() - invalid syntax does not panic",
async fn() {
await assertThrowsAsync(async () => {
- await Deno.compile("/main.js", {
- "/main.js": `
- export class Foo {
- constructor() {
- console.log("foo");
- }
- export get() {
- console.log("bar");
- }
- }`,
+ await Deno.emit("/main.js", {
+ sources: {
+ "/main.js": `
+ export class Foo {
+ constructor() {
+ console.log("foo");
+ }
+ export get() {
+ console.log("bar");
+ }
+ }`,
+ },
});
});
},
});
Deno.test({
- name: `Deno.compile() - Allows setting of "importsNotUsedAsValues"`,
+ name: 'Deno.emit() - allows setting of "importsNotUsedAsValues"',
async fn() {
- const [diagnostics] = await Deno.compile("/a.ts", {
- "/a.ts": `import { B } from "./b.ts";
- const b: B = { b: "b" };
- `,
- "/b.ts": `export interface B {
- b: string;
- };
- `,
- }, {
- importsNotUsedAsValues: "error",
+ const { diagnostics } = await Deno.emit("/a.ts", {
+ sources: {
+ "/a.ts": `import { B } from "./b.ts";
+ const b: B = { b: "b" };`,
+ "/b.ts": `export interface B {
+ b:string;
+ };`,
+ },
+ compilerOptions: {
+ importsNotUsedAsValues: "error",
+ },
});
assert(diagnostics);
assertEquals(diagnostics.length, 1);
diff --git a/cli/tests/lib_ref.ts b/cli/tests/lib_ref.ts
index 7b7bc4eca..2454f8b5d 100644
--- a/cli/tests/lib_ref.ts
+++ b/cli/tests/lib_ref.ts
@@ -1,14 +1,16 @@
-const [errors, program] = await Deno.compile(
+const { diagnostics, files } = await Deno.emit(
"/main.ts",
{
- "/main.ts":
- `/// <reference lib="dom" />\n\ndocument.getElementById("foo");\nDeno.args;`,
- },
- {
- target: "es2018",
- lib: ["es2018", "deno.ns"],
+ sources: {
+ "/main.ts":
+ `/// <reference lib="dom" />\n\ndocument.getElementById("foo");\nDeno.args;`,
+ },
+ compilerOptions: {
+ target: "es2018",
+ lib: ["es2018", "deno.ns"],
+ },
},
);
-console.log(errors);
-console.log(Object.keys(program).sort());
+console.log(diagnostics);
+console.log(Object.keys(files).sort());
diff --git a/cli/tests/lib_ref.ts.out b/cli/tests/lib_ref.ts.out
index 7aee0cc58..4e0f933fc 100644
--- a/cli/tests/lib_ref.ts.out
+++ b/cli/tests/lib_ref.ts.out
@@ -1,2 +1,2 @@
-undefined
+[]
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]
diff --git a/cli/tests/lib_runtime_api.ts b/cli/tests/lib_runtime_api.ts
index fc00825e9..450d9480b 100644
--- a/cli/tests/lib_runtime_api.ts
+++ b/cli/tests/lib_runtime_api.ts
@@ -1,12 +1,14 @@
-const [errors, program] = await Deno.compile(
+const { diagnostics, files } = await Deno.emit(
"/main.ts",
{
- "/main.ts": `document.getElementById("foo");`,
- },
- {
- lib: ["dom", "esnext"],
+ sources: {
+ "/main.ts": `document.getElementById("foo");`,
+ },
+ compilerOptions: {
+ lib: ["dom", "esnext"],
+ },
},
);
-console.log(errors);
-console.log(Object.keys(program).sort());
+console.log(diagnostics);
+console.log(Object.keys(files).sort());
diff --git a/cli/tests/lib_runtime_api.ts.out b/cli/tests/lib_runtime_api.ts.out
index 7aee0cc58..4e0f933fc 100644
--- a/cli/tests/lib_runtime_api.ts.out
+++ b/cli/tests/lib_runtime_api.ts.out
@@ -1,2 +1,2 @@
-undefined
+[]
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]
diff --git a/cli/tests/subdir/worker_unstable.ts b/cli/tests/subdir/worker_unstable.ts
index 3a1ed8b74..a5b5f7ba2 100644
--- a/cli/tests/subdir/worker_unstable.ts
+++ b/cli/tests/subdir/worker_unstable.ts
@@ -1,5 +1,5 @@
console.log(Deno.permissions.query);
-console.log(Deno.compile);
+console.log(Deno.emit);
self.onmessage = () => {
self.close();
};
diff --git a/cli/tests/unstable_worker.ts.out b/cli/tests/unstable_worker.ts.out
index cdbb7e911..ece47de97 100644
--- a/cli/tests/unstable_worker.ts.out
+++ b/cli/tests/unstable_worker.ts.out
@@ -1,2 +1,2 @@
[Function: query]
-[AsyncFunction: compile]
+[Function: emit]
diff --git a/cli/tsc_config.rs b/cli/tsc_config.rs
index 16661c768..1f420ed3a 100644
--- a/cli/tsc_config.rs
+++ b/cli/tsc_config.rs
@@ -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
/// either have no effect on the compilation or would cause the emit to not work
/// in Deno.
@@ -64,7 +73,6 @@ pub const IGNORED_COMPILER_OPTIONS: &[&str] = &[
"importHelpers",
"inlineSourceMap",
"inlineSources",
- "isolatedModules",
"module",
"noEmitHelpers",
"noLib",
@@ -97,6 +105,7 @@ pub const IGNORED_RUNTIME_COMPILER_OPTIONS: &[&str] = &[
"help",
"incremental",
"init",
+ "isolatedModules",
"listEmittedFiles",
"listFiles",
"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.
pub fn merge(&mut self, value: &Value) {
json_merge(&mut self.0, value);
diff --git a/runtime/js/40_compiler_api.js b/runtime/js/40_compiler_api.js
index ea963b67b..ced4bcb00 100644
--- a/runtime/js/40_compiler_api.js
+++ b/runtime/js/40_compiler_api.js
@@ -1,97 +1,88 @@
// 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
// compiler within Deno.
((window) => {
const core = window.Deno.core;
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
+ */
+
+ /**
+ * @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
+ */
- function opTranspile(
- request,
- ) {
- return core.jsonOpAsync("op_transpile", request);
+ /**
+ * @param {OpEmitRequest} request
+ * @returns {Promise<OpEmitResponse>}
+ */
+ function opEmit(request) {
+ return core.jsonOpAsync("op_emit", request);
}
+ /**
+ * @param {string} specifier
+ * @returns {string}
+ */
function checkRelative(specifier) {
return specifier.match(/^([\.\/\\]|https?:\/{2}|file:\/{2})/)
? specifier
: `./${specifier}`;
}
- // TODO(bartlomieju): change return type to interface?
- function transpileOnly(
- sources,
- options = {},
- ) {
- util.log("Deno.transpileOnly", { sources: Object.keys(sources), options });
- const payload = {
- sources,
- options: JSON.stringify(options),
- };
- return opTranspile(payload);
- }
-
- // TODO(bartlomieju): change return type to interface?
- 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];
- }
+ /**
+ * @typedef {object} EmitOptions
+ * @property {"esm"=} bundle
+ * @property {boolean=} check
+ * @property {Record<string, any>=} compilerOptions
+ * @property {ImportMap=} importMap
+ * @property {string=} importMapPath
+ * @property {Record<string, string>=} sources
+ */
- // TODO(bartlomieju): change return type to interface?
- async function bundle(
- rootName,
- 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];
+ /**
+ * @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")) {
+ rootSpecifier = rootSpecifier.toString();
+ }
+ if (!options.sources) {
+ rootSpecifier = checkRelative(rootSpecifier);
+ }
+ return opEmit({ rootSpecifier, ...options });
}
window.__bootstrap.compilerApi = {
- bundle,
- compile,
- transpileOnly,
+ emit,
};
})(this);
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 9188788ec..c71f175e4 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -94,9 +94,7 @@
signals: __bootstrap.signals.signals,
Signal: __bootstrap.signals.Signal,
SignalStream: __bootstrap.signals.SignalStream,
- transpileOnly: __bootstrap.compilerApi.transpileOnly,
- compile: __bootstrap.compilerApi.compile,
- bundle: __bootstrap.compilerApi.bundle,
+ emit: __bootstrap.compilerApi.emit,
permissions: __bootstrap.permissions.permissions,
Permissions: __bootstrap.permissions.Permissions,
PermissionStatus: __bootstrap.permissions.PermissionStatus,