🚨 Latest Research:Tanstack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack.Learn More
Socket
Book a DemoSign in
Socket

@tanstack/router-generator

Package Overview
Dependencies
Maintainers
7
Versions
577
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/router-generator - npm Package Compare versions

Comparing version
1.166.29
to
1.166.30
+0
-1
dist/cjs/config.cjs

@@ -52,3 +52,2 @@ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");

disableTypes: zod.z.boolean().optional().default(false),
verboseFileRoutes: zod.z.boolean().optional(),
addExtensions: zod.z.union([zod.z.boolean(), zod.z.string()]).optional().default(false).transform((v) => typeof v === "string" ? v.startsWith(".") ? v : `.${v}` : v),

@@ -55,0 +54,0 @@ enableRouteTreeFormatting: zod.z.boolean().optional().default(true),

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"config.cjs","names":[],"sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nconst tokenJsonRegexSchema = z.object({\n regex: z.string(),\n flags: z.string().optional(),\n})\n\nconst tokenMatcherSchema = z.union([\n z.string(),\n z.instanceof(RegExp),\n tokenJsonRegexSchema,\n])\n\nexport type TokenMatcherJson = string | z.infer<typeof tokenJsonRegexSchema>\n\nexport type TokenMatcher = z.infer<typeof tokenMatcherSchema>\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: tokenMatcherSchema.optional().default('index'),\n routeToken: tokenMatcherSchema.optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n verboseFileRoutes: z.boolean().optional(),\n addExtensions: z\n .union([z.boolean(), z.string()])\n .optional()\n .default(false)\n .transform((v) =>\n typeof v === 'string' ? (v.startsWith('.') ? v : `.${v}`) : v,\n ),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n // Parse file config (allows JSON regex-object form)\n const fileConfigRaw = JSON.parse(readFileSync(configFilePathJson, 'utf-8'))\n\n // Merge raw configs (inline overrides file), then parse once to apply defaults\n // This ensures file config values aren't overwritten by inline defaults\n const merged = {\n ...fileConfigRaw,\n ...inlineConfig,\n }\n config = configSchema.parse(merged)\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n // Check that indexToken and routeToken are not identical\n // Works for strings, RegExp, and JSON regex objects\n if (areTokensEqual(config.indexToken, config.routeToken)) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n\n/**\n * Compares two token matchers for equality.\n * Handles strings, RegExp instances, and JSON regex objects.\n */\nfunction areTokensEqual(a: TokenMatcher, b: TokenMatcher): boolean {\n // Both strings\n if (typeof a === 'string' && typeof b === 'string') {\n return a === b\n }\n\n // Both RegExp instances\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Both JSON regex objects\n if (\n typeof a === 'object' &&\n 'regex' in a &&\n typeof b === 'object' &&\n 'regex' in b\n ) {\n return a.regex === b.regex && (a.flags ?? '') === (b.flags ?? '')\n }\n\n // Mixed types - not equal\n return false\n}\n"],"mappings":"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"}
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nconst tokenJsonRegexSchema = z.object({\n regex: z.string(),\n flags: z.string().optional(),\n})\n\nconst tokenMatcherSchema = z.union([\n z.string(),\n z.instanceof(RegExp),\n tokenJsonRegexSchema,\n])\n\nexport type TokenMatcherJson = string | z.infer<typeof tokenJsonRegexSchema>\n\nexport type TokenMatcher = z.infer<typeof tokenMatcherSchema>\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: tokenMatcherSchema.optional().default('index'),\n routeToken: tokenMatcherSchema.optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n addExtensions: z\n .union([z.boolean(), z.string()])\n .optional()\n .default(false)\n .transform((v) =>\n typeof v === 'string' ? (v.startsWith('.') ? v : `.${v}`) : v,\n ),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n // Parse file config (allows JSON regex-object form)\n const fileConfigRaw = JSON.parse(readFileSync(configFilePathJson, 'utf-8'))\n\n // Merge raw configs (inline overrides file), then parse once to apply defaults\n // This ensures file config values aren't overwritten by inline defaults\n const merged = {\n ...fileConfigRaw,\n ...inlineConfig,\n }\n config = configSchema.parse(merged)\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n // Check that indexToken and routeToken are not identical\n // Works for strings, RegExp, and JSON regex objects\n if (areTokensEqual(config.indexToken, config.routeToken)) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n\n/**\n * Compares two token matchers for equality.\n * Handles strings, RegExp instances, and JSON regex objects.\n */\nfunction areTokensEqual(a: TokenMatcher, b: TokenMatcher): boolean {\n // Both strings\n if (typeof a === 'string' && typeof b === 'string') {\n return a === b\n }\n\n // Both RegExp instances\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Both JSON regex objects\n if (\n typeof a === 'object' &&\n 'regex' in a &&\n typeof b === 'object' &&\n 'regex' in b\n ) {\n return a.regex === b.regex && (a.flags ?? '') === (b.flags ?? '')\n }\n\n // Mixed types - not equal\n return false\n}\n"],"mappings":"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"}

@@ -134,3 +134,2 @@ import { z } from 'zod';

disableTypes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
verboseFileRoutes: z.ZodOptional<z.ZodBoolean>;
addExtensions: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>>, string | boolean, string | boolean | undefined>;

@@ -187,3 +186,2 @@ enableRouteTreeFormatting: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;

pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
verboseFileRoutes?: boolean | undefined;
routeTreeFileFooter?: string[] | ((...args: unknown[]) => string[]) | undefined;

@@ -221,3 +219,2 @@ autoCodeSplitting?: boolean | undefined;

disableTypes?: boolean | undefined;
verboseFileRoutes?: boolean | undefined;
addExtensions?: string | boolean | undefined;

@@ -224,0 +221,0 @@ enableRouteTreeFormatting?: boolean | undefined;

@@ -316,24 +316,2 @@ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");

}
if (config.verboseFileRoutes === false) {
const typeImport = {
specifiers: [],
source: this.targetTemplate.fullPkg,
importKind: "type"
};
let needsCreateFileRoute = false;
let needsCreateLazyFileRoute = false;
for (const node of sortedRouteNodes) {
if (require_utils.isRouteNodeValidForAugmentation(node)) {
if (node._fsRouteType !== "lazy") needsCreateFileRoute = true;
if (acc.routePiecesByPath[node.routePath]?.lazy) needsCreateLazyFileRoute = true;
}
if (needsCreateFileRoute && needsCreateLazyFileRoute) break;
}
if (needsCreateFileRoute) typeImport.specifiers.push({ imported: "CreateFileRoute" });
if (needsCreateLazyFileRoute) typeImport.specifiers.push({ imported: "CreateLazyFileRoute" });
if (typeImport.specifiers.length > 0) {
typeImport.specifiers.push({ imported: "FileRoutesByPath" });
imports.push(typeImport);
}
}
const routeTreeConfig = require_utils.buildRouteTreeConfig(acc.routeTree, config.disableTypes);

@@ -446,21 +424,2 @@ const createUpdateRoutes = sortedRouteNodes.map((node) => {

const importStatements = mergedImports.map(require_utils.buildImportString);
let moduleAugmentation = "";
if (config.verboseFileRoutes === false && !config.disableTypes) moduleAugmentation = opts.routeFileResult.map((node) => {
const getModuleDeclaration = (routeNode) => {
if (!require_utils.isRouteNodeValidForAugmentation(routeNode)) return "";
let moduleAugmentation = "";
if (routeNode._fsRouteType === "lazy") moduleAugmentation = `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`;
else moduleAugmentation = `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',
FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
FileRoutesByPath['${routeNode.routePath}']['id'],
FileRoutesByPath['${routeNode.routePath}']['path'],
FileRoutesByPath['${routeNode.routePath}']['fullPath']
>
`;
return `declare module './${require_utils.getImportPath(routeNode, config, this.generatedRouteTreePath)}' {
${moduleAugmentation}
}`;
};
return getModuleDeclaration(node);
}).join("\n");
const rootRouteImport = require_utils.getImportForRouteNode(rootRouteNode, config, this.generatedRouteTreePath, this.root);

@@ -483,3 +442,2 @@ routeImports.unshift(rootRouteImport);

fileRoutesByPathInterface,
moduleAugmentation,
routeTreeConfig.join("\n"),

@@ -546,4 +504,3 @@ routeTree,

routeId: escapedRoutePath,
lazy: node._fsRouteType === "lazy",
verboseFileRoutes: !(this.config.verboseFileRoutes === false)
lazy: node._fsRouteType === "lazy"
},

@@ -550,0 +507,0 @@ node

@@ -7,3 +7,2 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });

const require_getRouteNodes$1 = require("./filesystem/physical/getRouteNodes.cjs");
const require_utils$1 = require("./transform/utils.cjs");
const require_generator = require("./generator.cjs");

@@ -17,3 +16,2 @@ exports.Generator = require_generator.Generator;

exports.determineInitialRoutePath = require_utils.determineInitialRoutePath;
exports.ensureStringArgument = require_utils$1.ensureStringArgument;
exports.format = require_utils.format;

@@ -20,0 +18,0 @@ exports.getConfig = require_config.getConfig;

@@ -11,3 +11,2 @@ export { configSchema, getConfig, resolveConfigPath, baseConfigSchema, } from './config.cjs';

export { rootPathId } from './filesystem/physical/rootPathId.cjs';
export { ensureStringArgument } from './transform/utils.cjs';
export type { TransformImportsConfig, TransformContext, TransformOptions, } from './transform/types.cjs';
export type { TransformContext, TransformOptions } from './transform/types.cjs';

@@ -6,2 +6,5 @@ const require_utils = require("./utils.cjs");

}
function serializeRoutePath(routePath) {
return JSON.stringify(routePath);
}
function getTargetTemplate(config) {

@@ -35,4 +38,4 @@ const target = config.target;

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -49,4 +52,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -81,4 +84,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -95,4 +98,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -128,4 +131,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -143,4 +146,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -147,0 +150,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"file":"template.cjs","names":[],"sources":["../../src/template.ts"],"sourcesContent":["import { format } from './utils'\nimport type { Config } from './config'\n\ntype TemplateTag = 'tsrImports' | 'tsrPath' | 'tsrExportStart' | 'tsrExportEnd'\n\nexport function fillTemplate(\n config: Config,\n template: string,\n values: Record<TemplateTag, string>,\n) {\n const replaced = template.replace(\n /%%(\\w+)%%/g,\n (_, key) => values[key as TemplateTag] || '',\n )\n return format(replaced, config)\n}\n\nexport type TargetTemplate = {\n fullPkg: string\n subPkg: string\n rootRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: () => string\n tsrExportEnd: () => string\n }\n }\n route: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n lazyRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n}\n\nexport function getTargetTemplate(config: Config): TargetTemplate {\n const target = config.target\n switch (target) {\n case 'react':\n return {\n fullPkg: '@tanstack/react-router',\n subPkg: 'react-router',\n rootRoute: {\n template: () =>\n [\n 'import * as React from \"react\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<React.Fragment><div>Hello \"%%tsrPath%%\"!</div><Outlet /></React.Fragment>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/react-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'solid':\n return {\n fullPkg: '@tanstack/solid-router',\n subPkg: 'solid-router',\n rootRoute: {\n template: () =>\n [\n 'import * as Solid from \"solid-js\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<><div>Hello \"%%tsrPath%%\"!</div><Outlet /></>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/solid-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/solid-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/solid-router';\",\n\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'vue':\n return {\n fullPkg: '@tanstack/vue-router',\n subPkg: 'vue-router',\n rootRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return h(\"div\", {}, [\"Hello \\\\\"%%tsrPath%%\\\\\"!\", h(Outlet)]) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/vue-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/vue-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/vue-router';\",\n\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n default:\n throw new Error(`router-generator: Unknown target type: ${target}`)\n }\n}\n"],"mappings":";;AAKA,SAAgB,aACd,QACA,UACA,QACA;AAKA,QAAO,cAAA,OAJU,SAAS,QACxB,eACC,GAAG,QAAQ,OAAO,QAAuB,GAC3C,EACuB,OAAO;;AAgCjC,SAAgB,kBAAkB,QAAgC;CAChE,MAAM,SAAS,OAAO;AACtB,SAAQ,QAAR;EACE,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAC7D,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KAEN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAE7D,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,MACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KAEN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAE7D,oBAAoB;KACrB;IACF;GACF;EACH,QACE,OAAM,IAAI,MAAM,0CAA0C,SAAS"}
{"version":3,"file":"template.cjs","names":[],"sources":["../../src/template.ts"],"sourcesContent":["import { format } from './utils'\nimport type { Config } from './config'\n\ntype TemplateTag = 'tsrImports' | 'tsrPath' | 'tsrExportStart' | 'tsrExportEnd'\n\nexport function fillTemplate(\n config: Config,\n template: string,\n values: Record<TemplateTag, string>,\n) {\n const replaced = template.replace(\n /%%(\\w+)%%/g,\n (_, key) => values[key as TemplateTag] || '',\n )\n return format(replaced, config)\n}\n\nexport type TargetTemplate = {\n fullPkg: string\n subPkg: string\n rootRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: () => string\n tsrExportEnd: () => string\n }\n }\n route: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n lazyRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n}\n\nfunction serializeRoutePath(routePath: string) {\n return JSON.stringify(routePath)\n}\n\nexport function getTargetTemplate(config: Config): TargetTemplate {\n const target = config.target\n switch (target) {\n case 'react':\n return {\n fullPkg: '@tanstack/react-router',\n subPkg: 'react-router',\n rootRoute: {\n template: () =>\n [\n 'import * as React from \"react\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<React.Fragment><div>Hello \"%%tsrPath%%\"!</div><Outlet /></React.Fragment>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/react-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'solid':\n return {\n fullPkg: '@tanstack/solid-router',\n subPkg: 'solid-router',\n rootRoute: {\n template: () =>\n [\n 'import * as Solid from \"solid-js\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<><div>Hello \"%%tsrPath%%\"!</div><Outlet /></>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/solid-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/solid-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/solid-router';\",\n\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'vue':\n return {\n fullPkg: '@tanstack/vue-router',\n subPkg: 'vue-router',\n rootRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return h(\"div\", {}, [\"Hello \\\\\"%%tsrPath%%\\\\\"!\", h(Outlet)]) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/vue-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/vue-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/vue-router';\",\n\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n default:\n throw new Error(`router-generator: Unknown target type: ${target}`)\n }\n}\n"],"mappings":";;AAKA,SAAgB,aACd,QACA,UACA,QACA;AAKA,QAAO,cAAA,OAJU,SAAS,QACxB,eACC,GAAG,QAAQ,OAAO,QAAuB,GAC3C,EACuB,OAAO;;AAgCjC,SAAS,mBAAmB,WAAmB;AAC7C,QAAO,KAAK,UAAU,UAAU;;AAGlC,SAAgB,kBAAkB,QAAgC;CAChE,MAAM,SAAS,OAAO;AACtB,SAAQ,QAAR;EACE,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAC5E,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KAEF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAE5E,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,MACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KAEF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAE5E,oBAAoB;KACrB;IACF;GACF;EACH,QACE,OAAM,IAAI,MAAM,0CAA0C,SAAS"}

@@ -1,298 +0,315 @@

