@tanstack/start-plugin-core 1.161.3 → 1.162.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/dist/esm/import-protection-plugin/defaults.d.ts +6 -4
- package/dist/esm/import-protection-plugin/defaults.js +3 -12
- package/dist/esm/import-protection-plugin/defaults.js.map +1 -1
- package/dist/esm/import-protection-plugin/plugin.d.ts +1 -1
- package/dist/esm/import-protection-plugin/plugin.js +488 -257
- package/dist/esm/import-protection-plugin/plugin.js.map +1 -1
- package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +4 -2
- package/dist/esm/import-protection-plugin/postCompileUsage.js +31 -150
- package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -1
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +13 -9
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -1
- package/dist/esm/import-protection-plugin/sourceLocation.d.ts +32 -66
- package/dist/esm/import-protection-plugin/sourceLocation.js +129 -56
- package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -1
- package/dist/esm/import-protection-plugin/trace.d.ts +10 -0
- package/dist/esm/import-protection-plugin/trace.js +30 -44
- package/dist/esm/import-protection-plugin/trace.js.map +1 -1
- package/dist/esm/import-protection-plugin/utils.d.ts +8 -4
- package/dist/esm/import-protection-plugin/utils.js +43 -1
- package/dist/esm/import-protection-plugin/utils.js.map +1 -1
- package/dist/esm/import-protection-plugin/virtualModules.d.ts +7 -1
- package/dist/esm/import-protection-plugin/virtualModules.js +104 -135
- package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -1
- package/package.json +8 -8
- package/src/import-protection-plugin/defaults.ts +8 -19
- package/src/import-protection-plugin/plugin.ts +776 -433
- package/src/import-protection-plugin/postCompileUsage.ts +57 -229
- package/src/import-protection-plugin/rewriteDeniedImports.ts +34 -42
- package/src/import-protection-plugin/sourceLocation.ts +184 -185
- package/src/import-protection-plugin/trace.ts +38 -49
- package/src/import-protection-plugin/utils.ts +62 -1
- package/src/import-protection-plugin/virtualModules.ts +163 -177
|
@@ -1,23 +1,102 @@
|
|
|
1
|
-
import * as path from "pathe";
|
|
2
1
|
import { normalizePath } from "vite";
|
|
3
2
|
import { resolveViteId } from "../utils.js";
|
|
4
3
|
import { VITE_ENVIRONMENT_NAMES } from "../constants.js";
|
|
5
4
|
import { SERVER_FN_LOOKUP } from "../start-compiler-plugin/plugin.js";
|
|
6
|
-
import {
|
|
5
|
+
import { ImportGraph, formatViolation, buildTrace } from "./trace.js";
|
|
7
6
|
import { getDefaultImportProtectionRules, getMarkerSpecifiers } from "./defaults.js";
|
|
8
7
|
import { findPostCompileUsagePos } from "./postCompileUsage.js";
|
|
9
8
|
import { matchesAny, compileMatchers } from "./matchers.js";
|
|
10
|
-
import { normalizeFilePath, dedupePatterns } from "./utils.js";
|
|
9
|
+
import { escapeRegExp, normalizeFilePath, getOrCreate, clearNormalizeFilePathCache, dedupePatterns, extractImportSources, relativizePath } from "./utils.js";
|
|
11
10
|
import { collectMockExportNamesBySource } from "./rewriteDeniedImports.js";
|
|
12
11
|
import { RESOLVED_MOCK_MODULE_ID, loadSilentMockModule, RESOLVED_MOCK_EDGE_PREFIX, loadMockEdgeModule, RESOLVED_MOCK_RUNTIME_PREFIX, loadMockRuntimeModule, RESOLVED_MARKER_PREFIX, loadMarkerModule, MOCK_MODULE_ID, MOCK_EDGE_PREFIX, MOCK_RUNTIME_PREFIX, MARKER_PREFIX, mockRuntimeModuleIdFromViolation, makeMockEdgeModuleId } from "./virtualModules.js";
|
|
13
|
-
import { pickOriginalCodeFromSourcesContent, buildLineIndex,
|
|
12
|
+
import { clearImportPatternCache, pickOriginalCodeFromSourcesContent, buildLineIndex, ImportLocCache, findPostCompileUsageLocation, findImportStatementLocationFromTransformed, buildCodeSnippet, addTraceImportLocations } from "./sourceLocation.js";
|
|
13
|
+
const SERVER_FN_LOOKUP_QUERY = "?" + SERVER_FN_LOOKUP;
|
|
14
|
+
const RESOLVED_MARKER_SERVER_ONLY = resolveViteId(`${MARKER_PREFIX}server-only`);
|
|
15
|
+
const RESOLVED_MARKER_CLIENT_ONLY = resolveViteId(`${MARKER_PREFIX}client-only`);
|
|
16
|
+
const IMPORT_PROTECTION_DEBUG = process.env.TSR_IMPORT_PROTECTION_DEBUG === "1" || process.env.TSR_IMPORT_PROTECTION_DEBUG === "true";
|
|
17
|
+
const IMPORT_PROTECTION_DEBUG_FILTER = process.env.TSR_IMPORT_PROTECTION_DEBUG_FILTER;
|
|
18
|
+
function debugLog(...args) {
|
|
19
|
+
if (!IMPORT_PROTECTION_DEBUG) return;
|
|
20
|
+
console.warn("[import-protection:debug]", ...args);
|
|
21
|
+
}
|
|
22
|
+
function matchesDebugFilter(...values) {
|
|
23
|
+
if (!IMPORT_PROTECTION_DEBUG_FILTER) return true;
|
|
24
|
+
return values.some((v) => v.includes(IMPORT_PROTECTION_DEBUG_FILTER));
|
|
25
|
+
}
|
|
14
26
|
function importProtectionPlugin(opts) {
|
|
27
|
+
let devServer = null;
|
|
28
|
+
function buildTraceFromModuleGraph(envName, env, targetFile) {
|
|
29
|
+
if (!devServer) return null;
|
|
30
|
+
const environment = devServer.environments[envName];
|
|
31
|
+
if (!environment) return null;
|
|
32
|
+
const file = normalizeFilePath(targetFile);
|
|
33
|
+
const start = environment.moduleGraph.getModuleById(file);
|
|
34
|
+
if (!start) return null;
|
|
35
|
+
const nodeIds = /* @__PURE__ */ new Map();
|
|
36
|
+
function nodeId(n) {
|
|
37
|
+
let cached = nodeIds.get(n);
|
|
38
|
+
if (cached === void 0) {
|
|
39
|
+
cached = n.id ? normalizeFilePath(n.id) : n.url ? normalizeFilePath(n.url) : "";
|
|
40
|
+
nodeIds.set(n, cached);
|
|
41
|
+
}
|
|
42
|
+
return cached;
|
|
43
|
+
}
|
|
44
|
+
const queue = [start];
|
|
45
|
+
const visited = /* @__PURE__ */ new Set([start]);
|
|
46
|
+
const parent = /* @__PURE__ */ new Map();
|
|
47
|
+
let entryRoot = null;
|
|
48
|
+
let fallbackRoot = null;
|
|
49
|
+
let qi = 0;
|
|
50
|
+
while (qi < queue.length) {
|
|
51
|
+
const node = queue[qi++];
|
|
52
|
+
const id = nodeId(node);
|
|
53
|
+
if (id && env.graph.entries.has(id)) {
|
|
54
|
+
entryRoot = node;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const importers = node.importers;
|
|
58
|
+
if (importers.size === 0) {
|
|
59
|
+
if (!fallbackRoot) fallbackRoot = node;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const imp of importers) {
|
|
63
|
+
if (visited.has(imp)) continue;
|
|
64
|
+
visited.add(imp);
|
|
65
|
+
parent.set(imp, node);
|
|
66
|
+
queue.push(imp);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const root = entryRoot ?? fallbackRoot;
|
|
70
|
+
if (!root) return null;
|
|
71
|
+
const chain = [];
|
|
72
|
+
let cur = root;
|
|
73
|
+
for (let i = 0; i < config.maxTraceDepth + 2 && cur; i++) {
|
|
74
|
+
chain.push(cur);
|
|
75
|
+
if (cur === start) break;
|
|
76
|
+
cur = parent.get(cur);
|
|
77
|
+
}
|
|
78
|
+
const steps = [];
|
|
79
|
+
for (let i = 0; i < chain.length; i++) {
|
|
80
|
+
const id = nodeId(chain[i]);
|
|
81
|
+
if (!id) continue;
|
|
82
|
+
let specifier;
|
|
83
|
+
if (i + 1 < chain.length) {
|
|
84
|
+
const nextId = nodeId(chain[i + 1]);
|
|
85
|
+
if (nextId) {
|
|
86
|
+
specifier = env.graph.reverseEdges.get(nextId)?.get(id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
steps.push(specifier ? { file: id, specifier } : { file: id });
|
|
90
|
+
}
|
|
91
|
+
return steps.length ? steps : null;
|
|
92
|
+
}
|
|
15
93
|
const config = {
|
|
16
94
|
enabled: true,
|
|
17
95
|
root: "",
|
|
18
96
|
command: "build",
|
|
19
97
|
srcDirectory: "",
|
|
20
98
|
framework: opts.framework,
|
|
99
|
+
entryFiles: [],
|
|
21
100
|
effectiveBehavior: "error",
|
|
22
101
|
mockAccess: "error",
|
|
23
102
|
logMode: "once",
|
|
@@ -35,51 +114,34 @@ function importProtectionPlugin(opts) {
|
|
|
35
114
|
};
|
|
36
115
|
const envStates = /* @__PURE__ */ new Map();
|
|
37
116
|
const shared = { fileMarkerKind: /* @__PURE__ */ new Map() };
|
|
38
|
-
function createImportLocCache(env) {
|
|
39
|
-
const cache = /* @__PURE__ */ new Map();
|
|
40
|
-
const originalSet = cache.set.bind(cache);
|
|
41
|
-
cache.set = function(key, value) {
|
|
42
|
-
originalSet(key, value);
|
|
43
|
-
const sepIdx = key.indexOf("::");
|
|
44
|
-
if (sepIdx !== -1) {
|
|
45
|
-
const file = key.slice(0, sepIdx);
|
|
46
|
-
let fileKeys = env.importLocByFile.get(file);
|
|
47
|
-
if (!fileKeys) {
|
|
48
|
-
fileKeys = /* @__PURE__ */ new Set();
|
|
49
|
-
env.importLocByFile.set(file, fileKeys);
|
|
50
|
-
}
|
|
51
|
-
fileKeys.add(key);
|
|
52
|
-
}
|
|
53
|
-
return this;
|
|
54
|
-
};
|
|
55
|
-
return cache;
|
|
56
|
-
}
|
|
57
|
-
function getMockEdgeExports(env, importerId, source) {
|
|
58
|
-
const importerFile = normalizeFilePath(importerId);
|
|
59
|
-
return env.mockExportsByImporter.get(importerFile)?.get(source) ?? [];
|
|
60
|
-
}
|
|
61
117
|
function getMarkerKindForFile(fileId) {
|
|
62
118
|
const file = normalizeFilePath(fileId);
|
|
63
119
|
return shared.fileMarkerKind.get(file);
|
|
64
120
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
121
|
+
async function rebuildAndAnnotateTrace(provider, env, envName, normalizedImporter, specifier, importerLoc, traceOverride) {
|
|
122
|
+
let trace = traceOverride ?? buildTrace(env.graph, normalizedImporter, config.maxTraceDepth);
|
|
123
|
+
if (config.command === "serve") {
|
|
124
|
+
const mgTrace = buildTraceFromModuleGraph(
|
|
125
|
+
envName,
|
|
126
|
+
env,
|
|
127
|
+
normalizedImporter
|
|
128
|
+
);
|
|
129
|
+
if (mgTrace && mgTrace.length > trace.length) {
|
|
130
|
+
trace = mgTrace;
|
|
73
131
|
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async function buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, overrides) {
|
|
77
|
-
const trace = buildTrace(
|
|
78
|
-
env.graph,
|
|
79
|
-
normalizedImporter,
|
|
80
|
-
config.maxTraceDepth
|
|
81
|
-
);
|
|
132
|
+
}
|
|
82
133
|
await addTraceImportLocations(provider, trace, env.importLocCache);
|
|
134
|
+
if (trace.length > 0) {
|
|
135
|
+
const last = trace[trace.length - 1];
|
|
136
|
+
if (!last.specifier) last.specifier = specifier;
|
|
137
|
+
if (importerLoc && last.line == null) {
|
|
138
|
+
last.line = importerLoc.line;
|
|
139
|
+
last.column = importerLoc.column;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return trace;
|
|
143
|
+
}
|
|
144
|
+
async function buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, overrides, traceOverride) {
|
|
83
145
|
const loc = await findPostCompileUsageLocation(
|
|
84
146
|
provider,
|
|
85
147
|
importer,
|
|
@@ -91,14 +153,15 @@ function importProtectionPlugin(opts) {
|
|
|
91
153
|
source,
|
|
92
154
|
env.importLocCache
|
|
93
155
|
);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
156
|
+
const trace = await rebuildAndAnnotateTrace(
|
|
157
|
+
provider,
|
|
158
|
+
env,
|
|
159
|
+
envName,
|
|
160
|
+
normalizedImporter,
|
|
161
|
+
source,
|
|
162
|
+
loc,
|
|
163
|
+
traceOverride
|
|
164
|
+
);
|
|
102
165
|
const snippet = loc ? buildCodeSnippet(provider, importer, loc) : void 0;
|
|
103
166
|
return {
|
|
104
167
|
env: envName,
|
|
@@ -112,12 +175,12 @@ function importProtectionPlugin(opts) {
|
|
|
112
175
|
...overrides
|
|
113
176
|
};
|
|
114
177
|
}
|
|
115
|
-
async function
|
|
178
|
+
async function buildMarkerViolationFromResolvedImport(provider, env, envName, envType, importer, source, resolvedId, relativePath, traceOverride) {
|
|
116
179
|
const markerKind = getMarkerKindForFile(resolvedId);
|
|
117
180
|
const violates = envType === "client" && markerKind === "server" || envType === "server" && markerKind === "client";
|
|
118
181
|
if (!violates) return void 0;
|
|
119
182
|
const normalizedImporter = normalizeFilePath(importer);
|
|
120
|
-
|
|
183
|
+
return buildViolationInfo(
|
|
121
184
|
provider,
|
|
122
185
|
env,
|
|
123
186
|
envName,
|
|
@@ -129,13 +192,9 @@ function importProtectionPlugin(opts) {
|
|
|
129
192
|
type: "marker",
|
|
130
193
|
resolved: normalizeFilePath(resolvedId),
|
|
131
194
|
message: markerKind === "server" ? `Module "${relativePath}" is marked server-only but is imported in the client environment` : `Module "${relativePath}" is marked client-only but is imported in the server environment`
|
|
132
|
-
}
|
|
195
|
+
},
|
|
196
|
+
traceOverride
|
|
133
197
|
);
|
|
134
|
-
return handleViolation.call(ctx, env, info, opts2);
|
|
135
|
-
}
|
|
136
|
-
function buildMockEdgeModuleId(env, importerId, source, runtimeId) {
|
|
137
|
-
const exports = getMockEdgeExports(env, importerId, source);
|
|
138
|
-
return makeMockEdgeModuleId(exports, source, runtimeId);
|
|
139
198
|
}
|
|
140
199
|
function getEnvType(envName) {
|
|
141
200
|
return config.envTypeMap.get(envName) ?? "server";
|
|
@@ -154,7 +213,7 @@ function importProtectionPlugin(opts) {
|
|
|
154
213
|
function getEnv(envName) {
|
|
155
214
|
let envState = envStates.get(envName);
|
|
156
215
|
if (!envState) {
|
|
157
|
-
const
|
|
216
|
+
const transformResultCache = /* @__PURE__ */ new Map();
|
|
158
217
|
envState = {
|
|
159
218
|
graph: new ImportGraph(),
|
|
160
219
|
deniedSources: /* @__PURE__ */ new Set(),
|
|
@@ -162,33 +221,46 @@ function importProtectionPlugin(opts) {
|
|
|
162
221
|
mockExportsByImporter: /* @__PURE__ */ new Map(),
|
|
163
222
|
resolveCache: /* @__PURE__ */ new Map(),
|
|
164
223
|
resolveCacheByFile: /* @__PURE__ */ new Map(),
|
|
165
|
-
importLocCache:
|
|
166
|
-
// placeholder, replaced below
|
|
167
|
-
importLocByFile,
|
|
224
|
+
importLocCache: new ImportLocCache(),
|
|
168
225
|
seenViolations: /* @__PURE__ */ new Set(),
|
|
169
|
-
transformResultCache
|
|
170
|
-
transformResultKeysByFile: /* @__PURE__ */ new Map()
|
|
226
|
+
transformResultCache,
|
|
227
|
+
transformResultKeysByFile: /* @__PURE__ */ new Map(),
|
|
228
|
+
transformResultProvider: {
|
|
229
|
+
getTransformResult(id) {
|
|
230
|
+
const fullKey = normalizePath(id);
|
|
231
|
+
const exact = transformResultCache.get(fullKey);
|
|
232
|
+
if (exact) return exact;
|
|
233
|
+
const strippedKey = normalizeFilePath(id);
|
|
234
|
+
return strippedKey !== fullKey ? transformResultCache.get(strippedKey) : void 0;
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
postTransformImports: /* @__PURE__ */ new Map(),
|
|
238
|
+
hasSeenEntry: false,
|
|
239
|
+
serverFnLookupModules: /* @__PURE__ */ new Set(),
|
|
240
|
+
pendingViolations: /* @__PURE__ */ new Map()
|
|
171
241
|
};
|
|
172
|
-
envState.importLocCache = createImportLocCache(envState);
|
|
173
242
|
envStates.set(envName, envState);
|
|
174
243
|
}
|
|
175
244
|
return envState;
|
|
176
245
|
}
|
|
246
|
+
const shouldCheckImporterCache = /* @__PURE__ */ new Map();
|
|
177
247
|
function shouldCheckImporter(importer) {
|
|
178
|
-
|
|
248
|
+
let result = shouldCheckImporterCache.get(importer);
|
|
249
|
+
if (result !== void 0) return result;
|
|
250
|
+
const relativePath = relativizePath(importer, config.root);
|
|
179
251
|
if (config.excludeMatchers.length > 0 && matchesAny(relativePath, config.excludeMatchers)) {
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (config.
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return importer.startsWith(config.srcDirectory);
|
|
252
|
+
result = false;
|
|
253
|
+
} else if (config.ignoreImporterMatchers.length > 0 && matchesAny(relativePath, config.ignoreImporterMatchers)) {
|
|
254
|
+
result = false;
|
|
255
|
+
} else if (config.includeMatchers.length > 0) {
|
|
256
|
+
result = !!matchesAny(relativePath, config.includeMatchers);
|
|
257
|
+
} else if (config.srcDirectory) {
|
|
258
|
+
result = importer.startsWith(config.srcDirectory);
|
|
259
|
+
} else {
|
|
260
|
+
result = true;
|
|
190
261
|
}
|
|
191
|
-
|
|
262
|
+
shouldCheckImporterCache.set(importer, result);
|
|
263
|
+
return result;
|
|
192
264
|
}
|
|
193
265
|
function dedupeKey(type, importer, specifier, resolved) {
|
|
194
266
|
return `${type}:${importer}:${specifier}:${resolved ?? ""}`;
|
|
@@ -200,7 +272,182 @@ function importProtectionPlugin(opts) {
|
|
|
200
272
|
return false;
|
|
201
273
|
}
|
|
202
274
|
function getRelativePath(absolutePath) {
|
|
203
|
-
return normalizePath(
|
|
275
|
+
return relativizePath(normalizePath(absolutePath), config.root);
|
|
276
|
+
}
|
|
277
|
+
function registerEntries() {
|
|
278
|
+
const { resolvedStartConfig } = opts.getConfig();
|
|
279
|
+
for (const envDef of opts.environments) {
|
|
280
|
+
const envState = getEnv(envDef.name);
|
|
281
|
+
if (resolvedStartConfig.routerFilePath) {
|
|
282
|
+
envState.graph.addEntry(
|
|
283
|
+
normalizePath(resolvedStartConfig.routerFilePath)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (resolvedStartConfig.startFilePath) {
|
|
287
|
+
envState.graph.addEntry(
|
|
288
|
+
normalizePath(resolvedStartConfig.startFilePath)
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function checkPostTransformReachability(env, file) {
|
|
294
|
+
const visited = /* @__PURE__ */ new Set();
|
|
295
|
+
const queue = [file];
|
|
296
|
+
let hasUnknownEdge = false;
|
|
297
|
+
let qi = 0;
|
|
298
|
+
while (qi < queue.length) {
|
|
299
|
+
const current = queue[qi++];
|
|
300
|
+
if (visited.has(current)) continue;
|
|
301
|
+
visited.add(current);
|
|
302
|
+
if (env.graph.entries.has(current)) {
|
|
303
|
+
return "reachable";
|
|
304
|
+
}
|
|
305
|
+
const importers = env.graph.reverseEdges.get(current);
|
|
306
|
+
if (!importers) continue;
|
|
307
|
+
for (const [parent] of importers) {
|
|
308
|
+
if (visited.has(parent)) continue;
|
|
309
|
+
const keySet = env.transformResultKeysByFile.get(parent);
|
|
310
|
+
let anyVariantCached = false;
|
|
311
|
+
let edgeLive = false;
|
|
312
|
+
if (keySet) {
|
|
313
|
+
for (const k of keySet) {
|
|
314
|
+
const resolvedImports = env.postTransformImports.get(k);
|
|
315
|
+
if (resolvedImports) {
|
|
316
|
+
anyVariantCached = true;
|
|
317
|
+
if (resolvedImports.has(current)) {
|
|
318
|
+
edgeLive = true;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (!anyVariantCached) {
|
|
325
|
+
const resolvedImports = env.postTransformImports.get(parent);
|
|
326
|
+
if (resolvedImports) {
|
|
327
|
+
anyVariantCached = true;
|
|
328
|
+
if (resolvedImports.has(current)) {
|
|
329
|
+
edgeLive = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (!anyVariantCached) {
|
|
334
|
+
const hasTransformResult = env.transformResultCache.has(parent) || (keySet ? keySet.size > 0 : false);
|
|
335
|
+
if (hasTransformResult) {
|
|
336
|
+
hasUnknownEdge = true;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
queue.push(parent);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (edgeLive) {
|
|
343
|
+
queue.push(parent);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return hasUnknownEdge ? "unknown" : "unreachable";
|
|
348
|
+
}
|
|
349
|
+
async function processPendingViolations(env, warnFn) {
|
|
350
|
+
if (env.pendingViolations.size === 0) return;
|
|
351
|
+
const toDelete = [];
|
|
352
|
+
for (const [file, violations] of env.pendingViolations) {
|
|
353
|
+
const status = env.hasSeenEntry ? checkPostTransformReachability(env, file) : "reachable";
|
|
354
|
+
if (status === "reachable") {
|
|
355
|
+
for (const pv of violations) {
|
|
356
|
+
const key = dedupeKey(
|
|
357
|
+
pv.info.type,
|
|
358
|
+
pv.info.importer,
|
|
359
|
+
pv.info.specifier,
|
|
360
|
+
pv.info.resolved
|
|
361
|
+
);
|
|
362
|
+
if (!hasSeen(env, key)) {
|
|
363
|
+
const freshTrace = await rebuildAndAnnotateTrace(
|
|
364
|
+
env.transformResultProvider,
|
|
365
|
+
env,
|
|
366
|
+
pv.info.env,
|
|
367
|
+
pv.info.importer,
|
|
368
|
+
pv.info.specifier,
|
|
369
|
+
pv.info.importerLoc
|
|
370
|
+
);
|
|
371
|
+
if (freshTrace.length > pv.info.trace.length) {
|
|
372
|
+
pv.info.trace = freshTrace;
|
|
373
|
+
}
|
|
374
|
+
if (config.onViolation) {
|
|
375
|
+
const result = config.onViolation(pv.info);
|
|
376
|
+
if (result === false) continue;
|
|
377
|
+
}
|
|
378
|
+
warnFn(formatViolation(pv.info, config.root));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
toDelete.push(file);
|
|
382
|
+
} else if (status === "unreachable") {
|
|
383
|
+
toDelete.push(file);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
for (const file of toDelete) {
|
|
387
|
+
env.pendingViolations.delete(file);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function deferViolation(env, importerFile, info, mockReturnValue) {
|
|
391
|
+
getOrCreate(env.pendingViolations, importerFile, () => []).push({
|
|
392
|
+
info,
|
|
393
|
+
mockReturnValue: typeof mockReturnValue === "string" ? mockReturnValue : mockReturnValue?.id ?? ""
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function handleViolation(ctx, env, info, violationOpts) {
|
|
397
|
+
const key = dedupeKey(
|
|
398
|
+
info.type,
|
|
399
|
+
info.importer,
|
|
400
|
+
info.specifier,
|
|
401
|
+
info.resolved
|
|
402
|
+
);
|
|
403
|
+
if (!violationOpts?.silent) {
|
|
404
|
+
if (config.onViolation) {
|
|
405
|
+
const result = config.onViolation(info);
|
|
406
|
+
if (result === false) {
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const seen = hasSeen(env, key);
|
|
411
|
+
if (config.effectiveBehavior === "error") {
|
|
412
|
+
if (!seen) ctx.error(formatViolation(info, config.root));
|
|
413
|
+
return void 0;
|
|
414
|
+
}
|
|
415
|
+
if (!seen) {
|
|
416
|
+
ctx.warn(formatViolation(info, config.root));
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
if (config.effectiveBehavior === "error") {
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
env.deniedSources.add(info.specifier);
|
|
424
|
+
getOrCreate(env.deniedEdges, info.importer, () => /* @__PURE__ */ new Set()).add(
|
|
425
|
+
info.specifier
|
|
426
|
+
);
|
|
427
|
+
if (config.command === "serve") {
|
|
428
|
+
const runtimeId = mockRuntimeModuleIdFromViolation(
|
|
429
|
+
info,
|
|
430
|
+
config.mockAccess,
|
|
431
|
+
config.root
|
|
432
|
+
);
|
|
433
|
+
const importerFile = normalizeFilePath(info.importer);
|
|
434
|
+
const exports = env.mockExportsByImporter.get(importerFile)?.get(info.specifier) ?? [];
|
|
435
|
+
return resolveViteId(
|
|
436
|
+
makeMockEdgeModuleId(exports, info.specifier, runtimeId)
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
return { id: RESOLVED_MOCK_MODULE_ID, syntheticNamedExports: true };
|
|
440
|
+
}
|
|
441
|
+
async function reportOrDeferViolation(ctx, env, importerFile, info, shouldDefer, isPreTransformResolve) {
|
|
442
|
+
if (shouldDefer) {
|
|
443
|
+
const result = handleViolation(ctx, env, info, { silent: true });
|
|
444
|
+
deferViolation(env, importerFile, info, result);
|
|
445
|
+
await processPendingViolations(env, ctx.warn.bind(ctx));
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
return handleViolation(ctx, env, info, {
|
|
449
|
+
silent: isPreTransformResolve
|
|
450
|
+
});
|
|
204
451
|
}
|
|
205
452
|
return [
|
|
206
453
|
{
|
|
@@ -215,6 +462,10 @@ function importProtectionPlugin(opts) {
|
|
|
215
462
|
config.command = viteConfig.command;
|
|
216
463
|
const { startConfig, resolvedStartConfig } = opts.getConfig();
|
|
217
464
|
config.srcDirectory = resolvedStartConfig.srcDirectory;
|
|
465
|
+
config.entryFiles = [
|
|
466
|
+
resolvedStartConfig.routerFilePath,
|
|
467
|
+
resolvedStartConfig.startFilePath
|
|
468
|
+
].filter((f) => Boolean(f));
|
|
218
469
|
const userOpts = startConfig.importProtection;
|
|
219
470
|
if (userOpts?.enabled === false) {
|
|
220
471
|
config.enabled = false;
|
|
@@ -233,8 +484,11 @@ function importProtectionPlugin(opts) {
|
|
|
233
484
|
config.logMode = userOpts?.log ?? "once";
|
|
234
485
|
config.mockAccess = userOpts?.mockAccess ?? "error";
|
|
235
486
|
config.maxTraceDepth = userOpts?.maxTraceDepth ?? 20;
|
|
236
|
-
|
|
237
|
-
|
|
487
|
+
if (userOpts?.onViolation) {
|
|
488
|
+
const fn = userOpts.onViolation;
|
|
489
|
+
config.onViolation = (info) => fn(info);
|
|
490
|
+
}
|
|
491
|
+
const defaults = getDefaultImportProtectionRules();
|
|
238
492
|
const clientSpecifiers = dedupePatterns([
|
|
239
493
|
...defaults.client.specifiers,
|
|
240
494
|
...userOpts?.client?.specifiers ?? []
|
|
@@ -261,55 +515,37 @@ function importProtectionPlugin(opts) {
|
|
|
261
515
|
userOpts.ignoreImporters
|
|
262
516
|
);
|
|
263
517
|
}
|
|
264
|
-
const markers = getMarkerSpecifiers(
|
|
518
|
+
const markers = getMarkerSpecifiers();
|
|
265
519
|
config.markerSpecifiers = {
|
|
266
520
|
serverOnly: new Set(markers.serverOnly),
|
|
267
521
|
clientOnly: new Set(markers.clientOnly)
|
|
268
522
|
};
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
envState.graph.addEntry(
|
|
273
|
-
normalizePath(resolvedStartConfig.routerFilePath)
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
if (resolvedStartConfig.startFilePath) {
|
|
277
|
-
envState.graph.addEntry(
|
|
278
|
-
normalizePath(resolvedStartConfig.startFilePath)
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
523
|
+
},
|
|
524
|
+
configureServer(server) {
|
|
525
|
+
devServer = server;
|
|
282
526
|
},
|
|
283
527
|
buildStart() {
|
|
284
528
|
if (!config.enabled) return;
|
|
529
|
+
clearNormalizeFilePathCache();
|
|
530
|
+
clearImportPatternCache();
|
|
531
|
+
shouldCheckImporterCache.clear();
|
|
285
532
|
for (const envState of envStates.values()) {
|
|
286
533
|
envState.resolveCache.clear();
|
|
287
534
|
envState.resolveCacheByFile.clear();
|
|
288
535
|
envState.importLocCache.clear();
|
|
289
|
-
envState.importLocByFile.clear();
|
|
290
536
|
envState.seenViolations.clear();
|
|
291
537
|
envState.transformResultCache.clear();
|
|
292
538
|
envState.transformResultKeysByFile.clear();
|
|
539
|
+
envState.postTransformImports.clear();
|
|
540
|
+
envState.hasSeenEntry = false;
|
|
541
|
+
envState.serverFnLookupModules.clear();
|
|
293
542
|
envState.graph.clear();
|
|
294
543
|
envState.deniedSources.clear();
|
|
295
544
|
envState.deniedEdges.clear();
|
|
296
545
|
envState.mockExportsByImporter.clear();
|
|
297
546
|
}
|
|
298
547
|
shared.fileMarkerKind.clear();
|
|
299
|
-
|
|
300
|
-
const envState = getEnv(envDef.name);
|
|
301
|
-
const { resolvedStartConfig } = opts.getConfig();
|
|
302
|
-
if (resolvedStartConfig.routerFilePath) {
|
|
303
|
-
envState.graph.addEntry(
|
|
304
|
-
normalizePath(resolvedStartConfig.routerFilePath)
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
if (resolvedStartConfig.startFilePath) {
|
|
308
|
-
envState.graph.addEntry(
|
|
309
|
-
normalizePath(resolvedStartConfig.startFilePath)
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
548
|
+
registerEntries();
|
|
313
549
|
},
|
|
314
550
|
hotUpdate(ctx) {
|
|
315
551
|
if (!config.enabled) return;
|
|
@@ -319,13 +555,7 @@ function importProtectionPlugin(opts) {
|
|
|
319
555
|
const importerFile = normalizeFilePath(id);
|
|
320
556
|
shared.fileMarkerKind.delete(importerFile);
|
|
321
557
|
for (const envState of envStates.values()) {
|
|
322
|
-
|
|
323
|
-
if (locKeys) {
|
|
324
|
-
for (const key of locKeys) {
|
|
325
|
-
envState.importLocCache.delete(key);
|
|
326
|
-
}
|
|
327
|
-
envState.importLocByFile.delete(importerFile);
|
|
328
|
-
}
|
|
558
|
+
envState.importLocCache.deleteByFile(importerFile);
|
|
329
559
|
const resolveKeys = envState.resolveCacheByFile.get(importerFile);
|
|
330
560
|
if (resolveKeys) {
|
|
331
561
|
for (const key of resolveKeys) {
|
|
@@ -336,25 +566,46 @@ function importProtectionPlugin(opts) {
|
|
|
336
566
|
envState.graph.invalidate(importerFile);
|
|
337
567
|
envState.deniedEdges.delete(importerFile);
|
|
338
568
|
envState.mockExportsByImporter.delete(importerFile);
|
|
569
|
+
envState.serverFnLookupModules.delete(importerFile);
|
|
570
|
+
envState.pendingViolations.delete(importerFile);
|
|
339
571
|
const transformKeys = envState.transformResultKeysByFile.get(importerFile);
|
|
340
572
|
if (transformKeys) {
|
|
341
573
|
for (const key of transformKeys) {
|
|
342
574
|
envState.transformResultCache.delete(key);
|
|
575
|
+
envState.postTransformImports.delete(key);
|
|
343
576
|
}
|
|
344
577
|
envState.transformResultKeysByFile.delete(importerFile);
|
|
345
578
|
} else {
|
|
346
579
|
envState.transformResultCache.delete(importerFile);
|
|
580
|
+
envState.postTransformImports.delete(importerFile);
|
|
347
581
|
}
|
|
348
582
|
}
|
|
349
583
|
}
|
|
350
584
|
}
|
|
351
585
|
},
|
|
352
586
|
async resolveId(source, importer, _options) {
|
|
353
|
-
if (!config.enabled) return void 0;
|
|
354
587
|
const envName = this.environment.name;
|
|
355
588
|
const env = getEnv(envName);
|
|
356
589
|
const envType = getEnvType(envName);
|
|
357
|
-
const provider =
|
|
590
|
+
const provider = env.transformResultProvider;
|
|
591
|
+
const isScanResolve = !!_options.scan;
|
|
592
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
593
|
+
const importerPath = importer ? normalizeFilePath(importer) : "(entry)";
|
|
594
|
+
const isEntryResolve = !importer;
|
|
595
|
+
const filtered = IMPORT_PROTECTION_DEBUG_FILTER === "entry" ? isEntryResolve : matchesDebugFilter(source, importerPath);
|
|
596
|
+
if (filtered) {
|
|
597
|
+
debugLog("resolveId", {
|
|
598
|
+
env: envName,
|
|
599
|
+
envType,
|
|
600
|
+
source,
|
|
601
|
+
importer: importerPath,
|
|
602
|
+
isEntryResolve,
|
|
603
|
+
hasSeenEntry: env.hasSeenEntry,
|
|
604
|
+
command: config.command,
|
|
605
|
+
behavior: config.effectiveBehavior
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
358
609
|
if (source === MOCK_MODULE_ID) {
|
|
359
610
|
return RESOLVED_MOCK_MODULE_ID;
|
|
360
611
|
}
|
|
@@ -369,71 +620,55 @@ function importProtectionPlugin(opts) {
|
|
|
369
620
|
}
|
|
370
621
|
if (!importer) {
|
|
371
622
|
env.graph.addEntry(source);
|
|
623
|
+
env.hasSeenEntry = true;
|
|
372
624
|
return void 0;
|
|
373
625
|
}
|
|
374
626
|
if (source.startsWith("\0") || source.startsWith("virtual:")) {
|
|
375
627
|
return void 0;
|
|
376
628
|
}
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
629
|
+
const normalizedImporter = normalizeFilePath(importer);
|
|
630
|
+
const isDirectLookup = importer.includes(SERVER_FN_LOOKUP_QUERY);
|
|
631
|
+
if (isDirectLookup) {
|
|
632
|
+
env.serverFnLookupModules.add(normalizedImporter);
|
|
633
|
+
}
|
|
634
|
+
const isPreTransformResolve = isDirectLookup || env.serverFnLookupModules.has(normalizedImporter) || isScanResolve;
|
|
635
|
+
const isDevMock = config.command === "serve" && config.effectiveBehavior === "mock";
|
|
636
|
+
const shouldDefer = isDevMock && !isPreTransformResolve;
|
|
637
|
+
const markerKind = config.markerSpecifiers.serverOnly.has(source) ? "server" : config.markerSpecifiers.clientOnly.has(source) ? "client" : void 0;
|
|
638
|
+
if (markerKind) {
|
|
639
|
+
const existing = shared.fileMarkerKind.get(normalizedImporter);
|
|
640
|
+
if (existing && existing !== markerKind) {
|
|
382
641
|
this.error(
|
|
383
|
-
`[import-protection] File "${getRelativePath(
|
|
642
|
+
`[import-protection] File "${getRelativePath(normalizedImporter)}" has both server-only and client-only markers. This is not allowed.`
|
|
384
643
|
);
|
|
385
644
|
}
|
|
386
|
-
shared.fileMarkerKind.set(
|
|
387
|
-
|
|
645
|
+
shared.fileMarkerKind.set(normalizedImporter, markerKind);
|
|
646
|
+
const violatesEnv = envType === "client" && markerKind === "server" || envType === "server" && markerKind === "client";
|
|
647
|
+
if (violatesEnv) {
|
|
388
648
|
const info = await buildViolationInfo(
|
|
389
649
|
provider,
|
|
390
650
|
env,
|
|
391
651
|
envName,
|
|
392
652
|
envType,
|
|
393
653
|
importer,
|
|
394
|
-
|
|
654
|
+
normalizedImporter,
|
|
395
655
|
source,
|
|
396
656
|
{
|
|
397
657
|
type: "marker",
|
|
398
|
-
message: `Module "${getRelativePath(
|
|
658
|
+
message: markerKind === "server" ? `Module "${getRelativePath(normalizedImporter)}" is marked server-only but is imported in the client environment` : `Module "${getRelativePath(normalizedImporter)}" is marked client-only but is imported in the server environment`
|
|
399
659
|
}
|
|
400
660
|
);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
return resolveViteId(`${MARKER_PREFIX}server-only`);
|
|
406
|
-
}
|
|
407
|
-
if (config.markerSpecifiers.clientOnly.has(source)) {
|
|
408
|
-
const resolvedImporter = normalizeFilePath(importer);
|
|
409
|
-
const existing = shared.fileMarkerKind.get(resolvedImporter);
|
|
410
|
-
if (existing && existing !== "client") {
|
|
411
|
-
this.error(
|
|
412
|
-
`[import-protection] File "${getRelativePath(resolvedImporter)}" has both server-only and client-only markers. This is not allowed.`
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
shared.fileMarkerKind.set(resolvedImporter, "client");
|
|
416
|
-
if (envType === "server") {
|
|
417
|
-
const info = await buildViolationInfo(
|
|
418
|
-
provider,
|
|
661
|
+
await reportOrDeferViolation(
|
|
662
|
+
this,
|
|
419
663
|
env,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
source,
|
|
425
|
-
{
|
|
426
|
-
type: "marker",
|
|
427
|
-
message: `Module "${getRelativePath(resolvedImporter)}" is marked client-only but is imported in the server environment`
|
|
428
|
-
}
|
|
664
|
+
normalizedImporter,
|
|
665
|
+
info,
|
|
666
|
+
shouldDefer,
|
|
667
|
+
isPreTransformResolve
|
|
429
668
|
);
|
|
430
|
-
handleViolation.call(this, env, info, {
|
|
431
|
-
silent: isPreTransformResolve
|
|
432
|
-
});
|
|
433
669
|
}
|
|
434
|
-
return
|
|
670
|
+
return markerKind === "server" ? RESOLVED_MARKER_SERVER_ONLY : RESOLVED_MARKER_CLIENT_ONLY;
|
|
435
671
|
}
|
|
436
|
-
const normalizedImporter = normalizeFilePath(importer);
|
|
437
672
|
if (!shouldCheckImporter(normalizedImporter)) {
|
|
438
673
|
return void 0;
|
|
439
674
|
}
|
|
@@ -452,32 +687,39 @@ function importProtectionPlugin(opts) {
|
|
|
452
687
|
{
|
|
453
688
|
type: "specifier",
|
|
454
689
|
pattern: specifierMatch.pattern,
|
|
455
|
-
message: `Import "${source}" is denied in the
|
|
690
|
+
message: `Import "${source}" is denied in the ${envType} environment`
|
|
456
691
|
}
|
|
457
692
|
);
|
|
458
|
-
return
|
|
459
|
-
|
|
460
|
-
|
|
693
|
+
return reportOrDeferViolation(
|
|
694
|
+
this,
|
|
695
|
+
env,
|
|
696
|
+
normalizedImporter,
|
|
697
|
+
info,
|
|
698
|
+
shouldDefer,
|
|
699
|
+
isPreTransformResolve
|
|
700
|
+
);
|
|
461
701
|
}
|
|
462
702
|
const cacheKey = `${normalizedImporter}:${source}`;
|
|
463
703
|
let resolved;
|
|
464
704
|
if (env.resolveCache.has(cacheKey)) {
|
|
465
|
-
resolved = env.resolveCache.get(cacheKey)
|
|
705
|
+
resolved = env.resolveCache.get(cacheKey) ?? null;
|
|
466
706
|
} else {
|
|
467
707
|
const result = await this.resolve(source, importer, {
|
|
468
708
|
skipSelf: true
|
|
469
709
|
});
|
|
470
710
|
resolved = result ? normalizeFilePath(result.id) : null;
|
|
471
711
|
env.resolveCache.set(cacheKey, resolved);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
fileKeys.add(cacheKey);
|
|
712
|
+
getOrCreate(
|
|
713
|
+
env.resolveCacheByFile,
|
|
714
|
+
normalizedImporter,
|
|
715
|
+
() => /* @__PURE__ */ new Set()
|
|
716
|
+
).add(cacheKey);
|
|
478
717
|
}
|
|
479
718
|
if (resolved) {
|
|
480
719
|
const relativePath = getRelativePath(resolved);
|
|
720
|
+
if (isPreTransformResolve && !isScanResolve) {
|
|
721
|
+
env.serverFnLookupModules.add(resolved);
|
|
722
|
+
}
|
|
481
723
|
env.graph.addEdge(resolved, normalizedImporter, source);
|
|
482
724
|
const fileMatch = matchers.files.length > 0 ? matchesAny(relativePath, matchers.files) : void 0;
|
|
483
725
|
if (fileMatch) {
|
|
@@ -493,15 +735,19 @@ function importProtectionPlugin(opts) {
|
|
|
493
735
|
type: "file",
|
|
494
736
|
pattern: fileMatch.pattern,
|
|
495
737
|
resolved,
|
|
496
|
-
message: `Import "${source}" (resolved to "${relativePath}") is denied in the
|
|
738
|
+
message: `Import "${source}" (resolved to "${relativePath}") is denied in the ${envType} environment`
|
|
497
739
|
}
|
|
498
740
|
);
|
|
499
|
-
return
|
|
500
|
-
|
|
501
|
-
|
|
741
|
+
return reportOrDeferViolation(
|
|
742
|
+
this,
|
|
743
|
+
env,
|
|
744
|
+
normalizedImporter,
|
|
745
|
+
info,
|
|
746
|
+
shouldDefer,
|
|
747
|
+
isPreTransformResolve
|
|
748
|
+
);
|
|
502
749
|
}
|
|
503
|
-
const
|
|
504
|
-
this,
|
|
750
|
+
const markerInfo = await buildMarkerViolationFromResolvedImport(
|
|
505
751
|
provider,
|
|
506
752
|
env,
|
|
507
753
|
envName,
|
|
@@ -509,11 +755,17 @@ function importProtectionPlugin(opts) {
|
|
|
509
755
|
importer,
|
|
510
756
|
source,
|
|
511
757
|
resolved,
|
|
512
|
-
relativePath
|
|
513
|
-
{ silent: isPreTransformResolve }
|
|
758
|
+
relativePath
|
|
514
759
|
);
|
|
515
|
-
if (
|
|
516
|
-
return
|
|
760
|
+
if (markerInfo) {
|
|
761
|
+
return reportOrDeferViolation(
|
|
762
|
+
this,
|
|
763
|
+
env,
|
|
764
|
+
normalizedImporter,
|
|
765
|
+
markerInfo,
|
|
766
|
+
shouldDefer,
|
|
767
|
+
isPreTransformResolve
|
|
768
|
+
);
|
|
517
769
|
}
|
|
518
770
|
}
|
|
519
771
|
return void 0;
|
|
@@ -521,11 +773,23 @@ function importProtectionPlugin(opts) {
|
|
|
521
773
|
load: {
|
|
522
774
|
filter: {
|
|
523
775
|
id: new RegExp(
|
|
524
|
-
|
|
776
|
+
[
|
|
777
|
+
RESOLVED_MOCK_MODULE_ID,
|
|
778
|
+
RESOLVED_MARKER_PREFIX,
|
|
779
|
+
RESOLVED_MOCK_EDGE_PREFIX,
|
|
780
|
+
RESOLVED_MOCK_RUNTIME_PREFIX
|
|
781
|
+
].map(escapeRegExp).join("|")
|
|
525
782
|
)
|
|
526
783
|
},
|
|
527
784
|
handler(id) {
|
|
528
|
-
if (
|
|
785
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
786
|
+
if (matchesDebugFilter(id)) {
|
|
787
|
+
debugLog("load:handler", {
|
|
788
|
+
env: this.environment.name,
|
|
789
|
+
id: normalizePath(id)
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
529
793
|
if (id === RESOLVED_MOCK_MODULE_ID) {
|
|
530
794
|
return loadSilentMockModule();
|
|
531
795
|
}
|
|
@@ -547,25 +811,9 @@ function importProtectionPlugin(opts) {
|
|
|
547
811
|
}
|
|
548
812
|
},
|
|
549
813
|
{
|
|
550
|
-
//
|
|
551
|
-
// `enforce: 'pre'`
|
|
552
|
-
//
|
|
553
|
-
// so that the `resolveId` hook (in the main plugin above) can look up
|
|
554
|
-
// the importer's transform result and map violation locations back to
|
|
555
|
-
// original source.
|
|
556
|
-
//
|
|
557
|
-
// Why not use `ctx.load()` in `resolveId`?
|
|
558
|
-
// - Vite dev: `this.load()` returns a ModuleInfo proxy that throws on
|
|
559
|
-
// `.code` access — code is not exposed.
|
|
560
|
-
// - Rollup build: `ModuleInfo` has `.code` but NOT `.map`, so we
|
|
561
|
-
// can't map generated positions back to original source.
|
|
562
|
-
//
|
|
563
|
-
// By caching in the transform hook we get both code and the composed
|
|
564
|
-
// sourcemap that chains all the way back to the original file.
|
|
565
|
-
//
|
|
566
|
-
// Performance: only files under `srcDirectory` are cached because only
|
|
567
|
-
// those can be importers in a violation. Third-party code in
|
|
568
|
-
// node_modules is never checked.
|
|
814
|
+
// Captures transformed code + composed sourcemap for location mapping.
|
|
815
|
+
// Runs after all `enforce: 'pre'` hooks (including the Start compiler).
|
|
816
|
+
// Only files under `srcDirectory` are cached.
|
|
569
817
|
name: "tanstack-start-core:import-protection-transform-cache",
|
|
570
818
|
applyToEnvironment(env) {
|
|
571
819
|
if (!config.enabled) return false;
|
|
@@ -577,10 +825,18 @@ function importProtectionPlugin(opts) {
|
|
|
577
825
|
include: [/\.[cm]?[tj]sx?($|\?)/]
|
|
578
826
|
}
|
|
579
827
|
},
|
|
580
|
-
handler(code, id) {
|
|
581
|
-
if (!config.enabled) return void 0;
|
|
828
|
+
async handler(code, id) {
|
|
582
829
|
const envName = this.environment.name;
|
|
583
830
|
const file = normalizeFilePath(id);
|
|
831
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
832
|
+
if (matchesDebugFilter(file)) {
|
|
833
|
+
debugLog("transform-cache", {
|
|
834
|
+
env: envName,
|
|
835
|
+
id: normalizePath(id),
|
|
836
|
+
file
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
584
840
|
if (!shouldCheckImporter(file)) {
|
|
585
841
|
return void 0;
|
|
586
842
|
}
|
|
@@ -601,17 +857,20 @@ function importProtectionPlugin(opts) {
|
|
|
601
857
|
const lineIndex = buildLineIndex(code);
|
|
602
858
|
const cacheKey = normalizePath(id);
|
|
603
859
|
const envState = getEnv(envName);
|
|
860
|
+
if (id.includes(SERVER_FN_LOOKUP_QUERY)) {
|
|
861
|
+
envState.serverFnLookupModules.add(file);
|
|
862
|
+
}
|
|
604
863
|
envState.transformResultCache.set(cacheKey, {
|
|
605
864
|
code,
|
|
606
865
|
map,
|
|
607
866
|
originalCode,
|
|
608
867
|
lineIndex
|
|
609
868
|
});
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
869
|
+
const keySet = getOrCreate(
|
|
870
|
+
envState.transformResultKeysByFile,
|
|
871
|
+
file,
|
|
872
|
+
() => /* @__PURE__ */ new Set()
|
|
873
|
+
);
|
|
615
874
|
keySet.add(cacheKey);
|
|
616
875
|
if (cacheKey !== file) {
|
|
617
876
|
envState.transformResultCache.set(file, {
|
|
@@ -622,6 +881,24 @@ function importProtectionPlugin(opts) {
|
|
|
622
881
|
});
|
|
623
882
|
keySet.add(file);
|
|
624
883
|
}
|
|
884
|
+
const importSources = extractImportSources(code);
|
|
885
|
+
const resolvedChildren = /* @__PURE__ */ new Set();
|
|
886
|
+
for (const src of importSources) {
|
|
887
|
+
try {
|
|
888
|
+
const resolved = await this.resolve(src, id, { skipSelf: true });
|
|
889
|
+
if (resolved && !resolved.external) {
|
|
890
|
+
const resolvedPath = normalizeFilePath(resolved.id);
|
|
891
|
+
resolvedChildren.add(resolvedPath);
|
|
892
|
+
envState.graph.addEdge(resolvedPath, file, src);
|
|
893
|
+
}
|
|
894
|
+
} catch {
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
envState.postTransformImports.set(cacheKey, resolvedChildren);
|
|
898
|
+
if (cacheKey !== file) {
|
|
899
|
+
envState.postTransformImports.set(file, resolvedChildren);
|
|
900
|
+
}
|
|
901
|
+
await processPendingViolations(envState, this.warn.bind(this));
|
|
625
902
|
return void 0;
|
|
626
903
|
}
|
|
627
904
|
}
|
|
@@ -644,7 +921,6 @@ function importProtectionPlugin(opts) {
|
|
|
644
921
|
}
|
|
645
922
|
},
|
|
646
923
|
handler(code, id) {
|
|
647
|
-
if (!config.enabled) return void 0;
|
|
648
924
|
const envName = this.environment.name;
|
|
649
925
|
const envState = envStates.get(envName);
|
|
650
926
|
if (!envState) return void 0;
|
|
@@ -661,56 +937,11 @@ function importProtectionPlugin(opts) {
|
|
|
661
937
|
}
|
|
662
938
|
}
|
|
663
939
|
];
|
|
664
|
-
function handleViolation(env, info, opts2) {
|
|
665
|
-
const key = dedupeKey(
|
|
666
|
-
info.type,
|
|
667
|
-
info.importer,
|
|
668
|
-
info.specifier,
|
|
669
|
-
info.resolved
|
|
670
|
-
);
|
|
671
|
-
if (!opts2?.silent) {
|
|
672
|
-
if (config.onViolation) {
|
|
673
|
-
const result = config.onViolation(info);
|
|
674
|
-
if (result === false) {
|
|
675
|
-
return void 0;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
const seen = hasSeen(env, key);
|
|
679
|
-
if (config.effectiveBehavior === "error") {
|
|
680
|
-
if (!seen) this.error(formatViolation(info, config.root));
|
|
681
|
-
return void 0;
|
|
682
|
-
}
|
|
683
|
-
if (!seen) {
|
|
684
|
-
this.warn(formatViolation(info, config.root));
|
|
685
|
-
}
|
|
686
|
-
} else {
|
|
687
|
-
if (config.effectiveBehavior === "error") {
|
|
688
|
-
return void 0;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
env.deniedSources.add(info.specifier);
|
|
692
|
-
let edgeSet = env.deniedEdges.get(info.importer);
|
|
693
|
-
if (!edgeSet) {
|
|
694
|
-
edgeSet = /* @__PURE__ */ new Set();
|
|
695
|
-
env.deniedEdges.set(info.importer, edgeSet);
|
|
696
|
-
}
|
|
697
|
-
edgeSet.add(info.specifier);
|
|
698
|
-
if (config.command === "serve") {
|
|
699
|
-
const runtimeId = mockRuntimeModuleIdFromViolation(
|
|
700
|
-
info,
|
|
701
|
-
config.mockAccess,
|
|
702
|
-
config.root
|
|
703
|
-
);
|
|
704
|
-
return resolveViteId(
|
|
705
|
-
buildMockEdgeModuleId(env, info.importer, info.specifier, runtimeId)
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
return { id: RESOLVED_MOCK_MODULE_ID, syntheticNamedExports: true };
|
|
709
|
-
}
|
|
710
940
|
}
|
|
711
941
|
export {
|
|
712
942
|
RESOLVED_MOCK_MODULE_ID,
|
|
713
943
|
dedupePatterns,
|
|
944
|
+
extractImportSources,
|
|
714
945
|
importProtectionPlugin
|
|
715
946
|
};
|
|
716
947
|
//# sourceMappingURL=plugin.js.map
|