@octanejs/tanstack-start 0.1.27 → 0.1.29
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/README.md +19 -1
- package/THIRD_PARTY_NOTICES.md +6 -5
- package/package.json +18 -6
- package/src/client-only-server-strip-loader.js +7 -0
- package/src/client-only-server-strip.js +34 -25
- package/src/internal/README.md +7 -7
- package/src/internal/start-plugin-core/rsbuild/import-protection.d.ts +27 -0
- package/src/internal/start-plugin-core/rsbuild/import-protection.js +1193 -0
- package/src/internal/start-plugin-core/rsbuild/index.d.ts +9 -0
- package/src/internal/start-plugin-core/rsbuild/index.js +3 -0
- package/src/internal/start-plugin-core/rsbuild/normalized-client-build.d.ts +20 -0
- package/src/internal/start-plugin-core/rsbuild/normalized-client-build.js +261 -0
- package/src/internal/start-plugin-core/rsbuild/planning.d.ts +56 -0
- package/src/internal/start-plugin-core/rsbuild/planning.js +173 -0
- package/src/internal/start-plugin-core/rsbuild/plugin.d.ts +7 -0
- package/src/internal/start-plugin-core/rsbuild/plugin.js +504 -0
- package/src/internal/start-plugin-core/rsbuild/post-build.d.ts +10 -0
- package/src/internal/start-plugin-core/rsbuild/post-build.js +59 -0
- package/src/internal/start-plugin-core/rsbuild/schema.d.ts +2441 -0
- package/src/internal/start-plugin-core/rsbuild/schema.js +28 -0
- package/src/internal/start-plugin-core/rsbuild/server-middleware.d.ts +32 -0
- package/src/internal/start-plugin-core/rsbuild/server-middleware.js +139 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-host.d.ts +36 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-host.js +322 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-metadata-loader.d.ts +10 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-metadata-loader.js +12 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-metadata.d.ts +14 -0
- package/src/internal/start-plugin-core/rsbuild/start-compiler-metadata.js +5 -0
- package/src/internal/start-plugin-core/rsbuild/start-router-plugin.d.ts +19 -0
- package/src/internal/start-plugin-core/rsbuild/start-router-plugin.js +69 -0
- package/src/internal/start-plugin-core/rsbuild/swc-rsc.d.ts +17 -0
- package/src/internal/start-plugin-core/rsbuild/swc-rsc.js +118 -0
- package/src/internal/start-plugin-core/rsbuild/types.d.ts +17 -0
- package/src/internal/start-plugin-core/rsbuild/types.js +0 -0
- package/src/internal/start-plugin-core/rsbuild/virtual-modules.d.ts +60 -0
- package/src/internal/start-plugin-core/rsbuild/virtual-modules.js +359 -0
- package/src/plugin-rsbuild.d.ts +25 -0
- package/src/plugin-rsbuild.js +79 -0
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
import { normalizePath } from '../utils.js';
|
|
2
|
+
import {
|
|
3
|
+
buildResolutionCandidates,
|
|
4
|
+
buildSourceCandidates,
|
|
5
|
+
canonicalizeResolvedId,
|
|
6
|
+
checkFileDenial,
|
|
7
|
+
clearNormalizeFilePathCache,
|
|
8
|
+
dedupePatterns,
|
|
9
|
+
dedupeViolationKey,
|
|
10
|
+
isFileExcluded,
|
|
11
|
+
normalizeFilePath,
|
|
12
|
+
} from '../import-protection/utils.js';
|
|
13
|
+
import { ImportGraph, buildTrace, formatViolation } from '../import-protection/trace.js';
|
|
14
|
+
import {
|
|
15
|
+
getDefaultImportProtectionRules,
|
|
16
|
+
getMarkerSpecifiers,
|
|
17
|
+
} from '../import-protection/defaults.js';
|
|
18
|
+
import { compileMatchers, matchesAny } from '../import-protection/matchers.js';
|
|
19
|
+
import {
|
|
20
|
+
getImportProtectionEnvType,
|
|
21
|
+
getImportProtectionRelativePath,
|
|
22
|
+
getImportProtectionRulesForEnvironment,
|
|
23
|
+
shouldCheckImportProtectionImporter,
|
|
24
|
+
} from '../import-protection/adapterUtils.js';
|
|
25
|
+
import {
|
|
26
|
+
ImportLocCache,
|
|
27
|
+
addTraceImportLocations,
|
|
28
|
+
buildCodeSnippet,
|
|
29
|
+
buildLineIndex,
|
|
30
|
+
createImportSpecifierLocationIndex,
|
|
31
|
+
findImportStatementLocationFromTransformed,
|
|
32
|
+
findOriginalUsageLocation,
|
|
33
|
+
findPostCompileUsageLocation,
|
|
34
|
+
getOrCreateOriginalTransformResult,
|
|
35
|
+
indexToLineColumn,
|
|
36
|
+
normalizeSourceMap,
|
|
37
|
+
pickOriginalCodeFromSourcesContent,
|
|
38
|
+
} from '../import-protection/sourceLocation.js';
|
|
39
|
+
import {
|
|
40
|
+
findOriginalUnsafeUsagePosFromResult,
|
|
41
|
+
getImportSources,
|
|
42
|
+
getImportSourcesFromResult,
|
|
43
|
+
getMockExportNamesBySourceFromResult,
|
|
44
|
+
getNamedExportsFromResult,
|
|
45
|
+
} from '../import-protection/analysis.js';
|
|
46
|
+
import { rewriteDeniedImports } from '../import-protection/rewrite.js';
|
|
47
|
+
import { ExtensionlessAbsoluteIdResolver } from '../import-protection/extensionlessAbsoluteIdResolver.js';
|
|
48
|
+
import {
|
|
49
|
+
generateDevSelfDenialModule,
|
|
50
|
+
generateSelfContainedMockModule,
|
|
51
|
+
loadMockEdgeModule,
|
|
52
|
+
loadMockRuntimeModule,
|
|
53
|
+
loadSilentMockModule,
|
|
54
|
+
} from '../import-protection/virtualModules.js';
|
|
55
|
+
import { extname, resolve } from 'node:path';
|
|
56
|
+
import { writeFileSync } from 'node:fs';
|
|
57
|
+
//#region src/rsbuild/import-protection.ts
|
|
58
|
+
var importSpecifierLocationIndex = createImportSpecifierLocationIndex();
|
|
59
|
+
function isPerfEnabled() {
|
|
60
|
+
return (
|
|
61
|
+
process.env.TSR_IMPORT_PROTECTION_PERF === '1' ||
|
|
62
|
+
process.env.TSR_IMPORT_PROTECTION_PERF === 'true'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function createPerfCollector() {
|
|
66
|
+
const counters = /* @__PURE__ */ new Map();
|
|
67
|
+
const timings = /* @__PURE__ */ new Map();
|
|
68
|
+
const flushEnvironments = /* @__PURE__ */ new Set();
|
|
69
|
+
let flushCount = 0;
|
|
70
|
+
return {
|
|
71
|
+
count(name, value = 1) {
|
|
72
|
+
counters.set(name, (counters.get(name) ?? 0) + value);
|
|
73
|
+
},
|
|
74
|
+
time(name, startedAt) {
|
|
75
|
+
const duration = performance.now() - startedAt;
|
|
76
|
+
const timing = timings.get(name);
|
|
77
|
+
if (timing) {
|
|
78
|
+
timing.count++;
|
|
79
|
+
timing.totalMs += duration;
|
|
80
|
+
timing.maxMs = Math.max(timing.maxMs, duration);
|
|
81
|
+
} else
|
|
82
|
+
timings.set(name, {
|
|
83
|
+
count: 1,
|
|
84
|
+
totalMs: duration,
|
|
85
|
+
maxMs: duration,
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
flush(root, envName, phase) {
|
|
89
|
+
flushCount++;
|
|
90
|
+
flushEnvironments.add(envName);
|
|
91
|
+
const payload = {
|
|
92
|
+
adapter: 'rsbuild',
|
|
93
|
+
root,
|
|
94
|
+
phase,
|
|
95
|
+
flushCount,
|
|
96
|
+
flushEnvironments: Array.from(flushEnvironments).sort(),
|
|
97
|
+
counters: Object.fromEntries(counters),
|
|
98
|
+
timings: Object.fromEntries(
|
|
99
|
+
Array.from(timings, ([name, timing]) => [
|
|
100
|
+
name,
|
|
101
|
+
{
|
|
102
|
+
count: timing.count,
|
|
103
|
+
totalMs: Number(timing.totalMs.toFixed(3)),
|
|
104
|
+
avgMs: Number((timing.totalMs / timing.count).toFixed(3)),
|
|
105
|
+
maxMs: Number(timing.maxMs.toFixed(3)),
|
|
106
|
+
},
|
|
107
|
+
]),
|
|
108
|
+
),
|
|
109
|
+
};
|
|
110
|
+
const file = process.env.TSR_IMPORT_PROTECTION_PERF_FILE;
|
|
111
|
+
if (file) writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`);
|
|
112
|
+
else console.warn(`[import-protection:perf] ${JSON.stringify(payload, null, 2)}`);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
var IMPORT_PROTECTION_VIRTUAL_DIR = 'node_modules/.virtual/import-protection';
|
|
117
|
+
var MOCK_EDGE_FILE_PREFIX = 'mock-edge-';
|
|
118
|
+
var MOCK_RUNTIME_FILE_PREFIX = 'mock-runtime-';
|
|
119
|
+
var MOCK_SILENT_FILE = 'mock-silent.mjs';
|
|
120
|
+
function toBase64Url(input) {
|
|
121
|
+
return Buffer.from(JSON.stringify(input), 'utf8').toString('base64url');
|
|
122
|
+
}
|
|
123
|
+
function fromBase64Url(input) {
|
|
124
|
+
return JSON.parse(Buffer.from(input, 'base64url').toString('utf8'));
|
|
125
|
+
}
|
|
126
|
+
function getRulesForEnvironment(config, envName) {
|
|
127
|
+
return getImportProtectionRulesForEnvironment(config, envName);
|
|
128
|
+
}
|
|
129
|
+
function serializePattern(pattern) {
|
|
130
|
+
return typeof pattern === 'string' ? pattern : pattern.toString();
|
|
131
|
+
}
|
|
132
|
+
function dedupeKey(info) {
|
|
133
|
+
return dedupeViolationKey(info);
|
|
134
|
+
}
|
|
135
|
+
function getRsbuildResolvedImportProtectionCheck(relativeResolved, matchers) {
|
|
136
|
+
if (isFileExcluded(relativeResolved, matchers)) return;
|
|
137
|
+
const fileMatch = matchers.files.find((matcher) => matcher.test(relativeResolved));
|
|
138
|
+
if (fileMatch)
|
|
139
|
+
return {
|
|
140
|
+
type: 'file',
|
|
141
|
+
fileMatch,
|
|
142
|
+
};
|
|
143
|
+
return { type: 'marker' };
|
|
144
|
+
}
|
|
145
|
+
function getOrCreateEnvState(envStates, envName) {
|
|
146
|
+
let env = envStates.get(envName);
|
|
147
|
+
if (!env) {
|
|
148
|
+
env = {
|
|
149
|
+
resolveCache: /* @__PURE__ */ new Map(),
|
|
150
|
+
seenViolations: /* @__PURE__ */ new Set(),
|
|
151
|
+
buildTransformResults: /* @__PURE__ */ new Map(),
|
|
152
|
+
deferredFileViolations: [],
|
|
153
|
+
deferredFileViolationKeys: /* @__PURE__ */ new Set(),
|
|
154
|
+
};
|
|
155
|
+
envStates.set(envName, env);
|
|
156
|
+
}
|
|
157
|
+
return env;
|
|
158
|
+
}
|
|
159
|
+
function getVirtualModulePath(root, envName, filename) {
|
|
160
|
+
return normalizePath(resolve(root, IMPORT_PROTECTION_VIRTUAL_DIR, envName, filename));
|
|
161
|
+
}
|
|
162
|
+
function queuePendingWrite(shared, envName, filePath, code) {
|
|
163
|
+
let writes = shared.pendingWrites.get(envName);
|
|
164
|
+
if (!writes) {
|
|
165
|
+
writes = /* @__PURE__ */ new Map();
|
|
166
|
+
shared.pendingWrites.set(envName, writes);
|
|
167
|
+
}
|
|
168
|
+
writes.set(filePath, code);
|
|
169
|
+
}
|
|
170
|
+
function tryWriteVirtualModule(shared, envName, filePath, code) {
|
|
171
|
+
if (shared.virtualModules.get(filePath) === code) return filePath;
|
|
172
|
+
shared.virtualModules.set(filePath, code);
|
|
173
|
+
const vmPlugin = shared.vmPlugins[envName];
|
|
174
|
+
if (!vmPlugin || !shared.readyVmPlugins[envName]) {
|
|
175
|
+
queuePendingWrite(shared, envName, filePath, code);
|
|
176
|
+
return filePath;
|
|
177
|
+
}
|
|
178
|
+
vmPlugin.writeModule(filePath, code);
|
|
179
|
+
return filePath;
|
|
180
|
+
}
|
|
181
|
+
function flushPendingWrites(shared, envName) {
|
|
182
|
+
const writes = shared.pendingWrites.get(envName);
|
|
183
|
+
if (!writes?.size || !shared.readyVmPlugins[envName]) return;
|
|
184
|
+
for (const [filePath, code] of writes) {
|
|
185
|
+
shared.vmPlugins[envName]?.writeModule(filePath, code);
|
|
186
|
+
writes.delete(filePath);
|
|
187
|
+
}
|
|
188
|
+
if (writes.size === 0) shared.pendingWrites.delete(envName);
|
|
189
|
+
}
|
|
190
|
+
function ensureSilentMockModule(shared, envName) {
|
|
191
|
+
return tryWriteVirtualModule(
|
|
192
|
+
shared,
|
|
193
|
+
envName,
|
|
194
|
+
getVirtualModulePath(shared.root, envName, MOCK_SILENT_FILE),
|
|
195
|
+
loadSilentMockModule().code,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function ensureRuntimeMockModule(opts) {
|
|
199
|
+
const encoded = toBase64Url({
|
|
200
|
+
mode: opts.mode,
|
|
201
|
+
env: opts.env,
|
|
202
|
+
importer: opts.importer,
|
|
203
|
+
specifier: opts.specifier,
|
|
204
|
+
trace: [],
|
|
205
|
+
});
|
|
206
|
+
return tryWriteVirtualModule(
|
|
207
|
+
opts.shared,
|
|
208
|
+
opts.envName,
|
|
209
|
+
getVirtualModulePath(
|
|
210
|
+
opts.shared.root,
|
|
211
|
+
opts.envName,
|
|
212
|
+
`${MOCK_RUNTIME_FILE_PREFIX}${encoded}.mjs`,
|
|
213
|
+
),
|
|
214
|
+
loadMockRuntimeModule(encoded).code,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
function ensureMockEdgeModule(opts) {
|
|
218
|
+
const encoded = toBase64Url(opts.payload);
|
|
219
|
+
return tryWriteVirtualModule(
|
|
220
|
+
opts.shared,
|
|
221
|
+
opts.envName,
|
|
222
|
+
getVirtualModulePath(opts.shared.root, opts.envName, `${MOCK_EDGE_FILE_PREFIX}${encoded}.mjs`),
|
|
223
|
+
loadMockEdgeModule(encoded).code,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
function getMockEdgePayloadFromFile(filePath) {
|
|
227
|
+
const match = /(?:^|[\\/])mock-edge-([^/\\]+)\.mjs$/.exec(filePath);
|
|
228
|
+
if (!match) return;
|
|
229
|
+
try {
|
|
230
|
+
return fromBase64Url(match[1]);
|
|
231
|
+
} catch {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function loadOriginalCode(cache, file, loader) {
|
|
236
|
+
let result = cache.get(file);
|
|
237
|
+
if (!result) {
|
|
238
|
+
result = loader(file);
|
|
239
|
+
cache.set(file, result);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
async function loadOriginalCodeFromInputFileSystem(inputFileSystem, file) {
|
|
244
|
+
return new Promise((resolve) => {
|
|
245
|
+
inputFileSystem.readFile(file, (error, data) => {
|
|
246
|
+
if (error || data == null) {
|
|
247
|
+
resolve(void 0);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
resolve(typeof data === 'string' ? data : data.toString('utf8'));
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async function resolveAgainstImporter(opts) {
|
|
255
|
+
const importerDir = opts.ctx.context ?? opts.importerId.replace(/[/\\][^/\\]*$/, '');
|
|
256
|
+
const cacheKey = `${normalizeFilePath(importerDir)}:${opts.source}`;
|
|
257
|
+
if (opts.envState.resolveCache.has(cacheKey)) {
|
|
258
|
+
opts.perf?.count('resolve.cached');
|
|
259
|
+
return opts.envState.resolveCache.get(cacheKey) ?? null;
|
|
260
|
+
}
|
|
261
|
+
const startedAt = opts.perf ? performance.now() : 0;
|
|
262
|
+
opts.perf?.count('resolve.calls');
|
|
263
|
+
const resolved = await new Promise((resolve, reject) => {
|
|
264
|
+
opts.ctx.resolve(importerDir, opts.source, (error, result) => {
|
|
265
|
+
if (error) {
|
|
266
|
+
reject(error);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
resolve(typeof result === 'string' ? result : null);
|
|
270
|
+
});
|
|
271
|
+
})
|
|
272
|
+
.catch(() => null)
|
|
273
|
+
.finally(() => {
|
|
274
|
+
if (opts.perf) opts.perf.time('resolve', startedAt);
|
|
275
|
+
});
|
|
276
|
+
if (!resolved) {
|
|
277
|
+
opts.envState.resolveCache.set(cacheKey, null);
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const canonical = canonicalizeResolvedId(resolved, opts.config.root, (value) =>
|
|
281
|
+
opts.extensionlessResolver.resolve(value),
|
|
282
|
+
);
|
|
283
|
+
opts.envState.resolveCache.set(cacheKey, canonical);
|
|
284
|
+
return canonical;
|
|
285
|
+
}
|
|
286
|
+
function getModuleResource(module) {
|
|
287
|
+
const candidate = module;
|
|
288
|
+
return (
|
|
289
|
+
candidate.nameForCondition() ??
|
|
290
|
+
candidate.resourceResolveData?.resource ??
|
|
291
|
+
candidate.resource ??
|
|
292
|
+
candidate.userRequest ??
|
|
293
|
+
candidate.request
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
function getModuleFile(module) {
|
|
297
|
+
return normalizeFilePath(getModuleResource(module) ?? module.identifier());
|
|
298
|
+
}
|
|
299
|
+
var IMPORT_PROTECTION_PARSEABLE_EXTENSIONS = new Set([
|
|
300
|
+
'.ts',
|
|
301
|
+
'.tsx',
|
|
302
|
+
'.mts',
|
|
303
|
+
'.cts',
|
|
304
|
+
'.js',
|
|
305
|
+
'.jsx',
|
|
306
|
+
'.mjs',
|
|
307
|
+
'.cjs',
|
|
308
|
+
]);
|
|
309
|
+
function isImportProtectionSourceFile(file) {
|
|
310
|
+
if (!file) return false;
|
|
311
|
+
const extension = extname(normalizeFilePath(file));
|
|
312
|
+
return extension.length > 0 && IMPORT_PROTECTION_PARSEABLE_EXTENSIONS.has(extension);
|
|
313
|
+
}
|
|
314
|
+
function isImportProtectionSourceModule(module) {
|
|
315
|
+
return isImportProtectionSourceFile(getModuleResource(module));
|
|
316
|
+
}
|
|
317
|
+
function addTransformResult(cache, key, result) {
|
|
318
|
+
cache.set(normalizePath(key), result);
|
|
319
|
+
cache.set(normalizeFilePath(key), result);
|
|
320
|
+
}
|
|
321
|
+
function hasTransformResult(cache, key) {
|
|
322
|
+
return cache.has(normalizePath(key)) || cache.has(normalizeFilePath(key));
|
|
323
|
+
}
|
|
324
|
+
function deferFileViolation(envState, violation) {
|
|
325
|
+
const key = `${violation.importer}:${violation.specifier}:${violation.resolved}:${String(violation.pattern)}`;
|
|
326
|
+
if (envState.deferredFileViolationKeys.has(key)) return;
|
|
327
|
+
envState.deferredFileViolationKeys.add(key);
|
|
328
|
+
envState.deferredFileViolations.push(violation);
|
|
329
|
+
}
|
|
330
|
+
function hasOriginalUnsafeUsage(result, source, envType) {
|
|
331
|
+
if (!result) return false;
|
|
332
|
+
const originalResult = getOrCreateOriginalTransformResult(result);
|
|
333
|
+
if (!originalResult) return false;
|
|
334
|
+
return !!findOriginalUnsafeUsagePosFromResult(originalResult, source, envType);
|
|
335
|
+
}
|
|
336
|
+
async function buildTransformResultProvider(opts) {
|
|
337
|
+
const cache = /* @__PURE__ */ new Map();
|
|
338
|
+
if (opts.preloaded) for (const [key, result] of opts.preloaded) cache.set(key, result);
|
|
339
|
+
opts.perf?.count('processAssets.provider.modules', opts.modules.length);
|
|
340
|
+
for (const module of opts.modules) {
|
|
341
|
+
const source = module.originalSource();
|
|
342
|
+
if (!source) continue;
|
|
343
|
+
const sourceAndMapStartedAt = opts.perf ? performance.now() : 0;
|
|
344
|
+
const sourceAndMap = source.sourceAndMap();
|
|
345
|
+
if (opts.perf) opts.perf.time('processAssets.provider.sourceAndMap', sourceAndMapStartedAt);
|
|
346
|
+
const code = String(sourceAndMap.source);
|
|
347
|
+
const map = normalizeSourceMap(sourceAndMap.map);
|
|
348
|
+
const file = getModuleFile(module);
|
|
349
|
+
const resource = getModuleResource(module);
|
|
350
|
+
const originalCodeStartedAt = opts.perf ? performance.now() : 0;
|
|
351
|
+
const originalCode = map?.sourcesContent
|
|
352
|
+
? (pickOriginalCodeFromSourcesContent(map, resource ?? file, opts.root) ??
|
|
353
|
+
(resource ? await opts.loadOriginalCode(resource) : void 0))
|
|
354
|
+
: resource
|
|
355
|
+
? await opts.loadOriginalCode(resource)
|
|
356
|
+
: void 0;
|
|
357
|
+
if (opts.perf) opts.perf.time('processAssets.provider.originalCode', originalCodeStartedAt);
|
|
358
|
+
const result = {
|
|
359
|
+
code,
|
|
360
|
+
filename: resource ?? file,
|
|
361
|
+
map,
|
|
362
|
+
originalCode,
|
|
363
|
+
perf: opts.perf,
|
|
364
|
+
};
|
|
365
|
+
if (!hasTransformResult(cache, file)) addTransformResult(cache, file, result);
|
|
366
|
+
if (resource && !hasTransformResult(cache, resource))
|
|
367
|
+
addTransformResult(cache, resource, result);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
getTransformResult(id) {
|
|
371
|
+
return cache.get(normalizePath(id)) ?? cache.get(normalizeFilePath(id));
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function getConnectionRequest(dependency) {
|
|
376
|
+
const candidate = dependency;
|
|
377
|
+
return typeof candidate.request === 'string' ? candidate.request : void 0;
|
|
378
|
+
}
|
|
379
|
+
function addEntryModulesToGraph(opts) {
|
|
380
|
+
for (const entry of opts.compilation.entries.values())
|
|
381
|
+
for (const dependency of entry.dependencies) {
|
|
382
|
+
const module = opts.compilation.moduleGraph.getConnection(dependency)?.module;
|
|
383
|
+
if (!module) continue;
|
|
384
|
+
opts.graph.addEntry(getModuleFile(module));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function buildCompilationGraph(opts) {
|
|
388
|
+
const graph = new ImportGraph();
|
|
389
|
+
const edges = [];
|
|
390
|
+
const inactiveEdges = [];
|
|
391
|
+
addEntryModulesToGraph({
|
|
392
|
+
compilation: opts.compilation,
|
|
393
|
+
graph,
|
|
394
|
+
});
|
|
395
|
+
for (const module of opts.modules) {
|
|
396
|
+
const importer = getModuleFile(module);
|
|
397
|
+
const connections = opts.compilation.moduleGraph.getOutgoingConnectionsInOrder(module);
|
|
398
|
+
for (const connection of connections) {
|
|
399
|
+
if (!connection.module) continue;
|
|
400
|
+
const resolved = getModuleFile(connection.module);
|
|
401
|
+
const specifier = getConnectionRequest(connection.dependency);
|
|
402
|
+
if (isActiveConnection(connection)) {
|
|
403
|
+
graph.addEdge(resolved, importer, specifier);
|
|
404
|
+
edges.push({
|
|
405
|
+
importer,
|
|
406
|
+
specifier,
|
|
407
|
+
resolved,
|
|
408
|
+
});
|
|
409
|
+
} else
|
|
410
|
+
inactiveEdges.push({
|
|
411
|
+
importer,
|
|
412
|
+
specifier,
|
|
413
|
+
resolved,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
graph,
|
|
419
|
+
edges,
|
|
420
|
+
inactiveEdges,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function isActiveConnection(connection) {
|
|
424
|
+
if (typeof connection.getActiveState !== 'function') return true;
|
|
425
|
+
return connection.getActiveState(void 0) === true;
|
|
426
|
+
}
|
|
427
|
+
function findImportLocationInOriginalCode(provider, importer, source) {
|
|
428
|
+
const result = provider.getTransformResult(importer);
|
|
429
|
+
if (!result) return;
|
|
430
|
+
const originalResult = getOrCreateOriginalTransformResult(result);
|
|
431
|
+
if (!originalResult) return;
|
|
432
|
+
const index = importSpecifierLocationIndex.find(originalResult, source);
|
|
433
|
+
if (index === -1) return;
|
|
434
|
+
const loc = indexToLineColumn(
|
|
435
|
+
originalResult.lineIndex ?? (originalResult.lineIndex = buildLineIndex(originalResult.code)),
|
|
436
|
+
index,
|
|
437
|
+
);
|
|
438
|
+
return {
|
|
439
|
+
file: normalizeFilePath(importer),
|
|
440
|
+
line: loc.line,
|
|
441
|
+
column: loc.column,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
async function resolveImporterLocation(opts) {
|
|
445
|
+
if (opts.preferOriginalCode)
|
|
446
|
+
for (const candidate of opts.sourceCandidates) {
|
|
447
|
+
const loc =
|
|
448
|
+
findOriginalUsageLocation(opts.provider, opts.importer, candidate, opts.envType) ??
|
|
449
|
+
findImportLocationInOriginalCode(opts.provider, opts.importer, candidate);
|
|
450
|
+
if (loc) return loc;
|
|
451
|
+
}
|
|
452
|
+
for (const candidate of opts.sourceCandidates) {
|
|
453
|
+
const loc =
|
|
454
|
+
(await findPostCompileUsageLocation(opts.provider, opts.importer, candidate)) ||
|
|
455
|
+
(await findImportStatementLocationFromTransformed(
|
|
456
|
+
opts.provider,
|
|
457
|
+
opts.importer,
|
|
458
|
+
candidate,
|
|
459
|
+
opts.importLocCache,
|
|
460
|
+
importSpecifierLocationIndex.find,
|
|
461
|
+
));
|
|
462
|
+
if (loc) return loc;
|
|
463
|
+
}
|
|
464
|
+
if (!opts.preferOriginalCode)
|
|
465
|
+
for (const candidate of opts.sourceCandidates) {
|
|
466
|
+
const loc = findImportLocationInOriginalCode(opts.provider, opts.importer, candidate);
|
|
467
|
+
if (loc) return loc;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async function rebuildAndAnnotateTrace(opts) {
|
|
471
|
+
const trace = buildTrace(opts.graph, opts.importer, opts.maxTraceDepth);
|
|
472
|
+
await addTraceImportLocations(
|
|
473
|
+
opts.provider,
|
|
474
|
+
trace,
|
|
475
|
+
opts.importLocCache,
|
|
476
|
+
importSpecifierLocationIndex.find,
|
|
477
|
+
);
|
|
478
|
+
if (trace.length > 0) {
|
|
479
|
+
const last = trace[trace.length - 1];
|
|
480
|
+
if (!last.specifier) last.specifier = opts.specifier;
|
|
481
|
+
if (opts.importerLoc && last.line == null) {
|
|
482
|
+
last.line = opts.importerLoc.line;
|
|
483
|
+
last.column = opts.importerLoc.column;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return trace;
|
|
487
|
+
}
|
|
488
|
+
async function buildViolationInfo(opts) {
|
|
489
|
+
const startedAt = opts.perf ? performance.now() : 0;
|
|
490
|
+
opts.perf?.count('violations.enriched');
|
|
491
|
+
const importerLocStartedAt = opts.perf ? performance.now() : 0;
|
|
492
|
+
const importerLoc = await resolveImporterLocation({
|
|
493
|
+
provider: opts.provider,
|
|
494
|
+
importLocCache: opts.importLocCache,
|
|
495
|
+
importer: opts.importer,
|
|
496
|
+
sourceCandidates: buildSourceCandidates(opts.source, opts.resolved, opts.config.root),
|
|
497
|
+
preferOriginalCode: opts.preferOriginalCode,
|
|
498
|
+
envType: opts.envType,
|
|
499
|
+
});
|
|
500
|
+
if (opts.perf) opts.perf.time('violations.resolveImporterLocation', importerLocStartedAt);
|
|
501
|
+
const traceStartedAt = opts.perf ? performance.now() : 0;
|
|
502
|
+
const trace = await rebuildAndAnnotateTrace({
|
|
503
|
+
provider: opts.provider,
|
|
504
|
+
graph: opts.graph,
|
|
505
|
+
importLocCache: opts.importLocCache,
|
|
506
|
+
importer: opts.importer,
|
|
507
|
+
specifier: opts.source,
|
|
508
|
+
importerLoc,
|
|
509
|
+
maxTraceDepth: opts.config.maxTraceDepth,
|
|
510
|
+
});
|
|
511
|
+
if (opts.perf) opts.perf.time('violations.trace', traceStartedAt);
|
|
512
|
+
const snippetStartedAt = opts.perf ? performance.now() : 0;
|
|
513
|
+
const snippet = importerLoc
|
|
514
|
+
? buildCodeSnippet(opts.provider, opts.importer, importerLoc)
|
|
515
|
+
: void 0;
|
|
516
|
+
if (opts.perf && importerLoc) opts.perf.time('violations.snippet', snippetStartedAt);
|
|
517
|
+
const info = {
|
|
518
|
+
env: opts.envName,
|
|
519
|
+
envType: opts.envType,
|
|
520
|
+
behavior: opts.config.effectiveBehavior,
|
|
521
|
+
type: opts.type,
|
|
522
|
+
pattern: opts.pattern,
|
|
523
|
+
specifier: opts.source,
|
|
524
|
+
importer: opts.importer,
|
|
525
|
+
...(opts.resolved ? { resolved: opts.resolved } : {}),
|
|
526
|
+
...(importerLoc ? { importerLoc } : {}),
|
|
527
|
+
trace,
|
|
528
|
+
snippet,
|
|
529
|
+
};
|
|
530
|
+
if (opts.perf) opts.perf.time('violations.enrich', startedAt);
|
|
531
|
+
return info;
|
|
532
|
+
}
|
|
533
|
+
async function getMarkerKindForFile(opts) {
|
|
534
|
+
if (!isImportProtectionSourceFile(opts.file)) return;
|
|
535
|
+
let cached = opts.markerKindCache.get(opts.file);
|
|
536
|
+
if (!cached) {
|
|
537
|
+
cached = (async () => {
|
|
538
|
+
const code =
|
|
539
|
+
opts.provider.getTransformResult(opts.file)?.originalCode ??
|
|
540
|
+
(await opts.loadOriginalCode(opts.file));
|
|
541
|
+
if (!code) return;
|
|
542
|
+
const imports = getImportSources(code, opts.file);
|
|
543
|
+
const hasServerOnly = imports.some((source) =>
|
|
544
|
+
opts.config.markerSpecifiers.serverOnly.has(source),
|
|
545
|
+
);
|
|
546
|
+
const hasClientOnly = imports.some((source) =>
|
|
547
|
+
opts.config.markerSpecifiers.clientOnly.has(source),
|
|
548
|
+
);
|
|
549
|
+
if (hasServerOnly && !hasClientOnly) return 'server';
|
|
550
|
+
if (hasClientOnly && !hasServerOnly) return 'client';
|
|
551
|
+
})();
|
|
552
|
+
opts.markerKindCache.set(opts.file, cached);
|
|
553
|
+
}
|
|
554
|
+
return cached;
|
|
555
|
+
}
|
|
556
|
+
async function reportViolation(opts) {
|
|
557
|
+
const key = dedupeKey(opts.info);
|
|
558
|
+
if (opts.config.logMode !== 'always' && opts.envState.seenViolations.has(key)) {
|
|
559
|
+
opts.perf?.count('violations.deduped');
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
opts.envState.seenViolations.add(key);
|
|
563
|
+
opts.perf?.count('violations.reported');
|
|
564
|
+
if (opts.config.onViolation) {
|
|
565
|
+
const startedAt = opts.perf ? performance.now() : 0;
|
|
566
|
+
const result = await opts.config.onViolation(opts.info);
|
|
567
|
+
if (opts.perf) opts.perf.time('violations.onViolation', startedAt);
|
|
568
|
+
if (result === false) {
|
|
569
|
+
opts.perf?.count('violations.suppressed');
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const message = formatViolation(opts.info, opts.config.root);
|
|
574
|
+
const error = new opts.rspack.WebpackError(message);
|
|
575
|
+
if (opts.config.effectiveBehavior === 'error') opts.compilation.errors.push(error);
|
|
576
|
+
else opts.compilation.warnings.push(error);
|
|
577
|
+
}
|
|
578
|
+
function registerImportProtection(api, opts) {
|
|
579
|
+
const perf = isPerfEnabled() ? createPerfCollector() : void 0;
|
|
580
|
+
const extensionlessResolver = new ExtensionlessAbsoluteIdResolver();
|
|
581
|
+
const envStates = /* @__PURE__ */ new Map();
|
|
582
|
+
const fileReadCache = /* @__PURE__ */ new Map();
|
|
583
|
+
const shouldCheckImporterCache = /* @__PURE__ */ new Map();
|
|
584
|
+
const config = {
|
|
585
|
+
enabled: true,
|
|
586
|
+
root: '',
|
|
587
|
+
command: api.context.action === 'dev' ? 'serve' : 'build',
|
|
588
|
+
srcDirectory: '',
|
|
589
|
+
framework: opts.framework,
|
|
590
|
+
effectiveBehavior: 'error',
|
|
591
|
+
mockAccess: 'error',
|
|
592
|
+
logMode: 'once',
|
|
593
|
+
maxTraceDepth: 20,
|
|
594
|
+
compiledRules: {
|
|
595
|
+
client: {
|
|
596
|
+
specifiers: [],
|
|
597
|
+
files: [],
|
|
598
|
+
excludeFiles: [],
|
|
599
|
+
},
|
|
600
|
+
server: {
|
|
601
|
+
specifiers: [],
|
|
602
|
+
files: [],
|
|
603
|
+
excludeFiles: [],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
includeMatchers: [],
|
|
607
|
+
excludeMatchers: [],
|
|
608
|
+
ignoreImporterMatchers: [],
|
|
609
|
+
markerSpecifiers: {
|
|
610
|
+
serverOnly: /* @__PURE__ */ new Set(),
|
|
611
|
+
clientOnly: /* @__PURE__ */ new Set(),
|
|
612
|
+
},
|
|
613
|
+
envTypeMap: new Map(opts.environments.map((env) => [env.name, env.type])),
|
|
614
|
+
onViolation: void 0,
|
|
615
|
+
};
|
|
616
|
+
const shared = {
|
|
617
|
+
root: '',
|
|
618
|
+
virtualModules: /* @__PURE__ */ new Map(),
|
|
619
|
+
vmPlugins: {},
|
|
620
|
+
readyVmPlugins: {},
|
|
621
|
+
inputFileSystems: {},
|
|
622
|
+
pendingWrites: /* @__PURE__ */ new Map(),
|
|
623
|
+
};
|
|
624
|
+
function applyUserConfig() {
|
|
625
|
+
const { startConfig, resolvedStartConfig } = opts.getConfig();
|
|
626
|
+
config.root = resolvedStartConfig.root;
|
|
627
|
+
config.srcDirectory = resolvedStartConfig.srcDirectory;
|
|
628
|
+
shared.root = resolvedStartConfig.root;
|
|
629
|
+
const userOpts = startConfig.importProtection;
|
|
630
|
+
if (userOpts?.enabled === false) {
|
|
631
|
+
config.enabled = false;
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
config.enabled = true;
|
|
635
|
+
const behavior = userOpts?.behavior;
|
|
636
|
+
if (typeof behavior === 'string') config.effectiveBehavior = behavior;
|
|
637
|
+
else
|
|
638
|
+
config.effectiveBehavior =
|
|
639
|
+
config.command === 'serve' ? (behavior?.dev ?? 'mock') : (behavior?.build ?? 'error');
|
|
640
|
+
config.logMode = userOpts?.log ?? 'once';
|
|
641
|
+
config.mockAccess = userOpts?.mockAccess ?? 'error';
|
|
642
|
+
config.maxTraceDepth = userOpts?.maxTraceDepth ?? 20;
|
|
643
|
+
config.onViolation = userOpts?.onViolation ? (info) => userOpts.onViolation?.(info) : void 0;
|
|
644
|
+
const defaults = getDefaultImportProtectionRules();
|
|
645
|
+
const pick = (user, fallback) => (user ? [...user] : [...fallback]);
|
|
646
|
+
const clientSpecifiers = dedupePatterns([
|
|
647
|
+
...defaults.client.specifiers,
|
|
648
|
+
...(userOpts?.client?.specifiers ?? []),
|
|
649
|
+
]);
|
|
650
|
+
config.compiledRules.client = {
|
|
651
|
+
specifiers: compileMatchers(clientSpecifiers),
|
|
652
|
+
files: compileMatchers(pick(userOpts?.client?.files, defaults.client.files)),
|
|
653
|
+
excludeFiles: compileMatchers(
|
|
654
|
+
pick(userOpts?.client?.excludeFiles, defaults.client.excludeFiles),
|
|
655
|
+
),
|
|
656
|
+
};
|
|
657
|
+
config.compiledRules.server = {
|
|
658
|
+
specifiers: compileMatchers(
|
|
659
|
+
dedupePatterns(pick(userOpts?.server?.specifiers, defaults.server.specifiers)),
|
|
660
|
+
),
|
|
661
|
+
files: compileMatchers(pick(userOpts?.server?.files, defaults.server.files)),
|
|
662
|
+
excludeFiles: compileMatchers(
|
|
663
|
+
pick(userOpts?.server?.excludeFiles, defaults.server.excludeFiles),
|
|
664
|
+
),
|
|
665
|
+
};
|
|
666
|
+
config.includeMatchers = compileMatchers(userOpts?.include ?? []);
|
|
667
|
+
config.excludeMatchers = compileMatchers(userOpts?.exclude ?? []);
|
|
668
|
+
config.ignoreImporterMatchers = compileMatchers(userOpts?.ignoreImporters ?? []);
|
|
669
|
+
const markers = getMarkerSpecifiers();
|
|
670
|
+
config.markerSpecifiers = {
|
|
671
|
+
serverOnly: new Set(markers.serverOnly),
|
|
672
|
+
clientOnly: new Set(markers.clientOnly),
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function shouldCheckImporter(file) {
|
|
676
|
+
const normalizedFile = normalizeFilePath(file);
|
|
677
|
+
const cached = shouldCheckImporterCache.get(normalizedFile);
|
|
678
|
+
if (cached !== void 0) {
|
|
679
|
+
perf?.count('shouldCheckImporter.cached');
|
|
680
|
+
return cached;
|
|
681
|
+
}
|
|
682
|
+
const result = shouldCheckImportProtectionImporter(config, normalizedFile);
|
|
683
|
+
shouldCheckImporterCache.set(normalizedFile, result);
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
api.onBeforeBuild(() => {
|
|
687
|
+
const startedAt = perf ? performance.now() : 0;
|
|
688
|
+
applyUserConfig();
|
|
689
|
+
clearNormalizeFilePathCache();
|
|
690
|
+
extensionlessResolver.clear();
|
|
691
|
+
fileReadCache.clear();
|
|
692
|
+
shouldCheckImporterCache.clear();
|
|
693
|
+
envStates.clear();
|
|
694
|
+
if (perf) perf.time('onBeforeBuild', startedAt);
|
|
695
|
+
});
|
|
696
|
+
api.onBeforeDevCompile(() => {
|
|
697
|
+
const startedAt = perf ? performance.now() : 0;
|
|
698
|
+
applyUserConfig();
|
|
699
|
+
clearNormalizeFilePathCache();
|
|
700
|
+
extensionlessResolver.clear();
|
|
701
|
+
fileReadCache.clear();
|
|
702
|
+
shouldCheckImporterCache.clear();
|
|
703
|
+
for (const envState of envStates.values()) {
|
|
704
|
+
envState.resolveCache.clear();
|
|
705
|
+
envState.buildTransformResults.clear();
|
|
706
|
+
envState.deferredFileViolations.length = 0;
|
|
707
|
+
envState.deferredFileViolationKeys.clear();
|
|
708
|
+
}
|
|
709
|
+
if (perf) perf.time('onBeforeDevCompile', startedAt);
|
|
710
|
+
});
|
|
711
|
+
api.modifyRspackConfig((rspackConfig, utils) => {
|
|
712
|
+
const startedAt = perf ? performance.now() : 0;
|
|
713
|
+
applyUserConfig();
|
|
714
|
+
const envName = utils.environment.name;
|
|
715
|
+
const VMP = utils.rspack.experiments.VirtualModulesPlugin;
|
|
716
|
+
const vmPlugin = new VMP({});
|
|
717
|
+
shared.vmPlugins[envName] = vmPlugin;
|
|
718
|
+
shared.readyVmPlugins[envName] = false;
|
|
719
|
+
rspackConfig.plugins.push(vmPlugin);
|
|
720
|
+
rspackConfig.plugins.push({
|
|
721
|
+
apply(compiler) {
|
|
722
|
+
shared.inputFileSystems[envName] = compiler.inputFileSystem;
|
|
723
|
+
compiler.hooks.thisCompilation.tap(
|
|
724
|
+
'TanStackStartImportProtectionVirtualModulesReady',
|
|
725
|
+
() => {
|
|
726
|
+
shared.readyVmPlugins[envName] = true;
|
|
727
|
+
flushPendingWrites(shared, envName);
|
|
728
|
+
},
|
|
729
|
+
);
|
|
730
|
+
},
|
|
731
|
+
});
|
|
732
|
+
if (perf) perf.time('modifyRspackConfig', startedAt);
|
|
733
|
+
});
|
|
734
|
+
for (const environment of opts.environments)
|
|
735
|
+
api.transform(
|
|
736
|
+
{
|
|
737
|
+
test: /\.[cm]?[tj]sx?$/,
|
|
738
|
+
environments: [environment.name],
|
|
739
|
+
order: 'post',
|
|
740
|
+
},
|
|
741
|
+
async (ctx) => {
|
|
742
|
+
const startedAt = perf ? performance.now() : 0;
|
|
743
|
+
perf?.count('transform.calls');
|
|
744
|
+
perf?.count(`transform.env.${environment.name}`);
|
|
745
|
+
try {
|
|
746
|
+
if (!config.enabled) return ctx.code;
|
|
747
|
+
const envName = environment.name;
|
|
748
|
+
const envType = getImportProtectionEnvType(config, envName);
|
|
749
|
+
const envState = getOrCreateEnvState(envStates, envName);
|
|
750
|
+
const id = ctx.resource;
|
|
751
|
+
const file = normalizeFilePath(ctx.resourcePath);
|
|
752
|
+
if (!shouldCheckImporter(file)) {
|
|
753
|
+
perf?.count('transform.skippedImporter');
|
|
754
|
+
return ctx.code;
|
|
755
|
+
}
|
|
756
|
+
const matchers = getRulesForEnvironment(config, envName);
|
|
757
|
+
const relativeFile = getImportProtectionRelativePath(config.root, file);
|
|
758
|
+
const transformResult = {
|
|
759
|
+
code: ctx.code,
|
|
760
|
+
filename: file,
|
|
761
|
+
map: void 0,
|
|
762
|
+
originalCode: void 0,
|
|
763
|
+
perf,
|
|
764
|
+
};
|
|
765
|
+
const importSources = getImportSourcesFromResult(transformResult);
|
|
766
|
+
perf?.count('transform.importSources', importSources.length);
|
|
767
|
+
const transformedImportSources = new Set(importSources);
|
|
768
|
+
const transformInputFileSystem = shared.inputFileSystems[envName];
|
|
769
|
+
const loadOriginalCodeForTransform = transformInputFileSystem
|
|
770
|
+
? (target) => loadOriginalCodeFromInputFileSystem(transformInputFileSystem, target)
|
|
771
|
+
: () => Promise.resolve(void 0);
|
|
772
|
+
const originalCodeStartedAt = perf ? performance.now() : 0;
|
|
773
|
+
const originalCode =
|
|
774
|
+
config.command === 'build'
|
|
775
|
+
? await loadOriginalCode(fileReadCache, file, loadOriginalCodeForTransform)
|
|
776
|
+
: void 0;
|
|
777
|
+
if (perf && config.command === 'build')
|
|
778
|
+
perf.time('transform.originalCode.load', originalCodeStartedAt);
|
|
779
|
+
transformResult.originalCode = originalCode;
|
|
780
|
+
const originalTransformResult = originalCode
|
|
781
|
+
? getOrCreateOriginalTransformResult(transformResult)
|
|
782
|
+
: void 0;
|
|
783
|
+
const buildImportSourcesStartedAt = perf ? performance.now() : 0;
|
|
784
|
+
const buildImportSources = originalTransformResult
|
|
785
|
+
? getImportSourcesFromResult(originalTransformResult)
|
|
786
|
+
: [];
|
|
787
|
+
if (perf && originalCode) {
|
|
788
|
+
perf.time('transform.originalImportAnalysis', buildImportSourcesStartedAt);
|
|
789
|
+
perf.count('transform.originalImportSources', buildImportSources.length);
|
|
790
|
+
}
|
|
791
|
+
const buildTransformResult = config.command === 'build' ? transformResult : void 0;
|
|
792
|
+
if (config.command === 'build') {
|
|
793
|
+
const relativeBuildFile = getImportProtectionRelativePath(config.root, file);
|
|
794
|
+
addTransformResult(envState.buildTransformResults, file, buildTransformResult);
|
|
795
|
+
addTransformResult(
|
|
796
|
+
envState.buildTransformResults,
|
|
797
|
+
relativeBuildFile,
|
|
798
|
+
buildTransformResult,
|
|
799
|
+
);
|
|
800
|
+
if (id !== file)
|
|
801
|
+
addTransformResult(envState.buildTransformResults, id, buildTransformResult);
|
|
802
|
+
}
|
|
803
|
+
const hasServerOnlyMarker = importSources.some((source) =>
|
|
804
|
+
config.markerSpecifiers.serverOnly.has(source),
|
|
805
|
+
);
|
|
806
|
+
const hasClientOnlyMarker = importSources.some((source) =>
|
|
807
|
+
config.markerSpecifiers.clientOnly.has(source),
|
|
808
|
+
);
|
|
809
|
+
if (hasServerOnlyMarker && hasClientOnlyMarker)
|
|
810
|
+
throw new Error(
|
|
811
|
+
`[import-protection] File "${relativeFile}" has both server-only and client-only markers. This is not allowed.`,
|
|
812
|
+
);
|
|
813
|
+
const markerKind = hasServerOnlyMarker
|
|
814
|
+
? 'server'
|
|
815
|
+
: hasClientOnlyMarker
|
|
816
|
+
? 'client'
|
|
817
|
+
: void 0;
|
|
818
|
+
if (
|
|
819
|
+
checkFileDenial(relativeFile, matchers) ||
|
|
820
|
+
(envType === 'client' && markerKind === 'server') ||
|
|
821
|
+
(envType === 'server' && markerKind === 'client')
|
|
822
|
+
) {
|
|
823
|
+
let exportNames = [];
|
|
824
|
+
try {
|
|
825
|
+
exportNames = getNamedExportsFromResult(transformResult);
|
|
826
|
+
} catch {
|
|
827
|
+
exportNames = [];
|
|
828
|
+
}
|
|
829
|
+
if (config.command === 'build') return generateSelfContainedMockModule(exportNames);
|
|
830
|
+
const runtimeId = ensureRuntimeMockModule({
|
|
831
|
+
shared,
|
|
832
|
+
envName,
|
|
833
|
+
mode: config.mockAccess,
|
|
834
|
+
env: envName,
|
|
835
|
+
importer: file,
|
|
836
|
+
specifier: relativeFile,
|
|
837
|
+
});
|
|
838
|
+
return generateDevSelfDenialModule(exportNames, runtimeId);
|
|
839
|
+
}
|
|
840
|
+
const deniedSpecifierReplacements = /* @__PURE__ */ new Map();
|
|
841
|
+
let exportsBySource;
|
|
842
|
+
const getExportsBySource = () => {
|
|
843
|
+
if (exportsBySource) return exportsBySource;
|
|
844
|
+
try {
|
|
845
|
+
exportsBySource = getMockExportNamesBySourceFromResult(transformResult);
|
|
846
|
+
} catch {
|
|
847
|
+
exportsBySource = /* @__PURE__ */ new Map();
|
|
848
|
+
}
|
|
849
|
+
return exportsBySource;
|
|
850
|
+
};
|
|
851
|
+
for (const source of importSources) {
|
|
852
|
+
const specifierMatch = matchesAny(source, matchers.specifiers);
|
|
853
|
+
if (!specifierMatch) continue;
|
|
854
|
+
const resolved = await resolveAgainstImporter({
|
|
855
|
+
envState,
|
|
856
|
+
config,
|
|
857
|
+
ctx,
|
|
858
|
+
importerId: id,
|
|
859
|
+
source,
|
|
860
|
+
extensionlessResolver,
|
|
861
|
+
perf,
|
|
862
|
+
});
|
|
863
|
+
const runtimeId =
|
|
864
|
+
config.command === 'build'
|
|
865
|
+
? ensureSilentMockModule(shared, envName)
|
|
866
|
+
: ensureRuntimeMockModule({
|
|
867
|
+
shared,
|
|
868
|
+
envName,
|
|
869
|
+
mode: config.mockAccess,
|
|
870
|
+
env: envName,
|
|
871
|
+
importer: file,
|
|
872
|
+
specifier: source,
|
|
873
|
+
});
|
|
874
|
+
const replacement = ensureMockEdgeModule({
|
|
875
|
+
shared,
|
|
876
|
+
envName,
|
|
877
|
+
payload: {
|
|
878
|
+
exports: getExportsBySource().get(source) ?? [],
|
|
879
|
+
runtimeId,
|
|
880
|
+
violation: {
|
|
881
|
+
env: envName,
|
|
882
|
+
envType,
|
|
883
|
+
importer: file,
|
|
884
|
+
specifier: source,
|
|
885
|
+
...(resolved ? { resolved } : {}),
|
|
886
|
+
patternText: serializePattern(specifierMatch.pattern),
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
deniedSpecifierReplacements.set(source, replacement);
|
|
891
|
+
}
|
|
892
|
+
if (config.command === 'build')
|
|
893
|
+
for (const source of buildImportSources) {
|
|
894
|
+
if (transformedImportSources.has(source)) continue;
|
|
895
|
+
if (matchesAny(source, matchers.specifiers)) continue;
|
|
896
|
+
if (!hasOriginalUnsafeUsage(buildTransformResult, source, envType)) continue;
|
|
897
|
+
const resolved = await resolveAgainstImporter({
|
|
898
|
+
envState,
|
|
899
|
+
config,
|
|
900
|
+
ctx,
|
|
901
|
+
importerId: id,
|
|
902
|
+
source,
|
|
903
|
+
extensionlessResolver,
|
|
904
|
+
perf,
|
|
905
|
+
});
|
|
906
|
+
if (!resolved) continue;
|
|
907
|
+
const relativeResolved = getImportProtectionRelativePath(config.root, resolved);
|
|
908
|
+
const buildFileMatch = checkFileDenial(relativeResolved, matchers);
|
|
909
|
+
if (!buildFileMatch) continue;
|
|
910
|
+
deferFileViolation(envState, {
|
|
911
|
+
importer: file,
|
|
912
|
+
specifier: source,
|
|
913
|
+
resolved,
|
|
914
|
+
relativeResolved,
|
|
915
|
+
pattern: buildFileMatch.pattern,
|
|
916
|
+
useOriginalLocation: true,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if (deniedSpecifierReplacements.size === 0) return ctx.code;
|
|
920
|
+
const rewritten = rewriteDeniedImports(
|
|
921
|
+
ctx.code,
|
|
922
|
+
id,
|
|
923
|
+
new Set(deniedSpecifierReplacements.keys()),
|
|
924
|
+
(source) => deniedSpecifierReplacements.get(source) ?? source,
|
|
925
|
+
);
|
|
926
|
+
if (!rewritten) return ctx.code;
|
|
927
|
+
return {
|
|
928
|
+
code: rewritten.code,
|
|
929
|
+
map: normalizeSourceMap(rewritten.map) ?? null,
|
|
930
|
+
};
|
|
931
|
+
} finally {
|
|
932
|
+
if (perf) perf.time('transform', startedAt);
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
);
|
|
936
|
+
api.processAssets(
|
|
937
|
+
{
|
|
938
|
+
stage: 'report',
|
|
939
|
+
environments: opts.environments.map((environment) => environment.name),
|
|
940
|
+
},
|
|
941
|
+
async (context) => {
|
|
942
|
+
const envName = context.environment.name;
|
|
943
|
+
const startedAt = perf ? performance.now() : 0;
|
|
944
|
+
perf?.count('processAssets.calls');
|
|
945
|
+
perf?.count(`processAssets.env.${envName}`);
|
|
946
|
+
try {
|
|
947
|
+
if (!config.enabled) return;
|
|
948
|
+
const envType = getImportProtectionEnvType(config, envName);
|
|
949
|
+
const envState = getOrCreateEnvState(envStates, envName);
|
|
950
|
+
const matchers = getRulesForEnvironment(config, envName);
|
|
951
|
+
const processFileReadCache = /* @__PURE__ */ new Map();
|
|
952
|
+
const loadOriginalCodeFromCompilation = (file) =>
|
|
953
|
+
loadOriginalCode(
|
|
954
|
+
processFileReadCache,
|
|
955
|
+
file,
|
|
956
|
+
context.compilation.inputFileSystem
|
|
957
|
+
? (target) =>
|
|
958
|
+
loadOriginalCodeFromInputFileSystem(context.compilation.inputFileSystem, target)
|
|
959
|
+
: () => Promise.resolve(void 0),
|
|
960
|
+
);
|
|
961
|
+
const allModules = Array.from(context.compilation.modules);
|
|
962
|
+
const relevantModules = allModules.filter(isImportProtectionSourceModule);
|
|
963
|
+
perf?.count('processAssets.modules.total', allModules.length);
|
|
964
|
+
perf?.count('processAssets.modules.relevant', relevantModules.length);
|
|
965
|
+
const providerStartedAt = perf ? performance.now() : 0;
|
|
966
|
+
const provider = await buildTransformResultProvider({
|
|
967
|
+
modules: relevantModules,
|
|
968
|
+
root: config.root,
|
|
969
|
+
loadOriginalCode: loadOriginalCodeFromCompilation,
|
|
970
|
+
preloaded: envState.buildTransformResults,
|
|
971
|
+
perf,
|
|
972
|
+
});
|
|
973
|
+
if (perf) perf.time('processAssets.provider.build', providerStartedAt);
|
|
974
|
+
const importLocCache = new ImportLocCache();
|
|
975
|
+
const markerKindCache = /* @__PURE__ */ new Map();
|
|
976
|
+
const graphStartedAt = perf ? performance.now() : 0;
|
|
977
|
+
const { graph, edges, inactiveEdges } = buildCompilationGraph({
|
|
978
|
+
compilation: context.compilation,
|
|
979
|
+
modules: relevantModules,
|
|
980
|
+
});
|
|
981
|
+
if (perf) {
|
|
982
|
+
perf.time('processAssets.graph.build', graphStartedAt);
|
|
983
|
+
perf.count('processAssets.graph.edges', edges.length);
|
|
984
|
+
perf.count('processAssets.graph.inactiveEdges', inactiveEdges.length);
|
|
985
|
+
}
|
|
986
|
+
const liveFileEdgeKeys = new Set(
|
|
987
|
+
edges
|
|
988
|
+
.filter((edge) => !!edge.specifier)
|
|
989
|
+
.map(
|
|
990
|
+
(edge) =>
|
|
991
|
+
`${normalizeFilePath(edge.importer)}::${edge.specifier}::${normalizeFilePath(edge.resolved)}`,
|
|
992
|
+
),
|
|
993
|
+
);
|
|
994
|
+
const candidateCache = /* @__PURE__ */ new Map();
|
|
995
|
+
const getCandidates = (id) => {
|
|
996
|
+
const normalized = normalizeFilePath(id);
|
|
997
|
+
let candidates = candidateCache.get(normalized);
|
|
998
|
+
if (!candidates) {
|
|
999
|
+
candidates = buildResolutionCandidates(normalized);
|
|
1000
|
+
candidateCache.set(normalized, candidates);
|
|
1001
|
+
}
|
|
1002
|
+
return candidates;
|
|
1003
|
+
};
|
|
1004
|
+
const survivingModules = /* @__PURE__ */ new Set();
|
|
1005
|
+
for (const module of relevantModules)
|
|
1006
|
+
for (const candidate of getCandidates(getModuleFile(module)))
|
|
1007
|
+
survivingModules.add(candidate);
|
|
1008
|
+
const didModuleSurvive = (id) =>
|
|
1009
|
+
getCandidates(id).some((candidate) => survivingModules.has(candidate));
|
|
1010
|
+
for (const module of relevantModules) {
|
|
1011
|
+
const payload = getMockEdgePayloadFromFile(getModuleFile(module));
|
|
1012
|
+
if (!payload) continue;
|
|
1013
|
+
if (!shouldCheckImporter(payload.violation.importer)) continue;
|
|
1014
|
+
const info = await buildViolationInfo({
|
|
1015
|
+
config,
|
|
1016
|
+
provider,
|
|
1017
|
+
graph,
|
|
1018
|
+
importLocCache,
|
|
1019
|
+
perf,
|
|
1020
|
+
envName,
|
|
1021
|
+
envType,
|
|
1022
|
+
importer: payload.violation.importer,
|
|
1023
|
+
source: payload.violation.specifier,
|
|
1024
|
+
resolved: payload.violation.resolved,
|
|
1025
|
+
type: 'specifier',
|
|
1026
|
+
pattern: payload.violation.patternText,
|
|
1027
|
+
preferOriginalCode: true,
|
|
1028
|
+
});
|
|
1029
|
+
await reportViolation({
|
|
1030
|
+
config,
|
|
1031
|
+
envState,
|
|
1032
|
+
compilation: context.compilation,
|
|
1033
|
+
rspack: context.compiler.rspack,
|
|
1034
|
+
perf,
|
|
1035
|
+
info,
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
for (const edge of edges) {
|
|
1039
|
+
if (!edge.specifier) continue;
|
|
1040
|
+
if (!shouldCheckImporter(edge.importer)) continue;
|
|
1041
|
+
const importProtectionCheck = getRsbuildResolvedImportProtectionCheck(
|
|
1042
|
+
getImportProtectionRelativePath(config.root, edge.resolved),
|
|
1043
|
+
matchers,
|
|
1044
|
+
);
|
|
1045
|
+
if (!importProtectionCheck) continue;
|
|
1046
|
+
if (importProtectionCheck.type === 'file') {
|
|
1047
|
+
const info = await buildViolationInfo({
|
|
1048
|
+
config,
|
|
1049
|
+
provider,
|
|
1050
|
+
graph,
|
|
1051
|
+
importLocCache,
|
|
1052
|
+
perf,
|
|
1053
|
+
envName,
|
|
1054
|
+
envType,
|
|
1055
|
+
importer: edge.importer,
|
|
1056
|
+
source: edge.specifier,
|
|
1057
|
+
resolved: edge.resolved,
|
|
1058
|
+
type: 'file',
|
|
1059
|
+
pattern: importProtectionCheck.fileMatch.pattern,
|
|
1060
|
+
});
|
|
1061
|
+
await reportViolation({
|
|
1062
|
+
config,
|
|
1063
|
+
envState,
|
|
1064
|
+
compilation: context.compilation,
|
|
1065
|
+
rspack: context.compiler.rspack,
|
|
1066
|
+
perf,
|
|
1067
|
+
info,
|
|
1068
|
+
});
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
const markerKind = await getMarkerKindForFile({
|
|
1072
|
+
config,
|
|
1073
|
+
provider,
|
|
1074
|
+
loadOriginalCode: loadOriginalCodeFromCompilation,
|
|
1075
|
+
markerKindCache,
|
|
1076
|
+
file: edge.resolved,
|
|
1077
|
+
});
|
|
1078
|
+
if (!(
|
|
1079
|
+
(envType === 'client' && markerKind === 'server') ||
|
|
1080
|
+
(envType === 'server' && markerKind === 'client')
|
|
1081
|
+
))
|
|
1082
|
+
continue;
|
|
1083
|
+
const info = await buildViolationInfo({
|
|
1084
|
+
config,
|
|
1085
|
+
provider,
|
|
1086
|
+
graph,
|
|
1087
|
+
importLocCache,
|
|
1088
|
+
perf,
|
|
1089
|
+
envName,
|
|
1090
|
+
envType,
|
|
1091
|
+
importer: edge.importer,
|
|
1092
|
+
source: edge.specifier,
|
|
1093
|
+
resolved: edge.resolved,
|
|
1094
|
+
type: 'marker',
|
|
1095
|
+
});
|
|
1096
|
+
await reportViolation({
|
|
1097
|
+
config,
|
|
1098
|
+
envState,
|
|
1099
|
+
compilation: context.compilation,
|
|
1100
|
+
rspack: context.compiler.rspack,
|
|
1101
|
+
perf,
|
|
1102
|
+
info,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
if (config.command === 'build') {
|
|
1106
|
+
const seenInactiveFileEdgeKeys = /* @__PURE__ */ new Set();
|
|
1107
|
+
for (const edge of inactiveEdges) {
|
|
1108
|
+
if (!edge.specifier) continue;
|
|
1109
|
+
if (!shouldCheckImporter(edge.importer)) continue;
|
|
1110
|
+
const liveEdgeKey = `${normalizeFilePath(edge.importer)}::${edge.specifier}::${normalizeFilePath(edge.resolved)}`;
|
|
1111
|
+
if (liveFileEdgeKeys.has(liveEdgeKey)) continue;
|
|
1112
|
+
if (seenInactiveFileEdgeKeys.has(liveEdgeKey)) continue;
|
|
1113
|
+
seenInactiveFileEdgeKeys.add(liveEdgeKey);
|
|
1114
|
+
if (!didModuleSurvive(edge.resolved)) continue;
|
|
1115
|
+
if (!didModuleSurvive(edge.importer)) continue;
|
|
1116
|
+
if (
|
|
1117
|
+
!hasOriginalUnsafeUsage(
|
|
1118
|
+
provider.getTransformResult(edge.importer),
|
|
1119
|
+
edge.specifier,
|
|
1120
|
+
envType,
|
|
1121
|
+
)
|
|
1122
|
+
)
|
|
1123
|
+
continue;
|
|
1124
|
+
const fileMatch = checkFileDenial(
|
|
1125
|
+
getImportProtectionRelativePath(config.root, edge.resolved),
|
|
1126
|
+
matchers,
|
|
1127
|
+
);
|
|
1128
|
+
if (!fileMatch) continue;
|
|
1129
|
+
const info = await buildViolationInfo({
|
|
1130
|
+
config,
|
|
1131
|
+
provider,
|
|
1132
|
+
graph,
|
|
1133
|
+
importLocCache,
|
|
1134
|
+
perf,
|
|
1135
|
+
envName,
|
|
1136
|
+
envType,
|
|
1137
|
+
importer: edge.importer,
|
|
1138
|
+
source: edge.specifier,
|
|
1139
|
+
resolved: edge.resolved,
|
|
1140
|
+
type: 'file',
|
|
1141
|
+
pattern: fileMatch.pattern,
|
|
1142
|
+
preferOriginalCode: true,
|
|
1143
|
+
});
|
|
1144
|
+
await reportViolation({
|
|
1145
|
+
config,
|
|
1146
|
+
envState,
|
|
1147
|
+
compilation: context.compilation,
|
|
1148
|
+
rspack: context.compiler.rspack,
|
|
1149
|
+
perf,
|
|
1150
|
+
info,
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
for (const violation of envState.deferredFileViolations) {
|
|
1155
|
+
const liveEdgeKey = `${normalizeFilePath(violation.importer)}::${violation.specifier}::${normalizeFilePath(violation.resolved)}`;
|
|
1156
|
+
if (liveFileEdgeKeys.has(liveEdgeKey)) continue;
|
|
1157
|
+
if (!didModuleSurvive(violation.resolved)) continue;
|
|
1158
|
+
if (!didModuleSurvive(violation.importer)) continue;
|
|
1159
|
+
const info = await buildViolationInfo({
|
|
1160
|
+
config,
|
|
1161
|
+
provider,
|
|
1162
|
+
graph,
|
|
1163
|
+
importLocCache,
|
|
1164
|
+
perf,
|
|
1165
|
+
envName,
|
|
1166
|
+
envType,
|
|
1167
|
+
importer: violation.importer,
|
|
1168
|
+
source: violation.specifier,
|
|
1169
|
+
resolved: violation.resolved,
|
|
1170
|
+
type: 'file',
|
|
1171
|
+
pattern: violation.pattern,
|
|
1172
|
+
preferOriginalCode: violation.useOriginalLocation,
|
|
1173
|
+
});
|
|
1174
|
+
await reportViolation({
|
|
1175
|
+
config,
|
|
1176
|
+
envState,
|
|
1177
|
+
compilation: context.compilation,
|
|
1178
|
+
rspack: context.compiler.rspack,
|
|
1179
|
+
perf,
|
|
1180
|
+
info,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
} finally {
|
|
1184
|
+
if (perf) {
|
|
1185
|
+
perf.time('processAssets', startedAt);
|
|
1186
|
+
perf.flush(config.root, envName, 'processAssets');
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
//#endregion
|
|
1193
|
+
export { registerImportProtection };
|