/
generate.js
193 lines (163 loc) · 5.18 KB
/
generate.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import path, { relative } from 'path'
import upath from 'upath'
import fs from 'fs-extra'
import crc32 from 'crc/lib/crc32'
import consola from 'consola'
import globby from 'globby'
import destr from 'destr'
import { TARGETS } from '@nuxt/utils'
export async function generate (cmd) {
const nuxt = await getNuxt({ server: true }, cmd)
const generator = await cmd.getGenerator(nuxt)
await nuxt.server.listen(0)
const { errors } = await generator.generate({ build: false })
await nuxt.close()
if (cmd.argv['fail-on-error'] && errors.length > 0) {
throw new Error('Error generating pages, exiting with non-zero code')
}
}
export async function ensureBuild (cmd) {
const nuxt = await getNuxt({ _build: true, server: false }, cmd)
const { options } = nuxt
if (options.generate.cache === false || destr(process.env.NUXT_BUILD) || cmd.argv['force-build']) {
const builder = await cmd.getBuilder(nuxt)
await builder.build()
await nuxt.close()
return
}
// Default build ignore files
const ignore = [
options.buildDir,
options.dir.static,
options.generate.dir,
'node_modules',
'.**/*',
'.*',
'README.md'
]
// Extend ignore
const { generate } = options
if (typeof generate.cache.ignore === 'function') {
generate.cache.ignore = generate.cache.ignore(ignore)
} else if (Array.isArray(generate.cache.ignore)) {
generate.cache.ignore = generate.cache.ignore.concat(ignore)
}
await nuxt.callHook('generate:cache:ignore', generate.cache.ignore)
// Take a snapshot of current project
const snapshotOptions = {
rootDir: options.rootDir,
ignore: generate.cache.ignore.map(upath.normalize),
globbyOptions: generate.cache.globbyOptions
}
const currentBuildSnapshot = await snapshot(snapshotOptions)
// Detect process.env usage in nuxt.config
const processEnv = {}
if (nuxt.options._nuxtConfigFile) {
const configSrc = await fs.readFile(nuxt.options._nuxtConfigFile)
const envRegex = /process.env.(\w+)/g
let match
// eslint-disable-next-line no-cond-assign
while (match = envRegex.exec(configSrc)) {
processEnv[match[1]] = process.env[match[1]]
}
}
// Current build meta
const currentBuild = {
// @ts-ignore
nuxtVersion: nuxt.constructor.version,
ssr: nuxt.options.ssr,
target: nuxt.options.target,
snapshot: currentBuildSnapshot,
env: nuxt.options.env,
'process.env': processEnv
}
// Check if build can be skipped
const nuxtBuildFile = path.resolve(nuxt.options.buildDir, 'build.json')
if (fs.existsSync(nuxtBuildFile)) {
const previousBuild = destr(fs.readFileSync(nuxtBuildFile, 'utf-8')) || {}
// Quick diff
let needBuild = false
const fields = ['nuxtVersion', 'ssr', 'target']
if (nuxt.options.generate.ignoreEnv !== true) {
fields.push('env', 'process.env')
}
for (const field of fields) {
if (JSON.stringify(previousBuild[field]) !== JSON.stringify(currentBuild[field])) {
needBuild = true
consola.info(`Doing webpack rebuild because ${field} changed`)
break
}
}
// Full snapshot diff
if (!needBuild) {
const changed = compareSnapshots(previousBuild.snapshot, currentBuild.snapshot)
if (!changed) {
consola.success('Skipping webpack build as no changes detected')
await nuxt.close()
return
} else {
consola.info(`Doing webpack rebuild because ${changed} modified`)
}
}
}
// Webpack build
const builder = await cmd.getBuilder(nuxt)
await builder.build()
// Write build.json
fs.writeFileSync(nuxtBuildFile, JSON.stringify(currentBuild, null, 2), 'utf-8')
await nuxt.close()
}
async function getNuxt (args, cmd) {
const config = await cmd.getNuxtConfig({ dev: false, ...args })
if (config.target === TARGETS.static) {
config._export = true
} else {
config._legacyGenerate = true
}
config.buildDir = (config.static && config.static.cacheDir) || path.resolve(config.rootDir, 'node_modules/.cache/nuxt')
config.build = config.build || {}
// https://github.com/nuxt/nuxt.js/issues/7390
config.build.parallel = false
config.build.transpile = config.build.transpile || []
if (!config.static || !config.static.cacheDir) {
config.build.transpile.push('.cache/nuxt')
}
const nuxt = await cmd.getNuxt(config)
return nuxt
}
export function compareSnapshots (from, to) {
const allKeys = Array.from(new Set([
...Object.keys(from).sort(),
...Object.keys(to).sort()
]))
for (const key of allKeys) {
if (JSON.stringify(from[key]) !== JSON.stringify(to[key])) {
return key
}
}
return false
}
export async function snapshot ({ globbyOptions, ignore, rootDir }) {
const snapshot = {}
const files = await globby('**/*.*', {
...globbyOptions,
ignore,
cwd: rootDir,
absolute: true
})
await Promise.all(files.map(async (p) => {
const key = relative(rootDir, p)
try {
const fileContent = await fs.readFile(p)
snapshot[key] = {
checksum: await crc32(fileContent).toString(16)
}
} catch (e) {
// TODO: Check for other errors like permission denied
snapshot[key] = {
exists: false
}
}
}))
return snapshot
}