import { join, parse, resolve, toFileUrl } from '$std/path/mod.ts' import { bundleAsync } from 'lightningcss' import { Logger, cssImports, uInt8ArrayConcat } from './helpers.ts' export async function builder( { filename, dev, assetDir, remote }: { filename: string dev: boolean assetDir: string remote?: string }, ) { const { code, map } = await bundleAsync({ filename, minify: true, sourceMap: dev, resolver: { async read(pathOrUrl) { if (pathOrUrl.startsWith('https%3A%2F%2F')) { pathOrUrl = decodeURIComponent(pathOrUrl) } if (pathOrUrl.startsWith('https://')) { //use cache for remote if (cssImports.has(pathOrUrl)) { Logger.info('using cache for', `${pathOrUrl}`) const filepath = cssImports.get(pathOrUrl)! return Deno.readTextFile(filepath) } //update cache for new remote Logger.info('fetching and caching', `${pathOrUrl}`) const response = await fetch(pathOrUrl) const file = await response.arrayBuffer() const filename = encodeURIComponent(pathOrUrl.toString()) const filepath = join(assetDir, `${filename}.css`) await Deno.writeFile(filepath, new Uint8Array(file)) setTimeout(() => {}, 3_000) const { code, map } = await builder({ filename: filepath, dev, assetDir, remote: pathOrUrl, }) if (map) { const sourceMappingURL = new TextEncoder().encode( `\n/*# sourceMappingURL=${filepath}.map.css */`, ) await Deno.writeFile( filepath, uInt8ArrayConcat(code, sourceMappingURL), ) await Deno.writeFile(`${filepath}.map.css`, map) } else { await Deno.writeFile(filepath, code) } cssImports.set(pathOrUrl, filepath) return new TextDecoder().decode(file) } return Deno.readTextFile(pathOrUrl) }, resolve(specifier, from) { if (remote) { const url = new URL(specifier, remote) Logger.info('resolve remote imports', url.toString()) return url.toString() } //resolve local files normally if (!specifier.startsWith('https://') && !from.startsWith('https://')) { Logger.info('resolve local file', specifier) return resolve(parse(from).dir, specifier) } //construct asset url const baseUrl = from.startsWith('https://') ? from : toFileUrl(from) const url = new URL(specifier, baseUrl) Logger.info('resolve remote file', url.toString()) return url.toString() }, }, visitor: { Url({ loc, url }) { if (remote && !url.startsWith('data:')) { cache(url, remote, assetDir) } return { loc, url } } } }) return { code, map } } async function cache(url: string, base: string, assetDir: string) { const fullUrl = new URL(url, base) const response = await fetch(fullUrl) const file = await response.arrayBuffer() const filepath = join(assetDir, url.split('?')[0]) await Deno.writeFile(filepath, new Uint8Array(file)) }