@webdiscus/pug-loader
Advanced tools
Comparing version 2.4.1 to 2.5.0
# Change log | ||
## 2.5.0 (2022-06-02) | ||
- feat: add `watchFiles` option to watch for file changes in resolved dependencies | ||
- fix: in `:markdown` filter enable HTML tags in markdown source | ||
## 2.4.1 (2022-05-25) | ||
@@ -4,0 +8,0 @@ - docs: update readme |
{ | ||
"name": "@webdiscus/pug-loader", | ||
"version": "2.4.1", | ||
"version": "2.5.0", | ||
"description": "Pug loader renders Pug files into HTML or compiles them into a template function.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
108
README.md
@@ -30,6 +30,6 @@ <div align="center"> | ||
> _MyComponent.vue_ | ||
> ```vue | ||
> ```html | ||
> <template lang='pug'> | ||
> h1 Hello Pug! | ||
> p Use the '@webdiscus/pug-loader' | ||
> h1 Hello Pug! | ||
> p Use the '@webdiscus/pug-loader' | ||
> </template> | ||
@@ -40,14 +40,14 @@ > ``` | ||
> | ||
> | Features | `@webdiscus/pug-loader` | `pug-plain-loader` | | ||
> |-------------------------|-----------------------|---------------------| | ||
> | indent in `<templatе lang='pug'>` | ✅ | ❌ | | ||
> | render into HTML | ✅ | ✅ | | ||
> | render into template function | ✅ | ❌ | | ||
> | pass custom data to template | ✅ | ❌ | | ||
> | embedded filter `:highlight` | ✅ | ❌ | | ||
> | embedded filter `:markdown` | ✅ | ❌ | | ||
> | test coverage | > 98% | 0% | | ||
> | last update | right now | 4 years ago | | ||
> | Features | `@webdiscus/pug-loader` | `pug-plain-loader` | | ||
> |-------------------------|:-----------------------:|:---------------------:| | ||
> | indent in `<templatе lang='pug'>` | ✅ | ❌ | | ||
> | render into HTML | ✅ | ✅ | | ||
> | render into template function | ✅ | ❌ | | ||
> | pass custom data to template | ✅ | ❌ | | ||
> | embedded filter `:highlight` | ✅ | ❌ | | ||
> | embedded filter `:markdown` | ✅ | ❌ | | ||
> | test coverage | > 98% | 0% | | ||
> | last update | right now | 4 years ago | | ||
> | ||
> See [how to use Pug with Vue](#usage-with-vue) and [sources of usage example](https://github.com/webdiscus/pug-loader/tree/master/examples/hello-world-vue). | ||
> See [how to use Pug with Vue](#usage-with-vue) and [source of example](https://github.com/webdiscus/pug-loader/tree/master/examples/hello-world-vue). | ||
@@ -101,3 +101,3 @@ <br> | ||
- resolves required JavaScript modules or JSON in pug | ||
- integrated Pug filters: `:escape` `:code` `:highlight` `:markdown` with highlighting of code blocks | ||
- integrated [Pug filters]: [`:escape`] [`:code`] [`:highlight`] [`:markdown`] with highlighting of code blocks | ||
- passing custom data into Pug template | ||
@@ -122,3 +122,3 @@ - watching of changes in all dependencies | ||
```console | ||
```bash | ||
npm install pug-plugin --save-dev | ||
@@ -206,3 +206,3 @@ ``` | ||
```console | ||
```bash | ||
npm install @webdiscus/pug-loader --save-dev | ||
@@ -243,3 +243,3 @@ ``` | ||
```console | ||
```bash | ||
npm install @webdiscus/pug-loader --save-dev | ||
@@ -390,4 +390,4 @@ ``` | ||
<FILTER_NAME> : <FILTER_OPTIONS> | <TRUE>, | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
@@ -401,2 +401,23 @@ | ||
### `watchFiles` | ||
Type: `Array<RegExp>` Default: `[ /\.(pug|jade|js.{0,2}|.?js|ts.?|md|txt)$/i ]`<br> | ||
This option allows you to configure a list of `RegExp` to watch for file changes in resolved dependencies.\ | ||
The default value enables watching of Pug, scripts, markdown, etc. | ||
and ignores images, styles to avoid double processing via Webpack and via Pug's own compiler. | ||
In some cases, you may want to use one SCSS file for styling | ||
and include another SCSS file with a Pug filter for code syntax highlighting. | ||
The first SCSS file is watched via Webpack, but changes in the second will be ignored.\ | ||
For example, we want to watch for changes in all source examples such as `main.c`, `colors.scss`, etc. from the `/code-samples/` folder, | ||
to do this, add to the `watchFiles` option: | ||
```js | ||
{ | ||
watchFiles: [ | ||
/\\/code-samples\\/.+$/, | ||
] | ||
} | ||
``` | ||
> **Note:** Default RegExp array will be extends, not overridden. | ||
--- | ||
@@ -1001,3 +1022,3 @@ | ||
The example of webpack alias used in the table below: | ||
``` | ||
```js | ||
resolve: { | ||
@@ -1010,15 +1031,15 @@ alias: { | ||
| Example in Pug template | @webdiscus/<br>pug-loader<br>`render` / `html` methods | @webdiscus/<br>pug-loader<br>`compile` method | pugjs/<br>pug-loader | | ||
|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|----------------------------------------------|-----------------------| | ||
| `img(src=require('logo.png'))` | ✅ not recomended | ❌ | ❌ | | ||
| `img(src=require('./logo.png'))` | ✅ | ✅ | ✅ | | ||
| `img(src=require('../images/logo.png'))` | ✅ | ✅ | ✅ | | ||
| `img(src=require('~Images/logo.png'))` | ✅ | ✅ | ✅ | | ||
| `- var file = 'logo.png'`<br>``img(src=require(`~Images/${file}`))`` | ✅ | ✅ | ✅ | | ||
| `- var file = './logo.png'`<br>`img(src=require(file))` | ✅ | ✅ | ❌ | | ||
| `- var file = './images/logo.png'`<br>`img(src=require(file))` | ✅ | ✅ | ❌ | | ||
| `- var file = '../images/logo.png'`<br>`img(src=require(file))` | ✅ | ❌ | ❌ | | ||
| `- var file = 'logo.png'`<br>``img(src=require(`./images/${file}`))`` | ✅ ` | ✅ | ✅ | | ||
| `- var file = 'logo.png'`<br>`img(src=require('../images/' + file))` | ✅ | ✅ | ✅ | | ||
| `pugjs/pug-loader` can't resolve a resource<br>when used a mixin and require in same file: <br> `include mixins`<br>`img(src=require('./logo.png'))` | ✅ | ✅ | ❌ | | ||
| Example in Pug template | @webdiscus/<br>pug-loader<br>`render` / `html` methods | @webdiscus/<br>pug-loader<br>`compile` method | pugjs/<br>pug-loader | | ||
|-------------------------------------------------------------------------------------------------------|:------------------------------------------------------:|:---------------------------------------------:|:--------------------:| | ||
| `img(src=require('logo.png'))` | ✅ | ❌ | ❌ | | ||
| `img(src=require('./logo.png'))` | ✅ | ✅ | ✅ | | ||
| `img(src=require('../images/logo.png'))` | ✅ | ✅ | ✅ | | ||
| `img(src=require('~Images/logo.png'))` | ✅ | ✅ | ✅ | | ||
| `- var file = 'logo.png'`<br>``img(src=require(`~Images/${file}`))`` | ✅ | ✅ | ✅ | | ||
| `- var file = './logo.png'`<br>`img(src=require(file))` | ✅ | ✅ | ❌ | | ||
| `- var file = './images/logo.png'`<br>`img(src=require(file))` | ✅ | ✅ | ❌ | | ||
| `- var file = '../images/logo.png'`<br>`img(src=require(file))` | ✅ | ❌ | ❌ | | ||
| `- var file = 'logo.png'`<br>``img(src=require(`./images/${file}`))`` | ✅ | ✅ | ✅ | | ||
| `- var file = 'logo.png'`<br>`img(src=require('../images/' + file))` | ✅ | ✅ | ✅ | | ||
| resolve a resource<br>when used a mixin and require in same file<br>see the [issue](pug-loader-issue) | ✅ | ✅ | ❌ | | ||
@@ -1197,12 +1218,13 @@ --- | ||
<template> | ||
<div v-html='html'></div> | ||
<div v-html='demo'></div> | ||
</template> | ||
<script> | ||
import tmpl from './views/demo.pug'; | ||
// import Pug as template function | ||
import demoTmpl from './views/demo.pug'; | ||
// define custom data used in Pug template | ||
const locals = { colors: ['red', 'green', 'blue'] }; | ||
// pass custom data in Pug template | ||
const rawHtml = tmpl(locals); | ||
const demoHtml = demoTmpl(locals); | ||
@@ -1213,3 +1235,3 @@ export default { | ||
return { | ||
html: rawHtml | ||
demo: demoHtml | ||
} | ||
@@ -1288,2 +1310,8 @@ } | ||
[pug-plugin]: https://github.com/webdiscus/pug-plugin | ||
[pug-filters]: https://webdiscus.github.io/pug-loader/pug-filters | ||
[pug-loader-issue]: https://github.com/pugjs/pug-loader/issues/123 | ||
[Pug filters]: https://webdiscus.github.io/pug-loader/pug-filters | ||
[`:escape`]: https://webdiscus.github.io/pug-loader/pug-filters/escape.html | ||
[`:code`]: https://webdiscus.github.io/pug-loader/pug-filters/code.html | ||
[`:highlight`]: https://webdiscus.github.io/pug-loader/pug-filters/highlight.html | ||
[`:markdown`]: https://webdiscus.github.io/pug-loader/pug-filters/markdown.html |
@@ -53,3 +53,7 @@ const path = require('path'); | ||
let options = {}; | ||
let options = { | ||
// enable HTML tags in markdown source | ||
html: true, | ||
}; | ||
if (highlight != null && highlight.use) { | ||
@@ -62,12 +66,8 @@ adapter.init({ | ||
langPrefix = adapter.getLangPrefix(); | ||
options = { | ||
highlight: (text, lang) => { | ||
return `<pre class="${langPrefix}${lang}"><code>` + adapter.highlight(text, lang) + '</code></pre>'; | ||
}, | ||
options.highlight = (text, lang) => { | ||
return `<pre class="${langPrefix}${lang}"><code>` + adapter.highlight(text, lang) + '</code></pre>'; | ||
}; | ||
} else if (langPrefix) { | ||
options = { | ||
highlight: (text, lang) => { | ||
return `<pre class="${langPrefix}${lang}"><code>` + text + '</code></pre>'; | ||
}, | ||
options.highlight = (text, lang) => { | ||
return `<pre class="${langPrefix}${lang}"><code>` + text + '</code></pre>'; | ||
}; | ||
@@ -74,0 +74,0 @@ } |
@@ -8,3 +8,3 @@ // add polyfill for node.js >= 12.0.0 && < 15.0.0 | ||
const walk = require('pug-walk'); | ||
const { isWin } = require('./utils'); | ||
const { isWin, trimIndent } = require('./utils'); | ||
const resolver = require('./resolver'); | ||
@@ -27,5 +27,6 @@ const loader = require('./loader'); | ||
* @typedef {Object} LoaderDependency | ||
* @property {Array<RegExp>} watchFiles | ||
* @property {Function<loaderContext:Object>} init | ||
* @property {Function<file:string>} add | ||
* @property {Function} watch | ||
* @property {Function|RegExp} watch | ||
*/ | ||
@@ -35,6 +36,11 @@ | ||
files: new Set(), | ||
dependencyExt: new Set(['.pug', '.jade', '.json', '.js', '.mjs', '.ts']), | ||
watchFiles: [/\.(pug|jade|js.{0,2}|.?js|ts.?|md|txt)$/i], | ||
loaderContext: null, | ||
init(loaderContext) { | ||
init(loaderContext, { watchFiles }) { | ||
// avoid double push in array by watching | ||
if (this.loaderContext == null && watchFiles != null) { | ||
if (!Array.isArray(watchFiles)) watchFiles = [watchFiles]; | ||
this.watchFiles.push(...watchFiles); | ||
} | ||
this.loaderContext = loaderContext; | ||
@@ -49,4 +55,5 @@ }, | ||
add(file) { | ||
let ext = path.extname(file); | ||
if (!this.dependencyExt.has(ext)) return; | ||
if (!this.watchFiles.find((regex) => regex.test(file))) { | ||
return; | ||
} | ||
@@ -229,3 +236,3 @@ file = isWin ? path.normalize(file) : file; | ||
dependency.init(loaderContext); | ||
dependency.init(loaderContext, loaderOptions); | ||
resolver.setDependency(dependency); | ||
@@ -237,4 +244,4 @@ resolver.setLoader(loader); | ||
// remove indent in vue or react template | ||
const template = removeTemplateIndent(content); | ||
// remove indent in template | ||
const template = trimIndent(content); | ||
if (template !== false) content = template; | ||
@@ -292,43 +299,2 @@ | ||
/** | ||
* Remove indents in vue and react templates. | ||
* | ||
* For example, the content of this template contain the indent: | ||
* <template lang='pug'> | ||
* p text | ||
* </template> | ||
* | ||
* In Pug code the indent is not allowed and will be removed. | ||
* Supports for both spaces and tabs. | ||
* | ||
* @param {string} content The Pug code. | ||
* @returns {boolean|string} If no indent found return false otherwise return normalized code. | ||
*/ | ||
const removeTemplateIndent = (content) => { | ||
const SPACE = ' '; | ||
const TAB = '\t'; | ||
const NL = '\n'; | ||
// skip new lines, tabs and spaces at begin | ||
let codePos = 0; | ||
while (content[codePos] === TAB || content[codePos] === SPACE || content[codePos] === NL) { | ||
codePos++; | ||
} | ||
// find `start of line` pos at code line | ||
let startLinePos = codePos; | ||
while (startLinePos > 0 && content[--startLinePos] !== NL) {} | ||
if (content[startLinePos] === NL) startLinePos++; | ||
const indentSize = codePos - startLinePos; | ||
const indentCode = content[startLinePos] === TAB ? '\u0009' : '\u0020'; | ||
if (indentSize > 0) { | ||
const regexp = new RegExp(`^${indentCode}{${indentSize}}`, 'mg'); | ||
return content.replace(regexp, ''); | ||
} | ||
return false; | ||
}; | ||
module.exports = function (content, map, meta) { | ||
@@ -335,0 +301,0 @@ const callback = this.async(); |
@@ -121,2 +121,43 @@ const path = require('path'); | ||
/** | ||
* Remove indents in Vue and React templates. | ||
* | ||
* For example, the content of this template contain the indent: | ||
* <template lang='pug'> | ||
* p text | ||
* </template> | ||
* | ||
* In Pug code the indent is not allowed and will be removed. | ||
* Supports for both spaces and tabs. | ||
* | ||
* @param {string} content The Pug code. | ||
* @returns {boolean|string} If no indent found return false otherwise return normalized code. | ||
*/ | ||
const trimIndent = (content) => { | ||
const SPACE = ' '; | ||
const TAB = '\t'; | ||
const NL = '\n'; | ||
// skip new lines, tabs and spaces at begin | ||
let codePos = 0; | ||
while (content[codePos] === TAB || content[codePos] === SPACE || content[codePos] === NL) { | ||
codePos++; | ||
} | ||
// find `start of line` pos at code line | ||
let startLinePos = codePos; | ||
while (startLinePos > 0 && content[--startLinePos] !== NL) {} | ||
if (content[startLinePos] === NL) startLinePos++; | ||
const indentSize = codePos - startLinePos; | ||
const indentCode = content[startLinePos] === TAB ? '\u0009' : '\u0020'; | ||
if (indentSize > 0) { | ||
const regexp = new RegExp(`^${indentCode}{${indentSize}}`, 'mg'); | ||
return content.replace(regexp, ''); | ||
} | ||
return false; | ||
}; | ||
module.exports = { | ||
@@ -129,2 +170,3 @@ loaderName, | ||
injectExternalData, | ||
trimIndent, | ||
}; |
105916
1603
1305