-
Notifications
You must be signed in to change notification settings - Fork 378
/
assets-manager.ts
147 lines (132 loc) · 4.33 KB
/
assets-manager.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import * as chokidar from 'chokidar';
import { statSync } from 'fs';
import { sync } from 'glob';
import { dirname, join, sep } from 'path';
import * as shell from 'shelljs';
import {
ActionOnFile,
Asset,
AssetEntry,
Configuration,
} from '../configuration';
import { copyPathResolve } from './helpers/copy-path-resolve';
import { getValueOrDefault } from './helpers/get-value-or-default';
export class AssetsManager {
private watchAssetsKeyValue: { [key: string]: boolean } = {};
private watchers: chokidar.FSWatcher[] = [];
private actionInProgress = false;
/**
* Using on `nest build` to close file watch or the build process will not end
* Interval like process
* If no action has been taken recently close watchers
* If action has been taken recently flag and try again
*/
public closeWatchers() {
// Consider adjusting this for larger files
const timeoutMs = 500;
const closeFn = () => {
if (this.actionInProgress) {
this.actionInProgress = false;
setTimeout(closeFn, timeoutMs);
} else {
this.watchers.forEach((watcher) => watcher.close());
}
};
setTimeout(closeFn, timeoutMs);
}
public copyAssets(
configuration: Required<Configuration>,
appName: string,
outDir: string,
watchAssetsMode: boolean,
) {
const assets =
getValueOrDefault<Asset[]>(
configuration,
'compilerOptions.assets',
appName,
) || [];
if (assets.length <= 0) {
return;
}
try {
let sourceRoot = getValueOrDefault(configuration, 'sourceRoot', appName);
sourceRoot = join(process.cwd(), sourceRoot);
const filesToCopy = assets.map<AssetEntry>((item) => {
if (typeof item === 'string') {
return {
glob: join(sourceRoot, item),
outDir,
};
}
return {
outDir: item.outDir || outDir,
glob: join(sourceRoot, item.include!),
exclude: item.exclude ? join(sourceRoot, item.exclude) : undefined,
flat: item.flat, // deprecated field
watchAssets: item.watchAssets,
};
});
const isWatchEnabled =
getValueOrDefault<boolean>(
configuration,
'compilerOptions.watchAssets',
appName,
) || watchAssetsMode;
for (const item of filesToCopy) {
const option: ActionOnFile = {
action: 'change',
item,
path: '',
sourceRoot,
watchAssetsMode: isWatchEnabled,
};
if (isWatchEnabled || item.watchAssets) {
// prettier-ignore
const watcher = chokidar
.watch(item.glob, { ignored: item.exclude })
.on('add', (path: string) => this.actionOnFile({ ...option, path, action: 'change' }))
.on('change', (path: string) => this.actionOnFile({ ...option, path, action: 'change' }))
.on('unlink', (path: string) => this.actionOnFile({ ...option, path, action: 'unlink' }));
this.watchers.push(watcher);
} else {
const files = sync(item.glob, { ignore: item.exclude }).filter(
(matched) => statSync(matched).isFile(),
);
for (const path of files) {
this.actionOnFile({ ...option, path, action: 'change' });
}
}
}
} catch (err) {
throw new Error(
`An error occurred during the assets copying process. ${err.message}`,
);
}
}
private actionOnFile(option: ActionOnFile) {
const { action, item, path, sourceRoot, watchAssetsMode } = option;
const isWatchEnabled = watchAssetsMode || item.watchAssets;
// Allow to do action for the first time before check watchMode
if (!isWatchEnabled && this.watchAssetsKeyValue[path]) {
return;
}
// Set path value to true for watching the first time
this.watchAssetsKeyValue[path] = true;
// Set action to true to avoid watches getting cutoff
this.actionInProgress = true;
const dest = copyPathResolve(
path,
item.outDir!,
sourceRoot.split(sep).length,
);
// Copy to output dir if file is changed or added
if (action === 'change') {
shell.mkdir('-p', dirname(dest));
shell.cp(path, dest);
} else if (action === 'unlink') {
// Remove from output dir if file is deleted
shell.rm(dest);
}
}
}