Skip to content

Commit a4564c4

Browse files
committed
Extract source map URL from directive comments
Change the parser to extract the source map URL from directive comments. The relevant spec is here: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.lmz475t4mvbx This is part of similar work being done in M-C in https://bugzilla.mozilla.org/show_bug.cgi?id=1388855
1 parent a21f97d commit a4564c4

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

src/parser.rs

+5
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ impl<'i: 't, 't> Parser<'i, 't> {
268268
self.input.tokenizer.current_source_location()
269269
}
270270

271+
/// The source map URL.
272+
pub fn current_source_map_url(&self) -> Option<&str> {
273+
self.input.tokenizer.current_source_map_url()
274+
}
275+
271276
/// Return the current internal state of the parser (including position within the input).
272277
///
273278
/// This state can later be restored with the `Parser::reset` method.

src/size_of_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ size_of_test!(token, Token, 32);
3636
size_of_test!(std_cow_str, Cow<'static, str>, 32);
3737
size_of_test!(cow_rc_str, CowRcStr, 16);
3838

39-
size_of_test!(tokenizer, ::tokenizer::Tokenizer, 40);
40-
size_of_test!(parser_input, ::parser::ParserInput, 112);
39+
size_of_test!(tokenizer, ::tokenizer::Tokenizer, 56);
40+
size_of_test!(parser_input, ::parser::ParserInput, 128);
4141
size_of_test!(parser, ::parser::Parser, 16);
4242
size_of_test!(source_position, ::SourcePosition, 8);
4343
size_of_test!(parser_state, ::ParserState, 24);

src/tests.rs

+25
Original file line numberDiff line numberDiff line change
@@ -979,3 +979,28 @@ fn parse_entirely_reports_first_error() {
979979
let result: Result<(), _> = parser.parse_entirely(|_| Err(ParseError::Custom(E::Foo)));
980980
assert_eq!(result, Err(ParseError::Custom(E::Foo)));
981981
}
982+
983+
#[test]
984+
fn parse_comments() {
985+
let tests = vec![
986+
("/*# sourceMappingURL=here*/", Some("here")),
987+
("/*# sourceMappingURL=here */", Some("here")),
988+
("/*@ sourceMappingURL=here*/", Some("here")),
989+
("/*@ sourceMappingURL=there*/ /*# sourceMappingURL=here*/", Some("here")),
990+
("/*# sourceMappingURL=here there */", Some("here")),
991+
("/*# sourceMappingURL= here */", Some("")),
992+
("/*# sourceMappingURL=*/", Some("")),
993+
("/*# sourceMappingUR=here */", None),
994+
("/*! sourceMappingURL=here */", None),
995+
("/*# sourceMappingURL = here */", None),
996+
("/* # sourceMappingURL=here */", None)
997+
];
998+
999+
for test in tests {
1000+
let mut input = ParserInput::new(test.0);
1001+
let mut parser = Parser::new(&mut input);
1002+
while let Ok(_) = parser.next_including_whitespace() {
1003+
}
1004+
assert_eq!(parser.current_source_map_url(), test.1);
1005+
}
1006+
}

src/tokenizer.rs

+54
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub struct Tokenizer<'a> {
209209
current_line_number: u32,
210210
var_functions: SeenStatus,
211211
viewport_percentages: SeenStatus,
212+
source_map_url: Option<&'a str>,
212213
}
213214

214215
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -234,6 +235,7 @@ impl<'a> Tokenizer<'a> {
234235
current_line_number: first_line_number,
235236
var_functions: SeenStatus::DontCare,
236237
viewport_percentages: SeenStatus::DontCare,
238+
source_map_url: None,
237239
}
238240
}
239241

@@ -300,6 +302,11 @@ impl<'a> Tokenizer<'a> {
300302
}
301303
}
302304

305+
#[inline]
306+
pub fn current_source_map_url(&self) -> Option<&'a str> {
307+
self.source_map_url
308+
}
309+
303310
#[inline]
304311
pub fn state(&self) -> ParserState {
305312
ParserState {
@@ -597,6 +604,53 @@ fn consume_whitespace<'a>(tokenizer: &mut Tokenizer<'a>, newline: bool, is_cr: b
597604
fn consume_comment<'a>(tokenizer: &mut Tokenizer<'a>) -> &'a str {
598605
tokenizer.advance(2); // consume "/*"
599606
let start_position = tokenizer.position();
607+
608+
// If there is a source map directive, extract the URL.
609+
if tokenizer.starts_with(b"#") || tokenizer.starts_with(b"@") {
610+
tokenizer.advance(1);
611+
let directive_text = b" sourceMappingURL=";
612+
if tokenizer.starts_with(directive_text) {
613+
tokenizer.advance(directive_text.len());
614+
615+
// Scan for the next whitespace.
616+
let url_start_position = tokenizer.position();
617+
let mut url_end_position = tokenizer.position();
618+
while !tokenizer.is_eof() {
619+
match_byte! { tokenizer.next_byte_unchecked(),
620+
b' ' | b'\t' => {
621+
url_end_position = tokenizer.position();
622+
tokenizer.advance(1);
623+
break
624+
}
625+
b'\n' | b'\x0C' => {
626+
url_end_position = tokenizer.position();
627+
tokenizer.advance(1);
628+
tokenizer.seen_newline(false);
629+
break
630+
}
631+
b'\r' => {
632+
url_end_position = tokenizer.position();
633+
tokenizer.advance(1);
634+
tokenizer.seen_newline(true);
635+
break
636+
}
637+
b'*' => {
638+
if tokenizer.starts_with(b"*/") {
639+
// End of comment, so let the later loop finish up.
640+
url_end_position = tokenizer.position();
641+
break
642+
}
643+
tokenizer.advance(1);
644+
}
645+
_ => {
646+
tokenizer.advance(1);
647+
}
648+
}
649+
}
650+
tokenizer.source_map_url = Some(tokenizer.slice(url_start_position..url_end_position))
651+
}
652+
}
653+
600654
while !tokenizer.is_eof() {
601655
match_byte! { tokenizer.next_byte_unchecked(),
602656
b'*' => {

0 commit comments

Comments
 (0)