Projekt

Obecné

Profil

Stáhnout (5.69 KB) Statistiky
| Větev: | Revize:
1
/*
2
	MIT License http://www.opensource.org/licenses/mit-license.php
3
	Author Jason Anderson @diurnalist
4
*/
5
"use strict";
6

    
7
const REGEXP_HASH = /\[hash(?::(\d+))?\]/gi,
8
	REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/gi,
9
	REGEXP_MODULEHASH = /\[modulehash(?::(\d+))?\]/gi,
10
	REGEXP_CONTENTHASH = /\[contenthash(?::(\d+))?\]/gi,
11
	REGEXP_NAME = /\[name\]/gi,
12
	REGEXP_ID = /\[id\]/gi,
13
	REGEXP_MODULEID = /\[moduleid\]/gi,
14
	REGEXP_FILE = /\[file\]/gi,
15
	REGEXP_QUERY = /\[query\]/gi,
16
	REGEXP_FILEBASE = /\[filebase\]/gi,
17
	REGEXP_URL = /\[url\]/gi;
18

    
19
// Using global RegExp for .test is dangerous
20
// We use a normal RegExp instead of .test
21
const REGEXP_HASH_FOR_TEST = new RegExp(REGEXP_HASH.source, "i"),
22
	REGEXP_CHUNKHASH_FOR_TEST = new RegExp(REGEXP_CHUNKHASH.source, "i"),
23
	REGEXP_CONTENTHASH_FOR_TEST = new RegExp(REGEXP_CONTENTHASH.source, "i"),
24
	REGEXP_NAME_FOR_TEST = new RegExp(REGEXP_NAME.source, "i");
