Skip to content

Commit

Permalink
fix: resolving asset files on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
webdiscus committed Mar 14, 2024
1 parent 4d188f5 commit 88b6c2b
Show file tree
Hide file tree
Showing 18 changed files with 63 additions and 31 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change log

## 3.6.3 (2024-03-14)

- fix: resolving asset files on windows

## 3.6.2 (2024-03-11)

- fix: avoid recompiling all entry templates after changes of a non-entry partial file, [pug-plugin issue](https://github.com/webdiscus/pug-plugin/issues/66)
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": "html-bundler-webpack-plugin",
"version": "3.6.2",
"version": "3.6.3",
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
"keywords": [
"html",
Expand Down
3 changes: 3 additions & 0 deletions src/Loader/Modes/Compile.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const PreprocessorMode = require('./PreprocessorMode');
const { errorToHtml } = require('../Messages/Exeptions');
const { decodeReservedChars, escapeSequences } = require('../Utils');
const { isWin, pathToPosix } = require('../../Common/Helpers');

/**
* Compile into JS function and export as a JS module.
Expand Down Expand Up @@ -28,6 +29,8 @@ class Compile extends PreprocessorMode {
requireExpression(file) {
const quote = this.enclosingQuotes;

if (isWin) file = pathToPosix(file);

return `${quote} + require('${file}') + ${quote}`;
}

Expand Down
5 changes: 3 additions & 2 deletions src/Loader/Preprocessors/Eta/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ const preprocessor = (loaderContext, options) => {
* @return {string} The exported template function.
*/
export(templateFunction, { data }) {
// note: resolved the file is for node, therefore, we need to get the module path plus file for browser
const runtimeFile = path.join(path.dirname(require.resolve('eta')), 'browser.module.mjs');
// resolved the file is for node, therefore, we need to get the module path plus file for browser,
// fix windows-like path into the posix standard :-/
const runtimeFile = path.join(path.dirname(require.resolve('eta')), 'browser.module.mjs').replace(/\\/g, '/');
const exportFunctionName = 'templateFn';
const exportCode = 'module.exports=';

Expand Down
3 changes: 2 additions & 1 deletion src/Loader/Preprocessors/Handlebars/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ const preprocessor = (loaderContext, options) => {
* @return {string} The exported template function.
*/
export(precompiledTemplate, { data }) {
const runtimeFile = require.resolve('handlebars/dist/handlebars.runtime.min');
// fix windows-like path
const runtimeFile = require.resolve('handlebars/dist/handlebars.runtime.min').replace(/\\/g, '/');
const exportFunctionName = 'templateFn';
const exportCode = 'module.exports=';

Expand Down
12 changes: 8 additions & 4 deletions src/Loader/Preprocessors/Nunjucks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@ const preprocessor = (loaderContext, options = {}, { esModule, watch }) => {
if (requiredTemplates.has(templateFile)) continue;

// try to resolve the template file in multiple paths
const file = require.resolve(templateFile, { paths: viewPaths });
let file = require.resolve(templateFile, { paths: viewPaths });

if (file) {
// unique template name as the template path
const templatePath = path.relative(rootContext, file);
// unique template name as the template path, fix windows-like path
const templatePath = path.relative(rootContext, file).replace(/\\/g, '/');

// fix windows-like path
file = file.replace(/\\/g, '/');
dependencies += `dependencies["${templatePath}"] = require("${file}");`;

// if used partial paths (defined in `views` option) to include a partial,
Expand Down Expand Up @@ -113,7 +116,8 @@ const preprocessor = (loaderContext, options = {}, { esModule, watch }) => {
* @return {string} The exported template function.
*/
export(precompiledTemplate, { data }) {
const runtimeFile = require.resolve('nunjucks/browser/nunjucks-slim.min');
// fix windows-like path
const runtimeFile = require.resolve('nunjucks/browser/nunjucks-slim.min').replace(/\\/g, '/');

return `
var nunjucks = require('${runtimeFile}');
Expand Down
10 changes: 7 additions & 3 deletions src/Loader/Preprocessors/Pug/ResolvePlugin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require('path');
const Resolver = require('../../Resolver');
const { encodeReservedChars, isWin, pathToPosix } = require('../../Utils');
const { encodeReservedChars } = require('../../Utils');
const { isWin, pathToPosix } = require('../../../Common/Helpers');

const scriptExtensionRegexp = /\.js[a-z\d]*$/i;
const isRequireableScript = (file) => !path.extname(file) || scriptExtensionRegexp.test(file);
Expand Down Expand Up @@ -146,7 +147,8 @@ const LoaderResolvers = {
const requireExpression = (value, issuer, type = 'default') => {
const [, requiredFile] = /require\((.+?)(?=\))/.exec(value) || [];
const file = requiredFile || value;
//if (isWin) issuer = pathToPosix(issuer);

if (isWin) issuer = pathToPosix(issuer);

if (ResolvePlugin.mode === 'render') {
const requireType = requireTypes[type];
Expand All @@ -155,7 +157,9 @@ const requireExpression = (value, issuer, type = 'default') => {
}

if (ResolvePlugin.mode === 'compile') {
const interpolatedValue = Resolver.interpolate(file, issuer, type);
let interpolatedValue = Resolver.interpolate(file, issuer, type);

if (isWin) interpolatedValue = pathToPosix(interpolatedValue);

return requiredFile ? `require('${interpolatedValue}')` : `require(${interpolatedValue})`;
}
Expand Down
8 changes: 6 additions & 2 deletions src/Loader/Preprocessors/Twig/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ const preprocessor = (loaderContext, options) => {
const resolveDependency = async (token) => {
// if the `namespaces` twig option contains not absolute path, then a parsed path is the path relative to root context
const filePath = TwigEngine.path.parsePath(template, token.value);
const file = path.isAbsolute(filePath) ? filePath : path.resolve(rootContext, filePath);
let file = path.isAbsolute(filePath) ? filePath : path.resolve(rootContext, filePath);

token.value = makeTemplateId(rootContext, file);

// fix windows-like path
file = file.replace(/\\/g, '/');
dependencies.add(file);
loaderContext.addDependency(file);
};
Expand Down Expand Up @@ -144,7 +147,8 @@ const preprocessor = (loaderContext, options) => {
* @return {string} The exported template function.
*/
export(precompiledTemplate, { data, hot }) {
const runtimeFile = require.resolve('twig/twig.min.js');
// fix windows-like path
const runtimeFile = require.resolve('twig/twig.min.js').replace(/\\/g, '/');
const exportFunctionName = 'templateFn';
const exportCode = 'module.exports=';
let loadDependencies = '';
Expand Down
5 changes: 0 additions & 5 deletions src/Loader/Resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,6 @@ class Resolver {
return value;
}

// TODO: check on win
//if (isWin) interpolatedValue = pathToPosix(interpolatedValue);

// remove quotes: '/path/to/file.js' -> /path/to/file.js
let resolvedValue = interpolatedValue.slice(1, -1);
let resolvedFile;
Expand All @@ -304,8 +301,6 @@ class Resolver {
}
if (isScript) resolvedFile = this.resolveScriptExtension(resolvedFile);

// TODO: check on win
//return isWin ? pathToPosix(resolvedFile) : resolvedFile;
return resolvedFile;
}

Expand Down
6 changes: 5 additions & 1 deletion src/Loader/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class Template {
* - mailto:admin@test.com
* - `\\u0027 + require(\\u0027/resolved/path/to/file.ext\\u0027) + \\u0027` // an expression of resolved file via a template engine
*
* Allow special cases when value contains `:`
* - C:\path\to\file.ext
* - image.png?{size:600}
*
* @param {boolean} isBasedir Whether is used the `root` option.
* @param {string} type The type of source: 'style', 'script', 'asset'.
* @param {string} value The attribute value to resolve as an absolute file path.
Expand All @@ -108,7 +112,7 @@ class Template {
value.startsWith('//') ||
value.startsWith('#') ||
value.startsWith('\\u0027') ||
(value.indexOf(':') > 0 && value.indexOf('?{') < 0)
(value.indexOf(':') > 0 && value.indexOf(':\\') < 0 && value.indexOf('?{') < 0)
) {
return false;
}
Expand Down
9 changes: 8 additions & 1 deletion src/Loader/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ const resolveModule = (moduleName, context = process.cwd()) => {
*/
const eachAsync = async (data, fn) => (Array.isArray(data) ? Promise.all(data.map(fn)) : Promise.resolve());

const makeTemplateId = (context, filePath) => path.relative(context, filePath);
/**
* Make template ID as relative posix path.
*
* @param {string} context
* @param {string} filePath
* @return {string}
*/
const makeTemplateId = (context, filePath) => path.relative(context, filePath).replace(/\\/g, '/');

/**
* Inject a string before closing </head> tag.
Expand Down
3 changes: 2 additions & 1 deletion test/cases/hook-afterEmit/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');
* @return {Array<{resource: string, assetFile: string | Array<string>}>}
*/
const manifest = (entries) => {
const relPath = (file) => path.relative(__dirname, file);
// fix windows-like path
const relPath = (file) => path.relative(__dirname, file).replace(/\\/g, '/');
const assets = [];

//console.dir({ entries }, { depth: 7 });
Expand Down
2 changes: 1 addition & 1 deletion test/cases/integrity-publicPath-auto/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
target: 'web',

output: {
publicPath: 'auto', // test empty
//publicPath: 'auto', // test auto
path: path.join(__dirname, 'dist/'),
crossOriginLoading: 'use-credentials', // required for test Subresource Integrity
},
Expand Down
9 changes: 6 additions & 3 deletions test/cases/option-afterEmit/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ const manifest = (entries) => {

for (let entry of entries) {
assets.push({
resource: path.relative(__dirname, entry.resource),
// fix windows-like path
resource: path.relative(__dirname, entry.resource).replace(/\\/g, '/'),
assetFile: entry.assetFile,
});

for (let asset of entry.assets) {
switch (asset.type) {
case 'script':
let assetItem = {
resource: path.relative(__dirname, asset.resource),
// fix windows-like path
resource: path.relative(__dirname, asset.resource).replace(/\\/g, '/'),
assetFile: [],
};
assets.push(assetItem);
Expand All @@ -37,7 +39,8 @@ const manifest = (entries) => {
break;
case 'style':
assets.push({
resource: path.relative(__dirname, asset.resource),
// fix windows-like path
resource: path.relative(__dirname, asset.resource).replace(/\\/g, '/'),
assetFile: asset.assetFile,
});
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = {
},
beforePreprocessor: (template, { resourcePath, data }) => {
let sitename = 'Homepage';
if (resourcePath.includes('/about.html')) sitename = 'About';
if (resourcePath.includes('about.html')) sitename = 'About';
data.title = data.title.replace('[sitename]', sitename); // modify template data

// test return undefined
Expand Down
2 changes: 1 addition & 1 deletion test/cases/option-beforePreprocessor/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ module.exports = {
const loaderObject = loaderContext.loaders[loaderIndex];

let sitename = 'Homepage';
if (resourcePath.includes('/about.html')) sitename = 'About';
if (resourcePath.includes('about.html')) sitename = 'About';

let dataAsString = JSON.stringify(data).replace('[sitename]', sitename);
const newData = JSON.parse(dataAsString);
Expand Down
2 changes: 1 addition & 1 deletion test/integration-pug.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('require code', () => {
test('require js module relative', () => compareFiles('_pug/require-js-relative'));
});

describe('resolve assets', () => {
describe('resolve assets with require', () => {
test('img attributes', () => compareFiles('_pug/resolve-img-attributes'));
test('img attributes, require', () => compareFiles('_pug/resolve-img-attributes-require'));

Expand Down
7 changes: 4 additions & 3 deletions test/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('plugin hooks', () => {
});

describe('plugin callbacks', () => {
test('beforePreprocessor', () => compareFiles('option-beforePreprocessor'));
test('beforePreprocessor, return template', () => compareFiles('option-beforePreprocessor'));
test('beforePreprocessor, return undefined', () => compareFiles('option-beforePreprocessor-return-undefined'));

test('preprocessor', () => compareFiles('option-preprocessor'));
Expand Down Expand Up @@ -419,12 +419,13 @@ describe('special cases', () => {
// test('resolve hmr file', () => watchCompareFiles('resolve-hmr-file'));
});

describe('integrity', () => {
describe('integrity, common use cases', () => {
// TODO: implement and add tests for preload

// TODO: fix issue on windows
test('script, link, publicPath="auto"', () => compareFiles('integrity-publicPath-auto'));
test('script, link, publicPath=""', () => compareFiles('integrity-publicPath-empty'));
test('script, link, publicPath="/"', () => compareFiles('integrity-publicPath-root'));
test('script, link, publicPath="auto"', () => compareFiles('integrity-publicPath-auto'));

test('split chunks', () => compareFiles('integrity-split-chunks'));
test('import css', () => compareFiles('integrity-import-css-in-js'));
Expand Down

0 comments on commit 88b6c2b

Please sign in to comment.