-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows line endings break source maps #6694
Comments
We recently had improvements to our sourcemap code. Running the example in the playground, and pasting that into the visualizer, I'm getting correct sourcemaps: |
Turns out, this is actually due to Windows line endings. Reproduction: const { transformSync } = require("@swc/core");
let output = transformSync(
`/**
* foo
* @param data foo
* @returns foo
*/
export const fixupRiskConfigData = (data: any): types.RiskConfigType => {
if (x) {
return 123;
} else {
return 456;
}
};`.replaceAll("\n", "\r\n"),
{
jsc: {
parser: {
syntax: "typescript",
},
target: "es2022",
},
sourceMaps: "inline",
inlineSourcesContent: true,
}
);
console.log(output.code); I got the reproduction from a Git repo, and copying that code into the issue of course normalized the line endings... |
(And related to that, someone also reported similar sourcemap problem in Parcel) swc Repo for that: const { transformSync } = require("@swc/core");
let output = transformSync(
` console.log("test1") // line 1 correct
let text = \`
1
1
1
1
1
1
\`
console.log("test2") // this should be line 12, instead 17 in devtools
let test0
let test1
console.log("test3") // this only woks because last line if you add more content it wont work`.replaceAll("\n", "\r\n"),
{
jsc: {
parser: {
syntax: "typescript",
},
target: "es2022",
},
sourceMaps: "inline",
inlineSourcesContent: true,
}
);
console.log(output.code); |
Ah, ok, seems we need to update:
|
It's still not working for me: Using this on fb6770f: use std::collections::HashSet;
use swc::{self};
use swc_atoms::JsWord;
use swc_common::{
chain,
comments::SingleThreadedComments,
errors::{DiagnosticBuilder, Emitter, Handler},
source_map::SourceMapGenConfig,
sync::Lrc,
FileName, Globals, Mark, SourceMap, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::{CatchClause, Decl, Id, Ident, Module, Pat, Stmt, VarDeclKind};
use swc_ecma_codegen::{text_writer::JsWriter, Config};
use swc_ecma_parser::{lexer::Lexer, EsConfig, PResult, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_preset_env::{preset_env, BrowserData, Mode, Targets, Version};
use swc_ecma_transforms::{
compat::reserved_words::reserved_words,
fixer,
fixer::paren_remover,
helpers, hygiene,
optimization::simplify::{dead_branch_remover, expr_simplifier},
react::{self, Runtime},
resolver,
};
use swc_ecma_visit::{Fold, FoldWith, Visit, VisitWith};
#[derive(Debug, Clone, Default)]
pub struct ErrorBuffer(std::sync::Arc<std::sync::Mutex<Vec<swc_common::errors::Diagnostic>>>);
type SourceMapBuffer = Vec<(swc_common::BytePos, swc_common::LineCol)>;
impl Emitter for ErrorBuffer {
fn emit(&mut self, db: &DiagnosticBuilder) {
self.0.lock().unwrap().push((**db).clone());
}
}
fn main() {
let cm = Lrc::<SourceMap>::default();
// let src = r#"
// let a = function l1(){};
// let {x: {b}, x: [c]} = 0;
// let [d, [e], {f}] = 0;
// function g(){
// var l1;
// let l2;
// const l3;
// }
// class A {
// l1;
// func(){
// var l1;
// let l2;
// const l3;
// }
// }
// try {
// throw new Error();
// } catch (l1) {
// var l1 = 2;
// var h = 1;
// }
// "#;
let src = r#"/**
* foo
* @param data foo
* @returns foo
*/
export const fixupRiskConfigData = (data: any): types.RiskConfigType => {
if (x) {
return 123;
} else {
return 456;
}
};
"#
.replace("\n", "\r\n");
// let src = r#"
// let funcs = [];
// for (let i = 0; i < 2; i++) {
// for (let j = 0; j < 2; j++) {
// funcs.push(() => console.log(i, j));
// }
// }
// funcs.forEach((f) => f());
// "#;
let (module, comments) = parse(&src, "test.js", &cm).unwrap();
let error_buffer = ErrorBuffer::default();
let handler = Handler::with_emitter(true, false, Box::new(error_buffer.clone()));
swc_common::errors::HANDLER.set(&handler, || {
swc_common::GLOBALS.set(&Globals::new(), || {
helpers::HELPERS.set(&helpers::Helpers::new(true), || {
let unresolved_mark = Mark::fresh(Mark::root());
let top_level_mark = Mark::fresh(Mark::root());
let module =
module.fold_with(&mut resolver(unresolved_mark, top_level_mark, false));
// let mut global_vars = FindGlobalVariables {
// variables: HashSet::new(),
// var_kind: VarDeclKind::Var,
// in_block: false,
// catch_binding: None,
// };
// module.visit_with(&mut global_vars);
// println!("{:?}", global_vars.variables);
// let module = module.fold_with(&mut react::react(
// cm.clone(),
// Some(&comments),
// react::Options {
// runtime: Some(Runtime::Automatic),
// development: Some(false),
// refresh: Some(Default::default()),
// ..Default::default()
// },
// top_level_mark,
// ));
// let code = emit(&module, &comments, cm.clone());
// println!("#{}", code);
// let module = module.fold_with(&mut dead_branch_remover(unresolved_mark));
// let code = emit(&module, &comments, cm.clone());
// println!("#{}", code);
// let module = module.fold_with(&mut chain!(
// preset_env(
// top_level_mark,
// Some(&comments),
// swc_ecma_preset_env::Config {
// targets: Some(Targets::Versions(BrowserData {
// chrome: Some(Version {
// major: 70,
// minor: 0,
// patch: 0,
// }),
// ..Default::default()
// })),
// bugfixes: true,
// shipped_proposals: true,
// mode: Some(Mode::Entry),
// ..Default::default()
// },
// Default::default(),
// &mut Default::default()
// ),
// helpers::inject_helpers(),
// ));
// let module = module.fold_with(&mut chain!(
// hygiene(),
// resolver(unresolved_mark, top_level_mark, false)
// ));
// let module = module.fold_with(&mut chain!(
// paren_remover(Some(&comments)),
// // expr_simplifier(unresolved_mark, Default::default()),
// dead_branch_remover(unresolved_mark)
// ));
let module = module.fold_with(&mut dead_branch_remover(unresolved_mark));
// let module = module.fold_with(&mut ShowSyntaxContext {
// top_level_mark,
// unresolved_mark,
// });
let module = module.fold_with(&mut chain!(
reserved_words(),
hygiene(),
fixer(Some(&comments))
));
// for diagnostic in error_buffer.0.lock().unwrap().clone() {
// let message = diagnostic.message();
// let span = diagnostic.span.clone();
// println!("-- Diagnostic --\nMessage:{}", message);
// let span_labels = span.span_labels();
// if !span_labels.is_empty() {
// for span_label in span_labels {
// let start =
// cm.lookup_char_pos(span_label.span.lo);
// let end = cm.lookup_char_pos(span_label.span.hi);
// println!("{:?} - {:?} :: {:?}", start, end,
// span_label.label); }
// };
// }
let mut map_buf = vec![];
let (code, src_map_buf) = emit(cm.clone(), comments, &module).unwrap();
struct SourceMapConfigImpl;
impl SourceMapGenConfig for SourceMapConfigImpl {
fn file_name_to_source(&self, f: &swc_common::FileName) -> String {
f.to_string()
}
fn inline_sources_content(&self, _: &swc_common::FileName) -> bool {
true
}
}
cm.build_source_map_with_config(&src_map_buf, None, SourceMapConfigImpl)
.to_writer(&mut map_buf)
.unwrap();
// if cm
// .build_source_map(&src_map_buf)
// .to_writer(&mut map_buf)
// .is_ok()
// {
// println!("{}", String::from_utf8(map_buf).unwrap());
// }
// println!("{}", code);
println!(
"{}",
visualizer_url(&code, &String::from_utf8(map_buf).unwrap())
);
});
});
});
}
fn visualizer_url(code: &str, map: &str) -> String {
println!("{}", code);
println!("{}", map);
let code_len = format!("{}\0", code.len());
let map_len = format!("{}\0", map.len());
let hash = base64::encode(format!("{}{}{}{}", code_len, code, map_len, map));
format!("https://evanw.github.io/source-map-visualization/#{}", hash)
}
struct ShowSyntaxContext {
top_level_mark: Mark,
unresolved_mark: Mark,
}
impl Fold for ShowSyntaxContext {
fn fold_ident(&mut self, node: Ident) -> Ident {
let new_name: JsWord = format!(
"{}_{}{}{}",
node.sym,
node.span.ctxt().as_u32(),
if node.span.has_mark(self.top_level_mark) {
"_top"
} else {
""
},
if node.span.has_mark(self.unresolved_mark) {
"_unres"
} else {
""
},
)
.into();
Ident::new(new_name, node.span)
}
}
// struct FindGlobalVariables {
// variables: HashSet<Id>,
// var_kind: VarDeclKind,
// in_block: bool,
// catch_binding: Option<Id>,
// }
// impl Visit for FindGlobalVariables {
// fn visit_var_decl(&mut self, n: &swc_ecma_ast::VarDecl) {
// self.var_kind = n.kind;
// n.visit_children_with(self)
// }
// fn visit_binding_ident(&mut self, n: &swc_ecma_ast::BindingIdent) {
// if self.var_kind == VarDeclKind::Var || !self.in_block {
// self.variables.insert(n.id.clone().into());
// }
// }
// // fn visit_array_pat(&mut self, n: &swc_ecma_ast::ArrayPat) {
// // for e in &n.elems {
// // if let Some(Pat::Ident(id)) = e.as_ref() {}
// // }
// // }
// fn visit_assign_pat_prop(&mut self, n: &swc_ecma_ast::AssignPatProp) {
// if n.value.is_none() {
// self.variables.insert(n.key.clone().into());
// } else {
// n.value.visit_with(self)
// }
// // for e in &n.value {
// // if let Some(Pat::Ident(id)) = e.as_ref() {}
// // }
// }
// fn visit_fn_decl(&mut self, n: &swc_ecma_ast::FnDecl) {
// self.variables.insert(n.ident.clone().into());
// }
// fn visit_class_decl(&mut self, n: &swc_ecma_ast::ClassDecl) {
// self.variables.insert(n.ident.clone().into());
// }
// fn visit_stmt(&mut self, stmt: &swc_ecma_ast::Stmt) {
// match stmt {
// Stmt::Decl(decl) => decl.visit_with(self),
// Stmt::If(n) => {
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::For(n) => {
// n.init.visit_with(self);
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::ForIn(n) => {
// n.left.visit_with(self);
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::ForOf(n) => {
// n.left.visit_with(self);
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::While(n) => {
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::DoWhile(n) => {
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// Stmt::Switch(n) => {
// n.visit_children_with(self);
// }
// Stmt::Try(n) => {
// n.block.visit_with(self);
// if let Some(CatchClause { body, param, .. }) = n.handler {
// body.visit_with(self);
// }
// n.finalizer.visit_with(self);
// }
// Stmt::Labeled(n) => {
// n.visit_children_with(self);
// }
// Stmt::Block(n) => {
// self.in_block = true;
// n.visit_children_with(self);
// self.in_block = false;
// }
// _ => {}
// }
// }
// }
fn parse(
code: &str,
filename: &str,
cm: &Lrc<SourceMap>,
) -> PResult<(Module, SingleThreadedComments)> {
let source_file = cm.new_source_file(FileName::Real(filename.into()), code.into());
let comments = SingleThreadedComments::default();
let lexer = Lexer::new(
// Syntax::Es(EsConfig {
// jsx: true,
// ..Default::default()
// }),
Syntax::Typescript(TsConfig {
tsx: true,
..Default::default()
}),
Default::default(),
StringInput::from(&*source_file),
Some(&comments),
);
let mut parser = Parser::new_from(lexer);
match parser.parse_module() {
Err(err) => Err(err),
Ok(module) => Ok((module, comments)),
}
}
fn emit(
source_map: Lrc<SourceMap>,
comments: SingleThreadedComments,
module: &Module,
) -> Result<(String, SourceMapBuffer), std::io::Error> {
let mut src_map_buf = vec![];
let mut buf = vec![];
{
let writer = Box::new(JsWriter::new(
source_map.clone(),
"\n",
&mut buf,
Some(&mut src_map_buf),
));
let config = Config {
minify: false,
ascii_only: false,
omit_last_semi: false,
..Default::default()
};
let mut emitter = swc_ecma_codegen::Emitter {
cfg: config,
comments: Some(&comments),
cm: source_map,
wr: writer,
};
emitter.emit_module(module)?;
}
Ok((String::from_utf8(buf).unwrap(), src_map_buf))
} |
**Description:** There were several issues with the way we updated the current `LineCol` position during the printing of the generated file: - We used `chars` and `char_indices` (UTF-32) instead of `encode_utf16` (UTF-16) chars. - JS uses UCS-2 (basically UTF-16) for its strings, and source maps default to that implicitly. - `\r` was incorrectly handled - it didn't add a `line_start`; only `\n` did - `\r\n` was incorrectly handled - It was trying to let the `\n` path handle the `line_start`, but it called `chars.next()` which ate the `\n` char. I also took the opportunity to avoid the `Vec` allocations and reduced some code duplication. See the [before](https://evanw.github.io/source-map-visualization/#ODEzAC8qKgogKiBmb28KICogQHBhcmFtIGRhdGEgZm9vCiAqIEByZXR1cm5zIGZvbwogKi8gZXhwb3J0IGNvbnN0IGZpeHVwUmlza0NvbmZpZ0RhdGEgPSAoZGF0YSk9PnsKICAgIGlmICh4KSB7CiAgICAgICAgcmV0dXJuIDEyMzsKICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIDQ1NjsKICAgIH0KfTsKCi8vIyBzb3VyY2VNYXBwaW5nVVJMPWRhdGE6YXBwbGljYXRpb24vanNvbjtiYXNlNjQsZXlKMlpYSnphVzl1SWpvekxDSnpiM1Z5WTJWeklqcGJJbWx1Y0hWMExuUnpJbDBzSW5OdmRYSmpaWE5EYjI1MFpXNTBJanBiSWk4cUtseHlYRzRnS2lCbWIyOWNjbHh1SUNvZ1FIQmhjbUZ0SUdSaGRHRWdabTl2WEhKY2JpQXFJRUJ5WlhSMWNtNXpJR1p2YjF4eVhHNGdLaTljY2x4dVpYaHdiM0owSUdOdmJuTjBJR1pwZUhWd1VtbHphME52Ym1acFowUmhkR0VnUFNBb1pHRjBZVG9nWVc1NUtUb2dkSGx3WlhNdVVtbHphME52Ym1acFoxUjVjR1VnUFQ0Z2UxeHlYRzRnSUdsbUlDaDRLU0I3WEhKY2JpQWdJQ0J5WlhSMWNtNGdNVEl6TzF4eVhHNGdJSDBnWld4elpTQjdYSEpjYmlBZ0lDQnlaWFIxY200Z05EVTJPMXh5WEc0Z0lIMWNjbHh1ZlRzaVhTd2libUZ0WlhNaU9sc2labWw0ZFhCU2FYTnJRMjl1Wm1sblJHRjBZU0lzSW1SaGRHRWlMQ0o0SWwwc0ltMWhjSEJwYm1keklqb2lRVUZCUVN4dFJFRkpReXhIUVVORUxFOUJRVThzVFVGQlRVRXNjMEpCUVhOQ0xFTkJRVU5ETEU5QlFXOURPMGxCUTNSRkxFbEJRVWxETEVkQlFVYzdVVUZEVEN4UFFVRlBPMGxCUTFRc1QwRkJUenRSUVVOTUxFOUJRVTg3U1VGRFZDeERRVUZETzBGQlEwZ3NSVUZCUlNKOTQ0NAB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiaW5wdXQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIGZvb1xyXG4gKiBAcGFyYW0gZGF0YSBmb29cclxuICogQHJldHVybnMgZm9vXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZml4dXBSaXNrQ29uZmlnRGF0YSA9IChkYXRhOiBhbnkpOiB0eXBlcy5SaXNrQ29uZmlnVHlwZSA9PiB7XHJcbiAgaWYgKHgpIHtcclxuICAgIHJldHVybiAxMjM7XHJcbiAgfSBlbHNlIHtcclxuICAgIHJldHVybiA0NTY7XHJcbiAgfVxyXG59OyJdLCJuYW1lcyI6WyJmaXh1cFJpc2tDb25maWdEYXRhIiwiZGF0YSIsIngiXSwibWFwcGluZ3MiOiJBQUFBLG1EQUlDLEdBQ0QsT0FBTyxNQUFNQSxzQkFBc0IsQ0FBQ0MsT0FBb0M7SUFDdEUsSUFBSUMsR0FBRztRQUNMLE9BQU87SUFDVCxPQUFPO1FBQ0wsT0FBTztJQUNULENBQUM7QUFDSCxFQUFFIn0=) and [after](https://evanw.github.io/source-map-visualization/#ODIyAC8qKgogKiBmb28KICogQHBhcmFtIGRhdGEgZm9vCiAqIEByZXR1cm5zIGZvbwogKi8gZXhwb3J0IHZhciBmaXh1cFJpc2tDb25maWdEYXRhID0gZnVuY3Rpb24oZGF0YSkgewogICAgaWYgKHgpIHsKICAgICAgICByZXR1cm4gMTIzOwogICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4gNDU2OwogICAgfQp9OwoKLy8jIHNvdXJjZU1hcHBpbmdVUkw9ZGF0YTphcHBsaWNhdGlvbi9qc29uO2Jhc2U2NCxleUoyWlhKemFXOXVJam96TENKemIzVnlZMlZ6SWpwYklpNHZZWEJ3TG1weklsMHNJbk52ZFhKalpYTkRiMjUwWlc1MElqcGJJaThxS2x4eVhHNGdLaUJtYjI5Y2NseHVJQ29nUUhCaGNtRnRJR1JoZEdFZ1ptOXZYSEpjYmlBcUlFQnlaWFIxY201eklHWnZiMXh5WEc0Z0tpOWNjbHh1Wlhod2IzSjBJR052Ym5OMElHWnBlSFZ3VW1semEwTnZibVpwWjBSaGRHRWdQU0FvWkdGMFlUb2dZVzU1S1RvZ2RIbHdaWE11VW1semEwTnZibVpwWjFSNWNHVWdQVDRnZTF4eVhHNGdJR2xtSUNoNEtTQjdYSEpjYmlBZ0lDQnlaWFIxY200Z01USXpPMXh5WEc0Z0lIMGdaV3h6WlNCN1hISmNiaUFnSUNCeVpYUjFjbTRnTkRVMk8xeHlYRzRnSUgxY2NseHVmVHNpWFN3aWJtRnRaWE1pT2xzaVptbDRkWEJTYVhOclEyOXVabWxuUkdGMFlTSXNJbVJoZEdFaUxDSjRJbDBzSW0xaGNIQnBibWR6SWpvaVFVRkJRVHM3T3p0RFFVbERMRWRCUTBRc1QwRkJUeXhKUVVGTlFTeHpRa0ZCYzBJc1UwRkJRME1zVFVGQmIwTTdTVUZEZEVVc1NVRkJTVU1zUjBGQlJ6dFJRVU5NTEU5QlFVODdTVUZEVkN4UFFVRlBPMUZCUTB3c1QwRkJUenRKUVVOVUxFTkJRVU03UVVGRFNDeEZRVUZGSW4wPTQ0NgB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiLi9hcHAuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIGZvb1xyXG4gKiBAcGFyYW0gZGF0YSBmb29cclxuICogQHJldHVybnMgZm9vXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZml4dXBSaXNrQ29uZmlnRGF0YSA9IChkYXRhOiBhbnkpOiB0eXBlcy5SaXNrQ29uZmlnVHlwZSA9PiB7XHJcbiAgaWYgKHgpIHtcclxuICAgIHJldHVybiAxMjM7XHJcbiAgfSBlbHNlIHtcclxuICAgIHJldHVybiA0NTY7XHJcbiAgfVxyXG59OyJdLCJuYW1lcyI6WyJmaXh1cFJpc2tDb25maWdEYXRhIiwiZGF0YSIsIngiXSwibWFwcGluZ3MiOiJBQUFBOzs7O0NBSUMsR0FDRCxPQUFPLElBQU1BLHNCQUFzQixTQUFDQyxNQUFvQztJQUN0RSxJQUFJQyxHQUFHO1FBQ0wsT0FBTztJQUNULE9BQU87UUFDTCxPQUFPO0lBQ1QsQ0FBQztBQUNILEVBQUUifQ==) **Related issue:** - Closes #6694.
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Describe the bug
A block comment before a function produces invalid sourcemaps
Input code
(See #6694 (comment), this is due to Windows line endings)
Config
Expected behavior
The sourcemap should be correct
Actual behavior
No response
Version
1.3.23
Additional context
Found in parcel-bundler/parcel#8691
The text was updated successfully, but these errors were encountered: