mirror of
https://github.com/withastro/astro.git
synced 2025-03-17 23:11:29 -05:00
fix: [sessions] import storage driver in manifest (#12654)
* wip * wip * Export manifest in middleware * Changeset conf * Pass session to edge middleware * Support initial session data * Persist edge session on redirect * Remove middleware-related changes * Refactor * Remove vite plugin * Format * Simplify import * Handle missing config * Handle async resolution
This commit is contained in:
parent
3549203866
commit
ef8798f6cb
6 changed files with 47 additions and 68 deletions
|
@ -1,7 +1,7 @@
|
|||
import type { RoutingStrategies } from '../../i18n/utils.js';
|
||||
import type { ComponentInstance, SerializedRouteData } from '../../types/astro.js';
|
||||
import type { AstroMiddlewareInstance } from '../../types/public/common.js';
|
||||
import type { Locales, SessionConfig } from '../../types/public/config.js';
|
||||
import type { Locales, ResolvedSessionConfig, SessionConfig } from '../../types/public/config.js';
|
||||
import type {
|
||||
RouteData,
|
||||
SSRComponentMetadata,
|
||||
|
@ -70,7 +70,7 @@ export type SSRManifest = {
|
|||
middleware?: () => Promise<AstroMiddlewareInstance> | AstroMiddlewareInstance;
|
||||
checkOrigin: boolean;
|
||||
envGetSecretEnabled: boolean;
|
||||
sessionConfig?: SessionConfig<any>;
|
||||
sessionConfig?: ResolvedSessionConfig<any>
|
||||
};
|
||||
|
||||
export type SSRManifestI18n = {
|
||||
|
|
|
@ -22,6 +22,7 @@ import { type BuildInternals, cssOrder, mergeInlineCss } from '../internal.js';
|
|||
import type { AstroBuildPlugin } from '../plugin.js';
|
||||
import type { StaticBuildOptions } from '../types.js';
|
||||
import { makePageDataKey } from './util.js';
|
||||
import { resolveSessionDriver } from '../../session.js';
|
||||
|
||||
const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
|
||||
const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, 'g');
|
||||
|
@ -29,7 +30,7 @@ const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, 'g');
|
|||
export const SSR_MANIFEST_VIRTUAL_MODULE_ID = '@astrojs-manifest';
|
||||
export const RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID = '\0' + SSR_MANIFEST_VIRTUAL_MODULE_ID;
|
||||
|
||||
function vitePluginManifest(_options: StaticBuildOptions, internals: BuildInternals): VitePlugin {
|
||||
function vitePluginManifest(options: StaticBuildOptions, internals: BuildInternals): VitePlugin {
|
||||
return {
|
||||
name: '@astro/plugin-build-manifest',
|
||||
enforce: 'post',
|
||||
|
@ -52,11 +53,16 @@ function vitePluginManifest(_options: StaticBuildOptions, internals: BuildIntern
|
|||
`import { deserializeManifest as _deserializeManifest } from 'astro/app'`,
|
||||
`import { _privateSetManifestDontUseThis } from 'astro:ssr-manifest'`,
|
||||
];
|
||||
|
||||
const resolvedDriver = await resolveSessionDriver(options.settings.config.experimental?.session?.driver);
|
||||
|
||||
const contents = [
|
||||
`const manifest = _deserializeManifest('${manifestReplace}');`,
|
||||
`if (manifest.sessionConfig) manifest.sessionConfig.driverModule = ${resolvedDriver ? `() => import(${JSON.stringify(resolvedDriver)})` : 'null'};`,
|
||||
`_privateSetManifestDontUseThis(manifest);`,
|
||||
];
|
||||
const exports = [`export { manifest }`];
|
||||
|
||||
return [...imports, ...contents, ...exports].join('\n');
|
||||
}
|
||||
},
|
||||
|
|
|
@ -33,7 +33,6 @@ import markdownVitePlugin from '../vite-plugin-markdown/index.js';
|
|||
import astroScannerPlugin from '../vite-plugin-scanner/index.js';
|
||||
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
|
||||
import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js';
|
||||
import vitePluginSessions from '../vite-plugin-sessions/index.js';
|
||||
import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js';
|
||||
import type { SSRManifest } from './app/types.js';
|
||||
import type { Logger } from './logger/core.js';
|
||||
|
@ -144,7 +143,6 @@ export async function createVite(
|
|||
astroLoadFallbackPlugin({ fs, root: settings.config.root }),
|
||||
astroVitePlugin({ settings, logger }),
|
||||
astroScriptsPlugin({ settings }),
|
||||
vitePluginSessions({ settings }),
|
||||
// The server plugin is for dev only and having it run during the build causes
|
||||
// the build to run very slow as the filewatcher is triggered often.
|
||||
command === 'dev' && vitePluginAstroServer({ settings, logger, fs, manifest, ssrManifest }), // ssrManifest is only required in dev mode, where it gets created before a Vite instance is created, and get passed to this function
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
import { stringify, unflatten } from 'devalue';
|
||||
import { type BuiltinDriverOptions, type Driver, type Storage, builtinDrivers, createStorage } from 'unstorage';
|
||||
import type { SessionConfig, SessionDriverName } from '../types/public/config.js';
|
||||
import {
|
||||
type BuiltinDriverOptions,
|
||||
type Driver,
|
||||
type Storage,
|
||||
builtinDrivers,
|
||||
createStorage,
|
||||
} from 'unstorage';
|
||||
import type {
|
||||
ResolvedSessionConfig,
|
||||
SessionConfig,
|
||||
SessionDriverName,
|
||||
} from '../types/public/config.js';
|
||||
import type { AstroCookies } from './cookies/cookies.js';
|
||||
import type { AstroCookieSetOptions } from './cookies/cookies.js';
|
||||
import { SessionStorageInitError, SessionStorageSaveError } from './errors/errors-data.js';
|
||||
|
@ -15,7 +25,7 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
|
|||
// The cookies object.
|
||||
#cookies: AstroCookies;
|
||||
// The session configuration.
|
||||
#config: Omit<SessionConfig<TDriver>, 'cookie'>;
|
||||
#config: Omit<ResolvedSessionConfig<TDriver>, 'cookie'>;
|
||||
// The cookie config
|
||||
#cookieConfig?: AstroCookieSetOptions;
|
||||
// The cookie name
|
||||
|
@ -41,7 +51,7 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
|
|||
{
|
||||
cookie: cookieConfig = DEFAULT_COOKIE_NAME,
|
||||
...config
|
||||
}: Exclude<SessionConfig<TDriver>, undefined>,
|
||||
}: Exclude<ResolvedSessionConfig<TDriver>, undefined>,
|
||||
) {
|
||||
this.#cookies = cookies;
|
||||
if (typeof cookieConfig === 'object') {
|
||||
|
@ -354,7 +364,7 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
|
|||
return this.#storage;
|
||||
}
|
||||
// Use fsLite rather than fs, because fs can't be bundled. Add a default base path if not provided.
|
||||
if(this.#config.driver === 'fs' || this.#config.driver === 'fsLite') {
|
||||
if (this.#config.driver === 'fs' || this.#config.driver === 'fsLite') {
|
||||
this.#config.options ??= {};
|
||||
this.#config.driver = 'fsLite';
|
||||
(this.#config.options as BuiltinDriverOptions['fsLite']).base ??= '.astro/session';
|
||||
|
@ -370,21 +380,13 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
|
|||
}
|
||||
|
||||
let driver: ((config: SessionConfig<TDriver>['options']) => Driver) | null = null;
|
||||
const isBuiltin = this.#config.driver in builtinDrivers;
|
||||
// Try to load the driver from the built-in unstorage drivers.
|
||||
// Otherwise, assume it's a custom driver and load by name.
|
||||
const driverPackage: string = isBuiltin
|
||||
? builtinDrivers[this.#config.driver as keyof typeof builtinDrivers]
|
||||
: this.#config.driver;
|
||||
|
||||
const driverPackage = await resolveSessionDriver(this.#config.driver);
|
||||
try {
|
||||
// If driver is not a builtin, or in development or test, load the driver directly.
|
||||
if (!isBuiltin || process.env.NODE_ENV === 'development' || process.env.NODE_TEST_CONTEXT) {
|
||||
driver = await import(/* @vite-ignore */ driverPackage).then((r) => r.default || r);
|
||||
} else {
|
||||
// In production, load via the virtual module as it will be bundled by Vite
|
||||
// @ts-expect-error - virtual module
|
||||
driver = await import('@astro-session-driver').then((r) => r.default || r);
|
||||
if (this.#config.driverModule) {
|
||||
driver = (await this.#config.driverModule()).default;
|
||||
} else if (driverPackage) {
|
||||
driver = (await import(driverPackage)).default;
|
||||
}
|
||||
} catch (err: any) {
|
||||
// If the driver failed to load, throw an error.
|
||||
|
@ -431,3 +433,16 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// TODO: make this sync when we drop support for Node < 18.19.0
|
||||
export function resolveSessionDriver(driver: string | undefined): Promise<string> | string | null {
|
||||
if (!driver) {
|
||||
return null;
|
||||
}
|
||||
if (driver === 'fs') {
|
||||
return import.meta.resolve(builtinDrivers.fsLite);
|
||||
}
|
||||
if (driver in builtinDrivers) {
|
||||
return import.meta.resolve(builtinDrivers[driver as keyof typeof builtinDrivers]);
|
||||
}
|
||||
return driver;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import type {
|
|||
RemarkRehype,
|
||||
ShikiConfig,
|
||||
} from '@astrojs/markdown-remark';
|
||||
import type { BuiltinDriverName, BuiltinDriverOptions, Storage } from 'unstorage';
|
||||
import type { BuiltinDriverName, BuiltinDriverOptions, Driver, Storage } from 'unstorage';
|
||||
import type { UserConfig as OriginalViteUserConfig, SSROptions as ViteSSROptions } from 'vite';
|
||||
import type { ImageFit, ImageLayout } from '../../assets/types.js';
|
||||
import type { RemotePattern } from '../../assets/utils/remotePattern.js';
|
||||
|
@ -135,6 +135,10 @@ export type SessionConfig<TDriver extends SessionDriverName> =
|
|||
? TestSessionConfig
|
||||
: CustomSessionConfig;
|
||||
|
||||
export type ResolvedSessionConfig<TDriver extends SessionDriverName> = SessionConfig<TDriver> & {
|
||||
driverModule?: () => Promise<{ default: () => Driver }>;
|
||||
};
|
||||
|
||||
export interface ViteUserConfig extends OriginalViteUserConfig {
|
||||
ssr?: ViteSSROptions;
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { builtinDrivers } from 'unstorage';
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import type { AstroSettings } from '../types/astro.js';
|
||||
|
||||
const SESSION_DRIVER_ID = '@astro-session-driver';
|
||||
const RESOLVED_SESSION_DRIVER_ID = `\0${SESSION_DRIVER_ID}`;
|
||||
|
||||
export default function vitePluginSessions({ settings }: { settings: AstroSettings }): VitePlugin {
|
||||
return {
|
||||
name: 'astro:vite-plugin-sessions',
|
||||
enforce: 'pre',
|
||||
async resolveId(source) {
|
||||
if (source === SESSION_DRIVER_ID) {
|
||||
return RESOLVED_SESSION_DRIVER_ID;
|
||||
}
|
||||
// Resolve the driver entrypoint so that we bundle it
|
||||
if (source.startsWith('unstorage/drivers/')) {
|
||||
return fileURLToPath(import.meta.resolve(source));
|
||||
}
|
||||
},
|
||||
async load(id) {
|
||||
if (id === RESOLVED_SESSION_DRIVER_ID) {
|
||||
let driver = settings.config.experimental?.session?.driver;
|
||||
if (driver && driver in builtinDrivers) {
|
||||
if (driver === 'fs') {
|
||||
// fs tries to bundle chokidar (and therefore fsevents), which is a binary
|
||||
// fsLite is identical except it doesn't include a filesystem watcher (which we don't need)
|
||||
driver = 'fsLite';
|
||||
}
|
||||
driver = builtinDrivers[driver as keyof typeof builtinDrivers];
|
||||
}
|
||||
if (!driver) {
|
||||
return `export default function driver() { return null }`;
|
||||
}
|
||||
const resolved = await this.resolve(driver, undefined, { skipSelf: false });
|
||||
if (!resolved) {
|
||||
throw new Error(`Could not resolve session driver ${driver}`);
|
||||
}
|
||||
return `export { default } from ${JSON.stringify(resolved?.id)};`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Add table
Reference in a new issue