@tanstack/start-plugin-core 1.167.35 → 1.169.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/adapterUtils.d.ts +27 -0
- package/dist/esm/import-protection/adapterUtils.js +31 -0
- package/dist/esm/import-protection/adapterUtils.js.map +1 -0
- package/dist/esm/import-protection/analysis.d.ts +36 -0
- package/dist/esm/import-protection/analysis.js +407 -0
- package/dist/esm/import-protection/analysis.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/ast.js +1 -1
- package/dist/esm/import-protection/ast.js.map +1 -0
- package/dist/esm/import-protection/constants.d.ts +11 -0
- package/dist/esm/{import-protection-plugin → import-protection}/constants.js +7 -2
- package/dist/esm/import-protection/constants.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/defaults.js +1 -1
- package/dist/esm/import-protection/defaults.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.js +2 -2
- package/dist/esm/import-protection/extensionlessAbsoluteIdResolver.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/matchers.js +1 -1
- package/dist/esm/import-protection/matchers.js.map +1 -0
- package/dist/esm/{import-protection-plugin/rewriteDeniedImports.d.ts → import-protection/rewrite.d.ts} +0 -4
- package/dist/esm/import-protection/rewrite.js +121 -0
- package/dist/esm/import-protection/rewrite.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.d.ts +32 -3
- package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.js +65 -10
- package/dist/esm/import-protection/sourceLocation.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/trace.d.ts +0 -1
- package/dist/esm/{import-protection-plugin → import-protection}/trace.js +1 -1
- package/dist/esm/import-protection/trace.js.map +1 -0
- package/dist/esm/{import-protection-plugin → import-protection}/utils.d.ts +18 -1
- package/dist/esm/{import-protection-plugin → import-protection}/utils.js +13 -20
- package/dist/esm/import-protection/utils.js.map +1 -0
- package/dist/esm/import-protection/virtualModules.d.ts +25 -0
- package/dist/esm/{import-protection-plugin → import-protection}/virtualModules.js +5 -117
- package/dist/esm/import-protection/virtualModules.js.map +1 -0
- package/dist/esm/index.d.ts +1 -5
- package/dist/esm/index.js +2 -4
- package/dist/esm/post-build.d.ts +9 -0
- package/dist/esm/post-build.js +37 -0
- package/dist/esm/post-build.js.map +1 -0
- package/dist/esm/prerender.d.ts +11 -0
- package/dist/esm/prerender.js +159 -0
- package/dist/esm/prerender.js.map +1 -0
- package/dist/esm/rsbuild/dev-server.d.ts +21 -0
- package/dist/esm/rsbuild/dev-server.js +76 -0
- package/dist/esm/rsbuild/dev-server.js.map +1 -0
- package/dist/esm/rsbuild/import-protection.d.ts +10 -0
- package/dist/esm/rsbuild/import-protection.js +775 -0
- package/dist/esm/rsbuild/import-protection.js.map +1 -0
- package/dist/esm/rsbuild/index.d.ts +4 -0
- package/dist/esm/rsbuild/index.js +3 -0
- package/dist/esm/rsbuild/normalized-client-build.d.ts +18 -0
- package/dist/esm/rsbuild/normalized-client-build.js +207 -0
- package/dist/esm/rsbuild/normalized-client-build.js.map +1 -0
- package/dist/esm/rsbuild/planning.d.ts +52 -0
- package/dist/esm/rsbuild/planning.js +108 -0
- package/dist/esm/rsbuild/planning.js.map +1 -0
- package/dist/esm/rsbuild/plugin.d.ts +4 -0
- package/dist/esm/rsbuild/plugin.js +344 -0
- package/dist/esm/rsbuild/plugin.js.map +1 -0
- package/dist/esm/rsbuild/post-build.d.ts +6 -0
- package/dist/esm/rsbuild/post-build.js +57 -0
- package/dist/esm/rsbuild/post-build.js.map +1 -0
- package/dist/esm/rsbuild/schema.d.ts +3372 -0
- package/dist/esm/rsbuild/schema.js +12 -0
- package/dist/esm/rsbuild/schema.js.map +1 -0
- package/dist/esm/rsbuild/start-compiler-host.d.ts +20 -0
- package/dist/esm/rsbuild/start-compiler-host.js +150 -0
- package/dist/esm/rsbuild/start-compiler-host.js.map +1 -0
- package/dist/esm/rsbuild/start-router-plugin.d.ts +18 -0
- package/dist/esm/rsbuild/start-router-plugin.js +63 -0
- package/dist/esm/rsbuild/start-router-plugin.js.map +1 -0
- package/dist/esm/rsbuild/swc-rsc.d.ts +14 -0
- package/dist/esm/rsbuild/swc-rsc.js +93 -0
- package/dist/esm/rsbuild/swc-rsc.js.map +1 -0
- package/dist/esm/rsbuild/types.d.ts +17 -0
- package/dist/esm/rsbuild/types.js +0 -0
- package/dist/esm/rsbuild/virtual-modules.d.ts +53 -0
- package/dist/esm/rsbuild/virtual-modules.js +287 -0
- package/dist/esm/rsbuild/virtual-modules.js.map +1 -0
- package/dist/esm/schema.d.ts +43 -43
- package/dist/esm/start-compiler/compiler.d.ts +1 -1
- package/dist/esm/start-compiler/compiler.js +80 -9
- package/dist/esm/start-compiler/compiler.js.map +1 -1
- package/dist/esm/start-compiler/handleCreateServerFn.js +9 -0
- package/dist/esm/start-compiler/handleCreateServerFn.js.map +1 -1
- package/dist/esm/start-compiler/host.js +5 -1
- package/dist/esm/start-compiler/host.js.map +1 -1
- package/dist/esm/start-compiler/types.d.ts +1 -0
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +10 -1
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.js +41 -92
- package/dist/esm/vite/import-protection-plugin/plugin.js.map +1 -0
- package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/types.d.ts +5 -5
- package/dist/esm/vite/import-protection-plugin/virtualModules.d.ts +8 -0
- package/dist/esm/vite/import-protection-plugin/virtualModules.js +49 -0
- package/dist/esm/vite/import-protection-plugin/virtualModules.js.map +1 -0
- package/dist/esm/vite/index.d.ts +5 -0
- package/dist/esm/vite/index.js +4 -0
- package/dist/esm/vite/plugin.js +1 -1
- package/dist/esm/vite/plugin.js.map +1 -1
- package/dist/esm/vite/post-server-build.js +14 -32
- package/dist/esm/vite/post-server-build.js.map +1 -1
- package/dist/esm/vite/prerender.d.ts +2 -2
- package/dist/esm/vite/prerender.js +17 -147
- package/dist/esm/vite/prerender.js.map +1 -1
- package/dist/esm/vite/schema.d.ts +23 -23
- package/dist/esm/vite/start-compiler-plugin/hot-update.d.ts +2 -0
- package/dist/esm/vite/start-compiler-plugin/hot-update.js +16 -0
- package/dist/esm/vite/start-compiler-plugin/hot-update.js.map +1 -0
- package/dist/esm/vite/start-compiler-plugin/module-specifier.js +9 -4
- package/dist/esm/vite/start-compiler-plugin/module-specifier.js.map +1 -1
- package/dist/esm/vite/start-compiler-plugin/plugin.js +86 -13
- package/dist/esm/vite/start-compiler-plugin/plugin.js.map +1 -1
- package/package.json +32 -4
- package/src/import-protection/INTERNALS.md +266 -0
- package/src/import-protection/adapterUtils.ts +94 -0
- package/src/import-protection/analysis.ts +853 -0
- package/src/{import-protection-plugin → import-protection}/constants.ts +7 -0
- package/src/import-protection/rewrite.ts +229 -0
- package/src/{import-protection-plugin → import-protection}/sourceLocation.ts +125 -9
- package/src/{import-protection-plugin → import-protection}/trace.ts +0 -1
- package/src/{import-protection-plugin → import-protection}/utils.ts +36 -21
- package/src/{import-protection-plugin → import-protection}/virtualModules.ts +30 -177
- package/src/index.ts +1 -8
- package/src/post-build.ts +64 -0
- package/src/prerender.ts +292 -0
- package/src/rsbuild/INTERNALS-import-protection.md +169 -0
- package/src/rsbuild/dev-server.ts +129 -0
- package/src/rsbuild/import-protection.ts +1599 -0
- package/src/rsbuild/index.ts +4 -0
- package/src/rsbuild/normalized-client-build.ts +346 -0
- package/src/rsbuild/planning.ts +234 -0
- package/src/rsbuild/plugin.ts +754 -0
- package/src/rsbuild/post-build.ts +96 -0
- package/src/rsbuild/schema.ts +31 -0
- package/src/rsbuild/start-compiler-host.ts +250 -0
- package/src/rsbuild/start-router-plugin.ts +86 -0
- package/src/rsbuild/swc-rsc.ts +166 -0
- package/src/rsbuild/types.ts +20 -0
- package/src/rsbuild/virtual-modules.ts +565 -0
- package/src/start-compiler/compiler.ts +153 -19
- package/src/start-compiler/handleCreateServerFn.ts +18 -0
- package/src/start-compiler/types.ts +1 -0
- package/src/utils.ts +14 -0
- package/src/vite/import-protection-plugin/INTERNALS.md +187 -0
- package/src/{import-protection-plugin → vite/import-protection-plugin}/plugin.ts +73 -158
- package/src/{import-protection-plugin → vite/import-protection-plugin}/types.ts +5 -5
- package/src/vite/import-protection-plugin/virtualModules.ts +122 -0
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin.ts +1 -1
- package/src/vite/post-server-build.ts +14 -57
- package/src/vite/prerender.ts +19 -260
- package/src/vite/start-compiler-plugin/hot-update.ts +24 -0
- package/src/vite/start-compiler-plugin/module-specifier.ts +15 -5
- package/src/vite/start-compiler-plugin/plugin.ts +193 -18
- package/dist/esm/import-protection-plugin/ast.js.map +0 -1
- package/dist/esm/import-protection-plugin/constants.d.ts +0 -6
- package/dist/esm/import-protection-plugin/constants.js.map +0 -1
- package/dist/esm/import-protection-plugin/defaults.js.map +0 -1
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +0 -1
- package/dist/esm/import-protection-plugin/matchers.js.map +0 -1
- package/dist/esm/import-protection-plugin/plugin.js.map +0 -1
- package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +0 -13
- package/dist/esm/import-protection-plugin/postCompileUsage.js +0 -63
- package/dist/esm/import-protection-plugin/postCompileUsage.js.map +0 -1
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +0 -205
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +0 -1
- package/dist/esm/import-protection-plugin/sourceLocation.js.map +0 -1
- package/dist/esm/import-protection-plugin/trace.js.map +0 -1
- package/dist/esm/import-protection-plugin/utils.js.map +0 -1
- package/dist/esm/import-protection-plugin/virtualModules.d.ts +0 -78
- package/dist/esm/import-protection-plugin/virtualModules.js.map +0 -1
- package/dist/esm/start-compiler/load-module.d.ts +0 -14
- package/dist/esm/start-compiler/load-module.js +0 -18
- package/dist/esm/start-compiler/load-module.js.map +0 -1
- package/src/import-protection-plugin/INTERNALS.md +0 -700
- package/src/import-protection-plugin/postCompileUsage.ts +0 -100
- package/src/import-protection-plugin/rewriteDeniedImports.ts +0 -379
- package/src/start-compiler/load-module.ts +0 -31
- /package/dist/esm/{import-protection-plugin → import-protection}/ast.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/defaults.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → import-protection}/matchers.d.ts +0 -0
- /package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.d.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/ast.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/defaults.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.ts +0 -0
- /package/src/{import-protection-plugin → import-protection}/matchers.ts +0 -0
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
//#region src/vite/start-compiler-plugin/module-specifier.ts
|
|
2
2
|
function createViteDevServerFnModuleSpecifierEncoder(root) {
|
|
3
|
-
const
|
|
3
|
+
const normalizedRoot = root.replace(/\\/g, "/");
|
|
4
|
+
const rootWithTrailingSlash = normalizedRoot.endsWith("/") ? normalizedRoot : `${normalizedRoot}/`;
|
|
4
5
|
return ({ extractedFilename }) => {
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
return `/@
|
|
6
|
+
const normalizedFile = extractedFilename.replace(/\\/g, "/");
|
|
7
|
+
if (normalizedFile.startsWith(rootWithTrailingSlash)) return `/${normalizedFile.slice(rootWithTrailingSlash.length)}`;
|
|
8
|
+
return normalizedFile.startsWith("/") ? `/@fs${normalizedFile}` : `/@fs/${normalizedFile}`;
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
function decodeViteDevServerModuleSpecifier(moduleSpecifier) {
|
|
11
12
|
let sourceFile = moduleSpecifier;
|
|
12
13
|
if (sourceFile.startsWith("/@id/")) sourceFile = sourceFile.slice(5);
|
|
14
|
+
else if (sourceFile.startsWith("/@fs/")) {
|
|
15
|
+
sourceFile = sourceFile.slice(4);
|
|
16
|
+
sourceFile = sourceFile.replace(/^\/([A-Za-z]:\/)/, "$1");
|
|
17
|
+
} else if (sourceFile.startsWith("/")) sourceFile = sourceFile.slice(1);
|
|
13
18
|
const queryIndex = sourceFile.indexOf("?");
|
|
14
19
|
return queryIndex === -1 ? sourceFile : sourceFile.slice(0, queryIndex);
|
|
15
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-specifier.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/module-specifier.ts"],"sourcesContent":["import type { DevServerFnModuleSpecifierEncoder } from '../../start-compiler/types'\n\nexport function createViteDevServerFnModuleSpecifierEncoder(\n root: string,\n): DevServerFnModuleSpecifierEncoder {\n const
|
|
1
|
+
{"version":3,"file":"module-specifier.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/module-specifier.ts"],"sourcesContent":["import type { DevServerFnModuleSpecifierEncoder } from '../../start-compiler/types'\n\nexport function createViteDevServerFnModuleSpecifierEncoder(\n root: string,\n): DevServerFnModuleSpecifierEncoder {\n const normalizedRoot = root.replace(/\\\\/g, '/')\n const rootWithTrailingSlash = normalizedRoot.endsWith('/')\n ? normalizedRoot\n : `${normalizedRoot}/`\n\n return ({ extractedFilename }) => {\n const normalizedFile = extractedFilename.replace(/\\\\/g, '/')\n\n if (normalizedFile.startsWith(rootWithTrailingSlash)) {\n return `/${normalizedFile.slice(rootWithTrailingSlash.length)}`\n }\n\n return normalizedFile.startsWith('/')\n ? `/@fs${normalizedFile}`\n : `/@fs/${normalizedFile}`\n }\n}\n\nexport function decodeViteDevServerModuleSpecifier(\n moduleSpecifier: string,\n): string {\n let sourceFile = moduleSpecifier\n\n if (sourceFile.startsWith('/@id/')) {\n sourceFile = sourceFile.slice('/@id/'.length)\n } else if (sourceFile.startsWith('/@fs/')) {\n sourceFile = sourceFile.slice('/@fs'.length)\n sourceFile = sourceFile.replace(/^\\/([A-Za-z]:\\/)/, '$1')\n } else if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.slice(1)\n }\n\n const queryIndex = sourceFile.indexOf('?')\n\n return queryIndex === -1 ? sourceFile : sourceFile.slice(0, queryIndex)\n}\n"],"mappings":";AAEA,SAAgB,4CACd,MACmC;CACnC,MAAM,iBAAiB,KAAK,QAAQ,OAAO,IAAI;CAC/C,MAAM,wBAAwB,eAAe,SAAS,IAAI,GACtD,iBACA,GAAG,eAAe;AAEtB,SAAQ,EAAE,wBAAwB;EAChC,MAAM,iBAAiB,kBAAkB,QAAQ,OAAO,IAAI;AAE5D,MAAI,eAAe,WAAW,sBAAsB,CAClD,QAAO,IAAI,eAAe,MAAM,sBAAsB,OAAO;AAG/D,SAAO,eAAe,WAAW,IAAI,GACjC,OAAO,mBACP,QAAQ;;;AAIhB,SAAgB,mCACd,iBACQ;CACR,IAAI,aAAa;AAEjB,KAAI,WAAW,WAAW,QAAQ,CAChC,cAAa,WAAW,MAAM,EAAe;UACpC,WAAW,WAAW,QAAQ,EAAE;AACzC,eAAa,WAAW,MAAM,EAAc;AAC5C,eAAa,WAAW,QAAQ,oBAAoB,KAAK;YAChD,WAAW,WAAW,IAAI,CACnC,cAAa,WAAW,MAAM,EAAE;CAGlC,MAAM,aAAa,WAAW,QAAQ,IAAI;AAE1C,QAAO,eAAe,KAAK,aAAa,WAAW,MAAM,GAAG,WAAW"}
|
|
@@ -5,14 +5,46 @@ import { cleanId } from "../../start-compiler/utils.js";
|
|
|
5
5
|
import { detectKindsInCode } from "../../start-compiler/compiler.js";
|
|
6
6
|
import { getTransformCodeFilterForEnv } from "../../start-compiler/config.js";
|
|
7
7
|
import { createStartCompiler, mergeServerFnsById } from "../../start-compiler/host.js";
|
|
8
|
-
import { loadModuleForViteCompiler } from "../../start-compiler/load-module.js";
|
|
9
8
|
import { generateServerFnResolverModule } from "../../start-compiler/server-fn-resolver-module.js";
|
|
10
9
|
import { createViteDevServerFnModuleSpecifierEncoder, decodeViteDevServerModuleSpecifier } from "./module-specifier.js";
|
|
10
|
+
import { mergeHotUpdateModules } from "./hot-update.js";
|
|
11
11
|
import { resolve } from "pathe";
|
|
12
|
-
import assert from "node:assert";
|
|
13
12
|
import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
|
|
14
13
|
//#region src/vite/start-compiler-plugin/plugin.ts
|
|
15
14
|
var validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`;
|
|
15
|
+
var TSS_SERVERFN_SPLIT_PARAM = "tss-serverfn-split";
|
|
16
|
+
function invalidateMatchingFileModules(environment, ids, shouldInvalidate) {
|
|
17
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18
|
+
const invalidatedModules = [];
|
|
19
|
+
for (const id of ids) {
|
|
20
|
+
const fileModules = environment.moduleGraph.getModulesByFile(cleanId(id));
|
|
21
|
+
if (!fileModules) continue;
|
|
22
|
+
for (const fileModule of fileModules) {
|
|
23
|
+
if (!shouldInvalidate(fileModule)) continue;
|
|
24
|
+
environment.moduleGraph.invalidateModule(fileModule, seen);
|
|
25
|
+
invalidatedModules.push(fileModule);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return invalidatedModules;
|
|
29
|
+
}
|
|
30
|
+
function invalidateServerFnProviderModules(environment, ids) {
|
|
31
|
+
return invalidateMatchingFileModules(environment, ids, (fileModule) => fileModule.id?.includes(TSS_SERVERFN_SPLIT_PARAM) ?? false);
|
|
32
|
+
}
|
|
33
|
+
function invalidateServerFnLookupModules(environment, ids) {
|
|
34
|
+
invalidateMatchingFileModules(environment, ids, (fileModule) => fileModule.id?.includes("server-fn-module-lookup") ?? false);
|
|
35
|
+
}
|
|
36
|
+
function getServerFnProviderIds(ids) {
|
|
37
|
+
const providerIds = /* @__PURE__ */ new Set();
|
|
38
|
+
for (const id of ids) {
|
|
39
|
+
const cleanedId = cleanId(id);
|
|
40
|
+
providerIds.add(`${cleanedId}?${TSS_SERVERFN_SPLIT_PARAM}`);
|
|
41
|
+
}
|
|
42
|
+
return providerIds;
|
|
43
|
+
}
|
|
44
|
+
function invalidateModuleNodes(environment, modules) {
|
|
45
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46
|
+
for (const mod of modules) environment.moduleGraph.invalidateModule(mod, seen);
|
|
47
|
+
}
|
|
16
48
|
function getDevServerFnValidatorModule() {
|
|
17
49
|
return `
|
|
18
50
|
export async function getServerFnById(id, _access) {
|
|
@@ -81,13 +113,16 @@ function startCompilerPlugin(opts) {
|
|
|
81
113
|
getKnownServerFns: () => serverFnsById,
|
|
82
114
|
encodeModuleSpecifierInDev: mode === "dev" ? createViteDevServerFnModuleSpecifierEncoder(root) : void 0,
|
|
83
115
|
loadModule: async (id) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
if (mode === "build") {
|
|
117
|
+
const code = (await this.load({ id })).code ?? "";
|
|
118
|
+
compiler.ingestModule({
|
|
119
|
+
code,
|
|
120
|
+
id
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (this.environment.mode !== "dev") this.error(`could not load module ${id}: unknown environment mode ${this.environment.mode}`);
|
|
125
|
+
await this.environment.transformRequest(`${id}?${SERVER_FN_LOOKUP}`);
|
|
91
126
|
},
|
|
92
127
|
resolveId: async (source, importer) => {
|
|
93
128
|
const r = await this.resolve(source, importer);
|
|
@@ -109,13 +144,51 @@ function startCompilerPlugin(opts) {
|
|
|
109
144
|
},
|
|
110
145
|
hotUpdate(ctx) {
|
|
111
146
|
const compiler = compilers.get(this.environment.name);
|
|
147
|
+
const idsToInvalidate = /* @__PURE__ */ new Set();
|
|
148
|
+
const transitiveCompilerImportersToInvalidate = /* @__PURE__ */ new Set();
|
|
149
|
+
const importerModulesToInvalidate = /* @__PURE__ */ new Set();
|
|
112
150
|
ctx.modules.forEach((m) => {
|
|
113
151
|
if (m.id) {
|
|
114
|
-
|
|
115
|
-
|
|
152
|
+
idsToInvalidate.add(m.id);
|
|
153
|
+
const deleted = compiler?.invalidateModule(m.id);
|
|
154
|
+
if (deleted) transitiveCompilerImportersToInvalidate.add(cleanId(m.id));
|
|
155
|
+
if (deleted) m.importers.forEach((importer) => {
|
|
156
|
+
if (importer.id) {
|
|
157
|
+
idsToInvalidate.add(importer.id);
|
|
158
|
+
importerModulesToInvalidate.add(importer);
|
|
159
|
+
transitiveCompilerImportersToInvalidate.add(cleanId(importer.id));
|
|
160
|
+
}
|
|
116
161
|
});
|
|
117
162
|
}
|
|
118
163
|
});
|
|
164
|
+
const finishHotUpdate = async () => {
|
|
165
|
+
if (environment.type === "server" && compiler) {
|
|
166
|
+
const pendingImporters = [...transitiveCompilerImportersToInvalidate];
|
|
167
|
+
const seenImporters = new Set(pendingImporters);
|
|
168
|
+
while (pendingImporters.length > 0) {
|
|
169
|
+
const importerId = pendingImporters.pop();
|
|
170
|
+
const nestedImporters = await compiler.getTransitiveImporters(importerId);
|
|
171
|
+
for (const nestedImporterId of nestedImporters) {
|
|
172
|
+
if (seenImporters.has(nestedImporterId)) continue;
|
|
173
|
+
seenImporters.add(nestedImporterId);
|
|
174
|
+
pendingImporters.push(nestedImporterId);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const importerId of seenImporters) {
|
|
178
|
+
idsToInvalidate.add(importerId);
|
|
179
|
+
compiler.invalidateModule(importerId);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
invalidateModuleNodes(this.environment, importerModulesToInvalidate);
|
|
183
|
+
invalidateServerFnLookupModules(this.environment, idsToInvalidate);
|
|
184
|
+
if (environment.type !== "server") return;
|
|
185
|
+
invalidateModuleNodes(this.environment, ctx.modules);
|
|
186
|
+
const providerIdsToInvalidate = getServerFnProviderIds(idsToInvalidate);
|
|
187
|
+
for (const providerId of providerIdsToInvalidate) compiler?.invalidateModule(providerId);
|
|
188
|
+
const providerModules = invalidateServerFnProviderModules(this.environment, [...idsToInvalidate, ...providerIdsToInvalidate]);
|
|
189
|
+
return mergeHotUpdateModules(ctx.modules, providerModules);
|
|
190
|
+
};
|
|
191
|
+
return finishHotUpdate();
|
|
119
192
|
}
|
|
120
193
|
};
|
|
121
194
|
}
|
|
@@ -151,8 +224,8 @@ function startCompilerPlugin(opts) {
|
|
|
151
224
|
const sourceFile = decodeViteDevServerModuleSpecifier(decoded.file);
|
|
152
225
|
if (sourceFile) {
|
|
153
226
|
const absPath = resolve(root, sourceFile);
|
|
154
|
-
|
|
155
|
-
await this.environment.
|
|
227
|
+
if (this.environment.mode !== "dev") this.error(`could not validate server function ID ${fnId}: unknown environment mode ${this.environment.mode}`);
|
|
228
|
+
await this.environment.transformRequest(`${absPath}?${SERVER_FN_LOOKUP}`);
|
|
156
229
|
if (serverFnsById[fnId]) return `export {}`;
|
|
157
230
|
}
|
|
158
231
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/plugin.ts"],"sourcesContent":["import assert from 'node:assert'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { resolve as resolvePath } from 'pathe'\nimport {\n SERVER_FN_LOOKUP,\n TRANSFORM_ID_REGEX,\n VITE_ENVIRONMENT_NAMES,\n} from '../../constants'\nimport { detectKindsInCode } from '../../start-compiler/compiler'\nimport { getTransformCodeFilterForEnv } from '../../start-compiler/config'\nimport {\n createStartCompiler,\n mergeServerFnsById,\n} from '../../start-compiler/host'\nimport { loadModuleForViteCompiler } from '../../start-compiler/load-module'\nimport { generateServerFnResolverModule } from '../../start-compiler/server-fn-resolver-module'\nimport { cleanId } from '../../start-compiler/utils'\nimport { createVirtualModule } from '../createVirtualModule'\nimport { resolveViteId } from '../../utils'\nimport {\n createViteDevServerFnModuleSpecifierEncoder,\n decodeViteDevServerModuleSpecifier,\n} from './module-specifier'\nimport type { CompileStartFrameworkOptions } from '../../types'\nimport type {\n GenerateFunctionIdFnOptional,\n ServerFn,\n} from '../../start-compiler/types'\nimport type { PluginOption } from 'vite'\n\n// Re-export from shared constants for backwards compatibility\nexport { SERVER_FN_LOOKUP }\n\nconst validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`\n\nfunction getDevServerFnValidatorModule(): string {\n return `\nexport async function getServerFnById(id, _access) {\n const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id\n await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)\n const decoded = Buffer.from(id, 'base64url').toString('utf8')\n const devServerFn = JSON.parse(decoded)\n const mod = await import(/* @vite-ignore */ devServerFn.file)\n return mod[devServerFn.export]\n}\n`\n}\n\nfunction parseIdQuery(id: string): {\n filename: string\n query: {\n [k: string]: string\n }\n} {\n if (!id.includes('?')) return { filename: id, query: {} }\n const [filename, rawQuery] = id.split(`?`, 2) as [string, string]\n const query = Object.fromEntries(new URLSearchParams(rawQuery))\n return { filename, query }\n}\n\nexport interface StartCompilerPluginOptions {\n framework: CompileStartFrameworkOptions\n environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }>\n /**\n * Custom function ID generator (optional).\n */\n generateFunctionId?: GenerateFunctionIdFnOptional\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n}\n\nexport function startCompilerPlugin(\n opts: StartCompilerPluginOptions,\n): PluginOption {\n const compilers = new Map<string, ReturnType<typeof createStartCompiler>>()\n\n // Shared registry of server functions across all environments\n const serverFnsById: Record<string, ServerFn> = {}\n\n const onServerFnsById = (d: Record<string, ServerFn>) => {\n mergeServerFnsById(serverFnsById, d)\n }\n\n let root = process.cwd()\n // Determine which environments need the resolver (getServerFnById)\n // SSR environment always needs the resolver for server-side calls\n // Provider environment needs it for the actual implementation\n const ssrEnvName = VITE_ENVIRONMENT_NAMES.server\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = opts.providerEnvName === ssrEnvName\n\n // Environments that need the resolver: SSR (for server calls) and provider (for implementation)\n const appliedResolverEnvironments = new Set(\n ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],\n )\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n configResolved(config) {\n root = config.root\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers.get(this.environment.name)\n\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode = this.environment.mode === 'build' ? 'build' : 'dev'\n\n compiler = createStartCompiler({\n env: environment.type,\n envName: environment.name,\n root,\n mode,\n framework: opts.framework,\n providerEnvName: opts.providerEnvName,\n generateFunctionId: opts.generateFunctionId,\n onServerFnsById,\n getKnownServerFns: () => serverFnsById,\n encodeModuleSpecifierInDev:\n mode === 'dev'\n ? createViteDevServerFnModuleSpecifierEncoder(root)\n : undefined,\n loadModule: async (id: string) => {\n await loadModuleForViteCompiler({\n compiler: compiler!,\n mode: this.environment.mode,\n fetchModule:\n this.environment.mode === 'dev'\n ? this.environment.fetchModule.bind(this.environment)\n : undefined,\n loadModule: this.load.bind(this),\n id,\n })\n },\n\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n\n return null\n },\n })\n\n compilers.set(this.environment.name, compiler)\n }\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n const result = await compiler.compile({\n id,\n code,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers.get(this.environment.name)\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n const deleted = compiler?.invalidateModule(m.id)\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n compiler?.invalidateModule(importer.id)\n }\n })\n }\n }\n })\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers.get(this.environment.name)\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n // Validate server function ID in dev mode\n {\n name: 'tanstack-start-core:validate-server-fn-id',\n apply: 'serve',\n load: {\n filter: {\n id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),\n },\n async handler(id) {\n const parsed = parseIdQuery(id)\n const fnId = parsed.query.id\n if (fnId && serverFnsById[fnId]) {\n return `export {}`\n }\n\n // ID not yet registered — the source file may not have been\n // transformed in this dev session yet (e.g. cold restart with\n // cached client). Try to decode the ID, discover the source\n // file, trigger its compilation, and re-check.\n if (fnId) {\n try {\n const decoded = JSON.parse(\n Buffer.from(fnId, 'base64url').toString('utf8'),\n )\n if (\n typeof decoded.file === 'string' &&\n typeof decoded.export === 'string'\n ) {\n // Use the Vite strategy to decode the module specifier\n // back to the original source file path.\n const sourceFile = decodeViteDevServerModuleSpecifier(\n decoded.file,\n )\n\n if (sourceFile) {\n // Resolve to absolute path\n const absPath = resolvePath(root, sourceFile)\n\n // Trigger transform of the source file in this environment,\n // which will compile createServerFn calls and populate\n // serverFnsById as a side effect.\n // This plugin only runs in dev (apply: 'serve'), so mode\n // must be 'dev' — assert to narrow to DevEnvironment.\n assert(this.environment.mode === 'dev')\n await this.environment.fetchModule(absPath)\n\n // Re-check after lazy compilation\n if (serverFnsById[fnId]) {\n return `export {}`\n }\n }\n }\n } catch {\n // Decoding or fetching failed — fall through to error\n }\n }\n\n this.error(`Invalid server function ID: ${fnId}`)\n },\n },\n },\n // Manifest plugin for server environments\n createVirtualModule({\n name: 'tanstack-start-core:server-fn-resolver',\n moduleId: VIRTUAL_MODULES.serverFnResolver,\n enforce: 'pre',\n applyToEnvironment: (env) => {\n return appliedResolverEnvironments.has(env.name)\n },\n load() {\n if (this.environment.name !== opts.providerEnvName) {\n const mod = opts.environments.find(\n (e) => e.name === this.environment.name,\n )?.getServerFnById\n if (mod) {\n return mod\n }\n\n this.error(\n `No getServerFnById implementation found for caller environment: ${this.environment.name}`,\n )\n }\n\n if (this.environment.mode !== 'build') {\n return getDevServerFnValidatorModule()\n }\n\n // When SSR is the provider, server-only-referenced functions aren't in the manifest,\n // so no isClientReferenced check is needed.\n // When SSR is NOT the provider (custom provider env), server-only-referenced\n // functions ARE in the manifest and need the isClientReferenced check to\n // block direct client HTTP requests to server-only-referenced functions.\n return generateServerFnResolverModule({\n serverFnsById,\n includeClientReferencedCheck: !ssrIsProvider,\n })\n },\n }),\n ]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/plugin.ts"],"sourcesContent":["import { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { resolve as resolvePath } from 'pathe'\nimport {\n SERVER_FN_LOOKUP,\n TRANSFORM_ID_REGEX,\n VITE_ENVIRONMENT_NAMES,\n} from '../../constants'\nimport { detectKindsInCode } from '../../start-compiler/compiler'\nimport { getTransformCodeFilterForEnv } from '../../start-compiler/config'\nimport {\n createStartCompiler,\n mergeServerFnsById,\n} from '../../start-compiler/host'\nimport { generateServerFnResolverModule } from '../../start-compiler/server-fn-resolver-module'\nimport { cleanId } from '../../start-compiler/utils'\nimport { createVirtualModule } from '../createVirtualModule'\nimport { resolveViteId } from '../../utils'\nimport {\n createViteDevServerFnModuleSpecifierEncoder,\n decodeViteDevServerModuleSpecifier,\n} from './module-specifier'\nimport { mergeHotUpdateModules } from './hot-update'\nimport type { CompileStartFrameworkOptions } from '../../types'\nimport type {\n GenerateFunctionIdFnOptional,\n ServerFn,\n} from '../../start-compiler/types'\nimport type { EnvironmentModuleNode, PluginOption } from 'vite'\n\n// Re-export from shared constants for backwards compatibility\nexport { SERVER_FN_LOOKUP }\n\nconst validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`\nconst TSS_SERVERFN_SPLIT_PARAM = 'tss-serverfn-split'\n\ntype ModuleInvalidationEnvironment = {\n moduleGraph: {\n getModulesByFile: (file: string) => Set<EnvironmentModuleNode> | undefined\n invalidateModule: (\n mod: EnvironmentModuleNode,\n seen?: Set<EnvironmentModuleNode>,\n ) => void\n }\n}\n\nfunction invalidateMatchingFileModules(\n environment: ModuleInvalidationEnvironment,\n ids: Iterable<string>,\n shouldInvalidate: (mod: EnvironmentModuleNode) => boolean,\n) {\n const seen = new Set<EnvironmentModuleNode>()\n const invalidatedModules: Array<EnvironmentModuleNode> = []\n\n for (const id of ids) {\n const fileModules = environment.moduleGraph.getModulesByFile(cleanId(id))\n\n if (!fileModules) {\n continue\n }\n\n for (const fileModule of fileModules) {\n if (!shouldInvalidate(fileModule)) {\n continue\n }\n\n environment.moduleGraph.invalidateModule(fileModule, seen)\n invalidatedModules.push(fileModule)\n }\n }\n\n return invalidatedModules\n}\n\nfunction invalidateServerFnProviderModules(\n environment: {\n moduleGraph: {\n getModulesByFile: (file: string) => Set<EnvironmentModuleNode> | undefined\n invalidateModule: (\n mod: EnvironmentModuleNode,\n seen?: Set<EnvironmentModuleNode>,\n ) => void\n }\n },\n ids: Iterable<string>,\n) {\n return invalidateMatchingFileModules(\n environment,\n ids,\n (fileModule) => fileModule.id?.includes(TSS_SERVERFN_SPLIT_PARAM) ?? false,\n )\n}\n\nfunction invalidateServerFnLookupModules(\n environment: ModuleInvalidationEnvironment,\n ids: Iterable<string>,\n) {\n invalidateMatchingFileModules(\n environment,\n ids,\n (fileModule) => fileModule.id?.includes(SERVER_FN_LOOKUP) ?? false,\n )\n}\n\nfunction getServerFnProviderIds(ids: Iterable<string>) {\n const providerIds = new Set<string>()\n\n for (const id of ids) {\n const cleanedId = cleanId(id)\n providerIds.add(`${cleanedId}?${TSS_SERVERFN_SPLIT_PARAM}`)\n }\n\n return providerIds\n}\n\nfunction invalidateModuleNodes(\n environment: {\n moduleGraph: {\n invalidateModule: (\n mod: EnvironmentModuleNode,\n seen?: Set<EnvironmentModuleNode>,\n ) => void\n }\n },\n modules: Iterable<EnvironmentModuleNode>,\n) {\n const seen = new Set<EnvironmentModuleNode>()\n\n for (const mod of modules) {\n environment.moduleGraph.invalidateModule(mod, seen)\n }\n}\n\nfunction getDevServerFnValidatorModule(): string {\n return `\nexport async function getServerFnById(id, _access) {\n const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id\n await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)\n const decoded = Buffer.from(id, 'base64url').toString('utf8')\n const devServerFn = JSON.parse(decoded)\n const mod = await import(/* @vite-ignore */ devServerFn.file)\n return mod[devServerFn.export]\n}\n`\n}\n\nfunction parseIdQuery(id: string): {\n filename: string\n query: {\n [k: string]: string\n }\n} {\n if (!id.includes('?')) return { filename: id, query: {} }\n const [filename, rawQuery] = id.split(`?`, 2) as [string, string]\n const query = Object.fromEntries(new URLSearchParams(rawQuery))\n return { filename, query }\n}\n\nexport interface StartCompilerPluginOptions {\n framework: CompileStartFrameworkOptions\n environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }>\n /**\n * Custom function ID generator (optional).\n */\n generateFunctionId?: GenerateFunctionIdFnOptional\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n}\n\nexport function startCompilerPlugin(\n opts: StartCompilerPluginOptions,\n): PluginOption {\n const compilers = new Map<string, ReturnType<typeof createStartCompiler>>()\n\n // Shared registry of server functions across all environments\n const serverFnsById: Record<string, ServerFn> = {}\n\n const onServerFnsById = (d: Record<string, ServerFn>) => {\n mergeServerFnsById(serverFnsById, d)\n }\n\n let root = process.cwd()\n // Determine which environments need the resolver (getServerFnById)\n // SSR environment always needs the resolver for server-side calls\n // Provider environment needs it for the actual implementation\n const ssrEnvName = VITE_ENVIRONMENT_NAMES.server\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = opts.providerEnvName === ssrEnvName\n\n // Environments that need the resolver: SSR (for server calls) and provider (for implementation)\n const appliedResolverEnvironments = new Set(\n ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],\n )\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n configResolved(config) {\n root = config.root\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers.get(this.environment.name)\n\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode = this.environment.mode === 'build' ? 'build' : 'dev'\n\n compiler = createStartCompiler({\n env: environment.type,\n envName: environment.name,\n root,\n mode,\n framework: opts.framework,\n providerEnvName: opts.providerEnvName,\n generateFunctionId: opts.generateFunctionId,\n onServerFnsById,\n getKnownServerFns: () => serverFnsById,\n encodeModuleSpecifierInDev:\n mode === 'dev'\n ? createViteDevServerFnModuleSpecifierEncoder(root)\n : undefined,\n loadModule: async (id: string) => {\n if (mode === 'build') {\n const loaded = await this.load({ id })\n const code = loaded.code ?? ''\n\n compiler!.ingestModule({ code, id })\n return\n }\n\n if (this.environment.mode !== 'dev') {\n this.error(\n `could not load module ${id}: unknown environment mode ${this.environment.mode}`,\n )\n }\n\n await this.environment.transformRequest(\n `${id}?${SERVER_FN_LOOKUP}`,\n )\n },\n\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n\n return null\n },\n })\n\n compilers.set(this.environment.name, compiler)\n }\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n const result = await compiler.compile({\n id,\n code,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers.get(this.environment.name)\n const idsToInvalidate = new Set<string>()\n const transitiveCompilerImportersToInvalidate = new Set<string>()\n const importerModulesToInvalidate = new Set<EnvironmentModuleNode>()\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n idsToInvalidate.add(m.id)\n const deleted = compiler?.invalidateModule(m.id)\n\n if (deleted) {\n transitiveCompilerImportersToInvalidate.add(cleanId(m.id))\n }\n\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n idsToInvalidate.add(importer.id)\n importerModulesToInvalidate.add(importer)\n transitiveCompilerImportersToInvalidate.add(\n cleanId(importer.id),\n )\n }\n })\n }\n }\n })\n\n const finishHotUpdate = async () => {\n if (environment.type === 'server' && compiler) {\n const pendingImporters = [\n ...transitiveCompilerImportersToInvalidate,\n ]\n const seenImporters = new Set(pendingImporters)\n\n while (pendingImporters.length > 0) {\n const importerId = pendingImporters.pop()!\n const nestedImporters =\n await compiler.getTransitiveImporters(importerId)\n\n for (const nestedImporterId of nestedImporters) {\n if (seenImporters.has(nestedImporterId)) {\n continue\n }\n\n seenImporters.add(nestedImporterId)\n pendingImporters.push(nestedImporterId)\n }\n }\n\n for (const importerId of seenImporters) {\n idsToInvalidate.add(importerId)\n compiler.invalidateModule(importerId)\n }\n }\n\n invalidateModuleNodes(this.environment, importerModulesToInvalidate)\n invalidateServerFnLookupModules(this.environment, idsToInvalidate)\n\n if (environment.type !== 'server') {\n return\n }\n\n invalidateModuleNodes(this.environment, ctx.modules)\n\n const providerIdsToInvalidate =\n getServerFnProviderIds(idsToInvalidate)\n for (const providerId of providerIdsToInvalidate) {\n compiler?.invalidateModule(providerId)\n }\n\n const providerModules = invalidateServerFnProviderModules(\n this.environment,\n [...idsToInvalidate, ...providerIdsToInvalidate],\n )\n\n return mergeHotUpdateModules(ctx.modules, providerModules)\n }\n\n return finishHotUpdate()\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers.get(this.environment.name)\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n // Validate server function ID in dev mode\n {\n name: 'tanstack-start-core:validate-server-fn-id',\n apply: 'serve',\n load: {\n filter: {\n id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),\n },\n async handler(id) {\n const parsed = parseIdQuery(id)\n const fnId = parsed.query.id\n if (fnId && serverFnsById[fnId]) {\n return `export {}`\n }\n\n // ID not yet registered — the source file may not have been\n // transformed in this dev session yet (e.g. cold restart with\n // cached client). Try to decode the ID, discover the source\n // file, trigger its compilation, and re-check.\n if (fnId) {\n try {\n const decoded = JSON.parse(\n Buffer.from(fnId, 'base64url').toString('utf8'),\n )\n if (\n typeof decoded.file === 'string' &&\n typeof decoded.export === 'string'\n ) {\n // Use the Vite strategy to decode the module specifier\n // back to the original source file path.\n const sourceFile = decodeViteDevServerModuleSpecifier(\n decoded.file,\n )\n\n if (sourceFile) {\n // Resolve to absolute path\n const absPath = resolvePath(root, sourceFile)\n\n // Trigger transform of the source file in this environment,\n // which will compile createServerFn calls and populate\n // serverFnsById as a side effect.\n if (this.environment.mode !== 'dev') {\n this.error(\n `could not validate server function ID ${fnId}: unknown environment mode ${this.environment.mode}`,\n )\n }\n\n await this.environment.transformRequest(\n `${absPath}?${SERVER_FN_LOOKUP}`,\n )\n\n // Re-check after lazy compilation\n if (serverFnsById[fnId]) {\n return `export {}`\n }\n }\n }\n } catch {\n // Decoding or fetching failed — fall through to error\n }\n }\n\n this.error(`Invalid server function ID: ${fnId}`)\n },\n },\n },\n // Manifest plugin for server environments\n createVirtualModule({\n name: 'tanstack-start-core:server-fn-resolver',\n moduleId: VIRTUAL_MODULES.serverFnResolver,\n enforce: 'pre',\n applyToEnvironment: (env) => {\n return appliedResolverEnvironments.has(env.name)\n },\n load() {\n if (this.environment.name !== opts.providerEnvName) {\n const mod = opts.environments.find(\n (e) => e.name === this.environment.name,\n )?.getServerFnById\n if (mod) {\n return mod\n }\n\n this.error(\n `No getServerFnById implementation found for caller environment: ${this.environment.name}`,\n )\n }\n\n if (this.environment.mode !== 'build') {\n return getDevServerFnValidatorModule()\n }\n\n // When SSR is the provider, server-only-referenced functions aren't in the manifest,\n // so no isClientReferenced check is needed.\n // When SSR is NOT the provider (custom provider env), server-only-referenced\n // functions ARE in the manifest and need the isClientReferenced check to\n // block direct client HTTP requests to server-only-referenced functions.\n return generateServerFnResolverModule({\n serverFnsById,\n includeClientReferencedCheck: !ssrIsProvider,\n })\n },\n }),\n ]\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-plugin-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.169.0",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,24 @@
|
|
|
40
40
|
"default": "./dist/esm/utils.js"
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
|
+
"./vite": {
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./dist/esm/vite/index.d.ts",
|
|
46
|
+
"default": "./dist/esm/vite/index.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"./rsbuild": {
|
|
50
|
+
"import": {
|
|
51
|
+
"types": "./dist/esm/rsbuild/index.d.ts",
|
|
52
|
+
"default": "./dist/esm/rsbuild/index.js"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"./rsbuild/types": {
|
|
56
|
+
"import": {
|
|
57
|
+
"types": "./dist/esm/rsbuild/types.d.ts",
|
|
58
|
+
"default": "./dist/esm/rsbuild/types.js"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
43
61
|
"./package.json": "./package.json"
|
|
44
62
|
},
|
|
45
63
|
"sideEffects": false,
|
|
@@ -68,13 +86,14 @@
|
|
|
68
86
|
"xmlbuilder2": "^4.0.3",
|
|
69
87
|
"zod": "^3.24.2",
|
|
70
88
|
"@tanstack/router-core": "1.168.15",
|
|
71
|
-
"@tanstack/router-generator": "1.166.
|
|
72
|
-
"@tanstack/router-plugin": "1.167.
|
|
73
|
-
"@tanstack/router-utils": "1.161.
|
|
89
|
+
"@tanstack/router-generator": "1.166.33",
|
|
90
|
+
"@tanstack/router-plugin": "1.167.23",
|
|
91
|
+
"@tanstack/router-utils": "1.161.7",
|
|
74
92
|
"@tanstack/start-client-core": "1.167.17",
|
|
75
93
|
"@tanstack/start-server-core": "1.167.19"
|
|
76
94
|
},
|
|
77
95
|
"devDependencies": {
|
|
96
|
+
"@rsbuild/core": "^2.0.0",
|
|
78
97
|
"@types/babel__code-frame": "^7.0.6",
|
|
79
98
|
"@types/babel__core": "^7.20.5",
|
|
80
99
|
"@types/picomatch": "^4.0.2",
|
|
@@ -82,8 +101,17 @@
|
|
|
82
101
|
"@types/node": ">=20"
|
|
83
102
|
},
|
|
84
103
|
"peerDependencies": {
|
|
104
|
+
"@rsbuild/core": "^2.0.0",
|
|
85
105
|
"vite": ">=7.0.0"
|
|
86
106
|
},
|
|
107
|
+
"peerDependenciesMeta": {
|
|
108
|
+
"@rsbuild/core": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"vite": {
|
|
112
|
+
"optional": true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
87
115
|
"scripts": {
|
|
88
116
|
"clean": "rimraf ./dist && rimraf ./coverage",
|
|
89
117
|
"clean:snapshots": "rimraf **/*snapshot* --glob",
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Import Protection - Shared Internals
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`src/import-protection/` contains the adapter-independent core for TanStack
|
|
6
|
+
Start import protection.
|
|
7
|
+
|
|
8
|
+
Its current structure is:
|
|
9
|
+
|
|
10
|
+
- parse module code once per `TransformResult`
|
|
11
|
+
- derive a read-only `ImportAnalysis` object from that AST
|
|
12
|
+
- reuse that analysis for source extraction, import-specifier locations, mock
|
|
13
|
+
export discovery, named export discovery, and usage lookup
|
|
14
|
+
- keep AST mutation in a separate rewrite pass
|
|
15
|
+
- keep adapter lifecycle/state-machine behavior outside this directory
|
|
16
|
+
|
|
17
|
+
The shared layer is responsible for correctness-sensitive logic that must stay
|
|
18
|
+
aligned between Vite and Rsbuild.
|
|
19
|
+
|
|
20
|
+
## Shared Module Layout
|
|
21
|
+
|
|
22
|
+
### `analysis.ts`
|
|
23
|
+
|
|
24
|
+
The central shared analysis module.
|
|
25
|
+
|
|
26
|
+
It builds and caches `ImportAnalysis` on `TransformResult.analysis`.
|
|
27
|
+
|
|
28
|
+
`ImportAnalysis` contains:
|
|
29
|
+
|
|
30
|
+
- parsed AST
|
|
31
|
+
- line index
|
|
32
|
+
- import sources in source order
|
|
33
|
+
- import specifier literal locations
|
|
34
|
+
- imported bindings by source
|
|
35
|
+
- mock export names by source
|
|
36
|
+
- named exports
|
|
37
|
+
- usage-position cache
|
|
38
|
+
|
|
39
|
+
It exposes both result-based and code-based helpers:
|
|
40
|
+
|
|
41
|
+
- `getOrCreateImportAnalysis(result)`
|
|
42
|
+
- `getImportSourcesFromResult(result)`
|
|
43
|
+
- `getImportSources(code)`
|
|
44
|
+
- `getImportSpecifierLocationFromResult(result, source)`
|
|
45
|
+
- `getMockExportNamesBySourceFromResult(result)`
|
|
46
|
+
- `getMockExportNamesBySource(code)`
|
|
47
|
+
- `getNamedExportsFromResult(result)`
|
|
48
|
+
- `getNamedExports(code)`
|
|
49
|
+
- `findPostCompileUsagePosFromResult(result, source)`
|
|
50
|
+
- `findPostCompileUsagePos(code, source)`
|
|
51
|
+
- `findOriginalUnsafeUsagePosFromResult(result, source, envType)`
|
|
52
|
+
- `findOriginalUnsafeUsagePos(code, source, envType)`
|
|
53
|
+
- `isValidExportName(name)`
|
|
54
|
+
|
|
55
|
+
### `adapterUtils.ts`
|
|
56
|
+
|
|
57
|
+
Contains small adapter-facing shared helpers for decisions that are identical in
|
|
58
|
+
Vite and Rsbuild.
|
|
59
|
+
|
|
60
|
+
It currently owns:
|
|
61
|
+
|
|
62
|
+
- environment type selection from `envTypeMap`
|
|
63
|
+
- environment rule selection from `compiledRules`
|
|
64
|
+
- normalized root-relative path calculation
|
|
65
|
+
- importer filtering via include/exclude/ignore/srcDirectory rules
|
|
66
|
+
|
|
67
|
+
This keeps the adapters from duplicating the same targeting logic while still
|
|
68
|
+
leaving lifecycle-specific behavior in the adapter implementations.
|
|
69
|
+
|
|
70
|
+
### `rewrite.ts`
|
|
71
|
+
|
|
72
|
+
Contains the actual AST mutation pass for denied static imports and re-exports.
|
|
73
|
+
|
|
74
|
+
It is intentionally separate from `analysis.ts`.
|
|
75
|
+
|
|
76
|
+
Why:
|
|
77
|
+
|
|
78
|
+
- analysis is read-only and cacheable
|
|
79
|
+
- rewrite mutates AST structure and generates code/maps
|
|
80
|
+
- keeping them separate prevents adapter code from depending on mutation when it
|
|
81
|
+
only needs inspection
|
|
82
|
+
|
|
83
|
+
`rewriteDeniedImports()` rewrites denied static import/re-export edges to mock
|
|
84
|
+
module imports while preserving explicit bindings expected by the importer.
|
|
85
|
+
|
|
86
|
+
### `sourceLocation.ts`
|
|
87
|
+
|
|
88
|
+
Builds source locations and snippets from transformed code plus sourcemaps.
|
|
89
|
+
|
|
90
|
+
It reuses shared analysis for two important lookups:
|
|
91
|
+
|
|
92
|
+
- import-specifier positions in transformed code
|
|
93
|
+
- post-compile and original-source usage locations
|
|
94
|
+
|
|
95
|
+
It also owns:
|
|
96
|
+
|
|
97
|
+
- line-index helpers
|
|
98
|
+
- sourcemap normalization
|
|
99
|
+
- `sourcesContent` original-source selection
|
|
100
|
+
- import-location caching for traces
|
|
101
|
+
- code snippet formatting
|
|
102
|
+
|
|
103
|
+
### `virtualModules.ts`
|
|
104
|
+
|
|
105
|
+
Owns the shared mock module generators and payload encoding.
|
|
106
|
+
|
|
107
|
+
This includes:
|
|
108
|
+
|
|
109
|
+
- silent mock module code
|
|
110
|
+
- runtime diagnostic mock module code
|
|
111
|
+
- mock-edge module code with explicit named exports
|
|
112
|
+
- self-denial module generation for denied files
|
|
113
|
+
- marker module loading
|
|
114
|
+
|
|
115
|
+
The adapter-specific transport of these modules is not handled here.
|
|
116
|
+
|
|
117
|
+
### `defaults.ts`, `matchers.ts`, `utils.ts`, `trace.ts`
|
|
118
|
+
|
|
119
|
+
These remain shared support layers:
|
|
120
|
+
|
|
121
|
+
- `defaults.ts`: default deny/marker rules
|
|
122
|
+
- `matchers.ts`: compiled glob/regex matcher utilities
|
|
123
|
+
- `utils.ts`: path normalization, candidate generation, messages, defer logic,
|
|
124
|
+
generic helpers
|
|
125
|
+
- `trace.ts`: import graph, traces, snippet-aware message formatting
|
|
126
|
+
|
|
127
|
+
## Analysis Lifetime And Cache Model
|
|
128
|
+
|
|
129
|
+
The key cache decision is: analysis is cached per `TransformResult`, not
|
|
130
|
+
globally by source text.
|
|
131
|
+
|
|
132
|
+
That means:
|
|
133
|
+
|
|
134
|
+
- Vite can attach analysis to transform-cache results for the current module
|
|
135
|
+
version
|
|
136
|
+
- Rsbuild can attach analysis to transform results rebuilt from compilation or
|
|
137
|
+
preloaded during transform
|
|
138
|
+
- cache invalidation follows the host tool's normal module invalidation model
|
|
139
|
+
- long dev/HMR sessions do not accumulate an unbounded process-global AST cache
|
|
140
|
+
|
|
141
|
+
The analysis object is read-only after creation, except for the internal
|
|
142
|
+
`usageByKey` memoization map.
|
|
143
|
+
|
|
144
|
+
## Import Source Extraction
|
|
145
|
+
|
|
146
|
+
`getImportSources()` is now AST-driven and returns sources in encountered order.
|
|
147
|
+
|
|
148
|
+
It includes:
|
|
149
|
+
|
|
150
|
+
- `import ... from 'x'`
|
|
151
|
+
- `export ... from 'x'`
|
|
152
|
+
- `export * from 'x'`
|
|
153
|
+
- `import('x')`
|
|
154
|
+
- dynamic import call forms supported by Babel's AST shape
|
|
155
|
+
|
|
156
|
+
Adapter code that needs source extraction should call into `analysis.ts`
|
|
157
|
+
directly.
|
|
158
|
+
|
|
159
|
+
## Mock Export Discovery
|
|
160
|
+
|
|
161
|
+
Mock-edge modules need to know which named exports the importer expects.
|
|
162
|
+
|
|
163
|
+
`analysis.ts` derives that from the importer AST by recording:
|
|
164
|
+
|
|
165
|
+
- named import specifiers
|
|
166
|
+
- namespace member access
|
|
167
|
+
- default-import member access
|
|
168
|
+
- re-export specifiers
|
|
169
|
+
|
|
170
|
+
This produces `mockExportNamesBySource`, which is a safe over-approximation of
|
|
171
|
+
the named exports the adapter should expose on a mock-edge module.
|
|
172
|
+
|
|
173
|
+
## Named Export Discovery
|
|
174
|
+
|
|
175
|
+
Self-denial needs the denied file's export surface.
|
|
176
|
+
|
|
177
|
+
`getNamedExports()` and `getNamedExportsFromResult()` collect valid named
|
|
178
|
+
exports from:
|
|
179
|
+
|
|
180
|
+
- function/class declarations
|
|
181
|
+
- variable declarations, including destructuring patterns
|
|
182
|
+
- export specifiers and aliases
|
|
183
|
+
|
|
184
|
+
`default` is excluded from the named export list because the mock generators
|
|
185
|
+
handle default export separately.
|
|
186
|
+
|
|
187
|
+
## Usage Lookup And Safe Boundaries
|
|
188
|
+
|
|
189
|
+
Usage lookup is shared across adapters through `analysis.ts`.
|
|
190
|
+
|
|
191
|
+
The walker:
|
|
192
|
+
|
|
193
|
+
- tracks lexical/program/block/function bindings manually
|
|
194
|
+
- respects shadowing across nested scopes, block bindings, function params, and
|
|
195
|
+
`catch` params
|
|
196
|
+
- treats certain compiler-recognized boundaries as safe when looking for
|
|
197
|
+
original unsafe usage
|
|
198
|
+
|
|
199
|
+
Safe boundary recognition is environment-specific:
|
|
200
|
+
|
|
201
|
+
- client env safe boundaries:
|
|
202
|
+
- `createServerFn().handler(...)`
|
|
203
|
+
- `createMiddleware(...).server(...)`
|
|
204
|
+
- `createIsomorphicFn().server(...)`
|
|
205
|
+
- `createServerOnlyFn(...)`
|
|
206
|
+
- server env safe boundaries:
|
|
207
|
+
- `createClientOnlyFn(...)`
|
|
208
|
+
- `createIsomorphicFn().client(...)`
|
|
209
|
+
|
|
210
|
+
This is the core shared logic that keeps both adapters aligned on cases like
|
|
211
|
+
`boundary-safe`, `factory-safe`, and `compiler-leak`.
|
|
212
|
+
|
|
213
|
+
## Rewriting Versus Analysis
|
|
214
|
+
|
|
215
|
+
Analysis and rewrite are intentionally separate.
|
|
216
|
+
|
|
217
|
+
Reasons:
|
|
218
|
+
|
|
219
|
+
- adapters frequently need inspection without mutation
|
|
220
|
+
- mutation wants a fresh, isolated AST/codegen pass
|
|
221
|
+
- read-only analysis is much easier to cache safely
|
|
222
|
+
- source-location helpers and usage lookup should not depend on whether a module
|
|
223
|
+
was rewritten
|
|
224
|
+
|
|
225
|
+
The intended flow is:
|
|
226
|
+
|
|
227
|
+
1. build or reuse `ImportAnalysis`
|
|
228
|
+
2. inspect/import-locate/trace as needed
|
|
229
|
+
3. if a denied-specifier rewrite is needed, run `rewriteDeniedImports()`
|
|
230
|
+
|
|
231
|
+
## Adapter Contract
|
|
232
|
+
|
|
233
|
+
The shared layer deliberately does not decide:
|
|
234
|
+
|
|
235
|
+
- when a violation is discovered
|
|
236
|
+
- when it is reported versus deferred
|
|
237
|
+
- how dev/build lifecycle hooks work
|
|
238
|
+
- how virtual module ids/files are transported by the bundler
|
|
239
|
+
- how final compilation truth is obtained
|
|
240
|
+
|
|
241
|
+
Those are adapter responsibilities.
|
|
242
|
+
|
|
243
|
+
The shared layer does provide the primitives both adapters rely on:
|
|
244
|
+
|
|
245
|
+
- rule helpers
|
|
246
|
+
- AST parsing
|
|
247
|
+
- analysis
|
|
248
|
+
- rewrite
|
|
249
|
+
- virtual module code generation
|
|
250
|
+
- source locations/snippets
|
|
251
|
+
- trace formatting
|
|
252
|
+
|
|
253
|
+
## Maintainer Guidance
|
|
254
|
+
|
|
255
|
+
When making future import-protection changes:
|
|
256
|
+
|
|
257
|
+
1. If the behavior is about parsing, source extraction, export discovery, usage
|
|
258
|
+
lookup, source locations, rewrite mechanics, or mock codegen, it probably
|
|
259
|
+
belongs in `src/import-protection/`.
|
|
260
|
+
2. If the behavior is about hook timing, deferral, compilation truth, pending
|
|
261
|
+
queues, or environment-specific virtual-module transport, it probably belongs
|
|
262
|
+
in the adapter docs/code.
|
|
263
|
+
3. Prefer extending `ImportAnalysis` over adding new one-off parsers or wrapper
|
|
264
|
+
helpers.
|
|
265
|
+
4. Avoid reintroducing global source-text caches; cache on `TransformResult`
|
|
266
|
+
instead.
|