@atlaspack/core 2.30.3-dev-swc44-3ef36b21e.0 → 2.31.1-dev-swc44-b0b390313.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.
- package/CHANGELOG.md +29 -0
- package/dist/Atlaspack.js +2 -0
- package/dist/Transformation.js +24 -1
- package/dist/atlaspack-v3/AtlaspackV3.js +3 -0
- package/dist/atlaspack-v3/NapiWorkerPool.js +10 -0
- package/dist/atlaspack-v3/worker/compat/plugin-config.js +10 -22
- package/dist/atlaspack-v3/worker/compat/plugin-options.js +15 -0
- package/dist/atlaspack-v3/worker/side-effect-detector.js +243 -0
- package/dist/atlaspack-v3/worker/worker.js +140 -66
- package/dist/requests/ConfigRequest.js +24 -0
- package/lib/Atlaspack.js +5 -1
- package/lib/Transformation.js +31 -1
- package/lib/atlaspack-v3/AtlaspackV3.js +3 -0
- package/lib/atlaspack-v3/NapiWorkerPool.js +10 -0
- package/lib/atlaspack-v3/worker/compat/plugin-config.js +8 -27
- package/lib/atlaspack-v3/worker/compat/plugin-options.js +15 -0
- package/lib/atlaspack-v3/worker/side-effect-detector.js +215 -0
- package/lib/atlaspack-v3/worker/worker.js +152 -72
- package/lib/requests/ConfigRequest.js +25 -0
- package/lib/types/InternalConfig.d.ts +1 -2
- package/lib/types/atlaspack-v3/AtlaspackV3.d.ts +2 -1
- package/lib/types/atlaspack-v3/NapiWorkerPool.d.ts +1 -0
- package/lib/types/atlaspack-v3/index.d.ts +1 -0
- package/lib/types/atlaspack-v3/worker/compat/plugin-config.d.ts +3 -11
- package/lib/types/atlaspack-v3/worker/compat/plugin-options.d.ts +1 -0
- package/lib/types/atlaspack-v3/worker/side-effect-detector.d.ts +76 -0
- package/lib/types/atlaspack-v3/worker/worker.d.ts +26 -6
- package/lib/types/requests/ConfigRequest.d.ts +9 -1
- package/package.json +19 -19
- package/src/Atlaspack.ts +2 -0
- package/src/InternalConfig.ts +1 -1
- package/src/Transformation.ts +37 -2
- package/src/atlaspack-v3/AtlaspackV3.ts +8 -0
- package/src/atlaspack-v3/NapiWorkerPool.ts +17 -0
- package/src/atlaspack-v3/index.ts +1 -0
- package/src/atlaspack-v3/worker/compat/plugin-config.ts +8 -40
- package/src/atlaspack-v3/worker/compat/plugin-options.ts +15 -0
- package/src/atlaspack-v3/worker/side-effect-detector.ts +298 -0
- package/src/atlaspack-v3/worker/worker.ts +288 -172
- package/src/requests/ConfigRequest.ts +39 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @atlaspack/core
|
|
2
2
|
|
|
3
|
+
## 2.31.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#934](https://github.com/atlassian-labs/atlaspack/pull/934) [`02cc8b3`](https://github.com/atlassian-labs/atlaspack/commit/02cc8b32c06ca6b51806b33f6f707ca06e55e957) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Add experimental native persistent cache for Atlaspack V3.
|
|
8
|
+
|
|
9
|
+
- [#934](https://github.com/atlassian-labs/atlaspack/pull/934) [`02cc8b3`](https://github.com/atlassian-labs/atlaspack/commit/02cc8b32c06ca6b51806b33f6f707ca06e55e957) Thanks [@mattcompiles](https://github.com/mattcompiles)! - Add new Transformer `setup` method and deprecate `loadConfig`.
|
|
10
|
+
|
|
11
|
+
Atlaspack is moving to a pure Transformer model to improve caching performance and consistency.
|
|
12
|
+
The old `loadConfig` method which ran once per Asset goes against this behaviour is now deprecated.
|
|
13
|
+
The new `setup` method runs once per Transformer instance, allowing for better caching and performance optimizations.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`515149d`](https://github.com/atlassian-labs/atlaspack/commit/515149d0a0767d844af803efdc611646780ad0fe), [`02cc8b3`](https://github.com/atlassian-labs/atlaspack/commit/02cc8b32c06ca6b51806b33f6f707ca06e55e957), [`02cc8b3`](https://github.com/atlassian-labs/atlaspack/commit/02cc8b32c06ca6b51806b33f6f707ca06e55e957)]:
|
|
18
|
+
- @atlaspack/rust@3.16.0
|
|
19
|
+
- @atlaspack/feature-flags@2.27.5
|
|
20
|
+
- @atlaspack/cache@3.2.41
|
|
21
|
+
- @atlaspack/fs@2.15.41
|
|
22
|
+
- @atlaspack/logger@2.14.38
|
|
23
|
+
- @atlaspack/source-map@3.2.1
|
|
24
|
+
- @atlaspack/utils@3.2.7
|
|
25
|
+
- @atlaspack/graph@3.6.8
|
|
26
|
+
- @atlaspack/plugin@2.14.46
|
|
27
|
+
- @atlaspack/profiler@2.15.7
|
|
28
|
+
- @atlaspack/types@2.15.36
|
|
29
|
+
- @atlaspack/workers@2.14.46
|
|
30
|
+
- @atlaspack/package-manager@2.14.46
|
|
31
|
+
|
|
3
32
|
## 2.30.2
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/dist/Atlaspack.js
CHANGED
|
@@ -430,6 +430,7 @@ class Atlaspack {
|
|
|
430
430
|
return result;
|
|
431
431
|
},
|
|
432
432
|
unstable_requestStats: __classPrivateFieldGet(this, _Atlaspack_requestTracker, "f").flushStats(),
|
|
433
|
+
nativeCacheStats: await this.rustAtlaspack?.completeCacheSession(),
|
|
433
434
|
scopeHoistingStats,
|
|
434
435
|
};
|
|
435
436
|
// @ts-expect-error TS2345
|
|
@@ -452,6 +453,7 @@ class Atlaspack {
|
|
|
452
453
|
type: 'buildFailure',
|
|
453
454
|
diagnostics: Array.isArray(diagnostic) ? diagnostic : [diagnostic],
|
|
454
455
|
unstable_requestStats: __classPrivateFieldGet(this, _Atlaspack_requestTracker, "f").flushStats(),
|
|
456
|
+
nativeCacheStats: await this.rustAtlaspack?.completeCacheSession(),
|
|
455
457
|
};
|
|
456
458
|
// @ts-expect-error TS2345
|
|
457
459
|
await __classPrivateFieldGet(this, _Atlaspack_reporterRunner, "f").report(event);
|
package/dist/Transformation.js
CHANGED
|
@@ -60,6 +60,10 @@ const assert_1 = __importDefault(require("assert"));
|
|
|
60
60
|
const profiler_1 = require("@atlaspack/profiler");
|
|
61
61
|
const source_map_1 = __importDefault(require("@atlaspack/source-map"));
|
|
62
62
|
const feature_flags_1 = require("@atlaspack/feature-flags");
|
|
63
|
+
const build_cache_1 = require("@atlaspack/build-cache");
|
|
64
|
+
// Global setup config are not file-specific, so we only need to
|
|
65
|
+
// load them once per build.
|
|
66
|
+
const setupConfig = (0, build_cache_1.createBuildCache)();
|
|
63
67
|
class Transformation {
|
|
64
68
|
constructor({ request, options, config, workerApi }) {
|
|
65
69
|
this.configs = new Map();
|
|
@@ -357,11 +361,30 @@ class Transformation {
|
|
|
357
361
|
return nextPipeline;
|
|
358
362
|
}
|
|
359
363
|
async loadTransformerConfig(transformer, isSource) {
|
|
364
|
+
// Only load setup config for a transformer once per build.
|
|
365
|
+
let config = setupConfig.get(transformer.name);
|
|
366
|
+
if (config == null && transformer.plugin.setup != null) {
|
|
367
|
+
config = (0, InternalConfig_1.createConfig)({
|
|
368
|
+
plugin: transformer.name,
|
|
369
|
+
searchPath: (0, projectPath_1.toProjectPathUnsafe)('index'),
|
|
370
|
+
// Consider project setup config as source
|
|
371
|
+
isSource: true,
|
|
372
|
+
});
|
|
373
|
+
await (0, ConfigRequest_1.loadPluginSetup)(transformer.name, transformer.plugin.setup, config, this.options);
|
|
374
|
+
setupConfig.set(transformer.name, config);
|
|
375
|
+
}
|
|
376
|
+
if (config != null) {
|
|
377
|
+
for (let devDep of config.devDeps) {
|
|
378
|
+
await this.addDevDependency(devDep);
|
|
379
|
+
}
|
|
380
|
+
// `loadConfig` is not called for setup configs
|
|
381
|
+
return config;
|
|
382
|
+
}
|
|
360
383
|
let loadConfig = transformer.plugin.loadConfig;
|
|
361
384
|
if (!loadConfig) {
|
|
362
385
|
return;
|
|
363
386
|
}
|
|
364
|
-
|
|
387
|
+
config = (0, InternalConfig_1.createConfig)({
|
|
365
388
|
plugin: transformer.name,
|
|
366
389
|
isSource,
|
|
367
390
|
// @ts-expect-error TS2322
|
|
@@ -45,6 +45,16 @@ class NapiWorkerPool {
|
|
|
45
45
|
__classPrivateFieldGet(this, _NapiWorkerPool_napiWorkers, "f").push(new Promise((res) => worker.once('message', res)));
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
+
clearAllWorkerState() {
|
|
49
|
+
return Promise.all(__classPrivateFieldGet(this, _NapiWorkerPool_workers, "f").map((worker) => new Promise((res) => {
|
|
50
|
+
worker.postMessage('clearState');
|
|
51
|
+
worker.once('message', (message) => {
|
|
52
|
+
if (message == 'stateCleared') {
|
|
53
|
+
res();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
})));
|
|
57
|
+
}
|
|
48
58
|
workerCount() {
|
|
49
59
|
return __classPrivateFieldGet(this, _NapiWorkerPool_workerCount, "f");
|
|
50
60
|
}
|
|
@@ -13,32 +13,20 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
|
-
var
|
|
16
|
+
var _PluginConfig_inner;
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.PluginConfig = void 0;
|
|
19
19
|
const Config_1 = __importDefault(require("../../../public/Config"));
|
|
20
|
+
const InternalConfig_1 = require("../../../InternalConfig");
|
|
20
21
|
class PluginConfig {
|
|
21
|
-
constructor(
|
|
22
|
-
// @ts-expect-error TS2564
|
|
23
|
-
_PluginConfig_projectRoot.set(this, void 0);
|
|
22
|
+
constructor(configOpts, options) {
|
|
24
23
|
_PluginConfig_inner.set(this, void 0);
|
|
25
|
-
|
|
26
|
-
this.isSource = isSource;
|
|
27
|
-
this.searchPath = searchPath;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
invalidateOnConfigKeyChange: [],
|
|
32
|
-
invalidateOnFileCreate: [],
|
|
33
|
-
invalidateOnFileChange: new Set(),
|
|
34
|
-
devDeps: [],
|
|
35
|
-
searchPath: searchPath.replace(projectRoot + '/', ''),
|
|
36
|
-
}, {
|
|
37
|
-
projectRoot,
|
|
38
|
-
inputFS: fs,
|
|
39
|
-
outputFS: fs,
|
|
40
|
-
packageManager,
|
|
41
|
-
}), "f");
|
|
24
|
+
let internalConfig = (0, InternalConfig_1.createConfig)(configOpts);
|
|
25
|
+
this.isSource = internalConfig.isSource;
|
|
26
|
+
this.searchPath = internalConfig.searchPath;
|
|
27
|
+
// @ts-expect-error TS2564
|
|
28
|
+
this.env = internalConfig.env;
|
|
29
|
+
__classPrivateFieldSet(this, _PluginConfig_inner, new Config_1.default(internalConfig, options), "f");
|
|
42
30
|
}
|
|
43
31
|
// eslint-disable-next-line no-unused-vars
|
|
44
32
|
invalidateOnFileChange(filePath) { }
|
|
@@ -73,4 +61,4 @@ class PluginConfig {
|
|
|
73
61
|
}
|
|
74
62
|
}
|
|
75
63
|
exports.PluginConfig = PluginConfig;
|
|
76
|
-
|
|
64
|
+
_PluginConfig_inner = new WeakMap();
|
|
@@ -25,18 +25,21 @@ class PluginOptions {
|
|
|
25
25
|
if (!('projectRoot' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
26
26
|
throw new Error('PluginOptions.projectRoot');
|
|
27
27
|
}
|
|
28
|
+
this.used = true;
|
|
28
29
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").projectRoot;
|
|
29
30
|
}
|
|
30
31
|
get packageManager() {
|
|
31
32
|
if (!('packageManager' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
32
33
|
throw new Error('PluginOptions.packageManager');
|
|
33
34
|
}
|
|
35
|
+
this.used = true;
|
|
34
36
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").packageManager;
|
|
35
37
|
}
|
|
36
38
|
get mode() {
|
|
37
39
|
if (!('mode' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
38
40
|
throw new Error('PluginOptions.mode');
|
|
39
41
|
}
|
|
42
|
+
this.used = true;
|
|
40
43
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").mode;
|
|
41
44
|
}
|
|
42
45
|
get parcelVersion() {
|
|
@@ -44,30 +47,35 @@ class PluginOptions {
|
|
|
44
47
|
return 'UNKNOWN VERSION';
|
|
45
48
|
// throw new Error('PluginOptions.parcelVersion');
|
|
46
49
|
}
|
|
50
|
+
this.used = true;
|
|
47
51
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").parcelVersion;
|
|
48
52
|
}
|
|
49
53
|
get hmrOptions() {
|
|
50
54
|
if (!('hmrOptions' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
51
55
|
throw new Error('PluginOptions.hmrOptions');
|
|
52
56
|
}
|
|
57
|
+
this.used = true;
|
|
53
58
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").hmrOptions;
|
|
54
59
|
}
|
|
55
60
|
get serveOptions() {
|
|
56
61
|
if (!('serveOptions' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
57
62
|
throw new Error('PluginOptions.serveOptions');
|
|
58
63
|
}
|
|
64
|
+
this.used = true;
|
|
59
65
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").serveOptions;
|
|
60
66
|
}
|
|
61
67
|
get shouldBuildLazily() {
|
|
62
68
|
if (!('shouldBuildLazily' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
63
69
|
throw new Error('PluginOptions.shouldBuildLazily');
|
|
64
70
|
}
|
|
71
|
+
this.used = true;
|
|
65
72
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").shouldBuildLazily;
|
|
66
73
|
}
|
|
67
74
|
get shouldAutoInstall() {
|
|
68
75
|
if (!('shouldAutoInstall' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
69
76
|
throw new Error('PluginOptions.shouldAutoInstall');
|
|
70
77
|
}
|
|
78
|
+
this.used = true;
|
|
71
79
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").shouldAutoInstall;
|
|
72
80
|
}
|
|
73
81
|
get logLevel() {
|
|
@@ -80,40 +88,47 @@ class PluginOptions {
|
|
|
80
88
|
if (!('cacheDir' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
81
89
|
throw new Error('PluginOptions.cacheDir');
|
|
82
90
|
}
|
|
91
|
+
this.used = true;
|
|
83
92
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").cacheDir;
|
|
84
93
|
}
|
|
85
94
|
get inputFS() {
|
|
86
95
|
if (!('inputFS' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
87
96
|
throw new Error('PluginOptions.inputFS');
|
|
88
97
|
}
|
|
98
|
+
this.used = true;
|
|
89
99
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").inputFS;
|
|
90
100
|
}
|
|
91
101
|
get outputFS() {
|
|
92
102
|
if (!('outputFS' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
93
103
|
throw new Error('PluginOptions.outputFS');
|
|
94
104
|
}
|
|
105
|
+
this.used = true;
|
|
95
106
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").outputFS;
|
|
96
107
|
}
|
|
97
108
|
get instanceId() {
|
|
98
109
|
if (!('instanceId' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
99
110
|
throw new Error('PluginOptions.instanceId');
|
|
100
111
|
}
|
|
112
|
+
this.used = true;
|
|
101
113
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").instanceId;
|
|
102
114
|
}
|
|
103
115
|
get detailedReport() {
|
|
104
116
|
if (!('detailedReport' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
105
117
|
throw new Error('PluginOptions.detailedReport');
|
|
106
118
|
}
|
|
119
|
+
this.used = true;
|
|
107
120
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").detailedReport;
|
|
108
121
|
}
|
|
109
122
|
get featureFlags() {
|
|
110
123
|
if (!('featureFlags' in __classPrivateFieldGet(this, _PluginOptions_options, "f"))) {
|
|
111
124
|
throw new Error('PluginOptions.featureFlags');
|
|
112
125
|
}
|
|
126
|
+
this.used = true;
|
|
113
127
|
return __classPrivateFieldGet(this, _PluginOptions_options, "f").featureFlags;
|
|
114
128
|
}
|
|
115
129
|
constructor(options) {
|
|
116
130
|
_PluginOptions_options.set(this, void 0);
|
|
131
|
+
this.used = false;
|
|
117
132
|
// @ts-expect-error TS2322
|
|
118
133
|
__classPrivateFieldSet(this, _PluginOptions_options, options, "f");
|
|
119
134
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultDetector = exports.SideEffectDetector = void 0;
|
|
4
|
+
const async_hooks_1 = require("async_hooks");
|
|
5
|
+
/**
|
|
6
|
+
* Side effect detector using AsyncLocalStorage to track filesystem and environment variable
|
|
7
|
+
* access across concurrent async operations in a Node.js worker thread.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const detector = new SideEffectDetector();
|
|
11
|
+
* detector.install();
|
|
12
|
+
*
|
|
13
|
+
* const [result, sideEffects] = await detector.monitorSideEffects(async () => {
|
|
14
|
+
* return await someOperation();
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* console.log(sideEffects.fsUsage); // Array of filesystem accesses
|
|
18
|
+
* console.log(sideEffects.envUsage); // Array of environment variable accesses
|
|
19
|
+
*/
|
|
20
|
+
class SideEffectDetector {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.asyncStorage = new async_hooks_1.AsyncLocalStorage();
|
|
23
|
+
this.patchesInstalled = false;
|
|
24
|
+
this.originalMethods = {};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Install global patches for filesystem and environment variable monitoring.
|
|
28
|
+
* This should be called once when the worker starts up.
|
|
29
|
+
*/
|
|
30
|
+
install() {
|
|
31
|
+
if (this.patchesInstalled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._patchFilesystem();
|
|
35
|
+
this._patchProcessEnv();
|
|
36
|
+
this.patchesInstalled = true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Monitor side effects for an async operation.
|
|
40
|
+
*
|
|
41
|
+
* @param {Function} fn - Async function to monitor
|
|
42
|
+
* @param {Object} options - Optional configuration
|
|
43
|
+
* @param {string} options.label - Optional label for debugging
|
|
44
|
+
* @returns {Promise<[any, SideEffects]>} Tuple of [result, sideEffects]
|
|
45
|
+
*/
|
|
46
|
+
monitorSideEffects(packageName, fn) {
|
|
47
|
+
if (!this.patchesInstalled) {
|
|
48
|
+
throw new Error('SideEffectDetector: install() must be called before monitorSideEffects()');
|
|
49
|
+
}
|
|
50
|
+
const context = {
|
|
51
|
+
fsUsage: [],
|
|
52
|
+
envUsage: {
|
|
53
|
+
vars: new Set(),
|
|
54
|
+
didEnumerate: false,
|
|
55
|
+
},
|
|
56
|
+
packageName: packageName,
|
|
57
|
+
};
|
|
58
|
+
return this.asyncStorage.run(context, async () => {
|
|
59
|
+
const result = await fn();
|
|
60
|
+
return [result, context];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the current monitoring context, if any.
|
|
65
|
+
* Useful for debugging or custom instrumentation.
|
|
66
|
+
*
|
|
67
|
+
* @returns {Object|null} Current context or null if not monitoring
|
|
68
|
+
*/
|
|
69
|
+
getCurrentContext() {
|
|
70
|
+
return this.asyncStorage.getStore() || null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if currently monitoring side effects.
|
|
74
|
+
*
|
|
75
|
+
* @returns {boolean}
|
|
76
|
+
*/
|
|
77
|
+
isMonitoring() {
|
|
78
|
+
return this.asyncStorage.getStore() !== undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Patch filesystem methods to record access.
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
_patchFilesystem() {
|
|
85
|
+
// Inline require this to avoid babel transformer issue
|
|
86
|
+
const fs = require('fs');
|
|
87
|
+
const methodsToPatch = [
|
|
88
|
+
// Sync methods
|
|
89
|
+
'readFileSync',
|
|
90
|
+
'writeFileSync',
|
|
91
|
+
'appendFileSync',
|
|
92
|
+
'existsSync',
|
|
93
|
+
'statSync',
|
|
94
|
+
'lstatSync',
|
|
95
|
+
'readdirSync',
|
|
96
|
+
'mkdirSync',
|
|
97
|
+
'rmdirSync',
|
|
98
|
+
'unlinkSync',
|
|
99
|
+
'copyFileSync',
|
|
100
|
+
'renameSync',
|
|
101
|
+
'chmodSync',
|
|
102
|
+
'chownSync',
|
|
103
|
+
// Async methods
|
|
104
|
+
'readFile',
|
|
105
|
+
'writeFile',
|
|
106
|
+
'appendFile',
|
|
107
|
+
'stat',
|
|
108
|
+
'lstat',
|
|
109
|
+
'readdir',
|
|
110
|
+
'mkdir',
|
|
111
|
+
'rmdir',
|
|
112
|
+
'unlink',
|
|
113
|
+
'copyFile',
|
|
114
|
+
'rename',
|
|
115
|
+
'chmod',
|
|
116
|
+
'chown',
|
|
117
|
+
];
|
|
118
|
+
methodsToPatch.forEach((method) => {
|
|
119
|
+
if (typeof fs[method] === 'function') {
|
|
120
|
+
this.originalMethods[method] = fs[method];
|
|
121
|
+
const self = this;
|
|
122
|
+
// @ts-expect-error Dynamic method patching
|
|
123
|
+
fs[method] = function (path, ...args) {
|
|
124
|
+
// Record filesystem access in current context
|
|
125
|
+
const context = self.asyncStorage.getStore();
|
|
126
|
+
if (context) {
|
|
127
|
+
context.fsUsage.push({
|
|
128
|
+
method,
|
|
129
|
+
path: typeof path === 'string' ? path : path?.toString(),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return self.originalMethods[method].call(this, path, ...args);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Handle fs.promises methods
|
|
137
|
+
if (fs.promises) {
|
|
138
|
+
const promiseMethodsToPatch = [
|
|
139
|
+
'readFile',
|
|
140
|
+
'writeFile',
|
|
141
|
+
'appendFile',
|
|
142
|
+
'stat',
|
|
143
|
+
'lstat',
|
|
144
|
+
'readdir',
|
|
145
|
+
'mkdir',
|
|
146
|
+
'rmdir',
|
|
147
|
+
'unlink',
|
|
148
|
+
'copyFile',
|
|
149
|
+
'rename',
|
|
150
|
+
'chmod',
|
|
151
|
+
'chown',
|
|
152
|
+
];
|
|
153
|
+
const promises = fs.promises;
|
|
154
|
+
promiseMethodsToPatch.forEach((method) => {
|
|
155
|
+
if (typeof promises[method] === 'function') {
|
|
156
|
+
const originalKey = `promises_${method}`;
|
|
157
|
+
this.originalMethods[originalKey] = promises[method];
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
159
|
+
const self = this;
|
|
160
|
+
promises[method] = function (path, ...args) {
|
|
161
|
+
const context = self.asyncStorage.getStore();
|
|
162
|
+
if (context) {
|
|
163
|
+
context.fsUsage.push({
|
|
164
|
+
method: `promises.${method}`,
|
|
165
|
+
path: typeof path === 'string' ? path : String(path),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return self.originalMethods[originalKey].call(this, path, ...args);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Patch process.env to record environment variable access.
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
_patchProcessEnv() {
|
|
179
|
+
if (this.originalMethods.processEnv) {
|
|
180
|
+
return; // Already patched
|
|
181
|
+
}
|
|
182
|
+
this.originalMethods.processEnv = process.env;
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
184
|
+
const self = this;
|
|
185
|
+
// The following environment variables are allowed to be accessed by transformers
|
|
186
|
+
const allowedVars = new Set([
|
|
187
|
+
'ATLASPACK_ENABLE_SENTRY',
|
|
188
|
+
// TODO we should also add the other atlaspack env vars here
|
|
189
|
+
'NODE_V8_COVERAGE',
|
|
190
|
+
'VSCODE_INSPECTOR_OPTIONS',
|
|
191
|
+
'NODE_INSPECTOR_IPC',
|
|
192
|
+
'FORCE_COLOR',
|
|
193
|
+
'NO_COLOR',
|
|
194
|
+
'TTY',
|
|
195
|
+
]);
|
|
196
|
+
// Create a proxy that intercepts property access
|
|
197
|
+
process.env = new Proxy(this.originalMethods.processEnv, {
|
|
198
|
+
get(target, property) {
|
|
199
|
+
const context = self.asyncStorage.getStore();
|
|
200
|
+
if (context && typeof property === 'string') {
|
|
201
|
+
// Only record if this is a real environment variable access
|
|
202
|
+
// (not internal properties like 'constructor', 'valueOf', etc.)
|
|
203
|
+
if (!allowedVars.has(property) &&
|
|
204
|
+
(property in target || !property.startsWith('_'))) {
|
|
205
|
+
context.envUsage.vars.add(property);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return target[property];
|
|
209
|
+
},
|
|
210
|
+
set(target, property, value) {
|
|
211
|
+
const context = self.asyncStorage.getStore();
|
|
212
|
+
if (context && typeof property === 'string') {
|
|
213
|
+
if (!allowedVars.has(property) && property in target) {
|
|
214
|
+
context.envUsage.vars.add(property);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
target[property] = value;
|
|
218
|
+
return true;
|
|
219
|
+
},
|
|
220
|
+
has(target, property) {
|
|
221
|
+
const context = self.asyncStorage.getStore();
|
|
222
|
+
if (context && typeof property === 'string') {
|
|
223
|
+
if (!allowedVars.has(property) && property in target) {
|
|
224
|
+
context.envUsage.vars.add(property);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return property in target;
|
|
228
|
+
},
|
|
229
|
+
ownKeys(target) {
|
|
230
|
+
const context = self.asyncStorage.getStore();
|
|
231
|
+
if (context) {
|
|
232
|
+
context.envUsage.didEnumerate = true;
|
|
233
|
+
}
|
|
234
|
+
return Object.keys(target);
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.SideEffectDetector = SideEffectDetector;
|
|
240
|
+
/**
|
|
241
|
+
* Default instance for convenience. Most workers will only need one detector.
|
|
242
|
+
*/
|
|
243
|
+
exports.defaultDetector = new SideEffectDetector();
|