@hyperse/html-webpack-plugin-loader
Version:
A custom template loader that parses HTML templates for the `html-webpack-plugin` package
419 lines (338 loc) • 12.6 kB
Markdown
# @hyperse/html-webpack-plugin-loader
A powerful HTML template parser and manipulator for webpack applications, providing a fluent API for modifying HTML templates with ease.
<p align="left">
<a aria-label="Build" href="https://github.com/hyperse-io/html-webpack-plugin-loader/actions?query=workflow%3ACI">
<img alt="build" src="https://img.shields.io/github/actions/workflow/status/hyperse-io/html-webpack-plugin-loader/ci-integrity.yml?branch=main&label=ci&logo=github&style=flat-quare&labelColor=000000" />
</a>
<a aria-label="stable version" href="https://www.npmjs.com/package/@hyperse/html-webpack-plugin-loader">
<img alt="stable version" src="https://img.shields.io/npm/v/%40hyperse%2Fhtml-webpack-plugin-loader?branch=main&label=version&logo=npm&style=flat-quare&labelColor=000000" />
</a>
<a aria-label="Top language" href="https://github.com/hyperse-io/html-webpack-plugin-loader/search?l=typescript">
<img alt="GitHub top language" src="https://img.shields.io/github/languages/top/hyperse-io/html-webpack-plugin-loader?style=flat-square&labelColor=000&color=blue">
</a>
<a aria-label="Licence" href="https://github.com/hyperse-io/html-webpack-plugin-loader/blob/main/LICENSE">
<img alt="Licence" src="https://img.shields.io/github/license/hyperse-io/html-webpack-plugin-loader?style=flat-quare&labelColor=000000" />
</a>
</p>
A TypeScript-based HTML template parser that provides a fluent API for manipulating HTML documents. This package is designed to work seamlessly with webpack applications, allowing you to easily modify HTML templates during the build process.
## Features
- 🔄 Fluent API for HTML manipulation
- 🎯 Precise control over HTML document structure
- 📦 Easy integration with webpack
- 🚀 TypeScript support
- 🔍 Built-in support for common HTML modifications:
- 📝 Title tag management
- 🎨 Favicon handling with customizable attributes
- 📱 Meta tags management
- 💅 Style injection (both external and inline)
- 📜 Script injection (both external and inline)
- 🔄 Head and body modifications with position control
## Installation
```bash
npm install --save @hyperse/html-webpack-plugin-loader
```
## API Reference
### TemplateParser
The main class that provides HTML template manipulation capabilities.
```typescript
import { TemplateParser } from '@hyperse/html-webpack-plugin-loader';
// Create a new parser instance
const parser = new TemplateParser(htmlSource);
// Define template options
const templateOptions: TemplateOptions = {
// Set page title
title: 'My Page Title',
// Set website favicon with custom attributes
favicon: {
href: '/favicon.ico',
rel: 'icon',
attributes: {
type: 'image/x-icon',
sizes: '32x32',
},
},
// Set meta tags in head
headMetaTags: ['<meta name="description" content="My page description">'],
// Set external styles in head
headStyles: [
{
id: 'main-css',
href: '/styles/main.css',
position: 'end',
order: 1,
},
],
// Set inline styles in head
headInlineStyles: [
{
id: 'critical-css',
content: 'body { margin: 0; }',
position: 'beginning',
order: 0,
},
],
// Set external scripts in head
headScripts: [
{
id: 'main-js',
src: '/main.js',
position: 'end',
type: 'module',
async: true,
defer: false,
crossOrigin: 'anonymous',
integrity: 'sha384-hash',
nonce: 'abc123',
order: 1,
},
],
// Set inline scripts in head
headInlineScripts: [
{
id: 'inline-script',
content: 'console.log("Hello");',
position: 'end',
order: 2,
},
],
// Set scripts in body
bodyScripts: [
{
id: 'app-js',
src: '/app.js',
position: 'end',
type: 'module',
async: true,
defer: false,
order: 1,
},
],
};
// Use template options to modify HTML
const modifiedHtml = parser
.upsertTitleTag(templateOptions.title)
.upsertFaviconTag(
templateOptions.favicon.href,
templateOptions.favicon.rel,
templateOptions.favicon.attributes
)
.upsertHeadMetaTags(templateOptions.headMetaTags)
.upsertHeadStyles(templateOptions.headStyles)
.upsertHeadInlineStyles(templateOptions.headInlineStyles)
.upsertHeadScripts(templateOptions.headScripts)
.upsertHeadInlineScripts(templateOptions.headInlineScripts)
.upsertBodyScripts(templateOptions.bodyScripts)
.serialize();
```
#### Available Methods
- `upsertTitleTag(title: string)`: Updates or inserts the page title
- `upsertFaviconTag(href: string, rel?: string, attributes?: Record<string, string>)`: Updates or inserts the favicon link with optional rel and custom attributes
- `upsertHeadMetaTags(tags: string[])`: Updates or inserts meta tags in the head
- `upsertHeadStyles(styles: StyleItem[])`: Updates or inserts external style links in the head
- `upsertHeadInlineStyles(styles: StyleInlineItem[])`: Updates or inserts inline style tags in the head
- `upsertHeadScripts(scripts: ScriptItem[])`: Updates or inserts external script tags in the head
- `upsertHeadInlineScripts(scripts: ScriptInlineItem[])`: Updates or inserts inline script tags in the head
- `upsertBodyScripts(scripts: ScriptItem[])`: Updates or inserts script tags in the body
- `serialize()`: Converts the modified document back to HTML string
### `parseTemplate` Function
A utility function that provides a convenient way to parse and modify HTML templates in a single step.
```typescript
import { parseTemplate } from '@hyperse/html-webpack-plugin-loader';
// Parse and modify HTML template in one go
const modifiedHtml = parseTemplate(htmlSource, templateOptions).serialize();
```
The `parseTemplate` function is a shorthand for creating a `TemplateParser` instance and applying all template modifications at once. It accepts two parameters:
- `htmlSource: string`: The source HTML template to parse and modify
- `options: TemplateOptions`: The template modification options (same as described above)
This function is particularly useful when you want to perform all template modifications in a single operation without manually chaining multiple method calls.
### Type Definitions
```typescript
type Position = 'beginning' | 'end';
interface HtmlItemBase {
id: string;
position: Position;
order?: number;
}
interface StyleItem extends HtmlItemBase {
href: string;
}
interface StyleInlineItem extends HtmlItemBase {
content: string;
}
interface ScriptItem extends HtmlItemBase {
src: string;
type?: string;
async?: boolean;
defer?: boolean;
crossOrigin?: string;
integrity?: string;
nonce?: string;
}
interface ScriptInlineItem extends HtmlItemBase {
content: string;
}
interface FaviconItem {
href: string;
rel: string;
attributes: Record<string, string>;
}
interface TemplateOptions {
title?: string;
favicon?: FaviconItem;
headMetaTags?: string[];
headStyles?: StyleItem[];
headInlineStyles?: StyleInlineItem[];
headScripts?: ScriptItem[];
headInlineScripts?: ScriptInlineItem[];
bodyScripts?: ScriptItem[];
}
```
## Webpack Loader Integration with html-webpack-plugin
This package provides seamless integration with webpack through a custom loader. The loader works in conjunction with `html-webpack-plugin` to modify HTML templates during the build process.
### Basic Setup
First, ensure you have both `html-webpack-plugin` and this package installed:
```bash
npm install --save-dev html-webpack-plugin @hyperse/html-webpack-plugin-loader
```
### Webpack Configuration
Add the loader to your webpack configuration file (e.g., `webpack.config.js` or `webpack.config.ts`):
```javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const loader = require.resolve('@hyperse/html-webpack-plugin-loader/loader');
module.exports = {
// ... other webpack config
plugins: [
new HtmlWebpackPlugin({
template: `${loader}!/xxx/src/index.html`,
templateParameters: {
// Template options (same as TemplateOptions interface)
title: 'My Webpack App',
favicon: {
href: '/favicon.ico',
rel: 'icon',
attributes: {
type: 'image/x-icon',
},
},
headMetaTags: [
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
'<meta name="description" content="My webpack application">',
],
headStyles: [
{
id: 'main-css',
href: '/styles/main.css',
position: 'end',
},
],
bodyScripts: [
{
id: 'app-js',
src: '/app.js',
position: 'end',
type: 'module',
},
],
},
}),
],
};
```
### Usage with TypeScript
If you're using TypeScript, you can import the types for better type checking:
```typescript
import type { TemplateOptions } from '@hyperse/html-webpack-plugin-loader';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path';
const loader = require.resolve('@hyperse/html-webpack-plugin-loader/loader');
const templateParameters: TemplateOptions = {
title: 'My TypeScript Webpack App',
// ... other template options
};
const config = {
plugins: [
new HtmlWebpackPlugin({
template: `$loader}!/xxx/src/index.html`,
templateParameters,
}),
],
};
export default config;
```
### Advanced Usage
#### Dynamic Template Parameters
You can dynamically generate template parameters based on your environment or other conditions:
```javascript
const getTemplateParameters = (env) => ({
title: env.production ? 'Production App' : 'Development App',
headMetaTags: [
`<meta name="environment" content="${env.production ? 'production' : 'development'}">`,
],
// ... other options
});
const loader = require.resolve('@hyperse/html-webpack-plugin-loader/loader');
module.exports = (env) => ({
// ... other webpack config
plugins: [
new HtmlWebpackPlugin({
template: `${loader}!/xxx/src/index.html`,
templateParameters: getTemplateParameters(env),
}),
],
});
```
#### Multiple HTML Templates
You can use different template parameters for different HTML files:
```javascript
const loader = require.resolve('@hyperse/html-webpack-plugin-loader/loader');
module.exports = {
// ... other webpack config
plugins: [
new HtmlWebpackPlugin({
template: `${loader}!/xxx/src/index.html`,
filename: 'index.html',
chunks: ['main'],
templateParameters: {
title: 'Main Application',
// ... main app options
},
}),
new HtmlWebpackPlugin({
template: `${loader}!/xxx/src/index.html`,
filename: 'admin.html',
chunks: ['admin'],
templateParameters: {
title: 'Admin Dashboard',
// ... admin-specific options
},
}),
],
};
```
### Loader Options
The loader accepts only one option:
- `force` (boolean, optional): When set to `true`, forces template processing even if no template parameters are provided. Default is `false`.
### Best Practices
1. **Template Organization**:
- Keep your HTML templates in a dedicated directory (e.g., `src/templates/`)
- Use consistent naming conventions for your template files
2. **Environment-specific Configuration**:
- Use webpack's environment configuration to manage different settings for development and production
- Consider using environment variables for sensitive or environment-specific values
3. **Performance Optimization**:
- Use `position: 'end'` for non-critical scripts and styles
- Use `position: 'beginning'` for critical resources
- Consider using `order` property to control the loading sequence
4. **Security**:
- Always use `integrity` checks for external resources when possible
- Use `nonce` for inline scripts when implementing CSP
- Set appropriate `crossOrigin` attributes for external resources
5. **Maintenance**:
- Keep template parameters in a separate configuration file for better maintainability
- Use TypeScript for better type safety and IDE support
- Document your template parameters for team reference
## Contributing
Contributions are welcome! Please read our [contributing guidelines](https://github.com/hyperse-io/.github/blob/main/CONTRIBUTING.md) before submitting pull requests.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.