hmans / flutterby

A flexible, Ruby-powered static site generator.
http://www.flutterby.run
MIT License
57 stars 2 forks source link

Unknown extensions/filters get stripped from the filename #35

Closed localhost closed 7 years ago

localhost commented 7 years ago

If we have a minified JavaScript file named js/jquery.min.js it will be built as js/jquery.min, whereas the expected result would be to leave unknown extensions alone.

A file called jquery.scrollex.min.js ends up as jquery.scrollex.

The following entries appear in the log:

 • Unsupported filter 'js'
 • Exported /js/jquery.min
 • Unsupported filter 'js'
 • Unsupported filter 'min'
 • Exported /js/jquery.scrollex
hmans commented 7 years ago

Thanks for reporting this issue!

This is going to be interesting to fix, since Flutterby -- for the moment -- has a very simple and strict rule about how it deals with file extensions that clashes with files named like you described above. I am, however, very eager to fix this (in terms of resolving this obvious conflict), but need to think about how to do it without moving too far away from said simple rules.

For the time being, as a workaround, please rename your files to jquery-min.js -- I know it's not ideal, but it's the best I can think of right now.

hmans commented 7 years ago

Just to clarify, the logic currently implemented by Flutterby is this: given an input file named foo.a.b.c.d, it will apply the d, c and b filters and export the result as foo.a.

hmans commented 7 years ago

Here's a couple of ideas how we could fix the issue. I'm not in love with any of them so far, but let's see where we can take it from here:

Option 1: implement special case handling for min extensions, simply making the .min part of the base filename. Pro: quite easy to implement. Con: I'm not a big fan of special case handling, and it'll break as soon as there's another extension that's not min but should not be treated as an actual extension.

Option 2: rework the way filters are derived from input file names to essentially stop the filter chain at the first extension that does not have a filter. For example, a file named jquery.min.js.erb would run through the erb filter, but then export as jquery.min.js, because js has no associated filter. Pro: it's a sound logic that other parts of the framework would possibly also benefit from; Con: as soon as you add a filter for js files (for whatever reasons; maybe Babel preprocessing?), the whole thing will break down.

Option 3: keep a whitelist of "static file extensions" (eg. html, txt, js, ...) that will always stop the filter chain: Pro: it's a simple and easy to follow rule that can't be nullified simply by adding a new filter (as described in Option 2); Con: it robs Flutterby of some flexibility, and it's probably not going to be a lot of fun to actually build and maintain such a whitelist.

I'll think some more about this, and of course I'm happy to hear any alternative suggestions.

localhost commented 7 years ago

I really like the current chaining filter logic as such. I'm also quite excited about an Opal as a filter for JavaScript files.

So option 1 would also have to consider something like .pack.js and maybe some more. Option 2 seems to have even more downsides if one would love to integrate further JS preprocessing. I somewhat like option 3 even though it seems to be some work to keep such a list updated. But on the other side a filter could dynamically add a set of known extensions which it can work with. Option 3 makes most sense to me at the moment.

The basic problem seems to be that .min.js and .pack.js are somewhat reversed when compared with foo.js.min, which could minimize first and then apply a nil filter to .js. So maybe a filter should optionally be able to register a chain of extensions like .min.js and .pack.js for precompressed and minimized files.

Or maybe we could even have a wildcard like **.js and associate it with a nil filter. Where ** means everything including all previous filters and * means one filter. Con: sounds very complicated even as I write it.

hmans commented 7 years ago

At the moment, option 3 feels the most sane to me, too; ie., when working through a file's extensions, starting from the end, when encountering js or any other whitelisted "static" extension, Flutterby will stop processing the file. This would at least solve the issue, even though I still don't feel 100% comfortable with defining such a list (but I'll play around with some code later.)

I'm not terribly concerned about Flutterby then not seeing the min as a filter extensions, since the contents of the file will already be minified, with no further processing required. Besides that, I would prefer to move all asset compilation concerns (concatenation, minification, etc.) to whatever asset bundling code we may come up in Flutterby or a plugin gem in the future. I'm already experimenting with this in my hmans.io project:

Either way, I unfortunately won't have a lot of time for Flutterby this week due to client engagements, but I'll try to hack something up around the weekend.

hmans commented 7 years ago

After a couple of days of thinking about this (and stumbling over the way Flutterby currently deals with extensions in a project myself), I am currently leaning towards Option 2, ie.: if there is a file name with multiple extensions, Flutterby will consider all extensions to be filter extensions (starting with the last one, and moving towards the beginning of the file name), until it encounters any extension that is not recognized as a filter.

This assumes that users will probably never set up filters for extensions like html, txt or js.

Any additional comments on this? Otherwise, I'm planning on implementing this some time during the next couple of days.

localhost commented 7 years ago

Sounds good to me. I somewhat came to the same conclusion.

Will filters be able to handle different/combined extensions?

Edit: Just saw the commits/pull request. Looks good and to answer my own question - a filter seems to be able to handle multiple extensions by calling Flutterby::Filters.add for each extension it can handle.

How do you want to handle chained processing like .js and then e.g. minimization?

hmans commented 7 years ago

How do you want to handle chained processing like .js and then e.g. minimization?

I currently have no plans to perform minification/uglification through a filter -- instead, there will be a mechanism bundled with a (near!) future version of Flutterby that can bundle, concatenate, minify etc. a collection of assets (I have a basic version of this implemented in my blog.)

With files like jquery.min.js, they will probably already be minified.

Am I missing some obvious use case of per-file minifaction?

localhost commented 7 years ago

All good for now, I think.

hmans commented 7 years ago

36 has been merged. Closing this -- thanks!