@emulsify/core 4.3.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,16 @@ import {
39
39
  import { classifyBuildError } from './build-errors.js';
40
40
  import { renderBanner, renderRebuild, renderSummary } from './render.js';
41
41
  import { createStyler, supportsColor, supportsUnicode } from './format.js';
42
+ import {
43
+ buildInputFileRows,
44
+ buildInputRows,
45
+ buildOutputFileRows,
46
+ diffFingerprints,
47
+ fingerprintBundle,
48
+ summarizeBundle,
49
+ watchedRootLabel,
50
+ } from './source-roots.js';
51
+ import { isDetailed } from './verbosity.js';
42
52
  import { resolvePackageVersion } from '../../utils/package-version.js';
43
53
 
44
54
  /**
@@ -85,6 +95,7 @@ export function countEntries(input) {
85
95
  * now?: () => number,
86
96
  * clock?: () => Date,
87
97
  * colorEnabled?: boolean,
98
+ * detailed?: boolean,
88
99
  * version?: string
89
100
  * }} options - Plugin options.
90
101
  * @returns {import('vite').PluginOption} Develop reporter plugin.
@@ -97,6 +108,7 @@ export function developReporterPlugin({
97
108
  clock = () => new Date(),
98
109
  colorEnabled,
99
110
  unicodeEnabled,
111
+ detailed,
100
112
  version,
101
113
  } = {}) {
102
114
  const styler = createStyler(
@@ -104,14 +116,23 @@ export function developReporterPlugin({
104
116
  );
105
117
  const unicode =
106
118
  unicodeEnabled === undefined ? supportsUnicode() : unicodeEnabled;
119
+ const verbose = detailed === undefined ? isDetailed() : detailed;
107
120
 
108
121
  let watching = false;
109
122
  let outDir = 'dist';
110
- let entryCount;
123
+ let inputRows = [];
124
+ let inputFiles = [];
125
+ let watchLabel;
111
126
  let cycleStart = 0;
112
127
  let cyclePrinted = true;
113
128
  let firstCycleComplete = false;
114
129
  let changedFiles = [];
130
+ let writeTally;
131
+ let outputFiles = [];
132
+ let changedOutputs = [];
133
+ let removedOutputs = [];
134
+ let fingerprints = new Map();
135
+ let transformedModules = new Set();
115
136
 
116
137
  /**
117
138
  * Write an array of finished lines to the destination stream.
@@ -178,6 +199,10 @@ export function developReporterPlugin({
178
199
  durationMs,
179
200
  changedFiles,
180
201
  projectDir: env.projectDir,
202
+ moduleCount: verbose ? transformedModules.size : undefined,
203
+ changedOutputs,
204
+ removedOutputs,
205
+ detailed: verbose,
181
206
  styler,
182
207
  now: clock(),
183
208
  }),
@@ -232,6 +257,13 @@ export function developReporterPlugin({
232
257
  existsSync(join(env.projectDir, sharedDirectory)),
233
258
  ),
234
259
  },
260
+ platform: env.platform,
261
+ inputRows,
262
+ watchLabel,
263
+ write: writeTally,
264
+ inputFiles,
265
+ outputFiles,
266
+ unicode,
235
267
  styler,
236
268
  }),
237
269
  );
@@ -255,13 +287,36 @@ export function developReporterPlugin({
255
287
  if (!watching) return;
256
288
 
257
289
  outDir = config.build?.outDir || 'dist';
258
- entryCount = countEntries(config.build?.rollupOptions?.input);
290
+
291
+ // Attribution is resolved here, from the entry map the config already
292
+ // carries, so the summary can name each source root and what it
293
+ // contributed without touching the filesystem.
294
+ inputRows = buildInputRows({
295
+ entries: config.build?.rollupOptions?.input,
296
+ sourceRootRecords: env.projectStructure?.sourceRootRecords,
297
+ globalRootDirectories: env.projectStructure?.globalRoots,
298
+ projectDir: env.projectDir,
299
+ });
300
+
301
+ // Named from the resolved source roots rather than the input rows, so the
302
+ // label is the truth about what Rollup is watching even when a root
303
+ // produced no entries to report.
304
+ watchLabel = watchedRootLabel({
305
+ sourceRootRecords: env.projectStructure?.sourceRootRecords,
306
+ projectDir: env.projectDir,
307
+ });
308
+
309
+ // One stat per entry, once, and only when the listing will be printed.
310
+ if (verbose) {
311
+ inputFiles = buildInputFileRows({
312
+ entries: config.build?.rollupOptions?.input,
313
+ projectDir: env.projectDir,
314
+ });
315
+ }
259
316
 
260
317
  emit(
261
318
  renderBanner({
262
319
  version: version || resolvePackageVersion(env.projectDir),
263
- platform: env.platform,
264
- entryCount,
265
320
  unicode,
266
321
  styler,
267
322
  }),
@@ -273,8 +328,27 @@ export function developReporterPlugin({
273
328
  diagnostics.reset();
274
329
  cycleStart = now();
275
330
  cyclePrinted = false;
331
+ transformedModules = new Set();
332
+ changedOutputs = [];
333
+ removedOutputs = [];
276
334
  },
277
335
 
336
+ // Rolldown's own `N modules transformed.` count is unavailable here by
337
+ // design: it comes from Rust instrumentation that Vite only registers when
338
+ // `logLevel` admits info, and keeping the level low is what removes the
339
+ // colliding progress line. Counting the ids that reach this hook is
340
+ // equivalent for the question being asked — what did this cycle recompile —
341
+ // and it costs one Set insert per module. The hook is only attached in
342
+ // detailed mode so the default path is untouched.
343
+ ...(verbose
344
+ ? {
345
+ transform(_code, id) {
346
+ if (watching) transformedModules.add(id);
347
+ return null;
348
+ },
349
+ }
350
+ : {}),
351
+
278
352
  watchChange(id) {
279
353
  if (!watching) return;
280
354
  changedFiles.push(id);
@@ -297,7 +371,42 @@ export function developReporterPlugin({
297
371
  reportCycle();
298
372
  },
299
373
 
300
- writeBundle() {
374
+ // The bundle is reduced here rather than in the summary because this is the
375
+ // only hook that receives it. Raising `logLevel` to quiet the develop loop
376
+ // discards Rolldown's per-file asset table, and this recovers the three
377
+ // facts from it worth keeping.
378
+ writeBundle(_options, bundle) {
379
+ if (watching) {
380
+ writeTally = summarizeBundle(bundle);
381
+
382
+ if (verbose) {
383
+ // Rollup regenerates the whole bundle every cycle, so the first build
384
+ // lists everything and later cycles list only what came out different.
385
+ // Gzip is computed for the full listing once, then only for the files
386
+ // a rebuild changed — which is what keeps the watch loop from paying
387
+ // Rolldown's `computing gzip size...` pause on every keystroke.
388
+ const current = fingerprintBundle(bundle);
389
+
390
+ if (firstCycleComplete) {
391
+ const diff = diffFingerprints(fingerprints, current);
392
+ const changed = new Set(diff.changed);
393
+
394
+ changedOutputs = buildOutputFileRows(
395
+ Object.fromEntries(
396
+ Object.entries(bundle).filter(([fileName]) =>
397
+ changed.has(fileName),
398
+ ),
399
+ ),
400
+ );
401
+ removedOutputs = diff.removed;
402
+ } else {
403
+ outputFiles = buildOutputFileRows(bundle);
404
+ }
405
+
406
+ fingerprints = current;
407
+ }
408
+ }
409
+
301
410
  reportCycle();
302
411
  },
303
412