eslint-plugin-astro
Version:
ESLint plugin for Astro component
1,631 lines (1,613 loc) • 164 kB
JavaScript
'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