25

    
26
const withHashLength = (replacer, handlerFn, assetInfo) => {
27
	const fn = (match, hashLength, ...args) => {
28
		if (assetInfo) assetInfo.immutable = true;
29
		const length = hashLength && parseInt(hashLength, 10);
30
		if (length && handlerFn) {
31
			return handlerFn(length);
32
		}
33
		const hash = replacer(match, hashLength, ...args);
34
		return length ? hash.slice(0, length) : hash;
35
	};
36
	return fn;
37
};
38

    
39
const getReplacer = (value, allowEmpty) => {
40
	const fn = (match, ...args) => {
41
		// last argument in replacer is the entire input string
42
		const input = args[args.length - 1];
43
		if (value === null || value === undefined) {
44
			if (!allowEmpty) {
45
				throw new Error(
46
					`Path variable ${match} not implemented in this context: ${input}`
47
				);
48
			}
49
			return "";
50
		} else {
51
			return `${escapePathVariables(value)}`;
52
		}
53
	};
54
	return fn;
55
};
56

    
57
const escapePathVariables = value => {
58
	return typeof value === "string"
59
		? value.replace(/\[(\\*[\w:]+\\*)\]/gi, "[\\$1\\]")
60
		: value;
61
};
62

    
63
const replacePathVariables = (path, data, assetInfo) => {
64
	const chunk = data.chunk;
65
	const chunkId = chunk && chunk.id;
66
	const chunkName = chunk && (chunk.name || chunk.id);
67
	const chunkHash = chunk && (chunk.renderedHash || chunk.hash);
68
	const chunkHashWithLength = chunk && chunk.hashWithLength;
69
	const contentHashType = data.contentHashType;
70
	const contentHash =
71
		(chunk && chunk.contentHash && chunk.contentHash[contentHashType]) ||
72
		data.contentHash;
73
	const contentHashWithLength =
74
		(chunk &&
75
			chunk.contentHashWithLength &&
76
			chunk.contentHashWithLength[contentHashType]) ||
77
		data.contentHashWithLength;
78
	const module = data.module;
79
	const moduleId = module && module.id;
80
	const moduleHash = module && (module.renderedHash || module.hash);
81
	const moduleHashWithLength = module && module.hashWithLength;
82

    
83
	if (typeof path === "function") {
84
		path = path(data);
85
	}
86

    
87
	if (
88
		data.noChunkHash &&
89
		(REGEXP_CHUNKHASH_FOR_TEST.test(path) ||
90
			REGEXP_CONTENTHASH_FOR_TEST.test(path))
91
	) {
92
		throw new Error(
93
			`Cannot use [chunkhash] or [contenthash] for chunk in '${path}' (use [hash] instead)`
94
		);
95
	}
96

    
97
	return (
98
		path
99
			.replace(
100
				REGEXP_HASH,
101
				withHashLength(getReplacer(data.hash), data.hashWithLength, assetInfo)
102
			)
103
			.replace(
104
				REGEXP_CHUNKHASH,
105
				withHashLength(getReplacer(chunkHash), chunkHashWithLength, assetInfo)
106
			)
107
			.replace(
108
				REGEXP_CONTENTHASH,
109
				withHashLength(
110
					getReplacer(contentHash),
111
					contentHashWithLength,
112
					assetInfo
113
				)
114
			)
115
			.replace(
116
				REGEXP_MODULEHASH,
117
				withHashLength(getReplacer(moduleHash), moduleHashWithLength, assetInfo)
118
			)
119
			.replace(REGEXP_ID, getReplacer(chunkId))
120
			.replace(REGEXP_MODULEID, getReplacer(moduleId))
121
			.replace(REGEXP_NAME, getReplacer(chunkName))
122
			.replace(REGEXP_FILE, getReplacer(data.filename))
123
			.replace(REGEXP_FILEBASE, getReplacer(data.basename))
124
			// query is optional, it's OK if it's in a path but there's nothing to replace it with
125
			.replace(REGEXP_QUERY, getReplacer(data.query, true))
126
			// only available in sourceMappingURLComment
127
			.replace(REGEXP_URL, getReplacer(data.url))
128
			.replace(/\[\\(\\*[\w:]+\\*)\\\]/gi, "[$1]")
129
	);
130
};
131

    
132
class TemplatedPathPlugin {
133
	apply(compiler) {
134
		compiler.hooks.compilation.tap("TemplatedPathPlugin", compilation => {
135
			const mainTemplate = compilation.mainTemplate;
136

    
137
			mainTemplate.hooks.assetPath.tap(
138
				"TemplatedPathPlugin",
139
				replacePathVariables
140
			);
141

    
142
			mainTemplate.hooks.globalHash.tap(
143
				"TemplatedPathPlugin",
144
				(chunk, paths) => {
145
					const outputOptions = mainTemplate.outputOptions;
146
					const publicPath = outputOptions.publicPath || "";
147
					const filename = outputOptions.filename || "";
148
					const chunkFilename =
149
						outputOptions.chunkFilename || outputOptions.filename;
150
					if (
151
						REGEXP_HASH_FOR_TEST.test(publicPath) ||
152
						REGEXP_CHUNKHASH_FOR_TEST.test(publicPath) ||
153
						REGEXP_CONTENTHASH_FOR_TEST.test(publicPath) ||
154
						REGEXP_NAME_FOR_TEST.test(publicPath)
155
					)
156
						return true;
157
					if (REGEXP_HASH_FOR_TEST.test(filename)) return true;
158
					if (REGEXP_HASH_FOR_TEST.test(chunkFilename)) return true;
159
					if (REGEXP_HASH_FOR_TEST.test(paths.join("|"))) return true;
160
				}
161
			);
162

    
163
			mainTemplate.hooks.hashForChunk.tap(
164
				"TemplatedPathPlugin",
165
				(hash, chunk) => {
166
					const outputOptions = mainTemplate.outputOptions;
167
					const chunkFilename =
168
						outputOptions.chunkFilename || outputOptions.filename;
169
					if (REGEXP_CHUNKHASH_FOR_TEST.test(chunkFilename)) {
170
						hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
171
					}
172
					if (REGEXP_CONTENTHASH_FOR_TEST.test(chunkFilename)) {
173
						hash.update(
174
							JSON.stringify(
175
								chunk.getChunkMaps(true).contentHash.javascript || {}
176
							)
177
						);
178
					}
179
					if (REGEXP_NAME_FOR_TEST.test(chunkFilename)) {
180
						hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
181
					}
182
				}
183
			);
184
		});
185
	}
186
}
187

    
188
module.exports = TemplatedPathPlugin;
(127-127/144)