@tanstack/start-plugin-core 1.164.1 → 1.165.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.
@@ -48,6 +48,7 @@ function TanStackStartVitePluginCore(corePluginOpts, startPluginOpts) {
48
48
  }
49
49
  return { startConfig, resolvedStartConfig, corePluginOpts };
50
50
  };
51
+ let needsDevBaseRewrite = false;
51
52
  const capturedBundle = {};
52
53
  function getBundle(envName) {
53
54
  const bundle = capturedBundle[envName];
@@ -93,9 +94,7 @@ function TanStackStartVitePluginCore(corePluginOpts, startPluginOpts) {
93
94
  if (!joinPaths(["/", startConfig2.router.basepath, "/"]).startsWith(
94
95
  joinPaths(["/", resolvedStartConfig.viteAppBase, "/"])
95
96
  )) {
96
- this.error(
97
- "[tanstack-start]: During `vite dev`, `router.basepath` must start with the vite `base` config value"
98
- );
97
+ needsDevBaseRewrite = true;
99
98
  }
100
99
  }
101
100
  }
@@ -315,6 +314,27 @@ function TanStackStartVitePluginCore(corePluginOpts, startPluginOpts) {
315
314
  getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),
316
315
  getConfig
317
316
  }),
317
+ // When the vite base and router basepath are misaligned (e.g. base: '/_ui/', basepath: '/'),
318
+ // install a middleware that rewrites incoming request URLs to prepend the vite base prefix.
319
+ // This allows Vite's internal base middleware to accept the requests, then strips the prefix
320
+ // before passing to the SSR handler.
321
+ // Registered BEFORE devServerPlugin so this middleware is added to the Connect stack first,
322
+ // ensuring all subsequent middlewares (CSS, SSR, etc.) see the rewritten URL.
323
+ {
324
+ name: "tanstack-start-core:dev-base-rewrite",
325
+ configureServer(server) {
326
+ if (!needsDevBaseRewrite) {
327
+ return;
328
+ }
329
+ const basePrefix = resolvedStartConfig.viteAppBase.replace(/\/$/, "");
330
+ server.middlewares.use((req, _res, next) => {
331
+ if (req.url && !req.url.startsWith(basePrefix)) {
332
+ req.url = basePrefix + req.url;
333
+ }
334
+ next();
335
+ });
336
+ }
337
+ },
318
338
  devServerPlugin({ getConfig }),
319
339
  previewServerPlugin(),
