GithubHelp home page GithubHelp logo

posthtml / htmlnano Goto Github PK

View Code? Open in Web Editor NEW
237.0 237.0 29.0 2.29 MB

Modular HTML minifier, built on top of the PostHTML

Home Page: https://htmlnano.netlify.app

License: MIT License

JavaScript 99.88% TypeScript 0.12%

htmlnano's Introduction

NPM Tests Coverage Standard Code Style

PostHTML

PostHTML is a tool for transforming HTML/XML with JS plugins. PostHTML itself is very small. It includes only a HTML parser, a HTML node tree API and a node tree stringifier.

All HTML transformations are made by plugins. And these plugins are just small plain JS functions, which receive a HTML node tree, transform it, and return a modified tree.

For more detailed information about PostHTML in general take a look at the docs.

Dependencies

Name Status Description
posthtml-parser npm Parser HTML/XML to PostHTMLTree
posthtml-render npm Render PostHTMLTree to HTML/XML

Create to your project

npm init posthtml

Install

npm i -D posthtml

Usage

API

Sync

import posthtml from 'posthtml'

const html = `
  <component>
    <title>Super Title</title>
    <text>Awesome Text</text>
  </component>
`

const result = posthtml()
  .use(require('posthtml-custom-elements')())
  .process(html, { sync: true })
  .html

console.log(result)
<div class="component">
  <div class="title">Super Title</div>
  <div class="text">Awesome Text</div>
</div>

โš ๏ธ Async Plugins can't be used in sync mode and will throw an Error. It's recommended to use PostHTML asynchronously whenever possible.

Async

import posthtml from 'posthtml'

const html = `
  <html>
    <body>
      <p class="wow">OMG</p>
    </body>
  </html>
`

posthtml(
  [
    require('posthtml-to-svg-tags')(),
    require('posthtml-extend-attrs')({
      attrsTree: {
        '.wow' : {
          id: 'wow_id',
          fill: '#4A83B4',
          'fill-rule': 'evenodd',
          'font-family': 'Verdana'
        }
      }
    })
  ])
  .process(html/*, options */)
  .then((result) =>  console.log(result.html))
<svg xmlns="http://www.w3.org/2000/svg">
  <text
    class="wow"
    id="wow_id"
    fill="#4A83B4"
    fill-rule="evenodd" font-family="Verdana">
      OMG
  </text>
</svg>

Directives

import posthtml from 'posthtml'

const php = `
  <component>
    <title><?php echo $title; ?></title>
    <text><?php echo $article; ?></text>
  </component>
`

const result = posthtml()
  .use(require('posthtml-custom-elements')())
  .process(html, {
    directives: [
      { name: '?php', start: '<', end: '>' }
    ]
  })
  .html

console.log(result)
<div class="component">
  <div class="title"><?php echo $title; ?></div>
  <div class="text"><?php echo $article; ?></div>
</div>
npm i posthtml-cli
"scripts": {
  "posthtml": "posthtml -o output.html -i input.html -c config.json"
}
npm run posthtml
npm i -D gulp-posthtml
import tap from 'gulp-tap'
import posthtml from 'gulp-posthtml'
import { task, src, dest } from 'gulp'

task('html', () => {
  let path

  const plugins = [ require('posthtml-include')({ root: `${path}` }) ]
  const options = {}

  src('src/**/*.html')
    .pipe(tap((file) => path = file.path))
    .pipe(posthtml(plugins, options))
    .pipe(dest('build/'))
})

Check project-stub for an example with Gulp

npm i -D grunt-posthtml
posthtml: {
  options: {
    use: [
      require('posthtml-doctype')({ doctype: 'HTML 5' }),
      require('posthtml-include')({ root: './', encoding: 'utf-8' })
    ]
  },
  build: {
    files: [
      {
        dot: true,
        cwd: 'html/',
        src: ['*.html'],
        dest: 'tmp/',
        expand: true,
      }
    ]
  }
}
npm i -D html-loader posthtml-loader

v1.x

webpack.config.js

const config = {
  module: {
    loaders: [
      {
        test: /\.html$/,
        loader: 'html!posthtml'
      }
    ]
  },
  posthtml: (ctx) => ({
    parser: require('posthtml-pug'),
    plugins: [
      require('posthtml-bem')()
    ]
  })
}

export default config

v2.x

webpack.config.js

import { LoaderOptionsPlugin } from 'webpack'

const config = {
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
            options: { minimize: true }
          },
          {
            loader: 'posthtml-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new LoaderOptionsPlugin({
      options: {
        posthtml(ctx) {
          return {
            parser: require('posthtml-pug'),
            plugins: [
              require('posthtml-bem')()
            ]
          }
        }
      }
    })
  ]
}

export default config
$ npm i rollup-plugin-posthtml -D
# or
$ npm i rollup-plugin-posthtml-template -D
import { join } from 'path';

