Skip to content

Commit

Permalink
release v1.7.0
Browse files Browse the repository at this point in the history
- change the evaluation to interpolation of required files for `compile` method
- 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
  • Loading branch information
webdiscus committed Feb 7, 2022
1 parent c5ca7e3 commit e3738a0
Show file tree
Hide file tree
Showing 128 changed files with 753 additions and 307 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# 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 `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)
- added supports the `htmlWebpackPlugin.options` in pug template, #8
- added test case for require fonts in pug template
Expand Down
112 changes: 94 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,19 +494,77 @@ each item in myData
<a id="usage-embedded-resources" name="usage-embedded-resources" href="#usage-embedded-resources"></a>
## Usage embedded resources

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

```pug
img(src=require('./path/to/image.jpeg'))
```

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
> - the file in the current directory `MUST` start with `./`:
> ```pug
> img(src=require('./image.jpeg'))
> img(src=require('./sub/path/to/image.jpeg'))
> ```
> - the file in the parent directory `MUST` start with `../`:
> ```pug
> img(src=require('../images/image.jpeg'))
> ```
> - the file in the directory defined in `option.base` `MUST` start with `/`:
> ```pug
> img(src=require('/src/assets/images/image.jpeg'))
> ```
> - the file in the directory defined by `webpack aliase` `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 the 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 an alias from the `paths` defined in `tsconfig.json` with the `compile` method has the 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
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg)/,
test: /\.(png|jpg|jpeg|svg|ico)/,
type: 'asset/resource',
generator: {
filename: 'assets/images/[name].[hash:8][ext]',
Expand All @@ -516,13 +574,31 @@ module.exports = {
},
};
```
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/).

The example of dynamically generating embedded resources in template:
```pug
- files = ['image1.jpeg', 'image2.jpeg', 'image3.jpeg']
each file in files
img(src=require(file))
img(src=require(`./path/to/${file})`)
```

### File resolving examples
Expand All @@ -531,24 +607,24 @@ The example of webpack alias used in the table below:
```
resolve: {
alias: {
Images: path.join(__dirname, 'src/images/'),
Images: path.join(__dirname, 'src/assets/images/'),
},
}
```

| 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'))` | || |


<a id="usage-with-angular-component" name="usage-with-angular-component" href="#usage-with-angular-component"></a>
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@webdiscus/pug-loader",
"version": "1.6.4",
"version": "1.7.0",
"description": "The pug loader resolves paths and webpack aliases in a pug template and compiles it to HTML or into a template function.",
"keywords": [
"pug",
Expand Down Expand Up @@ -70,19 +70,19 @@
"webpack-merge": "^5.8.0"
},
"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",
"rimraf": "^3.0.2",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"webpack": "^5.67.0"
"webpack": "^5.68.0"
}
}
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const compilePugContent = function (content, callback) {

const locals = merge(loaderOptions.data || {}, htmlWebpackPluginOptions, resourceParams),
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);

callback(null, result);
};
Expand Down
143 changes: 58 additions & 85 deletions src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,65 @@

const { executeTemplateFunctionException } = require('./exeptions');

/**
* @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),
},
],
};

/**
Expand All @@ -42,7 +78,7 @@ const compileRequire = (file, templateFile) => {
* @returns {string}
*/
const renderRequire = (file, templateFile) => {
let resolvedFile = loaderResolver.resolve(file, templateFile);
const resolvedFile = loader.resolver.resolve(file, templateFile);

return `\\u0027 + require(\\u0027${resolvedFile}\\u0027) + \\u0027`;
};
Expand All @@ -54,7 +90,7 @@ const renderRequire = (file, templateFile) => {
* @param {string} templateFile The template file.
* @returns {string}
*/
const htmlRequire = (file, templateFile) => loaderResolver.resolve(file, templateFile);
const htmlRequire = (file, templateFile) => loader.resolver.resolve(file, templateFile);

/**
* @param {boolean} esModule
Expand Down Expand Up @@ -84,67 +120,4 @@ const runTemplateFunction = (funcBody, locals, methodRequire, templateFile) => {
return result;
};

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;
Loading

0 comments on commit e3738a0

Please sign in to comment.