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

Initial source-map support #675

Merged
merged 12 commits into from
Mar 21, 2015
Merged

Initial source-map support #675

merged 12 commits into from
Mar 21, 2015

Conversation

Diggsey
Copy link
Contributor

@Diggsey Diggsey commented Feb 23, 2015

This is still quite rough around the edges and could do with a lot more testing, but it works.

Pass "-m/--map" to lsc when compiling to generate source-maps.

See #452

@vendethiel
Copy link
Contributor

Wow, very impressive work, nicely done :-).

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 23, 2015

Currently there's no way to specify the output filename when compiling and running livescript on-the-fly, so the sourcemap you get back won't work properly. For now it will only work when compiling livescript to .js files ahead of time. However, the sourcemaps for those .js files should work just fine in the browser if they are being served as static assets.

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 23, 2015

Having looked into it, it's seems that a trival change to the "liveify" plugin for browserify would enable this to work with browserify (as per coffeeify):
https://github.com/jnordberg/coffeeify/blob/master/index.js#L64

For the browser, coffeescript seems to use data-uris to specify the sourcemap, but I haven't figured out how it specifies the original source yet (or if it even does).

@blvz
Copy link
Contributor

blvz commented Feb 23, 2015

I've also quickly tried to make it work with browserify, with no luck. Then, I've tried to see if browserify could also bundle all generated maps, again, with no luck. I'll have to try again with a little more time.

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 23, 2015

Source maps should work in the browser now with no extra work:

<html>
<head>
</head>
<body>
<script type="text/javascript" src="browser/livescript.js"></script>
<script type="text/ls">
    debugger
    a = [1,2,3]
    b = [x*2 for x in a]
    if b[1] > 2
        console.log "Hello, world!"
</script>
<script type="text/javascript">
    require("LiveScript").go();
</script>
</body>
</html>

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 24, 2015

@blvz I've improved the command line options, so you can now pass "--map=embedded" which will embed both the source map and the original livescript inside the generated javascript file. This is the only form which browserify will accept AFAIK.

If you use that option, and also pass "--debug" to browserify, it will attempt to generate a combined source-map for the entire set of inputs. However, in my tests it's been generating invalid source-maps as output, and I have yet to figure out why.

…andles newlines correctly

- Add debug output mode for source-maps
@blvz
Copy link
Contributor

blvz commented Feb 25, 2015

Very nice. Thanks for your effort. I've test it against a front-end app that uses mithril with some views deeply nested. Indeed, sourcemaps generated with browserify are slightly incorrect.

On Chrome Dev Tools, setting breakpoints in some-file.ls doesn't work, but setting them in bundle.js do. Of course, the breakpoint now points to the slightly wrong place in some-file.ls.

Enabling "pause on exceptions" made Dev Tools crash, but disabling it made the exception get printed in the console with correct file name and line number. Clicking on those, I could navigate to it and set a breakpoint, pausing the exception accordingly.

In the sample below, the correct file name and line number are shown in console, but navigating to it and setting a breakpoint fails.

...
  onclick: ->
    return if console.log \something # setting a breakpoint on this line is useless
...

@gkz
Copy link
Owner

gkz commented Feb 25, 2015

Great work!
As soon as the kinks are worked out I am ready to merge this in!

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 25, 2015

At least part of the problem is due to the very old version of jison being used - I've been trying to update to a newer version, but that's proving difficult, as the jison project doesn't appear to maintain any release notes or even decent documentation, and LiveScript does some unusual things such as replacing the lexer and applying some regexes to the generated code, so would appreciate some help with that side of things (even just an explanation of what it's doing!).

The old version of jison causes two problems:

  1. Multi-token grammar rules will pick up the line/column information for the last token in the rule, rather than the first
  2. Column information is not tracked at all, so I had to hack that in by abusing one of the other fields

Additionally, my changes to the custom lexer in LiveScript to get it to track column information are still imperfect, although even the original line tracking was not perfect either. The problem is that they're tracked independently from the input source, and so it's very easy for them to get out of sync. Ideally it would be rewritten so that location information and input source are kept in lock-step at all times.

Once the information produced by the lexer is accurate and jison is updated, then the only possible source of errors will be within the source-node tree constructed by the compiler, and that can be improved incrementally over time, on a case-by-case basis, as it will for the most part be subjective, and is easy to change.

@vendethiel
Copy link
Contributor

I think coffee updated the json version. That might prove interesting to check out.

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 26, 2015

Turned out that half the problem was that I'd updated to a buggy version of jison. With that solved, the update was successful, and the source-maps are MUCH better.

I've been using http://sokra.github.io/source-map-visualization/#custom to visualise the source maps if anyone's interested.

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 27, 2015

Sourcemap generation is pretty much perfect now (within the limits of v3 sourcemaps) at least for the common AST nodes.

@gkz
Copy link
Owner

gkz commented Feb 27, 2015

Awesome, great work Diggsey!

@raine
Copy link
Contributor

raine commented Feb 27, 2015

Can this be used to get stack traces with .ls source directly instead of having to compile and then check
the line numbers?

