UNPKG

svelte-scoped-uno

Version:

Use UnoCSS utility styles in a modular fashion in Svelte, with styles being stored only where needed.

613 lines (586 loc) 27.1 kB
import { expandVariantGroup, regexScopePlaceholder, warnOnce, toArray, presetUno, createGenerator } from 'unocss'; export * from 'unocss'; import { loadConfig } from '@unocss/config'; import MagicString from 'magic-string'; import { generate, parse, clone, walk } from 'css-tree'; import { readFileSync, existsSync, statSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const NOT_PRECEEDED_BY_DIGIT_OR_OPEN_PARENTHESIS_RE = /(?<![\d(])/; const SELECTOR_STARTING_WITH_BRACKET_OR_PERIOD_RE = /([[\.][\S\s]+?)/; const STYLES_RE = /({[\S\s]+?})/; const EXTRACT_SELECTOR_RE = new RegExp(NOT_PRECEEDED_BY_DIGIT_OR_OPEN_PARENTHESIS_RE.source + SELECTOR_STARTING_WITH_BRACKET_OR_PERIOD_RE.source + STYLES_RE.source, "g"); function wrapSelectorsWithGlobal(css) { return css.replace(EXTRACT_SELECTOR_RE, ":global($1)$2"); } const classesRE$1 = /class=(["'\`])([\S\s]*?)\1/g; const classDirectivesRE = /class:([\S]+?)={/g; const classDirectivesShorthandRE = /class:([^=>\s/]+)[{>\s/]/g; function findClasses(code) { const matchedClasses = [...code.matchAll(classesRE$1)]; const matchedClassDirectives = [...code.matchAll(classDirectivesRE)]; const matchedClassDirectivesShorthand = [...code.matchAll(classDirectivesShorthandRE)]; const classes = parseMatches(matchedClasses, "regular", 'class="'.length); const classDirectives = parseMatches(matchedClassDirectives, "directive", "class:".length); const classDirectivesShorthand = parseMatches(matchedClassDirectivesShorthand, "directiveShorthand", "class:".length); return [...classes, ...classDirectives, ...classDirectivesShorthand]; } function parseMatches(matches, type, prefixLength) { return matches.map((match) => { const body = match[type === "regular" ? 2 : 1]; const start = match.index + prefixLength; return { body: body.trim(), start, end: start + body.length, type }; }).filter(hasBody); } function hasBody(foundClass) { return foundClass.body; } const notInCommentRE = /(?<!<!--\s*)/; const stylesTagWithCapturedDirectivesRE = /<style([^>]*)>[\s\S]*?<\/style\s*>/; const actualStylesTagWithCapturedDirectivesRE = new RegExp(notInCommentRE.source + stylesTagWithCapturedDirectivesRE.source, "g"); const captureOpeningStyleTagWithAttributesRE = /(<style[^>]*>)/; function addGeneratedStylesIntoStyleBlock(code, styles) { const preExistingStylesTag = code.match(actualStylesTagWithCapturedDirectivesRE); if (preExistingStylesTag) return code.replace(captureOpeningStyleTagWithAttributesRE, `$1${styles}`); return `${code} <style>${styles}</style>`; } async function needsGenerated(token, uno) { const inSafelist = uno.config.safelist.includes(token); if (inSafelist) return false; const result = await uno.parseToken(token); return !!result; } function hash(str) { let i; let l; let hval = 2166136261; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } return `00000${(hval >>> 0).toString(36)}`.slice(-6); } function generateClassName(body, options, filename) { const { classPrefix = "uno-", combine = true, hashFn = hash } = options; if (combine) { const classPlusFilenameHash = hashFn(body + filename); return `${classPrefix}${classPlusFilenameHash}`; } else { const filenameHash = hashFn(filename); return `_${body}_${filenameHash}`; } } function isShortcut(token, shortcuts) { return shortcuts.some((s) => s[0] === token); } async function processDirective({ body: token, start, end, type }, options, uno, filename) { const isShortcutOrUtility = isShortcut(token, uno.config.shortcuts) || await needsGenerated(token, uno); if (!isShortcutOrUtility) return; const generatedClassName = generateClassName(token, options, filename); const content = type === "directiveShorthand" ? `${generatedClassName}={${token}}` : generatedClassName; return { rulesToGenerate: { [generatedClassName]: [token] }, codeUpdate: { content, start, end } }; } async function sortClassesIntoCategories(body, options, uno, filename) { const { combine = true } = options; const rulesToGenerate = {}; const ignore = []; const classes = body.trim().split(/\s+/); const knownClassesToCombine = []; for (const token of classes) { const isShortcutOrUtility = isShortcut(token, uno.config.shortcuts) || await needsGenerated(token, uno); if (!isShortcutOrUtility) { ignore.push(token); continue; } if (combine) { knownClassesToCombine.push(token); } else { const generatedClassName = generateClassName(token, options, filename); rulesToGenerate[generatedClassName] = [token]; } } if (knownClassesToCombine.length) { const generatedClassName = generateClassName(knownClassesToCombine.join(" "), options, filename); rulesToGenerate[generatedClassName] = knownClassesToCombine; } return { rulesToGenerate, ignore }; } const expressionsRE = /{[^{}]+?}/g; const classesRE = /(["'\`])([\S\s]+?)\1/g; async function processExpressions(body, options, uno, filename) { const rulesToGenerate = {}; const updatedExpressions = []; let restOfBody = body; const expressions = [...body.matchAll(expressionsRE)]; for (let [expression] of expressions) { restOfBody = restOfBody.replace(expression, "").trim(); const classes = [...expression.matchAll(classesRE)]; for (const [withQuotes, quoteMark, withoutQuotes] of classes) { const { rulesToGenerate: rulesFromExpression, ignore } = await sortClassesIntoCategories(withoutQuotes, options, uno, filename); Object.assign(rulesToGenerate, rulesFromExpression); const updatedClasses = Object.keys(rulesFromExpression).concat(ignore).join(" "); expression = expression.replace(withQuotes, quoteMark + updatedClasses + quoteMark); } updatedExpressions.push(expression); } return { rulesToGenerate, updatedExpressions, restOfBody }; } async function processClassBody({ body, start, end }, options, uno, filename) { const expandedBody = expandVariantGroup(body); const { rulesToGenerate: rulesFromExpressions, restOfBody, updatedExpressions } = await processExpressions(expandedBody, options, uno, filename); const { rulesToGenerate: rulesFromRegularClasses, ignore } = await sortClassesIntoCategories(restOfBody, options, uno, filename); const rulesToGenerate = { ...rulesFromExpressions, ...rulesFromRegularClasses }; if (!Object.keys(rulesToGenerate).length) return {}; const content = Object.keys(rulesFromRegularClasses).concat(ignore).concat(updatedExpressions).join(" "); const codeUpdate = { content, start, end }; return { rulesToGenerate, codeUpdate }; } async function processClasses(classes, options, uno, filename) { const result = { rulesToGenerate: {}, codeUpdates: [] }; for (const foundClass of classes) { if (foundClass.type === "regular") { const { rulesToGenerate, codeUpdate } = await processClassBody(foundClass, options, uno, filename); if (rulesToGenerate) Object.assign(result.rulesToGenerate, rulesToGenerate); if (codeUpdate) result.codeUpdates.push(codeUpdate); } else { const { rulesToGenerate, codeUpdate } = await processDirective(foundClass, options, uno, filename) || {}; if (rulesToGenerate) Object.assign(result.rulesToGenerate, rulesToGenerate); if (codeUpdate) result.codeUpdates.push(codeUpdate); } } return result; } async function transformClasses({ content, filename, uno, options }) { const classesToProcess = findClasses(content); if (!classesToProcess.length) return; const { rulesToGenerate, codeUpdates } = await processClasses(classesToProcess, options, uno, filename); if (!Object.keys(rulesToGenerate).length) return; const { map, code } = updateTemplateCodeIfNeeded(codeUpdates, content, filename); const generatedStyles = await generateStyles(rulesToGenerate, uno); const codeWithGeneratedStyles = addGeneratedStylesIntoStyleBlock(code, generatedStyles); return { code: codeWithGeneratedStyles, map }; } function updateTemplateCodeIfNeeded(codeUpdates, source, filename) { if (!codeUpdates.length) return { code: source, map: void 0 }; const s = new MagicString(source); for (const { start, end, content } of codeUpdates) s.overwrite(start, end, content); return { code: s.toString(), map: s.generateMap({ hires: true, source: filename }) }; } const removeCommentsToMakeGlobalWrappingEasy = true; async function generateStyles(rulesToGenerate, uno) { const originalShortcuts = uno.config.shortcuts; const shortcutsForThisComponent = Object.entries(rulesToGenerate); uno.config.shortcuts = [...originalShortcuts, ...shortcutsForThisComponent]; const selectorsToGenerate = Object.keys(rulesToGenerate); const { css } = await uno.generate( selectorsToGenerate, { preflights: false, safelist: false, minify: removeCommentsToMakeGlobalWrappingEasy } ); uno.config.shortcuts = originalShortcuts; const cssPreparedForSvelteCompiler = wrapSelectorsWithGlobal(css); return cssPreparedForSvelteCompiler; } function removeOuterQuotes(input) { if (!input) return ""; const match = input.match(/^(['"]).*\1$/); return match ? input.slice(1, -1) : input; } function writeUtilStyles([, selector, body, parent], s, node, childNode) { if (!selector) return; const selectorChanged = selector !== ".\\-"; if (!parent && !selectorChanged) return s.appendRight(childNode.loc.end.offset, body); const originalSelector = generate(node.prelude); if (parent && !selectorChanged) { const css2 = `${parent}{${originalSelector}{${body}}}`; return s.appendLeft(node.loc.end.offset, css2); } const utilSelector = selector.replace(regexScopePlaceholder, " "); const updatedSelector = generateUpdatedSelector(utilSelector, node.prelude); const svelteCompilerReadySelector = surroundAllButOriginalSelectorWithGlobal(originalSelector, updatedSelector); const rule = `${svelteCompilerReadySelector}{${body}}`; const css = parent ? `${parent}{${rule}}` : rule; s.appendLeft(node.loc.end.offset, css); } function generateUpdatedSelector(selector, _prelude) { const selectorAST = parse(selector, { context: "selector" }); const prelude = clone(_prelude); prelude.children.forEach((child) => { const parentSelectorAst = clone(selectorAST); parentSelectorAst.children.forEach((i) => { if (i.type === "ClassSelector" && i.name === "\\-") Object.assign(i, clone(child)); }); Object.assign(child, parentSelectorAst); }); return generate(prelude); } function surroundAllButOriginalSelectorWithGlobal(originalSelector, updatedSelector) { const wrapWithGlobal = (str) => `:global(${str})`; const originalSelectors = originalSelector.split(",").map((s) => s.trim()); const updatedSelectors = updatedSelector.split(",").map((s) => s.trim()); const resultSelectors = originalSelectors.map((original, index) => { const updated = updatedSelectors[index]; const [prefix, suffix] = updated.split(original).map((s) => s.trim()); const wrappedPrefix = prefix ? wrapWithGlobal(prefix) : ""; if (!suffix) return `${wrappedPrefix} ${original}`.trim(); const indexOfFirstCombinator = findFirstCombinatorIndex(suffix); if (indexOfFirstCombinator === -1) return `${wrappedPrefix} ${original}${suffix}`.trim(); const pseudo = suffix.substring(0, indexOfFirstCombinator).trim(); const siblingsOrDescendants = suffix.substring(indexOfFirstCombinator).trim(); return `${wrappedPrefix} ${original}${pseudo} ${wrapWithGlobal(siblingsOrDescendants)}`.trim(); }); return resultSelectors.join(", "); } function findFirstCombinatorIndex(input) { const combinators = [" ", ">", "~", "+"]; for (const c of combinators) { const indexOfFirstCombinator = input.indexOf(c); if (indexOfFirstCombinator !== -1) return indexOfFirstCombinator; } return -1; } async function getUtils(body, uno) { const classNames = expandVariantGroup(body).split(/\s+/g).map((className) => className.trim().replace(/\\/, "")); const utils = await parseUtils(classNames, uno); const sortedByRankIndex = utils.sort(([aIndex], [bIndex]) => aIndex - bIndex); const sortedByParentOrders = sortedByRankIndex.sort(([, , , aParent], [, , , bParent]) => (aParent ? uno.parentOrders.get(aParent) ?? 0 : 0) - (bParent ? uno.parentOrders.get(bParent) ?? 0 : 0)); return sortedByParentOrders.reduce((acc, item) => { const [, selector, body2, parent] = item; const sibling = acc.find(([, targetSelector, , targetParent]) => targetSelector === selector && targetParent === parent); if (sibling) sibling[2] += body2; else acc.push([...item]); return acc; }, []); } async function parseUtils(classNames, uno) { const foundUtils = []; for (const token of classNames) { const util = await uno.parseToken(token, "-"); if (util) foundUtils.push(util); else warnOnce(`'${token}' not found. You have a typo or need to add a preset.`); } return foundUtils.flat(); } const DEFAULT_APPLY_VARIABLES = ["--at-apply"]; async function transformApply({ content, uno, prepend, applyVariables, filename }) { applyVariables = toArray(applyVariables || DEFAULT_APPLY_VARIABLES); const hasApply = content.includes("@apply") || applyVariables.some((v) => content.includes(v)); if (!hasApply) return; const s = new MagicString(content); await walkCss({ s, uno, applyVariables }); if (!s.hasChanged()) return; if (prepend) s.prepend(prepend); return { code: s.toString(), map: s.generateMap({ hires: true, source: filename || "" }) }; } async function walkCss(ctx) { const ast = parse(ctx.s.original, { parseAtrulePrelude: false, positions: true }); if (ast.type !== "StyleSheet") return; const stack = []; walk(ast, (node) => { if (node.type === "Rule") stack.push(handleApply(ctx, node)); }); await Promise.all(stack); } async function handleApply(ctx, node) { const parsePromises = node.block.children.map(async (childNode) => { await parseApply(ctx, node, childNode); }); await Promise.all(parsePromises); } async function parseApply({ s, uno, applyVariables }, node, childNode) { const body = getChildNodeValue(childNode, applyVariables); if (!body) return; const utils = await getUtils(body, uno); if (!utils.length) return; for (const util of utils) writeUtilStyles(util, s, node, childNode); s.remove(childNode.loc.start.offset, childNode.loc.end.offset); } function getChildNodeValue(childNode, applyVariables) { if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") return childNode.prelude.value.trim(); if (childNode.type === "Declaration" && applyVariables.includes(childNode.property) && childNode.value.type === "Raw") return removeOuterQuotes(childNode.value.value.trim()); } function UnocssSveltePreprocess(options = {}, unoContextFromVite) { if (!options.classPrefix) options.classPrefix = "spu-"; let uno; return { markup: async ({ content, filename }) => { if (!uno) uno = await getGenerator(options.configOrPath, unoContextFromVite); return await transformClasses({ content, filename: filename || "", uno, options }); }, style: async ({ content, attributes, filename }) => { const addPreflights = !!attributes["uno:preflights"]; const addSafelist = !!attributes["uno:safelist"]; const checkForApply = options.applyVariables !== false; const changeNeeded = addPreflights || addSafelist || checkForApply; if (!changeNeeded) return; if (!uno) uno = await getGenerator(options.configOrPath); let preflightsSafelistCss = ""; if (addPreflights || addSafelist) { if (unoContextFromVite) warnOnce("Do not place preflights or safelist within an individual component as they already placed in your global styles injected into the head tag. These options are only for component libraries."); const { css } = await uno.generate([], { preflights: addPreflights, safelist: addSafelist, minify: true }); preflightsSafelistCss = css; } if (checkForApply) { return await transformApply({ content, prepend: preflightsSafelistCss, uno, applyVariables: options.applyVariables, filename }); } if (preflightsSafelistCss) return { code: preflightsSafelistCss }; } }; } async function getGenerator(configOrPath, unoContextFromVite) { if (unoContextFromVite) { await unoContextFromVite.ready; return unoContextFromVite.uno; } const defaults = { presets: [ presetUno() ] }; const { config } = await loadConfig(process.cwd(), configOrPath); return createGenerator(config, defaults); } function PassPreprocessToSveltePlugin(options = {}, ctx) { return { name: "svelte-scoped-uno:pass-preprocess", enforce: "pre", configResolved(viteConfig) { options = { ...options, combine: viteConfig.command === "build" }; }, api: { sveltePreprocess: UnocssSveltePreprocess(options, ctx) } }; } const GLOBAL_STYLES_PLACEHOLDER = "svelte_scoped_uno_global_styles"; const PLACEHOLDER_USER_SETS_IN_INDEX_HTML = "%svelte-scoped-uno.global%"; const DEV_GLOBAL_STYLES_DATA_TITLE = "svelte-scoped-uno global styles"; const tempTailwindFallbackReset = '*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}'; const _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url)); function getReset(injectReset) { if (injectReset.startsWith("@unocss/reset")) { const resolvedPNPM = resolve(resolve(_dirname, `../node_modules/${injectReset}`)); if (isFile(resolvedPNPM)) return readFileSync(resolvedPNPM, "utf-8"); const resolvedNPM = resolve(process.cwd(), "node_modules", injectReset); if (isFile(resolvedNPM)) return readFileSync(resolvedNPM, "utf-8"); if (injectReset === "@unocss/reset/tailwind.css") { console.log("Using tailwind reset fallback. Please file a bug report as this means there is an issue with the reset module resolving with this environment or package manager."); return tempTailwindFallbackReset; } throw new Error(`"${injectReset}" given as your injectReset value is not found. Please check to make sure it is one of the five supported @unocss/reset options. If it is file a bug report detailing your environment and package manager`); } if (injectReset.startsWith(".")) { const resolved = resolve(process.cwd(), injectReset); if (!isFile(resolved)) throw new Error(`"${injectReset}" given as your injectReset value is not a valid file path relative to the root of your project, where your vite config file sits. To give an example, if you placed a reset.css in your src directory, "./src/reset.css" would work.`); return readFileSync(resolved, "utf-8"); } if (injectReset.startsWith("/")) throw new Error(`Your injectReset value: "${injectReset}" is not a valid file path. To give an example, if you placed a reset.css in your src directory, "./src/reset.css" would work.`); const resolvedFromNodeModules = resolve(process.cwd(), "node_modules", injectReset); if (!isFile(resolvedFromNodeModules)) throw new Error(`"${injectReset}" given as your injectReset value is not a valid file path relative to your project's node_modules folder. Can you confirm that you've installed "${injectReset}"?`); return readFileSync(resolvedFromNodeModules, "utf-8"); } function isFile(path) { return existsSync(path) && statSync(path).isFile(); } function isServerHooksFile(path) { return path.includes("hooks") && path.includes("server"); } function replaceGlobalStylesPlaceholder(code, stylesTag) { const captureQuoteMark = "([\"'`])"; const matchCapturedQuoteMark = "\\1"; const QUOTES_WITH_PLACEHOLDER_RE = new RegExp(captureQuoteMark + GLOBAL_STYLES_PLACEHOLDER + matchCapturedQuoteMark); const escapedStylesTag = stylesTag.replaceAll(/`/g, "\\`"); return code.replace(QUOTES_WITH_PLACEHOLDER_RE, `\`${escapedStylesTag}\``); } async function generateGlobalCss(uno, injectReset) { const { css } = await uno.generate("", { preflights: true, safelist: true, minify: true }); const reset = injectReset ? getReset(injectReset) : ""; return reset + css; } const SVELTE_ERROR = `[unocss] You have not setup the svelte-scoped global styles correctly. You must place '${PLACEHOLDER_USER_SETS_IN_INDEX_HTML}' in your index.html file. `; const SVELTE_KIT_ERROR = `[unocss] You have not setup the svelte-scoped global styles correctly. You must place '${PLACEHOLDER_USER_SETS_IN_INDEX_HTML}' in your app.html file. You also need to have a transformPageChunk hook in your server hooks file with: \`html.replace('${PLACEHOLDER_USER_SETS_IN_INDEX_HTML}', '${GLOBAL_STYLES_PLACEHOLDER}')\`. You can see an example of the usage at https://github.com/jacob-8/svelte-scoped-uno/tree/main/examples/sveltekit-vite-plugin.`; function checkTransformPageChunkHook(server, isSvelteKit) { server.middlewares.use((req, res, next) => { const originalWrite = res.write; res.write = function(chunk, ...rest) { const str = chunk instanceof Buffer ? chunk.toString() : Array.isArray(chunk) || "at" in chunk ? Buffer.from(chunk).toString() : `${chunk}`; if (str.includes("<head>") && !str.includes(DEV_GLOBAL_STYLES_DATA_TITLE)) server.config.logger.error(isSvelteKit ? SVELTE_KIT_ERROR : SVELTE_ERROR, { timestamp: true }); return originalWrite.call(this, chunk, ...rest); }; next(); }); } function GlobalStylesPlugin({ ready, uno }, injectReset) { let isSvelteKit; let viteConfig; let unoCssFileReferenceId; let unoCssHashedLinkTag; return { name: "unocss:svelte-scoped:global-styles", async configResolved(_viteConfig) { viteConfig = _viteConfig; await ready; isSvelteKit = viteConfig.plugins.some((p) => p.name.includes("sveltekit")); }, // serve configureServer: (server) => checkTransformPageChunkHook(server, isSvelteKit), // serve async transform(code, id) { if (isSvelteKit && viteConfig.command === "serve" && isServerHooksFile(id)) { const css = await generateGlobalCss(uno, injectReset); return { code: replaceGlobalStylesPlaceholder(code, `<style type="text/css" data-title="${DEV_GLOBAL_STYLES_DATA_TITLE}">${css}</style>`) }; } }, // build async buildStart() { if (viteConfig.command === "build") { const css = await generateGlobalCss(uno, injectReset); unoCssFileReferenceId = this.emitFile({ type: "asset", name: "unocss-svelte-scoped-global.css", source: css }); } }, // build renderStart() { const unoCssFileName = this.getFileName(unoCssFileReferenceId); const base = viteConfig.base ?? "/"; unoCssHashedLinkTag = `<link href="${base}${unoCssFileName}" rel="stylesheet" />`; }, // build renderChunk(code, chunk) { if (isSvelteKit && chunk.moduleIds.some((id) => isServerHooksFile(id))) return replaceGlobalStylesPlaceholder(code, unoCssHashedLinkTag); }, // serve and build async transformIndexHtml(html) { if (!isSvelteKit) { if (viteConfig.command === "build") return html.replace(PLACEHOLDER_USER_SETS_IN_INDEX_HTML, unoCssHashedLinkTag); if (viteConfig.command === "serve") { const css = await generateGlobalCss(uno, injectReset); return html.replace(PLACEHOLDER_USER_SETS_IN_INDEX_HTML, `<style type="text/css" data-title="${DEV_GLOBAL_STYLES_DATA_TITLE}">${css}</style>`); } } } }; } function UnocssSvelteScopedVite(options = {}) { if (!options.injectReset && options.addReset) { options.injectReset = "@unocss/reset/tailwind.css"; console.warn('[svelte-scoped-uno] `addReset` is deprecated, please use `injectReset: "@unocss/reset/tailwind.css"` instead.'); } const context = createSvelteScopedContext(options.configOrPath); const plugins = [ GlobalStylesPlugin(context, options.injectReset) ]; if (!options.onlyGlobal) plugins.push(PassPreprocessToSveltePlugin(options, context)); return plugins; } function createSvelteScopedContext(configOrPath) { const uno = createGenerator(); const ready = reloadConfig(); async function reloadConfig() { const { config } = await loadConfig(process.cwd(), configOrPath); uno.setConfig(config); return config; } return { uno, ready }; } export { UnocssSveltePreprocess as PreprocessUnocss, UnocssSvelteScopedVite as SvelteScopedUno };