Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 72 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,49 +27,82 @@ rules: {

3. Advanced configuration

The stylelint plugin uses `enhanced-resolve` to find referenced files.
Each rule can configure the resolver with a `resolve` object.
By default, this stylelint plugin is configured with:

```js
{
resolve: {
extensions: ['.css'],
},
}
```

In order to find SASS files, add an additional extension:

```js
plugins: [
'stylelint-css-modules',
],
rules: {
'css-modules/composed-class-names': true,
'css-modules/css-variables': [true, {
resolve: {
extensions: ['.css', '.scss'],
},
}],
},
```
<details>
<summary>Default configuration</summary>

In order to leverage the SASS `~` operator, add an additional module:
The stylelint plugin uses `enhanced-resolve` to find referenced files.
Each rule can configure the resolver with a `resolve` object.
By default, this stylelint plugin is configured with:

```js
plugins: [
'stylelint-css-modules',
],
rules: {
'css-modules/composed-class-names': true,
'css-modules/css-variables': [true, {
```js
{
resolve: {
modules: ['node_modules', 'app/src'],
extensions: ['.css'],
modules: ['node_modules'],
},
}],
},
```
}
```
</details>

<details>
<summary>Configuring extensions</summary>

In order to find SASS files, add an additional extension:

```js
plugins: [
'stylelint-css-modules',
],
rules: {
'css-modules/composed-class-names': true,
'css-modules/css-variables': [true, {
resolve: {
extensions: ['.css', '.scss'],
},
}],
},
```
</details>

<details>
<summary>Configuring globally available variables</summary>

```js
plugins: [
'stylelint-css-modules',
],
rules: {
'css-modules/composed-class-names': true,
'css-modules/css-variables': [true, {
resolve: {
globals: [
path.join(__dirname, 'app/src/globals.css'),
],
},
}],
},
```
</details>

<details>
<summary>Configuring modules (e.g. `~` operator)</summary>

In order to leverage the SASS `~` operator, add an additional module:

```js
plugins: [
'stylelint-css-modules',
],
rules: {
'css-modules/composed-class-names': true,
'css-modules/css-variables': [true, {
resolve: {
modules: ['node_modules', 'app/src'],
},
}],
},
```
</details>

## Contributing

Expand Down
3 changes: 3 additions & 0 deletions fixtures/css-variables/globally-passes.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
border: var(--some-global-var);
}
3 changes: 3 additions & 0 deletions fixtures/css-variables/src/global-vars.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:root {
--some-global-var: 3px solid black;
}
6 changes: 5 additions & 1 deletion rules/css-variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ module.exports = stylelint.createPlugin('css-modules/css-variables', (primaryOpt
const defaultResolveOptions = {
extensions: ['.css'],
};
const options = secondaryOptionObject || { resolve: undefined };
const options = secondaryOptionObject || {};


function resolveFilePath(contextPath, filePath) {
const resolver = resolve.create.sync(Object.assign({}, defaultResolveOptions, options.resolve));
Expand Down Expand Up @@ -42,6 +43,8 @@ module.exports = stylelint.createPlugin('css-modules/css-variables', (primaryOpt
.filter(filePath => filePath.includes('.')) // TODO: Leverage Webpack extensions -- somehow
.map(filePath => resolveFilePath(contextPath, filePath))
;
const globals = (secondaryOptionObject.globals || [])
.map(filePath => resolveFilePath(contextPath, filePath));

root.walkDecls((decl) => {
const expressions = cssVariablesParser(decl.value);
Expand All @@ -50,6 +53,7 @@ module.exports = stylelint.createPlugin('css-modules/css-variables', (primaryOpt
variableValues
.filter(value => value.includes('--')) // Skip validation if the css variable had a non-var fallback
.filter(variable => !RegExp(`${variable}:`).test(decl.source.input.css)) // Is it defined locally?
.filter(variable => !globals.find(filePath => RegExp(`${variable}:`).test(fs.readFileSync(filePath)))) // Is it defined in a global file?
.filter(variable => !imports.find(filePath => RegExp(`${variable}:`).test(fs.readFileSync(filePath)))) // Is it defined in an imported file?
.forEach(variable => stylelint.utils.report({
index: decl.lastEach,
Expand Down
20 changes: 20 additions & 0 deletions rules/css-variables.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const stylelint = require('stylelint');
const path = require('path');

function configuration(options) {
return Object.assign({
Expand Down Expand Up @@ -26,6 +27,25 @@ test('css-variables rule parses correct usages of CSS variables', () => {
});
});

test('css-variables rule parses correct usages of globally available CSS variables', () => {
return stylelint.lint(configuration({
config: {
plugins: ['./css-variables.js'],
rules: {
'css-modules/css-variables': [true, {
globals: [
path.join(__dirname, '../fixtures/css-variables/src/global-vars.css'),
],
}],
},
},
files: require.resolve('../fixtures/css-variables/globally-passes.css'),
})).then(function (resultObject) {
const output = JSON.parse(resultObject.output);
expect(output[0].warnings).toEqual([]);
});
});

test('css-variables rule fails on undefined variable', () => {
return stylelint.lint(configuration({
files: require.resolve('../fixtures/css-variables/fails-undefined-variable.css'),
Expand Down