123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- 'use strict';
- const atRuleParamIndex = require('../../utils/atRuleParamIndex');
- const declarationValueIndex = require('../../utils/declarationValueIndex');
- const isStandardSyntaxSelector = require('../../utils/isStandardSyntaxSelector');
- const parseSelector = require('../../utils/parseSelector');
- const report = require('../../utils/report');
- const ruleMessages = require('../../utils/ruleMessages');
- const validateOptions = require('../../utils/validateOptions');
- const valueParser = require('postcss-value-parser');
- const ruleName = 'string-no-newline';
- const reNewLine = /\r?\n/;
- const messages = ruleMessages(ruleName, {
- rejected: 'Unexpected newline in string',
- });
- const meta = {
- url: 'https://stylelint.io/user-guide/rules/string-no-newline',
- };
- /** @type {import('stylelint').Rule} */
- const rule = (primary) => {
- return (root, result) => {
- const validOptions = validateOptions(result, ruleName, { actual: primary });
- if (!validOptions) {
- return;
- }
- root.walk((node) => {
- switch (node.type) {
- case 'atrule':
- checkDeclOrAtRule(node, node.params, atRuleParamIndex);
- break;
- case 'decl':
- checkDeclOrAtRule(node, node.value, declarationValueIndex);
- break;
- case 'rule':
- checkRule(node);
- break;
- }
- });
- /**
- * @param {import('postcss').Rule} ruleNode
- * @returns {void}
- */
- function checkRule(ruleNode) {
- // Get out quickly if there are no new line
- if (!reNewLine.test(ruleNode.selector)) {
- return;
- }
- if (!isStandardSyntaxSelector(ruleNode.selector)) {
- return;
- }
- parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => {
- selectorTree.walkAttributes((attributeNode) => {
- const { value, quoteMark } = attributeNode;
- if (!value || !reNewLine.test(value)) {
- return;
- }
- const openIndex = [
- // length of our attribute
- attributeNode.attribute,
- // length of our operator , ie '='
- attributeNode.operator || '',
- ].reduce(
- (index, str) => index + str.length,
- // index of the start of our attribute node in our source
- // plus 1 for the opening quotation mark
- attributeNode.sourceIndex + 1,
- );
- const valueLength = value.length + (quoteMark || '').length * 2;
- report({
- message: messages.rejected,
- node: ruleNode,
- index: openIndex,
- endIndex: openIndex + valueLength,
- result,
- ruleName,
- });
- });
- });
- }
- /**
- * @template {import('postcss').AtRule | import('postcss').Declaration} T
- * @param {T} node
- * @param {string} value
- * @param {(node: T) => number} getIndex
- * @returns {void}
- */
- function checkDeclOrAtRule(node, value, getIndex) {
- // Get out quickly if there are no new line
- if (!reNewLine.test(value)) {
- return;
- }
- valueParser(value).walk((valueNode) => {
- if (valueNode.type !== 'string') {
- return;
- }
- if (!reNewLine.test(valueNode.value)) {
- return;
- }
- const nodeIndex = getIndex(node);
- report({
- message: messages.rejected,
- node,
- index: nodeIndex + valueNode.sourceIndex,
- endIndex: nodeIndex + valueNode.sourceEndIndex,
- result,
- ruleName,
- });
- });
- }
- };
- };
- rule.ruleName = ruleName;
- rule.messages = messages;
- rule.meta = meta;
- module.exports = rule;
|