UNPKG

4.06 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { WebpackError } = require("..");
9const { getUsedModuleIdsAndModules } = require("./IdHelpers");
10
11/** @typedef {import("../Compiler")} Compiler */
12/** @typedef {import("../Module")} Module */
13
14const plugin = "SyncModuleIdsPlugin";
15
16class SyncModuleIdsPlugin {
17 /**
18 * @param {Object} options options
19 * @param {string} options.path path to file
20 * @param {string=} options.context context for module names
21 * @param {function(Module): boolean} options.test selector for modules
22 * @param {"read" | "create" | "merge" | "update"=} options.mode operation mode (defaults to merge)
23 */
24 constructor({ path, context, test, mode }) {
25 this._path = path;
26 this._context = context;
27 this._test = test || (() => true);
28 const readAndWrite = !mode || mode === "merge" || mode === "update";
29 this._read = readAndWrite || mode === "read";
30 this._write = readAndWrite || mode === "create";
31 this._prune = mode === "update";
32 }
33
34 /**
35 * Apply the plugin
36 * @param {Compiler} compiler the compiler instance
37 * @returns {void}
38 */
39 apply(compiler) {
40 /** @type {Map<string, string | number>} */
41 let data;
42 let dataChanged = false;
43 if (this._read) {
44 compiler.hooks.readRecords.tapAsync(plugin, callback => {
45 const fs = compiler.intermediateFileSystem;
46 fs.readFile(this._path, (err, buffer) => {
47 if (err) {
48 if (err.code !== "ENOENT") {
49 return callback(err);
50 }
51 return callback();
52 }
53 const json = JSON.parse(buffer.toString());
54 data = new Map();
55 for (const key of Object.keys(json)) {
56 data.set(key, json[key]);
57 }
58 dataChanged = false;
59 return callback();
60 });
61 });
62 }
63 if (this._write) {
64 compiler.hooks.emitRecords.tapAsync(plugin, callback => {
65 if (!data || !dataChanged) return callback();
66 const json = {};
67 const sorted = Array.from(data).sort(([a], [b]) => (a < b ? -1 : 1));
68 for (const [key, value] of sorted) {
69 json[key] = value;
70 }
71 const fs = compiler.intermediateFileSystem;
72 fs.writeFile(this._path, JSON.stringify(json), callback);
73 });
74 }
75 compiler.hooks.thisCompilation.tap(plugin, compilation => {
76 const associatedObjectForCache = compiler.root;
77 const context = this._context || compiler.context;
78 if (this._read) {
79 compilation.hooks.reviveModules.tap(plugin, (_1, _2) => {
80 if (!data) return;
81 const { chunkGraph } = compilation;
82 const [usedIds, modules] = getUsedModuleIdsAndModules(
83 compilation,
84 this._test
85 );
86 for (const module of modules) {
87 const name = module.libIdent({
88 context,
89 associatedObjectForCache
90 });
91 if (!name) continue;
92 const id = data.get(name);
93 const idAsString = `${id}`;
94 if (usedIds.has(idAsString)) {
95 const err = new WebpackError(
96 `SyncModuleIdsPlugin: Unable to restore id '${id}' from '${this._path}' as it's already used.`
97 );
98 err.module = module;
99 compilation.errors.push(err);
100 }
101 chunkGraph.setModuleId(module, id);
102 usedIds.add(idAsString);
103 }
104 });
105 }
106 if (this._write) {
107 compilation.hooks.recordModules.tap(plugin, modules => {
108 const { chunkGraph } = compilation;
109 let oldData = data;
110 if (!oldData) {
111 oldData = data = new Map();
112 } else if (this._prune) {
113 data = new Map();
114 }
115 for (const module of modules) {
116 if (this._test(module)) {
117 const name = module.libIdent({
118 context,
119 associatedObjectForCache
120 });
121 if (!name) continue;
122 const id = chunkGraph.getModuleId(module);
123 if (id === null) continue;
124 const oldId = oldData.get(name);
125 if (oldId !== id) {
126 dataChanged = true;
127 } else if (data === oldData) {
128 continue;
129 }
130 data.set(name, id);
131 }
132 }
133 if (data.size !== oldData.size) dataChanged = true;
134 });
135 }
136 });
137 }
138}
139
140module.exports = SyncModuleIdsPlugin;