Skip to content

Commit

Permalink
fix: inert qwik-city service worker during dev (#1209)
Browse files Browse the repository at this point in the history
adamdbradley authored Sep 6, 2022
1 parent 39a8a6b commit 25d24bd
Showing 9 changed files with 96 additions and 84 deletions.
50 changes: 5 additions & 45 deletions packages/qwik-city/buildtime/build.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
import type { BuildContext, BuildRoute, RouteSourceFile } from './types';
import type { BuildContext } from './types';
import { addError } from '../utils/format';
import { validateSourceFiles } from './routing/source-file';
import { walkRoutes } from './routing/walk-routes-dir';
import { getRouteParams } from '../runtime/src/library/routing';
import type { RouteParams } from '../runtime/src/library/types';
import { resolveSourceFiles } from './routing/resolve-source-file';

export async function build(ctx: BuildContext) {
try {
const opts = ctx.opts;
const routesDir = opts.routesDir;

const sourceFiles = await walkRoutes(routesDir);

const resolved = resolveSourceFiles(opts, sourceFiles);
ctx.layouts = resolved.layouts;
ctx.routes = resolved.routes;
ctx.errors = resolved.errors;
ctx.entries = resolved.entries;
ctx.serviceWorkers = resolved.serviceWorkers;
ctx.menus = resolved.menus;

validateBuild(ctx, sourceFiles);
await updateBuildContext(ctx);
validateBuild(ctx);
} catch (e) {
addError(ctx, e);
}
@@ -35,10 +20,7 @@ export async function build(ctx: BuildContext) {
}
}

export async function buildFromUrlPathname(
ctx: BuildContext,
pathname: string
): Promise<{ route: BuildRoute; params: RouteParams } | null> {
export async function updateBuildContext(ctx: BuildContext) {
const sourceFiles = await walkRoutes(ctx.opts.routesDir);

const resolved = resolveSourceFiles(ctx.opts, sourceFiles);
@@ -48,29 +30,9 @@ export async function buildFromUrlPathname(
ctx.entries = resolved.entries;
ctx.serviceWorkers = resolved.serviceWorkers;
ctx.menus = resolved.menus;

for (const d of ctx.diagnostics) {
if (d.type === 'error') {
console.error(d.message);
} else {
console.warn(d.message);
}
}

for (const route of resolved.routes) {
const match = route.pattern.exec(pathname);
if (match) {
return {
route,
params: getRouteParams(route.paramNames, match),
};
}
}

return null;
}

function validateBuild(ctx: BuildContext, sourceFiles: RouteSourceFile[]) {
function validateBuild(ctx: BuildContext) {
const pathnames = Array.from(new Set(ctx.routes.map((r) => r.pathname))).sort();

for (const pathname of pathnames) {
@@ -84,6 +46,4 @@ function validateBuild(ctx: BuildContext, sourceFiles: RouteSourceFile[]) {
);
}
}

validateSourceFiles(ctx, sourceFiles);
}
7 changes: 5 additions & 2 deletions packages/qwik-city/buildtime/routing/resolve-source-file.ts
Original file line number Diff line number Diff line change
@@ -185,19 +185,22 @@ function resolveEntry(opts: NormalizedPluginOptions, sourceFile: RouteSourceFile
id: createFileId(opts.routesDir, sourceFile.filePath),
filePath: sourceFile.filePath,
chunkFileName,
...parseRoutePathname(pathname),
};

return buildEntry;
}

function resolveServiceWorkerEntry(opts: NormalizedPluginOptions, sourceFile: RouteSourceFile) {
const pathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
const chunkFileName = pathname.slice(1) + sourceFile.extlessName + '.js';
const dirPathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
const pathname = dirPathname + sourceFile.extlessName + '.js';
const chunkFileName = pathname.slice(1);

const buildEntry: BuildEntry = {
id: createFileId(opts.routesDir, sourceFile.filePath),
filePath: sourceFile.filePath,
chunkFileName,
...parseRoutePathname(pathname),
};

return buildEntry;
28 changes: 1 addition & 27 deletions packages/qwik-city/buildtime/routing/source-file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { BuildContext, RouteSourceFile, RouteSourceFileName, RouteSourceType } from '../types';
import { addError } from '../../utils/format';
import type { RouteSourceFileName, RouteSourceType } from '../types';
import {
isModuleExt,
isEntryName,
@@ -52,28 +51,3 @@ export function getSourceFile(fileName: string) {

return null;
}

export function validateSourceFiles(ctx: BuildContext, sourceFiles: RouteSourceFile[]) {
for (const sourceFile of sourceFiles) {
const err = validateSourceFile(sourceFile);
if (err) {
addError(ctx, err);
}
}
}

function validateSourceFile(sourceFile: RouteSourceFile) {
// if (isTestDirName(sourceFile.dirName)) {
// return `Test directory "${sourceFile.filePath}" should not be included within the routes directory. Please move test directories to a different location.`;
// }

// if (isTestFileName(sourceFile.fileName)) {
// return `Test file "${sourceFile.filePath}" should not be included within the routes directory. Please move test files to a different location.`;
// }

// if (sourceFile.dirName.includes('@')) {
// return `Route directories cannot have a named layout. Please change the named layout from the directory "${sourceFile.dirPath}" to a file.`;
// }

return null;
}
2 changes: 1 addition & 1 deletion packages/qwik-city/buildtime/types.ts
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ export interface BuildLayout {
layoutName: string;
}

export interface BuildEntry {
export interface BuildEntry extends ParsedPathname {
id: string;
chunkFileName: string;
filePath: string;
60 changes: 51 additions & 9 deletions packages/qwik-city/buildtime/vite/dev-server.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import type { RouteModule } from '../../runtime/src/library/types';
import type { QwikViteDevResponse } from '../../../qwik/src/optimizer/src/plugins/vite';
import { loadUserResponse, updateRequestCtx } from '../../middleware/request-handler/user-response';
import { getQwikCityEnvData, pageHandler } from '../../middleware/request-handler/page-handler';
import { buildFromUrlPathname } from '../build';
import { updateBuildContext } from '../build';
import { endpointHandler } from '../../middleware/request-handler/endpoint-handler';
import {
errorResponse,
@@ -22,8 +22,23 @@ import {
} from '../../middleware/request-handler/redirect-handler';
import { normalizePath } from '../../utils/fs';
import type { RenderToStringResult } from '@builder.io/qwik/server';
import { getRouteParams } from '../../runtime/src/library/routing';

export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
const matchRouteRequest = async (pathname: string) => {
for (const route of ctx.routes) {
const match = route.pattern.exec(pathname);
if (match) {
return {
route,
params: getRouteParams(route.paramNames, match),
};
}
}

return null;
};

return async (req: Connect.IncomingMessage, res: ServerResponse, next: Connect.NextFunction) => {
try {
const url = new URL(req.originalUrl!, `http://${req.headers.host}`);
@@ -36,9 +51,19 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
const requestCtx = fromDevServerHttp(url, req, res);
updateRequestCtx(requestCtx, ctx.opts.trailingSlash);

const result = await buildFromUrlPathname(ctx, requestCtx.url.pathname);
if (result) {
const { route, params } = result;
await updateBuildContext(ctx);

for (const d of ctx.diagnostics) {
if (d.type === 'error') {
console.error(d.message);
} else {
console.warn(d.message);
}
}

const routeResult = await matchRouteRequest(requestCtx.url.pathname);
if (routeResult) {
const { route, params } = routeResult;

// use vite to dynamically load each layout/page module in this route's hierarchy
const routeModules: RouteModule[] = [];
@@ -108,11 +133,23 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) {
}
}

/**
* if no route match, but is html request, fast path to 404
* otherwise qwik plugin will take over render without envData causing error
*/
if (!result && req.headers.accept && req.headers.accept.includes('text/html')) {
if (!routeResult) {
// test if this is a dev service-worker.js request
for (const sw of ctx.serviceWorkers) {
const match = sw.pattern.exec(requestCtx.url.pathname);
if (match) {
res.setHeader('Content-Type', 'text/javascript');
res.end(DEV_SERVICE_WORKER);
return;
}
}
}

if (!routeResult && req.headers.accept && req.headers.accept.includes('text/html')) {
/**
* if no route match, but is html request, fast path to 404
* otherwise qwik plugin will take over render without envData causing error
*/
// TODO: after file change, need to manual page refresh to see changes currently
// there's two ways handling HMR for page endpoint with error
// 1. Html response inject `import.meta.hot.accept('./pageEndpoint_FILE_URL', () => { location.reload })`
@@ -275,3 +312,8 @@ function fromDevServerHttp(url: URL, req: Connect.IncomingMessage, res: ServerRe

return requestCtx;
}

const DEV_SERVICE_WORKER = `/* Qwik City Dev Service Worker */
addEventListener('install', () => self.skipWaiting());
addEventListener('activate', () => self.clients.claim());
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../../api-extractor.json",
"mainEntryPointFilePath": "<projectFolder>/dist-dev/dts-out/packages/qwik-city/runtime/src/library/service-worker/index.d.ts",
"apiReport": {
"enabled": true,
"reportFileName": "api.md",
"reportFolder": "<projectFolder>/packages/qwik-city/runtime/src/library/service-worker/",
"reportTempFolder": "<projectFolder>/dist-dev/api-extractor/qwik-city/runtime/service-worker"
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/packages/qwik-city/lib/service-worker.d.ts"
}
}
12 changes: 12 additions & 0 deletions packages/qwik-city/runtime/src/library/service-worker/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## API Report File for "@builder.io/qwik-city"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

// @alpha (undocumented)
export const setupServiceWorker: () => void;

// (No @packageDocumentation comment for this package)

```
5 changes: 5 additions & 0 deletions scripts/api.ts
Original file line number Diff line number Diff line change
@@ -54,6 +54,11 @@ export function apiExtractor(config: BuildConfig) {
join(config.packagesDir, 'qwik-city', 'runtime', 'src'),
join(config.packagesDir, 'qwik-city', 'lib', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'runtime', 'src', 'library', 'service-worker'),
join(config.packagesDir, 'qwik-city', 'lib', 'service-worker.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'buildtime', 'vite'),
1 change: 1 addition & 0 deletions scripts/qwik-city.ts
Original file line number Diff line number Diff line change
@@ -69,6 +69,7 @@ export async function buildQwikCity(config: BuildConfig) {
'index.qwik.cjs',
'service-worker.mjs',
'service-worker.cjs',
'service-worker.d.ts',
'modules.d.ts',
'middleware',
'static',

0 comments on commit 25d24bd

Please sign in to comment.