@atlaspack/core 2.30.2 → 2.31.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 (41) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/Atlaspack.js +2 -0
  3. package/dist/Transformation.js +24 -1
  4. package/dist/atlaspack-v3/AtlaspackV3.js +3 -0
  5. package/dist/atlaspack-v3/NapiWorkerPool.js +10 -0
  6. package/dist/atlaspack-v3/worker/compat/plugin-config.js +10 -22
  7. package/dist/atlaspack-v3/worker/compat/plugin-options.js +15 -0
  8. package/dist/atlaspack-v3/worker/side-effect-detector.js +243 -0
  9. package/dist/atlaspack-v3/worker/worker.js +140 -66
  10. package/dist/requests/ConfigRequest.js +24 -0
  11. package/lib/Atlaspack.js +5 -1
  12. package/lib/Transformation.js +31 -1
  13. package/lib/atlaspack-v3/AtlaspackV3.js +3 -0
  14. package/lib/atlaspack-v3/NapiWorkerPool.js +10 -0
  15. package/lib/atlaspack-v3/worker/compat/plugin-config.js +8 -27
  16. package/lib/atlaspack-v3/worker/compat/plugin-options.js +15 -0
  17. package/lib/atlaspack-v3/worker/side-effect-detector.js +215 -0
  18. package/lib/atlaspack-v3/worker/worker.js +152 -72
  19. package/lib/requests/ConfigRequest.js +25 -0
  20. package/lib/types/InternalConfig.d.ts +1 -2
  21. package/lib/types/atlaspack-v3/AtlaspackV3.d.ts +2 -1
  22. package/lib/types/atlaspack-v3/NapiWorkerPool.d.ts +1 -0
  23. package/lib/types/atlaspack-v3/index.d.ts +1 -0
  24. package/lib/types/atlaspack-v3/worker/compat/plugin-config.d.ts +3 -11
  25. package/lib/types/atlaspack-v3/worker/compat/plugin-options.d.ts +1 -0
  26. package/lib/types/atlaspack-v3/worker/side-effect-detector.d.ts +76 -0
  27. package/lib/types/atlaspack-v3/worker/worker.d.ts +26 -6
  28. package/lib/types/requests/ConfigRequest.d.ts +9 -1
  29. package/package.json +14 -14
  30. package/src/Atlaspack.ts +2 -0
  31. package/src/InternalConfig.ts +1 -1
  32. package/src/Transformation.ts +37 -2
  33. package/src/atlaspack-v3/AtlaspackV3.ts +8 -0
  34. package/src/atlaspack-v3/NapiWorkerPool.ts +17 -0
  35. package/src/atlaspack-v3/index.ts +1 -0
  36. package/src/atlaspack-v3/worker/compat/plugin-config.ts +8 -40
  37. package/src/atlaspack-v3/worker/compat/plugin-options.ts +15 -0
  38. package/src/atlaspack-v3/worker/side-effect-detector.ts +298 -0
  39. package/src/atlaspack-v3/worker/worker.ts +288 -172
  40. package/src/requests/ConfigRequest.ts +39 -0
  41. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.defaultDetector = exports.SideEffectDetector = void 0;
