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.7 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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"}