@atlaspack/core 2.16.2-dev.14 → 2.16.2-dev.55

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 (37) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/lib/Atlaspack.js +10 -2
  3. package/lib/AtlaspackConfig.schema.js +7 -1
  4. package/lib/BundleGraph.js +2 -100
  5. package/lib/PackagerRunner.js +44 -9
  6. package/lib/RequestTracker.js +326 -121
  7. package/lib/Transformation.js +2 -2
  8. package/lib/UncommittedAsset.js +17 -0
  9. package/lib/atlaspack-v3/worker/compat/environment.js +2 -2
  10. package/lib/atlaspack-v3/worker/compat/mutable-asset.js +6 -6
  11. package/lib/atlaspack-v3/worker/compat/plugin-config.js +5 -5
  12. package/lib/atlaspack-v3/worker/index.js +3 -0
  13. package/lib/atlaspack-v3/worker/worker.js +8 -0
  14. package/lib/dumpGraphToGraphViz.js +1 -1
  15. package/lib/public/BundleGraph.js +21 -8
  16. package/lib/public/Config.js +28 -0
  17. package/lib/requests/AssetGraphRequest.js +13 -1
  18. package/lib/requests/BundleGraphRequest.js +13 -1
  19. package/lib/requests/WriteBundleRequest.js +11 -2
  20. package/lib/resolveOptions.js +7 -4
  21. package/lib/worker.js +18 -1
  22. package/package.json +23 -19
  23. package/src/Atlaspack.js +13 -5
  24. package/src/BundleGraph.js +0 -167
  25. package/src/PackagerRunner.js +60 -9
  26. package/src/RequestTracker.js +491 -137
  27. package/src/UncommittedAsset.js +16 -1
  28. package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
  29. package/src/atlaspack-v3/worker/worker.js +7 -0
  30. package/src/public/BundleGraph.js +22 -15
  31. package/src/public/Config.js +39 -5
  32. package/src/requests/AssetGraphRequest.js +13 -3
  33. package/src/requests/BundleGraphRequest.js +13 -3
  34. package/src/requests/WriteBundleRequest.js +9 -2
  35. package/src/resolveOptions.js +4 -2
  36. package/test/RequestTracker.test.js +120 -5
  37. package/test/test-utils.js +1 -7
@@ -30,7 +30,12 @@ import {ATLASPACK_VERSION} from './constants';
30
30
  import {createAsset, createAssetIdFromOptions} from './assetUtils';
31
31
  import {BundleBehaviorNames} from './types';
32
32
  import {invalidateOnFileCreateToInternal, createInvalidations} from './utils';
33
- import {type ProjectPath, fromProjectPath} from './projectPath';
33
+ import {
34
+ type ProjectPath,
35
+ fromProjectPath,
36
+ fromProjectPathRelative,
37
+ } from './projectPath';
38
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
34
39
 
35
40
  type UncommittedAssetOptions = {|
36
41
  value: Asset,
@@ -153,7 +158,9 @@ export default class UncommittedAsset {
153
158
  size = content.length;
154
159
  }
155
160
 
161
+ // Maybe we should just store this in a file instead of LMDB
156
162
  await this.options.cache.setBlob(contentKey, content);
163
+
157
164
  return {size, hash};
158
165
  }
159
166
 
@@ -214,6 +221,9 @@ export default class UncommittedAsset {
214
221
  this.clearAST();
215
222
  }
216
223
 
