@octanejs/tanstack-start 0.1.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/LICENSE +22 -0
- package/README.md +52 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/package.json +149 -0
- package/src/client-only.d.ts +1 -0
- package/src/client-only.js +0 -0
- package/src/client-rpc.d.ts +1 -0
- package/src/client-rpc.js +1 -0
- package/src/client.d.ts +3 -0
- package/src/client.js +2 -0
- package/src/default-entry/client.js +12 -0
- package/src/default-entry/server.js +13 -0
- package/src/default-entry/start.js +1 -0
- package/src/default-entry-paths.d.ts +5 -0
- package/src/default-entry-paths.js +11 -0
- package/src/index.d.ts +11 -0
- package/src/index.js +22 -0
- package/src/internal/README.md +15 -0
- package/src/internal/router-generator/config.d.ts +255 -0
- package/src/internal/router-generator/config.js +134 -0
- package/src/internal/router-generator/filesystem/physical/getRouteNodes.d.ts +53 -0
- package/src/internal/router-generator/filesystem/physical/getRouteNodes.js +335 -0
- package/src/internal/router-generator/filesystem/physical/rootPathId.d.ts +1 -0
- package/src/internal/router-generator/filesystem/physical/rootPathId.js +4 -0
- package/src/internal/router-generator/filesystem/virtual/config.d.ts +3 -0
- package/src/internal/router-generator/filesystem/virtual/config.js +36 -0
- package/src/internal/router-generator/filesystem/virtual/getRouteNodes.d.ts +31 -0
- package/src/internal/router-generator/filesystem/virtual/getRouteNodes.js +272 -0
- package/src/internal/router-generator/filesystem/virtual/loadConfigFile.d.ts +1 -0
- package/src/internal/router-generator/filesystem/virtual/loadConfigFile.js +10 -0
- package/src/internal/router-generator/generator.d.ts +119 -0
- package/src/internal/router-generator/generator.js +1080 -0
- package/src/internal/router-generator/index.d.ts +40 -0
- package/src/internal/router-generator/index.js +49 -0
- package/src/internal/router-generator/logger.d.ts +8 -0
- package/src/internal/router-generator/logger.js +30 -0
- package/src/internal/router-generator/plugin/types.d.ts +37 -0
- package/src/internal/router-generator/template.d.ts +38 -0
- package/src/internal/router-generator/template.js +220 -0
- package/src/internal/router-generator/transform/transform.d.ts +8 -0
- package/src/internal/router-generator/transform/transform.js +385 -0
- package/src/internal/router-generator/transform/types.d.ts +34 -0
- package/src/internal/router-generator/types.d.ts +73 -0
- package/src/internal/router-generator/utils.d.ts +270 -0
- package/src/internal/router-generator/utils.js +864 -0
- package/src/internal/router-generator/validate-route-params.d.ts +13 -0
- package/src/internal/router-generator/validate-route-params.js +75 -0
- package/src/internal/router-plugin/context.d.ts +2 -0
- package/src/internal/router-plugin/context.js +2 -0
- package/src/internal/router-plugin/core/code-splitter/compilers.d.ts +69 -0
- package/src/internal/router-plugin/core/code-splitter/compilers.js +1042 -0
- package/src/internal/router-plugin/core/code-splitter/framework-options.d.ts +11 -0
- package/src/internal/router-plugin/core/code-splitter/framework-options.js +41 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/framework-plugins.d.ts +12 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/framework-plugins.js +35 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/octane-hmr-split-route-components.d.ts +5 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/octane-hmr-split-route-components.js +106 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/octane-split-route-components.d.ts +8 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/octane-split-route-components.js +85 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-refresh-ignored-route-exports.d.ts +2 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-refresh-ignored-route-exports.js +55 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-refresh-route-components.d.ts +2 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-refresh-route-components.js +50 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-stable-hmr-split-route-components.d.ts +5 -0
- package/src/internal/router-plugin/core/code-splitter/plugins/react-stable-hmr-split-route-components.js +54 -0
- package/src/internal/router-plugin/core/code-splitter/plugins.d.ts +62 -0
- package/src/internal/router-plugin/core/code-splitter/types.d.ts +9 -0
- package/src/internal/router-plugin/core/config.d.ts +294 -0
- package/src/internal/router-plugin/core/config.js +56 -0
- package/src/internal/router-plugin/core/constants.d.ts +12 -0
- package/src/internal/router-plugin/core/constants.js +13 -0
- package/src/internal/router-plugin/core/framework-plugin-order.d.ts +8 -0
- package/src/internal/router-plugin/core/framework-plugin-order.js +78 -0
- package/src/internal/router-plugin/core/hmr/handle-route-update.d.ts +1 -0
- package/src/internal/router-plugin/core/hmr/handle-route-update.js +126 -0
- package/src/internal/router-plugin/core/hmr/index.d.ts +5 -0
- package/src/internal/router-plugin/core/hmr/select-adapter.d.ts +17 -0
- package/src/internal/router-plugin/core/hmr/select-adapter.js +24 -0
- package/src/internal/router-plugin/core/hmr/vite-adapter.d.ts +18 -0
- package/src/internal/router-plugin/core/hmr/vite-adapter.js +73 -0
- package/src/internal/router-plugin/core/hmr/webpack-adapter.d.ts +22 -0
- package/src/internal/router-plugin/core/hmr/webpack-adapter.js +78 -0
- package/src/internal/router-plugin/core/router-code-splitter-plugin.d.ts +10 -0
- package/src/internal/router-plugin/core/router-code-splitter-plugin.js +244 -0
- package/src/internal/router-plugin/core/router-composed-plugin.d.ts +5 -0
- package/src/internal/router-plugin/core/router-composed-plugin.js +62 -0
- package/src/internal/router-plugin/core/router-generator-plugin.d.ts +10 -0
- package/src/internal/router-plugin/core/router-generator-plugin.js +125 -0
- package/src/internal/router-plugin/core/router-hmr-plugin.d.ts +13 -0
- package/src/internal/router-plugin/core/router-hmr-plugin.js +101 -0
- package/src/internal/router-plugin/core/router-plugin-context.d.ts +5 -0
- package/src/internal/router-plugin/core/router-plugin-context.js +6 -0
- package/src/internal/router-plugin/core/utils.d.ts +17 -0
- package/src/internal/router-plugin/core/utils.js +47 -0
- package/src/internal/router-plugin/esbuild.d.ts +197 -0
- package/src/internal/router-plugin/esbuild.js +58 -0
- package/src/internal/router-plugin/index.d.ts +19 -0
- package/src/internal/router-plugin/index.js +17 -0
- package/src/internal/router-plugin/rspack.d.ts +61 -0
- package/src/internal/router-plugin/rspack.js +92 -0
- package/src/internal/router-plugin/vite.d.ts +201 -0
- package/src/internal/router-plugin/vite.js +62 -0
- package/src/internal/router-plugin/webpack.d.ts +50 -0
- package/src/internal/router-plugin/webpack.js +77 -0
- package/src/internal/start-plugin-core/build-sitemap.d.ts +34 -0
- package/src/internal/start-plugin-core/build-sitemap.js +125 -0
- package/src/internal/start-plugin-core/config-context.d.ts +30 -0
- package/src/internal/start-plugin-core/config-context.js +89 -0
- package/src/internal/start-plugin-core/constants.d.ts +20 -0
- package/src/internal/start-plugin-core/constants.js +24 -0
- package/src/internal/start-plugin-core/debug.d.ts +1 -0
- package/src/internal/start-plugin-core/hydrate-when-transform.d.ts +5 -0
- package/src/internal/start-plugin-core/hydrate-when-transform.js +661 -0
- package/src/internal/start-plugin-core/hydration-constants.d.ts +1 -0
- package/src/internal/start-plugin-core/hydration-constants.js +4 -0
- package/src/internal/start-plugin-core/import-protection/adapterUtils.d.ts +40 -0
- package/src/internal/start-plugin-core/import-protection/adapterUtils.js +42 -0
- package/src/internal/start-plugin-core/import-protection/analysis.d.ts +63 -0
- package/src/internal/start-plugin-core/import-protection/analysis.js +533 -0
- package/src/internal/start-plugin-core/import-protection/constants.d.ts +11 -0
- package/src/internal/start-plugin-core/import-protection/constants.js +39 -0
- package/src/internal/start-plugin-core/import-protection/defaults.d.ts +19 -0
- package/src/internal/start-plugin-core/import-protection/defaults.js +34 -0
- package/src/internal/start-plugin-core/import-protection/extensionlessAbsoluteIdResolver.d.ts +22 -0
- package/src/internal/start-plugin-core/import-protection/extensionlessAbsoluteIdResolver.js +94 -0
- package/src/internal/start-plugin-core/import-protection/matchers.d.ts +18 -0
- package/src/internal/start-plugin-core/import-protection/matchers.js +39 -0
- package/src/internal/start-plugin-core/import-protection/rewrite.d.ts +29 -0
- package/src/internal/start-plugin-core/import-protection/rewrite.js +184 -0
- package/src/internal/start-plugin-core/import-protection/sourceLocation.d.ts +170 -0
- package/src/internal/start-plugin-core/import-protection/sourceLocation.js +376 -0
- package/src/internal/start-plugin-core/import-protection/trace.d.ts +89 -0
- package/src/internal/start-plugin-core/import-protection/trace.js +186 -0
- package/src/internal/start-plugin-core/import-protection/utils.d.ts +78 -0
- package/src/internal/start-plugin-core/import-protection/utils.js +171 -0
- package/src/internal/start-plugin-core/import-protection/virtualModules.d.ts +38 -0
- package/src/internal/start-plugin-core/import-protection/virtualModules.js +241 -0
- package/src/internal/start-plugin-core/index.d.ts +8 -0
- package/src/internal/start-plugin-core/index.js +2 -0
- package/src/internal/start-plugin-core/planning.d.ts +44 -0
- package/src/internal/start-plugin-core/planning.js +98 -0
- package/src/internal/start-plugin-core/post-build.d.ts +12 -0
- package/src/internal/start-plugin-core/post-build.js +40 -0
- package/src/internal/start-plugin-core/prerender.d.ts +17 -0
- package/src/internal/start-plugin-core/prerender.js +172 -0
- package/src/internal/start-plugin-core/queue.d.ts +37 -0
- package/src/internal/start-plugin-core/queue.js +118 -0
- package/src/internal/start-plugin-core/resolve-entries.d.ts +10 -0
- package/src/internal/start-plugin-core/resolve-entries.js +32 -0
- package/src/internal/start-plugin-core/schema.d.ts +1799 -0
- package/src/internal/start-plugin-core/schema.js +252 -0
- package/src/internal/start-plugin-core/serialization-adapters-module.d.ts +18 -0
- package/src/internal/start-plugin-core/serialization-adapters-module.js +37 -0
- package/src/internal/start-plugin-core/start-compiler/compiler.d.ts +181 -0
- package/src/internal/start-plugin-core/start-compiler/compiler.js +1078 -0
- package/src/internal/start-plugin-core/start-compiler/config.d.ts +20 -0
- package/src/internal/start-plugin-core/start-compiler/config.js +84 -0
- package/src/internal/start-plugin-core/start-compiler/handleClientOnlyJSX.d.ts +24 -0
- package/src/internal/start-plugin-core/start-compiler/handleClientOnlyJSX.js +25 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateIsomorphicFn.d.ts +11 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateIsomorphicFn.js +24 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateMiddleware.d.ts +11 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateMiddleware.js +35 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateServerFn.d.ts +22 -0
- package/src/internal/start-plugin-core/start-compiler/handleCreateServerFn.js +292 -0
- package/src/internal/start-plugin-core/start-compiler/handleEnvOnly.d.ts +14 -0
- package/src/internal/start-plugin-core/start-compiler/handleEnvOnly.js +41 -0
- package/src/internal/start-plugin-core/start-compiler/host.d.ts +44 -0
- package/src/internal/start-plugin-core/start-compiler/host.js +75 -0
- package/src/internal/start-plugin-core/start-compiler/server-fn-resolver-module.d.ts +10 -0
- package/src/internal/start-plugin-core/start-compiler/server-fn-resolver-module.js +87 -0
- package/src/internal/start-plugin-core/start-compiler/types.d.ts +119 -0
- package/src/internal/start-plugin-core/start-compiler/utils.d.ts +27 -0
- package/src/internal/start-plugin-core/start-compiler/utils.js +36 -0
- package/src/internal/start-plugin-core/start-manifest-plugin/inlineCss.d.ts +16 -0
- package/src/internal/start-plugin-core/start-manifest-plugin/inlineCss.js +131 -0
- package/src/internal/start-plugin-core/start-manifest-plugin/manifestBuilder.d.ts +64 -0
- package/src/internal/start-plugin-core/start-manifest-plugin/manifestBuilder.js +441 -0
- package/src/internal/start-plugin-core/start-router-plugin/constants.d.ts +1 -0
- package/src/internal/start-plugin-core/start-router-plugin/constants.js +4 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/compose-generator-plugins.d.ts +6 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/compose-generator-plugins.js +6 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/prerender-routes-plugin.d.ts +6 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/prerender-routes-plugin.js +27 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/routes-manifest-plugin.d.ts +6 -0
- package/src/internal/start-plugin-core/start-router-plugin/generator-plugins/routes-manifest-plugin.js +34 -0
- package/src/internal/start-plugin-core/start-router-plugin/pruneServerOnlySubtrees.d.ts +11 -0
- package/src/internal/start-plugin-core/start-router-plugin/pruneServerOnlySubtrees.js +42 -0
- package/src/internal/start-plugin-core/start-router-plugin/route-tree-footer.d.ts +6 -0
- package/src/internal/start-plugin-core/start-router-plugin/route-tree-footer.js +59 -0
- package/src/internal/start-plugin-core/types.d.ts +135 -0
- package/src/internal/start-plugin-core/types.js +4 -0
- package/src/internal/start-plugin-core/utils.d.ts +13 -0
- package/src/internal/start-plugin-core/utils.js +42 -0
- package/src/internal/start-plugin-core/vite/createVirtualModule.d.ts +15 -0
- package/src/internal/start-plugin-core/vite/createVirtualModule.js +27 -0
- package/src/internal/start-plugin-core/vite/dev-server-plugin/dev-styles.d.ts +26 -0
- package/src/internal/start-plugin-core/vite/dev-server-plugin/dev-styles.js +165 -0
- package/src/internal/start-plugin-core/vite/dev-server-plugin/plugin.d.ts +11 -0
- package/src/internal/start-plugin-core/vite/dev-server-plugin/plugin.js +170 -0
- package/src/internal/start-plugin-core/vite/import-protection-plugin/plugin.d.ts +4 -0
- package/src/internal/start-plugin-core/vite/import-protection-plugin/plugin.js +1553 -0
- package/src/internal/start-plugin-core/vite/import-protection-plugin/types.d.ts +103 -0
- package/src/internal/start-plugin-core/vite/import-protection-plugin/virtualModules.d.ts +40 -0
- package/src/internal/start-plugin-core/vite/import-protection-plugin/virtualModules.js +68 -0
- package/src/internal/start-plugin-core/vite/index.d.ts +13 -0
- package/src/internal/start-plugin-core/vite/index.js +4 -0
- package/src/internal/start-plugin-core/vite/load-env-plugin/plugin.d.ts +2 -0
- package/src/internal/start-plugin-core/vite/load-env-plugin/plugin.js +13 -0
- package/src/internal/start-plugin-core/vite/output-directory.d.ts +7 -0
- package/src/internal/start-plugin-core/vite/output-directory.js +18 -0
- package/src/internal/start-plugin-core/vite/planning.d.ts +112 -0
- package/src/internal/start-plugin-core/vite/planning.js +152 -0
- package/src/internal/start-plugin-core/vite/plugin.d.ts +7 -0
- package/src/internal/start-plugin-core/vite/plugin.js +208 -0
- package/src/internal/start-plugin-core/vite/plugins.d.ts +17 -0
- package/src/internal/start-plugin-core/vite/plugins.js +72 -0
- package/src/internal/start-plugin-core/vite/post-server-build.d.ts +9 -0
- package/src/internal/start-plugin-core/vite/post-server-build.js +22 -0
- package/src/internal/start-plugin-core/vite/prerender.d.ts +9 -0
- package/src/internal/start-plugin-core/vite/prerender.js +52 -0
- package/src/internal/start-plugin-core/vite/preview-server-plugin/plugin.d.ts +2 -0
- package/src/internal/start-plugin-core/vite/preview-server-plugin/plugin.js +54 -0
- package/src/internal/start-plugin-core/vite/schema.d.ts +2423 -0
- package/src/internal/start-plugin-core/vite/schema.js +17 -0
- package/src/internal/start-plugin-core/vite/serialization-adapters-plugin.d.ts +5 -0
- package/src/internal/start-plugin-core/vite/serialization-adapters-plugin.js +63 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/hot-update.d.ts +5 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/hot-update.js +14 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/module-specifier.d.ts +5 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/module-specifier.js +25 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/plugin.d.ts +29 -0
- package/src/internal/start-plugin-core/vite/start-compiler-plugin/plugin.js +380 -0
- package/src/internal/start-plugin-core/vite/start-manifest-plugin/normalized-client-build.d.ts +11 -0
- package/src/internal/start-plugin-core/vite/start-manifest-plugin/normalized-client-build.js +114 -0
- package/src/internal/start-plugin-core/vite/start-manifest-plugin/plugin.d.ts +3 -0
- package/src/internal/start-plugin-core/vite/start-manifest-plugin/plugin.js +103 -0
- package/src/internal/start-plugin-core/vite/start-router-plugin/plugin.d.ts +9 -0
- package/src/internal/start-plugin-core/vite/start-router-plugin/plugin.js +143 -0
- package/src/internal/start-plugin-core/vite/types.d.ts +17 -0
- package/src/package-names.js +10 -0
- package/src/plugin-vite.d.ts +23 -0
- package/src/plugin-vite.js +62 -0
- package/src/runtime/client/StartClient.d.ts +11 -0
- package/src/runtime/client/StartClient.js +16 -0
- package/src/runtime/client/hydrateStart.d.ts +4 -0
- package/src/runtime/client/hydrateStart.js +30 -0
- package/src/runtime/server/StartServer.d.ts +10 -0
- package/src/runtime/server/StartServer.js +6 -0
- package/src/runtime/server/defaultRenderHandler.d.ts +3 -0
- package/src/runtime/server/defaultRenderHandler.js +11 -0
- package/src/runtime/server/defaultStreamHandler.d.ts +3 -0
- package/src/runtime/server/defaultStreamHandler.js +13 -0
- package/src/server-entry.d.ts +11 -0
- package/src/server-entry.js +13 -0
- package/src/server-only.d.ts +1 -0
- package/src/server-only.js +0 -0
- package/src/server-rpc.d.ts +1 -0
- package/src/server-rpc.js +1 -0
- package/src/server.d.ts +6 -0
- package/src/server.js +4 -0
- package/src/ssr-rpc.d.ts +1 -0
- package/src/ssr-rpc.js +1 -0
- package/src/use-server-fn.d.ts +3 -0
- package/src/use-server-fn.js +18 -0
- package/src/validate-options.js +7 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { getOrCreate, normalizeFilePath } from './utils.js';
|
|
2
|
+
import {
|
|
3
|
+
findOriginalUnsafeUsagePosFromResult,
|
|
4
|
+
findPostCompileUsagePosFromResult,
|
|
5
|
+
getImportSpecifierLocationFromResult,
|
|
6
|
+
} from './analysis.js';
|
|
7
|
+
import * as path$1 from 'pathe';
|
|
8
|
+
import { SourceMapConsumer } from 'source-map';
|
|
9
|
+
//#region src/import-protection/sourceLocation.ts
|
|
10
|
+
function buildLineIndex(code) {
|
|
11
|
+
const offsets = [0];
|
|
12
|
+
for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) offsets.push(i + 1);
|
|
13
|
+
return { offsets };
|
|
14
|
+
}
|
|
15
|
+
function upperBound(values, x) {
|
|
16
|
+
let lo = 0;
|
|
17
|
+
let hi = values.length;
|
|
18
|
+
while (lo < hi) {
|
|
19
|
+
const mid = (lo + hi) >> 1;
|
|
20
|
+
if (values[mid] <= x) lo = mid + 1;
|
|
21
|
+
else hi = mid;
|
|
22
|
+
}
|
|
23
|
+
return lo;
|
|
24
|
+
}
|
|
25
|
+
function indexToLineColWithIndex(lineIndex, idx) {
|
|
26
|
+
const offsets = lineIndex.offsets;
|
|
27
|
+
const ub = upperBound(offsets, idx);
|
|
28
|
+
const lineIdx = Math.max(0, ub - 1);
|
|
29
|
+
const line = lineIdx + 1;
|
|
30
|
+
const lineStart = offsets[lineIdx] ?? 0;
|
|
31
|
+
return {
|
|
32
|
+
line,
|
|
33
|
+
column0: Math.max(0, idx - lineStart),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function indexToLineColumn(lineIndex, idx) {
|
|
37
|
+
const { line, column0 } = indexToLineColWithIndex(lineIndex, idx);
|
|
38
|
+
return {
|
|
39
|
+
line,
|
|
40
|
+
column: column0 + 1,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function normalizeSourceMap(map) {
|
|
44
|
+
if (!map) return;
|
|
45
|
+
return {
|
|
46
|
+
...map,
|
|
47
|
+
version: Number(map.version),
|
|
48
|
+
file: map.file ?? '',
|
|
49
|
+
names: Array.isArray(map.names) ? map.names : [],
|
|
50
|
+
sourcesContent: map.sourcesContent?.map((value) => value ?? '') ?? void 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pick the most-likely original source text for `importerFile` from
|
|
55
|
+
* a sourcemap that may contain multiple sources.
|
|
56
|
+
*/
|
|
57
|
+
function pickOriginalCodeFromSourcesContent(map, importerFile, root) {
|
|
58
|
+
if (!map?.sourcesContent || map.sources.length === 0) return;
|
|
59
|
+
const file = normalizeFilePath(importerFile);
|
|
60
|
+
const sourceRoot = map.sourceRoot;
|
|
61
|
+
const fileSeg = file.split('/').filter(Boolean);
|
|
62
|
+
const resolveBase = sourceRoot ? path$1.resolve(root, sourceRoot) : root;
|
|
63
|
+
let bestIdx = -1;
|
|
64
|
+
let bestScore = -1;
|
|
65
|
+
for (let i = 0; i < map.sources.length; i++) {
|
|
66
|
+
const content = map.sourcesContent[i];
|
|
67
|
+
if (typeof content !== 'string') continue;
|
|
68
|
+
const src = map.sources[i] ?? '';
|
|
69
|
+
const normalizedSrc = normalizeFilePath(src);
|
|
70
|
+
if (normalizedSrc === file) return content;
|
|
71
|
+
let resolved;
|
|
72
|
+
if (!src) resolved = '';
|
|
73
|
+
else if (path$1.isAbsolute(src)) resolved = normalizeFilePath(src);
|
|
74
|
+
else resolved = normalizeFilePath(path$1.resolve(resolveBase, src));
|
|
75
|
+
if (resolved === file) return content;
|
|
76
|
+
const normalizedSrcSeg = normalizedSrc.split('/').filter(Boolean);
|
|
77
|
+
const resolvedSeg =
|
|
78
|
+
resolved !== normalizedSrc ? resolved.split('/').filter(Boolean) : normalizedSrcSeg;
|
|
79
|
+
const score = Math.max(
|
|
80
|
+
segmentSuffixScore(normalizedSrcSeg, fileSeg),
|
|
81
|
+
segmentSuffixScore(resolvedSeg, fileSeg),
|
|
82
|
+
);
|
|
83
|
+
if (score > bestScore) {
|
|
84
|
+
bestScore = score;
|
|
85
|
+
bestIdx = i;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (bestIdx !== -1 && bestScore >= 1) return map.sourcesContent[bestIdx] ?? void 0;
|
|
89
|
+
return map.sourcesContent[0] ?? void 0;
|
|
90
|
+
}
|
|
91
|
+
/** Count matching path segments from the end of `aSeg` against `bSeg`. */
|
|
92
|
+
function segmentSuffixScore(aSeg, bSeg) {
|
|
93
|
+
let score = 0;
|
|
94
|
+
for (let i = aSeg.length - 1, j = bSeg.length - 1; i >= 0 && j >= 0; i--, j--) {
|
|
95
|
+
if (aSeg[i] !== bSeg[j]) break;
|
|
96
|
+
score++;
|
|
97
|
+
}
|
|
98
|
+
return score;
|
|
99
|
+
}
|
|
100
|
+
async function mapGeneratedToOriginal(map, generated, fallbackFile) {
|
|
101
|
+
const fallback = {
|
|
102
|
+
file: fallbackFile,
|
|
103
|
+
line: generated.line,
|
|
104
|
+
column: generated.column0 + 1,
|
|
105
|
+
};
|
|
106
|
+
if (!map) return fallback;
|
|
107
|
+
const consumer = await getSourceMapConsumer(map);
|
|
108
|
+
if (!consumer) return fallback;
|
|
109
|
+
try {
|
|
110
|
+
const orig = consumer.originalPositionFor({
|
|
111
|
+
line: generated.line,
|
|
112
|
+
column: generated.column0,
|
|
113
|
+
});
|
|
114
|
+
if (orig.line != null && orig.column != null)
|
|
115
|
+
return {
|
|
116
|
+
file: orig.source ? normalizeFilePath(orig.source) : fallbackFile,
|
|
117
|
+
line: orig.line,
|
|
118
|
+
column: orig.column + 1,
|
|
119
|
+
};
|
|
120
|
+
} catch {}
|
|
121
|
+
return fallback;
|
|
122
|
+
}
|
|
123
|
+
var consumerCache = /* @__PURE__ */ new WeakMap();
|
|
124
|
+
function getOriginalCode(result, importerFile, root) {
|
|
125
|
+
if (result.originalCode === void 0 && importerFile && root) {
|
|
126
|
+
const map = result.map;
|
|
127
|
+
if (map?.sourcesContent)
|
|
128
|
+
result.originalCode = pickOriginalCodeFromSourcesContent(map, importerFile, root);
|
|
129
|
+
}
|
|
130
|
+
return result.originalCode;
|
|
131
|
+
}
|
|
132
|
+
function toRawSourceMap(map) {
|
|
133
|
+
return {
|
|
134
|
+
...map,
|
|
135
|
+
file: map.file ?? '',
|
|
136
|
+
version: Number(map.version),
|
|
137
|
+
sourcesContent: map.sourcesContent?.map((s) => s ?? '') ?? [],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function getSourceMapConsumer(map) {
|
|
141
|
+
const cached = consumerCache.get(map);
|
|
142
|
+
if (cached) return cached;
|
|
143
|
+
const promise = (async () => {
|
|
144
|
+
try {
|
|
145
|
+
return await new SourceMapConsumer(toRawSourceMap(map));
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
consumerCache.set(map, promise);
|
|
151
|
+
return promise;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Cache for import statement locations with reverse index for O(1)
|
|
155
|
+
* invalidation by file. Keys: `${importerFile}::${source}`.
|
|
156
|
+
*/
|
|
157
|
+
var ImportLocCache = class {
|
|
158
|
+
cache = /* @__PURE__ */ new Map();
|
|
159
|
+
reverseIndex = /* @__PURE__ */ new Map();
|
|
160
|
+
has(key) {
|
|
161
|
+
return this.cache.has(key);
|
|
162
|
+
}
|
|
163
|
+
get(key) {
|
|
164
|
+
return this.cache.get(key);
|
|
165
|
+
}
|
|
166
|
+
set(key, value) {
|
|
167
|
+
this.cache.set(key, value);
|
|
168
|
+
const file = key.slice(0, key.indexOf('::'));
|
|
169
|
+
getOrCreate(this.reverseIndex, file, () => /* @__PURE__ */ new Set()).add(key);
|
|
170
|
+
}
|
|
171
|
+
clear() {
|
|
172
|
+
this.cache.clear();
|
|
173
|
+
this.reverseIndex.clear();
|
|
174
|
+
}
|
|
175
|
+
/** Remove all cache entries where the importer matches `file`. */
|
|
176
|
+
deleteByFile(file) {
|
|
177
|
+
const keys = this.reverseIndex.get(file);
|
|
178
|
+
if (keys) {
|
|
179
|
+
for (const key of keys) this.cache.delete(key);
|
|
180
|
+
this.reverseIndex.delete(file);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
function getOrCreateOriginalTransformResult(result, importerFile, root) {
|
|
185
|
+
getOriginalCode(result, importerFile, root);
|
|
186
|
+
if (!result.originalCode) return;
|
|
187
|
+
if (!result.originalResult) {
|
|
188
|
+
result.originalResult = {
|
|
189
|
+
code: result.originalCode,
|
|
190
|
+
filename: result.filename,
|
|
191
|
+
map: void 0,
|
|
192
|
+
originalCode: result.originalCode,
|
|
193
|
+
};
|
|
194
|
+
if (result.perf) result.originalResult.perf = result.perf;
|
|
195
|
+
}
|
|
196
|
+
return result.originalResult;
|
|
197
|
+
}
|
|
198
|
+
function createImportSpecifierLocationIndex() {
|
|
199
|
+
return {
|
|
200
|
+
find(result, source) {
|
|
201
|
+
if (!result.code.includes(source)) return -1;
|
|
202
|
+
return getImportSpecifierLocationFromResult(result, source);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Find the location of an import statement in a transformed module
|
|
208
|
+
* by searching the post-transform code and mapping back via sourcemap.
|
|
209
|
+
* Results are cached in `importLocCache`.
|
|
210
|
+
*/
|
|
211
|
+
async function findImportStatementLocationFromTransformed(
|
|
212
|
+
provider,
|
|
213
|
+
importerId,
|
|
214
|
+
source,
|
|
215
|
+
importLocCache,
|
|
216
|
+
findImportSpecifierLocationIndex,
|
|
217
|
+
) {
|
|
218
|
+
const importerFile = normalizeFilePath(importerId);
|
|
219
|
+
const cacheKey = `${importerFile}::${source}`;
|
|
220
|
+
if (importLocCache.has(cacheKey)) return importLocCache.get(cacheKey) ?? void 0;
|
|
221
|
+
try {
|
|
222
|
+
const res = provider.getTransformResult(importerId);
|
|
223
|
+
if (!res) {
|
|
224
|
+
importLocCache.set(cacheKey, null);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const { map } = res;
|
|
228
|
+
const lineIndex = res.lineIndex ?? (res.lineIndex = buildLineIndex(res.code));
|
|
229
|
+
const idx = findImportSpecifierLocationIndex(res, source);
|
|
230
|
+
if (idx === -1) {
|
|
231
|
+
importLocCache.set(cacheKey, null);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const loc = await mapGeneratedToOriginal(
|
|
235
|
+
map,
|
|
236
|
+
indexToLineColWithIndex(lineIndex, idx),
|
|
237
|
+
importerFile,
|
|
238
|
+
);
|
|
239
|
+
importLocCache.set(cacheKey, loc);
|
|
240
|
+
return loc;
|
|
241
|
+
} catch {
|
|
242
|
+
importLocCache.set(cacheKey, null);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Find the first post-compile usage location for a denied import specifier.
|
|
248
|
+
* Best-effort: searches transformed code for non-import uses of imported
|
|
249
|
+
* bindings and maps back to original source via sourcemap.
|
|
250
|
+
*/
|
|
251
|
+
async function findPostCompileUsageLocation(provider, importerId, source) {
|
|
252
|
+
try {
|
|
253
|
+
const importerFile = normalizeFilePath(importerId);
|
|
254
|
+
const res = provider.getTransformResult(importerId);
|
|
255
|
+
if (!res) return void 0;
|
|
256
|
+
const { code, map } = res;
|
|
257
|
+
if (!res.lineIndex) res.lineIndex = buildLineIndex(code);
|
|
258
|
+
const pos = findPostCompileUsagePosFromResult(res, source);
|
|
259
|
+
if (!pos) return void 0;
|
|
260
|
+
return await mapGeneratedToOriginal(map, pos, importerFile);
|
|
261
|
+
} catch {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Best-effort original-source usage lookup for cases where a later transform
|
|
267
|
+
* removes or rewrites the import from emitted code but preserves the original
|
|
268
|
+
* source in `sourcesContent`.
|
|
269
|
+
*/
|
|
270
|
+
function findOriginalUsageLocation(provider, importerId, source, envType, root) {
|
|
271
|
+
try {
|
|
272
|
+
const importerFile = normalizeFilePath(importerId);
|
|
273
|
+
const res = provider.getTransformResult(importerId);
|
|
274
|
+
if (!res) return void 0;
|
|
275
|
+
const originalResult = getOrCreateOriginalTransformResult(res, importerFile, root);
|
|
276
|
+
if (!originalResult) return void 0;
|
|
277
|
+
const pos = envType
|
|
278
|
+
? findOriginalUnsafeUsagePosFromResult(originalResult, source, envType)
|
|
279
|
+
: findPostCompileUsagePosFromResult(originalResult, source);
|
|
280
|
+
if (!pos) return void 0;
|
|
281
|
+
return {
|
|
282
|
+
file: importerFile,
|
|
283
|
+
line: pos.line,
|
|
284
|
+
column: pos.column0 + 1,
|
|
285
|
+
};
|
|
286
|
+
} catch {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Annotate each trace hop with the location of the import that created the
|
|
292
|
+
* edge (file:line:col). Skips steps that already have a location.
|
|
293
|
+
*/
|
|
294
|
+
async function addTraceImportLocations(
|
|
295
|
+
provider,
|
|
296
|
+
trace,
|
|
297
|
+
importLocCache,
|
|
298
|
+
findImportSpecifierLocationIndex,
|
|
299
|
+
) {
|
|
300
|
+
for (const step of trace) {
|
|
301
|
+
if (!step.specifier) continue;
|
|
302
|
+
if (step.line != null && step.column != null) continue;
|
|
303
|
+
const loc = await findImportStatementLocationFromTransformed(
|
|
304
|
+
provider,
|
|
305
|
+
step.file,
|
|
306
|
+
step.specifier,
|
|
307
|
+
importLocCache,
|
|
308
|
+
findImportSpecifierLocationIndex,
|
|
309
|
+
);
|
|
310
|
+
if (!loc) continue;
|
|
311
|
+
step.line = loc.line;
|
|
312
|
+
step.column = loc.column;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Build a vitest-style code snippet showing lines surrounding a location.
|
|
317
|
+
*
|
|
318
|
+
* Prefers `originalCode` from the sourcemap's sourcesContent; falls back
|
|
319
|
+
* to transformed code when unavailable.
|
|
320
|
+
*/
|
|
321
|
+
function buildCodeSnippet(provider, moduleId, loc, contextLines = 2, root) {
|
|
322
|
+
try {
|
|
323
|
+
const importerFile = normalizeFilePath(moduleId);
|
|
324
|
+
const res = provider.getTransformResult(moduleId);
|
|
325
|
+
if (!res) return void 0;
|
|
326
|
+
const sourceCode = getOriginalCode(res, importerFile, root) ?? res.code;
|
|
327
|
+
const targetLine = loc.line;
|
|
328
|
+
const targetCol = loc.column;
|
|
329
|
+
if (targetLine < 1) return void 0;
|
|
330
|
+
const allLines = sourceCode.split('\n');
|
|
331
|
+
for (let i = 0; i < allLines.length; i++) {
|
|
332
|
+
const line = allLines[i];
|
|
333
|
+
if (line.endsWith('\r')) allLines[i] = line.slice(0, -1);
|
|
334
|
+
}
|
|
335
|
+
const wantStart = Math.max(1, targetLine - contextLines);
|
|
336
|
+
const wantEnd = Math.min(allLines.length, targetLine + contextLines);
|
|
337
|
+
if (targetLine > allLines.length) return void 0;
|
|
338
|
+
const lines = allLines.slice(wantStart - 1, wantEnd);
|
|
339
|
+
const gutterWidth = String(wantEnd).length;
|
|
340
|
+
const sourceFile = loc.file ?? importerFile;
|
|
341
|
+
const snippetLines = [];
|
|
342
|
+
for (let i = 0; i < lines.length; i++) {
|
|
343
|
+
const ln = wantStart + i;
|
|
344
|
+
const lineContent = lines[i];
|
|
345
|
+
const lineNumStr = String(ln).padStart(gutterWidth, ' ');
|
|
346
|
+
const marker = ln === targetLine ? '>' : ' ';
|
|
347
|
+
snippetLines.push(` ${marker} ${lineNumStr} | ${lineContent}`);
|
|
348
|
+
if (ln === targetLine && targetCol > 0) {
|
|
349
|
+
const padding = ' '.repeat(targetCol - 1);
|
|
350
|
+
snippetLines.push(` ${' '.repeat(gutterWidth)} | ${padding}^`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
lines: snippetLines,
|
|
355
|
+
highlightLine: targetLine,
|
|
356
|
+
location: `${sourceFile}:${targetLine}:${targetCol}`,
|
|
357
|
+
};
|
|
358
|
+
} catch {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
export {
|
|
364
|
+
ImportLocCache,
|
|
365
|
+
addTraceImportLocations,
|
|
366
|
+
buildCodeSnippet,
|
|
367
|
+
buildLineIndex,
|
|
368
|
+
createImportSpecifierLocationIndex,
|
|
369
|
+
findImportStatementLocationFromTransformed,
|
|
370
|
+
findOriginalUsageLocation,
|
|
371
|
+
findPostCompileUsageLocation,
|
|
372
|
+
getOrCreateOriginalTransformResult,
|
|
373
|
+
indexToLineColumn,
|
|
374
|
+
normalizeSourceMap,
|
|
375
|
+
pickOriginalCodeFromSourcesContent,
|
|
376
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export interface TraceEdge {
|
|
2
|
+
importer: string;
|
|
3
|
+
specifier?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Per-environment reverse import graph.
|
|
7
|
+
* Maps a resolved module id to the set of modules that import it.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ImportGraph {
|
|
10
|
+
/**
|
|
11
|
+
* resolvedId -> Map<importer, specifier>
|
|
12
|
+
*
|
|
13
|
+
* We use a Map instead of a Set of objects so edges dedupe correctly.
|
|
14
|
+
*/
|
|
15
|
+
readonly reverseEdges: Map<string, Map<string, string | undefined>>;
|
|
16
|
+
/**
|
|
17
|
+
* Forward-edge index: importer -> Set<resolvedId>.
|
|
18
|
+
*
|
|
19
|
+
* Maintained alongside reverseEdges so that {@link invalidate} can remove
|
|
20
|
+
* all outgoing edges for a file in O(outgoing-edges) instead of scanning
|
|
21
|
+
* every reverse-edge map in the graph.
|
|
22
|
+
*/
|
|
23
|
+
private readonly forwardEdges;
|
|
24
|
+
readonly entries: Set<string>;
|
|
25
|
+
addEdge(resolved: string, importer: string, specifier?: string): void;
|
|
26
|
+
/** Convenience for tests/debugging. */
|
|
27
|
+
getEdges(resolved: string): Set<TraceEdge> | undefined;
|
|
28
|
+
addEntry(id: string): void;
|
|
29
|
+
clear(): void;
|
|
30
|
+
invalidate(id: string): void;
|
|
31
|
+
}
|
|
32
|
+
export interface TraceStep {
|
|
33
|
+
file: string;
|
|
34
|
+
specifier?: string;
|
|
35
|
+
line?: number;
|
|
36
|
+
column?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface Loc {
|
|
39
|
+
file?: string;
|
|
40
|
+
line: number;
|
|
41
|
+
column: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* BFS from a node upward through reverse edges to find the shortest
|
|
45
|
+
* path to an entry module.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildTrace(
|
|
48
|
+
graph: ImportGraph,
|
|
49
|
+
startNode: string,
|
|
50
|
+
maxDepth?: number,
|
|
51
|
+
): Array<TraceStep>;
|
|
52
|
+
export interface ViolationInfo {
|
|
53
|
+
env: string;
|
|
54
|
+
envType: 'client' | 'server';
|
|
55
|
+
type: 'specifier' | 'file' | 'marker';
|
|
56
|
+
behavior: 'error' | 'mock';
|
|
57
|
+
pattern?: string | RegExp;
|
|
58
|
+
specifier: string;
|
|
59
|
+
importer: string;
|
|
60
|
+
importerLoc?: Loc;
|
|
61
|
+
resolved?: string;
|
|
62
|
+
trace: Array<TraceStep>;
|
|
63
|
+
/** Vitest-style code snippet showing the offending usage in the leaf module. */
|
|
64
|
+
snippet?: {
|
|
65
|
+
lines: Array<string>;
|
|
66
|
+
highlightLine: number;
|
|
67
|
+
location: string;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Suggestion strings for server-only code leaking into client environments.
|
|
72
|
+
* Used by both `formatViolation` (terminal) and runtime mock modules (browser).
|
|
73
|
+
*/
|
|
74
|
+
export declare const CLIENT_ENV_SUGGESTIONS: readonly [
|
|
75
|
+
'Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge',
|
|
76
|
+
'Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)',
|
|
77
|
+
'Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations',
|
|
78
|
+
'Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code',
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* Suggestion strings for client-only code leaking into server environments.
|
|
82
|
+
* The JSX-specific suggestion is conditionally prepended by `formatViolation`.
|
|
83
|
+
*/
|
|
84
|
+
export declare const SERVER_ENV_SUGGESTIONS: readonly [
|
|
85
|
+
'Use createClientOnlyFn(() => ...) to mark it as client-only (returns undefined on the server)',
|
|
86
|
+
'Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations',
|
|
87
|
+
'Move the client-only import out of this file into a separate .client.ts module that is not imported by any server code',
|
|
88
|
+
];
|
|
89
|
+
export declare function formatViolation(info: ViolationInfo, root: string): string;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { getOrCreate, relativizePath } from './utils.js';
|
|
2
|
+
//#region src/import-protection/trace.ts
|
|
3
|
+
/**
|
|
4
|
+
* Per-environment reverse import graph.
|
|
5
|
+
* Maps a resolved module id to the set of modules that import it.
|
|
6
|
+
*/
|
|
7
|
+
var ImportGraph = class {
|
|
8
|
+
/**
|
|
9
|
+
* resolvedId -> Map<importer, specifier>
|
|
10
|
+
*
|
|
11
|
+
* We use a Map instead of a Set of objects so edges dedupe correctly.
|
|
12
|
+
*/
|
|
13
|
+
reverseEdges = /* @__PURE__ */ new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Forward-edge index: importer -> Set<resolvedId>.
|
|
16
|
+
*
|
|
17
|
+
* Maintained alongside reverseEdges so that {@link invalidate} can remove
|
|
18
|
+
* all outgoing edges for a file in O(outgoing-edges) instead of scanning
|
|
19
|
+
* every reverse-edge map in the graph.
|
|
20
|
+
*/
|
|
21
|
+
forwardEdges = /* @__PURE__ */ new Map();
|
|
22
|
+
entries = /* @__PURE__ */ new Set();
|
|
23
|
+
addEdge(resolved, importer, specifier) {
|
|
24
|
+
getOrCreate(this.reverseEdges, resolved, () => /* @__PURE__ */ new Map()).set(
|
|
25
|
+
importer,
|
|
26
|
+
specifier,
|
|
27
|
+
);
|
|
28
|
+
getOrCreate(this.forwardEdges, importer, () => /* @__PURE__ */ new Set()).add(resolved);
|
|
29
|
+
}
|
|
30
|
+
/** Convenience for tests/debugging. */
|
|
31
|
+
getEdges(resolved) {
|
|
32
|
+
const importers = this.reverseEdges.get(resolved);
|
|
33
|
+
if (!importers) return void 0;
|
|
34
|
+
const out = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const [importer, specifier] of importers)
|
|
36
|
+
out.add({
|
|
37
|
+
importer,
|
|
38
|
+
specifier,
|
|
39
|
+
});
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
addEntry(id) {
|
|
43
|
+
this.entries.add(id);
|
|
44
|
+
}
|
|
45
|
+
clear() {
|
|
46
|
+
this.reverseEdges.clear();
|
|
47
|
+
this.forwardEdges.clear();
|
|
48
|
+
this.entries.clear();
|
|
49
|
+
}
|
|
50
|
+
invalidate(id) {
|
|
51
|
+
const targets = this.forwardEdges.get(id);
|
|
52
|
+
if (targets) {
|
|
53
|
+
for (const resolved of targets) this.reverseEdges.get(resolved)?.delete(id);
|
|
54
|
+
this.forwardEdges.delete(id);
|
|
55
|
+
}
|
|
56
|
+
this.reverseEdges.delete(id);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* BFS from a node upward through reverse edges to find the shortest
|
|
61
|
+
* path to an entry module.
|
|
62
|
+
*/
|
|
63
|
+
function buildTrace(graph, startNode, maxDepth = 20) {
|
|
64
|
+
const visited = new Set([startNode]);
|
|
65
|
+
const depthByNode = new Map([[startNode, 0]]);
|
|
66
|
+
const down = /* @__PURE__ */ new Map();
|
|
67
|
+
const queue = [startNode];
|
|
68
|
+
let qi = 0;
|
|
69
|
+
let root = null;
|
|
70
|
+
while (qi < queue.length) {
|
|
71
|
+
const node = queue[qi++];
|
|
72
|
+
const depth = depthByNode.get(node);
|
|
73
|
+
const importers = graph.reverseEdges.get(node);
|
|
74
|
+
if (node !== startNode) {
|
|
75
|
+
if (graph.entries.has(node) || !importers || importers.size === 0) {
|
|
76
|
+
root = node;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (depth >= maxDepth) continue;
|
|
81
|
+
if (!importers || importers.size === 0) continue;
|
|
82
|
+
for (const [importer, specifier] of importers) {
|
|
83
|
+
if (visited.has(importer)) continue;
|
|
84
|
+
visited.add(importer);
|
|
85
|
+
depthByNode.set(importer, depth + 1);
|
|
86
|
+
down.set(importer, {
|
|
87
|
+
next: node,
|
|
88
|
+
specifier,
|
|
89
|
+
});
|
|
90
|
+
queue.push(importer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!root) root = startNode;
|
|
94
|
+
const trace = [];
|
|
95
|
+
let current = root;
|
|
96
|
+
for (let i = 0; i <= maxDepth + 1; i++) {
|
|
97
|
+
const link = down.get(current);
|
|
98
|
+
trace.push({
|
|
99
|
+
file: current,
|
|
100
|
+
specifier: link?.specifier,
|
|
101
|
+
});
|
|
102
|
+
if (!link) break;
|
|
103
|
+
current = link.next;
|
|
104
|
+
}
|
|
105
|
+
return trace;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Suggestion strings for server-only code leaking into client environments.
|
|
109
|
+
* Used by both `formatViolation` (terminal) and runtime mock modules (browser).
|
|
110
|
+
*/
|
|
111
|
+
var CLIENT_ENV_SUGGESTIONS = [
|
|
112
|
+
'Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge',
|
|
113
|
+
'Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)',
|
|
114
|
+
'Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations',
|
|
115
|
+
'Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code',
|
|
116
|
+
];
|
|
117
|
+
/**
|
|
118
|
+
* Suggestion strings for client-only code leaking into server environments.
|
|
119
|
+
* The JSX-specific suggestion is conditionally prepended by `formatViolation`.
|
|
120
|
+
*/
|
|
121
|
+
var SERVER_ENV_SUGGESTIONS = [
|
|
122
|
+
'Use createClientOnlyFn(() => ...) to mark it as client-only (returns undefined on the server)',
|
|
123
|
+
'Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations',
|
|
124
|
+
'Move the client-only import out of this file into a separate .client.ts module that is not imported by any server code',
|
|
125
|
+
];
|
|
126
|
+
function formatViolation(info, root) {
|
|
127
|
+
const rel = (p) => relativizePath(p, root);
|
|
128
|
+
const relLoc = (p, loc) => {
|
|
129
|
+
const r = rel(p);
|
|
130
|
+
const file = loc?.file ? rel(loc.file) : r;
|
|
131
|
+
return loc ? `${file}:${loc.line}:${loc.column}` : r;
|
|
132
|
+
};
|
|
133
|
+
const relTraceStep = (step) => {
|
|
134
|
+
const file = rel(step.file);
|
|
135
|
+
if (step.line == null) return file;
|
|
136
|
+
const col = step.column ?? 1;
|
|
137
|
+
return `${file}:${step.line}:${col}`;
|
|
138
|
+
};
|
|
139
|
+
const lines = [];
|
|
140
|
+
lines.push(``);
|
|
141
|
+
lines.push(`[import-protection] Import denied in ${info.envType} environment`);
|
|
142
|
+
lines.push(``);
|
|
143
|
+
if (info.type === 'specifier')
|
|
144
|
+
lines.push(` Denied by specifier pattern: ${String(info.pattern)}`);
|
|
145
|
+
else if (info.type === 'file') lines.push(` Denied by file pattern: ${String(info.pattern)}`);
|
|
146
|
+
else lines.push(` Denied by marker: module is restricted to the opposite environment`);
|
|
147
|
+
lines.push(` Importer: ${relLoc(info.importer, info.importerLoc)}`);
|
|
148
|
+
lines.push(` Import: "${rel(info.specifier)}"`);
|
|
149
|
+
if (info.resolved) lines.push(` Resolved: ${rel(info.resolved)}`);
|
|
150
|
+
if (info.trace.length > 0) {
|
|
151
|
+
lines.push(``);
|
|
152
|
+
lines.push(` Trace:`);
|
|
153
|
+
for (let i = 0; i < info.trace.length; i++) {
|
|
154
|
+
const step = info.trace[i];
|
|
155
|
+
const tag = i === 0 ? ' (entry)' : '';
|
|
156
|
+
const spec = step.specifier ? ` (import "${rel(step.specifier)}")` : '';
|
|
157
|
+
lines.push(` ${i + 1}. ${relTraceStep(step)}${tag}${spec}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (info.snippet) {
|
|
161
|
+
lines.push(``);
|
|
162
|
+
lines.push(` Code:`);
|
|
163
|
+
for (const snippetLine of info.snippet.lines) lines.push(snippetLine);
|
|
164
|
+
lines.push(``);
|
|
165
|
+
lines.push(` ${rel(info.snippet.location)}`);
|
|
166
|
+
}
|
|
167
|
+
lines.push(``);
|
|
168
|
+
if (info.envType === 'client') {
|
|
169
|
+
lines.push(` Suggestions:`);
|
|
170
|
+
for (const s of CLIENT_ENV_SUGGESTIONS) lines.push(` - ${s}`);
|
|
171
|
+
} else {
|
|
172
|
+
const snippetText = info.snippet?.lines.join('\n') ?? '';
|
|
173
|
+
const looksLikeJsx =
|
|
174
|
+
/<[A-Z]/.test(snippetText) || (/\{.*\(.*\).*\}/.test(snippetText) && /</.test(snippetText));
|
|
175
|
+
lines.push(` Suggestions:`);
|
|
176
|
+
if (looksLikeJsx)
|
|
177
|
+
lines.push(
|
|
178
|
+
` - Wrap the JSX in <ClientOnly fallback={<Loading />}>...</ClientOnly> so it only renders in the browser after hydration`,
|
|
179
|
+
);
|
|
180
|
+
for (const s of SERVER_ENV_SUGGESTIONS) lines.push(` - ${s}`);
|
|
181
|
+
}
|
|
182
|
+
lines.push(``);
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
export { CLIENT_ENV_SUGGESTIONS, ImportGraph, buildTrace, formatViolation };
|