├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── LOADER.md ├── README.md ├── lib ├── Error.js ├── index.js ├── options.js └── options.json ├── package.json └── test ├── Errors.test.js ├── __snapshots__ ├── Errors.test.js.snap └── loader.test.js.snap ├── fixtures ├── fixture.html ├── fixture.js ├── options │ ├── config │ │ └── posthtml.config.js │ ├── directives │ │ ├── fixture.html │ │ └── fixture.js │ ├── parser │ │ ├── fixture.js │ │ └── fixture.ssml │ └── render │ │ ├── fixture.html │ │ └── fixture.js ├── plugin.js └── posthtml.config.js ├── helpers └── compiler.js ├── loader.test.js └── options ├── __snapshots__ ├── config.test.js.snap ├── directives.test.js.snap ├── parser.test.js.snap ├── plugins.test.js.snap └── render.test.js.snap ├── config.test.js ├── directives.test.js ├── parser.test.js ├── plugins.test.js └── render.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | end_of_line = lf 7 | indent_style = space 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @michael-ciniawsky @GitScrum 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Briefly describe the issue you are experiencing (or the feature you want to see 2 | added to the plugin). Tell us what you were trying to do and what happened 3 | instead. Remember, this is _not_ a place to ask questions. For that, go to 4 | http://gitter.im/posthtml/posthtml 5 | 6 | ### `📝 Details` 7 | 8 | > Describe in more detail the problem you have been experiencing, if necessary. 9 | 10 | ### `❌ Error (Logs|Stacks)` 11 | 12 | > Create a [gist](https://gist.github.com) which is a paste of your **full** 13 | logs, and link them here. 14 | 15 | > ⚠️ Do **not** paste your full logs here (or at least hide them by using a `
` block), as it will make this issue long and hard 16 | to read! If you are reporting a bug, **always** include logs! 17 | 18 | ### `♻️ Reproduction (Code)` 19 | 20 | > :warning: Please remember that, with sample code; it's easier to reproduce a bug and much 21 | faster to fix it. 22 | 23 | > 🔗 Please refer to a simple code example. 24 | 25 | ```bash 26 | $ git clone https://github.com// 27 | ``` 28 | 29 | ### `🌐 Environment` 30 | 31 | > ℹ️ Please provide information about your current environment. 32 | 33 | |OS|node|npm/yarn|package| 34 | |:-:|:--:|:-:|:------:| 35 | |[name][version]|[version]|[version]|[version]| 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### `Notable Changes` 2 | 3 | > ✏️ Describe the big picture of your changes here to communicate to the maintainers 4 | why we should accept this pull request. If it fixes a bug or resolves a feature 5 | request, be sure to link to that issue down below. 6 | 7 | #### `Commit Message Summary (CHANGELOG)` 8 | 9 | ``` 10 | commit message body... 11 | ``` 12 | 13 | ### `Type` 14 | 15 | > ℹ️ What types of changes does your code introduce? 16 | 17 | > 👉 _Put an `x` in the boxes that apply and delete all others_ 18 | 19 | - [ ] CI 20 | - [ ] Fix 21 | - [ ] Perf 22 | - [ ] Docs 23 | - [ ] Test 24 | - [ ] Chore 25 | - [ ] Style 26 | - [ ] Build 27 | - [ ] Feature 28 | - [ ] Refactor 29 | 30 | ### `SemVer` 31 | 32 | > ℹ️ What changes to the current `semver` range does your PR introduce? 33 | 34 | > 👉 _Put an `x` in the boxes that apply and delete all others_ 35 | 36 | - [ ] Bug (:label: Patch) 37 | - [ ] Feature (:label: Minor) 38 | - [ ] Breaking Change (:label: Major) 39 | 40 | ### `Issues` 41 | 42 | > ℹ️ What issue (if any) are closed by your PR? 43 | 44 | > 👉 _Replace `#1` with the issue number that applies and remove the ``` ` ```_ 45 | 46 | - Fixes `#1` 47 | 48 | ### `Checklist` 49 | 50 | > ℹ️ You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. This is a reminder of what we are going to look for before merging your code. 51 | 52 | > 👉 _Put an `x` in the boxes that apply and delete all others._ 53 | 54 | - [ ] I have [read and sign the CLA](https://cla.js.foundation/webpack/webpack.js.org) 55 | - [ ] I checked out the [development guide](https://webpack.js.org/development/) for API and development guidelines 56 | - [ ] Lint and unit tests pass with my changes 57 | - [ ] I have added tests that prove my fix is effective/works 58 | - [ ] I have added necessary documentation (if appropriate) 59 | - [ ] Any dependent changes are merged and published in downstream modules 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | 3 | .DS_Store 4 | ._* 5 | 6 | # NODEJS 7 | 8 | npm-debug.log 9 | 10 | dmd 11 | jest 12 | coverage 13 | jsdoc-api 14 | node_modules 15 | .nyc_output 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # FILES 2 | 3 | .travis.yml 4 | .gitignore 5 | .editorconfig 6 | 7 | npm-debug.log 8 | 9 | # DIRECTORIES 10 | 11 | .github 12 | 13 | dmd 14 | test 15 | jest 16 | coverage 17 | jsdoc-api 18 | node_modules 19 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | - lts/* 6 | - 10 7 | 8 | after_success: 9 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 10 | 11 | notifications: 12 | email: false 13 | -------------------------------------------------------------------------------- /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.0.2](https://github.com/posthtml/posthtml-loader/compare/v1.0.1...v1.0.2) (2019-10-24) 6 | 7 | 8 | ## [1.0.1](https://github.com/posthtml/posthtml-loader/compare/v1.0.0...v1.0.1) (2017-12-18) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * **package:** point to the correct entry in `main` (`pkg.main`) ([efa672e](https://github.com/posthtml/posthtml-loader/commit/efa672e)) 14 | 15 | 16 | 17 | 18 | # [1.0.0](https://github.com/posthtml/posthtml-loader/compare/v0.10.3...v1.0.0) (2017-12-16) 19 | 20 | 21 | ### Bug Fixes 22 | 23 | * **Error:** add missing `'use strict'` pragma ([1316255](https://github.com/posthtml/posthtml-loader/commit/1316255)) 24 | 25 | 26 | ### Features 27 | 28 | * add parser query option ([6650acc](https://github.com/posthtml/posthtml-loader/commit/6650acc)) 29 | * **index:** add `options` validation (`schema-utils`) ([7bd5896](https://github.com/posthtml/posthtml-loader/commit/7bd5896)) 30 | * **index:** support `posthtml.config.js` && `result.messages` ([e05b44c](https://github.com/posthtml/posthtml-loader/commit/e05b44c)) 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | You want to help? You rock! Now, take a moment to be sure your contributions make sense to everyone else. 2 | 3 | ## Reporting Issues 4 | 5 | Found a problem? Want a new feature? 6 | 7 | - See if your issue or idea has [already been reported]. 8 | - Provide a [reduced test case] or a [live example]. 9 | 10 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 11 | 12 | ## Submitting Pull Requests 13 | 14 | Pull requests are the greatest contributions, so be sure they are focused in scope, and do avoid unrelated commits. 15 | 16 | 1. To begin, [fork this project], clone your fork, and add our upstream. 17 | ```bash 18 | # Clone your fork of the repo into the current directory 19 | git clone https://github.com//PLUGIN_NAME 20 | # Navigate to the newly cloned directory 21 | cd PLUGIN_NAME 22 | # Assign the original repo to a remote called "upstream" 23 | git remote add upstream https://github.com/GITHUB_NAME/PLUGIN_NAME 24 | # Install the tools necessary for development 25 | npm install 26 | ``` 27 | 28 | 2. Create a branch for your feature or fix: 29 | ```bash 30 | # Move into a new branch for a feature 31 | git checkout -b feature/thing 32 | ``` 33 | ```bash 34 | # Move into a new branch for a fix 35 | git checkout -b fix/something 36 | ``` 37 | 38 | 3. Be sure your code follows our practices. 39 | ```bash 40 | # Test current code 41 | npm run test 42 | ``` 43 | 44 | 4. Push your branch up to your fork: 45 | ```bash 46 | # Push a feature branch 47 | git push origin feature/thing 48 | ``` 49 | ```bash 50 | # Push a fix branch 51 | git push origin fix/something 52 | ``` 53 | 54 | 5. Now [open a pull request] with a clear title and description. 55 | 56 | [already been reported]: issues 57 | [fork this project]: fork 58 | [live example]: http://codepen.io/pen 59 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 60 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LOADER.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## posthtml-loader(html) ⇒ String 4 | PostHTML Loader 5 | 6 | **Kind**: global function 7 | **Returns**: String - html HTML 8 | **Requires**: module:loader-utils, module:schema-utils, module:posthtml, module:posthtml-load-config 9 | **Version**: 1.0.0 10 | **Author**: Michael Ciniawsky (@michael-ciniawsky) 11 | **License**: MIT 12 | 13 | | Param | Type | Description | 14 | | --- | --- | --- | 15 | | html | String | HTML | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm][npm]][npm-url] 2 | [![node][node]][node-url] 3 | [![deps][deps]][deps-url] 4 | [![tests][tests]][tests-url] 5 | [![coverage][cover]][cover-url] 6 | [![code style][style]][style-url] 7 | [![chat][chat]][chat-url] 8 | 9 |
10 | 11 | 12 | 13 | 14 |

