Skip to content

Commit

Permalink
added support for webpack alias an array of paths, #10
Browse files Browse the repository at this point in the history
fix optional prefix of alias in request when an alias name self contains the prefix
  • Loading branch information
webdiscus committed Feb 10, 2022
1 parent e3738a0 commit 1290902
Show file tree
Hide file tree
Showing 53 changed files with 370 additions and 66 deletions.
5 changes: 4 additions & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
coverage:
status:
project:
default:
target: 95%
patch:
default:
target: 90%
target: 95%

ignore:
- test/.*
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change log

## 1.7.1 (2022-02-10)
- added support for webpack alias an array of paths, [#10](https://github.com/webdiscus/pug-loader/issues/10)
- fix optional prefix of alias in request when an alias name self contains the prefix

## 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.
Expand Down
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,13 @@ Values:

> **Note:** The option `esModule` is irrelevant for the `html` method, because it returns a pure HTML string.
> For generates smaller and faster JS code, it is recommended to use this options:
> ```js
> {
> method: 'render',
> esModule: true
> }
>
> ```
💡 For generates smaller and faster template function, it is recommended to use following options:
```js
{
method: 'render',
esModule: true
}
```

### `data`
Type: `Object` Default: `{}`<br>
Expand Down Expand Up @@ -612,19 +611,19 @@ resolve: {
}
```

| Example in pug template | @webdiscus/<br>pug-loader<br>`render` / `html` methods | @webdiscus/<br>pug-loader<br>`compile` method | pugjs/<br>pug-loader |
| 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'))` ||||
| `img(src=require('logo.png'))` | ✅ but 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'))` ||||


<a id="usage-with-angular-component" name="usage-with-angular-component" href="#usage-with-angular-component"></a>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@webdiscus/pug-loader",
"version": "1.7.0",
"version": "1.7.1",
"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
17 changes: 3 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const containRequire = (obj) => obj.val && typeof obj.val === 'string' && obj.va
const compilePugContent = function (content, callback) {
let pugResult = {};
const loaderContext = this,
webpackOptionsResolve = getWebpackOptionsResolve(loaderContext),
webpackOptionsResolve = loaderContext.hasOwnProperty('_compiler')
? loaderContext._compiler.options.resolve || {}
: {},
loaderOptions = loaderContext.getOptions() || {},
esModule = loaderOptions.esModule === true,
resourceParams = getResourceParams(loaderContext.resourceQuery),
Expand Down Expand Up @@ -169,19 +171,6 @@ const getHtmlWebpackPluginOptions = (loaderContext) => {
return options;
};

/**
* @param {Object} loaderContext The context object of webpack loader.
* @returns {{}}
*/
const getWebpackOptionsResolve = (loaderContext) => {
let options = {};
if (loaderContext.hasOwnProperty('_compiler')) {
options = loaderContext._compiler.options.resolve || {};
}

return options;
};

module.exports = function (content, map, meta) {
const callback = this.async();
loaderContext = this;
Expand Down
80 changes: 53 additions & 27 deletions src/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,25 @@ const path = require('path');
const { isWin, pathToPosix } = require('./utils');
const { resolveException } = require('./exeptions');

const aliasRegexp = /^([~@])?(.*?)(?=\/)/;

/**
* Create regexp to match alias.
*
* @param {string} match The matched alias.
* @return {string} The regexp pattern with matched aliases.
* @param {string} request
* @returns {{aliasName: string, ignorePrefix: boolean, targetPath: string || array || null}}
*/
const aliasRegexp = (match) => `^[~@]?(${match})(?=\\/)`;
const parseAliasInRequest = (request) => {
const [, prefix, alias] = aliasRegexp.exec(request) || [];
const aliasName = (prefix || '') + (alias || '');
const targetPath = resolver.aliases[aliasName];
const ignorePrefix = prefix != null && alias != null && targetPath == null;

return {
// whether a prefix should be ignored to try resolve alias w/o prefix
ignorePrefix,
aliasName,
targetPath,
};
};

/**
* @param {string} path The start path to resolve.
Expand Down Expand Up @@ -42,10 +54,12 @@ const getFileResolverSync = (path, options) => {
* @typedef {Object} LoaderResolver
* @property {string} [basedir = '/']
* @property {Object} [aliases = {}]
* @property {boolean} hasAlias
* @property {boolean} hasPlugins
* @property {function(basedir:string, path:string, options:{})} init
* @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(value:string): string)} resolveAlias
* @property {function(templateFile:string, value:string, dependencies:string[]): string} resolveRequireCode
* @property {function(templateFile:string, value:string, method:LoaderMethod): string} resolveRequireResource
*/
Expand All @@ -57,6 +71,8 @@ let resolveFile = null;
*/
const resolver = {
basedir: '/',
hasAlias: false,
hasPlugins: false,

/**
* @param {string} basedir The the root directory of all absolute inclusion.
Expand All @@ -65,7 +81,10 @@ const resolver = {
*/
init: (basedir, path, options) => {
resolver.basedir = basedir;
resolver.aliases = options.alias;
resolver.aliases = options.alias || {};
resolver.hasAlias = Object.keys(resolver.aliases).length > 0;
resolver.hasPlugins = options.plugins && Object.keys(options.plugins).length > 0;

resolveFile = getFileResolverSync(path, options);
},

Expand All @@ -92,13 +111,19 @@ const resolver = {

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

// fallback to enhanced resolver
if (resolvedPath == null) {
if (resolvedPath == null || Array.isArray(resolvedPath)) {
try {
resolvedPath = resolveFile(context, file);
let request = file;
if (Array.isArray(resolvedPath)) {
// remove optional prefix in request for enhanced resolver
const { ignorePrefix } = parseAliasInRequest(request);
if (ignorePrefix) request = request.substring(1);
}
resolvedPath = resolveFile(context, request);
} catch (error) {
resolveException(error, file, templateFile);
}
Expand Down Expand Up @@ -147,7 +172,16 @@ const resolver = {
// resolve a webpack `resolve.alias`
if (resolvedPath == null) {
resolvedPath = resolver.resolveAlias(file.substring(1));
if (resolvedPath) resolvedPath = file[0] + resolvedPath;

if (typeof resolvedPath === 'string') {
resolvedPath = file[0] + resolvedPath;
} else if (Array.isArray(resolvedPath)) {
// try to resolve via enhanced resolver by webpack self at compilation time
resolvedPath = file;
// remove optional prefix in request for enhanced resolver
const { ignorePrefix } = parseAliasInRequest(file.substring(1));
if (ignorePrefix) resolvedPath = file[0] + file.substring(2);
}
}

if (isWin && resolvedPath != null) resolvedPath = pathToPosix(resolvedPath);
Expand All @@ -165,26 +199,18 @@ const resolver = {
/**
* 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.
* @param {string} request The value of extends/include/require().
* @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;
resolveAlias: (request) => {
if (resolver.hasAlias === false) return null;

let resolvedFile = value.replace(new RegExp(aliasRegexp(alias)), aliases[alias]);
let { ignorePrefix, aliasName, targetPath } = parseAliasInRequest(request);
// try resolve alias w/o prefix
if (ignorePrefix === true) targetPath = resolver.aliases[aliasName.substring(1)];

return path.join(resolvedFile);
return typeof targetPath === 'string' ? path.join(targetPath + request.substring(aliasName.length)) : targetPath;
//return typeof targetPath === 'string' ? targetPath + request.substring(aliasName.length) : null;
},

/**
Expand Down
2 changes: 1 addition & 1 deletion test/cases/extends-alias/src/template/index.pug
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extends ~Includes/layout

block head
append head
script
| console.log('Test block head')

Expand Down
1 change: 0 additions & 1 deletion test/cases/extends-alias/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ module.exports = {
resolve: {
alias: {
Includes: path.join(basePath, 'src/includes/'),
Template: path.join(basePath, 'src/template/'),
},
},

Expand Down
2 changes: 1 addition & 1 deletion test/cases/include-alias-resolve.alias/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = {
test: /\.pug$/,
loader: 'pug-loader',
options: {
method: 'compile',
method: 'render',
},
},
],
Expand Down
2 changes: 2 additions & 0 deletions test/cases/include-alias-resolve.plugins/expected/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Test includes</h1><h2>include A</h2><p>mixin A</p><h3>include B</h3><p>mixin B</p><h4>include C</h4><h1>Markdown test</h1>
<p>end</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include Includes/mixins
h2 include A
+testA
include Includes/include-b
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include Includes/mixins
h3 include B
+testB
include Includes/include-c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
h4 include C
include:markdown-it Includes/markdown.md
p end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Markdown test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mixin testA
p mixin A

mixin testB
p mixin B
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
h1 Test includes
include Includes/include-a
8 changes: 8 additions & 0 deletions test/cases/include-alias-resolve.plugins/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"Includes/*": ["includes/*"]
}
}
}
41 changes: 41 additions & 0 deletions test/cases/include-alias-resolve.plugins/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const path = require('path');
const PugPlugin = require('../../pug-plugin');
const TsConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

const basePath = path.resolve(__dirname);
const webRootPath = path.join(__dirname, 'public/');

module.exports = {
stats: {
children: true,
},

mode: 'production',

resolve: {
plugins: [new TsConfigPathsPlugin({ configFile: path.join(basePath, 'tsconfig.json') })],
},

output: {
path: webRootPath,
publicPath: '',
},

entry: {
index: 'src/template/index.pug',
},

plugins: [new PugPlugin()],

module: {
rules: [
{
test: /\.pug$/,
loader: 'pug-loader',
options: {
method: 'render',
},
},
],
},
};
1 change: 1 addition & 0 deletions test/cases/require-alias-array-compile/expected/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<img src="/assets/images/image.f78b30f4.jpeg"><img class="prefix" src="/assets/images/logo.f78b30f4.jpeg"><img class="char" src="/assets/images/logo.f78b30f4.jpeg"><img class="a1" src="/assets/images/logo-a1.f78b30f4.jpeg"><img class="a2" src="/assets/images/logo-a2.f78b30f4.jpeg"><img class="a3" src="/assets/images/logo-a3.f78b30f4.jpeg"><img class="b1" src="/assets/images/logo-b1.f78b30f4.jpeg"><img class="b2" src="/assets/images/logo-b2.f78b30f4.jpeg"><img class="b3" src="/assets/images/logo-b3.f78b30f4.jpeg"><img class="c1" src="/assets/images/logo-c1.f78b30f4.jpeg"><img class="c2" src="/assets/images/logo-c2.f78b30f4.jpeg"><img class="c3" src="/assets/images/logo-c3.f78b30f4.jpeg"><img class="d1" src="/assets/images/logo-d1.f78b30f4.jpeg"><img class="d2" src="/assets/images/logo-d2.f78b30f4.jpeg"><img class="d3" src="/assets/images/logo-d3.f78b30f4.jpeg">
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1290902

Please sign in to comment.