Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@webdiscus/pug-loader

Package Overview
Dependencies
Maintainers
1
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@webdiscus/pug-loader - npm Package Compare versions

Comparing version 1.6.4 to 1.7.0-beta.0

8

CHANGELOG.md
# Change log
## 1.7.0 (2022-02-07)
- possible BREAKING CHANGE (low probability): limiting for the method `compile` by resolving a variable in the argument of require() used in pug, see [resolve resources](https://github.com/webdiscus/pug-loader#resolve_resources) .\
The methods `render` and `html` are not affected.
- change the evaluation to interpolation of required files for the `compile` method () to fix issue `undefined variable`
- fix issue `undefined variable` for method `compile` by use the variables in pug w/o optional chaining
- added tests for the `compile` and `render` methods
- update packages
## 1.6.4 (2022-01-31)

@@ -4,0 +12,0 @@ - added supports the `htmlWebpackPlugin.options` in pug template, #8

12

package.json
{
"name": "@webdiscus/pug-loader",
"version": "1.6.4",
"version": "1.7.0-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.",

@@ -73,17 +73,17 @@ "keywords": [

"devDependencies": {
"@babel/core": "^7.16.12",
"@babel/core": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@types/jest": "^27.4.0",
"picocolors": "^1.0.0",
"css-loader": "^6.5.1",
"css-loader": "^6.6.0",
"html-loader": "^3.1.0",
"html-webpack-plugin": "^5.5.0",
"jest": "^27.4.7",
"jest": "^27.5.0",
"jstransformer-markdown-it": "^2.1.0",
"prettier": "^2.5.1",
"pug-plugin": "^1.2.4",
"pug-plugin": "^1.3.0-beta.0",
"rimraf": "^3.0.2",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"webpack": "^5.67.0"
"webpack": "^5.68.0"
}
}

@@ -497,3 +497,3 @@ <div align="center">

For processing image resources in templates with webpack use the `require()` function:
To handle resources in pug with webpack use the `require()` function:

@@ -504,3 +504,61 @@ ```pug

To handles embedded resources in pug add the webpack module `asset/resource`:
<a id="resolve_resources" name="resolve_resources" href="#resolve_resources"></a>
> ###💡 Resolve resources
> - in the current directory the path `MUST` start with `./`:
> ```pug
> img(src=require('./image.jpeg'))
> img(src=require('./sub/path/to/image.jpeg'))
> ```
> - in the parent directory the path `MUST` start with `../`:
> ```pug
> img(src=require('../images/image.jpeg'))
> ```
> - in the directory defined in `option.base` the path `MUST` start with `/`:
> ```pug
> img(src=require('/src/assets/images/image.jpeg'))
> ```
> - in the directory defined by `webpack aliase` the path `MAY` start with `~` or `@`, e.g. with the alias `Images: path.join(__dirname, 'src/assets/images/')`:
> ```pug
> img(src=require('Images/image.jpeg'))
> img(src=require('~Images/image.jpeg'))
> img(src=require('@Images/image.jpeg'))
> ```
> - ⚠️ using a variable with the `compile` method has a limitation: the variable `MUST NOT` contain a path, only a filename, because is interpolated at compile time:
> ```pug
> - const file = 'image.jpeg'
> img(src=require('./path/to/' + file)) // sub directory
> img(src=require('../path/to/' + file)) // parent directory
> img(src=require('/path/to/' + file)) // option.base directory
> img(src=require('~Images/' + file)) // webpack alias
> ```
> but in current directory, the filename `MUST` start with `./`:
> ```pug
> - const file = './image.jpeg'
> img(src=require(file))
> ```
> - ⚠️ using a alias from the `paths` defined in `tsconfig.json` with the `compile` method has a limitation: the required argument `MUST` be a string only, the webpack not supports an expression with alias:\
> tsconfig.json
> ```js
> {
> "compilerOptions": {
> "paths": {
> "@Images/*": ["assets/images/*"]
> }
> }
> }
> ```
> ```pug
> - const file = './image.jpeg'
> img(src=require('@Images/image.jpeg')) // webpack alias resolved via `resolve.plugiins` from `tsconfig.json`
> img(src=require('@Images/' + file)) // ERROR: Can't resolve '@Images' in require expression.
> ```
> - using a variable with `render` and `html` methods has no limitation: the variable `MAY` contain a path, because is resolved at runtime:
> ```pug
> - const file = '../parent/path/to/image.jpeg'
> img(src=require(file))
> img(src=require('~Images/' + file))
> img(src=require('@Images/' + file))
> ```
To handles images in pug add the webpack module `asset/resource`:
```js

@@ -511,3 +569,3 @@ module.exports = {

{
test: /\.(png|jpg|jpeg)/,
test: /\.(png|jpg|jpeg|svg|ico)/,
type: 'asset/resource',

@@ -522,2 +580,20 @@ generator: {

```
To handles fonts in pug add the webpack module `asset/resource`:
```js
module.exports = {
module: {
rules: [
{
test: /\.(eot|ttf|woff|woff2)/,
type: 'asset/resource',
generator: {
filename: 'assets/fonts/[name][ext]',
},
},
]
},
};
```
More information about asset-modules [see here](https://webpack.js.org/guides/asset-modules/).

@@ -529,3 +605,3 @@

each file in files
img(src=require(file))
img(src=require(`./path/to/${file})`)
```

@@ -539,3 +615,3 @@

alias: {
Images: path.join(__dirname, 'src/images/'),
Images: path.join(__dirname, 'src/assets/images/'),
},

@@ -545,15 +621,15 @@ }

| Code | @webdiscus/<br>pug-loader | pugjs/<br>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> |
| `- 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 = 'images/image.jpeg'`<br>`img(src=require(file))` | <span style="color:green">**OK**</span> | <span style="color:red">fail</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> |
| 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('image.jpeg'))` | ✅ but not recomended | ❌ | ❌ |
| `img(src=require('./image.jpeg'))` | ✅ | ✅ | ✅ |
| `img(src=require('../images/image.jpeg'))` | ✅ | ✅ | ✅ |
| `img(src=require('~Images/image.jpeg'))` | ✅ | ✅ | ✅ |
| `- var file = 'image.jpeg'`<br>``img(src=require(`~Images/${file}`))`` | ✅ | ✅ | ✅ |
| `- var file = './image.jpeg'`<br>`img(src=require(file))` | ✅ | ✅ | ❌ |
| `- var file = './images/image.jpeg'`<br>`img(src=require(file))` | ✅ | ❌ | ❌ |
| `- var file = '../images/image.jpeg'`<br>`img(src=require(file))` | ✅ | ❌ | ❌ |
| `- var file = 'image.jpeg'`<br>``img(src=require(`./images/${file}`))`` | ✅ | ✅ | ✅ |
| `- var file = 'image.jpeg'`<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('./image.jpeg'))` | ✅ | ✅ | ❌ |

@@ -560,0 +636,0 @@

@@ -141,3 +141,3 @@ // add polyfill for node.js >= 12.0.0 && < 15.0.0

funcBody = Object.keys(locals).length ? injectExternalVariables(pugResult.body, locals) : pugResult.body,
result = loaderMethod.run(loaderContext.resourcePath, funcBody, locals, esModule);
result = loaderMethod.export(loaderContext.resourcePath, funcBody, locals, esModule);

@@ -144,0 +144,0 @@ callback(null, result);

@@ -11,25 +11,61 @@ /**

/**
* @type {LoaderResolver}
*/
let loaderResolver;
const loader = {
/**
* @type {LoaderResolver}
*/
resolver: {},
/**
* Array of required files with method compile.
* @type {string[]}
*/
let assetFiles = [];
/**
* @param {LoaderResolver} resolver
*/
setResolver: (resolver) => {
loader.resolver = resolver;
},
/**
* Normalize filename in require() function for method `compile`.
*
* @param {string} file The resource file.
* @param {string} templateFile The template file.
* @returns {string}
*/
const compileRequire = (file, templateFile) => {
const resolvedFile = loaderResolver.resolve(file, templateFile);
assetFiles.push(resolvedFile);
/**
* Loader methods for returning a result.
* @type {LoaderMethod[]}
*/
methods: [
{
// compile into template function and export a JS module
method: 'compile',
queryParam: 'pug-compile',
return `require('${resolvedFile}')`;
requireResource: (file, templateFile) => {
const resolvedFile = loader.resolver.interpolate(file, templateFile);
return `require(${resolvedFile})`;
},
export: (templateFile, funcBody, locals, esModule) => {
return funcBody + ';' + getExportCode(esModule) + 'template;';
},
},
{
// render into HTML and export a JS module
method: 'render',
queryParam: 'pug-render',
requireResource: (file, templateFile) => `__PUG_LOADER_REQUIRE__(${file}, '${templateFile}')`,
export: (templateFile, funcBody, locals, esModule) => {
let result = runTemplateFunction(funcBody, locals, renderRequire, templateFile)
.replace(/\n/g, '\\n')
.replace(/'/g, "\\'")
.replace(/\\u0027/g, "'");
return getExportCode(esModule) + "'" + result + "';";
},
},
{
// render into HTML and return the pure string
// notes:
// - this method require an additional loader, like `html-loader`, to handle HTML string
// - the require() function for embedded resources must be removed to allow handle the `src` in `html-loader`
method: 'html',
queryParam: null,
requireResource: (file, templateFile) => `__PUG_LOADER_REQUIRE__(${file}, '${templateFile}')`,
export: (templateFile, funcBody, locals) => runTemplateFunction(funcBody, locals, htmlRequire, templateFile),
},
],
};

@@ -46,3 +82,3 @@

const renderRequire = (file, templateFile) => {
let resolvedFile = loaderResolver.resolve(file, templateFile);
const resolvedFile = loader.resolver.resolve(file, templateFile);

@@ -59,3 +95,3 @@ return `\\u0027 + require(\\u0027${resolvedFile}\\u0027) + \\u0027`;

*/
const htmlRequire = (file, templateFile) => loaderResolver.resolve(file, templateFile);
const htmlRequire = (file, templateFile) => loader.resolver.resolve(file, templateFile);

@@ -90,65 +126,2 @@ /**

const loader = {
/**
* @param {LoaderResolver} resolver
*/
setResolver: (resolver) => {
loaderResolver = resolver;
},
/**
* Loader methods for returning a result.
* @type {LoaderMethod[]}
*/
methods: [
{
// compile into template function and export a JS module
method: 'compile',
queryParam: 'pug-compile',
requireResource: (file, templateFile) => `__PUG_LOADER_REQUIRE__(${file}, '${templateFile}')`,
run: (templateFile, funcBody, locals, esModule) => {
if (~funcBody.indexOf('__PUG_LOADER_REQUIRE__(')) {
let index = 0;
assetFiles = [];
// execute the code to evaluate required path with variables
// and save resolved filenames in the cache for replacing in the template function
runTemplateFunction(funcBody, locals, compileRequire, templateFile);
// replace the required variable filename with resolved file
funcBody = funcBody.replaceAll(/__PUG_LOADER_REQUIRE__\(.+?\)/g, () => `require('${assetFiles[index++]}')`);
}
return funcBody + ';' + getExportCode(esModule) + 'template;';
},
},
{
// render into HTML and export a JS module
method: 'render',
queryParam: 'pug-render',
requireResource: (file, templateFile) => `__PUG_LOADER_REQUIRE__(${file}, '${templateFile}')`,
run: (templateFile, funcBody, locals, esModule) => {
let result = runTemplateFunction(funcBody, locals, renderRequire, templateFile)
.replace(/\n/g, '\\n')
.replace(/'/g, "\\'")
.replace(/\\u0027/g, "'");
return getExportCode(esModule) + "'" + result + "';";
},
},
{
// render into HTML and return the pure string
// notes:
// - this method require an additional loader, like `html-loader`, to handle HTML string
// - the require() function for embedded resources must be removed to allow handle the `src` in `html-loader`
method: 'html',
queryParam: null,
requireResource: (file, templateFile) => `__PUG_LOADER_REQUIRE__(${file}, '${templateFile}')`,
run: (templateFile, funcBody, locals) => runTemplateFunction(funcBody, locals, htmlRequire, templateFile),
},
],
};
module.exports = loader;

@@ -16,27 +16,2 @@ // the 'enhanced-resolve' package already used in webpack, don't need to define it in package.json

/**
* 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.

@@ -69,4 +44,8 @@ * @param {{}} options The enhanced-resolve options.

* @typedef {Object} LoaderResolver
* @property {string} [basedir = '/']
* @property {Object} [aliases = {}]
* @property {function(basedir:string, path:string, options:{})} init
* @property {(function(context:string, file:string): string)} resolve
* @property {(function(file:string, context:string): string)} resolve
* @property {(function(file:string, context:string): string)} interpolate
* @property {(function(value:string, aliases:{}=): string)} resolveAlias
* @property {function(templateFile:string, value:string, dependencies:string[]): string} resolveRequireCode

@@ -76,4 +55,2 @@ * @property {function(templateFile:string, value:string, method:LoaderMethod): string} resolveRequireResource

let pugLoaderBasedir = '/';
let aliases = null;
let resolveFile = null;

@@ -85,2 +62,4 @@

const resolver = {
basedir: '/',
/**

@@ -92,4 +71,4 @@ * @param {string} basedir The the root directory of all absolute inclusion.

init: (basedir, path, options) => {
pugLoaderBasedir = basedir;
aliases = options.alias;
resolver.basedir = basedir;
resolver.aliases = options.alias;
resolveFile = getFileResolverSync(path, options);

@@ -107,11 +86,11 @@ },

const context = path.dirname(templateFile);
let resolvedPath;
let resolvedPath = null;
// resolve an absolute path by prepending options.basedir
if (file[0] === '/') {
resolvedPath = path.join(pugLoaderBasedir, file);
resolvedPath = path.join(resolver.basedir, file);
}
// resolve a relative file
if (!resolvedPath && file[0] === '.') {
if (resolvedPath == null && file[0] === '.') {
resolvedPath = path.join(context, file);

@@ -121,8 +100,8 @@ }

// resolve a file by webpack `resolve.alias`
if (!resolvedPath) {
resolvedPath = resolveAlias(file, aliases);
if (resolvedPath == null) {
resolvedPath = resolver.resolveAlias(file, resolver.aliases);
}
// fallback to enhanced resolver
if (!resolvedPath) {
if (resolvedPath == null) {
try {

@@ -141,2 +120,80 @@ resolvedPath = resolveFile(context, file);

/**
* Interpolate filename for `compile` method.
*
* @note: the file is the argument of require() and can be any expression, like require('./' + file + '.jpg').
* See https://webpack.js.org/guides/dependency-management/#require-with-expression.
*
* @param {string} file The file to resolve.
* @param {string} templateFile The template file.
* @return {string}
*/
interpolate: (file, templateFile) => {
file = file.trim();
const quote = file[0];
let resolvedPath = null;
// the argument begin with a string quote
if ('\'"`'.indexOf(quote) >= 0) {
const context = path.dirname(templateFile) + '/';
// resolve an absolute path by prepending options.basedir
if (file[1] === '/') {
resolvedPath = file[0] + resolver.basedir + file.substring(2);
}
// resolve a relative file
// fix the issue when the required file has a relative path (`./` or `../`) and is in an included file
if (resolvedPath == null && file.substring(1, 4) === '../') {
resolvedPath = file[0] + context + file.substring(1);
}
// resolve a relative file
if (resolvedPath == null && file.substring(1, 3) === './') {
resolvedPath = file[0] + context + file.substring(3);
}
// resolve a webpack `resolve.alias`
if (resolvedPath == null) {
resolvedPath = resolver.resolveAlias(file.substring(1));
if (resolvedPath) resolvedPath = file[0] + resolvedPath;
}
if (isWin) resolvedPath = pathToPosix(resolvedPath);
} else {
// fix webpack require issue `Cannot find module` for the case:
// - var file = './image.jpeg';
// require(file) <- error
// require(file + '') <- solution
file += " + ''";
}
return resolvedPath || file;
},
/**
* Resolve an alias in the argument of require() function.
*
* @param {string} value The value of extends/include/require().
* @param {{}} [aliases = resolver.aliases] The `resolve.alias` of webpack config.
* @return {string | null} If found an alias return resolved normalized path otherwise return false.
*/
resolveAlias: (value, aliases = resolver.aliases) => {
if (!aliases) return null;
const patternAliases = Object.keys(aliases).join('|');
// webpack.alias is empty
if (!patternAliases) return null;
const [, alias] = new RegExp(aliasRegexp(patternAliases)).exec(value) || [];
// path contains no alias
if (!alias) return null;
let resolvedFile = value.replace(new RegExp(aliasRegexp(alias)), aliases[alias]);
return path.join(resolvedFile);
},
/**
* Resolve the source path in require().

@@ -143,0 +200,0 @@ *

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc