mde / ejs

Embedded JavaScript templates -- http://ejs.co
Apache License 2.0
7.72k stars 843 forks source link

Add support for source maps #123

Open sitegui opened 8 years ago

sitegui commented 8 years ago

Hi all,

In PR #56, it was discussed one can modify the EJS source before handing it to ejs.compile(). It works, but has the downside of making the extended error messages (with EJS source and line numbers) hard to read, since all source will probably be presented as one line.

Another solution is to minify the render()'s output, but this may be too expensive. In my benchmarks, using html-minifier, it takes more than 100ms (for my use case, that's too much).

But I think offering HTML minification as part of EJS is not the way to go either, since it would bloat this wonderful module too much and would not solve the underlying problem with pre-processed source.

What if EJS could accept a source map as part of the compilation and use it to report the right line number and original source? This way, HTML minification could be part of another module, separating concerns.

I'd like to hear you position on this issue: do you think this module should accommodate this use case?

Best regards, Guilherme

mde commented 8 years ago

This sounds great, if it can be implemented as an optional module. Then this could be added to the lib without creating a bunch of bloat. All that would need to be added would be the hooks.

RyanZim commented 8 years ago

@sitegui, are you interested in working on this? Otherwise, I'll add a help wanted label.

sitegui commented 8 years ago

Hi @RyanZim,

I'm not interested in taking this any longer, you can add the label ;)

For my use case, I've implemented a version of EJS that understands HTML, so that it can run the minification logic on compile time. It also allowed me to support a bunch of other HTML-related stuff (like linting and custom elements).

madcampos commented 7 years ago

Hi folks, I was reviewing info about source maps, mostly the spec and the html5 rocks article and was thinking: what will be translated to the source map? In JS/CSS the classes, functions, objects and other constructs are translated from the generating language in a 1 to 1 mapping. Will it be the same here? What parts of EJS will be translated to HTML, and to what in html? Tags? I'm curious so that I can help with this implementation.

mde commented 7 years ago

This is a really interesting question -- internally EJS maintains its own mapping of template source-code so it can provide correct error lines for thrown errors. I'm not even sure what minifying means here, because we could be talking about minifying the template source-code, or minifying the generated JavaScript source code.

I guess this would actually mean creating a traditional sourcemaps file for the generated JS source-code that would plug into the existing tracked line numbers in the original template source. This is a technically very interesting and challenging project.

RyanZim commented 7 years ago

Before we go too far on this, a few questions:

TimothyGu commented 7 years ago

I'm not sure about EJS/HTML to HTML sourcemap, but I do think EJS to compiled JS sourcemap could be helpful. We would be able to delete the rethrow functions, in favor of more standardized facilities like https://github.com/evanw/node-source-map-support.

RyanZim commented 7 years ago

@TimothyGu True; this wouldn't solve the original issue of handling html minification and keeping correct line numbers.

Another solution is to minify the render()'s output, but this may be too expensive. In my benchmarks, using html-minifier, it takes more than 100ms (for my use case, that's too much).

This begs the question: if we added sourcemaps, minified the the EJS templates, etc., would it be any faster?

sitegui commented 7 years ago

My original issue was about keeping the correct mapping from EJS run-time exceptions to the original code, after it has run through a pre-processor.

My idea back there was to create a HTML minifier pre-processor so that, for example, the following EJS code:

<span class="user">
    <%= user.name %>
</span>

would be compiled to <span class=user><%= user.name %></span>. So, say user is null, on rendering it would throw an exception and it should print the original three-line code and point to the error in the second line.

In this model, what is minified is the input to compile(), so the HTML minification logic is applied once, on startup. This answers:

This begs the question: if we added sourcemaps, minified the the EJS templates, etc., would it be any faster?

It would, if the template is compiled once and ran multiple times.

The original idea had nothing to do with outputing a source map, but instead with accepting one to map errors.

In the end, I've written ejs-html to handle HTML minification directly.

RyanZim commented 7 years ago

Thanks for chiming in @sitegui

As things stand now, I would be in favor of closing this.

@TimothyGu If you feel that line-number error mapping should/could be rewritten, that should be a separate issue. "If it ain't broke, don't fix it!" IMO, though.

If someone has something else to add to this discussion, please do so, otherwise, I will probably close in few days.

TimothyGu commented 7 years ago

No I definitely agree that it doesn't need to be rewritten, but if we have sourcemap support bridging with other sourcemap-aware tooling like Webpack could be easier, in addition to better rethrow.

v4dkou commented 3 years ago

From what I see, to implement source maps, we need to pass this.currentLine to the compiled __append in the scanLine method. Then if we extend this __append method to store an association between the source line number and output lines range, we'll get something like a source map.

https://github.com/mde/ejs/blob/main/lib/ejs.js#L885 https://github.com/mde/ejs/blob/main/lib/ejs.js#L827