From b6bd3557a5bca52cae4921137a1ca2cf58dcf980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 14 Feb 2024 13:42:25 +0100 Subject: [PATCH 1/2] minify css --- src/build.ts | 6 +++--- src/rollup.ts | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/build.ts b/src/build.ts index 3c0321a68..237be5dde 100644 --- a/src/build.ts +++ b/src/build.ts @@ -111,7 +111,7 @@ export async function build( const outputPath = join("_observablehq", name); effects.output.write(`${faint("bundle")} ${clientPath} ${faint("→")} `); const code = await (entry.endsWith(".css") - ? bundleStyles({path: clientPath}) + ? bundleStyles({path: clientPath, minify: true}) : rollupClient(clientPath, {minify: true})); await effects.writeFile(outputPath, code); } @@ -120,12 +120,12 @@ export async function build( const outputPath = join("_import", style.path); const sourcePath = join(root, style.path); effects.output.write(`${faint("style")} ${sourcePath} ${faint("→")} `); - const code = await bundleStyles({path: sourcePath}); + const code = await bundleStyles({path: sourcePath, minify: true}); await effects.writeFile(outputPath, code); } else { const outputPath = join("_observablehq", `theme-${style.theme}.css`); effects.output.write(`${faint("bundle")} theme-${style.theme}.css ${faint("→")} `); - const code = await bundleStyles({theme: style.theme}); + const code = await bundleStyles({theme: style.theme, minify: true}); await effects.writeFile(outputPath, code); } } diff --git a/src/rollup.ts b/src/rollup.ts index d9ed65f62..84cebf4fe 100644 --- a/src/rollup.ts +++ b/src/rollup.ts @@ -22,11 +22,20 @@ function rewriteInputsNamespace(code: string) { return code.replace(/\b__ns__\b/g, "inputs-3a86ea"); } -export async function bundleStyles({path, theme}: {path?: string; theme?: string[]}): Promise { +export async function bundleStyles({ + minify = false, + path, + theme +}: { + minify?: boolean; + path?: string; + theme?: string[]; +}): Promise { const result = await build({ bundle: true, ...(path ? {entryPoints: [path]} : {stdin: {contents: renderTheme(theme!), loader: "css"}}), write: false, + minify, alias: STYLE_MODULES }); const text = result.outputFiles[0].text; From 9bbe0df4e858404709d9b37881bb06cd0102c050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 15 Feb 2024 10:51:01 +0100 Subject: [PATCH 2/2] extract sourcemaps (WIP) --- src/build.ts | 15 +++++++++------ src/preview.ts | 6 +++--- src/render.ts | 4 ++-- src/rollup.ts | 29 ++++++++++++++++++++++------- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/build.ts b/src/build.ts index 082bfc85f..96067b7a2 100644 --- a/src/build.ts +++ b/src/build.ts @@ -109,10 +109,11 @@ export async function build( const clientPath = getClientPath(entry); const outputPath = join("_observablehq", name); effects.output.write(`${faint("bundle")} ${clientPath} ${faint("→")} `); - const code = await (entry.endsWith(".css") + const {text, sourcemap} = await (entry.endsWith(".css") ? bundleStyles({path: clientPath, minify: true}) : rollupClient(clientPath, {minify: true})); - await effects.writeFile(outputPath, code); + console.warn(sourcemap); + await effects.writeFile(outputPath, text); } if (config.search) { const outputPath = join("_observablehq", "minisearch.json"); @@ -125,13 +126,15 @@ export async function build( const outputPath = join("_import", style.path); const sourcePath = join(root, style.path); effects.output.write(`${faint("style")} ${sourcePath} ${faint("→")} `); - const code = await bundleStyles({path: sourcePath, minify: true}); - await effects.writeFile(outputPath, code); + const {text, sourcemap} = await bundleStyles({path: sourcePath, minify: true}); + await effects.writeFile(outputPath, text); + console.warn(sourcemap); } else { const outputPath = join("_observablehq", `theme-${style.theme}.css`); effects.output.write(`${faint("bundle")} theme-${style.theme}.css ${faint("→")} `); - const code = await bundleStyles({theme: style.theme, minify: true}); - await effects.writeFile(outputPath, code); + const {text, sourcemap} = await bundleStyles({theme: style.theme, minify: true}); + await effects.writeFile(outputPath, text); + console.warn(sourcemap); } } } diff --git a/src/preview.ts b/src/preview.ts index 93185dedf..052c2486e 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -103,7 +103,7 @@ export class PreviewServer { if (pathname.endsWith(".js")) { end(req, res, await rollupClient(path), "text/javascript"); } else if (pathname.endsWith(".css")) { - end(req, res, await bundleStyles({path}), "text/css"); + end(req, res, (await bundleStyles({path})).text, "text/css"); } else { throw new HttpError(`Not found: ${pathname}`, 404); } @@ -114,7 +114,7 @@ export class PreviewServer { } else if (pathname === "/_observablehq/minisearch.json") { end(req, res, await searchIndex(config), "application/json"); } else if ((match = /^\/_observablehq\/theme-(?[\w-]+(,[\w-]+)*)?\.css$/.exec(pathname))) { - end(req, res, await bundleStyles({theme: match.groups!.theme?.split(",") ?? []}), "text/css"); + end(req, res, (await bundleStyles({theme: match.groups!.theme?.split(",") ?? []})).text, "text/css"); } else if (pathname.startsWith("/_observablehq/")) { send(req, pathname.slice("/_observablehq".length), {root: publicRoot}).pipe(res); } else if (pathname.startsWith("/_import/")) { @@ -123,7 +123,7 @@ export class PreviewServer { try { if (pathname.endsWith(".css")) { await access(filepath, constants.R_OK); - end(req, res, await bundleStyles({path: filepath}), "text/css"); + end(req, res, (await bundleStyles({path: filepath})).text, "text/css"); return; } else if (pathname.endsWith(".js")) { const input = await readFile(filepath, "utf-8"); diff --git a/src/render.ts b/src/render.ts index 906de4a5f..c25707772 100644 --- a/src/render.ts +++ b/src/render.ts @@ -117,7 +117,7 @@ async function renderSidebar(title = "Home", pages: (Page | Section)[], path: st )}">
` : "" } @@ -142,7 +142,7 @@ async function renderSidebar(title = "Home", pages: (Page | Section)[], path: st `; } diff --git a/src/rollup.ts b/src/rollup.ts index 844433de6..8418921f8 100644 --- a/src/rollup.ts +++ b/src/rollup.ts @@ -33,19 +33,34 @@ export async function bundleStyles({ minify?: boolean; path?: string; theme?: string[]; -}): Promise { +}): Promise<{text: string; sourcemap?: string}> { const result = await build({ bundle: true, ...(path ? {entryPoints: [path]} : {stdin: {contents: renderTheme(theme!), loader: "css"}}), write: false, minify, + sourcemap: !!path, alias: STYLE_MODULES }); - const text = result.outputFiles[0].text; - return rewriteInputsNamespace(text); // TODO only for inputs + let text = result.outputFiles[0].text; + let sourcemap; + if (path) { + const groups = text.match( + /^(?.*)[/][*]# sourceMappingURL=data:application[/]json;base64,(?[a-zA-Z0-9+]+=) [*][/]$/m + )?.groups; + if (groups) { + ({text, sourcemap} = groups); + console.warn({path, sourcemap}); + } + } + if (path === "src/client/stdlib/inputs.js") text = rewriteInputsNamespace(text); + return {text, sourcemap}; } -export async function rollupClient(clientPath: string, {minify = false} = {}): Promise { +export async function rollupClient( + clientPath: string, + {minify = false} = {} +): Promise<{text: string; sourcemap?: string}> { const bundle = await rollup({ input: clientPath, external: [/^https:/], @@ -64,11 +79,11 @@ export async function rollupClient(clientPath: string, {minify = false} = {}): P ] }); try { - const output = await bundle.generate({format: "es"}); + const output = await bundle.generate({format: "es", sourcemap: "inline"}); let code = output.output.find((o): o is OutputChunk => o.type === "chunk")!.code; // TODO don’t assume one chunk? code = rewriteTypeScriptImports(code); - code = rewriteInputsNamespace(code); // TODO only for inputs - return code; + if (clientPath === "src/client/stdlib/inputs.js") code = rewriteInputsNamespace(code); // TODO only for inputs + return {text: code}; } finally { await bundle.close(); }