7
+ function _async_hooks() {
8
+ const data = require("async_hooks");
9
+ _async_hooks = function () {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
14
+ /**
15
+ * Side effect detector using AsyncLocalStorage to track filesystem and environment variable
16
+ * access across concurrent async operations in a Node.js worker thread.
17
+ *
18
+ * Usage:
19
+ * const detector = new SideEffectDetector();
20
+ * detector.install();
21
+ *
22
+ * const [result, sideEffects] = await detector.monitorSideEffects(async () => {
23
+ * return await someOperation();
24
+ * });
25
+ *
26
+ * console.log(sideEffects.fsUsage); // Array of filesystem accesses
27
+ * console.log(sideEffects.envUsage); // Array of environment variable accesses
28
+ */
29
+ class SideEffectDetector {
30
+ constructor() {
31
+ this.asyncStorage = new (_async_hooks().AsyncLocalStorage)();
32
+ this.patchesInstalled = false;
33
+ this.originalMethods = {};
34
+ }
35
+
36
+ /**
37
+ * Install global patches for filesystem and environment variable monitoring.
38
+ * This should be called once when the worker starts up.
39
+ */
40
+ install() {
41
+ if (this.patchesInstalled) {
42
+ return;
43
+ }
44
+ this._patchFilesystem();
45
+ this._patchProcessEnv();
46
+ this.patchesInstalled = true;
47
+ }
48
+
49
+ /**
50
+ * Monitor side effects for an async operation.
51
+ *
52
+ * @param {Function} fn - Async function to monitor
53
+ * @param {Object} options - Optional configuration
54
+ * @param {string} options.label - Optional label for debugging
55
+ * @returns {Promise<[any, SideEffects]>} Tuple of [result, sideEffects]
56
+ */
57
+ monitorSideEffects(packageName, fn) {
58
+ if (!this.patchesInstalled) {
59
+ throw new Error('SideEffectDetector: install() must be called before monitorSideEffects()');
60
+ }
61
+ const context = {
62
+ fsUsage: [],
63
+ envUsage: {
64
+ vars: new Set(),
65
+ didEnumerate: false
66
+ },
67
+ packageName: packageName
68
+ };
69
+ return this.asyncStorage.run(context, async () => {
70
+ const result = await fn();
71
+ return [result, context];
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Get the current monitoring context, if any.
77
+ * Useful for debugging or custom instrumentation.
78
+ *
79
+ * @returns {Object|null} Current context or null if not monitoring
80
+ */
81
+ getCurrentContext() {
82
+ return this.asyncStorage.getStore() || null;
83
+ }
84
+
85
+ /**
86
+ * Check if currently monitoring side effects.
87
+ *
88
+ * @returns {boolean}
89
+ */
90
+ isMonitoring() {
91
+ return this.asyncStorage.getStore() !== undefined;
92
+ }
93
+
94
+ /**
95
+ * Patch filesystem methods to record access.
96
+ * @private
97
+ */
98
+ _patchFilesystem() {
99
+ // Inline require this to avoid babel transformer issue
100
+ const fs = require('fs');
101
+ const methodsToPatch = [
102
+ // Sync methods
103
+ 'readFileSync', 'writeFileSync', 'appendFileSync', 'existsSync', 'statSync', 'lstatSync', 'readdirSync', 'mkdirSync', 'rmdirSync', 'unlinkSync', 'copyFileSync', 'renameSync', 'chmodSync', 'chownSync',
104
+ // Async methods
105
+ 'readFile', 'writeFile', 'appendFile', 'stat', 'lstat', 'readdir', 'mkdir', 'rmdir', 'unlink', 'copyFile', 'rename', 'chmod', 'chown'];
106
+ methodsToPatch.forEach(method => {
107
+ if (typeof fs[method] === 'function') {
108
+ this.originalMethods[method] = fs[method];
109
+ const self = this;
110
+
111
+ // @ts-expect-error Dynamic method patching
112
+ fs[method] = function (path, ...args) {
113
+ // Record filesystem access in current context
114
+ const context = self.asyncStorage.getStore();
115
+ if (context) {
116
+ context.fsUsage.push({
117
+ method,
118
+ path: typeof path === 'string' ? path : path === null || path === void 0 ? void 0 : path.toString()
119
+ });
120
+ }
121
+ return self.originalMethods[method].call(this, path, ...args);
122
+ };
123
+ }
124
+ });
125
+
126
+ // Handle fs.promises methods
127
+ if (fs.promises) {
128
+ const promiseMethodsToPatch = ['readFile', 'writeFile', 'appendFile', 'stat', 'lstat', 'readdir', 'mkdir', 'rmdir', 'unlink', 'copyFile', 'rename', 'chmod', 'chown'];
129
+ const promises = fs.promises;
130
+ promiseMethodsToPatch.forEach(method => {
131
+ if (typeof promises[method] === 'function') {
132
+ const originalKey = `promises_${method}`;
133
+ this.originalMethods[originalKey] = promises[method];
134
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
135
+ const self = this;
136
+ promises[method] = function (path, ...args) {
137
+ const context = self.asyncStorage.getStore();
138
+ if (context) {
139
+ context.fsUsage.push({
140
+ method: `promises.${method}`,
141
+ path: typeof path === 'string' ? path : String(path)
142
+ });
143
+ }
144
+ return self.originalMethods[originalKey].call(this, path, ...args);
145
+ };
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Patch process.env to record environment variable access.
153
+ * @private
154
+ */
155
+ _patchProcessEnv() {
156
+ if (this.originalMethods.processEnv) {
157
+ return; // Already patched
158
+ }
159
+
160
+ this.originalMethods.processEnv = process.env;
161
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
162
+ const self = this;
163
+ // The following environment variables are allowed to be accessed by transformers
164
+ const allowedVars = new Set(['ATLASPACK_ENABLE_SENTRY',
165
+ // TODO we should also add the other atlaspack env vars here
166
+ 'NODE_V8_COVERAGE', 'VSCODE_INSPECTOR_OPTIONS', 'NODE_INSPECTOR_IPC', 'FORCE_COLOR', 'NO_COLOR', 'TTY']);
167
+
168
+ // Create a proxy that intercepts property access
169
+ process.env = new Proxy(this.originalMethods.processEnv, {
170
+ get(target, property) {
171
+ const context = self.asyncStorage.getStore();
172
+ if (context && typeof property === 'string') {
173
+ // Only record if this is a real environment variable access
174
+ // (not internal properties like 'constructor', 'valueOf', etc.)
175
+ if (!allowedVars.has(property) && (property in target || !property.startsWith('_'))) {
176
+ context.envUsage.vars.add(property);
177
+ }
178
+ }
179
+ return target[property];
180
+ },
181
+ set(target, property, value) {
182
+ const context = self.asyncStorage.getStore();
183
+ if (context && typeof property === 'string') {
184
+ if (!allowedVars.has(property) && property in target) {
185
+ context.envUsage.vars.add(property);
186
+ }
187
+ }
188
+ target[property] = value;
189
+ return true;
190
+ },
191
+ has(target, property) {
192
+ const context = self.asyncStorage.getStore();
193
+ if (context && typeof property === 'string') {
194
+ if (!allowedVars.has(property) && property in target) {
195
+ context.envUsage.vars.add(property);
196
+ }
197
+ }
198
+ return property in target;
199
+ },
200
+ ownKeys(target) {
201
+ const context = self.asyncStorage.getStore();
202
+ if (context) {
203
+ context.envUsage.didEnumerate = true;
204
+ }
205
+ return Object.keys(target);
206
+ }
207
+ });
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Default instance for convenience. Most workers will only need one detector.
213
+ */
214
+ exports.SideEffectDetector = SideEffectDetector;
215
+ const defaultDetector = exports.defaultDetector = new SideEffectDetector();
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.AtlaspackWorker = void 0;
7
+ var _sideEffectDetector = require("./side-effect-detector");
7
8
  function _assert() {
8
9
  const data = _interopRequireDefault(require("assert"));
9
10
  _assert = function () {
@@ -51,6 +52,12 @@ var _compat = require("./compat");
51
52
  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); }
52
53
  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; }
53
54
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
55
+ /* eslint-disable import/first */
56
+
57
+ // Install side effect detection patches BEFORE importing any modules that use fs
58
+ const sideEffectDetector = new _sideEffectDetector.SideEffectDetector();
59
+ sideEffectDetector.install();
60
+
54
61
  // @ts-expect-error TS2305
55
62
 
56
63
  const CONFIG = Symbol.for('parcel-plugin-config');
@@ -59,17 +66,26 @@ class AtlaspackWorker {
59
66
  #transformers;
60
67
  #fs;
61
68
  #packageManager;
69
+ #options;
70
+ #sideEffectDetector;
62
71
  constructor() {
63
72
  this.#resolvers = new Map();
64
73
  this.#transformers = new Map();
65
74
  this.#fs = new (_fs().NodeFS)();
66
75
  this.#packageManager = new (_packageManager().NodePackageManager)(this.#fs, '/');
76
+ this.#sideEffectDetector = sideEffectDetector; // Use the global detector that was installed before imports
77
+ }
78
+
79
+ clearState() {
80
+ this.#resolvers.clear();
81
+ this.#transformers.clear();
82
+ this.#options = undefined;
67
83
  }
68
84
  loadPlugin = (0, _jsCallable.jsCallable)(async ({
69
85
  kind,
70
86
  specifier,
71
87
  resolveFrom,
72
- featureFlags
88
+ options
73
89
  }) => {
74
90
  // Use packageManager.require() instead of dynamic import() to support TypeScript plugins
75
91
  let resolvedModule = await this.#packageManager.require(specifier, resolveFrom, {
@@ -88,17 +104,21 @@ class AtlaspackWorker {
88
104
  } else {
89
105
  throw new Error(`Plugin could not be resolved\n\t${kind}\n\t${resolveFrom}\n\t${specifier}`);
90
106
  }
91
- // Set feature flags in the worker process
92
- // if (featureFlags) {
107
+ if (this.#options == null) {
108
+ this.#options = {
109
+ ...options,
110
+ inputFS: this.#fs,
111
+ outputFS: this.#fs,
112
+ packageManager: this.#packageManager,
113
+ shouldAutoInstall: false
114
+ };
115
+ }
116
+
93
117
  // Set feature flags in the worker process
94
118
  let featureFlagsModule = await this.#packageManager.require('@atlaspack/feature-flags', __filename, {
95
119
  shouldAutoInstall: false
96
120
  });
97
- featureFlagsModule.setFeatureFlags(featureFlags);
98
- // const {setFeatureFlags} = await import('@atlaspack/feature-flags');
99
- // setFeatureFlags(featureFlags);
100
- // }
101
-
121
+ featureFlagsModule.setFeatureFlags(options.featureFlags);
102
122
  switch (kind) {
103
123
  case 'resolver':
104
124
  this.#resolvers.set(specifier, {
@@ -106,53 +126,38 @@ class AtlaspackWorker {
106
126
  });
107
127
  break;
108
128
  case 'transformer':
109
- this.#transformers.set(specifier, {
110
- transformer: instance
111
- });
112
- break;
129
+ {
130
+ return this.initializeTransformer(instance, specifier);
131
+ }
113
132
  }
114
133
  });
115
134
  runResolverResolve = (0, _jsCallable.jsCallable)(async ({
116
135
  key,
117
136
  dependency: napiDependency,
118
137
  specifier,
119
- pipeline,
120
- pluginOptions
138
+ pipeline
121
139
  }) => {
122
140
  const state = this.#resolvers.get(key);
123
141
  if (!state) {
124
142
  throw new Error(`Resolver not found: ${key}`);
125
143
  }
126
- let packageManager = state.packageManager;
127
- if (!packageManager) {
128
- packageManager = new (_packageManager().NodePackageManager)(this.#fs, pluginOptions.projectRoot);
129
- state.packageManager = packageManager;
130
- }
131
144
  const env = new _compat.Environment(napiDependency.env);
132
145
  const dependency = new _compat.Dependency(napiDependency, env);
133
146
  const defaultOptions = {
134
147
  logger: new _compat.PluginLogger(),
135
148
  tracer: new _compat.PluginTracer(),
136
- options: new _compat.PluginOptions({
137
- ...pluginOptions,
138
- packageManager,
139
- shouldAutoInstall: false,
140
- inputFS: this.#fs,
141
- outputFS: this.#fs
142
- })
149
+ options: new _compat.PluginOptions(this.options)
143
150
  };
144
151
  if (!('config' in state)) {
145
152
  var _state$resolver$loadC, _state$resolver;
146
153
  // @ts-expect-error TS2345
147
154
  state.config = await ((_state$resolver$loadC = (_state$resolver = state.resolver).loadConfig) === null || _state$resolver$loadC === void 0 ? void 0 : _state$resolver$loadC.call(_state$resolver, {
148
155
  config: new _compat.PluginConfig({
149
- env,
156
+ env: napiDependency.env,
157
+ plugin: key,
150
158
  isSource: true,
151
- searchPath: specifier,
152
- projectRoot: pluginOptions.projectRoot,
153
- fs: this.#fs,
154
- packageManager
155
- }),
159
+ searchPath: 'index'
160
+ }, this.options),
156
161
  ...defaultOptions
157
162
  }));
158
163
  }
@@ -198,54 +203,55 @@ class AtlaspackWorker {
198
203
  });
199
204
  runTransformerTransform = (0, _jsCallable.jsCallable)(async ({
200
205
  key,
201
- env: napiEnv,
202
- options,
203
206
  asset: innerAsset
204
207
  }, contents, map) => {
205
- var _transformer$loadConf;
206
- const state = this.#transformers.get(key);
207
- if (!state) {
208
+ const instance = this.#transformers.get(key);
209
+ if (!instance) {
208
210
  throw new Error(`Transformer not found: ${key}`);
209
211
  }
210
- let packageManager = state.packageManager;
211
- if (!packageManager) {
212
- packageManager = new (_packageManager().NodePackageManager)(this.#fs, options.projectRoot);
213
- state.packageManager = packageManager;
214
- }
215
- const transformer = state.transformer;
212
+ let {
213
+ transformer,
214
+ config,
215
+ allowedEnv = new Set()
216
+ } = instance;
217
+ let cacheBailouts = [];
216
218
  const resolveFunc = (from, to) => {
217
219
  let customRequire = _module().createRequire(from);
218
220
  let resolvedPath = customRequire.resolve(to);
221
+ // Tranformer not cacheable due to use of the resolve function
222
+
223
+ cacheBailouts.push(`resolve(${from}, ${to})`);
219
224
  return Promise.resolve(resolvedPath);
220
225
  };
221
- const env = new _compat.Environment(napiEnv);
222
- const mutableAsset = new _compat.MutableAsset(innerAsset,
226
+ const env = new _compat.Environment(innerAsset.env);
227
+ let mutableAsset = new _compat.MutableAsset(innerAsset,
223
228
  // @ts-expect-error TS2345
224
- contents, env, this.#fs, map, options.projectRoot);
229
+ contents, env, this.#fs, map, this.options.projectRoot);
230
+ const pluginOptions = new _compat.PluginOptions(this.options);
225
231
  const defaultOptions = {
226
232
  logger: new _compat.PluginLogger(),
227
233
  tracer: new _compat.PluginTracer(),
228
- options: new _compat.PluginOptions({
229
- ...options,
230
- packageManager,
231
- shouldAutoInstall: false,
232
- inputFS: this.#fs,
233
- outputFS: this.#fs
234
- })
234
+ options: pluginOptions
235
235
  };
236
+ if (transformer.loadConfig) {
237
+ if (config != null) {
238
+ throw new Error(`Transformer (${key}) should not implement 'setup' and 'loadConfig'`);
239
+ }
240
+ // @ts-expect-error TS2345
241
+ config = await transformer.loadConfig({
242
+ config: new _compat.PluginConfig({
243
+ plugin: key,
244
+ isSource: innerAsset.isSource,
245
+ searchPath: innerAsset.filePath,
246
+ env
247
+ }, this.options),
248
+ ...defaultOptions
249
+ });
236
250
 
237
- // @ts-expect-error TS2345
238
- const config = await ((_transformer$loadConf = transformer.loadConfig) === null || _transformer$loadConf === void 0 ? void 0 : _transformer$loadConf.call(transformer, {
239
- config: new _compat.PluginConfig({
240
- env,
241
- isSource: true,
242
- searchPath: innerAsset.filePath,
243
- projectRoot: options.projectRoot,
244
- fs: this.#fs,
245
- packageManager
246
- }),
247
- ...defaultOptions
248
- }));
251
+ // Transformer uses the deprecated loadConfig API, so mark as not
252
+ // cachable
253
+ cacheBailouts.push(`Transformer.loadConfig`);
254
+ }
249
255
  if (transformer.parse) {
250
256
  const ast = await transformer.parse({
251
257
  // @ts-expect-error TS2322
@@ -257,14 +263,32 @@ class AtlaspackWorker {
257
263
  if (ast) {
258
264
  mutableAsset.setAST(ast);
259
265
  }
266
+ cacheBailouts.push(`Transformer.parse`);
260
267
  }
261
- const result = await state.transformer.transform({
268
+ const [result, sideEffects] = await this.#sideEffectDetector.monitorSideEffects(key, () => transformer.transform({
262
269
  // @ts-expect-error TS2322
263
270
  asset: mutableAsset,
264
271
  config,
265
272
  resolve: resolveFunc,
266
273
  ...defaultOptions
267
- });
274
+ }));
275
+ if (sideEffects.envUsage.didEnumerate) {
276
+ cacheBailouts.push(`Env access: enumeration of process.env`);
277
+ }
278
+ for (let variable of sideEffects.envUsage.vars) {
279
+ if (variable in allowedEnv) {
280
+ continue;
281
+ }
282
+ cacheBailouts.push(`Env access: ${variable}`);
283
+ }
284
+ for (let {
285
+ method,
286
+ path
287
+ } of sideEffects.fsUsage) {
288
+ cacheBailouts.push(`FS usage: ${method}(${path})`);
289
+ }
290
+ (0, _assert().default)(result.length === 1, '[V3] Unimplemented: Multiple asset return from Node transformer');
291
+ (0, _assert().default)(result[0] === mutableAsset, '[V3] Unimplemented: New asset returned from Node transformer');
268
292
  if (transformer.generate) {
269
293
  const ast = await mutableAsset.getAST();
270
294
  if (ast) {
@@ -287,8 +311,6 @@ class AtlaspackWorker {
287
311
  }
288
312
  }
289
313
  }
290
- (0, _assert().default)(result.length === 1, '[V3] Unimplemented: Multiple asset return from Node transformer');
291
- (0, _assert().default)(result[0] === mutableAsset, '[V3] Unimplemented: New asset returned from Node transformer');
292
314
  let assetBuffer = await mutableAsset.getBuffer();
293
315
 
294
316
  // If the asset has no code, we set the buffer to null, which we can
@@ -296,6 +318,10 @@ class AtlaspackWorker {
296
318
  if (assetBuffer.length === 0) {
297
319
  assetBuffer = null;
298
320
  }
321
+ if (pluginOptions.used) {
322
+ // Plugin options accessed, so not cachable
323
+ cacheBailouts.push(`Plugin options accessed`);
324
+ }
299
325
  return [{
300
326
  id: mutableAsset.id,
301
327
  bundleBehavior: _compat.bundleBehaviorMap.intoNullable(mutableAsset.bundleBehavior),
@@ -315,8 +341,57 @@ class AtlaspackWorker {
315
341
  // Only send back the map if it has changed
316
342
  mutableAsset.isMapDirty ?
317
343
  // @ts-expect-error TS2533
318
- JSON.stringify((await mutableAsset.getMap()).toVLQ()) : ''];
344
+ JSON.stringify((await mutableAsset.getMap()).toVLQ()) : '',
345
+ // Limit to first 10 bailouts
346
+ cacheBailouts.slice(0, 10)];
319
347
  });
348
+ get options() {
349
+ if (this.#options == null) {
350
+ throw new Error('Plugin options have not been initialized');
351
+ }
352
+ return this.#options;
353
+ }
354
+ async initializeTransformer(instance, specifier) {
355
+ let transformer = instance;
356
+ let setup, config, allowedEnv;
357
+ let packageManager = new (_packageManager().NodePackageManager)(this.#fs, this.options.projectRoot);
358
+ if (transformer.setup) {
359
+ var _setupResult$env;
360
+ let setupResult = await transformer.setup({
361
+ logger: new _compat.PluginLogger(),
362
+ options: new _compat.PluginOptions({
363
+ ...this.options,
364
+ shouldAutoInstall: false,
365
+ inputFS: this.#fs,
366
+ outputFS: this.#fs,
367
+ packageManager
368
+ }),
369
+ config: new _compat.PluginConfig({
370
+ plugin: specifier,
371
+ searchPath: 'index',
372
+ // Consider project setup config as source
373
+ isSource: true
374
+ }, this.options)
375
+ });
376
+ config = setupResult === null || setupResult === void 0 ? void 0 : setupResult.config;
377
+ allowedEnv = Object.fromEntries((setupResult === null || setupResult === void 0 || (_setupResult$env = setupResult.env) === null || _setupResult$env === void 0 ? void 0 : _setupResult$env.map(env => [env, process.env[env]])) || []);
378
+
379
+ // Always add the following env vars to the cache key
380
+ allowedEnv['NODE_ENV'] = process.env['NODE_ENV'];
381
+ setup = {
382
+ conditions: setupResult === null || setupResult === void 0 ? void 0 : setupResult.conditions,
383
+ config,
384
+ env: allowedEnv
385
+ };
386
+ }
387
+ this.#transformers.set(specifier, {
388
+ transformer,
389
+ config,
390
+ packageManager,
391
+ allowedEnv
392
+ });
393
+ return setup;
394
+ }
320
395
  }
321
396
 
322
397
  // Create napi worker and send it back to main thread
@@ -324,5 +399,10 @@ exports.AtlaspackWorker = AtlaspackWorker;
324
399
  const worker = new AtlaspackWorker();
325
400
  const napiWorker = napi().newNodejsWorker(worker);
326
401
  _worker_threads().parentPort === null || _worker_threads().parentPort === void 0 || _worker_threads().parentPort.postMessage(napiWorker);
327
-
328
- // @ts-expect-error TS2694
402
+ _worker_threads().parentPort === null || _worker_threads().parentPort === void 0 || _worker_threads().parentPort.setMaxListeners(_worker_threads().parentPort.getMaxListeners() + 1);
403
+ _worker_threads().parentPort === null || _worker_threads().parentPort === void 0 || _worker_threads().parentPort.addListener('message', message => {
404
+ if (message === 'clearState') {
405
+ worker.clearState();
406
+ _worker_threads().parentPort === null || _worker_threads().parentPort === void 0 || _worker_threads().parentPort.postMessage('stateCleared');
407
+ }
408
+ });
@@ -8,6 +8,7 @@ exports.getConfigKeyContentHash = getConfigKeyContentHash;
8
8
  exports.getConfigRequests = getConfigRequests;
9
9
  exports.getValueAtPath = getValueAtPath;
10
10
  exports.loadPluginConfig = loadPluginConfig;
11
+ exports.loadPluginSetup = loadPluginSetup;
11
12
  exports.runConfigRequest = runConfigRequest;
12
13
  function _utils() {
13
14
  const data = require("@atlaspack/utils");
@@ -87,6 +88,30 @@ async function loadPluginConfig(loadedPlugin, config, options) {
87
88
  });
88
89
  }
89
90
  }
91
+ async function loadPluginSetup(pluginName, setup, config, options) {
92
+ try {
93
+ let result = await setup({
94
+ config: new _Config.default(config, options),
95
+ options: new _PluginOptions.default((0, _utils2.optionsProxy)(options, option => {
96
+ config.invalidateOnOptionChange.add(option);
97
+ })),
98
+ logger: new (_logger().PluginLogger)({
99
+ origin: pluginName
100
+ }),
101
+ tracer: new (_profiler().PluginTracer)({
102
+ origin: pluginName,
103
+ category: 'setup'
104
+ })
105
+ });
106
+ config.result = result.config;
107
+ } catch (e) {
108
+ throw new (_diagnostic().default)({
109
+ diagnostic: (0, _diagnostic().errorToDiagnostic)(e, {
110
+ origin: pluginName
111
+ })
112
+ });
113
+ }
114
+ }
90
115
 
91
116
  /**
92
117
  * Return value at a given key path within an object.
@@ -2,7 +2,7 @@ import type { PackageName, ConfigResult } from '@atlaspack/types';
2
2
  import type { Config, InternalFileCreateInvalidation, InternalDevDepOptions } from './types';
3
3
  import type { ProjectPath } from './projectPath';
4
4
  import type { EnvironmentRef } from './EnvironmentManager';
5
- type ConfigOpts = {
5
+ export type ConfigOpts = {
6
6
  plugin: PackageName;
7
7
  searchPath: ProjectPath;
8
8
  isSource?: boolean;
@@ -21,4 +21,3 @@ type ConfigOpts = {
21
21
  invalidateOnBuild?: boolean;
22
22
  };
23
23
  export declare function createConfig({ plugin, isSource, searchPath, env, result, invalidateOnFileChange, invalidateOnConfigKeyChange, invalidateOnFileCreate, invalidateOnEnvChange, invalidateOnOptionChange, devDeps, invalidateOnStartup, invalidateOnBuild, }: ConfigOpts): Config;
24
- export {};
@@ -1,4 +1,4 @@
1
- import { AtlaspackNapi, Lmdb, AtlaspackNapiOptions } from '@atlaspack/rust';
1
+ import { AtlaspackNapi, Lmdb, AtlaspackNapiOptions, CacheStats } from '@atlaspack/rust';
2
2
  import type { Event } from '@parcel/watcher';
3
3
  import type { NapiWorkerPool as INapiWorkerPool } from '@atlaspack/types';
4
4
  export type AtlaspackV3Options = {
@@ -23,4 +23,5 @@ export declare class AtlaspackV3 {
23
23
  end(): void;
24
24
  buildAssetGraph(): Promise<any>;
25
25
  respondToFsEvents(events: Array<Event>): Promise<boolean>;
26
+ completeCacheSession(): Promise<CacheStats>;
26
27
  }
@@ -6,6 +6,7 @@ export type NapiWorkerPoolOptions = {
6
6
  export declare class NapiWorkerPool implements INapiWorkerPool {
7
7
  #private;
8
8
  constructor({ workerCount }?: NapiWorkerPoolOptions);
9
+ clearAllWorkerState(): Promise<void[]>;
9
10
  workerCount(): number;
10
11
  getWorkers(): Promise<Array<Transferable>>;
11
12
  shutdown(): void;
@@ -3,3 +3,4 @@ export { AtlaspackV3 } from './AtlaspackV3';
3
3
  export { NapiWorkerPool } from './NapiWorkerPool';
4
4
  export * from './AtlaspackV3';
5
5
  export * from './NapiWorkerPool';
6
+ export type { CacheStats } from '@atlaspack/rust';
@@ -1,19 +1,11 @@
1
- import type { Config as IPluginConfig, DevDepOptions, FilePath, Environment, FileCreateInvalidation, ConfigResultWithFilePath, PackageJSON, PackageManager as IPackageManager } from '@atlaspack/types';
2
- import type { FileSystem as IFileSystem } from '@atlaspack/fs';
3
- export type PluginConfigOptions = {
4
- isSource: boolean;
5
- searchPath: FilePath;
6
- projectRoot: FilePath;
7
- env: Environment;
8
- fs: IFileSystem;
9
- packageManager: IPackageManager;
10
- };
1
+ import type { Config as IPluginConfig, DevDepOptions, FilePath, Environment, FileCreateInvalidation, ConfigResultWithFilePath, PackageJSON } from '@atlaspack/types';
2
+ import { type ConfigOpts } from '../../../InternalConfig';
11
3
  export declare class PluginConfig implements IPluginConfig {
12
4
  #private;
13
5
  isSource: boolean;
14
6
  searchPath: FilePath;
15
7
  env: Environment;
16
- constructor({ env, isSource, searchPath, projectRoot, fs, packageManager, }: PluginConfigOptions);
8
+ constructor(configOpts: ConfigOpts, options: any);
17
9
  invalidateOnFileChange(filePath: FilePath): void;
18
10
  invalidateOnFileCreate(invalidations: FileCreateInvalidation): void;
19
11
  invalidateOnEnvChange(invalidation: string): void;
@@ -2,6 +2,7 @@ import type { PluginOptions as IPluginOptions, LogLevel, FileSystem, PackageMana
2
2
  import type { FeatureFlags } from '@atlaspack/feature-flags';
3
3
  export declare class PluginOptions implements IPluginOptions {
4
4
  #private;
5
+ used: boolean;
5
6
  get env(): EnvMap;
6
7
  get projectRoot(): FilePath;
7
8
  get packageManager(): PackageManager;