224
+ /**
225
+ * @deprecated This has been broken on any cache other than FSCache for a long time.
226
+ */
217
227
  setStream(stream: Readable) {
218
228
  this.content = stream;
219
229
  this.clearAST();
@@ -296,6 +306,11 @@ export default class UncommittedAsset {
296
306
  }
297
307
 
298
308
  getCacheKey(key: string): string {
309
+ if (getFeatureFlag('cachePerformanceImprovements')) {
310
+ const filePath = fromProjectPathRelative(this.value.filePath);
311
+ return `Asset/${ATLASPACK_VERSION}/${filePath}/${this.value.id}/${key}`;
312
+ }
313
+
299
314
  return hashString(ATLASPACK_VERSION + key + this.value.id);
300
315
  }
301
316
 
@@ -100,11 +100,15 @@ export class PluginConfig implements IPluginConfig {
100
100
  // eslint-disable-next-line no-unused-vars
101
101
  filePaths: Array<FilePath>,
102
102
  // eslint-disable-next-line no-unused-vars
103
- options?: {|
104
- packageKey?: string,
105
- parse?: boolean,
106
- exclude?: boolean,
107
- |},
103
+ options?:
104
+ | {|
105
+ packageKey?: string,
106
+ parse?: boolean,
107
+ exclude?: boolean,
108
+ |}
109
+ | {|
110
+ configKey?: string,
111
+ |},
108
112
  ): Promise<?ConfigResultWithFilePath<T>> {
109
113
  return this.#inner.getConfigFrom(searchPath, filePaths, options);
110
114
  }
@@ -141,6 +141,13 @@ export class AtlaspackWorker {
141
141
  };
142
142
  }
143
143
 
