Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Preload assets in Rollup apps - fixes #408 #440

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/core/create_compilers/RollupResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import extract_css from './extract_css';
import { left_pad } from '../../utils';
import { CompileResult, BuildInfo, CompileError, Chunk, CssFile } from './interfaces';
import { ManifestData, Dirs, PageComponent } from '../../interfaces';
import { locations } from '../../config';
import { get_slug } from '../utils';

export default class RollupResult implements CompileResult {
duration: number;
errors: CompileError[];
warnings: CompileError[];
chunks: Chunk[];
assets: Record<string, string>;
assets: Record<string, string | string[]>;
css_files: CssFile[];
css: {
main: string,
main: string | null,
chunks: Record<string, string[]>
};
summary: string;
Expand All @@ -33,14 +35,21 @@ export default class RollupResult implements CompileResult {
}));

this.css_files = compiler.css_files;

// TODO populate this properly. We don't have named chunks, as in
// webpack, but we can have a route -> [chunk] map or something
this.assets = {};

const routes_dir = locations.routes();

compiler.chunks.forEach(chunk => {
if (compiler.input in chunk.modules) {
this.assets.main = chunk.fileName;
} else {
Object.keys(chunk.modules).forEach((module: string) => {
if (!module.startsWith(routes_dir)) return;
if (/\.css$/.test(module)) return;
Copy link
Contributor Author

@nsivertsen nsivertsen Sep 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether these checks are the best way to detect whether a chunk is a page.


const page_slug = get_slug(path.relative(routes_dir, module));
this.assets[page_slug] = [...chunk.imports, chunk.fileName];
});
}
});

Expand Down
2 changes: 1 addition & 1 deletion src/core/create_compilers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface CompileResult {
errors: CompileError[];
warnings: CompileError[];
chunks: Chunk[];
assets: Record<string, string>;
assets: Record<string, string | string[]>;
css_files: CssFile[];

to_json: (manifest_data: ManifestData, dirs: Dirs) => BuildInfo
Expand Down
17 changes: 1 addition & 16 deletions src/core/create_manifest_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as fs from 'fs';
import * as path from 'path';
import { locations } from '../config';
import { Page, PageComponent, ServerRoute, ManifestData } from '../interfaces';
import { posixify, reserved_words } from './utils';
import { posixify, get_slug, reserved_words } from './utils';

export default function create_manifest_data(cwd = locations.routes()): ManifestData {
const components: PageComponent[] = [];
Expand Down Expand Up @@ -268,21 +268,6 @@ function get_parts(part: string): Part[] {
.filter(Boolean);
}

function get_slug(file: string) {
let name = file
.replace(/[\\\/]index/, '')
.replace(/_default([\/\\index])?\.html$/, 'index')
.replace(/[\/\\]/g, '_')
.replace(/\.\w+$/, '')
.replace(/\[([^(]+)(?:\([^(]+\))?\]/, '$$$1')
.replace(/[^a-zA-Z0-9_$]/g, c => {
return c === '.' ? '_' : `$${c.charCodeAt(0)}`
});

if (reserved_words.has(name)) name += '_';
return name;
}

function get_pattern(segments: Part[][], add_trailing_slash: boolean) {
return new RegExp(
`^` +
Expand Down
15 changes: 15 additions & 0 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ export function fudge_mtime(file: string) {
);
}

export function get_slug(file: string) {
let name = file
.replace(/[\\\/]index/, '')
.replace(/_default([\/\\index])?\.html$/, 'index')
.replace(/[\/\\]/g, '_')
.replace(/\.\w+$/, '')
.replace(/\[([^(]+)(?:\([^(]+\))?\]/, '$$$1')
.replace(/[^a-zA-Z0-9_$]/g, c => {
return c === '.' ? '_' : `$${c.charCodeAt(0)}`
});

if (reserved_words.has(name)) name += '_';
return name;
}

export const reserved_words = new Set([
'arguments',
'await',
Expand Down
28 changes: 22 additions & 6 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Page = {
pattern: RegExp;
parts: Array<{
name: string;
file: string | null;
component: Component;
params?: (match: RegExpMatchArray) => Record<string, string>;
}>
Expand Down Expand Up @@ -308,29 +309,44 @@ function get_page_handler(
bundler: 'rollup' | 'webpack',
shimport: string | null,
assets: Record<string, string | string[]>,
legacy_assets?: Record<string, string>
legacy_assets?: Record<string, string>,
css: {
main: string | null,
chunks: Record<string, string[]>
}
} = get_build_info();

res.setHeader('Content-Type', 'text/html');
res.setHeader('Cache-Control', dev() ? 'no-cache' : 'max-age=600');

// preload main.js and current route
// TODO detect other stuff we can preload? images, CSS, fonts?
let preloaded_chunks = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main];
// preload main js/css and current route
// TODO detect other stuff we can preload? images, fonts?
let preloaded_script_chunks = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main];
let preloaded_style_chunks = [build_info.css.main];
if (!error) {
page.parts.forEach(part => {
if (!part) return;

// using concat because it could be a string or an array. thanks webpack!
preloaded_chunks = preloaded_chunks.concat(build_info.assets[part.name]);
preloaded_script_chunks = preloaded_script_chunks.concat(build_info.assets[part.name]);
preloaded_style_chunks = preloaded_style_chunks.concat(build_info.css.chunks[part.file]);
});
}

const link = preloaded_chunks
preloaded_script_chunks = Array.from(new Set(preloaded_script_chunks));

const script_links = preloaded_script_chunks
.filter(file => file && !file.match(/\.map$/))
.map(file => `<${req.baseUrl}/client/${file}>;rel="preload";as="script"`)
.join(', ');

const style_links = preloaded_style_chunks
.filter(file => file && !file.match(/\.map$/))
.map(file => `<${req.baseUrl}/client/${file}>;rel="preload";as="style"`)
.join(', ');

const link = [script_links, style_links].join(', ');

res.setHeader('Link', link);

const store = store_getter ? store_getter(req, res) : null;
Expand Down