@atlaspack/core 2.16.2-canary.57 → 2.16.2-canary.59

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.
@@ -4,8 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.fromEnvironmentId = fromEnvironmentId;
7
+ exports.loadEnvironmentsFromCache = loadEnvironmentsFromCache;
7
8
  exports.toEnvironmentId = toEnvironmentId;
8
9
  exports.toEnvironmentRef = toEnvironmentRef;
10
+ exports.writeEnvironmentsToCache = writeEnvironmentsToCache;
9
11
  function _rust() {
10
12
  const data = require("@atlaspack/rust");
11
13
  _rust = function () {
@@ -20,6 +22,14 @@ function _featureFlags() {
20
22
  };
21
23
  return data;
22
24
  }
25
+ function _logger() {
26
+ const data = require("@atlaspack/logger");
27
+ _logger = function () {
28
+ return data;
29
+ };
30
+ return data;
31
+ }
32
+ var _constants = require("./constants");
23
33
  /*!
24
34
  * At the moment we're doing this change for `CoreEnvironment`,
25
35
  * but the same change must be made for `TypesEnvironment` in @atlaspack/types.
@@ -77,4 +87,51 @@ function fromEnvironmentId(id) {
77
87
  const env = Object.freeze((0, _rust().getEnvironment)(id));
78
88
  localEnvironmentCache.set(id, env);
79
89
  return env;
90
+ }
91
+
92
+ /**
93
+ * Writes all environments and their IDs to the cache
94
+ * @param {Cache} cache
95
+ * @returns {Promise<void>}
96
+ */
97
+ async function writeEnvironmentsToCache(cache) {
98
+ const environments = (0, _rust().getAllEnvironments)();
99
+ const environmentIds = new Set();
100
+
101
+ // Store each environment individually
102
+ for (const env of environments) {
103
+ environmentIds.add(env.id);
104
+ const envKey = `Environment/${_constants.ATLASPACK_VERSION}/${env.id}`;
105
+ await (0, _logger().instrument)(`RequestTracker::writeToCache::cache.put(${envKey})`, async () => {
106
+ await cache.set(envKey, env);
107
+ });
108
+ }
109
+
110
+ // Store the list of environment IDs
111
+ await (0, _logger().instrument)(`RequestTracker::writeToCache::cache.put(${`EnvironmentManager/${_constants.ATLASPACK_VERSION}`})`, async () => {
112
+ await cache.set(`EnvironmentManager/${_constants.ATLASPACK_VERSION}`, Array.from(environmentIds));
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Loads all environments and their IDs from the cache
118
+ * @param {Cache} cache
119
+ * @returns {Promise<void>}
120
+ */
121
+ async function loadEnvironmentsFromCache(cache) {
122
+ const cachedEnvIds = await cache.get(`EnvironmentManager/${_constants.ATLASPACK_VERSION}`);
123
+ if (cachedEnvIds == null) {
124
+ return;
125
+ }
126
+ const environments = [];
127
+ for (const envId of cachedEnvIds) {
128
+ const envKey = `Environment/${_constants.ATLASPACK_VERSION}/${envId}`;
129
+ const cachedEnv = await cache.get(envKey);
130
+ if (cachedEnv != null) {
131
+ environments.push(cachedEnv);
132
+ }
133
+ }
134
+ if (environments.length > 0) {
135
+ (0, _rust().setAllEnvironments)(environments);
136
+ }
80
137
  }
@@ -95,6 +95,7 @@ function _perf_hooks() {
95
95
  };
96
96
  return data;
97
97
  }
98
+ var _EnvironmentManager = require("./EnvironmentManager");
98
99
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
99
100
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
100
101
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -162,7 +163,7 @@ const nodeFromOption = (option, value) => ({
162
163
  hash: (0, _utils2.hashFromOption)(value)
163
164
  });
164
165
  const nodeFromConfigKey = (fileName, configKey, contentHash) => ({
165
- id: `config_key:${(0, _projectPath.fromProjectPathRelative)(fileName)}:${configKey}`,
166
+ id: `config_key:${(0, _projectPath.fromProjectPathRelative)(fileName)}:${JSON.stringify(configKey)}`,
166
167
  type: CONFIG_KEY,
167
168
  configKey,
168
169
  contentHash
@@ -695,10 +696,18 @@ class RequestGraph extends _graph().ContentGraph {
695
696
  this.removeNode(nodeId, removeOrphans);
696
697
  }
697
698
  let configKeyNodes = this.configKeyNodes.get(_filePath);
698
- if (configKeyNodes && (type === 'delete' || type === 'update')) {
699
+
700
+ // With granular invalidations we will always run this block,
701
+ // so even if we get a create event (for whatever reason), we will still
702
+ // try to limit invalidations from config key changes through hashing.
703
+ //
704
+ // Currently create events can invalidate a large number of nodes due to
705
+ // "create above" invalidations.
706
+ const isConfigKeyChange = (0, _featureFlags().getFeatureFlag)('granularTsConfigInvalidation') || type === 'delete' || type === 'update';
707
+ if (configKeyNodes && isConfigKeyChange) {
699
708
  for (let nodeId of configKeyNodes) {
700
709
  let isInvalid = type === 'delete';
701
- if (type === 'update') {
710
+ if (type !== 'delete') {
702
711
  let node = this.getNode(nodeId);
703
712
  (0, _assert().default)(node && node.type === CONFIG_KEY);
704
713
  let contentHash = await (0, _ConfigRequest.getConfigKeyContentHash)(_filePath, node.configKey, options);
@@ -1015,6 +1024,9 @@ class RequestTracker {
1015
1024
  total,
1016
1025
  size: this.graph.nodes.length
1017
1026
  });
1027
+ if ((0, _featureFlags().getFeatureFlag)('environmentDeduplication')) {
1028
+ await (0, _EnvironmentManager.writeEnvironmentsToCache)(options.cache);
1029
+ }
1018
1030
  let serialisedGraph = this.graph.serialize();
1019
1031
 
1020
1032
  // Delete an existing request graph cache, to prevent invalid states
@@ -1206,6 +1218,9 @@ async function loadRequestGraph(options) {
1206
1218
  ...commonMeta
1207
1219
  }
1208
1220
  });
1221
+ if ((0, _featureFlags().getFeatureFlag)('environmentDeduplication')) {
1222
+ await (0, _EnvironmentManager.loadEnvironmentsFromCache)(options.cache);
1223
+ }
1209
1224
  const hasRequestGraphInCache = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? await options.cache.has(requestGraphKey) : await options.cache.hasLargeBlob(requestGraphKey);
1210
1225
  if (hasRequestGraphInCache) {
1211
1226
  try {
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ exports.makeConfigProxy = makeConfigProxy;
7
8
  function _assert() {
8
9
  const data = _interopRequireDefault(require("assert"));
9
10
  _assert = function () {
@@ -37,6 +38,71 @@ function _featureFlags() {
37
38
  var _EnvironmentManager = require("../EnvironmentManager");
38
39
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39
40
  const internalConfigToConfig = new (_utils().DefaultWeakMap)(() => new WeakMap());
41
+
42
+ /**
43
+ * Implements read tracking over an object.
44
+ *
45
+ * Calling this function with a non-trivial object like a class instance will fail to work.
46
+ *
47
+ * We track reads to fields that resolve to:
48
+ *
49
+ * - primitive values
50
+ * - arrays
51
+ *
52
+ * That is, reading a nested field `a.b.c` will make a single call to `onRead` with the path
53
+ * `['a', 'b', 'c']`.
54
+ *
55
+ * In case the value is null or an array, we will track the read as well.
56
+ *
57
+ * Iterating over `Object.keys(obj.field)` will register a read for the `['field']` path.
58
+ * Other reads work normally.
59
+ *
60
+ * @example
61
+ *
62
+ * const usedPaths = new Set();
63
+ * const onRead = (path) => {
64
+ * usedPaths.add(path);
65
+ * };
66
+ *
67
+ * const config = makeConfigProxy(onRead, {a: {b: {c: 'd'}}})
68
+ * console.log(config.a.b.c);
69
+ * console.log(Array.from(usedPaths));
70
+ * // We get a single read for the path
71
+ * // ['a', 'b', 'c']
72
+ *
73
+ */
74
+ function makeConfigProxy(onRead, config) {
75
+ const reportedPaths = new Set();
76
+ const reportPath = path => {
77
+ if (reportedPaths.has(path)) {
78
+ return;
79
+ }
80
+ reportedPaths.add(path);
81
+ onRead(path);
82
+ };
83
+ const makeProxy = (target, path) => {
84
+ return new Proxy(target, {
85
+ ownKeys(target) {
86
+ reportPath(path);
87
+
88
+ // $FlowFixMe
89
+ return Object.getOwnPropertyNames(target);
90
+ },
91
+ get(target, prop) {
92
+ // $FlowFixMe
93
+ const value = target[prop];
94
+ if (typeof value === 'object' && value != null && !Array.isArray(value)) {
95
+ return makeProxy(value, [...path, prop]);
96
+ }
97
+ reportPath([...path, prop]);
98
+ return value;
99
+ }
100
+ });
101
+ };
102
+
103
+ // $FlowFixMe
104
+ return makeProxy(config, []);
105
+ }
40
106
  class PublicConfig {
41
107
  #config /*: Config */;
42
108
  #pkg /*: ?PackageJSON */;
@@ -127,32 +193,30 @@ class PublicConfig {
127
193
  });
128
194
  if (pkg && pkg.contents[packageKey]) {
129
195
  // Invalidate only when the package key changes
130
- this.invalidateOnConfigKeyChange(pkg.filePath, packageKey);
196
+ this.invalidateOnConfigKeyChange(pkg.filePath, [packageKey]);
131
197
  return {
132
198
  contents: pkg.contents[packageKey],
133
199
  filePath: pkg.filePath
134
200
  };
135
201
  }
136
202
  }
137
- if ((0, _featureFlags().getFeatureFlag)('granularTsConfigInvalidation')) {
138
- const configKey = options === null || options === void 0 ? void 0 : options.configKey;
139
- if (configKey != null) {
140
- for (let fileName of fileNames) {
141
- let config = await this.getConfigFrom(searchPath, [fileName], {
142
- exclude: true
203
+ const readTracking = options === null || options === void 0 ? void 0 : options.readTracking;
204
+ if (readTracking === true) {
205
+ for (let fileName of fileNames) {
206
+ const config = await this.getConfigFrom(searchPath, [fileName], {
207
+ exclude: true
208
+ });
209
+ if (config != null) {
210
+ return Promise.resolve({
211
+ contents: makeConfigProxy(keyPath => {
212
+ this.invalidateOnConfigKeyChange(config.filePath, keyPath);
213
+ }, config.contents),
214
+ filePath: config.filePath
143
215
  });
144
- if (config && config.contents[configKey]) {
145
- // Invalidate only when the package key changes
146
- this.invalidateOnConfigKeyChange(config.filePath, configKey);
147
- return {
148
- contents: config.contents[configKey],
149
- filePath: config.filePath
150
- };
151
- }
152
216
  }
153
-
154
- // fall through so that file above invalidations are registered
155
217
  }
218
+
219
+ // fall through so that file above invalidations are registered
156
220
  }
157
221
 
158
222
  if (fileNames.length === 0) {
@@ -219,7 +283,9 @@ class PublicConfig {
219
283
  if (this.#pkg) {
220
284
  return this.#pkg;
221
285
  }
222
- let pkgConfig = await this.getConfig(['package.json']);
286
+ let pkgConfig = await this.getConfig(['package.json'], {
287
+ readTracking: (0, _featureFlags().getFeatureFlag)('granularTsConfigInvalidation')
288
+ });
223
289
  if (!pkgConfig) {
224
290
  return null;
225
291
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getConfigHash = getConfigHash;
7
7
  exports.getConfigKeyContentHash = getConfigKeyContentHash;
8
8
  exports.getConfigRequests = getConfigRequests;
9
+ exports.getValueAtPath = getValueAtPath;
9
10
  exports.loadPluginConfig = loadPluginConfig;
10
11
  exports.runConfigRequest = runConfigRequest;
11
12
  function _utils() {
@@ -86,19 +87,41 @@ async function loadPluginConfig(loadedPlugin, config, options) {
86
87
  });
87
88
  }
88
89
  }
90
+
91
+ /**
92
+ * Return value at a given key path within an object.
93
+ *
94
+ * @example
95
+ * const obj = { a: { b: { c: 'd' } } };
96
+ * getValueAtPath(obj, ['a', 'b', 'c']); // 'd'
97
+ * getValueAtPath(obj, ['a', 'b', 'd']); // undefined
98
+ * getValueAtPath(obj, ['a', 'b']); // { c: 'd' }
99
+ * getValueAtPath(obj, ['a', 'b', 'c', 'd']); // undefined
100
+ */
101
+ function getValueAtPath(obj, key) {
102
+ let current = obj;
103
+ for (let part of key) {
104
+ if (current == null) {
105
+ return undefined;
106
+ }
107
+ current = current[part];
108
+ }
109
+ return current;
110
+ }
89
111
  const configKeyCache = (0, _buildCache().createBuildCache)();
90
112
  async function getConfigKeyContentHash(filePath, configKey, options) {
91
- let cacheKey = `${(0, _projectPath.fromProjectPathRelative)(filePath)}:${configKey}`;
113
+ let cacheKey = `${(0, _projectPath.fromProjectPathRelative)(filePath)}:${JSON.stringify(configKey)}`;
92
114
  let cachedValue = configKeyCache.get(cacheKey);
93
115
  if (cachedValue) {
94
116
  return cachedValue;
95
117
  }
96
- let conf = await (0, _utils().readConfig)(options.inputFS, (0, _projectPath.fromProjectPath)(options.projectRoot, filePath));
97
- if (conf == null || conf.config[configKey] == null) {
118
+ const conf = await (0, _utils().readConfig)(options.inputFS, (0, _projectPath.fromProjectPath)(options.projectRoot, filePath));
119
+ const value = getValueAtPath(conf === null || conf === void 0 ? void 0 : conf.config, configKey);
120
+ if (conf == null || value == null) {
98
121
  // This can occur when a config key has been removed entirely during `respondToFSEvents`
99
122
  return '';
100
123
  }
101
- let contentHash = typeof conf.config[configKey] === 'object' ? (0, _utils().hashObject)(conf.config[configKey]) : (0, _rust().hashString)(JSON.stringify(conf.config[configKey]));
124
+ const contentHash = typeof value === 'object' ? (0, _utils().hashObject)(value) : (0, _rust().hashString)(JSON.stringify(value));
102
125
  configKeyCache.set(cacheKey, contentHash);
103
126
  return contentHash;
104
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/core",
3
- "version": "2.16.2-canary.57+6dd4ccb75",
3
+ "version": "2.16.2-canary.59+0b2f6f557",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -21,21 +21,21 @@
21
21
  "check-ts": "tsc --noEmit index.d.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@atlaspack/build-cache": "2.13.3-canary.125+6dd4ccb75",
25
- "@atlaspack/cache": "3.1.1-canary.57+6dd4ccb75",
26
- "@atlaspack/diagnostic": "2.14.1-canary.125+6dd4ccb75",
27
- "@atlaspack/events": "2.14.1-canary.125+6dd4ccb75",
28
- "@atlaspack/feature-flags": "2.14.1-canary.125+6dd4ccb75",
29
- "@atlaspack/fs": "2.14.5-canary.57+6dd4ccb75",
30
- "@atlaspack/graph": "3.4.1-canary.125+6dd4ccb75",
31
- "@atlaspack/logger": "2.14.5-canary.57+6dd4ccb75",
32
- "@atlaspack/package-manager": "2.14.5-canary.57+6dd4ccb75",
33
- "@atlaspack/plugin": "2.14.5-canary.57+6dd4ccb75",
34
- "@atlaspack/profiler": "2.14.1-canary.125+6dd4ccb75",
35
- "@atlaspack/rust": "3.2.1-canary.57+6dd4ccb75",
36
- "@atlaspack/types": "2.14.5-canary.57+6dd4ccb75",
37
- "@atlaspack/utils": "2.14.5-canary.57+6dd4ccb75",
38
- "@atlaspack/workers": "2.14.5-canary.57+6dd4ccb75",
24
+ "@atlaspack/build-cache": "2.13.3-canary.127+0b2f6f557",
25
+ "@atlaspack/cache": "3.1.1-canary.59+0b2f6f557",
26
+ "@atlaspack/diagnostic": "2.14.1-canary.127+0b2f6f557",
27
+ "@atlaspack/events": "2.14.1-canary.127+0b2f6f557",
28
+ "@atlaspack/feature-flags": "2.14.1-canary.127+0b2f6f557",
29
+ "@atlaspack/fs": "2.14.5-canary.59+0b2f6f557",
30
+ "@atlaspack/graph": "3.4.1-canary.127+0b2f6f557",
31
+ "@atlaspack/logger": "2.14.5-canary.59+0b2f6f557",
32
+ "@atlaspack/package-manager": "2.14.5-canary.59+0b2f6f557",
33
+ "@atlaspack/plugin": "2.14.5-canary.59+0b2f6f557",
34
+ "@atlaspack/profiler": "2.14.1-canary.127+0b2f6f557",
35
+ "@atlaspack/rust": "3.2.1-canary.59+0b2f6f557",
36
+ "@atlaspack/types": "2.14.5-canary.59+0b2f6f557",
37
+ "@atlaspack/utils": "2.14.5-canary.59+0b2f6f557",
38
+ "@atlaspack/workers": "2.14.5-canary.59+0b2f6f557",
39
39
  "@mischnic/json-sourcemap": "^0.1.0",
40
40
  "@parcel/source-map": "^2.1.1",
41
41
  "base-x": "^3.0.8",
@@ -71,5 +71,5 @@
71
71
  "./src/serializerCore.js": "./src/serializerCore.browser.js"
72
72
  },
73
73
  "type": "commonjs",
74
- "gitHead": "6dd4ccb753541de32322d881f973d571dd57e4ca"
74
+ "gitHead": "0b2f6f55794d3ff6e2f5a41f963e7e5dd8ad9f8d"
75
75
  }
@@ -4,8 +4,16 @@
4
4
  * but the same change must be made for `TypesEnvironment` in @atlaspack/types.
5
5
  */
6
6
  import type {Environment as CoreEnvironment} from './types';
7
- import {addEnvironment, getEnvironment} from '@atlaspack/rust';
7
+ import {type Cache} from '@atlaspack/cache';
8
+ import {
9
+ addEnvironment,
10
+ getEnvironment,
11
+ getAllEnvironments,
12
+ setAllEnvironments,
13
+ } from '@atlaspack/rust';
8
14
  import {getFeatureFlag} from '@atlaspack/feature-flags';
15
+ import {instrument} from '@atlaspack/logger';
16
+ import {ATLASPACK_VERSION} from './constants';
9
17
 
10
18
  const localEnvironmentCache = new Map<string, CoreEnvironment>();
11
19
 
@@ -74,3 +82,64 @@ export function fromEnvironmentId(id: EnvironmentRef): CoreEnvironment {
74
82
  localEnvironmentCache.set(id, env);
75
83
  return env;
76
84
  }
85
+
86
+ /**
87
+ * Writes all environments and their IDs to the cache
88
+ * @param {Cache} cache
89
+ * @returns {Promise<void>}
90
+ */
91
+ export async function writeEnvironmentsToCache(cache: Cache): Promise<void> {
92
+ const environments = getAllEnvironments();
93
+ const environmentIds = new Set<string>();
94
+
95
+ // Store each environment individually
96
+ for (const env of environments) {
97
+ environmentIds.add(env.id);
98
+ const envKey = `Environment/${ATLASPACK_VERSION}/${env.id}`;
99
+
100
+ await instrument(
101
+ `RequestTracker::writeToCache::cache.put(${envKey})`,
102
+ async () => {
103
+ await cache.set(envKey, env);
104
+ },
105
+ );
106
+ }
107
+
108
+ // Store the list of environment IDs
109
+ await instrument(
110
+ `RequestTracker::writeToCache::cache.put(${`EnvironmentManager/${ATLASPACK_VERSION}`})`,
111
+ async () => {
112
+ await cache.set(
113
+ `EnvironmentManager/${ATLASPACK_VERSION}`,
114
+ Array.from(environmentIds),
115
+ );
116
+ },
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Loads all environments and their IDs from the cache
122
+ * @param {Cache} cache
123
+ * @returns {Promise<void>}
124
+ */
125
+ export async function loadEnvironmentsFromCache(cache: Cache): Promise<void> {
126
+ const cachedEnvIds = await cache.get(
127
+ `EnvironmentManager/${ATLASPACK_VERSION}`,
128
+ );
129
+
130
+ if (cachedEnvIds == null) {
131
+ return;
132
+ }
133
+
134
+ const environments = [];
135
+ for (const envId of cachedEnvIds) {
136
+ const envKey = `Environment/${ATLASPACK_VERSION}/${envId}`;
137
+ const cachedEnv = await cache.get(envKey);
138
+ if (cachedEnv != null) {
139
+ environments.push(cachedEnv);
140
+ }
141
+ }
142
+ if (environments.length > 0) {
143
+ setAllEnvironments(environments);
144
+ }
145
+ }
@@ -24,7 +24,7 @@ type ConfigOpts = {|
24
24
  invalidateOnFileChange?: Set<ProjectPath>,
25
25
  invalidateOnConfigKeyChange?: Array<{|
26
26
  filePath: ProjectPath,
27
- configKey: string,
27
+ configKey: string[],
28
28
  |}>,
29
29
  invalidateOnFileCreate?: Array<InternalFileCreateInvalidation>,
30
30
  invalidateOnEnvChange?: Set<string>,
@@ -30,15 +30,15 @@ import nullthrows from 'nullthrows';
30
30
 
31
31
  import {
32
32
  ATLASPACK_VERSION,
33
- VALID,
34
- INITIAL_BUILD,
35
33
  FILE_CREATE,
36
- FILE_UPDATE,
37
34
  FILE_DELETE,
35
+ FILE_UPDATE,
38
36
  ENV_CHANGE,
37
+ ERROR,
38
+ INITIAL_BUILD,
39
39
  OPTION_CHANGE,
40
40
  STARTUP,
41
- ERROR,
41
+ VALID,
42
42
  } from './constants';
43
43
  import type {AtlaspackV3} from './atlaspack-v3/AtlaspackV3';
44
44
  import {
@@ -71,6 +71,11 @@ import type {
71
71
  import {BuildAbortError, assertSignalNotAborted, hashFromOption} from './utils';
72
72
  import {performance} from 'perf_hooks';
73
73
 
74
+ import {
75
+ loadEnvironmentsFromCache,
76
+ writeEnvironmentsToCache,
77
+ } from './EnvironmentManager';
78
+
74
79
  export const requestGraphEdgeTypes = {
75
80
  subrequest: 2,
76
81
  invalidated_by_update: 3,
@@ -144,7 +149,7 @@ type OptionNode = {|
144
149
  type ConfigKeyNode = {|
145
150
  id: ContentKey,
146
151
  +type: typeof CONFIG_KEY,
147
- configKey: string,
152
+ configKey: string[],
148
153
  contentHash: string,
149
154
  |};
150
155
 
@@ -216,7 +221,7 @@ export type RunAPI<TResult: RequestResult> = {|
216
221
  invalidateOnFileUpdate: (ProjectPath) => void,
217
222
  invalidateOnConfigKeyChange: (
218
223
  filePath: ProjectPath,
219
- configKey: string,
224
+ configKey: string[],
220
225
  contentHash: string,
221
226
  ) => void,
222
227
  invalidateOnStartup: () => void,
@@ -283,10 +288,12 @@ const nodeFromOption = (option: string, value: mixed): RequestGraphNode => ({
283
288
 
284
289
  const nodeFromConfigKey = (
285
290
  fileName: ProjectPath,
286
- configKey: string,
291
+ configKey: string[],
287
292
  contentHash: string,
288
293
  ): RequestGraphNode => ({
289
- id: `config_key:${fromProjectPathRelative(fileName)}:${configKey}`,
294
+ id: `config_key:${fromProjectPathRelative(fileName)}:${JSON.stringify(
295
+ configKey,
296
+ )}`,
290
297
  type: CONFIG_KEY,
291
298
  configKey,
292
299
  contentHash,
@@ -527,7 +534,7 @@ export class RequestGraph extends ContentGraph<
527
534
  invalidateOnConfigKeyChange(
528
535
  requestNodeId: NodeId,
529
536
  filePath: ProjectPath,
530
- configKey: string,
537
+ configKey: string[],
531
538
  contentHash: string,
532
539
  ) {
533
540
  let configKeyNodeId = this.addNode(
@@ -1109,11 +1116,22 @@ export class RequestGraph extends ContentGraph<
1109
1116
  }
1110
1117
 
1111
1118
  let configKeyNodes = this.configKeyNodes.get(_filePath);
1112
- if (configKeyNodes && (type === 'delete' || type === 'update')) {
1119
+
1120
+ // With granular invalidations we will always run this block,
1121
+ // so even if we get a create event (for whatever reason), we will still
1122
+ // try to limit invalidations from config key changes through hashing.
1123
+ //
1124
+ // Currently create events can invalidate a large number of nodes due to
1125
+ // "create above" invalidations.
1126
+ const isConfigKeyChange =
1127
+ getFeatureFlag('granularTsConfigInvalidation') ||
1128
+ type === 'delete' ||
1129
+ type === 'update';
1130
+ if (configKeyNodes && isConfigKeyChange) {
1113
1131
  for (let nodeId of configKeyNodes) {
1114
1132
  let isInvalid = type === 'delete';
1115
1133
 
1116
- if (type === 'update') {
1134
+ if (type !== 'delete') {
1117
1135
  let node = this.getNode(nodeId);
1118
1136
  invariant(node && node.type === CONFIG_KEY);
1119
1137
 
@@ -1559,6 +1577,10 @@ export default class RequestTracker {
1559
1577
  size: this.graph.nodes.length,
1560
1578
  });
1561
1579
 
1580
+ if (getFeatureFlag('environmentDeduplication')) {
1581
+ await writeEnvironmentsToCache(options.cache);
1582
+ }
1583
+
1562
1584
  let serialisedGraph = this.graph.serialize();
1563
1585
 
1564
1586
  // Delete an existing request graph cache, to prevent invalid states
@@ -1843,6 +1865,10 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1843
1865
  },
1844
1866
  });
1845
1867
 
1868
+ if (getFeatureFlag('environmentDeduplication')) {
1869
+ await loadEnvironmentsFromCache(options.cache);
1870
+ }
1871
+
1846
1872
  const hasRequestGraphInCache = getFeatureFlag('cachePerformanceImprovements')
1847
1873
  ? await options.cache.has(requestGraphKey)
1848
1874
  : await options.cache.hasLargeBlob(requestGraphKey);
@@ -107,7 +107,7 @@ export class PluginConfig implements IPluginConfig {
107
107
  exclude?: boolean,
108
108
  |}
109
109
  | {|
110
- configKey?: string,
110
+ readTracking?: boolean,
111
111
  |},
112
112
  ): Promise<?ConfigResultWithFilePath<T>> {
113
113
  return this.#inner.getConfigFrom(searchPath, filePaths, options);