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

Extend internal rollup plugin to replace extract_css #1415

Merged
merged 1 commit into from
Aug 25, 2020
Merged
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
6 changes: 0 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"puppeteer": "^5.0.0",
"require-relative": "^0.8.7",
"rollup": "^2.21.0",
"rollup-dependency-tree": "^0.0.2",
"rollup-plugin-svelte": "^5.1.0",
"rollup-plugin-typescript2": "^0.27.1",
"sade": "^1.6.1",
Expand Down
21 changes: 5 additions & 16 deletions runtime/src/server/middleware/get_page_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ export function get_page_handler(
shimport: string | null,
assets: Record<string, string | string[]>,
dependencies: Record<string, string[]>,
css: {
main: string | null,
chunks: Record<string, string[]>
},
legacy_assets?: Record<string, string>
} = get_build_info();

Expand All @@ -73,17 +69,9 @@ export function get_page_handler(

let es6_preload = false;
if (build_info.bundler === 'rollup') {

es6_preload = true;

const route = page.parts[page.parts.length - 1].file;

// JS
preload_files = preload_files.concat(build_info.dependencies[route]);

// CSS
preload_files = preload_files.concat(build_info.css.main);
preload_files = preload_files.concat(build_info.css.chunks[route]);
}

const link = preload_files
Expand Down Expand Up @@ -323,16 +311,17 @@ export function get_page_handler(

// TODO make this consistent across apps
// TODO embed build_info in placeholder.ts
if (build_info.css && build_info.css.main) {
if (build_info.dependencies) {
const css_chunks = new Set();
if (build_info.css.main) css_chunks.add(build_info.css.main);
page.parts.forEach(part => {
if (!part) return;
const css_chunks_for_part = build_info.css.chunks[part.file];
const css_chunks_for_part = build_info.dependencies[part.file];

if (css_chunks_for_part) {
css_chunks_for_part.forEach(chunk => {
css_chunks.add(chunk);
if (chunk.endsWith('.css')) {
css_chunks.add(chunk);
}
});
}
});
Expand Down
7 changes: 7 additions & 0 deletions src/api/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import minify_html from './utils/minify_html';
import { create_compilers, create_app, create_manifest_data, create_serviceworker_manifest } from '../core';
import { copy_shimport } from './utils/copy_shimport';
import read_template from '../core/read_template';
import inject_resources from '../core/create_compilers/inject';
import { CompileResult } from '../core/create_compilers/interfaces';
import { noop } from './utils/noop';
import validate_bundler from './utils/validate_bundler';
Expand Down Expand Up @@ -104,8 +105,14 @@ export async function build({
}

fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify(build_info));
if (bundler === 'rollup') {
inject_resources(path.join(dest, 'build.json'), path.join(dest, 'client'));
}

const server_stats = await server.compile();
if (bundler === 'rollup') {
inject_resources(path.join(dest, 'build.json'), path.join(dest, 'server'));
}
oncompile({
type: 'server',
result: server_stats
Expand Down
10 changes: 8 additions & 2 deletions src/api/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as ports from 'port-authority';
import { EventEmitter } from 'events';
import { create_manifest_data, create_app, create_compilers, create_serviceworker_manifest } from '../core';
import { Compiler, Compilers } from '../core/create_compilers';
import inject_resources from '../core/create_compilers/inject';
import { CompileResult } from '../core/create_compilers/interfaces';
import Deferred from './utils/Deferred';
import validate_bundler from './utils/validate_bundler';
Expand Down Expand Up @@ -233,6 +234,9 @@ class Watcher extends EventEmitter {
},

handle_result: (result: CompileResult) => {
if (this.bundler === 'rollup') {
inject_resources(path.join(this.dirs.dest, 'build.json'), path.join(this.dirs.dest, 'server'));
}
deferred.promise.then(() => {
const restart = () => {
this.crashed = false;
Expand Down Expand Up @@ -333,11 +337,13 @@ class Watcher extends EventEmitter {
handle_result: (result: CompileResult) => {
fs.writeFileSync(
path.join(dest, 'build.json'),

// TODO should be more explicit that to_json has effects
JSON.stringify(result.to_json(manifest_data, this.dirs), null, ' ')
);

if (this.bundler === 'rollup') {
inject_resources(path.join(this.dirs.dest, 'build.json'), path.join(this.dirs.dest, 'client'));
}

const client_files = result.chunks.map(chunk => `client/${chunk.file}`);

create_serviceworker_manifest({
Expand Down
139 changes: 132 additions & 7 deletions src/core/create_compilers/RollupCompiler.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
import * as path from 'path';
import color from 'kleur';
import relative from 'require-relative';
import { InputOption, RollupError } from 'rollup';
import {
InputOption,
PluginContext,
TransformResult,
NormalizedInputOptions,
NormalizedOutputOptions,
RollupError,
OutputBundle,
OutputChunk
} from 'rollup';
import { ChunkResolver } from './chunk';
import { chunk_content_from_modules, extract_sourcemap, emit_code_and_sourcemap } from './code';
import { CompileResult } from './interfaces';
import RollupResult from './RollupResult';

const stderr = console.error.bind(console);

let rollup: any;

const get_entry_point_output_chunk = (bundle: OutputBundle, entry_point?: string) => {
if (entry_point === undefined) {
throw new Error("Internal error: entry_point cannot be undefined");
}

let entry_point_output_chunk: OutputChunk;
for (const chunk of Object.values(bundle)) {
if ((chunk as OutputChunk).facadeModuleId === entry_point) {
entry_point_output_chunk = chunk as OutputChunk;
}
}

if (!entry_point_output_chunk) {
throw new Error(`Internal error: No chunk for entry point: ${entry_point} in: ${Object.keys(bundle)}`);
}

if (entry_point_output_chunk.type !== 'chunk') {
throw new Error(`Internal error: Wrong type for entry point chunk: ${entry_point} in: ${Object.keys(bundle)}`);
}

return entry_point_output_chunk;
};

export default class RollupCompiler {
_: Promise<any>;
_oninvalid: (filename: string) => void;
Expand All @@ -18,6 +52,7 @@ export default class RollupCompiler {
errors: any[];
chunks: any[];
css_files: Array<{ id: string; code: string }>;
dependencies: Record<string, string[]>;

constructor(config: any) {
this._ = this.get_config(config);
Expand All @@ -26,23 +61,113 @@ export default class RollupCompiler {
this.errors = [];
this.chunks = [];
this.css_files = [];
this.dependencies = {};
}

async get_config(mod: any) {
let entry_point: string | undefined;

const that = this;
const sourcemap = mod.output.sourcemap;

// TODO this is hacky, and doesn't need to apply to all three compilers
(mod.plugins || (mod.plugins = [])).push({
name: 'sapper-internal',
options: (opts: any) => {
this.input = opts.input;
options(opts: any) {
that.input = opts.input;
},
buildStart(this: PluginContext, options: NormalizedInputOptions): void {
const input = options.input;
const inputs: Array<{alias: string, file: string}> = [];

if (typeof input === 'string') {
inputs.push({alias: 'main', file: input});
} else if (Array.isArray(input)) {
inputs.push(...input.map(file => ({file, alias: file})));
} else {
for (const alias in input) {
inputs.push({file: input[alias], alias});
}
}
if (!entry_point) {
entry_point = inputs[0].file;
}
},
renderChunk: (code: string, chunk: any) => {
this.chunks.push(chunk);
renderChunk(code: string, chunk: any) {
that.chunks.push(chunk);
},
transform: (code: string, id: string) => {
transform(code: string, id: string): TransformResult {
// TODO: see if we can remove after release of https://github.com/sveltejs/rollup-plugin-svelte/pull/72
if (/\.css$/.test(id)) {
this.css_files.push({ id, code });
that.css_files.push({ id, code });
return {code: '', moduleSideEffects: 'no-treeshake'};
}
},
async generateBundle(this: PluginContext, options: NormalizedOutputOptions, bundle: OutputBundle): Promise<void> {
const entry_point_output_chunk = get_entry_point_output_chunk(bundle, entry_point);

const chunk_resolver = new ChunkResolver<OutputChunk>({
id: chunk => chunk.fileName,
resolve_id: chunk_file => {
const oc = bundle[chunk_file];
return oc && oc.type === 'chunk' ? oc : undefined;
},
internals: chunk => ({
id: chunk.fileName,
facadeId: chunk.facadeModuleId,
name: chunk.name,
file_name: chunk.fileName,
dep_names: chunk === entry_point_output_chunk ? [...chunk.imports] : [...chunk.imports, ...chunk.dynamicImports],
manifest: Object.keys(chunk.modules),
type: options.format === 'es' ? 'module' : 'script'
}),
module_imports: js_module => {
const module_info = this.getModuleInfo(js_module);
return [
...module_info.importedIds,
...module_info.dynamicallyImportedIds
].filter(id => /\.css$/.test(id));
},
chunks_from_modules: (chunk, css_modules) => {
const name = chunk.name + '.css';
const file_name = emit_code_and_sourcemap({
sourcemap,
output: chunk_content_from_modules(
css_modules,
css_module => {
const f = that.css_files.find(file => file.id === css_module);
return f && extract_sourcemap(f.code, css_module);
}
),
sourcemap_url_prefix: '',
output_file_name: name,
emit: (filename: string, source: string | Uint8Array) => {
const moduleid = this.emitFile({ name: filename, type: 'asset', source });
const file = this.getFileName(moduleid);
return file;
}
});

return [{
id: file_name,
facadeId: chunk.facadeModuleId,
name,
file_name,
manifest: css_modules,
dep_names: []
}];
}
});

const output_chunks = Object.values(bundle).filter(output => output.type === 'chunk') as OutputChunk[];
const chunks = await Promise.all(output_chunks.map(chunk => chunk_resolver.resolve_chunk(chunk)));
const dependencies = {};
for (const chunk of chunks) {
if (chunk.facadeId) {
dependencies[chunk.facadeId] = Array.from(chunk.transitive_deps).map(dep => dep.file_name);
}
}
that.dependencies = dependencies;
}
});

Expand Down
22 changes: 8 additions & 14 deletions src/core/create_compilers/RollupResult.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as path from 'path';
import colors from 'kleur';
import pb from 'pretty-bytes';
import transitiveDeps from 'rollup-dependency-tree';
import RollupCompiler from './RollupCompiler';
import extract_css from './extract_css';
import { left_pad, normalize_path } from '../../utils';
import { CompileResult, BuildInfo, CompileError, Chunk, CssFile } from './interfaces';
import { ManifestData, Dirs } from '../../interfaces';
Expand All @@ -17,10 +15,6 @@ export default class RollupResult implements CompileResult {
assets: Record<string, string>;
dependencies: Record<string, string[]>;
css_files: CssFile[];
css: {
main: string;
chunks: Record<string, string[]>;
};
sourcemap: boolean | 'inline';
summary: string;

Expand All @@ -38,6 +32,7 @@ export default class RollupResult implements CompileResult {
}));

this.css_files = compiler.css_files;
this.dependencies = compiler.dependencies;

this.assets = {};

Expand All @@ -55,8 +50,6 @@ export default class RollupResult implements CompileResult {
}
}

this.dependencies = transitiveDeps(compiler.chunks);

this.summary = compiler.chunks.map(chunk => {
const size_color = chunk.code.length > 150000 ? colors.bold().red : chunk.code.length > 50000 ? colors.bold().yellow : colors.bold().white;
const size_label = left_pad(pb(chunk.code.length), 10);
Expand Down Expand Up @@ -91,20 +84,21 @@ export default class RollupResult implements CompileResult {
}).join('\n');
}

to_json(manifest_data: ManifestData, dirs: Dirs): BuildInfo {
relative_dependencies(routes_dir: string) {
const dependencies = {};
Object.entries(this.dependencies).forEach(([key, value]) => {
dependencies[path.relative(dirs.routes, key)] = value;
dependencies[normalize_path(path.relative(routes_dir, key)).replace(/\\/g, '/')] = value;
});
return dependencies;
}

to_json(manifest_data: ManifestData, dirs: Dirs): BuildInfo {
const dependencies = (this.relative_dependencies(dirs.routes));
return {
bundler: 'rollup',
shimport: shimport_version,
assets: this.assets,
dependencies,

// TODO extract_css has side-effects that don't belong in a method called to_json
css: extract_css(this, manifest_data.components, dirs, this.sourcemap)
dependencies
};
}

Expand Down
16 changes: 1 addition & 15 deletions src/core/create_compilers/WebpackResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,7 @@ export default class WebpackResult implements CompileResult {
return {
bundler: 'webpack',
shimport: null, // webpack has its own loader
assets: this.assets,
css: {
main: extract_css(this.assets.main),
chunks: manifest_data.components
.reduce((chunks: Record<string, string[]>, component: PageComponent) => {
const css_dependencies = [];
const css = extract_css(this.assets[component.name]);

if (css) css_dependencies.push(css);

chunks[component.file] = css_dependencies;

return chunks;
}, {})
}
assets: this.assets
};
}

Expand Down
Loading