import posthtml from 'rollup-plugin-posthtml-template';
// or
// import posthtml from 'rollup-plugin-posthtml';

import sugarml from 'posthtml-sugarml';  // npm i posthtml-sugarml -D
import include from 'posthtml-include';  // npm i posthtml-include -D

export default {
  entry: join(__dirname, 'main.js'),
  dest: join(__dirname, 'bundle.js'),
  format: 'iife',
  plugins: [
    posthtml({
      parser: sugarml(),
      plugins: [include()],
      template: true  // only rollup-plugin-posthtml-template
    })
  ]
};

Parser

import pug from 'posthtml-pug'

posthtml().process(html, { parser: pug(options) }).then((result) => result.html)
Name Status Description
posthtml-pug npm Pug Parser
sugarml npm SugarML Parser

Plugins

In case you want to develop your own plugin, we recommend using posthtml-plugin-starter to get started.

Maintainers


Ivan Demidov

Ivan Voischev

Contributors

Backers

Thank you to all our backers! ๐Ÿ™ [Become a backer]

htmlnano's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

htmlnano's Issues

Remove empty elements

Source:

<div>hello <span><b></b></span></div>

Minified:

<div>hello</div>

By default the module must be disabled, since it could break the page. It should probably have a few options:

  • remove all empty elements
  • remove empty elements without attributes (class, id, etc)

Minify meta refresh values

<meta http-equiv="refresh" content="5; url=">
<meta http-equiv="refresh" content="5; url=http://example.com/">

can be shortened to:

<meta http-equiv="refresh" content="5">
<meta http-equiv="refresh" content="5; http://example.com/">

http-equiv-refresh

Minify content inside conditional comments

Source:

<!--[if lte IE 7]>
<style type="text/css">
.title {
  color: red;
}
</style>
<![endif]-->

Minified:

<!--[if lte IE 7]><style>.title {color:red}</style><![endif]-->

The module should parse the content inside a conditional comment and minify it with htmlnano.

Remove package-lock.json

It's meant for apps, not published packages. They are automatically npmignored which means that while users of the project will install whatever is most recent that conform to semver, maintainers of the project can be locked into older versions, unable to reproduce possible issues before making a release.

Please remove the file and gitignore it.

mergeScripts should insert semicolons between <script> tags

With the following HTML and default options:

<script>document.write('Hello, ')</script><script>document.write('World!')</script>

It should produce:

<script>document.write('Hello, ');document.write('World!');</script>

But instead you get the following error:

Error: SyntaxError: Unexpected token: name (document)

So it appears that it is concatenating the two <script> tags as this (without a semicolon in between):

<script>document.write('Hello, ')document.write('World!')</script>

If you put the semicolon there yourself, mergeScripts does something even stranger. It replaces it with a comma:

<script>document.write('Hello, ');</script><script>document.write('World!')</script>

becomes

<script>document.write('Hello, '),document.write('World!');</script>

The comma is valid, but it does seem an odd choice over a semicolon.

Does not minify svgs that have the <use> tag in them

Pretty much what it sounds like. I created an example here. You can view the results of this code in three places:

  1. No minification: https://lying-equinox.glitch.me/
  2. Minification with minifySvg set to false: https://lying-equinox.glitch.me/minify_good
  3. Minification with minifySvg set to default (which is true): https://lying-equinox.glitch.me/minify_bad

If you inspect element the minify_bad path, you can see how htmlnano butchers the <use> tag's contents. Not sure if this is intentional behavior or not but it makes dealing with svgs difficult (which is unfortunate because they tend to create a lot of whitespace and such if not minimized).

Minify script templates

Source:

<script type="text/x-handlebars-template">
    <div class="entry">
        <h1>{{title}}</h1>
    </div>
</script>

Minified:

<script type="text/x-handlebars-template"><div class="entry"><h1>{{title}}</h1></div></script>

Minify CSS

A module for minifying CSS inside <style> (cssnano?).

Error minifying npmjs.com

Error minifying npmjs.com main page:

{ message: 'Unexpected token: punc (:)',
  filename: 0,
  line: 1,
  col: 13,
  pos: 13,
  stack: `Error
at new JS_Parse_Error (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:1524:18)
at js_error (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:1532:11)
at croak (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2023:9)
at token_error (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2031:9)
at unexpected (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2037:9)
at semicolon (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2057:43)
at simple_statement (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2237:73)
at eval (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2090:47)
at eval (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2070:24)
at block_ (eval at <anonymous> (/Users/maltsev/dev/js/htmlnano/node_modules/uglify-js/tools/node.js:24:4), <anonymous>:2350:20)` }

Published patch for recent PR

I would like to request a patch be published whenever you get a chance.

And for the future for quick iterations--is there a simple way to include unreleased htmlnano in a project? I'm currently trying "htmlnano": "github:Rebelmail/htmlnano#rebel" in our package.json with yarn. The branch is the PR I made with the compiled files committed. It works--the first time installed with no yarn.lock and then subsequent installs (or adding new packages) seems to remove the compiled files. I'm going to try converting back to npm to see if it works as desired, but do you know of any other approaches (besides a private repo or publishing)?

