Skip to content
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

Segment-less sourcemap generated for static templates #6092

Closed
benmccann opened this issue Mar 17, 2021 · 5 comments · Fixed by #6427
Closed

Segment-less sourcemap generated for static templates #6092

benmccann opened this issue Mar 17, 2021 · 5 comments · Fixed by #6427
Labels

Comments

@benmccann
Copy link
Member

This originally broke source maps with SvelteKit. A workaround was added to Vite, but we still don't get source maps for static files.

Svelte generates a segment-less sourcemap when there's no dynamic content in the template. Appears it's coming from the use of code-red to print out the AST tree: https://github.com/Rich-Harris/code-red/blob/3b32d2ef5bd954cb85a0d005f3a328bae57c6a97/src/print/index.ts#L60-L86

Normally, the AST being printed would contain loc objects that point into the original code. But when Svelte is processing a file without any dynamic code, it doesn't attach any locs (though it does add start and end). I think this is a bad behavior, Svelte should be associating source location for static code as well. Eg, something like the following:

<h1>decoded-sourcemap</h1>
<div>replace me</div>

The h1 should be associated with { start: { line: 1, column: 2 } }, the decoded-sourcemap with { start: { line: 1, column: 5 } }, etc. With the AST given the correct loc associations, code-red's printer will make a sourcemap with valid segments, and the bug will disappear.

CC @milahu @dmitrage as our source map experts

@milahu
Copy link
Contributor

milahu commented Mar 17, 2021

how NOT to reproduce
<div>
  <button onclick="console.log('this is App.svelte line 2')">click me</button>
</div>

actual result

this is App.svelte line 2                 (index):19

probably this should say App.svelte:2

where does chunk come from?
// in code-red print(): console.dir(chunk);
{ content: 'main', loc: undefined, has_newline: false }
{ content: '.', loc: undefined, has_newline: false }
{ content: 'innerHTML', loc: undefined, has_newline: false }
{ content: ' = ', loc: undefined, has_newline: false }
{ content: '`', loc: undefined, has_newline: false }
{
  content: '<button onclick="console.log(&#39;this is App.svelte line 2&#39;)">click me</button>',
  loc: undefined,
  has_newline: false
}
// console.trace(chunk):
Trace: {
  content: '<button onclick="console.log(&#39;this is App.svelte line 2&#39;)">click me</button>',
  loc: undefined,
  has_newline: false
}
    at print (compiler.js:1978:10)
    at Component.generate (compiler.js:20088:9)
    at compile (compiler.js:21405:19)
    at Object.transform (rollup-plugin-svelte/index.js:111:21)

Component.generate

generate(result) {

  const program = { type: 'Program', body: result.js };

  js = print(program, {
    sourceMapSource: compile_options.filename
  });

compile

function compile(source, options = {}) {
  const ast = parse$2(source, options);
  const component = new Component(
    ast,
    source,
    options.name || get_name_from_filename(options.filename) || 'Component',
    options,
    stats,
    warnings
  );
  const result = dom(component, options);
  //const result = ssr(component, options);
  return component.generate(result);
}
where is the source location lost?

relevant code in src/compiler/compile/render_dom/wrappers/Element/index.ts line 303

to_html((this.fragment.nodes as unknown as Array<ElementWrapper | TextWrapper>), block, literal, state, can_use_raw_text);

this.fragment.nodes has source locations of html elements

this.fragment.nodes[0] ==
ElementWrapper {
  node: Element$1 {
    start: 8,
    end: 84,
    type: 'Element',
    attributes: [Array],
    name: 'button',

after to_html, the locations are lost:

state.quasi ==
{
  type: 'TemplateElement',
  value: {
    raw: '<button onclick="console.log(&#39;this is App.svelte line 2&#39;)">click me</button>'
  }
}

src/compiler/compile/render_dom/wrappers/Element/index.ts line 306

block.chunks.create.push(
  b`${node}.${this.can_use_innerhtml ? 'innerHTML' : 'textContent'} = ${literal};`
);
what is b`....` ?

what is ( b`....` )? code-red

function b(strings, ...values) {
  const str = join$1(strings);
  const comments = [];
  const ast = acorn.parse(str,  acorn_opts(comments, str));
  inject(str, ast, values, comments);
  return ast.body;
}
ast.body[0].expression.right ==
{
  type: 'TemplateLiteral',
  expressions: [],
  quasis: [
    {
      type: 'TemplateElement',
      value: {
        raw: '<button onclick="console.log(&#39;this is App.svelte line 2&#39;)">click me</button>'
      }
    }
  ]
}

to trace markup, we would need major changes in render_dom and render_ssr

@dummdidumm
Copy link
Member

I think the empty source map makes sense in a way because there is no JS transformation done on a html-only code, so Svelte does not add any mappings. In order to support this case, the compiler needs to add some mappings to the html-part, don't know how tricky that would be.

@jridgewell
Copy link

jridgewell commented Mar 17, 2021

I think the empty source map makes sense in a way because there is no JS transformation done on a html-only code, so Svelte does not add any mappings.

Note that even when I fix ampproject/remapping#116, your sourcemaps are going to look broken. The remapped output would look like:

{
  "version": 3,
  "mappings": "AAAA,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;;;;;;;",
  "names": [],
  "sources": [ "App.svelte" ]
}

Notice the ;;;;;;;, and only segments on the first line. Well, the first line of svelte output is /* App.svelte generated by Svelte v3.35.0 */. If you were to load this into a sourcemap visualizer, it'd look like a comment was created from HTML elements (the <h1> and <div>). It won't make any sense.

In order to support this case, the compiler needs to add some mappings to the html-part, don't know how tricky that would be.

Not too tricky. You're already tracking start and end indexes as @milahu details in the "where is the source location lost?" section. You just need to convert that to { line: number, column: number } by figuring out how many newline characters came before the start/end indexes (and how many columns into that line you are). A datastructure to track the last index lookup and incrementally process would make it decently fast.

@tanhauhau tanhauhau mentioned this issue Jun 22, 2021
4 tasks
@tanhauhau
Copy link
Member

Well, yes we already have the start and end position, it is not hard to get the loc(action), which is the line + column of the start and end position

the tricky part of the figuring out sourcemap for the markup is that 1 line in .svelte

<h1></h1>

is broken down to multiple stages / pieces of code in .js

  • declaring the variable let h1
  • creating the element h1 = element("h1")
  • inserting the element into the DOM append(target, h1)
  • updating the element
  • removing the element from the DOM detach(h1)

so if there's a breakpoint on <h1></h1> where should it break at?


trying out all the options, breaking at the append(...) call expression make the most sense to me right now:

breakpoint.mov

it is most visual, as, stepping next, will see the element inserted into the DOM immediately.

@Conduitry
Copy link
Member

The fix in #6427 has been released in 3.39.0.

dummdidumm added a commit that referenced this issue Feb 15, 2024
Add source map merging for preprocessors and get tests passing.

- fixed some issues around the `sources` array where they weren't calculated relative to the input correctly
- adjusted some CSS tests because due to our own CSS parser the AST is less granular, so there are less mappings now. Don't think this is a problem, but worth thinking about
- removed enableSourcemap but only log a warning, the reason this was introduced was to mitigate a bug in Vite which occured when having the source map inlined into the SSR'd output. Since SSR doesn't contain inlined CSS anymore (and if it did, we would omit the source map) the reason for which it was introduced no longer exists
- files without js mapping in it have no source mappings yet (originally added in Svelte 4 for #6092)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants