@atlaspack/core 2.16.2-canary.13 → 2.16.2-canary.131

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 (95) hide show
  1. package/CHANGELOG.md +300 -0
  2. package/index.d.ts +4 -0
  3. package/lib/AssetGraph.js +27 -7
  4. package/lib/Atlaspack.js +35 -27
  5. package/lib/AtlaspackConfig.schema.js +7 -1
  6. package/lib/BundleGraph.js +8 -5
  7. package/lib/Dependency.js +6 -2
  8. package/lib/Environment.js +5 -3
  9. package/lib/EnvironmentManager.js +137 -0
  10. package/lib/InternalConfig.js +3 -2
  11. package/lib/PackagerRunner.js +54 -16
  12. package/lib/RequestTracker.js +345 -132
  13. package/lib/SymbolPropagation.js +14 -0
  14. package/lib/Transformation.js +2 -2
  15. package/lib/UncommittedAsset.js +20 -2
  16. package/lib/applyRuntimes.js +2 -1
  17. package/lib/assetUtils.js +2 -1
  18. package/lib/atlaspack-v3/AtlaspackV3.js +16 -3
  19. package/lib/atlaspack-v3/worker/compat/environment.js +2 -2
  20. package/lib/atlaspack-v3/worker/compat/mutable-asset.js +6 -6
  21. package/lib/atlaspack-v3/worker/compat/plugin-config.js +5 -5
  22. package/lib/atlaspack-v3/worker/index.js +3 -0
  23. package/lib/atlaspack-v3/worker/worker.js +8 -0
  24. package/lib/dumpGraphToGraphViz.js +1 -1
  25. package/lib/index.js +29 -1
  26. package/lib/public/Asset.js +7 -9
  27. package/lib/public/Bundle.js +12 -13
  28. package/lib/public/BundleGraph.js +3 -2
  29. package/lib/public/BundleGroup.js +2 -3
  30. package/lib/public/Config.js +95 -8
  31. package/lib/public/Dependency.js +4 -4
  32. package/lib/public/Environment.js +2 -3
  33. package/lib/public/MutableBundleGraph.js +5 -4
  34. package/lib/public/PluginOptions.js +1 -2
  35. package/lib/public/Target.js +4 -4
  36. package/lib/requests/AssetGraphRequest.js +13 -1
  37. package/lib/requests/AssetGraphRequestRust.js +17 -2
  38. package/lib/requests/AssetRequest.js +2 -1
  39. package/lib/requests/BundleGraphRequest.js +13 -1
  40. package/lib/requests/ConfigRequest.js +27 -4
  41. package/lib/requests/DevDepRequest.js +11 -1
  42. package/lib/requests/PathRequest.js +10 -0
  43. package/lib/requests/TargetRequest.js +18 -16
  44. package/lib/requests/WriteBundleRequest.js +15 -3
  45. package/lib/requests/WriteBundlesRequest.js +22 -1
  46. package/lib/resolveOptions.js +7 -4
  47. package/lib/worker.js +18 -1
  48. package/package.json +18 -25
  49. package/src/AssetGraph.js +30 -7
  50. package/src/Atlaspack.js +40 -23
  51. package/src/BundleGraph.js +13 -8
  52. package/src/Dependency.js +13 -5
  53. package/src/Environment.js +9 -6
  54. package/src/EnvironmentManager.js +145 -0
  55. package/src/InternalConfig.js +6 -5
  56. package/src/PackagerRunner.js +72 -20
  57. package/src/RequestTracker.js +526 -157
  58. package/src/SymbolPropagation.js +13 -1
  59. package/src/UncommittedAsset.js +23 -3
  60. package/src/applyRuntimes.js +6 -1
  61. package/src/assetUtils.js +4 -3
  62. package/src/atlaspack-v3/AtlaspackV3.js +24 -3
  63. package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
  64. package/src/atlaspack-v3/worker/index.js +2 -1
  65. package/src/atlaspack-v3/worker/worker.js +7 -0
  66. package/src/index.js +5 -1
  67. package/src/public/Asset.js +13 -6
  68. package/src/public/Bundle.js +12 -11
  69. package/src/public/BundleGraph.js +10 -2
  70. package/src/public/BundleGroup.js +2 -2
  71. package/src/public/Config.js +132 -18
  72. package/src/public/Dependency.js +4 -3
  73. package/src/public/Environment.js +2 -2
  74. package/src/public/MutableBundleGraph.js +8 -5
  75. package/src/public/PluginOptions.js +1 -1
  76. package/src/public/Target.js +4 -3
  77. package/src/requests/AssetGraphRequest.js +13 -3
  78. package/src/requests/AssetGraphRequestRust.js +14 -2
  79. package/src/requests/AssetRequest.js +2 -1
  80. package/src/requests/BundleGraphRequest.js +13 -3
  81. package/src/requests/ConfigRequest.js +33 -9
  82. package/src/requests/DevDepRequest.js +22 -9
  83. package/src/requests/PathRequest.js +4 -0
  84. package/src/requests/TargetRequest.js +19 -25
  85. package/src/requests/WriteBundleRequest.js +14 -8
  86. package/src/requests/WriteBundlesRequest.js +31 -3
  87. package/src/resolveOptions.js +4 -2
  88. package/src/types.js +10 -7
  89. package/test/Environment.test.js +43 -34
  90. package/test/EnvironmentManager.test.js +192 -0
  91. package/test/PublicEnvironment.test.js +10 -7
  92. package/test/RequestTracker.test.js +124 -29
  93. package/test/public/Config.test.js +108 -0
  94. package/test/requests/ConfigRequest.test.js +199 -7
  95. package/test/test-utils.js +4 -9
@@ -3,6 +3,7 @@
3
3
  import type {ContentKey, NodeId} from '@atlaspack/graph';
4
4
  import type {Meta, Symbol} from '@atlaspack/types';
5
5
  import type {Diagnostic} from '@atlaspack/diagnostic';
6
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
6
7
  import type {
7
8
  AssetNode,
8
9
  DependencyNode,
@@ -17,7 +18,7 @@ import {setEqual} from '@atlaspack/utils';
17
18
  import logger from '@atlaspack/logger';
18
19
  import {md, convertSourceLocationToHighlight} from '@atlaspack/diagnostic';
19
20
  import {instrument} from '@atlaspack/logger';
20
- import {BundleBehavior, Priority} from './types';
21
+ import {BundleBehavior, Priority, SpecifierType} from './types';
21
22
  import {fromProjectPathRelative, fromProjectPath} from './projectPath';
22
23
 
23
24
  export function propagateSymbols({
@@ -62,6 +63,17 @@ export function propagateSymbols({
62
63
  changedAssets,
63
64
  assetGroupsWithRemovedParents,
64
65
  (assetNode, incomingDeps, outgoingDeps) => {
66
+ if (getFeatureFlag('emptyFileStarRexportFix')) {
67
+ if (
68
+ assetNode.value.meta.emptyFileStarReexport &&
69
+ incomingDeps.every(
70
+ (d) => d.value.specifierType === SpecifierType.esm,
71
+ )
72
+ ) {
73
+ assetNode.value.symbols?.delete('*');
74
+ }
75
+ }
76
+
65
77
  // exportSymbol -> identifier
66
78
  let assetSymbols: ?$ReadOnlyMap<
67
79
  Symbol,
@@ -30,7 +30,13 @@ 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';
39
+ import {fromEnvironmentId} from './EnvironmentManager';
34
40
 
35
41
  type UncommittedAssetOptions = {|
36
42
  value: Asset,
@@ -153,7 +159,9 @@ export default class UncommittedAsset {
153
159
  size = content.length;
154
160
  }
155
161
 
162
+ // Maybe we should just store this in a file instead of LMDB
156
163
  await this.options.cache.setBlob(contentKey, content);
164
+
157
165
  return {size, hash};
158
166
  }
159
167
 
@@ -214,6 +222,9 @@ export default class UncommittedAsset {
214
222
  this.clearAST();
215
223
  }
216
224
 
225
+ /**
226
+ * @deprecated This has been broken on any cache other than FSCache for a long time.
227
+ */
217
228
  setStream(stream: Readable) {
218
229
  this.content = stream;
219
230
  this.clearAST();
@@ -296,6 +307,11 @@ export default class UncommittedAsset {
296
307
  }
297
308
 
298
309
  getCacheKey(key: string): string {
310
+ if (getFeatureFlag('cachePerformanceImprovements')) {
311
+ const filePath = fromProjectPathRelative(this.value.filePath);
312
+ return `Asset/${ATLASPACK_VERSION}/${filePath}/${this.value.id}/${key}`;
313
+ }
314
+
299
315
  return hashString(ATLASPACK_VERSION + key + this.value.id);
300
316
  }
301
317
 
@@ -306,7 +322,11 @@ export default class UncommittedAsset {
306
322
  ...rest,
307
323
  // $FlowFixMe "convert" the $ReadOnlyMaps to the interal mutable one
308
324
  symbols,
309
- env: mergeEnvironments(this.options.projectRoot, this.value.env, env),
325
+ env: mergeEnvironments(
326
+ this.options.projectRoot,
327
+ fromEnvironmentId(this.value.env),
328
+ env,
329
+ ),
310
330
  sourceAssetId: this.value.id,
311
331
  sourcePath: fromProjectPath(
312
332
  this.options.projectRoot,
@@ -371,7 +391,7 @@ export default class UncommittedAsset {
371
391
  isSource: this.value.isSource,
372
392
  env: mergeEnvironments(
373
393
  this.options.projectRoot,
374
- this.value.env,
394
+ fromEnvironmentId(this.value.env),
375
395
  result.env,
376
396
  ),
377
397
  dependencies:
@@ -34,6 +34,7 @@ import {createDevDependency, runDevDepRequest} from './requests/DevDepRequest';
34
34
  import {toProjectPath, fromProjectPathRelative} from './projectPath';
35
35
  import {tracer, PluginTracer} from '@atlaspack/profiler';
36
36
  import {DefaultMap} from '@atlaspack/utils';
37
+ import {fromEnvironmentId} from './EnvironmentManager';
37
38
 
38
39
  type RuntimeConnection = {|
39
40
  bundle: InternalBundle,
@@ -159,7 +160,11 @@ export default async function applyRuntimes<TResult: RequestResult>({
159
160
  let assetGroup = {
160
161
  code,
161
162
  filePath: toProjectPath(options.projectRoot, sourceName),
162
- env: mergeEnvironments(options.projectRoot, bundle.env, env),
163
+ env: mergeEnvironments(
164
+ options.projectRoot,
165
+ fromEnvironmentId(bundle.env),
166
+ env,
167
+ ),
163
168
  // Runtime assets should be considered source, as they should be
164
169
  // e.g. compiled to run in the target environment
165
170
  isSource: true,
package/src/assetUtils.js CHANGED
@@ -16,7 +16,6 @@ import type {
16
16
  Asset,
17
17
  RequestInvalidation,
18
18
  Dependency,
19
- Environment,
20
19
  AtlaspackOptions,
21
20
  } from './types';
22
21
 
@@ -40,6 +39,8 @@ import {hashString, createAssetId as createAssetIdRust} from '@atlaspack/rust';
40
39
  import {BundleBehavior as BundleBehaviorMap} from './types';
41
40
  import {PluginTracer} from '@atlaspack/profiler';
42
41
  import {identifierRegistry} from './IdentifierRegistry';
42
+ import type {EnvironmentRef} from './EnvironmentManager';
43
+ import {toEnvironmentId} from './EnvironmentManager';
43
44
 
44
45
  export type AssetOptions = {|
45
46
  id?: string,
@@ -56,7 +57,7 @@ export type AssetOptions = {|
56
57
  bundleBehavior?: ?BundleBehavior,
57
58
  isBundleSplittable?: ?boolean,
58
59
  isSource: boolean,
59
- env: Environment,
60
+ env: EnvironmentRef,
60
61
  meta?: Meta,
61
62
  outputHash?: ?string,
62
63
  pipeline?: ?string,
@@ -71,7 +72,7 @@ export type AssetOptions = {|
71
72
 
72
73
  export function createAssetIdFromOptions(options: AssetOptions): string {
73
74
  const data = {
74
- environmentId: options.env.id,
75
+ environmentId: toEnvironmentId(options.env),
75
76
  filePath: options.filePath,
76
77
  code: options.code,
77
78
  pipeline: options.pipeline,
@@ -28,9 +28,17 @@ export type AtlaspackV3Options = {|
28
28
 
29
29
  export class AtlaspackV3 {
30
30
  _atlaspack_napi: AtlaspackNapi;
31
+ _napiWorkerPool: INapiWorkerPool;
32
+ _isDefaultNapiWorkerPool: boolean;
31
33
 
32
- constructor(atlaspack_napi: AtlaspackNapi) {
34
+ constructor(
35
+ atlaspack_napi: AtlaspackNapi,
36
+ napiWorkerPool: INapiWorkerPool,
37
+ isDefaultNapiWorkerPool: boolean,
38
+ ) {
33
39
  this._atlaspack_napi = atlaspack_napi;
40
+ this._napiWorkerPool = napiWorkerPool;
41
+ this._isDefaultNapiWorkerPool = isDefaultNapiWorkerPool;
34
42
  }
35
43
 
36
44
  static async create({
@@ -38,7 +46,7 @@ export class AtlaspackV3 {
38
46
  packageManager,
39
47
  threads,
40
48
  lmdb,
41
- napiWorkerPool = new NapiWorkerPool(),
49
+ napiWorkerPool,
42
50
  ...options
43
51
  }: AtlaspackV3Options): Promise<AtlaspackV3> {
44
52
  options.logLevel = options.logLevel || 'error';
@@ -47,6 +55,12 @@ export class AtlaspackV3 {
47
55
  options.defaultTargetOptions.engines =
48
56
  options.defaultTargetOptions.engines || {};
49
57
 
58
+ let isDefaultNapiWorkerPool = false;
59
+ if (!napiWorkerPool) {
60
+ napiWorkerPool = new NapiWorkerPool();
61
+ isDefaultNapiWorkerPool = true;
62
+ }
63
+
50
64
  const [internal, error] = await atlaspackNapiCreate(
51
65
  {
52
66
  fs,
@@ -64,7 +78,14 @@ export class AtlaspackV3 {
64
78
  });
65
79
  }
66
80
 
67
- return new AtlaspackV3(internal);
81
+ return new AtlaspackV3(internal, napiWorkerPool, isDefaultNapiWorkerPool);
82
+ }
83
+
84
+ end(): void {
85
+ // If the worker pool was provided to us, don't shut it down, it's up to the provider.
86
+ if (this._isDefaultNapiWorkerPool) {
87
+ this._napiWorkerPool.shutdown();
88
+ }
68
89
  }
69
90
 
70
91
  async buildAssetGraph(): Promise<any> {
@@ -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
+ readTracking?: boolean,
111
+ |},
108
112
  ): Promise<?ConfigResultWithFilePath<T>> {
109
113
  return this.#inner.getConfigFrom(searchPath, filePaths, options);
110
114
  }
@@ -1,5 +1,6 @@
1
1
  if (
2
- process.env.ATLASPACK_BUILD_ENV !== 'production' ||
2
+ process.env.ATLASPACK_SOURCES === 'true' ||
3
+ process.env.ATLASPACK_BUILD_ENV === 'test' ||
3
4
  process.env.ATLASPACK_SELF_BUILD
4
5
  ) {
5
6
  require('@atlaspack/babel-register');
@@ -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: {
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // @flow
2
+ import * as EnvironmentManager from './EnvironmentManager';
2
3
 
3
4
  export {
4
5
  default,
@@ -8,6 +9,9 @@ export {
8
9
  createWorkerFarm,
9
10
  INTERNAL_RESOLVE,
10
11
  INTERNAL_TRANSFORM,
12
+ WORKER_PATH,
11
13
  } from './Atlaspack';
12
-
14
+ export {ATLASPACK_VERSION} from './constants';
15
+ export {default as resolveOptions} from './resolveOptions';
13
16
  export * from './atlaspack-v3';
17
+ export {EnvironmentManager};
@@ -36,6 +36,7 @@ import {
36
36
  BundleBehaviorNames,
37
37
  } from '../types';
38
38
  import {toInternalSourceLocation} from '../utils';
39
+ import {fromEnvironmentId} from '../EnvironmentManager';
39
40
 
40
41
  const inspect = Symbol.for('nodejs.util.inspect.custom');
41
42
 
@@ -80,7 +81,7 @@ export function assetFromValue(
80
81
 
81
82
  class BaseAsset {
82
83
  #asset: CommittedAsset | UncommittedAsset;
83
- #query /*: ?URLSearchParams */;
84
+ #query: ?URLSearchParams;
84
85
 
85
86
  constructor(asset: CommittedAsset | UncommittedAsset) {
86
87
  this.#asset = asset;
@@ -101,7 +102,10 @@ class BaseAsset {
101
102
  }
102
103
 
103
104
  get env(): IEnvironment {
104
- return new Environment(this.#asset.value.env, this.#asset.options);
105
+ return new Environment(
106
+ fromEnvironmentId(this.#asset.value.env),
107
+ this.#asset.options,
108
+ );
105
109
  }
106
110
 
107
111
  get fs(): FileSystem {
@@ -191,8 +195,8 @@ class BaseAsset {
191
195
  }
192
196
 
193
197
  export class Asset extends BaseAsset implements IAsset {
194
- #asset /*: CommittedAsset | UncommittedAsset */;
195
- #env /*: ?Environment */;
198
+ #asset: CommittedAsset | UncommittedAsset;
199
+ #env: ?Environment;
196
200
 
197
201
  constructor(asset: CommittedAsset | UncommittedAsset): Asset {
198
202
  let assetValueToAsset = asset.value.committed
@@ -210,7 +214,10 @@ export class Asset extends BaseAsset implements IAsset {
210
214
  }
211
215
 
212
216
  get env(): IEnvironment {
213
- this.#env ??= new Environment(this.#asset.value.env, this.#asset.options);
217
+ this.#env ??= new Environment(
218
+ fromEnvironmentId(this.#asset.value.env),
219
+ this.#asset.options,
220
+ );
214
221
  return this.#env;
215
222
  }
216
223
 
@@ -220,7 +227,7 @@ export class Asset extends BaseAsset implements IAsset {
220
227
  }
221
228
 
222
229
  export class MutableAsset extends BaseAsset implements IMutableAsset {
223
- #asset /*: UncommittedAsset */;
230
+ #asset: UncommittedAsset;
224
231
 
225
232
  constructor(asset: UncommittedAsset): MutableAsset {
226
233
  let existing = assetValueToMutableAsset.get(asset.value);
@@ -34,6 +34,7 @@ import {
34
34
  import Target from './Target';
35
35
  import {BundleBehaviorNames} from '../types';
36
36
  import {fromProjectPath} from '../projectPath';
37
+ import {fromEnvironmentId} from '../EnvironmentManager';
37
38
 
38
39
  const inspect = Symbol.for('nodejs.util.inspect.custom');
39
40
 
@@ -67,9 +68,9 @@ export function bundleToInternalBundleGraph(bundle: IBundle): BundleGraph {
67
68
  let _private = {};
68
69
 
69
70
  export class Bundle implements IBundle {
70
- #bundle /*: InternalBundle */;
71
- #bundleGraph /*: BundleGraph */;
72
- #options /*: AtlaspackOptions */;
71
+ #bundle: InternalBundle;
72
+ #bundleGraph: BundleGraph;
73
+ #options: AtlaspackOptions;
73
74
 
74
75
  // $FlowFixMe
75
76
  [inspect]() {
@@ -123,7 +124,7 @@ export class Bundle implements IBundle {
123
124
  }
124
125
 
125
126
  get env(): IEnvironment {
126
- return new Environment(this.#bundle.env, this.#options);
127
+ return new Environment(fromEnvironmentId(this.#bundle.env), this.#options);
127
128
  }
128
129
 
129
130
  get needsStableName(): ?boolean {
@@ -213,9 +214,9 @@ export class Bundle implements IBundle {
213
214
  }
214
215
 
215
216
  export class NamedBundle extends Bundle implements INamedBundle {
216
- #bundle /*: InternalBundle */;
217
- #bundleGraph /*: BundleGraph */;
218
- #options /*: AtlaspackOptions */;
217
+ #bundle: InternalBundle;
218
+ #bundleGraph: BundleGraph;
219
+ #options: AtlaspackOptions;
219
220
 
220
221
  constructor(
221
222
  sentinel: mixed,
@@ -267,10 +268,10 @@ export class NamedBundle extends Bundle implements INamedBundle {
267
268
  }
268
269
 
269
270
  export class PackagedBundle extends NamedBundle implements IPackagedBundle {
270
- #bundle /*: InternalBundle */;
271
- #bundleGraph /*: BundleGraph */;
272
- #options /*: AtlaspackOptions */;
273
- #bundleInfo /*: ?PackagedBundleInfo */;
271
+ #bundle: InternalBundle;
272
+ #bundleGraph: BundleGraph;
273
+ #options: AtlaspackOptions;
274
+ #bundleInfo: ?PackagedBundleInfo;
274
275
 
275
276
  constructor(
276
277
  sentinel: mixed,
@@ -489,10 +489,18 @@ export default class BundleGraph<TBundle: IBundle>
489
489
  for (let bundle of bundles) {
490
490
  const conditions = bundleConditions.get(bundle.id) ?? new Map();
491
491
 
492
+ const currentCondition = conditions.get(cond.key);
493
+
492
494
  conditions.set(cond.key, {
493
495
  bundle,
494
- ifTrueBundles,
495
- ifFalseBundles,
496
+ ifTrueBundles: [
497
+ ...(currentCondition?.ifTrueBundles ?? []),
498
+ ...ifTrueBundles,
499
+ ],
500
+ ifFalseBundles: [
501
+ ...(currentCondition?.ifFalseBundles ?? []),
502
+ ...ifFalseBundles,
503
+ ],
496
504
  });
497
505
 
498
506
  bundleConditions.set(bundle.id, conditions);
@@ -28,8 +28,8 @@ export function bundleGroupToInternalBundleGroup(
28
28
  const inspect = Symbol.for('nodejs.util.inspect.custom');
29
29
 
30
30
  export default class BundleGroup implements IBundleGroup {
31
- #bundleGroup /*: InternalBundleGroup */;
32
- #options /*: AtlaspackOptions */;
31
+ #bundleGroup: InternalBundleGroup;
32
+ #options: AtlaspackOptions;
33
33
 
34
34
  constructor(
35
35
  bundleGroup: InternalBundleGroup,
@@ -20,17 +20,94 @@ import {
20
20
  } from '@atlaspack/utils';
21
21
  import Environment from './Environment';
22
22
  import {fromProjectPath, toProjectPath} from '../projectPath';
23
+ import {fromEnvironmentId} from '../EnvironmentManager';
23
24
 
24
25
  const internalConfigToConfig: DefaultWeakMap<
25
26
  AtlaspackOptions,
26
27
  WeakMap<Config, PublicConfig>,
27
28
  > = new DefaultWeakMap(() => new WeakMap());
28
29
 
30
+ /**
31
+ * Implements read tracking over an object.
32
+ *
33
+ * Calling this function with a non-trivial object like a class instance will fail to work.
34
+ *
35
+ * We track reads to fields that resolve to:
36
+ *
37
+ * - primitive values
38
+ * - arrays
39
+ *
40
+ * That is, reading a nested field `a.b.c` will make a single call to `onRead` with the path
41
+ * `['a', 'b', 'c']`.
42
+ *
43
+ * In case the value is null or an array, we will track the read as well.
44
+ *
45
+ * Iterating over `Object.keys(obj.field)` will register a read for the `['field']` path.
46
+ * Other reads work normally.
47
+ *
48
+ * @example
49
+ *
50
+ * const usedPaths = new Set();
51
+ * const onRead = (path) => {
52
+ * usedPaths.add(path);
53
+ * };
54
+ *
55
+ * const config = makeConfigProxy(onRead, {a: {b: {c: 'd'}}})
56
+ * console.log(config.a.b.c);
57
+ * console.log(Array.from(usedPaths));
58
+ * // We get a single read for the path
59
+ * // ['a', 'b', 'c']
60
+ *
61
+ */
62
+ export function makeConfigProxy<T>(
63
+ onRead: (path: string[]) => void,
64
+ config: T,
65
+ ): T {
66
+ const reportedPaths = new Set();
67
+ const reportPath = (path) => {
68
+ if (reportedPaths.has(path)) {
69
+ return;
70
+ }
71
+ reportedPaths.add(path);
72
+ onRead(path);
73
+ };
74
+
75
+ const makeProxy = (target, path) => {
76
+ return new Proxy(target, {
77
+ ownKeys(target) {
78
+ reportPath(path);
79
+
80
+ // $FlowFixMe
81
+ return Object.getOwnPropertyNames(target);
82
+ },
83
+ get(target, prop) {
84
+ // $FlowFixMe
85
+ const value = target[prop];
86
+
87
+ if (
88
+ typeof value === 'object' &&
89
+ value != null &&
90
+ !Array.isArray(value)
91
+ ) {
92
+ return makeProxy(value, [...path, prop]);
93
+ }
94
+
95
+ reportPath([...path, prop]);
96
+
97
+ return value;
98
+ },
99
+ });
100
+ };
101
+
102
+ // $FlowFixMe
103
+ return makeProxy(config, []);
104
+ }
105
+
29
106
  export default class PublicConfig implements IConfig {
30
- #config /*: Config */;
31
- #pkg /*: ?PackageJSON */;
32
- #pkgFilePath /*: ?FilePath */;
33
- #options /*: AtlaspackOptions */;
107
+ #config: Config;
108
+ #pkg: ?PackageJSON;
109
+ #pkgFilePath: ?FilePath;
110
+ #options: AtlaspackOptions;
34
111
 
35
112
  constructor(config: Config, options: AtlaspackOptions): PublicConfig {
36
113
  let existing = internalConfigToConfig.get(options).get(config);
@@ -45,7 +122,7 @@ export default class PublicConfig implements IConfig {
45
122
  }
46
123
 
47
124
  get env(): Environment {
48
- return new Environment(this.#config.env, this.#options);
125
+ return new Environment(fromEnvironmentId(this.#config.env), this.#options);
49
126
  }
50
127
 
51
128
  get searchPath(): FilePath {
@@ -75,7 +152,7 @@ export default class PublicConfig implements IConfig {
75
152
  );
76
153
  }
77
154
 
78
- invalidateOnConfigKeyChange(filePath: FilePath, configKey: string) {
155
+ invalidateOnConfigKeyChange(filePath: FilePath, configKey: string[]) {
79
156
  this.#config.invalidateOnConfigKeyChange.push({
80
157
  filePath: toProjectPath(this.#options.projectRoot, filePath),
81
158
  configKey,
@@ -132,11 +209,22 @@ export default class PublicConfig implements IConfig {
132
209
  async getConfigFrom<T>(
133
210
  searchPath: FilePath,
134
211
  fileNames: Array<string>,
135
- options: ?{|
136
- packageKey?: string,
137
- parse?: boolean,
138
- exclude?: boolean,
139
- |},
212
+ options:
213
+ | ?{|
214
+ /**
215
+ * @deprecated Use `configKey` instead.
216
+ */
217
+ packageKey?: string,
218
+ parse?: boolean,
219
+ exclude?: boolean,
220
+ |}
221
+ | ?{|
222
+ /**
223
+ * If specified, this function will return a proxy object that will track reads to
224
+ * config fields and only register invalidations for when those keys change.
225
+ */
226
+ readTracking?: boolean,
227
+ |},
140
228
  ): Promise<?ConfigResultWithFilePath<T>> {
141
229
  let packageKey = options?.packageKey;
142
230
  if (packageKey != null) {
@@ -146,7 +234,7 @@ export default class PublicConfig implements IConfig {
146
234
 
147
235
  if (pkg && pkg.contents[packageKey]) {
148
236
  // Invalidate only when the package key changes
149
- this.invalidateOnConfigKeyChange(pkg.filePath, packageKey);
237
+ this.invalidateOnConfigKeyChange(pkg.filePath, [packageKey]);
150
238
 
151
239
  return {
152
240
  contents: pkg.contents[packageKey],
@@ -155,6 +243,26 @@ export default class PublicConfig implements IConfig {
155
243
  }
156
244
  }
157
245
 
246
+ const readTracking = options?.readTracking;
247
+ if (readTracking === true) {
248
+ for (let fileName of fileNames) {
249
+ const config = await this.getConfigFrom(searchPath, [fileName], {
250
+ exclude: true,
251
+ });
252
+
253
+ if (config != null) {
254
+ return Promise.resolve({
255
+ contents: makeConfigProxy((keyPath) => {
256
+ this.invalidateOnConfigKeyChange(config.filePath, keyPath);
257
+ }, config.contents),
258
+ filePath: config.filePath,
259
+ });
260
+ }
261
+ }
262
+
263
+ // fall through so that file above invalidations are registered
264
+ }
265
+
158
266
  if (fileNames.length === 0) {
159
267
  return null;
160
268
  }
@@ -234,11 +342,15 @@ export default class PublicConfig implements IConfig {
234
342
 
235
343
  getConfig<T>(
236
344
  filePaths: Array<FilePath>,
237
- options: ?{|
238
- packageKey?: string,
239
- parse?: boolean,
240
- exclude?: boolean,
241
- |},
345
+ options:
346
+ | ?{|
347
+ packageKey?: string,
348
+ parse?: boolean,
349
+ exclude?: boolean,
350
+ |}
351
+ | {|
352
+ readTracking?: boolean,
353
+ |},
242
354
  ): Promise<?ConfigResultWithFilePath<T>> {
243
355
  return this.getConfigFrom(this.searchPath, filePaths, options);
244
356
  }
@@ -248,7 +360,9 @@ export default class PublicConfig implements IConfig {
248
360
  return this.#pkg;
249
361
  }
250
362
 
251
- let pkgConfig = await this.getConfig<PackageJSON>(['package.json']);
363
+ let pkgConfig = await this.getConfig<PackageJSON>(['package.json'], {
364
+ readTracking: true,
365
+ });
252
366
  if (!pkgConfig) {
253
367
  return null;
254
368
  }
@@ -27,6 +27,7 @@ import {
27
27
  } from '../types';
28
28
  import {fromProjectPath} from '../projectPath';
29
29
  import {fromInternalSourceLocation} from '../utils';
30
+ import {fromEnvironmentId} from '../EnvironmentManager';
30
31
 
31
32
  const SpecifierTypeNames = Object.keys(SpecifierTypeMap);
32
33
  const PriorityNames = Object.keys(Priority);
@@ -58,8 +59,8 @@ export function getPublicDependency(
58
59
  }
59
60
 
60
61
  export default class Dependency implements IDependency {
61
- #dep /*: InternalDependency */;
62
- #options /*: AtlaspackOptions */;
62
+ #dep: InternalDependency;
63
+ #options: AtlaspackOptions;
63
64
 
64
65
  constructor(dep: InternalDependency, options: AtlaspackOptions): Dependency {
65
66
  this.#dep = dep;
@@ -112,7 +113,7 @@ export default class Dependency implements IDependency {
112
113
  }
113
114
 
114
115
  get env(): IEnvironment {
115
- return new Environment(this.#dep.env, this.#options);
116
+ return new Environment(fromEnvironmentId(this.#dep.env), this.#options);
116
117
  }
117
118
 
118
119
  get packageConditions(): ?Array<string> {
@@ -156,8 +156,8 @@ export function environmentToInternalEnvironment(
156
156
  }
157
157
 
158
158
  export default class Environment implements IEnvironment {
159
- #environment /*: InternalEnvironment */;
160
- #options /*: AtlaspackOptions */;
159
+ #environment: InternalEnvironment;
160
+ #options: AtlaspackOptions;
161
161
 
162
162
  constructor(
163
163
  env: InternalEnvironment,