UNPKG

3.22 kBPlain TextView Raw
1import * as chokidar from "chokidar";
2import * as findUp from "find-up";
3import * as path from "path";
4import anymatch from "anymatch";
5import { logger } from "backfill-logger";
6import { outputFolderAsArray } from "backfill-config";
7
8let changedFilesOutsideScope: string[] = [];
9let changedFilesInsideScope: string[] = [];
10
11let watcher: chokidar.FSWatcher;
12
13function getGitRepositoryRoot(packageRoot: string) {
14 const nearestGitFolder = findUp.sync(".git", {
15 cwd: packageRoot,
16 type: "directory"
17 });
18
19 if (nearestGitFolder) {
20 // Return the parent folder of some/path/.git
21 return path.join(nearestGitFolder, "..");
22 }
23
24 return packageRoot;
25}
26
27function addGlobstars(globPatterns: string[]): string[] {
28 const folders = globPatterns.map(p => path.posix.join("**", p, "**", "*"));
29 const files = globPatterns.map(p => path.posix.join("**", p));
30
31 return [...folders, ...files];
32}
33
34export function initializeWatcher(
35 packageRoot: string,
36 internalCacheFolder: string,
37 logFolder: string,
38 outputFolder: string | string[],
39 hashGlobs: string[]
40) {
41 // Trying to find the git root and using it as an approximation of code boundary
42 const repositoryRoot = getGitRepositoryRoot(packageRoot);
43
44 // Empty the arrays
45 changedFilesOutsideScope = [];
46 changedFilesInsideScope = [];
47
48 logger.info("Running in AUDIT mode");
49 logger.info(`[audit] Watching file changes in: ${repositoryRoot}`);
50 logger.info(`[audit] Backfill will cache folder: ${outputFolder}`);
51
52 // Define globs
53 const ignoreGlobs = addGlobstars([
54 ".git",
55 ".cache",
56 logFolder,
57 internalCacheFolder
58 ]);
59
60 const cacheFolderGlob = outputFolderAsArray(outputFolder).map(folder =>
61 path.posix.join("**", folder, "**")
62 );
63
64 watcher = chokidar
65 .watch(hashGlobs, {
66 ignored: ignoreGlobs,
67 cwd: repositoryRoot,
68 persistent: true,
69 ignoreInitial: true,
70 followSymlinks: false,
71 usePolling: true
72 })
73 .on("all", (event, filePath) => {
74 const logLine = `${filePath} (${event})`;
75 logger.silly(`[audit] File change: ${logLine}`);
76
77 if (!anymatch(cacheFolderGlob, filePath)) {
78 changedFilesOutsideScope.push(logLine);
79 } else {
80 changedFilesInsideScope.push(logLine);
81 }
82 });
83}
84
85export const sideEffectWarningString =
86 "[audit] The following files got changed outside of the scope of the folder to be cached:";
87export const sideEffectCallToActionString =
88 "[audit] You should make sure that these changes are non-essential, as they would not be brought back on a cache-hit.";
89export const noSideEffectString =
90 "[audit] All observed file changes were within the scope of the folder to be cached.";
91
92async function delay(time: number) {
93 return new Promise(resolve => {
94 setTimeout(resolve, time);
95 });
96}
97
98export async function closeWatcher() {
99 // Wait for one second before closing, giving time for file changes to propagate
100 await delay(1000);
101
102 if (changedFilesOutsideScope.length > 0) {
103 logger.warn(sideEffectWarningString);
104 changedFilesOutsideScope.forEach(file => logger.warn(`- ${file}`));
105 logger.warn(sideEffectCallToActionString);
106 } else {
107 logger.info(noSideEffectString);
108 }
109
110 watcher.close();
111}