UNPKG

eslint-plugin-astro

Version:
1,662 lines (1,649 loc) 157 kB
import Module, { createRequire } from 'module'; import path4 from 'path'; import { getSourceCode as getSourceCode$1, getFilename as getFilename$1, getCwd as getCwd$1 } from 'eslint-compat-utils'; import { AST_NODE_TYPES } from '@typescript-eslint/types'; import { ReferenceTracker, READ, getPropertyName, isOpeningParenToken, isSemicolonToken, getStaticValue, isParenthesized, isClosingParenToken, isCommaToken, isClosingBraceToken } from '@eslint-community/eslint-utils'; import postcss from 'postcss'; import { decode } from '@jridgewell/sourcemap-codec'; import parser from 'postcss-selector-parser'; import * as parser2 from 'astro-eslint-parser'; import { parseTemplate, traverseNodes } from 'astro-eslint-parser'; import globals from 'globals'; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all2) => { for (var name2 in all2) __defProp(target, name2, { get: all2[name2], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var hasTypescriptEslintParser, tsESLintParser; var init_has_typescript_eslint_parser = __esm({ "src/configs/has-typescript-eslint-parser.ts"() { hasTypescriptEslintParser = false; tsESLintParser = null; try { const cwd = process.cwd(); const relativeTo = path4.join(cwd, "__placeholder__.js"); if (tsESLintParser = createRequire(relativeTo)("@typescript-eslint/parser")) hasTypescriptEslintParser = true; } catch { } } }); // src/environments/index.ts var environments; var init_environments = __esm({ "src/environments/index.ts"() { environments = { astro: { globals: { // Astro object Astro: false, // JSX Fragment Fragment: false } } }; } }); // src/utils/index.ts function createRule(ruleName, rule) { return { meta: { ...rule.meta, docs: { available: () => true, ...rule.meta.docs, url: `https://ota-meshi.github.io/eslint-plugin-astro/rules/${ruleName}/`, ruleId: `astro/${ruleName}`, ruleName } }, create: rule.create }; } var init_utils = __esm({ "src/utils/index.ts"() { } }); function getSourceCode(context) { return getSourceCode$1(context); } function getFilename(context) { return getFilename$1(context); } function getCwd(context) { return getCwd$1(context); } var init_compat = __esm({ "src/utils/compat.ts"() { } }); function getAttributeName(node) { if (node.type === "JSXSpreadAttribute") { return null; } const { name: name2 } = node; return getName(name2); } function getElementName(node) { const nameNode = node.openingElement.name; return getName(nameNode); } function findAttribute(node, name2) { const openingElement = node.openingElement; for (const attr of openingElement.attributes) { if (attr.type === "JSXSpreadAttribute") { continue; } if (getAttributeName(attr) === name2) { return attr; } } return null; } function getSpreadAttributes(node) { const openingElement = node.openingElement; return openingElement.attributes.filter( (attr) => attr.type === "JSXSpreadAttribute" ); } function getStaticAttributeStringValue(node, context) { const value = getStaticAttributeValue(node, context); if (!value) { return null; } return value.value != null ? String(value.value) : value.value; } function getStaticAttributeValue(node, context) { if (node.value?.type === AST_NODE_TYPES.Literal) { return { value: node.value.value }; } if (context && node.value?.type === "JSXExpressionContainer" && node.value.expression.type !== "JSXEmptyExpression") { const sourceCode = getSourceCode(context); const staticValue = getStaticValue( node.value.expression, sourceCode.scopeManager.globalScope ); if (staticValue != null) { return staticValue; } } return null; } function isStringCallExpression(node) { if (node.type === AST_NODE_TYPES.CallExpression) { return node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === "String"; } return false; } function isStringLiteral(node) { return node.type === AST_NODE_TYPES.Literal && typeof node.value === "string"; } function extractConcatExpressions(node, sourceCode) { if (node.operator !== "+") { return null; } const leftResult = processLeft(node.left); if (leftResult == null) { return null; } return [...leftResult, node.right]; function processLeft(expr) { if (expr.type === AST_NODE_TYPES.BinaryExpression) { if (!isParenthesized(expr, sourceCode) && expr.operator !== "*" && expr.operator !== "/") { return extractConcatExpressions(expr, sourceCode); } } return [expr]; } } function getStringIfConstant(node) { if (node.type === "Literal") { if (typeof node.value === "string") return node.value; } else if (node.type === "TemplateLiteral") { let str = ""; const quasis = [...node.quasis]; const expressions = [...node.expressions]; let quasi, expr; while (quasi = quasis.shift()) { str += quasi.value.cooked; expr = expressions.shift(); if (expr) { const exprStr = getStringIfConstant(expr); if (exprStr == null) { return null; } str += exprStr; } } return str; } else if (node.type === "BinaryExpression") { if (node.operator === "+") { const left = getStringIfConstant(node.left); if (left == null) { return null; } const right = getStringIfConstant(node.right); if (right == null) { return null; } return left + right; } } return null; } function needParentheses(node, kind) { if (node.type === "ArrowFunctionExpression" || node.type === "AssignmentExpression" || node.type === "BinaryExpression" || node.type === "ConditionalExpression" || node.type === "LogicalExpression" || node.type === "SequenceExpression" || node.type === "UnaryExpression" || node.type === "UpdateExpression") return true; return false; } function getParenthesizedTokens(node, sourceCode) { let lastLeft = sourceCode.getFirstToken(node); let lastRight = sourceCode.getLastToken(node); let maybeLeftParen, maybeRightParen; while ((maybeLeftParen = sourceCode.getTokenBefore(lastLeft)) && (maybeRightParen = sourceCode.getTokenAfter(lastRight)) && isOpeningParenToken(maybeLeftParen) && isClosingParenToken(maybeRightParen) && // Avoid false positive such as `if (a) {}` maybeLeftParen !== getParentSyntaxParen(node, sourceCode)) { lastLeft = maybeLeftParen; lastRight = maybeRightParen; maybeLeftParen = sourceCode.getTokenBefore(lastLeft); maybeRightParen = sourceCode.getTokenAfter(lastRight); } return { left: lastLeft, right: lastRight }; } function getParenthesizedRange(node, sourceCode) { const { left, right } = getParenthesizedTokens(node, sourceCode); return [left.range[0], right.range[1]]; } function getParentSyntaxParen(node, sourceCode) { const parent = node.parent; switch (parent.type) { case "CallExpression": case "NewExpression": if (parent.arguments.length === 1 && parent.arguments[0] === node) { return sourceCode.getTokenAfter(parent.callee, { includeComments: false, filter: isOpeningParenToken }); } return null; case "DoWhileStatement": if (parent.test === node) { return sourceCode.getTokenAfter(parent.body, { includeComments: false, filter: isOpeningParenToken }); } return null; case "IfStatement": case "WhileStatement": if (parent.test === node) { return sourceCode.getFirstToken(parent, { includeComments: false, skip: 1 }); } return null; case "ImportExpression": if (parent.source === node) { return sourceCode.getFirstToken(parent, { includeComments: false, skip: 1 }); } return null; case "SwitchStatement": if (parent.discriminant === node) { return sourceCode.getFirstToken(parent, { includeComments: false, skip: 1 }); } return null; case "WithStatement": if (parent.object === node) { return sourceCode.getFirstToken(parent, { includeComments: false, skip: 1 }); } return null; default: return null; } } function getName(nameNode) { if (nameNode.type === "JSXIdentifier") { return nameNode.name; } if (nameNode.type === "JSXNamespacedName") { return `${nameNode.namespace.name}:${nameNode.name.name}`; } if (nameNode.type === "JSXMemberExpression") { return `${getName(nameNode.object)}.${nameNode.property.name}`; } return null; } function isTokenOnSameLine(left, right) { return left?.loc?.end.line === right?.loc?.start.line; } function getNextLocation(sourceCode, { column, line }) { if (column < sourceCode.lines[line - 1].length) { return { column: column + 1, line }; } if (line < sourceCode.lines.length) { return { column: 0, line: line + 1 }; } return null; } function getUpperFunction(node) { for (let currentNode = node; currentNode; currentNode = currentNode.parent) { if (anyFunctionPattern.test(currentNode.type)) return currentNode; } return null; } var anyFunctionPattern; var init_ast_utils = __esm({ "src/utils/ast-utils.ts"() { init_compat(); anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; } }); // src/rules/missing-client-only-directive-value.ts var missing_client_only_directive_value_default; var init_missing_client_only_directive_value = __esm({ "src/rules/missing-client-only-directive-value.ts"() { init_utils(); init_ast_utils(); init_compat(); missing_client_only_directive_value_default = createRule("missing-client-only-directive-value", { meta: { docs: { description: "the client:only directive is missing the correct component's framework value", category: "Possible Errors", recommended: true }, schema: [], messages: { missingValue: "`client:only` directive is missing a value" }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } function verifyDirectiveValue(attr) { const directiveName = getAttributeName(attr); if (directiveName !== "client:only") return; const directiveValue = getStaticAttributeValue(attr, context); if (directiveValue !== null) return; context.report({ node: attr.name, messageId: "missingValue" }); } return { JSXAttribute: verifyDirectiveValue, AstroTemplateLiteralAttribute: verifyDirectiveValue }; } }); } }); // src/rules/no-conflict-set-directives.ts var no_conflict_set_directives_default; var init_no_conflict_set_directives = __esm({ "src/rules/no-conflict-set-directives.ts"() { init_utils(); init_ast_utils(); init_compat(); no_conflict_set_directives_default = createRule("no-conflict-set-directives", { meta: { docs: { description: "disallow conflicting set directives and child contents", category: "Possible Errors", recommended: true }, schema: [], messages: { conflict: "{{name}} conflicts with {{conflictTargets}}." }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } return { JSXElement(node) { const reportData = []; for (const attr of node.openingElement.attributes) { const directiveName = getAttributeName(attr); if (directiveName === "set:text" || directiveName === "set:html") { reportData.push({ loc: attr.loc, name: `'${directiveName}'` }); } } if (reportData.length) { const targetChildren = node.children.filter((child) => { if (child.type === "AstroHTMLComment") { return false; } if (child.type === "JSXText" || child.type === "AstroRawText") { return Boolean(child.value.trim()); } return true; }).map((child) => { if (child.type === "JSXText" || child.type === "AstroRawText") { const leadingSpaces = /^\s*/.exec(child.value)[0]; const trailingSpaces = /\s*$/.exec(child.value)[0]; return { loc: { start: sourceCode.getLocFromIndex( child.range[0] + leadingSpaces.length ), end: sourceCode.getLocFromIndex( child.range[1] - trailingSpaces.length ) } }; } return child; }); if (targetChildren.length) { reportData.push({ loc: { start: targetChildren[0].loc.start, end: targetChildren[targetChildren.length - 1].loc.end }, name: "child contents" }); } } if (reportData.length >= 2) { for (const data of reportData) { const conflictTargets = reportData.filter((d) => d !== data).map((d) => d.name); context.report({ loc: data.loc, messageId: "conflict", data: { name: data.name, conflictTargets: [ conflictTargets.slice(0, -1).join(", "), conflictTargets.slice(-1)[0] ].filter(Boolean).join(", and ") } }); } } } }; } }); } }); var no_deprecated_astro_canonicalurl_default; var init_no_deprecated_astro_canonicalurl = __esm({ "src/rules/no-deprecated-astro-canonicalurl.ts"() { init_utils(); init_compat(); no_deprecated_astro_canonicalurl_default = createRule("no-deprecated-astro-canonicalurl", { meta: { docs: { description: "disallow using deprecated `Astro.canonicalURL`", category: "Possible Errors", recommended: true }, schema: [], messages: { deprecated: "'Astro.canonicalURL' is deprecated. Use 'Astro.url' helper instead." }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } return { "Program:exit"(node) { const tracker = new ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { canonicalURL: { [READ]: true } } })) { context.report({ node: node2, messageId: "deprecated", data: { name: path5.join(".") } }); } } }; } }); } }); var no_deprecated_astro_fetchcontent_default; var init_no_deprecated_astro_fetchcontent = __esm({ "src/rules/no-deprecated-astro-fetchcontent.ts"() { init_utils(); init_compat(); no_deprecated_astro_fetchcontent_default = createRule("no-deprecated-astro-fetchcontent", { meta: { docs: { description: "disallow using deprecated `Astro.fetchContent()`", category: "Possible Errors", recommended: true }, schema: [], messages: { deprecated: "'Astro.fetchContent()' is deprecated. Use 'Astro.glob()' instead." }, type: "problem", fixable: "code" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } return { "Program:exit"(node) { const tracker = new ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { fetchContent: { [READ]: true } } })) { context.report({ node: node2, messageId: "deprecated", data: { name: path5.join(".") }, fix(fixer) { if (node2.type !== "MemberExpression" || node2.computed) { return null; } return fixer.replaceText(node2.property, "glob"); } }); } } }; } }); } }); var no_deprecated_astro_resolve_default; var init_no_deprecated_astro_resolve = __esm({ "src/rules/no-deprecated-astro-resolve.ts"() { init_utils(); init_compat(); no_deprecated_astro_resolve_default = createRule("no-deprecated-astro-resolve", { meta: { docs: { description: "disallow using deprecated `Astro.resolve()`", category: "Possible Errors", recommended: true }, schema: [], messages: { deprecated: "'Astro.resolve()' is deprecated." }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } return { "Program:exit"(node) { const tracker = new ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { resolve: { [READ]: true } } })) { context.report({ node: node2, messageId: "deprecated", data: { name: path5.join(".") } }); } } }; } }); } }); // src/rules/no-deprecated-getentrybyslug.ts var no_deprecated_getentrybyslug_default; var init_no_deprecated_getentrybyslug = __esm({ "src/rules/no-deprecated-getentrybyslug.ts"() { init_utils(); init_compat(); no_deprecated_getentrybyslug_default = createRule("no-deprecated-getentrybyslug", { meta: { docs: { description: "disallow using deprecated `getEntryBySlug()`", category: "Possible Errors", recommended: true }, schema: [], messages: { deprecated: "'getEntryBySlug()' is deprecated. Use 'getEntry()' instead." }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } return { ImportSpecifier(node) { if (node.imported.type === "Identifier" && node.imported.name === "getEntryBySlug" && node.parent?.type === "ImportDeclaration" && node.parent.source.value === "astro:content") { context.report({ node, messageId: "deprecated" }); } } }; } }); } }); // src/rules/no-exports-from-components.ts var ALLOWED_EXPORTS, no_exports_from_components_default; var init_no_exports_from_components = __esm({ "src/rules/no-exports-from-components.ts"() { init_utils(); init_compat(); ALLOWED_EXPORTS = /* @__PURE__ */ new Set(["getStaticPaths", "partial", "prerender"]); no_exports_from_components_default = createRule("no-exports-from-components", { meta: { docs: { description: "disallow value export", category: "Possible Errors", // TODO: Switch to recommended: true, in next major version recommended: false }, schema: [], messages: { disallowExport: "Exporting values from components is not allowed." }, type: "problem" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } function verifyDeclaration(node) { if (!node) return; if (node.type.startsWith("TS") && !node.type.endsWith("Expression")) { return; } if (node.type === "FunctionDeclaration" && node.id && ALLOWED_EXPORTS.has(node.id.name) || node.type === "VariableDeclaration" && node.declarations.every( (decl) => decl.id.type === "Identifier" && ALLOWED_EXPORTS.has(decl.id.name) )) { return; } context.report({ node, messageId: "disallowExport" }); } return { ExportAllDeclaration(node) { if (node.exportKind === "type") return; context.report({ node, messageId: "disallowExport" }); }, ExportDefaultDeclaration(node) { if (node.exportKind === "type") return; verifyDeclaration(node.declaration); }, ExportNamedDeclaration(node) { if (node.exportKind === "type") return; verifyDeclaration(node.declaration); for (const spec of node.specifiers) { if (spec.exportKind === "type" || spec.exported.type !== "Identifier") continue; if (ALLOWED_EXPORTS.has(spec.exported.name)) { continue; } context.report({ node: spec, messageId: "disallowExport" }); } } }; } }); } }); // src/rules/no-set-html-directive.ts var no_set_html_directive_default; var init_no_set_html_directive = __esm({ "src/rules/no-set-html-directive.ts"() { init_utils(); init_ast_utils(); init_compat(); no_set_html_directive_default = createRule("no-set-html-directive", { meta: { docs: { description: "disallow use of `set:html` to prevent XSS attack", category: "Security Vulnerability", recommended: false }, schema: [], messages: { unexpected: "`set:html` can lead to XSS attack." }, type: "suggestion" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } function verifyName(attr) { if (getAttributeName(attr) !== "set:html") { return; } context.report({ node: attr.name, messageId: "unexpected" }); } return { JSXAttribute: verifyName, AstroTemplateLiteralAttribute: verifyName }; } }); } }); // src/rules/no-set-text-directive.ts var no_set_text_directive_default; var init_no_set_text_directive = __esm({ "src/rules/no-set-text-directive.ts"() { init_utils(); init_ast_utils(); init_compat(); no_set_text_directive_default = createRule("no-set-text-directive", { meta: { docs: { description: "disallow use of `set:text`", category: "Best Practices", recommended: false }, schema: [], messages: { disallow: "Don't use `set:text`." }, type: "suggestion", fixable: "code" }, create(context) { const sourceCode = getSourceCode(context); if (!sourceCode.parserServices.isAstro) { return {}; } function verifyName(attr) { if (getAttributeName(attr) !== "set:text") { return; } context.report({ node: attr.name, messageId: "disallow", *fix(fixer) { const element = attr.parent.parent; if (!attr.value || !element || element.type !== "JSXElement") { return; } if (element.children.some( (child) => child.type !== "JSXText" || child.value.trim() )) { return; } const valueText = attr.type === "AstroTemplateLiteralAttribute" ? `{${sourceCode.getText(attr.value)}}` : sourceCode.getText(attr.value); if (element.openingElement.selfClosing) { if (sourceCode.text.slice( element.openingElement.range[1] - 2, element.openingElement.range[1] ) !== "/>") { return; } yield fixer.remove(attr); yield fixer.removeRange([ element.openingElement.range[1] - 2, element.openingElement.range[1] - 1 ]); yield fixer.insertTextAfter( element.openingElement, `${valueText}</${sourceCode.getText( element.openingElement.name )}>` ); } else { yield fixer.remove(attr); yield* element.children.map((child) => fixer.remove(child)); yield fixer.insertTextAfter(element.openingElement, valueText); } } }); } return { JSXAttribute: verifyName, AstroTemplateLiteralAttribute: verifyName }; } }); } }); function loadModule(context, name2) { const sourceCode = getSourceCode(context); const key = sourceCode.ast; let modules = cache.get(key); if (!modules) { modules = {}; cache.set(key, modules); } const mod = modules[name2]; if (mod) return mod; try { const cwd = getCwd(context); const relativeTo = path4.join(cwd, "__placeholder__.js"); return modules[name2] = Module.createRequire(relativeTo)(name2); } catch { return null; } } function getContentRange(node) { if (node.closingElement) { return [node.openingElement.range[1], node.closingElement.range[0]]; } return [node.openingElement.range[1], node.range[1]]; } var cache; var init_utils2 = __esm({ "src/utils/transform/utils.ts"() { init_compat(); cache = /* @__PURE__ */ new WeakMap(); } }); function transform(node, context) { const postcssLoadConfig = loadPostcssLoadConfig(context); if (!postcssLoadConfig) { return null; } const inputRange = getContentRange(node); const sourceCode = getSourceCode(context); const code = sourceCode.text.slice(...inputRange); const filename = `${getFilename(context)}.css`; try { const config = postcssLoadConfig.sync({ cwd: getCwd(context) ?? process.cwd(), from: filename }); const result = postcss(config.plugins).process(code, { ...config.options, map: { inline: false } }); return { inputRange, output: result.content, mappings: result.map.toJSON().mappings }; } catch { return null; } } function loadPostcssLoadConfig(context) { return loadModule(context, "postcss-load-config"); } var init_postcss = __esm({ "src/utils/transform/postcss.ts"() { init_utils2(); init_compat(); } }); // src/utils/transform/sass.ts function transform2(node, context, type) { const sass = loadSass(context); if (!sass) { return null; } const inputRange = getContentRange(node); const sourceCode = getSourceCode(context); const code = sourceCode.text.slice(...inputRange); try { const output = sass.compileString(code, { sourceMap: true, syntax: type === "sass" ? "indented" : void 0 }); if (!output) { return null; } return { inputRange, output: output.css, mappings: output.sourceMap.mappings }; } catch { return null; } } function loadSass(context) { return loadModule(context, "sass"); } var init_sass = __esm({ "src/utils/transform/sass.ts"() { init_utils2(); init_compat(); } }); // src/utils/transform/less.ts function transform3(node, context) { const less = loadLess(context); if (!less) { return null; } const inputRange = getContentRange(node); const sourceCode = getSourceCode(context); const code = sourceCode.text.slice(...inputRange); const filename = `${getFilename(context)}.less`; try { let output; less.render( code, { sourceMap: {}, syncImport: true, filename, lint: false }, (_error, result) => { output = result; } ); if (!output) { return null; } return { inputRange, output: output.css, mappings: JSON.parse(output.map).mappings }; } catch { return null; } } function loadLess(context) { return loadModule(context, "less"); } var init_less = __esm({ "src/utils/transform/less.ts"() { init_utils2(); init_compat(); } }); // src/utils/transform/stylus.ts function transform4(node, context) { const stylus = loadStylus(context); if (!stylus) { return null; } const inputRange = getContentRange(node); const sourceCode = getSourceCode(context); const code = sourceCode.text.slice(...inputRange); const filename = `${getFilename(context)}.stylus`; try { let output; const style = stylus(code, { filename }).set("sourcemap", {}); style.render((_error, outputCode) => { output = outputCode; }); if (output == null) { return null; } return { inputRange, output, mappings: style.sourcemap.mappings }; } catch { return null; } } function loadStylus(context) { return loadModule(context, "stylus"); } var init_stylus = __esm({ "src/utils/transform/stylus.ts"() { init_utils2(); init_compat(); } }); // src/utils/transform/lines-and-columns.ts function sortedLastIndex(array, value) { let lower = 0; let upper = array.length; while (lower < upper) { const mid = Math.floor(lower + (upper - lower) / 2); const target = array[mid]; if (target < value) { lower = mid + 1; } else if (target > value) { upper = mid; } else { return mid + 1; } } return upper; } var LinesAndColumns; var init_lines_and_columns = __esm({ "src/utils/transform/lines-and-columns.ts"() { LinesAndColumns = class { constructor(code) { const len = code.length; const lineStartIndices = [0]; for (let index = 0; index < len; index++) { const c = code[index]; if (c === "\r") { const next = code[index + 1] || ""; if (next === "\n") { index++; } lineStartIndices.push(index + 1); } else if (c === "\n") { lineStartIndices.push(index + 1); } } this.code = code; this.lineStartIndices = lineStartIndices; } getLocFromIndex(index) { const lineNumber = sortedLastIndex(this.lineStartIndices, index); return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] }; } getIndexFromLoc(loc) { const lineIndex = loc.line - 1; if (this.lineStartIndices.length > lineIndex) { const lineStartIndex = this.lineStartIndices[lineIndex]; const positionIndex = lineStartIndex + loc.column; return positionIndex; } else if (this.lineStartIndices.length === lineIndex) { return this.code.length + loc.column; } return this.code.length + loc.column; } }; } }); function getStyleContentCSS(node, context) { const cachedResult = cache2.get(node); if (cachedResult) { return cachedResult; } const sourceCode = getSourceCode(context); const langNode = findAttribute(node, "lang"); const lang = langNode && getStaticAttributeStringValue(langNode); if (!langNode || lang === "css") { const inputRange = getContentRange(node); return { css: sourceCode.text.slice(...inputRange), remap: (i) => inputRange[0] + i }; } let transform5 = null; if (lang === "postcss") { transform5 = transform(node, context); } else if (lang === "scss" || lang === "sass") { transform5 = transform2(node, context, lang); } else if (lang === "less") { transform5 = transform3(node, context); } else if (lang === "styl" || lang === "stylus") { transform5 = transform4(node, context); } if (!transform5) { return null; } const result = transformToStyleContentCSS(transform5, context); cache2.set(node, result); return result; } function transformToStyleContentCSS(transform5, context) { const sourceCode = getSourceCode(context); let outputLocs = null; let inputLocs = null; let decoded = null; return { css: transform5.output, remap: (index) => { outputLocs = outputLocs ?? new LinesAndColumns(transform5.output); inputLocs = inputLocs ?? new LinesAndColumns(sourceCode.text.slice(...transform5.inputRange)); const outputCodePos = outputLocs.getLocFromIndex(index); const inputCodePos = remapPosition(outputCodePos); return inputLocs.getIndexFromLoc(inputCodePos) + transform5.inputRange[0]; } }; function remapPosition(pos) { decoded = decoded ?? decode(transform5.mappings); const lineMaps = decoded[pos.line - 1]; if (!lineMaps?.length) { for (let line = pos.line - 1; line >= 0; line--) { const prevLineMaps = decoded[line]; if (prevLineMaps?.length) { const [, , sourceCodeLine2, sourceCodeColumn2] = prevLineMaps[prevLineMaps.length - 1]; return { line: sourceCodeLine2 + 1, column: sourceCodeColumn2 }; } } return { line: -1, column: -1 }; } for (let index = 0; index < lineMaps.length - 1; index++) { const [generateCodeColumn2, , sourceCodeLine2, sourceCodeColumn2] = lineMaps[index]; if (generateCodeColumn2 <= pos.column && pos.column < lineMaps[index + 1][0]) { return { line: sourceCodeLine2 + 1, column: sourceCodeColumn2 + (pos.column - generateCodeColumn2) }; } } const [generateCodeColumn, , sourceCodeLine, sourceCodeColumn] = lineMaps[lineMaps.length - 1]; return { line: sourceCodeLine + 1, column: sourceCodeColumn + (pos.column - generateCodeColumn) }; } } var cache2; var init_transform = __esm({ "src/utils/transform/index.ts"() { init_ast_utils(); init_utils2(); init_postcss(); init_sass(); init_less(); init_stylus(); init_lines_and_columns(); init_compat(); cache2 = /* @__PURE__ */ new WeakMap(); } }); function parseSelector(selector, context) { let astSelector; try { astSelector = parser().astSync(selector); } catch (error) { return [ { error, selector, offset: 0, test: () => false } ]; } return astSelector.nodes.map((sel) => { const nodes = removeGlobals(cleanSelectorChildren(sel)); try { const test = selectorToJSXElementMatcher(nodes, context); return { selector: sel.toString().trim(), offset: sel.sourceIndex ?? sel.nodes[0].sourceIndex, test(element) { return test(element, null); } }; } catch (error) { if (error instanceof SelectorError) { return { error, selector: sel.toString().trim(), offset: sel.sourceIndex ?? sel.nodes[0].sourceIndex, test: () => false }; } throw error; } }); function removeGlobals(nodes) { let start = 0; let end = nodes.length; while (nodes[end - 1] && isGlobalPseudo(nodes[end - 1])) { end--; if (nodes[end - 1]?.type === "combinator") { end--; } } while (nodes[start] && isGlobalPseudo(nodes[start])) { start++; if (nodes[start]?.type === "combinator") { start++; } } if (nodes.some(isRootPseudo)) { while (nodes[start] && !isRootPseudo(nodes[start])) { start++; } start++; while (nodes[start] && nodes[start].type !== "combinator") { start++; } if (nodes[start]?.type === "combinator") { start++; } } return nodes.slice(start, end); } } function selectorsToJSXElementMatcher(selectorNodes, context) { const selectors = selectorNodes.map( (n) => selectorToJSXElementMatcher(cleanSelectorChildren(n), context) ); return (element, subject) => selectors.some((sel) => sel(element, subject)); } function isDescendantCombinator(node) { return Boolean(node && node.type === "combinator" && !node.value.trim()); } function cleanSelectorChildren(selector) { const nodes = []; let last = null; for (const node of selector.nodes) { if (node.type === "root") { throw new SelectorError("Unexpected state type=root"); } if (node.type === "comment") { continue; } if ((last == null || last.type === "combinator") && isDescendantCombinator(node)) { continue; } if (isDescendantCombinator(last) && node.type === "combinator") { nodes.pop(); } nodes.push(node); last = node; } if (isDescendantCombinator(last)) { nodes.pop(); } return nodes; } function selectorToJSXElementMatcher(selectorChildren, context) { const nodes = [...selectorChildren]; let node = nodes.shift(); let result = null; while (node) { if (node.type === "combinator") { const combinator = node.value; node = nodes.shift(); if (!node) { throw new SelectorError(`Expected selector after '${combinator}'.`); } if (node.type === "combinator") { throw new SelectorError(`Unexpected combinator '${node.value}'.`); } const right = nodeToJSXElementMatcher(node, context); result = combination( result || // for :has() ((element, subject) => element === subject), combinator, right ); } else { const sel = nodeToJSXElementMatcher(node, context); result = result ? compound(result, sel) : sel; } node = nodes.shift(); } if (!result) { return () => true; } return result; } function combination(left, combinator, right) { switch (combinator.trim()) { case "": return (element, subject) => { if (right(element, null)) { let parent = element.parent; while (parent.node) { if (left(parent, subject)) { return true; } parent = parent.parent; } } return false; }; case ">": return (element, subject) => { if (right(element, null)) { const parent = element.parent; if (parent.node) { return left(parent, subject); } } return false; }; case "+": return (element, subject) => { if (right(element, null)) { const before = getBeforeElement(element); if (before) { return left(before, subject); } } return false; }; case "~": return (element, subject) => { if (right(element, null)) { for (const before of getBeforeElements(element)) { if (left(before, subject)) { return true; } } } return false; }; default: throw new SelectorError(`Unknown combinator: ${combinator}.`); } } function nodeToJSXElementMatcher(selector, context) { const baseMatcher = (() => { switch (selector.type) { case "attribute": return attributeNodeToJSXElementMatcher(selector, context); case "class": return classNameNodeToJSXElementMatcher(selector, context); case "id": return identifierNodeToJSXElementMatcher(selector, context); case "tag": return tagNodeToJSXElementMatcher(selector); case "universal": return universalNodeToJSXElementMatcher(); case "pseudo": return pseudoNodeToJSXElementMatcher(selector, context); case "nesting": throw new SelectorError("Unsupported nesting selector."); case "string": throw new SelectorError(`Unknown selector: ${selector.value}.`); default: throw new SelectorError(`Unknown selector: ${selector.value}.`); } })(); return (element, subject) => { if (isComponentElement(element)) { return false; } return baseMatcher(element, subject); }; } function attributeNodeToJSXElementMatcher(selector, context) { const key = selector.attribute; if (!selector.operator) { return (element, _) => { return hasAttribute(element, key, context); }; } const value = selector.value || ""; switch (selector.operator) { case "=": return buildJSXElementMatcher(value, (attr, val) => attr === val); case "~=": return buildJSXElementMatcher( value, (attr, val) => attr.split(/\s+/u).includes(val) ); case "|=": return buildJSXElementMatcher( value, (attr, val) => attr === val || attr.startsWith(`${val}-`) ); case "^=": return buildJSXElementMatcher(value, (attr, val) => attr.startsWith(val)); case "$=": return buildJSXElementMatcher(value, (attr, val) => attr.endsWith(val)); case "*=": return buildJSXElementMatcher(value, (attr, val) => attr.includes(val)); default: throw new SelectorError(`Unsupported operator: ${selector.operator}.`); } function buildJSXElementMatcher(selectorValue, test) { const val = selector.insensitive ? selectorValue.toLowerCase() : selectorValue; return (element) => { const attr = getAttribute(element, key, context); if (attr == null) { return false; } if (attr.unknown || !attr.staticValue) { return true; } const attrValue = attr.staticValue.value; return test( selector.insensitive ? attrValue.toLowerCase() : attrValue, val ); }; } } function classNameNodeToJSXElementMatcher(selector, context) { const className = selector.value; return (element) => { const attr = getAttribute(element, "class", context); if (attr == null) { return false; } if (attr.unknown || !attr.staticValue) { return true; } const attrValue = attr.staticValue.value; return attrValue.split(/\s+/u).includes(className); }; } function identifierNodeToJSXElementMatcher(selector, context) { const id = selector.value; return (element) => { const attr = getAttribute(element, "id", context); if (attr == null) { return false; } if (attr.unknown || !attr.staticValue) { return true; } const attrValue = attr.staticValue.value; return attrValue === id; }; } function tagNodeToJSXElementMatcher(selector) { const name2 = selector.value; return (element) => { const elementName = getElementName(element.node); return elementName === name2; }; } function universalNodeToJSXElementMatcher(_selector) { return () => true; } function pseudoNodeToJSXElementMatcher(selector, context) { const pseudo = selector.value; switch (pseudo) { case ":is": case ":where": return selectorsToJSXElementMatcher(selector.nodes, context); case ":has": return pseudoHasSelectorsToJSXElementMatcher(selector.nodes, context); case ":empty": return (element) => element.node.children.every( (child) => child.type === "JSXText" && !child.value.trim() || child.type === "AstroHTMLComment" ); // https://docs.astro.build/en/guides/styling/#global-styles case ":global": { return () => true; } default: return () => true; } } function pseudoHasSelectorsToJSXElementMatcher(selectorNodes, context) { const selectors = selectorNodes.map( (n) => pseudoHasSelectorToJSXElementMatcher(n, context) ); return (element, subject) => selectors.some((sel) => sel(element, subject)); } function pseudoHasSelectorToJSXElementMatcher(selector, context) { const nodes = cleanSelectorChildren(selector); const selectors = selectorToJSXElementMatcher(nodes, context); const firstNode = nodes[0]; if (firstNode.type === "combinator" && (firstNode.value === "+" || firstNode.value === "~")) { return buildJSXElementMatcher((element) => getAfterElements(element)); } return buildJSXElementMatcher((element) => element.childElements); function buildJSXElementMatcher(getStartElements) { return (element) => { const elements = [...getStartElements(element)]; let curr; while (curr = elements.shift()) { const el = curr; if (selectors(el, element)) { return true; } elements.push(...el.childElements); } return false; }; } } function getBeforeElement(element) { return getBeforeElements(element).pop() || null; } function getBeforeElements(element) { const parent = element.parent; if (!parent) { return []; } const index = parent.childElements.indexOf(element); return parent.childElements.slice( 0, element.withinExpression ? index + 1 : index ); } function getAfterElements(element) { const parent = element.parent; if (!parent) { return []; } const index = parent.childElements.indexOf(element); return parent.childElements.slice( element.withinExpression ? index : index + 1 ); } function compound(a, b) { return (element, subject) => a(element, subject) && b(element, subject); } function isComponentElement(element) { const elementName = getElementName(element.node); return elementName == null || elementName.toLowerCase() !== elementName; } function isGlobalPseudo(node) { return node.type === "pseudo" && node.value === ":global"; } function isRootPseudo(node) { return node.type === "pseudo" && node.value === ":root"; } function hasAttribute(element, attribute, context) { const attr = getAttribute(element, attribute, context); if (attr) { return true; } return false; } function getAttribute(element, attribute, context) { const attr = findAttribute(element.node, attribute); if (attr) { if (attr.value == null) { return { unknown: false, hasAttr: true, staticValue: { value: "" } }; } const value = getStaticAttributeStringValue(attr, context); if (value == null) { return { unknown: false, hasAttr: true, staticValue: null }; } return { unknown: false, hasAttr: true, staticValue: { value } }; } if (attribute === "class") { const result = getClassListAttribute(element, context); if (result) { return result; } } const spreadAttributes = getSpreadAttributes(element.node); if (spreadAttributes.length === 0) { return null; } return { unknown: true }; } function getClassListAttribute(element, context) { const attr = findAttribute(element.node, "class:list"); if (attr) { if (attr.value == null) { return { unknown: false, hasAttr: true, staticValue: { value: "" } }; } const classList = extractClassList(attr, context); if (classList === null) { return { unknown: false, hasAttr: true, staticValue: null }; } return { unknown: false, hasAttr: true, staticValue: { value: classList.classList.join(" ") } }; } return null; } function extractClassList(node, context) { if (node.value?.type === AST_NODE_TYPES.Literal) { return { classList: [String(node.value.value)] }; } if (node.value?.type === "JSXExpressionContainer" && node.value.expression.type !== "JSXEmptyExpression") { const classList = []; for (const className of extractClassListFromExpression( node.value.expression, context )) { if (className == null) { ret