├── .gitignore ├── LICENSE ├── README.md ├── VueCliPluginHTMLReplace.js ├── index.js ├── package.json └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | *.log* 4 | 5 | .idea 6 | .vscode 7 | *.suo 8 | *.ntvs* 9 | *.njsproj 10 | *.sln 11 | *.sw* 12 | 13 | .DS_Store 14 | .DS_Store? 15 | ._* 16 | .Spotlight-V100 17 | .[Tt]rashes 18 | eh[Tt]humbs.db 19 | [Tt]humbs.db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Tristan Pavard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/vue-cli-plugin-html-replace.svg)](https://www.npmjs.com/package/vue-cli-plugin-html-replace) 2 | [![GitHub license](https://img.shields.io/github/license/tpavard/vue-cli-plugin-html-replace.svg)](https://github.com/tpavard/vue-cli-plugin-html-replace/blob/master/LICENSE) 3 | 4 | > [!CAUTION] 5 | > This project is not maintained anymore. 6 | > Please use a modern solution like [unplugin-replace](https://github.com/unplugin/unplugin-replace). 7 | 8 | # Vue-cli Plugin HTML Replace 9 | 10 | A simple plugin to replace contents within your HTML files generated by [Vue CLI](https://github.com/vuejs/vue-cli). 11 | 12 | > [!TIP] 13 | > You might not need this plugin for simple cases. 14 | > See [env variables interpolation](https://cli.vuejs.org/guide/html-and-static-assets.html#interpolation). 15 | 16 | - [Getting started](#getting-started) 17 | - [Configuration](#configuration) 18 | - [Basic usage](#basic-usage) 19 | - [Multi-page mode](#multi-page-mode) 20 | - [API](#api) 21 | - [Global](#global) 22 | - [Pattern](#pattern) 23 | 24 | ## Getting started 25 | 26 | :warning: Make sure you have [Vue CLI 3.x.x](https://github.com/vuejs/vue-cli) installed: 27 | 28 | ``` 29 | vue -V 30 | ``` 31 | 32 | Navigate to your vue app folder and add the cli plugin: 33 | 34 | ``` 35 | vue add html-replace 36 | ``` 37 | ## Configuration 38 | 39 | ### Basic usage 40 | 41 | This plugin can be configured by defining `htmlReplace` via the `pluginOptions` in your `vue.config.js`. 42 | 43 | The `patterns` property must either be an object or an array of objects. Each pattern must contain both `match` and `replacement` options, otherwise it will be ignored. 44 | 45 | ```javascript 46 | module.exports = { 47 | pluginOptions: { 48 | htmlReplace: { 49 | enable: (process.env.NODE_ENV === "production"), 50 | patterns: [ 51 | { 52 | match: "foo", 53 | replacement: "bar", 54 | }, 55 | { 56 | match: /any/g, 57 | replacement: "Globally replaced", 58 | }, 59 | { 60 | match: /(.*)<\/title>/, 61 | replacement: (match, $1) => `<title>"${$1}" has been replaced.`, 62 | }, 63 | ], 64 | }, 65 | }, 66 | }; 67 | ``` 68 | 69 | ### Multi-page mode 70 | 71 | In case you'd need to prevent patterns to be applied to some of your pages, you can specify either one of `excludes` or `includes` options. If both options are set, then `excludes` will be ignored. 72 | 73 | Each entry must be named after the key given for the page(s) to be excluded/included. These properties are optional and will be ignored when you're not building your app in multi-page mode. Thus all the patterns would be applied to your HTML files. 74 | 75 | ```javascript 76 | module.exports = { 77 | pages: { 78 | index: { 79 | entry: "src/App_1/main.js", 80 | filename: "app_1.html" 81 | }, 82 | app_2: "src/App_2/main.js", 83 | app_3: "src/App_3/main.js", 84 | }, 85 | pluginOptions: { 86 | htmlReplace: { 87 | patterns: [ 88 | { 89 | match: "foo", 90 | replacement: "All of your pages will be affected", 91 | }, 92 | { 93 | match: /.*<\/title>/, 94 | replacement: "<title>This is the index page", 95 | includes: "index", 96 | }, 97 | { 98 | match: /.*<\/title>/, 99 | replacement: "<title>This is the second page", 100 | excludes: ["index" , "app_3"], 101 | }, 102 | ], 103 | }, 104 | }, 105 | }; 106 | ``` 107 | 108 | ## API 109 | 110 | ### Global 111 | 112 | | Name | Type | Default | Description | 113 | | :--: | :--: | :--: | --- | 114 | | `enable` | `Boolean` | `true` | Enables/disables the plugin. | 115 | | `patterns` | `Object` \| `Array` | `[]` | Defines some patterns and how to replace their corresponding matches. | 116 | 117 | ### Pattern 118 | 119 | | Name | Type | Default | Description | 120 | | :--: | :--: | :--: | --- | 121 | | `match` | `String` \| `RegExp` | `null` | Defines the matches to be replaced. When missing, the pattern will be ignored. | 122 | | `replacement` | `String` \| `Function:String` | `null` | Specifies the value with which to replace the matches. When missing, the pattern will be ignored. | 123 | | `includes` | `String` \| `Array` | `null` | Includes the pages for which the pattern will be applied to, when deploying your app in multi-page mode. Each entry must be named after the key of a page. | 124 | | `excludes` | `String` \| `Array` | `null` | Prevents the pattern to be applied to specific pages in multi-page mode. Each entry must be named after the key of a page. This option will be ignored if `includes` has already been set. | 125 | -------------------------------------------------------------------------------- /VueCliPluginHTMLReplace.js: -------------------------------------------------------------------------------- 1 | const { typeOf, isObject, warn } = require("./utils.js"); 2 | 3 | module.exports = class VueCliPluginHTMLReplace { 4 | constructor(patterns, pages) { 5 | this._pages = pages; 6 | this._index = Array.isArray(pages) ? 0 : null; 7 | this._patterns = (Array.isArray(patterns) ? patterns : [patterns]) 8 | .filter(pattern => { 9 | if (pattern && pattern.pattern) { 10 | warn("The 'pattern' option is deprecated. Please use 'match' instead: https://github.com/tpavard/vue-cli-plugin-html-replace#pattern"); 11 | pattern.match = pattern.pattern; 12 | } 13 | return isObject(pattern) 14 | && /String|Function/.test(typeOf(pattern.replacement)) 15 | && /String|RegExp/.test(typeOf(pattern.match)); 16 | }) 17 | .map(pattern => this._validation(pattern)); 18 | } 19 | 20 | // @private 21 | _validation({ 22 | match, 23 | replacement, 24 | includes, 25 | excludes, 26 | }) { 27 | if (this._index !== null) { 28 | if (typeof includes === "string") { 29 | includes = [includes]; 30 | } else if (typeof excludes === "string") { 31 | excludes = [excludes]; 32 | } else { 33 | includes = Array.isArray(includes) ? includes.filter(val => typeof val === "string") : null; 34 | excludes = Array.isArray(excludes) ? excludes.filter(val => typeof val === "string") : null; 35 | } 36 | 37 | if (includes != null && excludes != null) { 38 | excludes = null; 39 | } 40 | } else { 41 | includes = null; 42 | excludes = null; 43 | } 44 | 45 | return ({ 46 | match, 47 | replacement, 48 | includes, 49 | excludes 50 | }); 51 | } 52 | 53 | // @private 54 | _process(data, callback) { 55 | const filename = this._index === null ? this._pages : this._pages[this._index++]; 56 | const isCurrent = Object.keys(data.assets.chunks).includes(filename); 57 | 58 | this._patterns.forEach(({ 59 | match, 60 | replacement, 61 | includes, 62 | excludes, 63 | }) => { 64 | if (isCurrent 65 | && (this._index === null 66 | || (!includes && !excludes) 67 | || (this._index !== null 68 | && ( 69 | (includes && includes.includes(filename)) || 70 | (excludes && !excludes.includes(filename)) 71 | )))) { 72 | data.html = data.html.replace(match, replacement); 73 | } 74 | }); 75 | callback(null, data); 76 | } 77 | 78 | apply(compiler) { 79 | if (this._patterns) { 80 | compiler.hooks.compilation 81 | .tap("VueCliPluginHTMLReplace", compilation => compilation.hooks.htmlWebpackPluginAfterHtmlProcessing 82 | .tapAsync("vue-cli-plugin-html-replace", (data, callback) => this._process(data, callback))); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { isObject } = require("./utils.js"); 2 | const VueCliPluginHTMLReplace = require("./VueCliPluginHTMLReplace.js"); 3 | 4 | module.exports = (api, { 5 | pluginOptions = {}, 6 | pages = {}, 7 | } = {}) => { 8 | const { 9 | patterns = [], 10 | enable = true, 11 | } = pluginOptions.htmlReplace || {}; 12 | 13 | if (isObject(pluginOptions.htmlReplace) && enable === true 14 | && (Array.isArray(patterns) || isObject(patterns))) { 15 | const keys = Object.keys(pages); 16 | 17 | api.configureWebpack(({ plugins }) => { 18 | plugins.push(new VueCliPluginHTMLReplace(patterns, keys.length ? keys : "app")); 19 | }); 20 | } 21 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli-plugin-html-replace", 3 | "version": "1.3.0", 4 | "description": "A simple plugin to replace contents within your HTML files generated by Vue CLI.", 5 | "main": "index.js", 6 | "homepage": "https://github.com/tpavard/vue-cli-plugin-html-replace", 7 | "bugs": "https://github.com/tpavard/vue-cli-plugin-html-replace/issues", 8 | "author": "Tristan Pavard ", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tpavard/vue-cli-plugin-html-replace.git" 12 | }, 13 | "scripts": {}, 14 | "keywords": [ 15 | "webpack", 16 | "plugin", 17 | "vue", 18 | "vuejs", 19 | "vue-cli", 20 | "vuecli", 21 | "cli", 22 | "html", 23 | "replace" 24 | ], 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | function typeOf(value) { 2 | if (value === null) return "Null"; 3 | let type = typeof value; 4 | if (type !== "object") return `${type.charAt(0).toUpperCase()}${type.slice(1)}`; 5 | type = Object.prototype.toString.call(value).slice(8, -1); 6 | return type === "Object" ? value.constructor.name : type; 7 | } 8 | 9 | function isObject(val) { 10 | return typeOf(val) === "Object"; 11 | } 12 | 13 | function warn(msg) { 14 | console.log("\x1b[31m%s\x1b[0m", `\n${msg}`); 15 | } 16 | 17 | module.exports = { 18 | typeOf, 19 | isObject, 20 | warn, 21 | }; 22 | --------------------------------------------------------------------------------