1 | const fs = require('./utils/fs');
|
2 | const path = require('path');
|
3 | const md5 = require('./utils/md5');
|
4 | const objectHash = require('./utils/objectHash');
|
5 | const pkg = require('../package.json');
|
6 | const logger = require('./Logger');
|
7 |
|
8 |
|
9 | const OPTION_KEYS = ['publicURL', 'minify', 'hmr', 'target'];
|
10 |
|
11 | class FSCache {
|
12 | constructor(options) {
|
13 | this.dir = path.resolve(options.cacheDir || '.cache');
|
14 | this.dirExists = false;
|
15 | this.invalidated = new Set();
|
16 | this.optionsHash = objectHash(
|
17 | OPTION_KEYS.reduce((p, k) => ((p[k] = options[k]), p), {
|
18 | version: pkg.version
|
19 | })
|
20 | );
|
21 | }
|
22 |
|
23 | async ensureDirExists() {
|
24 | await fs.mkdirp(this.dir);
|
25 | this.dirExists = true;
|
26 | }
|
27 |
|
28 | getCacheFile(filename) {
|
29 | let hash = md5(this.optionsHash + filename);
|
30 | return path.join(this.dir, hash + '.json');
|
31 | }
|
32 |
|
33 | async writeDepMtimes(data) {
|
34 |
|
35 | for (let dep of data.dependencies) {
|
36 | if (dep.includedInParent) {
|
37 | let stats = await fs.stat(dep.name);
|
38 | dep.mtime = stats.mtime.getTime();
|
39 | }
|
40 | }
|
41 | }
|
42 |
|
43 | async write(filename, data) {
|
44 | try {
|
45 | await this.ensureDirExists();
|
46 | await this.writeDepMtimes(data);
|
47 | await fs.writeFile(this.getCacheFile(filename), JSON.stringify(data));
|
48 | this.invalidated.delete(filename);
|
49 | } catch (err) {
|
50 | logger.error('Error writing to cache', err);
|
51 | }
|
52 | }
|
53 |
|
54 | async checkDepMtimes(data) {
|
55 |
|
56 |
|
57 | for (let dep of data.dependencies) {
|
58 | if (dep.includedInParent) {
|
59 | let stats = await fs.stat(dep.name);
|
60 | if (stats.mtime > dep.mtime) {
|
61 | return false;
|
62 | }
|
63 | }
|
64 | }
|
65 |
|
66 | return true;
|
67 | }
|
68 |
|
69 | async read(filename) {
|
70 | if (this.invalidated.has(filename)) {
|
71 | return null;
|
72 | }
|
73 |
|
74 | let cacheFile = this.getCacheFile(filename);
|
75 |
|
76 | try {
|
77 | let stats = await fs.stat(filename);
|
78 | let cacheStats = await fs.stat(cacheFile);
|
79 |
|
80 | if (stats.mtime > cacheStats.mtime) {
|
81 | return null;
|
82 | }
|
83 |
|
84 | let json = await fs.readFile(cacheFile);
|
85 | let data = JSON.parse(json);
|
86 | if (!await this.checkDepMtimes(data)) {
|
87 | return null;
|
88 | }
|
89 |
|
90 | return data;
|
91 | } catch (err) {
|
92 | return null;
|
93 | }
|
94 | }
|
95 |
|
96 | invalidate(filename) {
|
97 | this.invalidated.add(filename);
|
98 | }
|
99 |
|
100 | async delete(filename) {
|
101 | try {
|
102 | await fs.unlink(this.getCacheFile(filename));
|
103 | this.invalidated.delete(filename);
|
104 | } catch (err) {
|
105 |
|
106 | }
|
107 | }
|
108 | }
|
109 |
|
110 | module.exports = FSCache;
|