New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

pug-plugin

Package Overview
Dependencies
Maintainers
1
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pug-plugin - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

12

CHANGELOG.md
# Change log
## 1.2.0 (2022-01-11)
- added support `webpack serve`
- added support require of style source directly in pug, e.g.:
```pug
link(rel='stylesheet' href=require('~Styles/main.scss'))
```
- added ansi styling by console output
- improve performance
- code optimisation
## 1.1.1 (2021-12-10)
- update pug-loader: fixed path issues on windows
- update pug-loader: fixed path issues on Windows

@@ -6,0 +16,0 @@ ## 1.1.0 (2021-12-07)

36

package.json
{
"name": "pug-plugin",
"version": "1.1.1",
"description": "The pug plugin for webpack to handle pug files from webpack entries and save them as HTML.",
"version": "1.2.0",
"description": "The pug plugin to handle the pug, html, css, scss files in webpack entry and extract css from pug.",
"keywords": [

@@ -9,9 +9,14 @@ "pug",

"template",
"css",
"scss",
"styles",
"extract",
"webpack",
"entry",
"pug-loader",
"assets",
"resource",
"plugin",
"loader",
"pug-plugin",
"html-loader",
"HtmlWebpackPlugin"
"pug-loader"
],

@@ -57,18 +62,21 @@ "license": "ISC",

"dependencies": {
"@webdiscus/pug-loader": "^1.5.1",
"@webdiscus/pug-loader": "^1.6.0-alpha1",
"ansis": "^1.3.2",
"webpack-merge": "^5.8.0"
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.4",
"@types/jest": "^27.0.3",
"@babel/core": "^7.16.7",
"@babel/preset-env": "^7.16.8",
"@types/jest": "^27.4.0",
"css-loader": "^6.5.1",
"html-loader": "^3.0.1",
"jest": "^27.4.3",
"html-loader": "^3.1.0",
"jest": "^27.4.7",
"mini-css-extract-plugin": "^2.4.6",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"sass": "^1.44.0",
"sass-loader": "^12.3.0",
"webpack": "^5.64.4"
"sass": "^1.47.0",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"webpack": "^5.65.0"
}
}

@@ -1,2 +0,2 @@