320
340
  {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { joinPaths } from '@tanstack/router-core'\nimport * as vite from 'vite'\nimport { crawlFrameworkPkgs } from 'vitefu'\nimport { join } from 'pathe'\nimport { escapePath } from 'tinyglobby'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { previewServerPlugin } from './preview-server-plugin/plugin'\nimport { parseStartConfig } from './schema'\nimport { resolveEntry } from './resolve-entries'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { importProtectionPlugin } from './import-protection-plugin/plugin'\nimport type {\n GetConfigFn,\n ResolvedStartConfig,\n TanStackStartVitePluginCoreOptions,\n} from './types'\nimport type { ViteEnvironmentNames } from './constants'\nimport type {\n TanStackStartInputConfig,\n TanStackStartOutputConfig,\n} from './schema'\nimport type { PluginOption } from 'vite'\n\nfunction isFullUrl(str: string): boolean {\n try {\n new URL(str)\n return true\n } catch {\n return false\n }\n}\n\nexport function TanStackStartVitePluginCore(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartInputConfig,\n): Array<PluginOption> {\n // Determine the provider environment for server functions\n // If providerEnv is set, use that; otherwise default to SSR as the provider\n const serverFnProviderEnv =\n corePluginOpts.serverFn?.providerEnv || VITE_ENVIRONMENT_NAMES.server\n const ssrIsProvider = serverFnProviderEnv === VITE_ENVIRONMENT_NAMES.server\n\n const resolvedStartConfig: ResolvedStartConfig = {\n root: '',\n startFilePath: undefined,\n routerFilePath: '',\n srcDirectory: '',\n viteAppBase: '',\n serverFnProviderEnv,\n }\n\n let startConfig: TanStackStartOutputConfig | null\n const getConfig: GetConfigFn = () => {\n if (!resolvedStartConfig.root) {\n throw new Error(`Cannot get config before root is resolved`)\n }\n if (!startConfig) {\n startConfig = parseStartConfig(\n startPluginOpts,\n corePluginOpts,\n resolvedStartConfig.root,\n )\n }\n return { startConfig, resolvedStartConfig, corePluginOpts }\n }\n\n const capturedBundle: Partial<\n Record<ViteEnvironmentNames, vite.Rollup.OutputBundle>\n > = {}\n\n function getBundle(envName: ViteEnvironmentNames): vite.Rollup.OutputBundle {\n const bundle = capturedBundle[envName]\n if (!bundle) {\n throw new Error(`No bundle captured for environment: ${envName}`)\n }\n return bundle\n }\n\n const environments: Array<{ name: string; type: 'client' | 'server' }> = [\n { name: VITE_ENVIRONMENT_NAMES.client, type: 'client' },\n { name: VITE_ENVIRONMENT_NAMES.server, type: 'server' },\n ]\n if (\n corePluginOpts.serverFn?.providerEnv &&\n !environments.find((e) => e.name === corePluginOpts.serverFn?.providerEnv)\n ) {\n environments.push({\n name: corePluginOpts.serverFn.providerEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n resolvedStartConfig.viteAppBase = joinPaths([\n '/',\n viteConfig.base,\n '/',\n ])\n }\n const root = viteConfig.root || process.cwd()\n resolvedStartConfig.root = root\n\n const { startConfig } = getConfig()\n if (startConfig.router.basepath === undefined) {\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n startConfig.router.basepath =\n resolvedStartConfig.viteAppBase.replace(/^\\/|\\/$/g, '')\n } else {\n startConfig.router.basepath = '/'\n }\n } else {\n if (command === 'serve' && !viteConfig.server?.middlewareMode) {\n // when serving, we must ensure that router basepath and viteAppBase are aligned\n if (\n !joinPaths(['/', startConfig.router.basepath, '/']).startsWith(\n joinPaths(['/', resolvedStartConfig.viteAppBase, '/']),\n )\n ) {\n this.error(\n '[tanstack-start]: During `vite dev`, `router.basepath` must start with the vite `base` config value',\n )\n }\n }\n }\n\n const TSS_SERVER_FN_BASE = joinPaths([\n '/',\n startConfig.router.basepath,\n startConfig.serverFns.base,\n '/',\n ])\n const resolvedSrcDirectory = join(root, startConfig.srcDirectory)\n resolvedStartConfig.srcDirectory = resolvedSrcDirectory\n\n const startFilePath = resolveEntry({\n type: 'start entry',\n configuredEntry: startConfig.start.entry,\n defaultEntry: 'start',\n resolvedSrcDirectory,\n required: false,\n })\n resolvedStartConfig.startFilePath = startFilePath\n\n const routerFilePath = resolveEntry({\n type: 'router entry',\n configuredEntry: startConfig.router.entry,\n defaultEntry: 'router',\n resolvedSrcDirectory,\n required: true,\n })\n resolvedStartConfig.routerFilePath = routerFilePath\n\n const clientEntryPath = resolveEntry({\n type: 'client entry',\n configuredEntry: startConfig.client.entry,\n defaultEntry: 'client',\n resolvedSrcDirectory,\n required: false,\n })\n\n const serverEntryPath = resolveEntry({\n type: 'server entry',\n configuredEntry: startConfig.server.entry,\n defaultEntry: 'server',\n resolvedSrcDirectory,\n required: false,\n })\n\n const clientAlias = vite.normalizePath(\n clientEntryPath ?? corePluginOpts.defaultEntryPaths.client,\n )\n const serverAlias = vite.normalizePath(\n serverEntryPath ?? corePluginOpts.defaultEntryPaths.server,\n )\n const startAlias = vite.normalizePath(\n startFilePath ?? corePluginOpts.defaultEntryPaths.start,\n )\n const routerAlias = vite.normalizePath(routerFilePath)\n\n const entryAliasConfiguration: Record<\n (typeof ENTRY_POINTS)[keyof typeof ENTRY_POINTS],\n string\n > = {\n [ENTRY_POINTS.client]: clientAlias,\n [ENTRY_POINTS.server]: serverAlias,\n [ENTRY_POINTS.start]: startAlias,\n [ENTRY_POINTS.router]: routerAlias,\n }\n\n const startPackageName =\n `@tanstack/${corePluginOpts.framework}-start` as const\n\n // crawl packages that have start in \"peerDependencies\"\n // see https://github.com/svitejs/vitefu/blob/d8d82fa121e3b2215ba437107093c77bde51b63b/src/index.js#L95-L101\n\n // this is currently uncached; could be implemented similarly as vite handles lock file changes\n // see https://github.com/vitejs/vite/blob/557f797d29422027e8c451ca50dd84bf8c41b5f0/packages/vite/src/node/optimizer/index.ts#L1282\n\n const crawlFrameworkPkgsResult = await crawlFrameworkPkgs({\n root: process.cwd(),\n isBuild: command === 'build',\n isFrameworkPkgByJson(pkgJson) {\n const peerDependencies = pkgJson['peerDependencies']\n\n if (peerDependencies) {\n if (\n startPackageName in peerDependencies ||\n '@tanstack/start-client-core' in peerDependencies\n ) {\n return true\n }\n }\n\n return false\n },\n })\n\n return {\n // see https://vite.dev/config/shared-options.html#apptype\n // this will prevent vite from injecting middlewares that we don't want\n appType: viteConfig.appType ?? 'custom',\n environments: {\n [VITE_ENVIRONMENT_NAMES.client]: {\n consumer: 'client',\n build: {\n rollupOptions: {\n input: {\n main: ENTRY_POINTS.client,\n },\n },\n outDir: getClientOutputDirectory(viteConfig),\n },\n optimizeDeps: {\n exclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n // Ensure user code can be crawled for dependencies\n entries: [clientAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n [VITE_ENVIRONMENT_NAMES.server]: {\n consumer: 'server',\n build: {\n ssr: true,\n rollupOptions: {\n input:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.rollupOptions?.input ?? serverAlias,\n },\n outDir: getServerOutputDirectory(viteConfig),\n commonjsOptions: {\n include: [/node_modules/],\n },\n copyPublicDir:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.copyPublicDir ?? false,\n },\n optimizeDeps: {\n // Ensure user code can be crawled for dependencies\n entries: [serverAlias, startAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n },\n\n resolve: {\n noExternal: [\n // ENTRY_POINTS.start,\n '@tanstack/start**',\n `@tanstack/${corePluginOpts.framework}-start**`,\n ...crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n ],\n alias: {\n ...entryAliasConfiguration,\n },\n },\n /* prettier-ignore */\n define: {\n // define is an esbuild function that replaces the any instances of given keys with the given values\n // i.e: __FRAMEWORK_NAME__ can be replaced with JSON.stringify(\"TanStack Start\")\n // This is not the same as injecting environment variables.\n\n ...defineReplaceEnv('TSS_SERVER_FN_BASE', TSS_SERVER_FN_BASE),\n ...defineReplaceEnv('TSS_CLIENT_OUTPUT_DIR', getClientOutputDirectory(viteConfig)),\n ...defineReplaceEnv('TSS_ROUTER_BASEPATH', startConfig.router.basepath),\n ...(command === 'serve' ? defineReplaceEnv('TSS_SHELL', startConfig.spa?.enabled ? 'true' : 'false') : {}),\n ...defineReplaceEnv('TSS_DEV_SERVER', command === 'serve' ? 'true' : 'false'),\n // Replace NODE_ENV during build (unless opted out) for dead code elimination in server bundles\n ...(command === 'build' && startConfig.server.build.staticNodeEnv ? {\n 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || viteConfig.mode || 'production'),\n } : {}),\n },\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n const client = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n const server = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!client) {\n throw new Error('Client environment not found')\n }\n\n if (!server) {\n throw new Error('SSR environment not found')\n }\n\n if (!client.isBuilt) {\n // Build the client bundle first\n await builder.build(client)\n }\n if (!server.isBuilt) {\n // Build the SSR bundle\n await builder.build(server)\n }\n\n // If a custom provider environment is configured (not SSR),\n // build it last so the manifest includes functions from all environments\n if (!ssrIsProvider) {\n const providerEnv = builder.environments[serverFnProviderEnv]\n if (!providerEnv) {\n throw new Error(\n `Provider environment \"${serverFnProviderEnv}\" not found`,\n )\n }\n if (!providerEnv.isBuilt) {\n // Build the provider environment last\n // This ensures all server functions are discovered from client/ssr builds\n await builder.build(providerEnv)\n }\n }\n },\n },\n }\n },\n },\n // Separate plugin for buildApp hook with enforce: 'post'\n // This ensures proper ordering with other plugins that also have\n // buildApp hooks with order: 'post'. The enforce: 'post' ensures this\n // runs after other plugins (like Nitro) complete their builds.\n {\n name: 'tanstack-start-core:post-build',\n enforce: 'post',\n buildApp: {\n order: 'post',\n async handler(builder) {\n const { startConfig } = getConfig()\n await postServerBuild({ builder, startConfig })\n },\n },\n },\n // Server function plugin handles:\n // 1. Identifying createServerFn().handler() calls\n // 2. Extracting server functions to separate modules\n // 3. Replacing call sites with RPC stubs\n // 4. Generating the server function manifest\n // Also handles createIsomorphicFn, createServerOnlyFn, createClientOnlyFn, createMiddleware\n startCompilerPlugin({\n framework: corePluginOpts.framework,\n environments,\n generateFunctionId: startPluginOpts?.serverFns?.generateFunctionId,\n providerEnvName: serverFnProviderEnv,\n }),\n importProtectionPlugin({\n getConfig,\n framework: corePluginOpts.framework,\n environments,\n providerEnvName: serverFnProviderEnv,\n }),\n tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),\n loadEnvPlugin(),\n startManifestPlugin({\n getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n devServerPlugin({ getConfig }),\n previewServerPlugin(),\n {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(e) {\n return (\n e.name === VITE_ENVIRONMENT_NAMES.client ||\n e.name === VITE_ENVIRONMENT_NAMES.server\n )\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as ViteEnvironmentNames\n if (!Object.values(VITE_ENVIRONMENT_NAMES).includes(environment)) {\n throw new Error(`Unknown environment: ${environment}`)\n }\n capturedBundle[environment] = bundle\n },\n },\n ]\n}\n\nfunction defineReplaceEnv<TKey extends string, TValue extends string>(\n key: TKey,\n value: TValue,\n): { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue } {\n return {\n [`process.env.${key}`]: JSON.stringify(value),\n [`import.meta.env.${key}`]: JSON.stringify(value),\n } as { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue }\n}\n"],"names":["startConfig"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,SAAS,UAAU,KAAsB;AACvC,MAAI;AACF,QAAI,IAAI,GAAG;AACX,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,4BACd,gBACA,iBACqB;AAGrB,QAAM,sBACJ,eAAe,UAAU,eAAe,uBAAuB;AACjE,QAAM,gBAAgB,wBAAwB,uBAAuB;AAErE,QAAM,sBAA2C;AAAA,IAC/C,MAAM;AAAA,IACN,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,aAAa;AAAA,IACb;AAAA,EAAA;AAGF,MAAI;AACJ,QAAM,YAAyB,MAAM;AACnC,QAAI,CAAC,oBAAoB,MAAM;AAC7B,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,QAAI,CAAC,aAAa;AAChB,oBAAc;AAAA,QACZ;AAAA,QACA;AAAA,QACA,oBAAoB;AAAA,MAAA;AAAA,IAExB;AACA,WAAO,EAAE,aAAa,qBAAqB,eAAA;AAAA,EAC7C;AAEA,QAAM,iBAEF,CAAA;AAEJ,WAAS,UAAU,SAAyD;AAC1E,UAAM,SAAS,eAAe,OAAO;AACrC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,uCAAuC,OAAO,EAAE;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,eAAmE;AAAA,IACvE,EAAE,MAAM,uBAAuB,QAAQ,MAAM,SAAA;AAAA,IAC7C,EAAE,MAAM,uBAAuB,QAAQ,MAAM,SAAA;AAAA,EAAS;AAExD,MACE,eAAe,UAAU,eACzB,CAAC,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,eAAe,UAAU,WAAW,GACzE;AACA,iBAAa,KAAK;AAAA,MAChB,MAAM,eAAe,SAAS;AAAA,MAC9B,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM,OAAO,YAAY,EAAE,WAAW;AACpC,4BAAoB,cAAc,WAAW,QAAQ;AACrD,YAAI,CAAC,UAAU,oBAAoB,WAAW,GAAG;AAC/C,8BAAoB,cAAc,UAAU;AAAA,YAC1C;AAAA,YACA,WAAW;AAAA,YACX;AAAA,UAAA,CACD;AAAA,QACH;AACA,cAAM,OAAO,WAAW,QAAQ,QAAQ,IAAA;AACxC,4BAAoB,OAAO;AAE3B,cAAM,EAAE,aAAAA,aAAAA,IAAgB,UAAA;AACxB,YAAIA,aAAY,OAAO,aAAa,QAAW;AAC7C,cAAI,CAAC,UAAU,oBAAoB,WAAW,GAAG;AAC/CA,yBAAY,OAAO,WACjB,oBAAoB,YAAY,QAAQ,YAAY,EAAE;AAAA,UAC1D,OAAO;AACLA,yBAAY,OAAO,WAAW;AAAA,UAChC;AAAA,QACF,OAAO;AACL,cAAI,YAAY,WAAW,CAAC,WAAW,QAAQ,gBAAgB;AAE7D,gBACE,CAAC,UAAU,CAAC,KAAKA,aAAY,OAAO,UAAU,GAAG,CAAC,EAAE;AAAA,cAClD,UAAU,CAAC,KAAK,oBAAoB,aAAa,GAAG,CAAC;AAAA,YAAA,GAEvD;AACA,mBAAK;AAAA,gBACH;AAAA,cAAA;AAAA,YAEJ;AAAA,UACF;AAAA,QACF;AAEA,cAAM,qBAAqB,UAAU;AAAA,UACnC;AAAA,UACAA,aAAY,OAAO;AAAA,UACnBA,aAAY,UAAU;AAAA,UACtB;AAAA,QAAA,CACD;AACD,cAAM,uBAAuB,KAAK,MAAMA,aAAY,YAAY;AAChE,4BAAoB,eAAe;AAEnC,cAAM,gBAAgB,aAAa;AAAA,UACjC,MAAM;AAAA,UACN,iBAAiBA,aAAY,MAAM;AAAA,UACnC,cAAc;AAAA,UACd;AAAA,UACA,UAAU;AAAA,QAAA,CACX;AACD,4BAAoB,gBAAgB;AAEpC,cAAM,iBAAiB,aAAa;AAAA,UAClC,MAAM;AAAA,UACN,iBAAiBA,aAAY,OAAO;AAAA,UACpC,cAAc;AAAA,UACd;AAAA,UACA,UAAU;AAAA,QAAA,CACX;AACD,4BAAoB,iBAAiB;AAErC,cAAM,kBAAkB,aAAa;AAAA,UACnC,MAAM;AAAA,UACN,iBAAiBA,aAAY,OAAO;AAAA,UACpC,cAAc;AAAA,UACd;AAAA,UACA,UAAU;AAAA,QAAA,CACX;AAED,cAAM,kBAAkB,aAAa;AAAA,UACnC,MAAM;AAAA,UACN,iBAAiBA,aAAY,OAAO;AAAA,UACpC,cAAc;AAAA,UACd;AAAA,UACA,UAAU;AAAA,QAAA,CACX;AAED,cAAM,cAAc,KAAK;AAAA,UACvB,mBAAmB,eAAe,kBAAkB;AAAA,QAAA;AAEtD,cAAM,cAAc,KAAK;AAAA,UACvB,mBAAmB,eAAe,kBAAkB;AAAA,QAAA;AAEtD,cAAM,aAAa,KAAK;AAAA,UACtB,iBAAiB,eAAe,kBAAkB;AAAA,QAAA;AAEpD,cAAM,cAAc,KAAK,cAAc,cAAc;AAErD,cAAM,0BAGF;AAAA,UACF,CAAC,aAAa,MAAM,GAAG;AAAA,UACvB,CAAC,aAAa,MAAM,GAAG;AAAA,UACvB,CAAC,aAAa,KAAK,GAAG;AAAA,UACtB,CAAC,aAAa,MAAM,GAAG;AAAA,QAAA;AAGzB,cAAM,mBACJ,aAAa,eAAe,SAAS;AAQvC,cAAM,2BAA2B,MAAM,mBAAmB;AAAA,UACxD,MAAM,QAAQ,IAAA;AAAA,UACd,SAAS,YAAY;AAAA,UACrB,qBAAqB,SAAS;AAC5B,kBAAM,mBAAmB,QAAQ,kBAAkB;AAEnD,gBAAI,kBAAkB;AACpB,kBACE,oBAAoB,oBACpB,iCAAiC,kBACjC;AACA,uBAAO;AAAA,cACT;AAAA,YACF;AAEA,mBAAO;AAAA,UACT;AAAA,QAAA,CACD;AAED,eAAO;AAAA;AAAA;AAAA,UAGL,SAAS,WAAW,WAAW;AAAA,UAC/B,cAAc;AAAA,YACZ,CAAC,uBAAuB,MAAM,GAAG;AAAA,cAC/B,UAAU;AAAA,cACV,OAAO;AAAA,gBACL,eAAe;AAAA,kBACb,OAAO;AAAA,oBACL,MAAM,aAAa;AAAA,kBAAA;AAAA,gBACrB;AAAA,gBAEF,QAAQ,yBAAyB,UAAU;AAAA,cAAA;AAAA,cAE7C,cAAc;AAAA,gBACZ,SAAS,yBAAyB,aAAa;AAAA;AAAA,gBAE/C,SAAS,CAAC,aAAa,WAAW,EAAE;AAAA,kBAAI,CAAC;AAAA;AAAA,oBAEvC,WAAW,KAAK;AAAA;AAAA,gBAAA;AAAA,cAClB;AAAA,YACF;AAAA,YAEF,CAAC,uBAAuB,MAAM,GAAG;AAAA,cAC/B,UAAU;AAAA,cACV,OAAO;AAAA,gBACL,KAAK;AAAA,gBACL,eAAe;AAAA,kBACb,OACE,WAAW,eAAe,uBAAuB,MAAM,GACnD,OAAO,eAAe,SAAS;AAAA,gBAAA;AAAA,gBAEvC,QAAQ,yBAAyB,UAAU;AAAA,gBAC3C,iBAAiB;AAAA,kBACf,SAAS,CAAC,cAAc;AAAA,gBAAA;AAAA,gBAE1B,eACE,WAAW,eAAe,uBAAuB,MAAM,GACnD,OAAO,iBAAiB;AAAA,cAAA;AAAA,cAEhC,cAAc;AAAA;AAAA,gBAEZ,SAAS,CAAC,aAAa,YAAY,WAAW,EAAE;AAAA,kBAAI,CAAC;AAAA;AAAA,oBAEnD,WAAW,KAAK;AAAA;AAAA,gBAAA;AAAA,cAClB;AAAA,YACF;AAAA,UACF;AAAA,UAGF,SAAS;AAAA,YACP,YAAY;AAAA;AAAA,cAEV;AAAA,cACA,aAAa,eAAe,SAAS;AAAA,cACrC,GAAG,yBAAyB,IAAI,WAAW,KAAA;AAAA,YAAK;AAAA,YAElD,OAAO;AAAA,cACL,GAAG;AAAA,YAAA;AAAA,UACL;AAAA;AAAA,UAGF,QAAQ;AAAA;AAAA;AAAA;AAAA,YAKN,GAAG,iBAAiB,sBAAsB,kBAAkB;AAAA,YAC5D,GAAG,iBAAiB,yBAAyB,yBAAyB,UAAU,CAAC;AAAA,YACjF,GAAG,iBAAiB,uBAAuBA,aAAY,OAAO,QAAQ;AAAA,YACtE,GAAI,YAAY,UAAU,iBAAiB,aAAaA,aAAY,KAAK,UAAU,SAAS,OAAO,IAAI,CAAA;AAAA,YACvG,GAAG,iBAAiB,kBAAkB,YAAY,UAAU,SAAS,OAAO;AAAA;AAAA,YAE5E,GAAI,YAAY,WAAWA,aAAY,OAAO,MAAM,gBAAgB;AAAA,cAClE,wBAAwB,KAAK,UAAU,QAAQ,IAAI,YAAY,WAAW,QAAQ,YAAY;AAAA,YAAA,IAC5F,CAAA;AAAA,UAAC;AAAA,UAEP,SAAS;AAAA,YACP,eAAe;AAAA,YACf,MAAM,SAAS,SAAS;AACtB,oBAAM,SAAS,QAAQ,aAAa,uBAAuB,MAAM;AACjE,oBAAM,SAAS,QAAQ,aAAa,uBAAuB,MAAM;AAEjE,kBAAI,CAAC,QAAQ;AACX,sBAAM,IAAI,MAAM,8BAA8B;AAAA,cAChD;AAEA,kBAAI,CAAC,QAAQ;AACX,sBAAM,IAAI,MAAM,2BAA2B;AAAA,cAC7C;AAEA,kBAAI,CAAC,OAAO,SAAS;AAEnB,sBAAM,QAAQ,MAAM,MAAM;AAAA,cAC5B;AACA,kBAAI,CAAC,OAAO,SAAS;AAEnB,sBAAM,QAAQ,MAAM,MAAM;AAAA,cAC5B;AAIA,kBAAI,CAAC,eAAe;AAClB,sBAAM,cAAc,QAAQ,aAAa,mBAAmB;AAC5D,oBAAI,CAAC,aAAa;AAChB,wBAAM,IAAI;AAAA,oBACR,yBAAyB,mBAAmB;AAAA,kBAAA;AAAA,gBAEhD;AACA,oBAAI,CAAC,YAAY,SAAS;AAGxB,wBAAM,QAAQ,MAAM,WAAW;AAAA,gBACjC;AAAA,cACF;AAAA,YACF;AAAA,UAAA;AAAA,QACF;AAAA,MAEJ;AAAA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMF;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU;AAAA,QACR,OAAO;AAAA,QACP,MAAM,QAAQ,SAAS;AACrB,gBAAM,EAAE,aAAAA,aAAAA,IAAgB,UAAA;AACxB,gBAAM,gBAAgB,EAAE,SAAS,aAAAA,cAAa;AAAA,QAChD;AAAA,MAAA;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQF,oBAAoB;AAAA,MAClB,WAAW,eAAe;AAAA,MAC1B;AAAA,MACA,oBAAoB,iBAAiB,WAAW;AAAA,MAChD,iBAAiB;AAAA,IAAA,CAClB;AAAA,IACD,uBAAuB;AAAA,MACrB;AAAA,MACA,WAAW,eAAe;AAAA,MAC1B;AAAA,MACA,iBAAiB;AAAA,IAAA,CAClB;AAAA,IACD,oBAAoB,iBAAiB,WAAW,cAAc;AAAA,IAC9D,cAAA;AAAA,IACA,oBAAoB;AAAA,MAClB,iBAAiB,MAAM,UAAU,uBAAuB,MAAM;AAAA,MAC9D;AAAA,IAAA,CACD;AAAA,IACD,gBAAgB,EAAE,WAAW;AAAA,IAC7B,oBAAA;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,mBAAmB,GAAG;AACpB,eACE,EAAE,SAAS,uBAAuB,UAClC,EAAE,SAAS,uBAAuB;AAAA,MAEtC;AAAA,MACA,SAAS;AAAA,MACT,eAAe,UAAU,QAAQ;AAC/B,cAAM,cAAc,KAAK,YAAY;AACrC,YAAI,CAAC,OAAO,OAAO,sBAAsB,EAAE,SAAS,WAAW,GAAG;AAChE,gBAAM,IAAI,MAAM,wBAAwB,WAAW,EAAE;AAAA,QACvD;AACA,uBAAe,WAAW,IAAI;AAAA,MAChC;AAAA,IAAA;AAAA,EACF;AAEJ;AAEA,SAAS,iBACP,KACA,OACsE;AACtE,SAAO;AAAA,IACL,CAAC,eAAe,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AAAA,IAC5C,CAAC,mBAAmB,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AAAA,EAAA;AAEpD;"}
1
+ {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { joinPaths } from '@tanstack/router-core'\nimport * as vite from 'vite'\nimport { crawlFrameworkPkgs } from 'vitefu'\nimport { join } from 'pathe'\nimport { escapePath } from 'tinyglobby'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { previewServerPlugin } from './preview-server-plugin/plugin'\nimport { parseStartConfig } from './schema'\nimport { resolveEntry } from './resolve-entries'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { importProtectionPlugin } from './import-protection-plugin/plugin'\nimport type {\n GetConfigFn,\n ResolvedStartConfig,\n TanStackStartVitePluginCoreOptions,\n} from './types'\nimport type { ViteEnvironmentNames } from './constants'\nimport type {\n TanStackStartInputConfig,\n TanStackStartOutputConfig,\n} from './schema'\nimport type { PluginOption } from 'vite'\n\nfunction isFullUrl(str: string): boolean {\n try {\n new URL(str)\n return true\n } catch {\n return false\n }\n}\n\nexport function TanStackStartVitePluginCore(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartInputConfig,\n): Array<PluginOption> {\n // Determine the provider environment for server functions\n // If providerEnv is set, use that; otherwise default to SSR as the provider\n const serverFnProviderEnv =\n corePluginOpts.serverFn?.providerEnv || VITE_ENVIRONMENT_NAMES.server\n const ssrIsProvider = serverFnProviderEnv === VITE_ENVIRONMENT_NAMES.server\n\n const resolvedStartConfig: ResolvedStartConfig = {\n root: '',\n startFilePath: undefined,\n routerFilePath: '',\n srcDirectory: '',\n viteAppBase: '',\n serverFnProviderEnv,\n }\n\n let startConfig: TanStackStartOutputConfig | null\n const getConfig: GetConfigFn = () => {\n if (!resolvedStartConfig.root) {\n throw new Error(`Cannot get config before root is resolved`)\n }\n if (!startConfig) {\n startConfig = parseStartConfig(\n startPluginOpts,\n corePluginOpts,\n resolvedStartConfig.root,\n )\n }\n return { startConfig, resolvedStartConfig, corePluginOpts }\n }\n\n // When the router basepath and vite base are misaligned during dev,\n // we install a URL rewrite middleware instead of erroring.\n let needsDevBaseRewrite = false\n\n const capturedBundle: Partial<\n Record<ViteEnvironmentNames, vite.Rollup.OutputBundle>\n > = {}\n\n function getBundle(envName: ViteEnvironmentNames): vite.Rollup.OutputBundle {\n const bundle = capturedBundle[envName]\n if (!bundle) {\n throw new Error(`No bundle captured for environment: ${envName}`)\n }\n return bundle\n }\n\n const environments: Array<{ name: string; type: 'client' | 'server' }> = [\n { name: VITE_ENVIRONMENT_NAMES.client, type: 'client' },\n { name: VITE_ENVIRONMENT_NAMES.server, type: 'server' },\n ]\n if (\n corePluginOpts.serverFn?.providerEnv &&\n !environments.find((e) => e.name === corePluginOpts.serverFn?.providerEnv)\n ) {\n environments.push({\n name: corePluginOpts.serverFn.providerEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n resolvedStartConfig.viteAppBase = joinPaths([\n '/',\n viteConfig.base,\n '/',\n ])\n }\n const root = viteConfig.root || process.cwd()\n resolvedStartConfig.root = root\n\n const { startConfig } = getConfig()\n if (startConfig.router.basepath === undefined) {\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n startConfig.router.basepath =\n resolvedStartConfig.viteAppBase.replace(/^\\/|\\/$/g, '')\n } else {\n startConfig.router.basepath = '/'\n }\n } else {\n if (command === 'serve' && !viteConfig.server?.middlewareMode) {\n // when serving, we must ensure that router basepath and viteAppBase are aligned\n if (\n !joinPaths(['/', startConfig.router.basepath, '/']).startsWith(\n joinPaths(['/', resolvedStartConfig.viteAppBase, '/']),\n )\n ) {\n // The router basepath and vite base are misaligned.\n // Instead of erroring, we install a dev-server middleware that\n // rewrites incoming request URLs to prepend the vite base prefix.\n // This allows users to have e.g. base: '/_ui/' for asset URLs\n // while keeping router basepath at '/' for page navigation.\n needsDevBaseRewrite = true\n }\n }\n }\n\n const TSS_SERVER_FN_BASE = joinPaths([\n '/',\n startConfig.router.basepath,\n startConfig.serverFns.base,\n '/',\n ])\n const resolvedSrcDirectory = join(root, startConfig.srcDirectory)\n resolvedStartConfig.srcDirectory = resolvedSrcDirectory\n\n const startFilePath = resolveEntry({\n type: 'start entry',\n configuredEntry: startConfig.start.entry,\n defaultEntry: 'start',\n resolvedSrcDirectory,\n required: false,\n })\n resolvedStartConfig.startFilePath = startFilePath\n\n const routerFilePath = resolveEntry({\n type: 'router entry',\n configuredEntry: startConfig.router.entry,\n defaultEntry: 'router',\n resolvedSrcDirectory,\n required: true,\n })\n resolvedStartConfig.routerFilePath = routerFilePath\n\n const clientEntryPath = resolveEntry({\n type: 'client entry',\n configuredEntry: startConfig.client.entry,\n defaultEntry: 'client',\n resolvedSrcDirectory,\n required: false,\n })\n\n const serverEntryPath = resolveEntry({\n type: 'server entry',\n configuredEntry: startConfig.server.entry,\n defaultEntry: 'server',\n resolvedSrcDirectory,\n required: false,\n })\n\n const clientAlias = vite.normalizePath(\n clientEntryPath ?? corePluginOpts.defaultEntryPaths.client,\n )\n const serverAlias = vite.normalizePath(\n serverEntryPath ?? corePluginOpts.defaultEntryPaths.server,\n )\n const startAlias = vite.normalizePath(\n startFilePath ?? corePluginOpts.defaultEntryPaths.start,\n )\n const routerAlias = vite.normalizePath(routerFilePath)\n\n const entryAliasConfiguration: Record<\n (typeof ENTRY_POINTS)[keyof typeof ENTRY_POINTS],\n string\n > = {\n [ENTRY_POINTS.client]: clientAlias,\n [ENTRY_POINTS.server]: serverAlias,\n [ENTRY_POINTS.start]: startAlias,\n [ENTRY_POINTS.router]: routerAlias,\n }\n\n const startPackageName =\n `@tanstack/${corePluginOpts.framework}-start` as const\n\n // crawl packages that have start in \"peerDependencies\"\n // see https://github.com/svitejs/vitefu/blob/d8d82fa121e3b2215ba437107093c77bde51b63b/src/index.js#L95-L101\n\n // this is currently uncached; could be implemented similarly as vite handles lock file changes\n // see https://github.com/vitejs/vite/blob/557f797d29422027e8c451ca50dd84bf8c41b5f0/packages/vite/src/node/optimizer/index.ts#L1282\n\n const crawlFrameworkPkgsResult = await crawlFrameworkPkgs({\n root: process.cwd(),\n isBuild: command === 'build',\n isFrameworkPkgByJson(pkgJson) {\n const peerDependencies = pkgJson['peerDependencies']\n\n if (peerDependencies) {\n if (\n startPackageName in peerDependencies ||\n '@tanstack/start-client-core' in peerDependencies\n ) {\n return true\n }\n }\n\n return false\n },\n })\n\n return {\n // see https://vite.dev/config/shared-options.html#apptype\n // this will prevent vite from injecting middlewares that we don't want\n appType: viteConfig.appType ?? 'custom',\n environments: {\n [VITE_ENVIRONMENT_NAMES.client]: {\n consumer: 'client',\n build: {\n rollupOptions: {\n input: {\n main: ENTRY_POINTS.client,\n },\n },\n outDir: getClientOutputDirectory(viteConfig),\n },\n optimizeDeps: {\n exclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n // Ensure user code can be crawled for dependencies\n entries: [clientAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n [VITE_ENVIRONMENT_NAMES.server]: {\n consumer: 'server',\n build: {\n ssr: true,\n rollupOptions: {\n input:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.rollupOptions?.input ?? serverAlias,\n },\n outDir: getServerOutputDirectory(viteConfig),\n commonjsOptions: {\n include: [/node_modules/],\n },\n copyPublicDir:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.copyPublicDir ?? false,\n },\n optimizeDeps: {\n // Ensure user code can be crawled for dependencies\n entries: [serverAlias, startAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n },\n\n resolve: {\n noExternal: [\n // ENTRY_POINTS.start,\n '@tanstack/start**',\n `@tanstack/${corePluginOpts.framework}-start**`,\n ...crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n ],\n alias: {\n ...entryAliasConfiguration,\n },\n },\n /* prettier-ignore */\n define: {\n // define is an esbuild function that replaces the any instances of given keys with the given values\n // i.e: __FRAMEWORK_NAME__ can be replaced with JSON.stringify(\"TanStack Start\")\n // This is not the same as injecting environment variables.\n\n ...defineReplaceEnv('TSS_SERVER_FN_BASE', TSS_SERVER_FN_BASE),\n ...defineReplaceEnv('TSS_CLIENT_OUTPUT_DIR', getClientOutputDirectory(viteConfig)),\n ...defineReplaceEnv('TSS_ROUTER_BASEPATH', startConfig.router.basepath),\n ...(command === 'serve' ? defineReplaceEnv('TSS_SHELL', startConfig.spa?.enabled ? 'true' : 'false') : {}),\n ...defineReplaceEnv('TSS_DEV_SERVER', command === 'serve' ? 'true' : 'false'),\n // Replace NODE_ENV during build (unless opted out) for dead code elimination in server bundles\n ...(command === 'build' && startConfig.server.build.staticNodeEnv ? {\n 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || viteConfig.mode || 'production'),\n } : {}),\n },\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n const client = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n const server = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!client) {\n throw new Error('Client environment not found')\n }\n\n if (!server) {\n throw new Error('SSR environment not found')\n }\n\n if (!client.isBuilt) {\n // Build the client bundle first\n await builder.build(client)\n }\n if (!server.isBuilt) {\n // Build the SSR bundle\n await builder.build(server)\n }\n\n // If a custom provider environment is configured (not SSR),\n // build it last so the manifest includes functions from all environments\n if (!ssrIsProvider) {\n const providerEnv = builder.environments[serverFnProviderEnv]\n if (!providerEnv) {\n throw new Error(\n `Provider environment \"${serverFnProviderEnv}\" not found`,\n )\n }\n if (!providerEnv.isBuilt) {\n // Build the provider environment last\n // This ensures all server functions are discovered from client/ssr builds\n await builder.build(providerEnv)\n }\n }\n },\n },\n }\n },\n },\n // Separate plugin for buildApp hook with enforce: 'post'\n // This ensures proper ordering with other plugins that also have\n // buildApp hooks with order: 'post'. The enforce: 'post' ensures this\n // runs after other plugins (like Nitro) complete their builds.\n {\n name: 'tanstack-start-core:post-build',\n enforce: 'post',\n buildApp: {\n order: 'post',\n async handler(builder) {\n const { startConfig } = getConfig()\n await postServerBuild({ builder, startConfig })\n },\n },\n },\n // Server function plugin handles:\n // 1. Identifying createServerFn().handler() calls\n // 2. Extracting server functions to separate modules\n // 3. Replacing call sites with RPC stubs\n // 4. Generating the server function manifest\n // Also handles createIsomorphicFn, createServerOnlyFn, createClientOnlyFn, createMiddleware\n startCompilerPlugin({\n framework: corePluginOpts.framework,\n environments,\n generateFunctionId: startPluginOpts?.serverFns?.generateFunctionId,\n providerEnvName: serverFnProviderEnv,\n }),\n importProtectionPlugin({\n getConfig,\n framework: corePluginOpts.framework,\n environments,\n providerEnvName: serverFnProviderEnv,\n }),\n tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),\n loadEnvPlugin(),\n startManifestPlugin({\n getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n // When the vite base and router basepath are misaligned (e.g. base: '/_ui/', basepath: '/'),\n // install a middleware that rewrites incoming request URLs to prepend the vite base prefix.\n // This allows Vite's internal base middleware to accept the requests, then strips the prefix\n // before passing to the SSR handler.\n // Registered BEFORE devServerPlugin so this middleware is added to the Connect stack first,\n // ensuring all subsequent middlewares (CSS, SSR, etc.) see the rewritten URL.\n {\n name: 'tanstack-start-core:dev-base-rewrite',\n configureServer(server) {\n if (!needsDevBaseRewrite) {\n return\n }\n const basePrefix = resolvedStartConfig.viteAppBase.replace(/\\/$/, '')\n server.middlewares.use((req, _res, next) => {\n if (req.url && !req.url.startsWith(basePrefix)) {\n req.url = basePrefix + req.url\n }\n next()\n })\n },\n },\n devServerPlugin({ getConfig }),\n previewServerPlugin(),\n {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(e) {\n return (\n e.name === VITE_ENVIRONMENT_NAMES.client ||\n e.name === VITE_ENVIRONMENT_NAMES.server\n )\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as ViteEnvironmentNames\n if (!Object.values(VITE_ENVIRONMENT_NAMES).includes(environment)) {\n throw new Error(`Unknown environment: ${environment}`)\n }\n capturedBundle[environment] = bundle\n },\n },\n ]\n}\n\nfunction defineReplaceEnv<TKey extends string, TValue extends string>(\n key: TKey,\n value: TValue,\n): { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue } {\n return {\n [`process.env.${key}`]: JSON.stringify(value),\n [`import.meta.env.${key}`]: JSON.stringify(value),\n } as { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue }\n}\n"],"names":["startConfig"],"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.164.1",
3
+ "version": "1.165.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -61,9 +61,9 @@
61
61
  "xmlbuilder2": "^4.0.3",
62
62
  "zod": "^3.24.2",
63
63
  "@tanstack/router-core": "1.163.3",
64
+ "@tanstack/router-generator": "1.164.0",
64
65
  "@tanstack/router-utils": "1.161.4",
65
66
  "@tanstack/start-client-core": "1.164.1",
66
- "@tanstack/router-generator": "1.164.0",
67
67
  "@tanstack/router-plugin": "1.164.0",
68
68
  "@tanstack/start-server-core": "1.164.1"
69
69
  },
package/src/plugin.ts CHANGED
@@ -73,6 +73,10 @@ export function TanStackStartVitePluginCore(
73
73
  return { startConfig, resolvedStartConfig, corePluginOpts }
74
74
  }
75
75
 
76
+ // When the router basepath and vite base are misaligned during dev,
77
+ // we install a URL rewrite middleware instead of erroring.
78
+ let needsDevBaseRewrite = false
79
+
76
80
  const capturedBundle: Partial<
77
81
  Record<ViteEnvironmentNames, vite.Rollup.OutputBundle>
78
82
  > = {}
@@ -130,9 +134,12 @@ export function TanStackStartVitePluginCore(
130
134
  joinPaths(['/', resolvedStartConfig.viteAppBase, '/']),
131
135
  )
132
136
  ) {
133
- this.error(
134
- '[tanstack-start]: During `vite dev`, `router.basepath` must start with the vite `base` config value',
135
- )
137
+ // The router basepath and vite base are misaligned.
138
+ // Instead of erroring, we install a dev-server middleware that
139
+ // rewrites incoming request URLs to prepend the vite base prefix.
140
+ // This allows users to have e.g. base: '/_ui/' for asset URLs
141
+ // while keeping router basepath at '/' for page navigation.
142
+ needsDevBaseRewrite = true
136
143
  }
137
144
  }
138
145
  }
