@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.
- 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 +14 -14
- 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
|
@@ -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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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:
|
|
152
|
-
|
|
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
|
-
|
|
206
|
-
|
|
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
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
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(
|
|
222
|
-
|
|
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:
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
|
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
|
-
|
|
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;
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import type { Config as IPluginConfig, DevDepOptions, FilePath, Environment, FileCreateInvalidation, ConfigResultWithFilePath, PackageJSON
|
|
2
|
-
import
|
|
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(
|
|
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;
|