@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
@@ -1,3 +1,10 @@
1
+ /* eslint-disable import/first */
2
+ import {SideEffectDetector} from './side-effect-detector';
3
+
4
+ // Install side effect detection patches BEFORE importing any modules that use fs
5
+ const sideEffectDetector = new SideEffectDetector();
6
+ sideEffectDetector.install();
7
+
1
8
  import assert from 'assert';
2
9
  import * as napi from '@atlaspack/rust';
3
10
  // @ts-expect-error TS2305
@@ -10,6 +17,7 @@ import type {
10
17
  FilePath,
11
18
  FileSystem,
12
19
  } from '@atlaspack/types';
20
+ import type {FeatureFlags} from '@atlaspack/feature-flags';
13
21
  import {parentPort} from 'worker_threads';
14
22
  import * as module from 'module';
15
23
 
@@ -25,7 +33,6 @@ import {
25
33
  bundleBehaviorMap,
26
34
  dependencyPriorityMap,
27
35
  } from './compat';
28
- import {FeatureFlags} from '@atlaspack/feature-flags';
29
36
 
30
37
  const CONFIG = Symbol.for('parcel-plugin-config');
31
38
 
@@ -34,16 +41,25 @@ export class AtlaspackWorker {
34
41
  #transformers: Map<string, TransformerState<any>>;
35
42
  #fs: FileSystem;
36
43
  #packageManager: NodePackageManager;
44
+ #options: Options | undefined;
45
+ #sideEffectDetector: SideEffectDetector;
37
46
 
38
47
  constructor() {
39
48
  this.#resolvers = new Map();
40
49
  this.#transformers = new Map();
41
50
  this.#fs = new NodeFS();
42
51
  this.#packageManager = new NodePackageManager(this.#fs, '/');
52
+ this.#sideEffectDetector = sideEffectDetector; // Use the global detector that was installed before imports
53
+ }
54
+
55
+ clearState() {
56
+ this.#resolvers.clear();
57
+ this.#transformers.clear();
58
+ this.#options = undefined;
43
59
  }
44
60
 
45
61
  loadPlugin: JsCallable<[LoadPluginOptions], Promise<undefined>> = jsCallable(
46
- async ({kind, specifier, resolveFrom, featureFlags}) => {
62
+ async ({kind, specifier, resolveFrom, options}) => {
47
63
  // Use packageManager.require() instead of dynamic import() to support TypeScript plugins
48
64
  let resolvedModule = await this.#packageManager.require(
49
65
  specifier,
@@ -70,26 +86,32 @@ export class AtlaspackWorker {
70
86
  `Plugin could not be resolved\n\t${kind}\n\t${resolveFrom}\n\t${specifier}`,
71
87
  );
72
88
  }
73
- // Set feature flags in the worker process
74
- // if (featureFlags) {
89
+
90
+ if (this.#options == null) {
91
+ this.#options = {
92
+ ...options,
93
+ inputFS: this.#fs,
94
+ outputFS: this.#fs,
95
+ packageManager: this.#packageManager,
96
+ shouldAutoInstall: false,
97
+ };
98
+ }
99
+
75
100
  // Set feature flags in the worker process
76
101
  let featureFlagsModule = await this.#packageManager.require(
77
102
  '@atlaspack/feature-flags',
78
103
  __filename,
79
104
  {shouldAutoInstall: false},
80
105
  );
81
- featureFlagsModule.setFeatureFlags(featureFlags);
82
- // const {setFeatureFlags} = await import('@atlaspack/feature-flags');
83
- // setFeatureFlags(featureFlags);
84
- // }
106
+ featureFlagsModule.setFeatureFlags(options.featureFlags);
85
107
 
86
108
  switch (kind) {
87
109
  case 'resolver':
88
110
  this.#resolvers.set(specifier, {resolver: instance});
89
111
  break;
90
- case 'transformer':
91
- this.#transformers.set(specifier, {transformer: instance});
92
- break;
112
+ case 'transformer': {
113
+ return this.initializeTransformer(instance, specifier);
114
+ }
93
115
  }
94
116
  },
95
117
  );
