11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.2k stars 494 forks source link

Minify Javascript - But as a separate file #1425

Closed surjithctly closed 4 years ago

surjithctly commented 4 years ago

Hello,

I have seen Eleventy uses "Terser" plugin for minification. However the docs only explain how to inline it.

Suppose I have a file js/main.js then I want to minify it using Terser as js/main.min.js and then run addPassthroughCopy to copy to _site.

src/js/main.js --> src/js/main.min.js --> _site/js/main.min.js

is it something possible with Eleventy? if yes, Can share the sample code?

Thanks a lot.

pdehaan commented 4 years ago

Wondering if you could just create src/js/main.js.njk (or liquid or whatever template you want), wrap the whole template it in some custom {% jsmin %}...{% endjsmin %} Terser paired tags, and then set a custom permalink to "/js/main.min.js" in your frontmatter. Possibly not ideal as you'd probably lose the ability to lint your JavaScript via ESLint in the process though. Not sure if it'd be better to do minification via a separate post-Eleventy-build hook in your package.json file's "scritps":

"build": "eleventy build",
"postbuild": "npm run minify:js",
"minify:js": "terser src/js/main.js --compress --mangle -o _site/js/main.min.js",
"start": "....",
"test": "...."
surjithctly commented 4 years ago

Hello @pdehaan

Writing JS inside njk doesn’t seems a good idea. I was thinking Eleventy provides some option for this like Gulp.

The second option seems a good idea, but how can we make sure it always run together? In your example, I think I needed to run both command separately.

Still wondering how do others do it..

pdehaan commented 4 years ago

Npm scripts with “pre” and “post” prefixes are a bit special. https://www.yld.io/blog/using-npm-pre-and-post-hooks

So if you type npm run build, it would automatically running any prebuild, build, then postbuild script. So you’d only need to type one command, but it wouldnt run postbuild if you typed eleventy directly from your Terminal.

pdehaan commented 4 years ago

Sorry, me again... OK I think I found a solution. By putting the JavaScript file in /src/_includes/, and then including the file elsewhere and wrapping it in a {% terser %}...{% endterser %} shortcode, I think I got your use case working (without the need for extra build steps).

https://github.com/pdehaan/11ty-terser-test

tree . -aI "node_modules|.git"
.
├── .eleventy.js
├── .gitignore
├── package-lock.json
├── package.json
│
├── src # <<<<< INPUT FOLDER
│   ├── _includes
│   │   ├── layouts
│   │   │   └── base.njk
│   │   └── site.js
│   ├── index.njk
│   └── scripts.njk
│
└── www # <<<<< OUTPUT FOLDER
    ├── index.html
    └── site.min.js

4 directories, 10 files

So, src/_includes/site.js contains some truly horrible JavaScript. In src/scripts.njk we set our output file permalink (so it writes a JavaScript file), and include our site.js file from src/_includes/, and wrap all the included output in our {% terser %}...{% endterser %} paired shortcode:

---
permalink: /site.min.js
---

{% terser %}
{%- include "site.js" -%}
{% endterser %}

This should let you still keep your JavaScript in a .js file so it can easily be formatted by Prettier or linted by ESLint, and your minified file gets written out to a separate file instead of inlined.

And if we open www/site.min.js in our code editor, we can see that it is beautifully minified.


function hello(l){l||(l="World"),alert("Hello "+l)}
Unminified src/_includes/site.js ```js // COPY 2020 BY PEOPLE function hello(name) { if (!name) { name = "World"; } alert("Hello" + " " + name) return true; } ```

Finally, my terser paired shortcode is just the same example I posted over in https://github.com/11ty/eleventy/issues/1344#issuecomment-676497888 since I couldn't remember how to do the async terser in a sync paired shortcode off the top of my head.

const makeSynchronous = require("make-synchronous");

const jsmin = makeSynchronous(async (code="", opts={}) => {
  const Terser = require("terser");
  try {
    const minified = await Terser.minify(code, opts);
    return minified.code;
  } catch (err) {
    console.error(err);
    // Unexpected minify error. Return unminified code.
    return code;
  }
});

module.exports = function (eleventyConfig) {
  eleventyConfig.addPairedShortcode("terser", jsmin);

  return {
    dir: {
      input: "src",
      output: "www"
    }
  };
};
surjithctly commented 4 years ago

Thanks a lot. Let me try it.

pdehaan commented 4 years ago

Actually, another (probably better) approach is to use Eleventy transforms.

Example repo at https://github.com/pdehaan/11ty-transform-async (does both HTML prettification/minification and JavaScript minification).

surjithctly commented 4 years ago

Oh Cool...

However don’t you think there should be a simple way in eleventy to handle this? Something similar to Gulp?

pdehaan commented 4 years ago

I think #117 would make this easier, so feel free to upvote that for a better workflow.

I don't know that adding a bunch of extra complexity to make Eleventy behave like Gulp is the best solution. Pretty sure you can use Gulp+Eleventy together now if you want. I've seen several 11ty templates out there that have full build systems like webpack or parcel or babel which do all sorts of clever file processing and that seems to work fairly well if you need a more robust solution.

davidpetrey commented 3 years ago

I thought that because I was using Gulp (because I couldn't figure out how to set up my build using eleventy.js) that I was in a way, doing it wrong. Or that using eleventy.js was the better way to do it. It's confusing that there aren't "standard" ways of doing this without reverting to Grunt or Gulp. But I am very thankful that 11ty can work easily with other build tools.

@surjithctly I laughed at your comment because that was one of the most surprising things about 11ty. Even after looking at many starter projects I had to fall back to Gulp because it was familiar.

pdehaan commented 3 years ago

I thought that because I was using Gulp (because I couldn't figure out how to set up my build using eleventy.js) that I was in a way, doing it wrong.

I dont think there is anything wrong with using Gulp+Eleventy together. I've seen quite a few starter projects that use both, or integrate with other tools like Tailwind. In fact, https://piccalil.li/course/learn-eleventy-from-scratch/ has a great section (See Module 2: Asset Pipeline) on using Gulp for processing Sass (via gulp-clean-css, and gulp-sass), fonts (via get-google-fonts), and images (via gulp-imagemin) w/ Eleventy. Highly recommend checking the course out.