Minify JS

A module for minifying JS inside <script> and JS-attributes (like onlick).

Add comparison with other minifiers

Add the comparison to the README.

Perhaps it'll be better to write a script which compares different minifiers and generates the result table.

Add a safe preset

It would be nice to have an option called safe that sets the optimal preset for minifying html safely, without having to thinker around untill you get a universal htmlnano config that works safe for everyone.

This idea came from trying to optimise the universal config for parcel, as we from time to time get a bug report with an unsafe transform.

If there is intrest for this feature but no available time, I'd love to implement this

[v0.1.6] `tree.forEach` is not a `{Function}`

minifiererror

file.html (import)

<div class="import"></div>

module.html

<div>
 <import src="./file.html"></import>
 <img src="./file.png">

 <div class="container"></div>
<div>

webpack.config.js

{
  test: /\.$html/,
  use: [
    { 
      loader: 'html-loader', 
      options: { 
        minimize: true 
      } 
    }
  ]
}

module

import HTML__IMPORT__0 from './file.html';
import HTML__URL__0 from './file.png';

export default `
<div>
 ${HTML__IMPORT__0}
 <img src="${HTML__URL__0}">

 <div class="container"></div>
<div>
`

entrypoint

import html from './module.html'

console.log(html)

/*
<div>
 <div class="import"></div>
 <img src="4453847865t5u.png">

 <div class="container"></div>
<div>
*/

Specify which SVGO plugins to use

Is there any way to enable/disable specific SVGO plugins using the config file? Right now my .htmlnanorc looks like this:

{
    "minifySvg": false
}

And I'd like to have something like this:

{
    "minifySvg": {
        "plugins": [
            { "removeViewBox": false }
        ]
    }
}

Remove attribute quotes

Source:

<div class="foo" title="hello world"></div>

Minified:

<div class=foo title="hello world"></div>

Split out into separate modules

For example, I have a need to merge all <style> elements for a different transformation before minifying the entire document. Currently, I'll need to run htmlnano with all sub-modules disabled except for mergeStyles, which poses a problem for a future where new sub-modules are added.

Edit:
mergeStyles could replace the lesser quality posthtml-collect-styles.

Smart encapsulation

@stevenvachon:

switch between ' and " (per attribute) as attribute value encapsulators to find which produces the smallest output (due to any necessary commenting: attr='blah 'blah'' vs attr="blah 'blah'")

Remove redundant attributes

Source:

<form method="get">
    <input type="text">
    <script type="text/javascript"></script>
</form>

Minified:

<form>
    <input>
    <script></script>
</form>

Merge styles

Merge several <style> into one. Consider if the style tag's position is significant for rendering.

@stevenvachon, thanks for the idea.

Collapse boolean attributes

Source:

<button disabled="disabled">click</button>
<button disabled="">me</button>

Minified:

<button disabled>click</button>
<button disabled>me</button>

Conservative whitespace collapse misses some spaces

Before:

<head>
<style>body{color:red}</style>
<style></style>
</head>
<body>

After:

<head> <style>body{color:red}</style>  </head> <body>

Notice the two spaces after </style>.

Whitespace collapse should take place after element removals, if it isn't already.

Minify srcset values

<img srcset="image.png 1x, image2.png 2x">
<img srcset=" ">

can be shortened to:

<img srcset="image.png,image2.png 2x">
<img>

parse-srcset

Move to it's own Organisation (htmlnano)

๐Ÿ‘‹

There are plenty of Issues with help-wanted label and I'm eager to help out. Beforehand I would like to discuss an eventual move of this module and gulp-htmlnano to it's own org (Just for preserving the name atm, will be your ownership of course :) ). If you don't like the idea, please give my some insight of your prefered way to collaborate here.

Why

  • indicates seriousity
  • better community building
  • better collaboration options
  • removes the impression of being a 'one-man' show, maybe hard to justify corporate usage for some folks

Structure

  • htmlnano-cli (@me)
  • htmlnano
  • gulp-htmlnano
  • grunt-htmlnano (@me)
  • maybe move all kinds of minifications to their own posthtml-plugins (but secondary and discussable :), then chore done by @me aswell)

Fix minifyCss test on Travis CI

All tests pass successfully on my local machine, but on Travis CI they always fail with the following error:

1) minifyCss
       should minify CSS inside <style>:
     Error: global leak detected: Base64
      at <anonymous>

De-duplicate list attribute values

such as class, ping and rel.

<a class="class1   class2 class2" ping="http://domain1.com, http://domain2.com" rel="me me   external">

would compile to:

<a class="class1 class2" ping="http://domain1.com,http://domain2.com" rel="me external">

These would be handy: list-to-array, dupe

If we wanted to de-dupe URL lists (such as ping), we could use url-relation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.