[![npm version](https://badge.fury.io/js/pug-plugin.svg)](https://badge.fury.io/js/pug-plugin)
[![npm](https://img.shields.io/npm/v/pug-plugin?logo=npm&color=brightgreen "npm package")](https://www.npmjs.com/package/pug-plugin "download npm package")
[![node](https://img.shields.io/node/v/pug-plugin)](https://nodejs.org)

@@ -10,3 +10,4 @@ [![node](https://img.shields.io/github/package-json/dependency-version/webdiscus/pug-plugin/peer/webpack)](https://webpack.js.org/)

Webpack plugin for the [Pug](https://pugjs.org) templates.\
This plugin extract HTML and CSS from `pug` `html` `scss` `css` files defined by `webpack entry` into output directory.
This plugin extract HTML and CSS from `pug` `html` `scss` `css` files defined by `webpack entry`
and dynamically replace in a template the required source filename of CSS, image with a public hashed name.

@@ -17,3 +18,4 @@ Using the `pug-plugin` and `pug` `html` `scss` `css` assets in the `webpack entry` no longer requires additional plugins such as:

- [webpack-remove-empty-scripts](https://github.com/webdiscus/webpack-remove-empty-scripts)
or [webpack-fix-style-only-entries](https://github.com/fqborges/webpack-fix-style-only-entries) (bug fix plugins for `mini-css-extract-plugin`)
or [webpack-fix-style-only-entries](https://github.com/fqborges/webpack-fix-style-only-entries) \
(bug fix plugins for `mini-css-extract-plugin`)
- [pug-loader](https://github.com/webdiscus/pug-loader) (this loader is already included in the `pug-plugin`)

@@ -26,6 +28,39 @@

```console
```bash
npm install pug-plugin --save-dev
```
## Quick Start
The minimal configuration in `webpack.config.js`:
```js
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'public/'),
publicPath: '/', // must be defined any path, `auto` is not supported
},
entry: {
index: './src/pages/index.pug', // ==> public/index.html
},
plugins: [
new PugPlugin(), // add the plugin
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader, // add the pug-loader
},
],
},
};
```
> **Note**: this configuration work without `html-webpack-plugin`.
## Motivation

@@ -139,3 +174,3 @@

outputPath: path.join(__dirname, 'public/assets/css/'),
filename: isProduction ? '[name][contenthash:8].css' : '[name].css'
filename: isProduction ? '[name].[contenthash:8].css' : '[name].css'
},

@@ -192,2 +227,11 @@ ],

```
- extract CSS via `require()` directly in pug and replace the source filename with a public hashed name. In this case is no need to define the style in the webpack entry:
```pug
link(rel='stylesheet' href=require('~Styles/main.scss'))
```
output
```html
<link rel="stylesheet" href="/assets/css/main.6f4d012e.css">
```
[see complete example of usage](#require-style)

@@ -337,11 +381,102 @@ <a id="options" name="options" href="#options"></a>

### Extract CSS file from SASS
<a id="require-style" name="require-style" href="#require-style"></a>
### Extract CSS via `require` in pug
Dependencies:
- `css-loader` this loader translates CSS into JS strings
- `sass-loader` need to handle the `.scss` file type
- `sass` needed for `sass-loader` to compile Sass CSS
Dependencies:
- `css-loader` handles `.css` files and prepare CSS for any CSS extractor
- `sass-loader` handles `.scss` files
- `sass` compiles Sass to CSS
Install: `npm install css-loader sass sass-loader --save-dev`
In this case no to need define the style in webpack entry.
The CSS will be extracted from a style source required directly in the pug template.
The pug template `src/templates/index.pug`:
```pug
html
head
link(href=require('~Styles/my-style.scss'))
body
p Hello World!
```
Add to the `webpack.config.js` following:
```js
module.exports = {
entry: {
// ...
index: 'src/templates/index.pug', // the pug file with required style
},
plugins: [
// ...
// handle pug and style files defined in webpack.entry
new PugPlugin({
modules: [
PugPlugin.extractCss(), // enable CSS extraction
],
}),
],
module: {
rules: [
// ...
{
test: /\.pug$/,
loader: PugPlugin.loader, // the pug-loader is already included in the PugPlugin
},
{
test: /\.(css|sass|scss)$/,
type: 'asset/resource', // extract css in pug via require
generator: {
filename: 'assets/css/[name].[hash:8].css', // save extracted css in public path
},
use: [ 'css-loader', 'sass-loader' ], // extract css from a style source
}
],
},
}
```
The generated HTML:
```html
<html>
<head>
<link rel="stylesheet" href="/assets/css/main.f57966f4.css">
</head>
<body>
<p>Hello World!</p>
</body>
</html>
```
> **Note**: don't needed any additional plugin, like `mini-css-extract-plugin`.
## ! ACHTUNG ! ATTENTION !
### STOP import styles in JavaScript! This is very BAD praxis.
**DON'T DO IT :** `import ./src/styles.scss` This is popular but `dirty way`!
### Clarification
The importing of styles in JavaScript triggers the events in Webpack which call the `mini-css-extract-plugin` loader
to extract CSS from imported style source. Then the `html-webpack-plugin` using a magic add the `<link href="styles.css">` with filename of extracted CSS to any HTML file in head at last position.
Your can't define in which HTML file will be added style and in which order. You are not in control of this process!
This process requires two different plugins and has poor performance.\
The single `pug-plugin` does it with right way, in one step and much faster.
> ### Correct ways
> - add a source style file directly in pug via `require`, like `link(href=require('~Styles/styles.scss'))`
> - add a compiled css file directly in pug, like `link(href='/assets/styles.css')` and add the source of the style in webpack entry.\
> Yes, in this case may be needed additional assets manifest plugin to replace original filename with hashed name. But this is the right way.\
> In the future, will be added support to automatically replace the asset file name with a hashed one.
### Extract CSS from SASS defined in webpack entry
Dependencies:
- `css-loader` handles `.css` files and prepare CSS for any CSS extractor
- `sass-loader` handles `.scss` files
- `sass` compiles Sass to CSS
Install: `npm install css-loader sass sass-loader --save-dev`
webpack.config.js

@@ -363,6 +498,6 @@ ```js

modules: [
// add the module to extract CSS into output file
// add the module to extract CSS
// see options https://github.com/webdiscus/pug-plugin#options
PugPlugin.extractCss({
filename: isProduction ? '[name][contenthash:8].css' : '[name].css',
filename: isProduction ? '[name].[contenthash:8].css' : '[name].css',
})

@@ -464,3 +599,3 @@ ],

PugPlugin.extractCss({
filename: isProduction ? '[name][contenthash:8].css' : '[name].css',
filename: isProduction ? '[name].[contenthash:8].css' : '[name].css',
sourcePath: 'src/assets/sass/',

@@ -467,0 +602,0 @@ outputPath: 'assets/css/',

@@ -1,7 +0,5 @@

const fs = require('fs');
const path = require('path');
const ansis = require('ansis');
const { merge } = require('webpack-merge');
const colstr = require('./color-string');
const { plugin, isFunction, shallowEqual } = require('./utils');
const { plugin, isFunction, requireResource } = require('./utils');
const { extractHtml, extractCss } = require('./modules');

@@ -40,7 +38,7 @@

* and save the file relative by output path, defined in webpack.options.output.path.
* @property {string | function(PathData, AssetInfo): string} filenameTemplate The filename template.
* @property {string | function(PathData, AssetInfo): string} filenameTemplate The filename template or function.
* @property {string} filename The asset filename.
* The template strings support only this substitutions: [name], [base], [path], [ext], [id], [contenthash], [contenthash:nn]
* See https://webpack.js.org/configuration/output/#outputfilename
* @property {string} import
* @property {string} importFile
* @property {string} outputPath

@@ -50,8 +48,17 @@ * @property {string} sourcePath

* See https://webpack.js.org/configuration/output/#outputlibrary
* @property {function(string, AssetEntry, Compilation): string} [postprocess = null] The post process for extracted content from entry.
* @property {function(string, AssetInfo, Compilation): string} [postprocess = null] The post process for extracted content from entry.
* @property {Array} resources
* @property {boolean} [verbose = false] Show an information by handles of the entry in a postprocess.
* @property {RawSource} RawSource The reference of the class compiler.webpack.sources.RawSource.
*/
/**
* @typedef {Object} EntryAssetInfo
* @property {boolean} isEntry True if is the asset from entry, false if asset is required from pug.
* @property {string} entryFile The entry file.
* @property {string | (function(PathData, AssetInfo): string)} filename The filename template or function.
* @property {string} sourceFile The source file.
* @property {string} assetFile The output asset file with relative path by webpack output path.
*/
/**
* @var {PugPluginOptions} defaultOptions

@@ -68,6 +75,8 @@ */

// experimental, reserved feature for the future: each entry has its own local options that override global options
// each entry has its own local options that override global options
modules: [],
};
const MODULE_TYPE = `${plugin}/entry`;
class PugPlugin {

@@ -77,2 +86,8 @@ /** @type {AssetEntry[]} */

/**
* Resources required in the template.
* @type {[]}
*/
entryAssets = [];
entryLibrary = {

@@ -91,12 +106,6 @@ name: 'return',

this.verbose = this.options.verbose;
}
/**
* Get the entry by filename.
*
* @param {string} filename The filename is a key of the compilation assets object.
* @return {AssetEntry}
*/
getEntry(filename) {
return this.entries.find((entry) => entry.filename === filename);
if (options.modules && !Array.isArray(options.modules)) {
this.optionModulesException(options.modules);
}
}

@@ -107,2 +116,10 @@

const webpackOutputPath = compiler.options.output.path;
const webpackOutputPublicPath = compiler.options.output.publicPath;
const { RawSource } = compiler.webpack.sources;
// TODO resolve 'auto' publicPath
if (webpackOutputPublicPath === 'auto') this.publicPathException();
requireResource.publicPath = webpackOutputPublicPath;
// enable library type `jsonp` for compilation JS from source into HTML string via Function()

@@ -113,5 +130,11 @@ if (compiler.options.output.enabledLibraryTypes.indexOf('jsonp') < 0) {

// TODO prevent warning split chunk 240 KB
// const { splitChunks } = compiler.options.optimization;
// if (splitChunks) {
// if (splitChunks.defaultSizeTypes.includes('...')) {
// splitChunks.defaultSizeTypes.push(MODULE_TYPE);
// }
// }
compiler.hooks.entryOption.tap(plugin, (context, entries) => {
const webpackOutputPath = compiler.options.output.path;
if (!this.options.sourcePath) this.options.sourcePath = compiler.options.context;

@@ -131,3 +154,3 @@ if (!this.options.outputPath) this.options.outputPath = webpackOutputPath;

let sourceFile = entry.import[0];
const module = this.options.modules.find((module) => module.enabled !== false && module.test.test(sourceFile));
const module = this.getModule(sourceFile);

@@ -160,3 +183,3 @@ if (!extensionRegexp.test(sourceFile) && !module) continue;

assetFile: undefined,
import: sourceFile,
importFile: sourceFile,
sourcePath: sourcePath,

@@ -166,4 +189,3 @@ outputPath: outputPath,

postprocess: isFunction(postprocess) ? postprocess : null,
verbose: verbose,
RawSource: compiler.webpack.sources.RawSource,
verbose,
};

@@ -173,3 +195,3 @@

// define lazy memoized getter for the property `filename`, it will be generated later
if (!assetEntry.filename)
if (!assetEntry.filename) {
Object.defineProperty(assetEntry, 'filename', {

@@ -181,5 +203,7 @@ get() {

delete this.filename;
return (this.filename = filename);
},
});
}

@@ -193,47 +217,133 @@ return isFunction(filenameTemplate) ? filenameTemplate(pathData, assetInfo) : filenameTemplate;

compiler.hooks.compilation.tap(plugin, (compilation) => {
let { verbose } = this.options;
compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
const verbose = this.verbose;
// todo resolve 'auto' publicPath
if (compilation.outputOptions.publicPath === 'auto') this.publicPathException();
compilation.hooks.buildModule.tap(plugin, (module) => {
if (this.isChunkModuleInEntry(module)) {
module.type = MODULE_TYPE;
}
});
// render source code
compilation.hooks.renderManifest.tap(plugin, (result, { chunk }) => {
const { chunkGraph } = compilation;
const { HotUpdateChunk } = compiler.webpack;
const filenameTemplate = chunk.filenameTemplate;
let source = '',
compiledResult = '';
// don't hot update chunks
// TODO research whether the chunk can be the instance of HotUpdateChunk
if (chunk instanceof HotUpdateChunk) return;
const entry = this.getEntryByName(chunk.name);
// process only entries supported by this plugin
if (!entry) return;
requireResource.resources = {};
for (const module of chunkGraph.getChunkModules(chunk)) {
if (module.type === 'asset/resource') {
requireResource.resources[module.resource] = module.buildInfo.filename;
this.entryAssets.push({
entryFile: entry.importFile,
sourceFile: module.resource,
assetFile: module.buildInfo.filename,
});
} else if (module.type === MODULE_TYPE) {
source = module.originalSource().source().toString();
source = this.getFunctionCode(source);
}
}
if (~source.indexOf('___CSS_LOADER_')) {
compiledResult = this.extractCss(source, { sourceFile: entry.importFile, moduleId: chunk.id });
} else {
compiledResult = this.extractHtml(source, entry);
}
result.push({
render: () => new RawSource(compiledResult),
filenameTemplate,
pathOptions: { chunk, contentHashType: 'javascript' },
identifier: `${plugin}.${chunk.id}`,
hash: chunk.contentHash['javascript'],
});
});
//
compilation.hooks.chunkAsset.tap(plugin, (chunk, filename) => {
// TODO for @next release
// - collect entry files for manifest to replace original name with hashed
//const asset = compilation.getAsset(filename);
//const assetInfo = compilation.assetsInfo.get(filename) || {};
});
// only here can be an asset deleted or emitted
compilation.hooks.processAssets.tap(
{
name: plugin,
// run this process before all others
stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONAL - 1000,
stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
},
(assets) => {
let filename, result;
/** @type {null|string|object|function} result */
let filename;
for (filename in assets) {
const entry = this.getEntry(filename);
const entry = this.getEntryByFilename(filename);
let entryAsset, module, assetFile, source, result, postprocess;
if (!entry) continue;
if (verbose) this.verboseEntry(entry);
if (entry) {
// the asset defined in webpack entry
if (verbose) this.verboseEntry(entry);
assetFile = entry.assetFile;
postprocess = entry.postprocess;
// the result of rendering in the `renderManifest` hook
result = assets[filename].source();
} else {
// the asset required in pug
module = this.getModule(filename);
if (!module) continue;
postprocess = module.postprocess;
const source = assets[filename].source();
// generates the html string from source code
result = new Function('', source)();
if (result == null) this.entryException(entry);
// extract required resource which is not presents in webpack entry
entryAsset = this.entryAssets.find((item) => item.assetFile === filename);
if (!entryAsset) {
// remove double assets from webpack entry processed via `asset/resource`
compilation.deleteAsset(filename);
continue;
}
// detect result of ES module
if (result.default) result = result.default;
assetFile = filename;
source = assets[filename].source().toString();
source = this.getFunctionCode(source);
if (isFunction(result)) {
try {
// pug-loader.method: compile
// note: all external variables are already assigned to locals in the template function via pug-loader,
// the argument must be empty object to avoid error by reading property from undefined
result = result({});
} catch (error) {
this.executeAssetSourceException(error, entry);
if (~source.indexOf('___CSS_LOADER_')) {
const importFile = path.join(webpackOutputPath, filename);
if (verbose) {
entryAsset.assetFile = path.join(webpackOutputPath, assetFile);
this.verboseExtractAsset(entryAsset);
}
result = this.extractCss(source, { sourceFile: importFile, moduleId: filename });
}
}
if (entry.postprocess) {
const info = {
isEntry: !!entry,
entryFile: entry ? entry.file : entryAsset.entryFile,
filename: entry ? entry.filenameTemplate : module.filename,
sourceFile: entry ? entry.importFile : entryAsset.sourceFile,
assetFile,
};
if (result == null) {
this.entryException('The extract from source is undefined.', source, info);
}
if (postprocess) {
try {
result = entry.postprocess(result, entry, compilation);
result = postprocess(result, info, compilation);
} catch (error) {
this.postprocessException(error, entry);
this.postprocessException(error, info);
}

@@ -245,3 +355,3 @@ }

compilation.deleteAsset(filename);
compilation.emitAsset(entry.assetFile, new entry.RawSource(result, false));
compilation.emitAsset(assetFile, new RawSource(result, false));
}

@@ -255,19 +365,142 @@ }

/**
* Extract the html from source code.
*
* @param {string} source
* @param {AssetEntry} entry
* @returns {string}
*/
extractHtml(source, entry) {
let result = new Function('require', source)(requireResource);
if (isFunction(result)) {
try {
return result();
} catch (error) {
this.executeAssetSourceException(error, entry);
}
}
return result;
}
/**
* Extract the css and source map from code generated by`css-loader`.
*
* @param {string} source The source generated by `css-loader`.
* @param {string} sourceFile The full path of source file.
* @param {string} moduleId The id of the css module.
* @returns {string}
*/
extractCss(source, { sourceFile, moduleId }) {
// fix path in `require()` by `css-loader` options: { esModule: false }
// replace `import from` with `require()` by `css-loader` options: { esModule: true }
const matches = ~source.indexOf('require(')
? source.matchAll(/.+?(\w+) = require\("(.+)"\);/g)
: source.matchAll(/import (.+) from "(.+)";/g);
for (const [match, variable, file] of matches) {
const fullPath = path.join(path.dirname(sourceFile), file);
const relPath = path.relative(__dirname, fullPath);
const replace = `var ${variable} = require('${relPath}');`;
source = source.replace(match, replace);
}
// generate the export object of the `css-loader` from source
// this object has the own method `toString()` for concatenation of all source strings
// see node_modules/css-loader/dist/runtime/sourceMaps.js
const result = new Function('require, module', source)(require, { id: moduleId });
return result.toString();
}
/**
* @param {string} filename The filename is a key of the compilation assets object.
* @return {AssetEntry}
*/
getEntryByFilename(filename) {
return this.entries.find((entry) => entry.filename === filename);
}
/**
* @param {string} name The entry name.
* @returns {AssetEntry}
*/
getEntryByName(name) {
return this.entries.find((entry) => entry.name === name);
}
/**
* @param {string} filename
* @returns {ModuleOptions}
*/
getModule(filename) {
return this.options.modules.find((module) => module.enabled !== false && module.test.test(filename));
}
/**
* @param {Object} module The webpack chunk module.
* @returns {boolean}
*/
isChunkModuleInEntry(module) {
const importFile = module.resource;
return importFile ? this.entries.find((entry) => entry.importFile === importFile) !== undefined : false;
}
/**
* Transform source of module to function code.
* @param {string} source
* @returns {string}
*/
getFunctionCode(source) {
if (~source.indexOf('export default')) {
source = source.replace('export default', `return`);
} else if (~source.indexOf('module.exports')) {
source = source.replace(/module.exports\s*=/, `return `);
}
return source;
}
/**
* @param {AssetEntry} entry
*/
verboseEntry(entry) {
if (!entry) return;
if (!this.hasVerboseOut) console.log('\n');
this.hasVerboseOut = true;
console.log(
`[${colstr.yellow(plugin)}] Compile the entry ${colstr.green(entry.name)}\n` +
` - filename: ${typeof entry.filename === 'function' ? colstr.cyan('[Function]') : entry.filename}\n` +
` - import: ${entry.import}\n` +
` - output: ${entry.file}\n`
`${ansis.black.bgYellow(`[${plugin}]`)} Compile the entry ${ansis.green(entry.name)}\n` +
` - filename: ${
typeof entry.filename === 'function' ? ansis.cyan('[Function]') : ansis.magenta(entry.filenameTemplate)
}\n` +
` - source: ${ansis.cyan(entry.importFile)}\n` +
` - output: ${ansis.cyanBright(entry.file)}\n`
);
}
publicPathException() {
/**
* @param {string} entryFile
* @param {string} sourceFile
* @param {string} assetFile
*/
verboseExtractAsset({ entryFile, sourceFile, assetFile }) {
if (!this.hasVerboseOut) console.log('\n');
this.hasVerboseOut = true;
console.log(
`${ansis.black.bgYellow(`[${plugin}]`)} Extract the asset from ${ansis.green(entryFile)}\n` +
` - source: ${ansis.cyan(sourceFile)}\n` +
` - output: ${ansis.cyanBright(assetFile)}\n`
);
}
/**
* @param {ModuleOptions[]} modules
* @throws {Error}
*/
optionModulesException(modules) {
throw new Error(
`\n[${plugin}] This plugin yet not support 'auto' publicPath.\n` +
`Define a publicPath in the webpack configuration, e.g. output: { publicPath: '/' }.\n`
`\n${ansis.black.bgRedBright(`[${plugin}]`)} The plugin option ${ansis.green(
'modules'
)} must be the array of ${ansis.green('ModuleOptions')} but given:\n` +
ansis.cyanBright(JSON.stringify(modules)) +
`\n`
);

@@ -277,18 +510,8 @@ }

/**
* @param {AssetEntry} entry
* @throws {Error}
*/
entryException(entry) {
const existFile = fs.existsSync(entry.import);
publicPathException() {
throw new Error(
`\n[${plugin}] The compiled source from the file "${entry.filename}" is undefined.` +
'\n' +
`Possible reasons: \n` +
(!existFile ? ` - the import file '${entry.import}' is not found\n` : '') +
(existFile ? ` - the compiled file '${entry.file}' is not executable JavaScript\n` : '') +
(entry.library && !shallowEqual(entry.library, this.entryLibrary)
? ` - the library must be undefined or exact ${JSON.stringify(this.entryLibrary)}, but given ${
entry.library
}\n`
: '') +
'\n'
`\n${ansis.black.bgRedBright(`[${plugin}]`)} This plugin yet not support 'auto' publicPath.\n` +
`Define a publicPath in the webpack configuration, e.g. output: { publicPath: '/' }.\n`
);

@@ -298,9 +521,37 @@ }

/**
* @param {string} errorMessage
* @param {string} source
* @param {EntryAssetInfo} info
* @throws {Error}
*/
entryException(errorMessage, source, info) {
let error =
`\n${ansis.black.bgRedBright(`[${plugin}]`)} Fail by entry file ${ansis.cyan(info.assetFile)}. ` +
errorMessage +
'\n' +
ansis.yellow(`Possible reasons:`) +
'\n';
const [unresolvedRequire] = /(<[^>]+require\(.+\).+?>)/.exec(source) || [];
if (unresolvedRequire) {
error += ` - the extracted JavaScript contain not resolved ${ansis.cyanBright('require()')}:\n`;
error += ansis.magenta(unresolvedRequire);
} else {
error += ` - the extracted JavaScript is not executable:\n`;
error += ansis.cyanBright(source);
error += '\n';
}
throw new Error(ansis.white(error) + '\n');
}
/**
* @param {Error} error
* @param {AssetEntry} entry
* @throws {Error}
*/
executeAssetSourceException(error, entry) {
throw new Error(
`\n[${plugin}] Asset source execution failed by the entry '${entry.name}'.\n` +
`The file '${entry.import}'.\n` +
`\n${ansis.black.bgRedBright(`[${plugin}]`)} Asset source execution failed by the entry '${entry.name}'.\n` +
`The file '${entry.importFile}'.\n` +
error

@@ -312,8 +563,9 @@ );

* @param {Error} error
* @param {AssetEntry} entry
* @param {EntryAssetInfo} info
* @throws {Error}
*/
postprocessException(error, entry) {
postprocessException(error, info) {
throw new Error(
`\n[${plugin}] Postprocess execution failed by the entry '${entry.name}'.\n` +
`The file '${entry.import}'.\n` +
`\n${ansis.black.bgRedBright(`[${plugin}]`)} Postprocess execution failed by the entry '${info.entryFile}'.\n` +
`The source file '${info.sourceFile}'.\n` +
error

@@ -320,0 +572,0 @@ );

@@ -1,5 +0,4 @@

const path = require('path');
const { merge } = require('webpack-merge');
const { plugin } = require('./utils');
const colstr = require('./color-string');
const ansis = require('ansis');

@@ -29,3 +28,3 @@ /**

/**
* @param {string} content
* @param {string} content The extracted html.
* @param {AssetEntry} entry

@@ -46,3 +45,3 @@ * @param {Compilation} compilation

/**
* The lightweight plugin module to extract the css and source map from asset content.
* The lightweight plugin module to extract the css from asset content.
* todo test cases:

@@ -66,3 +65,3 @@ * - add supports a chunk name template with [id]

/**
* @param {array} content The result from css-loader.
* @param {string} content The css content generated by css-loader.
* @param {AssetEntry} entry

@@ -73,23 +72,8 @@ * @param {Compilation} compilation

postprocess: (content, entry, compilation) => {
const [item] = content;
const [sourceFile, source, nop, sourceMap] = item;
let sourceMappingURL = '';
if (entry.verbose)
if (entry.verbose) {
console.log(
colstr.bgYellow(`[${plugin}]`, colstr.colors.black) +
colstr.bgGreen(` Extract CSS `, colstr.colors.black) +
colstr.cyan(' ' + entry.file)
ansis.black.bgYellow(`[${plugin}]`) + ansis.black.bgGreen(` Extract CSS `) + ansis.cyan(' ' + entry.file)
);
// add css source map for development mode
if (compilation.options.devtool) {
const rawSourceMap = new entry.RawSource(JSON.stringify(sourceMap));
const mapFile = entry.assetFile + '.map';
compilation.emitAsset(mapFile, rawSourceMap);
sourceMappingURL = `\n/*# sourceMappingURL=${path.basename(mapFile)} */`;
}
return source + sourceMappingURL;
return content;
},

@@ -96,0 +80,0 @@ };

@@ -0,1 +1,2 @@

const path = require('path');
const plugin = 'pug-plugin';

@@ -6,2 +7,28 @@

/**
* Require the resource file from source in pug template.
*
* @param {string} file The required file from source directory.
* @returns {string} The output asset filename generated by filename template.
*/
const requireResource = (file) => {
const self = requireResource;
// normalize the path, e.g.: 'path/to/../to/file' => 'path/to/file'
const normalizedFile = path.join(file);
const assetFile = self.resources[normalizedFile];
return path.join(self.publicPath, assetFile);
};
/**
* @type {string} The the output public path is `webpack.options.output.publicPath`.
*/
requireResource.publicPath = '';
/**
* The mapping of the source file to the generated asset file.
* @type {{key: string}}
*/
requireResource.resources = {};
/**
* Simple equal of two objects.

@@ -26,65 +53,7 @@ *

//const AUTO_PUBLIC_PATH = '__pug_plugin_public_path_auto__';
//const ABSOLUTE_PUBLIC_PATH = 'webpack:///pug-plugin/';
//const SINGLE_DOT_PATH_SEGMENT = '__pug_plugin_single_dot_path_segment__';
// todo Implement 'auto' publicPath, following is just research code from mini-css-extract-plugin:
/*const getPublicPath = (compilation) => {
let { publicPath } = compilation.outputOptions || { publicPath: '' };
//if (typeof options.publicPath === 'string') {
// publicPath = options.publicPath;
//} else if (isFunction(options.publicPath)) {
// publicPath = options.publicPath(this.resourcePath, this.rootContext);
//}
if (publicPath === 'auto') {
publicPath = AUTO_PUBLIC_PATH;
}
const isAbsolutePublicPath = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/.test(publicPath);
const publicPathForExtract = isAbsolutePublicPath
? publicPath
: `${ABSOLUTE_PUBLIC_PATH}${publicPath.replace(/\./g, SINGLE_DOT_PATH_SEGMENT)}`;
return publicPathForExtract;
};*/
// todo Resolve 'auto' publicPath into real path, following is just research code from mini-css-extract-plugin:
/*function resolvePublicPath(filename, outputPath, enforceRelative) {
let depth = -1;
let append = '';
outputPath = outputPath.replace(/[\\/]$/, '');
for (const part of filename.split(/[/\\]+/)) {
if (part === '..') {
if (depth > -1) {
depth--;
} else {
const i = outputPath.lastIndexOf('/');
const j = outputPath.lastIndexOf('\\');
const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
if (pos < 0) return `${outputPath}/`;
append = `${outputPath.slice(pos + 1)}/${append}`;
outputPath = outputPath.slice(0, pos);
}
} else if (part !== '.') {
depth++;
}
}
return depth > 0 ? `${'../'.repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
}*/
module.exports = {
plugin,
isFunction,
requireResource,
shallowEqual,
//AUTO_PUBLIC_PATH,
//ABSOLUTE_PUBLIC_PATH,
//SINGLE_DOT_PATH_SEGMENT,
//getPublicPath,
//resolvePublicPath,
};
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