UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

138 lines (137 loc) 4.5 kB
import fs from "node:fs"; import { createRequire } from "node:module"; import { preprocessCSS } from "vite"; import { AstroErrorData, CSSError, positionAt } from "../errors/index.js"; import { normalizePath } from "../viteUtils.js"; function rewriteCssUrls(css, base) { if (!base || base === "/") { return css; } const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base; if (!normalizedBase.startsWith("/")) { return css; } const cssUrlRE = ( // eslint-disable-next-line regexp/no-unused-capturing-group /(?<!@import\s+)(?<=^|[^\w\-\u0080-\uffff])url\((\s*('[^']+'|"[^"]+")\s*|(?:\\.|[^'")\\])+)\)/g ); return css.replace(cssUrlRE, (match, rawUrl) => { let url = rawUrl.trim(); let quote = ""; if (url.startsWith("'") && url.endsWith("'") || url.startsWith('"') && url.endsWith('"')) { quote = url[0]; url = url.slice(1, -1); } url = url.trim(); const isRootRelative = url.startsWith("/") && !url.startsWith("//"); const isExternal = url.startsWith("data:") || url.startsWith("http:") || url.startsWith("https:"); const alreadyHasBase = url.startsWith(normalizedBase + "/"); if (isRootRelative && !isExternal && !alreadyHasBase) { return `url(${quote}${normalizedBase}${url}${quote})`; } return match; }); } function withNestingExcluded(viteConfig) { let Features; try { const requireFromRoot = createRequire(viteConfig.root + "/"); Features = requireFromRoot("lightningcss").Features; } catch { return void 0; } const lcss = viteConfig.css?.lightningcss ?? {}; const prevExclude = lcss.exclude ?? 0; return { ...viteConfig, css: { ...viteConfig.css, lightningcss: { ...lcss, exclude: prevExclude | Features.Nesting } } }; } function createStylePreprocessor({ filename, viteConfig, astroConfig, cssPartialCompileResults, cssTransformErrors }) { let processedStylesCount = 0; return async (content, attrs) => { const index = processedStylesCount++; const lang = `.${attrs?.lang || "css"}`.toLowerCase(); const id = `${filename}?astro&type=style&index=${index}&lang${lang}`; try { const effectiveViteConfig = viteConfig.css?.transformer === "lightningcss" ? withNestingExcluded(viteConfig) ?? viteConfig : viteConfig; const result = await preprocessCSS(content, id, effectiveViteConfig); const rewrittenCode = rewriteCssUrls(result.code, astroConfig.base); cssPartialCompileResults[index] = { // Use `in` operator to handle both Go compiler (boolean `true`) and // Rust compiler (empty string `""`) representations of boolean attributes. isGlobal: "is:global" in attrs, dependencies: result.deps ? [...result.deps].map((dep) => normalizePath(dep)) : [] }; let map; if (result.map) { if (typeof result.map === "string") { map = result.map; } else if (result.map.mappings) { map = result.map.toString(); } } return { code: rewrittenCode, map }; } catch (err) { try { err = enhanceCSSError(err, filename, content); } catch { } cssTransformErrors.push(err); return { error: err + "" }; } }; } function enhanceCSSError(err, filename, cssContent) { const fileContent = fs.readFileSync(filename).toString(); const styleTagBeginning = fileContent.indexOf(cssContent); if (err.name === "CssSyntaxError") { const errorLine = positionAt(styleTagBeginning, fileContent).line + (err.line ?? 0); return new CSSError({ ...AstroErrorData.CSSSyntaxError, message: err.reason, location: { file: filename, line: errorLine, column: err.column }, stack: err.stack }); } if (err.line && err.column) { const errorLine = positionAt(styleTagBeginning, fileContent).line + (err.line ?? 0); return new CSSError({ ...AstroErrorData.UnknownCSSError, message: err.message, location: { file: filename, line: errorLine, column: err.column }, frame: err.frame, stack: err.stack }); } const errorPosition = positionAt(styleTagBeginning, fileContent); errorPosition.line += 1; return new CSSError({ name: "CSSError", message: err.message, location: { file: filename, line: errorPosition.line, column: 0 }, frame: err.frame, stack: err.stack }); } export { createStylePreprocessor };