oidc-spa 10.2.6 → 10.2.8
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/core/createOidc.js +1 -1
- package/esm/core/createOidc.mjs +1 -1
- package/esm/tanstack-start/react/createOidcSpaUtils.mjs +1 -1
- package/esm/tanstack-start/react/createOidcSpaUtils.mjs.map +1 -1
- package/esm/vendor/build-runtime/babel.mjs +4604 -6150
- package/esm/vendor/build-runtime/magic-string.mjs +1 -1
- package/esm/vite-plugin/handleClientEntrypoint.d.ts +5 -0
- package/esm/vite-plugin/handleClientEntrypoint.mjs +23 -17
- package/esm/vite-plugin/handleClientEntrypoint.mjs.map +1 -1
- package/esm/vite-plugin/handleTanstackStartClientOutput.d.ts +16 -0
- package/esm/vite-plugin/handleTanstackStartClientOutput.mjs +277 -0
- package/esm/vite-plugin/handleTanstackStartClientOutput.mjs.map +1 -0
- package/esm/vite-plugin/vite-plugin.mjs +51 -3
- package/esm/vite-plugin/vite-plugin.mjs.map +1 -1
- package/package.json +2 -2
- package/src/tanstack-start/react/createOidcSpaUtils.ts +1 -1
- package/src/vite-plugin/handleClientEntrypoint.ts +41 -27
- package/src/vite-plugin/handleTanstackStartClientOutput.ts +415 -0
- package/src/vite-plugin/vite-plugin.ts +66 -4
- package/vendor/frontend/webcrypto-liner-shim.js +1 -1
- package/vendor/server/evt.js +1 -1
- package/vendor/server/jose.js +1 -1
- package/vendor/server/tsafe.js +1 -1
- package/vendor/server/zod.js +1 -1
|
@@ -43,30 +43,10 @@ export function createHandleClientEntrypoint(params: {
|
|
|
43
43
|
projectType
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const { id, pluginContext } = params;
|
|
51
|
-
const { path: rawPath, queryParams } = splitId(id);
|
|
52
|
-
|
|
53
|
-
const normalizedRequestPath = normalizeRequestPath(rawPath);
|
|
54
|
-
const isMatch =
|
|
55
|
-
normalizedRequestPath && normalizedRequestPath === entryResolution.normalizedPath;
|
|
56
|
-
|
|
57
|
-
if (!isMatch) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const isOriginalRequest = queryParams.getAll(ORIGINAL_QUERY_PARAM).includes("true");
|
|
62
|
-
|
|
63
|
-
if (isOriginalRequest) {
|
|
64
|
-
entryResolution.watchFiles.forEach(file => pluginContext.addWatchFile(file));
|
|
65
|
-
return fs.readFile(entryResolution.absolutePath, "utf8");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
entryResolution.watchFiles.forEach(file => pluginContext.addWatchFile(file));
|
|
69
|
-
|
|
46
|
+
function getClientEntrypointSource(params: {
|
|
47
|
+
applicationEntrypointImportSpecifier: string;
|
|
48
|
+
}): string {
|
|
49
|
+
const { applicationEntrypointImportSpecifier } = params;
|
|
70
50
|
const { browserRuntimeFreeze, tokenSubstitution, DPoP, sessionRestorationMethod } =
|
|
71
51
|
oidcSpaVitePluginParams ?? {};
|
|
72
52
|
|
|
@@ -94,8 +74,7 @@ export function createHandleClientEntrypoint(params: {
|
|
|
94
74
|
`});`,
|
|
95
75
|
``,
|
|
96
76
|
`if (shouldLoadApp) {`,
|
|
97
|
-
|
|
98
|
-
`import("./${path.basename(entryResolution.absolutePath)}?${ORIGINAL_QUERY_PARAM}=true");`,
|
|
77
|
+
`import(${JSON.stringify(applicationEntrypointImportSpecifier)});`,
|
|
99
78
|
`}`
|
|
100
79
|
].join("\n");
|
|
101
80
|
|
|
@@ -141,7 +120,42 @@ export function createHandleClientEntrypoint(params: {
|
|
|
141
120
|
}
|
|
142
121
|
}
|
|
143
122
|
|
|
144
|
-
|
|
123
|
+
async function load_handleClientEntrypoint(params: {
|
|
124
|
+
id: string;
|
|
125
|
+
pluginContext: PluginContext;
|
|
126
|
+
doUseOriginalEntrypoint: boolean;
|
|
127
|
+
buildMarker?: string;
|
|
128
|
+
}): Promise<null | string> {
|
|
129
|
+
const { id, pluginContext, doUseOriginalEntrypoint, buildMarker } = params;
|
|
130
|
+
const { path: rawPath, queryParams } = splitId(id);
|
|
131
|
+
|
|
132
|
+
const normalizedRequestPath = normalizeRequestPath(rawPath);
|
|
133
|
+
const isMatch =
|
|
134
|
+
normalizedRequestPath && normalizedRequestPath === entryResolution.normalizedPath;
|
|
135
|
+
|
|
136
|
+
if (!isMatch) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const isOriginalRequest =
|
|
141
|
+
doUseOriginalEntrypoint || queryParams.getAll(ORIGINAL_QUERY_PARAM).includes("true");
|
|
142
|
+
|
|
143
|
+
if (isOriginalRequest) {
|
|
144
|
+
entryResolution.watchFiles.forEach(file => pluginContext.addWatchFile(file));
|
|
145
|
+
const originalSource = await fs.readFile(entryResolution.absolutePath, "utf8");
|
|
146
|
+
return buildMarker === undefined ? originalSource : `${buildMarker}\n${originalSource}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
entryResolution.watchFiles.forEach(file => pluginContext.addWatchFile(file));
|
|
150
|
+
|
|
151
|
+
return getClientEntrypointSource({
|
|
152
|
+
applicationEntrypointImportSpecifier: `./${path.basename(
|
|
153
|
+
entryResolution.absolutePath
|
|
154
|
+
)}?${ORIGINAL_QUERY_PARAM}=true`
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return { load_handleClientEntrypoint, getClientEntrypointSource };
|
|
145
159
|
}
|
|
146
160
|
|
|
147
161
|
function resolveEntryForProject({
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import type { ResolvedConfig, Rollup } from "vite";
|
|
6
|
+
|
|
7
|
+
const BOOTSTRAP_ENTRY_ID = "virtual:oidc-spa/tanstack-start-client-bootstrap";
|
|
8
|
+
const RESOLVED_BOOTSTRAP_ENTRY_ID = `\0${BOOTSTRAP_ENTRY_ID}`;
|
|
9
|
+
const APPLICATION_ENTRY_PLACEHOLDER = "oidc-spa:tanstack-start-application-entry";
|
|
10
|
+
|
|
11
|
+
type PluginContextWithEnvironment = Rollup.PluginContext & {
|
|
12
|
+
environment?: {
|
|
13
|
+
name?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type MutableOutputChunk = {
|
|
18
|
+
map: Rollup.SourceMap | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function createHandleTanstackStartClientOutput(params: {
|
|
22
|
+
resolvedConfig: ResolvedConfig;
|
|
23
|
+
getClientEntrypointSource: (params: { applicationEntrypointImportSpecifier: string }) => string;
|
|
24
|
+
}) {
|
|
25
|
+
const { resolvedConfig, getClientEntrypointSource } = params;
|
|
26
|
+
|
|
27
|
+
const bootstrapSource = getClientEntrypointSource({
|
|
28
|
+
applicationEntrypointImportSpecifier: APPLICATION_ENTRY_PLACEHOLDER
|
|
29
|
+
});
|
|
30
|
+
const applicationEntryBuildMarker = getApplicationEntryBuildMarker({ bootstrapSource });
|
|
31
|
+
|
|
32
|
+
function resolveId(id: string): null | string | { id: string; external: true } {
|
|
33
|
+
if (id === BOOTSTRAP_ENTRY_ID) {
|
|
34
|
+
return RESOLVED_BOOTSTRAP_ENTRY_ID;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (id === APPLICATION_ENTRY_PLACEHOLDER) {
|
|
38
|
+
return { id, external: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function load(id: string): null | string {
|
|
45
|
+
return id === RESOLVED_BOOTSTRAP_ENTRY_ID ? bootstrapSource : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildStart(this: Rollup.PluginContext): void {
|
|
49
|
+
if (!isClientEnvironment({ pluginContext: this, resolvedConfig })) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// This keeps the bootstrap independent from the application graph. The
|
|
54
|
+
// temporary entry is finalized and removed before TanStack captures it.
|
|
55
|
+
this.emitFile({
|
|
56
|
+
type: "chunk",
|
|
57
|
+
id: BOOTSTRAP_ENTRY_ID,
|
|
58
|
+
name: "oidc-spa-bootstrap",
|
|
59
|
+
preserveSignature: false
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function generateBundle(
|
|
64
|
+
this: Rollup.PluginContext,
|
|
65
|
+
_options: Rollup.NormalizedOutputOptions,
|
|
66
|
+
bundle: Rollup.OutputBundle
|
|
67
|
+
): Promise<void> {
|
|
68
|
+
if (!isClientEnvironment({ pluginContext: this, resolvedConfig })) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const bootstrapChunk = Object.values(bundle).find(
|
|
73
|
+
(output): output is Rollup.OutputChunk =>
|
|
74
|
+
output.type === "chunk" && output.moduleIds.includes(RESOLVED_BOOTSTRAP_ENTRY_ID)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (bootstrapChunk === undefined) {
|
|
78
|
+
this.error("oidc-spa: The TanStack Start client bootstrap chunk was not emitted.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const applicationEntryChunks = Object.values(bundle).filter(
|
|
82
|
+
(output): output is Rollup.OutputChunk =>
|
|
83
|
+
output.type === "chunk" && output.isEntry && output !== bootstrapChunk
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (applicationEntryChunks.length !== 1) {
|
|
87
|
+
this.error(
|
|
88
|
+
`oidc-spa: Expected exactly one TanStack Start client entry chunk, found ${applicationEntryChunks.length}.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const [entryChunk] = applicationEntryChunks;
|
|
93
|
+
|
|
94
|
+
assertBootstrapSynchronousGraphIsIsolated({
|
|
95
|
+
pluginContext: this,
|
|
96
|
+
bundle,
|
|
97
|
+
bootstrapChunk,
|
|
98
|
+
applicationEntryChunk: entryChunk
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const applicationEntryFileName = getApplicationEntryFileName({
|
|
102
|
+
entryFileName: entryChunk.fileName,
|
|
103
|
+
bundle
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (const output of Object.values(bundle)) {
|
|
107
|
+
if (output.type !== "chunk") {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
rewriteChunkEntryReferences({
|
|
112
|
+
chunk: output,
|
|
113
|
+
previousEntryFileName: entryChunk.fileName,
|
|
114
|
+
applicationEntryFileName
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const applicationEntryCode = entryChunk.code;
|
|
119
|
+
const applicationEntryMap = entryChunk.map;
|
|
120
|
+
const outputHash = createHash("sha256")
|
|
121
|
+
.update(applicationEntryCode)
|
|
122
|
+
.update("\0")
|
|
123
|
+
.update(bootstrapChunk.code)
|
|
124
|
+
.digest("base64url")
|
|
125
|
+
.slice(0, 12);
|
|
126
|
+
const finalizedBootstrapFileName = addFileNameSuffix({
|
|
127
|
+
fileName: bootstrapChunk.fileName,
|
|
128
|
+
suffix: outputHash
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const finalizedBootstrapCode = replaceApplicationEntryPlaceholder({
|
|
132
|
+
code: bootstrapChunk.code,
|
|
133
|
+
applicationEntryImportSpecifier: toRelativeImportSpecifier({
|
|
134
|
+
importerFileName: finalizedBootstrapFileName,
|
|
135
|
+
importedFileName: applicationEntryFileName
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
delete bundle[bootstrapChunk.fileName];
|
|
140
|
+
|
|
141
|
+
this.emitFile({
|
|
142
|
+
type: "prebuilt-chunk",
|
|
143
|
+
fileName: finalizedBootstrapFileName,
|
|
144
|
+
code: finalizedBootstrapCode,
|
|
145
|
+
exports: bootstrapChunk.exports
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
this.emitFile({
|
|
149
|
+
type: "prebuilt-chunk",
|
|
150
|
+
fileName: applicationEntryFileName,
|
|
151
|
+
code: applicationEntryCode,
|
|
152
|
+
exports: entryChunk.exports,
|
|
153
|
+
map:
|
|
154
|
+
applicationEntryMap === null
|
|
155
|
+
? undefined
|
|
156
|
+
: {
|
|
157
|
+
...applicationEntryMap,
|
|
158
|
+
file: path.posix.basename(applicationEntryFileName)
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const bootstrapImportSpecifier = toRelativeImportSpecifier({
|
|
163
|
+
importerFileName: entryChunk.fileName,
|
|
164
|
+
importedFileName: finalizedBootstrapFileName
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
entryChunk.code = `import ${JSON.stringify(bootstrapImportSpecifier)};\n`;
|
|
168
|
+
|
|
169
|
+
const mutableEntryChunk = entryChunk as unknown as MutableOutputChunk;
|
|
170
|
+
mutableEntryChunk.map = null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { resolveId, load, buildStart, generateBundle, applicationEntryBuildMarker };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getApplicationEntryBuildMarker(params: { bootstrapSource: string }): string {
|
|
177
|
+
const { bootstrapSource } = params;
|
|
178
|
+
const packageVersion = findOidcSpaPackageVersion();
|
|
179
|
+
|
|
180
|
+
const fingerprint = createHash("sha256")
|
|
181
|
+
.update(packageVersion)
|
|
182
|
+
.update("\0")
|
|
183
|
+
.update(bootstrapSource)
|
|
184
|
+
.digest("hex")
|
|
185
|
+
.slice(0, 16);
|
|
186
|
+
|
|
187
|
+
// This property read survives tree-shaking and executes only after the
|
|
188
|
+
// security bootstrap elects to load the application.
|
|
189
|
+
return `globalThis[${JSON.stringify(`__oidcSpaBuild_${fingerprint}`)}];`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function findOidcSpaPackageVersion(): string {
|
|
193
|
+
let directory = path.dirname(fileURLToPath(import.meta.url));
|
|
194
|
+
|
|
195
|
+
while (true) {
|
|
196
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
197
|
+
|
|
198
|
+
if (existsSync(packageJsonPath)) {
|
|
199
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
|
|
200
|
+
name?: unknown;
|
|
201
|
+
version?: unknown;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
if (packageJson.name === "oidc-spa" && typeof packageJson.version === "string") {
|
|
205
|
+
return packageJson.version;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const parentDirectory = path.dirname(directory);
|
|
210
|
+
|
|
211
|
+
if (parentDirectory === directory) {
|
|
212
|
+
throw new Error("oidc-spa: Could not read the package version for the build marker.");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
directory = parentDirectory;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function assertBootstrapSynchronousGraphIsIsolated(params: {
|
|
220
|
+
pluginContext: Rollup.PluginContext;
|
|
221
|
+
bundle: Rollup.OutputBundle;
|
|
222
|
+
bootstrapChunk: Rollup.OutputChunk;
|
|
223
|
+
applicationEntryChunk: Rollup.OutputChunk;
|
|
224
|
+
}): void {
|
|
225
|
+
const { pluginContext, bundle, bootstrapChunk, applicationEntryChunk } = params;
|
|
226
|
+
const allowedModuleIds = new Set<string>();
|
|
227
|
+
const pendingModuleIds = [RESOLVED_BOOTSTRAP_ENTRY_ID];
|
|
228
|
+
|
|
229
|
+
while (pendingModuleIds.length !== 0) {
|
|
230
|
+
const moduleId = pendingModuleIds.pop();
|
|
231
|
+
|
|
232
|
+
if (moduleId === undefined || allowedModuleIds.has(moduleId)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
allowedModuleIds.add(moduleId);
|
|
237
|
+
|
|
238
|
+
const moduleInfo = pluginContext.getModuleInfo(moduleId);
|
|
239
|
+
|
|
240
|
+
if (moduleInfo === null) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
pendingModuleIds.push(...moduleInfo.importedIds);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const chunksByFileName = new Map(
|
|
248
|
+
Object.values(bundle)
|
|
249
|
+
.filter((output): output is Rollup.OutputChunk => output.type === "chunk")
|
|
250
|
+
.map(chunk => [chunk.fileName, chunk])
|
|
251
|
+
);
|
|
252
|
+
const pendingChunks = [bootstrapChunk];
|
|
253
|
+
const visitedChunkFileNames = new Set<string>();
|
|
254
|
+
|
|
255
|
+
while (pendingChunks.length !== 0) {
|
|
256
|
+
const chunk = pendingChunks.pop();
|
|
257
|
+
|
|
258
|
+
if (chunk === undefined || visitedChunkFileNames.has(chunk.fileName)) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
visitedChunkFileNames.add(chunk.fileName);
|
|
263
|
+
|
|
264
|
+
if (chunk === applicationEntryChunk) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
"oidc-spa: TanStack Start placed the application entry in the synchronous bootstrap graph."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const unexpectedModuleId = chunk.moduleIds.find(
|
|
271
|
+
moduleId => !allowedModuleIds.has(moduleId) && !isViteRuntimeModule(moduleId)
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
if (unexpectedModuleId !== undefined) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`oidc-spa: TanStack Start placed an application module in the synchronous bootstrap graph: ${unexpectedModuleId}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
for (const importedFileName of chunk.imports) {
|
|
281
|
+
const importedChunk = chunksByFileName.get(importedFileName);
|
|
282
|
+
|
|
283
|
+
if (importedChunk !== undefined) {
|
|
284
|
+
pendingChunks.push(importedChunk);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function isViteRuntimeModule(moduleId: string): boolean {
|
|
291
|
+
return (
|
|
292
|
+
moduleId.startsWith("\0vite/") ||
|
|
293
|
+
moduleId.startsWith("\0rolldown/") ||
|
|
294
|
+
moduleId.startsWith("\0commonjsHelpers") ||
|
|
295
|
+
moduleId.includes("vite/preload-helper")
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function addFileNameSuffix(params: { fileName: string; suffix: string }): string {
|
|
300
|
+
const { fileName, suffix } = params;
|
|
301
|
+
const extension = path.posix.extname(fileName);
|
|
302
|
+
if (extension === "") {
|
|
303
|
+
return `${fileName}-${suffix}`;
|
|
304
|
+
}
|
|
305
|
+
return `${fileName.slice(0, -extension.length)}-${suffix}${extension}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function isClientEnvironment(params: {
|
|
309
|
+
pluginContext: Rollup.PluginContext;
|
|
310
|
+
resolvedConfig: ResolvedConfig;
|
|
311
|
+
}): boolean {
|
|
312
|
+
const { pluginContext, resolvedConfig } = params;
|
|
313
|
+
const environmentName = (pluginContext as PluginContextWithEnvironment).environment?.name;
|
|
314
|
+
|
|
315
|
+
if (environmentName !== undefined) {
|
|
316
|
+
return environmentName === "client";
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return !resolvedConfig.build.ssr;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getApplicationEntryFileName(params: {
|
|
323
|
+
entryFileName: string;
|
|
324
|
+
bundle: Rollup.OutputBundle;
|
|
325
|
+
}): string {
|
|
326
|
+
const { entryFileName, bundle } = params;
|
|
327
|
+
const directory = path.posix.dirname(entryFileName);
|
|
328
|
+
const basename = path.posix.basename(entryFileName);
|
|
329
|
+
|
|
330
|
+
for (let index = 0; index < basename.length; index++) {
|
|
331
|
+
if (basename[index] !== "-") {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const candidateBasename = `${basename.slice(0, index)}_${basename.slice(index + 1)}`;
|
|
336
|
+
const candidate = path.posix.join(directory === "." ? "" : directory, candidateBasename);
|
|
337
|
+
|
|
338
|
+
if (bundle[candidate] === undefined) {
|
|
339
|
+
return candidate;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
for (let index = 0; index < basename.length; index++) {
|
|
344
|
+
if (basename[index] === "_") {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const candidateBasename = `${basename.slice(0, index)}_${basename.slice(index + 1)}`;
|
|
349
|
+
const candidate = path.posix.join(directory === "." ? "" : directory, candidateBasename);
|
|
350
|
+
|
|
351
|
+
if (bundle[candidate] === undefined) {
|
|
352
|
+
return candidate;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
throw new Error(`oidc-spa: Could not allocate an application entry beside "${entryFileName}".`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function rewriteChunkEntryReferences(params: {
|
|
360
|
+
chunk: Rollup.OutputChunk;
|
|
361
|
+
previousEntryFileName: string;
|
|
362
|
+
applicationEntryFileName: string;
|
|
363
|
+
}): void {
|
|
364
|
+
const { chunk, previousEntryFileName, applicationEntryFileName } = params;
|
|
365
|
+
const previousRelativeSpecifier = toRelativeImportSpecifier({
|
|
366
|
+
importerFileName: chunk.fileName,
|
|
367
|
+
importedFileName: previousEntryFileName
|
|
368
|
+
});
|
|
369
|
+
const applicationRelativeSpecifier = toRelativeImportSpecifier({
|
|
370
|
+
importerFileName: chunk.fileName,
|
|
371
|
+
importedFileName: applicationEntryFileName
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const replacements = new Map([
|
|
375
|
+
[previousEntryFileName, applicationEntryFileName],
|
|
376
|
+
[previousRelativeSpecifier, applicationRelativeSpecifier]
|
|
377
|
+
]);
|
|
378
|
+
|
|
379
|
+
for (const [from, to] of replacements) {
|
|
380
|
+
if (from === to) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (from.length !== to.length) {
|
|
385
|
+
throw new Error("oidc-spa: Internal entry rewrite changed generated code offsets.");
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
chunk.code = chunk.code.split(from).join(to);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function replaceApplicationEntryPlaceholder(params: {
|
|
393
|
+
code: string;
|
|
394
|
+
applicationEntryImportSpecifier: string;
|
|
395
|
+
}): string {
|
|
396
|
+
const { code, applicationEntryImportSpecifier } = params;
|
|
397
|
+
const occurrences = code.split(APPLICATION_ENTRY_PLACEHOLDER).length - 1;
|
|
398
|
+
|
|
399
|
+
if (occurrences !== 1) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`oidc-spa: Expected one application entry placeholder in the generated bootstrap, found ${occurrences}.`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return code.replace(APPLICATION_ENTRY_PLACEHOLDER, applicationEntryImportSpecifier);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function toRelativeImportSpecifier(params: {
|
|
409
|
+
importerFileName: string;
|
|
410
|
+
importedFileName: string;
|
|
411
|
+
}): string {
|
|
412
|
+
const { importerFileName, importedFileName } = params;
|
|
413
|
+
const relativePath = path.posix.relative(path.posix.dirname(importerFileName), importedFileName);
|
|
414
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
415
|
+
}
|
|
@@ -6,6 +6,7 @@ import { createHandleServerEntrypoint } from "./handleServerEntrypoint";
|
|
|
6
6
|
import { manageOptimizedDeps } from "./manageOptimizedDeps";
|
|
7
7
|
import { transformCreateFileRoute } from "./transformTanstackRouterCreateFileRoute";
|
|
8
8
|
import { getProjectType, type ProjectType } from "./projectType";
|
|
9
|
+
import { createHandleTanstackStartClientOutput } from "./handleTanstackStartClientOutput";
|
|
9
10
|
|
|
10
11
|
export type OidcSpaVitePluginParams = {
|
|
11
12
|
/** See: https://docs.oidc-spa.dev/v/v10/security-features/browser-runtime-freeze */
|
|
@@ -67,11 +68,25 @@ export function oidcSpa(params: OidcSpaVitePluginParams = {}) {
|
|
|
67
68
|
let load_handleClientEntrypoint:
|
|
68
69
|
| ReturnType<typeof createHandleClientEntrypoint>["load_handleClientEntrypoint"]
|
|
69
70
|
| undefined = undefined;
|
|
71
|
+
let resolveId_handleTanstackStartClientOutput:
|
|
72
|
+
| ReturnType<typeof createHandleTanstackStartClientOutput>["resolveId"]
|
|
73
|
+
| undefined = undefined;
|
|
74
|
+
let load_handleTanstackStartClientOutput:
|
|
75
|
+
| ReturnType<typeof createHandleTanstackStartClientOutput>["load"]
|
|
76
|
+
| undefined = undefined;
|
|
77
|
+
let buildStart_handleTanstackStartClientOutput:
|
|
78
|
+
| ReturnType<typeof createHandleTanstackStartClientOutput>["buildStart"]
|
|
79
|
+
| undefined = undefined;
|
|
80
|
+
let generateBundle_handleTanstackStartClientOutput:
|
|
81
|
+
| ReturnType<typeof createHandleTanstackStartClientOutput>["generateBundle"]
|
|
82
|
+
| undefined = undefined;
|
|
83
|
+
let tanstackStartApplicationEntryBuildMarker: string | undefined = undefined;
|
|
70
84
|
let load_handleServerEntrypoint:
|
|
71
85
|
| ReturnType<typeof createHandleServerEntrypoint>["load_handleServerEntrypoint"]
|
|
72
86
|
| undefined = undefined;
|
|
73
87
|
|
|
74
88
|
let projectType: ProjectType | undefined = undefined;
|
|
89
|
+
let isBuild = false;
|
|
75
90
|
|
|
76
91
|
const plugin: Plugin = {
|
|
77
92
|
name: "oidc-spa",
|
|
@@ -90,15 +105,33 @@ export function oidcSpa(params: OidcSpaVitePluginParams = {}) {
|
|
|
90
105
|
return userConfig;
|
|
91
106
|
},
|
|
92
107
|
configResolved(resolvedConfig) {
|
|
108
|
+
isBuild = resolvedConfig.command === "build";
|
|
93
109
|
projectType = getProjectType({
|
|
94
110
|
pluginNames: resolvedConfig.plugins.map(({ name }) => name)
|
|
95
111
|
});
|
|
96
112
|
|
|
97
|
-
|
|
113
|
+
const clientEntrypointHandler = createHandleClientEntrypoint({
|
|
98
114
|
oidcSpaVitePluginParams: params,
|
|
99
115
|
resolvedConfig,
|
|
100
116
|
projectType
|
|
101
|
-
})
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
load_handleClientEntrypoint = clientEntrypointHandler.load_handleClientEntrypoint;
|
|
120
|
+
|
|
121
|
+
if (projectType === "tanstack-start" && isBuild) {
|
|
122
|
+
const tanstackStartClientOutputHandler = createHandleTanstackStartClientOutput({
|
|
123
|
+
resolvedConfig,
|
|
124
|
+
getClientEntrypointSource: clientEntrypointHandler.getClientEntrypointSource
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
resolveId_handleTanstackStartClientOutput = tanstackStartClientOutputHandler.resolveId;
|
|
128
|
+
load_handleTanstackStartClientOutput = tanstackStartClientOutputHandler.load;
|
|
129
|
+
buildStart_handleTanstackStartClientOutput = tanstackStartClientOutputHandler.buildStart;
|
|
130
|
+
generateBundle_handleTanstackStartClientOutput =
|
|
131
|
+
tanstackStartClientOutputHandler.generateBundle;
|
|
132
|
+
tanstackStartApplicationEntryBuildMarker =
|
|
133
|
+
tanstackStartClientOutputHandler.applicationEntryBuildMarker;
|
|
134
|
+
}
|
|
102
135
|
|
|
103
136
|
load_handleServerEntrypoint = createHandleServerEntrypoint({
|
|
104
137
|
resolvedConfig,
|
|
@@ -123,13 +156,37 @@ export function oidcSpa(params: OidcSpaVitePluginParams = {}) {
|
|
|
123
156
|
|
|
124
157
|
return transformed;
|
|
125
158
|
},
|
|
159
|
+
resolveId(id) {
|
|
160
|
+
return resolveId_handleTanstackStartClientOutput?.(id) ?? null;
|
|
161
|
+
},
|
|
162
|
+
buildStart() {
|
|
163
|
+
buildStart_handleTanstackStartClientOutput?.call(this);
|
|
164
|
+
},
|
|
126
165
|
async load(id) {
|
|
166
|
+
{
|
|
167
|
+
const r = load_handleTanstackStartClientOutput?.(id) ?? null;
|
|
168
|
+
|
|
169
|
+
if (r !== null) {
|
|
170
|
+
return r;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
127
174
|
{
|
|
128
175
|
assert(load_handleClientEntrypoint !== undefined);
|
|
129
176
|
|
|
177
|
+
const doUseOriginalEntrypoint =
|
|
178
|
+
projectType === "tanstack-start" &&
|
|
179
|
+
isBuild &&
|
|
180
|
+
((this as typeof this & { environment?: { name?: string } }).environment?.name ??
|
|
181
|
+
"client") === "client";
|
|
182
|
+
|
|
130
183
|
const r = await load_handleClientEntrypoint({
|
|
131
184
|
id,
|
|
132
|
-
pluginContext: this
|
|
185
|
+
pluginContext: this,
|
|
186
|
+
doUseOriginalEntrypoint,
|
|
187
|
+
buildMarker: doUseOriginalEntrypoint
|
|
188
|
+
? tanstackStartApplicationEntryBuildMarker
|
|
189
|
+
: undefined
|
|
133
190
|
});
|
|
134
191
|
|
|
135
192
|
if (r !== null) {
|
|
@@ -149,8 +206,13 @@ export function oidcSpa(params: OidcSpaVitePluginParams = {}) {
|
|
|
149
206
|
return r;
|
|
150
207
|
}
|
|
151
208
|
}
|
|
152
|
-
|
|
153
209
|
return null;
|
|
210
|
+
},
|
|
211
|
+
generateBundle: {
|
|
212
|
+
order: "pre",
|
|
213
|
+
async handler(options, bundle) {
|
|
214
|
+
await generateBundle_handleTanstackStartClientOutput?.call(this, options, bundle);
|
|
215
|
+
}
|
|
154
216
|
}
|
|
155
217
|
};
|
|
156
218
|
|