summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSatya Rohith <me@satyarohith.com>2021-05-19 09:48:01 +0530
committerGitHub <noreply@github.com>2021-05-19 14:18:01 +1000
commit5127bb0d895bd2b5bfae62d977f786dc3014bea5 (patch)
tree1f7557a36a0ba77a5027b4eecf3b4c96ade39056
parent7cf674d4111b0c9e21f2dcba0dcfd8213573fa5f (diff)
fix(runtime): support source maps with Deno.emit() and bundle (#10510)
Closes: #10413
-rw-r--r--cli/ast.rs5
-rw-r--r--cli/config_file.rs1
-rw-r--r--cli/module_graph.rs54
-rw-r--r--cli/tests/compiler_api_test.ts57
4 files changed, 102 insertions, 15 deletions
diff --git a/cli/ast.rs b/cli/ast.rs
index 76a5f1362..636dc1881 100644
--- a/cli/ast.rs
+++ b/cli/ast.rs
@@ -205,6 +205,9 @@ pub struct EmitOptions {
/// Should the source map be inlined in the emitted code file, or provided
/// as a separate file. Defaults to `true`.
pub inline_source_map: bool,
+ // Should a corresponding .map file be created for the output. This should be
+ // false if inline_source_map is true. Defaults to `false`.
+ pub source_map: bool,
/// When transforming JSX, what value should be used for the JSX factory.
/// Defaults to `React.createElement`.
pub jsx_factory: String,
@@ -222,6 +225,7 @@ impl Default for EmitOptions {
emit_metadata: false,
imports_not_used_as_values: ImportsNotUsedAsValues::Remove,
inline_source_map: true,
+ source_map: false,
jsx_factory: "React.createElement".into(),
jsx_fragment_factory: "React.Fragment".into(),
transform_jsx: true,
@@ -244,6 +248,7 @@ impl From<config_file::TsConfig> for EmitOptions {
emit_metadata: options.emit_decorator_metadata,
imports_not_used_as_values,
inline_source_map: options.inline_source_map,
+ source_map: options.source_map,
jsx_factory: options.jsx_factory,
jsx_fragment_factory: options.jsx_fragment_factory,
transform_jsx: options.jsx == "react",
diff --git a/cli/config_file.rs b/cli/config_file.rs
index a7fe7f2de..1875f9906 100644
--- a/cli/config_file.rs
+++ b/cli/config_file.rs
@@ -24,6 +24,7 @@ pub struct EmitConfigOptions {
pub emit_decorator_metadata: bool,
pub imports_not_used_as_values: String,
pub inline_source_map: bool,
+ pub source_map: bool,
pub jsx: String,
pub jsx_factory: String,
pub jsx_fragment_factory: String,
diff --git a/cli/module_graph.rs b/cli/module_graph.rs
index 1ca738977..8613be1e7 100644
--- a/cli/module_graph.rs
+++ b/cli/module_graph.rs
@@ -769,7 +769,8 @@ impl Graph {
"checkJs": false,
"emitDecoratorMetadata": false,
"importsNotUsedAsValues": "remove",
- "inlineSourceMap": true,
+ "inlineSourceMap": false,
+ "sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
@@ -777,7 +778,7 @@ impl Graph {
let maybe_ignored_options = ts_config
.merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
- let s = self.emit_bundle(
+ let (src, _) = self.emit_bundle(
&root_specifier,
&ts_config.into(),
&BundleType::Module,
@@ -787,7 +788,7 @@ impl Graph {
("Total time".to_string(), start.elapsed().as_millis() as u32),
]);
- Ok((s, stats, maybe_ignored_options))
+ Ok((src, stats, maybe_ignored_options))
}
/// Type check the module graph, corresponding to the options provided.
@@ -945,6 +946,7 @@ impl Graph {
"experimentalDecorators": true,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": false,
+ "sourceMap": false,
"isolatedModules": true,
"jsx": "react",
"jsxFactory": "React.createElement",
@@ -958,6 +960,8 @@ impl Graph {
let opts = match options.bundle_type {
BundleType::Module | BundleType::Classic => json!({
"noEmit": true,
+ "removeComments": true,
+ "sourceMap": true,
}),
BundleType::None => json!({
"outDir": "deno://",
@@ -1008,12 +1012,15 @@ impl Graph {
"Only a single root module supported."
);
let specifier = &graph.roots[0];
- let s = graph.emit_bundle(
+ let (src, maybe_src_map) = graph.emit_bundle(
specifier,
&config.into(),
&options.bundle_type,
)?;
- emitted_files.insert("deno:///bundle.js".to_string(), s);
+ emitted_files.insert("deno:///bundle.js".to_string(), src);
+ if let Some(src_map) = maybe_src_map {
+ emitted_files.insert("deno:///bundle.js.map".to_string(), src_map);
+ }
}
BundleType::None => {
for emitted_file in &response.emitted_files {
@@ -1060,13 +1067,16 @@ impl Graph {
"Only a single root module supported."
);
let specifier = &self.roots[0];
- let s = self.emit_bundle(
+ let (src, maybe_src_map) = self.emit_bundle(
specifier,
&config.into(),
&options.bundle_type,
)?;
emit_count += 1;
- emitted_files.insert("deno:///bundle.js".to_string(), s);
+ emitted_files.insert("deno:///bundle.js".to_string(), src);
+ if let Some(src_map) = maybe_src_map {
+ emitted_files.insert("deno:///bundle.js.map".to_string(), src_map);
+ }
}
BundleType::None => {
let emit_options: ast::EmitOptions = config.into();
@@ -1118,7 +1128,7 @@ impl Graph {
specifier: &ModuleSpecifier,
emit_options: &ast::EmitOptions,
bundle_type: &BundleType,
- ) -> Result<String, AnyError> {
+ ) -> Result<(String, Option<String>), AnyError> {
let cm = Rc::new(swc_common::SourceMap::new(
swc_common::FilePathMapping::empty(),
));
@@ -1150,13 +1160,17 @@ impl Graph {
.bundle(entries)
.context("Unable to output bundle during Graph::bundle().")?;
let mut buf = Vec::new();
+ let mut src_map_buf = Vec::new();
{
let mut emitter = swc_ecmascript::codegen::Emitter {
cfg: swc_ecmascript::codegen::Config { minify: false },
cm: cm.clone(),
comments: None,
wr: Box::new(swc_ecmascript::codegen::text_writer::JsWriter::new(
- cm, "\n", &mut buf, None,
+ cm.clone(),
+ "\n",
+ &mut buf,
+ Some(&mut src_map_buf),
)),
};
@@ -1164,8 +1178,24 @@ impl Graph {
.emit_module(&output[0].module)
.context("Unable to emit bundle during Graph::bundle().")?;
}
+ let mut src = String::from_utf8(buf)
+ .context("Emitted bundle is an invalid utf-8 string.")?;
+ let mut map: Option<String> = None;
+ {
+ let mut buf = Vec::new();
+ cm.build_source_map_from(&mut src_map_buf, None)
+ .to_writer(&mut buf)?;
+
+ if emit_options.inline_source_map {
+ src.push_str("//# sourceMappingURL=data:application/json;base64,");
+ let encoded_map = base64::encode(buf);
+ src.push_str(&encoded_map);
+ } else if emit_options.source_map {
+ map = Some(String::from_utf8(buf)?);
+ }
+ }
- String::from_utf8(buf).context("Emitted bundle is an invalid utf-8 string.")
+ Ok((src, map))
}
/// Update the handler with any modules that are marked as _dirty_ and update
@@ -1606,6 +1636,7 @@ impl Graph {
"emitDecoratorMetadata": false,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": true,
+ "sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
@@ -2413,9 +2444,10 @@ pub mod tests {
.expect("should have emitted");
assert!(result_info.diagnostics.is_empty());
assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(emitted_files.len(), 1);
+ assert_eq!(emitted_files.len(), 2);
let actual = emitted_files.get("deno:///bundle.js");
assert!(actual.is_some());
+ assert!(emitted_files.contains_key("deno:///bundle.js.map"));
let actual = actual.unwrap();
assert!(actual.contains("const b = \"b\";"));
assert!(actual.contains("console.log(mod);"));
diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts
index c6e7de651..00116e7e1 100644
--- a/cli/tests/compiler_api_test.ts
+++ b/cli/tests/compiler_api_test.ts
@@ -2,6 +2,7 @@
import {
assert,
assertEquals,
+ assertStringIncludes,
assertThrowsAsync,
} from "../../test_util/std/testing/asserts.ts";
@@ -188,7 +189,10 @@ Deno.test({
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
- assertEquals(Object.keys(files), ["deno:///bundle.js"]);
+ assertEquals(
+ Object.keys(files).sort(),
+ ["deno:///bundle.js", "deno:///bundle.js.map"].sort(),
+ );
assert(files["deno:///bundle.js"].includes(`const bar1 = "bar"`));
},
});
@@ -205,7 +209,10 @@ Deno.test({
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
- assertEquals(Object.keys(files), ["deno:///bundle.js"]);
+ assertEquals(
+ Object.keys(files).sort(),
+ ["deno:///bundle.js", "deno:///bundle.js.map"].sort(),
+ );
assert(files["deno:///bundle.js"].length);
},
});
@@ -226,7 +233,10 @@ Deno.test({
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
- assertEquals(Object.keys(files), ["deno:///bundle.js"]);
+ assertEquals(
+ Object.keys(files).sort(),
+ ["deno:///bundle.js.map", "deno:///bundle.js"].sort(),
+ );
assert(files["deno:///bundle.js"].includes(`const bar1 = "bar"`));
},
});
@@ -333,9 +343,10 @@ Deno.test({
});
assert(diagnostics);
assertEquals(diagnostics.length, 0);
- assertEquals(Object.keys(files).length, 1);
+ assertEquals(Object.keys(files).length, 2);
assert(files["deno:///bundle.js"].startsWith("(function() {\n"));
assert(files["deno:///bundle.js"].endsWith("})();\n"));
+ assert(files["deno:///bundle.js.map"]);
},
});
@@ -357,3 +368,41 @@ Deno.test({
);
},
});
+
+Deno.test({
+ name: `Deno.emit() - support source maps with bundle option`,
+ async fn() {
+ {
+ const { diagnostics, files } = await Deno.emit("/a.ts", {
+ bundle: "classic",
+ sources: {
+ "/a.ts": `import { b } from "./b.ts";
+ console.log(b);`,
+ "/b.ts": `export const b = "b";`,
+ },
+ compilerOptions: {
+ inlineSourceMap: true,
+ sourceMap: false,
+ },
+ });
+ assert(diagnostics);
+ assertEquals(diagnostics.length, 0);
+ assertEquals(Object.keys(files).length, 1);
+ assertStringIncludes(files["deno:///bundle.js"], "sourceMappingURL");
+ }
+
+ const { diagnostics, files } = await Deno.emit("/a.ts", {
+ bundle: "classic",
+ sources: {
+ "/a.ts": `import { b } from "./b.ts";
+ console.log(b);`,
+ "/b.ts": `export const b = "b";`,
+ },
+ });
+ assert(diagnostics);
+ assertEquals(diagnostics.length, 0);
+ assertEquals(Object.keys(files).length, 2);
+ assert(files["deno:///bundle.js"]);
+ assert(files["deno:///bundle.js.map"]);
+ },
+});