@@ -389,6 +396,27 @@ export function TanStackStartVitePluginCore(
389
396
  getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),
390
397
  getConfig,
391
398
  }),
399
+ // When the vite base and router basepath are misaligned (e.g. base: '/_ui/', basepath: '/'),
400
+ // install a middleware that rewrites incoming request URLs to prepend the vite base prefix.
401
+ // This allows Vite's internal base middleware to accept the requests, then strips the prefix
402
+ // before passing to the SSR handler.
403
+ // Registered BEFORE devServerPlugin so this middleware is added to the Connect stack first,
404
+ // ensuring all subsequent middlewares (CSS, SSR, etc.) see the rewritten URL.
405
+ {
406
+ name: 'tanstack-start-core:dev-base-rewrite',
407
+ configureServer(server) {
408
+ if (!needsDevBaseRewrite) {
409
+ return
410
+ }
411
+ const basePrefix = resolvedStartConfig.viteAppBase.replace(/\/$/, '')
412
+ server.middlewares.use((req, _res, next) => {
413
+ if (req.url && !req.url.startsWith(basePrefix)) {
414
+ req.url = basePrefix + req.url
415
+ }
416
+ next()
417
+ })
418
+ },
419
+ },
392
420
  devServerPlugin({ getConfig }),
393
421
  previewServerPlugin(),
394
422
  {