@@ -9,7 +9,6 @@ import getPort from "get-port";
99import stripIndent from "strip-indent" ;
1010import { sync as spawnSync , spawn } from "cross-spawn" ;
1111import type { JsonObject } from "type-fest" ;
12- import { createRequestListener } from "@mjackson/node-fetch-server" ;
1312
1413import {
1514 type ServerBuild ,
@@ -26,6 +25,65 @@ const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
2625const root = path . join ( __dirname , "../.." ) ;
2726const TMP_DIR = path . join ( root , ".tmp" , "integration" ) ;
2827
28+ export async function spawnTestServer ( {
29+ command,
30+ regex,
31+ validate,
32+ env = { } ,
33+ cwd,
34+ timeout = 20000 ,
35+ } : {
36+ command : string [ ] ;
37+ regex : RegExp ;
38+ validate ?: ( matches : RegExpMatchArray ) => void | Promise < void > ;
39+ env ?: Record < string , string > ;
40+ cwd ?: string ;
41+ timeout ?: number ;
42+ } ) : Promise < { stop : VoidFunction } > {
43+ return new Promise ( ( accept , reject ) => {
44+ let serverProcess = spawn ( command [ 0 ] , command . slice ( 1 ) , {
45+ env : { ...process . env , ...env } ,
46+ cwd,
47+ stdio : "pipe" ,
48+ } ) ;
49+
50+ let started = false ;
51+ let stdout = "" ;
52+ let rejectTimeout = setTimeout ( ( ) => {
53+ reject ( new Error ( `Timed out waiting for server to start (${ timeout } ms)` ) ) ;
54+ } , timeout ) ;
55+
56+ serverProcess . stderr . pipe ( process . stderr ) ;
57+ serverProcess . stdout . on ( "data" , ( chunk : Buffer ) => {
58+ if ( started ) return ;
59+ let newChunk = chunk . toString ( ) ;
60+ stdout += newChunk ;
61+ let match = stdout . match ( regex ) ;
62+ if ( match ) {
63+ clearTimeout ( rejectTimeout ) ;
64+ started = true ;
65+
66+ Promise . resolve ( validate ?.( match ) )
67+ . then ( ( ) => {
68+ accept ( {
69+ stop : ( ) => {
70+ serverProcess . kill ( ) ;
71+ } ,
72+ } ) ;
73+ } )
74+ . catch ( ( error : unknown ) => {
75+ reject ( error ) ;
76+ } ) ;
77+ }
78+ } ) ;
79+
80+ serverProcess . on ( "error" , ( error : unknown ) => {
81+ clearTimeout ( rejectTimeout ) ;
82+ reject ( error ) ;
83+ } ) ;
84+ } ) ;
85+ }
86+
2987export interface FixtureInit {
3088 buildStdio ?: Writable ;
3189 files ?: { [ filename : string ] : string } ;
@@ -230,66 +288,29 @@ export async function createAppFixture(fixture: Fixture, mode?: ServerMode) {
230288 stop : VoidFunction ;
231289 } > => {
232290 if ( fixture . useReactRouterServe ) {
233- return new Promise ( async ( accept , reject ) => {
234- let port = await getPort ( ) ;
235-
236- let nodebin = process . argv [ 0 ] ;
237- let serveProcess = spawn (
238- nodebin ,
239- [
240- "node_modules/@react-router/serve/dist/cli.js" ,
241- "build/server/index.js" ,
242- ] ,
243- {
244- env : {
245- ...process . env ,
246- NODE_ENV : mode || "production" ,
247- PORT : port . toFixed ( 0 ) ,
248- } ,
249- cwd : fixture . projectDir ,
250- stdio : "pipe" ,
251- }
252- ) ;
253- // Wait for `started at http://localhost:${port}` to be printed
254- // and extract the port from it.
255- let started = false ;
256- let stdout = "" ;
257- let rejectTimeout = setTimeout ( ( ) => {
258- reject (
259- new Error ( "Timed out waiting for react-router-serve to start" )
260- ) ;
261- } , 20000 ) ;
262- serveProcess . stderr . pipe ( process . stderr ) ;
263- serveProcess . stdout . on ( "data" , ( chunk ) => {
264- if ( started ) return ;
265- let newChunk = chunk . toString ( ) ;
266- stdout += newChunk ;
267- let match : RegExpMatchArray | null = stdout . match (
268- / \[ r e a c t - r o u t e r - s e r v e \] h t t p : \/ \/ l o c a l h o s t : ( \d + ) \s /
269- ) ;
270- if ( match ) {
271- clearTimeout ( rejectTimeout ) ;
272- started = true ;
273- let parsedPort = parseInt ( match [ 1 ] , 10 ) ;
274-
275- if ( port !== parsedPort ) {
276- reject (
277- new Error (
278- `Expected react-router-serve to start on port ${ port } , but it started on port ${ parsedPort } `
279- )
280- ) ;
281- return ;
282- }
283-
284- accept ( {
285- stop : ( ) => {
286- serveProcess . kill ( ) ;
287- } ,
288- port,
289- } ) ;
291+ let port = await getPort ( ) ;
292+ let { stop } = await spawnTestServer ( {
293+ cwd : fixture . projectDir ,
294+ command : [
295+ process . argv [ 0 ] ,
296+ "node_modules/@react-router/serve/dist/cli.js" ,
297+ "build/server/index.js" ,
298+ ] ,
299+ env : {
300+ NODE_ENV : mode || "production" ,
301+ PORT : port . toFixed ( 0 ) ,
302+ } ,
303+ regex : / \[ r e a c t - r o u t e r - s e r v e \] h t t p : \/ \/ l o c a l h o s t : ( \d + ) \s / ,
304+ validate : ( matches ) => {
305+ let parsedPort = parseInt ( matches [ 1 ] , 10 ) ;
306+ if ( port !== parsedPort ) {
307+ throw new Error (
308+ `Expected react-router-serve to start on port ${ port } , but it started on port ${ parsedPort } `
309+ ) ;
290310 }
291- } ) ;
311+ } ,
292312 } ) ;
313+ return { stop, port } ;
293314 }
294315
295316 if ( fixture . isSpaMode ) {
@@ -334,21 +355,25 @@ export async function createAppFixture(fixture: Fixture, mode?: ServerMode) {
334355 }
335356
336357 if ( fixture . templateName . includes ( "parcel" ) ) {
337- return new Promise ( async ( accept ) => {
338- let port = await getPort ( ) ;
339- let app = express ( ) ;
340- app . use ( express . static ( path . join ( fixture . projectDir , "public" ) ) ) ;
341- app . use (
342- "/client" ,
343- express . static ( path . join ( fixture . projectDir , "build/client" ) )
344- ) ;
345-
346- app . all ( "*" , createRequestListener ( fixture . handler ) ) ;
347-
348- let server = app . listen ( port ) ;
349-
350- accept ( { stop : server . close . bind ( server ) , port } ) ;
358+ let port = await getPort ( ) ;
359+ let { stop } = await spawnTestServer ( {
360+ cwd : fixture . projectDir ,
361+ command : [ process . argv [ 0 ] , "start.js" ] ,
362+ env : {
363+ NODE_ENV : mode || "production" ,
364+ PORT : port . toFixed ( 0 ) ,
365+ } ,
366+ regex : / S e r v e r l i s t e n i n g o n p o r t ( \d + ) \s / ,
367+ validate : ( matches ) => {
368+ let parsedPort = parseInt ( matches [ 1 ] , 10 ) ;
369+ if ( port !== parsedPort ) {
370+ throw new Error (
371+ `Expected Parcel build server to start on port ${ port } , but it started on port ${ parsedPort } `
372+ ) ;
373+ }
374+ } ,
351375 } ) ;
376+ return { stop, port } ;
352377 }
353378
354379 const build = fixture . build ;
0 commit comments