@atlaspack/core 2.35.0 → 2.38.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Atlaspack.js +41 -6
  3. package/dist/atlaspack-v3/AtlaspackV3.js +7 -4
  4. package/dist/atlaspack-v3/fs.js +1 -0
  5. package/dist/atlaspack-v3/worker/worker.js +11 -2
  6. package/dist/requests/AssetGraphRequestRust.js +5 -1
  7. package/dist/requests/AtlaspackBuildRequest.js +3 -0
  8. package/dist/requests/BundleGraphRequest.js +9 -6
  9. package/dist/requests/BundleGraphRequestRust.js +6 -1
  10. package/dist/requests/BundleGraphRequestUtils.js +133 -2
  11. package/dist/requests/WriteBundleRequest.js +168 -17
  12. package/lib/Atlaspack.js +47 -15
  13. package/lib/atlaspack-v3/AtlaspackV3.js +7 -4
  14. package/lib/atlaspack-v3/fs.js +1 -0
  15. package/lib/atlaspack-v3/worker/worker.js +13 -2
  16. package/lib/requests/AssetGraphRequestRust.js +5 -1
  17. package/lib/requests/AtlaspackBuildRequest.js +9 -0
  18. package/lib/requests/BundleGraphRequest.js +10 -7
  19. package/lib/requests/BundleGraphRequestRust.js +6 -1
  20. package/lib/requests/BundleGraphRequestUtils.js +132 -2
  21. package/lib/requests/WriteBundleRequest.js +181 -13
  22. package/lib/types/atlaspack-v3/AtlaspackV3.d.ts +3 -2
  23. package/lib/types/atlaspack-v3/fs.d.ts +1 -0
  24. package/lib/types/requests/BundleGraphRequestUtils.d.ts +7 -0
  25. package/lib/types/requests/WriteBundleRequest.d.ts +33 -0
  26. package/package.json +15 -15
  27. package/src/Atlaspack.ts +54 -12
  28. package/src/atlaspack-v3/AtlaspackV3.ts +22 -4
  29. package/src/atlaspack-v3/fs.ts +5 -0
  30. package/src/atlaspack-v3/worker/worker.ts +11 -2
  31. package/src/requests/AssetGraphRequestRust.ts +5 -1
  32. package/src/requests/AtlaspackBuildRequest.ts +4 -0
  33. package/src/requests/BundleGraphRequest.ts +11 -6
  34. package/src/requests/BundleGraphRequestRust.ts +8 -1
  35. package/src/requests/BundleGraphRequestUtils.ts +157 -1
  36. package/src/requests/WriteBundleRequest.ts +202 -22
  37. package/test/requests/WriteBundleRequest.test.ts +363 -0
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.SourceMapHashRefRewriteStream = void 0;
6
7
  exports.applyReplacementsToSourceMap = applyReplacementsToSourceMap;
8
+ exports.applyReplacementsToVLQMappings = applyReplacementsToVLQMappings;
7
9
  exports.computeSourceMapRoot = computeSourceMapRoot;
8
10
  exports.default = createWriteBundleRequest;
9
11
  var _constants = require("../constants");
@@ -78,7 +80,7 @@ function _featureFlags() {
78
80
  }
79
81
  var _EnvironmentManager = require("../EnvironmentManager");
