summaryrefslogtreecommitdiff
path: root/src/compiler/transform/postcss-scoped-styles/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/transform/postcss-scoped-styles/index.ts')
-rw-r--r--src/compiler/transform/postcss-scoped-styles/index.ts106
1 files changed, 0 insertions, 106 deletions
diff --git a/src/compiler/transform/postcss-scoped-styles/index.ts b/src/compiler/transform/postcss-scoped-styles/index.ts
deleted file mode 100644
index 23350869c..000000000
--- a/src/compiler/transform/postcss-scoped-styles/index.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Declaration, Plugin } from 'postcss';
-
-interface AstroScopedOptions {
- className: string;
-}
-
-interface Selector {
- start: number;
- end: number;
- value: string;
-}
-
-const CSS_SEPARATORS = new Set([' ', ',', '+', '>', '~']);
-const KEYFRAME_PERCENT = /\d+\.?\d*%/;
-
-/** 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 Rules
- * Given a selector string (`.btn>span,.nav>span`), add an additional CSS class to every selector (`.btn.myClass>span.myClass,.nav.myClass>span.myClass`)
- * @param {string} selector The minified selector string to parse. Cannot contain arbitrary whitespace (other than child selector syntax).
- * @param {string} className The CSS class to apply.
- */
-export function scopeRule(selector: string, className: string) {
- // if this is a keyframe keyword, return original selector
- if (selector === 'from' || selector === 'to' || KEYFRAME_PERCENT.test(selector)) {
- return selector;
- }
-
- // For everything else, parse & scope
- const c = className.replace(/^\.?/, '.'); // make sure class always has leading '.'
- const selectors: Selector[] = [];
- let ss = selector; // final output
-
- // Pass 1: parse selector string; extract top-level selectors
- {
- let start = 0;
- let lastValue = '';
- let parensOpen = false;
- for (let n = 0; n < ss.length; n++) {
- const isEnd = n === selector.length - 1;
- if (selector[n] === '(') parensOpen = true;
- if (selector[n] === ')') parensOpen = false;
- if (isEnd || (parensOpen === false && CSS_SEPARATORS.has(selector[n]))) {
- lastValue = selector.substring(start, isEnd ? undefined : n);
- if (!lastValue) continue;
- selectors.push({ start, end: isEnd ? n + 1 : n, value: lastValue });
- start = n + 1;
- }
- }
- }
-
- // Pass 2: starting from end, transform selectors w/ scoped class
- for (let i = selectors.length - 1; i >= 0; i--) {
- const { start, end, value } = selectors[i];
- const head = ss.substring(0, start);
- const tail = ss.substring(end);
-
- // replace '*' with className
- if (value === '*') {
- ss = head + c + tail;
- continue;
- }
-
- // leave :global() alone!
- if (value.startsWith(':global(')) {
- ss =
- head +
- ss
- .substring(start, end)
- .replace(/^:global\(/, '')
- .replace(/\)$/, '') +
- tail;
- 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(':');
- if (pseudoIndex > 0) {
- // if there‘s a pseudoclass (:focus)
- ss = head + newSelector.substring(start, pseudoIndex) + c + newSelector.substr(pseudoIndex) + tail;
- } else {
- ss = head + newSelector + c + tail;
- }
- }
-
- return ss;
-}
-
-/** PostCSS Scope plugin */
-export default function astroScopedStyles(options: AstroScopedOptions): Plugin {
- return {
- postcssPlugin: '@astro/postcss-scoped-styles',
- Rule(rule) {
- rule.selector = scopeRule(rule.selector, options.className);
- },
- };
-}