diff --git a/src/compiler/config/test/validate-dev-server.spec.ts b/src/compiler/config/test/validate-dev-server.spec.ts
index ca4cb248d84..85def6eba9e 100644
--- a/src/compiler/config/test/validate-dev-server.spec.ts
+++ b/src/compiler/config/test/validate-dev-server.spec.ts
@@ -276,4 +276,32 @@ describe('validateDevServer', () => {
const { config } = validateConfig(inputConfig, mockLoadConfigInit());
expect(config.devServer.prerenderConfig).toBe(wwwOutputTarget.prerenderConfig);
});
+
+ describe('pingRoute', () => {
+ it('should default to /ping', () => {
+ // Ensure the pingRoute is not set in the inputConfig so we know we're testing the
+ // default value added during validation
+ delete inputConfig.devServer.pingRoute;
+ const { config } = validateConfig(inputConfig, mockLoadConfigInit());
+ expect(config.devServer.pingRoute).toBe('/ping');
+ });
+
+ it('should set user defined pingRoute', () => {
+ inputConfig.devServer = { ...inputDevServerConfig, pingRoute: '/my-ping' };
+ const { config } = validateConfig(inputConfig, mockLoadConfigInit());
+ expect(config.devServer.pingRoute).toBe('/my-ping');
+ });
+
+ it('should prefix pingRoute with a "/"', () => {
+ inputConfig.devServer = { ...inputDevServerConfig, pingRoute: 'my-ping' };
+ const { config } = validateConfig(inputConfig, mockLoadConfigInit());
+ expect(config.devServer.pingRoute).toBe('/my-ping');
+ });
+
+ it('should clear ping route if set to null', () => {
+ inputConfig.devServer = { ...inputDevServerConfig, pingRoute: null };
+ const { config } = validateConfig(inputConfig, mockLoadConfigInit());
+ expect(config.devServer.pingRoute).toBe(null);
+ });
+ });
});
diff --git a/src/compiler/config/validate-dev-server.ts b/src/compiler/config/validate-dev-server.ts
index 14ebdea7168..d92c67967ee 100644
--- a/src/compiler/config/validate-dev-server.ts
+++ b/src/compiler/config/validate-dev-server.ts
@@ -29,6 +29,16 @@ export const validateDevServer = (config: d.ValidatedConfig, diagnostics: d.Diag
devServer.address = devServer.address.split('/')[0];
+ // Validate "ping" route option
+ if (devServer.pingRoute !== null) {
+ let pingRoute = isString(devServer.pingRoute) ? devServer.pingRoute : '/ping';
+ if (!pingRoute.startsWith('/')) {
+ pingRoute = `/${pingRoute}`;
+ }
+
+ devServer.pingRoute = pingRoute;
+ }
+
// split on `:` to get the domain and the (possibly present) port
// separately. we've already sliced off the protocol (if present) above
// so we can safely split on `:` here.
diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts
index 3e4c2c7bbaf..d638add9dde 100644
--- a/src/declarations/stencil-public-compiler.ts
+++ b/src/declarations/stencil-public-compiler.ts
@@ -651,6 +651,15 @@ export interface DevServerConfig extends StencilDevServerConfig {
prerenderConfig?: string;
protocol?: 'http' | 'https';
srcIndexHtml?: string;
+
+ /**
+ * Route to be used for the "ping" sub-route of the Stencil dev server.
+ * This route will return a 200 status code once the Stencil build has finished.
+ * Setting this to `null` will disable the ping route.
+ *
+ * Defaults to `/ping`
+ */
+ pingRoute?: string | null;
}
export interface HistoryApiFallback {
diff --git a/src/dev-server/request-handler.ts b/src/dev-server/request-handler.ts
index 27f18c0d2a9..dc8704ecb10 100644
--- a/src/dev-server/request-handler.ts
+++ b/src/dev-server/request-handler.ts
@@ -26,6 +26,21 @@ export function createRequestHandler(devServerConfig: d.DevServerConfig, serverC
return serverCtx.serve302(req, res);
}
+ if (devServerConfig.pingRoute !== null && req.pathname === devServerConfig.pingRoute) {
+ return serverCtx
+ .getBuildResults()
+ .then((result) => {
+ if (!result.hasSuccessfulBuild) {
+ return serverCtx.serve500(incomingReq, res, 'Build not successful', 'build error');
+ }
+
+ res.writeHead(200, 'OK');
+ res.write('OK');
+ res.end();
+ })
+ .catch(() => serverCtx.serve500(incomingReq, res, 'Error getting build results', 'ping error'));
+ }
+
if (isDevClient(req.pathname) && devServerConfig.websocket) {
return serveDevClient(devServerConfig, serverCtx, req, res);
}
diff --git a/src/dev-server/test/req-handler.spec.ts b/src/dev-server/test/req-handler.spec.ts
index e679588f203..152dfe55e5c 100644
--- a/src/dev-server/test/req-handler.spec.ts
+++ b/src/dev-server/test/req-handler.spec.ts
@@ -439,6 +439,32 @@ describe('request-handler', () => {
expect(h).toBe(`88mph`);
});
});
+
+ describe('pingRoute', () => {
+ it('should return a 200 for successful build', async () => {
+ serverCtx.getBuildResults = () =>
+ Promise.resolve({ hasSuccessfulBuild: true }) as Promise;
+
+ const handler = createRequestHandler(devServerConfig, serverCtx);
+
+ req.url = '/ping';
+
+ await handler(req, res);
+ expect(res.$statusCode).toBe(200);
+ });
+
+ it('should return a 500 for unsuccessful build', async () => {
+ serverCtx.getBuildResults = () =>
+ Promise.resolve({ hasSuccessfulBuild: false }) as Promise;
+
+ const handler = createRequestHandler(devServerConfig, serverCtx);
+
+ req.url = '/ping';
+
+ await handler(req, res);
+ expect(res.$statusCode).toBe(500);
+ });
+ });
});
interface TestServerResponse extends ServerResponse {