UNPKG

9.71 kBJavaScriptView Raw
1// @ts-check
2import fs from 'fs'
3import path from 'path'
4import nodeResolve from '@rollup/plugin-node-resolve'
5import typescript from '@rollup/plugin-typescript'
6import commonjs from '@rollup/plugin-commonjs'
7import json from '@rollup/plugin-json'
8import alias from '@rollup/plugin-alias'
9import license from 'rollup-plugin-license'
10import MagicString from 'magic-string'
11import chalk from 'chalk'
12
13/**
14 * @type { import('rollup').RollupOptions }
15 */
16const envConfig = {
17 input: path.resolve(__dirname, 'src/client/env.ts'),
18 plugins: [
19 typescript({
20 target: 'es2018',
21 include: ['src/client/env.ts'],
22 baseUrl: path.resolve(__dirname, 'src/env'),
23 paths: {
24 'types/*': ['../../types/*']
25 }
26 })
27 ],
28 output: {
29 file: path.resolve(__dirname, 'dist/client', 'env.mjs'),
30 sourcemap: true
31 }
32}
33
34/**
35 * @type { import('rollup').RollupOptions }
36 */
37const clientConfig = {
38 input: path.resolve(__dirname, 'src/client/client.ts'),
39 external: ['./env'],
40 plugins: [
41 typescript({
42 target: 'es2018',
43 include: ['src/client/**/*.ts'],
44 baseUrl: path.resolve(__dirname, 'src/client'),
45 paths: {
46 'types/*': ['../../types/*']
47 }
48 })
49 ],
50 output: {
51 file: path.resolve(__dirname, 'dist/client', 'client.mjs'),
52 sourcemap: true
53 }
54}
55
56/**
57 * @type { import('rollup').RollupOptions }
58 */
59const sharedNodeOptions = {
60 treeshake: {
61 moduleSideEffects: 'no-external',
62 propertyReadSideEffects: false,
63 tryCatchDeoptimization: false
64 },
65 output: {
66 dir: path.resolve(__dirname, 'dist/node'),
67 entryFileNames: `[name].js`,
68 chunkFileNames: 'chunks/dep-[hash].js',
69 exports: 'named',
70 format: 'cjs',
71 externalLiveBindings: false,
72 freeze: false,
73 sourcemap: true
74 },
75 onwarn(warning, warn) {
76 // node-resolve complains a lot about this but seems to still work?
77 if (warning.message.includes('Package subpath')) {
78 return
79 }
80 // we use the eval('require') trick to deal with optional deps
81 if (warning.message.includes('Use of eval')) {
82 return
83 }
84 if (warning.message.includes('Circular dependency')) {
85 return
86 }
87 warn(warning)
88 }
89}
90
91/**
92 * @type { import('rollup').RollupOptions }
93 */
94const nodeConfig = {
95 ...sharedNodeOptions,
96 input: {
97 index: path.resolve(__dirname, 'src/node/index.ts'),
98 cli: path.resolve(__dirname, 'src/node/cli.ts')
99 },
100 external: [
101 'fsevents',
102 ...Object.keys(require('./package.json').dependencies)
103 ],
104 plugins: [
105 alias({
106 // packages with "module" field that doesn't play well with cjs bundles
107 entries: {
108 '@vue/compiler-dom': require.resolve(
109 '@vue/compiler-dom/dist/compiler-dom.cjs.js'
110 ),
111 'big.js': require.resolve('big.js/big.js')
112 }
113 }),
114 nodeResolve({ preferBuiltins: true }),
115 typescript({
116 target: 'es2019',
117 include: ['src/**/*.ts'],
118 esModuleInterop: true
119 }),
120 // Some deps have try...catch require of optional deps, but rollup will
121 // generate code that force require them upfront for side effects.
122 // Shim them with eval() so rollup can skip these calls.
123 shimDepsPlugin({
124 'plugins/terser.ts': {
125 src: `require.resolve('terser'`,
126 replacement: `require.resolve('vite/dist/node/terser'`
127 },
128 // chokidar -> fsevents
129 'fsevents-handler.js': {
130 src: `require('fsevents')`,
131 replacement: `eval('require')('fsevents')`
132 },
133 // cac re-assigns module.exports even in its mjs dist
134 'cac/dist/index.mjs': {
135 src: `if (typeof module !== "undefined") {`,
136 replacement: `if (false) {`
137 },
138 // postcss-import -> sugarss
139 'process-content.js': {
140 src: 'require("sugarss")',
141 replacement: `eval('require')('sugarss')`
142 },
143 'import-from/index.js': {
144 pattern: /require\(resolveFrom/g,
145 replacement: `eval('require')(resolveFrom`
146 },
147 'lilconfig/dist/index.js': {
148 pattern: /: require,/g,
149 replacement: `: eval('require'),`
150 }
151 }),
152 // Optional peer deps of ws. Native deps that are mostly for performance.
153 // Since ws is not that perf critical for us, just ignore these deps.
154 ignoreDepPlugin({
155 bufferutil: 1,
156 'utf-8-validate': 1
157 }),
158 commonjs({ extensions: ['.js'] }),
159 json(),
160 licensePlugin()
161 ]
162}
163
164/**
165 * Terser needs to be run inside a worker, so it cannot be part of the main
166 * bundle. We produce a separate bundle for it and shims plugin/terser.ts to
167 * use the production path during build.
168 *
169 * @type { import('rollup').RollupOptions }
170 */
171const terserConfig = {
172 ...sharedNodeOptions,
173 output: {
174 ...sharedNodeOptions.output,
175 exports: 'default'
176 },
177 input: {
178 terser: require.resolve('terser')
179 },
180 plugins: [nodeResolve(), commonjs()]
181}
182
183/**
184 * @type { (deps: Record<string, { src?: string, replacement: string, pattern?: RegExp }>) => import('rollup').Plugin }
185 */
186function shimDepsPlugin(deps) {
187 const transformed = {}
188
189 return {
190 name: 'shim-deps',
191 transform(code, id) {
192 for (const file in deps) {
193 if (id.replace(/\\/g, '/').endsWith(file)) {
194 const { src, replacement, pattern } = deps[file]
195
196 const magicString = new MagicString(code)
197 if (src) {
198 const pos = code.indexOf(src)
199 if (pos < 0) {
200 this.error(
201 `Could not find expected src "${src}" in file "${file}"`
202 )
203 }
204 transformed[file] = true
205 magicString.overwrite(pos, pos + src.length, replacement)
206 console.log(`shimmed: ${file}`)
207 }
208
209 if (pattern) {
210 let match
211 while ((match = pattern.exec(code))) {
212 transformed[file] = true
213 const start = match.index
214 const end = start + match[0].length
215 magicString.overwrite(start, end, replacement)
216 }
217 if (!transformed[file]) {
218 this.error(
219 `Could not find expected pattern "${pattern}" in file "${file}"`
220 )
221 }
222 console.log(`shimmed: ${file}`)
223 }
224
225 return {
226 code: magicString.toString(),
227 map: magicString.generateMap({ hires: true })
228 }
229 }
230 }
231 },
232 buildEnd(err) {
233 if (!err) {
234 for (const file in deps) {
235 if (!transformed[file]) {
236 this.error(
237 `Did not find "${file}" which is supposed to be shimmed, was the file renamed?`
238 )
239 }
240 }
241 }
242 }
243 }
244}
245
246/**
247 * @type { (deps: Record<string, any>) => import('rollup').Plugin }
248 */
249function ignoreDepPlugin(ignoredDeps) {
250 return {
251 name: 'ignore-deps',
252 resolveId(id) {
253 if (id in ignoredDeps) {
254 return id
255 }
256 },
257 load(id) {
258 if (id in ignoredDeps) {
259 console.log(`ignored: ${id}`)
260 return ''
261 }
262 }
263 }
264}
265
266function licensePlugin() {
267 return license({
268 thirdParty(dependencies) {
269 // https://github.com/rollup/rollup/blob/master/build-plugins/generate-license-file.js
270 // MIT Licensed https://github.com/rollup/rollup/blob/master/LICENSE-CORE.md
271 const coreLicense = fs.readFileSync(
272 path.resolve(__dirname, '../../LICENSE')
273 )
274 const licenses = new Set()
275 const dependencyLicenseTexts = dependencies
276 .sort(({ name: nameA }, { name: nameB }) =>
277 nameA > nameB ? 1 : nameB > nameA ? -1 : 0
278 )
279 .map(
280 ({
281 name,
282 license,
283 licenseText,
284 author,
285 maintainers,
286 contributors,
287 repository
288 }) => {
289 let text = `## ${name}\n`
290 if (license) {
291 text += `License: ${license}\n`
292 }
293 const names = new Set()
294 if (author && author.name) {
295 names.add(author.name)
296 }
297 for (const person of maintainers.concat(contributors)) {
298 if (person && person.name) {
299 names.add(person.name)
300 }
301 }
302 if (names.size > 0) {
303 text += `By: ${Array.from(names).join(', ')}\n`
304 }
305 if (repository) {
306 text += `Repository: ${repository.url || repository}\n`
307 }
308 if (licenseText) {
309 text +=
310 '\n' +
311 licenseText
312 .trim()
313 .replace(/(\r\n|\r)/gm, '\n')
314 .split('\n')
315 .map((line) => `> ${line}`)
316 .join('\n') +
317 '\n'
318 }
319 licenses.add(license)
320 return text
321 }
322 )
323 .join('\n---------------------------------------\n\n')
324 const licenseText =
325 `# Vite core license\n` +
326 `Vite is released under the MIT license:\n\n` +
327 coreLicense +
328 `\n# Licenses of bundled dependencies\n` +
329 `The published Vite artifact additionally contains code with the following licenses:\n` +
330 `${Array.from(licenses).join(', ')}\n\n` +
331 `# Bundled dependencies:\n` +
332 dependencyLicenseTexts
333 const existingLicenseText = fs.readFileSync('LICENSE.md', 'utf8')
334 if (existingLicenseText !== licenseText) {
335 fs.writeFileSync('LICENSE.md', licenseText)
336 console.warn(
337 chalk.yellow(
338 '\nLICENSE.md updated. You should commit the updated file.\n'
339 )
340 )
341 }
342 }
343 })
344}
345
346export default [envConfig, clientConfig, nodeConfig, terserConfig]