Skip to content

Commit cf9df4b

Browse files
feat(command-dev): support frameworks without a port (#4020)
* feat(command-dev): support frameworks without a port * test(windows): fix race condition * test(windows): remove snapshot in favor of string matching Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent cbf029f commit cf9df4b

File tree

6 files changed

+80
-38
lines changed

6 files changed

+80
-38
lines changed

npm-shrinkwrap.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"dependencies": {
8181
"@netlify/build": "^26.1.1",
8282
"@netlify/config": "^17.0.3",
83-
"@netlify/framework-info": "^8.0.0",
83+
"@netlify/framework-info": "^8.0.1",
8484
"@netlify/local-functions-proxy": "^1.1.1",
8585
"@netlify/plugin-edge-handlers": "^3.0.2",
8686
"@netlify/plugins-list": "^6.2.1",

src/utils/detect-server-settings.js

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ const getDefaultDist = () => {
100100
return process.cwd()
101101
}
102102

103+
const getStaticServerPort = async ({ devConfig }) => {
104+
const port = await acquirePort({
105+
configuredPort: devConfig.staticServerPort,
106+
defaultPort: DEFAULT_STATIC_PORT,
107+
errorMessage: 'Could not acquire configured static server port',
108+
})
109+
110+
return port
111+
}
112+
103113
/**
104114
*
105115
* @param {object} param0
@@ -132,11 +142,7 @@ const handleStaticServer = async ({ devConfig, options, projectDir }) => {
132142
const dist = options.dir || devConfig.publish || getDefaultDist()
133143
log(`${NETLIFYDEVWARN} Running static server from "${path.relative(path.dirname(projectDir), dist)}"`)
134144

135-
const frameworkPort = await acquirePort({
136-
configuredPort: devConfig.staticServerPort,
137-
defaultPort: DEFAULT_STATIC_PORT,
138-
errorMessage: 'Could not acquire configured static server port',
139-
})
145+
const frameworkPort = await getStaticServerPort({ devConfig })
140146
return {
141147
...(devConfig.command && { command: devConfig.command }),
142148
useStaticServer: true,
@@ -156,7 +162,7 @@ const getSettingsFromFramework = (framework) => {
156162
dev: {
157163
commands: [command],
158164
port: frameworkPort,
159-
pollingStrategies,
165+
pollingStrategies = [],
160166
},
161167
name: frameworkName,
162168
staticAssetsDirectory: staticDir,
@@ -238,24 +244,40 @@ const handleCustomFramework = ({ devConfig }) => {
238244
}
239245
}
240246

247+
const mergeSettings = async ({ devConfig, frameworkSettings = {} }) => {
248+
const {
249+
command: frameworkCommand,
250+
frameworkPort: frameworkDetectedPort,
251+
dist,
252+
framework,
253+
env,
254+
pollingStrategies = [],
255+
} = frameworkSettings
256+
257+
const command = devConfig.command || frameworkCommand
258+
const frameworkPort = devConfig.targetPort || frameworkDetectedPort
259+
// if the framework doesn't start a server, we use a static one
260+
const useStaticServer = !(command && frameworkPort)
261+
return {
262+
command,
263+
frameworkPort: useStaticServer ? await getStaticServerPort({ devConfig }) : frameworkPort,
264+
dist: devConfig.publish || dist || getDefaultDist(),
265+
framework,
266+
env,
267+
pollingStrategies,
268+
useStaticServer,
269+
}
270+
}
271+
241272
/**
242273
* Handles a forced framework and retrieves the settings for it
243274
* @param {*} param0
244275
* @returns {Promise<import('./types').BaseServerSettings>}
245276
*/
246277
const handleForcedFramework = async ({ devConfig, projectDir }) => {
247278
// this throws if `devConfig.framework` is not a supported framework
248-
const { command, dist, env, framework, frameworkPort, pollingStrategies } = getSettingsFromFramework(
249-
await getFramework(devConfig.framework, { projectDir }),
250-
)
251-
return {
252-
command: devConfig.command || command,
253-
frameworkPort: devConfig.targetPort || frameworkPort,
254-
dist: devConfig.publish || dist,
255-
framework,
256-
env,
257-
pollingStrategies,
258-
}
279+
const frameworkSettings = getSettingsFromFramework(await getFramework(devConfig.framework, { projectDir }))
280+
return mergeSettings({ devConfig, frameworkSettings })
259281
}
260282

261283
/**
@@ -285,15 +307,7 @@ const detectServerSettings = async (devConfig, options, projectDir) => {
285307
settings = await handleStaticServer({ options, devConfig, projectDir })
286308
} else {
287309
validateFrameworkConfig({ devConfig })
288-
const { command, frameworkPort, dist, framework, env, pollingStrategies = [] } = frameworkSettings || {}
289-
settings = {
290-
command: devConfig.command || command,
291-
frameworkPort: devConfig.targetPort || frameworkPort,
292-
dist: devConfig.publish || dist || getDefaultDist(),
293-
framework,
294-
env,
295-
pollingStrategies,
296-
}
310+
settings = await mergeSettings({ devConfig, frameworkSettings })
297311
}
298312
} else if (devConfig.framework === '#custom') {
299313
validateFrameworkConfig({ devConfig })

src/utils/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type BaseServerSettings = {
1818
dist: string
1919

2020
// static serving
21-
useStaticServer?: true
21+
useStaticServer?: boolean
2222

2323
// Framework specific part
2424
/** A port where a proxy can listen to it */

tests/framework-detection.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,31 @@ test('should pass framework-info env to framework sub process', async (t) => {
301301
t.snapshot(normalize(error.stdout))
302302
})
303303
})
304+
305+
test('should start static service for frameworks without port, forced framework', async (t) => {
306+
await withSiteBuilder('site-with-remix', async (builder) => {
307+
await builder.withNetlifyToml({ config: { dev: { framework: 'remix' } } }).buildAsync()
308+
309+
// a failure is expected since this is not a true remix project
310+
const error = await t.throwsAsync(() => withDevServer({ cwd: builder.directory }, () => {}, true))
311+
t.true(error.stdout.includes(`Failed running command: remix watch. Please verify 'remix' exists`))
312+
})
313+
})
314+
315+
test('should start static service for frameworks without port, detected framework', async (t) => {
316+
await withSiteBuilder('site-with-remix', async (builder) => {
317+
await builder
318+
.withPackageJson({
319+
packageJson: {
320+
dependencies: { remix: '^1.0.0', '@remix-run/netlify': '^1.0.0' },
321+
scripts: {},
322+
},
323+
})
324+
.withContentFile({ path: 'remix.config.js', content: '' })
325+
.buildAsync()
326+
327+
// a failure is expected since this is not a true remix project
328+
const error = await t.throwsAsync(() => withDevServer({ cwd: builder.directory }, () => {}, true))
329+
t.true(error.stdout.includes(`Failed running command: remix watch. Please verify 'remix' exists`))
330+
})
331+
})

