This repository has been archived by the owner on Apr 21, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
David Clark
committed
Sep 20, 2016
1 parent
39af73a
commit a8cdc54
Showing
10 changed files
with
385 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": ["eslint-config-davidtheclark-node"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,27 @@ | ||
# stylelint-processor-arbitrary-tags | ||
A stylelint processor that allows you to lint CSS within arbitrary tags | ||
|
||
A [stylelint processor](http://stylelint.io/user-guide/configuration/#processors) that allows you to lint CSS within arbitrary tags. | ||
|
||
The module uses a regular expression to identify code within the specified tags, then passes the code on to stylelint. | ||
|
||
By default, it looks for code within `<style>` tags. | ||
|
||
## Options | ||
|
||
### startTag | ||
|
||
Type: `string` that's RegExp-ready | ||
|
||
Default: `'\\<style[\\s\\S]*?>'` | ||
|
||
### endTag | ||
|
||
Type: `string` that's RegExp-ready | ||
|
||
Default: `'</\\s*?style>'` | ||
|
||
### body | ||
|
||
Type: `string` that's RegExp-ready | ||
|
||
Default: `'[\\s\\S]*?'` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
'use strict'; | ||
|
||
const execall = require('execall'); | ||
const splitLines = require('split-lines'); | ||
const reindent = require('./lib/reindent'); | ||
|
||
const sourceToLineMap = new Map(); | ||
|
||
module.exports = function (options) { | ||
options = options || {}; | ||
options.startTag = options.startTag || '\\<style[\\s\\S]*?>'; | ||
options.endTag = options.endTag || '</\\s*?style>'; | ||
options.body = options.body || '[\\s\\S]*?'; | ||
|
||
const snippetRegexp = new RegExp(`(${options.startTag})(${options.body})${options.endTag}`, 'g'); | ||
|
||
function transformCode(code, filepath) { | ||
const extractedToSourceLineMap = new Map(); | ||
let extractedCode = ''; | ||
let currentExtractedCodeLine = 0; | ||
|
||
execall(snippetRegexp, code).forEach((match) => { | ||
const openingTag = match.sub[0]; | ||
const reindentData = reindent(match.sub[1]); | ||
const bodyText = reindentData.text; | ||
if (!bodyText) return; | ||
|
||
const startLine = splitLines(code.slice(0, match.index + openingTag.length)).length; | ||
const linesWithin = splitLines(bodyText).length; | ||
|
||
for (let i = 0; i < linesWithin; i++) { | ||
currentExtractedCodeLine += 1; | ||
extractedToSourceLineMap.set(currentExtractedCodeLine, { | ||
line: startLine + i, | ||
indentColumns: reindentData.indentColumns, | ||
}); | ||
} | ||
|
||
extractedCode += bodyText + '\n\n'; | ||
currentExtractedCodeLine += 1; | ||
}); | ||
|
||
sourceToLineMap.set(filepath, extractedToSourceLineMap); | ||
return extractedCode; | ||
} | ||
|
||
function transformResult(result, filepath) { | ||
const extractedToSourceLineMap = sourceToLineMap.get(filepath); | ||
result.warnings.forEach((warning) => { | ||
if (!warning.line) return; | ||
const warningSourceMap = extractedToSourceLineMap.get(warning.line); | ||
warning.line = warningSourceMap.line; | ||
warning.column = warning.column + warningSourceMap.indentColumns; | ||
}); | ||
} | ||
|
||
return { | ||
code: transformCode, | ||
result: transformResult, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// From https://github.com/sindresorhus/strip-indent, | ||
// but providing in the result a note about the length of indentation | ||
// that was stripped | ||
'use strict'; | ||
|
||
module.exports = function (str) { | ||
const match = str.match(/^[ \t]*(?=\S)/gm); | ||
|
||
if (!match) return str; | ||
|
||
const indent = Math.min.apply(Math, match.map((x) => x.length)); | ||
const re = new RegExp(`^[ \\t]{${indent}}`, 'gm'); | ||
|
||
return { | ||
text: indent > 0 ? str.replace(re, '') : str, | ||
indentColumns: indent, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "stylelint-processor-arbitrary-tags", | ||
"version": "0.1.0", | ||
"description": "A stylelint processor that allows you to lint CSS within arbitrary tags", | ||
"main": "index.js", | ||
"scripts": { | ||
"lint": "eslint .", | ||
"tap": "tap test/test.js", | ||
"test": "npm run tap && npm run lint" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/mapbox/stylelint-processor-arbitrary-tags.git" | ||
}, | ||
"keywords": [ | ||
"stylelint", | ||
"stylelint-processor" | ||
], | ||
"author": "Mapbox", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/mapbox/stylelint-processor-arbitrary-tags/issues" | ||
}, | ||
"homepage": "https://github.com/mapbox/stylelint-processor-arbitrary-tags#readme", | ||
"devDependencies": { | ||
"eslint": "^3.4.0", | ||
"eslint-config-davidtheclark-node": "^0.1.1", | ||
"eslint-plugin-node": "^2.0.0", | ||
"lodash": "^4.16.0", | ||
"stylelint": "^7.3.0", | ||
"tap": "^7.0.0" | ||
}, | ||
"dependencies": { | ||
"execall": "^1.0.0", | ||
"split-lines": "^1.1.0", | ||
"strip-indent": "^2.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<style>.foo {}</style> | ||
</head> | ||
<body> | ||
<style>.foo { color: pink; } | ||
.bar {} | ||
</style> | ||
|
||
<p>And some other text.</p> | ||
|
||
<style> | ||
.foo { color: pink; } | ||
.bar {}</style> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
--- | ||
title: Something Special | ||
--- | ||
|
||
Here is some text. | ||
|
||
{% | ||
highlight css | ||
%} | ||
.foo {} | ||
{% endhighlight %} | ||
|
||
And some other text. | ||
|
||
{% highlight css %} | ||
.foo { color: pink; } | ||
.bar {} | ||
{% endhighlight %} | ||
|
||
And the end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--- | ||
title: Something Special | ||
--- | ||
|
||
Here is some text. | ||
|
||
<style | ||
id="my-styles" | ||
data-foo="bar" | ||
> | ||
.foo {} | ||
</style> | ||
|
||
And some other text. | ||
|
||
<style> | ||
.foo { color: pink; } | ||
.bar {} | ||
</style> | ||
|
||
And the end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
'use strict'; | ||
|
||
const test = require('tap').test; | ||
const stylelint = require('stylelint'); | ||
const _ = require('lodash'); | ||
const path = require('path'); | ||
|
||
const pathToProcessor = path.join(__dirname, '../index.js'); | ||
|
||
const markdownExpectedWarnings = [ | ||
{ | ||
line: 11, | ||
column: 6, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
{ | ||
line: 17, | ||
column: 5, | ||
rule: 'indentation', | ||
severity: 'error', | ||
text: 'Expected indentation of 0 spaces (indentation)', | ||
}, | ||
{ | ||
line: 18, | ||
column: 8, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
]; | ||
|
||
test('markdown', (t) => { | ||
const fixture = path.join(__dirname, './fixtures/markdown.md'); | ||
stylelint.lint({ | ||
files: [fixture], | ||
config: { | ||
processors: [pathToProcessor], | ||
rules: { | ||
'block-no-empty': true, | ||
indentation: 2, | ||
}, | ||
}, | ||
}).then((data) => { | ||
t.equal(data.results.length, 1, 'number of results'); | ||
const result = data.results[0]; | ||
t.equal(result.source, fixture, 'filename'); | ||
t.deepEqual(_.orderBy(result.warnings, ['line', 'column']), markdownExpectedWarnings); | ||
t.end(); | ||
}).catch(t.threw); | ||
}); | ||
|
||
const htmlExpectedWarnings = [ | ||
{ | ||
line: 4, | ||
column: 6, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
{ | ||
line: 8, | ||
column: 7, | ||
rule: 'indentation', | ||
severity: 'error', | ||
text: 'Expected indentation of 0 spaces (indentation)', | ||
}, | ||
{ | ||
line: 8, | ||
column: 12, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
{ | ||
line: 15, | ||
column: 5, | ||
rule: 'indentation', | ||
severity: 'error', | ||
text: 'Expected indentation of 0 spaces (indentation)', | ||
}, | ||
{ | ||
line: 15, | ||
column: 10, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
]; | ||
|
||
test('html', (t) => { | ||
const fixture = path.join(__dirname, './fixtures/html.html'); | ||
stylelint.lint({ | ||
files: [fixture], | ||
config: { | ||
processors: [pathToProcessor], | ||
rules: { | ||
'block-no-empty': true, | ||
indentation: 2, | ||
}, | ||
}, | ||
}).then((data) => { | ||
t.equal(data.results.length, 1, 'number of results'); | ||
const result = data.results[0]; | ||
t.equal(result.source, fixture, 'filename'); | ||
|
||
t.deepEqual(_.orderBy(result.warnings, ['line', 'column']), htmlExpectedWarnings); | ||
t.end(); | ||
}).catch(t.threw); | ||
}); | ||
|
||
test('markdown and html', (t) => { | ||
const fixtureOne = path.join(__dirname, './fixtures/markdown.md'); | ||
const fixtureTwo = path.join(__dirname, './fixtures/html.html'); | ||
stylelint.lint({ | ||
files: [fixtureOne, fixtureTwo], | ||
config: { | ||
processors: [pathToProcessor], | ||
rules: { | ||
'block-no-empty': true, | ||
indentation: 2, | ||
}, | ||
}, | ||
}).then((data) => { | ||
t.equal(data.results.length, 2, 'number of results'); | ||
|
||
t.equal(data.results[0].source, fixtureOne); | ||
t.deepEqual(_.orderBy(data.results[0].warnings, ['line', 'column']), markdownExpectedWarnings); | ||
|
||
t.equal(data.results[1].source, fixtureTwo); | ||
t.deepEqual(_.orderBy(data.results[1].warnings, ['line', 'column']), htmlExpectedWarnings); | ||
|
||
t.end(); | ||
}).catch(t.threw); | ||
}); | ||
|
||
const liquidExpectedWarnings = [ | ||
{ | ||
line: 10, | ||
column: 6, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
{ | ||
line: 17, | ||
column: 10, | ||
rule: 'block-no-empty', | ||
severity: 'error', | ||
text: 'Unexpected empty block (block-no-empty)', | ||
}, | ||
]; | ||
|
||
test('liquid, custom tags', (t) => { | ||
const fixture = path.join(__dirname, './fixtures/liquid.md'); | ||
|
||
const options = { | ||
startTag: '\\{%\\s*highlight css\\s*%\\}', | ||
endTag: '\\{%\\s*endhighlight\\s*%\\}', | ||
}; | ||
|
||
stylelint.lint({ | ||
files: [fixture], | ||
config: { | ||
processors: [[pathToProcessor, options]], | ||
rules: { | ||
'block-no-empty': true, | ||
indentation: 2, | ||
}, | ||
}, | ||
}).then((data) => { | ||
t.equal(data.results.length, 1, 'number of results'); | ||
const result = data.results[0]; | ||
t.equal(result.source, fixture, 'filename'); | ||
t.deepEqual(_.orderBy(result.warnings, ['line', 'column']), liquidExpectedWarnings); | ||
t.end(); | ||
}).catch(t.threw); | ||
}); |