PostHTML Loader

15 |
16 | 17 |

Install

18 | 19 | ```bash 20 | npm i -D posthtml-loader 21 | ``` 22 | 23 |

Usage

24 | 25 | ```js 26 | import html from './file.html' 27 | ``` 28 | 29 | **webpack.config.js** 30 | ```js 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.html$/, 35 | use: [ 36 | 'html-loader', 37 | { 38 | loader: 'posthtml-loader', 39 | options: { 40 | ident: 'posthtml', 41 | parser: 'PostHTML Parser', 42 | plugins: [ 43 | /* PostHTML Plugins */ 44 | require('posthtml-plugin')(options) 45 | ] 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | }, 52 | ``` 53 | 54 |

Options

55 | 56 | |Name|Type|Default|Description| 57 | |:--:|:--:|:-----:|:----------| 58 | |**[`config`](#config)**|`{Object}`|`undefined`|PostHTML Config| 59 | |**[`parser`](#parser)**|`{String/Function}`|`undefined`|PostHTML Parser| 60 | |**[`skipParse`](#skipParse)**|`{Boolean}`|`false`|PostHTML Options SkipParse| 61 | |**[`render`](#render)**|`{String/Function}`|`undefined`|PostHTML Render| 62 | |**[`plugins`](#plugins)**|`{Array/Function}`|`[]`|PostHTML Plugins| 63 | |**[`sync`](#sync)**|`{boolean}`|`false`|PostHTML Options Sync| 64 | |**[`directives`](#directives)**|`{Array}`|`[]`|PostHTML Options custom [Directives](https://github.com/posthtml/posthtml-parser#directives)| 65 | 66 | ### `Config` 67 | 68 | |Name|Type|Default|Description| 69 | |:--:|:--:|:-----:|:----------| 70 | |**[`path`](#path)**|`{String}`|`loader.resourcePath`|PostHTML Config Path| 71 | |**[`ctx`](#context)**|`{Object}`|`{}`|PostHTML Config Context| 72 | 73 | 74 | If you want to use are shareable config file instead of inline options in your `webpack.config.js` create a `posthtml.config.js` file and place it somewhere down the file tree in your project. The nearest config relative to `dirname(file)` currently processed by the loader applies. This enables **Config Cascading**. Despite some edge cases the config file will be loaded automatically and **no** additional setup is required. If you don't intend to use Config Cascading, it's recommended to place `posthtml.config.js` in the **root** `./` of your project 75 | 76 | ``` 77 | src 78 | ├── components 79 | │   ├── component.html 80 | │   ├── posthtml.config.js (components) 81 | ├── index.html 82 | ├── posthtml.config.js (index) 83 | └── webpack.config.js 84 | ``` 85 | 86 | #### `Path` 87 | 88 | If you normally place all your config files in a separate folder e.g `./config` it is necessary to explicitly set the config path in `webpack.config.js` 89 | 90 | **webpack.config.js** 91 | ```js 92 | { 93 | loader: 'posthtml-loader', 94 | options: { 95 | config: { 96 | path: 'path/to/.config/' 97 | } 98 | } 99 | } 100 | ``` 101 | 102 | #### `Context` 103 | 104 | |Name|Type|Default|Description| 105 | |:--:|:--:|:-----:|:----------| 106 | |`env`|`{String}`|`'development'`|process.env.NODE_ENV| 107 | |`file`|`{Object}`|`{ dirname, basename, extname }`|File| 108 | |`options`|`{Object}`|`{}`|Plugin Options (Context)| 109 | 110 | [**posthtml.config.js**](https://github.com/posthtml/posthtml-load-config) 111 | ```js 112 | module.exports = ({ file, options, env }) => ({ 113 | parser: 'posthtml-sugarml', 114 | plugins: { 115 | 'posthtml-include': options.include, 116 | 'posthtml-content': options.content, 117 | 'htmlnano': env === 'production' ? {} : false 118 | } 119 | }) 120 | ``` 121 | 122 | **webpack.config.js** 123 | ```js 124 | { 125 | loader: 'posthtml-loader', 126 | options: { 127 | config: { 128 | ctx: { 129 | include: {...options}, 130 | content: {...options} 131 | } 132 | } 133 | } 134 | } 135 | ``` 136 | 137 | ### `Parser` 138 | 139 | If you want to use a custom parser e.g [SugarML](https://github.com/posthtml/sugarml), you can pass it in under the `parser` key in the loader options 140 | 141 | #### `{String}` 142 | 143 | **webpack.config.js** 144 | ```js 145 | { 146 | loader: 'posthtml-loader', 147 | options: { 148 | parser: 'posthtml-sugarml' 149 | } 150 | } 151 | ``` 152 | 153 | #### `{Function}` 154 | 155 | **webpack.config.js** 156 | ```js 157 | { 158 | loader: 'posthtml-loader', 159 | options: { 160 | parser: require('posthtml-sugarml')() 161 | } 162 | } 163 | ``` 164 | 165 | ### `skipParse` 166 | 167 | If you want to use disable parsing, you can pass it in under the `skipParse` key in the loader options 168 | 169 | #### `{Boolean}` 170 | 171 | **webpack.config.js** 172 | ```js 173 | { 174 | loader: 'posthtml-loader', 175 | options: { 176 | skipParse: false 177 | } 178 | } 179 | ``` 180 | 181 | ### `Render` 182 | 183 | If you want to use a custom render, you can pass it in under the `render` key in the loader options 184 | 185 | #### `{String}` 186 | 187 | **webpack.config.js** 188 | ```js 189 | { 190 | loader: 'posthtml-loader', 191 | options: { 192 | render: 'posthtml-you-render' 193 | } 194 | } 195 | ``` 196 | 197 | #### `{Function}` 198 | 199 | **webpack.config.js** 200 | ```js 201 | { 202 | loader: 'posthtml-loader', 203 | options: { 204 | parser: require('posthtml-you-render')() 205 | } 206 | } 207 | ``` 208 | 209 | ### `Plugins` 210 | 211 | Plugins are specified under the `plugins` key in the loader options 212 | 213 | #### `{Array}` 214 | 215 | **webpack.config.js** 216 | ```js 217 | { 218 | loader: 'posthtml-loader', 219 | options: { 220 | plugins: [ 221 | require('posthtml-plugin')() 222 | ] 223 | } 224 | } 225 | ``` 226 | 227 | #### `{Function}` 228 | 229 | **webpack.config.js** 230 | ```js 231 | { 232 | loader: 'posthtml-loader', 233 | options: { 234 | plugins (loader) { 235 | return [ 236 | require('posthtml-plugin')() 237 | ] 238 | } 239 | } 240 | } 241 | ``` 242 | 243 | ### `Sync` 244 | 245 | Enables sync mode, plugins will run synchronously, throws an error when used with async plugins 246 | 247 | #### `{Boolean}` 248 | 249 | **webpack.config.js** 250 | ```js 251 | { 252 | loader: 'posthtml-loader', 253 | options: { 254 | sync: true 255 | } 256 | } 257 | ``` 258 | 259 | ### `Directives` 260 | 261 | If you want to use a custom directives, you can pass it in under the `directives` key in the loader options 262 | 263 | #### `{Array}` 264 | 265 | **webpack.config.js** 266 | ```js 267 | { 268 | loader: 'posthtml-loader', 269 | options: { 270 | directives: [{name: '?php', start: '<', end: '>'}] 271 | } 272 | } 273 | ``` 274 | 275 |

Maintainer

276 | 277 | 278 | 279 | 280 | 286 | 291 | 292 | 293 |
281 | 283 |
284 | Michael Ciniawsky 285 |
287 | 288 |
289 | Ivan Demidov 290 |
294 | 295 | 296 | [npm]: https://img.shields.io/npm/v/posthtml-loader.svg 297 | [npm-url]: https://npmjs.com/package/posthtml-loader 298 | 299 | [node]: https://img.shields.io/node/v/posthtml-loader.svg 300 | [node-url]: https://nodejs.org/ 301 | 302 | [deps]: https://david-dm.org/posthtml/posthtml-loader.svg 303 | [deps-url]: https://david-dm.org/posthtml/posthtml-loader 304 | 305 | [tests]: http://img.shields.io/travis/posthtml/posthtml-loader.svg 306 | [tests-url]: https://travis-ci.org/posthtml/posthtml-loader 307 | 308 | [cover]: https://coveralls.io/repos/github/posthtml/posthtml-loader/badge.svg 309 | [cover-url]: https://coveralls.io/github/posthtml/posthtml-loader 310 | 311 | [style]: https://img.shields.io/badge/code%20style-standard-yellow.svg 312 | [style-url]: http://standardjs.com/ 313 | 314 | [chat]: https://badges.gitter.im/posthtml/posthtml.svg 315 | [chat-url]: https://gitter.im/posthtml/posthtml 316 | -------------------------------------------------------------------------------- /lib/Error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class LoaderError extends Error { 4 | constructor (err) { 5 | super(err) 6 | 7 | this.name = 'PostHTML Loader' 8 | this.message = `\n\n${err.message}\n` 9 | 10 | // TODO(michael-ciniawsky) 11 | // add 'SyntaxError', 'PluginError', 'PluginWarning' 12 | 13 | Error.captureStackTrace(this, this.constructor) 14 | } 15 | } 16 | 17 | module.exports = LoaderError 18 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | 5 | const loaderUtils = require('loader-utils') 6 | const validateOptions = require('schema-utils') 7 | 8 | const schema = require('./options.json') 9 | 10 | const posthtml = require('posthtml') 11 | const posthtmlrc = require('posthtml-load-config') 12 | 13 | const parseOptions = require('./options') 14 | 15 | const LoaderError = require('./Error') 16 | /** 17 | * PostHTML Loader 18 | * 19 | * @author Michael Ciniawsky (@michael-ciniawsky) 20 | * @license MIT 21 | * 22 | * @version 1.0.0 23 | * 24 | * @requires loader-utils 25 | * @requires schema-utils 26 | * 27 | * @requires posthtml 28 | * @requires posthtml-load-config 29 | * 30 | * @method posthtml-loader 31 | * 32 | * @param {String} html HTML 33 | * 34 | * @return {String} html HTML 35 | */ 36 | module.exports = function loader (html, map, meta) { 37 | // Loader Options 38 | const options = loaderUtils.getOptions(this) || {} 39 | 40 | validateOptions(schema, options, { name: 'PostHTML Loader', baseDataPath: 'options' }) 41 | 42 | // Make the loader async 43 | const cb = this.async() 44 | const file = this.resourcePath 45 | 46 | Promise.resolve().then(() => { 47 | const length = Object.keys(options) 48 | .filter((option) => { 49 | switch (option) { 50 | case 'ident': 51 | case 'config': 52 | return 53 | default: 54 | return option 55 | } 56 | }) 57 | .length 58 | 59 | if (length) { 60 | return parseOptions.call(this, options) 61 | } 62 | 63 | const rc = { 64 | path: path.dirname(file), 65 | ctx: { 66 | file: { 67 | extname: path.extname(file), 68 | dirname: path.dirname(file), 69 | basename: path.basename(file) 70 | }, 71 | options: {} 72 | } 73 | } 74 | 75 | if (options.config) { 76 | if (options.config.path) { 77 | rc.path = path.resolve(options.config.path) 78 | } 79 | 80 | if (options.config.ctx) { 81 | rc.ctx.options = options.config.ctx 82 | } 83 | } 84 | 85 | return posthtmlrc(rc.ctx, rc.path, { argv: false }) 86 | }) 87 | .then((config) => { 88 | if (!config) config = {} 89 | 90 | if (config.file) this.addDependency(config.file) 91 | 92 | if (config.options) { 93 | // Disable overriding `options.to` (`posthtml.config.js`) 94 | if (config.options.to) delete config.options.to 95 | // Disable overriding `options.from` (`posthtml.config.js`) 96 | if (config.options.from) delete config.options.from 97 | } 98 | 99 | const plugins = config.plugins || [] 100 | const options = Object.assign( 101 | { from: file, to: file }, 102 | config.options 103 | ) 104 | 105 | if (typeof options.parser === 'string') { 106 | options.parser = require(options.parser)() 107 | } 108 | 109 | if (typeof options.render === 'string') { 110 | options.render = require(options.render)() 111 | } 112 | 113 | return posthtml(plugins) 114 | .process(html, options) 115 | .then((result) => { 116 | if (result.messages) { 117 | result.messages.forEach((msg) => { 118 | switch (msg.type) { 119 | case 'error': 120 | this.emitError(msg.message) 121 | 122 | break 123 | case 'warning': 124 | this.emitWarning(msg.message) 125 | 126 | break 127 | case 'dependency': 128 | this.addDependency(msg.file) 129 | 130 | break 131 | default: 132 | break 133 | } 134 | }) 135 | } 136 | 137 | html = result.html 138 | 139 | if (this.loaderIndex === 0) { 140 | html = `export default \`${html}\`` 141 | 142 | cb(null, html) 143 | 144 | return null 145 | } 146 | 147 | if (!meta) meta = {} 148 | 149 | meta.ast = { type: 'posthtml', root: result.tree } 150 | meta.messages = result.messages 151 | 152 | cb(null, html, map, meta) 153 | 154 | return null 155 | }) 156 | }) 157 | .catch((err) => { 158 | cb(new LoaderError(err)) 159 | 160 | return null 161 | }) 162 | } 163 | -------------------------------------------------------------------------------- /lib/options.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function parseOptions (params) { 4 | let { plugins, ...options } = params 5 | 6 | if (typeof plugins === 'function') { 7 | plugins = plugins.call(this, this) 8 | } 9 | 10 | if (typeof plugins === 'undefined') plugins = [] 11 | else if (!Array.isArray(plugins)) plugins = [plugins] 12 | 13 | return Promise.resolve({ options, plugins }) 14 | } 15 | -------------------------------------------------------------------------------- /lib/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "sync": { 5 | "type": "boolean" 6 | }, 7 | "directives": { 8 | "type": "array", 9 | "items": { 10 | "type": "object", 11 | "properties": { 12 | "name": { 13 | "type": "string" 14 | }, 15 | "start": { 16 | "type": "string" 17 | }, 18 | "end": { 19 | "type": "string" 20 | } 21 | } 22 | } 23 | }, 24 | "skipParse": { 25 | "type": "boolean" 26 | }, 27 | "ident": { 28 | "type": "string" 29 | }, 30 | "config": { 31 | "type": "object", 32 | "properties": { 33 | "path": { 34 | "type": "string" 35 | }, 36 | "ctx": { 37 | "type": "object" 38 | } 39 | }, 40 | "additionalProperties": false 41 | }, 42 | "parser": { 43 | "oneOf": [ 44 | { "type": "string" }, 45 | { "type": "object" }, 46 | { "instanceof": "Function" } 47 | ] 48 | }, 49 | "render": { 50 | "oneOf": [ 51 | { "type": "string" }, 52 | { "type": "object" }, 53 | { "instanceof": "Function" } 54 | ] 55 | }, 56 | "plugins": { 57 | "oneOf": [ 58 | { "type": "array" }, 59 | { "type": "object" }, 60 | { "instanceof": "Function" } 61 | ] 62 | } 63 | }, 64 | "additionalProperties": false 65 | } 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "posthtml-loader", 3 | "description": "PostHTML for Webpack", 4 | "version": "2.0.1", 5 | "main": "lib/index.js", 6 | "engines": { 7 | "node": ">= 10" 8 | }, 9 | "files": [ 10 | "lib" 11 | ], 12 | "scripts": { 13 | "lint": "standard --env jest", 14 | "clean": "rm -rf jest coverage jsdoc-api dmd test/builds", 15 | "pretest": "npm run clean", 16 | "test": "jest --verbose --coverage", 17 | "docs": "jsdoc2md lib/index.js > LOADER.md", 18 | "release": "standard-version" 19 | }, 20 | "dependencies": { 21 | "loader-utils": "^2.0.0", 22 | "posthtml": "^0.13.3", 23 | "posthtml-load-config": "^2.0.0", 24 | "schema-utils": "^2.5.0" 25 | }, 26 | "devDependencies": { 27 | "coveralls": "^3.0.7", 28 | "del": "^5.1.0", 29 | "jest": "^26.4.2", 30 | "jsdoc-to-markdown": "^6.0.1", 31 | "memory-fs": "^0.5.0", 32 | "posthtml-sugarml": "1.0.0-alpha3", 33 | "standard": "^14.3.1", 34 | "standard-version": "^9.0.0", 35 | "webpack": "^4.41.2" 36 | }, 37 | "keywords": [ 38 | "HTML", 39 | "Loader", 40 | "PostHTML", 41 | "Webpack" 42 | ], 43 | "author": "Michael Ciniawsky (@michael-ciniawsky)", 44 | "contributors": [ 45 | { 46 | "name": "Ivan Demidov", 47 | "email": "Scrum@list.ru", 48 | "url": "https://twitter.com/Scrum_" 49 | } 50 | ], 51 | "repository": "https://github.com/posthtml/posthtml-loader.git", 52 | "bugs": "https://github.com/posthtml/posthtml-loader/issues", 53 | "homepage": "https://github.com/posthtml/posthtml-loader", 54 | "license": "MIT" 55 | } 56 | -------------------------------------------------------------------------------- /test/Errors.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | describe('Errors', () => { 4 | test('Validation Error', () => { 5 | const loader = require('../lib') 6 | 7 | const error = () => loader.call({ query: { plugins: 1 } }) 8 | 9 | expect(error).toThrow() 10 | expect(error).toThrowErrorMatchingSnapshot() 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /test/__snapshots__/Errors.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Errors Validation Error 1`] = ` 4 | "Invalid options object. PostHTML Loader has been initialized using an options object that does not match the API schema. 5 | - options.plugins should be one of these: 6 | [any, ...] | object { … } | function 7 | Details: 8 | * options.plugins should be an array: 9 | [any, ...] 10 | * options.plugins should be an object: 11 | object { … } 12 | * options.plugins should be an instance of function." 13 | `; 14 | -------------------------------------------------------------------------------- /test/__snapshots__/loader.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Loader Defaults 1`] = ` 4 | "export default \`
Hello
5 | \`" 6 | `; 7 | -------------------------------------------------------------------------------- /test/fixtures/fixture.html: -------------------------------------------------------------------------------- 1 |
Hello
2 | -------------------------------------------------------------------------------- /test/fixtures/fixture.js: -------------------------------------------------------------------------------- 1 | import html from './fixture.html' // eslint-disable-line 2 | -------------------------------------------------------------------------------- /test/fixtures/options/config/posthtml.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (ctx) => ({ 2 | to: 'delete.html', 3 | from: 'delete.html', 4 | plugins: [ 5 | ctx.options.plugin ? require('../../plugin')() : false 6 | ] 7 | }) 8 | -------------------------------------------------------------------------------- /test/fixtures/options/directives/fixture.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/options/directives/fixture.js: -------------------------------------------------------------------------------- 1 | import html from './fixture.html' // eslint-disable-line 2 | -------------------------------------------------------------------------------- /test/fixtures/options/parser/fixture.js: -------------------------------------------------------------------------------- 1 | import html from './fixture.ssml' // eslint-disable-line 2 | -------------------------------------------------------------------------------- /test/fixtures/options/parser/fixture.ssml: -------------------------------------------------------------------------------- 1 | div Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/options/render/fixture.html: -------------------------------------------------------------------------------- 1 | Hello -------------------------------------------------------------------------------- /test/fixtures/options/render/fixture.js: -------------------------------------------------------------------------------- 1 | import html from './fixture.html' // eslint-disable-line 2 | -------------------------------------------------------------------------------- /test/fixtures/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function plugin (options) { 4 | options = Object.assign({}, options) 5 | 6 | return function (tree) { 7 | tree.walk((node) => { 8 | if (node.tag === 'div') node.tag = 'section' 9 | 10 | return node 11 | }) 12 | 13 | return tree 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/posthtml.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('./plugin')() 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/helpers/compiler.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const del = require('del') 5 | const webpack = require('webpack') 6 | const MemoryFS = require('memory-fs') 7 | 8 | const optimization = (config) => { 9 | return { 10 | splitChunks: { 11 | minChunks: Infinity 12 | } 13 | } 14 | } 15 | 16 | const modules = (config) => { 17 | return { 18 | rules: config.rules 19 | ? config.rules 20 | : config.loader 21 | ? [ 22 | { 23 | test: config.loader.test || /\.txt$/, 24 | use: { 25 | loader: path.resolve(__dirname, '../../lib'), 26 | options: config.loader.options || {} 27 | } 28 | } 29 | ] 30 | : [] 31 | } 32 | } 33 | 34 | const plugins = config => ([].concat(config.plugins || [])) 35 | 36 | const output = (config) => { 37 | return { 38 | path: path.resolve( 39 | __dirname, 40 | `../outputs/${config.output ? config.output : ''}` 41 | ), 42 | filename: '[name].js', 43 | chunkFilename: '[name].chunk.js' 44 | } 45 | } 46 | 47 | module.exports = function (fixture, config, options) { 48 | config = { 49 | mode: 'development', 50 | devtool: config.devtool || 'sourcemap', 51 | context: path.resolve(__dirname, '..', 'fixtures'), 52 | entry: `./${fixture}`, 53 | output: output(config), 54 | optimization: optimization(config), 55 | module: modules(config), 56 | plugins: plugins(config) 57 | } 58 | 59 | options = Object.assign({ output: false }, options) 60 | 61 | if (options.output) del.sync(config.output.path) 62 | 63 | const compiler = webpack(config) 64 | 65 | if (!options.output) compiler.outputFileSystem = new MemoryFS() 66 | 67 | return new Promise((resolve, reject) => compiler.run((err, stats) => { 68 | if (err) reject(err) 69 | 70 | resolve(stats) 71 | })) 72 | } 73 | -------------------------------------------------------------------------------- /test/loader.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('./helpers/compiler') 4 | 5 | describe('Loader', () => { 6 | test('Defaults', () => { 7 | const config = { 8 | loader: { 9 | test: /\.html$/, 10 | options: {} 11 | } 12 | } 13 | 14 | return webpack('fixture.js', config) 15 | .then((stats) => { 16 | const [module] = stats.toJson().modules 17 | 18 | expect(module.source).toMatchSnapshot() 19 | }) 20 | .catch((err) => err) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/options/__snapshots__/config.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options config ctx - {Object} 1`] = ` 4 | "export default \`
Hello
5 | \`" 6 | `; 7 | 8 | exports[`Options config path - {String} 1`] = ` 9 | "export default \`
Hello
10 | \`" 11 | `; 12 | -------------------------------------------------------------------------------- /test/options/__snapshots__/directives.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options directives {Array} 1`] = `"export default \`\`"`; 4 | -------------------------------------------------------------------------------- /test/options/__snapshots__/parser.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options parser {Object} 1`] = `"export default \`
Hello
\`"`; 4 | 5 | exports[`Options parser {String} 1`] = `"export default \`
Hello
\`"`; 6 | -------------------------------------------------------------------------------- /test/options/__snapshots__/plugins.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options plugins {Array} 1`] = ` 4 | "export default \`
Hello
5 | \`" 6 | `; 7 | 8 | exports[`Options plugins {Function} - {Array} 1`] = ` 9 | "export default \`
Hello
10 | \`" 11 | `; 12 | 13 | exports[`Options plugins {Function} - {Object} 1`] = ` 14 | "export default \`
Hello
15 | \`" 16 | `; 17 | -------------------------------------------------------------------------------- /test/options/__snapshots__/render.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options render {Object} 1`] = `"export default \`Hello\`"`; 4 | 5 | exports[`Options render {String} 1`] = `"export default \`Hello\`"`; 6 | -------------------------------------------------------------------------------- /test/options/config.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('../helpers/compiler') 4 | 5 | describe('Options', () => { 6 | describe('config', () => { 7 | test('path - {String}', () => { 8 | const config = { 9 | loader: { 10 | test: /\.html$/, 11 | options: { 12 | config: { 13 | path: 'test/fixtures/posthtml.config.js' 14 | } 15 | } 16 | } 17 | } 18 | 19 | return webpack('fixture.js', config) 20 | .then((stats) => { 21 | const [module] = stats.toJson().modules 22 | 23 | expect(module.source).toMatchSnapshot() 24 | }) 25 | .catch((err) => err) 26 | }) 27 | 28 | test('ctx - {Object}', () => { 29 | const config = { 30 | loader: { 31 | test: /\.html$/, 32 | options: { 33 | config: { 34 | path: 'test/fixtures/options/config/posthtml.config.js', 35 | ctx: { plugin: true } 36 | } 37 | } 38 | } 39 | } 40 | 41 | return webpack('fixture.js', config) 42 | .then((stats) => { 43 | const [module] = stats.toJson().modules 44 | 45 | expect(module.source).toMatchSnapshot() 46 | }) 47 | .catch((err) => err) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/options/directives.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('../helpers/compiler') 4 | 5 | describe('Options', () => { 6 | describe('directives', () => { 7 | test('{Array}', () => { 8 | const config = { 9 | loader: { 10 | test: /\.html$/, 11 | options: { 12 | directives: [{ 13 | name: '?php', 14 | start: '<', 15 | end: '>' 16 | }] 17 | } 18 | } 19 | } 20 | 21 | return webpack('options/directives/fixture.js', config) 22 | .then((stats) => { 23 | const [module] = stats.toJson().modules 24 | 25 | expect(module.source).toMatchSnapshot() 26 | }) 27 | .catch((err) => err) 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/options/parser.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('../helpers/compiler') 4 | 5 | describe('Options', () => { 6 | describe('parser', () => { 7 | test('{String}', () => { 8 | const config = { 9 | loader: { 10 | test: /\.ssml$/, 11 | options: { 12 | parser: 'posthtml-sugarml' 13 | } 14 | } 15 | } 16 | 17 | return webpack('options/parser/fixture.js', config) 18 | .then((stats) => { 19 | const module = stats.toJson().modules[1] 20 | 21 | expect(module.source).toMatchSnapshot() 22 | }) 23 | .catch((err) => err) 24 | }) 25 | 26 | test('{Object}', () => { 27 | const config = { 28 | loader: { 29 | test: /\.ssml$/, 30 | options: { 31 | parser: require('posthtml-sugarml')() 32 | } 33 | } 34 | } 35 | 36 | return webpack('options/parser/fixture.js', config) 37 | .then((stats) => { 38 | const module = stats.toJson().modules[1] 39 | 40 | expect(module.source).toMatchSnapshot() 41 | }) 42 | .catch((err) => err) 43 | }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/options/plugins.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('../helpers/compiler') 4 | 5 | describe('Options', () => { 6 | describe('plugins', () => { 7 | test('{Array}', () => { 8 | const config = { 9 | loader: { 10 | test: /\.html$/, 11 | options: { 12 | ident: 'posthtml', 13 | plugins: [ 14 | require('../fixtures/plugin')() 15 | ] 16 | } 17 | } 18 | } 19 | 20 | return webpack('fixture.js', config) 21 | .then((stats) => { 22 | const [module] = stats.toJson().modules 23 | 24 | expect(module.source).toMatchSnapshot() 25 | }) 26 | .catch((err) => err) 27 | }) 28 | 29 | test('{Function} - {Array}', () => { 30 | const config = { 31 | loader: { 32 | test: /\.html$/, 33 | options: { 34 | ident: 'posthtml', 35 | plugins () { 36 | return [ 37 | require('../fixtures/plugin')() 38 | ] 39 | } 40 | } 41 | } 42 | } 43 | 44 | return webpack('fixture.js', config) 45 | .then((stats) => { 46 | const [module] = stats.toJson().modules 47 | 48 | expect(module.source).toMatchSnapshot() 49 | }) 50 | .catch((err) => err) 51 | }) 52 | 53 | test('{Function} - {Object}', () => { 54 | const config = { 55 | loader: { 56 | test: /\.html$/, 57 | options: { 58 | ident: 'posthtml', 59 | plugins () { 60 | return require('../fixtures/plugin')() 61 | } 62 | } 63 | } 64 | } 65 | 66 | return webpack('fixture.js', config) 67 | .then((stats) => { 68 | const [module] = stats.toJson().modules 69 | 70 | expect(module.source).toMatchSnapshot() 71 | }) 72 | .catch((err) => err) 73 | }) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /test/options/render.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('../helpers/compiler') 4 | 5 | describe('Options', () => { 6 | describe('render', () => { 7 | test('{String}', () => { 8 | const config = { 9 | loader: { 10 | test: /\.html$/, 11 | options: { 12 | render: 'posthtml-render' 13 | } 14 | } 15 | } 16 | 17 | return webpack('options/render/fixture.js', config) 18 | .then((stats) => { 19 | const [module] = stats.toJson().modules 20 | 21 | expect(module.source).toMatchSnapshot() 22 | }) 23 | .catch((err) => err) 24 | }) 25 | 26 | test('{Object}', () => { 27 | const config = { 28 | loader: { 29 | test: /\.html$/, 30 | options: { 31 | parser: require('posthtml-render') 32 | } 33 | } 34 | } 35 | 36 | return webpack('options/render/fixture.js', config) 37 | .then((stats) => { 38 | const [module] = stats.toJson().modules 39 | 40 | expect(module.source).toMatchSnapshot() 41 | }) 42 | .catch((err) => err) 43 | }) 44 | }) 45 | }) 46 | --------------------------------------------------------------------------------