Socket
Socket
Sign inDemoInstall

@asvetliakov/stylelint-processor-styled-components

Package Overview
Dependencies
3
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.1 to 1.5.0

.github/ISSUE_TEMPLATE.md

0

.eslintrc.js

@@ -0,0 +0,0 @@ module.exports = {

@@ -0,1 +1,42 @@

### 1.5.0
* fix line number is undefined if source is empty
* support new option `parserPlugins`, which allows to specify custom babel plugins to parse
### 1.4.0
* upgrade babylon parser to @babel/parser
* able to show parse errors to users
* support new option `importName`, which allows to import wrapper components object from other names
* filter redundant warnings introduced by v1.2.0
* more intelligent guessing for interpolations
### 1.3.2
* added support for optional chaining to our babylon parser
* various minor development improvements that shouldn't affect the product
### 1.3.1
* FIX: Interpolations following multiline comments no longer incorrectly error
* README UPDATE: Recommend the recommended instead of standard syntax and no longer recommend the syntax config option for stylelint
### 1.3.0
* FIX: Moved to using babylon's new parser that handles typescript, this will fix the problems we had with interpolation tags not being useable in Typescript
* FIX: We now properly handle applying sourcemaps to CSS syntax errors and displaying correct line numbers for errors, Stylelint v9+ is required for this fix though as the biggest part of the fix was on their end
* Stylelint v9+ was added as a peerdependency due to above reasons, no promises are made with this version if you do not update your Stylelint version
### 1.2.2
* FIX: Correctly remove base indentation from injectGlobal when using tabs
### 1.2.1
* FIX: Don't throw errors on using processor without filename (with Node.js API / CLI)
### 1.2.0
* Substitute multiline interpolations with relevant multiline substitutions so line information about errors should now be correctly displayed
### 1.1.1
* FIX: Also catch errors regarding resolving absolute path of file and avoid them bubbling up to Stylelint
### 1.1.0
* Add support for Node v4
* Catch errors to avoid them bubbling up to Stylelint (which broke the Stylelint plugin for VSCode)
* upgrade typescript-eslint-parser to 9.0.0
### 1.0.0

@@ -2,0 +43,0 @@ * added support for interpolation tagging to take care of all interpolation edge cases

133

lib/index.js

@@ -1,43 +0,118 @@

var path = require('path');
"use strict";
var parse = require('./parsers/index');
const path = require('path');
var sourceMapsCorrections = {};
var DEFAULT_OPTIONS = {
moduleName: 'styled-components'
const parse = require('./parsers/index');
let inputId = 1;
const interpolationLinesMap = {};
const sourceMapsCorrections = {};
const errorWasThrown = {};
const DEFAULT_OPTIONS = {
moduleName: 'styled-components',
importName: 'default'
};
module.exports = function (options) {
return {
// Get string for stylelint to lint
code(input, filepath) {
var absolutePath = path.resolve(process.cwd(), filepath);
function isCausedBySubstitution(warning, line, interpolationLines) {
return interpolationLines.some(({
start,
end
}) => {
if (line > start && line < end) {
// Inner interpolation lines must be
return true;
} else if (line === start) {
return ['value-list-max-empty-lines', 'comment-empty-line-before'].indexOf(warning.rule) >= 0;
} else if (line === end) {
return ['comment-empty-line-before', 'indentation'].indexOf(warning.rule) >= 0;
} else {
return false;
}
});
}
module.exports = options => ({
// Get string for stylelint to lint
code(input, filepath) {
let absolutePath;
if (filepath) {
absolutePath = path.resolve(process.cwd(), filepath);
} else {
absolutePath = `<input css ${inputId}>`;
inputId += 1;
}
try {
sourceMapsCorrections[absolutePath] = {};
var _parse = parse(input, absolutePath, Object.assign({}, DEFAULT_OPTIONS, options)),
extractedCSS = _parse.extractedCSS,
sourceMap = _parse.sourceMap; // Save source location, merging existing corrections with current corrections
const _parse = parse(input, absolutePath, Object.assign({}, DEFAULT_OPTIONS, options)),
extractedCSS = _parse.extractedCSS,
interpolationLines = _parse.interpolationLines,
sourceMap = _parse.sourceMap; // Save dummy interpolation lines
interpolationLinesMap[absolutePath] = interpolationLines.concat(interpolationLinesMap[absolutePath] || []); // Save source location, merging existing corrections with current corrections
sourceMapsCorrections[absolutePath] = Object.assign(sourceMapsCorrections[absolutePath], sourceMap);
delete errorWasThrown[absolutePath];
return extractedCSS;
},
} catch (e) {
// Always save the error
errorWasThrown[absolutePath] = e; // Incorrect interpolations will throw CssSyntaxError and they'll be handled by stylelint
// so we can throw it out but not for others
// Fix sourcemaps
result(stylelintResult, filepath) {
var lineCorrection = sourceMapsCorrections[filepath];
var warnings = stylelintResult.warnings.map(function (warning) {
return Object.assign({}, warning, {
// Replace "brace" with "backtick" in warnings, e.g.
// "Unexpected empty line before closing backtick" (instead of "brace")
text: warning.text.replace(/brace/, 'backtick'),
line: lineCorrection[warning.line]
if (e.name === 'CssSyntaxError') {
throw e;
}
return '';
}
},
// Fix sourcemaps
result(stylelintResult, filepath) {
const err = errorWasThrown[filepath];
if (err) {
if (err.name === 'CssSyntaxError') {
// We threw an error ourselves, in this case we have already put correct
// line/column numbers so no source maps are needed
// (and would actually break the line numbers)
return stylelintResult;
} else {
// For other errors, wrap them into the result
return Object.assign({}, stylelintResult, {
errored: true,
parseErrors: [{
line: err.loc && err.loc.line,
column: err.loc && err.loc.column,
rule: 'parseError',
severity: 'error',
text: `${err.message}`
}]
});
});
return Object.assign({}, stylelintResult, {
warnings
});
}
}
};
};
const interpolationLines = interpolationLinesMap[filepath] || [];
const lineCorrection = sourceMapsCorrections[filepath];
const warnings = stylelintResult.warnings.filter(warning => // Filter false-positive warnings generated by interpolations substitution
!isCausedBySubstitution(warning, lineCorrection[warning.line], interpolationLines)).map(warning => Object.assign({}, warning, {
// Replace "brace" with "backtick" in warnings, e.g.
// "Unexpected empty line before closing backtick" (instead of "brace")
text: warning.text.replace(/brace/, 'backtick'),
line: lineCorrection[warning.line] || warning.line
}));
const result = Object.assign({}, stylelintResult, {
warnings
}); // Undo `errored` if no warnings with error severity any more
if (result.errored && !warnings.some(warning => warning.severity === 'error')) {
delete result.errored;
}
return result;
}
});

@@ -1,8 +0,10 @@

var babylon = require('babylon');
"use strict";
module.exports = function (input) {
return babylon.parse(input, {
sourceType: 'module',
plugins: ['jsx', 'flow', 'objectRestSpread', 'decorators', 'classProperties', 'exportExtensions', 'asyncGenerators', 'functionBind', 'functionSent', 'dynamicImport']
});
};
const babylon = require('@babel/parser');
module.exports = (type, plugins) => input => babylon.parse(input, {
sourceType: 'module',
plugins: [type].concat(plugins || ['jsx', 'objectRestSpread', ['decorators', {
decoratorsBeforeExport: true
}], 'classProperties', 'exportExtensions', 'asyncGenerators', 'functionBind', 'functionSent', 'dynamicImport', 'optionalCatchBinding', 'optionalChaining'])
});

@@ -1,53 +0,55 @@

var flowParser = require('./create-parser')('flow');
"use strict";
var tsParser = require('./create-parser')('typescript');
const estreeParse = require('./babylon-parser');
var traverse = require('babel-traverse').default;
const traverse = require('@babel/traverse').default;
var isStyled = require('../utils/styled').isStyled;
const isStyled = require('../utils/styled').isStyled;
var isHelper = require('../utils/styled').isHelper;
const isHelper = require('../utils/styled').isHelper;
var isStyledImport = require('../utils/styled').isStyledImport;
const isStyledImport = require('../utils/styled').isStyledImport;
var hasAttrsCall = require('../utils/styled').hasAttrsCall;
const hasAttrsCall = require('../utils/styled').hasAttrsCall;
var getAttrsObject = require('../utils/styled').getAttrsObject;
const getAttrsObject = require('../utils/styled').getAttrsObject;
var isExtendCall = require('../utils/styled').isExtendCall;
const isExtendCall = require('../utils/styled').isExtendCall;
var wrapSelector = require('../utils/general').wrapSelector;
const wrapSelector = require('../utils/general').wrapSelector;
var wrapKeyframes = require('../utils/general').wrapKeyframes;
const wrapKeyframes = require('../utils/general').wrapKeyframes;
var fixIndentation = require('../utils/general').fixIndentation;
const fixIndentation = require('../utils/general').fixIndentation;
var removeBaseIndentation = require('../utils/general').removeBaseIndentation;
const removeBaseIndentation = require('../utils/general').removeBaseIndentation;
var isStylelintComment = require('../utils/general').isStylelintComment;
const isStylelintComment = require('../utils/general').isStylelintComment;
var getTTLContent = require('../utils/tagged-template-literal.js').getTaggedTemplateLiteralContent;
const getTTLContent = require('../utils/tagged-template-literal.js').getTaggedTemplateLiteralContent;
var parseImports = require('../utils/parse').parseImports;
const parseImports = require('../utils/parse').parseImports;
var getSourceMap = require('../utils/parse').getSourceMap;
const getSourceMap = require('../utils/parse').getSourceMap;
var processStyledComponentsFile = function processStyledComponentsFile(ast, absolutePath, options) {
var extractedCSS = [];
var ignoreRuleComments = [];
var importedNames = {
const processStyledComponentsFile = (ast, absolutePath, options) => {
const extractedCSS = [];
let ignoreRuleComments = [];
let importedNames = {
default: 'styled',
css: 'css',
keyframes: 'keyframes',
injectGlobal: 'injectGlobal'
injectGlobal: 'injectGlobal',
createGlobalStyle: 'createGlobalStyle'
};
var sourceMap = {};
let sourceMap = {};
const interpolationLines = [];
traverse(ast, {
noScope: true,
enter(_ref) {
var node = _ref.node;
enter({
node
}) {
if (node.type !== 'Program' && node.leadingComments) {
node.leadingComments.forEach(function (comment) {
node.leadingComments.forEach(comment => {
if (isStylelintComment(comment.value)) {

@@ -64,4 +66,4 @@ ignoreRuleComments.push(`/*${comment.value}*/`);

var helper = isHelper(node, importedNames);
var processedNode = Object.assign({}, node);
const helper = isHelper(node, importedNames);
const processedNode = Object.assign({}, node);

@@ -72,9 +74,9 @@ if (hasAttrsCall(node)) {

if (!helper && !isStyled(processedNode, importedNames.default) && !isExtendCall(node)) {
if (!helper && !isStyled(processedNode, importedNames[options.importName]) && !isExtendCall(node)) {
return;
}
var content = getTTLContent(node, absolutePath);
var fixedContent = fixIndentation(content).text;
var wrappedContent;
const content = getTTLContent(node, absolutePath);
const fixedContent = fixIndentation(content).text;
let wrappedContent;

@@ -98,5 +100,12 @@ switch (helper) {

var stylelintCommentsAdded = ignoreRuleComments.length > 0 ? `${ignoreRuleComments.join('\n')}\n${wrappedContent}` : wrappedContent;
const stylelintCommentsAdded = ignoreRuleComments.length > 0 ? `${ignoreRuleComments.join('\n')}\n${wrappedContent}` : wrappedContent;
extractedCSS.push(stylelintCommentsAdded);
sourceMap = Object.assign(sourceMap, getSourceMap(extractedCSS.join('\n'), wrappedContent, processedNode.loc.start.line));
sourceMap = Object.assign(sourceMap, getSourceMap(extractedCSS.join('\n'), wrappedContent, processedNode.quasi.loc.start.line)); // Save dummy interpolation lines
node.quasi.expressions.forEach((expression, i) => {
interpolationLines.push({
start: node.quasi.quasis[i].loc.end.line,
end: node.quasi.quasis[i + 1].loc.start.line
});
});
/**

@@ -113,2 +122,3 @@ * All queued comments have been added to the file so we don't need to, and actually shouldn't

extractedCSS: extractedCSS.join('\n'),
interpolationLines,
sourceMap

@@ -118,12 +128,6 @@ };

module.exports = function (input, absolutePath, options) {
var ast = null;
if (absolutePath.endsWith('.ts') || absolutePath.endsWith('.tsx')) {
ast = tsParser(input);
} else {
ast = flowParser(input);
}
module.exports = (input, absolutePath, options) => {
const typedParser = absolutePath.endsWith('.ts') || absolutePath.endsWith('.tsx') ? 'typescript' : 'flow';
const ast = estreeParse(typedParser, options.parserPlugins)(input);
return processStyledComponentsFile(ast, absolutePath, options);
};

@@ -1,5 +0,7 @@

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
"use strict";
// selector count
var count = 0;
const CssError = require('postcss/lib/css-syntax-error'); // selector count
let count = 0;
/**

@@ -11,5 +13,5 @@ * Based on https://github.com/mapbox/stylelint-processor-markdown

var fixIndentation = function fixIndentation(str) {
const fixIndentation = str => {
// Get whitespaces
var match = str.match(/^[ \t]*(?=\S|$)/gm); // Handle oneline TTL case and empty line etc.
const match = str.match(/^[ \t]*(?=\S|$)/gm); // Handle oneline TTL case and empty line etc.

@@ -24,8 +26,8 @@ if (!match || match.length <= 1) {

var baseIndentationLength = match[match.length - 1].length; // Remove whitespace on empty lines before reindenting
const baseIndentationLength = match[match.length - 1].length; // Remove whitespace on empty lines before reindenting
var emptyLinesHandled = str.replace(/^[ \t]$/gm, ''); // Normalize indentation by removing common indent
const emptyLinesHandled = str.replace(/^[ \t]$/gm, ''); // Normalize indentation by removing common indent
var re = new RegExp(String.raw`^[ \t]{${baseIndentationLength}}`, 'gm');
var reIndentedString = emptyLinesHandled.replace(re, '');
const re = new RegExp(String.raw`^[ \t]{${baseIndentationLength}}`, 'gm');
const reIndentedString = emptyLinesHandled.replace(re, '');
return {

@@ -37,7 +39,7 @@ text: reIndentedString,

var removeBaseIndentation = function removeBaseIndentation(str) {
const removeBaseIndentation = str => {
// Remove empty lines
var emptyLinesRemoved = str.replace(/^[ \t]*$/gm, ''); // Get whitespaces
const emptyLinesRemoved = str.replace(/^[ \t]*$/gm, ''); // Get whitespaces
var match = emptyLinesRemoved.match(/^[ \t]*(?=\S|$)/gm);
const match = emptyLinesRemoved.match(/^[ \t]*(?=\S|$)/gm);

@@ -48,7 +50,3 @@ if (!match || match.length <= 1) {

var indentationLevels = match.map(function (indent) {
return indent.length;
}).filter(function (indent) {
return indent > 0;
}); // Math,min would return infinity if list is empty
const indentationLevels = match.map(indent => indent.length).filter(indent => indent > 0); // Math,min would return infinity if list is empty

@@ -59,11 +57,11 @@ if (indentationLevels.length === 0) {

var baseIndentationLevel = Math.min.apply(Math, _toConsumableArray(indentationLevels)); // Remove baseIndentation from string
const baseIndentationLevel = Math.min(...indentationLevels); // Remove baseIndentation from string
var regex = new RegExp(String.raw`^[ /t]{${baseIndentationLevel}}`, 'gm');
var baseIndentationRemoved = emptyLinesRemoved.replace(regex, '');
const regex = new RegExp(String.raw`^[ \t]{${baseIndentationLevel}}`, 'gm');
const baseIndentationRemoved = emptyLinesRemoved.replace(regex, '');
return baseIndentationRemoved;
};
var nextNonWhitespaceChar = function nextNonWhitespaceChar(text) {
var matches = text.match(/^\s*([^\s])/);
const nextNonWhitespaceChar = text => {
const matches = text.match(/^\s*([^\s])/);

@@ -77,11 +75,9 @@ if (matches) {

var reverseString = function reverseString(str) {
return str.split('').reverse().join('');
};
const reverseString = str => str.split('').reverse().join('');
var isLastDeclarationCompleted = function isLastDeclarationCompleted(text) {
const isLastDeclarationCompleted = text => {
// We disregard all comments in this assessment of declaration completion
var commentsRemoved = text.replace(/\/\*.*?\*\//g, '');
var reversedText = reverseString(commentsRemoved);
var lastNonWhitespaceChar = nextNonWhitespaceChar(reversedText);
const commentsRemoved = text.replace(/\/\*[\s\S]*?\*\//g, '');
const reversedText = reverseString(commentsRemoved);
const lastNonWhitespaceChar = nextNonWhitespaceChar(reversedText);

@@ -96,9 +92,5 @@ if (lastNonWhitespaceChar === ';' || lastNonWhitespaceChar === '}' || lastNonWhitespaceChar === '{' || lastNonWhitespaceChar === null) {

var wrapSelector = function wrapSelector(content) {
return `.selector${count += 1} {${content}}\n`;
};
const wrapSelector = content => `.selector${count += 1} {${content}}\n`;
var wrapKeyframes = function wrapKeyframes(content) {
return `@keyframes {${content}}\n`;
};
const wrapKeyframes = content => `@keyframes {${content}}\n`;
/**

@@ -111,10 +103,8 @@ * The reason we put a \s before .* in the last part of the regex is to make sure we don't

var isStylelintComment = function isStylelintComment(comment) {
return /^\s*stylelint-(?:enable|disable)(?:\s.*)?$/.test(comment);
};
const isStylelintComment = comment => /^\s*stylelint-(?:enable|disable)(?:\s.*)?$/.test(comment);
var extrapolateShortenedCommand = function extrapolateShortenedCommand(commands, shortCommand, absolutePath, location) {
var extrapolatedCommand = null; // We use .some so we can break the loop using return true
const extrapolateShortenedCommand = (commands, shortCommand, absolutePath, location) => {
let extrapolatedCommand = null; // We use .some so we can break the loop using return true
commands.some(function (singleCommand) {
commands.some(singleCommand => {
if (singleCommand.substr(0, shortCommand.length) === shortCommand) {

@@ -127,3 +117,3 @@ if (extrapolatedCommand === null) {

// This will probably never throw, as all our current commands start with different letters
throw new Error(`ERROR at ${absolutePath} line ${location.line} column ${location.column}:` + '\nYou shortened a Styled Components interpolation tag ambiguously, add a few more characters to fix this error');
throw new CssError('You shortened a Styled Components interpolation tag ambiguously, add a few more characters to fix this error', location.line, location.column, undefined, absolutePath);
}

@@ -130,0 +120,0 @@ } // continue loop

@@ -0,10 +1,10 @@

"use strict";
/**
* Parsing helpers
*/
var parseImports = function parseImports(node, currentNames) {
var names = Object.assign({}, currentNames);
var imports = node.specifiers.filter(function (specifier) {
return specifier.type === 'ImportDefaultSpecifier' || specifier.type === 'ImportSpecifier';
});
imports.forEach(function (singleImport) {
const parseImports = (node, currentNames) => {
const names = Object.assign({}, currentNames);
const imports = node.specifiers.filter(specifier => specifier.type === 'ImportDefaultSpecifier' || specifier.type === 'ImportSpecifier');
imports.forEach(singleImport => {
if (singleImport.imported) {

@@ -21,10 +21,10 @@ // Is helper method

var getSourceMap = function getSourceMap(fullCSS, fragmentCSS, startInSource) {
var correction = {}; // Save which line in the full CSS is which line in the source
const getSourceMap = (fullCSS, fragmentCSS, startInSource) => {
const correction = {}; // Save which line in the full CSS is which line in the source
var fullCSSLength = fullCSS.split(/\n/).length;
var currentCSSLength = fragmentCSS.split(/\n/).length;
var currentCSSStart = fullCSSLength - currentCSSLength + 1; // eslint-disable-next-line no-plusplus
const fullCSSLength = fullCSS.split(/\n/).length;
const currentCSSLength = fragmentCSS.split(/\n/).length;
const currentCSSStart = fullCSSLength - currentCSSLength + 1; // eslint-disable-next-line no-plusplus
for (var i = 0; i < currentCSSLength + 1; i++) {
for (let i = 0; i < currentCSSLength + 1; i++) {
correction[currentCSSStart + i] = startInSource + i;

@@ -31,0 +31,0 @@ }

@@ -1,4 +0,6 @@

var path = require('path');
"use strict";
var isTaggedTemplateLiteral = require('./tagged-template-literal').isTaggedTemplateLiteral;
const path = require('path');
const isTaggedTemplateLiteral = require('./tagged-template-literal').isTaggedTemplateLiteral;
/**

@@ -9,5 +11,3 @@ * Check if something is a styled-components import declaration

var isStyledImport = function isStyledImport(node, moduleName) {
return node.type === 'ImportDeclaration' && path.basename(node.source.value) === moduleName;
};
const isStyledImport = (node, moduleName) => node.type === 'ImportDeclaration' && path.basename(node.source.value) === moduleName;
/**

@@ -21,10 +21,7 @@ * Check if something is a styled shorthand call

var isStyledShorthand = function isStyledShorthand(node, styledVariableName) {
return (// Check that it's an object
node.tag && node.tag.object && // Check that the object matches the imported name
node.tag.object.name === styledVariableName && // Check that a property exists, otherwise it's just styled
// without any call
node.tag.property
);
};
const isStyledShorthand = (node, styledVariableName) => // Check that it's an object
node.tag && node.tag.object && // Check that the object matches the imported name
node.tag.object.name === styledVariableName && // Check that a property exists, otherwise it's just styled
// without any call
node.tag.property;
/**

@@ -36,8 +33,5 @@ * Check if a node is a styld call

var isStyledCall = function isStyledCall(node, styledVariableName) {
return (// Check that it's a function call
node.tag && node.tag.callee && // And that the function name matches the imported name
node.tag.callee.name === styledVariableName
);
};
const isStyledCall = (node, styledVariableName) => // Check that it's a function call
node.tag && node.tag.callee && // And that the function name matches the imported name
node.tag.callee.name === styledVariableName;
/**

@@ -48,13 +42,8 @@ * Check if it has a .attrs postfix which we in that case handle specially

var hasAttrsCall = function hasAttrsCall(node) {
return (// Check that it's a function call
node.tag && node.tag.callee && // Check that the last member of the call is attrs
node.tag.callee.property && node.tag.callee.property.name === 'attrs'
);
}; // We don't need the checks here as they were checked in hasAttrsCall
const hasAttrsCall = node => // Check that it's a function call
node.tag && node.tag.callee && // Check that the last member of the call is attrs
node.tag.callee.property && node.tag.callee.property.name === 'attrs'; // We don't need the checks here as they were checked in hasAttrsCall
var getAttrsObject = function getAttrsObject(node) {
return node.tag.callee.object;
};
const getAttrsObject = node => node.tag.callee.object;
/**

@@ -65,5 +54,3 @@ * Check if something is a styled component call

var isStyled = function isStyled(node, styledVariableName) {
return isTaggedTemplateLiteral(node) && (isStyledCall(node, styledVariableName) || isStyledShorthand(node, styledVariableName));
};
const isStyled = (node, styledVariableName) => isTaggedTemplateLiteral(node) && (isStyledCall(node, styledVariableName) || isStyledShorthand(node, styledVariableName));
/**

@@ -76,5 +63,3 @@ * Check if it is a .extend call and we pretty reasonable assume that any TTL that ends

var isExtendCall = function isExtendCall(node) {
return node.tag && node.tag.property && node.tag.property.name === 'extend';
};
const isExtendCall = node => node.tag && node.tag.property && node.tag.property.name === 'extend';
/**

@@ -87,6 +72,6 @@ * Check if something is a call to one of our helper methods

var isHelper = function isHelper(node, importedNames) {
const isHelper = (node, importedNames) => {
if (!isTaggedTemplateLiteral(node)) return false;
var helper;
Object.keys(importedNames).forEach(function (name) {
let helper;
Object.keys(importedNames).forEach(name => {
if (importedNames[name] === node.tag.name) {

@@ -93,0 +78,0 @@ helper = name; // eslint-disable-next-line no-useless-return

@@ -1,6 +0,12 @@

var nextNonWhitespaceChar = require('./general').nextNonWhitespaceChar;
"use strict";
var isLastDeclarationCompleted = require('./general').isLastDeclarationCompleted;
const CssError = require('postcss/lib/css-syntax-error');
var extrapolateShortenedCommand = require('./general').extrapolateShortenedCommand;
const reverseString = require('./general').reverseString;
const nextNonWhitespaceChar = require('./general').nextNonWhitespaceChar;
const isLastDeclarationCompleted = require('./general').isLastDeclarationCompleted;
const extrapolateShortenedCommand = require('./general').extrapolateShortenedCommand;
/**

@@ -11,5 +17,3 @@ * Check if a node is a tagged template literal

var isTaggedTemplateLiteral = function isTaggedTemplateLiteral(node) {
return node.type === 'TaggedTemplateExpression';
};
const isTaggedTemplateLiteral = node => node.type === 'TaggedTemplateExpression';
/**

@@ -20,5 +24,3 @@ * Check if a tagged template literal has interpolations

var hasInterpolations = function hasInterpolations(node) {
return !node.quasi.quasis[0].tail;
};
const hasInterpolations = node => !node.quasi.quasis[0].tail;
/**

@@ -29,5 +31,3 @@ * Retrieves all the starting and ending comments of a TTL expression

var retrieveStartEndComments = function retrieveStartEndComments(expression) {
return (expression.leadingComments || []).concat(expression.trailingComments || []);
};
const retrieveStartEndComments = expression => (expression.leadingComments || []).concat(expression.trailingComments || []);
/**

@@ -38,5 +38,3 @@ * Checks if given comment value is an interpolation tag

var isScTag = function isScTag(comment) {
return /^\s*?sc-[a-z]/.test(comment);
};
const isScTag = comment => /^\s*?sc-[a-z]/.test(comment);
/**

@@ -47,11 +45,9 @@ * Checks if an interpolation has an sc comment tag

var hasInterpolationTag = function hasInterpolationTag(expression) {
var relevantComments = retrieveStartEndComments(expression).map(function (commentObject) {
return commentObject.value;
});
const hasInterpolationTag = expression => {
const relevantComments = retrieveStartEndComments(expression).map(commentObject => commentObject.value);
return relevantComments.some(isScTag);
};
var extractScTagInformation = function extractScTagInformation(comment) {
var matchArray = comment.match(/^\s*?sc-([a-z]+)\s*(?:(?:'(.*?)')|(?:"(.*?)"))?\s*$/);
const extractScTagInformation = comment => {
const matchArray = comment.match(/^\s*?sc-([a-z]+)\s*(?:(?:'(.*?)')|(?:"(.*?)"))?\s*$/);

@@ -69,3 +65,3 @@ if (matchArray === null) {

var interpolationTagAPI = ['block', 'selector', 'declaration', 'property', 'value', 'custom'];
const interpolationTagAPI = ['block', 'selector', 'declaration', 'property', 'value', 'custom'];
/**

@@ -75,12 +71,12 @@ * Enact the interpolation tagging API

var parseInterpolationTag = function parseInterpolationTag(expression, id, absolutePath) {
var relevantComments = retrieveStartEndComments(expression);
var substitute;
relevantComments.some(function (comment) {
const parseInterpolationTag = (expression, id, absolutePath) => {
const relevantComments = retrieveStartEndComments(expression);
let substitute;
relevantComments.some(comment => {
if (isScTag(comment.value)) {
// We always assume that there is only one sc tag in an interpolation
var scTagInformation = extractScTagInformation(comment.value);
const scTagInformation = extractScTagInformation(comment.value);
if (scTagInformation === null) {
throw new Error(`ERROR at ${absolutePath} line ${comment.loc.start.line} column ${comment.loc.start.column}:` + '\nWe were unable to parse your Styled Components interpolation tag, this is most likely due to lack of quotes in an sc-custom tag, refer to the documentation for correct format');
throw new CssError('We were unable to parse your Styled Components interpolation tag, this is most likely due to lack of quotes in an sc-custom tag, refer to the documentation for correct format', comment.loc.start.line, comment.loc.start.column, undefined, absolutePath);
}

@@ -92,3 +88,3 @@

case 'selector':
substitute = 'div';
substitute = `.sc-selector${id}`;
break;

@@ -114,3 +110,3 @@

default:
throw new Error(`ERROR at ${absolutePath} line ${comment.loc.start.line} column ${comment.loc.start.column}:` + '\nYou tagged a Styled Components interpolation with an invalid sc- tag. Refer to the documentation to see valid interpolation tags');
throw new CssError('You tagged a Styled Components interpolation with an invalid sc- tag. Refer to the documentation to see valid interpolation tags', comment.loc.start.line, comment.loc.start.column, undefined, absolutePath);
}

@@ -130,19 +126,30 @@

var interleave = function interleave(quasis, expressions, absolutePath) {
const interleave = (quasis, expressions, absolutePath) => {
// Used for making sure our dummy mixins are all unique
var count = 0;
var css = '';
let count = 0;
let css = '';
for (var i = 0, l = expressions.length; i < l; i += 1) {
var prevText = quasis[i].value.raw;
var nextText = quasis[i + 1].value.raw;
for (let i = 0, l = expressions.length; i < l; i += 1) {
const prevText = quasis[i].value.raw;
const nextText = quasis[i + 1].value.raw;
const prevChar = nextNonWhitespaceChar(reverseString(prevText));
const nextChar = nextNonWhitespaceChar(nextText);
css += prevText;
var substitute = void 0;
let substitute;
if (hasInterpolationTag(expressions[i])) {
substitute = parseInterpolationTag(expressions[i], count, absolutePath);
count += 1; // No sc tag so we guess defaults
} else if (nextChar === '{') {
// Guess as selector, which shares format with `parseInterpolationTag`, but not `wrapSelector`
substitute = `.sc-selector${count}`;
count += 1;
} else if (prevChar === ':') {
// After a colon and not a pseudo-class, then guess as value
substitute = '$dummyValue';
} else if (nextChar === ':') {
// Before a colon, then guess as property
substitute = `-styled-mixin${count}`;
count += 1;
} else if (isLastDeclarationCompleted(css)) {
// No sc tag so we guess defaults
/** This block assumes that if you put an interpolation in the position

@@ -166,2 +173,11 @@ * of the start of a declaration that the interpolation will

substitute = '$dummyValue';
} // Make sure substituted by same count of lines
const targetLines = quasis[i + 1].loc.start.line - quasis[i].loc.end.line + 1;
let currentLines = substitute.split('\n').length;
while (currentLines < targetLines) {
substitute += '\n/* dummyComment */';
currentLines += 1;
}

@@ -182,3 +198,3 @@

var getTaggedTemplateLiteralContent = function getTaggedTemplateLiteralContent(node, absolutePath) {
const getTaggedTemplateLiteralContent = (node, absolutePath) => {
if (hasInterpolations(node)) {

@@ -185,0 +201,0 @@ return interleave(node.quasi.quasis, node.quasi.expressions, absolutePath);

@@ -0,0 +0,0 @@ MIT License

{
"name": "@asvetliakov/stylelint-processor-styled-components",
"version": "1.0.1",
"version": "1.5.0",
"description": "A stylelint processor for styled-components",

@@ -10,6 +10,10 @@ "main": "lib/index.js",

"prettier": "prettier --write --print-width 100 --tab-width 2 --no-semi --single-quote '{src/**/*.{js,jsx},test/*.{js,jsx}}'",
"test": "npm run build && jest",
"precommit": "lint-staged",
"test": "npm run build && jest test/*.test.js",
"build": "babel src --out-dir lib",
"prepublish": "npm run build"
},
"jest": {
"setupTestFrameworkScriptFile": "./test/jest-setup.js"
},
"repository": {

@@ -47,7 +51,6 @@ "type": "git",

"devDependencies": {
"@babel/cli": "^7.0.0-beta.32",
"@babel/core": "^7.0.0-beta.32",
"@babel/preset-env": "^7.0.0-beta.32",
"babel-plugin-istanbul": "^4.1.5",
"babel-plugin-jest-hoist": "^21.2.0",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"eslint": "^3.19.0",

@@ -57,21 +60,18 @@ "eslint-config-airbnb-base": "^11.2.0",

"husky": "^0.14.3",
"jest": "^21.3.0-beta.8",
"jest": "^23.5.0",
"lint-staged": "^4.0.1",
"lodash": "^4.15.0",
"prettier": "^1.4.4",
"stylelint": "^8.0.0",
"stylelint": "^9.1.1",
"typescript": "~2.6.1"
},
"peerDependency": {
"stylelint-config-styled-components": "^0.1.1"
"stylelint-config-styled-components": "^0.1.1",
"stylelint": ">= 9"
},
"dependencies": {
"@babel/traverse": "^7.0.0-beta.32",
"babylon": "7.0.0-beta.32"
},
"jest": {
"transform": {
"\\.(js|jsx)": "<rootDir>/preprocessor.js"
}
"@babel/parser": "^7.0.0",
"@babel/traverse": "^7.0.0",
"postcss": "^6.0.14"
}
}

@@ -19,3 +19,3 @@ # `stylelint-processor-styled-components`

- The [`stylelint-config-styled-components`](https://github.com/styled-components/stylelint-config-styled-components) config to disable stylelint rules that clash with `styled-components`
- Your favorite `stylelint` config! (for example [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard))
- Your favorite `stylelint` config! (for example [`stylelint-config-recommended`](https://github.com/stylelint/stylelint-config-recommended))

@@ -27,3 +27,3 @@ ```

stylelint-config-styled-components
stylelint-config-standard)
stylelint-config-recommended)
```

@@ -37,10 +37,9 @@

"extends": [
"stylelint-config-standard",
"stylelint-config-recommended",
"stylelint-config-styled-components"
],
"syntax": "scss"
]
}
```
> **NOTE:** The processor works with Flow- and TypeScript-typed files too! (we'll assume TypeScript usage if you're files end in `.ts` or `.tsx`)
> **NOTE:** The processor works with Flow- and TypeScript-typed files too! (we'll assume TypeScript usage if your files end in `.ts` or `.tsx`)

@@ -53,3 +52,3 @@ ## [Documentation](https://www.styled-components.com/docs/tooling#stylelint)

- [Webpack](https://www.styled-components.com/docs/tooling#webpack)
- [Interpolation tagging](https://www.styled-components.com/docs/tooling#interpolation-taggingp)
- [Interpolation tagging](https://www.styled-components.com/docs/tooling#interpolation-tagging)
- [Tags](https://www.styled-components.com/docs/tooling#tags)

@@ -56,0 +55,0 @@ - [sc-custom](https://www.styled-components.com/docs/tooling#sc-custom)

const path = require('path')
const parse = require('./parsers/index')
let inputId = 1
const interpolationLinesMap = {}
const sourceMapsCorrections = {}
const errorWasThrown = {}
const DEFAULT_OPTIONS = {
moduleName: 'styled-components'
moduleName: 'styled-components',
importName: 'default'
}
function isCausedBySubstitution(warning, line, interpolationLines) {
return interpolationLines.some(({ start, end }) => {
if (line > start && line < end) {
// Inner interpolation lines must be
return true
} else if (line === start) {
return ['value-list-max-empty-lines', 'comment-empty-line-before'].indexOf(warning.rule) >= 0
} else if (line === end) {
return ['comment-empty-line-before', 'indentation'].indexOf(warning.rule) >= 0
} else {
return false
}
})
}
module.exports = options => ({
// Get string for stylelint to lint
code(input, filepath) {
const absolutePath = path.resolve(process.cwd(), filepath)
sourceMapsCorrections[absolutePath] = {}
const { extractedCSS, sourceMap } = parse(
input,
absolutePath,
Object.assign({}, DEFAULT_OPTIONS, options)
)
// Save source location, merging existing corrections with current corrections
sourceMapsCorrections[absolutePath] = Object.assign(
sourceMapsCorrections[absolutePath],
sourceMap
)
return extractedCSS
let absolutePath
if (filepath) {
absolutePath = path.resolve(process.cwd(), filepath)
} else {
absolutePath = `<input css ${inputId}>`
inputId += 1
}
try {
sourceMapsCorrections[absolutePath] = {}
const { extractedCSS, interpolationLines, sourceMap } = parse(
input,
absolutePath,
Object.assign({}, DEFAULT_OPTIONS, options)
)
// Save dummy interpolation lines
interpolationLinesMap[absolutePath] = interpolationLines.concat(
interpolationLinesMap[absolutePath] || []
)
// Save source location, merging existing corrections with current corrections
sourceMapsCorrections[absolutePath] = Object.assign(
sourceMapsCorrections[absolutePath],
sourceMap
)
delete errorWasThrown[absolutePath]
return extractedCSS
} catch (e) {
// Always save the error
errorWasThrown[absolutePath] = e
// Incorrect interpolations will throw CssSyntaxError and they'll be handled by stylelint
// so we can throw it out but not for others
if (e.name === 'CssSyntaxError') {
throw e
}
return ''
}
},
// Fix sourcemaps
result(stylelintResult, filepath) {
const err = errorWasThrown[filepath]
if (err) {
if (err.name === 'CssSyntaxError') {
// We threw an error ourselves, in this case we have already put correct
// line/column numbers so no source maps are needed
// (and would actually break the line numbers)
return stylelintResult
} else {
// For other errors, wrap them into the result
return Object.assign({}, stylelintResult, {
errored: true,
parseErrors: [
{
line: err.loc && err.loc.line,
column: err.loc && err.loc.column,
rule: 'parseError',
severity: 'error',
text: `${err.message}`
}
]
})
}
}
const interpolationLines = interpolationLinesMap[filepath] || []
const lineCorrection = sourceMapsCorrections[filepath]
const warnings = stylelintResult.warnings.map(warning =>
Object.assign({}, warning, {
// Replace "brace" with "backtick" in warnings, e.g.
// "Unexpected empty line before closing backtick" (instead of "brace")
text: warning.text.replace(/brace/, 'backtick'),
line: lineCorrection[warning.line]
})
)
const warnings = stylelintResult.warnings
.filter(
warning =>
// Filter false-positive warnings generated by interpolations substitution
!isCausedBySubstitution(warning, lineCorrection[warning.line], interpolationLines)
)
.map(warning =>
Object.assign({}, warning, {
// Replace "brace" with "backtick" in warnings, e.g.
// "Unexpected empty line before closing backtick" (instead of "brace")
text: warning.text.replace(/brace/, 'backtick'),
line: lineCorrection[warning.line] || warning.line
})
)
return Object.assign({}, stylelintResult, { warnings })
const result = Object.assign({}, stylelintResult, { warnings })
// Undo `errored` if no warnings with error severity any more
if (result.errored && !warnings.some(warning => warning.severity === 'error')) {
delete result.errored
}
return result
}
})

@@ -1,5 +0,4 @@

const flowParser = require('./create-parser')('flow');
const tsParser = require('./create-parser')('typescript');
const estreeParse = require('./babylon-parser')
const traverse = require('babel-traverse').default
const traverse = require('@babel/traverse').default
const isStyled = require('../utils/styled').isStyled

@@ -28,5 +27,7 @@ const isHelper = require('../utils/styled').isHelper

keyframes: 'keyframes',
injectGlobal: 'injectGlobal'
injectGlobal: 'injectGlobal',
createGlobalStyle: 'createGlobalStyle'
}
let sourceMap = {}
const interpolationLines = []
traverse(ast, {

@@ -51,3 +52,7 @@ noScope: true,

}
if (!helper && !isStyled(processedNode, importedNames.default) && !isExtendCall(node)) {
if (
!helper &&
!isStyled(processedNode, importedNames[options.importName]) &&
!isExtendCall(node)
) {
return

@@ -80,4 +85,12 @@ }

sourceMap,
getSourceMap(extractedCSS.join('\n'), wrappedContent, processedNode.loc.start.line)
getSourceMap(extractedCSS.join('\n'), wrappedContent, processedNode.quasi.loc.start.line)
)
// Save dummy interpolation lines
node.quasi.expressions.forEach((expression, i) => {
interpolationLines.push({
start: node.quasi.quasis[i].loc.end.line,
end: node.quasi.quasis[i + 1].loc.start.line
})
})
/**

@@ -93,2 +106,3 @@ * All queued comments have been added to the file so we don't need to, and actually shouldn't

extractedCSS: extractedCSS.join('\n'),
interpolationLines,
sourceMap

@@ -99,9 +113,7 @@ }

module.exports = (input, absolutePath, options) => {
let ast = null
if (absolutePath.endsWith('.ts') || absolutePath.endsWith('.tsx')) {
ast = tsParser(input)
} else {
ast = flowParser(input)
}
const typedParser = absolutePath.endsWith('.ts') || absolutePath.endsWith('.tsx')
? 'typescript'
: 'flow'
const ast = estreeParse(typedParser, options.parserPlugins)(input)
return processStyledComponentsFile(ast, absolutePath, options)
}

@@ -0,1 +1,2 @@

const CssError = require('postcss/lib/css-syntax-error')
// selector count

@@ -51,3 +52,3 @@ let count = 0

// Remove baseIndentation from string
const regex = new RegExp(String.raw`^[ /t]{${baseIndentationLevel}}`, 'gm')
const regex = new RegExp(String.raw`^[ \t]{${baseIndentationLevel}}`, 'gm')
const baseIndentationRemoved = emptyLinesRemoved.replace(regex, '')

@@ -70,3 +71,3 @@ return baseIndentationRemoved

// We disregard all comments in this assessment of declaration completion
const commentsRemoved = text.replace(/\/\*.*?\*\//g, '')
const commentsRemoved = text.replace(/\/\*[\s\S]*?\*\//g, '')
const reversedText = reverseString(commentsRemoved)

@@ -108,5 +109,8 @@ const lastNonWhitespaceChar = nextNonWhitespaceChar(reversedText)

// This will probably never throw, as all our current commands start with different letters
throw new Error(
`ERROR at ${absolutePath} line ${location.line} column ${location.column}:` +
'\nYou shortened a Styled Components interpolation tag ambiguously, add a few more characters to fix this error'
throw new CssError(
'You shortened a Styled Components interpolation tag ambiguously, add a few more characters to fix this error',
location.line,
location.column,
undefined,
absolutePath
)

@@ -113,0 +117,0 @@ }

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ const path = require('path')

@@ -0,1 +1,3 @@

const CssError = require('postcss/lib/css-syntax-error')
const reverseString = require('./general').reverseString
const nextNonWhitespaceChar = require('./general').nextNonWhitespaceChar

@@ -60,6 +62,8 @@ const isLastDeclarationCompleted = require('./general').isLastDeclarationCompleted

if (scTagInformation === null) {
throw new Error(
`ERROR at ${absolutePath} line ${comment.loc.start.line} column ${comment.loc.start
.column}:` +
'\nWe were unable to parse your Styled Components interpolation tag, this is most likely due to lack of quotes in an sc-custom tag, refer to the documentation for correct format'
throw new CssError(
'We were unable to parse your Styled Components interpolation tag, this is most likely due to lack of quotes in an sc-custom tag, refer to the documentation for correct format',
comment.loc.start.line,
comment.loc.start.column,
undefined,
absolutePath
)

@@ -75,3 +79,3 @@ }

case 'selector':
substitute = 'div'
substitute = `.sc-selector${id}`
break

@@ -97,6 +101,8 @@

default:
throw new Error(
`ERROR at ${absolutePath} line ${comment.loc.start.line} column ${comment.loc.start
.column}:` +
'\nYou tagged a Styled Components interpolation with an invalid sc- tag. Refer to the documentation to see valid interpolation tags'
throw new CssError(
'You tagged a Styled Components interpolation with an invalid sc- tag. Refer to the documentation to see valid interpolation tags',
comment.loc.start.line,
comment.loc.start.column,
undefined,
absolutePath
)

@@ -121,2 +127,4 @@ }

const nextText = quasis[i + 1].value.raw
const prevChar = nextNonWhitespaceChar(reverseString(prevText))
const nextChar = nextNonWhitespaceChar(nextText)

@@ -128,4 +136,15 @@ css += prevText

count += 1
// No sc tag so we guess defaults
} else if (nextChar === '{') {
// Guess as selector, which shares format with `parseInterpolationTag`, but not `wrapSelector`
substitute = `.sc-selector${count}`
count += 1
} else if (prevChar === ':') {
// After a colon and not a pseudo-class, then guess as value
substitute = '$dummyValue'
} else if (nextChar === ':') {
// Before a colon, then guess as property
substitute = `-styled-mixin${count}`
count += 1
} else if (isLastDeclarationCompleted(css)) {
// No sc tag so we guess defaults
/** This block assumes that if you put an interpolation in the position

@@ -149,2 +168,10 @@ * of the start of a declaration that the interpolation will

}
// Make sure substituted by same count of lines
const targetLines = quasis[i + 1].loc.start.line - quasis[i].loc.end.line + 1
let currentLines = substitute.split('\n').length
while (currentLines < targetLines) {
substitute += '\n/* dummyComment */'
currentLines += 1
}
css += substitute

@@ -151,0 +178,0 @@ }

@@ -0,0 +0,0 @@ import styled, { keyframes, css } from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ /* stylelint-disable */

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components';

@@ -0,0 +0,0 @@ import styled from 'styled-components';

@@ -15,2 +15,5 @@ import styled from 'styled-components';

}
${/* sc-selector */ ':active'} {
background-color: green;
}
`;

@@ -22,2 +25,3 @@

${/* sc-declaration */ 'dummy'}
${/* sc-declaration */ 'dummy2'}
`;

@@ -24,0 +28,0 @@

@@ -17,1 +17,27 @@ import styled, { css } from 'styled-components'

`
// #110
const Bla = styled.div`
${Button} {
${something}: blue;
}
background: ${bla};
${someValue}
`
// Multi-line interpolations
const MultiLineDiv = styled.div`
color: ${
'red'
};
${
'long values'
}
`;

@@ -6,6 +6,14 @@ import styled from 'styled-components'

// ⚠️ BAD INDENTATION ⚠️
const Button2 = styled.button`
const Button2 = styled.button.attrs({
type: 'normal'
})`
display: block;
${
props => props.isHovering && interpolatedStyle
}
position: ${
props => props.position
};
color: ${color};
background: blue;
`

@@ -119,1 +119,17 @@ import styled from 'styled-components'

`
const Button13 = styled.button`
display: block;
/*
multiline comment with "*" and "/"
*/
${colorExpression}
`
const Button14 = styled.button`
display: block;
/**
* JSDoc style comment
*/
${colorExpression}
`

@@ -0,0 +0,0 @@ import something from 'some-lib'

@@ -0,0 +0,0 @@ import emotion from 'emotion'

@@ -0,0 +0,0 @@ import emotion from '../../emotion'

@@ -0,0 +0,0 @@ import React from 'react'

@@ -0,0 +0,0 @@ // Global variables

@@ -0,0 +0,0 @@ import styled, { css, keyframes, injectGlobal } from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components';

@@ -0,0 +0,0 @@ import notStyled, {

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -0,0 +0,0 @@ import styled from 'some-other-lib'

@@ -0,0 +0,0 @@ import styled from 'styled-components'

@@ -28,2 +28,15 @@ import * as React from 'react';

color: blue;
`
`
// ⚠️ BAD INDENTATION ⚠️ (Generic Type)
export const Button3 = styled.button<IAccordionContainerProps>`
color: blue;
`;
// ⚠️ BAD INDENTATION ⚠️ (Inline Generic Type)
export const Button4 = styled.button<{ a: boolean; b: string }>`
color: blue;
`;

@@ -0,0 +0,0 @@ import * as React from 'react';

@@ -7,2 +7,3 @@ const stylelint = require('stylelint')

'block-no-empty': true,
'property-no-unknown': true,
indentation: 2

@@ -138,2 +139,26 @@ }

})
describe('js style comments', () => {
describe('valid', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/hard/valid-js-comments.js')
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
expect(data.errored).toEqual(false)
})
it('should not have any warnings', () => {
expect(data.results[0].warnings).toEqual([])
})
})
})
})

@@ -17,3 +17,2 @@ const stylelint = require('stylelint')

files: [fixture],
syntax: 'scss',
config: {

@@ -20,0 +19,0 @@ processors: [processor],

@@ -11,50 +11,101 @@ const stylelint = require('stylelint')

describe('inject-global', () => {
let fixture
let data
describe('using spaces', () => {
let fixture
let data
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
syntax: 'scss',
config: {
processors: [processor],
rules
}
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
config: {
processors: [processor],
rules
}
})
.then(result => {
data = result
done()
})
.catch(err => {
// eslint-disable-next-line
console.log(err)
data = err
done()
})
})
describe('valid fixtures', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/inject-global/valid-spaces.js')
})
.then(result => {
data = result
done()
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
.catch(err => {
// eslint-disable-next-line
console.log(err)
data = err
done()
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
})
describe('valid fixtures', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/inject-global/valid.js')
})
it('should not have errored', () => {
expect(data.errored).toEqual(false)
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
it('should not have any warnings', () => {
expect(data.results[0].warnings.length).toEqual(0)
})
})
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
describe('using tabs', () => {
let fixture
let data
it('should not have errored', () => {
expect(data.errored).toEqual(false)
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
config: {
processors: [processor],
rules: Object.assign({}, rules, { indentation: 'tab' })
}
})
.then(result => {
data = result
done()
})
.catch(err => {
// eslint-disable-next-line
console.log(err)
data = err
done()
})
})
it('should not have any warnings', () => {
expect(data.results[0].warnings.length).toEqual(0)
describe('valid fixtures', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/inject-global/valid-tabs.js')
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
expect(data.errored).toEqual(false)
})
it('should not have any warnings', () => {
expect(data.results[0].warnings.length).toEqual(0)
})
})
})
})

@@ -8,2 +8,3 @@ const stylelint = require('stylelint')

'declaration-block-no-duplicate-properties': true,
'no-duplicate-selectors': true,
indentation: 2

@@ -20,3 +21,2 @@ }

files: [fixture],
syntax: 'scss',
config: {

@@ -64,10 +64,16 @@ processors: [processor],

it('should throw an error', () => {
expect(data).toEqual(expect.any(Error))
it('should return error', () => {
expect(data.errored).toEqual(true)
})
it('should have exactly one warning', () => {
expect(data.results[0].warnings.length).toBe(1)
})
it('should throw correct error', () => {
expect(data.message).toMatch(
/fixtures\/interpolation-tagging\/invalid-tag\.js line 5 column 4:\n.*invalid sc- tag/
)
const warning = data.results[0].warnings[0]
expect(warning.line).toBe(5)
expect(warning.column).toBe(4)
expect(warning.severity).toBe('error')
expect(warning.text).toMatch(/Styled Components.*invalid sc- tag/)
})

@@ -81,12 +87,18 @@ })

it('should throw an error', () => {
expect(data).toEqual(expect.any(Error))
it('should return error', () => {
expect(data.errored).toEqual(true)
})
it('should have exactly one warning', () => {
expect(data.results[0].warnings.length).toBe(1)
})
it('should throw correct error', () => {
expect(data.message).toMatch(
/fixtures\/interpolation-tagging\/invalid-custom\.js line 5 column 4:\n.*We were unable to parse/
)
const warning = data.results[0].warnings[0]
expect(warning.line).toBe(5)
expect(warning.column).toBe(4)
expect(warning.severity).toBe('error')
expect(warning.text).toMatch(/We were unable to parse.*Styled Components/)
})
})
})

@@ -7,3 +7,6 @@ const stylelint = require('stylelint')

'block-no-empty': true,
'comment-empty-line-before': 'always',
'declaration-block-no-duplicate-properties': true,
'value-list-max-empty-lines': 0,
'max-empty-lines': 1,
indentation: 2

@@ -20,3 +23,2 @@ }

files: [fixture],
syntax: 'scss',
config: {

@@ -83,2 +85,6 @@ processors: [processor],

})
it('should have the indentation warning in the right line', () => {
expect(data.results[0].warnings[0].line).toEqual(16)
})
})

@@ -85,0 +91,0 @@

@@ -22,81 +22,204 @@ const stylelint = require('stylelint')

// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
syntax: 'scss',
config: {
// Set moduleName option to "emotion"
processors: [[processor, { moduleName: 'emotion' }]],
rules
}
describe('moduleName', () => {
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
config: {
// Set moduleName option to "emotion"
processors: [[processor, { moduleName: 'emotion' }]],
rules
}
})
.then(result => {
data = result
done()
})
.catch(err => {
console.log(err)
data = err
done()
})
})
describe('moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/module-name.js')
})
.then(result => {
data = result
done()
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
.catch(err => {
console.log(err)
data = err
done()
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
})
describe('moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/module-name.js')
})
it('should have errored', () => {
expect(data.results[0].errored).toEqual(true)
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should have one warning (i.e. wrong lines of code)', () => {
expect(data.results[0].warnings.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
it('should have a block-no-empty as the first warning', () => {
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')
})
})
it('should have errored', () => {
expect(data.results[0].errored).toEqual(true)
})
describe('relative moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/relative-module-name.js')
})
it('should have one warning (i.e. wrong lines of code)', () => {
expect(data.results[0].warnings.length).toEqual(1)
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should have errored', () => {
expect(data.results[0].errored).toEqual(true)
})
it('should have one warning (i.e. wrong lines of code)', () => {
expect(data.results[0].warnings.length).toEqual(1)
})
it('should have a block-no-empty as the first warning', () => {
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')
})
})
it('should have a block-no-empty as the first warning', () => {
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')
describe('invalid moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/invalid-module-name.js')
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
expect(data.results[0].errored).toEqual(undefined)
})
})
})
describe('relative moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/relative-module-name.js')
describe('importName', () => {
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
stylelint
.lint({
files: [fixture],
config: {
// Set importName option to "notDefault"
processors: [[processor, { importName: 'notDefault' }]],
rules
}
})
.then(result => {
data = result
done()
})
.catch(err => {
console.log(err)
data = err
done()
})
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
describe('importName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/import-name.js')
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should have errored', () => {
expect(data.results[0].errored).toEqual(true)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should have one warning (i.e. wrong lines of code)', () => {
expect(data.results[0].warnings.length).toEqual(1)
it('should have errored', () => {
expect(data.results[0].errored).toEqual(true)
})
it('should have one warning (i.e. wrong lines of code)', () => {
expect(data.results[0].warnings.length).toEqual(1)
})
it('should have a block-no-empty as the first warning', () => {
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')
})
})
it('should have a block-no-empty as the first warning', () => {
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')
describe('invalid importName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/invalid-import-name.js')
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
console.log(data)
expect(data.results[0].errored).toEqual(undefined)
})
})
})
describe('invalid moduleName', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/options/invalid-module-name.js')
describe('parserPlugins', () => {
// NOTE beforeEach() runs _after_ the beforeAll() hooks of the describe() blocks, so `fixture`
// will have the right path
beforeEach(done => {
const plugins = [
'jsx',
'objectRestSpread',
['decorators', { decoratorsBeforeExport: true }],
'classProperties',
'exportExtensions',
'asyncGenerators',
'functionBind',
'functionSent',
'dynamicImport',
'optionalCatchBinding',
'optionalChaining',
// Enable experimental feature
'exportDefaultFrom'
]
stylelint
.lint({
code: "export Container from './Container';",
config: {
processors: [[processor, { parserPlugins: plugins }]],
rules
}
})
.then(result => {
data = result
done()
})
.catch(err => {
console.log(err)
data = err
done()
})
})

@@ -108,7 +231,3 @@

it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
it('should have not errored', () => {
expect(data.results[0].errored).toEqual(undefined)

@@ -115,0 +234,0 @@ })

@@ -10,2 +10,9 @@ /**

const rules = {
'rule-empty-line-before': [
'always-multi-line',
{
except: ['first-nested'],
ignore: ['after-comment']
}
],
'block-no-empty': true,

@@ -25,3 +32,2 @@ indentation: 2

files: [fixture],
syntax: 'scss',
config: {

@@ -63,2 +69,25 @@ processors: [processor],

})
describe('Line Numbers Report Correctly', () => {
beforeAll(() => {
fixture = path.join(__dirname, './fixtures/real-world/LineNumbersReportedAccurate.js')
})
it('should have one result', () => {
expect(data.results.length).toEqual(1)
})
it('should use the right file', () => {
expect(data.results[0].source).toEqual(fixture)
})
it('should not have errored', () => {
expect(data.errored).toEqual(true)
})
it('should identify the line number correctly', () => {
const errorLine = 20
expect(data.results[0].warnings[0].line).toEqual(errorLine)
})
})
})

@@ -28,3 +28,2 @@ const stylelint = require('stylelint')

files: [fixture],
syntax: 'scss',
config: {

@@ -31,0 +30,0 @@ processors: [processor],

@@ -20,3 +20,2 @@ const stylelint = require('stylelint')

files: [fixture],
syntax: 'scss',
config: {

@@ -40,3 +39,3 @@ processors: [processor],

expect(data.results.length).toEqual(1)
expect(data.results[0].warnings.length).toEqual(5)
expect(data.results[0].warnings.length).toEqual(7)
expect(data.results[0].warnings[0].rule).toEqual('block-no-empty')

@@ -47,2 +46,4 @@ expect(data.results[0].warnings[1].rule).toEqual('indentation')

expect(data.results[0].warnings[4].rule).toEqual('indentation')
expect(data.results[0].warnings[5].rule).toEqual('indentation')
expect(data.results[0].warnings[6].rule).toEqual('indentation')
done()

@@ -61,2 +62,12 @@ })

it('should report errors when parsing typescript files with SyntaxError', done => {
const fixture = path.join(__dirname, './fixtures/typescript/ts-syntax-parse-error.ts')
doLint(fixture, done).then(data => {
expect(data.results.length).toEqual(1)
expect(data.results[0].parseErrors.length).toEqual(1)
expect(data.results[0].parseErrors[0].rule).toEqual('parseError')
done()
})
})
it('should report errors in TSX files(typescript + JSX)', done => {

@@ -63,0 +74,0 @@ const fixture = path.join(__dirname, './fixtures/typescript/ts-syntax-jsx-invalid.tsx')

@@ -14,2 +14,11 @@ const interleave = require('../lib/utils/tagged-template-literal').interleave

const mockLoc = (startLine, endLine) => ({
start: {
line: startLine
},
end: {
line: endLine
}
})
describe('utils', () => {

@@ -32,2 +41,3 @@ describe('interleave', () => {

{
loc: mockLoc(1, 3),
value: {

@@ -38,2 +48,3 @@ raw: '\n display: block;\n color: '

{
loc: mockLoc(3, 5),
value: {

@@ -46,2 +57,3 @@ raw: ';\n background: blue;\n'

{
loc: mockLoc(3, 3),
name: 'color'

@@ -58,2 +70,3 @@ }

{
loc: mockLoc(1, 3),
value: {

@@ -64,2 +77,3 @@ raw: '\n display: block;\n '

{
loc: mockLoc(3, 5),
value: {

@@ -72,2 +86,3 @@ raw: '\n background: blue;\n'

{
loc: mockLoc(3, 3),
name: undefined

@@ -84,2 +99,3 @@ }

{
loc: mockLoc(1, 3),
value: {

@@ -90,2 +106,3 @@ raw: '\n display: block;\n border: '

{
loc: mockLoc(3, 3),
value: {

@@ -96,2 +113,3 @@ raw: ' '

{
loc: mockLoc(3, 3),
value: {

@@ -102,2 +120,3 @@ raw: ' '

{
loc: mockLoc(3, 5),
value: {

@@ -110,8 +129,11 @@ raw: ';\n background: blue;\n'

{
loc: mockLoc(3, 3),
name: 'borderWidth'
},
{
loc: mockLoc(3, 3),
name: 'borderStyle'
},
{
loc: mockLoc(3, 3),
name: 'color'

@@ -128,2 +150,3 @@ }

{
loc: mockLoc(1, 2),
value: {

@@ -134,2 +157,3 @@ raw: '\n display: '

{
loc: mockLoc(2, 2),
value: {

@@ -140,2 +164,3 @@ raw: '; background: '

{
loc: mockLoc(2, 3),
value: {

@@ -148,5 +173,7 @@ raw: ';\n'

{
loc: mockLoc(2, 2),
name: 'display'
},
{
loc: mockLoc(2, 2),
name: 'background'

@@ -161,2 +188,3 @@ }

{
loc: mockLoc(1, 2),
value: {

@@ -167,2 +195,3 @@ raw: '\n display: '

{
loc: mockLoc(2, 2),
value: {

@@ -173,2 +202,3 @@ raw: '; '

{
loc: mockLoc(2, 3),
value: {

@@ -181,5 +211,7 @@ raw: '\n'

{
loc: mockLoc(2, 2),
name: 'display'
},
{
loc: mockLoc(3, 3),
name: undefined

@@ -199,2 +231,3 @@ }

{
loc: mockLoc(1, 2),
value: {

@@ -205,2 +238,3 @@ raw: '\n display: '

{
loc: mockLoc(2, 2),
value: {

@@ -211,2 +245,3 @@ raw: '; '

{
loc: mockLoc(2, 2),
value: {

@@ -217,2 +252,3 @@ raw: ' '

{
loc: mockLoc(2, 3),
value: {

@@ -225,8 +261,11 @@ raw: '\n'

{
loc: mockLoc(2, 2),
name: 'display'
},
{
loc: mockLoc(2, 2),
name: undefined
},
{
loc: mockLoc(2, 2),
name: undefined

@@ -357,11 +396,43 @@ }

const prevCSS3 = `
display: block;
/*
multiline comment with "*" and "/"
*/
`
expect(fn(prevCSS3)).toBe(true)
const prevCSS4 = `
display: block;
/*
* JSDoc style comment
*/
`
expect(fn(prevCSS4)).toBe(true)
const prevCSS5 = `
display: /* stylelint-disable */
`
expect(fn(prevCSS3)).toBe(false)
expect(fn(prevCSS5)).toBe(false)
const prevCSS4 = `
const prevCSS6 = `
display:
/* stylelint-disable */
`
expect(fn(prevCSS4)).toBe(false)
expect(fn(prevCSS6)).toBe(false)
const prevCSS7 = `
display:
/*
multiline comment with "*" and "/"
*/
`
expect(fn(prevCSS7)).toBe(false)
const prevCSS8 = `
display:
/*
* JSDoc style comment
*/
`
expect(fn(prevCSS8)).toBe(false)
})

@@ -456,3 +527,3 @@ })

const selectorExpression = prepExpression('selector')
expect(fn(selectorExpression, 1, 'path/to/file')).toBe('div')
expect(fn(selectorExpression, 1, 'path/to/file')).toBe('.sc-selector1')

@@ -478,3 +549,3 @@ const blockExpression = prepExpression('block')

expect(fn.bind(null, invalidExpression, 1, 'path/to/file')).toThrow(
/path\/to\/file line 1 column 3:\n.*invalid sc- tag/
/path\/to\/file:1:3:.*invalid sc- tag/
)

@@ -504,3 +575,3 @@ })

expect(fn.bind(this, commands, 'he', '/path/to/file', { line: 4, column: 6 })).toThrow(
/path\/to\/file line 4 column 6:/
/path\/to\/file:4:6:/
)

@@ -507,0 +578,0 @@ })

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc