diff --git a/plugins/css_bundler/src/builder.ts b/plugins/css_bundler/src/builder.ts index 9e09875..b7150d2 100644 --- a/plugins/css_bundler/src/builder.ts +++ b/plugins/css_bundler/src/builder.ts @@ -1,12 +1,13 @@ import { join, parse, resolve, toFileUrl } from '$std/path/mod.ts' import { bundleAsync } from 'lightningcss' -import { cssImports, hashFile, Logger } from './helpers.ts' +import { cssImports, Logger, uInt8ArrayConcat } from './helpers.ts' export async function builder( - { filename, dev, assetDir }: { + { filename, dev, assetDir, remote }: { filename: string dev: boolean assetDir: string + remote?: string }, ) { const { code, map } = await bundleAsync({ @@ -14,43 +15,74 @@ export async function builder( minify: true, sourceMap: dev, resolver: { - read(path) { - return Deno.readTextFile(path) + 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) }, - async resolve(specifier, from) { + 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) } - //use cache for remote - if (cssImports.has(`${from}/${specifier}`)) { - Logger.info('using cache for', `${from}/${specifier}`) - return cssImports.get(`${from}/${specifier}`) - } - - //update cache for new remote - Logger.info('fetching and caching', `${from}/${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()) - //fetch asset - const response = await fetch(url) - const file = await response.arrayBuffer() - const filename = await hashFile(file) - - //!TODO recursive bundle to cache all remote imports - // bundleAsync({ minify: true, sourceMap: true, '' }) - // const { code, map } = transform({ minify: true, sourceMap: dev, code: new Uint8Array(file), filename: url.toString() }) - - // const { code, map } = await builder({ filename: url.toString(), assetDir, dev }) - - const filepath = join(assetDir, `${filename}.css`) - await Deno.writeFile(filepath, new Uint8Array(file)) - cssImports.set(`${from}/${specifier}`, filepath) - return filepath + return url.toString() }, }, })