tests/utils/dev-server.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const getExecaOptions = ({ cwd, env }) => ({
3232
encoding: 'utf8',
3333
})
3434

35-
const startServer = async ({ cwd, offline = true, env = {}, args = [] }) => {
35+
const startServer = async ({ cwd, offline = true, env = {}, args = [], expectFailure = false }) => {
3636
const tryPort = currentPort
3737
currentPort += 1
3838
const port = await getPort({ port: tryPort })
@@ -49,7 +49,7 @@ const startServer = async ({ cwd, offline = true, env = {}, args = [] }) => {
4949
let selfKilled = false
5050
ps.stdout.on('data', (data) => {
5151
outputBuffer.push(data)
52-
if (data.includes('Server now ready on')) {
52+
if (!expectFailure && data.includes('Server now ready on')) {
5353
resolve({
5454
url,
5555
host,
@@ -75,7 +75,7 @@ const startDevServer = async (options, expectFailure) => {
7575
// eslint-disable-next-line fp/no-loops
7676
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7777
try {
78-
const { timeout, ...server } = await startServer(options)
78+
const { timeout, ...server } = await startServer({ ...options, expectFailure })
7979
if (timeout) {
8080
throw new Error(`Timed out starting dev server.\nServer Output:\n${server.output}`)
8181
}

0 commit comments

Comments
 (0)