80
82
  function _sourceMap() {
81
- const data = _interopRequireDefault(require("@atlaspack/source-map"));
83
+ const data = require("@atlaspack/source-map");
82
84
  _sourceMap = function () {
83
85
  return data;
84
86
  };
@@ -90,6 +92,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
90
92
  const HASH_REF_PREFIX_LEN = _constants.HASH_REF_PREFIX.length;
91
93
  const BOUNDARY_LENGTH = _constants.HASH_REF_PREFIX.length + 32 - 1;
92
94
  const HASH_REF_PLACEHOLDER_LEN = HASH_REF_PREFIX_LEN + _constants.HASH_REF_HASH_LEN;
95
+
96
+ // The JSON key prefix we scan for in the source map stream.
97
+ const MAPPINGS_KEY_BUF = Buffer.from('"mappings":"');
93
98
  /**
94
99
  * Writes a bundle to the dist directory, replacing hash references with the final content hashes.
95
100
  */
@@ -179,21 +184,11 @@ async function run({
179
184
  await writeFiles(contentStream, info, hashRefToNameHash, options, config, outputFS, filePath, writeOptions, devDeps, api, bundleReplacements);
180
185
  const hasSourceMap = await options.cache.has(mapKey);
181
186
  if (mapKey && env.sourceMap && !env.sourceMap.inline && hasSourceMap) {
187
+ const mapEntry = await options.cache.getBlob(mapKey);
182
188
  let mapStream;
183
189
  if ((0, _featureFlags().getFeatureFlag)('fixSourceMapHashRefs') && bundleReplacements && bundleReplacements.length > 0) {
184
- const mapEntry = await options.cache.getBlob(mapKey);
185
- const mapBuffer = Buffer.isBuffer(mapEntry) ? mapEntry : Buffer.from(mapEntry);
186
- const projectRoot = typeof options.projectRoot === 'string' ? options.projectRoot : String(options.projectRoot);
187
- const sourceMap = new (_sourceMap().default)(projectRoot, mapBuffer);
188
- applyReplacementsToSourceMap(sourceMap, bundleReplacements);
189
- const mapJson = await sourceMap.stringify({
190
- format: 'string',
191
- file: name,
192
- sourceRoot: computeSourceMapRoot(bundle, options)
193
- });
194
- mapStream = (0, _utils().blobToStream)(Buffer.from(typeof mapJson === 'string' ? mapJson : JSON.stringify(mapJson), 'utf8'));
190
+ mapStream = (0, _utils().blobToStream)(mapEntry).pipe(new SourceMapHashRefRewriteStream(bundleReplacements));
195
191
  } else {
196
- const mapEntry = await options.cache.getBlob(mapKey);
197
192
  mapStream = (0, _utils().blobToStream)(mapEntry);
198
193
  }
199
194
  await writeFiles(mapStream, info, hashRefToNameHash, options, config, outputFS, (0, _projectPath.toProjectPathUnsafe)((0, _projectPath.fromProjectPathRelative)(filePath) + '.map'), writeOptions, devDeps, api);
@@ -228,6 +223,178 @@ function applyReplacementsToSourceMap(sourceMap, replacements) {
228
223
  }
229
224
  }
230
225
 
226
+ /**
227
+ * Applies hash-ref replacement column offsets directly to a VLQ mappings
228
+ * string without deserializing the full source map into a native struct.
229
+ *
230
+ * Each replacement r describes a hash-ref that was substituted in the output
231
+ * file. r.column is in the progressively-shifted post-replacement coordinate
232
+ * space (matching the already-shifted source map state after all previous
233
+ * offsetColumns calls), so thresholds are applied sequentially against the
234
+ * running absCol values exactly as the native offsetColumns implementation does.
235
+ */
236
+ function applyReplacementsToVLQMappings(mappings, replacements) {
237
+ if (replacements.length === 0) return mappings;
238
+
239
+ // Group replacements by line (0-indexed), sorted by column ascending.
240
+ const byLine = new Map();
241
+ for (const r of replacements) {
242
+ let arr = byLine.get(r.line);
243
+ if (!arr) {
244
+ arr = [];
245
+ byLine.set(r.line, arr);
246
+ }
247
+ arr.push(r);
248
+ }
249
+ for (const arr of byLine.values()) {
250
+ arr.sort((a, b) => a.column - b.column);
251
+ }
252
+ const lines = mappings.split(';');
253
+ const resultLines = [];
254
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
255
+ const lineReps = byLine.get(lineIdx);
256
+ if (!lineReps || lineReps.length === 0) {
257
+ resultLines.push(lines[lineIdx]);
258
+ continue;
259
+ }
260
+ const line = lines[lineIdx];
261
+ if (!line) {
262
+ resultLines.push('');
263
+ continue;
264
+ }
265
+
266
+ // Decode segment column deltas to absolute columns.
267
+ const segments = line.split(',');
268
+ const colVlqEnds = [];
269
+ const absCols = [];
270
+ let absCol = 0;
271
+ for (const seg of segments) {
272
+ const {
273
+ value: colDelta,
274
+ nextPos
275
+ } = (0, _sourceMap().decodeVLQ)(seg, 0);
276
+ absCol += colDelta;
277
+ colVlqEnds.push(nextPos);
278
+ absCols.push(absCol);
279
+ }
280
+
281
+ // Apply each replacement's column shift sequentially against the
282
+ // current absCol values (which have already been adjusted by previous
283
+ // replacements on this line), mirroring the sequential offsetColumns calls.
284
+ for (const r of lineReps) {
285
+ const delta = r.newLength - r.originalLength;
286
+ if (delta === 0) continue;
287
+ const threshold = r.column + r.originalLength;
288
+ for (let i = 0; i < absCols.length; i++) {
289
+ if (absCols[i] >= threshold) {
290
+ absCols[i] += delta;
291
+ }
292
+ }
293
+ }
294
+
295
+ // Re-encode with updated absolute columns; only the leading column VLQ
296
+ // field of each segment changes – the tail bytes are sliced unchanged.
297
+ const resultSegments = [];
298
+ let prevAbsCol = 0;
299
+ for (let i = 0; i < segments.length; i++) {
300
+ const newDelta = absCols[i] - prevAbsCol;
301
+ prevAbsCol = absCols[i];
302
+ resultSegments.push((0, _sourceMap().encodeVLQ)(newDelta) + segments[i].slice(colVlqEnds[i]));
303
+ }
304
+ resultLines.push(resultSegments.join(','));
305
+ }
306
+ return resultLines.join(';');
307
+ }
308
+ /**
309
+ * A Transform stream that rewrites the "mappings" VLQ field of a source map
310
+ * JSON to account for hash-ref replacements, without ever loading the full
311
+ * JSON object or the native Rust SourceMapInner into memory.
312
+ *
313
+ * Field order in cached source maps (from partialVlqMapToSourceMap / toVLQ):
314
+ * mappings → sources → sourcesContent → names → version → file → sourceRoot
315
+ *
316
+ * "mappings" is the very first field, so we scan only a tiny header before
317
+ * switching to zero-copy passthrough for the bulk sourcesContent bytes.
318
+ */
319
+ class SourceMapHashRefRewriteStream extends _stream().Transform {
320
+ constructor(replacements) {
321
+ super();
322
+ this.replacements = replacements;
323
+ this.state = 'scanning';
324
+ this.scanBuf = Buffer.alloc(0);
325
+ this.mappingsBufs = [];
326
+ }
327
+
328
+ // @ts-expect-error TS7006
329
+ _transform(chunk, _encoding, cb) {
330
+ if (this.state === 'passthrough') {
331
+ this.push(chunk);
332
+ cb();
333
+ return;
334
+ }
335
+ if (this.state === 'scanning') {
336
+ const combined = Buffer.concat([this.scanBuf, chunk]);
337
+ const idx = combined.indexOf(MAPPINGS_KEY_BUF);
338
+ if (idx === -1) {
339
+ // Key not yet found – hold back enough bytes to handle a split key.
340
+ const keepLen = Math.min(combined.length, MAPPINGS_KEY_BUF.length - 1);
341
+ if (combined.length > keepLen) {
342
+ this.push(combined.slice(0, combined.length - keepLen));
343
+ }
344
+ this.scanBuf = combined.slice(combined.length - keepLen);
345
+ cb();
346
+ return;
347
+ }
348
+
349
+ // Emit everything up to and including the key.
350
+ const keyEnd = idx + MAPPINGS_KEY_BUF.length;
351
+ this.push(combined.slice(0, keyEnd));
352
+ this.scanBuf = Buffer.alloc(0);
353
+ this.state = 'buffering';
354
+ this._bufferingTransform(combined.slice(keyEnd), cb);
355
+ return;
356
+ }
357
+
358
+ // state === 'buffering'
359
+ this._bufferingTransform(chunk, cb);
360
+ }
361
+
362
+ // @ts-expect-error TS7006
363
+ _bufferingTransform(chunk, cb) {
364
+ // Mappings values contain only base64 chars, ';', and ',' – no escaping –
365
+ // so scanning for the closing '"' (0x22) is safe.
366
+ const closeIdx = chunk.indexOf(0x22);
367
+ if (closeIdx === -1) {
368
+ this.mappingsBufs.push(chunk);
369
+ cb();
370
+ return;
371
+ }
372
+ this.mappingsBufs.push(chunk.slice(0, closeIdx));
373
+
374
+ // VLQ chars are all ASCII (<128), so latin1 round-trips without loss.
375
+ const mappingsStr = Buffer.concat(this.mappingsBufs).toString('latin1');
376
+ const rewritten = applyReplacementsToVLQMappings(mappingsStr, this.replacements);
377
+ this.push(Buffer.from(rewritten, 'latin1'));
378
+
379
+ // Emit the closing '"' and everything remaining in one push.
380
+ this.push(chunk.slice(closeIdx));
381
+ this.state = 'passthrough';
382
+ this.mappingsBufs = [];
383
+ cb();
384
+ }
385
+
386
+ // @ts-expect-error TS7006
387
+ _flush(cb) {
388
+ if (this.state === 'scanning' && this.scanBuf.length > 0) {
389
+ this.push(this.scanBuf);
390
+ } else if (this.state === 'buffering') {
391
+ // Malformed JSON – flush whatever we buffered as-is.
392
+ this.push(Buffer.concat(this.mappingsBufs));
393
+ }
394
+ cb();
395
+ }
396
+ }
397
+
231
398
  /**
232
399
  * Computes the sourceRoot for a source map file. This is the relative path from
233
400
  * the output directory back to the project root, so that source paths (stored
@@ -238,6 +405,7 @@ function applyReplacementsToSourceMap(sourceMap, replacements) {
238
405
  *
239
406
  * This logic must stay in sync with PackagerRunner.generateSourceMap.
240
407
  */
408
+ exports.SourceMapHashRefRewriteStream = SourceMapHashRefRewriteStream;
241
409
  function computeSourceMapRoot(bundle, options) {
242
410
  let name = (0, _nullthrows().default)(bundle.name);
243
411
  let filePath = (0, _projectPath.joinProjectPath)(bundle.target.distDir, name);
@@ -24,8 +24,9 @@ export declare class AtlaspackV3 {
24
24
  constructor(atlaspack_napi: AtlaspackNapi, napiWorkerPool: INapiWorkerPool, isDefaultNapiWorkerPool: boolean);
25
25
  static create({ fs, packageManager, threads, lmdb, napiWorkerPool, ...options }: AtlaspackV3Options): Promise<AtlaspackV3>;
26
26
  end(): void;
27
- buildAssetGraph(): Promise<any>;
28
- buildBundleGraph(): Promise<any>;
27
+ buildAssetGraph(progressCallback?: (eventJson: string) => void): Promise<any>;
28
+ buildBundleGraph(progressCallback?: (eventJson: string) => void): Promise<any>;
29
+ build(progressCallback: (eventJson: string) => void): Promise<any>;
29
30
  loadBundleGraph(bundleGraph: BundleGraph): Promise<void>;
30
31
  updateBundleGraph(bundleGraph: BundleGraph, changedAssetIds: string[]): Promise<void>;
31
32
  package(bundleId: string, options?: PackageOptions): Promise<[RunPackagerRunnerResult, Diagnostic | null]>;
@@ -9,4 +9,5 @@ export declare class FileSystemV3 implements FileSystem {
9
9
  isFile: JsCallable<[FilePath], boolean>;
10
10
  isDir: JsCallable<[FilePath], boolean>;
11
11
  readFile: JsCallable<[FilePath, Encoding], string>;
12
+ writeFile: JsCallable<[FilePath, number[]], Promise<void>>;
12
13
  }
@@ -17,6 +17,13 @@ import type { BundleGraphResult } from './BundleGraphRequest';
17
17
  * Throws an assertion error if duplicate bundle names are found.
18
18
  */
19
19
  export declare function validateBundles(bundleGraph: InternalBundleGraph): void;
20
+ /**
21
+ * Dump a canonical JSON snapshot of the bundle graph for parity comparison.
22
+ * Gated by ATLASPACK_DUMP_BUNDLE_GRAPH environment variable which specifies the output directory.
23
+ * The snapshot captures bundle identity, type, contained assets, and bundle group structure
24
+ * in a deterministic, sorted format suitable for diffing.
25
+ */
26
+ export declare function dumpBundleGraphSnapshot(bundleGraph: InternalBundleGraph, variant: 'js' | 'rust'): void;
20
27
  /**
21
28
  * Names a bundle by running through the configured namers until one returns a name.
22
29
  */
@@ -4,6 +4,7 @@ import type { StaticRunOpts } from '../RequestTracker';
4
4
  import type { Bundle, PackagedBundleInfo, AtlaspackOptions } from '../types';
5
5
  import type BundleGraph from '../BundleGraph';
6
6
  import type { BundleInfo } from '../PackagerRunner';
7
+ import { Transform } from 'stream';
7
8
  import { requestTypes } from '../RequestTracker';
8
9
  import SourceMap from '@atlaspack/source-map';
9
10
  export type HashRefReplacement = {
@@ -33,6 +34,38 @@ export type WriteBundleRequest = {
33
34
  */
34
35
  export default function createWriteBundleRequest(input: WriteBundleRequestInput): WriteBundleRequest;
35
36
  export declare function applyReplacementsToSourceMap(sourceMap: SourceMap, replacements: HashRefReplacement[]): void;
37
+ /**
38
+ * Applies hash-ref replacement column offsets directly to a VLQ mappings
39
+ * string without deserializing the full source map into a native struct.
40
+ *
41
+ * Each replacement r describes a hash-ref that was substituted in the output
42
+ * file. r.column is in the progressively-shifted post-replacement coordinate
43
+ * space (matching the already-shifted source map state after all previous
44
+ * offsetColumns calls), so thresholds are applied sequentially against the
45
+ * running absCol values exactly as the native offsetColumns implementation does.
46
+ */
47
+ export declare function applyReplacementsToVLQMappings(mappings: string, replacements: HashRefReplacement[]): string;
48
+ /**
49
+ * A Transform stream that rewrites the "mappings" VLQ field of a source map
50
+ * JSON to account for hash-ref replacements, without ever loading the full
51
+ * JSON object or the native Rust SourceMapInner into memory.
52
+ *
53
+ * Field order in cached source maps (from partialVlqMapToSourceMap / toVLQ):
54
+ * mappings → sources → sourcesContent → names → version → file → sourceRoot
55
+ *
56
+ * "mappings" is the very first field, so we scan only a tiny header before
57
+ * switching to zero-copy passthrough for the bulk sourcesContent bytes.
58
+ */
59
+ export declare class SourceMapHashRefRewriteStream extends Transform {
60
+ private replacements;
61
+ private state;
62
+ private scanBuf;
63
+ private mappingsBufs;
64
+ constructor(replacements: HashRefReplacement[]);
65
+ _transform(chunk: Buffer, _encoding: string, cb: any): void;
66
+ private _bufferingTransform;
67
+ _flush(cb: any): void;
68
+ }
36
69
  /**
37
70
  * Computes the sourceRoot for a source map file. This is the relative path from
38
71
  * the output directory back to the project root, so that source paths (stored
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/core",
3
- "version": "2.35.0",
3
+ "version": "2.38.0",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,22 +23,22 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@mischnic/json-sourcemap": "^0.1.0",
26
- "@atlaspack/build-cache": "2.13.13",
27
- "@atlaspack/cache": "3.2.50",
26
+ "@atlaspack/build-cache": "2.13.14",
27
+ "@atlaspack/cache": "3.2.53",
28
28
  "@atlaspack/diagnostic": "2.14.4",
29
29
  "@atlaspack/events": "2.14.4",
30
- "@atlaspack/feature-flags": "2.30.1",
31
- "@atlaspack/fs": "2.15.50",
32
- "@atlaspack/graph": "3.6.17",
33
- "@atlaspack/logger": "2.14.47",
34
- "@atlaspack/package-manager": "2.14.55",
35
- "@atlaspack/plugin": "2.14.55",
36
- "@atlaspack/profiler": "2.15.16",
37
- "@atlaspack/rust": "3.24.0",
38
- "@atlaspack/types": "2.15.45",
39
- "@atlaspack/utils": "3.3.7",
40
- "@atlaspack/workers": "2.14.55",
41
- "@atlaspack/source-map": "3.2.10",
30
+ "@atlaspack/feature-flags": "2.31.0",
31
+ "@atlaspack/fs": "2.15.53",
32
+ "@atlaspack/graph": "3.6.20",
33
+ "@atlaspack/logger": "2.14.50",
34
+ "@atlaspack/package-manager": "2.14.58",
35
+ "@atlaspack/plugin": "2.14.58",
36
+ "@atlaspack/profiler": "2.15.19",
37
+ "@atlaspack/rust": "3.26.0",
38
+ "@atlaspack/types": "2.15.48",
39
+ "@atlaspack/utils": "3.4.0",
40
+ "@atlaspack/workers": "2.14.58",
41
+ "@atlaspack/source-map": "3.3.2",
42
42
  "base-x": "^3.0.8",
43
43
  "browserslist": "^4.6.6",
44
44
  "clone": "^2.1.1",
package/src/Atlaspack.ts CHANGED
@@ -67,6 +67,7 @@ import {AtlaspackV3, FileSystemV3} from './atlaspack-v3';
67
67
  import createAssetGraphRequestJS from './requests/AssetGraphRequest';
68
68
  import {createAssetGraphRequestRust} from './requests/AssetGraphRequestRust';
69
69
  import type {AssetGraphRequestResult} from './requests/AssetGraphRequest';
70
+ import {getBundleGraph} from './requests/BundleGraphRequestRust';
70
71
  import {loadRustWorkerThreadDylibHack} from './rustWorkerThreadDylibHack';
71
72
 
72
73
  registerCoreWithSerializer();
@@ -171,6 +172,7 @@ export default class Atlaspack {
171
172
  let rustAtlaspack: AtlaspackV3;
172
173
  if (
173
174
  resolvedOptions.featureFlags.atlaspackV3 ||
175
+ resolvedOptions.featureFlags.fullNative ||
174
176
  resolvedOptions.featureFlags.nativePackager
175
177
  ) {
176
178
  // eslint-disable-next-line no-unused-vars
@@ -433,19 +435,59 @@ export default class Atlaspack {
433
435
 
434
436
  this.#requestTracker.graph.invalidateOnBuildNodes();
435
437
 
436
- let request = createAtlaspackBuildRequest({
437
- optionsRef: this.#optionsRef,
438
- requestedAssetIds: this.#requestedAssetIds,
439
- signal,
440
- });
438
+ let bundleGraph: any;
439
+ let bundleInfo: Map<any, any>;
440
+ let changedAssets: Map<any, any>;
441
+ let assetRequests: Array<any>;
442
+ let scopeHoistingStats: any;
443
+
444
+ if (getFeatureFlag('fullNative') && this.rustAtlaspack) {
445
+ let [result, error] = await this.rustAtlaspack.build(
446
+ (eventJson: string) => {
447
+ let event = JSON.parse(eventJson);
448
+ this.#reporterRunner.report(event);
449
+ },
450
+ );
451
+
452
+ if (error) {
453
+ throw new ThrowableDiagnostic({diagnostic: error});
454
+ }
455
+ ({bundleGraph, changedAssets} = getBundleGraph(result));
456
+ bundleInfo = new Map(
457
+ (result.bundleInfo ?? []).map(
458
+ (info: {
459
+ bundleId: string;
460
+ filePath: string;
461
+ type: string;
462
+ size: number;
463
+ time: number;
464
+ }) => [
465
+ info.bundleId,
466
+ {
467
+ filePath: toProjectPath(options.projectRoot, info.filePath),
468
+ bundleId: info.bundleId,
469
+ type: info.type,
470
+ stats: {size: info.size, time: info.time},
471
+ },
472
+ ],
473
+ ),
474
+ );
475
+ assetRequests = result.assetRequests ?? [];
476
+ } else {
477
+ let request = createAtlaspackBuildRequest({
478
+ optionsRef: this.#optionsRef,
479
+ requestedAssetIds: this.#requestedAssetIds,
480
+ signal,
481
+ });
441
482
 
442
- let {
443
- bundleGraph,
444
- bundleInfo,
445
- changedAssets,
446
- assetRequests,
447
- scopeHoistingStats,
448
- } = await this.#requestTracker.runRequest(request, {force: true});
483
+ ({
484
+ bundleGraph,
485
+ bundleInfo,
486
+ changedAssets,
487
+ assetRequests,
488
+ scopeHoistingStats,
489
+ } = await this.#requestTracker.runRequest(request, {force: true}));
490
+ }
449
491
 
450
492
  this.#requestedAssetIds.clear();
451
493
 
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  atlaspackNapiCreate,
3
+ atlaspackNapiBuild,
3
4
  atlaspackNapiBuildAssetGraph,
4
5
  atlaspackNapiBuildBundleGraph,
5
6
  atlaspackNapiRespondToFsEvents,
@@ -100,12 +101,29 @@ export class AtlaspackV3 {
100
101
  }
101
102
  }
102
103
 
103
- buildAssetGraph(): Promise<any> {
104
- return atlaspackNapiBuildAssetGraph(this._atlaspack_napi) as Promise<any>;
104
+ buildAssetGraph(
105
+ progressCallback?: (eventJson: string) => void,
106
+ ): Promise<any> {
107
+ return atlaspackNapiBuildAssetGraph(
108
+ this._atlaspack_napi,
109
+ progressCallback,
110
+ ) as Promise<any>;
105
111
  }
106
112
 
107
- buildBundleGraph(): Promise<any> {
108
- return atlaspackNapiBuildBundleGraph(this._atlaspack_napi) as Promise<any>;
113
+ buildBundleGraph(
114
+ progressCallback?: (eventJson: string) => void,
115
+ ): Promise<any> {
116
+ return atlaspackNapiBuildBundleGraph(
117
+ this._atlaspack_napi,
118
+ progressCallback,
119
+ ) as Promise<any>;
120
+ }
121
+
122
+ build(progressCallback: (eventJson: string) => void): Promise<any> {
123
+ return atlaspackNapiBuild(
124
+ this._atlaspack_napi,
125
+ progressCallback,
126
+ ) as Promise<any>;
109
127
  }
110
128
 
111
129
  loadBundleGraph(bundleGraph: BundleGraph): Promise<void> {
@@ -51,4 +51,9 @@ export class FileSystemV3 implements FileSystem {
51
51
  }
52
52
  },
53
53
  );
54
+
55
+ writeFile: JsCallable<[FilePath, number[]], Promise<void>> = jsCallable(
56
+ (path: FilePath, contents: number[]) =>
57
+ this.#fs.writeFile(path, Buffer.from(contents)),
58
+ );
54
59
  }
@@ -173,15 +173,24 @@ export class AtlaspackWorker {
173
173
  resolution: {type: 'excluded'},
174
174
  };
175
175
  }
176
+ // A resolver may return a result without filePath to indicate it didn't
177
+ // resolve the dependency (equivalent to returning null). The JS-side
178
+ // PathRequest treats this as "try the next resolver".
179
+ if (!result.filePath) {
180
+ return {
181
+ invalidations: [],
182
+ resolution: {type: 'unresolved'},
183
+ };
184
+ }
176
185
 
177
186
  return {
178
187
  invalidations: [],
179
188
  resolution: {
180
189
  type: 'resolved',
181
- filePath: result.filePath || '',
190
+ filePath: result.filePath,
182
191
  canDefer: result.canDefer || false,
183
192
  sideEffects: result.sideEffects ?? true,
184
- code: result.code || undefined,
193
+ code: result.code ?? undefined,
185
194
  meta: result.meta || undefined,
186
195
  pipeline: result.pipeline || undefined,
187
196
  priority: dependencyPriorityMap.intoNullable(result.priority),
@@ -7,6 +7,7 @@ import {getFeatureFlag} from '@atlaspack/feature-flags';
7
7
 
8
8
  import AssetGraph from '../AssetGraph';
9
9
  import type {AtlaspackV3} from '../atlaspack-v3';
10
+ import {report} from '../ReporterRunner';
10
11
  import {requestTypes, StaticRunOpts} from '../RequestTracker';
11
12
  import {propagateSymbols} from '../SymbolPropagation';
12
13
  import type {
@@ -54,7 +55,10 @@ export function createAssetGraphRequestRust(
54
55
  run: async (runInput) => {
55
56
  let options = runInput.options;
56
57
  let {assetGraphPromise, commitPromise} =
57
- await rustAtlaspack.buildAssetGraph();
58
+ await rustAtlaspack.buildAssetGraph((eventJson: string) => {
59
+ let event = JSON.parse(eventJson);
60
+ report(event);
61
+ });
58
62
 
59
63
  let [serializedAssetGraph, assetGraphError] =
60
64
  (await assetGraphPromise) as [SerializedAssetGraphDelta, Error | null];
@@ -23,6 +23,7 @@ import {tracer} from '@atlaspack/profiler';
23
23
  import {requestTypes} from '../RequestTracker';
24
24
  import {getFeatureFlag} from '@atlaspack/feature-flags';
25
25
  import {fromEnvironmentId} from '../EnvironmentManager';
26
+ import {tracer as atlaspackTracer} from '@atlaspack/logger';
26
27
 
27
28
  type AtlaspackBuildRequestInput = {
28
29
  optionsRef: SharedReference;
@@ -144,6 +145,8 @@ async function run({
144
145
  });
145
146
 
146
147
  let packagingMeasurement = tracer.createMeasurement('packaging');
148
+ const span = atlaspackTracer.enter('writeBundles');
149
+
147
150
  let writeBundlesRequest = createWriteBundlesRequest({
148
151
  bundleGraph,
149
152
  optionsRef,
@@ -152,6 +155,7 @@ async function run({
152
155
  let {bundleInfo, scopeHoistingStats} =
153
156
  await api.runRequest(writeBundlesRequest);
154
157
  packagingMeasurement && packagingMeasurement.end();
158
+ atlaspackTracer.exit(span);
155
159
  assertSignalNotAborted(signal);
156
160
 
157
161
  return {
@@ -43,6 +43,7 @@ import {
43
43
  nameBundle,
44
44
  loadPluginConfigWithDevDeps,
45
45
  runDevDepRequest as runDevDepRequestShared,
46
+ dumpBundleGraphSnapshot,
46
47
  } from './BundleGraphRequestUtils';
47
48
  import createAssetGraphRequestJS from './AssetGraphRequest';
48
49
  import {createAssetGraphRequestRust} from './AssetGraphRequestRust';
@@ -396,16 +397,20 @@ class BundlerRunner {
396
397
  }
397
398
 
398
399
  // this the normal bundle workflow (bundle, optimizing, run-times, naming)
399
- await bundler.bundle({
400
- bundleGraph: mutableBundleGraph,
401
- config: this.configs.get(plugin.name)?.result,
402
- options: this.pluginOptions,
403
- logger,
404
- tracer,
400
+ await instrumentAsync('bundle (V2)', async () => {
401
+ await bundler.bundle({
402
+ bundleGraph: mutableBundleGraph,
403
+ config: this.configs.get(plugin.name)?.result,
404
+ options: this.pluginOptions,
405
+ logger,
406
+ tracer,
407
+ });
405
408
  });
406
409
 
407
410
  measurement && measurement.end();
408
411
 
412
+ dumpBundleGraphSnapshot(internalBundleGraph, 'js');
413
+
409
414
  if (this.pluginOptions.mode === 'production') {
410
415
  let optimizeMeasurement;
411
416
  try {
@@ -7,6 +7,7 @@ import {instrument, instrumentAsync, PluginLogger} from '@atlaspack/logger';
7
7
  import {getFeatureFlag} from '@atlaspack/feature-flags';
8
8
 
9
9
  import InternalBundleGraph, {bundleGraphEdgeTypes} from '../BundleGraph';
10
+ import {report} from '../ReporterRunner';
10
11
  import dumpGraphToGraphViz from '../dumpGraphToGraphViz';
11
12
  import nullthrows from 'nullthrows';
12
13
  import {hashString} from '@atlaspack/rust';
@@ -32,6 +33,7 @@ import {
32
33
  nameBundle,
33
34
  loadPluginConfigWithDevDeps,
34
35
  runDevDepRequest,
36
+ dumpBundleGraphSnapshot,
35
37
  } from './BundleGraphRequestUtils';
36
38
  import {toEnvironmentRef} from '../EnvironmentManager';
37
39
  import {getEnvironmentHash} from '../Environment';
@@ -95,7 +97,10 @@ export default function createBundleGraphRequestRust(
95
97
  invariant(rustAtlaspack, 'BundleGraphRequestRust requires rustAtlaspack');
96
98
 
97
99
  let {bundleGraphPromise, commitPromise} =
98
- await rustAtlaspack.buildBundleGraph();
100
+ await rustAtlaspack.buildBundleGraph((eventJson: string) => {
101
+ let event = JSON.parse(eventJson);
102
+ report(event);
103
+ });
99
104
  let [serializedBundleGraph, bundleGraphError] =
100
105
  (await bundleGraphPromise) as [SerializedBundleGraph, Error | null];
101
106
 
@@ -109,6 +114,8 @@ export default function createBundleGraphRequestRust(
109
114
  () => getBundleGraph(serializedBundleGraph),
110
115
  );
111
116
 
117
+ dumpBundleGraphSnapshot(bundleGraph, 'rust');
118
+
112
119
  const runner = new NativeBundlerRunner(
113
120
  {api, options} as any,
114
121
  input.optionsRef,