require("../_virtual/_rolldown/runtime.cjs");
const require_utils = require("../utils.cjs");
const require_utils$1 = require("./utils.cjs");
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
let magic_string = require("magic-string");
magic_string = require_runtime.__toESM(magic_string);
let _babel_types = require("@babel/types");
_babel_types = require_runtime.__toESM(_babel_types);
let _tanstack_router_utils = require("@tanstack/router-utils");
let recast = require("recast");
let source_map = require("source-map");
//#region src/transform/transform.ts
var b = recast.types.builders;
async function transform({ ctx, source, node }) {
let appliedChanges = false;
var routeConstructors = ["createFileRoute", "createLazyFileRoute"];
function transform({ ctx, source, node }) {
let ast;
try {
ast = (0, recast.parse)(source, {
sourceFileName: "output.ts",
parser: { parse(code) {
return (0, _tanstack_router_utils.parseAst)({
code,
tokens: true
});
} }
});
} catch (e) {
console.error("Error parsing code", ctx.routeId, source, e);
ast = (0, _tanstack_router_utils.parseAst)({ code: source });
} catch (error) {
return {
result: "error",
error: e
error
};
}
const preferredQuote = detectPreferredQuoteStyle(ast);
let routeExportHandled = false;
function onExportFound(decl) {
if (decl.init?.type === "CallExpression") {
const callExpression = decl.init;
const firstArgument = callExpression.arguments[0];
if (firstArgument) {
if (firstArgument.type === "ObjectExpression") {
const staticProperties = firstArgument.properties.flatMap((p) => {
if (p.type === "ObjectProperty" && p.key.type === "Identifier") return p.key.name;
return [];
});
node.createFileRouteProps = new Set(staticProperties);
}
}
let identifier;
if (callExpression.callee.type === "Identifier") {
identifier = callExpression.callee;
if (ctx.verboseFileRoutes) {
callExpression.callee = b.callExpression(identifier, [b.stringLiteral(ctx.routeId)]);
appliedChanges = true;
}
} else if (callExpression.callee.type === "CallExpression" && callExpression.callee.callee.type === "Identifier") {
identifier = callExpression.callee.callee;
if (!ctx.verboseFileRoutes) {
callExpression.callee = identifier;
appliedChanges = true;
} else appliedChanges = require_utils$1.ensureStringArgument(callExpression.callee, ctx.routeId, ctx.preferredQuote);
}
if (identifier === void 0) throw new Error(`expected identifier to be present in ${ctx.routeId} for export "Route"`);
if (identifier.name === "createFileRoute" && ctx.lazy) {
identifier.name = "createLazyFileRoute";
appliedChanges = true;
} else if (identifier.name === "createLazyFileRoute" && !ctx.lazy) {
identifier.name = "createFileRoute";
appliedChanges = true;
}
} else throw new Error(`expected "Route" export to be initialized by a CallExpression`);
routeExportHandled = true;
const exportedRouteNames = getExportedRouteNames(ast.program.body);
if (exportedRouteNames.size === 0) return { result: "no-route-export" };
const { calls: routeCalls, hasUnsupportedRouteId, hasMalformedRouteCall } = findExportedRouteCalls(ast.program.body, exportedRouteNames);
if (routeCalls.length === 0 && hasMalformedRouteCall) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`)
};
if (routeCalls.length === 0 && hasUnsupportedRouteId) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected route id to be a string literal or plain template literal in ${ctx.routeId}`)
};
if (routeCalls.length === 0) return { result: "not-modified" };
if (routeCalls.length > 1) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`)
};
const routeCall = routeCalls[0];
const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg);
const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg);
if (createFileRouteProps) node.createFileRouteProps = createFileRouteProps;
const expectedCallee = getExpectedRouteConstructor(ctx.lazy);
const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`;
const currentRouteId = source.slice(routeCall.routeIdArg.start, routeCall.routeIdArg.end);
const targetModule = `@tanstack/${ctx.target}-router`;
const imports = parseTargetImports(ast.program.body, source, targetModule);
const s = new magic_string.default(source);
let modified = false;
if (routeCall.callee.name !== expectedCallee) {
s.update(routeCall.callee.start, routeCall.callee.end, expectedCallee);
modified = true;
}
const program = ast.program;
for (const n of program.body) {
if (n.type === "ExportNamedDeclaration") {
if (n.declaration?.type === "VariableDeclaration") {
const decl = n.declaration.declarations[0];
if (decl && decl.type === "VariableDeclarator" && decl.id.type === "Identifier") {
if (decl.id.name === "Route") onExportFound(decl);
}
} else if (n.declaration === null && n.specifiers) {
for (const spec of n.specifiers) if (typeof spec.exported.name === "string") {
if (spec.exported.name === "Route") {
const variableName = spec.local?.name || spec.exported.name;
for (const decl of program.body) if (decl.type === "VariableDeclaration" && decl.declarations[0]) {
const variable = decl.declarations[0];
if (variable.type === "VariableDeclarator" && variable.id.type === "Identifier" && variable.id.name === variableName) {
onExportFound(variable);
break;
}
}
}
}
}
}
if (routeExportHandled) break;
if (currentRouteId !== expectedRouteId) {
s.update(routeCall.routeIdArg.start, routeCall.routeIdArg.end, expectedRouteId);
modified = true;
}
if (!routeExportHandled) return { result: "no-route-export" };
const imports = {
required: [],
banned: []
if (updateRouteImports({
imports,
source,
s,
targetModule,
required: expectedCallee,
lineEnding: getLineEnding(source)
})) modified = true;
if (!modified) return { result: "not-modified" };
return {
result: "modified",
output: s.toString()
};
const targetModule = `@tanstack/${ctx.target}-router`;
if (ctx.verboseFileRoutes === false) imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }, { imported: "createFileRoute" }]
}];
else if (ctx.lazy) {
imports.required = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }]
}];
imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createFileRoute" }]
}];
} else {
imports.required = [{
source: targetModule,
specifiers: [{ imported: "createFileRoute" }]
}];
imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }]
}];
}
function getExportedRouteNames(body) {
const exportedRouteNames = /* @__PURE__ */ new Set();
for (const statement of body) {
if (!_babel_types.isExportNamedDeclaration(statement) || statement.source) continue;
if (_babel_types.isVariableDeclaration(statement.declaration)) {
for (const declarator of statement.declaration.declarations) if (_babel_types.isIdentifier(declarator.id) && declarator.id.name === "Route") exportedRouteNames.add("Route");
}
for (const specifier of statement.specifiers) {
if (!_babel_types.isExportSpecifier(specifier) || getExportedName(specifier.exported) !== "Route") continue;
const localName = getLocalBindingName(specifier.local);
if (localName) exportedRouteNames.add(localName);
}
}
imports.required = require_utils.mergeImportDeclarations(imports.required);
imports.banned = require_utils.mergeImportDeclarations(imports.banned);
const importStatementCandidates = [];
const importDeclarationsToRemove = [];
for (const n of program.body) {
const findImport = (opts) => (i) => {
if (i.source === opts.source) {
const importKind = i.importKind || "value";
return (opts.importKind || "value") === importKind;
return exportedRouteNames;
}
function findExportedRouteCalls(body, exportedRouteNames) {
const calls = [];
let hasUnsupportedRouteId = false;
let hasMalformedRouteCall = false;
for (const statement of body) {
const declaration = getVariableDeclaration(statement);
if (!declaration) continue;
for (const declarator of declaration.declarations) {
if (!_babel_types.isIdentifier(declarator.id) || !exportedRouteNames.has(declarator.id.name)) continue;
const init = getRouteConstructorInit(declarator.init);
if (!init) {
if (isDirectRouteConstructorCall(declarator.init)) hasMalformedRouteCall = true;
continue;
}
return false;
};
if (n.type === "ImportDeclaration" && typeof n.source.value === "string") {
const filterImport = findImport({
source: n.source.value,
importKind: n.importKind
const routeIdArg = init.innerCall.arguments[0];
if (isSupportedRouteId(routeIdArg)) calls.push({
callee: init.callee,
routeIdArg,
optionsArg: init.outerCall.arguments[0]
});
let requiredImports = imports.required.filter(filterImport)[0];
const bannedImports = imports.banned.filter(filterImport)[0];
if (!requiredImports && !bannedImports) continue;
const importSpecifiersToRemove = [];
if (n.specifiers) {
for (const spec of n.specifiers) {
if (!requiredImports && !bannedImports) break;
if (spec.type === "ImportSpecifier" && typeof spec.imported.name === "string") {
if (requiredImports) {
const requiredImportIndex = requiredImports.specifiers.findIndex((imp) => imp.imported === spec.imported.name);
if (requiredImportIndex !== -1) {
requiredImports.specifiers.splice(requiredImportIndex, 1);
if (requiredImports.specifiers.length === 0) {
imports.required = imports.required.splice(imports.required.indexOf(requiredImports), 1);
requiredImports = void 0;
}
} else importStatementCandidates.push(n);
}
if (bannedImports) {
if (bannedImports.specifiers.findIndex((imp) => imp.imported === spec.imported.name) !== -1) importSpecifiersToRemove.push(spec);
}
}
}
if (importSpecifiersToRemove.length > 0) {
appliedChanges = true;
n.specifiers = n.specifiers.filter((spec) => !importSpecifiersToRemove.includes(spec));
if (n.specifiers.length === 0) importDeclarationsToRemove.push(n);
}
}
else hasUnsupportedRouteId = true;
}
}
imports.required.forEach((requiredImport) => {
if (requiredImport.specifiers.length > 0) {
appliedChanges = true;
if (importStatementCandidates.length > 0) {
const importStatement = importStatementCandidates.find((importStatement) => {
if (importStatement.source.value === requiredImport.source) return (importStatement.importKind || "value") === (requiredImport.importKind || "value");
return false;
});
if (importStatement) {
if (importStatement.specifiers === void 0) importStatement.specifiers = [];
const importSpecifiersToAdd = requiredImport.specifiers.map((spec) => b.importSpecifier(b.identifier(spec.imported), b.identifier(spec.imported)));
importStatement.specifiers = [...importStatement.specifiers, ...importSpecifiersToAdd];
return;
}
}
const importStatement = b.importDeclaration(requiredImport.specifiers.map((spec) => b.importSpecifier(b.identifier(spec.imported), spec.local ? b.identifier(spec.local) : null)), b.stringLiteral(requiredImport.source));
program.body.unshift(importStatement);
return {
calls,
hasUnsupportedRouteId,
hasMalformedRouteCall
};
}
function getVariableDeclaration(statement) {
const declaration = _babel_types.isExportNamedDeclaration(statement) ? statement.declaration : statement;
return _babel_types.isVariableDeclaration(declaration) ? declaration : null;
}
function getExportedName(node) {
return _babel_types.isIdentifier(node) ? node.name : node.value;
}
function getLocalBindingName(node) {
return _babel_types.isIdentifier(node) ? node.name : null;
}
function getRouteConstructorInit(expression) {
if (!expression || !_babel_types.isCallExpression(expression)) return null;
if (!_babel_types.isCallExpression(expression.callee)) return null;
const innerCall = expression.callee;
if (!_babel_types.isIdentifier(innerCall.callee) || !isRouteConstructor(innerCall.callee)) return null;
return {
callee: innerCall.callee,
outerCall: expression,
innerCall
};
}
function isDirectRouteConstructorCall(expression) {
return !!expression && _babel_types.isCallExpression(expression) && _babel_types.isIdentifier(expression.callee) && isRouteConstructor(expression.callee);
}
function isRouteConstructor(callee) {
return routeConstructors.includes(callee.name);
}
function isSupportedRouteId(arg) {
return !!arg && (_babel_types.isStringLiteral(arg) || _babel_types.isTemplateLiteral(arg) && arg.expressions.length === 0);
}
function getRouteIdQuote(source, arg) {
const raw = source.slice(arg.start, arg.end);
if (raw.startsWith("'")) return "'";
if (raw.startsWith("\"")) return "\"";
return "`";
}
function getCreateFileRouteProps(arg) {
if (!arg || !_babel_types.isObjectExpression(arg)) return;
const props = /* @__PURE__ */ new Set();
for (const property of arg.properties) {
if (!_babel_types.isObjectProperty(property) || property.computed) continue;
if (_babel_types.isIdentifier(property.key)) {
props.add(property.key.name);
continue;
}
if (_babel_types.isStringLiteral(property.key)) props.add(property.key.value);
}
return props;
}
function parseTargetImports(body, source, targetModule) {
const imports = [];
for (const statement of body) {
if (!_babel_types.isImportDeclaration(statement) || statement.importKind === "type" || statement.source.value !== targetModule) continue;
const rawSource = source.slice(statement.source.start, statement.source.end);
const importStatement = source.slice(statement.start, statement.end);
imports.push({
declaration: statement,
defaultImport: statement.specifiers.find((specifier) => _babel_types.isImportDefaultSpecifier(specifier))?.local.name,
namespace: statement.specifiers.find((specifier) => _babel_types.isImportNamespaceSpecifier(specifier))?.local.name,
named: statement.specifiers.filter((specifier) => _babel_types.isImportSpecifier(specifier)).map((specifier) => ({
imported: _babel_types.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value,
local: specifier.local.name,
importKind: specifier.importKind ?? void 0
})),
moduleName: statement.source.value,
quote: rawSource[0],
semicolon: importStatement.trimEnd().endsWith(";")
});
}
return imports;
}
function updateRouteImports({ imports, source, s, targetModule, required, lineEnding }) {
const analysis = analyzeRouteImports(imports, required);
if (analysis.kind === "ok") return false;
if (analysis.kind === "rename") {
s.update(analysis.imported.start, analysis.imported.end, analysis.next);
s.update(analysis.local.start, analysis.local.end, analysis.next);
return true;
}
return normalizeRouteImports({
imports,
source,
s,
targetModule,
required,
lineEnding
});
if (importDeclarationsToRemove.length > 0) {
appliedChanges = true;
for (const importDeclaration of importDeclarationsToRemove) if (importDeclaration.specifiers?.length === 0) {
const index = program.body.indexOf(importDeclaration);
if (index !== -1) program.body.splice(index, 1);
}
function analyzeRouteImports(imports, required) {
const opposite = getOtherRouteConstructor(required);
let requiredCount = 0;
let oppositeCount = 0;
let renameCandidate;
for (const declaration of imports) for (const specifier of declaration.declaration.specifiers) {
if (!_babel_types.isImportSpecifier(specifier)) continue;
const imported = specifier.imported;
if (!_babel_types.isIdentifier(imported)) return { kind: "normalize" };
if (!isRouteConstructorName(imported.name)) continue;
if (specifier.local.name !== imported.name) return { kind: "normalize" };
if (imported.name === required) {
requiredCount++;
continue;
}
if (imported.name === opposite) {
oppositeCount++;
renameCandidate = {
imported,
local: specifier.local,
next: required
};
}
}
if (!appliedChanges) return { result: "not-modified" };
const printResult = (0, recast.print)(ast, {
reuseWhitespace: true,
sourceMapName: "output.map"
});
let transformedCode = printResult.code;
if (printResult.map) transformedCode = await fixTransformedOutputText({
originalCode: source,
transformedCode,
sourceMap: printResult.map,
preferredQuote
});
return {
result: "modified",
output: transformedCode
if (requiredCount === 1 && oppositeCount === 0) return { kind: "ok" };
if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) return {
kind: "rename",
...renameCandidate
};
return { kind: "normalize" };
}
async function fixTransformedOutputText({ originalCode, transformedCode, sourceMap, preferredQuote }) {
const originalLines = originalCode.split("\n");
const transformedLines = transformedCode.split("\n");
const defaultUsesSemicolons = detectSemicolonUsage(originalCode);
const consumer = await new source_map.SourceMapConsumer(sourceMap);
return transformedLines.map((line, i) => {
const transformedLineNum = i + 1;
let origLineText = void 0;
for (let col = 0; col < line.length; col++) {
const mapped = consumer.originalPositionFor({
line: transformedLineNum,
column: col
});
if (mapped.line != null && mapped.line > 0) {
origLineText = originalLines[mapped.line - 1];
break;
}
function normalizeRouteImports({ imports, source, s, targetModule, required, lineEnding }) {
const owner = imports.find((declaration) => hasNamedImport(declaration.named, required)) ?? imports.find((declaration) => !declaration.namespace);
let modified = false;
for (const declaration of imports) {
const named = normalizeNamedImports({
named: declaration.named,
required,
isOwner: declaration === owner
});
if (sameNamedImports(declaration.named, named)) continue;
const replacement = renderImportDeclaration({
...declaration,
named
});
if (replacement === null) {
s.remove(declaration.declaration.start, getRemovalEnd(source, declaration.declaration.end));
modified = true;
continue;
}
if (origLineText !== void 0) {
if (origLineText === line) return origLineText;
return fixLine(line, {
originalLine: origLineText,
useOriginalSemicolon: true,
useOriginalQuotes: true,
fallbackQuote: preferredQuote
});
} else return fixLine(line, {
originalLine: null,
useOriginalSemicolon: false,
useOriginalQuotes: false,
fallbackQuote: preferredQuote,
fallbackSemicolon: defaultUsesSemicolons
});
}).join("\n");
s.update(declaration.declaration.start, declaration.declaration.end, replacement);
modified = true;
}
if (!owner) {
const quote = imports[0]?.quote ?? "'";
const semicolon = imports[0]?.semicolon ?? false;
s.prepend(`import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ";" : ""}${lineEnding}`);
modified = true;
}
return modified;
}
function fixLine(line, { originalLine, useOriginalSemicolon, useOriginalQuotes, fallbackQuote, fallbackSemicolon = true }) {
let result = line;
if (useOriginalQuotes && originalLine) result = fixQuotes(result, originalLine, fallbackQuote);
else if (!useOriginalQuotes && fallbackQuote) result = fixQuotesToPreferred(result, fallbackQuote);
if (useOriginalSemicolon && originalLine) {
const hadSemicolon = originalLine.trimEnd().endsWith(";");
const hasSemicolon = result.trimEnd().endsWith(";");
if (hadSemicolon && !hasSemicolon) result += ";";
if (!hadSemicolon && hasSemicolon) result = result.replace(/;\s*$/, "");
} else if (!useOriginalSemicolon) {
const hasSemicolon = result.trimEnd().endsWith(";");
if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\s*$/, "");
if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ";";
function normalizeNamedImports({ named, required, isOwner }) {
const banned = getOtherRouteConstructor(required);
const nextNamed = [];
const seen = /* @__PURE__ */ new Set();
for (const specifier of named) {
if (specifier.imported === banned) continue;
if (specifier.local === required && (specifier.imported !== required || !isOwner)) continue;
const key = `${specifier.importKind ?? "value"}:${specifier.imported}:${specifier.local}`;
if (seen.has(key)) continue;
seen.add(key);
nextNamed.push(specifier);
}
return result;
if (isOwner && !hasNamedImport(nextNamed, required)) nextNamed.push({
imported: required,
local: required
});
return nextNamed;
}
function fixQuotes(line, originalLine, fallbackQuote) {
let originalQuote = detectQuoteFromLine(originalLine);
if (!originalQuote) originalQuote = fallbackQuote;
return fixQuotesToPreferred(line, originalQuote);
function getExpectedRouteConstructor(lazy) {
return lazy ? "createLazyFileRoute" : "createFileRoute";
}
function fixQuotesToPreferred(line, quote) {
return line.replace(/(['"`])([^'"`\\]*(?:\\.[^'"`\\]*)*)\1/g, (_, q, content) => {
return `${quote}${content.replaceAll(quote, `\\${quote}`)}${quote}`;
});
function getOtherRouteConstructor(constructor) {
return constructor === "createFileRoute" ? "createLazyFileRoute" : "createFileRoute";
}
function detectQuoteFromLine(line) {
const match = line.match(/(['"`])(?:\\.|[^\\])*?\1/);
return match ? match[1] : null;
function hasNamedImport(named, required) {
return named.some((specifier) => specifier.imported === required && specifier.local === required && specifier.importKind !== "type");
}
function detectSemicolonUsage(code) {
const lines = code.split("\n").map((l) => l.trim());
const total = lines.length;
return lines.filter((l) => l.endsWith(";")).length > total / 2;
function sameNamedImports(left, right) {
return left.length === right.length && left.every((specifier, index) => specifier.imported === right[index].imported && specifier.local === right[index].local && specifier.importKind === right[index].importKind);
}
function detectPreferredQuoteStyle(ast) {
let single = 0;
let double = 0;
(0, recast.visit)(ast, { visitStringLiteral(path) {
if (path.parent.node.type !== "JSXAttribute") {
const raw = path.node.extra?.raw;
if (raw?.startsWith("'")) single++;
else if (raw?.startsWith("\"")) double++;
}
return false;
} });
if (single >= double) return "'";
return "\"";
function isRouteConstructorName(value) {
return routeConstructors.includes(value);
}
function renderImportDeclaration(importDeclaration) {
const parts = [];
if (importDeclaration.defaultImport) parts.push(importDeclaration.defaultImport);
if (importDeclaration.namespace) parts.push(`* as ${importDeclaration.namespace}`);
if (importDeclaration.named.length > 0) parts.push(`{ ${importDeclaration.named.map((specifier) => `${specifier.importKind === "type" ? "type " : ""}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`).join(", ")} }`);
if (parts.length === 0) return null;
return `import ${parts.join(", ")} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ";" : ""}`;
}
function getLineEnding(source) {
if (source.includes("\r\n")) return "\r\n";
if (source.includes("\n")) return "\n";
if (source.includes("\r")) return "\r";
return "\n";
}
function getRemovalEnd(source, end) {
let pos = end;
while (pos < source.length && (source[pos] === " " || source[pos] === " ")) pos++;
if (source[pos] === "\r" && source[pos + 1] === "\n") return pos + 2;
if (source[pos] === "\n" || source[pos] === "\r") return pos + 1;
return end;
}
//#endregion

@@ -299,0 +316,0 @@ exports.transform = transform;

@@ -1,1 +0,1 @@

{"version":3,"file":"transform.cjs","names":[],"sources":["../../../src/transform/transform.ts"],"sourcesContent":["import { parseAst } from '@tanstack/router-utils'\nimport { parse, print, types, visit } from 'recast'\nimport { SourceMapConsumer } from 'source-map'\nimport { mergeImportDeclarations } from '../utils'\nimport { ensureStringArgument } from './utils'\nimport type { ImportDeclaration } from '../types'\nimport type { RawSourceMap } from 'source-map'\nimport type { TransformOptions, TransformResult } from './types'\n\nconst b = types.builders\n\nexport async function transform({\n ctx,\n source,\n node,\n}: TransformOptions): Promise<TransformResult> {\n let appliedChanges = false as boolean\n let ast: types.namedTypes.File\n try {\n ast = parse(source, {\n sourceFileName: 'output.ts',\n parser: {\n parse(code: string) {\n return parseAst({\n code,\n // we need to instruct babel to produce tokens,\n // otherwise recast will try to generate the tokens via its own parser and will fail\n tokens: true,\n })\n },\n },\n })\n } catch (e) {\n console.error('Error parsing code', ctx.routeId, source, e)\n return {\n result: 'error',\n error: e,\n }\n }\n\n const preferredQuote = detectPreferredQuoteStyle(ast)\n\n let routeExportHandled = false as boolean\n function onExportFound(decl: types.namedTypes.VariableDeclarator) {\n if (decl.init?.type === 'CallExpression') {\n const callExpression = decl.init\n const firstArgument = callExpression.arguments[0]\n if (firstArgument) {\n if (firstArgument.type === 'ObjectExpression') {\n const staticProperties = firstArgument.properties.flatMap((p) => {\n if (p.type === 'ObjectProperty' && p.key.type === 'Identifier') {\n return p.key.name\n }\n return []\n })\n node.createFileRouteProps = new Set(staticProperties)\n }\n }\n let identifier: types.namedTypes.Identifier | undefined\n // `const Route = createFileRoute({ ... })`\n if (callExpression.callee.type === 'Identifier') {\n identifier = callExpression.callee\n if (ctx.verboseFileRoutes) {\n // we need to add the string literal via another CallExpression\n callExpression.callee = b.callExpression(identifier, [\n b.stringLiteral(ctx.routeId),\n ])\n appliedChanges = true\n }\n }\n // `const Route = createFileRoute('/path')({ ... })`\n else if (\n callExpression.callee.type === 'CallExpression' &&\n callExpression.callee.callee.type === 'Identifier'\n ) {\n identifier = callExpression.callee.callee\n if (!ctx.verboseFileRoutes) {\n // we need to remove the route id\n callExpression.callee = identifier\n appliedChanges = true\n } else {\n // check if the route id is correct\n appliedChanges = ensureStringArgument(\n callExpression.callee,\n ctx.routeId,\n ctx.preferredQuote,\n )\n }\n }\n if (identifier === undefined) {\n throw new Error(\n `expected identifier to be present in ${ctx.routeId} for export \"Route\"`,\n )\n }\n if (identifier.name === 'createFileRoute' && ctx.lazy) {\n identifier.name = 'createLazyFileRoute'\n appliedChanges = true\n } else if (identifier.name === 'createLazyFileRoute' && !ctx.lazy) {\n identifier.name = 'createFileRoute'\n appliedChanges = true\n }\n } else {\n throw new Error(\n `expected \"Route\" export to be initialized by a CallExpression`,\n )\n }\n routeExportHandled = true\n }\n\n const program: types.namedTypes.Program = ast.program\n // first pass: find Route export\n for (const n of program.body) {\n if (n.type === 'ExportNamedDeclaration') {\n // direct export of a variable declaration, e.g. `export const Route = createFileRoute('/path')`\n if (n.declaration?.type === 'VariableDeclaration') {\n const decl = n.declaration.declarations[0]\n if (\n decl &&\n decl.type === 'VariableDeclarator' &&\n decl.id.type === 'Identifier'\n ) {\n if (decl.id.name === 'Route') {\n onExportFound(decl)\n }\n }\n }\n // this is an export without a declaration, e.g. `export { Route }`\n else if (n.declaration === null && n.specifiers) {\n for (const spec of n.specifiers) {\n if (typeof spec.exported.name === 'string') {\n if (spec.exported.name === 'Route') {\n const variableName = spec.local?.name || spec.exported.name\n // find the matching variable declaration by iterating over the top-level declarations\n for (const decl of program.body) {\n if (\n decl.type === 'VariableDeclaration' &&\n decl.declarations[0]\n ) {\n const variable = decl.declarations[0]\n if (\n variable.type === 'VariableDeclarator' &&\n variable.id.type === 'Identifier' &&\n variable.id.name === variableName\n ) {\n onExportFound(variable)\n break\n }\n }\n }\n }\n }\n }\n }\n }\n if (routeExportHandled) {\n break\n }\n }\n\n if (!routeExportHandled) {\n return {\n result: 'no-route-export',\n }\n }\n\n const imports: {\n required: Array<ImportDeclaration>\n banned: Array<ImportDeclaration>\n } = {\n required: [],\n banned: [],\n }\n\n const targetModule = `@tanstack/${ctx.target}-router`\n if (ctx.verboseFileRoutes === false) {\n imports.banned = [\n {\n source: targetModule,\n specifiers: [\n { imported: 'createLazyFileRoute' },\n { imported: 'createFileRoute' },\n ],\n },\n ]\n } else {\n if (ctx.lazy) {\n imports.required = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createLazyFileRoute' }],\n },\n ]\n imports.banned = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createFileRoute' }],\n },\n ]\n } else {\n imports.required = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createFileRoute' }],\n },\n ]\n imports.banned = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createLazyFileRoute' }],\n },\n ]\n }\n }\n\n imports.required = mergeImportDeclarations(imports.required)\n imports.banned = mergeImportDeclarations(imports.banned)\n\n const importStatementCandidates: Array<types.namedTypes.ImportDeclaration> =\n []\n const importDeclarationsToRemove: Array<types.namedTypes.ImportDeclaration> =\n []\n\n // second pass: apply import rules, but only if a matching export for the plugin was found\n for (const n of program.body) {\n const findImport =\n (opts: { source: string; importKind?: 'type' | 'value' | 'typeof' }) =>\n (i: ImportDeclaration) => {\n if (i.source === opts.source) {\n const importKind = i.importKind || 'value'\n const expectedImportKind = opts.importKind || 'value'\n return expectedImportKind === importKind\n }\n return false\n }\n if (n.type === 'ImportDeclaration' && typeof n.source.value === 'string') {\n const filterImport = findImport({\n source: n.source.value,\n importKind: n.importKind,\n })\n let requiredImports = imports.required.filter(filterImport)[0]\n\n const bannedImports = imports.banned.filter(filterImport)[0]\n if (!requiredImports && !bannedImports) {\n continue\n }\n const importSpecifiersToRemove: types.namedTypes.ImportDeclaration['specifiers'] =\n []\n if (n.specifiers) {\n for (const spec of n.specifiers) {\n if (!requiredImports && !bannedImports) {\n break\n }\n if (\n spec.type === 'ImportSpecifier' &&\n typeof spec.imported.name === 'string'\n ) {\n if (requiredImports) {\n const requiredImportIndex = requiredImports.specifiers.findIndex(\n (imp) => imp.imported === spec.imported.name,\n )\n if (requiredImportIndex !== -1) {\n // import is already present, remove it from requiredImports\n requiredImports.specifiers.splice(requiredImportIndex, 1)\n if (requiredImports.specifiers.length === 0) {\n imports.required = imports.required.splice(\n imports.required.indexOf(requiredImports),\n 1,\n )\n requiredImports = undefined\n }\n } else {\n // add the import statement to the candidates\n importStatementCandidates.push(n)\n }\n }\n if (bannedImports) {\n const bannedImportIndex = bannedImports.specifiers.findIndex(\n (imp) => imp.imported === spec.imported.name,\n )\n if (bannedImportIndex !== -1) {\n importSpecifiersToRemove.push(spec)\n }\n }\n }\n }\n if (importSpecifiersToRemove.length > 0) {\n appliedChanges = true\n n.specifiers = n.specifiers.filter(\n (spec) => !importSpecifiersToRemove.includes(spec),\n )\n\n // mark the import statement as to be deleted if it is now empty\n if (n.specifiers.length === 0) {\n importDeclarationsToRemove.push(n)\n }\n }\n }\n }\n }\n imports.required.forEach((requiredImport) => {\n if (requiredImport.specifiers.length > 0) {\n appliedChanges = true\n if (importStatementCandidates.length > 0) {\n // find the first import statement that matches both the module and the import kind\n const importStatement = importStatementCandidates.find(\n (importStatement) => {\n if (importStatement.source.value === requiredImport.source) {\n const importKind = importStatement.importKind || 'value'\n const requiredImportKind = requiredImport.importKind || 'value'\n return importKind === requiredImportKind\n }\n return false\n },\n )\n if (importStatement) {\n if (importStatement.specifiers === undefined) {\n importStatement.specifiers = []\n }\n const importSpecifiersToAdd = requiredImport.specifiers.map((spec) =>\n b.importSpecifier(\n b.identifier(spec.imported),\n b.identifier(spec.imported),\n ),\n )\n importStatement.specifiers = [\n ...importStatement.specifiers,\n ...importSpecifiersToAdd,\n ]\n return\n }\n }\n const importStatement = b.importDeclaration(\n requiredImport.specifiers.map((spec) =>\n b.importSpecifier(\n b.identifier(spec.imported),\n spec.local ? b.identifier(spec.local) : null,\n ),\n ),\n b.stringLiteral(requiredImport.source),\n )\n program.body.unshift(importStatement)\n }\n })\n if (importDeclarationsToRemove.length > 0) {\n appliedChanges = true\n for (const importDeclaration of importDeclarationsToRemove) {\n // check if the import declaration is still empty\n if (importDeclaration.specifiers?.length === 0) {\n const index = program.body.indexOf(importDeclaration)\n if (index !== -1) {\n program.body.splice(index, 1)\n }\n }\n }\n }\n\n if (!appliedChanges) {\n return {\n result: 'not-modified',\n }\n }\n\n const printResult = print(ast, {\n reuseWhitespace: true,\n sourceMapName: 'output.map',\n })\n let transformedCode = printResult.code\n if (printResult.map) {\n const fixedOutput = await fixTransformedOutputText({\n originalCode: source,\n transformedCode,\n sourceMap: printResult.map as RawSourceMap,\n preferredQuote,\n })\n transformedCode = fixedOutput\n }\n return {\n result: 'modified',\n output: transformedCode,\n }\n}\n\nasync function fixTransformedOutputText({\n originalCode,\n transformedCode,\n sourceMap,\n preferredQuote,\n}: {\n originalCode: string\n transformedCode: string\n sourceMap: RawSourceMap\n preferredQuote: '\"' | \"'\"\n}) {\n const originalLines = originalCode.split('\\n')\n const transformedLines = transformedCode.split('\\n')\n\n const defaultUsesSemicolons = detectSemicolonUsage(originalCode)\n\n const consumer = await new SourceMapConsumer(sourceMap)\n\n const fixedLines = transformedLines.map((line, i) => {\n const transformedLineNum = i + 1\n\n let origLineText: string | undefined = undefined\n\n for (let col = 0; col < line.length; col++) {\n const mapped = consumer.originalPositionFor({\n line: transformedLineNum,\n column: col,\n })\n if (mapped.line != null && mapped.line > 0) {\n origLineText = originalLines[mapped.line - 1]\n break\n }\n }\n\n if (origLineText !== undefined) {\n if (origLineText === line) {\n return origLineText\n }\n return fixLine(line, {\n originalLine: origLineText,\n useOriginalSemicolon: true,\n useOriginalQuotes: true,\n fallbackQuote: preferredQuote,\n })\n } else {\n return fixLine(line, {\n originalLine: null,\n useOriginalSemicolon: false,\n useOriginalQuotes: false,\n fallbackQuote: preferredQuote,\n fallbackSemicolon: defaultUsesSemicolons,\n })\n }\n })\n\n return fixedLines.join('\\n')\n}\n\nfunction fixLine(\n line: string,\n {\n originalLine,\n useOriginalSemicolon,\n useOriginalQuotes,\n fallbackQuote,\n fallbackSemicolon = true,\n }: {\n originalLine: string | null\n useOriginalSemicolon: boolean\n useOriginalQuotes: boolean\n fallbackQuote: string\n fallbackSemicolon?: boolean\n },\n) {\n let result = line\n\n if (useOriginalQuotes && originalLine) {\n result = fixQuotes(result, originalLine, fallbackQuote)\n } else if (!useOriginalQuotes && fallbackQuote) {\n result = fixQuotesToPreferred(result, fallbackQuote)\n }\n\n if (useOriginalSemicolon && originalLine) {\n const hadSemicolon = originalLine.trimEnd().endsWith(';')\n const hasSemicolon = result.trimEnd().endsWith(';')\n if (hadSemicolon && !hasSemicolon) result += ';'\n if (!hadSemicolon && hasSemicolon) result = result.replace(/;\\s*$/, '')\n } else if (!useOriginalSemicolon) {\n const hasSemicolon = result.trimEnd().endsWith(';')\n if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\\s*$/, '')\n if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ';'\n }\n\n return result\n}\n\nfunction fixQuotes(line: string, originalLine: string, fallbackQuote: string) {\n let originalQuote = detectQuoteFromLine(originalLine)\n if (!originalQuote) {\n originalQuote = fallbackQuote\n }\n return fixQuotesToPreferred(line, originalQuote)\n}\n\nfunction fixQuotesToPreferred(line: string, quote: string) {\n // Replace existing quotes with preferred quote\n return line.replace(\n /(['\"`])([^'\"`\\\\]*(?:\\\\.[^'\"`\\\\]*)*)\\1/g,\n (_, q, content) => {\n const escaped = content.replaceAll(quote, `\\\\${quote}`)\n return `${quote}${escaped}${quote}`\n },\n )\n}\n\nfunction detectQuoteFromLine(line: string) {\n const match = line.match(/(['\"`])(?:\\\\.|[^\\\\])*?\\1/)\n return match ? match[1] : null\n}\n\nfunction detectSemicolonUsage(code: string) {\n const lines = code.split('\\n').map((l) => l.trim())\n const total = lines.length\n const withSemis = lines.filter((l) => l.endsWith(';')).length\n return withSemis > total / 2\n}\n\nexport function detectPreferredQuoteStyle(ast: types.ASTNode): \"'\" | '\"' {\n let single = 0\n let double = 0\n\n visit(ast, {\n visitStringLiteral(path) {\n if (path.parent.node.type !== 'JSXAttribute') {\n const raw = path.node.extra?.raw\n if (raw?.startsWith(\"'\")) single++\n else if (raw?.startsWith('\"')) double++\n }\n return false\n },\n })\n\n if (single >= double) {\n return \"'\"\n }\n return '\"'\n}\n"],"mappings":"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"}
{"version":3,"file":"transform.cjs","names":[],"sources":["../../../src/transform/transform.ts"],"sourcesContent":["import MagicString from 'magic-string'\nimport * as t from '@babel/types'\nimport { parseAst } from '@tanstack/router-utils'\nimport type { TransformOptions, TransformResult } from './types'\n\nconst routeConstructors = ['createFileRoute', 'createLazyFileRoute'] as const\n\ntype RouteConstructorName = (typeof routeConstructors)[number]\ntype SupportedRouteId = t.StringLiteral | t.TemplateLiteral\ntype NamedImport = {\n imported: string\n local: string\n importKind?: 'type' | 'typeof' | 'value'\n}\n\ntype ParsedImportDeclaration = {\n declaration: t.ImportDeclaration\n defaultImport?: string\n namespace?: string\n named: Array<NamedImport>\n moduleName: string\n quote: '\"' | \"'\"\n semicolon: boolean\n}\n\ntype RouteImportAnalysis =\n | { kind: 'ok' }\n | {\n kind: 'rename'\n imported: t.Identifier\n local: t.Identifier\n next: RouteConstructorName\n }\n | { kind: 'normalize' }\n\ntype RouteCall = {\n callee: t.Identifier & { name: RouteConstructorName }\n routeIdArg: SupportedRouteId\n optionsArg: t.CallExpression['arguments'][number] | undefined\n}\n\ntype RouteCallAnalysis = {\n calls: Array<RouteCall>\n hasUnsupportedRouteId: boolean\n hasMalformedRouteCall: boolean\n}\n\nexport function transform({\n ctx,\n source,\n node,\n}: TransformOptions): TransformResult {\n let ast: ReturnType<typeof parseAst>\n\n try {\n ast = parseAst({ code: source })\n } catch (error) {\n return {\n result: 'error',\n error,\n }\n }\n\n const exportedRouteNames = getExportedRouteNames(ast.program.body)\n\n if (exportedRouteNames.size === 0) {\n return { result: 'no-route-export' }\n }\n\n const {\n calls: routeCalls,\n hasUnsupportedRouteId,\n hasMalformedRouteCall,\n } = findExportedRouteCalls(ast.program.body, exportedRouteNames)\n\n if (routeCalls.length === 0 && hasMalformedRouteCall) {\n return {\n result: 'error',\n error: new Error(\n `expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`,\n ),\n }\n }\n\n if (routeCalls.length === 0 && hasUnsupportedRouteId) {\n return {\n result: 'error',\n error: new Error(\n `expected route id to be a string literal or plain template literal in ${ctx.routeId}`,\n ),\n }\n }\n\n if (routeCalls.length === 0) {\n return { result: 'not-modified' }\n }\n\n if (routeCalls.length > 1) {\n return {\n result: 'error',\n error: new Error(\n `expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`,\n ),\n }\n }\n\n const routeCall = routeCalls[0]!\n const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg)\n\n const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg)\n if (createFileRouteProps) {\n node.createFileRouteProps = createFileRouteProps\n }\n\n const expectedCallee = getExpectedRouteConstructor(ctx.lazy)\n const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`\n const currentRouteId = source.slice(\n routeCall.routeIdArg.start!,\n routeCall.routeIdArg.end!,\n )\n const targetModule = `@tanstack/${ctx.target}-router`\n const imports = parseTargetImports(ast.program.body, source, targetModule)\n\n const s = new MagicString(source)\n let modified = false\n\n if (routeCall.callee.name !== expectedCallee) {\n s.update(routeCall.callee.start!, routeCall.callee.end!, expectedCallee)\n modified = true\n }\n\n if (currentRouteId !== expectedRouteId) {\n s.update(\n routeCall.routeIdArg.start!,\n routeCall.routeIdArg.end!,\n expectedRouteId,\n )\n modified = true\n }\n\n if (\n updateRouteImports({\n imports,\n source,\n s,\n targetModule,\n required: expectedCallee,\n lineEnding: getLineEnding(source),\n })\n ) {\n modified = true\n }\n\n if (!modified) {\n return { result: 'not-modified' }\n }\n\n return {\n result: 'modified',\n output: s.toString(),\n }\n}\n\nfunction getExportedRouteNames(body: Array<t.Statement>) {\n const exportedRouteNames = new Set<string>()\n\n for (const statement of body) {\n if (!t.isExportNamedDeclaration(statement) || statement.source) {\n continue\n }\n\n if (t.isVariableDeclaration(statement.declaration)) {\n for (const declarator of statement.declaration.declarations) {\n if (t.isIdentifier(declarator.id) && declarator.id.name === 'Route') {\n exportedRouteNames.add('Route')\n }\n }\n }\n\n for (const specifier of statement.specifiers) {\n if (\n !t.isExportSpecifier(specifier) ||\n getExportedName(specifier.exported) !== 'Route'\n ) {\n continue\n }\n\n const localName = getLocalBindingName(specifier.local)\n if (localName) {\n exportedRouteNames.add(localName)\n }\n }\n }\n\n return exportedRouteNames\n}\n\nfunction findExportedRouteCalls(\n body: Array<t.Statement>,\n exportedRouteNames: Set<string>,\n): RouteCallAnalysis {\n const calls: Array<RouteCall> = []\n let hasUnsupportedRouteId = false\n let hasMalformedRouteCall = false\n\n for (const statement of body) {\n const declaration = getVariableDeclaration(statement)\n if (!declaration) {\n continue\n }\n\n for (const declarator of declaration.declarations) {\n if (\n !t.isIdentifier(declarator.id) ||\n !exportedRouteNames.has(declarator.id.name)\n ) {\n continue\n }\n\n const init = getRouteConstructorInit(declarator.init)\n if (!init) {\n if (isDirectRouteConstructorCall(declarator.init)) {\n hasMalformedRouteCall = true\n }\n continue\n }\n\n const routeIdArg = init.innerCall.arguments[0]\n if (isSupportedRouteId(routeIdArg)) {\n calls.push({\n callee: init.callee,\n routeIdArg,\n optionsArg: init.outerCall.arguments[0],\n })\n } else {\n hasUnsupportedRouteId = true\n }\n }\n }\n\n return { calls, hasUnsupportedRouteId, hasMalformedRouteCall }\n}\n\nfunction getVariableDeclaration(statement: t.Statement) {\n const declaration = t.isExportNamedDeclaration(statement)\n ? statement.declaration\n : statement\n\n return t.isVariableDeclaration(declaration) ? declaration : null\n}\n\nfunction getExportedName(node: t.Identifier | t.StringLiteral) {\n return t.isIdentifier(node) ? node.name : node.value\n}\n\nfunction getLocalBindingName(node: t.Identifier | t.StringLiteral) {\n return t.isIdentifier(node) ? node.name : null\n}\n\nfunction getRouteConstructorInit(expression: t.Expression | null | undefined) {\n if (!expression || !t.isCallExpression(expression)) {\n return null\n }\n\n if (!t.isCallExpression(expression.callee)) {\n return null\n }\n\n const innerCall = expression.callee\n\n if (\n !t.isIdentifier(innerCall.callee) ||\n !isRouteConstructor(innerCall.callee)\n ) {\n return null\n }\n\n return {\n callee: innerCall.callee,\n outerCall: expression,\n innerCall,\n }\n}\n\nfunction isDirectRouteConstructorCall(\n expression: t.Expression | null | undefined,\n) {\n return (\n !!expression &&\n t.isCallExpression(expression) &&\n t.isIdentifier(expression.callee) &&\n isRouteConstructor(expression.callee)\n )\n}\n\nfunction isRouteConstructor(callee: t.Identifier): callee is t.Identifier & {\n name: RouteConstructorName\n} {\n return routeConstructors.includes(callee.name as RouteConstructorName)\n}\n\nfunction isSupportedRouteId(\n arg: t.CallExpression['arguments'][number] | undefined,\n): arg is SupportedRouteId {\n return (\n !!arg &&\n (t.isStringLiteral(arg) ||\n (t.isTemplateLiteral(arg) && arg.expressions.length === 0))\n )\n}\n\nfunction getRouteIdQuote(\n source: string,\n arg: SupportedRouteId,\n): '\"' | \"'\" | '`' {\n const raw = source.slice(arg.start!, arg.end!)\n\n if (raw.startsWith(\"'\")) return \"'\"\n if (raw.startsWith('\"')) return '\"'\n return '`'\n}\n\nfunction getCreateFileRouteProps(\n arg: t.CallExpression['arguments'][number] | undefined,\n) {\n if (!arg || !t.isObjectExpression(arg)) {\n return undefined\n }\n\n const props = new Set<string>()\n\n for (const property of arg.properties) {\n if (!t.isObjectProperty(property) || property.computed) {\n continue\n }\n\n if (t.isIdentifier(property.key)) {\n props.add(property.key.name)\n continue\n }\n\n if (t.isStringLiteral(property.key)) {\n props.add(property.key.value)\n }\n }\n\n return props\n}\n\nfunction parseTargetImports(\n body: Array<t.Statement>,\n source: string,\n targetModule: string,\n) {\n const imports: Array<ParsedImportDeclaration> = []\n\n for (const statement of body) {\n if (\n !t.isImportDeclaration(statement) ||\n statement.importKind === 'type' ||\n statement.source.value !== targetModule\n ) {\n continue\n }\n\n const rawSource = source.slice(\n statement.source.start!,\n statement.source.end!,\n )\n const importStatement = source.slice(statement.start!, statement.end!)\n\n imports.push({\n declaration: statement,\n defaultImport: statement.specifiers.find((specifier) =>\n t.isImportDefaultSpecifier(specifier),\n )?.local.name,\n namespace: statement.specifiers.find((specifier) =>\n t.isImportNamespaceSpecifier(specifier),\n )?.local.name,\n named: statement.specifiers\n .filter((specifier): specifier is t.ImportSpecifier =>\n t.isImportSpecifier(specifier),\n )\n .map((specifier) => ({\n imported: t.isIdentifier(specifier.imported)\n ? specifier.imported.name\n : specifier.imported.value,\n local: specifier.local.name,\n importKind: specifier.importKind ?? undefined,\n })),\n moduleName: statement.source.value,\n quote: rawSource[0] as '\"' | \"'\",\n semicolon: importStatement.trimEnd().endsWith(';'),\n })\n }\n\n return imports\n}\n\nfunction updateRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n}: {\n imports: Array<ParsedImportDeclaration>\n source: string\n s: MagicString\n targetModule: string\n required: RouteConstructorName\n lineEnding: '\\r\\n' | '\\n' | '\\r'\n}) {\n const analysis = analyzeRouteImports(imports, required)\n\n if (analysis.kind === 'ok') {\n return false\n }\n\n if (analysis.kind === 'rename') {\n s.update(analysis.imported.start!, analysis.imported.end!, analysis.next)\n s.update(analysis.local.start!, analysis.local.end!, analysis.next)\n return true\n }\n\n return normalizeRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n })\n}\n\nfunction analyzeRouteImports(\n imports: Array<ParsedImportDeclaration>,\n required: RouteConstructorName,\n): RouteImportAnalysis {\n const opposite = getOtherRouteConstructor(required)\n let requiredCount = 0\n let oppositeCount = 0\n let renameCandidate:\n | {\n imported: t.Identifier\n local: t.Identifier\n next: RouteConstructorName\n }\n | undefined\n\n for (const declaration of imports) {\n for (const specifier of declaration.declaration.specifiers) {\n if (!t.isImportSpecifier(specifier)) {\n continue\n }\n\n const imported = specifier.imported\n if (!t.isIdentifier(imported)) {\n return { kind: 'normalize' }\n }\n\n if (!isRouteConstructorName(imported.name)) {\n continue\n }\n\n if (specifier.local.name !== imported.name) {\n return { kind: 'normalize' }\n }\n\n if (imported.name === required) {\n requiredCount++\n continue\n }\n\n if (imported.name === opposite) {\n oppositeCount++\n renameCandidate = {\n imported,\n local: specifier.local,\n next: required,\n }\n }\n }\n }\n\n if (requiredCount === 1 && oppositeCount === 0) {\n return { kind: 'ok' }\n }\n\n if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) {\n return {\n kind: 'rename',\n ...renameCandidate,\n }\n }\n\n return { kind: 'normalize' }\n}\n\nfunction normalizeRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n}: {\n imports: Array<ParsedImportDeclaration>\n source: string\n s: MagicString\n targetModule: string\n required: RouteConstructorName\n lineEnding: '\\r\\n' | '\\n' | '\\r'\n}) {\n const owner =\n imports.find((declaration) =>\n hasNamedImport(declaration.named, required),\n ) ?? imports.find((declaration) => !declaration.namespace)\n\n let modified = false\n\n for (const declaration of imports) {\n const named = normalizeNamedImports({\n named: declaration.named,\n required,\n isOwner: declaration === owner,\n })\n\n if (sameNamedImports(declaration.named, named)) {\n continue\n }\n\n const replacement = renderImportDeclaration({\n ...declaration,\n named,\n })\n\n if (replacement === null) {\n s.remove(\n declaration.declaration.start!,\n getRemovalEnd(source, declaration.declaration.end!),\n )\n modified = true\n continue\n }\n\n s.update(\n declaration.declaration.start!,\n declaration.declaration.end!,\n replacement,\n )\n modified = true\n }\n\n if (!owner) {\n const quote = imports[0]?.quote ?? \"'\"\n const semicolon = imports[0]?.semicolon ?? false\n s.prepend(\n `import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ';' : ''}${lineEnding}`,\n )\n modified = true\n }\n\n return modified\n}\n\nfunction normalizeNamedImports({\n named,\n required,\n isOwner,\n}: {\n named: Array<NamedImport>\n required: RouteConstructorName\n isOwner: boolean\n}) {\n const banned = getOtherRouteConstructor(required)\n const nextNamed: Array<NamedImport> = []\n const seen = new Set<string>()\n\n for (const specifier of named) {\n if (specifier.imported === banned) {\n continue\n }\n\n if (\n specifier.local === required &&\n (specifier.imported !== required || !isOwner)\n ) {\n continue\n }\n\n const key = `${specifier.importKind ?? 'value'}:${specifier.imported}:${specifier.local}`\n if (seen.has(key)) {\n continue\n }\n\n seen.add(key)\n nextNamed.push(specifier)\n }\n\n if (isOwner && !hasNamedImport(nextNamed, required)) {\n nextNamed.push({ imported: required, local: required })\n }\n\n return nextNamed\n}\n\nfunction getExpectedRouteConstructor(lazy: boolean): RouteConstructorName {\n return lazy ? 'createLazyFileRoute' : 'createFileRoute'\n}\n\nfunction getOtherRouteConstructor(\n constructor: RouteConstructorName,\n): RouteConstructorName {\n return constructor === 'createFileRoute'\n ? 'createLazyFileRoute'\n : 'createFileRoute'\n}\n\nfunction hasNamedImport(\n named: Array<NamedImport>,\n required: RouteConstructorName,\n) {\n return named.some(\n (specifier) =>\n specifier.imported === required &&\n specifier.local === required &&\n specifier.importKind !== 'type',\n )\n}\n\nfunction sameNamedImports(left: Array<NamedImport>, right: Array<NamedImport>) {\n return (\n left.length === right.length &&\n left.every(\n (specifier, index) =>\n specifier.imported === right[index]!.imported &&\n specifier.local === right[index]!.local &&\n specifier.importKind === right[index]!.importKind,\n )\n )\n}\n\nfunction isRouteConstructorName(value: string): value is RouteConstructorName {\n return routeConstructors.includes(value as RouteConstructorName)\n}\n\nfunction renderImportDeclaration(importDeclaration: {\n defaultImport?: string\n namespace?: string\n named: Array<NamedImport>\n moduleName: string\n quote: '\"' | \"'\"\n semicolon: boolean\n}) {\n const parts: Array<string> = []\n\n if (importDeclaration.defaultImport) {\n parts.push(importDeclaration.defaultImport)\n }\n\n if (importDeclaration.namespace) {\n parts.push(`* as ${importDeclaration.namespace}`)\n }\n\n if (importDeclaration.named.length > 0) {\n parts.push(\n `{ ${importDeclaration.named\n .map(\n (specifier) =>\n `${specifier.importKind === 'type' ? 'type ' : ''}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`,\n )\n .join(', ')} }`,\n )\n }\n\n if (parts.length === 0) {\n return null\n }\n\n return `import ${parts.join(', ')} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ';' : ''}`\n}\n\nfunction getLineEnding(source: string): '\\r\\n' | '\\n' | '\\r' {\n if (source.includes('\\r\\n')) {\n return '\\r\\n'\n }\n\n if (source.includes('\\n')) {\n return '\\n'\n }\n\n if (source.includes('\\r')) {\n return '\\r'\n }\n\n return '\\n'\n}\n\nfunction getRemovalEnd(source: string, end: number) {\n let pos = end\n while (pos < source.length && (source[pos] === ' ' || source[pos] === '\\t')) {\n pos++\n }\n\n if (source[pos] === '\\r' && source[pos + 1] === '\\n') {\n return pos + 2\n }\n\n if (source[pos] === '\\n' || source[pos] === '\\r') {\n return pos + 1\n }\n\n return end\n}\n"],"mappings":"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"}

@@ -1,4 +0,2 @@

import { types } from 'recast';
import { TransformOptions, TransformResult } from './types.cjs';
export declare function transform({ ctx, source, node, }: TransformOptions): Promise<TransformResult>;
export declare function detectPreferredQuoteStyle(ast: types.ASTNode): "'" | '"';
export declare function transform({ ctx, source, node, }: TransformOptions): TransformResult;

@@ -1,2 +0,2 @@

import { ImportDeclaration, RouteNode } from '../types.cjs';
import { RouteNode } from '../types.cjs';
import { Config } from '../config.cjs';

@@ -19,6 +19,2 @@ export interface TransformOptions {

};
export interface TransformImportsConfig {
banned?: Array<ImportDeclaration>;
required?: Array<ImportDeclaration>;
}
export interface TransformContext {

@@ -28,4 +24,2 @@ target: Config['target'];

lazy: boolean;
verboseFileRoutes: boolean;
preferredQuote?: '"' | "'";
}

@@ -18,3 +18,2 @@ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");

this.prefixToRoute = /* @__PURE__ */ new Map();
this.layoutRoutes = [];
for (const route of routes) {

@@ -24,5 +23,3 @@ if (!route.routePath || route.routePath === `/__root`) continue;

this.prefixToRoute.set(route.routePath, route);
if (route._fsRouteType === "pathless_layout" || route._fsRouteType === "layout" || route._fsRouteType === "__root") this.layoutRoutes.push(route);
}
this.layoutRoutes.sort((a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0));
}

@@ -409,13 +406,2 @@ /**

/**
* Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
* Also asserts that the RouteNode is defined.
*
* @param routeNode - The RouteNode to check.
* @returns A boolean indicating whether the RouteNode is defined.
*/
function isRouteNodeValidForAugmentation(routeNode) {
if (!routeNode || routeNode.isVirtual) return false;
return true;
}
/**
* Infers the path for use by TS

@@ -632,3 +618,2 @@ */

exports.getImportForRouteNode = getImportForRouteNode;
exports.getImportPath = getImportPath;
exports.getResolvedRouteNodeVariableName = getResolvedRouteNodeVariableName;

@@ -638,3 +623,2 @@ exports.hasEscapedLeadingUnderscore = hasEscapedLeadingUnderscore;

exports.inferFullPath = inferFullPath;
exports.isRouteNodeValidForAugmentation = isRouteNodeValidForAugmentation;
exports.isSegmentPathless = isSegmentPathless;

@@ -641,0 +625,0 @@ exports.mergeImportDeclarations = mergeImportDeclarations;

@@ -1,1 +0,1 @@

{"version":3,"file":"utils.cjs","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n private layoutRoutes: Array<RouteNode> = []\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n\n if (\n route._fsRouteType === 'pathless_layout' ||\n route._fsRouteType === 'layout' ||\n route._fsRouteType === '__root'\n ) {\n this.layoutRoutes.push(route)\n }\n }\n\n // Sort by path length descending for longest-match-first\n this.layoutRoutes.sort(\n (a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),\n )\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nexport function isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"mappings":"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"}
{"version":3,"file":"utils.cjs","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n }\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nfunction isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Infers the path for use by TS\n */\nconst inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nconst inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nconst dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nfunction getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"mappings":"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"}

@@ -10,3 +10,2 @@ import { Config, TokenMatcher } from './config.cjs';

private prefixToRoute;
private layoutRoutes;
constructor(routes: Array<RouteNode>);

@@ -84,3 +83,2 @@ /**

}): RegExp;
export declare function isBracketWrappedSegment(segment: string): boolean;
export declare function unwrapBracketWrappedSegment(segment: string): string;

@@ -166,14 +164,2 @@ export declare function removeLeadingUnderscores(s: string, routeToken: string): string;

/**
* Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
* Also asserts that the RouteNode is defined.
*
* @param routeNode - The RouteNode to check.
* @returns A boolean indicating whether the RouteNode is defined.
*/
export declare function isRouteNodeValidForAugmentation(routeNode?: RouteNode): routeNode is RouteNode;
/**
* Infers the path for use by TS
*/
export declare const inferPath: (routeNode: RouteNode) => string;
/**
* Infers the full path for use by TS

@@ -194,14 +180,5 @@ */

export declare const createRouteNodesById: (routeNodes: Array<RouteNode>) => Map<string, RouteNode>;
/**
* Infers to path
*/
export declare const inferTo: (routeNode: RouteNode) => string;
/**
* Dedupes branches and index routes
*/
export declare const dedupeBranchesAndIndexRoutes: (routes: Array<RouteNode>) => Array<RouteNode>;
export declare function checkRouteFullPathUniqueness(_routes: Array<RouteNode>, config: Config): void;
export declare function buildRouteTreeConfig(nodes: Array<RouteNode>, disableTypes: boolean, depth?: number): Array<string>;
export declare function buildImportString(importDeclaration: ImportDeclaration): string;
export declare function lowerCaseFirstChar(value: string): string;
export declare function mergeImportDeclarations(imports: Array<ImportDeclaration>): Array<ImportDeclaration>;

@@ -215,3 +192,2 @@ export declare const findParent: (node: RouteNode | undefined) => string;

}): string;
export declare function getImportPath(node: RouteNode, config: Config, generatedRouteTreePath: string): string;
export declare function getImportForRouteNode(node: RouteNode, config: Config, generatedRouteTreePath: string, root: string): ImportDeclaration;

@@ -134,3 +134,2 @@ import { z } from 'zod';

disableTypes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
verboseFileRoutes: z.ZodOptional<z.ZodBoolean>;
addExtensions: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>>, string | boolean, string | boolean | undefined>;

@@ -187,3 +186,2 @@ enableRouteTreeFormatting: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;

pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
verboseFileRoutes?: boolean | undefined;
routeTreeFileFooter?: string[] | ((...args: unknown[]) => string[]) | undefined;

@@ -221,3 +219,2 @@ autoCodeSplitting?: boolean | undefined;

disableTypes?: boolean | undefined;
verboseFileRoutes?: boolean | undefined;
addExtensions?: string | boolean | undefined;

@@ -224,0 +221,0 @@ enableRouteTreeFormatting?: boolean | undefined;

@@ -50,3 +50,2 @@ import { virtualRootRouteSchema } from "./filesystem/virtual/config.js";

disableTypes: z.boolean().optional().default(false),
verboseFileRoutes: z.boolean().optional(),
addExtensions: z.union([z.boolean(), z.string()]).optional().default(false).transform((v) => typeof v === "string" ? v.startsWith(".") ? v : `.${v}` : v),

@@ -53,0 +52,0 @@ enableRouteTreeFormatting: z.boolean().optional().default(true),

@@ -1,1 +0,1 @@

{"version":3,"file":"config.js","names":[],"sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nconst tokenJsonRegexSchema = z.object({\n regex: z.string(),\n flags: z.string().optional(),\n})\n\nconst tokenMatcherSchema = z.union([\n z.string(),\n z.instanceof(RegExp),\n tokenJsonRegexSchema,\n])\n\nexport type TokenMatcherJson = string | z.infer<typeof tokenJsonRegexSchema>\n\nexport type TokenMatcher = z.infer<typeof tokenMatcherSchema>\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: tokenMatcherSchema.optional().default('index'),\n routeToken: tokenMatcherSchema.optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n verboseFileRoutes: z.boolean().optional(),\n addExtensions: z\n .union([z.boolean(), z.string()])\n .optional()\n .default(false)\n .transform((v) =>\n typeof v === 'string' ? (v.startsWith('.') ? v : `.${v}`) : v,\n ),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n // Parse file config (allows JSON regex-object form)\n const fileConfigRaw = JSON.parse(readFileSync(configFilePathJson, 'utf-8'))\n\n // Merge raw configs (inline overrides file), then parse once to apply defaults\n // This ensures file config values aren't overwritten by inline defaults\n const merged = {\n ...fileConfigRaw,\n ...inlineConfig,\n }\n config = configSchema.parse(merged)\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n // Check that indexToken and routeToken are not identical\n // Works for strings, RegExp, and JSON regex objects\n if (areTokensEqual(config.indexToken, config.routeToken)) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n\n/**\n * Compares two token matchers for equality.\n * Handles strings, RegExp instances, and JSON regex objects.\n */\nfunction areTokensEqual(a: TokenMatcher, b: TokenMatcher): boolean {\n // Both strings\n if (typeof a === 'string' && typeof b === 'string') {\n return a === b\n }\n\n // Both RegExp instances\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Both JSON regex objects\n if (\n typeof a === 'object' &&\n 'regex' in a &&\n typeof b === 'object' &&\n 'regex' in b\n ) {\n return a.regex === b.regex && (a.flags ?? '') === (b.flags ?? '')\n }\n\n // Mixed types - not equal\n return false\n}\n"],"mappings":"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"}
{"version":3,"file":"config.js","names":[],"sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nconst tokenJsonRegexSchema = z.object({\n regex: z.string(),\n flags: z.string().optional(),\n})\n\nconst tokenMatcherSchema = z.union([\n z.string(),\n z.instanceof(RegExp),\n tokenJsonRegexSchema,\n])\n\nexport type TokenMatcherJson = string | z.infer<typeof tokenJsonRegexSchema>\n\nexport type TokenMatcher = z.infer<typeof tokenMatcherSchema>\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: tokenMatcherSchema.optional().default('index'),\n routeToken: tokenMatcherSchema.optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n addExtensions: z\n .union([z.boolean(), z.string()])\n .optional()\n .default(false)\n .transform((v) =>\n typeof v === 'string' ? (v.startsWith('.') ? v : `.${v}`) : v,\n ),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n // Parse file config (allows JSON regex-object form)\n const fileConfigRaw = JSON.parse(readFileSync(configFilePathJson, 'utf-8'))\n\n // Merge raw configs (inline overrides file), then parse once to apply defaults\n // This ensures file config values aren't overwritten by inline defaults\n const merged = {\n ...fileConfigRaw,\n ...inlineConfig,\n }\n config = configSchema.parse(merged)\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n // Check that indexToken and routeToken are not identical\n // Works for strings, RegExp, and JSON regex objects\n if (areTokensEqual(config.indexToken, config.routeToken)) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n\n/**\n * Compares two token matchers for equality.\n * Handles strings, RegExp instances, and JSON regex objects.\n */\nfunction areTokensEqual(a: TokenMatcher, b: TokenMatcher): boolean {\n // Both strings\n if (typeof a === 'string' && typeof b === 'string') {\n return a === b\n }\n\n // Both RegExp instances\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Both JSON regex objects\n if (\n typeof a === 'object' &&\n 'regex' in a &&\n typeof b === 'object' &&\n 'regex' in b\n ) {\n return a.regex === b.regex && (a.flags ?? '') === (b.flags ?? '')\n }\n\n // Mixed types - not equal\n return false\n}\n"],"mappings":"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"}
import { logging } from "./logger.js";
import { rootPathId } from "./filesystem/physical/rootPathId.js";
import { RoutePrefixMap, buildFileRoutesByPathInterface, buildImportString, buildRouteTreeConfig, checkFileExists, checkRouteFullPathUniqueness, createRouteNodesByFullPath, createRouteNodesById, createRouteNodesByTo, createTokenRegex, determineNodePath, findParent, format, getImportForRouteNode, getImportPath, getResolvedRouteNodeVariableName, hasParentRoute, isRouteNodeValidForAugmentation, isSegmentPathless, mergeImportDeclarations, multiSortBy, removeExt, removeGroups, removeLastSegmentFromPath, removeLayoutSegmentsWithEscape, removeTrailingSlash, removeUnderscoresWithEscape, replaceBackslash, trimPathLeft } from "./utils.js";
import { RoutePrefixMap, buildFileRoutesByPathInterface, buildImportString, buildRouteTreeConfig, checkFileExists, checkRouteFullPathUniqueness, createRouteNodesByFullPath, createRouteNodesById, createRouteNodesByTo, createTokenRegex, determineNodePath, findParent, format, getImportForRouteNode, getResolvedRouteNodeVariableName, hasParentRoute, isSegmentPathless, mergeImportDeclarations, multiSortBy, removeExt, removeGroups, removeLastSegmentFromPath, removeLayoutSegmentsWithEscape, removeTrailingSlash, removeUnderscoresWithEscape, replaceBackslash, trimPathLeft } from "./utils.js";
import { getRouteNodes } from "./filesystem/virtual/getRouteNodes.js";

@@ -312,24 +312,2 @@ import { getRouteNodes as getRouteNodes$1, isVirtualConfigFile } from "./filesystem/physical/getRouteNodes.js";

}
if (config.verboseFileRoutes === false) {
const typeImport = {
specifiers: [],
source: this.targetTemplate.fullPkg,
importKind: "type"
};
let needsCreateFileRoute = false;
let needsCreateLazyFileRoute = false;
for (const node of sortedRouteNodes) {
if (isRouteNodeValidForAugmentation(node)) {
if (node._fsRouteType !== "lazy") needsCreateFileRoute = true;
if (acc.routePiecesByPath[node.routePath]?.lazy) needsCreateLazyFileRoute = true;
}
if (needsCreateFileRoute && needsCreateLazyFileRoute) break;
}
if (needsCreateFileRoute) typeImport.specifiers.push({ imported: "CreateFileRoute" });
if (needsCreateLazyFileRoute) typeImport.specifiers.push({ imported: "CreateLazyFileRoute" });
if (typeImport.specifiers.length > 0) {
typeImport.specifiers.push({ imported: "FileRoutesByPath" });
imports.push(typeImport);
}
}
const routeTreeConfig = buildRouteTreeConfig(acc.routeTree, config.disableTypes);

@@ -442,21 +420,2 @@ const createUpdateRoutes = sortedRouteNodes.map((node) => {

const importStatements = mergedImports.map(buildImportString);
let moduleAugmentation = "";
if (config.verboseFileRoutes === false && !config.disableTypes) moduleAugmentation = opts.routeFileResult.map((node) => {
const getModuleDeclaration = (routeNode) => {
if (!isRouteNodeValidForAugmentation(routeNode)) return "";
let moduleAugmentation = "";
if (routeNode._fsRouteType === "lazy") moduleAugmentation = `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`;
else moduleAugmentation = `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',
FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
FileRoutesByPath['${routeNode.routePath}']['id'],
FileRoutesByPath['${routeNode.routePath}']['path'],
FileRoutesByPath['${routeNode.routePath}']['fullPath']
>
`;
return `declare module './${getImportPath(routeNode, config, this.generatedRouteTreePath)}' {
${moduleAugmentation}
}`;
};
return getModuleDeclaration(node);
}).join("\n");
const rootRouteImport = getImportForRouteNode(rootRouteNode, config, this.generatedRouteTreePath, this.root);

@@ -479,3 +438,2 @@ routeImports.unshift(rootRouteImport);

fileRoutesByPathInterface,
moduleAugmentation,
routeTreeConfig.join("\n"),

@@ -542,4 +500,3 @@ routeTree,

routeId: escapedRoutePath,
lazy: node._fsRouteType === "lazy",
verboseFileRoutes: !(this.config.verboseFileRoutes === false)
lazy: node._fsRouteType === "lazy"
},

@@ -546,0 +503,0 @@ node

@@ -11,3 +11,2 @@ export { configSchema, getConfig, resolveConfigPath, baseConfigSchema, } from './config.js';

export { rootPathId } from './filesystem/physical/rootPathId.js';
export { ensureStringArgument } from './transform/utils.js';
export type { TransformImportsConfig, TransformContext, TransformOptions, } from './transform/types.js';
export type { TransformContext, TransformOptions } from './transform/types.js';

@@ -6,4 +6,3 @@ import { baseConfigSchema, configSchema, getConfig, resolveConfigPath } from "./config.js";

import { getRouteNodes } from "./filesystem/physical/getRouteNodes.js";
import { ensureStringArgument } from "./transform/utils.js";
import { Generator } from "./generator.js";
export { Generator, baseConfigSchema, capitalize, checkRouteFullPathUniqueness, cleanPath, configSchema, determineInitialRoutePath, ensureStringArgument, format, getConfig, inferFullPath, multiSortBy, getRouteNodes as physicalGetRouteNodes, removeExt, removeLeadingSlash, removeTrailingSlash, removeUnderscores, replaceBackslash, resetRegex, resolveConfigPath, rootPathId, routePathToVariable, trimPathLeft, getRouteNodes$1 as virtualGetRouteNodes, writeIfDifferent };
export { Generator, baseConfigSchema, capitalize, checkRouteFullPathUniqueness, cleanPath, configSchema, determineInitialRoutePath, format, getConfig, inferFullPath, multiSortBy, getRouteNodes as physicalGetRouteNodes, removeExt, removeLeadingSlash, removeTrailingSlash, removeUnderscores, replaceBackslash, resetRegex, resolveConfigPath, rootPathId, routePathToVariable, trimPathLeft, getRouteNodes$1 as virtualGetRouteNodes, writeIfDifferent };

@@ -6,2 +6,5 @@ import { format } from "./utils.js";

}
function serializeRoutePath(routePath) {
return JSON.stringify(routePath);
}
function getTargetTemplate(config) {

@@ -35,4 +38,4 @@ const target = config.target;

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -49,4 +52,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -81,4 +84,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -95,4 +98,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -128,4 +131,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createFileRoute(" : `export const Route = createFileRoute('${routePath}')(`,
tsrImports: () => "import { createFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -143,4 +146,4 @@ }

imports: {
tsrImports: () => config.verboseFileRoutes === false ? "" : "import { createLazyFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => config.verboseFileRoutes === false ? "export const Route = createLazyFileRoute(" : `export const Route = createLazyFileRoute('${routePath}')(`,
tsrImports: () => "import { createLazyFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) => `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ");"

@@ -147,0 +150,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"file":"template.js","names":[],"sources":["../../src/template.ts"],"sourcesContent":["import { format } from './utils'\nimport type { Config } from './config'\n\ntype TemplateTag = 'tsrImports' | 'tsrPath' | 'tsrExportStart' | 'tsrExportEnd'\n\nexport function fillTemplate(\n config: Config,\n template: string,\n values: Record<TemplateTag, string>,\n) {\n const replaced = template.replace(\n /%%(\\w+)%%/g,\n (_, key) => values[key as TemplateTag] || '',\n )\n return format(replaced, config)\n}\n\nexport type TargetTemplate = {\n fullPkg: string\n subPkg: string\n rootRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: () => string\n tsrExportEnd: () => string\n }\n }\n route: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n lazyRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n}\n\nexport function getTargetTemplate(config: Config): TargetTemplate {\n const target = config.target\n switch (target) {\n case 'react':\n return {\n fullPkg: '@tanstack/react-router',\n subPkg: 'react-router',\n rootRoute: {\n template: () =>\n [\n 'import * as React from \"react\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<React.Fragment><div>Hello \"%%tsrPath%%\"!</div><Outlet /></React.Fragment>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/react-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'solid':\n return {\n fullPkg: '@tanstack/solid-router',\n subPkg: 'solid-router',\n rootRoute: {\n template: () =>\n [\n 'import * as Solid from \"solid-js\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<><div>Hello \"%%tsrPath%%\"!</div><Outlet /></>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/solid-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/solid-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/solid-router';\",\n\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'vue':\n return {\n fullPkg: '@tanstack/vue-router',\n subPkg: 'vue-router',\n rootRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return h(\"div\", {}, [\"Hello \\\\\"%%tsrPath%%\\\\\"!\", h(Outlet)]) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/vue-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createFileRoute } from '@tanstack/vue-router';\",\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createFileRoute('\n : `export const Route = createFileRoute('${routePath}')(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n config.verboseFileRoutes === false\n ? ''\n : \"import { createLazyFileRoute } from '@tanstack/vue-router';\",\n\n tsrExportStart: (routePath) =>\n config.verboseFileRoutes === false\n ? 'export const Route = createLazyFileRoute('\n : `export const Route = createLazyFileRoute('${routePath}')(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n default:\n throw new Error(`router-generator: Unknown target type: ${target}`)\n }\n}\n"],"mappings":";;AAKA,SAAgB,aACd,QACA,UACA,QACA;AAKA,QAAO,OAJU,SAAS,QACxB,eACC,GAAG,QAAQ,OAAO,QAAuB,GAC3C,EACuB,OAAO;;AAgCjC,SAAgB,kBAAkB,QAAgC;CAChE,MAAM,SAAS,OAAO;AACtB,SAAQ,QAAR;EACE,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAC7D,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KAEN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAE7D,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,MACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KACN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,0CACA,yCAAyC,UAAU;KACzD,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE,OAAO,sBAAsB,QACzB,KACA;KAEN,iBAAiB,cACf,OAAO,sBAAsB,QACzB,8CACA,6CAA6C,UAAU;KAE7D,oBAAoB;KACrB;IACF;GACF;EACH,QACE,OAAM,IAAI,MAAM,0CAA0C,SAAS"}
{"version":3,"file":"template.js","names":[],"sources":["../../src/template.ts"],"sourcesContent":["import { format } from './utils'\nimport type { Config } from './config'\n\ntype TemplateTag = 'tsrImports' | 'tsrPath' | 'tsrExportStart' | 'tsrExportEnd'\n\nexport function fillTemplate(\n config: Config,\n template: string,\n values: Record<TemplateTag, string>,\n) {\n const replaced = template.replace(\n /%%(\\w+)%%/g,\n (_, key) => values[key as TemplateTag] || '',\n )\n return format(replaced, config)\n}\n\nexport type TargetTemplate = {\n fullPkg: string\n subPkg: string\n rootRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: () => string\n tsrExportEnd: () => string\n }\n }\n route: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n lazyRoute: {\n template: () => string\n imports: {\n tsrImports: () => string\n tsrExportStart: (routePath: string) => string\n tsrExportEnd: () => string\n }\n }\n}\n\nfunction serializeRoutePath(routePath: string) {\n return JSON.stringify(routePath)\n}\n\nexport function getTargetTemplate(config: Config): TargetTemplate {\n const target = config.target\n switch (target) {\n case 'react':\n return {\n fullPkg: '@tanstack/react-router',\n subPkg: 'react-router',\n rootRoute: {\n template: () =>\n [\n 'import * as React from \"react\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<React.Fragment><div>Hello \"%%tsrPath%%\"!</div><Outlet /></React.Fragment>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/react-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/react-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'solid':\n return {\n fullPkg: '@tanstack/solid-router',\n subPkg: 'solid-router',\n rootRoute: {\n template: () =>\n [\n 'import * as Solid from \"solid-js\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return (<><div>Hello \"%%tsrPath%%\"!</div><Outlet /></>) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/solid-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/solid-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return <div>Hello \"%%tsrPath%%\"!</div> };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/solid-router';\",\n\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n case 'vue':\n return {\n fullPkg: '@tanstack/vue-router',\n subPkg: 'vue-router',\n rootRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RootComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RootComponent() { return h(\"div\", {}, [\"Hello \\\\\"%%tsrPath%%\\\\\"!\", h(Outlet)]) };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { Outlet, createRootRoute } from '@tanstack/vue-router';\",\n tsrExportStart: () => 'export const Route = createRootRoute(',\n tsrExportEnd: () => ');',\n },\n },\n route: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createFileRoute } from '@tanstack/vue-router';\",\n tsrExportStart: (routePath) =>\n `export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,\n tsrExportEnd: () => ');',\n },\n },\n lazyRoute: {\n template: () =>\n [\n 'import { h } from \"vue\"\\n',\n '%%tsrImports%%',\n '\\n\\n',\n '%%tsrExportStart%%{\\n component: RouteComponent\\n }%%tsrExportEnd%%\\n\\n',\n 'function RouteComponent() { return h(\"div\", {}, \"Hello \\\\\"%%tsrPath%%\\\\\"!\") };\\n',\n ].join(''),\n imports: {\n tsrImports: () =>\n \"import { createLazyFileRoute } from '@tanstack/vue-router';\",\n\n tsrExportStart: (routePath) =>\n `export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,\n\n tsrExportEnd: () => ');',\n },\n },\n }\n default:\n throw new Error(`router-generator: Unknown target type: ${target}`)\n }\n}\n"],"mappings":";;AAKA,SAAgB,aACd,QACA,UACA,QACA;AAKA,QAAO,OAJU,SAAS,QACxB,eACC,GAAG,QAAQ,OAAO,QAAuB,GAC3C,EACuB,OAAO;;AAgCjC,SAAS,mBAAmB,WAAmB;AAC7C,QAAO,KAAK,UAAU,UAAU;;AAGlC,SAAgB,kBAAkB,QAAgC;CAChE,MAAM,SAAS,OAAO;AACtB,SAAQ,QAAR;EACE,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAC5E,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,QACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KAEF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAE5E,oBAAoB;KACrB;IACF;GACF;EACH,KAAK,MACH,QAAO;GACL,SAAS;GACT,QAAQ;GACR,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,sBAAsB;KACtB,oBAAoB;KACrB;IACF;GACD,OAAO;IACL,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KACF,iBAAiB,cACf,wCAAwC,mBAAmB,UAAU,CAAC;KACxE,oBAAoB;KACrB;IACF;GACD,WAAW;IACT,gBACE;KACE;KACA;KACA;KACA;KACA;KACD,CAAC,KAAK,GAAG;IACZ,SAAS;KACP,kBACE;KAEF,iBAAiB,cACf,4CAA4C,mBAAmB,UAAU,CAAC;KAE5E,oBAAoB;KACrB;IACF;GACF;EACH,QACE,OAAM,IAAI,MAAM,0CAA0C,SAAS"}

@@ -1,4 +0,2 @@

import { types } from 'recast';
import { TransformOptions, TransformResult } from './types.js';
export declare function transform({ ctx, source, node, }: TransformOptions): Promise<TransformResult>;
export declare function detectPreferredQuoteStyle(ast: types.ASTNode): "'" | '"';
export declare function transform({ ctx, source, node, }: TransformOptions): TransformResult;

@@ -1,297 +0,312 @@

import { mergeImportDeclarations } from "../utils.js";
import { ensureStringArgument } from "./utils.js";
import MagicString from "magic-string";
import * as t from "@babel/types";
import { parseAst } from "@tanstack/router-utils";
import { parse, print, types, visit } from "recast";
import { SourceMapConsumer } from "source-map";
//#region src/transform/transform.ts
var b = types.builders;
async function transform({ ctx, source, node }) {
let appliedChanges = false;
var routeConstructors = ["createFileRoute", "createLazyFileRoute"];
function transform({ ctx, source, node }) {
let ast;
try {
ast = parse(source, {
sourceFileName: "output.ts",
parser: { parse(code) {
return parseAst({
code,
tokens: true
});
} }
});
} catch (e) {
console.error("Error parsing code", ctx.routeId, source, e);
ast = parseAst({ code: source });
} catch (error) {
return {
result: "error",
error: e
error
};
}
const preferredQuote = detectPreferredQuoteStyle(ast);
let routeExportHandled = false;
function onExportFound(decl) {
if (decl.init?.type === "CallExpression") {
const callExpression = decl.init;
const firstArgument = callExpression.arguments[0];
if (firstArgument) {
if (firstArgument.type === "ObjectExpression") {
const staticProperties = firstArgument.properties.flatMap((p) => {
if (p.type === "ObjectProperty" && p.key.type === "Identifier") return p.key.name;
return [];
});
node.createFileRouteProps = new Set(staticProperties);
}
}
let identifier;
if (callExpression.callee.type === "Identifier") {
identifier = callExpression.callee;
if (ctx.verboseFileRoutes) {
callExpression.callee = b.callExpression(identifier, [b.stringLiteral(ctx.routeId)]);
appliedChanges = true;
}
} else if (callExpression.callee.type === "CallExpression" && callExpression.callee.callee.type === "Identifier") {
identifier = callExpression.callee.callee;
if (!ctx.verboseFileRoutes) {
callExpression.callee = identifier;
appliedChanges = true;
} else appliedChanges = ensureStringArgument(callExpression.callee, ctx.routeId, ctx.preferredQuote);
}
if (identifier === void 0) throw new Error(`expected identifier to be present in ${ctx.routeId} for export "Route"`);
if (identifier.name === "createFileRoute" && ctx.lazy) {
identifier.name = "createLazyFileRoute";
appliedChanges = true;
} else if (identifier.name === "createLazyFileRoute" && !ctx.lazy) {
identifier.name = "createFileRoute";
appliedChanges = true;
}
} else throw new Error(`expected "Route" export to be initialized by a CallExpression`);
routeExportHandled = true;
const exportedRouteNames = getExportedRouteNames(ast.program.body);
if (exportedRouteNames.size === 0) return { result: "no-route-export" };
const { calls: routeCalls, hasUnsupportedRouteId, hasMalformedRouteCall } = findExportedRouteCalls(ast.program.body, exportedRouteNames);
if (routeCalls.length === 0 && hasMalformedRouteCall) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`)
};
if (routeCalls.length === 0 && hasUnsupportedRouteId) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected route id to be a string literal or plain template literal in ${ctx.routeId}`)
};
if (routeCalls.length === 0) return { result: "not-modified" };
if (routeCalls.length > 1) return {
result: "error",
error: /* @__PURE__ */ new Error(`expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`)
};
const routeCall = routeCalls[0];
const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg);
const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg);
if (createFileRouteProps) node.createFileRouteProps = createFileRouteProps;
const expectedCallee = getExpectedRouteConstructor(ctx.lazy);
const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`;
const currentRouteId = source.slice(routeCall.routeIdArg.start, routeCall.routeIdArg.end);
const targetModule = `@tanstack/${ctx.target}-router`;
const imports = parseTargetImports(ast.program.body, source, targetModule);
const s = new MagicString(source);
let modified = false;
if (routeCall.callee.name !== expectedCallee) {
s.update(routeCall.callee.start, routeCall.callee.end, expectedCallee);
modified = true;
}
const program = ast.program;
for (const n of program.body) {
if (n.type === "ExportNamedDeclaration") {
if (n.declaration?.type === "VariableDeclaration") {
const decl = n.declaration.declarations[0];
if (decl && decl.type === "VariableDeclarator" && decl.id.type === "Identifier") {
if (decl.id.name === "Route") onExportFound(decl);
}
} else if (n.declaration === null && n.specifiers) {
for (const spec of n.specifiers) if (typeof spec.exported.name === "string") {
if (spec.exported.name === "Route") {
const variableName = spec.local?.name || spec.exported.name;
for (const decl of program.body) if (decl.type === "VariableDeclaration" && decl.declarations[0]) {
const variable = decl.declarations[0];
if (variable.type === "VariableDeclarator" && variable.id.type === "Identifier" && variable.id.name === variableName) {
onExportFound(variable);
break;
}
}
}
}
}
}
if (routeExportHandled) break;
if (currentRouteId !== expectedRouteId) {
s.update(routeCall.routeIdArg.start, routeCall.routeIdArg.end, expectedRouteId);
modified = true;
}
if (!routeExportHandled) return { result: "no-route-export" };
const imports = {
required: [],
banned: []
if (updateRouteImports({
imports,
source,
s,
targetModule,
required: expectedCallee,
lineEnding: getLineEnding(source)
})) modified = true;
if (!modified) return { result: "not-modified" };
return {
result: "modified",
output: s.toString()
};
const targetModule = `@tanstack/${ctx.target}-router`;
if (ctx.verboseFileRoutes === false) imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }, { imported: "createFileRoute" }]
}];
else if (ctx.lazy) {
imports.required = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }]
}];
imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createFileRoute" }]
}];
} else {
imports.required = [{
source: targetModule,
specifiers: [{ imported: "createFileRoute" }]
}];
imports.banned = [{
source: targetModule,
specifiers: [{ imported: "createLazyFileRoute" }]
}];
}
function getExportedRouteNames(body) {
const exportedRouteNames = /* @__PURE__ */ new Set();
for (const statement of body) {
if (!t.isExportNamedDeclaration(statement) || statement.source) continue;
if (t.isVariableDeclaration(statement.declaration)) {
for (const declarator of statement.declaration.declarations) if (t.isIdentifier(declarator.id) && declarator.id.name === "Route") exportedRouteNames.add("Route");
}
for (const specifier of statement.specifiers) {
if (!t.isExportSpecifier(specifier) || getExportedName(specifier.exported) !== "Route") continue;
const localName = getLocalBindingName(specifier.local);
if (localName) exportedRouteNames.add(localName);
}
}
imports.required = mergeImportDeclarations(imports.required);
imports.banned = mergeImportDeclarations(imports.banned);
const importStatementCandidates = [];
const importDeclarationsToRemove = [];
for (const n of program.body) {
const findImport = (opts) => (i) => {
if (i.source === opts.source) {
const importKind = i.importKind || "value";
return (opts.importKind || "value") === importKind;
return exportedRouteNames;
}
function findExportedRouteCalls(body, exportedRouteNames) {
const calls = [];
let hasUnsupportedRouteId = false;
let hasMalformedRouteCall = false;
for (const statement of body) {
const declaration = getVariableDeclaration(statement);
if (!declaration) continue;
for (const declarator of declaration.declarations) {
if (!t.isIdentifier(declarator.id) || !exportedRouteNames.has(declarator.id.name)) continue;
const init = getRouteConstructorInit(declarator.init);
if (!init) {
if (isDirectRouteConstructorCall(declarator.init)) hasMalformedRouteCall = true;
continue;
}
return false;
};
if (n.type === "ImportDeclaration" && typeof n.source.value === "string") {
const filterImport = findImport({
source: n.source.value,
importKind: n.importKind
const routeIdArg = init.innerCall.arguments[0];
if (isSupportedRouteId(routeIdArg)) calls.push({
callee: init.callee,
routeIdArg,
optionsArg: init.outerCall.arguments[0]
});
let requiredImports = imports.required.filter(filterImport)[0];
const bannedImports = imports.banned.filter(filterImport)[0];
if (!requiredImports && !bannedImports) continue;
const importSpecifiersToRemove = [];
if (n.specifiers) {
for (const spec of n.specifiers) {
if (!requiredImports && !bannedImports) break;
if (spec.type === "ImportSpecifier" && typeof spec.imported.name === "string") {
if (requiredImports) {
const requiredImportIndex = requiredImports.specifiers.findIndex((imp) => imp.imported === spec.imported.name);
if (requiredImportIndex !== -1) {
requiredImports.specifiers.splice(requiredImportIndex, 1);
if (requiredImports.specifiers.length === 0) {
imports.required = imports.required.splice(imports.required.indexOf(requiredImports), 1);
requiredImports = void 0;
}
} else importStatementCandidates.push(n);
}
if (bannedImports) {
if (bannedImports.specifiers.findIndex((imp) => imp.imported === spec.imported.name) !== -1) importSpecifiersToRemove.push(spec);
}
}
}
if (importSpecifiersToRemove.length > 0) {
appliedChanges = true;
n.specifiers = n.specifiers.filter((spec) => !importSpecifiersToRemove.includes(spec));
if (n.specifiers.length === 0) importDeclarationsToRemove.push(n);
}
}
else hasUnsupportedRouteId = true;
}
}
imports.required.forEach((requiredImport) => {
if (requiredImport.specifiers.length > 0) {
appliedChanges = true;
if (importStatementCandidates.length > 0) {
const importStatement = importStatementCandidates.find((importStatement) => {
if (importStatement.source.value === requiredImport.source) return (importStatement.importKind || "value") === (requiredImport.importKind || "value");
return false;
});
if (importStatement) {
if (importStatement.specifiers === void 0) importStatement.specifiers = [];
const importSpecifiersToAdd = requiredImport.specifiers.map((spec) => b.importSpecifier(b.identifier(spec.imported), b.identifier(spec.imported)));
importStatement.specifiers = [...importStatement.specifiers, ...importSpecifiersToAdd];
return;
}
}
const importStatement = b.importDeclaration(requiredImport.specifiers.map((spec) => b.importSpecifier(b.identifier(spec.imported), spec.local ? b.identifier(spec.local) : null)), b.stringLiteral(requiredImport.source));
program.body.unshift(importStatement);
return {
calls,
hasUnsupportedRouteId,
hasMalformedRouteCall
};
}
function getVariableDeclaration(statement) {
const declaration = t.isExportNamedDeclaration(statement) ? statement.declaration : statement;
return t.isVariableDeclaration(declaration) ? declaration : null;
}
function getExportedName(node) {
return t.isIdentifier(node) ? node.name : node.value;
}
function getLocalBindingName(node) {
return t.isIdentifier(node) ? node.name : null;
}
function getRouteConstructorInit(expression) {
if (!expression || !t.isCallExpression(expression)) return null;
if (!t.isCallExpression(expression.callee)) return null;
const innerCall = expression.callee;
if (!t.isIdentifier(innerCall.callee) || !isRouteConstructor(innerCall.callee)) return null;
return {
callee: innerCall.callee,
outerCall: expression,
innerCall
};
}
function isDirectRouteConstructorCall(expression) {
return !!expression && t.isCallExpression(expression) && t.isIdentifier(expression.callee) && isRouteConstructor(expression.callee);
}
function isRouteConstructor(callee) {
return routeConstructors.includes(callee.name);
}
function isSupportedRouteId(arg) {
return !!arg && (t.isStringLiteral(arg) || t.isTemplateLiteral(arg) && arg.expressions.length === 0);
}
function getRouteIdQuote(source, arg) {
const raw = source.slice(arg.start, arg.end);
if (raw.startsWith("'")) return "'";
if (raw.startsWith("\"")) return "\"";
return "`";
}
function getCreateFileRouteProps(arg) {
if (!arg || !t.isObjectExpression(arg)) return;
const props = /* @__PURE__ */ new Set();
for (const property of arg.properties) {
if (!t.isObjectProperty(property) || property.computed) continue;
if (t.isIdentifier(property.key)) {
props.add(property.key.name);
continue;
}
if (t.isStringLiteral(property.key)) props.add(property.key.value);
}
return props;
}
function parseTargetImports(body, source, targetModule) {
const imports = [];
for (const statement of body) {
if (!t.isImportDeclaration(statement) || statement.importKind === "type" || statement.source.value !== targetModule) continue;
const rawSource = source.slice(statement.source.start, statement.source.end);
const importStatement = source.slice(statement.start, statement.end);
imports.push({
declaration: statement,
defaultImport: statement.specifiers.find((specifier) => t.isImportDefaultSpecifier(specifier))?.local.name,
namespace: statement.specifiers.find((specifier) => t.isImportNamespaceSpecifier(specifier))?.local.name,
named: statement.specifiers.filter((specifier) => t.isImportSpecifier(specifier)).map((specifier) => ({
imported: t.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value,
local: specifier.local.name,
importKind: specifier.importKind ?? void 0
})),
moduleName: statement.source.value,
quote: rawSource[0],
semicolon: importStatement.trimEnd().endsWith(";")
});
}
return imports;
}
function updateRouteImports({ imports, source, s, targetModule, required, lineEnding }) {
const analysis = analyzeRouteImports(imports, required);
if (analysis.kind === "ok") return false;
if (analysis.kind === "rename") {
s.update(analysis.imported.start, analysis.imported.end, analysis.next);
s.update(analysis.local.start, analysis.local.end, analysis.next);
return true;
}
return normalizeRouteImports({
imports,
source,
s,
targetModule,
required,
lineEnding
});
if (importDeclarationsToRemove.length > 0) {
appliedChanges = true;
for (const importDeclaration of importDeclarationsToRemove) if (importDeclaration.specifiers?.length === 0) {
const index = program.body.indexOf(importDeclaration);
if (index !== -1) program.body.splice(index, 1);
}
function analyzeRouteImports(imports, required) {
const opposite = getOtherRouteConstructor(required);
let requiredCount = 0;
let oppositeCount = 0;
let renameCandidate;
for (const declaration of imports) for (const specifier of declaration.declaration.specifiers) {
if (!t.isImportSpecifier(specifier)) continue;
const imported = specifier.imported;
if (!t.isIdentifier(imported)) return { kind: "normalize" };
if (!isRouteConstructorName(imported.name)) continue;
if (specifier.local.name !== imported.name) return { kind: "normalize" };
if (imported.name === required) {
requiredCount++;
continue;
}
if (imported.name === opposite) {
oppositeCount++;
renameCandidate = {
imported,
local: specifier.local,
next: required
};
}
}
if (!appliedChanges) return { result: "not-modified" };
const printResult = print(ast, {
reuseWhitespace: true,
sourceMapName: "output.map"
});
let transformedCode = printResult.code;
if (printResult.map) transformedCode = await fixTransformedOutputText({
originalCode: source,
transformedCode,
sourceMap: printResult.map,
preferredQuote
});
return {
result: "modified",
output: transformedCode
if (requiredCount === 1 && oppositeCount === 0) return { kind: "ok" };
if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) return {
kind: "rename",
...renameCandidate
};
return { kind: "normalize" };
}
async function fixTransformedOutputText({ originalCode, transformedCode, sourceMap, preferredQuote }) {
const originalLines = originalCode.split("\n");
const transformedLines = transformedCode.split("\n");
const defaultUsesSemicolons = detectSemicolonUsage(originalCode);
const consumer = await new SourceMapConsumer(sourceMap);
return transformedLines.map((line, i) => {
const transformedLineNum = i + 1;
let origLineText = void 0;
for (let col = 0; col < line.length; col++) {
const mapped = consumer.originalPositionFor({
line: transformedLineNum,
column: col
});
if (mapped.line != null && mapped.line > 0) {
origLineText = originalLines[mapped.line - 1];
break;
}
function normalizeRouteImports({ imports, source, s, targetModule, required, lineEnding }) {
const owner = imports.find((declaration) => hasNamedImport(declaration.named, required)) ?? imports.find((declaration) => !declaration.namespace);
let modified = false;
for (const declaration of imports) {
const named = normalizeNamedImports({
named: declaration.named,
required,
isOwner: declaration === owner
});
if (sameNamedImports(declaration.named, named)) continue;
const replacement = renderImportDeclaration({
...declaration,
named
});
if (replacement === null) {
s.remove(declaration.declaration.start, getRemovalEnd(source, declaration.declaration.end));
modified = true;
continue;
}
if (origLineText !== void 0) {
if (origLineText === line) return origLineText;
return fixLine(line, {
originalLine: origLineText,
useOriginalSemicolon: true,
useOriginalQuotes: true,
fallbackQuote: preferredQuote
});
} else return fixLine(line, {
originalLine: null,
useOriginalSemicolon: false,
useOriginalQuotes: false,
fallbackQuote: preferredQuote,
fallbackSemicolon: defaultUsesSemicolons
});
}).join("\n");
s.update(declaration.declaration.start, declaration.declaration.end, replacement);
modified = true;
}
if (!owner) {
const quote = imports[0]?.quote ?? "'";
const semicolon = imports[0]?.semicolon ?? false;
s.prepend(`import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ";" : ""}${lineEnding}`);
modified = true;
}
return modified;
}
function fixLine(line, { originalLine, useOriginalSemicolon, useOriginalQuotes, fallbackQuote, fallbackSemicolon = true }) {
let result = line;
if (useOriginalQuotes && originalLine) result = fixQuotes(result, originalLine, fallbackQuote);
else if (!useOriginalQuotes && fallbackQuote) result = fixQuotesToPreferred(result, fallbackQuote);
if (useOriginalSemicolon && originalLine) {
const hadSemicolon = originalLine.trimEnd().endsWith(";");
const hasSemicolon = result.trimEnd().endsWith(";");
if (hadSemicolon && !hasSemicolon) result += ";";
if (!hadSemicolon && hasSemicolon) result = result.replace(/;\s*$/, "");
} else if (!useOriginalSemicolon) {
const hasSemicolon = result.trimEnd().endsWith(";");
if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\s*$/, "");
if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ";";
function normalizeNamedImports({ named, required, isOwner }) {
const banned = getOtherRouteConstructor(required);
const nextNamed = [];
const seen = /* @__PURE__ */ new Set();
for (const specifier of named) {
if (specifier.imported === banned) continue;
if (specifier.local === required && (specifier.imported !== required || !isOwner)) continue;
const key = `${specifier.importKind ?? "value"}:${specifier.imported}:${specifier.local}`;
if (seen.has(key)) continue;
seen.add(key);
nextNamed.push(specifier);
}
return result;
if (isOwner && !hasNamedImport(nextNamed, required)) nextNamed.push({
imported: required,
local: required
});
return nextNamed;
}
function fixQuotes(line, originalLine, fallbackQuote) {
let originalQuote = detectQuoteFromLine(originalLine);
if (!originalQuote) originalQuote = fallbackQuote;
return fixQuotesToPreferred(line, originalQuote);
function getExpectedRouteConstructor(lazy) {
return lazy ? "createLazyFileRoute" : "createFileRoute";
}
function fixQuotesToPreferred(line, quote) {
return line.replace(/(['"`])([^'"`\\]*(?:\\.[^'"`\\]*)*)\1/g, (_, q, content) => {
return `${quote}${content.replaceAll(quote, `\\${quote}`)}${quote}`;
});
function getOtherRouteConstructor(constructor) {
return constructor === "createFileRoute" ? "createLazyFileRoute" : "createFileRoute";
}
function detectQuoteFromLine(line) {
const match = line.match(/(['"`])(?:\\.|[^\\])*?\1/);
return match ? match[1] : null;
function hasNamedImport(named, required) {
return named.some((specifier) => specifier.imported === required && specifier.local === required && specifier.importKind !== "type");
}
function detectSemicolonUsage(code) {
const lines = code.split("\n").map((l) => l.trim());
const total = lines.length;
return lines.filter((l) => l.endsWith(";")).length > total / 2;
function sameNamedImports(left, right) {
return left.length === right.length && left.every((specifier, index) => specifier.imported === right[index].imported && specifier.local === right[index].local && specifier.importKind === right[index].importKind);
}
function detectPreferredQuoteStyle(ast) {
let single = 0;
let double = 0;
visit(ast, { visitStringLiteral(path) {
if (path.parent.node.type !== "JSXAttribute") {
const raw = path.node.extra?.raw;
if (raw?.startsWith("'")) single++;
else if (raw?.startsWith("\"")) double++;
}
return false;
} });
if (single >= double) return "'";
return "\"";
function isRouteConstructorName(value) {
return routeConstructors.includes(value);
}
function renderImportDeclaration(importDeclaration) {
const parts = [];
if (importDeclaration.defaultImport) parts.push(importDeclaration.defaultImport);
if (importDeclaration.namespace) parts.push(`* as ${importDeclaration.namespace}`);
if (importDeclaration.named.length > 0) parts.push(`{ ${importDeclaration.named.map((specifier) => `${specifier.importKind === "type" ? "type " : ""}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`).join(", ")} }`);
if (parts.length === 0) return null;
return `import ${parts.join(", ")} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ";" : ""}`;
}
function getLineEnding(source) {
if (source.includes("\r\n")) return "\r\n";
if (source.includes("\n")) return "\n";
if (source.includes("\r")) return "\r";
return "\n";
}
function getRemovalEnd(source, end) {
let pos = end;
while (pos < source.length && (source[pos] === " " || source[pos] === " ")) pos++;
if (source[pos] === "\r" && source[pos + 1] === "\n") return pos + 2;
if (source[pos] === "\n" || source[pos] === "\r") return pos + 1;
return end;
}
//#endregion

@@ -298,0 +313,0 @@ export { transform };

@@ -1,1 +0,1 @@

{"version":3,"file":"transform.js","names":[],"sources":["../../../src/transform/transform.ts"],"sourcesContent":["import { parseAst } from '@tanstack/router-utils'\nimport { parse, print, types, visit } from 'recast'\nimport { SourceMapConsumer } from 'source-map'\nimport { mergeImportDeclarations } from '../utils'\nimport { ensureStringArgument } from './utils'\nimport type { ImportDeclaration } from '../types'\nimport type { RawSourceMap } from 'source-map'\nimport type { TransformOptions, TransformResult } from './types'\n\nconst b = types.builders\n\nexport async function transform({\n ctx,\n source,\n node,\n}: TransformOptions): Promise<TransformResult> {\n let appliedChanges = false as boolean\n let ast: types.namedTypes.File\n try {\n ast = parse(source, {\n sourceFileName: 'output.ts',\n parser: {\n parse(code: string) {\n return parseAst({\n code,\n // we need to instruct babel to produce tokens,\n // otherwise recast will try to generate the tokens via its own parser and will fail\n tokens: true,\n })\n },\n },\n })\n } catch (e) {\n console.error('Error parsing code', ctx.routeId, source, e)\n return {\n result: 'error',\n error: e,\n }\n }\n\n const preferredQuote = detectPreferredQuoteStyle(ast)\n\n let routeExportHandled = false as boolean\n function onExportFound(decl: types.namedTypes.VariableDeclarator) {\n if (decl.init?.type === 'CallExpression') {\n const callExpression = decl.init\n const firstArgument = callExpression.arguments[0]\n if (firstArgument) {\n if (firstArgument.type === 'ObjectExpression') {\n const staticProperties = firstArgument.properties.flatMap((p) => {\n if (p.type === 'ObjectProperty' && p.key.type === 'Identifier') {\n return p.key.name\n }\n return []\n })\n node.createFileRouteProps = new Set(staticProperties)\n }\n }\n let identifier: types.namedTypes.Identifier | undefined\n // `const Route = createFileRoute({ ... })`\n if (callExpression.callee.type === 'Identifier') {\n identifier = callExpression.callee\n if (ctx.verboseFileRoutes) {\n // we need to add the string literal via another CallExpression\n callExpression.callee = b.callExpression(identifier, [\n b.stringLiteral(ctx.routeId),\n ])\n appliedChanges = true\n }\n }\n // `const Route = createFileRoute('/path')({ ... })`\n else if (\n callExpression.callee.type === 'CallExpression' &&\n callExpression.callee.callee.type === 'Identifier'\n ) {\n identifier = callExpression.callee.callee\n if (!ctx.verboseFileRoutes) {\n // we need to remove the route id\n callExpression.callee = identifier\n appliedChanges = true\n } else {\n // check if the route id is correct\n appliedChanges = ensureStringArgument(\n callExpression.callee,\n ctx.routeId,\n ctx.preferredQuote,\n )\n }\n }\n if (identifier === undefined) {\n throw new Error(\n `expected identifier to be present in ${ctx.routeId} for export \"Route\"`,\n )\n }\n if (identifier.name === 'createFileRoute' && ctx.lazy) {\n identifier.name = 'createLazyFileRoute'\n appliedChanges = true\n } else if (identifier.name === 'createLazyFileRoute' && !ctx.lazy) {\n identifier.name = 'createFileRoute'\n appliedChanges = true\n }\n } else {\n throw new Error(\n `expected \"Route\" export to be initialized by a CallExpression`,\n )\n }\n routeExportHandled = true\n }\n\n const program: types.namedTypes.Program = ast.program\n // first pass: find Route export\n for (const n of program.body) {\n if (n.type === 'ExportNamedDeclaration') {\n // direct export of a variable declaration, e.g. `export const Route = createFileRoute('/path')`\n if (n.declaration?.type === 'VariableDeclaration') {\n const decl = n.declaration.declarations[0]\n if (\n decl &&\n decl.type === 'VariableDeclarator' &&\n decl.id.type === 'Identifier'\n ) {\n if (decl.id.name === 'Route') {\n onExportFound(decl)\n }\n }\n }\n // this is an export without a declaration, e.g. `export { Route }`\n else if (n.declaration === null && n.specifiers) {\n for (const spec of n.specifiers) {\n if (typeof spec.exported.name === 'string') {\n if (spec.exported.name === 'Route') {\n const variableName = spec.local?.name || spec.exported.name\n // find the matching variable declaration by iterating over the top-level declarations\n for (const decl of program.body) {\n if (\n decl.type === 'VariableDeclaration' &&\n decl.declarations[0]\n ) {\n const variable = decl.declarations[0]\n if (\n variable.type === 'VariableDeclarator' &&\n variable.id.type === 'Identifier' &&\n variable.id.name === variableName\n ) {\n onExportFound(variable)\n break\n }\n }\n }\n }\n }\n }\n }\n }\n if (routeExportHandled) {\n break\n }\n }\n\n if (!routeExportHandled) {\n return {\n result: 'no-route-export',\n }\n }\n\n const imports: {\n required: Array<ImportDeclaration>\n banned: Array<ImportDeclaration>\n } = {\n required: [],\n banned: [],\n }\n\n const targetModule = `@tanstack/${ctx.target}-router`\n if (ctx.verboseFileRoutes === false) {\n imports.banned = [\n {\n source: targetModule,\n specifiers: [\n { imported: 'createLazyFileRoute' },\n { imported: 'createFileRoute' },\n ],\n },\n ]\n } else {\n if (ctx.lazy) {\n imports.required = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createLazyFileRoute' }],\n },\n ]\n imports.banned = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createFileRoute' }],\n },\n ]\n } else {\n imports.required = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createFileRoute' }],\n },\n ]\n imports.banned = [\n {\n source: targetModule,\n specifiers: [{ imported: 'createLazyFileRoute' }],\n },\n ]\n }\n }\n\n imports.required = mergeImportDeclarations(imports.required)\n imports.banned = mergeImportDeclarations(imports.banned)\n\n const importStatementCandidates: Array<types.namedTypes.ImportDeclaration> =\n []\n const importDeclarationsToRemove: Array<types.namedTypes.ImportDeclaration> =\n []\n\n // second pass: apply import rules, but only if a matching export for the plugin was found\n for (const n of program.body) {\n const findImport =\n (opts: { source: string; importKind?: 'type' | 'value' | 'typeof' }) =>\n (i: ImportDeclaration) => {\n if (i.source === opts.source) {\n const importKind = i.importKind || 'value'\n const expectedImportKind = opts.importKind || 'value'\n return expectedImportKind === importKind\n }\n return false\n }\n if (n.type === 'ImportDeclaration' && typeof n.source.value === 'string') {\n const filterImport = findImport({\n source: n.source.value,\n importKind: n.importKind,\n })\n let requiredImports = imports.required.filter(filterImport)[0]\n\n const bannedImports = imports.banned.filter(filterImport)[0]\n if (!requiredImports && !bannedImports) {\n continue\n }\n const importSpecifiersToRemove: types.namedTypes.ImportDeclaration['specifiers'] =\n []\n if (n.specifiers) {\n for (const spec of n.specifiers) {\n if (!requiredImports && !bannedImports) {\n break\n }\n if (\n spec.type === 'ImportSpecifier' &&\n typeof spec.imported.name === 'string'\n ) {\n if (requiredImports) {\n const requiredImportIndex = requiredImports.specifiers.findIndex(\n (imp) => imp.imported === spec.imported.name,\n )\n if (requiredImportIndex !== -1) {\n // import is already present, remove it from requiredImports\n requiredImports.specifiers.splice(requiredImportIndex, 1)\n if (requiredImports.specifiers.length === 0) {\n imports.required = imports.required.splice(\n imports.required.indexOf(requiredImports),\n 1,\n )\n requiredImports = undefined\n }\n } else {\n // add the import statement to the candidates\n importStatementCandidates.push(n)\n }\n }\n if (bannedImports) {\n const bannedImportIndex = bannedImports.specifiers.findIndex(\n (imp) => imp.imported === spec.imported.name,\n )\n if (bannedImportIndex !== -1) {\n importSpecifiersToRemove.push(spec)\n }\n }\n }\n }\n if (importSpecifiersToRemove.length > 0) {\n appliedChanges = true\n n.specifiers = n.specifiers.filter(\n (spec) => !importSpecifiersToRemove.includes(spec),\n )\n\n // mark the import statement as to be deleted if it is now empty\n if (n.specifiers.length === 0) {\n importDeclarationsToRemove.push(n)\n }\n }\n }\n }\n }\n imports.required.forEach((requiredImport) => {\n if (requiredImport.specifiers.length > 0) {\n appliedChanges = true\n if (importStatementCandidates.length > 0) {\n // find the first import statement that matches both the module and the import kind\n const importStatement = importStatementCandidates.find(\n (importStatement) => {\n if (importStatement.source.value === requiredImport.source) {\n const importKind = importStatement.importKind || 'value'\n const requiredImportKind = requiredImport.importKind || 'value'\n return importKind === requiredImportKind\n }\n return false\n },\n )\n if (importStatement) {\n if (importStatement.specifiers === undefined) {\n importStatement.specifiers = []\n }\n const importSpecifiersToAdd = requiredImport.specifiers.map((spec) =>\n b.importSpecifier(\n b.identifier(spec.imported),\n b.identifier(spec.imported),\n ),\n )\n importStatement.specifiers = [\n ...importStatement.specifiers,\n ...importSpecifiersToAdd,\n ]\n return\n }\n }\n const importStatement = b.importDeclaration(\n requiredImport.specifiers.map((spec) =>\n b.importSpecifier(\n b.identifier(spec.imported),\n spec.local ? b.identifier(spec.local) : null,\n ),\n ),\n b.stringLiteral(requiredImport.source),\n )\n program.body.unshift(importStatement)\n }\n })\n if (importDeclarationsToRemove.length > 0) {\n appliedChanges = true\n for (const importDeclaration of importDeclarationsToRemove) {\n // check if the import declaration is still empty\n if (importDeclaration.specifiers?.length === 0) {\n const index = program.body.indexOf(importDeclaration)\n if (index !== -1) {\n program.body.splice(index, 1)\n }\n }\n }\n }\n\n if (!appliedChanges) {\n return {\n result: 'not-modified',\n }\n }\n\n const printResult = print(ast, {\n reuseWhitespace: true,\n sourceMapName: 'output.map',\n })\n let transformedCode = printResult.code\n if (printResult.map) {\n const fixedOutput = await fixTransformedOutputText({\n originalCode: source,\n transformedCode,\n sourceMap: printResult.map as RawSourceMap,\n preferredQuote,\n })\n transformedCode = fixedOutput\n }\n return {\n result: 'modified',\n output: transformedCode,\n }\n}\n\nasync function fixTransformedOutputText({\n originalCode,\n transformedCode,\n sourceMap,\n preferredQuote,\n}: {\n originalCode: string\n transformedCode: string\n sourceMap: RawSourceMap\n preferredQuote: '\"' | \"'\"\n}) {\n const originalLines = originalCode.split('\\n')\n const transformedLines = transformedCode.split('\\n')\n\n const defaultUsesSemicolons = detectSemicolonUsage(originalCode)\n\n const consumer = await new SourceMapConsumer(sourceMap)\n\n const fixedLines = transformedLines.map((line, i) => {\n const transformedLineNum = i + 1\n\n let origLineText: string | undefined = undefined\n\n for (let col = 0; col < line.length; col++) {\n const mapped = consumer.originalPositionFor({\n line: transformedLineNum,\n column: col,\n })\n if (mapped.line != null && mapped.line > 0) {\n origLineText = originalLines[mapped.line - 1]\n break\n }\n }\n\n if (origLineText !== undefined) {\n if (origLineText === line) {\n return origLineText\n }\n return fixLine(line, {\n originalLine: origLineText,\n useOriginalSemicolon: true,\n useOriginalQuotes: true,\n fallbackQuote: preferredQuote,\n })\n } else {\n return fixLine(line, {\n originalLine: null,\n useOriginalSemicolon: false,\n useOriginalQuotes: false,\n fallbackQuote: preferredQuote,\n fallbackSemicolon: defaultUsesSemicolons,\n })\n }\n })\n\n return fixedLines.join('\\n')\n}\n\nfunction fixLine(\n line: string,\n {\n originalLine,\n useOriginalSemicolon,\n useOriginalQuotes,\n fallbackQuote,\n fallbackSemicolon = true,\n }: {\n originalLine: string | null\n useOriginalSemicolon: boolean\n useOriginalQuotes: boolean\n fallbackQuote: string\n fallbackSemicolon?: boolean\n },\n) {\n let result = line\n\n if (useOriginalQuotes && originalLine) {\n result = fixQuotes(result, originalLine, fallbackQuote)\n } else if (!useOriginalQuotes && fallbackQuote) {\n result = fixQuotesToPreferred(result, fallbackQuote)\n }\n\n if (useOriginalSemicolon && originalLine) {\n const hadSemicolon = originalLine.trimEnd().endsWith(';')\n const hasSemicolon = result.trimEnd().endsWith(';')\n if (hadSemicolon && !hasSemicolon) result += ';'\n if (!hadSemicolon && hasSemicolon) result = result.replace(/;\\s*$/, '')\n } else if (!useOriginalSemicolon) {\n const hasSemicolon = result.trimEnd().endsWith(';')\n if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\\s*$/, '')\n if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ';'\n }\n\n return result\n}\n\nfunction fixQuotes(line: string, originalLine: string, fallbackQuote: string) {\n let originalQuote = detectQuoteFromLine(originalLine)\n if (!originalQuote) {\n originalQuote = fallbackQuote\n }\n return fixQuotesToPreferred(line, originalQuote)\n}\n\nfunction fixQuotesToPreferred(line: string, quote: string) {\n // Replace existing quotes with preferred quote\n return line.replace(\n /(['\"`])([^'\"`\\\\]*(?:\\\\.[^'\"`\\\\]*)*)\\1/g,\n (_, q, content) => {\n const escaped = content.replaceAll(quote, `\\\\${quote}`)\n return `${quote}${escaped}${quote}`\n },\n )\n}\n\nfunction detectQuoteFromLine(line: string) {\n const match = line.match(/(['\"`])(?:\\\\.|[^\\\\])*?\\1/)\n return match ? match[1] : null\n}\n\nfunction detectSemicolonUsage(code: string) {\n const lines = code.split('\\n').map((l) => l.trim())\n const total = lines.length\n const withSemis = lines.filter((l) => l.endsWith(';')).length\n return withSemis > total / 2\n}\n\nexport function detectPreferredQuoteStyle(ast: types.ASTNode): \"'\" | '\"' {\n let single = 0\n let double = 0\n\n visit(ast, {\n visitStringLiteral(path) {\n if (path.parent.node.type !== 'JSXAttribute') {\n const raw = path.node.extra?.raw\n if (raw?.startsWith(\"'\")) single++\n else if (raw?.startsWith('\"')) double++\n }\n return false\n },\n })\n\n if (single >= double) {\n return \"'\"\n }\n return '\"'\n}\n"],"mappings":"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"}
{"version":3,"file":"transform.js","names":[],"sources":["../../../src/transform/transform.ts"],"sourcesContent":["import MagicString from 'magic-string'\nimport * as t from '@babel/types'\nimport { parseAst } from '@tanstack/router-utils'\nimport type { TransformOptions, TransformResult } from './types'\n\nconst routeConstructors = ['createFileRoute', 'createLazyFileRoute'] as const\n\ntype RouteConstructorName = (typeof routeConstructors)[number]\ntype SupportedRouteId = t.StringLiteral | t.TemplateLiteral\ntype NamedImport = {\n imported: string\n local: string\n importKind?: 'type' | 'typeof' | 'value'\n}\n\ntype ParsedImportDeclaration = {\n declaration: t.ImportDeclaration\n defaultImport?: string\n namespace?: string\n named: Array<NamedImport>\n moduleName: string\n quote: '\"' | \"'\"\n semicolon: boolean\n}\n\ntype RouteImportAnalysis =\n | { kind: 'ok' }\n | {\n kind: 'rename'\n imported: t.Identifier\n local: t.Identifier\n next: RouteConstructorName\n }\n | { kind: 'normalize' }\n\ntype RouteCall = {\n callee: t.Identifier & { name: RouteConstructorName }\n routeIdArg: SupportedRouteId\n optionsArg: t.CallExpression['arguments'][number] | undefined\n}\n\ntype RouteCallAnalysis = {\n calls: Array<RouteCall>\n hasUnsupportedRouteId: boolean\n hasMalformedRouteCall: boolean\n}\n\nexport function transform({\n ctx,\n source,\n node,\n}: TransformOptions): TransformResult {\n let ast: ReturnType<typeof parseAst>\n\n try {\n ast = parseAst({ code: source })\n } catch (error) {\n return {\n result: 'error',\n error,\n }\n }\n\n const exportedRouteNames = getExportedRouteNames(ast.program.body)\n\n if (exportedRouteNames.size === 0) {\n return { result: 'no-route-export' }\n }\n\n const {\n calls: routeCalls,\n hasUnsupportedRouteId,\n hasMalformedRouteCall,\n } = findExportedRouteCalls(ast.program.body, exportedRouteNames)\n\n if (routeCalls.length === 0 && hasMalformedRouteCall) {\n return {\n result: 'error',\n error: new Error(\n `expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`,\n ),\n }\n }\n\n if (routeCalls.length === 0 && hasUnsupportedRouteId) {\n return {\n result: 'error',\n error: new Error(\n `expected route id to be a string literal or plain template literal in ${ctx.routeId}`,\n ),\n }\n }\n\n if (routeCalls.length === 0) {\n return { result: 'not-modified' }\n }\n\n if (routeCalls.length > 1) {\n return {\n result: 'error',\n error: new Error(\n `expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`,\n ),\n }\n }\n\n const routeCall = routeCalls[0]!\n const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg)\n\n const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg)\n if (createFileRouteProps) {\n node.createFileRouteProps = createFileRouteProps\n }\n\n const expectedCallee = getExpectedRouteConstructor(ctx.lazy)\n const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`\n const currentRouteId = source.slice(\n routeCall.routeIdArg.start!,\n routeCall.routeIdArg.end!,\n )\n const targetModule = `@tanstack/${ctx.target}-router`\n const imports = parseTargetImports(ast.program.body, source, targetModule)\n\n const s = new MagicString(source)\n let modified = false\n\n if (routeCall.callee.name !== expectedCallee) {\n s.update(routeCall.callee.start!, routeCall.callee.end!, expectedCallee)\n modified = true\n }\n\n if (currentRouteId !== expectedRouteId) {\n s.update(\n routeCall.routeIdArg.start!,\n routeCall.routeIdArg.end!,\n expectedRouteId,\n )\n modified = true\n }\n\n if (\n updateRouteImports({\n imports,\n source,\n s,\n targetModule,\n required: expectedCallee,\n lineEnding: getLineEnding(source),\n })\n ) {\n modified = true\n }\n\n if (!modified) {\n return { result: 'not-modified' }\n }\n\n return {\n result: 'modified',\n output: s.toString(),\n }\n}\n\nfunction getExportedRouteNames(body: Array<t.Statement>) {\n const exportedRouteNames = new Set<string>()\n\n for (const statement of body) {\n if (!t.isExportNamedDeclaration(statement) || statement.source) {\n continue\n }\n\n if (t.isVariableDeclaration(statement.declaration)) {\n for (const declarator of statement.declaration.declarations) {\n if (t.isIdentifier(declarator.id) && declarator.id.name === 'Route') {\n exportedRouteNames.add('Route')\n }\n }\n }\n\n for (const specifier of statement.specifiers) {\n if (\n !t.isExportSpecifier(specifier) ||\n getExportedName(specifier.exported) !== 'Route'\n ) {\n continue\n }\n\n const localName = getLocalBindingName(specifier.local)\n if (localName) {\n exportedRouteNames.add(localName)\n }\n }\n }\n\n return exportedRouteNames\n}\n\nfunction findExportedRouteCalls(\n body: Array<t.Statement>,\n exportedRouteNames: Set<string>,\n): RouteCallAnalysis {\n const calls: Array<RouteCall> = []\n let hasUnsupportedRouteId = false\n let hasMalformedRouteCall = false\n\n for (const statement of body) {\n const declaration = getVariableDeclaration(statement)\n if (!declaration) {\n continue\n }\n\n for (const declarator of declaration.declarations) {\n if (\n !t.isIdentifier(declarator.id) ||\n !exportedRouteNames.has(declarator.id.name)\n ) {\n continue\n }\n\n const init = getRouteConstructorInit(declarator.init)\n if (!init) {\n if (isDirectRouteConstructorCall(declarator.init)) {\n hasMalformedRouteCall = true\n }\n continue\n }\n\n const routeIdArg = init.innerCall.arguments[0]\n if (isSupportedRouteId(routeIdArg)) {\n calls.push({\n callee: init.callee,\n routeIdArg,\n optionsArg: init.outerCall.arguments[0],\n })\n } else {\n hasUnsupportedRouteId = true\n }\n }\n }\n\n return { calls, hasUnsupportedRouteId, hasMalformedRouteCall }\n}\n\nfunction getVariableDeclaration(statement: t.Statement) {\n const declaration = t.isExportNamedDeclaration(statement)\n ? statement.declaration\n : statement\n\n return t.isVariableDeclaration(declaration) ? declaration : null\n}\n\nfunction getExportedName(node: t.Identifier | t.StringLiteral) {\n return t.isIdentifier(node) ? node.name : node.value\n}\n\nfunction getLocalBindingName(node: t.Identifier | t.StringLiteral) {\n return t.isIdentifier(node) ? node.name : null\n}\n\nfunction getRouteConstructorInit(expression: t.Expression | null | undefined) {\n if (!expression || !t.isCallExpression(expression)) {\n return null\n }\n\n if (!t.isCallExpression(expression.callee)) {\n return null\n }\n\n const innerCall = expression.callee\n\n if (\n !t.isIdentifier(innerCall.callee) ||\n !isRouteConstructor(innerCall.callee)\n ) {\n return null\n }\n\n return {\n callee: innerCall.callee,\n outerCall: expression,\n innerCall,\n }\n}\n\nfunction isDirectRouteConstructorCall(\n expression: t.Expression | null | undefined,\n) {\n return (\n !!expression &&\n t.isCallExpression(expression) &&\n t.isIdentifier(expression.callee) &&\n isRouteConstructor(expression.callee)\n )\n}\n\nfunction isRouteConstructor(callee: t.Identifier): callee is t.Identifier & {\n name: RouteConstructorName\n} {\n return routeConstructors.includes(callee.name as RouteConstructorName)\n}\n\nfunction isSupportedRouteId(\n arg: t.CallExpression['arguments'][number] | undefined,\n): arg is SupportedRouteId {\n return (\n !!arg &&\n (t.isStringLiteral(arg) ||\n (t.isTemplateLiteral(arg) && arg.expressions.length === 0))\n )\n}\n\nfunction getRouteIdQuote(\n source: string,\n arg: SupportedRouteId,\n): '\"' | \"'\" | '`' {\n const raw = source.slice(arg.start!, arg.end!)\n\n if (raw.startsWith(\"'\")) return \"'\"\n if (raw.startsWith('\"')) return '\"'\n return '`'\n}\n\nfunction getCreateFileRouteProps(\n arg: t.CallExpression['arguments'][number] | undefined,\n) {\n if (!arg || !t.isObjectExpression(arg)) {\n return undefined\n }\n\n const props = new Set<string>()\n\n for (const property of arg.properties) {\n if (!t.isObjectProperty(property) || property.computed) {\n continue\n }\n\n if (t.isIdentifier(property.key)) {\n props.add(property.key.name)\n continue\n }\n\n if (t.isStringLiteral(property.key)) {\n props.add(property.key.value)\n }\n }\n\n return props\n}\n\nfunction parseTargetImports(\n body: Array<t.Statement>,\n source: string,\n targetModule: string,\n) {\n const imports: Array<ParsedImportDeclaration> = []\n\n for (const statement of body) {\n if (\n !t.isImportDeclaration(statement) ||\n statement.importKind === 'type' ||\n statement.source.value !== targetModule\n ) {\n continue\n }\n\n const rawSource = source.slice(\n statement.source.start!,\n statement.source.end!,\n )\n const importStatement = source.slice(statement.start!, statement.end!)\n\n imports.push({\n declaration: statement,\n defaultImport: statement.specifiers.find((specifier) =>\n t.isImportDefaultSpecifier(specifier),\n )?.local.name,\n namespace: statement.specifiers.find((specifier) =>\n t.isImportNamespaceSpecifier(specifier),\n )?.local.name,\n named: statement.specifiers\n .filter((specifier): specifier is t.ImportSpecifier =>\n t.isImportSpecifier(specifier),\n )\n .map((specifier) => ({\n imported: t.isIdentifier(specifier.imported)\n ? specifier.imported.name\n : specifier.imported.value,\n local: specifier.local.name,\n importKind: specifier.importKind ?? undefined,\n })),\n moduleName: statement.source.value,\n quote: rawSource[0] as '\"' | \"'\",\n semicolon: importStatement.trimEnd().endsWith(';'),\n })\n }\n\n return imports\n}\n\nfunction updateRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n}: {\n imports: Array<ParsedImportDeclaration>\n source: string\n s: MagicString\n targetModule: string\n required: RouteConstructorName\n lineEnding: '\\r\\n' | '\\n' | '\\r'\n}) {\n const analysis = analyzeRouteImports(imports, required)\n\n if (analysis.kind === 'ok') {\n return false\n }\n\n if (analysis.kind === 'rename') {\n s.update(analysis.imported.start!, analysis.imported.end!, analysis.next)\n s.update(analysis.local.start!, analysis.local.end!, analysis.next)\n return true\n }\n\n return normalizeRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n })\n}\n\nfunction analyzeRouteImports(\n imports: Array<ParsedImportDeclaration>,\n required: RouteConstructorName,\n): RouteImportAnalysis {\n const opposite = getOtherRouteConstructor(required)\n let requiredCount = 0\n let oppositeCount = 0\n let renameCandidate:\n | {\n imported: t.Identifier\n local: t.Identifier\n next: RouteConstructorName\n }\n | undefined\n\n for (const declaration of imports) {\n for (const specifier of declaration.declaration.specifiers) {\n if (!t.isImportSpecifier(specifier)) {\n continue\n }\n\n const imported = specifier.imported\n if (!t.isIdentifier(imported)) {\n return { kind: 'normalize' }\n }\n\n if (!isRouteConstructorName(imported.name)) {\n continue\n }\n\n if (specifier.local.name !== imported.name) {\n return { kind: 'normalize' }\n }\n\n if (imported.name === required) {\n requiredCount++\n continue\n }\n\n if (imported.name === opposite) {\n oppositeCount++\n renameCandidate = {\n imported,\n local: specifier.local,\n next: required,\n }\n }\n }\n }\n\n if (requiredCount === 1 && oppositeCount === 0) {\n return { kind: 'ok' }\n }\n\n if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) {\n return {\n kind: 'rename',\n ...renameCandidate,\n }\n }\n\n return { kind: 'normalize' }\n}\n\nfunction normalizeRouteImports({\n imports,\n source,\n s,\n targetModule,\n required,\n lineEnding,\n}: {\n imports: Array<ParsedImportDeclaration>\n source: string\n s: MagicString\n targetModule: string\n required: RouteConstructorName\n lineEnding: '\\r\\n' | '\\n' | '\\r'\n}) {\n const owner =\n imports.find((declaration) =>\n hasNamedImport(declaration.named, required),\n ) ?? imports.find((declaration) => !declaration.namespace)\n\n let modified = false\n\n for (const declaration of imports) {\n const named = normalizeNamedImports({\n named: declaration.named,\n required,\n isOwner: declaration === owner,\n })\n\n if (sameNamedImports(declaration.named, named)) {\n continue\n }\n\n const replacement = renderImportDeclaration({\n ...declaration,\n named,\n })\n\n if (replacement === null) {\n s.remove(\n declaration.declaration.start!,\n getRemovalEnd(source, declaration.declaration.end!),\n )\n modified = true\n continue\n }\n\n s.update(\n declaration.declaration.start!,\n declaration.declaration.end!,\n replacement,\n )\n modified = true\n }\n\n if (!owner) {\n const quote = imports[0]?.quote ?? \"'\"\n const semicolon = imports[0]?.semicolon ?? false\n s.prepend(\n `import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ';' : ''}${lineEnding}`,\n )\n modified = true\n }\n\n return modified\n}\n\nfunction normalizeNamedImports({\n named,\n required,\n isOwner,\n}: {\n named: Array<NamedImport>\n required: RouteConstructorName\n isOwner: boolean\n}) {\n const banned = getOtherRouteConstructor(required)\n const nextNamed: Array<NamedImport> = []\n const seen = new Set<string>()\n\n for (const specifier of named) {\n if (specifier.imported === banned) {\n continue\n }\n\n if (\n specifier.local === required &&\n (specifier.imported !== required || !isOwner)\n ) {\n continue\n }\n\n const key = `${specifier.importKind ?? 'value'}:${specifier.imported}:${specifier.local}`\n if (seen.has(key)) {\n continue\n }\n\n seen.add(key)\n nextNamed.push(specifier)\n }\n\n if (isOwner && !hasNamedImport(nextNamed, required)) {\n nextNamed.push({ imported: required, local: required })\n }\n\n return nextNamed\n}\n\nfunction getExpectedRouteConstructor(lazy: boolean): RouteConstructorName {\n return lazy ? 'createLazyFileRoute' : 'createFileRoute'\n}\n\nfunction getOtherRouteConstructor(\n constructor: RouteConstructorName,\n): RouteConstructorName {\n return constructor === 'createFileRoute'\n ? 'createLazyFileRoute'\n : 'createFileRoute'\n}\n\nfunction hasNamedImport(\n named: Array<NamedImport>,\n required: RouteConstructorName,\n) {\n return named.some(\n (specifier) =>\n specifier.imported === required &&\n specifier.local === required &&\n specifier.importKind !== 'type',\n )\n}\n\nfunction sameNamedImports(left: Array<NamedImport>, right: Array<NamedImport>) {\n return (\n left.length === right.length &&\n left.every(\n (specifier, index) =>\n specifier.imported === right[index]!.imported &&\n specifier.local === right[index]!.local &&\n specifier.importKind === right[index]!.importKind,\n )\n )\n}\n\nfunction isRouteConstructorName(value: string): value is RouteConstructorName {\n return routeConstructors.includes(value as RouteConstructorName)\n}\n\nfunction renderImportDeclaration(importDeclaration: {\n defaultImport?: string\n namespace?: string\n named: Array<NamedImport>\n moduleName: string\n quote: '\"' | \"'\"\n semicolon: boolean\n}) {\n const parts: Array<string> = []\n\n if (importDeclaration.defaultImport) {\n parts.push(importDeclaration.defaultImport)\n }\n\n if (importDeclaration.namespace) {\n parts.push(`* as ${importDeclaration.namespace}`)\n }\n\n if (importDeclaration.named.length > 0) {\n parts.push(\n `{ ${importDeclaration.named\n .map(\n (specifier) =>\n `${specifier.importKind === 'type' ? 'type ' : ''}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`,\n )\n .join(', ')} }`,\n )\n }\n\n if (parts.length === 0) {\n return null\n }\n\n return `import ${parts.join(', ')} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ';' : ''}`\n}\n\nfunction getLineEnding(source: string): '\\r\\n' | '\\n' | '\\r' {\n if (source.includes('\\r\\n')) {\n return '\\r\\n'\n }\n\n if (source.includes('\\n')) {\n return '\\n'\n }\n\n if (source.includes('\\r')) {\n return '\\r'\n }\n\n return '\\n'\n}\n\nfunction getRemovalEnd(source: string, end: number) {\n let pos = end\n while (pos < source.length && (source[pos] === ' ' || source[pos] === '\\t')) {\n pos++\n }\n\n if (source[pos] === '\\r' && source[pos + 1] === '\\n') {\n return pos + 2\n }\n\n if (source[pos] === '\\n' || source[pos] === '\\r') {\n return pos + 1\n }\n\n return end\n}\n"],"mappings":"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"}

@@ -1,2 +0,2 @@

import { ImportDeclaration, RouteNode } from '../types.js';
import { RouteNode } from '../types.js';
import { Config } from '../config.js';

@@ -19,6 +19,2 @@ export interface TransformOptions {

};
export interface TransformImportsConfig {
banned?: Array<ImportDeclaration>;
required?: Array<ImportDeclaration>;
}
export interface TransformContext {

@@ -28,4 +24,2 @@ target: Config['target'];

lazy: boolean;
verboseFileRoutes: boolean;
preferredQuote?: '"' | "'";
}

@@ -10,3 +10,2 @@ import { Config, TokenMatcher } from './config.js';

private prefixToRoute;
private layoutRoutes;
constructor(routes: Array<RouteNode>);

@@ -84,3 +83,2 @@ /**

}): RegExp;
export declare function isBracketWrappedSegment(segment: string): boolean;
export declare function unwrapBracketWrappedSegment(segment: string): string;

@@ -166,14 +164,2 @@ export declare function removeLeadingUnderscores(s: string, routeToken: string): string;

/**
* Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
* Also asserts that the RouteNode is defined.
*
* @param routeNode - The RouteNode to check.
* @returns A boolean indicating whether the RouteNode is defined.
*/
export declare function isRouteNodeValidForAugmentation(routeNode?: RouteNode): routeNode is RouteNode;
/**
* Infers the path for use by TS
*/
export declare const inferPath: (routeNode: RouteNode) => string;
/**
* Infers the full path for use by TS

@@ -194,14 +180,5 @@ */

export declare const createRouteNodesById: (routeNodes: Array<RouteNode>) => Map<string, RouteNode>;
/**
* Infers to path
*/
export declare const inferTo: (routeNode: RouteNode) => string;
/**
* Dedupes branches and index routes
*/
export declare const dedupeBranchesAndIndexRoutes: (routes: Array<RouteNode>) => Array<RouteNode>;
export declare function checkRouteFullPathUniqueness(_routes: Array<RouteNode>, config: Config): void;
export declare function buildRouteTreeConfig(nodes: Array<RouteNode>, disableTypes: boolean, depth?: number): Array<string>;
export declare function buildImportString(importDeclaration: ImportDeclaration): string;
export declare function lowerCaseFirstChar(value: string): string;
export declare function mergeImportDeclarations(imports: Array<ImportDeclaration>): Array<ImportDeclaration>;

@@ -215,3 +192,2 @@ export declare const findParent: (node: RouteNode | undefined) => string;

}): string;
export declare function getImportPath(node: RouteNode, config: Config, generatedRouteTreePath: string): string;
export declare function getImportForRouteNode(node: RouteNode, config: Config, generatedRouteTreePath: string, root: string): ImportDeclaration;

@@ -14,3 +14,2 @@ import "./filesystem/physical/rootPathId.js";

this.prefixToRoute = /* @__PURE__ */ new Map();
this.layoutRoutes = [];
for (const route of routes) {

@@ -20,5 +19,3 @@ if (!route.routePath || route.routePath === `/__root`) continue;

this.prefixToRoute.set(route.routePath, route);
if (route._fsRouteType === "pathless_layout" || route._fsRouteType === "layout" || route._fsRouteType === "__root") this.layoutRoutes.push(route);
}
this.layoutRoutes.sort((a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0));
}

@@ -405,13 +402,2 @@ /**

/**
* Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
* Also asserts that the RouteNode is defined.
*
* @param routeNode - The RouteNode to check.
* @returns A boolean indicating whether the RouteNode is defined.
*/
function isRouteNodeValidForAugmentation(routeNode) {
if (!routeNode || routeNode.isVirtual) return false;
return true;
}
/**
* Infers the path for use by TS

@@ -610,4 +596,4 @@ */

//#endregion
export { RoutePrefixMap, buildFileRoutesByPathInterface, buildImportString, buildRouteTreeConfig, capitalize, checkFileExists, checkRouteFullPathUniqueness, cleanPath, createRouteNodesByFullPath, createRouteNodesById, createRouteNodesByTo, createTokenRegex, determineInitialRoutePath, determineNodePath, escapeRegExp, findParent, format, getImportForRouteNode, getImportPath, getResolvedRouteNodeVariableName, hasEscapedLeadingUnderscore, hasParentRoute, inferFullPath, isRouteNodeValidForAugmentation, isSegmentPathless, mergeImportDeclarations, multiSortBy, removeExt, removeGroups, removeLastSegmentFromPath, removeLayoutSegmentsWithEscape, removeLeadingSlash, removeTrailingSlash, removeUnderscores, removeUnderscoresWithEscape, replaceBackslash, resetRegex, routePathToVariable, trimPathLeft, unwrapBracketWrappedSegment, writeIfDifferent };
export { RoutePrefixMap, buildFileRoutesByPathInterface, buildImportString, buildRouteTreeConfig, capitalize, checkFileExists, checkRouteFullPathUniqueness, cleanPath, createRouteNodesByFullPath, createRouteNodesById, createRouteNodesByTo, createTokenRegex, determineInitialRoutePath, determineNodePath, escapeRegExp, findParent, format, getImportForRouteNode, getResolvedRouteNodeVariableName, hasEscapedLeadingUnderscore, hasParentRoute, inferFullPath, isSegmentPathless, mergeImportDeclarations, multiSortBy, removeExt, removeGroups, removeLastSegmentFromPath, removeLayoutSegmentsWithEscape, removeLeadingSlash, removeTrailingSlash, removeUnderscores, removeUnderscoresWithEscape, replaceBackslash, resetRegex, routePathToVariable, trimPathLeft, unwrapBracketWrappedSegment, writeIfDifferent };
//# sourceMappingURL=utils.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n private layoutRoutes: Array<RouteNode> = []\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n\n if (\n route._fsRouteType === 'pathless_layout' ||\n route._fsRouteType === 'layout' ||\n route._fsRouteType === '__root'\n ) {\n this.layoutRoutes.push(route)\n }\n }\n\n // Sort by path length descending for longest-match-first\n this.layoutRoutes.sort(\n (a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),\n )\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nexport function isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"mappings":"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"}
{"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n }\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nfunction isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Infers the path for use by TS\n */\nconst inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nconst inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nconst dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nfunction getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"mappings":"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"}
{
"name": "@tanstack/router-generator",
"version": "1.166.29",
"version": "1.166.30",
"description": "Modern and scalable routing for React applications",

@@ -52,5 +52,5 @@ "author": "Tanner Linsley",

"dependencies": {
"@babel/types": "^7.28.5",
"magic-string": "^0.30.21",
"prettier": "^3.5.0",
"recast": "^0.23.11",
"source-map": "^0.7.4",
"tsx": "^4.19.2",

@@ -65,3 +65,3 @@ "zod": "^3.24.2",

"@types/node": ">=20",
"@tanstack/react-router": "1.168.18"
"@tanstack/react-router": "1.168.19"
},

@@ -68,0 +68,0 @@ "scripts": {

@@ -52,3 +52,2 @@ import path from 'node:path'

disableTypes: z.boolean().optional().default(false),
verboseFileRoutes: z.boolean().optional(),
addExtensions: z

@@ -55,0 +54,0 @@ .union([z.boolean(), z.string()])

@@ -28,6 +28,4 @@ import path from 'node:path'

getImportForRouteNode,
getImportPath,
getResolvedRouteNodeVariableName,
hasParentRoute,
isRouteNodeValidForAugmentation,
isSegmentPathless,

@@ -662,34 +660,2 @@ mergeImportDeclarations,

}
if (config.verboseFileRoutes === false) {
const typeImport: ImportDeclaration = {
specifiers: [],
source: this.targetTemplate.fullPkg,
importKind: 'type',
}
let needsCreateFileRoute = false
let needsCreateLazyFileRoute = false
for (const node of sortedRouteNodes) {
if (isRouteNodeValidForAugmentation(node)) {
if (node._fsRouteType !== 'lazy') {
needsCreateFileRoute = true
}
if (acc.routePiecesByPath[node.routePath!]?.lazy) {
needsCreateLazyFileRoute = true
}
}
if (needsCreateFileRoute && needsCreateLazyFileRoute) break
}
if (needsCreateFileRoute) {
typeImport.specifiers.push({ imported: 'CreateFileRoute' })
}
if (needsCreateLazyFileRoute) {
typeImport.specifiers.push({ imported: 'CreateLazyFileRoute' })
}
if (typeImport.specifiers.length > 0) {
typeImport.specifiers.push({ imported: 'FileRoutesByPath' })
imports.push(typeImport)
}
}
const routeTreeConfig = buildRouteTreeConfig(

@@ -968,32 +934,2 @@ acc.routeTree,

let moduleAugmentation = ''
if (config.verboseFileRoutes === false && !config.disableTypes) {
moduleAugmentation = opts.routeFileResult
.map((node) => {
const getModuleDeclaration = (routeNode?: RouteNode) => {
if (!isRouteNodeValidForAugmentation(routeNode)) {
return ''
}
let moduleAugmentation = ''
if (routeNode._fsRouteType === 'lazy') {
moduleAugmentation = `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`
} else {
moduleAugmentation = `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',
FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
FileRoutesByPath['${routeNode.routePath}']['id'],
FileRoutesByPath['${routeNode.routePath}']['path'],
FileRoutesByPath['${routeNode.routePath}']['fullPath']
>
`
}
return `declare module './${getImportPath(routeNode, config, this.generatedRouteTreePath)}' {
${moduleAugmentation}
}`
}
return getModuleDeclaration(node)
})
.join('\n')
}
const rootRouteImport = getImportForRouteNode(

@@ -1026,3 +962,2 @@ rootRouteNode,

fileRoutesByPathInterface,
moduleAugmentation,
routeTreeConfig.join('\n'),

@@ -1145,3 +1080,2 @@ routeTree,

lazy: node._fsRouteType === 'lazy',
verboseFileRoutes: !(this.config.verboseFileRoutes === false),
},

@@ -1148,0 +1082,0 @@ node,

@@ -48,8 +48,2 @@ export {

export { ensureStringArgument } from './transform/utils'
export type {
TransformImportsConfig,
TransformContext,
TransformOptions,
} from './transform/types'
export type { TransformContext, TransformOptions } from './transform/types'

@@ -47,2 +47,6 @@ import { format } from './utils'

function serializeRoutePath(routePath: string) {
return JSON.stringify(routePath)
}
export function getTargetTemplate(config: Config): TargetTemplate {

@@ -81,9 +85,5 @@ const target = config.target

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createFileRoute } from '@tanstack/react-router';",
"import { createFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createFileRoute('
: `export const Route = createFileRoute('${routePath}')(`,
`export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ');',

@@ -102,9 +102,5 @@ },

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createLazyFileRoute } from '@tanstack/react-router';",
"import { createLazyFileRoute } from '@tanstack/react-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createLazyFileRoute('
: `export const Route = createLazyFileRoute('${routePath}')(`,
`export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ');',

@@ -144,9 +140,5 @@ },

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createFileRoute } from '@tanstack/solid-router';",
"import { createFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createFileRoute('
: `export const Route = createFileRoute('${routePath}')(`,
`export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ');',

@@ -165,10 +157,6 @@ },

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createLazyFileRoute } from '@tanstack/solid-router';",
"import { createLazyFileRoute } from '@tanstack/solid-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createLazyFileRoute('
: `export const Route = createLazyFileRoute('${routePath}')(`,
`export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,

@@ -210,9 +198,5 @@ tsrExportEnd: () => ');',

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createFileRoute } from '@tanstack/vue-router';",
"import { createFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createFileRoute('
: `export const Route = createFileRoute('${routePath}')(`,
`export const Route = createFileRoute(${serializeRoutePath(routePath)})(`,
tsrExportEnd: () => ');',

@@ -232,10 +216,6 @@ },

tsrImports: () =>
config.verboseFileRoutes === false
? ''
: "import { createLazyFileRoute } from '@tanstack/vue-router';",
"import { createLazyFileRoute } from '@tanstack/vue-router';",
tsrExportStart: (routePath) =>
config.verboseFileRoutes === false
? 'export const Route = createLazyFileRoute('
: `export const Route = createLazyFileRoute('${routePath}')(`,
`export const Route = createLazyFileRoute(${serializeRoutePath(routePath)})(`,

@@ -242,0 +222,0 @@ tsrExportEnd: () => ');',

@@ -0,529 +1,716 @@

import MagicString from 'magic-string'
import * as t from '@babel/types'
import { parseAst } from '@tanstack/router-utils'
import { parse, print, types, visit } from 'recast'
import { SourceMapConsumer } from 'source-map'
import { mergeImportDeclarations } from '../utils'
import { ensureStringArgument } from './utils'
import type { ImportDeclaration } from '../types'
import type { RawSourceMap } from 'source-map'
import type { TransformOptions, TransformResult } from './types'
const b = types.builders
const routeConstructors = ['createFileRoute', 'createLazyFileRoute'] as const
export async function transform({
type RouteConstructorName = (typeof routeConstructors)[number]
type SupportedRouteId = t.StringLiteral | t.TemplateLiteral
type NamedImport = {
imported: string
local: string
importKind?: 'type' | 'typeof' | 'value'
}
type ParsedImportDeclaration = {
declaration: t.ImportDeclaration
defaultImport?: string
namespace?: string
named: Array<NamedImport>
moduleName: string
quote: '"' | "'"
semicolon: boolean
}
type RouteImportAnalysis =
| { kind: 'ok' }
| {
kind: 'rename'
imported: t.Identifier
local: t.Identifier
next: RouteConstructorName
}
| { kind: 'normalize' }
type RouteCall = {
callee: t.Identifier & { name: RouteConstructorName }
routeIdArg: SupportedRouteId
optionsArg: t.CallExpression['arguments'][number] | undefined
}
type RouteCallAnalysis = {
calls: Array<RouteCall>
hasUnsupportedRouteId: boolean
hasMalformedRouteCall: boolean
}
export function transform({
ctx,
source,
node,
}: TransformOptions): Promise<TransformResult> {
let appliedChanges = false as boolean
let ast: types.namedTypes.File
}: TransformOptions): TransformResult {
let ast: ReturnType<typeof parseAst>
try {
ast = parse(source, {
sourceFileName: 'output.ts',
parser: {
parse(code: string) {
return parseAst({
code,
// we need to instruct babel to produce tokens,
// otherwise recast will try to generate the tokens via its own parser and will fail
tokens: true,
})
},
},
})
} catch (e) {
console.error('Error parsing code', ctx.routeId, source, e)
ast = parseAst({ code: source })
} catch (error) {
return {
result: 'error',
error: e,
error,
}
}
const preferredQuote = detectPreferredQuoteStyle(ast)
const exportedRouteNames = getExportedRouteNames(ast.program.body)
let routeExportHandled = false as boolean
function onExportFound(decl: types.namedTypes.VariableDeclarator) {
if (decl.init?.type === 'CallExpression') {
const callExpression = decl.init
const firstArgument = callExpression.arguments[0]
if (firstArgument) {
if (firstArgument.type === 'ObjectExpression') {
const staticProperties = firstArgument.properties.flatMap((p) => {
if (p.type === 'ObjectProperty' && p.key.type === 'Identifier') {
return p.key.name
}
return []
})
node.createFileRouteProps = new Set(staticProperties)
}
}
let identifier: types.namedTypes.Identifier | undefined
// `const Route = createFileRoute({ ... })`
if (callExpression.callee.type === 'Identifier') {
identifier = callExpression.callee
if (ctx.verboseFileRoutes) {
// we need to add the string literal via another CallExpression
callExpression.callee = b.callExpression(identifier, [
b.stringLiteral(ctx.routeId),
])
appliedChanges = true
}
}
// `const Route = createFileRoute('/path')({ ... })`
else if (
callExpression.callee.type === 'CallExpression' &&
callExpression.callee.callee.type === 'Identifier'
) {
identifier = callExpression.callee.callee
if (!ctx.verboseFileRoutes) {
// we need to remove the route id
callExpression.callee = identifier
appliedChanges = true
} else {
// check if the route id is correct
appliedChanges = ensureStringArgument(
callExpression.callee,
ctx.routeId,
ctx.preferredQuote,
)
}
}
if (identifier === undefined) {
throw new Error(
`expected identifier to be present in ${ctx.routeId} for export "Route"`,
)
}
if (identifier.name === 'createFileRoute' && ctx.lazy) {
identifier.name = 'createLazyFileRoute'
appliedChanges = true
} else if (identifier.name === 'createLazyFileRoute' && !ctx.lazy) {
identifier.name = 'createFileRoute'
appliedChanges = true
}
} else {
throw new Error(
`expected "Route" export to be initialized by a CallExpression`,
)
if (exportedRouteNames.size === 0) {
return { result: 'no-route-export' }
}
const {
calls: routeCalls,
hasUnsupportedRouteId,
hasMalformedRouteCall,
} = findExportedRouteCalls(ast.program.body, exportedRouteNames)
if (routeCalls.length === 0 && hasMalformedRouteCall) {
return {
result: 'error',
error: new Error(
`expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`,
),
}
routeExportHandled = true
}
const program: types.namedTypes.Program = ast.program
// first pass: find Route export
for (const n of program.body) {
if (n.type === 'ExportNamedDeclaration') {
// direct export of a variable declaration, e.g. `export const Route = createFileRoute('/path')`
if (n.declaration?.type === 'VariableDeclaration') {
const decl = n.declaration.declarations[0]
if (
decl &&
decl.type === 'VariableDeclarator' &&
decl.id.type === 'Identifier'
) {
if (decl.id.name === 'Route') {
onExportFound(decl)
}
}
}
// this is an export without a declaration, e.g. `export { Route }`
else if (n.declaration === null && n.specifiers) {
for (const spec of n.specifiers) {
if (typeof spec.exported.name === 'string') {
if (spec.exported.name === 'Route') {
const variableName = spec.local?.name || spec.exported.name
// find the matching variable declaration by iterating over the top-level declarations
for (const decl of program.body) {
if (
decl.type === 'VariableDeclaration' &&
decl.declarations[0]
) {
const variable = decl.declarations[0]
if (
variable.type === 'VariableDeclarator' &&
variable.id.type === 'Identifier' &&
variable.id.name === variableName
) {
onExportFound(variable)
break
}
}
}
}
}
}
}
if (routeCalls.length === 0 && hasUnsupportedRouteId) {
return {
result: 'error',
error: new Error(
`expected route id to be a string literal or plain template literal in ${ctx.routeId}`,
),
}
if (routeExportHandled) {
break
}
}
if (!routeExportHandled) {
if (routeCalls.length === 0) {
return { result: 'not-modified' }
}
if (routeCalls.length > 1) {
return {
result: 'no-route-export',
result: 'error',
error: new Error(
`expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`,
),
}
}
const imports: {
required: Array<ImportDeclaration>
banned: Array<ImportDeclaration>
} = {
required: [],
banned: [],
const routeCall = routeCalls[0]!
const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg)
const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg)
if (createFileRouteProps) {
node.createFileRouteProps = createFileRouteProps
}
const expectedCallee = getExpectedRouteConstructor(ctx.lazy)
const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`
const currentRouteId = source.slice(
routeCall.routeIdArg.start!,
routeCall.routeIdArg.end!,
)
const targetModule = `@tanstack/${ctx.target}-router`
if (ctx.verboseFileRoutes === false) {
imports.banned = [
{
source: targetModule,
specifiers: [
{ imported: 'createLazyFileRoute' },
{ imported: 'createFileRoute' },
],
},
]
} else {
if (ctx.lazy) {
imports.required = [
{
source: targetModule,
specifiers: [{ imported: 'createLazyFileRoute' }],
},
]
imports.banned = [
{
source: targetModule,
specifiers: [{ imported: 'createFileRoute' }],
},
]
} else {
imports.required = [
{
source: targetModule,
specifiers: [{ imported: 'createFileRoute' }],
},
]
imports.banned = [
{
source: targetModule,
specifiers: [{ imported: 'createLazyFileRoute' }],
},
]
}
const imports = parseTargetImports(ast.program.body, source, targetModule)
const s = new MagicString(source)
let modified = false
if (routeCall.callee.name !== expectedCallee) {
s.update(routeCall.callee.start!, routeCall.callee.end!, expectedCallee)
modified = true
}
imports.required = mergeImportDeclarations(imports.required)
imports.banned = mergeImportDeclarations(imports.banned)
if (currentRouteId !== expectedRouteId) {
s.update(
routeCall.routeIdArg.start!,
routeCall.routeIdArg.end!,
expectedRouteId,
)
modified = true
}
const importStatementCandidates: Array<types.namedTypes.ImportDeclaration> =
[]
const importDeclarationsToRemove: Array<types.namedTypes.ImportDeclaration> =
[]
if (
updateRouteImports({
imports,
source,
s,
targetModule,
required: expectedCallee,
lineEnding: getLineEnding(source),
})
) {
modified = true
}
// second pass: apply import rules, but only if a matching export for the plugin was found
for (const n of program.body) {
const findImport =
(opts: { source: string; importKind?: 'type' | 'value' | 'typeof' }) =>
(i: ImportDeclaration) => {
if (i.source === opts.source) {
const importKind = i.importKind || 'value'
const expectedImportKind = opts.importKind || 'value'
return expectedImportKind === importKind
if (!modified) {
return { result: 'not-modified' }
}
return {
result: 'modified',
output: s.toString(),
}
}
function getExportedRouteNames(body: Array<t.Statement>) {
const exportedRouteNames = new Set<string>()
for (const statement of body) {
if (!t.isExportNamedDeclaration(statement) || statement.source) {
continue
}
if (t.isVariableDeclaration(statement.declaration)) {
for (const declarator of statement.declaration.declarations) {
if (t.isIdentifier(declarator.id) && declarator.id.name === 'Route') {
exportedRouteNames.add('Route')
}
return false
}
if (n.type === 'ImportDeclaration' && typeof n.source.value === 'string') {
const filterImport = findImport({
source: n.source.value,
importKind: n.importKind,
})
let requiredImports = imports.required.filter(filterImport)[0]
}
const bannedImports = imports.banned.filter(filterImport)[0]
if (!requiredImports && !bannedImports) {
for (const specifier of statement.specifiers) {
if (
!t.isExportSpecifier(specifier) ||
getExportedName(specifier.exported) !== 'Route'
) {
continue
}
const importSpecifiersToRemove: types.namedTypes.ImportDeclaration['specifiers'] =
[]
if (n.specifiers) {
for (const spec of n.specifiers) {
if (!requiredImports && !bannedImports) {
break
}
if (
spec.type === 'ImportSpecifier' &&
typeof spec.imported.name === 'string'
) {
if (requiredImports) {
const requiredImportIndex = requiredImports.specifiers.findIndex(
(imp) => imp.imported === spec.imported.name,
)
if (requiredImportIndex !== -1) {
// import is already present, remove it from requiredImports
requiredImports.specifiers.splice(requiredImportIndex, 1)
if (requiredImports.specifiers.length === 0) {
imports.required = imports.required.splice(
imports.required.indexOf(requiredImports),
1,
)
requiredImports = undefined
}
} else {
// add the import statement to the candidates
importStatementCandidates.push(n)
}
}
if (bannedImports) {
const bannedImportIndex = bannedImports.specifiers.findIndex(
(imp) => imp.imported === spec.imported.name,
)
if (bannedImportIndex !== -1) {
importSpecifiersToRemove.push(spec)
}
}
}
}
if (importSpecifiersToRemove.length > 0) {
appliedChanges = true
n.specifiers = n.specifiers.filter(
(spec) => !importSpecifiersToRemove.includes(spec),
)
// mark the import statement as to be deleted if it is now empty
if (n.specifiers.length === 0) {
importDeclarationsToRemove.push(n)
}
}
const localName = getLocalBindingName(specifier.local)
if (localName) {
exportedRouteNames.add(localName)
}
}
}
imports.required.forEach((requiredImport) => {
if (requiredImport.specifiers.length > 0) {
appliedChanges = true
if (importStatementCandidates.length > 0) {
// find the first import statement that matches both the module and the import kind
const importStatement = importStatementCandidates.find(
(importStatement) => {
if (importStatement.source.value === requiredImport.source) {
const importKind = importStatement.importKind || 'value'
const requiredImportKind = requiredImport.importKind || 'value'
return importKind === requiredImportKind
}
return false
},
)
if (importStatement) {
if (importStatement.specifiers === undefined) {
importStatement.specifiers = []
}
const importSpecifiersToAdd = requiredImport.specifiers.map((spec) =>
b.importSpecifier(
b.identifier(spec.imported),
b.identifier(spec.imported),
),
)
importStatement.specifiers = [
...importStatement.specifiers,
...importSpecifiersToAdd,
]
return
}
return exportedRouteNames
}
function findExportedRouteCalls(
body: Array<t.Statement>,
exportedRouteNames: Set<string>,
): RouteCallAnalysis {
const calls: Array<RouteCall> = []
let hasUnsupportedRouteId = false
let hasMalformedRouteCall = false
for (const statement of body) {
const declaration = getVariableDeclaration(statement)
if (!declaration) {
continue
}
for (const declarator of declaration.declarations) {
if (
!t.isIdentifier(declarator.id) ||
!exportedRouteNames.has(declarator.id.name)
) {
continue
}
const importStatement = b.importDeclaration(
requiredImport.specifiers.map((spec) =>
b.importSpecifier(
b.identifier(spec.imported),
spec.local ? b.identifier(spec.local) : null,
),
),
b.stringLiteral(requiredImport.source),
)
program.body.unshift(importStatement)
}
})
if (importDeclarationsToRemove.length > 0) {
appliedChanges = true
for (const importDeclaration of importDeclarationsToRemove) {
// check if the import declaration is still empty
if (importDeclaration.specifiers?.length === 0) {
const index = program.body.indexOf(importDeclaration)
if (index !== -1) {
program.body.splice(index, 1)
const init = getRouteConstructorInit(declarator.init)
if (!init) {
if (isDirectRouteConstructorCall(declarator.init)) {
hasMalformedRouteCall = true
}
continue
}
const routeIdArg = init.innerCall.arguments[0]
if (isSupportedRouteId(routeIdArg)) {
calls.push({
callee: init.callee,
routeIdArg,
optionsArg: init.outerCall.arguments[0],
})
} else {
hasUnsupportedRouteId = true
}
}
}
if (!appliedChanges) {
return {
result: 'not-modified',
}
return { calls, hasUnsupportedRouteId, hasMalformedRouteCall }
}
function getVariableDeclaration(statement: t.Statement) {
const declaration = t.isExportNamedDeclaration(statement)
? statement.declaration
: statement
return t.isVariableDeclaration(declaration) ? declaration : null
}
function getExportedName(node: t.Identifier | t.StringLiteral) {
return t.isIdentifier(node) ? node.name : node.value
}
function getLocalBindingName(node: t.Identifier | t.StringLiteral) {
return t.isIdentifier(node) ? node.name : null
}
function getRouteConstructorInit(expression: t.Expression | null | undefined) {
if (!expression || !t.isCallExpression(expression)) {
return null
}
const printResult = print(ast, {
reuseWhitespace: true,
sourceMapName: 'output.map',
})
let transformedCode = printResult.code
if (printResult.map) {
const fixedOutput = await fixTransformedOutputText({
originalCode: source,
transformedCode,
sourceMap: printResult.map as RawSourceMap,
preferredQuote,
})
transformedCode = fixedOutput
if (!t.isCallExpression(expression.callee)) {
return null
}
const innerCall = expression.callee
if (
!t.isIdentifier(innerCall.callee) ||
!isRouteConstructor(innerCall.callee)
) {
return null
}
return {
result: 'modified',
output: transformedCode,
callee: innerCall.callee,
outerCall: expression,
innerCall,
}
}
async function fixTransformedOutputText({
originalCode,
transformedCode,
sourceMap,
preferredQuote,
function isDirectRouteConstructorCall(
expression: t.Expression | null | undefined,
) {
return (
!!expression &&
t.isCallExpression(expression) &&
t.isIdentifier(expression.callee) &&
isRouteConstructor(expression.callee)
)
}
function isRouteConstructor(callee: t.Identifier): callee is t.Identifier & {
name: RouteConstructorName
} {
return routeConstructors.includes(callee.name as RouteConstructorName)
}
function isSupportedRouteId(
arg: t.CallExpression['arguments'][number] | undefined,
): arg is SupportedRouteId {
return (
!!arg &&
(t.isStringLiteral(arg) ||
(t.isTemplateLiteral(arg) && arg.expressions.length === 0))
)
}
function getRouteIdQuote(
source: string,
arg: SupportedRouteId,
): '"' | "'" | '`' {
const raw = source.slice(arg.start!, arg.end!)
if (raw.startsWith("'")) return "'"
if (raw.startsWith('"')) return '"'
return '`'
}
function getCreateFileRouteProps(
arg: t.CallExpression['arguments'][number] | undefined,
) {
if (!arg || !t.isObjectExpression(arg)) {
return undefined
}
const props = new Set<string>()
for (const property of arg.properties) {
if (!t.isObjectProperty(property) || property.computed) {
continue
}
if (t.isIdentifier(property.key)) {
props.add(property.key.name)
continue
}
if (t.isStringLiteral(property.key)) {
props.add(property.key.value)
}
}
return props
}
function parseTargetImports(
body: Array<t.Statement>,
source: string,
targetModule: string,
) {
const imports: Array<ParsedImportDeclaration> = []
for (const statement of body) {
if (
!t.isImportDeclaration(statement) ||
statement.importKind === 'type' ||
statement.source.value !== targetModule
) {
continue
}
const rawSource = source.slice(
statement.source.start!,
statement.source.end!,
)
const importStatement = source.slice(statement.start!, statement.end!)
imports.push({
declaration: statement,
defaultImport: statement.specifiers.find((specifier) =>
t.isImportDefaultSpecifier(specifier),
)?.local.name,
namespace: statement.specifiers.find((specifier) =>
t.isImportNamespaceSpecifier(specifier),
)?.local.name,
named: statement.specifiers
.filter((specifier): specifier is t.ImportSpecifier =>
t.isImportSpecifier(specifier),
)
.map((specifier) => ({
imported: t.isIdentifier(specifier.imported)
? specifier.imported.name
: specifier.imported.value,
local: specifier.local.name,
importKind: specifier.importKind ?? undefined,
})),
moduleName: statement.source.value,
quote: rawSource[0] as '"' | "'",
semicolon: importStatement.trimEnd().endsWith(';'),
})
}
return imports
}
function updateRouteImports({
imports,
source,
s,
targetModule,
required,
lineEnding,
}: {
originalCode: string
transformedCode: string
sourceMap: RawSourceMap
preferredQuote: '"' | "'"
imports: Array<ParsedImportDeclaration>
source: string
s: MagicString
targetModule: string
required: RouteConstructorName
lineEnding: '\r\n' | '\n' | '\r'
}) {
const originalLines = originalCode.split('\n')
const transformedLines = transformedCode.split('\n')
const analysis = analyzeRouteImports(imports, required)
const defaultUsesSemicolons = detectSemicolonUsage(originalCode)
if (analysis.kind === 'ok') {
return false
}
const consumer = await new SourceMapConsumer(sourceMap)
if (analysis.kind === 'rename') {
s.update(analysis.imported.start!, analysis.imported.end!, analysis.next)
s.update(analysis.local.start!, analysis.local.end!, analysis.next)
return true
}
const fixedLines = transformedLines.map((line, i) => {
const transformedLineNum = i + 1
return normalizeRouteImports({
imports,
source,
s,
targetModule,
required,
lineEnding,
})
}
let origLineText: string | undefined = undefined
function analyzeRouteImports(
imports: Array<ParsedImportDeclaration>,
required: RouteConstructorName,
): RouteImportAnalysis {
const opposite = getOtherRouteConstructor(required)
let requiredCount = 0
let oppositeCount = 0
let renameCandidate:
| {
imported: t.Identifier
local: t.Identifier
next: RouteConstructorName
}
| undefined
for (let col = 0; col < line.length; col++) {
const mapped = consumer.originalPositionFor({
line: transformedLineNum,
column: col,
})
if (mapped.line != null && mapped.line > 0) {
origLineText = originalLines[mapped.line - 1]
break
for (const declaration of imports) {
for (const specifier of declaration.declaration.specifiers) {
if (!t.isImportSpecifier(specifier)) {
continue
}
}
if (origLineText !== undefined) {
if (origLineText === line) {
return origLineText
const imported = specifier.imported
if (!t.isIdentifier(imported)) {
return { kind: 'normalize' }
}
return fixLine(line, {
originalLine: origLineText,
useOriginalSemicolon: true,
useOriginalQuotes: true,
fallbackQuote: preferredQuote,
})
} else {
return fixLine(line, {
originalLine: null,
useOriginalSemicolon: false,
useOriginalQuotes: false,
fallbackQuote: preferredQuote,
fallbackSemicolon: defaultUsesSemicolons,
})
if (!isRouteConstructorName(imported.name)) {
continue
}
if (specifier.local.name !== imported.name) {
return { kind: 'normalize' }
}
if (imported.name === required) {
requiredCount++
continue
}
if (imported.name === opposite) {
oppositeCount++
renameCandidate = {
imported,
local: specifier.local,
next: required,
}
}
}
})
}
return fixedLines.join('\n')
if (requiredCount === 1 && oppositeCount === 0) {
return { kind: 'ok' }
}
if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) {
return {
kind: 'rename',
...renameCandidate,
}
}
return { kind: 'normalize' }
}
function fixLine(
line: string,
{
originalLine,
useOriginalSemicolon,
useOriginalQuotes,
fallbackQuote,
fallbackSemicolon = true,
}: {
originalLine: string | null
useOriginalSemicolon: boolean
useOriginalQuotes: boolean
fallbackQuote: string
fallbackSemicolon?: boolean
},
) {
let result = line
function normalizeRouteImports({
imports,
source,
s,
targetModule,
required,
lineEnding,
}: {
imports: Array<ParsedImportDeclaration>
source: string
s: MagicString
targetModule: string
required: RouteConstructorName
lineEnding: '\r\n' | '\n' | '\r'
}) {
const owner =
imports.find((declaration) =>
hasNamedImport(declaration.named, required),
) ?? imports.find((declaration) => !declaration.namespace)
if (useOriginalQuotes && originalLine) {
result = fixQuotes(result, originalLine, fallbackQuote)
} else if (!useOriginalQuotes && fallbackQuote) {
result = fixQuotesToPreferred(result, fallbackQuote)
let modified = false
for (const declaration of imports) {
const named = normalizeNamedImports({
named: declaration.named,
required,
isOwner: declaration === owner,
})
if (sameNamedImports(declaration.named, named)) {
continue
}
const replacement = renderImportDeclaration({
...declaration,
named,
})
if (replacement === null) {
s.remove(
declaration.declaration.start!,
getRemovalEnd(source, declaration.declaration.end!),
)
modified = true
continue
}
s.update(
declaration.declaration.start!,
declaration.declaration.end!,
replacement,
)
modified = true
}
if (useOriginalSemicolon && originalLine) {
const hadSemicolon = originalLine.trimEnd().endsWith(';')
const hasSemicolon = result.trimEnd().endsWith(';')
if (hadSemicolon && !hasSemicolon) result += ';'
if (!hadSemicolon && hasSemicolon) result = result.replace(/;\s*$/, '')
} else if (!useOriginalSemicolon) {
const hasSemicolon = result.trimEnd().endsWith(';')
if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\s*$/, '')
if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ';'
if (!owner) {
const quote = imports[0]?.quote ?? "'"
const semicolon = imports[0]?.semicolon ?? false
s.prepend(
`import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ';' : ''}${lineEnding}`,
)
modified = true
}
return result
return modified
}
function fixQuotes(line: string, originalLine: string, fallbackQuote: string) {
let originalQuote = detectQuoteFromLine(originalLine)
if (!originalQuote) {
originalQuote = fallbackQuote
function normalizeNamedImports({
named,
required,
isOwner,
}: {
named: Array<NamedImport>
required: RouteConstructorName
isOwner: boolean
}) {
const banned = getOtherRouteConstructor(required)
const nextNamed: Array<NamedImport> = []
const seen = new Set<string>()
for (const specifier of named) {
if (specifier.imported === banned) {
continue
}
if (
specifier.local === required &&
(specifier.imported !== required || !isOwner)
) {
continue
}
const key = `${specifier.importKind ?? 'value'}:${specifier.imported}:${specifier.local}`
if (seen.has(key)) {
continue
}
seen.add(key)
nextNamed.push(specifier)
}
return fixQuotesToPreferred(line, originalQuote)
if (isOwner && !hasNamedImport(nextNamed, required)) {
nextNamed.push({ imported: required, local: required })
}
return nextNamed
}
function fixQuotesToPreferred(line: string, quote: string) {
// Replace existing quotes with preferred quote
return line.replace(
/(['"`])([^'"`\\]*(?:\\.[^'"`\\]*)*)\1/g,
(_, q, content) => {
const escaped = content.replaceAll(quote, `\\${quote}`)
return `${quote}${escaped}${quote}`
},
function getExpectedRouteConstructor(lazy: boolean): RouteConstructorName {
return lazy ? 'createLazyFileRoute' : 'createFileRoute'
}
function getOtherRouteConstructor(
constructor: RouteConstructorName,
): RouteConstructorName {
return constructor === 'createFileRoute'
? 'createLazyFileRoute'
: 'createFileRoute'
}
function hasNamedImport(
named: Array<NamedImport>,
required: RouteConstructorName,
) {
return named.some(
(specifier) =>
specifier.imported === required &&
specifier.local === required &&
specifier.importKind !== 'type',
)
}
function detectQuoteFromLine(line: string) {
const match = line.match(/(['"`])(?:\\.|[^\\])*?\1/)
return match ? match[1] : null
function sameNamedImports(left: Array<NamedImport>, right: Array<NamedImport>) {
return (
left.length === right.length &&
left.every(
(specifier, index) =>
specifier.imported === right[index]!.imported &&
specifier.local === right[index]!.local &&
specifier.importKind === right[index]!.importKind,
)
)
}
function detectSemicolonUsage(code: string) {
const lines = code.split('\n').map((l) => l.trim())
const total = lines.length
const withSemis = lines.filter((l) => l.endsWith(';')).length
return withSemis > total / 2
function isRouteConstructorName(value: string): value is RouteConstructorName {
return routeConstructors.includes(value as RouteConstructorName)
}
export function detectPreferredQuoteStyle(ast: types.ASTNode): "'" | '"' {
let single = 0
let double = 0
function renderImportDeclaration(importDeclaration: {
defaultImport?: string
namespace?: string
named: Array<NamedImport>
moduleName: string
quote: '"' | "'"
semicolon: boolean
}) {
const parts: Array<string> = []
visit(ast, {
visitStringLiteral(path) {
if (path.parent.node.type !== 'JSXAttribute') {
const raw = path.node.extra?.raw
if (raw?.startsWith("'")) single++
else if (raw?.startsWith('"')) double++
}
return false
},
})
if (importDeclaration.defaultImport) {
parts.push(importDeclaration.defaultImport)
}
if (single >= double) {
return "'"
if (importDeclaration.namespace) {
parts.push(`* as ${importDeclaration.namespace}`)
}
return '"'
if (importDeclaration.named.length > 0) {
parts.push(
`{ ${importDeclaration.named
.map(
(specifier) =>
`${specifier.importKind === 'type' ? 'type ' : ''}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`,
)
.join(', ')} }`,
)
}
if (parts.length === 0) {
return null
}
return `import ${parts.join(', ')} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ';' : ''}`
}
function getLineEnding(source: string): '\r\n' | '\n' | '\r' {
if (source.includes('\r\n')) {
return '\r\n'
}
if (source.includes('\n')) {
return '\n'
}
if (source.includes('\r')) {
return '\r'
}
return '\n'
}
function getRemovalEnd(source: string, end: number) {
let pos = end
while (pos < source.length && (source[pos] === ' ' || source[pos] === '\t')) {
pos++
}
if (source[pos] === '\r' && source[pos + 1] === '\n') {
return pos + 2
}
if (source[pos] === '\n' || source[pos] === '\r') {
return pos + 1
}
return end
}

@@ -1,2 +0,2 @@

import type { ImportDeclaration, RouteNode } from '../types'
import type { RouteNode } from '../types'
import type { Config } from '../config'

@@ -26,7 +26,2 @@

export interface TransformImportsConfig {
banned?: Array<ImportDeclaration>
required?: Array<ImportDeclaration>
}
export interface TransformContext {

@@ -36,4 +31,2 @@ target: Config['target']

lazy: boolean
verboseFileRoutes: boolean
preferredQuote?: '"' | "'"
}

@@ -16,3 +16,2 @@ /* eslint-disable @typescript-eslint/prefer-for-of */

private prefixToRoute: Map<string, RouteNode> = new Map()
private layoutRoutes: Array<RouteNode> = []

@@ -38,16 +37,3 @@ constructor(routes: Array<RouteNode>) {

this.prefixToRoute.set(route.routePath, route)
if (
route._fsRouteType === 'pathless_layout' ||
route._fsRouteType === 'layout' ||
route._fsRouteType === '__root'
) {
this.layoutRoutes.push(route)
}
}
// Sort by path length descending for longest-match-first
this.layoutRoutes.sort(
(a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),
)
}

@@ -487,3 +473,3 @@

export function isBracketWrappedSegment(segment: string): boolean {
function isBracketWrappedSegment(segment: string): boolean {
return segment.startsWith('[') && segment.endsWith(']')

@@ -692,21 +678,5 @@ }

/**
* Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
* Also asserts that the RouteNode is defined.
*
* @param routeNode - The RouteNode to check.
* @returns A boolean indicating whether the RouteNode is defined.
*/
export function isRouteNodeValidForAugmentation(
routeNode?: RouteNode,
): routeNode is RouteNode {
if (!routeNode || routeNode.isVirtual) {
return false
}
return true
}
/**
* Infers the path for use by TS
*/
export const inferPath = (routeNode: RouteNode): string => {
const inferPath = (routeNode: RouteNode): string => {
if (routeNode.cleanedPath === '/') {

@@ -818,3 +788,3 @@ return routeNode.cleanedPath ?? ''

*/
export const inferTo = (routeNode: RouteNode): string => {
const inferTo = (routeNode: RouteNode): string => {
const fullPath = inferFullPath(routeNode)

@@ -830,3 +800,3 @@

*/
export const dedupeBranchesAndIndexRoutes = (
const dedupeBranchesAndIndexRoutes = (
routes: Array<RouteNode>,

@@ -953,10 +923,2 @@ ): Array<RouteNode> => {

export function lowerCaseFirstChar(value: string) {
if (!value[0]) {
return value
}
return value[0].toLowerCase() + value.slice(1)
}
export function mergeImportDeclarations(

@@ -1032,3 +994,3 @@ imports: Array<ImportDeclaration>,

export function getImportPath(
function getImportPath(
node: RouteNode,

@@ -1035,0 +997,0 @@ config: Config,

require("../_virtual/_rolldown/runtime.cjs");
//#region src/transform/utils.ts
var b = require("recast").types.builders;
function ensureStringArgument(callExpression, value, preferredQuote) {
const argument = callExpression.arguments[0];
if (!argument) {
let stringLiteral;
if (!preferredQuote) stringLiteral = b.stringLiteral.from({ value });
else stringLiteral = b.stringLiteral.from({
value,
extra: {
rawValue: value,
raw: `${preferredQuote}${value}${preferredQuote}`
}
});
callExpression.arguments.push(stringLiteral);
return true;
} else if (argument.type === "StringLiteral") {
if (argument.value !== value) {
argument.value = value;
return true;
}
} else if (argument.type === "TemplateLiteral") {
if (argument.quasis.length === 1 && argument.quasis[0] && argument.quasis[0].value.raw !== value) {
argument.quasis[0].value.raw = value;
return true;
}
}
return false;
}
//#endregion
exports.ensureStringArgument = ensureStringArgument;
//# sourceMappingURL=utils.cjs.map
{"version":3,"file":"utils.cjs","names":[],"sources":["../../../src/transform/utils.ts"],"sourcesContent":["import { types } from 'recast'\n\nconst b = types.builders\n\nexport function ensureStringArgument(\n callExpression: types.namedTypes.CallExpression,\n value: string,\n preferredQuote?: \"'\" | '\"',\n) {\n const argument = callExpression.arguments[0]\n if (!argument) {\n let stringLiteral: types.namedTypes.StringLiteral\n if (!preferredQuote) {\n stringLiteral = b.stringLiteral.from({ value })\n } else {\n stringLiteral = b.stringLiteral.from({\n value,\n extra: {\n rawValue: value,\n raw: `${preferredQuote}${value}${preferredQuote}`,\n },\n })\n }\n callExpression.arguments.push(stringLiteral)\n return true\n } else if (argument.type === 'StringLiteral') {\n if (argument.value !== value) {\n argument.value = value\n return true\n }\n } else if (argument.type === 'TemplateLiteral') {\n if (\n argument.quasis.length === 1 &&\n argument.quasis[0] &&\n argument.quasis[0].value.raw !== value\n ) {\n argument.quasis[0].value.raw = value\n return true\n }\n }\n return false\n}\n"],"mappings":";;AAEA,IAAM,sBAAI,MAAM;AAEhB,SAAgB,qBACd,gBACA,OACA,gBACA;CACA,MAAM,WAAW,eAAe,UAAU;AAC1C,KAAI,CAAC,UAAU;EACb,IAAI;AACJ,MAAI,CAAC,eACH,iBAAgB,EAAE,cAAc,KAAK,EAAE,OAAO,CAAC;MAE/C,iBAAgB,EAAE,cAAc,KAAK;GACnC;GACA,OAAO;IACL,UAAU;IACV,KAAK,GAAG,iBAAiB,QAAQ;IAClC;GACF,CAAC;AAEJ,iBAAe,UAAU,KAAK,cAAc;AAC5C,SAAO;YACE,SAAS,SAAS;MACvB,SAAS,UAAU,OAAO;AAC5B,YAAS,QAAQ;AACjB,UAAO;;YAEA,SAAS,SAAS;MAEzB,SAAS,OAAO,WAAW,KAC3B,SAAS,OAAO,MAChB,SAAS,OAAO,GAAG,MAAM,QAAQ,OACjC;AACA,YAAS,OAAO,GAAG,MAAM,MAAM;AAC/B,UAAO;;;AAGX,QAAO"}
import { types } from 'recast';
export declare function ensureStringArgument(callExpression: types.namedTypes.CallExpression, value: string, preferredQuote?: "'" | '"'): boolean;
import { types } from 'recast';
export declare function ensureStringArgument(callExpression: types.namedTypes.CallExpression, value: string, preferredQuote?: "'" | '"'): boolean;
import { types } from "recast";
//#region src/transform/utils.ts
var b = types.builders;
function ensureStringArgument(callExpression, value, preferredQuote) {
const argument = callExpression.arguments[0];
if (!argument) {
let stringLiteral;
if (!preferredQuote) stringLiteral = b.stringLiteral.from({ value });
else stringLiteral = b.stringLiteral.from({
value,
extra: {
rawValue: value,
raw: `${preferredQuote}${value}${preferredQuote}`
}
});
callExpression.arguments.push(stringLiteral);
return true;
} else if (argument.type === "StringLiteral") {
if (argument.value !== value) {
argument.value = value;
return true;
}
} else if (argument.type === "TemplateLiteral") {
if (argument.quasis.length === 1 && argument.quasis[0] && argument.quasis[0].value.raw !== value) {
argument.quasis[0].value.raw = value;
return true;
}
}
return false;
}
//#endregion
export { ensureStringArgument };
//# sourceMappingURL=utils.js.map
{"version":3,"file":"utils.js","names":[],"sources":["../../../src/transform/utils.ts"],"sourcesContent":["import { types } from 'recast'\n\nconst b = types.builders\n\nexport function ensureStringArgument(\n callExpression: types.namedTypes.CallExpression,\n value: string,\n preferredQuote?: \"'\" | '\"',\n) {\n const argument = callExpression.arguments[0]\n if (!argument) {\n let stringLiteral: types.namedTypes.StringLiteral\n if (!preferredQuote) {\n stringLiteral = b.stringLiteral.from({ value })\n } else {\n stringLiteral = b.stringLiteral.from({\n value,\n extra: {\n rawValue: value,\n raw: `${preferredQuote}${value}${preferredQuote}`,\n },\n })\n }\n callExpression.arguments.push(stringLiteral)\n return true\n } else if (argument.type === 'StringLiteral') {\n if (argument.value !== value) {\n argument.value = value\n return true\n }\n } else if (argument.type === 'TemplateLiteral') {\n if (\n argument.quasis.length === 1 &&\n argument.quasis[0] &&\n argument.quasis[0].value.raw !== value\n ) {\n argument.quasis[0].value.raw = value\n return true\n }\n }\n return false\n}\n"],"mappings":";;AAEA,IAAM,IAAI,MAAM;AAEhB,SAAgB,qBACd,gBACA,OACA,gBACA;CACA,MAAM,WAAW,eAAe,UAAU;AAC1C,KAAI,CAAC,UAAU;EACb,IAAI;AACJ,MAAI,CAAC,eACH,iBAAgB,EAAE,cAAc,KAAK,EAAE,OAAO,CAAC;MAE/C,iBAAgB,EAAE,cAAc,KAAK;GACnC;GACA,OAAO;IACL,UAAU;IACV,KAAK,GAAG,iBAAiB,QAAQ;IAClC;GACF,CAAC;AAEJ,iBAAe,UAAU,KAAK,cAAc;AAC5C,SAAO;YACE,SAAS,SAAS;MACvB,SAAS,UAAU,OAAO;AAC5B,YAAS,QAAQ;AACjB,UAAO;;YAEA,SAAS,SAAS;MAEzB,SAAS,OAAO,WAAW,KAC3B,SAAS,OAAO,MAChB,SAAS,OAAO,GAAG,MAAM,QAAQ,OACjC;AACA,YAAS,OAAO,GAAG,MAAM,MAAM;AAC/B,UAAO;;;AAGX,QAAO"}
import { types } from 'recast'
const b = types.builders
export function ensureStringArgument(
callExpression: types.namedTypes.CallExpression,
value: string,
preferredQuote?: "'" | '"',
) {
const argument = callExpression.arguments[0]
if (!argument) {
let stringLiteral: types.namedTypes.StringLiteral
if (!preferredQuote) {
stringLiteral = b.stringLiteral.from({ value })
} else {
stringLiteral = b.stringLiteral.from({
value,
extra: {
rawValue: value,
raw: `${preferredQuote}${value}${preferredQuote}`,
},
})
}
callExpression.arguments.push(stringLiteral)
return true
} else if (argument.type === 'StringLiteral') {
if (argument.value !== value) {
argument.value = value
return true
}
} else if (argument.type === 'TemplateLiteral') {
if (
argument.quasis.length === 1 &&
argument.quasis[0] &&
argument.quasis[0].value.raw !== value
) {
argument.quasis[0].value.raw = value
return true
}
}
return false
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display