diff --git a/src/build.ts b/src/build.ts index 539776d3f..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") - ? bundleStyles({path: clientPath}) + 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}); - 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}); - 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 547a1126f..8418921f8 100644 --- a/src/rollup.ts +++ b/src/rollup.ts @@ -25,18 +25,42 @@ 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<{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:/], @@ -55,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(); }