@lomray/vite-ssr-boost 3.3.5 → 4.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/entry.d.ts +2 -3
- package/browser/entry.js +1 -1
- package/browser/entry.js.map +1 -1
- package/cli/helpers/keyboard-input.js.map +1 -1
- package/cli/helpers/vite-reset-cache.js.map +1 -1
- package/cli/run-amplify-build.js.map +1 -1
- package/cli/run-docker-build.js.map +1 -1
- package/cli/run-vercel-build.js.map +1 -1
- package/cli.js.map +1 -1
- package/components/navigate.d.ts +1 -1
- package/components/navigate.js +1 -1
- package/components/navigate.js.map +1 -1
- package/components/scroll-to-top.js +1 -1
- package/components/scroll-to-top.js.map +1 -1
- package/helpers/build-router-state.d.ts +1 -1
- package/helpers/build-router-state.js.map +1 -1
- package/helpers/dev-marker.js.map +1 -1
- package/helpers/get-server-state.js.map +1 -1
- package/helpers/handle-response.d.ts +1 -1
- package/helpers/handle-response.js.map +1 -1
- package/helpers/import-route.d.ts +3 -243
- package/helpers/import-route.js.map +1 -1
- package/helpers/print-server-info.js.map +1 -1
- package/helpers/resolve-server-urls.js.map +1 -1
- package/helpers/serialize-errors.d.ts +1 -1
- package/helpers/serialize-errors.js +1 -1
- package/helpers/serialize-errors.js.map +1 -1
- package/helpers/ssr-meta.js.map +1 -1
- package/interfaces/fc-route.d.ts +2 -2
- package/interfaces/fc-route.js.map +1 -1
- package/interfaces/route-object.d.ts +1 -1
- package/node/entry.d.ts +1 -1
- package/node/entry.js +1 -1
- package/node/entry.js.map +1 -1
- package/node/render.d.ts +1 -2
- package/node/render.js +1 -1
- package/node/render.js.map +1 -1
- package/node/server.js.map +1 -1
- package/node/write-response.js.map +1 -1
- package/package.json +19 -19
- package/plugins/handle-custom-entrypoint.js.map +1 -1
- package/plugins/make-aliases.js.map +1 -1
- package/plugins/normalize-route.js.map +1 -1
- package/services/build.js.map +1 -1
- package/services/logger.js.map +1 -1
- package/services/parse-routes.js.map +1 -1
- package/services/path-normalize.js.map +1 -1
- package/services/prepare-server.js.map +1 -1
- package/services/server-api.js.map +1 -1
- package/services/server-config.js.map +1 -1
- package/services/ssr-manifest.d.ts +3 -4
- package/services/ssr-manifest.js.map +1 -1
package/services/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { LogErrorOptions, Logger as ViteLogger, LogOptions } from 'vite';\n\nconst LogLevels = {\n error: 1,\n warn: 2,\n info: 3,\n};\n\ninterface ILoggerOptions {\n logLevel?: number;\n logFilter?: (params: ILogParams) => boolean;\n}\n\ninterface ILogParams {\n level: keyof typeof LogLevels;\n msg?: string;\n options?: LogErrorOptions;\n}\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n public hasWarned: boolean;\n\n /**\n * Current log level\n */\n public loglevel: number;\n\n /**\n * Custom log filter\n * Return 'true' to skip output\n */\n public logFilter: ILoggerOptions['logFilter'] | undefined;\n\n /**\n * @constructor\n */\n public constructor({ logFilter, logLevel = 3 }: ILoggerOptions = {}) {\n this.loglevel = logLevel;\n this.logFilter = logFilter;\n }\n\n /**\n * @inheritDoc\n */\n public clearScreen(): void {\n //\n }\n\n /**\n * Should we skip current log depends on current log level\n */\n protected shouldSkipLog(level: number): boolean {\n return level > this.loglevel;\n }\n\n /**\n * Common log\n */\n protected log(params: ILogParams): void {\n const { level, msg, options } = params;\n\n if (this.shouldSkipLog(LogLevels[level]) || this.logFilter?.(params)) {\n return;\n }\n\n const args = [msg, options?.error].filter(Boolean);\n\n console[level](...args);\n }\n\n /**\n * @inheritDoc\n */\n public error(msg: string, options?: LogErrorOptions): void {\n this.log({ msg, options, level: 'error' });\n }\n\n /**\n * @inheritDoc\n */\n public hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n public info(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'info' });\n }\n\n /**\n * @inheritDoc\n */\n public warn(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'warn' });\n }\n\n /**\n * @inheritDoc\n */\n public warnOnce(msg: string, options: LogOptions): void {\n this.warn(msg, options);\n }\n}\n\nexport default Logger;\n"],"names":["LogLevels","error","warn","info","Logger","hasWarned","loglevel","logFilter","constructor","logLevel","this","clearScreen","shouldSkipLog","level","log","params","msg","options","args","filter","Boolean","console","hasErrorLogged","warnOnce"],"mappings":"AAEA,MAAMA,EAAY,CAChBC,MAAO,EACPC,KAAM,EACNC,KAAM,GAiBR,MAAMC,EAIGC,UAKAC,SAMAC,UAKPC,aAAmBD,UAAEA,EAASE,SAAEA,EAAW,GAAsB,CAAA,GAC/DC,KAAKJ,SAAWG,EAChBC,KAAKH,UAAYA,
|
|
1
|
+
{"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { LogErrorOptions, Logger as ViteLogger, LogOptions } from 'vite';\n\nconst LogLevels = {\n error: 1,\n warn: 2,\n info: 3,\n};\n\ninterface ILoggerOptions {\n logLevel?: number;\n logFilter?: (params: ILogParams) => boolean;\n}\n\ninterface ILogParams {\n level: keyof typeof LogLevels;\n msg?: string;\n options?: LogErrorOptions;\n}\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n public hasWarned: boolean;\n\n /**\n * Current log level\n */\n public loglevel: number;\n\n /**\n * Custom log filter\n * Return 'true' to skip output\n */\n public logFilter: ILoggerOptions['logFilter'] | undefined;\n\n /**\n * @constructor\n */\n public constructor({ logFilter, logLevel = 3 }: ILoggerOptions = {}) {\n this.loglevel = logLevel;\n this.logFilter = logFilter;\n }\n\n /**\n * @inheritDoc\n */\n public clearScreen(): void {\n //\n }\n\n /**\n * Should we skip current log depends on current log level\n */\n protected shouldSkipLog(level: number): boolean {\n return level > this.loglevel;\n }\n\n /**\n * Common log\n */\n protected log(params: ILogParams): void {\n const { level, msg, options } = params;\n\n if (this.shouldSkipLog(LogLevels[level]) || this.logFilter?.(params)) {\n return;\n }\n\n const args = [msg, options?.error].filter(Boolean);\n\n console[level](...args);\n }\n\n /**\n * @inheritDoc\n */\n public error(msg: string, options?: LogErrorOptions): void {\n this.log({ msg, options, level: 'error' });\n }\n\n /**\n * @inheritDoc\n */\n public hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n public info(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'info' });\n }\n\n /**\n * @inheritDoc\n */\n public warn(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'warn' });\n }\n\n /**\n * @inheritDoc\n */\n public warnOnce(msg: string, options: LogOptions): void {\n this.warn(msg, options);\n }\n}\n\nexport default Logger;\n"],"names":["LogLevels","error","warn","info","Logger","hasWarned","loglevel","logFilter","constructor","logLevel","this","clearScreen","shouldSkipLog","level","log","params","msg","options","args","filter","Boolean","console","hasErrorLogged","warnOnce"],"mappings":"AAEA,MAAMA,EAAY,CAChBC,MAAO,EACPC,KAAM,EACNC,KAAM,GAiBR,MAAMC,EAIGC,UAKAC,SAMAC,UAKPC,aAAmBD,UAAEA,EAASE,SAAEA,EAAW,GAAsB,CAAA,GAC/DC,KAAKJ,SAAWG,EAChBC,KAAKH,UAAYA,EAMZI,eAOGC,cAAcC,GACtB,OAAOA,EAAQH,KAAKJ,SAMZQ,IAAIC,GACZ,MAAMF,MAAEA,EAAKG,IAAEA,EAAGC,QAAEA,GAAYF,EAEhC,GAAIL,KAAKE,cAAcZ,EAAUa,KAAWH,KAAKH,YAAYQ,GAC3D,OAGF,MAAMG,EAAO,CAACF,EAAKC,GAAShB,OAAOkB,OAAOC,SAE1CC,QAAQR,MAAUK,GAMbjB,MAAMe,EAAaC,GACxBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,UAM3BS,iBACL,OAAO,EAMFnB,KAAKa,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SAM3BX,KAAKc,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SAM3BU,SAASP,EAAaC,GAC3BP,KAAKR,KAAKc,EAAKC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-routes.js","sources":["../../src/services/parse-routes.ts"],"sourcesContent":["import fs from 'fs';\nimport { resolve } from 'node:path';\nimport path from 'path';\nimport babelGenerate from '@babel/generator';\nimport type * as GenerateTypes from '@babel/generator';\nimport * as parser from '@babel/parser';\nimport type { ParseResult } from '@babel/parser';\nimport babelTraverse from '@babel/traverse';\nimport type * as TraverseTypes from '@babel/traverse';\nimport type {\n CallExpression,\n File as BabelFile,\n VariableDeclaration,\n ObjectExpression,\n} from '@babel/types';\nimport {\n isObjectProperty,\n isIdentifier,\n identifier,\n stringLiteral,\n objectProperty,\n isArrayExpression,\n isJSXElement,\n isJSXIdentifier,\n isObjectExpression,\n} from '@babel/types';\nimport type { Alias } from 'vite';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n//\n// @ts-expect-error known import problem\nconst generate = (babelGenerate.default ?? babelGenerate) as (typeof GenerateTypes)['default'];\n// @ts-expect-error known import problem\nconst traverse = (babelTraverse.default ?? babelTraverse) as (typeof TraverseTypes)['default'];\n\ninterface IPathImport {\n routesPath: string | null;\n exportName: string | null;\n}\n\ninterface IMapImports {\n [name: string]: {\n path: string;\n isDefault: boolean; // is default import?\n };\n}\n\nexport type TRoutesTree = {\n index: number;\n import: string;\n children: TRoutesTree[];\n};\n\n/**\n * Parse react router routes array\n */\nclass ParseRoutes {\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n }\n\n /**\n * Parse routes\n */\n public parse(): TRoutesTree[] {\n const { clientFile, root } = this.config.getParams();\n\n const clientEntrypoint = resolve(root, clientFile);\n const routesEntrypoint = this.findRoutesEntrypoint(clientEntrypoint);\n\n if (!routesEntrypoint?.routesPath) {\n throw new Error(`Unable to find routes file import in ${clientFile}`);\n }\n\n const { routesPath, exportName } = routesEntrypoint;\n const routeFilepath = this.resolveFilename(routesPath, clientEntrypoint);\n\n return this.recursiveBuildRoutesTree(routeFilepath, exportName);\n }\n\n /**\n * Parse file and return ast\n */\n private parseFile(filename: string): ParseResult<BabelFile> | null {\n try {\n const code = fs.readFileSync(filename, 'utf-8');\n\n return parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Find route import filepath\n */\n private getImportPath(\n ast: ParseResult<BabelFile>,\n importName: string | null,\n ): IPathImport | null {\n let routesPath: string | null = null;\n let exportName: string | null = null;\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n if (specifier.local.name === importName) {\n exportName = specifier.type === 'ImportDefaultSpecifier' ? null : importName;\n routesPath = importNode.source.value;\n }\n });\n },\n });\n\n return {\n routesPath,\n exportName,\n };\n }\n\n /**\n * Find routes array inside code\n */\n private findRoutesDefinition(\n ast: ParseResult<BabelFile>,\n exportName: string | null,\n ): null | VariableDeclaration {\n let exportNameResolved = exportName;\n\n // noinspection JSUnusedGlobalSymbols\n traverse(ast, {\n ExportNamedDeclaration({ node }) {\n if (!node.declaration && node.specifiers.length > 0) {\n node.specifiers.forEach((specifier) => {\n // @ts-expect-error missing in types\n const exportedName = specifier.exported.name as string;\n\n if (exportName === null && specifier.type === 'ExportSpecifier') {\n if (specifier.local.name === 'default') {\n exportNameResolved = exportedName;\n }\n } else if (exportedName === exportName) {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = specifier.local.name as string;\n }\n });\n }\n },\n ExportDefaultDeclaration({ node }) {\n if (exportName === null) {\n if (node.declaration.type === 'Identifier') {\n exportNameResolved = node.declaration.name;\n // @ts-expect-error missing in types\n } else if (node.declaration.type === 'VariableDeclaration') {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = node.declaration.declarations[0].id.name as string;\n }\n }\n },\n });\n\n if (exportNameResolved) {\n let variableNode: VariableDeclaration | null = null;\n\n traverse(ast, {\n VariableDeclaration({ node }) {\n node.declarations.forEach((declaration) => {\n // @ts-expect-error missing in types\n if (declaration.id.name === exportNameResolved) {\n variableNode = node;\n }\n });\n },\n });\n\n return variableNode;\n }\n\n return null;\n }\n\n /**\n * Entrypoint routes file\n */\n private findRoutesEntrypoint(clientEntrypoint: string): IPathImport | null {\n const ast = this.parseFile(clientEntrypoint);\n\n let routesVariable: string | null = null;\n\n if (!ast) {\n return routesVariable;\n }\n\n traverse(ast, {\n CallExpression({ node }) {\n if (\n // @ts-expect-error missing in types\n node.callee.name === 'entryClient' &&\n node.arguments.length >= 2 &&\n node.arguments[1].type === 'Identifier'\n ) {\n routesVariable = node.arguments[1].name;\n }\n },\n });\n\n return this.getImportPath(ast, routesVariable);\n }\n\n /**\n * Resolve route filename import\n */\n private resolveFilename(filename: string, relativeFile?: string): string | null {\n let resolvedFilename = filename;\n\n if ((filename.startsWith('./') || filename.startsWith('../')) && relativeFile) {\n resolvedFilename = path.resolve(path.dirname(relativeFile), filename);\n }\n\n const filepath = this.pathNormalize.getAppPath(resolvedFilename, true);\n\n return this.pathNormalize.findAppFile(filepath!);\n }\n\n /**\n * Parse ast array routes objects\n */\n private parseRoutesArray(\n elements: TraverseTypes.Node[],\n importsMap: IMapImports,\n relativeFile: string,\n ): TRoutesTree[] {\n const results: TRoutesTree[] = [];\n\n elements.forEach((node, index) => {\n if (node.type === 'ObjectExpression') {\n const routeInfo: TRoutesTree = { index, import: '', children: [] };\n\n node.properties.forEach((prop) => {\n const objectProp = prop as {\n key: { name: string };\n value: { type: string; elements: TraverseTypes.Node[] };\n };\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'ArrayExpression') {\n routeInfo.children = this.parseRoutesArray(\n objectProp.value.elements,\n importsMap,\n relativeFile,\n );\n }\n\n // async routes\n if (\n objectProp.key.name === 'lazy' &&\n objectProp.value.type === 'ArrowFunctionExpression'\n ) {\n // @ts-expect-error incorrect types\n const importCall = objectProp.value.body as CallExpression;\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n\n if (importArg.type === 'StringLiteral') {\n routeInfo.import = importArg.value;\n }\n }\n }\n\n // static routes: Component\n if (objectProp.key.name === 'Component' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n // static routes: element\n if (objectProp.key.name === 'element' && objectProp.value.type === 'JSXElement') {\n // @ts-expect-error incorrect types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const importName = objectProp.value?.openingElement?.name?.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath, isDefault } = importsMap[importName] ?? {};\n\n if (importPath) {\n const childrenFilePath = this.resolveFilename(importPath, relativeFile);\n\n if (childrenFilePath) {\n routeInfo.children = this.recursiveBuildRoutesTree(\n childrenFilePath,\n isDefault ? null : importName,\n );\n }\n }\n }\n });\n\n if (routeInfo.import || routeInfo.children.length > 0) {\n results.push(routeInfo);\n }\n }\n });\n\n return results;\n }\n\n /**\n * Parse imports map from ast\n */\n private static parseImportsMap(ast: ParseResult<BabelFile>): IMapImports {\n const importsMap: IMapImports = {};\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n importsMap[specifier.local.name] = {\n path: importNode.source.value,\n isDefault: specifier.type === 'ImportDefaultSpecifier',\n };\n });\n },\n });\n\n return importsMap;\n }\n\n /**\n * Recursive build routes tree with dynamic imports\n */\n private recursiveBuildRoutesTree(\n filename: string | null,\n exportName: string | null = null,\n ): TRoutesTree[] {\n if (!filename) {\n return [];\n }\n\n const ast = this.parseFile(filename);\n\n if (!ast) {\n return [];\n }\n\n const routesNode = this.findRoutesDefinition(ast, exportName);\n const results: TRoutesTree[] = [];\n\n if (!routesNode) {\n return results;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n // @ts-expect-error missing types\n const elements = routesNode.declarations[0].init?.elements as TraverseTypes.Node[];\n\n results.push(...this.parseRoutesArray(elements, importsMap, filename));\n\n return results;\n }\n\n /**\n * Add pathId to static routes\n */\n private static processRouteFileCode(\n nodePath: TraverseTypes.NodePath<ObjectExpression>,\n importsMap: IMapImports,\n ): void {\n nodePath.node.properties.forEach((property) => {\n if (isObjectProperty(property) && isIdentifier(property.key)) {\n if (property.key.name === 'element' || property.key.name === 'Component') {\n let componentName = '';\n\n if (isJSXElement(property.value) && isJSXIdentifier(property.value.openingElement.name)) {\n componentName = property.value.openingElement.name.name;\n } else if (isIdentifier(property.value)) {\n componentName = property.value.name;\n }\n\n const parent = nodePath.findParent((p) => isArrayExpression(p.node));\n const importName = importsMap[componentName]?.path;\n\n if (parent && importName) {\n const pathIdProperty = objectProperty(identifier('pathId'), stringLiteral(importName));\n\n // Insert the pathId property right after the element property\n nodePath.node.properties.splice(\n nodePath.node.properties.indexOf(property) + 1,\n 0,\n pathIdProperty,\n );\n }\n }\n\n if (property.key.name === 'children' && isArrayExpression(property.value)) {\n // Process each object in the children array recursively\n property.value.elements.forEach((element) => {\n if (isObjectExpression(element)) {\n ParseRoutes.processRouteFileCode(\n { node: element } as TraverseTypes.NodePath<ObjectExpression>,\n importsMap,\n );\n }\n });\n }\n }\n });\n }\n\n /**\n * Inject pathId to sync routes\n */\n public static injectPathId(code: string): string {\n if (!code) {\n return code;\n }\n\n const ast = parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n\n if (!ast) {\n return code;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n traverse(ast, {\n ObjectExpression(nodePath) {\n ParseRoutes.processRouteFileCode(nodePath, importsMap);\n },\n });\n\n return generate(ast, {\n retainLines: true,\n }).code;\n }\n}\n\nexport default ParseRoutes;\n"],"names":["generate","babelGenerate","default","traverse","babelTraverse","ParseRoutes","pathNormalize","config","constructor","viteAliases","this","PathNormalize","parse","clientFile","root","getParams","clientEntrypoint","resolve","routesEntrypoint","findRoutesEntrypoint","routesPath","Error","exportName","routeFilepath","resolveFilename","recursiveBuildRoutesTree","parseFile","filename","code","fs","readFileSync","parser","sourceType","plugins","e","getImportPath","ast","importName","ImportDeclaration","nodePath","importNode","node","specifiers","forEach","specifier","local","name","type","source","value","findRoutesDefinition","exportNameResolved","ExportNamedDeclaration","declaration","length","exportedName","exported","ExportDefaultDeclaration","declarations","id","variableNode","VariableDeclaration","routesVariable","CallExpression","callee","arguments","relativeFile","resolvedFilename","startsWith","path","dirname","filepath","getAppPath","findAppFile","parseRoutesArray","elements","importsMap","results","index","routeInfo","import","children","properties","prop","objectProp","key","importCall","body","importArg","importPath","openingElement","isDefault","childrenFilePath","push","static","routesNode","parseImportsMap","init","property","isObjectProperty","isIdentifier","componentName","isJSXElement","isJSXIdentifier","parent","findParent","p","isArrayExpression","pathIdProperty","objectProperty","identifier","stringLiteral","splice","indexOf","element","isObjectExpression","processRouteFileCode","ObjectExpression","retainLines"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse-routes.js","sources":["../../src/services/parse-routes.ts"],"sourcesContent":["import fs from 'fs';\nimport { resolve } from 'node:path';\nimport path from 'path';\nimport babelGenerate from '@babel/generator';\nimport type * as GenerateTypes from '@babel/generator';\nimport * as parser from '@babel/parser';\nimport type { ParseResult } from '@babel/parser';\nimport babelTraverse from '@babel/traverse';\nimport type * as TraverseTypes from '@babel/traverse';\nimport type {\n CallExpression,\n File as BabelFile,\n VariableDeclaration,\n ObjectExpression,\n} from '@babel/types';\nimport {\n isObjectProperty,\n isIdentifier,\n identifier,\n stringLiteral,\n objectProperty,\n isArrayExpression,\n isJSXElement,\n isJSXIdentifier,\n isObjectExpression,\n} from '@babel/types';\nimport type { Alias } from 'vite';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n//\n// @ts-expect-error known import problem\nconst generate = (babelGenerate.default ?? babelGenerate) as (typeof GenerateTypes)['default'];\n// @ts-expect-error known import problem\nconst traverse = (babelTraverse.default ?? babelTraverse) as (typeof TraverseTypes)['default'];\n\ninterface IPathImport {\n routesPath: string | null;\n exportName: string | null;\n}\n\ninterface IMapImports {\n [name: string]: {\n path: string;\n isDefault: boolean; // is default import?\n };\n}\n\nexport type TRoutesTree = {\n index: number;\n import: string;\n children: TRoutesTree[];\n};\n\n/**\n * Parse react router routes array\n */\nclass ParseRoutes {\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n }\n\n /**\n * Parse routes\n */\n public parse(): TRoutesTree[] {\n const { clientFile, root } = this.config.getParams();\n\n const clientEntrypoint = resolve(root, clientFile);\n const routesEntrypoint = this.findRoutesEntrypoint(clientEntrypoint);\n\n if (!routesEntrypoint?.routesPath) {\n throw new Error(`Unable to find routes file import in ${clientFile}`);\n }\n\n const { routesPath, exportName } = routesEntrypoint;\n const routeFilepath = this.resolveFilename(routesPath, clientEntrypoint);\n\n return this.recursiveBuildRoutesTree(routeFilepath, exportName);\n }\n\n /**\n * Parse file and return ast\n */\n private parseFile(filename: string): ParseResult<BabelFile> | null {\n try {\n const code = fs.readFileSync(filename, 'utf-8');\n\n return parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Find route import filepath\n */\n private getImportPath(\n ast: ParseResult<BabelFile>,\n importName: string | null,\n ): IPathImport | null {\n let routesPath: string | null = null;\n let exportName: string | null = null;\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n if (specifier.local.name === importName) {\n exportName = specifier.type === 'ImportDefaultSpecifier' ? null : importName;\n routesPath = importNode.source.value;\n }\n });\n },\n });\n\n return {\n routesPath,\n exportName,\n };\n }\n\n /**\n * Find routes array inside code\n */\n private findRoutesDefinition(\n ast: ParseResult<BabelFile>,\n exportName: string | null,\n ): null | VariableDeclaration {\n let exportNameResolved = exportName;\n\n // noinspection JSUnusedGlobalSymbols\n traverse(ast, {\n ExportNamedDeclaration({ node }) {\n if (!node.declaration && node.specifiers.length > 0) {\n node.specifiers.forEach((specifier) => {\n // @ts-expect-error missing in types\n const exportedName = specifier.exported.name as string;\n\n if (exportName === null && specifier.type === 'ExportSpecifier') {\n if (specifier.local.name === 'default') {\n exportNameResolved = exportedName;\n }\n } else if (exportedName === exportName) {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = specifier.local.name as string;\n }\n });\n }\n },\n ExportDefaultDeclaration({ node }) {\n if (exportName === null) {\n if (node.declaration.type === 'Identifier') {\n exportNameResolved = node.declaration.name;\n // @ts-expect-error missing in types\n } else if (node.declaration.type === 'VariableDeclaration') {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = node.declaration.declarations[0].id.name as string;\n }\n }\n },\n });\n\n if (exportNameResolved) {\n let variableNode: VariableDeclaration | null = null;\n\n traverse(ast, {\n VariableDeclaration({ node }) {\n node.declarations.forEach((declaration) => {\n // @ts-expect-error missing in types\n if (declaration.id.name === exportNameResolved) {\n variableNode = node;\n }\n });\n },\n });\n\n return variableNode;\n }\n\n return null;\n }\n\n /**\n * Entrypoint routes file\n */\n private findRoutesEntrypoint(clientEntrypoint: string): IPathImport | null {\n const ast = this.parseFile(clientEntrypoint);\n\n let routesVariable: string | null = null;\n\n if (!ast) {\n return routesVariable;\n }\n\n traverse(ast, {\n CallExpression({ node }) {\n if (\n // @ts-expect-error missing in types\n node.callee.name === 'entryClient' &&\n node.arguments.length >= 2 &&\n node.arguments[1].type === 'Identifier'\n ) {\n routesVariable = node.arguments[1].name;\n }\n },\n });\n\n return this.getImportPath(ast, routesVariable);\n }\n\n /**\n * Resolve route filename import\n */\n private resolveFilename(filename: string, relativeFile?: string): string | null {\n let resolvedFilename = filename;\n\n if ((filename.startsWith('./') || filename.startsWith('../')) && relativeFile) {\n resolvedFilename = path.resolve(path.dirname(relativeFile), filename);\n }\n\n const filepath = this.pathNormalize.getAppPath(resolvedFilename, true);\n\n return this.pathNormalize.findAppFile(filepath!);\n }\n\n /**\n * Parse ast array routes objects\n */\n private parseRoutesArray(\n elements: TraverseTypes.Node[],\n importsMap: IMapImports,\n relativeFile: string,\n ): TRoutesTree[] {\n const results: TRoutesTree[] = [];\n\n elements.forEach((node, index) => {\n if (node.type === 'ObjectExpression') {\n const routeInfo: TRoutesTree = { index, import: '', children: [] };\n\n node.properties.forEach((prop) => {\n const objectProp = prop as {\n key: { name: string };\n value: { type: string; elements: TraverseTypes.Node[] };\n };\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'ArrayExpression') {\n routeInfo.children = this.parseRoutesArray(\n objectProp.value.elements,\n importsMap,\n relativeFile,\n );\n }\n\n // async routes\n if (\n objectProp.key.name === 'lazy' &&\n objectProp.value.type === 'ArrowFunctionExpression'\n ) {\n // @ts-expect-error incorrect types\n const importCall = objectProp.value.body as CallExpression;\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n\n if (importArg.type === 'StringLiteral') {\n routeInfo.import = importArg.value;\n }\n }\n }\n\n // static routes: Component\n if (objectProp.key.name === 'Component' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n // static routes: element\n if (objectProp.key.name === 'element' && objectProp.value.type === 'JSXElement') {\n // @ts-expect-error incorrect types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const importName = objectProp.value?.openingElement?.name?.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath, isDefault } = importsMap[importName] ?? {};\n\n if (importPath) {\n const childrenFilePath = this.resolveFilename(importPath, relativeFile);\n\n if (childrenFilePath) {\n routeInfo.children = this.recursiveBuildRoutesTree(\n childrenFilePath,\n isDefault ? null : importName,\n );\n }\n }\n }\n });\n\n if (routeInfo.import || routeInfo.children.length > 0) {\n results.push(routeInfo);\n }\n }\n });\n\n return results;\n }\n\n /**\n * Parse imports map from ast\n */\n private static parseImportsMap(ast: ParseResult<BabelFile>): IMapImports {\n const importsMap: IMapImports = {};\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n importsMap[specifier.local.name] = {\n path: importNode.source.value,\n isDefault: specifier.type === 'ImportDefaultSpecifier',\n };\n });\n },\n });\n\n return importsMap;\n }\n\n /**\n * Recursive build routes tree with dynamic imports\n */\n private recursiveBuildRoutesTree(\n filename: string | null,\n exportName: string | null = null,\n ): TRoutesTree[] {\n if (!filename) {\n return [];\n }\n\n const ast = this.parseFile(filename);\n\n if (!ast) {\n return [];\n }\n\n const routesNode = this.findRoutesDefinition(ast, exportName);\n const results: TRoutesTree[] = [];\n\n if (!routesNode) {\n return results;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n // @ts-expect-error missing types\n const elements = routesNode.declarations[0].init?.elements as TraverseTypes.Node[];\n\n results.push(...this.parseRoutesArray(elements, importsMap, filename));\n\n return results;\n }\n\n /**\n * Add pathId to static routes\n */\n private static processRouteFileCode(\n nodePath: TraverseTypes.NodePath<ObjectExpression>,\n importsMap: IMapImports,\n ): void {\n nodePath.node.properties.forEach((property) => {\n if (isObjectProperty(property) && isIdentifier(property.key)) {\n if (property.key.name === 'element' || property.key.name === 'Component') {\n let componentName = '';\n\n if (isJSXElement(property.value) && isJSXIdentifier(property.value.openingElement.name)) {\n componentName = property.value.openingElement.name.name;\n } else if (isIdentifier(property.value)) {\n componentName = property.value.name;\n }\n\n const parent = nodePath.findParent((p) => isArrayExpression(p.node));\n const importName = importsMap[componentName]?.path;\n\n if (parent && importName) {\n const pathIdProperty = objectProperty(identifier('pathId'), stringLiteral(importName));\n\n // Insert the pathId property right after the element property\n nodePath.node.properties.splice(\n nodePath.node.properties.indexOf(property) + 1,\n 0,\n pathIdProperty,\n );\n }\n }\n\n if (property.key.name === 'children' && isArrayExpression(property.value)) {\n // Process each object in the children array recursively\n property.value.elements.forEach((element) => {\n if (isObjectExpression(element)) {\n ParseRoutes.processRouteFileCode(\n { node: element } as TraverseTypes.NodePath<ObjectExpression>,\n importsMap,\n );\n }\n });\n }\n }\n });\n }\n\n /**\n * Inject pathId to sync routes\n */\n public static injectPathId(code: string): string {\n if (!code) {\n return code;\n }\n\n const ast = parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n\n if (!ast) {\n return code;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n traverse(ast, {\n ObjectExpression(nodePath) {\n ParseRoutes.processRouteFileCode(nodePath, importsMap);\n },\n });\n\n return generate(ast, {\n retainLines: true,\n }).code;\n }\n}\n\nexport default ParseRoutes;\n"],"names":["generate","babelGenerate","default","traverse","babelTraverse","ParseRoutes","pathNormalize","config","constructor","viteAliases","this","PathNormalize","parse","clientFile","root","getParams","clientEntrypoint","resolve","routesEntrypoint","findRoutesEntrypoint","routesPath","Error","exportName","routeFilepath","resolveFilename","recursiveBuildRoutesTree","parseFile","filename","code","fs","readFileSync","parser","sourceType","plugins","e","getImportPath","ast","importName","ImportDeclaration","nodePath","importNode","node","specifiers","forEach","specifier","local","name","type","source","value","findRoutesDefinition","exportNameResolved","ExportNamedDeclaration","declaration","length","exportedName","exported","ExportDefaultDeclaration","declarations","id","variableNode","VariableDeclaration","routesVariable","CallExpression","callee","arguments","relativeFile","resolvedFilename","startsWith","path","dirname","filepath","getAppPath","findAppFile","parseRoutesArray","elements","importsMap","results","index","routeInfo","import","children","properties","prop","objectProp","key","importCall","body","importArg","importPath","openingElement","isDefault","childrenFilePath","push","static","routesNode","parseImportsMap","init","property","isObjectProperty","isIdentifier","componentName","isJSXElement","isJSXIdentifier","parent","findParent","p","isArrayExpression","pathIdProperty","objectProperty","identifier","stringLiteral","splice","indexOf","element","isObjectExpression","processRouteFileCode","ObjectExpression","retainLines"],"mappings":"2ZA+BA,MAAMA,EAAYC,EAAcC,SAAWD,EAErCE,EAAYC,EAAcF,SAAWE,EAuB3C,MAAMC,EAIeC,cAKAC,OAKnBC,YAAYD,EAAsBE,GAChCC,KAAKH,OAASA,EACdG,KAAKJ,cAAgB,IAAIK,EAAcJ,EAAQE,GAM1CG,QACL,MAAMC,WAAEA,EAAUC,KAAEA,GAASJ,KAAKH,OAAOQ,YAEnCC,EAAmBC,EAAQH,EAAMD,GACjCK,EAAmBR,KAAKS,qBAAqBH,GAEnD,IAAKE,GAAkBE,WACrB,MAAM,IAAIC,MAAM,wCAAwCR,KAG1D,MAAMO,WAAEA,EAAUE,WAAEA,GAAeJ,EAC7BK,EAAgBb,KAAKc,gBAAgBJ,EAAYJ,GAEvD,OAAON,KAAKe,yBAAyBF,EAAeD,GAM9CI,UAAUC,GAChB,IACE,MAAMC,EAAOC,EAAGC,aAAaH,EAAU,SAEvC,OAAOI,EAAOnB,MAAMgB,EAAM,CACxBI,WAAY,SACZC,QAAS,CAAC,aAAc,SAE1B,MAAOC,GACP,OAAO,MAOHC,cACNC,EACAC,GAEA,IAAIjB,EAA4B,KAC5BE,EAA4B,KAehC,OAbAnB,EAASiC,EAAK,CACZE,kBAAkBC,GAChB,MAAMC,EAAaD,EAASE,KAE5BD,EAAWE,WAAWC,SAASC,IACzBA,EAAUC,MAAMC,OAAST,IAC3Bf,EAAgC,2BAAnBsB,EAAUG,KAAoC,KAAOV,EAClEjB,EAAaoB,EAAWQ,OAAOC,SAGpC,IAGI,CACL7B,aACAE,cAOI4B,qBACNd,EACAd,GAEA,IAAI6B,EAAqB7B,EAoCzB,GAjCAnB,EAASiC,EAAK,CACZgB,wBAAuBX,KAAEA,KAClBA,EAAKY,aAAeZ,EAAKC,WAAWY,OAAS,GAChDb,EAAKC,WAAWC,SAASC,IAEvB,MAAMW,EAAeX,EAAUY,SAASV,KAErB,OAAfxB,GAA0C,oBAAnBsB,EAAUG,KACN,YAAzBH,EAAUC,MAAMC,OAClBK,EAAqBI,GAEdA,IAAiBjC,IAG1B6B,EAAqBP,EAAUC,MAAMC,QAI5C,EACDW,0BAAyBhB,KAAEA,IACN,OAAfnB,IAC4B,eAA1BmB,EAAKY,YAAYN,KACnBI,EAAqBV,EAAKY,YAAYP,KAEH,wBAA1BL,EAAKY,YAAYN,OAG1BI,EAAqBV,EAAKY,YAAYK,aAAa,GAAGC,GAAGb,MAG9D,IAGCK,EAAoB,CACtB,IAAIS,EAA2C,KAa/C,OAXAzD,EAASiC,EAAK,CACZyB,qBAAoBpB,KAAEA,IACpBA,EAAKiB,aAAaf,SAASU,IAErBA,EAAYM,GAAGb,OAASK,IAC1BS,EAAenB,KAGpB,IAGImB,EAGT,OAAO,KAMDzC,qBAAqBH,GAC3B,MAAMoB,EAAM1B,KAAKgB,UAAUV,GAE3B,IAAI8C,EAAgC,KAEpC,OAAK1B,GAILjC,EAASiC,EAAK,CACZ2B,gBAAetB,KAAEA,IAGQ,gBAArBA,EAAKuB,OAAOlB,MACZL,EAAKwB,UAAUX,QAAU,GACE,eAA3Bb,EAAKwB,UAAU,GAAGlB,OAElBe,EAAiBrB,EAAKwB,UAAU,GAAGnB,KAEtC,IAGIpC,KAAKyB,cAAcC,EAAK0B,IAhBtBA,EAsBHtC,gBAAgBG,EAAkBuC,GACxC,IAAIC,EAAmBxC,GAElBA,EAASyC,WAAW,OAASzC,EAASyC,WAAW,SAAWF,IAC/DC,EAAmBE,EAAKpD,QAAQoD,EAAKC,QAAQJ,GAAevC,IAG9D,MAAM4C,EAAW7D,KAAKJ,cAAckE,WAAWL,GAAkB,GAEjE,OAAOzD,KAAKJ,cAAcmE,YAAYF,GAMhCG,iBACNC,EACAC,EACAV,GAEA,MAAMW,EAAyB,GAoF/B,OAlFAF,EAAShC,SAAQ,CAACF,EAAMqC,KACtB,GAAkB,qBAAdrC,EAAKM,KAA6B,CACpC,MAAMgC,EAAyB,CAAED,QAAOE,OAAQ,GAAIC,SAAU,IAE9DxC,EAAKyC,WAAWvC,SAASwC,IACvB,MAAMC,EAAaD,EAcnB,GAT4B,aAAxBC,EAAWC,IAAIvC,MAAiD,oBAA1BsC,EAAWnC,MAAMF,OACzDgC,EAAUE,SAAWvE,KAAKgE,iBACxBU,EAAWnC,MAAM0B,SACjBC,EACAV,IAMsB,SAAxBkB,EAAWC,IAAIvC,MACW,4BAA1BsC,EAAWnC,MAAMF,KACjB,CAEA,MAAMuC,EAAaF,EAAWnC,MAAMsC,KAEpC,GAAwB,mBAApBD,EAAWvC,MAAwD,WAA3BuC,EAAWtB,OAAOjB,KAAmB,CAC/E,MAAOyC,GAAaF,EAAWrB,UAER,kBAAnBuB,EAAUzC,OACZgC,EAAUC,OAASQ,EAAUvC,QAMnC,GAA4B,cAAxBmC,EAAWC,IAAIvC,MAAkD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAEjF,MAAMV,EAAa+C,EAAWnC,MAAMH,MAC5BuB,KAAMoB,GAAeb,EAAWvC,IAAe,CAAE,EAErDoD,IACFV,EAAUC,OAASS,GAKvB,GAA4B,YAAxBL,EAAWC,IAAIvC,MAAgD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAG/E,MAAMV,EAAa+C,EAAWnC,OAAOyC,gBAAgB5C,MAAMA,MACnDuB,KAAMoB,GAAeb,EAAWvC,IAAe,CAAE,EAErDoD,IACFV,EAAUC,OAASS,GAIvB,GAA4B,aAAxBL,EAAWC,IAAIvC,MAAiD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAEhF,MAAMV,EAAa+C,EAAWnC,MAAMH,MAC5BuB,KAAMoB,EAAUE,UAAEA,GAAcf,EAAWvC,IAAe,CAAE,EAEpE,GAAIoD,EAAY,CACd,MAAMG,EAAmBlF,KAAKc,gBAAgBiE,EAAYvB,GAEtD0B,IACFb,EAAUE,SAAWvE,KAAKe,yBACxBmE,EACAD,EAAY,KAAOtD,UAOzB0C,EAAUC,QAAUD,EAAUE,SAAS3B,OAAS,IAClDuB,EAAQgB,KAAKd,OAKZF,EAMDiB,uBAAuB1D,GAC7B,MAAMwC,EAA0B,CAAE,EAelC,OAbAzE,EAASiC,EAAK,CACZE,kBAAkBC,GAChB,MAAMC,EAAaD,EAASE,KAE5BD,EAAWE,WAAWC,SAASC,IAC7BgC,EAAWhC,EAAUC,MAAMC,MAAQ,CACjCuB,KAAM7B,EAAWQ,OAAOC,MACxB0C,UAA8B,2BAAnB/C,EAAUG,KACtB,GAEJ,IAGI6B,EAMDnD,yBACNE,EACAL,EAA4B,MAE5B,IAAKK,EACH,MAAO,GAGT,MAAMS,EAAM1B,KAAKgB,UAAUC,GAE3B,IAAKS,EACH,MAAO,GAGT,MAAM2D,EAAarF,KAAKwC,qBAAqBd,EAAKd,GAC5CuD,EAAyB,GAE/B,IAAKkB,EACH,OAAOlB,EAGT,MAAMD,EAAavE,EAAY2F,gBAAgB5D,GAGzCuC,EAAWoB,EAAWrC,aAAa,GAAGuC,MAAMtB,SAIlD,OAFAE,EAAQgB,QAAQnF,KAAKgE,iBAAiBC,EAAUC,EAAYjD,IAErDkD,EAMDiB,4BACNvD,EACAqC,GAEArC,EAASE,KAAKyC,WAAWvC,SAASuD,IAChC,GAAIC,EAAiBD,IAAaE,EAAaF,EAASb,KAAM,CAC5D,GAA0B,YAAtBa,EAASb,IAAIvC,MAA4C,cAAtBoD,EAASb,IAAIvC,KAAsB,CACxE,IAAIuD,EAAgB,GAEhBC,EAAaJ,EAASjD,QAAUsD,EAAgBL,EAASjD,MAAMyC,eAAe5C,MAChFuD,EAAgBH,EAASjD,MAAMyC,eAAe5C,KAAKA,KAC1CsD,EAAaF,EAASjD,SAC/BoD,EAAgBH,EAASjD,MAAMH,MAGjC,MAAM0D,EAASjE,EAASkE,YAAYC,GAAMC,EAAkBD,EAAEjE,QACxDJ,EAAauC,EAAWyB,IAAgBhC,KAE9C,GAAImC,GAAUnE,EAAY,CACxB,MAAMuE,EAAiBC,EAAeC,EAAW,UAAWC,EAAc1E,IAG1EE,EAASE,KAAKyC,WAAW8B,OACvBzE,EAASE,KAAKyC,WAAW+B,QAAQf,GAAY,EAC7C,EACAU,IAKoB,aAAtBV,EAASb,IAAIvC,MAAuB6D,EAAkBT,EAASjD,QAEjEiD,EAASjD,MAAM0B,SAAShC,SAASuE,IAC3BC,EAAmBD,IACrB7G,EAAY+G,qBACV,CAAE3E,KAAMyE,GACRtC,UAYPkB,oBAAoBlE,GACzB,IAAKA,EACH,OAAOA,EAGT,MAAMQ,EAAML,EAAOnB,MAAMgB,EAAM,CAC7BI,WAAY,SACZC,QAAS,CAAC,aAAc,SAG1B,IAAKG,EACH,OAAOR,EAGT,MAAMgD,EAAavE,EAAY2F,gBAAgB5D,GAQ/C,OANAjC,EAASiC,EAAK,CACZiF,iBAAiB9E,GACflC,EAAY+G,qBAAqB7E,EAAUqC,EAC5C,IAGI5E,EAASoC,EAAK,CACnBkF,aAAa,IACZ1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-normalize.js","sources":["../../src/services/path-normalize.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'path';\nimport type { Alias } from 'vite';\nimport type ServerConfig from '@services/server-config';\n\n/**\n * Service for work with path and aliases\n */\nclass PathNormalize {\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n }\n\n /**\n * Get vite aliases\n */\n public getAliases(): Record<string, string> {\n const aliases: Record<string, string> = {};\n\n this.viteAliases?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return filename postfix\n */\n public getImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['', '.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Resolve app path\n */\n public getAppPath(appPath?: string, withRoot = false): string | undefined {\n if (!appPath) {\n return;\n }\n\n const { root } = this.config.getParams();\n let fullPath = appPath;\n\n // relative import\n if (appPath.startsWith('./') || appPath.startsWith('../')) {\n fullPath = path.resolve(root, appPath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = appPath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = appPath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\n\n if (withRoot) {\n return fullPath;\n }\n\n return fullPath.replace(root, '').replace(/^(\\/)|(\\/)$/g, '');\n }\n\n /**\n * Find app filepath\n */\n public findAppFile(basePath: string): string | null {\n const postfixes = this.getImportPostfix();\n\n for (const postfix of postfixes) {\n const filepath = `${basePath}${postfix}`;\n\n if (fs.existsSync(filepath) && fs.statSync(filepath).isFile()) {\n return filepath;\n }\n }\n\n return null;\n }\n}\n\nexport default PathNormalize;\n"],"names":["PathNormalize","viteAliases","config","constructor","this","getVite","resolve","alias","getAliases","aliases","forEach","find","replacement","getImportPostfix","map","prefix","ext","flat","getAppPath","appPath","withRoot","root","getParams","fullPath","startsWith","path","routeAlias","split","replace","win32","sep","join","posix","findAppFile","basePath","postfixes","postfix","filepath","fs","existsSync","statSync","isFile"],"mappings":"2CAQA,MAAMA,EAIeC,YAKAC,OAKnBC,YAAYD,EAAsBD,GAChCG,KAAKF,OAASA,EACdE,KAAKH,YAAcA,GAAeC,EAAOG,WAAWH,QAAQI,QAAQC,
|
|
1
|
+
{"version":3,"file":"path-normalize.js","sources":["../../src/services/path-normalize.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'path';\nimport type { Alias } from 'vite';\nimport type ServerConfig from '@services/server-config';\n\n/**\n * Service for work with path and aliases\n */\nclass PathNormalize {\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n }\n\n /**\n * Get vite aliases\n */\n public getAliases(): Record<string, string> {\n const aliases: Record<string, string> = {};\n\n this.viteAliases?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return filename postfix\n */\n public getImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['', '.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Resolve app path\n */\n public getAppPath(appPath?: string, withRoot = false): string | undefined {\n if (!appPath) {\n return;\n }\n\n const { root } = this.config.getParams();\n let fullPath = appPath;\n\n // relative import\n if (appPath.startsWith('./') || appPath.startsWith('../')) {\n fullPath = path.resolve(root, appPath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = appPath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = appPath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\n\n if (withRoot) {\n return fullPath;\n }\n\n return fullPath.replace(root, '').replace(/^(\\/)|(\\/)$/g, '');\n }\n\n /**\n * Find app filepath\n */\n public findAppFile(basePath: string): string | null {\n const postfixes = this.getImportPostfix();\n\n for (const postfix of postfixes) {\n const filepath = `${basePath}${postfix}`;\n\n if (fs.existsSync(filepath) && fs.statSync(filepath).isFile()) {\n return filepath;\n }\n }\n\n return null;\n }\n}\n\nexport default PathNormalize;\n"],"names":["PathNormalize","viteAliases","config","constructor","this","getVite","resolve","alias","getAliases","aliases","forEach","find","replacement","getImportPostfix","map","prefix","ext","flat","getAppPath","appPath","withRoot","root","getParams","fullPath","startsWith","path","routeAlias","split","replace","win32","sep","join","posix","findAppFile","basePath","postfixes","postfix","filepath","fs","existsSync","statSync","isFile"],"mappings":"2CAQA,MAAMA,EAIeC,YAKAC,OAKnBC,YAAYD,EAAsBD,GAChCG,KAAKF,OAASA,EACdE,KAAKH,YAAcA,GAAeC,EAAOG,WAAWH,QAAQI,QAAQC,MAM/DC,aACL,MAAMC,EAAkC,CAAE,EAU1C,OARAL,KAAKH,aAAaS,SAAQ,EAAGC,OAAMC,kBACb,iBAATD,IAIXF,EAAQE,GAAQC,EAAW,IAGtBH,EAMFI,mBACL,MAAO,CAAC,GAAI,UACTC,KAAKC,GAAW,CAAC,GAAI,MAAO,MAAO,QAAQD,KAAKE,GAAQ,GAAGD,IAASC,QACpEC,OAMEC,WAAWC,EAAkBC,GAAW,GAC7C,IAAKD,EACH,OAGF,MAAME,KAAEA,GAASjB,KAAKF,OAAOoB,YAC7B,IAAIC,EAAWJ,EAGf,GAAIA,EAAQK,WAAW,OAASL,EAAQK,WAAW,OACjDD,EAAWE,EAAKnB,QAAQe,EAAMF,OACzB,CAEL,MAAMV,EAAUL,KAAKI,cAEdkB,GAAcP,EAAQQ,MAAM,KAE/BlB,EAAQiB,KACVH,EAAWJ,EAAQS,QAAQF,EAAYjB,EAAQiB,KAOnD,OAFAH,EAAWA,EAASI,MAAMF,EAAKI,MAAMC,KAAKC,KAAKN,EAAKO,MAAMF,KAEtDV,EACKG,EAGFA,EAASK,QAAQP,EAAM,IAAIO,QAAQ,eAAgB,IAMrDK,YAAYC,GACjB,MAAMC,EAAY/B,KAAKS,mBAEvB,IAAK,MAAMuB,KAAWD,EAAW,CAC/B,MAAME,EAAW,GAAGH,IAAWE,IAE/B,GAAIE,EAAGC,WAAWF,IAAaC,EAAGE,SAASH,GAAUI,SACnD,OAAOJ,EAIX,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { pathToFileURL } from 'node:url';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Server API\n */\n protected readonly serverApi: ServerApi;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Hook which calls after express server started\n */\n public onServerStarted?: IEntrypointOptions['onServerStarted'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * Middlewares configs\n */\n protected middlewaresConfigs?: IPrepareRenderOut['middlewares'];\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig, serverApi?: ServerApi) {\n this.config = config;\n this.serverApi = serverApi ?? new ServerApi();\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig, serverApi?: ServerApi): PrepareServer {\n return new PrepareServer(config, serverApi);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default as IPrepareRenderOut;\n } else {\n resolvedEntrypoint = (\n (await import(pathToFileURL(entrypointPath).toString())) as { default: IPrepareRenderOut }\n ).default;\n }\n } catch (e) {\n if (\n e instanceof Error &&\n e.message.includes('Cannot find module') &&\n e.message.includes('/build/')\n ) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )} or provide path to build dir: ${chalk.yellow(\n 'ssr-boost start --build-dir build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay, loggerProd, loggerDev, middlewares } =\n resolvedEntrypoint;\n\n this.middlewaresConfigs = middlewares;\n\n if (loggerProd && isProd) {\n this.config.setLogger(loggerProd);\n } else if (loggerDev && !isProd) {\n this.config.setLogger(loggerDev);\n }\n\n const { onServerCreated, onServerStarted, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\n };\n this.onServerCreated = onServerCreated;\n this.onServerStarted = onServerStarted;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile, clientFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n const clientFileEntry = path.posix.normalize(\n `${this.config.getVite()?.config.base}/${clientFile}`,\n );\n\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (\n await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html, indexFile)\n )\n // remove 'async' attribute from app entrypoint for development\n // it might cause problems with preambles from @vitejs/plugin-react\n .replace(\n new RegExp(\n `<script[^>]*?\\\\bsrc=[\"']/?${clientFileEntry}([^\"']*)[\"'][^>]*?\\\\sasync\\\\b`,\n 'g',\n ),\n (match) => match.replace(/\\sasync\\b/, ''),\n );\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!, this.serverApi);\n\n return this;\n }\n\n /**\n * Return SSR express middlewares configs\n */\n public getMiddlewaresConfig(): NonNullable<PrepareServer['middlewaresConfigs']> {\n const { compression, expressStatic } = this.middlewaresConfigs ?? {};\n\n return {\n compression:\n compression !== false\n ? {\n ...(compression ?? {}),\n }\n : false,\n expressStatic:\n expressStatic !== false\n ? {\n ...(expressStatic ?? {}),\n basename: expressStatic?.basename ?? '/',\n }\n : false,\n };\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","serverApi","entrypoint","onServerCreated","onServerStarted","html","middlewaresConfigs","constructor","this","ServerApi","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","loggerProd","loggerDev","middlewares","setLogger","renderParams","req","indexFile","clientFile","fs","readFileSync","modifiedHtml","clientFileEntry","posix","normalize","base","transformIndexHtml","originalUrl","replace","RegExp","match","split","loadEntrypoint","getApp","getMiddlewaresConfig","compression","expressStatic","basename"],"mappings":"oKA6BA,MAAMA,EAIeC,OAKAC,UAKTC,WAKAC,gBAKHC,gBAKGC,KAKAC,mBAKVC,YAAsBP,EAAsBC,GAC1CO,KAAKR,OAASA,EACdQ,KAAKP,UAAYA,GAAa,IAAIQ,
|
|
1
|
+
{"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { pathToFileURL } from 'node:url';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Server API\n */\n protected readonly serverApi: ServerApi;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Hook which calls after express server started\n */\n public onServerStarted?: IEntrypointOptions['onServerStarted'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * Middlewares configs\n */\n protected middlewaresConfigs?: IPrepareRenderOut['middlewares'];\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig, serverApi?: ServerApi) {\n this.config = config;\n this.serverApi = serverApi ?? new ServerApi();\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig, serverApi?: ServerApi): PrepareServer {\n return new PrepareServer(config, serverApi);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default as IPrepareRenderOut;\n } else {\n resolvedEntrypoint = (\n (await import(pathToFileURL(entrypointPath).toString())) as { default: IPrepareRenderOut }\n ).default;\n }\n } catch (e) {\n if (\n e instanceof Error &&\n e.message.includes('Cannot find module') &&\n e.message.includes('/build/')\n ) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )} or provide path to build dir: ${chalk.yellow(\n 'ssr-boost start --build-dir build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay, loggerProd, loggerDev, middlewares } =\n resolvedEntrypoint;\n\n this.middlewaresConfigs = middlewares;\n\n if (loggerProd && isProd) {\n this.config.setLogger(loggerProd);\n } else if (loggerDev && !isProd) {\n this.config.setLogger(loggerDev);\n }\n\n const { onServerCreated, onServerStarted, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\n };\n this.onServerCreated = onServerCreated;\n this.onServerStarted = onServerStarted;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile, clientFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n const clientFileEntry = path.posix.normalize(\n `${this.config.getVite()?.config.base}/${clientFile}`,\n );\n\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (\n await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html, indexFile)\n )\n // remove 'async' attribute from app entrypoint for development\n // it might cause problems with preambles from @vitejs/plugin-react\n .replace(\n new RegExp(\n `<script[^>]*?\\\\bsrc=[\"']/?${clientFileEntry}([^\"']*)[\"'][^>]*?\\\\sasync\\\\b`,\n 'g',\n ),\n (match) => match.replace(/\\sasync\\b/, ''),\n );\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!, this.serverApi);\n\n return this;\n }\n\n /**\n * Return SSR express middlewares configs\n */\n public getMiddlewaresConfig(): NonNullable<PrepareServer['middlewaresConfigs']> {\n const { compression, expressStatic } = this.middlewaresConfigs ?? {};\n\n return {\n compression:\n compression !== false\n ? {\n ...(compression ?? {}),\n }\n : false,\n expressStatic:\n expressStatic !== false\n ? {\n ...(expressStatic ?? {}),\n basename: expressStatic?.basename ?? '/',\n }\n : false,\n };\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","serverApi","entrypoint","onServerCreated","onServerStarted","html","middlewaresConfigs","constructor","this","ServerApi","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","loggerProd","loggerDev","middlewares","setLogger","renderParams","req","indexFile","clientFile","fs","readFileSync","modifiedHtml","clientFileEntry","posix","normalize","base","transformIndexHtml","originalUrl","replace","RegExp","match","split","loadEntrypoint","getApp","getMiddlewaresConfig","compression","expressStatic","basename"],"mappings":"oKA6BA,MAAMA,EAIeC,OAKAC,UAKTC,WAKAC,gBAKHC,gBAKGC,KAKAC,mBAKVC,YAAsBP,EAAsBC,GAC1CO,KAAKR,OAASA,EACdQ,KAAKP,UAAYA,GAAa,IAAIQ,EAM7BC,YAAYV,EAAsBC,GACvC,OAAO,IAAIF,EAAcC,EAAQC,GAM5BU,qBAAqBC,GAAa,GAEvC,GAAIJ,KAAKN,YAAcM,KAAKR,OAAOa,OACjC,OAAOL,KAAKN,WAGd,MAAMY,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeP,KAAKR,OAAOgB,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAQMQ,OAAOC,EAAcL,GAAgBM,aAC5CC,eAPMhB,KAAKR,OAAOyB,UAAWC,cAAcT,EAAgB,CACzDU,eAAe,KAEjBH,QAMJ,MAAOI,GACP,GACEA,aAAaC,OACbD,EAAEE,QAAQC,SAAS,uBACnBH,EAAEE,QAAQC,SAAS,WAcnB,OAZAvB,KAAKR,OACFgC,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,oDACiCF,EAAME,OACvC,yCAKDC,EAAQC,KAAK,GAGtB,MAAMV,GAGHhB,GAAcQ,EAAmBmB,aAC7BnB,EAAmBmB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,EAAUC,WAAEA,EAAUC,UAAEA,EAASC,YAAEA,GAC/DzB,EAEFZ,KAAKF,mBAAqBuC,EAEtBF,GAAc9B,EAChBL,KAAKR,OAAO8C,UAAUH,GACbC,IAAc/B,GACvBL,KAAKR,OAAO8C,UAAUF,GAGxB,MAAMzC,gBAAEA,EAAeC,gBAAEA,KAAoB2C,SACpCR,IAAO,CACZvC,OAAQQ,KAAKR,WACR,CAAE,EAWX,OATAQ,KAAKN,WAAa,CAChBsC,SACAC,SACAC,gBACGK,GAELvC,KAAKL,gBAAkBA,EACvBK,KAAKJ,gBAAkBA,EAEhBI,KAAKN,WAMPS,eAAeqC,GACpB,MAAMnC,OAAEA,EAAMC,KAAEA,EAAImC,UAAEA,EAASC,WAAEA,GAAe1C,KAAKR,OAAOgB,YAEvDR,KAAKH,MAASQ,IACjBL,KAAKH,KAAO8C,EAAGC,aAAalC,EAAKC,QAAQ,GAAGL,KAAQmC,KAAc,UAGpE,IAAII,EAAe7C,KAAKH,KAExB,IAAKQ,EAAQ,CACX,MAAMyC,EAAkBpC,EAAKqC,MAAMC,UACjC,GAAGhD,KAAKR,OAAOyB,WAAWzB,OAAOyD,QAAQP,KAM3CG,SACQ7C,KAAKR,OAAOyB,UAAWiC,mBAAmBV,EAAIW,YAAanD,KAAKH,KAAM4C,IAI3EW,QACC,IAAIC,OACF,6BAA6BP,iCAC7B,MAEDQ,GAAUA,EAAMF,QAAQ,YAAa,MAI5C,OAAOP,EAAaU,MAAM,2BAMrBpD,qBAIL,aAHMH,KAAKwD,uBACLxD,KAAKL,kBAAkBK,KAAKR,OAAOiE,SAAWzD,KAAKP,YAElDO,KAMF0D,uBACL,MAAMC,YAAEA,EAAWC,cAAEA,GAAkB5D,KAAKF,oBAAsB,CAAE,EAEpE,MAAO,CACL6D,aACkB,IAAhBA,GACI,IACMA,GAAe,CAAA,GAG3BC,eACoB,IAAlBA,GACI,IACMA,GAAiB,CAAA,EACrBC,SAAUD,GAAeC,UAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-api.js","sources":["../../src/services/server-api.ts"],"sourcesContent":["/**\n * Allow to configure server on fly\n */\nclass ServerApi {\n /**\n * Block access to index.html\n */\n protected isAllowIndexHtml = false;\n\n /**\n * Server can return index.html\n */\n public hasAccessIndexHtml(): boolean {\n return this.isAllowIndexHtml;\n }\n\n /**\n * Allow/disallow return index.html on request\n */\n public changeAccessIndexHtml(isAllowed: boolean): void {\n this.isAllowIndexHtml = isAllowed;\n }\n}\n\nexport default ServerApi;\n"],"names":["ServerApi","isAllowIndexHtml","hasAccessIndexHtml","this","changeAccessIndexHtml","isAllowed"],"mappings":"AAGA,MAAMA,EAIMC,kBAAmB,EAKtBC,qBACL,OAAOC,KAAKF,
|
|
1
|
+
{"version":3,"file":"server-api.js","sources":["../../src/services/server-api.ts"],"sourcesContent":["/**\n * Allow to configure server on fly\n */\nclass ServerApi {\n /**\n * Block access to index.html\n */\n protected isAllowIndexHtml = false;\n\n /**\n * Server can return index.html\n */\n public hasAccessIndexHtml(): boolean {\n return this.isAllowIndexHtml;\n }\n\n /**\n * Allow/disallow return index.html on request\n */\n public changeAccessIndexHtml(isAllowed: boolean): void {\n this.isAllowIndexHtml = isAllowed;\n }\n}\n\nexport default ServerApi;\n"],"names":["ServerApi","isAllowIndexHtml","hasAccessIndexHtml","this","changeAccessIndexHtml","isAllowed"],"mappings":"AAGA,MAAMA,EAIMC,kBAAmB,EAKtBC,qBACL,OAAOC,KAAKF,iBAMPG,sBAAsBC,GAC3BF,KAAKF,iBAAmBI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport type { IBuildEntrypoint } from '@services/build';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n isModulePreload?: boolean;\n mode?: string;\n entrypointName?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n clientFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * Add module preload scripts to server output\n */\n public readonly isModulePreload: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Run specified entrypoint\n */\n protected readonly entrypointName?: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default params\n */\n protected defaultParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * Default root dir\n */\n protected defaultBuildRoots = ['./build', './dist'];\n\n /**\n * @constructor\n */\n protected constructor(\n {\n entrypointName,\n isProd = false,\n isHost = false,\n isOnlyClient = false,\n isModulePreload = false,\n mode = 'production',\n }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.entrypointName = entrypointName;\n this.defaultParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n isSPA: isOnlyClient,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Lookup build folder\n */\n protected getBuildDir(root?: string): string {\n for (const dir of [root, ...this.defaultBuildRoots]) {\n if (dir && fs.existsSync(dir)) {\n return dir;\n }\n }\n\n return root ?? this.defaultBuildRoots[0];\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n this.applyEntrypointConfig();\n\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n const {\n root,\n publicDir,\n indexFile,\n clientFile,\n serverFile,\n host: defaultHost,\n isSPA,\n } = this.defaultParams;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const entrypoint = this.getEntrypoint();\n\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : (config?.server.host ?? defaultHost!);\n const port = Number(config?.env.VITE_PORT ?? config?.server.port ?? this.defaultParams.port!);\n\n this.params = {\n root: config?.root ?? this.getBuildDir(root),\n publicDir: config?.publicDir ?? publicDir!,\n indexFile: pluginConfig.indexFile ?? indexFile!,\n clientFile: pluginConfig.clientFile ?? clientFile!,\n serverFile: pluginConfig.serverFile ?? serverFile!,\n pluginPath,\n host,\n port,\n isSPA: entrypoint ? entrypoint.type === 'spa' : isSPA!,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp(express: Express): void {\n this.app = express;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp(): Express | undefined {\n return this.app;\n }\n\n /**\n * Return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n\n /**\n * Set custom logger\n */\n public setLogger(logger: Logger): void {\n this.logger = logger;\n }\n\n /**\n * Apply config to specified entrypoint\n */\n protected applyEntrypointConfig(): void {\n const config = this.getPluginConfig();\n\n if (!this.vite || !config || !this.entrypointName) {\n return;\n }\n\n const entrypointConfig = this.getEntrypoint();\n\n // apply specified entrypoint config\n if (entrypointConfig) {\n (['indexFile', 'clientFile', 'serverFile'] as const).forEach((optName) => {\n if (entrypointConfig[optName]) {\n config[optName] = entrypointConfig[optName]!;\n }\n });\n }\n }\n\n /**\n * Get current entrypoint\n */\n protected getEntrypoint(): IBuildEntrypoint | undefined {\n const config = this.vite ? getPluginConfig(this.vite.config) : undefined;\n\n return config?.entrypoint?.find(({ name }) => name === this.entrypointName);\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","mode","entrypointName","vite","app","params","defaultParams","logger","defaultBuildRoots","constructor","isOnlyClient","prodParams","this","publicDir","indexFile","serverFile","host","isSPA","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","applyEntrypointConfig","pluginConfig","getPluginConfig","config","clientFile","defaultHost","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","entrypoint","getEntrypoint","server","port","Number","env","VITE_PORT","type","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger","setLogger","entrypointConfig","forEach","optName","find","name"],"mappings":"sHAkCA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,KAKGC,eAKTC,KAKAC,IAKAC,OAKAC,cAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEP,eACEA,EAAcJ,OACdA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKC,KACvBA,EAAO,cAETU,GAEAC,KAAKd,OAASA,EACdc,KAAKb,OAASA,EACda,KAAKZ,gBAAkBA,EACvBY,KAAKX,KAAOA,EACZW,KAAKV,eAAiBA,EACtBU,KAAKN,cAAgB,CACnBO,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,MAAOP,KACJC,GAGLC,KAAKM,
|
|
1
|
+
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport type { IBuildEntrypoint } from '@services/build';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n isModulePreload?: boolean;\n mode?: string;\n entrypointName?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n clientFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * Add module preload scripts to server output\n */\n public readonly isModulePreload: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Run specified entrypoint\n */\n protected readonly entrypointName?: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default params\n */\n protected defaultParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * Default root dir\n */\n protected defaultBuildRoots = ['./build', './dist'];\n\n /**\n * @constructor\n */\n protected constructor(\n {\n entrypointName,\n isProd = false,\n isHost = false,\n isOnlyClient = false,\n isModulePreload = false,\n mode = 'production',\n }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.entrypointName = entrypointName;\n this.defaultParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n isSPA: isOnlyClient,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Lookup build folder\n */\n protected getBuildDir(root?: string): string {\n for (const dir of [root, ...this.defaultBuildRoots]) {\n if (dir && fs.existsSync(dir)) {\n return dir;\n }\n }\n\n return root ?? this.defaultBuildRoots[0];\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n this.applyEntrypointConfig();\n\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n const {\n root,\n publicDir,\n indexFile,\n clientFile,\n serverFile,\n host: defaultHost,\n isSPA,\n } = this.defaultParams;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const entrypoint = this.getEntrypoint();\n\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : (config?.server.host ?? defaultHost!);\n const port = Number(config?.env.VITE_PORT ?? config?.server.port ?? this.defaultParams.port!);\n\n this.params = {\n root: config?.root ?? this.getBuildDir(root),\n publicDir: config?.publicDir ?? publicDir!,\n indexFile: pluginConfig.indexFile ?? indexFile!,\n clientFile: pluginConfig.clientFile ?? clientFile!,\n serverFile: pluginConfig.serverFile ?? serverFile!,\n pluginPath,\n host,\n port,\n isSPA: entrypoint ? entrypoint.type === 'spa' : isSPA!,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp(express: Express): void {\n this.app = express;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp(): Express | undefined {\n return this.app;\n }\n\n /**\n * Return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n\n /**\n * Set custom logger\n */\n public setLogger(logger: Logger): void {\n this.logger = logger;\n }\n\n /**\n * Apply config to specified entrypoint\n */\n protected applyEntrypointConfig(): void {\n const config = this.getPluginConfig();\n\n if (!this.vite || !config || !this.entrypointName) {\n return;\n }\n\n const entrypointConfig = this.getEntrypoint();\n\n // apply specified entrypoint config\n if (entrypointConfig) {\n (['indexFile', 'clientFile', 'serverFile'] as const).forEach((optName) => {\n if (entrypointConfig[optName]) {\n config[optName] = entrypointConfig[optName]!;\n }\n });\n }\n }\n\n /**\n * Get current entrypoint\n */\n protected getEntrypoint(): IBuildEntrypoint | undefined {\n const config = this.vite ? getPluginConfig(this.vite.config) : undefined;\n\n return config?.entrypoint?.find(({ name }) => name === this.entrypointName);\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","mode","entrypointName","vite","app","params","defaultParams","logger","defaultBuildRoots","constructor","isOnlyClient","prodParams","this","publicDir","indexFile","serverFile","host","isSPA","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","applyEntrypointConfig","pluginConfig","getPluginConfig","config","clientFile","defaultHost","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","entrypoint","getEntrypoint","server","port","Number","env","VITE_PORT","type","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger","setLogger","entrypointConfig","forEach","optName","find","name"],"mappings":"sHAkCA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,KAKGC,eAKTC,KAKAC,IAKAC,OAKAC,cAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEP,eACEA,EAAcJ,OACdA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKC,KACvBA,EAAO,cAETU,GAEAC,KAAKd,OAASA,EACdc,KAAKb,OAASA,EACda,KAAKZ,gBAAkBA,EACvBY,KAAKX,KAAOA,EACZW,KAAKV,eAAiBA,EACtBU,KAAKN,cAAgB,CACnBO,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,MAAOP,KACJC,GAGLC,KAAKM,aAMAC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIxB,EAAauB,EAASC,GAMzBC,YAAYC,GACpB,IAAK,MAAMC,IAAO,CAACD,KAASX,KAAKJ,mBAC/B,GAAIgB,GAAOC,EAAGC,WAAWF,GACvB,OAAOA,EAIX,OAAOD,GAAQX,KAAKJ,kBAAkB,GAM9BU,aACRN,KAAKe,wBAEL,MAAMC,EAAgBhB,KAAKiB,mBAAqB,CAAA,GAC1CC,OAAEA,GAAWlB,KAAKT,MAAQ,CAAE,GAC5BoB,KACJA,EAAIV,UACJA,EAASC,UACTA,EAASiB,WACTA,EAAUhB,WACVA,EACAC,KAAMgB,EAAWf,MACjBA,GACEL,KAAKN,cACH2B,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJR,EAAaQ,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpEC,EAAa7B,KAAK8B,gBAElB1B,EAC2B,kBAAxBc,GAAQa,OAAO3B,MAAsBJ,KAAKb,OAC7C,UACC+B,GAAQa,OAAO3B,MAAQgB,EACxBY,EAAOC,OAAOf,GAAQgB,IAAIC,WAAajB,GAAQa,OAAOC,MAAQhC,KAAKN,cAAcsC,MAEvFhC,KAAKP,OAAS,CACZkB,KAAMO,GAAQP,MAAQX,KAAKU,YAAYC,GACvCV,UAAWiB,GAAQjB,WAAaA,EAChCC,UAAWc,EAAad,WAAaA,EACrCiB,WAAYH,EAAaG,YAAcA,EACvChB,WAAYa,EAAab,YAAcA,EACvCqB,aACApB,OACA4B,OACA3B,MAAOwB,EAAiC,QAApBA,EAAWO,KAAiB/B,EAChDnB,OAAQc,KAAKd,QAEfc,KAAKL,OAASK,KAAKT,MAAM2B,OAAOvB,QAAU,IAAI0C,EAMzCC,QAAQ/C,GACbS,KAAKT,KAAOA,EAEZS,KAAKM,aAMAiC,OAAOC,GACZxC,KAAKR,IAAMgD,EAONC,UACL,OAAOzC,KAAKT,KAMPmD,SACL,OAAO1C,KAAKR,IAOPyB,kBACL,OAAOjB,KAAKT,KAAO0B,EAAgBjB,KAAKT,KAAK2B,aAAUyB,EAMlDC,YACL,OAAO5C,KAAKP,OAMPoD,YACL,OAAO7C,KAAKL,OAMPmD,UAAUnD,GACfK,KAAKL,OAASA,EAMNoB,wBACR,MAAMG,EAASlB,KAAKiB,kBAEpB,IAAKjB,KAAKT,OAAS2B,IAAWlB,KAAKV,eACjC,OAGF,MAAMyD,EAAmB/C,KAAK8B,gBAG1BiB,GACD,CAAC,YAAa,aAAc,cAAwBC,SAASC,IACxDF,EAAiBE,KACnB/B,EAAO+B,GAAWF,EAAiBE,OASjCnB,gBACR,MAAMZ,EAASlB,KAAKT,KAAO0B,EAAgBjB,KAAKT,KAAK2B,aAAUyB,EAE/D,OAAOzB,GAAQW,YAAYqB,MAAK,EAAGC,UAAWA,IAASnD,KAAKV"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Socket } from 'node:net';
|
|
3
|
-
import {
|
|
4
|
-
import { RouteObject } from 'react-router-dom';
|
|
3
|
+
import { RouteObject, RouterState } from 'react-router';
|
|
5
4
|
import { Alias, ModuleNode } from 'vite';
|
|
6
5
|
import { IRequestContext } from "../node/render.js";
|
|
7
6
|
import { TRoutesTree } from "./parse-routes.js";
|
|
@@ -165,14 +164,14 @@ declare class SsrManifest {
|
|
|
165
164
|
/**
|
|
166
165
|
* Get route assets
|
|
167
166
|
*/
|
|
168
|
-
protected getAssets(routes?:
|
|
167
|
+
protected getAssets(routes?: RouterState['matches']): IAsset[];
|
|
169
168
|
/**
|
|
170
169
|
* Get development route assets
|
|
171
170
|
*/
|
|
172
171
|
/**
|
|
173
172
|
* Get development route assets
|
|
174
173
|
*/
|
|
175
|
-
protected getAssetsDev(routes?:
|
|
174
|
+
protected getAssetsDev(routes?: RouterState['matches']): IAsset[];
|
|
176
175
|
/**
|
|
177
176
|
* Get module assets
|
|
178
177
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport chalk from 'chalk';\nimport type { RouteObject } from 'react-router-dom';\nimport type { Alias, ModuleNode } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Recursive walk routes and return id's with route import path\n */\n protected async getAsyncRoutesIds(\n routes: RouteObject[],\n index?: string,\n ): Promise<Record<string, string | undefined>> {\n const result: Record<string, string | undefined> = {};\n\n // reason: await + array index\n // eslint-disable-next-line @typescript-eslint/no-for-in-array\n for (const routeIndex in routes) {\n const route = routes[routeIndex];\n const routeId = [index, routeIndex].filter(Boolean).join('-');\n\n if (route.lazy) {\n try {\n const resolvedRoute: IAsyncRoute = await route.lazy();\n\n result[routeId] = this.pathNormalize.getAppPath(resolvedRoute?.pathId);\n } catch (e) {\n console.error(chalk.red('Failed to load route:'), route.path, e);\n }\n } else if (route.children) {\n Object.assign(result, await this.getAsyncRoutesIds(route.children, routeId));\n }\n }\n\n return result;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n res[asset] = {\n url: path.posix.normalize(`${this.basename}/${asset}`),\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public async buildRoutesManifest(isNodeParsing: boolean): Promise<void> {\n const prepareServer = PrepareServer.init(\n ServerConfig.init({ isProd: true }, { root: this.getOutDir() }),\n );\n const manifest = this.loadClientManifest();\n let routesPaths: Record<string, string | undefined>;\n\n if (isNodeParsing) {\n const { routes } = await prepareServer.loadEntrypoint(false);\n\n routesPaths = await this.getAsyncRoutesIds(routes as RouteObject[]);\n } else {\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n\n routesPaths = this.getRoutesTreeIds(routesService.parse());\n }\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: AgnosticDataRouteMatch[]): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: AgnosticDataRouteMatch[]): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: TAssets = {};\n const postfixes = this.pathNormalize.getImportPostfix();\n const rootId = path.resolve(\n this.root,\n this.config.getPluginConfig()?.clientFile ?? 'client.ts',\n );\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n if (!module?.clientImportedModules.size || skipModules.has(module.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module.file!);\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch (e) {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","async","routes","index","routeIndex","route","routeId","filter","Boolean","join","lazy","resolvedRoute","getAppPath","pathId","e","console","error","chalk","red","children","Object","assign","getAsyncRoutesIds","getRoutesTreeIds","forEach","String","import","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","url","posix","normalize","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","isNodeParsing","prepareServer","PrepareServer","init","ServerConfig","isProd","routesPaths","loadEntrypoint","routesService","ParseRoutes","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","split","at","includes","code","match","groups","style","content","warn","yellowBright","script","toLowerCase","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { RouteObject, RouterState } from 'react-router';\nimport type { Alias, ModuleNode } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Recursive walk routes and return id's with route import path\n */\n protected async getAsyncRoutesIds(\n routes: RouteObject[],\n index?: string,\n ): Promise<Record<string, string | undefined>> {\n const result: Record<string, string | undefined> = {};\n\n // reason: await + array index\n // eslint-disable-next-line @typescript-eslint/no-for-in-array\n for (const routeIndex in routes) {\n const route = routes[routeIndex];\n const routeId = [index, routeIndex].filter(Boolean).join('-');\n\n if (route.lazy) {\n try {\n const resolvedRoute: IAsyncRoute = await route.lazy();\n\n result[routeId] = this.pathNormalize.getAppPath(resolvedRoute?.pathId);\n } catch (e) {\n console.error(chalk.red('Failed to load route:'), route.path, e);\n }\n } else if (route.children) {\n Object.assign(result, await this.getAsyncRoutesIds(route.children, routeId));\n }\n }\n\n return result;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n res[asset] = {\n url: path.posix.normalize(`${this.basename}/${asset}`),\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public async buildRoutesManifest(isNodeParsing: boolean): Promise<void> {\n const prepareServer = PrepareServer.init(\n ServerConfig.init({ isProd: true }, { root: this.getOutDir() }),\n );\n const manifest = this.loadClientManifest();\n let routesPaths: Record<string, string | undefined>;\n\n if (isNodeParsing) {\n const { routes } = await prepareServer.loadEntrypoint(false);\n\n routesPaths = await this.getAsyncRoutesIds(routes as RouteObject[]);\n } else {\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n\n routesPaths = this.getRoutesTreeIds(routesService.parse());\n }\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: RouterState['matches']): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: RouterState['matches']): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: TAssets = {};\n const postfixes = this.pathNormalize.getImportPostfix();\n const rootId = path.resolve(\n this.root,\n this.config.getPluginConfig()?.clientFile ?? 'client.ts',\n );\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n if (!module?.clientImportedModules.size || skipModules.has(module.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module.file!);\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch (e) {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","async","routes","index","routeIndex","route","routeId","filter","Boolean","join","lazy","resolvedRoute","getAppPath","pathId","e","console","error","chalk","red","children","Object","assign","getAsyncRoutesIds","getRoutesTreeIds","forEach","String","import","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","url","posix","normalize","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","isNodeParsing","prepareServer","PrepareServer","init","ServerConfig","isProd","routesPaths","loadEntrypoint","routesService","ParseRoutes","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","split","at","includes","code","match","groups","style","content","warn","yellowBright","script","toLowerCase","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}
|