| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | const path = require("path"); | 
					
						
							|  |  |  | const util = require("util"); | 
					
						
							|  |  |  | const fs = require("fs"); | 
					
						
							|  |  |  | const rimraf = require("rimraf"); | 
					
						
							|  |  |  | const vm = require("vm"); | 
					
						
							|  |  |  | const webpack = require("../"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const readdir = util.promisify(fs.readdir); | 
					
						
							|  |  |  | const writeFile = util.promisify(fs.writeFile); | 
					
						
							|  |  |  | const utimes = util.promisify(fs.utimes); | 
					
						
							|  |  |  | const mkdir = util.promisify(fs.mkdir); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe("Persistent Caching", () => { | 
					
						
							|  |  |  | 	const tempPath = path.resolve(__dirname, "js", "persistent-caching"); | 
					
						
							|  |  |  | 	const outputPath = path.resolve(tempPath, "output"); | 
					
						
							|  |  |  | 	const cachePath = path.resolve(tempPath, "cache"); | 
					
						
							|  |  |  | 	const srcPath = path.resolve(tempPath, "src"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const config = { | 
					
						
							|  |  |  | 		mode: "none", | 
					
						
							|  |  |  | 		context: tempPath, | 
					
						
							|  |  |  | 		cache: { | 
					
						
							|  |  |  | 			type: "filesystem", | 
					
						
							|  |  |  | 			buildDependencies: { | 
					
						
							|  |  |  | 				// avoid rechecking build dependencies
 | 
					
						
							|  |  |  | 				// for performance
 | 
					
						
							|  |  |  | 				// this is already covered by another test case
 | 
					
						
							|  |  |  | 				defaultWebpack: [] | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			cacheLocation: cachePath | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		target: "node", | 
					
						
							|  |  |  | 		output: { | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 			library: { type: "commonjs-module", export: "default" }, | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 			path: outputPath | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-16 15:37:11 +08:00
										 |  |  | 	beforeEach(done => { | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 		rimraf(tempPath, done); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const updateSrc = async data => { | 
					
						
							|  |  |  | 		const ts = new Date(Date.now() - 10000); | 
					
						
							|  |  |  | 		await mkdir(srcPath, { recursive: true }); | 
					
						
							|  |  |  | 		for (const key of Object.keys(data)) { | 
					
						
							|  |  |  | 			const p = path.resolve(srcPath, key); | 
					
						
							|  |  |  | 			await writeFile(p, data[key]); | 
					
						
							|  |  |  | 			await utimes(p, ts, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-16 15:37:11 +08:00
										 |  |  | 	const compile = async (configAdditions = {}) => { | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 		return new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2020-04-16 15:37:11 +08:00
										 |  |  | 			webpack({ ...config, ...configAdditions }, (err, stats) => { | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 				if (err) return reject(err); | 
					
						
							| 
									
										
										
										
											2020-05-26 23:43:08 +08:00
										 |  |  | 				if (stats.hasErrors()) | 
					
						
							|  |  |  | 					return reject(stats.toString({ preset: "errors-only" })); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 				resolve(stats); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 	const execute = () => { | 
					
						
							|  |  |  | 		const cache = {}; | 
					
						
							|  |  |  | 		const require = name => { | 
					
						
							|  |  |  | 			if (cache[name]) return cache[name].exports; | 
					
						
							|  |  |  | 			if (!name.endsWith(".js")) name += ".js"; | 
					
						
							|  |  |  | 			const p = path.resolve(outputPath, name); | 
					
						
							|  |  |  | 			const source = fs.readFileSync(p, "utf-8"); | 
					
						
							|  |  |  | 			const context = {}; | 
					
						
							|  |  |  | 			const fn = vm.runInThisContext( | 
					
						
							|  |  |  | 				`(function(require, module, exports) { ${source} })`, | 
					
						
							|  |  |  | 				context, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					filename: p | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			const m = { exports: {} }; | 
					
						
							|  |  |  | 			cache[name] = m; | 
					
						
							|  |  |  | 			fn(require, m, m.exports); | 
					
						
							|  |  |  | 			return m.exports; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		return require("./main"); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it("should merge multiple small files", async () => { | 
					
						
							|  |  |  | 		const files = Array.from({ length: 30 }).map((_, i) => `file${i}.js`); | 
					
						
							|  |  |  | 		const data = { | 
					
						
							|  |  |  | 			"index.js": `
 | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | ${files.map((f, i) => `import f${i} from "./${f}";`).join("\n")} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default ${files.map((_, i) => `f${i}`).join(" + ")}; | 
					
						
							|  |  |  | `
 | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		for (const file of files) { | 
					
						
							|  |  |  | 			data[file] = `export default 1;`; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		await updateSrc(data); | 
					
						
							|  |  |  | 		await compile(); | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 		expect(execute()).toBe(30); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 		for (let i = 0; i < 30; i++) { | 
					
						
							|  |  |  | 			updateSrc({ | 
					
						
							|  |  |  | 				[files[i]]: `export default 2;` | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			await compile(); | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 			expect(execute()).toBe(31 + i); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		const cacheFiles = await readdir(cachePath); | 
					
						
							|  |  |  | 		expect(cacheFiles.length).toBeLessThan(20); | 
					
						
							|  |  |  | 		expect(cacheFiles.length).toBeGreaterThan(10); | 
					
						
							|  |  |  | 	}, 60000); | 
					
						
							| 
									
										
										
										
											2020-04-16 15:37:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	it("should optimize unused content", async () => { | 
					
						
							|  |  |  | 		const data = { | 
					
						
							|  |  |  | 			"a.js": 'import "react-dom";', | 
					
						
							|  |  |  | 			"b.js": 'import "acorn";', | 
					
						
							|  |  |  | 			"c.js": 'import "core-js";', | 
					
						
							|  |  |  | 			"d.js": 'import "date-fns";', | 
					
						
							|  |  |  | 			"e.js": 'import "lodash";' | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const createEntry = items => { | 
					
						
							|  |  |  | 			const entry = {}; | 
					
						
							|  |  |  | 			for (const item of items.split("")) entry[item] = `./src/${item}.js`; | 
					
						
							|  |  |  | 			return entry; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		await updateSrc(data); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("abcde") }); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("abc") }); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("cde") }); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("acd") }); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("bce") }); | 
					
						
							|  |  |  | 		await compile({ entry: createEntry("abcde") }); | 
					
						
							|  |  |  | 		const cacheFiles = await readdir(cachePath); | 
					
						
							|  |  |  | 		expect(cacheFiles.length).toBeGreaterThan(4); | 
					
						
							|  |  |  | 	}, 60000); | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	it("should allow persistent caching of container related objects", async () => { | 
					
						
							|  |  |  | 		const data = { | 
					
						
							|  |  |  | 			"index.js": | 
					
						
							|  |  |  | 				"export default import('container/src/exposed').then(m => m.default);", | 
					
						
							| 
									
										
										
										
											2020-05-26 23:43:08 +08:00
										 |  |  | 			"exposed.js": "import lib from 'lib'; export default 21 + lib;", | 
					
						
							| 
									
										
										
										
											2020-06-16 00:46:38 +08:00
										 |  |  | 			"lib.js": "export default 20", | 
					
						
							|  |  |  | 			"lib2.js": "export default 21" | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 		await updateSrc(data); | 
					
						
							|  |  |  | 		const configAdditions = { | 
					
						
							|  |  |  | 			plugins: [ | 
					
						
							|  |  |  | 				new webpack.container.ModuleFederationPlugin({ | 
					
						
							|  |  |  | 					name: "container", | 
					
						
							|  |  |  | 					library: { type: "commonjs-module" }, | 
					
						
							|  |  |  | 					exposes: ["./src/exposed"], | 
					
						
							| 
									
										
										
										
											2020-05-21 01:15:16 +08:00
										 |  |  | 					remotes: { | 
					
						
							| 
									
										
										
										
											2020-05-26 23:43:08 +08:00
										 |  |  | 						container: ["./no-container", "./container"] | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					shared: { | 
					
						
							|  |  |  | 						lib: { | 
					
						
							|  |  |  | 							import: "./src/lib", | 
					
						
							|  |  |  | 							shareKey: "lib", | 
					
						
							| 
									
										
										
										
											2020-06-16 00:46:38 +08:00
										 |  |  | 							version: "1.2.0", | 
					
						
							| 
									
										
										
										
											2020-05-26 23:43:08 +08:00
										 |  |  | 							requiredVersion: "^1.0.0" | 
					
						
							| 
									
										
										
										
											2020-06-16 00:46:38 +08:00
										 |  |  | 						}, | 
					
						
							|  |  |  | 						"./src/lib2": { | 
					
						
							|  |  |  | 							shareKey: "lib", | 
					
						
							|  |  |  | 							version: "1.2.3" | 
					
						
							| 
									
										
										
										
											2020-05-26 23:43:08 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-05-21 01:15:16 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-04-29 23:40:08 +08:00
										 |  |  | 				}) | 
					
						
							|  |  |  | 			] | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		await compile(configAdditions); | 
					
						
							|  |  |  | 		await expect(execute()).resolves.toBe(42); | 
					
						
							|  |  |  | 		await updateSrc({ | 
					
						
							|  |  |  | 			"exposed.js": "module.exports = { ok: true };" | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		await compile(configAdditions); | 
					
						
							|  |  |  | 		await expect(execute()).resolves.toEqual({ ok: true }); | 
					
						
							| 
									
										
										
										
											2020-07-08 17:11:17 +08:00
										 |  |  | 	}, 60000); | 
					
						
							|  |  |  | }); |