|
1 | | -/** |
2 | | - * Copyright 2018 Google LLC |
3 | | - * |
4 | | - * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
5 | | - * use this file except in compliance with the License. You may obtain a copy of |
6 | | - * the License at |
7 | | - * |
8 | | - * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | - * |
10 | | - * Unless required by applicable law or agreed to in writing, software |
11 | | - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
12 | | - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
13 | | - * License for the specific language governing permissions and limitations under |
14 | | - * the License. |
15 | | - */ |
16 | | - |
17 | 1 | const path = require('path'); |
18 | | -const chalk = require('chalk'); |
19 | | -const { promisify } = require('util'); |
20 | | -const globPromise = require('glob'); |
21 | | -const minimatch = require('minimatch'); |
22 | | -const gzipSize = require('gzip-size'); |
23 | | -const brotliSize = require('brotli-size'); |
24 | | -const prettyBytes = require('pretty-bytes'); |
25 | | -const { toMap, dedupe } = require('./utils.js'); |
26 | | -const glob = promisify(globPromise); |
27 | | - |
28 | | -brotliSize.file = (path, options) => { |
29 | | - return new Promise((resolve, reject) => { |
30 | | - const stream = fs.createReadStream(path); |
31 | | - stream.on('error', reject); |
32 | | - |
33 | | - const brotliStream = stream.pipe(brotliSize.stream(options)); |
34 | | - brotliStream.on('error', reject); |
35 | | - brotliStream.on('brotli-size', resolve); |
36 | | - }); |
37 | | -}; |
38 | | - |
| 2 | +const SizePluginCore = require('size-plugin-core'); |
39 | 3 |
|
40 | 4 | const defaults = { |
41 | 5 | gzip: true, |
42 | 6 | brotli: false, |
43 | | - pattern: '**/*.{mjs,js,css,html}', |
| 7 | + pattern: '**/*.{mjs,js,jsx,css,html}', |
44 | 8 | exclude: undefined, |
45 | | - columnWidth: 20 |
| 9 | + writeFile: true, |
| 10 | + publish: false |
46 | 11 | }; |
47 | | - |
| 12 | +/** |
| 13 | + * Size Plugin for Rollup |
| 14 | + * @param {Object} options |
| 15 | + * @param {string} [options.gzip] use gzip compression for files |
| 16 | + * @param {string} [options.brotli] use brotli compression for files |
| 17 | + * @param {string} [options.pattern] minimatch pattern of files to track |
| 18 | + * @param {string} [options.exclude] minimatch pattern of files NOT to track |
| 19 | + * @param {string} [options.filename] file name to save filesizes to disk |
| 20 | + * @param {boolean} [options.publish] option to publish filesizes to size-plugin-store |
| 21 | + * @param {boolean} [options.writeFile] option to save filesizes to disk |
| 22 | + */ |
48 | 23 | function bundleSize(options) { |
49 | | - const { pattern, exclude, columnWidth, brotli } = Object.assign(defaults, options); |
50 | | - |
51 | | - let max = ''; |
52 | | - let firstTime = true; |
53 | | - let initialSizes; |
54 | | - |
55 | | - const compressionSize = brotli ? brotliSize : gzipSize; |
56 | | - |
57 | | - function buildStart() { |
58 | | - max = ''; |
59 | | - } |
60 | | - |
61 | | - function renderChunk(code) { |
62 | | - max = max += code; |
63 | | - return null; |
64 | | - } |
| 24 | + const coreOptions = Object.assign(defaults, options); |
| 25 | + coreOptions.compression = coreOptions.brotli ? 'brotli' : 'gzip'; |
65 | 26 |
|
| 27 | + const core = new SizePluginCore(coreOptions); |
66 | 28 | async function generateBundle(outputOptions, bundle) { |
67 | | - if (firstTime) { |
68 | | - firstTime = false; |
69 | | - initialSizes = await getSizes(path.dirname(outputOptions.file)); |
70 | | - } |
71 | | - outputSizes(bundle).catch(console.error); |
72 | | - } |
73 | | - |
74 | | - async function outputSizes(assets) { |
75 | | - // map of filenames to their previous size |
76 | | - const sizesBefore = await Promise.resolve(initialSizes); |
77 | | - const isMatched = minimatch.filter(pattern); |
78 | | - const isExcluded = exclude ? minimatch.filter(exclude) : () => false; |
79 | | - const assetNames = Object.keys(assets).filter( |
80 | | - file => isMatched(file) && !isExcluded(file) |
81 | | - ); |
82 | | - const sizes = await Promise.all( |
83 | | - assetNames.map(name => compressionSize(assets[name].code)) |
84 | | - ); |
85 | | - |
86 | | - // map of de-hashed filenames to their final size |
87 | | - initialSizes = toMap(assetNames, sizes); |
88 | | - |
89 | | - // get a list of unique filenames |
90 | | - const files = Object.keys(initialSizes).filter(dedupe); |
91 | | - |
92 | | - const width = Math.max(...files.map(file => file.length)); |
93 | | - let output = ''; |
94 | | - for (const name of files) { |
95 | | - const size = initialSizes[name] || 0; |
96 | | - const delta = size - (sizesBefore[name] || 0); |
97 | | - const msg = |
98 | | - new Array( |
99 | | - (width !== name.length ? width : columnWidth) - name.length + 2 |
100 | | - ).join(' ') + |
101 | | - name + |
102 | | - ' ⏤ '; |
103 | | - const color = |
104 | | - size > 100 * 1024 |
105 | | - ? 'red' |
106 | | - : size > 40 * 1024 |
107 | | - ? 'yellow' |
108 | | - : size > 20 * 1024 |
109 | | - ? 'cyan' |
110 | | - : 'green'; |
111 | | - let sizeText = chalk[color](prettyBytes(size)); |
112 | | - if (delta) { |
113 | | - let deltaText = (delta > 0 ? '+' : '') + prettyBytes(delta); |
114 | | - if (delta > 1024) { |
115 | | - sizeText = chalk.bold(sizeText); |
116 | | - deltaText = chalk.red(deltaText); |
117 | | - } else if (delta < -10) { |
118 | | - deltaText = chalk.green(deltaText); |
119 | | - } |
120 | | - sizeText += ` (${deltaText})`; |
121 | | - } |
122 | | - output += msg + sizeText; |
123 | | - } |
124 | | - if (output) { |
125 | | - console.log(output); |
| 29 | + try { |
| 30 | + const assets = Object.keys(bundle).reduce((agg, key) => { |
| 31 | + agg[key] = { |
| 32 | + source: bundle[key].code |
| 33 | + }; |
| 34 | + return agg; |
| 35 | + }, {}); |
| 36 | + const outputPath = outputOptions.dir |
| 37 | + ? path.resolve(outputOptions.dir) |
| 38 | + : path.dirname(outputOptions.file); |
| 39 | + outputOptions.file && |
| 40 | + (core.options.pattern = `${Object.keys(assets).pop()}`); |
| 41 | + const output = await core.execute(assets, outputPath); |
| 42 | + output && console.log(output); |
| 43 | + } catch (error) { |
| 44 | + console.error(error); |
126 | 45 | } |
127 | 46 | } |
128 | | - |
129 | | - async function getSizes(cwd) { |
130 | | - const files = await glob(pattern, { cwd, ignore: exclude }); |
131 | | - |
132 | | - const sizes = await Promise.all( |
133 | | - files.map(file => compressionSize.file(path.join(cwd, file)).catch(() => null)) |
134 | | - ); |
135 | | - |
136 | | - return toMap(files, sizes); |
137 | | - } |
138 | | - |
139 | 47 | return { |
140 | 48 | name: 'rollup-plugin-size', |
141 | | - buildStart, |
142 | | - renderChunk, |
143 | 49 | generateBundle |
144 | 50 | }; |
145 | 51 | } |
|
0 commit comments