Skip to content

Commit

Permalink
feat(prefetch): add prefetch script to populate cache with web-worker. (
Browse files Browse the repository at this point in the history
QwikDev#46)

* feat(prefetch): add prefetch script to populate cache with web-worker.

* fixup! feat(prefetch): add prefetch script to populate cache with web-worker.
  • Loading branch information
mhevery authored Aug 3, 2021
1 parent 650c7f4 commit e1e1a43
Show file tree
Hide file tree
Showing 28 changed files with 648 additions and 88 deletions.
2 changes: 1 addition & 1 deletion DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ npm run integration.server

Then navigate to http://localhost:8080/

The `npm run integration.server` comands runs the server in `development` mode, where files are not minified, source maps are inlined, and there's additional logging. To run code minified with external source maps and without extra logs, run `npm run integration.server.prod`, which is what the end-to-end tests use.
The `npm run integration.server` commands runs the server in `development` mode, where files are not minified, source maps are inlined, and there's additional logging. To run code minified with external source maps and without extra logs, run `npm run integration.server.prod`, which is what the end-to-end tests use.

## Running All Tests

Expand Down
47 changes: 36 additions & 11 deletions integration/devserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
/* eslint no-console: ["off"] */
import type { Request, Response, NextFunction } from 'express';
import express from 'express';
import { mkdirSync, writeFileSync } from 'fs';
import { mkdirSync, writeFileSync, readFile } from 'fs';
import { dirname, join } from 'path';
import {
createEsbuilder,
createClientEsbuildOptions,
createServerEsbuildOptions,
createTimer,
getQwikLoaderScript,
Optimizer,
OutputFile,
} from '@builder.io/qwik/optimizer';
import type { BuildOptions } from 'esbuild';
import type { RenderToStringResult } from '@builder.io/qwik/server';
import { getImports } from '@builder.io/qwik/server';
import mri from 'mri';
import srcMap from 'source-map-support';
import type { Socket } from 'net';
Expand Down Expand Up @@ -101,7 +101,7 @@ async function startServer() {
resetNodeJsModuleCache(outDir);

const result: RenderToStringResult = await indexModule.default({
url: req.originalUrl,
url: 'http://' + req.hostname + '/' + req.originalUrl,
outDir,
});

Expand Down Expand Up @@ -147,12 +147,26 @@ async function startServer() {
res.type('text/plain');
return;
}
const outJs = result.outputFiles.find(
(o) => o.platform === 'client' && o.path.endsWith(fileName)
);
const getFileContent = function getFileContent(fileName: string) {
if (fileName.startsWith('./')) {
fileName = fileName.substr(2);
}
return result.outputFiles.find((o) => o.platform === 'client' && o.path.endsWith(fileName));
};
const outJs = getFileContent(fileName);
if (outJs) {
if (debug) console.debug(`client:`, req.originalUrl);
res.type('application/javascript');
const imports = await getImports('./' + fileName, async (f) => getFileContent(f)!.text);
if (true as any) {
res.setHeader(
'Link',
imports
.map((path) => `<http://localhost:8080/${path.substr(2)}>; rel=prefetch; as=script`)
.join(', ')
);
res.setHeader('Cache-Control', 'max-age=' + 60 * 60 * 1000);
}
res.send(outJs.text);
return;
}
Expand All @@ -161,13 +175,17 @@ async function startServer() {
}

const app = express();
app.get('/qwikloader.js', (req, res) => {
res.type('application/javascript');
res.send(getQwikLoaderScript({ debug }));
});
app.get('/qwikloader.debug.js', streamFile('qwikloader.debug.js'));
app.get('/prefetch.debug.js', streamFile('prefetch.debug.js'));
app.get('/qwikloader.js', streamFile('qwikloader.js'));
app.get('/prefetch.js', streamFile('prefetch.js'));
app.use(devSsr);
app.use(devModules);
app.use(express.static(rootDir));
app.use(
express.static(rootDir, {
maxAge: 60 * 60 * 1000,
})
);
let server = app.listen(args.port);

const connections = new Map<string, Socket>();
Expand All @@ -193,6 +211,13 @@ async function startServer() {
process.title = 'qwik-devserver';
}

function streamFile(fileName: string) {
return (req: Request, res: Response) => {
res.type('application/javascript');
readFile(join('dist-dev', '@builder.io-qwik', fileName), (err, data) => res.send(String(data)));
};
}

// custom updates only required for local dev of source files
function localDevPreBuild(qwikDir: string, clientOpts: BuildOptions, serverOpts: BuildOptions) {
clientOpts.plugins = [
Expand Down
1 change: 1 addition & 0 deletions integration/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<li><a href="./hello_static/">hello_static</a></li>
<li><a href="./hello_server/">hello_server</a></li>
<li><a href="./todo/">todo</a></li>
<li><a href="./prefetch/">prefetch</a></li>
<li><a href="./specs/">specs</a></li>
</ul>
</body>
Expand Down
31 changes: 31 additions & 0 deletions integration/prefetch/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>Preloading Application</title>
<script src="/qwikloader.js" type="module"></script>
<script src="/prefetch.js" type="module"></script>
<style>
.block {
border: 1px solid black;
}
.spacer {
height: 2000px;
}
</style>
</head>
<body>
<div>
<div
class="block"
on:.
on:click="./eager"
on:ignore="./eager?params#hash"
on:dup="./duplicate?bar#foo"
>
Visible
</div>
<div class="spacer"></div>
<div class="block" on:. on:click="./lazy" on:dup="./duplicate">Off screen</div>
</div>
</body>
</html>
10 changes: 9 additions & 1 deletion integration/todo/index.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@ import {
RenderToStringOptions,
QwikLoader,
QwikProtocols,
QwikPrefetch,
} from '@builder.io/qwik/server';
import { ToDoApp } from './ui/TodoApp';

const DEBUG = true;

/**
* Entry point for server-side pre-rendering.
*
* @returns a promise when all of the rendering is completed.
*/
export default function (opts: RenderToStringOptions) {
const url = new URL(opts.url || '');
const params = url.searchParams || {};
return renderToString(
<html>
<head>
Expand All @@ -37,7 +42,10 @@ export default function (opts: RenderToStringOptions) {
</head>
<body>
<ToDoApp />
<QwikLoader events={['click', 'dblclick', 'keyup', 'blur']} debug={true} />
<QwikLoader events={['click', 'dblclick', 'keyup', 'blur']} debug={DEBUG} />
{Object.prototype.hasOwnProperty.call(params, 'prefetch') ? (
<QwikPrefetch debug={DEBUG} />
) : null}
</body>
</html>,
opts
Expand Down
5 changes: 3 additions & 2 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { mkdirSync } from 'fs';
import { submoduleCore } from './submodule-core';
import { submoduleJsxRuntime } from './submodule-jsx-runtime';
import { submoduleOptimizer } from './submodule-optimizer';
import { submodulePrefetch } from './submodule-prefetch';
import { submoduleQwikLoader } from './submodule-qwikloader';
import { submoduleServer } from './submodule-server';
import { submoduleTesting } from './submodule-testing';
Expand Down Expand Up @@ -42,13 +43,13 @@ export async function build(config: BuildConfig) {
submoduleCore(config),
submoduleJsxRuntime(config),
submoduleQwikLoader(config),
submoduleServer(config),
submodulePrefetch(config),
submoduleTesting(config),
generatePackageJson(config),
copyFiles(config),
buildDevServer(config),
]);
await submoduleOptimizer(config);
await Promise.all([submoduleOptimizer(config), submoduleServer(config)]);
}

if (config.api) {
Expand Down
2 changes: 2 additions & 0 deletions scripts/package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ const PACKAGE_FILES = [
'optimizer.mjs.map',
'optimizer.d.ts',
'package.json',
'prefetch.js',
'prefetch.debug.js',
'qwikloader.js',
'qwikloader.debug.js',
'qwikloader.optimize.js',
Expand Down
70 changes: 42 additions & 28 deletions scripts/submodule-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,57 @@ import {
target,
watcher,
} from './util';
import { watch } from 'rollup';

/**
* Builds @builder.io/optimizer
*/
export async function submoduleOptimizer(config: BuildConfig) {
const submodule = 'optimizer';

const opts: BuildOptions = {
entryPoints: [join(config.srcDir, submodule, 'index.ts')],
entryNames: submodule,
outdir: config.pkgDir,
bundle: true,
sourcemap: 'external',
target,
banner,
external: [...nodeBuiltIns, 'esbuild'],
define: inlineQwikScripts(config),
};
async function buildSubmodule() {
const opts: BuildOptions = {
entryPoints: [join(config.srcDir, submodule, 'index.ts')],
entryNames: submodule,
outdir: config.pkgDir,
bundle: true,
sourcemap: 'external',
target,
banner,
external: [...nodeBuiltIns, 'esbuild'],
incremental: config.watch,
define: inlineQwikScripts(config),
};

const esm = build({
...opts,
format: 'esm',
outExtension: { '.js': '.mjs' },
watch: watcher(config, submodule),
});
const esm = await build({
...opts,
format: 'esm',
outExtension: { '.js': '.mjs' },
watch: watcher(config, submodule),
});

const cjs = build({
...opts,
format: 'cjs',
outExtension: { '.js': '.cjs' },
watch: watcher(config),
platform: 'node',
target: nodeTarget,
inject: [injectGlobalThisPoly(config)],
});
const cjs = await build({
...opts,
format: 'cjs',
outExtension: { '.js': '.cjs' },
watch: watcher(config),
platform: 'node',
target: nodeTarget,
inject: [injectGlobalThisPoly(config)],
});

await Promise.all([esm, cjs]);
console.log('🦋', submodule);

console.log('🦋', submodule);
if (config.watch) {
const watcher = watch({ input: join(config.pkgDir, 'prefetch.debug.js') });
watcher.on('change', (id) => {
esm.stop!();
cjs.stop!();
watcher.close();
setTimeout(buildSubmodule);
});
}
}

await buildSubmodule();
}
Loading

0 comments on commit e1e1a43

Please sign in to comment.