@webdiscus/pug-loader
Advanced tools
Comparing version 1.6.0 to 1.6.1-beta.0
# Change log | ||
## 1.6.1 (2022-01-19) | ||
- added supports resolving for aliases from webpack `resolve.plugins` by required resources, like styles, scripts | ||
- fix resolving issues by usage the variable filename contained parent relative path in require() function | ||
- improve resolving the required files by all methods | ||
- refactoring | ||
- added more test cases | ||
## 1.6.0 (2022-01-12) | ||
- added supports resolving aliases from `webpack.resolve.plugins` | ||
if a file is by `webpack.resolve.alais` not resolved, then uses the slow enhanced resolver | ||
- added supports resolving for aliases from webpack `resolve.plugins` by include / extends | ||
if a file is by webpack `resolve.alais` not resolved, then uses the slow enhanced resolver | ||
- update packages | ||
## 1.5.1 (2021-12-10) | ||
- bugfix: fix path resolving on Windows | ||
- fix path resolving on Windows | ||
- some optimisations | ||
@@ -15,16 +22,16 @@ - code refactoring | ||
- NEW: the `pug-loader` is now the part of the [pug-plugin](https://github.com/webdiscus/pug-plugin). | ||
- feature: added option `basedir` for all absolute inclusion | ||
- test: add test for new option `basedir` | ||
- test: refactoring of tests | ||
- added the option `basedir` for all absolute inclusion | ||
- added the test for new option `basedir` | ||
- refactoring of tests | ||
## 1.4.6 (2021-12-06) | ||
- cleanup: remove needles console.log, cleanup code | ||
- devDeps: update packages | ||
- docs: minor update readme | ||
- tests: add tests for an exception and an option | ||
- tests: refactoring test utils | ||
- remove needles console.log, cleanup code | ||
- added tests for an exception and an option | ||
- refactoring test utils | ||
- update readme | ||
- update packages | ||
## 1.4.5 (2021-11-22) | ||
- improvement: optimization of `render` and `html` methods | ||
- bugfix: fix require() for CommonJS module. Now is possible use the CommonJS module directly in the pug, e.g.: | ||
- improve the `render` and `html` methods | ||
- fix require() for CommonJS module. Now is possible use the CommonJS module directly in the pug, e.g.: | ||
```pug | ||
@@ -34,14 +41,14 @@ - var someModule = require('some-module'); | ||
``` | ||
- bugfix: fix 'Unexpected token' by render method | ||
- bugfix: fix 'Unterminated string' by render method | ||
- fix 'Unexpected token' by render method | ||
- fix 'Unterminated string' by render method | ||
## 1.4.4 (2021-11-19) | ||
- compatibility: add polyfill `replaceAll()` for node.js < 15 | ||
- bugfix: fix for the parsing multiple `require` in a single string code | ||
- added the polyfill `replaceAll()` for node.js < 15 | ||
- fix for the parsing multiple `require` in a single string code | ||
## 1.4.3 (2021-11-18) | ||
- update: minor code update and cleanup | ||
- minor code update and cleanup | ||
## 1.4.2 (2021-11-18) | ||
- feature: now supports require of JS and JSON data files in pug at compile time, e.g.: | ||
- added supports for require of JS and JSON data files in pug at compile time, e.g.: | ||
```pug | ||
@@ -52,10 +59,10 @@ - var someData = require('some-data.json'); | ||
``` | ||
- bugfix: fix issues by samples | ||
- fix issues by samples | ||
## 1.4.1 (2021-11-17) | ||
- improvement: inner optimizations for the `render` method | ||
- update: code cleanup | ||
- code cleanup | ||
## 1.4.0 (2021-11-16) | ||
- feature: added the option `esModule` to enable/disable ES modules syntax in generated JS modules | ||
- added the option `esModule` to enable/disable ES modules syntax in generated JS modules | ||
- improvement: some code improvements | ||
@@ -67,17 +74,17 @@ | ||
## 1.3.0 (2021-11-15) | ||
- feature: the `render` method has been improved. Now the method render a pug into HTML really at compile time without limitations for resolving an embedded resource. | ||
- the `render` method has been improved. Now the method render a pug into HTML really at compile time without limitations for resolving an embedded resource. | ||
This method do same result as any other pug-loader + html-loader, even faster, generate smaller code and with all that not need an additional loader. | ||
- update: some code and test refactorings | ||
- refactoring | ||
## 1.2.0 (2021-11-12) | ||
- feature: added for the loader option `method` new value `html` to render the template function into pure HTML string,\ | ||
- added the new loader method `html` to render the template function into pure HTML string,\ | ||
this method require additional loader, e.g. `html-loader` | ||
- update: added directory with samples for usage this loader with Angular Component | ||
- added directory with samples for usage this loader with Angular Component | ||
## 1.1.1 (2021-11-10) | ||
- bugfix: fix config for tests | ||
- update: cleanup tests | ||
- fix config for tests | ||
- cleanup tests | ||
## 1.1.0 (2021-11-10) | ||
- feature: usage of `compile` or `render` methods in JavaScript: | ||
- added supports for usage of `compile` or `render` methods in JavaScript: | ||
- added loader option `method: render|compile` to render into HTML or compile into a template function all templates required in js file | ||
@@ -88,21 +95,20 @@ - in the js require() can be used the query parameter `?pug-render` to render the pug template directly into HTML, independent of loader option `method`, \ | ||
e.g. `const tmpl = require('template.pug?pug-compile')` | ||
- feature: pass a custom data into a template at compile time: | ||
- added the passing a custom data into a template at compile time: | ||
- added loader option `data: {}` to pass a data into all templates at compile time, e.g. useful for the i18n data | ||
- in the js require () query you can use URL `key=value` or JSON `{key:value}` parameters to pass them into the template at compile time, \ | ||
e.g. `const tmpl = require('template.pug?key1=value1&{"key2":"value2","key3":"value3"}')` | ||
- update: dependencies in package.json | ||
- update dependencies in package.json | ||
## 1.0.3 (2021-10-21) | ||
- update: added missed dependency for test | ||
- update: dependencies for test | ||
- update: readme | ||
- added missed dependency for test | ||
- update dependencies for test | ||
- update readme | ||
## 1.0.2 (2021-10-20) | ||
- update: added the test case to cover the `pugjs/pug-loader` [issue](https://github.com/pugjs/pug-loader/issues/123) : `Module not found: Error: Can't resolve` when use a mixin and require on same file.\ | ||
note: this pug-loader work fine und hasn't this issue. Here is just added the test case for the problem of pugjs/pug-loader to be sure that this problem doesn't occur in this pug-loader. | ||
- added the test case to cover the `pugjs/pug-loader` [issue](https://github.com/pugjs/pug-loader/issues/123) : `Module not found: Error: Can't resolve` when use a mixin and require on same file.\ | ||
## 1.0.1 (2021-10-20) | ||
- update: devDependencies in `package.json` | ||
- update: readme | ||
- update: cleanup code | ||
- update devDependencies in `package.json` | ||
- update readme | ||
- cleanup code | ||
@@ -109,0 +115,0 @@ ## 1.0.0 (2021-10-19) |
{ | ||
"name": "@webdiscus/pug-loader", | ||
"version": "1.6.0", | ||
"version": "1.6.1-beta.0", | ||
"description": "The pug loader resolves paths and webpack aliases in a pug template and compiles it to HTML or into a template function.", | ||
@@ -65,2 +65,3 @@ "keywords": [ | ||
"dependencies": { | ||
"ansis": "^1.3.2", | ||
"pug": ">=3.0.2", | ||
@@ -73,11 +74,13 @@ "webpack-merge": "^5.8.0" | ||
"@types/jest": "^27.4.0", | ||
"css-loader": "^6.5.1", | ||
"html-loader": "^3.1.0", | ||
"html-webpack-plugin": "^5.5.0", | ||
"jest": "^27.4.7", | ||
"jstransformer-markdown-it": "^2.1.0", | ||
"prettier": "^2.5.1", | ||
"pug-plugin": "^1.2.0", | ||
"pug-plugin": "^1.2.1", | ||
"rimraf": "^3.0.2", | ||
"tsconfig-paths-webpack-plugin": "^3.5.2", | ||
"webpack": "^5.65.0" | ||
"webpack": "^5.66.0" | ||
} | ||
} |
@@ -11,3 +11,3 @@ [![npm](https://img.shields.io/npm/v/@webdiscus/pug-loader?logo=npm&color=brightgreen "npm package")](https://www.npmjs.com/package/@webdiscus/pug-loader "download npm package") | ||
Webpack loader for the [Pug](https://pugjs.org) templates.\ | ||
The pug loader resolves paths and webpack aliases for `extends`/`include`/`require()` in a pug template and compiles it to HTML or into a template function. | ||
The pug loader resolves paths and webpack aliases for `extends`/`include`/`require()` in a pug template and compiles it to HTML or to a template function. | ||
@@ -21,5 +21,5 @@ > **NEW:** The `pug-loader` is now the part of the [pug-plugin](https://github.com/webdiscus/pug-plugin). | ||
- supports features and options of original [`pugjs/pug-loader`](https://github.com/pugjs/pug-loader/) | ||
- up to 4x faster than original `pugjs/pug-loader` at webpack starting | ||
- up to 8x faster than original `pugjs/pug-loader` at webpack watching during compile changes in dependencies | ||
- many time faster than original `pugjs/pug-loader` | ||
- supports Webpack `resolve.alias` and `resolve.plugins`, works with and without the prefixes `~` `@` | ||
- supports path resolving from `tsconfig.json` using [`tsconfig-paths-webpack-plugin`](https://github.com/dividab/tsconfig-paths-webpack-plugin) | ||
- supports integration with `Angular Component` | ||
@@ -525,3 +525,3 @@ - supports the syntax of `CommonJS` and `ES modules` in generated templates for loading them via `require` or `import` | ||
options: { | ||
esModule: false, // need to allow use require() for load a tempale in JavaScript | ||
esModule: false, // allow to use the require() for load a templqte in JavaScript | ||
}, | ||
@@ -816,27 +816,2 @@ }, | ||
### Known issues / features by usage embedded resources | ||
Due to the peculiarities of the pug compiler, | ||
the interpolation of the argument to the `require()` function depends on its string and variable parts.\ | ||
Use a relative path only in the string before the variable. | ||
The variable must contain only the filename without specifying a path. | ||
Examples of <span style="color: red">incorrect</span> usage: | ||
```pug | ||
- filename = './image.jpeg' | ||
img(src=require(filename)) | ||
``` | ||
```pug | ||
- filename = '../relative/path/to/resource/image.jpeg' | ||
img(src=require(filename)) | ||
``` | ||
Examples of <span style="color: green">correct</span> usage: | ||
```pug | ||
- filename = 'image.jpeg' | ||
img(src=require(filename)) | ||
``` | ||
```pug | ||
- filename = 'image.jpeg' | ||
img(src=require('../relative/path/to/resource/' + filename)) | ||
``` | ||
The example of dynamically generating embedded resources in template: | ||
@@ -846,5 +821,7 @@ ```pug | ||
each file in files | ||
img(src=require('../images/' + file)) | ||
img(src=require(file)) | ||
``` | ||
### File resolving examples | ||
The example of webpack alias used in the table below: | ||
@@ -854,3 +831,3 @@ ``` | ||
alias: { | ||
SourceImages: path.join(__dirname, 'src/images/'), | ||
Images: path.join(__dirname, 'src/images/'), | ||
}, | ||
@@ -860,26 +837,24 @@ } | ||
Examples for using embedded resources: | ||
| Code | @webdiscus/pug-loader | pugjs/pug-loader | | ||
|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------|-----------------------------------------| | ||
| `img(src=require('image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `img(src=require('./image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `img(src=require('./images/image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `img(src=require('../images/image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `img(src=require('Images/image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'image.jpeg'`<br>`img(src=require('Images/' + file))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'image.jpeg'`<br>``img(src=require(`Images/${file}`))`` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'image.jpeg'`<br>`img(src=require(file))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `- var file = 'image.jpeg'`<br> ``img(src=require(`${file}`))`` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `- var file = 'image.jpeg'`<br>`img(src=require('./' + file))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = './image.jpeg'`<br>`img(src=require(file))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `- var file = './image.jpeg'`<br>`img(src=require('' + file))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'images/image.jpeg'`<br>`img(src=require(file))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `- var file = 'image.jpeg'`<br>`img(src=require('./images/' + file))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'image.jpeg'`<br>``img(src=require(`./images/${file}`))`` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = '../images/image.jpeg'`<br>`img(src=require(file))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
| `- var file = 'image.jpeg'`<br>`img(src=require('../images/' + file))` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| `- var file = 'image.jpeg'`<br>``img(src=require(`../images/${file}`))`` | <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | | ||
| the `pugjs/pug-loader` can't resolve when used a mixin and require on same file: <br> `include mixins`<br>`img(src=require('./image.jpeg'))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | | ||
Code | @webdiscus/pug-loader| pugjs/pug-loader | Note | ||
---|---|---|--- | ||
`img(src=require('image.jpeg'))`| <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | ||
`img(src=require('./image.jpeg'))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`img(src=require('./images/image.jpeg'))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`img(src=require('../images/image.jpeg'))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`img(src=require('SourceImages/image.jpeg'))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | Usage of the webpack alias to images directory. | ||
`- file = 'image.jpeg'`<br>`img(src=require('SourceImages/' + file))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`- file = 'image.jpeg'`<br>``img(src=require(`SourceImages/${file}`))``|<span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`- file = 'image.jpeg'`<br>`img(src=require(file))`| <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | ||
`- file = 'image.jpeg'`<br> ``img(src=require(`${file}`))``| <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | ||
`- file = 'image.jpeg'`<br>`img(src=require('./' + file))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`- file = './image.jpeg'`<br>`img(src=require(file))`| <span style="color:red">fail</span> | <span style="color:red">fail</span> | Don't use `./` in variable of filename. | ||
`- file = './image.jpeg'`<br>`img(src=require('' + file))`| <span style="color:red">fail</span> | <span style="color:green">**OK**</span> | Don't use `./` in variable of filename. | ||
`- file = 'images/image.jpeg'`<br>`img(src=require(file))`| <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | ||
`- file = 'image.jpeg'`<br>`img(src=require('./images/' + file))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`- file = 'image.jpeg'`<br>``img(src=require(`./images/${file}`))``| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
`- file = '../images/image.jpeg'`<br>`img(src=require(file))`| <span style="color:red">fail</span> | <span style="color:red">fail</span> | Don't use a path in a variable. | ||
`- file = 'image.jpeg'`<br>`img(src=require('../images/' + file))`| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | Define a path separately as string and add to she the variable contained only a filename. | ||
`- file = 'image.jpeg'`<br>``img(src=require(`../images/${file}`))``| <span style="color:green">**OK**</span> | <span style="color:green">**OK**</span> | ||
Include the template from sub directory: <br> `include mixins`<br>``img(src=require('./image.jpeg'))``| <span style="color:green">**OK**</span> | <span style="color:red">fail</span> | when use a mixin and require on same file, then `pugjs/pug-loader` can't resolve the file in require(). | ||
--- | ||
@@ -913,6 +888,4 @@ > Important: in examples used name of loader as `pug-loader`, because it is defined as **alias** at resolveLoader: | ||
<!-- prettier-ignore-start --> | ||
[pug]: https://github.com/pugjs/pug | ||
[pug-api]: https://pugjs.org/api/reference.html | ||
[pug-plugin]: https://github.com/webdiscus/pug-plugin | ||
<!-- prettier-ignore-end --> |
104
src/index.js
@@ -8,20 +8,10 @@ // add polyfill for node.js >= 12.0.0 && < 15.0.0 | ||
const { merge } = require('webpack-merge'); | ||
const { | ||
resolveTemplatePath, | ||
resolveRequireCode, | ||
resolveRequireResource, | ||
getResourceParams, | ||
injectExternalVariables, | ||
} = require('./utils'); | ||
const { getPugResolverSync, getDirResolverSync } = require('./resolver'); | ||
const loaderMethods = require('./loader-methods'); | ||
const loader = 'pug-loader'; | ||
const { loaderName, getResourceParams, injectExternalVariables } = require('./utils'); | ||
const resolver = require('./resolver'); | ||
const loader = require('./loader'); | ||
// the variables with global scope for the resolvePlugin | ||
let webpackResolveAlias = null, | ||
loaderMethod = null, | ||
let loaderMethod = null, | ||
codeDependencies = [], | ||
loaderContext = null, | ||
resolvePugSync, | ||
resolveDirSync; | ||
loaderContext = null; | ||
@@ -38,52 +28,8 @@ /** | ||
* @param {string} filename The extends/include filename in template. | ||
* @param {string} source The absolute path to template. | ||
* @param {string} templateFile The absolute path to template. | ||
* @param {{}} options The options of pug compiler. | ||
* @return {string} | ||
*/ | ||
resolve: (filename, source, options) => { | ||
const originalFile = filename.trim(); | ||
let result = originalFile; | ||
resolve: (filename, templateFile, options) => resolver.resolve(filename.trim(), templateFile.trim()), | ||
// absolute path is resolved by prepending options.basedir | ||
if (originalFile[0] === '/') { | ||
if (!options.basedir) optionBasedirException(); | ||
return path.join(options.basedir, originalFile); | ||
} | ||
// relative to the current file | ||
if (originalFile[0] === '.') { | ||
return path.join(path.dirname(source.trim()), result); | ||
} | ||
if (!source) noSourceException(); | ||
// resolve by `resolve.alias` | ||
if (webpackResolveAlias) { | ||
result = resolveTemplatePath(originalFile, webpackResolveAlias); | ||
} | ||
// #Method 1 | ||
// fallback to slow resolver for `resolve.plugins` | ||
// resolve alias or relative path to the current file | ||
// note: relative path is allowed without `./` | ||
// see https://pugjs.org/language/includes.html | ||
if (result === originalFile) { | ||
result = resolvePugSync(path.dirname(source.trim()), filename); | ||
//console.log('### RESOLVE\nfile:', filename, '\nsource:', source, '\n==', result); | ||
} | ||
// #Method 2 | ||
// fallback to slow resolver for `resolve.plugins`, | ||
// the alias must begin with any non-word char, like @ or ~ | ||
// if (result === originalFile && !/^[a-z]{1}/i.test(result)) { | ||
// result = resolvePugSync(filename); | ||
// console.log('### RESOLVE\nfile:', filename, '\nsource:', source, '\n==', result); | ||
// } | ||
// if (!path.isAbsolute(result)) { | ||
// result = path.join(path.dirname(source.trim()), result); | ||
// } | ||
return result; | ||
}, | ||
/** | ||
@@ -100,3 +46,3 @@ * Resolve the filename for require(). | ||
if (containRequire(node)) { | ||
let result = resolveRequireCode(node.filename, node.val, webpackResolveAlias, codeDependencies); | ||
let result = resolver.resolveRequireCode(node.filename, node.val, codeDependencies); | ||
if (result && result !== node.val) node.val = result; | ||
@@ -108,3 +54,3 @@ } | ||
if (containRequire(attr)) { | ||
let result = resolveRequireResource(attr.filename, attr.val, webpackResolveAlias, loaderMethod); | ||
let result = resolver.resolveRequireResource(attr.filename, attr.val, loaderMethod); | ||
if (result && result !== attr.val) attr.val = result; | ||
@@ -125,10 +71,2 @@ } | ||
const optionBasedirException = () => { | ||
throw new Error(`[${loader}] the "basedir" option is required to use includes and extends with "absolute" paths.`); | ||
}; | ||
const noSourceException = () => { | ||
throw new Error(`[${loader}] the "filename" option is required to use includes and extends with "relative" paths.`); | ||
}; | ||
/** | ||
@@ -140,2 +78,3 @@ * @param {string} content The pug template. | ||
const compilePugContent = function (content, callback) { | ||
const webpackOptionsResolve = this._compiler.options.resolve || {}; | ||
let res = {}; | ||
@@ -147,9 +86,9 @@ | ||
resourceParams = getResourceParams(loaderContext.resourceQuery), | ||
// the rule: a method defined in the resource query has highest priority over a method defined in the loader options | ||
// because a method from loader options is global but a query method override by local usage a global method | ||
methodFromQuery = loaderMethods.find((item) => resourceParams.hasOwnProperty(item.queryParam)), | ||
methodFromOptions = loaderMethods.find((item) => loaderOptions.method === item.method); | ||
// the rule: a method defined in the resource query has highest priority over a method defined in the loaderName options | ||
// because a method from loaderName options is global but a query method override by local usage a global method | ||
methodFromQuery = loader.methods.find((item) => resourceParams.hasOwnProperty(item.queryParam)), | ||
methodFromOptions = loader.methods.find((item) => loaderOptions.method === item.method); | ||
// define the `loaderMethod` for global scope in this module | ||
loaderMethod = methodFromQuery || methodFromOptions || loaderMethods[0]; | ||
loaderMethod = methodFromQuery || methodFromOptions || loader.methods[0]; | ||
@@ -181,2 +120,6 @@ const options = { | ||
// initialize the resolver | ||
resolver.init(options.basedir, this.rootContext, webpackOptionsResolve); | ||
loader.setResolver(resolver); | ||
loaderContext.cacheable && loaderContext.cacheable(true); | ||
@@ -190,3 +133,3 @@ | ||
if (exception.filename) loaderContext.addDependency(path.normalize(exception.filename)); | ||
callback(`\n[${loader}] Pug compilation failed.\n` + exception.toString()); | ||
callback(`\n[${loaderName}] Pug compilation failed.\n` + exception.toString()); | ||
return; | ||
@@ -210,12 +153,5 @@ } | ||
module.exports = function (content, map, meta) { | ||
const webpackResolve = this._compiler.options.resolve || {}; | ||
const callback = this.async(); | ||
loaderContext = this; | ||
// save resolve.alias from webpack config for global scope in this module, defaults the `webpackResolveAlias` is null | ||
if (webpackResolve.alias && Object.keys(webpackResolve.alias).length > 0) webpackResolveAlias = webpackResolve.alias; | ||
resolvePugSync = getPugResolverSync(this.rootContext, webpackResolve); | ||
//resolveDirSync = getDirResolverSync(this.rootContext, webpackResolve); | ||
compilePugContent.call(this, content, (err, result) => { | ||
@@ -222,0 +158,0 @@ if (err) return callback(err); |
// the 'enhanced-resolve' package already used in webpack, don't need to define it in package.json | ||
const ResolverFactory = require('enhanced-resolve'); | ||
const path = require('path'); | ||
const ansis = require('ansis'); | ||
const { loaderName, isWin, pathToPosix } = require('./utils'); | ||
// reserved for next release | ||
const DirectoryResolvePlugin = require('./directory-resolve-plugin'); | ||
/** | ||
* Create regexp to match alias. | ||
* | ||
* @param {string} match The matched alias. | ||
* @return {string} The regexp pattern with matched aliases. | ||
*/ | ||
const aliasRegexp = (match) => `^[~@]?(${match})(?=\\/)`; | ||
/** | ||
* Resolve an alias in the argument of require() function. | ||
* | ||
* @param {string} value The value of extends/include/require(). | ||
* @param {{}} aliases The `resolve.alias` of webpack config. | ||
* @return {string | false} If found an alias return resolved normalized path otherwise return false. | ||
*/ | ||
const resolveAlias = (value, aliases) => { | ||
if (!aliases) return false; | ||
const patternAliases = Object.keys(aliases).join('|'); | ||
// webpack.alias is empty | ||
if (!patternAliases) return false; | ||
const [, alias] = new RegExp(aliasRegexp(patternAliases)).exec(value) || []; | ||
// path contains no alias | ||
if (!alias) return false; | ||
let resolvedFile = value.replace(new RegExp(aliasRegexp(alias)), aliases[alias]); | ||
return path.join(resolvedFile); | ||
}; | ||
/** | ||
* @param {string} path The start path to resolve. | ||
@@ -12,3 +45,3 @@ * @param {{}} options The enhanced-resolve options. | ||
*/ | ||
const getPugResolverSync = (path, options) => { | ||
const getFileResolverSync = (path, options) => { | ||
const resolve = ResolverFactory.create.sync({ | ||
@@ -23,4 +56,3 @@ ...options, | ||
mainFiles: [], | ||
extensions: ['.pug'], | ||
restrictions: [/\.(pug)$/i], | ||
extensions: [], | ||
preferRelative: true, | ||
@@ -36,39 +68,116 @@ }); | ||
const resolveException = (file, templateFile, error) => { | ||
throw new Error( | ||
`\n${ansis.black.bgRedBright(`[${loaderName}]`)} the file ${ansis.yellow( | ||
file | ||
)} can't be resolved in the pug template:\n` + | ||
ansis.cyan(templateFile) + | ||
`` | ||
); | ||
}; | ||
/** | ||
* TODO resolve only directory, without file | ||
* | ||
* @param {string} path The start path to resolve. | ||
* @param {{}} options The enhanced-resolve options. | ||
* @returns {function(context:string, request:string): string | false} | ||
* @typedef {Object} LoaderResolver | ||
* @property {function(basedir:string, path:string, options:{})} init | ||
* @property {(function(context:string, file:string): string)} resolve | ||
* @property {function(templateFile:string, value:string, dependencies:string[]): string} resolveRequireCode | ||
* @property {function(templateFile:string, value:string, method:LoaderMethod): string} resolveRequireResource | ||
*/ | ||
const getDirResolverSync = (path, options) => { | ||
const resolve = ResolverFactory.create.sync({ | ||
...options, | ||
aliasFields: [], | ||
conditionNames: [], | ||
descriptionFiles: [], | ||
mainFields: [], | ||
mainFiles: [], | ||
extensions: [], | ||
exportsFields: [], | ||
modules: [], | ||
let pugLoaderBasedir = '/'; | ||
let aliases = null; | ||
let resolveFile = null; | ||
plugins: [ | ||
'...', | ||
//new DirectoryResolvePlugin('undescribed-existing-directory', 'resolved'), // ok for resolve.alias | ||
//new DirectoryResolvePlugin('relative', 'resolved'), // ok for resolve.alias, but not for resolve.plugins | ||
//new DirectoryResolvePlugin('resolved', 'file'), // max | ||
new DirectoryResolvePlugin('directory', 'relative'), // | ||
], | ||
/** | ||
* @type LoaderResolver | ||
*/ | ||
const resolver = { | ||
/** | ||
* @param {string} basedir The the root directory of all absolute inclusion. | ||
* @param {string} path The root context path. | ||
* @param {{}} options The webpack `resolve` configuration. | ||
*/ | ||
init: (basedir, path, options) => { | ||
pugLoaderBasedir = basedir; | ||
aliases = options.alias; | ||
resolveFile = getFileResolverSync(path, options); | ||
}, | ||
preferRelative: true, | ||
}); | ||
/** | ||
* Resolve filename. | ||
* | ||
* @param {string} file The file to resolve. | ||
* @param {string} templateFile The template file. | ||
* @return {string} | ||
*/ | ||
resolve: (file, templateFile) => { | ||
const context = path.dirname(templateFile); | ||
let resolvedPath; | ||
return (request) => resolve(path, request); | ||
// resolve an absolute path by prepending options.basedir | ||
if (file[0] === '/') { | ||
resolvedPath = path.join(pugLoaderBasedir, file); | ||
} | ||
// resolve a relative file | ||
if (!resolvedPath && file[0] === '.') { | ||
resolvedPath = path.join(context, file); | ||
} | ||
// resolve a file by webpack `resolve.alias` | ||
if (!resolvedPath) { | ||
resolvedPath = resolveAlias(file, aliases); | ||
} | ||
// fallback to enhanced resolver | ||
if (!resolvedPath) { | ||
try { | ||
resolvedPath = resolveFile(context, file); | ||
} catch (error) { | ||
resolveException(file, templateFile, error); | ||
} | ||
} | ||
// Windows only: fix the path format | ||
if (isWin) resolvedPath = pathToPosix(resolvedPath); | ||
return resolvedPath; | ||
}, | ||
/** | ||
* Resolve the code file path in require(). | ||
* | ||
* @param {string} templateFile The filename of the template where resolves the resource. | ||
* @param {string} value The resource value include require(). | ||
* @param {string[]} dependencies The list of dependencies for watching. | ||
* @return {string} | ||
*/ | ||
resolveRequireCode: (templateFile, value, dependencies) => | ||
value.replaceAll(/(require\(.+?\))/g, (value) => { | ||
const [, file] = /(?<=require\("|'|`)(.+)(?=`|'|"\))/.exec(value) || []; | ||
let resolvedPath = resolver.resolve(file, templateFile); | ||
// Important: delete the file from require.cache to allow reloading cached files after changes by watch. | ||
delete require.cache[resolvedPath]; | ||
dependencies.push(resolvedPath); | ||
return `require('${resolvedPath}')`; | ||
}), | ||
/** | ||
* Resolve a path in the argument of require() function. | ||
* | ||
* @param {string} templateFile The filename of the template where resolves the resource. | ||
* @param {string} value The resource value include require(). | ||
* @param {LoaderMethod} method The object of the current method. | ||
* @return {string} | ||
*/ | ||
resolveRequireResource: (templateFile, value, method) => { | ||
const [, file] = /(?<=require\()(.+)(?=\))/.exec(value) || []; | ||
if (!file) return value; | ||
return method.requireResource(file, templateFile); | ||
}, | ||
}; | ||
module.exports = { | ||
getPugResolverSync, | ||
getDirResolverSync, | ||
}; | ||
module.exports = resolver; |
125
src/utils.js
const path = require('path'); | ||
const { merge } = require('webpack-merge'); | ||
const loaderName = 'pug-loader'; | ||
const isWin = path.sep === '\\'; | ||
@@ -66,121 +68,2 @@ const isJSON = (str) => typeof str === 'string' && str.length > 1 && str[0] === '{' && str[str.length - 1] === '}'; | ||
/** | ||
* Create regexp to match alias for extends / include / raw include. | ||
* | ||
* @param {string} match The matched alias. | ||
* @return {string} The regex pattern with matched aliases. | ||
*/ | ||
const regexpFileAlias = (match) => `^[~@]?(${match})(?=\\/)`; | ||
/** | ||
* Create regexp to match alias for require(''). | ||
* | ||
* @param {string} match The matched alias. | ||
* @return {string} The regex pattern with matched aliases. | ||
*/ | ||
const regexpRequireAlias = (match) => `(?<=["'\`])[~@]?(${match})(?=\\/)`; | ||
/** | ||
* Resolve a path in pug template. | ||
* | ||
* @param {string} value value The value of extends/include. | ||
* @param {{}} aliases The `resolve.alias` of webpack config. | ||
* @return {string} | ||
*/ | ||
const resolveTemplatePath = (value, aliases) => resolveAlias(value, aliases, regexpFileAlias); | ||
/** | ||
* Resolve the code file path in require(). | ||
* | ||
* @param {string} templateFile The filename of the template where resolves the resource. | ||
* @param {string} value The resource value include require(). | ||
* @param {{}} aliases The resolve.alias from webpack config. | ||
* @param {string[]} dependencies The list of dependencies for watching. | ||
* @return {string} | ||
*/ | ||
const resolveRequireCode = (templateFile, value, aliases, dependencies) => | ||
value.replaceAll(/(require\(.+?\))/g, (value) => { | ||
const [, sourcePath] = /(?<=require\("|'|`)(.+)(?=`|'|"\))/.exec(value) || []; | ||
let resolvedPath = resolveTemplatePath(sourcePath, aliases); | ||
if (resolvedPath === sourcePath) resolvedPath = path.join(path.dirname(templateFile), sourcePath); | ||
// Windows only: fix the path format | ||
if (isWin) resolvedPath = pathToPosix(resolvedPath); | ||
// Important: delete the file from require.cache to allow reload cached files after changes by watch. | ||
delete require.cache[resolvedPath]; | ||
dependencies.push(resolvedPath); | ||
return `require('${resolvedPath}')`; | ||
}); | ||
/** | ||
* Resolve a path in the argument of require() function. | ||
* | ||
* @param {string} templateFile The filename of the template where resolves the resource. | ||
* @param {string} value The resource value include require(). | ||
* @param {{}} aliases The resolve.alias from webpack config. | ||
* @param {LoaderMethod} method The object of the current method. | ||
* @return {string|null} | ||
*/ | ||
const resolveRequireResource = function (templateFile, value, aliases, method) { | ||
// match an argument of require(sourcePath) | ||
const [, sourcePath] = /(?<=require\()(.+)(?=\))/.exec(value) || []; | ||
if (!sourcePath) return value; | ||
let resourcePath = sourcePath; | ||
// replace alias with absolute path | ||
let resolvedPath = resolveAlias(resourcePath, aliases, regexpRequireAlias); | ||
// if the alias is not found in the path, | ||
// then add the absolute path of the current template at the beginning of the argument, | ||
// e.g. like this require('/path/to/template/' + 'filename.jpeg') | ||
if (resolvedPath === resourcePath) { | ||
// delete `./` from path, because at begin will be added full path like `/path/to/current/dir/` | ||
resourcePath = resourcePath.replace(/(?<=[^\.])(\.\/)/, ''); | ||
// if an argument of require() begin with a relative parent path as the string template with a variable, | ||
// like require(`../images/${file}`), then extract the relative path to the separate string | ||
if (resourcePath.indexOf('`../') === 0) { | ||
const relPathRegex = /(?<=`)(.+)(?=\$\{)/; | ||
const [, relPath] = relPathRegex.exec(value); | ||
if (relPath) { | ||
resourcePath = `'${relPath}' + ` + resourcePath.replace(relPathRegex, ''); | ||
} | ||
} | ||
resolvedPath = `'${path.dirname(templateFile)}/' + ${resourcePath}`; | ||
} | ||
// Windows only: fix the path format | ||
if (isWin) resolvedPath = pathToPosix(resolvedPath); | ||
return method.requireResource(resolvedPath); | ||
}; | ||
/** | ||
* Replace founded alias in require argument. | ||
* | ||
* @param {string} value The value of extends/include/require(). | ||
* @param {{}} aliases The `resolve.alias` of webpack config. | ||
* @param {function(string):string} regexp The function return a regex pattern string. The argument is alias name. | ||
* @return {string} The string with replaced alias. | ||
*/ | ||
const resolveAlias = (value, aliases, regexp) => { | ||
if (!aliases) return value; | ||
const patternAliases = Object.keys(aliases).join('|'); | ||
// webpack.alias is empty | ||
if (!patternAliases) return value; | ||
const [, alias] = new RegExp(regexp(patternAliases)).exec(value) || []; | ||
// path contains no alias | ||
if (!alias) return value; | ||
return value.replace(new RegExp(regexp(alias)), aliases[alias]).replace('//', '/'); | ||
}; | ||
/** | ||
* Inject external variables from the resource query, from the loader options | ||
@@ -200,9 +83,7 @@ * and merge them variables with the `locals` variable. | ||
module.exports = { | ||
loaderName, | ||
isWin, | ||
pathToPosix, | ||
getResourceParams, | ||
resolveTemplatePath, | ||
resolveRequireCode, | ||
resolveRequireResource, | ||
injectExternalVariables, | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
56956
5
13
9
523
1
1
884
1
+ Addedansis@^1.3.2
+ Addedansis@1.5.6(transitive)