diff options
author | 2021-04-02 12:50:30 -0600 | |
---|---|---|
committer | 2021-04-02 12:50:30 -0600 | |
commit | b58b493948ef7b6453ba8c4e94c4bd2fcaea8452 (patch) | |
tree | 4bb0426f35e3c7e6a8c7523e6f0aaa1e90407113 /src | |
parent | 004b3ea6a0b48ecc04d2a0daf9aae374983bed08 (diff) | |
download | astro-b58b493948ef7b6453ba8c4e94c4bd2fcaea8452.tar.gz astro-b58b493948ef7b6453ba8c4e94c4bd2fcaea8452.tar.zst astro-b58b493948ef7b6453ba8c4e94c4bd2fcaea8452.zip |
Fix body from being scoped (#56)
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/optimize/postcss-scoped-styles/index.ts | 9 | ||||
-rw-r--r-- | src/compiler/optimize/styles.ts | 30 |
2 files changed, 30 insertions, 9 deletions
diff --git a/src/compiler/optimize/postcss-scoped-styles/index.ts b/src/compiler/optimize/postcss-scoped-styles/index.ts index 0d1253350..01c0acd94 100644 --- a/src/compiler/optimize/postcss-scoped-styles/index.ts +++ b/src/compiler/optimize/postcss-scoped-styles/index.ts @@ -12,6 +12,9 @@ interface Selector { const CSS_SEPARATORS = new Set([' ', ',', '+', '>', '~']); +/** HTML tags that should never get scoped classes */ +export const NEVER_SCOPED_TAGS = new Set<string>(['base', 'body', 'font', 'frame', 'frameset', 'head', 'html', 'link', 'meta', 'noframes', 'noscript', 'script', 'style', 'title']); + /** * Scope Selectors * Given a selector string (`.btn>span,.nav>span`), add an additional CSS class to every selector (`.btn.myClass>span.myClass,.nav.myClass>span.myClass`) @@ -62,6 +65,12 @@ export function scopeSelectors(selector: string, className: string) { continue; } + // don‘t scope body, title, etc. + if (NEVER_SCOPED_TAGS.has(value)) { + ss = head + value + tail; + continue; + } + // scope everything else let newSelector = ss.substring(start, end); const pseudoIndex = newSelector.indexOf(':'); diff --git a/src/compiler/optimize/styles.ts b/src/compiler/optimize/styles.ts index 36e6f1d7d..72781fefe 100644 --- a/src/compiler/optimize/styles.ts +++ b/src/compiler/optimize/styles.ts @@ -7,7 +7,7 @@ import sass from 'sass'; import { RuntimeMode } from '../../@types/astro'; import { OptimizeOptions, Optimizer } from '../../@types/optimizer'; import type { TemplateNode } from '../../parser/interfaces'; -import astroScopedStyles from './postcss-scoped-styles/index.js'; +import astroScopedStyles, { NEVER_SCOPED_TAGS } from './postcss-scoped-styles/index.js'; type StyleType = 'css' | 'scss' | 'sass' | 'postcss'; @@ -26,9 +26,6 @@ const getStyleType: Map<string, StyleType> = new Map([ ['text/scss', 'scss'], ]); -/** HTML tags that should never get scoped classes */ -const NEVER_SCOPED_TAGS = new Set<string>(['html', 'head', 'body', 'script', 'style', 'link', 'meta']); - /** Should be deterministic, given a unique filename */ function hashFromFilename(filename: string): string { const hash = crypto.createHash('sha256'); @@ -55,6 +52,15 @@ export interface TransformStyleOptions { mode: RuntimeMode; } +/** given a class="" string, does it contain a given class? */ +function hasClass(classList: string, className: string): boolean { + if (!className) return false; + for (const c of classList.split(' ')) { + if (className === c.trim()) return true; + } + return false; +} + /** Convert styles to scoped CSS */ async function transformStyle(code: string, { type, filename, scopedClass, mode }: TransformStyleOptions): Promise<StyleTransformResult> { let styleType: StyleType = 'css'; // important: assume CSS as default @@ -149,12 +155,18 @@ export default function optimizeStyles({ compileOptions, filename, fileID }: Opt const attr = node.attributes[classIndex]; for (let k = 0; k < attr.value.length; k++) { if (attr.value[k].type === 'Text') { - // string literal - attr.value[k].raw += ' ' + scopedClass; - attr.value[k].data += ' ' + scopedClass; + // don‘t add same scopedClass twice + if (!hasClass(attr.value[k].data, scopedClass)) { + // string literal + attr.value[k].raw += ' ' + scopedClass; + attr.value[k].data += ' ' + scopedClass; + } } else if (attr.value[k].type === 'MustacheTag' && attr.value[k]) { - // MustacheTag - attr.value[k].content = `(${attr.value[k].content}) + ' ${scopedClass}'`; + // don‘t add same scopedClass twice (this check is a little more basic, but should suffice) + if (!attr.value[k].content.includes(`' ${scopedClass}'`)) { + // MustacheTag + attr.value[k].content = `(${attr.value[k].content}) + ' ${scopedClass}'`; + } } } } |