144
+ if (result.isExcluded) {
145
+ return {
146
+ invalidations: [],
147
+ resolution: {type: 'excluded'},
148
+ };
149
+ }
150
+
144
151
  return {
145
152
  invalidations: [],
146
153
  resolution: {
@@ -31,6 +31,7 @@ import Dependency, {
31
31
  import {targetToInternalTarget} from './Target';
32
32
  import {fromInternalSourceLocation} from '../utils';
33
33
  import BundleGroup, {bundleGroupToInternalBundleGroup} from './BundleGroup';
34
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
34
35
 
35
36
  // Friendly access for other modules within this package that need access
36
37
  // to the internal bundle.
@@ -185,16 +186,6 @@ export default class BundleGraph<TBundle: IBundle>
185
186
  );
186
187
  }
187
188
 
188
- getReferencedAssets(
189
- bundle: IBundle,
190
- cache: Map<Bundle, Set<string>>,
191
- ): Set<string> {
192
- return this.#graph.getReferencedAssets(
193
- bundleToInternalBundle(bundle),
194
- cache,
195
- );
196
- }
197
-
198
189
  hasParentBundleOfType(bundle: IBundle, type: string): boolean {
199
190
  return this.#graph.hasParentBundleOfType(
200
191
  bundleToInternalBundle(bundle),
@@ -499,11 +490,27 @@ export default class BundleGraph<TBundle: IBundle>
499
490
  for (let bundle of bundles) {
500
491
  const conditions = bundleConditions.get(bundle.id) ?? new Map();
501
492
 
502
- conditions.set(cond.key, {
503
- bundle,
504
- ifTrueBundles,
505
- ifFalseBundles,
506
- });
493
+ const currentCondition = conditions.get(cond.key);
494
+
495
+ if (getFeatureFlag('conditionalBundlingReporterSameConditionFix')) {
496
+ conditions.set(cond.key, {
497
+ bundle,
498
+ ifTrueBundles: [
499
+ ...(currentCondition?.ifTrueBundles ?? []),
500
+ ...ifTrueBundles,
501
+ ],
502
+ ifFalseBundles: [
503
+ ...(currentCondition?.ifFalseBundles ?? []),
504
+ ...ifFalseBundles,
505
+ ],
506
+ });
507
+ } else {
508
+ conditions.set(cond.key, {
509
+ bundle,
510
+ ifTrueBundles,
511
+ ifFalseBundles,
512
+ });
513
+ }
507
514
 
508
515
  bundleConditions.set(bundle.id, conditions);
509
516
  }
@@ -20,6 +20,7 @@ import {
20
20
  } from '@atlaspack/utils';
21
21
  import Environment from './Environment';
22
22
  import {fromProjectPath, toProjectPath} from '../projectPath';
23
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
23
24
 
24
25
  const internalConfigToConfig: DefaultWeakMap<
25
26
  AtlaspackOptions,
@@ -132,11 +133,21 @@ export default class PublicConfig implements IConfig {
132
133
  async getConfigFrom<T>(
133
134
  searchPath: FilePath,
134
135
  fileNames: Array<string>,
135
- options: ?{|
136
- packageKey?: string,
137
- parse?: boolean,
138
- exclude?: boolean,
139
- |},
136
+ options:
137
+ | ?{|
138
+ /**
139
+ * @deprecated Use `configKey` instead.
140
+ */
141
+ packageKey?: string,
142
+ parse?: boolean,
143
+ exclude?: boolean,
144
+ |}
145
+ | ?{|
146
+ /**
147
+ * If specified, only invalidate when this config key changes.
148
+ */
149
+ configKey?: string,
150
+ |},
140
151
  ): Promise<?ConfigResultWithFilePath<T>> {
141
152
  let packageKey = options?.packageKey;
142
153
  if (packageKey != null) {
@@ -155,6 +166,29 @@ export default class PublicConfig implements IConfig {
155
166
  }
156
167
  }
157
168
 
169
+ if (getFeatureFlag('granularTsConfigInvalidation')) {
170
+ const configKey = options?.configKey;
171
+ if (configKey != null) {
172
+ for (let fileName of fileNames) {
173
+ let config = await this.getConfigFrom(searchPath, [fileName], {
174
+ exclude: true,
175
+ });
176
+
177
+ if (config && config.contents[configKey]) {
178
+ // Invalidate only when the package key changes
179
+ this.invalidateOnConfigKeyChange(config.filePath, configKey);
180
+
181
+ return {
182
+ contents: config.contents[configKey],
183
+ filePath: config.filePath,
184
+ };
185
+ }
186
+ }
187
+
188
+ // fall through so that file above invalidations are registered
189
+ }
190
+ }
191
+
158
192
  if (fileNames.length === 0) {
159
193
  return null;
160
194
  }
@@ -20,6 +20,7 @@ import logger from '@atlaspack/logger';
20
20
 
21
21
  import invariant from 'assert';
22
22
  import nullthrows from 'nullthrows';
23
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
23
24
  import {PromiseQueue, setEqual} from '@atlaspack/utils';
24
25
  import {hashString} from '@atlaspack/rust';
25
26
  import ThrowableDiagnostic from '@atlaspack/diagnostic';
@@ -161,12 +162,21 @@ export class AssetGraphBuilder {
161
162
  this.shouldBuildLazily = shouldBuildLazily ?? false;
162
163
  this.lazyIncludes = lazyIncludes ?? [];
163
164
  this.lazyExcludes = lazyExcludes ?? [];
164
- this.cacheKey =
165
- hashString(
165
+ if (getFeatureFlag('cachePerformanceImprovements')) {
166
+ const key = hashString(
166
167
  `${ATLASPACK_VERSION}${name}${JSON.stringify(entries) ?? ''}${
167
168
  options.mode
168
169
  }${options.shouldBuildLazily ? 'lazy' : 'eager'}`,
169
- ) + '-AssetGraph';
170
+ );
171
+ this.cacheKey = `AssetGraph/${ATLASPACK_VERSION}/${options.mode}/${key}`;
172
+ } else {
173
+ this.cacheKey =
174
+ hashString(
175
+ `${ATLASPACK_VERSION}${name}${JSON.stringify(entries) ?? ''}${
176
+ options.mode
177
+ }${options.shouldBuildLazily ? 'lazy' : 'eager'}`,
178
+ ) + '-AssetGraph';
179
+ }
170
180
 
171
181
  this.isSingleChangeRebuild =
172
182
  api
@@ -20,6 +20,7 @@ import invariant from 'assert';
20
20
  import assert from 'assert';
21
21
  import nullthrows from 'nullthrows';
22
22
  import {PluginLogger} from '@atlaspack/logger';
23
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
23
24
  import ThrowableDiagnostic, {errorToDiagnostic} from '@atlaspack/diagnostic';
24
25
  import AssetGraph from '../AssetGraph';
25
26
  import BundleGraph from '../public/BundleGraph';
@@ -282,12 +283,21 @@ class BundlerRunner {
282
283
  this.pluginOptions = new PluginOptions(
283
284
  optionsProxy(this.options, api.invalidateOnOptionChange),
284
285
  );
285
- this.cacheKey =
286
- hashString(
286
+ if (getFeatureFlag('cachePerformanceImprovements')) {
287
+ const key = hashString(
287
288
  `${ATLASPACK_VERSION}:BundleGraph:${
288
289
  JSON.stringify(options.entries) ?? ''
289
290
  }${options.mode}${options.shouldBuildLazily ? 'lazy' : 'eager'}`,
290
- ) + '-BundleGraph';
291
+ );
292
+ this.cacheKey = `BundleGraph/${ATLASPACK_VERSION}/${options.mode}/${key}`;
293
+ } else {
294
+ this.cacheKey =
295
+ hashString(
296
+ `${ATLASPACK_VERSION}:BundleGraph:${
297
+ JSON.stringify(options.entries) ?? ''
298
+ }${options.mode}${options.shouldBuildLazily ? 'lazy' : 'eager'}`,
299
+ ) + '-BundleGraph';
300
+ }
291
301
  }
292
302
 
293
303
  async loadConfigs() {
@@ -40,6 +40,7 @@ import {AtlaspackConfig} from '../AtlaspackConfig';
40
40
  import ThrowableDiagnostic, {errorToDiagnostic} from '@atlaspack/diagnostic';
41
41
  import {PluginTracer, tracer} from '@atlaspack/profiler';
42
42
  import {requestTypes} from '../RequestTracker';
43
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
43
44
 
44
45
  const HASH_REF_PREFIX_LEN = HASH_REF_PREFIX.length;
45
46
  const BOUNDARY_LENGTH = HASH_REF_PREFIX.length + 32 - 1;
@@ -167,14 +168,20 @@ async function run({input, options, api}) {
167
168
  api,
168
169
  );
169
170
 
171
+ const hasSourceMap = getFeatureFlag('cachePerformanceImprovements')
172
+ ? await options.cache.hasLargeBlob(mapKey)
173
+ : await options.cache.has(mapKey);
170
174
  if (
171
175
  mapKey &&
172
176
  bundle.env.sourceMap &&
173
177
  !bundle.env.sourceMap.inline &&
174
- (await options.cache.has(mapKey))
178
+ hasSourceMap
175
179
  ) {
180
+ const mapEntry = getFeatureFlag('cachePerformanceImprovements')
181
+ ? await options.cache.getLargeBlob(mapKey)
182
+ : await options.cache.getBlob(mapKey);
176
183
  await writeFiles(
177
- blobToStream(await options.cache.getBlob(mapKey)),
184
+ blobToStream(mapEntry),
178
185
  info,
179
186
  hashRefToNameHash,
180
187
  options,
@@ -153,8 +153,10 @@ export default async function resolveOptions(
153
153
 
154
154
  const needsRustLmdbCache = getFeatureFlag('atlaspackV3');
155
155
 
156
- if (!needsRustLmdbCache && !(outputFS instanceof NodeFS)) {
157
- return new FSCache(outputFS, cacheDir);
156
+ if (!getFeatureFlag('cachePerformanceImprovements')) {
157
+ if (!needsRustLmdbCache && !(outputFS instanceof NodeFS)) {
158
+ return new FSCache(outputFS, cacheDir);
159
+ }
158
160
  }
159
161
 
160
162
  return new LMDBLiteCache(cacheDir);
@@ -5,19 +5,39 @@ import nullthrows from 'nullthrows';
5
5
  import RequestTracker, {
6
6
  type RunAPI,
7
7
  cleanUpOrphans,
8
+ runInvalidation,
9
+ getBiggestFSEventsInvalidations,
10
+ invalidateRequestGraphFSEvents,
8
11
  } from '../src/RequestTracker';
9
12
  import {Graph} from '@atlaspack/graph';
13
+ import {LMDBLiteCache} from '@atlaspack/cache';
10
14
  import WorkerFarm from '@atlaspack/workers';
11
15
  import {DEFAULT_OPTIONS} from './test-utils';
12
16
  import {FILE_CREATE, FILE_UPDATE, INITIAL_BUILD} from '../src/constants';
13
17
  import {makeDeferredWithPromise} from '@atlaspack/utils';
14
18
  import {toProjectPath} from '../src/projectPath';
15
- import {DEFAULT_FEATURE_FLAGS, setFeatureFlags} from '../../feature-flags/src';
16
-
17
- const options = DEFAULT_OPTIONS;
19
+ import {
20
+ DEFAULT_FEATURE_FLAGS,
21
+ setFeatureFlags,
22
+ } from '../../../unified/src/feature-flags/index.js';
23
+ import sinon from 'sinon';
24
+ import type {AtlaspackOptions} from '../src/types';
25
+
26
+ const options = {
27
+ ...DEFAULT_OPTIONS,
28
+ cache: new LMDBLiteCache(DEFAULT_OPTIONS.cacheDir),
29
+ };
18
30
  const farm = new WorkerFarm({workerPath: require.resolve('../src/worker')});
19
31
 
20
32
  describe('RequestTracker', () => {
33
+ beforeEach(async () => {
34
+ await options.cache.ensure();
35
+
36
+ for (const key of options.cache.keys()) {
37
+ await options.cache.getNativeRef().delete(key);
38
+ }
39
+ });
40
+
21
41
  it('should not run requests that have not been invalidated', async () => {
22
42
  let tracker = new RequestTracker({farm, options});
23
43
  await tracker.runRequest({
@@ -482,7 +502,7 @@ describe('RequestTracker', () => {
482
502
  input: null,
483
503
  });
484
504
  const requestId = tracker.graph.getNodeIdByContentKey('abc');
485
- const invalidated = await tracker.respondToFSEvents(
505
+ const {didInvalidate: invalidated} = await tracker.respondToFSEvents(
486
506
  [
487
507
  {
488
508
  type: 'update',
@@ -521,7 +541,7 @@ describe('RequestTracker', () => {
521
541
  input: null,
522
542
  });
523
543
  const requestId = tracker.graph.getNodeIdByContentKey('abc');
524
- const invalidated = await tracker.respondToFSEvents(
544
+ const {didInvalidate: invalidated} = await tracker.respondToFSEvents(
525
545
  [
526
546
  {
527
547
  type: 'create',
@@ -584,3 +604,98 @@ root --- node1 --- node2 ----------- orphan1 --- orphan2
584
604
  assert.equal(Array.from(graph.getAllEdges()).length, 3);
585
605
  });
586
606
  });
607
+
608
+ describe('runInvalidation', () => {
609
+ it('calls an invalidationFn and tracks the number of invalidated nodes', async () => {
610
+ const mockRequestTracker = {
611
+ getInvalidNodeCount: sinon.stub(),
612
+ };
613
+
614
+ mockRequestTracker.getInvalidNodeCount.returns(10000);
615
+ const result = await runInvalidation(mockRequestTracker, {
616
+ key: 'fsEvents',
617
+ fn: () => {
618
+ mockRequestTracker.getInvalidNodeCount.returns(30000);
619
+ return {
620
+ biggestInvalidations: [{path: 'my-file', count: 10000}],
621
+ };
622
+ },
623
+ });
624
+
625
+ assert.equal(result.key, 'fsEvents');
626
+ assert.equal(result.count, 20000);
627
+ assert.deepEqual(result.detail, {
628
+ biggestInvalidations: [{path: 'my-file', count: 10000}],
629
+ });
630
+ assert(result.duration > 0, 'Duration was not reported');
631
+ });
632
+ });
633
+
634
+ describe('invalidateRequestGraphFSEvents', () => {
635
+ it('calls requestGraph.respondToFSEvents and returns the biggest invalidations', async () => {
636
+ const requestGraph = {
637
+ respondToFSEvents: sinon.stub(),
638
+ };
639
+
640
+ requestGraph.respondToFSEvents.returns({
641
+ invalidationsByPath: new Map([
642
+ ['file-1', 10],
643
+ ['file-2', 5000],
644
+ ['file-3', 8000],
645
+ ]),
646
+ });
647
+ // $FlowFixMe
648
+ const options: AtlaspackOptions = {
649
+ unstableFileInvalidations: undefined,
650
+ };
651
+
652
+ const result = await invalidateRequestGraphFSEvents(requestGraph, options, [
653
+ {
654
+ path: 'file-1',
655
+ type: 'create',
656
+ },
657
+ {
658
+ path: 'file-2',
659
+ type: 'update',
660
+ },
661
+ {
662
+ path: 'file-3',
663
+ type: 'delete',
664
+ },
665
+ ]);
666
+
667
+ assert.deepEqual(result.biggestInvalidations, [
668
+ {path: 'file-3', count: 8000},
669
+ {path: 'file-2', count: 5000},
670
+ {path: 'file-1', count: 10},
671
+ ]);
672
+ assert.equal(requestGraph.respondToFSEvents.callCount, 1);
673
+ assert.deepEqual(requestGraph.respondToFSEvents.args[0], [
674
+ [
675
+ {path: 'file-1', type: 'create'},
676
+ {path: 'file-2', type: 'update'},
677
+ {path: 'file-3', type: 'delete'},
678
+ ],
679
+ options,
680
+ 10000,
681
+ true,
682
+ ]);
683
+ });
684
+ });
685
+
686
+ describe('getBiggestFSEventsInvalidations', () => {
687
+ it('returns the paths that invalidated the most nodes', () => {
688
+ const invalidationsByPath = new Map([
689
+ ['file-1', 10],
690
+ ['file-2', 5000],
691
+ ['file-3', 8000],
692
+ ['file-4', 1000],
693
+ ['file-5', 1000],
694
+ ]);
695
+
696
+ assert.deepEqual(getBiggestFSEventsInvalidations(invalidationsByPath, 2), [
697
+ {path: 'file-3', count: 8000},
698
+ {path: 'file-2', count: 5000},
699
+ ]);
700
+ });
701
+ });
@@ -3,18 +3,12 @@
3
3
  import type {Environment, AtlaspackOptions, Target} from '../src/types';
4
4
 
5
5
  import {DEFAULT_FEATURE_FLAGS} from '@atlaspack/feature-flags';
6
- import {FSCache} from '@atlaspack/cache';
7
- import tempy from 'tempy';
8
- import {inputFS, outputFS} from '@atlaspack/test-utils';
6
+ import {inputFS, outputFS, cache, cacheDir} from '@atlaspack/test-utils';
9
7
  import {relativePath} from '@atlaspack/utils';
10
8
  import {NodePackageManager} from '@atlaspack/package-manager';
11
9
  import {createEnvironment} from '../src/Environment';
12
10
  import {toProjectPath} from '../src/projectPath';
13
11
 
14
- let cacheDir = tempy.directory();
15
- export let cache: FSCache = new FSCache(outputFS, cacheDir);
16
- cache.ensure();
17
-
18
12
  export const DEFAULT_OPTIONS: AtlaspackOptions = {
19
13
  cacheDir,
20
14
  parcelVersion: '',