├── .prettierrc.json ├── .npmignore ├── index.ts ├── .travis.yml ├── jasmine.json ├── .gitignore ├── .editorconfig ├── tsconfig.json ├── src ├── types │ └── clean-css │ │ └── index.d.ts ├── test │ ├── exports.spec.ts │ ├── strategy.spec.ts │ └── minifyHTMLLiterals.spec.ts ├── strategy.ts └── minifyHTMLLiterals.ts ├── LICENSE.md ├── package.json ├── CHANGELOG.md └── README.md /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/* 2 | !**/*.js 3 | !**/*.map 4 | !**/*.d.ts 5 | !**/*.md 6 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/minifyHTMLLiterals'; 2 | export * from './src/strategy'; 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | script: 5 | - npm test 6 | - npm run coveralls 7 | -------------------------------------------------------------------------------- /jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_files": ["**/*spec.ts"], 3 | "stopSpecOnExpectationFailure": false, 4 | "random": true 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | /.nyc_output 4 | **/*.js 5 | **/*.map 6 | **/*.d.ts 7 | !src/types/**/*.d.ts 8 | .wireit -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "noUnusedLocals": true, 7 | "noUnusedParameters": true, 8 | "sourceMap": true, 9 | "strict": true, 10 | "target": "esnext", 11 | "typeRoots": ["./src/types", "./node_modules/@types"] 12 | }, 13 | "include": ["index.ts", "src/**/*.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /src/types/clean-css/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'clean-css/lib/options/optimization-level' { 2 | import { Options, OptimizationsOptions } from 'clean-css'; 3 | 4 | export interface OptimizationLevel { 5 | Zero: '0'; 6 | One: '1'; 7 | Two: '2'; 8 | } 9 | 10 | export const OptimizationLevel: OptimizationLevel; 11 | 12 | export interface OptimizationLevelOptions { 13 | [OptimizationLevel.Zero]: {}; 14 | [OptimizationLevel.One]: Required< 15 | Omit, 'all'> 16 | >; 17 | [OptimizationLevel.Two]: Required< 18 | Omit, 'all'> 19 | >; 20 | } 21 | 22 | export function optimizationLevelFrom( 23 | source: Options['level'] 24 | ): OptimizationLevelOptions; 25 | } 26 | -------------------------------------------------------------------------------- /src/test/exports.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as min from '../../index.js'; 3 | import { 4 | defaultGenerateSourceMap, 5 | defaultShouldMinify, 6 | defaultShouldMinifyCSS, 7 | defaultValidation, 8 | minifyHTMLLiterals 9 | } from '../minifyHTMLLiterals.js'; 10 | import { 11 | adjustMinifyCSSOptions, 12 | defaultMinifyCSSOptions, 13 | defaultMinifyOptions, 14 | defaultStrategy 15 | } from '../strategy.js'; 16 | 17 | describe('exports', () => { 18 | it('should export minifyHTMLLiterals() function and defaults', () => { 19 | expect(min).to.deep.equal({ 20 | minifyHTMLLiterals, 21 | defaultGenerateSourceMap, 22 | defaultShouldMinify, 23 | defaultShouldMinifyCSS, 24 | defaultValidation, 25 | defaultMinifyOptions, 26 | defaultMinifyCSSOptions, 27 | defaultStrategy, 28 | adjustMinifyCSSOptions 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Elizabeth Mitchell 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minify-html-literals", 3 | "version": "1.3.5", 4 | "description": "Minify HTML template literal strings", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "author": "Liz Mitchell ", 8 | "license": "MIT", 9 | "keywords": [ 10 | "minify", 11 | "html", 12 | "literal", 13 | "literals", 14 | "template", 15 | "tagged", 16 | "lit-html" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/asyncLiz/minify-html-literals.git" 21 | }, 22 | "scripts": { 23 | "build:ts": "wireit", 24 | "test": "wireit", 25 | "coveralls": "cat coverage/lcov.info | coveralls", 26 | "lint": "prettier --write \"*.{js,ts,json,md}\" \"{src,test}/**/*\"", 27 | "precommit": "lint-staged", 28 | "release": "standard-version", 29 | "prepublishOnly": "npm run build" 30 | }, 31 | "wireit": { 32 | "build:ts": { 33 | "command": "tsc --pretty", 34 | "clean": "if-file-deleted", 35 | "files": [ 36 | "src/**/*.ts", 37 | "!src/**/*.d.ts", 38 | "tsconfig.json" 39 | ], 40 | "output": [ 41 | "src/**/*.{js,d.ts,js.map}", 42 | "!src/types/**/*.d.ts" 43 | ] 44 | }, 45 | "test": { 46 | "command": "nyc mocha -r ts-node/register -r source-map-support/register src/test/*.js", 47 | "dependencies": [ 48 | "build:ts" 49 | ], 50 | "output": [ 51 | ".nyc_output", 52 | "coverage" 53 | ] 54 | } 55 | }, 56 | "lint-staged": { 57 | "*.{js,ts,json,md}": [ 58 | "prettier --write", 59 | "git add" 60 | ] 61 | }, 62 | "nyc": { 63 | "extension": [ 64 | ".ts" 65 | ], 66 | "include": [ 67 | "index.ts", 68 | "src/**/*.ts" 69 | ], 70 | "exclude": [ 71 | "**/*.d.ts" 72 | ], 73 | "reporter": [ 74 | "html", 75 | "lcovonly", 76 | "text-summary" 77 | ], 78 | "all": true 79 | }, 80 | "dependencies": { 81 | "clean-css": "^4.2.1", 82 | "html-minifier-terser": "^7.2.0", 83 | "magic-string": "^0.25.0", 84 | "parse-literals": "^1.2.1" 85 | }, 86 | "devDependencies": { 87 | "@types/chai": "^4.1.4", 88 | "@types/clean-css": "^4.2.0", 89 | "@types/html-minifier-terser": "^7.0.2", 90 | "@types/mocha": "^5.2.5", 91 | "@types/node": "^14.14.32", 92 | "@types/sinon": "^5.0.1", 93 | "chai": "^4.1.2", 94 | "coveralls": "^3.0.2", 95 | "husky": "^0.14.3", 96 | "lint-staged": "^7.2.0", 97 | "mocha": "^7.2.0", 98 | "nyc": "^15.0.0", 99 | "prettier": "^1.13.7", 100 | "rimraf": "^2.6.2", 101 | "sinon": "^6.1.4", 102 | "source-map-support": "^0.5.6", 103 | "standard-version": "^9.0.0", 104 | "ts-node": "^7.0.0", 105 | "typescript": "^4.2.3", 106 | "wireit": "^0.9.5" 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/strategy.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { minify } from 'html-minifier-terser'; 3 | import { defaultMinifyOptions, defaultStrategy } from '../strategy'; 4 | import { TemplatePart } from 'parse-literals'; 5 | 6 | describe('strategy', () => { 7 | describe('default', () => { 8 | const parts: TemplatePart[] = [ 9 | { 10 | text: '

', 11 | start: 0, 12 | end: 4 13 | }, 14 | { 15 | text: '

', 16 | start: 4, 17 | end: 5 18 | } 19 | ]; 20 | 21 | describe('getPlaceholder()', () => { 22 | it('should return a string with @ and () in it with no spaces', () => { 23 | const placeholder = defaultStrategy.getPlaceholder(parts); 24 | expect(placeholder.indexOf('@')).to.equal(0, 'should start with @'); 25 | expect(placeholder).to.include('()', 'should contain function parens'); 26 | }); 27 | 28 | it('should append "_" if placeholder exists in templates', () => { 29 | const regularPlaceholder = defaultStrategy.getPlaceholder(parts); 30 | const oneUnderscore = defaultStrategy.getPlaceholder([ 31 | { text: regularPlaceholder, start: 0, end: regularPlaceholder.length } 32 | ]); 33 | 34 | expect(oneUnderscore).not.to.equal(regularPlaceholder); 35 | expect(oneUnderscore).to.include('_'); 36 | 37 | const twoUnderscores = defaultStrategy.getPlaceholder([ 38 | { 39 | text: regularPlaceholder, 40 | start: 0, 41 | end: regularPlaceholder.length 42 | }, 43 | { 44 | text: oneUnderscore, 45 | start: regularPlaceholder.length, 46 | end: regularPlaceholder.length + oneUnderscore.length 47 | } 48 | ]); 49 | 50 | expect(twoUnderscores).not.to.equal(regularPlaceholder); 51 | expect(twoUnderscores).not.to.equal(oneUnderscore); 52 | expect(twoUnderscores).to.include('__'); 53 | }); 54 | 55 | it('should return a value that is preserved by html-minifier when splitting', async () => { 56 | const placeholder = defaultStrategy.getPlaceholder(parts); 57 | const minHtml = await defaultStrategy.minifyHTML( 58 | ` 59 | 71 |

72 | ${placeholder} 73 |

74 |
75 | `, 76 | defaultMinifyOptions 77 | ); 78 | 79 | console.log(minHtml); 80 | 81 | // 8 placeholders, 9 parts 82 | expect( 83 | defaultStrategy.splitHTMLByPlaceholder(minHtml, placeholder) 84 | ).to.have.lengthOf(9); 85 | }); 86 | }); 87 | 88 | describe('combineHTMLStrings()', () => { 89 | it('should join part texts by the placeholder', () => { 90 | const expected = '

EXP

'; 91 | expect(defaultStrategy.combineHTMLStrings(parts, 'EXP')).to.equal( 92 | expected 93 | ); 94 | }); 95 | }); 96 | 97 | describe('minifyHTML()', () => { 98 | it('should call minify() with html and options', async () => { 99 | const placeholder = defaultStrategy.getPlaceholder(parts); 100 | const html = ` 101 | 102 |

${placeholder}

103 |
    104 |
  • ${placeholder}
  • 105 |
106 | `; 107 | 108 | expect( 109 | await defaultStrategy.minifyHTML(html, defaultMinifyOptions) 110 | ).to.equal(await minify(html, defaultMinifyOptions)); 111 | }); 112 | }); 113 | 114 | describe('splitHTMLByPlaceholder()', () => { 115 | it('should split string by the placeholder', () => { 116 | const expected = ['

', '

']; 117 | expect( 118 | defaultStrategy.splitHTMLByPlaceholder('

EXP

', 'EXP') 119 | ).to.deep.equal(expected); 120 | }); 121 | 122 | it('should handle if a placeholder is missing its semicolon', () => { 123 | const expected = ['

', '

']; 124 | const html = `

EXP;

`; 125 | expect( 126 | defaultStrategy.splitHTMLByPlaceholder(html, 'EXP;') 127 | ).to.deep.equal(expected); 128 | }); 129 | }); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.3.5](https://github.com/asyncLiz/minify-html-literals/compare/v1.3.4...v1.3.5) (2021-03-09) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * typing build error ([9571152](https://github.com/asyncLiz/minify-html-literals/commit/9571152724e542d329aea6e79bf609a67e20bec6)) 11 | 12 | ### [1.3.4](https://github.com/asyncLiz/minify-html-literals/compare/v1.3.3...v1.3.4) (2021-03-09) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * build errors ([ee4d596](https://github.com/asyncLiz/minify-html-literals/commit/ee4d596c2797cea335af2c43ba0368ec3d6fa518)) 18 | 19 | ### [1.3.3](https://github.com/asyncLiz/minify-html-literals/compare/v1.3.2...v1.3.3) (2021-03-09) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * html attribute placeholders throwing split error [#28](https://github.com/asyncLiz/minify-html-literals/issues/28) ([b1e14dc](https://github.com/asyncLiz/minify-html-literals/commit/b1e14dca1a5ed9e6599193f474992729953f885d)) 25 | * minify multiline svg elements ([9f37d2d](https://github.com/asyncLiz/minify-html-literals/commit/9f37d2d6442a6533a90c1728f80aeb78d6060d9b)) 26 | * parse errors with JS comments in HTML attributes with no quotes ([0f5a842](https://github.com/asyncLiz/minify-html-literals/commit/0f5a842c54f3514c72c79eaf6749f15770818550)) 27 | * spaces in pseudo classes (like ::part) are not removed ([85526fc](https://github.com/asyncLiz/minify-html-literals/commit/85526fcb889e288e1adbb5c7ff9feca41d45acff)), closes [#26](https://github.com/asyncLiz/minify-html-literals/issues/26) 28 | 29 | ### [1.3.2](https://github.com/asyncLiz/minify-html-literals/compare/v1.3.1...v1.3.2) (2020-08-18) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * css tagged templates not respecting semicolons ([#22](https://github.com/asyncLiz/minify-html-literals/issues/22)) ([3651a0b](https://github.com/asyncLiz/minify-html-literals/commit/3651a0bc30167deccdfb21b4177827072df16cb5)) 35 | 36 | ### [1.3.1](https://github.com/asyncLiz/minify-html-literals/compare/v1.3.0...v1.3.1) (2020-06-10) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * don't remove attribute quotes by default. Fixes [#12](https://github.com/asyncLiz/minify-html-literals/issues/12). ([#13](https://github.com/asyncLiz/minify-html-literals/issues/13)) ([e18ae65](https://github.com/asyncLiz/minify-html-literals/commit/e18ae65e202802cb2fd793089f76de3af54fec6f)) 42 | 43 | ## [1.3.0](https://github.com/asyncLiz/minify-html-literals/compare/v1.2.2...v1.3.0) (2020-02-08) 44 | 45 | ### Features 46 | 47 | - minify svg-tagged templates [#9](https://github.com/asyncLiz/minify-html-literals/issues/9) ([62da810](https://github.com/asyncLiz/minify-html-literals/commit/62da810894a1f2c3705783ebb1a4264cf8989ee4)) 48 | 49 | ### Bug Fixes 50 | 51 | - update to html-minifier v4.0.0 ([6ddfd10](https://github.com/asyncLiz/minify-html-literals/commit/6ddfd104307347b7a66739b3c4e418bb6686e94e)) 52 | - update to parse-literals v1.2.0 ([bba4c7d](https://github.com/asyncLiz/minify-html-literals/commit/bba4c7d12b9d92635ed1d72d00d69086a45d8edb)) 53 | 54 | 55 | 56 | ## [1.2.2](https://github.com/asyncLiz/minify-html-literals/compare/v1.2.1...v1.2.2) (2019-02-13) 57 | 58 | ### Bug Fixes 59 | 60 | - failure to minify templates prefixed with comments ([8805f69](https://github.com/asyncLiz/minify-html-literals/commit/8805f69)) 61 | 62 | 63 | 64 | ## [1.2.1](https://github.com/asyncLiz/minify-html-literals/compare/v1.2.0...v1.2.1) (2019-02-13) 65 | 66 | ### Bug Fixes 67 | 68 | - remove source files from package ([b53c052](https://github.com/asyncLiz/minify-html-literals/commit/b53c052)) 69 | 70 | 71 | 72 | # [1.2.0](https://github.com/asyncLiz/minify-html-literals/compare/v1.1.2...v1.2.0) (2019-02-13) 73 | 74 | ### Features 75 | 76 | - add ability to minify css-tagged templates ([d37a037](https://github.com/asyncLiz/minify-html-literals/commit/d37a037)), closes [#3](https://github.com/asyncLiz/minify-html-literals/issues/3) 77 | 78 | 79 | 80 | ## [1.1.2](https://github.com/asyncLiz/minify-html-literals/compare/v1.1.1...v1.1.2) (2018-11-29) 81 | 82 | ### Bug Fixes 83 | 84 | - update to html-minifier 3.5.21 ([11a9f6b](https://github.com/asyncLiz/minify-html-literals/commit/11a9f6b)) 85 | - **strategy:** error when minifying inline CSS style placeholders [#1](https://github.com/asyncLiz/minify-html-literals/issues/1) ([2226ae2](https://github.com/asyncLiz/minify-html-literals/commit/2226ae2)) 86 | 87 | 88 | 89 | ## [1.1.1](https://github.com/asyncLiz/minify-html-literals/compare/v1.1.0...v1.1.1) (2018-10-25) 90 | 91 | ### Bug Fixes 92 | 93 | - fail to minify with 27 |

\${title}

28 |
    29 | \${items.map(item => { 30 | return getHTML()\` 31 |
  • \${item}
  • 32 | \`; 33 | })} 34 |
35 | \`; 36 | }`, 37 | { 38 | fileName: 'render.js' 39 | } 40 | ); 41 | 42 | console.log(result.code); 43 | // function render(title, items) { 44 | // return html`

${title}

    ${items.map(item => { 45 | // return getHTML()`
  • ${item}
  • `; 46 | // })}
`; 47 | // } 48 | 49 | console.log(result.map); 50 | // { 51 | // "version": 3, 52 | // "file": "render.js.map", 53 | // "sources": ["render.js"], 54 | // "sourcesContent": [null], 55 | // "names": [], 56 | // "mappings": "AAAA;gBACgB,qDAMU,QAAQ,SAE1B;2BACmB,IACX,OAAO,KACb;WACC,KAEP;" 57 | // } 58 | ``` 59 | 60 | ### ES5 Transpiling Warning 61 | 62 | Be sure to minify template literals _before_ transpiling to ES5. Otherwise, the API will not be able to find any template literal (`` `${}` ``) strings. 63 | 64 | ## Supported Source Syntax 65 | 66 | - JavaScript 67 | - TypeScript 68 | 69 | ## Options 70 | 71 | ### Basic 72 | 73 | The following options are common to typical use cases. 74 | 75 | | Property | Type | Default | Description | 76 | | --------------------------- | -------------------------------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 77 | | `fileName` | string | | _Required._ The name of the file, used for syntax parsing and source maps. | 78 | | `minifyOptions?` | [html-minifier options](https://www.npmjs.com/package/html-minifier#options-quick-reference) | `defaultMinifyOptions` | Defaults to production-ready minification. | 79 | | `minifyOptions?.minifyCSS?` | [clean-css options](https://www.npmjs.com/package/clean-css#constructor-options) | `defaultMinifyCSSOptions` | Uses clean-css defaults. | 80 | | `shouldMinify?` | function | `defaultShouldMinify` | A function that determines whether or not an HTML template should be minified. Defaults to minify all tagged templates whose tag name contains "html" (case insensitive). | 81 | | `shouldMinifyCSS?` | function | `defaultShouldMinifyCSS` | A function that determines whether or not a CSS template should be minified. Defaults to minify all tagged templates whose tag name contains "css" (case insensitive). | 82 | 83 | ### Advanced 84 | 85 | All aspects of the API are exposed and customizable. The following options will not typically be used unless you need to change how a certain aspect of the API handles a use case. 86 | 87 | | Property | Type | Default | Description | 88 | | ----------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | 89 | | `generateSourceMap?` | boolean or `(ms: MagicString, fileName: string) => SourceMap | undefined` | `defaultGenerateSourceMap` | Set to `false` to disable source maps, or a custom function to control how source maps are generated from a `MagicString` instance. | 90 | | `strategy?` | object | `defaultStrategy` | An object with methods defining how to minify HTML. The default strategy uses [html-minifier](https://www.npmjs.com/package/html-minifier). | 91 | | `validate?` | boolean or object | `defaultValidation` | Set to `false` to disable strategy validation checks, or to a custom set of validation functions. This is only useful when implementing a custom strategy. | 92 | | `parseLiterals?` | function | [parse-literals](https://www.npmjs.com/package/parse-literals) | Override the function used to parse template literals from a source string. | 93 | | `parseLiteralsOptions?` | object | | Additional options to pass to `parseLiterals()` | 94 | | `MagicString?` | function | [MagicString](https://www.npmjs.com/package/magic-string) | Override the MagicString-like constructor to use for manipulating the source string and generating source maps. | 95 | 96 | ## Customization Examples 97 | 98 | ### Minify non-tagged templates 99 | 100 | > This is particularly useful for libraries that define templates without using tags, such as Polymer's ``. 101 | 102 | ```js 103 | import { minifyHTMLLiterals, defaultShouldMinify } from 'minify-html-literals'; 104 | 105 | minifyHTMLLiterals( 106 | ` 107 | template.innerHTML = \` 108 | 109 | 114 | 115 | \`; 116 | `, 117 | { 118 | fileName: 'render.js', 119 | shouldMinify(template) { 120 | return ( 121 | defaultShouldMinify(template) || 122 | template.parts.some(part => { 123 | return part.text.includes(''); 124 | }) 125 | ); 126 | } 127 | } 128 | ); 129 | ``` 130 | 131 | ### Do not minify CSS 132 | 133 | ```js 134 | import { minifyHTMLLiterals, defaultMinifyOptions } from 'minify-html-literals'; 135 | 136 | minifyHTMLLiterals(source, { 137 | fileName: 'render.js', 138 | minifyOptions: { 139 | ...defaultMinifyOptions, 140 | minifyCSS: false 141 | }, 142 | shouldMinifyCSS: () => false 143 | }); 144 | ``` 145 | 146 | ### Modify generated SourceMap 147 | 148 | ```js 149 | minifyHTMLLiterals(source, { 150 | fileName: 'render.js', 151 | generateSourceMap(ms, fileName) { 152 | return ms.generateMap({ 153 | file: `${fileName}-converted.map`, // change file name 154 | source: fileName, 155 | includeContent: true // include source contents 156 | }); 157 | } 158 | }); 159 | ``` 160 | -------------------------------------------------------------------------------- /src/strategy.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // Reference needed for d.ts distribution files in rollup-plugin-minify-html-literals 3 | import * as CleanCSS from 'clean-css'; 4 | import { 5 | OptimizationLevel, 6 | optimizationLevelFrom 7 | } from 'clean-css/lib/options/optimization-level'; 8 | import { Options as HTMLOptions, minify } from 'html-minifier-terser'; 9 | import { TemplatePart } from 'parse-literals'; 10 | 11 | /** 12 | * A strategy on how to minify HTML and optionally CSS. 13 | * 14 | * @template O minify HTML options 15 | * @template C minify CSS options 16 | */ 17 | export interface Strategy { 18 | /** 19 | * Retrieve a placeholder for the given array of template parts. The 20 | * placeholder returned should be the same if the function is invoked with the 21 | * same array of parts. 22 | * 23 | * The placeholder should be an HTML-compliant string that is not present in 24 | * any of the parts' text. 25 | * 26 | * @param parts the parts to get a placeholder for 27 | * @returns the placeholder 28 | */ 29 | getPlaceholder(parts: TemplatePart[]): string; 30 | /** 31 | * Combines the parts' HTML text strings together into a single string using 32 | * the provided placeholder. The placeholder indicates where a template 33 | * expression occurs. 34 | * 35 | * @param parts the parts to combine 36 | * @param placeholder the placeholder to use between parts 37 | * @returns the combined parts' text strings 38 | */ 39 | combineHTMLStrings(parts: TemplatePart[], placeholder: string): string; 40 | /** 41 | * Minifies the provided HTML string. 42 | * 43 | * @param html the html to minify 44 | * @param options html minify options 45 | * @returns minified HTML string 46 | */ 47 | minifyHTML(html: string, options?: O): Promise; 48 | /** 49 | * Minifies the provided CSS string. 50 | * 51 | * @param css the css to minfiy 52 | * @param options css minify options 53 | * @returns minified CSS string 54 | */ 55 | minifyCSS?(css: string, options?: C): string; 56 | /** 57 | * Splits a minified HTML string back into an array of strings from the 58 | * provided placeholder. The returned array of strings should be the same 59 | * length as the template parts that were combined to make the HTML string. 60 | * 61 | * @param html the html string to split 62 | * @param placeholder the placeholder to split by 63 | * @returns an array of html strings 64 | */ 65 | splitHTMLByPlaceholder(html: string, placeholder: string): string[]; 66 | } 67 | 68 | /** 69 | * The default clean-css options, optimized for production 70 | * minification. 71 | */ 72 | export const defaultMinifyCSSOptions: CleanCSS.Options = {}; 73 | 74 | /** 75 | * The default html-minifier options, optimized for production 76 | * minification. 77 | */ 78 | export const defaultMinifyOptions: HTMLOptions = { 79 | caseSensitive: true, 80 | collapseWhitespace: true, 81 | decodeEntities: true, 82 | minifyCSS: defaultMinifyCSSOptions, 83 | minifyJS: true, 84 | processConditionalComments: true, 85 | removeAttributeQuotes: false, 86 | removeComments: true, 87 | removeEmptyAttributes: true, 88 | removeScriptTypeAttributes: true, 89 | removeStyleLinkTypeAttributes: true, 90 | useShortDoctype: true 91 | }; 92 | 93 | /** 94 | * The default strategy. This uses html-minifier to minify HTML and 95 | * clean-css to minify CSS. 96 | */ 97 | export const defaultStrategy: Strategy = { 98 | getPlaceholder(parts) { 99 | // Using @ and (); will cause the expression not to be removed in CSS. 100 | // However, sometimes the semicolon can be removed (ex: inline styles). 101 | // In those cases, we want to make sure that the HTML splitting also 102 | // accounts for the missing semicolon. 103 | const suffix = '();'; 104 | let placeholder = '@TEMPLATE_EXPRESSION'; 105 | while (parts.some(part => part.text.includes(placeholder + suffix))) { 106 | placeholder += '_'; 107 | } 108 | 109 | return placeholder + suffix; 110 | }, 111 | combineHTMLStrings(parts, placeholder) { 112 | return parts.map(part => part.text).join(placeholder); 113 | }, 114 | minifyHTML: async function(html, options = {}) { 115 | let minifyCSSOptions: HTMLOptions['minifyCSS']; 116 | if (options.minifyCSS) { 117 | if ( 118 | options.minifyCSS !== true && 119 | typeof options.minifyCSS !== 'function' 120 | ) { 121 | minifyCSSOptions = { ...options.minifyCSS }; 122 | } else { 123 | minifyCSSOptions = {}; 124 | } 125 | } else { 126 | minifyCSSOptions = false; 127 | } 128 | 129 | let adjustedMinifyCSSOptions: 130 | | false 131 | | ReturnType = false; 132 | if (minifyCSSOptions) { 133 | adjustedMinifyCSSOptions = adjustMinifyCSSOptions(minifyCSSOptions); 134 | } 135 | 136 | let result = await minify(html, { 137 | ...options, 138 | minifyCSS: adjustedMinifyCSSOptions 139 | }); 140 | 141 | if (options.collapseWhitespace) { 142 | // html-minifier does not support removing newlines inside 143 | // attributes. Support this, but be careful not to remove newlines from 144 | // supported areas (such as within
 and