-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
131 lines (117 loc) · 5 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import * as path from "path";
import { ParsedPath } from "path";
import * as webpack from "webpack";
import * as HtmlWebpackPlugin from "html-webpack-plugin";
import {HtmlTagObject} from "html-webpack-plugin";
function replaceJS(html: string, publicPath: string, assetTags: HtmlTagObject[], compilerOptions: webpack.Configuration): string {
return replace(html, publicPath, assetTags, "script", "src", compilerOptions, /(<script[\S\s]*?src=['"])(.+?)(['"][^>]*?>)/gi);
}
function replaceCSS(html: string, publicPath: string, assetTags: HtmlTagObject[], compilerOptions: webpack.Configuration): string {
return replace(html, publicPath, assetTags, "link", "href", compilerOptions, /(<link[\S\s]*?href=['"])(.+?)(['"][^>]*?>)/gi);
}
function replace(html: string, publicPath: string, assetTags: HtmlTagObject[], filterAssetTagName: string, filterAssetTagAttribute: string,
compilerOptions: webpack.Configuration, regex: RegExp): string {
const basePath: string = getBasePath(compilerOptions);
let output: string = "";
let lastIndex: number = 0;
let result: RegExpExecArray | null;
while ((result = regex.exec(html)) !== null) {
const scriptPrefix: string = result[1];
const scriptSource: string = result[2];
const scriptSuffix: string = result[3];
output += html.substring(lastIndex, result.index);
output += scriptPrefix;
// Resolve script source path
const resolvedScriptSource: string = path.join(basePath, scriptSource);
const scriptSourceName: string = getPathName(resolvedScriptSource);
// Determine if source should be replaced
let replaceFile: string|undefined;
for (let i = assetTags.length - 1; i >= 0; i--) {
const assetTag: HtmlTagObject = assetTags[i];
if (assetTag.tagName !== filterAssetTagName) {
continue;
}
if (!(filterAssetTagAttribute in assetTag.attributes)) {
continue;
}
const assetPath: string | boolean = assetTag.attributes[filterAssetTagAttribute];
if (typeof assetPath !== "string") {
continue;
}
let assetFileName: string;
if (publicPath && assetPath.startsWith(publicPath)) {
assetFileName = assetPath.slice(publicPath.length);
} else {
assetFileName = assetPath;
}
if (scriptSourceName === getPathName(path.join(basePath, assetFileName))) {
// Replace!
replaceFile = assetPath;
// Remove file from files array
assetTags.splice(i, 1);
}
}
if (replaceFile != null) {
output += replaceFile.split(path.win32.sep).join(path.posix.sep);
} else {
output += scriptSource;
}
output += scriptSuffix;
lastIndex = regex.lastIndex;
}
output += html.substring(lastIndex);
return output;
}
function getBasePath(compilerOptions: webpack.Configuration): string {
let base: string;
if (compilerOptions.output && compilerOptions.output.path) {
base = compilerOptions.output.path;
} else if (compilerOptions.context) {
base = compilerOptions.context;
} else {
base = __dirname;
}
return base;
}
function getPathName(filePath: string): string {
let parsedPath: ParsedPath = path.parse(filePath);
const dir: string = parsedPath.dir;
while (parsedPath.ext.length > 0) {
filePath = parsedPath.name;
parsedPath = path.parse(filePath);
}
return path.join(dir, parsedPath.name);
}
const pluginName = "ReplaceUrlHtmlWebpackPlugin";
/**
*
*/
export class ReplaceUrlHtmlWebpackPlugin {
/**
* @override
*/
public apply(compiler: webpack.Compiler): void {
const compilerOptions: webpack.Configuration = compiler.options;
compiler.hooks.compilation.tap(pluginName, (compilation: webpack.compilation.Compilation) => {
let publicPath: string | undefined;
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, (data, cb) => {
publicPath = data.assets.publicPath;
cb(null, data);
});
HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(pluginName, (data, cb) => {
if (publicPath != null) {
// Process both head and body tags
for (const assetTags of [data.headTags, data.bodyTags]) {
// Replace asset tags in HTML
let html: string = data.html;
html = replaceJS(html, publicPath, assetTags, compilerOptions);
html = replaceCSS(html, publicPath, assetTags, compilerOptions);
// Assign HTML back to data object
data.html = html;
}
}
cb(null, data);
});
});
}
}