UNPKG

eslint-plugin-astro

Version:
1,631 lines (1,613 loc) 164 kB
'use strict'; var Module = require('module'); var path4 = require('path'); var eslintCompatUtils = require('eslint-compat-utils'); var types = require('@typescript-eslint/types'); var eslintUtils = require('@eslint-community/eslint-utils'); var postcss = require('postcss'); var sourcemapCodec = require('@jridgewell/sourcemap-codec'); var parser = require('postcss-selector-parser'); var parser2 = require('astro-eslint-parser'); var globals = require('globals'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var Module__default = /*#__PURE__*/_interopDefault(Module); var path4__default = /*#__PURE__*/_interopDefault(path4); var postcss__default = /*#__PURE__*/_interopDefault(postcss); var parser__default = /*#__PURE__*/_interopDefault(parser); var parser2__namespace = /*#__PURE__*/_interopNamespace(parser2); var globals__default = /*#__PURE__*/_interopDefault(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 __commonJS = (cb, mod) => function __require2() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all3) => { for (var name2 in all3) __defProp(target, name2, { get: all3[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__default.default.join(cwd, "__placeholder__.js"); if (tsESLintParser = Module.createRequire(relativeTo)("@typescript-eslint/parser")) hasTypescriptEslintParser = true; } catch { } } }); // src/configs/base.ts function buildLegacyBase() { return { plugins: ["astro"], overrides: [ { // Define the configuration for `.astro` file. files: ["*.astro"], // Enable this plugin plugins: ["astro"], env: { // Enables global variables available in Astro components. node: true, "astro/astro": true, es2020: true }, // Allows Astro components to be parsed. parser: __require.resolve("astro-eslint-parser"), // Parse the script in `.astro` as TypeScript by adding the following configuration. parserOptions: { parser: hasTypescriptEslintParser ? "@typescript-eslint/parser" : void 0, extraFileExtensions: [".astro"], // The script of Astro components uses ESM. sourceType: "module" }, rules: { // eslint-plugin-astro rules // Enable base rules } }, { // Define the configuration for `<script>` tag. // Script in `<script>` is assigned a virtual file name with the `.js` extension. files: ["**/*.astro/*.js", "*.astro/*.js"], env: { browser: true, es2020: true }, parserOptions: { sourceType: "module" }, rules: { // If you are using "prettier/prettier" rule, // you don't need to format inside <script> as it will be formatted as a `.astro` file. "prettier/prettier": "off" } }, { // Define the configuration for `<script>` tag when using `client-side-ts` processor. // Script in `<script>` is assigned a virtual file name with the `.ts` extension. files: ["**/*.astro/*.ts", "*.astro/*.ts"], env: { browser: true, es2020: true }, parser: hasTypescriptEslintParser ? "@typescript-eslint/parser" : void 0, parserOptions: { sourceType: "module", project: null }, rules: { // If you are using "prettier/prettier" rule, // you don't need to format inside <script> as it will be formatted as a `.astro` file. "prettier/prettier": "off" } } ] }; } var init_base = __esm({ "src/configs/base.ts"() { init_has_typescript_eslint_parser(); } }); // src/configs/recommended.ts var baseExtend, recommended_default; var init_recommended = __esm({ "src/configs/recommended.ts"() { baseExtend = "plugin:astro/base"; recommended_default = { extends: [baseExtend], rules: { // eslint-plugin-astro rules "astro/missing-client-only-directive-value": "error", "astro/no-conflict-set-directives": "error", "astro/no-deprecated-astro-canonicalurl": "error", "astro/no-deprecated-astro-fetchcontent": "error", "astro/no-deprecated-astro-resolve": "error", "astro/no-deprecated-getentrybyslug": "error", "astro/no-unused-define-vars-in-style": "error", "astro/valid-compile": "error" } }; } }); // 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 eslintCompatUtils.getSourceCode(context); } function getFilename(context) { return eslintCompatUtils.getFilename(context); } function getCwd(context) { return eslintCompatUtils.getCwd(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 === types.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 = eslintUtils.getStaticValue( node.value.expression, sourceCode.scopeManager.globalScope ); if (staticValue != null) { return staticValue; } } return null; } function isStringCallExpression(node) { if (node.type === types.AST_NODE_TYPES.CallExpression) { return node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === "String"; } return false; } function isStringLiteral(node) { return node.type === types.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 === types.AST_NODE_TYPES.BinaryExpression) { if (!eslintUtils.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)) && eslintUtils.isOpeningParenToken(maybeLeftParen) && eslintUtils.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: eslintUtils.isOpeningParenToken }); } return null; case "DoWhileStatement": if (parent.test === node) { return sourceCode.getTokenAfter(parent.body, { includeComments: false, filter: eslintUtils.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 eslintUtils.ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { canonicalURL: { [eslintUtils.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 eslintUtils.ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { fetchContent: { [eslintUtils.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 eslintUtils.ReferenceTracker(sourceCode.getScope(node)); for (const { node: node2, path: path5 } of tracker.iterateGlobalReferences({ Astro: { resolve: { [eslintUtils.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__default.default.join(cwd, "__placeholder__.js"); return modules[name2] = Module__default.default.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__default.default(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 ?? sourcemapCodec.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__default.default().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 pseudoHasSelectorToJSXEle