@@ -98,53 +120,33 @@ export class AtlaspackWorker {
98
120
  [RunResolverResolveOptions],
99
121
  Promise<RunResolverResolveResult>
100
122
  > = jsCallable(
101
- async ({
102
- key,
103
- dependency: napiDependency,
104
- specifier,
105
- pipeline,
106
- pluginOptions,
107
- }) => {
123
+ async ({key, dependency: napiDependency, specifier, pipeline}) => {
108
124
  const state = this.#resolvers.get(key);
109
125
  if (!state) {
110
126
  throw new Error(`Resolver not found: ${key}`);
111
127
  }
112
128
 
113
- let packageManager = state.packageManager;
114
- if (!packageManager) {
115
- packageManager = new NodePackageManager(
116
- this.#fs,
117
- pluginOptions.projectRoot,
118
- );
119
- state.packageManager = packageManager;
120
- }
121
-
122
129
  const env = new Environment(napiDependency.env);
123
130
  const dependency = new Dependency(napiDependency, env);
124
131
 
125
132
  const defaultOptions = {
126
133
  logger: new PluginLogger(),
127
134
  tracer: new PluginTracer(),
128
- options: new PluginOptions({
129
- ...pluginOptions,
130
- packageManager,
131
- shouldAutoInstall: false,
132
- inputFS: this.#fs,
133
- outputFS: this.#fs,
134
- }),
135
+ options: new PluginOptions(this.options),
135
136
  } as const;
136
137
 
137
138
  if (!('config' in state)) {
138
139
  // @ts-expect-error TS2345
139
140
  state.config = await state.resolver.loadConfig?.({
140
- config: new PluginConfig({
141
- env,
142
- isSource: true,
143
- searchPath: specifier,
144
- projectRoot: pluginOptions.projectRoot,
145
- fs: this.#fs,
146
- packageManager,
147
- }),
141
+ config: new PluginConfig(
142
+ {
143
+ env: napiDependency.env,
144
+ plugin: key,
145
+ isSource: true,
146
+ searchPath: 'index',
147
+ },
148
+ this.options,
149
+ ),
148
150
  ...defaultOptions,
149
151
  });
150
152
  }
@@ -192,155 +194,248 @@ export class AtlaspackWorker {
192
194
  runTransformerTransform: JsCallable<
193
195
  [RunTransformerTransformOptions, Buffer, string | null | undefined],
194
196
  Promise<RunTransformerTransformResult>
195
- > = jsCallable(
196
- async ({key, env: napiEnv, options, asset: innerAsset}, contents, map) => {
197
- const state = this.#transformers.get(key);
198
- if (!state) {
199
- throw new Error(`Transformer not found: ${key}`);
200
- }
197
+ > = jsCallable(async ({key, asset: innerAsset}, contents, map) => {
198
+ const instance = this.#transformers.get(key);
199
+ if (!instance) {
200
+ throw new Error(`Transformer not found: ${key}`);
201
+ }
201
202
 
202
- let packageManager = state.packageManager;
203
- if (!packageManager) {
204
- packageManager = new NodePackageManager(this.#fs, options.projectRoot);
205
- state.packageManager = packageManager;
206
- }
203
+ let {transformer, config, allowedEnv = new Set()} = instance;
207
204
 
208
- const transformer: Transformer<any> = state.transformer;
209
- const resolveFunc = (from: string, to: string): Promise<any> => {
210
- let customRequire = module.createRequire(from);
211
- let resolvedPath = customRequire.resolve(to);
205
+ let cacheBailouts = [];
212
206
 
213
- return Promise.resolve(resolvedPath);
214
- };
207
+ const resolveFunc = (from: string, to: string): Promise<any> => {
208
+ let customRequire = module.createRequire(from);
209
+ let resolvedPath = customRequire.resolve(to);
210
+ // Tranformer not cacheable due to use of the resolve function
215
211
 
216
- const env = new Environment(napiEnv);
217
- const mutableAsset = new MutableAsset(
218
- innerAsset,
219
- // @ts-expect-error TS2345
220
- contents,
221
- env,
222
- this.#fs,
223
- map,
224
- options.projectRoot,
225
- );
212
+ cacheBailouts.push(`resolve(${from}, ${to})`);
226
213
 
227
- const defaultOptions = {
228
- logger: new PluginLogger(),
229
- tracer: new PluginTracer(),
230
- options: new PluginOptions({
231
- ...options,
232
- packageManager,
233
- shouldAutoInstall: false,
234
- inputFS: this.#fs,
235
- outputFS: this.#fs,
236
- }),
237
- } as const;
214
+ return Promise.resolve(resolvedPath);
215
+ };
238
216
 
217
+ const env = new Environment(innerAsset.env);
218
+ let mutableAsset = new MutableAsset(
219
+ innerAsset,
239
220
  // @ts-expect-error TS2345
240
- const config = await transformer.loadConfig?.({
241
- config: new PluginConfig({
242
- env,
243
- isSource: true,
244
- searchPath: innerAsset.filePath,
245
- projectRoot: options.projectRoot,
246
- fs: this.#fs,
247
- packageManager,
248
- }),
221
+ contents,
222
+ env,
223
+ this.#fs,
224
+ map,
225
+ this.options.projectRoot,
226
+ );
227
+
228
+ const pluginOptions = new PluginOptions(this.options);
229
+ const defaultOptions = {
230
+ logger: new PluginLogger(),
231
+ tracer: new PluginTracer(),
232
+ options: pluginOptions,
233
+ } as const;
234
+
235
+ if (transformer.loadConfig) {
236
+ if (config != null) {
237
+ throw new Error(
238
+ `Transformer (${key}) should not implement 'setup' and 'loadConfig'`,
239
+ );
240
+ }
241
+ // @ts-expect-error TS2345
242
+ config = await transformer.loadConfig({
243
+ config: new PluginConfig(
244
+ {
245
+ plugin: key,
246
+ isSource: innerAsset.isSource,
247
+ searchPath: innerAsset.filePath,
248
+ env,
249
+ },
250
+ this.options,
251
+ ),
249
252
  ...defaultOptions,
250
253
  });
251
254
 
252
- if (transformer.parse) {
253
- const ast = await transformer.parse({
254
- // @ts-expect-error TS2322
255
- asset: mutableAsset,
256
- config,
257
- resolve: resolveFunc,
258
- ...defaultOptions,
259
- });
260
- if (ast) {
261
- mutableAsset.setAST(ast);
262
- }
263
- }
255
+ // Transformer uses the deprecated loadConfig API, so mark as not
256
+ // cachable
257
+ cacheBailouts.push(`Transformer.loadConfig`);
258
+ }
264
259
 
265
- const result = await state.transformer.transform({
260
+ if (transformer.parse) {
261
+ const ast = await transformer.parse({
266
262
  // @ts-expect-error TS2322
267
263
  asset: mutableAsset,
268
264
  config,
269
265
  resolve: resolveFunc,
270
266
  ...defaultOptions,
271
267
  });
268
+ if (ast) {
269
+ mutableAsset.setAST(ast);
270
+ }
271
+ cacheBailouts.push(`Transformer.parse`);
272
+ }
273
+
274
+ const [result, sideEffects] =
275
+ await this.#sideEffectDetector.monitorSideEffects(key, () =>
276
+ transformer.transform({
277
+ // @ts-expect-error TS2322
278
+ asset: mutableAsset,
279
+ config,
280
+ resolve: resolveFunc,
281
+ ...defaultOptions,
282
+ }),
283
+ );
284
+
285
+ if (sideEffects.envUsage.didEnumerate) {
286
+ cacheBailouts.push(`Env access: enumeration of process.env`);
287
+ }
272
288
 
273
- if (transformer.generate) {
274
- const ast = await mutableAsset.getAST();
275
- if (ast) {
276
- const output = await transformer.generate({
277
- // @ts-expect-error TS2322
278
- asset: mutableAsset,
279
- ast,
280
- ...defaultOptions,
281
- });
282
-
283
- if (typeof output.content === 'string') {
284
- mutableAsset.setCode(output.content);
285
- } else if (output.content instanceof Buffer) {
286
- mutableAsset.setBuffer(output.content);
287
- } else {
288
- // @ts-expect-error TS2345
289
- mutableAsset.setStream(output.content);
290
- }
291
-
292
- if (output.map) {
293
- mutableAsset.setMap(output.map);
294
- }
289
+ for (let variable of sideEffects.envUsage.vars) {
290
+ if (variable in allowedEnv) {
291
+ continue;
292
+ }
293
+
294
+ cacheBailouts.push(`Env access: ${variable}`);
295
+ }
296
+
297
+ for (let {method, path} of sideEffects.fsUsage) {
298
+ cacheBailouts.push(`FS usage: ${method}(${path})`);
299
+ }
300
+
301
+ assert(
302
+ result.length === 1,
303
+ '[V3] Unimplemented: Multiple asset return from Node transformer',
304
+ );
305
+
306
+ assert(
307
+ result[0] === mutableAsset,
308
+ '[V3] Unimplemented: New asset returned from Node transformer',
309
+ );
310
+
311
+ if (transformer.generate) {
312
+ const ast = await mutableAsset.getAST();
313
+ if (ast) {
314
+ const output = await transformer.generate({
315
+ // @ts-expect-error TS2322
316
+ asset: mutableAsset,
317
+ ast,
318
+ ...defaultOptions,
319
+ });
320
+
321
+ if (typeof output.content === 'string') {
322
+ mutableAsset.setCode(output.content);
323
+ } else if (output.content instanceof Buffer) {
324
+ mutableAsset.setBuffer(output.content);
325
+ } else {
326
+ // @ts-expect-error TS2345
327
+ mutableAsset.setStream(output.content);
328
+ }
329
+
330
+ if (output.map) {
331
+ mutableAsset.setMap(output.map);
295
332
  }
296
333
  }
334
+ }
335
+
336
+ let assetBuffer: Buffer | null = await mutableAsset.getBuffer();
337
+
338
+ // If the asset has no code, we set the buffer to null, which we can
339
+ // detect in Rust, to avoid passing back an empty buffer, which we can't.
340
+ if (assetBuffer.length === 0) {
341
+ assetBuffer = null;
342
+ }
343
+
344
+ if (pluginOptions.used) {
345
+ // Plugin options accessed, so not cachable
346
+ cacheBailouts.push(`Plugin options accessed`);
347
+ }
348
+
349
+ return [
350
+ {
351
+ id: mutableAsset.id,
352
+ bundleBehavior: bundleBehaviorMap.intoNullable(
353
+ mutableAsset.bundleBehavior,
354
+ ),
355
+ code: [],
356
+ filePath: mutableAsset.filePath,
357
+ isBundleSplittable: mutableAsset.isBundleSplittable,
358
+ isSource: mutableAsset.isSource,
359
+ meta: mutableAsset.meta,
360
+ pipeline: mutableAsset.pipeline,
361
+ // Query should be undefined if it's empty
362
+ query: mutableAsset.query.toString() || undefined,
363
+ sideEffects: mutableAsset.sideEffects,
364
+ symbols: mutableAsset.symbols.intoNapi(),
365
+ type: mutableAsset.type,
366
+ uniqueKey: mutableAsset.uniqueKey,
367
+ },
368
+ assetBuffer,
369
+ // Only send back the map if it has changed
370
+ mutableAsset.isMapDirty
371
+ ? // @ts-expect-error TS2533
372
+ JSON.stringify((await mutableAsset.getMap()).toVLQ())
373
+ : '',
374
+ // Limit to first 10 bailouts
375
+ cacheBailouts.slice(0, 10),
376
+ ];
377
+ });
378
+
379
+ get options() {
380
+ if (this.#options == null) {
381
+ throw new Error('Plugin options have not been initialized');
382
+ }
383
+ return this.#options;
384
+ }
297
385
 
298
- assert(
299
- result.length === 1,
300
- '[V3] Unimplemented: Multiple asset return from Node transformer',
301
- );
386
+ async initializeTransformer(instance: Transformer<any>, specifier: string) {
387
+ let transformer = instance;
388
+ let setup, config, allowedEnv;
389
+
390
+ let packageManager = new NodePackageManager(
391
+ this.#fs,
392
+ this.options.projectRoot,
393
+ );
302
394
 
303
- assert(
304
- result[0] === mutableAsset,
305
- '[V3] Unimplemented: New asset returned from Node transformer',
395
+ if (transformer.setup) {
396
+ let setupResult = await transformer.setup({
397
+ logger: new PluginLogger(),
398
+ options: new PluginOptions({
399
+ ...this.options,
400
+ shouldAutoInstall: false,
401
+ inputFS: this.#fs,
402
+ outputFS: this.#fs,
403
+ packageManager,
404
+ }),
405
+ config: new PluginConfig(
406
+ {
407
+ plugin: specifier,
408
+ searchPath: 'index',
409
+ // Consider project setup config as source
410
+ isSource: true,
411
+ },
412
+ this.options,
413
+ ),
414
+ });
415
+ config = setupResult?.config;
416
+ allowedEnv = Object.fromEntries(
417
+ setupResult?.env?.map((env) => [env, process.env[env]]) || [],
306
418
  );
307
419
 
308
- let assetBuffer: Buffer | null = await mutableAsset.getBuffer();
420
+ // Always add the following env vars to the cache key
421
+ allowedEnv['NODE_ENV'] = process.env['NODE_ENV'];
309
422
 
310
- // If the asset has no code, we set the buffer to null, which we can
311
- // detect in Rust, to avoid passing back an empty buffer, which we can't.
312
- if (assetBuffer.length === 0) {
313
- assetBuffer = null;
314
- }
423
+ setup = {
424
+ conditions: setupResult?.conditions,
425
+ config,
426
+ env: allowedEnv,
427
+ };
428
+ }
315
429
 
316
- return [
317
- {
318
- id: mutableAsset.id,
319
- bundleBehavior: bundleBehaviorMap.intoNullable(
320
- mutableAsset.bundleBehavior,
321
- ),
322
- code: [],
323
- filePath: mutableAsset.filePath,
324
- isBundleSplittable: mutableAsset.isBundleSplittable,
325
- isSource: mutableAsset.isSource,
326
- meta: mutableAsset.meta,
327
- pipeline: mutableAsset.pipeline,
328
- // Query should be undefined if it's empty
329
- query: mutableAsset.query.toString() || undefined,
330
- sideEffects: mutableAsset.sideEffects,
331
- symbols: mutableAsset.symbols.intoNapi(),
332
- type: mutableAsset.type,
333
- uniqueKey: mutableAsset.uniqueKey,
334
- },
335
- assetBuffer,
336
- // Only send back the map if it has changed
337
- mutableAsset.isMapDirty
338
- ? // @ts-expect-error TS2533
339
- JSON.stringify((await mutableAsset.getMap()).toVLQ())
340
- : '',
341
- ];
342
- },
343
- );
430
+ this.#transformers.set(specifier, {
431
+ transformer,
432
+ config,
433
+ packageManager,
434
+ allowedEnv,
435
+ });
436
+
437
+ return setup;
438
+ }
344
439
  }
345
440
 
346
441
  // Create napi worker and send it back to main thread
@@ -348,27 +443,45 @@ const worker = new AtlaspackWorker();
348
443
  const napiWorker = napi.newNodejsWorker(worker);
349
444
  parentPort?.postMessage(napiWorker);
350
445
 
446
+ parentPort?.setMaxListeners(parentPort.getMaxListeners() + 1);
447
+ parentPort?.addListener('message', (message: unknown) => {
448
+ if (message === 'clearState') {
449
+ worker.clearState();
450
+ parentPort?.postMessage('stateCleared');
451
+ }
452
+ });
453
+
351
454
  type ResolverState<T> = {
352
455
  resolver: Resolver<T>;
353
456
  config?: T;
354
457
  packageManager?: NodePackageManager;
355
458
  };
356
459
 
357
- type TransformerState<T> = {
460
+ type TransformerState<ConfigType> = {
358
461
  packageManager?: NodePackageManager;
359
- transformer: Transformer<T>;
462
+ transformer: Transformer<ConfigType>;
463
+ config?: ConfigType;
464
+ allowedEnv?: Record<string, string | undefined>;
360
465
  };
361
466
 
362
467
  type LoadPluginOptions = {
363
468
  kind: 'resolver' | 'transformer';
364
469
  specifier: string;
365
470
  resolveFrom: string;
366
- featureFlags?: FeatureFlags;
471
+ options: RpcPluginOptions;
367
472
  };
368
473
 
369
474
  type RpcPluginOptions = {
370
475
  projectRoot: string;
371
476
  mode: string;
477
+ featureFlags: FeatureFlags;
478
+ };
479
+
480
+ type Options = RpcPluginOptions & {
481
+ inputFS: FileSystem;
482
+ outputFS: FileSystem;
483
+ packageManager: NodePackageManager;
484
+ shouldAutoInstall: boolean;
372
485
  };
373
486
 
374
487
  type RunResolverResolveOptions = {
@@ -377,7 +490,6 @@ type RunResolverResolveOptions = {
377
490
  dependency: napi.Dependency;
378
491
  specifier: FilePath;
379
492
  pipeline: string | null | undefined;
380
- pluginOptions: RpcPluginOptions;
381
493
  };
382
494
 
383
495
  type RunResolverResolveResult = {
@@ -406,10 +518,14 @@ type RunTransformerTransformOptions = {
406
518
  key: string;
407
519
  // @ts-expect-error TS2724
408
520
  env: napi.Environment;
409
- options: RpcPluginOptions;
410
521
  // @ts-expect-error TS2694
411
522
  asset: napi.Asset;
412
523
  };
413
524
 
414
- // @ts-expect-error TS2694
415
- type RunTransformerTransformResult = [napi.RpcAssetResult, Buffer, string];
525
+ type RunTransformerTransformResult = [
526
+ // @ts-expect-error TS2694
527
+ napi.RpcAssetResult,
528
+ Buffer,
529
+ string,
530
+ boolean,
531
+ ];
@@ -6,6 +6,7 @@ import type {
6
6
  PluginTracer as IPluginTracer,
7
7
  NamedBundle as INamedBundle,
8
8
  BundleGraph as IBundleGraph,
9
+ TransformerSetup,
9
10
  } from '@atlaspack/types';
10
11
  import {readConfig, hashObject} from '@atlaspack/utils';
11
12
  import type {
@@ -29,6 +30,13 @@ import {PluginTracer} from '@atlaspack/profiler';
29
30
  import {requestTypes} from '../RequestTracker';
30
31
  import {fromProjectPath, fromProjectPathRelative} from '../projectPath';
31
32
 
33
+ type Setup<T> = (arg1: {
34
+ config: IConfig;
35
+ options: IPluginOptions;
36
+ logger: IPluginLogger;
37
+ tracer: IPluginTracer;
38
+ }) => Async<TransformerSetup<T>>;
39
+
32
40
  export type PluginWithLoadConfig = {
33
41
  loadConfig?: (arg1: {
34
42
  config: IConfig;
@@ -104,6 +112,37 @@ export async function loadPluginConfig<T extends PluginWithLoadConfig>(
104
112
  }
105
113
  }
106
114
 
115
+ export async function loadPluginSetup<T>(
116
+ pluginName: string,
117
+ setup: Setup<T>,
118
+ config: Config,
119
+ options: AtlaspackOptions,
120
+ ): Promise<void> {
121
+ try {
122
+ let result = await setup({
123
+ config: new PublicConfig(config, options),
124
+ options: new PluginOptions(
125
+ optionsProxy(options, (option) => {
126
+ config.invalidateOnOptionChange.add(option);
127
+ }),
128
+ ),
129
+ logger: new PluginLogger({origin: pluginName}),
130
+ tracer: new PluginTracer({
131
+ origin: pluginName,
132
+ category: 'setup',
133
+ }),
134
+ });
135
+
136
+ config.result = result.config;
137
+ } catch (e: any) {
138
+ throw new ThrowableDiagnostic({
139
+ diagnostic: errorToDiagnostic(e, {
140
+ origin: pluginName,
141
+ }),
142
+ });
143
+ }
144
+ }
145
+
107
146
  /**
108
147
  * Return value at a given key path within an object.
109
148
  *