This repository has been archived by the owner on Mar 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindex.js
151 lines (128 loc) · 5.87 KB
/
index.js
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
148
149
150
151
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
const path = require('path')
const { promisify } = require('util')
const glob = promisify(require('glob'))
const minimatch = require('minimatch')
const debug = require('debug')('prebuild-webpack-plugin')
const PLUGIN_NAME = 'PrebuildWebpackPlugin'
module.exports = class PrebuildWebpackPlugin {
constructor({
build,
files = {},
watch = () => {},
clearCacheOnUpdate = false,
compilationNameFilter
} = {}) {
if (!build) {
throw new Error(
'The PrebuildWebpackPlugin expects a build argument. Please provide one.'
)
}
this.firstRun = true
this.build = build
this.files = files
this.watch = watch
this.compilationNameFilter = compilationNameFilter
this.clearCacheOnUpdate = clearCacheOnUpdate
}
// If the user provided a `files: { pattern }` config, this function will
// go and fetch all the matching files from the filesystem.
async getMatchedFiles() {
if (!this.files.pattern) return []
if (this.matchedFilesCache) return this.matchedFilesCache
debug('start: get matched files')
// glob resolves from either `options.cwd` or `process.cwd()` if not provided
// we replicate this option so we can return an absolute path
const root =
this.files.options && this.files.options.cwd
? this.files.options.cwd
: process.cwd()
// run the glob matcher and set the cache
const files = await glob(this.files.pattern, this.files.options)
this.matchedFilesCache = files
debug('finish: get matched files')
// glob returns paths relative to the root, but we want absolute paths, so we join
// the root on before returning
return files.map(f => path.join(root, f))
}
// Getting the file that changes per watch trigger is a bit involved when it comes
// to getting it out of the webpack compilation object - this abstracts out the process.
getChangedFile(compiler) {
const { watchFileSystem } = compiler
const watcher = watchFileSystem.watcher || watchFileSystem.wfs.watcher
const changedFile = Object.keys(watcher.mtimes)
return changedFile
}
// If a compilation name is given, returns whether the current compilation matches
// If not, returns false
filterCompilation(compilation) {
if (!compilation.name || !this.compilationNameFilter) return false
return compilation.name !== this.compilationNameFilter
}
// If the user provided a matcher, we filter for those files to pass into the function
// Otherwise we just run the function.
async runInitialBuild(compiler, compilation) {
const matchedFiles = await this.getMatchedFiles()
return this.build(compiler, compilation, matchedFiles)
}
// "apply" is the main entrypoint for webpack plugins
apply(compiler) {
// The "beforeRun" hook runs only on single webpack build, triggering only once
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async compilation => {
if (this.filterCompilation(compilation)) return Promise.resolve()
debug(`running "beforeRun" hook for compilation: ${compilation.name}`)
return this.runInitialBuild(compiler, compilation)
})
// The "watchRun" hook runs only when using 'watch mode' with webpack, triggering
// every time that webpack recompiles on a change triggered by the watcher
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async compilation => {
if (this.filterCompilation(compilation)) return Promise.resolve()
debug(`running "watchRun" hook for compilation: ${compilation.name}`)
// By default we cache the matched files for speed. This has the drawback of
// potentially having issues with new files that are added. If you're willing
// to take the perf hit on every update in exchange for better new file detection
// you can enable this option and we'll clear the cache each watch round.
// TODO: if we can get webpack to tell us when a new file was added, we could optimize
// this such that it only ran in this condition.
if (this.clearCacheOnUpdate) {
debug('clearing matched files cache')
this.matchedFilesCache = null
}
// Since "watchRun" runs every time webpack compiles in watch mode, we limit the
// initial build to the first execution.
if (this.firstRun) {
this.firstRun = false
debug('watch mode, running initial build only')
return this.runInitialBuild(compiler, compilation)
}
// At this point we're done unless we have a file pattern matcher passed in
if (!this.files.pattern) return Promise.resolve()
// If so, we get the file that was just changed, if there was one changed
const changedFile = this.getChangedFile(compiler)
if (!changedFile.length) return Promise.resolve()
// If the changed file doesn't match the provided pattern, we're done
const changedMatch = minimatch.match(changedFile, this.files.pattern)
if (!changedMatch.length) return Promise.resolve()
// If it does, we run the user-provided watch function
return this.watch(compiler, compilation, changedMatch)
})
// The "emit" hook runs before webpack is about to write out assets, at the end
// of the compile process.
compiler.hooks.emit.tapPromise(PLUGIN_NAME, async compilation => {
if (this.filterCompilation(compilation)) return Promise.resolve()
debug(`running "emit" hook for compilation: ${compilation.name}`)
// Grab the matched files so we can add them as dependencies
// If there are none, we're done
const matchedFiles = await this.getMatchedFiles()
if (!matchedFiles.length) return Promise.resolve()
// Add all matched files as dependencies to webpack so that they are
if (this.files.addFilesAsDependencies) {
matchedFiles.map(f => compilation.fileDependencies.add(f))
}
return Promise.resolve()
})
}
}