-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add stylelint theme alignment tool; harden settings for s2 (#3026
- Loading branch information
1 parent
1830942
commit 544a803
Showing
15 changed files
with
399 additions
and
287 deletions.
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,5 @@ | ||
--- | ||
"@spectrum-tools/theme-alignment": major | ||
--- | ||
|
||
Initial release of the stylelint theme alignment tool. This package uses the base file (themes/spectrum.css) for a Spectrum CSS component as a "source of truth" and validates the sub-themes (i.e., themes/express.css) use only selectors and custom properties defined in the base file. |
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,7 @@ | ||
--- | ||
"@spectrum-tools/stylelint-no-unknown-custom-properties": patch | ||
"@spectrum-tools/stylelint-no-unused-custom-properties": patch | ||
"@spectrum-tools/stylelint-no-missing-var": patch | ||
--- | ||
|
||
Dependency updates to align with versions used in the parent repository. |
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
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
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
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 |
---|---|---|
|
@@ -5,24 +5,32 @@ | |
"description": "Report on any unknown custom property definitions", | ||
"license": "Apache-2.0", | ||
"author": "Adobe", | ||
"contributors": [ | ||
"Cassondra Roberts <[email protected]>" | ||
], | ||
"type": "module", | ||
"main": "index.js", | ||
"files": [ | ||
"package.json", | ||
"index.js", | ||
"*.md" | ||
], | ||
"scripts": { | ||
"test": "ava" | ||
}, | ||
"dependencies": { | ||
"colors": "^1.4.0", | ||
"fast-glob": "^3.3.2", | ||
"postcss": "^8.4.45", | ||
"postcss-value-parser": "^4.2.0" | ||
"postcss": "^8.4.47", | ||
"postcss-values-parser": "^6.0.2" | ||
}, | ||
"peerDependencies": { | ||
"stylelint": ">=16.0.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^6.1.3", | ||
"c8": "^9.1.0", | ||
"stylelint": "^16.5.0" | ||
"c8": "^10.1.2", | ||
"stylelint": "^16.9.0" | ||
}, | ||
"keywords": [ | ||
"css", | ||
|
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
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 |
---|---|---|
|
@@ -4,22 +4,30 @@ | |
"description": "Report on any unused custom property definitions", | ||
"license": "Apache-2.0", | ||
"author": "Adobe", | ||
"contributors": [ | ||
"Cassondra Roberts <[email protected]>" | ||
], | ||
"type": "module", | ||
"main": "index.js", | ||
"files": [ | ||
"package.json", | ||
"index.js", | ||
"*.md" | ||
], | ||
"scripts": { | ||
"test": "ava" | ||
}, | ||
"dependencies": { | ||
"colors": "^1.4.0", | ||
"postcss-value-parser": "^4.2.0" | ||
"postcss-values-parser": "^6.0.2" | ||
}, | ||
"peerDependencies": { | ||
"stylelint": ">=16.0.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^6.1.3", | ||
"c8": "^9.1.0", | ||
"stylelint": "^16.5.0" | ||
"c8": "^10.1.2", | ||
"stylelint": "^16.9.0" | ||
}, | ||
"keywords": [ | ||
"css", | ||
|
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 @@ | ||
# Change 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 |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# stylelint-no-unused-custom-properties | ||
|
||
> Remove or report on unused variable definitions | ||
## Installation | ||
|
||
```sh | ||
yarn add -D @spectrum-tools/stylelint-no-unused-custom-properties | ||
``` | ||
|
||
## Usage | ||
|
||
Assuming you have some variables defined and rule(s) that use them: | ||
|
||
```css | ||
:root { | ||
--prefix-component-background-color: blue; | ||
--prefix-component-width: 10px; | ||
--prefix-component-height: 10px; | ||
--prefix-component-size: 10px; | ||
} | ||
|
||
.component { | ||
background-color: var(--prefix-component-background-color); | ||
|
||
width: var(--prefix-component-width); | ||
height: var(--prefix-component-height); | ||
} | ||
``` | ||
|
||
The variables that are not used in any rule will be removed from the output or reported to the console: | ||
|
||
```css | ||
:root { | ||
--prefix-component-background-color: blue; | ||
--prefix-component-width: 10px; | ||
--prefix-component-height: 10px; | ||
} | ||
|
||
.component { | ||
background-color: var(--prefix-component-background-color); | ||
|
||
width: var(--prefix-component-width); | ||
height: var(--prefix-component-height); | ||
} | ||
``` | ||
|
||
To allow variables to be defined without being used, such as when you want to pass custom properties down to a child component, you can add a `/* @passthrough */` comment to the variable definition: | ||
|
||
```css | ||
:root { | ||
/* @passthrough */ | ||
--nested-component-background-color: blue; | ||
--prefix-component-width: 10px; | ||
--prefix-component-height: 10px; | ||
--prefix-component-size: 10px; | ||
} | ||
``` | ||
|
||
To allow a group of properties to be passed down, you can prefix the set with `/* @passthrough start */` and suffix it with `/* @passthrough end */`: | ||
|
||
```css | ||
:root { | ||
/* @passthrough start */ | ||
--nested-component-background-color: blue; | ||
--nested-component-width: 10px; | ||
/* @passthrough end */ | ||
|
||
--prefix-component-height: 10px; | ||
--prefix-component-size: 10px; | ||
} | ||
``` | ||
|
||
## Options | ||
|
||
### `ignoreList` (default: `[]`) | ||
|
||
An array of strings or regular expressions that will be matched against the variable name. If a match is found, the variable will be ignored. |
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,143 @@ | ||
/*! | ||
* Copyright 2024 Adobe. All rights reserved. | ||
* This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. You may obtain a copy | ||
* of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under | ||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
* OF ANY KIND, either express or implied. See the License for the specific language | ||
* governing permissions and limitations under the License. | ||
*/ | ||
|
||
import fs from "node:fs"; | ||
import { relative, sep } from "node:path"; | ||
|
||
import postcss from "postcss"; | ||
import valuesParser from "postcss-values-parser"; | ||
import stylelint from "stylelint"; | ||
|
||
const { | ||
createPlugin, | ||
utils: { report, ruleMessages, validateOptions } | ||
} = stylelint; | ||
|
||
import "colors"; | ||
|
||
const ruleName = "spectrum-tools/theme-alignment"; | ||
const messages = ruleMessages(ruleName, { | ||
missing: (baseFile, sourceFile, rootPath) => `A base file (${relative(rootPath, baseFile)}) is required to validate ${relative(rootPath, sourceFile)}.`, | ||
// Report if a selector is in this file but not in the base file | ||
expected: (selector, baseFile, rootPath) => `Selector "${selector}" is not used or defined in the base file (${relative(rootPath, baseFile)}).`, | ||
// Report if a custom property is used in this file but not in the base file | ||
referenced: (property, baseFile, rootPath) => `Custom property "${property}" is not used or defined by the base file (${relative(rootPath, baseFile)}).`, | ||
}); | ||
|
||
/** @type {import('stylelint').Plugin} */ | ||
const ruleFunction = (enabled) => { | ||
return (root, result) => { | ||
const validOptions = validateOptions( | ||
result, | ||
ruleName, | ||
{ | ||
actual: enabled, | ||
possible: [true], | ||
}, | ||
); | ||
|
||
if (!validOptions) return; | ||
|
||
const sourceFile = root.source.input.file; | ||
const parts = sourceFile ? sourceFile.split(sep) : []; | ||
const isTheme = parts[parts.length - 2] === "themes"; | ||
const filename = parts[parts.length - 1]; | ||
|
||
if (!isTheme || filename === "spectrum.css") return; | ||
|
||
// All the parts of the source file but replace the filename with spectrum-two.css | ||
const baseFile = [...parts.slice(0, -1), "spectrum.css"].join(sep); | ||
const rootPath = parts.slice(0, -2).join(sep); | ||
|
||
// If the base file doesn't exist, throw an error | ||
if (!fs.existsSync(baseFile)) { | ||
report({ | ||
message: messages.missing, | ||
messageArgs: [baseFile, sourceFile, rootPath], | ||
node: root, | ||
result, | ||
ruleName, | ||
}); | ||
return; | ||
} | ||
|
||
// Read in the base file and parse it | ||
const baseContent = fs.readFileSync(baseFile, "utf8"); | ||
const baseRoot = postcss.parse(baseContent); | ||
|
||
/* A list of all selectors in the base file */ | ||
const baseSelectors = new Set(); | ||
/* A list of all properties in the base file */ | ||
const baseProperties = new Set(); | ||
|
||
/* Iterate over selectors in the base root */ | ||
baseRoot.walkRules((rule) => { | ||
// Add this selector to the selectors set | ||
baseSelectors.add(rule.selector); | ||
|
||
rule.walkDecls((decl) => { | ||
// If this is a custom property, add it to the properties set | ||
if (decl.prop.startsWith("--")) { | ||
baseProperties.add(decl.prop); | ||
} | ||
|
||
// If the value of this declaration includes a custom property, add it to the properties set | ||
const parsed = valuesParser.parse(decl.value); | ||
parsed.walk((node) => { | ||
if (node.type === "func" && node.value === "var") { | ||
baseProperties.add(node.nodes[0].value); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
/* Iterate over selectors in the source root and validate that they align with the base */ | ||
root.walkRules((rule) => { | ||
// Check if this selector exists in the base | ||
if (!baseSelectors.has(rule.selector)) { | ||
// Report any selectors that don't exist in the base | ||
report({ | ||
message: messages.expected, | ||
messageArgs: [rule.selector, baseFile, rootPath], | ||
node: rule, | ||
result, | ||
ruleName, | ||
}); | ||
return; | ||
} | ||
|
||
rule.walkDecls((decl) => { | ||
const isProperty = decl.prop.startsWith("--"); | ||
// @todo should report that this is setting something other than a custom property in the theme file | ||
if (!isProperty) { | ||
return; | ||
} | ||
|
||
// If this is a custom property, check if it's used in the base | ||
if (!baseProperties.has(decl.prop)) { | ||
report({ | ||
message: messages.referenced, | ||
messageArgs: [decl.prop, baseFile, rootPath], | ||
node: decl, | ||
result, | ||
ruleName, | ||
}); | ||
} | ||
}); | ||
}); | ||
}; | ||
}; | ||
|
||
ruleFunction.ruleName = ruleName; | ||
ruleFunction.messages = messages; | ||
|
||
export default createPlugin(ruleName, ruleFunction); |
Oops, something went wrong.