UNPKG

@strapi/strapi

Version:

An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite

1 lines 11.8 kB
{"version":3,"file":"watch.mjs","sources":["../../../../src/node/vite/watch.ts"],"sourcesContent":["import path from 'node:path';\nimport http from 'node:http';\nimport fs from 'node:fs/promises';\nimport { Server } from 'node:net';\nimport type { Core } from '@strapi/types';\n\nimport { mergeConfigWithUserConfig, resolveDevelopmentConfig } from './config';\n\nimport type { BuildContext } from '../create-build-context';\n\ninterface ViteWatcher {\n close(): Promise<void>;\n}\n\nconst HMR_DEFAULT_PORT = 5173;\nconst MAX_PORT_ATTEMPTS = 30;\n\nconst findAvailablePort = (\n startingPort: number,\n attemptsLeft = MAX_PORT_ATTEMPTS\n): Promise<number> => {\n return new Promise((resolve, reject) => {\n if (attemptsLeft <= 0) {\n reject(new Error(`No available ports found after ${MAX_PORT_ATTEMPTS} attempts.`));\n return;\n }\n\n const server = new Server();\n server.listen(startingPort, () => {\n const { port } = server.address() as { port: number };\n server.close(() => resolve(port));\n });\n\n server.on('error', (err: any) => {\n if (err.code === 'EADDRINUSE') {\n resolve(findAvailablePort(startingPort + 1, attemptsLeft - 1));\n } else {\n reject(err);\n }\n });\n });\n};\n\nconst createHMRServer = () => {\n return http.createServer(\n // http server request handler. keeps the same with\n // https://github.com/websockets/ws/blob/45e17acea791d865df6b255a55182e9c42e5877a/lib/websocket-server.js#L88-L96\n (_, res) => {\n const body = http.STATUS_CODES[426]; // Upgrade Required\n\n res.writeHead(426, {\n 'Content-Length': body?.length ?? 0,\n 'Content-Type': 'text/plain',\n });\n\n res.end(body);\n }\n );\n};\n\nconst watch = async (ctx: BuildContext): Promise<ViteWatcher> => {\n const hmrServer = createHMRServer();\n\n // Allowing Vite to find an available port doesn't work, so we'll find an available port manually\n // and use that. There is therefore a very slight race condition if you start up two servers at the same time\n // one might fail, or it might start up but listen on the wrong port.\n const availablePort = await findAvailablePort(HMR_DEFAULT_PORT);\n ctx.options.hmrServer = hmrServer;\n ctx.options.hmrClientPort = availablePort;\n\n const config = await resolveDevelopmentConfig(ctx);\n\n const finalConfig = await mergeConfigWithUserConfig(config, ctx);\n\n const hmrConfig = config.server?.hmr;\n\n // If the server used for Vite hmr is the one we've created (<> no user override)\n if (typeof hmrConfig === 'object' && hmrConfig.server === hmrServer) {\n // Only restart the hmr server when Strapi's server is listening\n strapi.server.httpServer.on('listening', async () => {\n hmrServer.listen(availablePort);\n });\n }\n\n ctx.logger.debug('Vite config', finalConfig);\n\n const { createServer } = await import('vite');\n\n const vite = await createServer(finalConfig);\n\n const viteMiddlewares: Core.MiddlewareHandler = (koaCtx, next) => {\n return new Promise((resolve, reject) => {\n const prefix = ctx.basePath.replace(ctx.adminPath, '').replace(/\\/+$/, '');\n\n const originalPath = koaCtx.path;\n if (!koaCtx.path.startsWith(prefix)) {\n koaCtx.path = `${prefix}${koaCtx.path}`;\n }\n\n // Set cache-control headers to prevent caching issues during development restarts\n koaCtx.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');\n koaCtx.set('Pragma', 'no-cache');\n koaCtx.set('Expires', '0');\n koaCtx.set('Surrogate-Control', 'no-store');\n\n vite.middlewares(koaCtx.req, koaCtx.res, (err: unknown) => {\n if (err) {\n reject(err);\n } else {\n if (!koaCtx.res.headersSent) {\n koaCtx.path = originalPath;\n }\n\n resolve(next());\n }\n });\n });\n };\n\n const serveAdmin: Core.MiddlewareHandler = async (koaCtx, next) => {\n await next();\n\n if (koaCtx.method !== 'HEAD' && koaCtx.method !== 'GET') {\n return;\n }\n\n if (koaCtx.body != null || koaCtx.status !== 404) {\n return;\n }\n\n const url = koaCtx.originalUrl;\n\n try {\n let template = await fs.readFile(\n path.relative(ctx.cwd, '.strapi/client/index.html'),\n 'utf-8'\n );\n template = await vite.transformIndexHtml(url, template);\n\n koaCtx.type = 'html';\n koaCtx.body = template;\n } catch (error) {\n ctx.logger.error('Failed to serve admin panel in development mode:', error);\n // Don't fallback to other handlers in development mode to prevent MIME type conflicts\n koaCtx.status = 500;\n koaCtx.body = 'Admin panel temporarily unavailable during server restart';\n }\n };\n\n const adminRoute = `${ctx.adminPath}/:path*`;\n\n // Remove any existing admin routes to prevent conflicts during restart\n const existingRoutes = ctx.strapi.server.router.stack.filter(\n (layer) => layer.path === adminRoute\n );\n existingRoutes.forEach((route) => {\n const index = ctx.strapi.server.router.stack.indexOf(route);\n if (index > -1) {\n ctx.strapi.server.router.stack.splice(index, 1);\n }\n });\n\n ctx.strapi.server.router.get(adminRoute, serveAdmin);\n ctx.strapi.server.router.use(adminRoute, viteMiddlewares);\n\n return {\n async close() {\n await vite.close();\n\n if (hmrServer.listening) {\n // Manually close the hmr server\n // /!\\ This operation MUST be done after calling .close() on the vite\n // instance to avoid flaky behaviors with attached clients\n await new Promise<void>((resolve, reject) => {\n hmrServer.close((err) => (err ? reject(err) : resolve()));\n });\n }\n },\n };\n};\n\nexport { watch };\nexport type { ViteWatcher };\n"],"names":["HMR_DEFAULT_PORT","MAX_PORT_ATTEMPTS","findAvailablePort","startingPort","attemptsLeft","Promise","resolve","reject","Error","server","Server","listen","port","address","close","on","err","code","createHMRServer","http","createServer","_","res","body","STATUS_CODES","writeHead","length","end","watch","ctx","hmrServer","availablePort","options","hmrClientPort","config","resolveDevelopmentConfig","finalConfig","mergeConfigWithUserConfig","hmrConfig","hmr","strapi","httpServer","logger","debug","vite","viteMiddlewares","koaCtx","next","prefix","basePath","replace","adminPath","originalPath","path","startsWith","set","middlewares","req","headersSent","serveAdmin","method","status","url","originalUrl","template","fs","readFile","relative","cwd","transformIndexHtml","type","error","adminRoute","existingRoutes","router","stack","filter","layer","forEach","route","index","indexOf","splice","get","use","listening"],"mappings":"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"}