summaryrefslogtreecommitdiff
path: root/cli/util/text_encoding.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-06-05 11:04:16 -0400
committerGitHub <noreply@github.com>2024-06-05 17:04:16 +0200
commit7ed90a20d04982ae15a52ae2378cbffd4b6839df (patch)
tree3297d6f7227fbf1cf80e17a2a376ef4dfa52e6ad /cli/util/text_encoding.rs
parent0544d60012006b1c7799d8b6eafacec9567901ad (diff)
fix: better handling of npm resolution occurring on workers (#24094)
Closes https://github.com/denoland/deno/issues/24063
Diffstat (limited to 'cli/util/text_encoding.rs')
-rw-r--r--cli/util/text_encoding.rs167
1 files changed, 140 insertions, 27 deletions
diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs
index 25d827eb6..d2e0832c9 100644
--- a/cli/util/text_encoding.rs
+++ b/cli/util/text_encoding.rs
@@ -1,45 +1,133 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::ops::Range;
+
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
-use deno_core::ModuleCodeString;
+use deno_core::ModuleSourceCode;
static SOURCE_MAP_PREFIX: &[u8] =
b"//# sourceMappingURL=data:application/json;base64,";
-pub fn source_map_from_code(code: &ModuleCodeString) -> Option<Vec<u8>> {
- let bytes = code.as_bytes();
- let last_line = bytes.rsplit(|u| *u == b'\n').next()?;
- if last_line.starts_with(SOURCE_MAP_PREFIX) {
- let input = last_line.split_at(SOURCE_MAP_PREFIX.len()).1;
- let decoded_map = BASE64_STANDARD
- .decode(input)
- .expect("Unable to decode source map from emitted file.");
- Some(decoded_map)
- } else {
- None
- }
+pub fn source_map_from_code(code: &[u8]) -> Option<Vec<u8>> {
+ let range = find_source_map_range(code)?;
+ let source_map_range = &code[range];
+ let input = source_map_range.split_at(SOURCE_MAP_PREFIX.len()).1;
+ let decoded_map = BASE64_STANDARD.decode(input).ok()?;
+ Some(decoded_map)
}
/// Truncate the source code before the source map.
-pub fn code_without_source_map(mut code: ModuleCodeString) -> ModuleCodeString {
- let bytes = code.as_bytes();
- for i in (0..bytes.len()).rev() {
- if bytes[i] == b'\n' {
- if bytes[i + 1..].starts_with(SOURCE_MAP_PREFIX) {
- code.truncate(i + 1);
+pub fn code_without_source_map(code: ModuleSourceCode) -> ModuleSourceCode {
+ use deno_core::ModuleCodeBytes;
+
+ match code {
+ ModuleSourceCode::String(mut code) => {
+ if let Some(range) = find_source_map_range(code.as_bytes()) {
+ code.truncate(range.start);
}
- return code;
+ ModuleSourceCode::String(code)
}
+ ModuleSourceCode::Bytes(code) => {
+ if let Some(range) = find_source_map_range(code.as_bytes()) {
+ let source_map_index = range.start;
+ ModuleSourceCode::Bytes(match code {
+ ModuleCodeBytes::Static(bytes) => {
+ ModuleCodeBytes::Static(&bytes[..source_map_index])
+ }
+ ModuleCodeBytes::Boxed(bytes) => {
+ // todo(dsherret): should be possible without cloning
+ ModuleCodeBytes::Boxed(
+ bytes[..source_map_index].to_vec().into_boxed_slice(),
+ )
+ }
+ ModuleCodeBytes::Arc(bytes) => ModuleCodeBytes::Boxed(
+ bytes[..source_map_index].to_vec().into_boxed_slice(),
+ ),
+ })
+ } else {
+ ModuleSourceCode::Bytes(code)
+ }
+ }
+ }
+}
+
+fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
+ fn last_non_blank_line_range(code: &[u8]) -> Option<Range<usize>> {
+ let mut hit_non_whitespace = false;
+ let mut range_end = code.len();
+ for i in (0..code.len()).rev() {
+ match code[i] {
+ b' ' | b'\t' => {
+ if !hit_non_whitespace {
+ range_end = i;
+ }
+ }
+ b'\n' | b'\r' => {
+ if hit_non_whitespace {
+ return Some(i + 1..range_end);
+ }
+ range_end = i;
+ }
+ _ => {
+ hit_non_whitespace = true;
+ }
+ }
+ }
+ None
+ }
+
+ let range = last_non_blank_line_range(code)?;
+ if code[range.start..range.end].starts_with(SOURCE_MAP_PREFIX) {
+ Some(range)
+ } else {
+ None
}
- code
}
#[cfg(test)]
mod tests {
+ use std::sync::Arc;
+
+ use deno_core::ModuleCodeBytes;
+ use deno_core::ModuleCodeString;
+
use super::*;
#[test]
+ fn test_source_map_from_code() {
+ let to_string =
+ |bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
+ assert_eq!(
+ source_map_from_code(
+ b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=",
+ ).map(to_string),
+ Some("testingtesting".to_string())
+ );
+ assert_eq!(
+ source_map_from_code(
+ b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n",
+ ).map(to_string),
+ Some("testingtesting".to_string())
+ );
+ assert_eq!(
+ source_map_from_code(
+ b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n",
+ ),
+ None
+ );
+ assert_eq!(
+ source_map_from_code(
+ b"\"use strict\";
+
+throw new Error(\"Hello world!\");
+//# sourceMappingURL=data:application/json;base64,{",
+ ),
+ None
+ );
+ }
+
+ #[test]
fn test_source_without_source_map() {
run_test("", "");
run_test("\n", "\n");
@@ -62,14 +150,39 @@ mod tests {
"\n//# sourceMappingURL=data:application/json;base64,test",
"\n",
);
+ run_test(
+ "test\n//# sourceMappingURL=data:application/json;base64,test\n\n",
+ "test\n",
+ );
+ run_test(
+ "test\n//# sourceMappingURL=data:application/json;base64,test\n \n ",
+ "test\n",
+ );
fn run_test(input: &'static str, output: &'static str) {
- assert_eq!(
- code_without_source_map(ModuleCodeString::from_static(input))
- .as_str()
- .to_owned(),
- output
- );
+ let forms = [
+ ModuleSourceCode::String(ModuleCodeString::from_static(input)),
+ ModuleSourceCode::String({
+ let text: Arc<str> = input.into();
+ text.into()
+ }),
+ ModuleSourceCode::String({
+ let text: String = input.into();
+ text.into()
+ }),
+ ModuleSourceCode::Bytes(ModuleCodeBytes::Static(input.as_bytes())),
+ ModuleSourceCode::Bytes(ModuleCodeBytes::Boxed(
+ input.as_bytes().to_vec().into_boxed_slice(),
+ )),
+ ModuleSourceCode::Bytes(ModuleCodeBytes::Arc(
+ input.as_bytes().to_vec().into(),
+ )),
+ ];
+ for form in forms {
+ let result = code_without_source_map(form);
+ let bytes = result.as_bytes();
+ assert_eq!(bytes, output.as_bytes());
+ }
}
}
}