@@ -292,9 +304,10 @@ exports import
then @doHeredoc code, index, q
else @strnum q+q; 2
if q is \"
debugger
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray debugger here :)

@Diggsey
Copy link
Contributor Author

Diggsey commented Feb 27, 2015

@vendethiel Fixed!
@raine Yes. If you are running in a browser this should work off the bat, assuming you have sourcemaps enabled in the browser config. For node, you'll have to install the source-map-support npm package. In both cases you must tell lsc to generate source-maps using "--map=linked/linked-src/embedded" (unless you're compiling code on-the-fly in the browser, in which case it will generate source-maps by default).

@igl
Copy link
Contributor

igl commented Feb 27, 2015

Awesome! Always bet on LS. :)

I am going to try this out with node-source-map-support tonight.

@raine
Copy link
Contributor

raine commented Feb 27, 2015

Thank you @Diggsey, got the sourcemaps working and they seem accurate! Great work.

Here's a simple project with sourcemaps enabled if someone's interested.

@Diggsey
Copy link
Contributor Author

Diggsey commented Mar 6, 2015

@gkz Anything else need doing?

@gkz
Copy link
Owner

gkz commented Mar 6, 2015

I just need to check it over and merge it in

@dead-claudia
Copy link
Contributor

I'm already excited.

@gkz
Copy link
Owner

gkz commented Mar 16, 2015

Sorry guys, I've been busy with work and school. I'll take a look at this in the coming week.

@gkz gkz merged commit ffc6f74 into gkz:master Mar 21, 2015
@gkz
Copy link
Owner

gkz commented Mar 21, 2015

Works great! Awesome work @Diggsey!

source-maps

@vendethiel
Copy link
Contributor

🍰

@awkay
Copy link

awkay commented Apr 5, 2015

This officially improves my chances of getting it adopted where I work! Thanks!

@blvz
Copy link
Contributor

blvz commented Apr 8, 2015

I didn't care much about it at first, but this is opening lots of opportunities, like code coverage:
gotwarlost/istanbul#212

So thanks again, @Diggsey.

@gkz
Copy link
Owner

gkz commented Apr 10, 2015

Hi @Diggsey, I was wondering if you could give some comment on how the new source map functionality works. Ie. if I want to create a new ast node, what do I need to do? I see sn(this, ....args) used, anything special about how the args are laid out?

@Diggsey
Copy link
Contributor Author

Diggsey commented Apr 10, 2015

"sn(this, ...args)" creates a new sourcemap node: it takes the line and column information from 'this' (an AST node), and the text content for the node is given by recursively concatenating the content of sub-nodes passed in as '...args' (with plain js strings being the leaf nodes)

The aim when generating sourcenodes is to associate the location information of an AST node with precisely the javascript which that AST node generates, and it's a little more tricky than it sounds:

When generating source-nodes for the expression '1 + 2' in some simple language, it might involve two types of AST nodes, Literal and Addition. Literal is easy, it just returns "sn(this, value)", whereas Addition is more complicated: "sn(null, left, sn(this, ' + '), right)". The "null" means the sourcenode is just used for grouping - it doesn't supply any location information.

You might be tempted to just do this: "sn(this, left, ' + ', right)", and that's what I started out doing, but the problem is that "left" or "right" might contain code which is not associated with any location in the input - that code would get erroniously associated with the location information from 'this'.

@gkz
Copy link
Owner

gkz commented Apr 15, 2015

Thanks for the info!

Would you be able to give a brief description of what the different options do (linked, linked-src, embedded, debug) to add to the docs in for the upcoming release?

@Diggsey
Copy link
Contributor Author

Diggsey commented Apr 15, 2015

Sure, there are three files involved when generating sourcemaps:
a) The original LiveScript source
b) The sourcemap
c) The generated javascript source

'a' can optionally be embedded inside 'b', and 'b' can optionally be embedded inside 'c' via a comment.

linked: No embedding, 'c' links to 'b' via a relative path, and same for 'b' to 'a'
linked-src: 'b' is embedded inside 'c', but 'a' is linked to
embedded: Everything is embedded inside 'c'
debug: Same as linked, but will also output a human readable representation of the source-node tree (similar to the output from the 'ast' option) to a '.map.debug' file.

Use linked or linked-src if you are directly serving the output from lsc to the browser - (ie. not performing further processing). They keep the original source separate, so the javascript file is still small. linked-src just means you have one fewer file to carry around at the expense of increasing the size of the javascript file.

Use embedded for everything else - it's self-contained and it's the only form most other tools such as browserify will accept as input. The file will be significantly larger, but you can remedy this by running a separate tool at the end of your build pipeline to split the output back into the 'linked' form.

@dead-claudia
Copy link
Contributor

You can open a PR in the gh-pages branch. That's where this information is most useful.

@gkz
Copy link
Owner

gkz commented Apr 26, 2015

I'll add the information to gh-pages, but I would appreciate if people who are more familiar with source-maps than me could add some tests using the --map option to https://github.com/gkz/LiveScript/blob/master/test/cli.ls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants