From dde827ddd44e4280bccfd1217a3658dc9f3f0c71 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Thu, 10 Nov 2022 07:46:55 -0800 Subject: [PATCH 01/19] Initial README restructure --- stylelint-polaris/README.md | 72 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/stylelint-polaris/README.md b/stylelint-polaris/README.md index c0ce7d9296f..3248f353184 100644 --- a/stylelint-polaris/README.md +++ b/stylelint-polaris/README.md @@ -1,62 +1,38 @@ -# Stylelint Polaris (WIP) +# Stylelint Polaris -## Experimental package structure +## Package structure ``` stylelint-polaris/ -├─ plugins/ -│ ├─ custom-properties-allowed-list.js -| | # (Optional) Public facing plugins for advanced configurations -| | # (See advanced config example below) -| ├─ index.js -| | -├─ configs/ -| | # Common rules for `polaris-react` and Polaris consumers -| ├─ shared.js -| | -| | # Applied in `polaris-react` containing: -| | # - shared.js -| | # - specific `custom-properties-allowed-list` rules -│ ├─ internal.js -| -| # Public facing config containing: -| # - shared.js -| # - specific `custom-properties-allowed-list` rules for Polaris consumers -├─ index.js +|-- plugins/ +| | # Custom plugin for categorizing built-in and custom rules +| |-- coverage.js +| | # Additional custom rules +| |-- custom-properties-allowed-list.js +| | # Plugins entry point +| | # (See advanced config example below) +| |__ index.js +| # Main stylelint-polaris config +|__ index.js ``` -### Polaris react usage +### Usage -```json5 -// polaris-react/package.json -{ - "stylelint": { - "extends": [ - "@shopify/stylelint-polaris/configs/internal" - ] - }, -}; -``` - -### Consumer usage - -#### Basic +### Basic ```json5 -// consumer/package.json +// package.json { "stylelint": { - "extends": [ - "@shopify/stylelint-polaris" - ] + "extends": ["@shopify/stylelint-polaris"] }, }; ``` -#### Advanced +### Advanced ```js -// consumer/stylelintrc.js +// .stylelintrc.js module.exports = { extends: ['@shopify/stylelint-polaris'], plugins: ['@shopify/stylelint-polaris/plugins'], @@ -79,8 +55,16 @@ module.exports = { yarn install ``` -2. Run `stylelint` on `polaris-react` +2. Build `@shopify/polaris` dependencies, but not `@shopify/polaris` itself + +```sh +yarn build -- --filter=@shopify/polaris^... +``` + +> Note: Remove the `^` character if you do want to build `@shopify/polaris` + +3. Run `stylelint` on `polaris-react` ```sh -yarn lint:stylelint +cd polaris-react && yarn lint:styles ``` From 17b51cc91130c61f16d4dd46810c4559f58f99c5 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Thu, 10 Nov 2022 07:46:55 -0800 Subject: [PATCH 02/19] Initial README restructure --- stylelint-polaris/README.md | 72 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/stylelint-polaris/README.md b/stylelint-polaris/README.md index c0ce7d9296f..3248f353184 100644 --- a/stylelint-polaris/README.md +++ b/stylelint-polaris/README.md @@ -1,62 +1,38 @@ -# Stylelint Polaris (WIP) +# Stylelint Polaris -## Experimental package structure +## Package structure ``` stylelint-polaris/ -├─ plugins/ -│ ├─ custom-properties-allowed-list.js -| | # (Optional) Public facing plugins for advanced configurations -| | # (See advanced config example below) -| ├─ index.js -| | -├─ configs/ -| | # Common rules for `polaris-react` and Polaris consumers -| ├─ shared.js -| | -| | # Applied in `polaris-react` containing: -| | # - shared.js -| | # - specific `custom-properties-allowed-list` rules -│ ├─ internal.js -| -| # Public facing config containing: -| # - shared.js -| # - specific `custom-properties-allowed-list` rules for Polaris consumers -├─ index.js +|-- plugins/ +| | # Custom plugin for categorizing built-in and custom rules +| |-- coverage.js +| | # Additional custom rules +| |-- custom-properties-allowed-list.js +| | # Plugins entry point +| | # (See advanced config example below) +| |__ index.js +| # Main stylelint-polaris config +|__ index.js ``` -### Polaris react usage +### Usage -```json5 -// polaris-react/package.json -{ - "stylelint": { - "extends": [ - "@shopify/stylelint-polaris/configs/internal" - ] - }, -}; -``` - -### Consumer usage - -#### Basic +### Basic ```json5 -// consumer/package.json +// package.json { "stylelint": { - "extends": [ - "@shopify/stylelint-polaris" - ] + "extends": ["@shopify/stylelint-polaris"] }, }; ``` -#### Advanced +### Advanced ```js -// consumer/stylelintrc.js +// .stylelintrc.js module.exports = { extends: ['@shopify/stylelint-polaris'], plugins: ['@shopify/stylelint-polaris/plugins'], @@ -79,8 +55,16 @@ module.exports = { yarn install ``` -2. Run `stylelint` on `polaris-react` +2. Build `@shopify/polaris` dependencies, but not `@shopify/polaris` itself + +```sh +yarn build -- --filter=@shopify/polaris^... +``` + +> Note: Remove the `^` character if you do want to build `@shopify/polaris` + +3. Run `stylelint` on `polaris-react` ```sh -yarn lint:stylelint +cd polaris-react && yarn lint:styles ``` From b203b5ac933c9a907fadd6bd838c7925e9d51898 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Thu, 10 Nov 2022 19:30:55 -0500 Subject: [PATCH 03/19] [stylelint-polaris] Categorize coverage rules (#7617) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### WHY are these changes introduced Closes [#7505](https://github.com/Shopify/polaris/issues/7505) ### WHAT is this pull request doing? ### How to 🎩 🖥 [Local development instructions](https://github.com/Shopify/polaris/blob/main/README.md#local-development) 🗒 [General tophatting guidelines](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md) 📄 [Changelog guidelines](https://github.com/Shopify/polaris/blob/main/.github/CONTRIBUTING.md#changelog)
Copy-paste this code in playground/Playground.tsx: ```jsx import React from 'react'; import {Page} from '../src'; export function Playground() { return ( {/* Add the code you want to test in here */} ); } ```
### 🎩 checklist - [ ] Tested on [mobile](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md#cross-browser-testing) - [ ] Tested on [multiple browsers](https://help.shopify.com/en/manual/shopify-admin/supported-browsers) - [ ] Tested for [accessibility](https://github.com/Shopify/polaris/blob/main/documentation/Accessibility%20testing.md) - [ ] Updated the component's `README.md` with documentation changes - [ ] [Tophatted documentation](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting%20documentation.md) changes in the style guide Co-authored-by: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> --- .changeset/purple-knives-enjoy.md | 5 + patches/stylelint+14.8.0.patch | 67 ++ stylelint-polaris/configs/coverage.js | 578 ++++++++++-------- stylelint-polaris/plugins/coverage/index.js | 89 ++- .../plugins/coverage/index.test.js | 15 +- stylelint-polaris/utils/index.js | 23 +- 6 files changed, 491 insertions(+), 286 deletions(-) create mode 100644 .changeset/purple-knives-enjoy.md create mode 100644 patches/stylelint+14.8.0.patch diff --git a/.changeset/purple-knives-enjoy.md b/.changeset/purple-knives-enjoy.md new file mode 100644 index 00000000000..856a028f0bc --- /dev/null +++ b/.changeset/purple-knives-enjoy.md @@ -0,0 +1,5 @@ +--- +'@shopify/stylelint-polaris': minor +--- + +Categorize coverage rules diff --git a/patches/stylelint+14.8.0.patch b/patches/stylelint+14.8.0.patch new file mode 100644 index 00000000000..ac5cc54fdf3 --- /dev/null +++ b/patches/stylelint+14.8.0.patch @@ -0,0 +1,67 @@ +diff --git a/node_modules/stylelint/lib/utils/checkAgainstRule.js b/node_modules/stylelint/lib/utils/checkAgainstRule.js +index 83706c5..feda9b8 100644 +--- a/node_modules/stylelint/lib/utils/checkAgainstRule.js ++++ b/node_modules/stylelint/lib/utils/checkAgainstRule.js +@@ -27,7 +27,9 @@ function checkAgainstRule(options, callback) { + + if (!options.ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); + +- const rule = rules[options.ruleName]; ++ const pluginFunction = options?.result?.stylelint?.config?.pluginFunctions?.[options.ruleName]; ++ ++ const rule = typeof pluginFunction === 'function' ? pluginFunction : rules[options.ruleName]; + + if (!rule) throw new Error(`Rule '${options.ruleName}' does not exist`); + +@@ -44,7 +46,7 @@ function checkAgainstRule(options, callback) { + // @ts-expect-error - this error should not occur with PostCSS 8 + const tmpPostcssResult = new Result(); + +- rule(settings[0], /** @type {O} */ (settings[1]), {})(options.root, tmpPostcssResult); ++ rule(settings[0], /** @type {O} */(settings[1]), {fix: options.fix})(options.root, tmpPostcssResult); + + for (const warning of tmpPostcssResult.warnings()) callback(warning); + } +diff --git a/node_modules/stylelint/lib/utils/report.js b/node_modules/stylelint/lib/utils/report.js +index 02a3c3c..5573371 100644 +--- a/node_modules/stylelint/lib/utils/report.js ++++ b/node_modules/stylelint/lib/utils/report.js +@@ -15,7 +15,7 @@ + * @type {typeof import('stylelint').utils.report} + */ + module.exports = function report(problem) { +- const { ruleName, result, message, line, node, index, endIndex, word } = problem; ++ const { ruleName, result, message, line, node, index, endIndex, word, severity: customSeverity } = problem; + + result.stylelint = result.stylelint || { + ruleSeverities: {}, +@@ -72,7 +72,7 @@ module.exports = function report(problem) { + } + } + +- const severity = result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName]; ++ const severity = customSeverity || result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName]; + + if (!result.stylelint.stylelintError && severity === 'error') { + result.stylelint.stylelintError = true; +diff --git a/node_modules/stylelint/types/stylelint/index.d.ts b/node_modules/stylelint/types/stylelint/index.d.ts +index b6ec792..fa1b3b9 100644 +--- a/node_modules/stylelint/types/stylelint/index.d.ts ++++ b/node_modules/stylelint/types/stylelint/index.d.ts +@@ -362,6 +362,7 @@ declare module 'stylelint' { + }; + word?: string; + line?: number; ++ severity?: Severity; + }; + + export type PublicApi = PostCSS.PluginCreator & { +@@ -436,7 +437,7 @@ declare module 'stylelint' { + * against a specific rule and do something with the warnings + */ + checkAgainstRule: ( +- options: { ruleName: string; ruleSettings: ConfigRuleSettings; root: PostCSS.Root }, ++ options: { ruleName: string; ruleSettings: ConfigRuleSettings; root: PostCSS.Root, result?: PostcssResult }, + callback: (warning: PostCSS.Warning) => void, + ) => void; + }; diff --git a/stylelint-polaris/configs/coverage.js b/stylelint-polaris/configs/coverage.js index 3e374719ff3..4636b280071 100644 --- a/stylelint-polaris/configs/coverage.js +++ b/stylelint-polaris/configs/coverage.js @@ -10,266 +10,338 @@ module.exports = { 'stylelint-scss', '../plugins/at-rule-disallowed-list', '../plugins/global-disallowed-list', + '../plugins/coverage', ], rules: { - 'at-rule-disallowed-list': [['keyframes'], {severity: 'warning'}], - 'color-named': ['never', {severity: 'warning'}], - 'color-no-hex': [true, {severity: 'warning'}], - 'declaration-property-value-disallowed-list': [ - { - display: ['grid', 'flex'], - top: [/(?!var\(--p-).+$/], - bottom: [/(?!var\(--p-).+$/], - left: [/(?!var\(--p-).+$/], - right: [/(?!var\(--p-).+$/], - width: [/(?!var\(--p-).+$/], - height: [/(?!var\(--p-).+$/], - // Allow `0`, `1`, values between 0 and 1 (limit 2 decimal places), and custom properties - // https://regex101.com/r/kIlVrQ/1 - opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], - 'z-index': [/(?!var\(--p-).+$/], - 'font-weight': [/(\$.*|[0-9]+)/], - }, - {severity: 'warning'}, - ], - 'function-disallowed-list': [ - [ - 'brightness', - 'contrast', - 'hue-rotate', - 'hsl', - 'hsla', - 'invert', - 'rgb', - 'rgba', - 'sepia', - // Include Sass namespace - // https://regex101.com/r/UdW0oV/1 - /([\w-]+\.)?available-names/, - /([\w-]+\.)?border-radius/, - /([\w-]+\.)?border-width/, - /([\w-]+\.)?border/, - /([\w-]+\.)?breakpoint/, - /([\w-]+\.)?color-multiply/, - /([\w-]+\.)?color/, - /([\w-]+\.)?control-height/, - /([\w-]+\.)?control-icon-transition/, - /([\w-]+\.)?control-slim-height/, - /([\w-]+\.)?control-vertical-padding/, - /([\w-]+\.)?duration/, - /([\w-]+\.)?easing/, - /([\w-]+\.)?em/, - /([\w-]+\.)?filter/, - /([\w-]+\.)?font-family/, - /([\w-]+\.)?font-size/, - /([\w-]+\.)?layout-width/, - /([\w-]+\.)?line-height/, - /([\w-]+\.)?map-extend/, - /([\w-]+\.)?mobile-nav-width/, - /([\w-]+\.)?ms-high-contrast-color/, - /([\w-]+\.)?nav-min-window-corrected/, - /([\w-]+\.)?px/, - /([\w-]+\.)?rem/, - /([\w-]+\.)?shadow/, - /([\w-]+\.)?spacing/, - /([\w-]+\.)?thumbnail-size/, - /([\w-]+\.)?top-bar-height/, - /([\w-]+\.)?z-index/, - ], - {severity: 'warning'}, - ], - 'property-disallowed-list': [ - [ - 'position', - 'grid', - 'flex', - 'flex-grow', - 'flex-shrink', - 'flex-basis', - 'justify-content', - 'align-items', - 'grid-row', - 'grid-row-start', - 'grid-row-end', - 'grid-column', - 'grid-column-start', - 'grid-column-end', - 'grid-template', - 'grid-template-areas', - 'grid-template-rows', - 'grid-template-columns', - 'grid-area', - 'display', - ], - {severity: 'warning'}, - ], - 'unit-disallowed-list': [ - ['px', 'rem', 'em', 's', 'ms'], - {severity: 'warning'}, - ], - 'scss/function-color-relative': [true, {severity: 'warning'}], - 'stylelint-polaris/at-rule-disallowed-list': [ - { - include: [ - // Legacy Scss mixins + 'stylelint-polaris/coverage': { + colors: { + 'color-named': 'never', + 'color-no-hex': true, + // TODO: Receiving an error that the rule doesn't exist + // 'scss/function-color-relative': true, + 'declaration-property-value-disallowed-list': { + opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], + }, + 'function-disallowed-list': [ + // Include Sass namespace // https://regex101.com/r/UdW0oV/1 - /([\w-]+\.)?after-topbar-sheet($|\()/, - /([\w-]+\.)?base-button-disabled($|\()/, - /([\w-]+\.)?breakpoint-after($|\()/, - /([\w-]+\.)?breakpoint-before($|\()/, - /([\w-]+\.)?button-base($|\()/, - /([\w-]+\.)?button-filled($|\()/, - /([\w-]+\.)?button-full-width($|\()/, - /([\w-]+\.)?button-outline-disabled($|\()/, - /([\w-]+\.)?button-outline($|\()/, - /([\w-]+\.)?color-icon($|\()/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?focus-ring($|\()/, - /([\w-]+\.)?frame-when-nav-displayed($|\()/, - /([\w-]+\.)?frame-when-nav-hidden($|\()/, - /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, - /([\w-]+\.)?hidden-when-printing($|\()/, - /([\w-]+\.)?high-contrast-border($|\()/, - /([\w-]+\.)?high-contrast-button-outline($|\()/, - /([\w-]+\.)?high-contrast-outline($|\()/, + 'brightness', + 'contrast', + 'hue-rotate', + 'hsl', + 'hsla', + 'invert', + 'rgb', + 'rgba', + 'sepia', + /([\w-]+\.)?color-multiply/, + /([\w-]+\.)?color/, + /([\w-]+\.)?filter/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + // mixins + /([\w-]+\.)?color-icon($|\()/, + /([\w-]+\.)?recolor-icon($|\()/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?ms-high-contrast-color/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-colors/, + /\$color-filter-palette-data/, + /\$color-palette-data/, + // Legacy custom properties + /--p-override-transparent/, + /--p-badge-mix-blend-mode/, + ], + }, + motion: { + 'at-rule-disallowed-list': ['keyframes'], + 'function-disallowed-list': [ + /([\w-]+\.)?control-icon-transition/, + /([\w-]+\.)?duration/, + /([\w-]+\.)?easing/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [/([\w-]+\.)?skeleton-shimmer($|\()/], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$duration-data/, + /\$polaris-duration-map/, + /\$skeleton-shimmer-duration/, + /\$easing-data/, + // Legacy custom properties + /--p-range-slider-thumb-scale/, + /--p-duration-1-0-0/, + /--p-duration-1-5-0/, + ], + }, + typography: { + 'declaration-property-value-disallowed-list': { + 'font-weight': [/(\$.*|[0-9]+)/], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?font-family/, + /([\w-]+\.)?font-size/, + /([\w-]+\.)?line-height/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?truncate($|\()/, + /([\w-]+\.)?text-breakword($|\()/, + /([\w-]+\.)?text-emphasis-normal($|\()/, + /([\w-]+\.)?text-emphasis-strong($|\()/, + /([\w-]+\.)?text-emphasis-subdued($|\()/, + /([\w-]+\.)?text-style-body($|\()/, + /([\w-]+\.)?text-style-button-large($|\()/, + /([\w-]+\.)?text-style-button($|\()/, + /([\w-]+\.)?text-style-caption($|\()/, + /([\w-]+\.)?text-style-display-large($|\()/, + /([\w-]+\.)?text-style-display-medium($|\()/, + /([\w-]+\.)?text-style-display-small($|\()/, + /([\w-]+\.)?text-style-display-x-large($|\()/, + /([\w-]+\.)?text-style-heading($|\()/, + /([\w-]+\.)?text-style-input($|\()/, + /([\w-]+\.)?text-style-subheading($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$typography-condensed/, + /\$typography-condensed/, + /\$base-font-size/, + /\$line-height-data/, + /\$font-family-data/, + /\$font-size-data/, + /\$default-browser-font-size/, + // Legacy custom properties + /--p-button-font/, + /--p-badge-font/, + ], + }, + layout: { + 'declaration-property-value-disallowed-list': [ + { + display: ['grid', 'flex'], + top: [/(?!var\(--p-).+$/], + bottom: [/(?!var\(--p-).+$/], + left: [/(?!var\(--p-).+$/], + right: [/(?!var\(--p-).+$/], + width: [/(?!var\(--p-).+$/], + height: [/(?!var\(--p-).+$/], + 'z-index': [/(?!var\(--p-).+$/], + }, + {severity: 'warning'}, + ], + 'property-disallowed-list': [ + [ + 'position', + 'grid', + 'flex', + 'flex-grow', + 'flex-shrink', + 'flex-basis', + 'justify-content', + 'align-items', + 'grid-row', + 'grid-row-start', + 'grid-row-end', + 'grid-column', + 'grid-column-start', + 'grid-column-end', + 'grid-template', + 'grid-template-areas', + 'grid-template-rows', + 'grid-template-columns', + 'grid-area', + 'display', + ], + {severity: 'warning'}, + ], + 'function-disallowed-list': [ + /([\w-]+\.)?nav-min-window-corrected/, + /([\w-]+\.)?control-height/, + /([\w-]+\.)?control-slim-height/, + /([\w-]+\.)?mobile-nav-width/, + /([\w-]+\.)?thumbnail-size/, /([\w-]+\.)?icon-size($|\()/, - /([\w-]+\.)?layout-flex-fix($|\()/, - /([\w-]+\.)?list-selected-indicator($|\()/, - /([\w-]+\.)?no-focus-ring($|\()/, - /([\w-]+\.)?page-actions-layout($|\()/, - /([\w-]+\.)?page-content-breakpoint-after($|\()/, - /([\w-]+\.)?page-content-breakpoint-before($|\()/, - /([\w-]+\.)?page-content-layout($|\()/, - /([\w-]+\.)?page-content-when-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, - /([\w-]+\.)?page-content-when-layout-stacked($|\()/, - /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, - /([\w-]+\.)?page-content-when-partially-condensed($|\()/, - /([\w-]+\.)?page-header-has-navigation($|\()/, - /([\w-]+\.)?page-header-has-secondary-actions($|\()/, - /([\w-]+\.)?page-header-layout($|\()/, - /([\w-]+\.)?page-header-without-navigation($|\()/, - /([\w-]+\.)?page-layout($|\()/, - /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, - /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, - /([\w-]+\.)?page-title-layout($|\()/, - /([\w-]+\.)?page-when-not-max-width($|\()/, - /([\w-]+\.)?plain-button-backdrop($|\()/, - /([\w-]+\.)?print-hidden($|\()/, - /([\w-]+\.)?range-thumb-selectors($|\()/, - /([\w-]+\.)?range-track-selectors($|\()/, - /([\w-]+\.)?recolor-icon($|\()/, + /([\w-]+\.)?top-bar-height/, + /([\w-]+\.)?z-index/, /([\w-]+\.)?safe-area-for($|\()/, - /([\w-]+\.)?skeleton-content($|\()/, - /([\w-]+\.)?skeleton-page-header-layout($|\()/, - /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, - /([\w-]+\.)?skeleton-shimmer($|\()/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?hidden-when-printing($|\()/, + /([\w-]+\.)?print-hidden($|\()/, + /([\w-]+\.)?layout-flex-fix($|\()/, + /([\w-]+\.)?skeleton-page-header-layout($|\()/, + /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$layout-width-data/, + /\$navigation-width/, + /\$small-thumbnail-size/, + /\$large-thumbnail-size/, + /\$medium-thumbnail-size/, + /\$thumbnail-sizes/, + // Legacy custom properties + /--p-range-slider-thumb-size-base/, + /--p-range-slider-thumb-size-active/, + /--p-override-visible/, + /--p-icon-size/, + /--p-choice-size/, + ], + }, + spacing: { + 'function-disallowed-list': [ + /([\w-]+\.)?control-vertical-padding/, + /([\w-]+\.)?em/, + /([\w-]+\.)?px/, + /([\w-]+\.)?rem/, + /([\w-]+\.)?spacing/, + ], + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-spacing/, + /\$spacing-data/, + /\$actions-vertical-spacing/, + // Legacy custom properties + /--p-button-group-item-spacing/, + /--p-choice-margin/, + /--p-text-field-spinner-offset/, + ], + }, + shape: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?border-radius/, + /([\w-]+\.)?border-width/, + /([\w-]+\.)?border/, + /([\w-]+\.)?high-contrast-border($|\()/, + /([\w-]+\.)?high-contrast-button-outline($|\()/, + /([\w-]+\.)?high-contrast-outline($|\()/, + /([\w-]+\.)?focus-ring($|\()/, + /([\w-]+\.)?no-focus-ring($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$border-radius-data/, + /\$border-width-data/, + /\$borders-data/, + // Legacy custom properties + /--p-border-radius-base/, + /--p-border-radius-wide/, + /--p-border-radius-full/, + /--p-control-border-width/, + /--p-thin-border-subdued/, + /--p-banner-border-default/, + /--p-banner-border-success/, + /--p-banner-border-highlight/, + /--p-banner-border-warning/, + /--p-banner-border-critical/, + /--p-text-field-focus-ring-border-radius/, + /--p-text-field-focus-ring-offset/, + ], + }, + depth: { + 'function-disallowed-list': [/([\w-]+\.)?shadow/], + 'stylelint-polaris/global-disallowed-list': [ + /\$shadows-data/, + /\$fixed-element-stacking-order/, + /\$global-elements/, + // Legacy custom properties + /--p-button-drop-shadow/, + /--p-button-inner-shadow/, + /--p-button-pressed-inner-shadow/, + /--p-card-shadow/, + /--p-popover-shadow/, + /--p-modal-shadow/, + /--p-top-bar-shadow/, + /--p-override-loading-z-index/, + ], + }, + conventions: { + 'unit-disallowed-list': [ + // TODO: Should 's' and 'ms' move to `motion`? + ['px', 'rem', 'em', 's', 'ms'], + {severity: 'warning'}, + ], + 'stylelint-polaris/global-disallowed-list': [ + / \* \$/, + // Legacy custom properties + /--p-override-none/, + /--p-override-one/, + /--p-override-zero/, + /--p-non-null-content/, + ], + }, + mediaQueries: { + 'function-disallowed-list': [ + /([\w-]+\.)?breakpoint/, + /([\w-]+\.)?layout-width/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?after-topbar-sheet($|\()/, + /([\w-]+\.)?breakpoint-after($|\()/, + /([\w-]+\.)?breakpoint-before($|\()/, + /([\w-]+\.)?frame-when-nav-displayed($|\()/, + /([\w-]+\.)?frame-when-nav-hidden($|\()/, + /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, + /([\w-]+\.)?page-actions-layout($|\()/, + /([\w-]+\.)?page-content-breakpoint-after($|\()/, + /([\w-]+\.)?page-content-breakpoint-before($|\()/, + /([\w-]+\.)?page-content-layout($|\()/, + /([\w-]+\.)?page-content-when-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, + /([\w-]+\.)?page-content-when-layout-stacked($|\()/, + /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, + /([\w-]+\.)?page-content-when-partially-condensed($|\()/, + /([\w-]+\.)?page-header-has-navigation($|\()/, + /([\w-]+\.)?page-header-has-secondary-actions($|\()/, + /([\w-]+\.)?page-header-layout($|\()/, + /([\w-]+\.)?page-header-without-navigation($|\()/, + /([\w-]+\.)?page-layout($|\()/, + /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, + /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, + /([\w-]+\.)?page-title-layout($|\()/, + /([\w-]+\.)?page-when-not-max-width($|\()/, + /([\w-]+\.)?when-typography-condensed($|\()/, + /([\w-]+\.)?when-typography-not-condensed($|\()/, + /([\w-]+\.)?when-not-printing($|\()/, + /([\w-]+\.)?when-printing($|\()/, + ], + }, + }, + legacySass: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?base-button-disabled($|\()/, + /([\w-]+\.)?button-base($|\()/, + /([\w-]+\.)?button-filled($|\()/, + /([\w-]+\.)?button-full-width($|\()/, + /([\w-]+\.)?button-outline-disabled($|\()/, + /([\w-]+\.)?button-outline($|\()/, + /([\w-]+\.)?plain-button-backdrop($|\()/, + /([\w-]+\.)?unstyled-button($|\()/, + /([\w-]+\.)?skeleton-content($|\()/, + /([\w-]+\.)?unstyled-input($|\()/, + /([\w-]+\.)?unstyled-link($|\()/, + /([\w-]+\.)?unstyled-list($|\()/, + /([\w-]+\.)?range-thumb-selectors($|\()/, + /([\w-]+\.)?range-track-selectors($|\()/, + /([\w-]+\.)?visually-hidden($|\()/, + ], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?available-names/, + /([\w-]+\.)?map-extend/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?list-selected-indicator($|\()/, /([\w-]+\.)?state($|\()/, - /([\w-]+\.)?text-breakword($|\()/, - /([\w-]+\.)?text-emphasis-normal($|\()/, - /([\w-]+\.)?text-emphasis-strong($|\()/, - /([\w-]+\.)?text-emphasis-subdued($|\()/, - /([\w-]+\.)?text-style-body($|\()/, - /([\w-]+\.)?text-style-button-large($|\()/, - /([\w-]+\.)?text-style-button($|\()/, - /([\w-]+\.)?text-style-caption($|\()/, - /([\w-]+\.)?text-style-display-large($|\()/, - /([\w-]+\.)?text-style-display-medium($|\()/, - /([\w-]+\.)?text-style-display-small($|\()/, - /([\w-]+\.)?text-style-display-x-large($|\()/, - /([\w-]+\.)?text-style-heading($|\()/, - /([\w-]+\.)?text-style-input($|\()/, - /([\w-]+\.)?text-style-subheading($|\()/, - /([\w-]+\.)?truncate($|\()/, - /([\w-]+\.)?unstyled-button($|\()/, - /([\w-]+\.)?unstyled-input($|\()/, - /([\w-]+\.)?unstyled-link($|\()/, - /([\w-]+\.)?unstyled-list($|\()/, - /([\w-]+\.)?visually-hidden($|\()/, - /([\w-]+\.)?when-not-printing($|\()/, - /([\w-]+\.)?when-printing($|\()/, - /([\w-]+\.)?when-typography-condensed($|\()/, - /([\w-]+\.)?when-typography-not-condensed($|\()/, ], }, - {severity: 'warning'}, - ], - 'stylelint-polaris/global-disallowed-list': [ - [ - // Legacy custom properties - /--p-button-font/, - /--p-badge-font/, - /--p-override-loading-z-index/, - /--p-override-none/, - /--p-override-transparent/, - /--p-override-one/, - /--p-override-visible/, - /--p-override-zero/, - /--p-non-null-content/, - /--p-badge-mix-blend-mode/, - /--p-range-slider-thumb-scale/, - /--p-duration-1-0-0/, - /--p-duration-1-5-0/, - /--p-border-radius-base/, - /--p-border-radius-wide/, - /--p-border-radius-full/, - /--p-text-field-focus-ring-border-radius/, - /--p-card-shadow/, - /--p-popover-shadow/, - /--p-modal-shadow/, - /--p-top-bar-shadow/, - /--p-button-drop-shadow/, - /--p-button-inner-shadow/, - /--p-button-pressed-inner-shadow/, - /--p-control-border-width/, - /--p-thin-border-subdued/, - /--p-banner-border-default/, - /--p-banner-border-success/, - /--p-banner-border-highlight/, - /--p-banner-border-warning/, - /--p-banner-border-critical/, - /--p-button-group-item-spacing/, - /--p-choice-margin/, - /--p-text-field-spinner-offset/, - /--p-text-field-focus-ring-offset/, - /--p-icon-size/, - /--p-range-slider-thumb-size-base/, - /--p-choice-size/, - /--p-range-slider-thumb-size-active/, - // Other warnings - / \* \$/, - /\$actions-vertical-spacing/, - /\$base-font-size/, - /\$border-radius-data/, - /\$border-width-data/, - /\$borders-data/, - /\$color-filter-palette-data/, - /\$color-palette-data/, - /\$default-browser-font-size/, - /\$duration-data/, - /\$easing-data/, - /\$fixed-element-stacking-order/, - /\$font-family-data/, - /\$font-size-data/, - /\$global-elements/, - /\$large-thumbnail-size/, - /\$layout-width-data/, - /\$line-height-data/, - /\$medium-thumbnail-size/, - /\$navigation-width/, - /\$polaris-colors/, - /\$polaris-duration-map/, - /\$polaris-spacing/, - /\$shadows-data/, - /\$skeleton-shimmer-duration/, - /\$small-thumbnail-size/, - /\$spacing-data/, - /\$thumbnail-sizes/, - /\$typography-condensed/, - /\$typography-condensed/, - ], - {severity: 'warning'}, - ], + }, }, }; diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index 5423352a398..ebb4df9556d 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -1,6 +1,6 @@ const stylelint = require('stylelint'); -const {isObject, isNumber} = require('../../utils'); +const {isPlainObject, isNumber} = require('../../utils'); const ruleName = 'stylelint-polaris/coverage'; @@ -10,24 +10,35 @@ const ruleName = 'stylelint-polaris/coverage'; * }} PrimaryOptions */ +// Setting `line` to an invalid line number forces the warning to be reported +// and the `report({node})` option is used to display the location information: +// https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 +const forceReport = {line: -1}; + module.exports = stylelint.createPlugin( ruleName, /** @param {PrimaryOptions} primaryOptions */ - (primaryOptions) => { + (primaryOptions, secondaryOptions, context) => { const isPrimaryOptionsValid = validatePrimaryOptions(primaryOptions); - const rules = !isPrimaryOptionsValid - ? [] - : Object.entries(primaryOptions).flatMap( - ([categoryName, categoryConfigRules]) => - Object.entries(categoryConfigRules).map( - ([categoryRuleName, categoryRuleSettings]) => ({ - categoryRuleName, - categoryRuleSettings, - coverageRuleName: `${ruleName}/${categoryName}`, - }), - ), - ); + const rules = []; + + for (const [categoryName, categoryConfigRules] of Object.entries( + primaryOptions, + )) { + for (const [categoryRuleName, categoryRuleSettings] of Object.entries( + categoryConfigRules, + )) { + rules.push({ + coverageRuleName: `${ruleName}/${categoryName}`, + categoryRuleName, + categoryRuleSettings, + categoryRuleSeverity: categoryRuleSettings?.[1]?.severity, + categoryRuleFix: + context.fix && !categoryRuleSettings?.[1]?.disableFix, + }); + } + } return (root, result) => { const validOptions = stylelint.utils.validateOptions(result, ruleName, { @@ -37,20 +48,34 @@ module.exports = stylelint.createPlugin( if (!validOptions) return; for (const rule of rules) { - const {categoryRuleName, categoryRuleSettings, coverageRuleName} = rule; + const { + coverageRuleName, + categoryRuleName, + categoryRuleSettings, + categoryRuleSeverity, + categoryRuleFix, + } = rule; stylelint.utils.checkAgainstRule( { ruleName: categoryRuleName, - ruleSettings: categoryRuleSettings, + ruleSettings: normalizeRuleSettings(categoryRuleSettings), + fix: categoryRuleFix, root, + result, }, (warning) => { stylelint.utils.report({ result, - node: warning.node, ruleName: coverageRuleName, - message: warning.text.replace(categoryRuleName, coverageRuleName), + message: warning.text, + severity: + categoryRuleSeverity ?? + result.stylelint.config?.defaultSeverity ?? + 'error', + // If `warning.node` is NOT present, the warning is + // referring to a misconfigured rule + ...(warning.node ? {node: warning.node} : forceReport), }); }, ); @@ -88,11 +113,7 @@ module.exports = stylelint.createPlugin( node: disabledRange.comment, // Note: `stylelint-disable` comments (without next-line) appear to // be special cased in that they do not trigger warnings when reported. - // Setting `line` to an invalid line number forces the warning to be - // reported and the above comment `node` is used to display the - // location information: - // https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 - line: -1, + ...forceReport, }); } }; @@ -134,11 +155,29 @@ function isUnclosedDisabledRange(disabledRange) { } function validatePrimaryOptions(primaryOptions) { - if (!isObject(primaryOptions)) return false; + if (!isPlainObject(primaryOptions)) return false; for (const categoryConfigRules of Object.values(primaryOptions)) { - if (!isObject(categoryConfigRules)) return false; + if (!isPlainObject(categoryConfigRules)) return false; } return true; } + +/** + * @param {import('stylelint').ConfigRuleSettings} ruleSettings + */ +function normalizeRuleSettings(ruleSettings) { + if ( + // Let `stylelint` normalize the rule settings + !Array.isArray(ruleSettings) || + // Assume rule settings are already normalized + Array.isArray(ruleSettings[0]) || + // Assume rule settings are already normalized + isPlainObject(ruleSettings[1]) + ) { + return ruleSettings; + } + + return [ruleSettings]; +} diff --git a/stylelint-polaris/plugins/coverage/index.test.js b/stylelint-polaris/plugins/coverage/index.test.js index dc82f11b66d..ddfafc63801 100644 --- a/stylelint-polaris/plugins/coverage/index.test.js +++ b/stylelint-polaris/plugins/coverage/index.test.js @@ -1,14 +1,20 @@ +const path = require('path'); + const {ruleName} = require('.'); const config = { motion: { 'at-rule-disallowed-list': [['keyframes'], {severity: 'warning'}], }, + legacy: { + // Test case for calling `checkAgainstRule` with custom rules + 'stylelint-polaris/global-disallowed-list': [/--p-legacy-var/], + }, }; testRule({ ruleName, - plugins: [__dirname], + plugins: [__dirname, path.resolve(__dirname, '../global-disallowed-list')], config, customSyntax: 'postcss-scss', accept: [ @@ -47,8 +53,13 @@ testRule({ { code: '@keyframes foo {}', description: 'Uses disallowed at-rule', + message: 'Unexpected at-rule "keyframes" (at-rule-disallowed-list)', + }, + { + code: '.class {color: var(--p-legacy-var);}', + description: 'Uses disallowed legacy variable', message: - 'Unexpected at-rule "keyframes" (stylelint-polaris/coverage/motion)', + 'Unexpected disallowed value "--p-legacy-var" (stylelint-polaris/global-disallowed-list)', }, { code: ` diff --git a/stylelint-polaris/utils/index.js b/stylelint-polaris/utils/index.js index 25eb90bccfd..d6392145880 100644 --- a/stylelint-polaris/utils/index.js +++ b/stylelint-polaris/utils/index.js @@ -182,13 +182,24 @@ function isNumber(value) { } /** - * Checks if the value is an object and not an array or null. - * https://github.com/jonschlinkert/isobject/blob/15d5d58ea9fbc632dffd52917ac6791cd92251ab/index.js#L9 - * @param {unknown} value + * Checks if a value is a plain object. + * + * An object is plain if it's created by either {}, new Object(), or Object.create(null). + * https://github.com/sindresorhus/is-plain-obj/blob/68e8cc77bb1bbd0bf7d629d3574b6ca70289b2cc/index.js#L1 */ -function isObject(value) { +function isPlainObject(value) { + if (typeof value !== 'object' || value === null) { + return false; + } + + const prototype = Object.getPrototypeOf(value); + return ( - value != null && typeof value === 'object' && Array.isArray(value) === false + (prototype === null || + prototype === Object.prototype || + Object.getPrototypeOf(prototype) === null) && + !(Symbol.toStringTag in value) && + !(Symbol.iterator in value) ); } @@ -223,7 +234,7 @@ module.exports.hasScssInterpolation = hasScssInterpolation; module.exports.isBoolean = isBoolean; module.exports.isCustomProperty = isCustomProperty; module.exports.isNumber = isNumber; -module.exports.isObject = isObject; +module.exports.isPlainObject = isPlainObject; module.exports.isRegExp = isRegExp; module.exports.isScssInterpolation = isScssInterpolation; module.exports.isString = isString; From ea15796c13e8e4d6757cb2da18e3e94203fe46a1 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:31:00 -0800 Subject: [PATCH 04/19] Consolidate `stylelint-polaris` configs (#7700) --- .stylelintrc.js | 17 +- .../ContextualSaveBar/ContextualSaveBar.scss | 5 +- stylelint-polaris/configs/coverage.js | 347 --------------- stylelint-polaris/configs/internal.js | 59 --- stylelint-polaris/configs/shared.js | 10 - stylelint-polaris/index.js | 406 +++++++++++++++--- 6 files changed, 361 insertions(+), 483 deletions(-) delete mode 100644 stylelint-polaris/configs/coverage.js delete mode 100644 stylelint-polaris/configs/internal.js delete mode 100644 stylelint-polaris/configs/shared.js diff --git a/.stylelintrc.js b/.stylelintrc.js index 860c982360c..e498b0f9a52 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,27 +1,20 @@ /** @type {import('stylelint').Config} */ module.exports = { extends: ['@shopify/stylelint-plugin/prettier', './stylelint-polaris'], - // Disabling @shopify/stylelint-plugin/configs/core no-unknown-animations as styelint + // Disabling @shopify/stylelint-plugin/configs/core no-unknown-animations as stylelint // is not aware of global Polaris keyframes // TODO: create custom plugin to ensure animation-names match Polaris keyframe names rules: { - 'no-unknown-animations': undefined, + 'no-unknown-animations': null, 'value-keyword-case': ['lower', {camelCaseSvgKeywords: true}], }, overrides: [ - { - files: ['polaris-react/**/*.{css,scss}'], - extends: [ - '@shopify/stylelint-plugin/prettier', - './stylelint-polaris/configs/internal', - ], - }, { files: ['polaris-migrator/**/tests/*.{css,scss}'], rules: { - 'comment-empty-line-before': undefined, - 'declaration-property-value-disallowed-list': undefined, - 'function-disallowed-list': undefined, + 'comment-empty-line-before': null, + 'declaration-property-value-disallowed-list': null, + 'function-disallowed-list': null, }, }, ], diff --git a/polaris-react/src/components/Frame/components/ContextualSaveBar/ContextualSaveBar.scss b/polaris-react/src/components/Frame/components/ContextualSaveBar/ContextualSaveBar.scss index 4ffc22d0b89..7ac77880be0 100644 --- a/polaris-react/src/components/Frame/components/ContextualSaveBar/ContextualSaveBar.scss +++ b/polaris-react/src/components/Frame/components/ContextualSaveBar/ContextualSaveBar.scss @@ -1,13 +1,12 @@ @import '../../../../styles/common'; .ContextualSaveBar { - // Used to apply dark theme to action buttons - /* stylelint-disable stylelint-polaris/custom-properties-allowed-list */ + /* stylelint-disable -- polaris: Used to apply dark theme to action buttons */ --p-surface: var(--p-surface-dark); --p-text: var(--p-text-on-dark); --p-action-secondary-hovered: var(--p-action-secondary-hovered-dark); --p-action-secondary-pressed: var(--p-action-secondary-pressed-dark); - /* stylelint-enable stylelint-polaris/custom-properties-allowed-list */ + /* stylelint-enable */ display: flex; height: $top-bar-height; background: var(--p-surface-dark); diff --git a/stylelint-polaris/configs/coverage.js b/stylelint-polaris/configs/coverage.js deleted file mode 100644 index 4636b280071..00000000000 --- a/stylelint-polaris/configs/coverage.js +++ /dev/null @@ -1,347 +0,0 @@ -/** - * Stylelint config to ensure compliance with Polaris and improve coverage. - */ - -/** - * @type {import('stylelint').Config} - */ -module.exports = { - plugins: [ - 'stylelint-scss', - '../plugins/at-rule-disallowed-list', - '../plugins/global-disallowed-list', - '../plugins/coverage', - ], - rules: { - 'stylelint-polaris/coverage': { - colors: { - 'color-named': 'never', - 'color-no-hex': true, - // TODO: Receiving an error that the rule doesn't exist - // 'scss/function-color-relative': true, - 'declaration-property-value-disallowed-list': { - opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], - }, - 'function-disallowed-list': [ - // Include Sass namespace - // https://regex101.com/r/UdW0oV/1 - 'brightness', - 'contrast', - 'hue-rotate', - 'hsl', - 'hsla', - 'invert', - 'rgb', - 'rgba', - 'sepia', - /([\w-]+\.)?color-multiply/, - /([\w-]+\.)?color/, - /([\w-]+\.)?filter/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - // mixins - /([\w-]+\.)?color-icon($|\()/, - /([\w-]+\.)?recolor-icon($|\()/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?ms-high-contrast-color/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$polaris-colors/, - /\$color-filter-palette-data/, - /\$color-palette-data/, - // Legacy custom properties - /--p-override-transparent/, - /--p-badge-mix-blend-mode/, - ], - }, - motion: { - 'at-rule-disallowed-list': ['keyframes'], - 'function-disallowed-list': [ - /([\w-]+\.)?control-icon-transition/, - /([\w-]+\.)?duration/, - /([\w-]+\.)?easing/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [/([\w-]+\.)?skeleton-shimmer($|\()/], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$duration-data/, - /\$polaris-duration-map/, - /\$skeleton-shimmer-duration/, - /\$easing-data/, - // Legacy custom properties - /--p-range-slider-thumb-scale/, - /--p-duration-1-0-0/, - /--p-duration-1-5-0/, - ], - }, - typography: { - 'declaration-property-value-disallowed-list': { - 'font-weight': [/(\$.*|[0-9]+)/], - }, - 'function-disallowed-list': [ - /([\w-]+\.)?font-family/, - /([\w-]+\.)?font-size/, - /([\w-]+\.)?line-height/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?truncate($|\()/, - /([\w-]+\.)?text-breakword($|\()/, - /([\w-]+\.)?text-emphasis-normal($|\()/, - /([\w-]+\.)?text-emphasis-strong($|\()/, - /([\w-]+\.)?text-emphasis-subdued($|\()/, - /([\w-]+\.)?text-style-body($|\()/, - /([\w-]+\.)?text-style-button-large($|\()/, - /([\w-]+\.)?text-style-button($|\()/, - /([\w-]+\.)?text-style-caption($|\()/, - /([\w-]+\.)?text-style-display-large($|\()/, - /([\w-]+\.)?text-style-display-medium($|\()/, - /([\w-]+\.)?text-style-display-small($|\()/, - /([\w-]+\.)?text-style-display-x-large($|\()/, - /([\w-]+\.)?text-style-heading($|\()/, - /([\w-]+\.)?text-style-input($|\()/, - /([\w-]+\.)?text-style-subheading($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$typography-condensed/, - /\$typography-condensed/, - /\$base-font-size/, - /\$line-height-data/, - /\$font-family-data/, - /\$font-size-data/, - /\$default-browser-font-size/, - // Legacy custom properties - /--p-button-font/, - /--p-badge-font/, - ], - }, - layout: { - 'declaration-property-value-disallowed-list': [ - { - display: ['grid', 'flex'], - top: [/(?!var\(--p-).+$/], - bottom: [/(?!var\(--p-).+$/], - left: [/(?!var\(--p-).+$/], - right: [/(?!var\(--p-).+$/], - width: [/(?!var\(--p-).+$/], - height: [/(?!var\(--p-).+$/], - 'z-index': [/(?!var\(--p-).+$/], - }, - {severity: 'warning'}, - ], - 'property-disallowed-list': [ - [ - 'position', - 'grid', - 'flex', - 'flex-grow', - 'flex-shrink', - 'flex-basis', - 'justify-content', - 'align-items', - 'grid-row', - 'grid-row-start', - 'grid-row-end', - 'grid-column', - 'grid-column-start', - 'grid-column-end', - 'grid-template', - 'grid-template-areas', - 'grid-template-rows', - 'grid-template-columns', - 'grid-area', - 'display', - ], - {severity: 'warning'}, - ], - 'function-disallowed-list': [ - /([\w-]+\.)?nav-min-window-corrected/, - /([\w-]+\.)?control-height/, - /([\w-]+\.)?control-slim-height/, - /([\w-]+\.)?mobile-nav-width/, - /([\w-]+\.)?thumbnail-size/, - /([\w-]+\.)?icon-size($|\()/, - /([\w-]+\.)?top-bar-height/, - /([\w-]+\.)?z-index/, - /([\w-]+\.)?safe-area-for($|\()/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?hidden-when-printing($|\()/, - /([\w-]+\.)?print-hidden($|\()/, - /([\w-]+\.)?layout-flex-fix($|\()/, - /([\w-]+\.)?skeleton-page-header-layout($|\()/, - /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$layout-width-data/, - /\$navigation-width/, - /\$small-thumbnail-size/, - /\$large-thumbnail-size/, - /\$medium-thumbnail-size/, - /\$thumbnail-sizes/, - // Legacy custom properties - /--p-range-slider-thumb-size-base/, - /--p-range-slider-thumb-size-active/, - /--p-override-visible/, - /--p-icon-size/, - /--p-choice-size/, - ], - }, - spacing: { - 'function-disallowed-list': [ - /([\w-]+\.)?control-vertical-padding/, - /([\w-]+\.)?em/, - /([\w-]+\.)?px/, - /([\w-]+\.)?rem/, - /([\w-]+\.)?spacing/, - ], - 'stylelint-polaris/global-disallowed-list': [ - /\$polaris-spacing/, - /\$spacing-data/, - /\$actions-vertical-spacing/, - // Legacy custom properties - /--p-button-group-item-spacing/, - /--p-choice-margin/, - /--p-text-field-spinner-offset/, - ], - }, - shape: { - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?border-radius/, - /([\w-]+\.)?border-width/, - /([\w-]+\.)?border/, - /([\w-]+\.)?high-contrast-border($|\()/, - /([\w-]+\.)?high-contrast-button-outline($|\()/, - /([\w-]+\.)?high-contrast-outline($|\()/, - /([\w-]+\.)?focus-ring($|\()/, - /([\w-]+\.)?no-focus-ring($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$border-radius-data/, - /\$border-width-data/, - /\$borders-data/, - // Legacy custom properties - /--p-border-radius-base/, - /--p-border-radius-wide/, - /--p-border-radius-full/, - /--p-control-border-width/, - /--p-thin-border-subdued/, - /--p-banner-border-default/, - /--p-banner-border-success/, - /--p-banner-border-highlight/, - /--p-banner-border-warning/, - /--p-banner-border-critical/, - /--p-text-field-focus-ring-border-radius/, - /--p-text-field-focus-ring-offset/, - ], - }, - depth: { - 'function-disallowed-list': [/([\w-]+\.)?shadow/], - 'stylelint-polaris/global-disallowed-list': [ - /\$shadows-data/, - /\$fixed-element-stacking-order/, - /\$global-elements/, - // Legacy custom properties - /--p-button-drop-shadow/, - /--p-button-inner-shadow/, - /--p-button-pressed-inner-shadow/, - /--p-card-shadow/, - /--p-popover-shadow/, - /--p-modal-shadow/, - /--p-top-bar-shadow/, - /--p-override-loading-z-index/, - ], - }, - conventions: { - 'unit-disallowed-list': [ - // TODO: Should 's' and 'ms' move to `motion`? - ['px', 'rem', 'em', 's', 'ms'], - {severity: 'warning'}, - ], - 'stylelint-polaris/global-disallowed-list': [ - / \* \$/, - // Legacy custom properties - /--p-override-none/, - /--p-override-one/, - /--p-override-zero/, - /--p-non-null-content/, - ], - }, - mediaQueries: { - 'function-disallowed-list': [ - /([\w-]+\.)?breakpoint/, - /([\w-]+\.)?layout-width/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?after-topbar-sheet($|\()/, - /([\w-]+\.)?breakpoint-after($|\()/, - /([\w-]+\.)?breakpoint-before($|\()/, - /([\w-]+\.)?frame-when-nav-displayed($|\()/, - /([\w-]+\.)?frame-when-nav-hidden($|\()/, - /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, - /([\w-]+\.)?page-actions-layout($|\()/, - /([\w-]+\.)?page-content-breakpoint-after($|\()/, - /([\w-]+\.)?page-content-breakpoint-before($|\()/, - /([\w-]+\.)?page-content-layout($|\()/, - /([\w-]+\.)?page-content-when-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, - /([\w-]+\.)?page-content-when-layout-stacked($|\()/, - /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, - /([\w-]+\.)?page-content-when-partially-condensed($|\()/, - /([\w-]+\.)?page-header-has-navigation($|\()/, - /([\w-]+\.)?page-header-has-secondary-actions($|\()/, - /([\w-]+\.)?page-header-layout($|\()/, - /([\w-]+\.)?page-header-without-navigation($|\()/, - /([\w-]+\.)?page-layout($|\()/, - /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, - /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, - /([\w-]+\.)?page-title-layout($|\()/, - /([\w-]+\.)?page-when-not-max-width($|\()/, - /([\w-]+\.)?when-typography-condensed($|\()/, - /([\w-]+\.)?when-typography-not-condensed($|\()/, - /([\w-]+\.)?when-not-printing($|\()/, - /([\w-]+\.)?when-printing($|\()/, - ], - }, - }, - legacySass: { - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?base-button-disabled($|\()/, - /([\w-]+\.)?button-base($|\()/, - /([\w-]+\.)?button-filled($|\()/, - /([\w-]+\.)?button-full-width($|\()/, - /([\w-]+\.)?button-outline-disabled($|\()/, - /([\w-]+\.)?button-outline($|\()/, - /([\w-]+\.)?plain-button-backdrop($|\()/, - /([\w-]+\.)?unstyled-button($|\()/, - /([\w-]+\.)?skeleton-content($|\()/, - /([\w-]+\.)?unstyled-input($|\()/, - /([\w-]+\.)?unstyled-link($|\()/, - /([\w-]+\.)?unstyled-list($|\()/, - /([\w-]+\.)?range-thumb-selectors($|\()/, - /([\w-]+\.)?range-track-selectors($|\()/, - /([\w-]+\.)?visually-hidden($|\()/, - ], - }, - 'function-disallowed-list': [ - /([\w-]+\.)?available-names/, - /([\w-]+\.)?map-extend/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?list-selected-indicator($|\()/, - /([\w-]+\.)?state($|\()/, - ], - }, - }, - }, -}; diff --git a/stylelint-polaris/configs/internal.js b/stylelint-polaris/configs/internal.js deleted file mode 100644 index 1ca11a41105..00000000000 --- a/stylelint-polaris/configs/internal.js +++ /dev/null @@ -1,59 +0,0 @@ -const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); - -/** - * Internal Stylelint config for @shopify/polaris - */ - -const { - ruleName: customPropertiesAllowedListRuleName, -} = require('../plugins/custom-properties-allowed-list'); -const { - ruleName: mediaQueriesAllowedList, -} = require('../plugins/media-queries-allowed-list'); - -/** - * Allowed Polaris token custom properties. - * - * Result: ['--p-background', '--p-text', etc...] - */ -const polarisCustomPropertyNames = getCustomPropertyNames(tokens); - -/** - * Allowed custom property names in Polaris component styles. - */ -const polarisComponentCustomProperties = /--pc-.+/; - -/** - * @type {import('stylelint').Config} - */ -module.exports = { - extends: ['./shared'], - plugins: [ - '../plugins/custom-properties-allowed-list', - '../plugins/media-queries-allowed-list', - ], - rules: { - [customPropertiesAllowedListRuleName]: { - allowedProperties: [ - '--polaris-version-number', - polarisComponentCustomProperties, - ], - allowedValues: { - '/.+/': [ - polarisComponentCustomProperties, - ...polarisCustomPropertyNames, - ], - }, - }, - [mediaQueriesAllowedList]: { - // Allowed media types and media conditions - // https://www.w3.org/TR/mediaqueries-5/#media - allowedMediaTypes: ['print', 'screen'], - allowedMediaFeatureNames: ['forced-colors', '-ms-high-contrast'], - allowedScssInterpolations: [ - // TODO: Add utility to @shopify/polaris-tokens to getMediaConditionNames - /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, - ], - }, - }, -}; diff --git a/stylelint-polaris/configs/shared.js b/stylelint-polaris/configs/shared.js deleted file mode 100644 index 35a64292be3..00000000000 --- a/stylelint-polaris/configs/shared.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Shared Stylelint config between @shopify/polaris and @shopify/polaris consumers - */ - -/** - * @type {import('stylelint').Config} - */ -module.exports = { - extends: ['./coverage'], -}; diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index c4152f9bc32..e28c698ca3c 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -1,67 +1,369 @@ const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); -const { - ruleName: customPropertiesAllowedListRuleName, -} = require('./plugins/custom-properties-allowed-list'); -const { - ruleName: mediaQueriesAllowedList, -} = require('./plugins/media-queries-allowed-list'); - -/** - * Allowed Polaris token custom properties. - * - * @example ['--p-text', '--p-background'] - */ -const polarisCustomPropertyNames = getCustomPropertyNames(tokens); - -/** - * User defined custom property names. - * - * Determined by allowing any custom property that is not prefixed with `--p-` or `--pc-`. - */ -const userDefinedCustomPropertyNames = /--(?!pc?-).+/; - -/** - * @type {import('stylelint').Config} - */ +/** @type {import('stylelint').Config} */ module.exports = { - extends: ['./configs/shared'], plugins: [ + 'stylelint-scss', + './plugins/coverage', + './plugins/global-disallowed-list', + './plugins/at-rule-disallowed-list', './plugins/custom-properties-allowed-list', './plugins/media-queries-allowed-list', ], rules: { - /** - * Custom property constraints: - * - Allow any user defined custom properties - * - Allow `--p-*` Polaris custom properties as values - * - Disallow `--p-*` Polaris custom properties as property overrides - * - Disallow `--pc-*` Polaris component custom properties as values and property overrides - */ - [customPropertiesAllowedListRuleName]: [ - { - allowedProperties: [userDefinedCustomPropertyNames], - allowedValues: { - '/.+/': [ - ...polarisCustomPropertyNames, - userDefinedCustomPropertyNames, + 'stylelint-polaris/coverage': { + colors: { + 'color-named': 'never', + 'color-no-hex': true, + 'scss/function-color-relative': true, + 'declaration-property-value-disallowed-list': { + opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], + }, + 'function-disallowed-list': [ + // Include Sass namespace + // https://regex101.com/r/UdW0oV/1 + 'brightness', + 'contrast', + 'hue-rotate', + 'hsl', + 'hsla', + 'invert', + 'rgb', + 'rgba', + 'sepia', + /([\w-]+\.)?color-multiply/, + /([\w-]+\.)?color/, + /([\w-]+\.)?filter/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + // mixins + /([\w-]+\.)?color-icon($|\()/, + /([\w-]+\.)?recolor-icon($|\()/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?ms-high-contrast-color/, ], }, + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-colors/, + /\$color-filter-palette-data/, + /\$color-palette-data/, + // Legacy custom properties + /--p-override-transparent/, + /--p-badge-mix-blend-mode/, + ], + }, + motion: { + 'at-rule-disallowed-list': ['keyframes'], + 'function-disallowed-list': [ + /([\w-]+\.)?control-icon-transition/, + /([\w-]+\.)?duration/, + /([\w-]+\.)?easing/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [/([\w-]+\.)?skeleton-shimmer($|\()/], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$duration-data/, + /\$polaris-duration-map/, + /\$skeleton-shimmer-duration/, + /\$easing-data/, + // Legacy custom properties + /--p-range-slider-thumb-scale/, + /--p-duration-1-0-0/, + /--p-duration-1-5-0/, + ], }, - {severity: 'warning'}, - ], - [mediaQueriesAllowedList]: [ - { - // Allowed media types and media conditions - // https://www.w3.org/TR/mediaqueries-5/#media - allowedMediaTypes: ['print', 'screen'], - allowedMediaFeatureNames: ['forced-colors', '-ms-high-contrast'], - allowedScssInterpolations: [ - // TODO: Add utility to @shopify/polaris-tokens to getMediaConditionNames - /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, + typography: { + 'declaration-property-value-disallowed-list': { + 'font-weight': [/(\$.*|[0-9]+)/], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?font-family/, + /([\w-]+\.)?font-size/, + /([\w-]+\.)?line-height/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?truncate($|\()/, + /([\w-]+\.)?text-breakword($|\()/, + /([\w-]+\.)?text-emphasis-normal($|\()/, + /([\w-]+\.)?text-emphasis-strong($|\()/, + /([\w-]+\.)?text-emphasis-subdued($|\()/, + /([\w-]+\.)?text-style-body($|\()/, + /([\w-]+\.)?text-style-button-large($|\()/, + /([\w-]+\.)?text-style-button($|\()/, + /([\w-]+\.)?text-style-caption($|\()/, + /([\w-]+\.)?text-style-display-large($|\()/, + /([\w-]+\.)?text-style-display-medium($|\()/, + /([\w-]+\.)?text-style-display-small($|\()/, + /([\w-]+\.)?text-style-display-x-large($|\()/, + /([\w-]+\.)?text-style-heading($|\()/, + /([\w-]+\.)?text-style-input($|\()/, + /([\w-]+\.)?text-style-subheading($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$typography-condensed/, + /\$typography-condensed/, + /\$base-font-size/, + /\$line-height-data/, + /\$font-family-data/, + /\$font-size-data/, + /\$default-browser-font-size/, + // Legacy custom properties + /--p-button-font/, + /--p-badge-font/, + ], + }, + layout: { + 'declaration-property-value-disallowed-list': [ + { + display: ['grid', 'flex'], + top: [/(?!var\(--p-).+$/], + bottom: [/(?!var\(--p-).+$/], + left: [/(?!var\(--p-).+$/], + right: [/(?!var\(--p-).+$/], + width: [/(?!var\(--p-).+$/], + height: [/(?!var\(--p-).+$/], + 'z-index': [/(?!var\(--p-).+$/], + }, + {severity: 'warning'}, + ], + 'property-disallowed-list': [ + [ + 'position', + 'grid', + 'flex', + 'flex-grow', + 'flex-shrink', + 'flex-basis', + 'justify-content', + 'align-items', + 'grid-row', + 'grid-row-start', + 'grid-row-end', + 'grid-column', + 'grid-column-start', + 'grid-column-end', + 'grid-template', + 'grid-template-areas', + 'grid-template-rows', + 'grid-template-columns', + 'grid-area', + 'display', + ], + {severity: 'warning'}, + ], + 'function-disallowed-list': [ + /([\w-]+\.)?nav-min-window-corrected/, + /([\w-]+\.)?control-height/, + /([\w-]+\.)?control-slim-height/, + /([\w-]+\.)?mobile-nav-width/, + /([\w-]+\.)?thumbnail-size/, + /([\w-]+\.)?icon-size($|\()/, + /([\w-]+\.)?top-bar-height/, + /([\w-]+\.)?z-index/, + /([\w-]+\.)?safe-area-for($|\()/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?hidden-when-printing($|\()/, + /([\w-]+\.)?print-hidden($|\()/, + /([\w-]+\.)?layout-flex-fix($|\()/, + /([\w-]+\.)?skeleton-page-header-layout($|\()/, + /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$layout-width-data/, + /\$navigation-width/, + /\$small-thumbnail-size/, + /\$large-thumbnail-size/, + /\$medium-thumbnail-size/, + /\$thumbnail-sizes/, + // Legacy custom properties + /--p-range-slider-thumb-size-base/, + /--p-range-slider-thumb-size-active/, + /--p-override-visible/, + /--p-icon-size/, + /--p-choice-size/, + ], + }, + spacing: { + 'function-disallowed-list': [ + /([\w-]+\.)?control-vertical-padding/, + /([\w-]+\.)?em/, + /([\w-]+\.)?px/, + /([\w-]+\.)?rem/, + /([\w-]+\.)?spacing/, + ], + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-spacing/, + /\$spacing-data/, + /\$actions-vertical-spacing/, + // Legacy custom properties + /--p-button-group-item-spacing/, + /--p-choice-margin/, + /--p-text-field-spinner-offset/, + ], + }, + shape: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?border-radius/, + /([\w-]+\.)?border-width/, + /([\w-]+\.)?border/, + /([\w-]+\.)?high-contrast-border($|\()/, + /([\w-]+\.)?high-contrast-button-outline($|\()/, + /([\w-]+\.)?high-contrast-outline($|\()/, + /([\w-]+\.)?focus-ring($|\()/, + /([\w-]+\.)?no-focus-ring($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$border-radius-data/, + /\$border-width-data/, + /\$borders-data/, + // Legacy custom properties + /--p-border-radius-base/, + /--p-border-radius-wide/, + /--p-border-radius-full/, + /--p-control-border-width/, + /--p-thin-border-subdued/, + /--p-banner-border-default/, + /--p-banner-border-success/, + /--p-banner-border-highlight/, + /--p-banner-border-warning/, + /--p-banner-border-critical/, + /--p-text-field-focus-ring-border-radius/, + /--p-text-field-focus-ring-offset/, + ], + }, + depth: { + 'function-disallowed-list': [/([\w-]+\.)?shadow/], + 'stylelint-polaris/global-disallowed-list': [ + /\$shadows-data/, + /\$fixed-element-stacking-order/, + /\$global-elements/, + // Legacy custom properties + /--p-button-drop-shadow/, + /--p-button-inner-shadow/, + /--p-button-pressed-inner-shadow/, + /--p-card-shadow/, + /--p-popover-shadow/, + /--p-modal-shadow/, + /--p-top-bar-shadow/, + /--p-override-loading-z-index/, + ], + }, + conventions: { + 'unit-disallowed-list': [ + // TODO: Should 's' and 'ms' move to `motion`? + ['px', 'rem', 'em', 's', 'ms'], + {severity: 'warning'}, + ], + 'stylelint-polaris/custom-properties-allowed-list': { + // Allow any custom property not prefixed with `--p-`, `--pc-`, or `--polaris-version-` + allowedProperties: [/--(?!(p|pc|polaris-version)-).+/], + allowedValues: { + '/.+/': [ + // Note: Order is important. + // The first pattern validates `--p-*` + // custom properties are valid Polaris tokens + ...getCustomPropertyNames(tokens), + // and the second pattern flags unknown `--p-*` custom properties + // or usages of our "private" `--pc-*` custom properties + /--(?!(p|pc)-).+/, + ], + }, + }, + 'stylelint-polaris/global-disallowed-list': [ + / \* \$/, + // Legacy custom properties + /--p-override-none/, + /--p-override-one/, + /--p-override-zero/, + /--p-non-null-content/, + ], + }, + mediaQueries: { + 'function-disallowed-list': [ + /([\w-]+\.)?breakpoint/, + /([\w-]+\.)?layout-width/, + ], + 'stylelint-polaris/media-queries-allowed-list': { + // Allowed media types and media conditions + // https://www.w3.org/TR/mediaqueries-5/#media + allowedMediaTypes: ['print', 'screen'], + allowedMediaFeatureNames: ['forced-colors', '-ms-high-contrast'], + allowedScssInterpolations: [ + // TODO: Add utility to @shopify/polaris-tokens to getMediaConditionNames + /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, + ], + }, + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?after-topbar-sheet($|\()/, + /([\w-]+\.)?breakpoint-after($|\()/, + /([\w-]+\.)?breakpoint-before($|\()/, + /([\w-]+\.)?frame-when-nav-displayed($|\()/, + /([\w-]+\.)?frame-when-nav-hidden($|\()/, + /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, + /([\w-]+\.)?page-actions-layout($|\()/, + /([\w-]+\.)?page-content-breakpoint-after($|\()/, + /([\w-]+\.)?page-content-breakpoint-before($|\()/, + /([\w-]+\.)?page-content-layout($|\()/, + /([\w-]+\.)?page-content-when-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, + /([\w-]+\.)?page-content-when-layout-stacked($|\()/, + /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, + /([\w-]+\.)?page-content-when-partially-condensed($|\()/, + /([\w-]+\.)?page-header-has-navigation($|\()/, + /([\w-]+\.)?page-header-has-secondary-actions($|\()/, + /([\w-]+\.)?page-header-layout($|\()/, + /([\w-]+\.)?page-header-without-navigation($|\()/, + /([\w-]+\.)?page-layout($|\()/, + /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, + /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, + /([\w-]+\.)?page-title-layout($|\()/, + /([\w-]+\.)?page-when-not-max-width($|\()/, + /([\w-]+\.)?when-typography-condensed($|\()/, + /([\w-]+\.)?when-typography-not-condensed($|\()/, + /([\w-]+\.)?when-not-printing($|\()/, + /([\w-]+\.)?when-printing($|\()/, + ], + }, + }, + legacySass: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?base-button-disabled($|\()/, + /([\w-]+\.)?button-base($|\()/, + /([\w-]+\.)?button-filled($|\()/, + /([\w-]+\.)?button-full-width($|\()/, + /([\w-]+\.)?button-outline-disabled($|\()/, + /([\w-]+\.)?button-outline($|\()/, + /([\w-]+\.)?plain-button-backdrop($|\()/, + /([\w-]+\.)?unstyled-button($|\()/, + /([\w-]+\.)?skeleton-content($|\()/, + /([\w-]+\.)?unstyled-input($|\()/, + /([\w-]+\.)?unstyled-link($|\()/, + /([\w-]+\.)?unstyled-list($|\()/, + /([\w-]+\.)?range-thumb-selectors($|\()/, + /([\w-]+\.)?range-track-selectors($|\()/, + /([\w-]+\.)?visually-hidden($|\()/, + ], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?available-names/, + /([\w-]+\.)?map-extend/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?list-selected-indicator($|\()/, + /([\w-]+\.)?state($|\()/, ], }, - {severity: 'warning'}, - ], + }, }, }; From 424839cb88a69545e24a32205015564877330bfc Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 15 Nov 2022 10:43:38 -0800 Subject: [PATCH 05/19] Remove Stylelint patches and upgrade version to `^14.15.0` (#7719) --- package.json | 2 +- patches/stylelint+14.8.0.patch | 67 --------------- stylelint-polaris/package.json | 2 +- yarn.lock | 153 +++++++++++++++++---------------- 4 files changed, 79 insertions(+), 145 deletions(-) delete mode 100644 patches/stylelint+14.8.0.patch diff --git a/package.json b/package.json index 1ce4217243c..66e5ec58f2e 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "rollup": "^2.70.2", "rollup-plugin-node-externals": "^4.0.0", "size-limit": "^5.0.3", - "stylelint": "^14.1.0", + "stylelint": "^14.15.0", "ts-node": "^10.7.0", "turbo": "^1.2.8", "typescript": "^4.6.3" diff --git a/patches/stylelint+14.8.0.patch b/patches/stylelint+14.8.0.patch deleted file mode 100644 index ac5cc54fdf3..00000000000 --- a/patches/stylelint+14.8.0.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/node_modules/stylelint/lib/utils/checkAgainstRule.js b/node_modules/stylelint/lib/utils/checkAgainstRule.js -index 83706c5..feda9b8 100644 ---- a/node_modules/stylelint/lib/utils/checkAgainstRule.js -+++ b/node_modules/stylelint/lib/utils/checkAgainstRule.js -@@ -27,7 +27,9 @@ function checkAgainstRule(options, callback) { - - if (!options.ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); - -- const rule = rules[options.ruleName]; -+ const pluginFunction = options?.result?.stylelint?.config?.pluginFunctions?.[options.ruleName]; -+ -+ const rule = typeof pluginFunction === 'function' ? pluginFunction : rules[options.ruleName]; - - if (!rule) throw new Error(`Rule '${options.ruleName}' does not exist`); - -@@ -44,7 +46,7 @@ function checkAgainstRule(options, callback) { - // @ts-expect-error - this error should not occur with PostCSS 8 - const tmpPostcssResult = new Result(); - -- rule(settings[0], /** @type {O} */ (settings[1]), {})(options.root, tmpPostcssResult); -+ rule(settings[0], /** @type {O} */(settings[1]), {fix: options.fix})(options.root, tmpPostcssResult); - - for (const warning of tmpPostcssResult.warnings()) callback(warning); - } -diff --git a/node_modules/stylelint/lib/utils/report.js b/node_modules/stylelint/lib/utils/report.js -index 02a3c3c..5573371 100644 ---- a/node_modules/stylelint/lib/utils/report.js -+++ b/node_modules/stylelint/lib/utils/report.js -@@ -15,7 +15,7 @@ - * @type {typeof import('stylelint').utils.report} - */ - module.exports = function report(problem) { -- const { ruleName, result, message, line, node, index, endIndex, word } = problem; -+ const { ruleName, result, message, line, node, index, endIndex, word, severity: customSeverity } = problem; - - result.stylelint = result.stylelint || { - ruleSeverities: {}, -@@ -72,7 +72,7 @@ module.exports = function report(problem) { - } - } - -- const severity = result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName]; -+ const severity = customSeverity || result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName]; - - if (!result.stylelint.stylelintError && severity === 'error') { - result.stylelint.stylelintError = true; -diff --git a/node_modules/stylelint/types/stylelint/index.d.ts b/node_modules/stylelint/types/stylelint/index.d.ts -index b6ec792..fa1b3b9 100644 ---- a/node_modules/stylelint/types/stylelint/index.d.ts -+++ b/node_modules/stylelint/types/stylelint/index.d.ts -@@ -362,6 +362,7 @@ declare module 'stylelint' { - }; - word?: string; - line?: number; -+ severity?: Severity; - }; - - export type PublicApi = PostCSS.PluginCreator & { -@@ -436,7 +437,7 @@ declare module 'stylelint' { - * against a specific rule and do something with the warnings - */ - checkAgainstRule: ( -- options: { ruleName: string; ruleSettings: ConfigRuleSettings; root: PostCSS.Root }, -+ options: { ruleName: string; ruleSettings: ConfigRuleSettings; root: PostCSS.Root, result?: PostcssResult }, - callback: (warning: PostCSS.Warning) => void, - ) => void; - }; diff --git a/stylelint-polaris/package.json b/stylelint-polaris/package.json index 9d992ccee84..8c3c2a99453 100644 --- a/stylelint-polaris/package.json +++ b/stylelint-polaris/package.json @@ -37,7 +37,7 @@ "@shopify/polaris-tokens": "^6.3.0" }, "peerDependencies": { - "stylelint": "^14.1.0" + "stylelint": "^14.15.0" }, "jest": { "preset": "jest-preset-stylelint" diff --git a/yarn.lock b/yarn.lock index 58db01fe291..ffa5aab1460 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2276,6 +2276,11 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" +"@csstools/selector-specificity@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36" + integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== + "@discoveryjs/json-ext@^0.5.3": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -7745,13 +7750,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-regexp@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" - integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== - dependencies: - is-regexp "^2.0.0" - clone-response@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" @@ -7872,11 +7870,16 @@ color-support@^1.1.2, color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colord@^2.9.1, colord@^2.9.2: +colord@^2.9.1: version "2.9.2" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + colorette@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" @@ -8209,7 +8212,7 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: +cosmiconfig@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== @@ -8220,6 +8223,17 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cp-file@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" @@ -8392,10 +8406,10 @@ css-declaration-sorter@^6.2.2: resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz#bfd2f6f50002d6a3ae779a87d3a0c5d5b10e0f02" integrity sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg== -css-functions-list@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.0.1.tgz#1460df7fb584d1692c30b105151dbb988c8094f9" - integrity sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw== +css-functions-list@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.1.0.tgz#cf5b09f835ad91a00e5959bcfc627cd498e1321b" + integrity sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w== css-in-js-utils@^2.0.0: version "2.0.1" @@ -10041,13 +10055,6 @@ execa@^6.0.0, execa@^6.1.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" -execall@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" - integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== - dependencies: - clone-regexp "^2.1.0" - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -10290,7 +10297,7 @@ fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.5: +fast-glob@^3.2.12, fast-glob@^3.2.5: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -10350,10 +10357,10 @@ fast-uri@^2.0.0, fast-uri@^2.1.0: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-2.1.0.tgz#9279432d6b53675c90116b947ed2bbba582d6fb5" integrity sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA== -fastest-levenshtein@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" - integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastest-stable-stringify@^2.0.2: version "2.0.2" @@ -11174,11 +11181,6 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -get-stdin@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" - integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -13020,11 +13022,6 @@ is-regex@^1.1.2, is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-regexp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" - integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== - is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -14109,10 +14106,10 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== -known-css-properties@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.24.0.tgz#19aefd85003ae5698a5560d2b55135bf5432155c" - integrity sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA== +known-css-properties@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.26.0.tgz#008295115abddc045a9f4ed7e2a84dc8b3a77649" + integrity sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg== koa-compose@^4.1.0: version "4.1.0" @@ -16132,11 +16129,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -normalize-selector@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" - integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= - normalize-url@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" @@ -17767,7 +17759,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.2.1, postcss@^8.2.15, postcss@^8.3.1, postcss@^8.3.11, postcss@^8.4.12: +postcss@^8.2.1, postcss@^8.2.15, postcss@^8.3.1, postcss@^8.3.11: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== @@ -17776,6 +17768,15 @@ postcss@^8.2.1, postcss@^8.2.15, postcss@^8.3.1, postcss@^8.3.11, postcss@^8.4.1 picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.19: + version "8.4.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.19.tgz#61178e2add236b17351897c8bcc0b4c8ecab56fc" + integrity sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + postinstall-postinstall@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" @@ -20027,11 +20028,6 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -specificity@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" - integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== - split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" @@ -20531,21 +20527,20 @@ stylelint-scss@^4.0.0: postcss-selector-parser "^6.0.6" postcss-value-parser "^4.1.0" -stylelint@^14.1.0: - version "14.8.0" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.8.0.tgz#506959bc1424610cdac9eaefca8fb2420292cf73" - integrity sha512-uIyIWMSBSVcj73Gn3nTvPyNsYdwTpxo1W6dWTIa1nm8JKgUi3FIobSXLgrRE6joLidoA0FdgAhCaqxwTF2ikrQ== +stylelint@^14.15.0: + version "14.15.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.15.0.tgz#4df55078e734869f81f6b85bbec2d56a4b478ece" + integrity sha512-JOgDAo5QRsqiOZPZO+B9rKJvBm64S0xasbuRPAbPs6/vQDgDCnZLIiw6XcAS6GQKk9k1sBWR6rmH3Mfj8OknKg== dependencies: + "@csstools/selector-specificity" "^2.0.2" balanced-match "^2.0.0" - colord "^2.9.2" - cosmiconfig "^7.0.1" - css-functions-list "^3.0.1" + colord "^2.9.3" + cosmiconfig "^7.1.0" + css-functions-list "^3.1.0" debug "^4.3.4" - execall "^2.0.0" - fast-glob "^3.2.11" - fastest-levenshtein "^1.0.12" + fast-glob "^3.2.12" + fastest-levenshtein "^1.0.16" file-entry-cache "^6.0.1" - get-stdin "^8.0.0" global-modules "^2.0.0" globby "^11.1.0" globjoin "^0.1.4" @@ -20554,29 +20549,27 @@ stylelint@^14.1.0: import-lazy "^4.0.0" imurmurhash "^0.1.4" is-plain-object "^5.0.0" - known-css-properties "^0.24.0" + known-css-properties "^0.26.0" mathml-tag-names "^2.1.3" meow "^9.0.0" micromatch "^4.0.5" normalize-path "^3.0.0" - normalize-selector "^0.2.0" picocolors "^1.0.0" - postcss "^8.4.12" + postcss "^8.4.19" postcss-media-query-parser "^0.2.3" postcss-resolve-nested-selector "^0.1.1" postcss-safe-parser "^6.0.0" postcss-selector-parser "^6.0.10" postcss-value-parser "^4.2.0" resolve-from "^5.0.0" - specificity "^0.4.1" string-width "^4.2.3" strip-ansi "^6.0.1" style-search "^0.1.0" - supports-hyperlinks "^2.2.0" + supports-hyperlinks "^2.3.0" svg-tags "^1.0.0" - table "^6.8.0" + table "^6.8.1" v8-compile-cache "^2.3.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" stylis@^4.0.6: version "4.1.2" @@ -20617,6 +20610,14 @@ supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-hyperlinks@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -20683,10 +20684,10 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" -table@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" - integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== +table@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: ajv "^8.0.1" lodash.truncate "^4.4.2" @@ -22699,10 +22700,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" From 41cd2ac602f1d7750468214259947eefcd3ca2f7 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Wed, 16 Nov 2022 19:23:57 -0500 Subject: [PATCH 06/19] add customSyntax SCSS --- stylelint-polaris/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index e28c698ca3c..f2fd123a21d 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -2,6 +2,7 @@ const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); /** @type {import('stylelint').Config} */ module.exports = { + customSyntax: 'postcss-scss', plugins: [ 'stylelint-scss', './plugins/coverage', From 9843c80a12d7d466d934e116108762c6b8e564d2 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:12:02 -0800 Subject: [PATCH 07/19] Remove custom disable comment logic in favor of built-in Stylelint configurations --- stylelint-polaris/index.js | 726 +++++++++--------- stylelint-polaris/plugins/coverage/index.js | 92 +-- .../plugins/coverage/index.test.js | 61 +- 3 files changed, 376 insertions(+), 503 deletions(-) diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index f2fd123a21d..9b1f1bdb2c4 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -1,8 +1,379 @@ const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); +/** @type {import('./plugins/coverage').PrimaryOptions} */ +const stylelintPolarisCoverageOptions = { + colors: { + 'color-named': 'never', + 'color-no-hex': true, + 'scss/function-color-relative': true, + 'declaration-property-value-disallowed-list': { + opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], + }, + 'function-disallowed-list': [ + // Include Sass namespace + // https://regex101.com/r/UdW0oV/1 + 'brightness', + 'contrast', + 'hue-rotate', + 'hsl', + 'hsla', + 'invert', + 'rgb', + 'rgba', + 'sepia', + /([\w-]+\.)?color-multiply/, + /([\w-]+\.)?color/, + /([\w-]+\.)?filter/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + // mixins + /([\w-]+\.)?color-icon($|\()/, + /([\w-]+\.)?recolor-icon($|\()/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?ms-high-contrast-color/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-colors/, + /\$color-filter-palette-data/, + /\$color-palette-data/, + // Legacy custom properties + /--p-override-transparent/, + /--p-badge-mix-blend-mode/, + ], + }, + motion: { + 'at-rule-disallowed-list': ['keyframes'], + 'function-disallowed-list': [ + /([\w-]+\.)?control-icon-transition/, + /([\w-]+\.)?duration/, + /([\w-]+\.)?easing/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [/([\w-]+\.)?skeleton-shimmer($|\()/], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$duration-data/, + /\$polaris-duration-map/, + /\$skeleton-shimmer-duration/, + /\$easing-data/, + // Legacy custom properties + /--p-range-slider-thumb-scale/, + /--p-duration-1-0-0/, + /--p-duration-1-5-0/, + ], + }, + typography: { + 'declaration-property-value-disallowed-list': { + 'font-weight': [/(\$.*|[0-9]+)/], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?font-family/, + /([\w-]+\.)?font-size/, + /([\w-]+\.)?line-height/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?truncate($|\()/, + /([\w-]+\.)?text-breakword($|\()/, + /([\w-]+\.)?text-emphasis-normal($|\()/, + /([\w-]+\.)?text-emphasis-strong($|\()/, + /([\w-]+\.)?text-emphasis-subdued($|\()/, + /([\w-]+\.)?text-style-body($|\()/, + /([\w-]+\.)?text-style-button-large($|\()/, + /([\w-]+\.)?text-style-button($|\()/, + /([\w-]+\.)?text-style-caption($|\()/, + /([\w-]+\.)?text-style-display-large($|\()/, + /([\w-]+\.)?text-style-display-medium($|\()/, + /([\w-]+\.)?text-style-display-small($|\()/, + /([\w-]+\.)?text-style-display-x-large($|\()/, + /([\w-]+\.)?text-style-heading($|\()/, + /([\w-]+\.)?text-style-input($|\()/, + /([\w-]+\.)?text-style-subheading($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$typography-condensed/, + /\$typography-condensed/, + /\$base-font-size/, + /\$line-height-data/, + /\$font-family-data/, + /\$font-size-data/, + /\$default-browser-font-size/, + // Legacy custom properties + /--p-button-font/, + /--p-badge-font/, + ], + }, + layout: { + 'declaration-property-value-disallowed-list': [ + { + display: ['grid', 'flex'], + top: [/(?!var\(--p-).+$/], + bottom: [/(?!var\(--p-).+$/], + left: [/(?!var\(--p-).+$/], + right: [/(?!var\(--p-).+$/], + width: [/(?!var\(--p-).+$/], + height: [/(?!var\(--p-).+$/], + 'z-index': [/(?!var\(--p-).+$/], + }, + {severity: 'warning'}, + ], + 'property-disallowed-list': [ + [ + 'position', + 'grid', + 'flex', + 'flex-grow', + 'flex-shrink', + 'flex-basis', + 'justify-content', + 'align-items', + 'grid-row', + 'grid-row-start', + 'grid-row-end', + 'grid-column', + 'grid-column-start', + 'grid-column-end', + 'grid-template', + 'grid-template-areas', + 'grid-template-rows', + 'grid-template-columns', + 'grid-area', + 'display', + ], + {severity: 'warning'}, + ], + 'function-disallowed-list': [ + /([\w-]+\.)?nav-min-window-corrected/, + /([\w-]+\.)?control-height/, + /([\w-]+\.)?control-slim-height/, + /([\w-]+\.)?mobile-nav-width/, + /([\w-]+\.)?thumbnail-size/, + /([\w-]+\.)?icon-size($|\()/, + /([\w-]+\.)?top-bar-height/, + /([\w-]+\.)?z-index/, + /([\w-]+\.)?safe-area-for($|\()/, + ], + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?hidden-when-printing($|\()/, + /([\w-]+\.)?print-hidden($|\()/, + /([\w-]+\.)?layout-flex-fix($|\()/, + /([\w-]+\.)?skeleton-page-header-layout($|\()/, + /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$layout-width-data/, + /\$navigation-width/, + /\$small-thumbnail-size/, + /\$large-thumbnail-size/, + /\$medium-thumbnail-size/, + /\$thumbnail-sizes/, + // Legacy custom properties + /--p-range-slider-thumb-size-base/, + /--p-range-slider-thumb-size-active/, + /--p-override-visible/, + /--p-icon-size/, + /--p-choice-size/, + ], + }, + spacing: { + 'function-disallowed-list': [ + /([\w-]+\.)?control-vertical-padding/, + /([\w-]+\.)?em/, + /([\w-]+\.)?px/, + /([\w-]+\.)?rem/, + /([\w-]+\.)?spacing/, + ], + 'stylelint-polaris/global-disallowed-list': [ + /\$polaris-spacing/, + /\$spacing-data/, + /\$actions-vertical-spacing/, + // Legacy custom properties + /--p-button-group-item-spacing/, + /--p-choice-margin/, + /--p-text-field-spinner-offset/, + ], + }, + shape: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?border-radius/, + /([\w-]+\.)?border-width/, + /([\w-]+\.)?border/, + /([\w-]+\.)?high-contrast-border($|\()/, + /([\w-]+\.)?high-contrast-button-outline($|\()/, + /([\w-]+\.)?high-contrast-outline($|\()/, + /([\w-]+\.)?focus-ring($|\()/, + /([\w-]+\.)?no-focus-ring($|\()/, + ], + }, + 'stylelint-polaris/global-disallowed-list': [ + /\$border-radius-data/, + /\$border-width-data/, + /\$borders-data/, + // Legacy custom properties + /--p-border-radius-base/, + /--p-border-radius-wide/, + /--p-border-radius-full/, + /--p-control-border-width/, + /--p-thin-border-subdued/, + /--p-banner-border-default/, + /--p-banner-border-success/, + /--p-banner-border-highlight/, + /--p-banner-border-warning/, + /--p-banner-border-critical/, + /--p-text-field-focus-ring-border-radius/, + /--p-text-field-focus-ring-offset/, + ], + }, + depth: { + 'function-disallowed-list': [/([\w-]+\.)?shadow/], + 'stylelint-polaris/global-disallowed-list': [ + /\$shadows-data/, + /\$fixed-element-stacking-order/, + /\$global-elements/, + // Legacy custom properties + /--p-button-drop-shadow/, + /--p-button-inner-shadow/, + /--p-button-pressed-inner-shadow/, + /--p-card-shadow/, + /--p-popover-shadow/, + /--p-modal-shadow/, + /--p-top-bar-shadow/, + /--p-override-loading-z-index/, + ], + }, + conventions: { + 'unit-disallowed-list': [ + // TODO: Should 's' and 'ms' move to `motion`? + ['px', 'rem', 'em', 's', 'ms'], + {severity: 'warning'}, + ], + 'stylelint-polaris/custom-properties-allowed-list': { + // Allow any custom property not prefixed with `--p-`, `--pc-`, or `--polaris-version-` + allowedProperties: [/--(?!(p|pc|polaris-version)-).+/], + allowedValues: { + '/.+/': [ + // Note: Order is important. + // The first pattern validates `--p-*` + // custom properties are valid Polaris tokens + ...getCustomPropertyNames(tokens), + // and the second pattern flags unknown `--p-*` custom properties + // or usages of our "private" `--pc-*` custom properties + /--(?!(p|pc)-).+/, + ], + }, + }, + 'stylelint-polaris/global-disallowed-list': [ + / \* \$/, + // Legacy custom properties + /--p-override-none/, + /--p-override-one/, + /--p-override-zero/, + /--p-non-null-content/, + ], + }, + mediaQueries: { + 'function-disallowed-list': [ + /([\w-]+\.)?breakpoint/, + /([\w-]+\.)?layout-width/, + ], + 'stylelint-polaris/media-queries-allowed-list': { + // Allowed media types and media conditions + // https://www.w3.org/TR/mediaqueries-5/#media + allowedMediaTypes: ['print', 'screen'], + allowedMediaFeatureNames: ['forced-colors', '-ms-high-contrast'], + allowedScssInterpolations: [ + // TODO: Add utility to @shopify/polaris-tokens to getMediaConditionNames + /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, + ], + }, + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?after-topbar-sheet($|\()/, + /([\w-]+\.)?breakpoint-after($|\()/, + /([\w-]+\.)?breakpoint-before($|\()/, + /([\w-]+\.)?frame-when-nav-displayed($|\()/, + /([\w-]+\.)?frame-when-nav-hidden($|\()/, + /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, + /([\w-]+\.)?page-actions-layout($|\()/, + /([\w-]+\.)?page-content-breakpoint-after($|\()/, + /([\w-]+\.)?page-content-breakpoint-before($|\()/, + /([\w-]+\.)?page-content-layout($|\()/, + /([\w-]+\.)?page-content-when-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, + /([\w-]+\.)?page-content-when-layout-stacked($|\()/, + /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, + /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, + /([\w-]+\.)?page-content-when-partially-condensed($|\()/, + /([\w-]+\.)?page-header-has-navigation($|\()/, + /([\w-]+\.)?page-header-has-secondary-actions($|\()/, + /([\w-]+\.)?page-header-layout($|\()/, + /([\w-]+\.)?page-header-without-navigation($|\()/, + /([\w-]+\.)?page-layout($|\()/, + /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, + /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, + /([\w-]+\.)?page-title-layout($|\()/, + /([\w-]+\.)?page-when-not-max-width($|\()/, + /([\w-]+\.)?when-typography-condensed($|\()/, + /([\w-]+\.)?when-typography-not-condensed($|\()/, + /([\w-]+\.)?when-not-printing($|\()/, + /([\w-]+\.)?when-printing($|\()/, + ], + }, + }, + legacySass: { + 'stylelint-polaris/at-rule-disallowed-list': { + include: [ + /([\w-]+\.)?base-button-disabled($|\()/, + /([\w-]+\.)?button-base($|\()/, + /([\w-]+\.)?button-filled($|\()/, + /([\w-]+\.)?button-full-width($|\()/, + /([\w-]+\.)?button-outline-disabled($|\()/, + /([\w-]+\.)?button-outline($|\()/, + /([\w-]+\.)?plain-button-backdrop($|\()/, + /([\w-]+\.)?unstyled-button($|\()/, + /([\w-]+\.)?skeleton-content($|\()/, + /([\w-]+\.)?unstyled-input($|\()/, + /([\w-]+\.)?unstyled-link($|\()/, + /([\w-]+\.)?unstyled-list($|\()/, + /([\w-]+\.)?range-thumb-selectors($|\()/, + /([\w-]+\.)?range-track-selectors($|\()/, + /([\w-]+\.)?visually-hidden($|\()/, + ], + }, + 'function-disallowed-list': [ + /([\w-]+\.)?available-names/, + /([\w-]+\.)?map-extend/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?list-selected-indicator($|\()/, + /([\w-]+\.)?state($|\()/, + ], + }, +}; + +const stylelintPolarisCoverageCategories = Object.keys( + stylelintPolarisCoverageOptions, +).join('|'); + /** @type {import('stylelint').Config} */ module.exports = { customSyntax: 'postcss-scss', + reportDescriptionlessDisables: true, + reportInvalidScopeDisables: [ + true, + { + except: [ + new RegExp( + String.raw`^stylelint-polaris\/coverage\/(?:${stylelintPolarisCoverageCategories})$`, + ), + ], + }, + ], plugins: [ 'stylelint-scss', './plugins/coverage', @@ -12,359 +383,6 @@ module.exports = { './plugins/media-queries-allowed-list', ], rules: { - 'stylelint-polaris/coverage': { - colors: { - 'color-named': 'never', - 'color-no-hex': true, - 'scss/function-color-relative': true, - 'declaration-property-value-disallowed-list': { - opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], - }, - 'function-disallowed-list': [ - // Include Sass namespace - // https://regex101.com/r/UdW0oV/1 - 'brightness', - 'contrast', - 'hue-rotate', - 'hsl', - 'hsla', - 'invert', - 'rgb', - 'rgba', - 'sepia', - /([\w-]+\.)?color-multiply/, - /([\w-]+\.)?color/, - /([\w-]+\.)?filter/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - // mixins - /([\w-]+\.)?color-icon($|\()/, - /([\w-]+\.)?recolor-icon($|\()/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?ms-high-contrast-color/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$polaris-colors/, - /\$color-filter-palette-data/, - /\$color-palette-data/, - // Legacy custom properties - /--p-override-transparent/, - /--p-badge-mix-blend-mode/, - ], - }, - motion: { - 'at-rule-disallowed-list': ['keyframes'], - 'function-disallowed-list': [ - /([\w-]+\.)?control-icon-transition/, - /([\w-]+\.)?duration/, - /([\w-]+\.)?easing/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [/([\w-]+\.)?skeleton-shimmer($|\()/], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$duration-data/, - /\$polaris-duration-map/, - /\$skeleton-shimmer-duration/, - /\$easing-data/, - // Legacy custom properties - /--p-range-slider-thumb-scale/, - /--p-duration-1-0-0/, - /--p-duration-1-5-0/, - ], - }, - typography: { - 'declaration-property-value-disallowed-list': { - 'font-weight': [/(\$.*|[0-9]+)/], - }, - 'function-disallowed-list': [ - /([\w-]+\.)?font-family/, - /([\w-]+\.)?font-size/, - /([\w-]+\.)?line-height/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?truncate($|\()/, - /([\w-]+\.)?text-breakword($|\()/, - /([\w-]+\.)?text-emphasis-normal($|\()/, - /([\w-]+\.)?text-emphasis-strong($|\()/, - /([\w-]+\.)?text-emphasis-subdued($|\()/, - /([\w-]+\.)?text-style-body($|\()/, - /([\w-]+\.)?text-style-button-large($|\()/, - /([\w-]+\.)?text-style-button($|\()/, - /([\w-]+\.)?text-style-caption($|\()/, - /([\w-]+\.)?text-style-display-large($|\()/, - /([\w-]+\.)?text-style-display-medium($|\()/, - /([\w-]+\.)?text-style-display-small($|\()/, - /([\w-]+\.)?text-style-display-x-large($|\()/, - /([\w-]+\.)?text-style-heading($|\()/, - /([\w-]+\.)?text-style-input($|\()/, - /([\w-]+\.)?text-style-subheading($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$typography-condensed/, - /\$typography-condensed/, - /\$base-font-size/, - /\$line-height-data/, - /\$font-family-data/, - /\$font-size-data/, - /\$default-browser-font-size/, - // Legacy custom properties - /--p-button-font/, - /--p-badge-font/, - ], - }, - layout: { - 'declaration-property-value-disallowed-list': [ - { - display: ['grid', 'flex'], - top: [/(?!var\(--p-).+$/], - bottom: [/(?!var\(--p-).+$/], - left: [/(?!var\(--p-).+$/], - right: [/(?!var\(--p-).+$/], - width: [/(?!var\(--p-).+$/], - height: [/(?!var\(--p-).+$/], - 'z-index': [/(?!var\(--p-).+$/], - }, - {severity: 'warning'}, - ], - 'property-disallowed-list': [ - [ - 'position', - 'grid', - 'flex', - 'flex-grow', - 'flex-shrink', - 'flex-basis', - 'justify-content', - 'align-items', - 'grid-row', - 'grid-row-start', - 'grid-row-end', - 'grid-column', - 'grid-column-start', - 'grid-column-end', - 'grid-template', - 'grid-template-areas', - 'grid-template-rows', - 'grid-template-columns', - 'grid-area', - 'display', - ], - {severity: 'warning'}, - ], - 'function-disallowed-list': [ - /([\w-]+\.)?nav-min-window-corrected/, - /([\w-]+\.)?control-height/, - /([\w-]+\.)?control-slim-height/, - /([\w-]+\.)?mobile-nav-width/, - /([\w-]+\.)?thumbnail-size/, - /([\w-]+\.)?icon-size($|\()/, - /([\w-]+\.)?top-bar-height/, - /([\w-]+\.)?z-index/, - /([\w-]+\.)?safe-area-for($|\()/, - ], - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?hidden-when-printing($|\()/, - /([\w-]+\.)?print-hidden($|\()/, - /([\w-]+\.)?layout-flex-fix($|\()/, - /([\w-]+\.)?skeleton-page-header-layout($|\()/, - /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$layout-width-data/, - /\$navigation-width/, - /\$small-thumbnail-size/, - /\$large-thumbnail-size/, - /\$medium-thumbnail-size/, - /\$thumbnail-sizes/, - // Legacy custom properties - /--p-range-slider-thumb-size-base/, - /--p-range-slider-thumb-size-active/, - /--p-override-visible/, - /--p-icon-size/, - /--p-choice-size/, - ], - }, - spacing: { - 'function-disallowed-list': [ - /([\w-]+\.)?control-vertical-padding/, - /([\w-]+\.)?em/, - /([\w-]+\.)?px/, - /([\w-]+\.)?rem/, - /([\w-]+\.)?spacing/, - ], - 'stylelint-polaris/global-disallowed-list': [ - /\$polaris-spacing/, - /\$spacing-data/, - /\$actions-vertical-spacing/, - // Legacy custom properties - /--p-button-group-item-spacing/, - /--p-choice-margin/, - /--p-text-field-spinner-offset/, - ], - }, - shape: { - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?border-radius/, - /([\w-]+\.)?border-width/, - /([\w-]+\.)?border/, - /([\w-]+\.)?high-contrast-border($|\()/, - /([\w-]+\.)?high-contrast-button-outline($|\()/, - /([\w-]+\.)?high-contrast-outline($|\()/, - /([\w-]+\.)?focus-ring($|\()/, - /([\w-]+\.)?no-focus-ring($|\()/, - ], - }, - 'stylelint-polaris/global-disallowed-list': [ - /\$border-radius-data/, - /\$border-width-data/, - /\$borders-data/, - // Legacy custom properties - /--p-border-radius-base/, - /--p-border-radius-wide/, - /--p-border-radius-full/, - /--p-control-border-width/, - /--p-thin-border-subdued/, - /--p-banner-border-default/, - /--p-banner-border-success/, - /--p-banner-border-highlight/, - /--p-banner-border-warning/, - /--p-banner-border-critical/, - /--p-text-field-focus-ring-border-radius/, - /--p-text-field-focus-ring-offset/, - ], - }, - depth: { - 'function-disallowed-list': [/([\w-]+\.)?shadow/], - 'stylelint-polaris/global-disallowed-list': [ - /\$shadows-data/, - /\$fixed-element-stacking-order/, - /\$global-elements/, - // Legacy custom properties - /--p-button-drop-shadow/, - /--p-button-inner-shadow/, - /--p-button-pressed-inner-shadow/, - /--p-card-shadow/, - /--p-popover-shadow/, - /--p-modal-shadow/, - /--p-top-bar-shadow/, - /--p-override-loading-z-index/, - ], - }, - conventions: { - 'unit-disallowed-list': [ - // TODO: Should 's' and 'ms' move to `motion`? - ['px', 'rem', 'em', 's', 'ms'], - {severity: 'warning'}, - ], - 'stylelint-polaris/custom-properties-allowed-list': { - // Allow any custom property not prefixed with `--p-`, `--pc-`, or `--polaris-version-` - allowedProperties: [/--(?!(p|pc|polaris-version)-).+/], - allowedValues: { - '/.+/': [ - // Note: Order is important. - // The first pattern validates `--p-*` - // custom properties are valid Polaris tokens - ...getCustomPropertyNames(tokens), - // and the second pattern flags unknown `--p-*` custom properties - // or usages of our "private" `--pc-*` custom properties - /--(?!(p|pc)-).+/, - ], - }, - }, - 'stylelint-polaris/global-disallowed-list': [ - / \* \$/, - // Legacy custom properties - /--p-override-none/, - /--p-override-one/, - /--p-override-zero/, - /--p-non-null-content/, - ], - }, - mediaQueries: { - 'function-disallowed-list': [ - /([\w-]+\.)?breakpoint/, - /([\w-]+\.)?layout-width/, - ], - 'stylelint-polaris/media-queries-allowed-list': { - // Allowed media types and media conditions - // https://www.w3.org/TR/mediaqueries-5/#media - allowedMediaTypes: ['print', 'screen'], - allowedMediaFeatureNames: ['forced-colors', '-ms-high-contrast'], - allowedScssInterpolations: [ - // TODO: Add utility to @shopify/polaris-tokens to getMediaConditionNames - /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, - ], - }, - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?after-topbar-sheet($|\()/, - /([\w-]+\.)?breakpoint-after($|\()/, - /([\w-]+\.)?breakpoint-before($|\()/, - /([\w-]+\.)?frame-when-nav-displayed($|\()/, - /([\w-]+\.)?frame-when-nav-hidden($|\()/, - /([\w-]+\.)?frame-with-nav-when-not-max-width($|\()/, - /([\w-]+\.)?page-actions-layout($|\()/, - /([\w-]+\.)?page-content-breakpoint-after($|\()/, - /([\w-]+\.)?page-content-breakpoint-before($|\()/, - /([\w-]+\.)?page-content-layout($|\()/, - /([\w-]+\.)?page-content-when-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-layout-not-stacked($|\()/, - /([\w-]+\.)?page-content-when-layout-stacked($|\()/, - /([\w-]+\.)?page-content-when-not-fully-condensed($|\()/, - /([\w-]+\.)?page-content-when-not-partially-condensed($|\()/, - /([\w-]+\.)?page-content-when-partially-condensed($|\()/, - /([\w-]+\.)?page-header-has-navigation($|\()/, - /([\w-]+\.)?page-header-has-secondary-actions($|\()/, - /([\w-]+\.)?page-header-layout($|\()/, - /([\w-]+\.)?page-header-without-navigation($|\()/, - /([\w-]+\.)?page-layout($|\()/, - /([\w-]+\.)?page-padding-not-fully-condensed($|\()/, - /([\w-]+\.)?page-padding-not-partially-condensed($|\()/, - /([\w-]+\.)?page-title-layout($|\()/, - /([\w-]+\.)?page-when-not-max-width($|\()/, - /([\w-]+\.)?when-typography-condensed($|\()/, - /([\w-]+\.)?when-typography-not-condensed($|\()/, - /([\w-]+\.)?when-not-printing($|\()/, - /([\w-]+\.)?when-printing($|\()/, - ], - }, - }, - legacySass: { - 'stylelint-polaris/at-rule-disallowed-list': { - include: [ - /([\w-]+\.)?base-button-disabled($|\()/, - /([\w-]+\.)?button-base($|\()/, - /([\w-]+\.)?button-filled($|\()/, - /([\w-]+\.)?button-full-width($|\()/, - /([\w-]+\.)?button-outline-disabled($|\()/, - /([\w-]+\.)?button-outline($|\()/, - /([\w-]+\.)?plain-button-backdrop($|\()/, - /([\w-]+\.)?unstyled-button($|\()/, - /([\w-]+\.)?skeleton-content($|\()/, - /([\w-]+\.)?unstyled-input($|\()/, - /([\w-]+\.)?unstyled-link($|\()/, - /([\w-]+\.)?unstyled-list($|\()/, - /([\w-]+\.)?range-thumb-selectors($|\()/, - /([\w-]+\.)?range-track-selectors($|\()/, - /([\w-]+\.)?visually-hidden($|\()/, - ], - }, - 'function-disallowed-list': [ - /([\w-]+\.)?available-names/, - /([\w-]+\.)?map-extend/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?list-selected-indicator($|\()/, - /([\w-]+\.)?state($|\()/, - ], - }, - }, + 'stylelint-polaris/coverage': stylelintPolarisCoverageOptions, }, }; diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index ebb4df9556d..253d6e27e13 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -1,6 +1,6 @@ const stylelint = require('stylelint'); -const {isPlainObject, isNumber} = require('../../utils'); +const {isPlainObject} = require('../../utils'); const ruleName = 'stylelint-polaris/coverage'; @@ -59,7 +59,7 @@ module.exports = stylelint.createPlugin( stylelint.utils.checkAgainstRule( { ruleName: categoryRuleName, - ruleSettings: normalizeRuleSettings(categoryRuleSettings), + ruleSettings: categoryRuleSettings, fix: categoryRuleFix, root, result, @@ -80,80 +80,10 @@ module.exports = stylelint.createPlugin( }, ); } - - const disabledCoverageWarnings = - result.stylelint.disabledWarnings?.filter((disabledWarning) => - disabledWarning.rule.startsWith(ruleName), - ); - - if (!disabledCoverageWarnings?.length) return; - - const disabledCoverageLines = Array.from( - new Set(disabledCoverageWarnings.map(({line}) => line)), - ); - - // Ensure all stylelint-polaris/coverage disable comments - // have a description prefixed with "polaris:" - for (const disabledRange of result.stylelint.disabledRanges.all) { - if ( - !isDisabledCoverageRule(disabledCoverageLines, disabledRange) || - disabledRange.description?.startsWith('polaris:') - ) { - continue; - } - - const stylelintDisableText = disabledRange.comment.text - .split('--')[0] - .trim(); - - stylelint.utils.report({ - message: `Expected /* ${stylelintDisableText} -- polaris: Reason for disabling */`, - ruleName, - result, - node: disabledRange.comment, - // Note: `stylelint-disable` comments (without next-line) appear to - // be special cased in that they do not trigger warnings when reported. - ...forceReport, - }); - } }; }, ); -/** - * @param {number[]} disabledCoverageLines - * @param {import('stylelint').DisabledRange} disabledRange - */ -function isDisabledCoverageRule(disabledCoverageLines, disabledRange) { - if (isUnclosedDisabledRange(disabledRange)) { - return disabledCoverageLines.some( - (disabledCoverageLine) => disabledCoverageLine >= disabledRange.start, - ); - } - - return disabledCoverageLines.some( - (coverageDisabledLine) => - coverageDisabledLine >= disabledRange.start && - coverageDisabledLine <= disabledRange.end, - ); -} - -/** - * Checks if the `disabledRange` is an unclosed `stylelint-disable` comment - * e.g. The `stylelint-disable` comment is NOT followed by `stylelint-enable` - * @param {import('stylelint').DisabledRange} disabledRange - */ -function isUnclosedDisabledRange(disabledRange) { - if ( - !disabledRange.comment.text.startsWith('stylelint-disable-next-line') && - !isNumber(disabledRange.end) - ) { - return true; - } - - return false; -} - function validatePrimaryOptions(primaryOptions) { if (!isPlainObject(primaryOptions)) return false; @@ -163,21 +93,3 @@ function validatePrimaryOptions(primaryOptions) { return true; } - -/** - * @param {import('stylelint').ConfigRuleSettings} ruleSettings - */ -function normalizeRuleSettings(ruleSettings) { - if ( - // Let `stylelint` normalize the rule settings - !Array.isArray(ruleSettings) || - // Assume rule settings are already normalized - Array.isArray(ruleSettings[0]) || - // Assume rule settings are already normalized - isPlainObject(ruleSettings[1]) - ) { - return ruleSettings; - } - - return [ruleSettings]; -} diff --git a/stylelint-polaris/plugins/coverage/index.test.js b/stylelint-polaris/plugins/coverage/index.test.js index ddfafc63801..2c92a729934 100644 --- a/stylelint-polaris/plugins/coverage/index.test.js +++ b/stylelint-polaris/plugins/coverage/index.test.js @@ -22,76 +22,19 @@ testRule({ code: '@media (min-width: 320px) {}', description: 'Uses allowed at-rule', }, - { - code: ` - /* stylelint-disable -- polaris: context */ - @keyframes foo {} - /* stylelint-enable */ - `, - description: - 'Uses disallowed at-rule with disable/enable comment and context', - }, - { - code: ` - /* stylelint-disable -- polaris: context */ - @keyframes foo {} - `, - description: - 'Uses disallowed at-rule with disable comment and context and without enable comment', - }, - { - code: ` - /* stylelint-disable-next-line -- polaris: context */ - @keyframes foo {} - `, - description: - 'Uses disallowed at-rule with disable next line comment and context', - }, ], reject: [ { code: '@keyframes foo {}', - description: 'Uses disallowed at-rule', + description: 'Uses disallowed at-rule (built-in rule)', message: 'Unexpected at-rule "keyframes" (at-rule-disallowed-list)', }, { code: '.class {color: var(--p-legacy-var);}', - description: 'Uses disallowed legacy variable', + description: 'Uses disallowed legacy variable (custom rule)', message: 'Unexpected disallowed value "--p-legacy-var" (stylelint-polaris/global-disallowed-list)', }, - { - code: ` - /* stylelint-disable */ - @keyframes foo {} - /* stylelint-enable */ - `, - description: - 'Uses disallowed at-rule with disable/enable comment and without context', - message: - 'Expected /* stylelint-disable -- polaris: Reason for disabling */', - }, - { - code: ` - /* stylelint-disable */ - - @keyframes fooz {} - `, - description: - 'Uses disallowed at-rule with disable comment and without context and enable comment', - message: - 'Expected /* stylelint-disable -- polaris: Reason for disabling */', - }, - { - code: ` - /* stylelint-disable-next-line */ - @keyframes foo {} - `, - description: - 'Uses disallowed at-rule with disable next line comment and without context', - message: - 'Expected /* stylelint-disable-next-line -- polaris: Reason for disabling */', - }, ], }); From 2ce801cd50c80c9346c2109e7af91eae35ddc2dc Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Thu, 10 Nov 2022 13:20:48 -0500 Subject: [PATCH 08/19] [stylelint-polaris] PoC for custom messages --- stylelint-polaris/index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index 9b1f1bdb2c4..794101f7d6d 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -4,7 +4,17 @@ const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); const stylelintPolarisCoverageOptions = { colors: { 'color-named': 'never', - 'color-no-hex': true, + 'color-no-hex': [ + true, + { + // TODO: Add custom meta support in coverage.js + meta: { + url: 'https://polaris.shopify.com/tokens/color', + }, + message: (hex) => + `Unexpected use of hex code: value "${hex}" should be replaced with a Polaris color token.`, + }, + ], 'scss/function-color-relative': true, 'declaration-property-value-disallowed-list': { opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], From 10dd6f808023a4f1015b25231df73dda560f9ec6 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Wed, 16 Nov 2022 16:04:52 -0500 Subject: [PATCH 09/19] Update coverage plugin to use customMessages and ruleMetadata --- .stylelintrc.js | 6 +- stylelint-polaris/index.js | 12 +--- stylelint-polaris/plugins/coverage/index.js | 80 +++++++++++++-------- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/.stylelintrc.js b/.stylelintrc.js index e498b0f9a52..05fdbfd350e 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,9 +1,9 @@ /** @type {import('stylelint').Config} */ module.exports = { - extends: ['@shopify/stylelint-plugin/prettier', './stylelint-polaris'], - // Disabling @shopify/stylelint-plugin/configs/core no-unknown-animations as stylelint - // is not aware of global Polaris keyframes + extends: [/*'@shopify/stylelint-plugin/prettier', */ './stylelint-polaris'], + // Disabling @shopify/stylelint-plugin/configs/core no-unknown-animations as stylelint is not aware of global Polaris keyframes // TODO: create custom plugin to ensure animation-names match Polaris keyframe names + customSyntax: 'postcss-scss', rules: { 'no-unknown-animations': null, 'value-keyword-case': ['lower', {camelCaseSvgKeywords: true}], diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index 794101f7d6d..9b1f1bdb2c4 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -4,17 +4,7 @@ const {getCustomPropertyNames, tokens} = require('@shopify/polaris-tokens'); const stylelintPolarisCoverageOptions = { colors: { 'color-named': 'never', - 'color-no-hex': [ - true, - { - // TODO: Add custom meta support in coverage.js - meta: { - url: 'https://polaris.shopify.com/tokens/color', - }, - message: (hex) => - `Unexpected use of hex code: value "${hex}" should be replaced with a Polaris color token.`, - }, - ], + 'color-no-hex': true, 'scss/function-color-relative': true, 'declaration-property-value-disallowed-list': { opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index 253d6e27e13..44306770256 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -2,7 +2,10 @@ const stylelint = require('stylelint'); const {isPlainObject} = require('../../utils'); -const ruleName = 'stylelint-polaris/coverage'; +const customMessages = require('./customMessages'); +const ruleMetadata = require('./ruleMetadata'); + +const coverageRuleName = 'stylelint-polaris/coverage'; /** * @typedef {{ @@ -10,13 +13,11 @@ const ruleName = 'stylelint-polaris/coverage'; * }} PrimaryOptions */ -// Setting `line` to an invalid line number forces the warning to be reported -// and the `report({node})` option is used to display the location information: -// https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 +// Setting `line` to an invalid line number forces the warning to be reported and the `report({node})` option is used to display the location information: https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 const forceReport = {line: -1}; module.exports = stylelint.createPlugin( - ruleName, + coverageRuleName, /** @param {PrimaryOptions} primaryOptions */ (primaryOptions, secondaryOptions, context) => { const isPrimaryOptionsValid = validatePrimaryOptions(primaryOptions); @@ -26,55 +27,74 @@ module.exports = stylelint.createPlugin( for (const [categoryName, categoryConfigRules] of Object.entries( primaryOptions, )) { - for (const [categoryRuleName, categoryRuleSettings] of Object.entries( + for (const [stylelintRuleName, ruleSettings] of Object.entries( categoryConfigRules, )) { rules.push({ - coverageRuleName: `${ruleName}/${categoryName}`, - categoryRuleName, - categoryRuleSettings, - categoryRuleSeverity: categoryRuleSettings?.[1]?.severity, - categoryRuleFix: - context.fix && !categoryRuleSettings?.[1]?.disableFix, + ruleName: `${coverageRuleName}/${categoryName}`, + stylelintRuleName, + ruleSettings, + customMessage: customMessages[categoryName][stylelintRuleName], + metadata: ruleMetadata[categoryName], + severity: ruleSettings?.[1]?.severity, + fix: context.fix && !ruleSettings?.[1]?.disableFix, }); } } return (root, result) => { - const validOptions = stylelint.utils.validateOptions(result, ruleName, { - actual: isPrimaryOptionsValid, - }); + const validOptions = stylelint.utils.validateOptions( + result, + coverageRuleName, + { + actual: isPrimaryOptionsValid, + }, + ); if (!validOptions) return; for (const rule of rules) { const { - coverageRuleName, - categoryRuleName, - categoryRuleSettings, - categoryRuleSeverity, - categoryRuleFix, + ruleName, + stylelintRuleName, + ruleSettings, + customMessage, + metadata, + severity, + fix, } = rule; stylelint.utils.checkAgainstRule( { - ruleName: categoryRuleName, - ruleSettings: categoryRuleSettings, - fix: categoryRuleFix, + ruleName: stylelintRuleName, + ruleSettings, + fix, root, result, }, (warning) => { + const {message, args = ['value']} = customMessage; + + const messageArgs = args.map( + (nodeProp) => warning?.node?.[nodeProp], + ); + // Stylelint's VS Code extension only looks for custom messages and metadata on the `customMessages` and `ruleMetadata` properties of the stylelint postcss result, otherwise it uses the `messages` and `meta` values set on the built in stylelint rule functions. + result.stylelint.customMessages = { + [ruleName]: message(...messageArgs), + }; + + result.stylelint.ruleMetadata = { + [ruleName]: metadata, + }; + stylelint.utils.report({ result, - ruleName: coverageRuleName, - message: warning.text, + ruleName, + message, + messageArgs, severity: - categoryRuleSeverity ?? - result.stylelint.config?.defaultSeverity ?? - 'error', - // If `warning.node` is NOT present, the warning is - // referring to a misconfigured rule + severity ?? result.stylelint.config?.defaultSeverity ?? 'error', + // If `warning.node` is NOT present, the warning is referring to a misconfigured rule ...(warning.node ? {node: warning.node} : forceReport), }); }, From 2ef530d2e2905e0bc4efe77f1138fdf205e9d40d Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Mon, 21 Nov 2022 14:40:48 -0500 Subject: [PATCH 10/19] Add coverage plugin documentation --- stylelint-polaris/plugins/coverage/README.md | 855 +++++++++++++++++++ 1 file changed, 855 insertions(+) create mode 100644 stylelint-polaris/plugins/coverage/README.md diff --git a/stylelint-polaris/plugins/coverage/README.md b/stylelint-polaris/plugins/coverage/README.md new file mode 100644 index 00000000000..1328e09a96a --- /dev/null +++ b/stylelint-polaris/plugins/coverage/README.md @@ -0,0 +1,855 @@ +# stylelint-polaris/coverage + +Disallows CSS declarations that lower Polaris coverage of colors, typography, layout, shape, spacing, depth, motion, and breakpoints in your project. + +## Conventions + +### unit-disallowed-list + +```diff +- font-size: 12px; +- line-height: 1.5rem +- transition-duration: 200ms; +``` + +```diff ++ font-size: var(--p-font-size-75); ++ line-height: var(--p-font-line-height-3); ++ transition-duration: var(--p-duration-200); +``` + +## Colors + +### color-no-hex + +```diff +- color: #202223; +- fill: #5c5f62; +``` + +```diff ++ color: var(--p-text); ++ fill: var(--p-icon) +``` + +### color-named + +```diff +- color: black; +- fill: dimgray; +``` + +```diff ++ color: var(--p-text); ++ fill: var(--p-icon) +``` + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Font + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Layout + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +## Shape + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Spacing + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Depth + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Motion + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Breakpoints + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Z-Index + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` + +## Legacy + +### declaration-property-value-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### declaration-property-unit-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### function-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### at-rule-disallowed-list + +```diff + +``` + +```diff + +``` + +```diff + +``` + +```diff + +``` + +### property-disallowed-list + +```diff + +``` From 98c909f6280d7c8eb24c04488f8d58b2a644a4c6 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Mon, 21 Nov 2022 14:41:14 -0500 Subject: [PATCH 11/19] Add coverage rule custom messages --- .../plugins/coverage/customMessages.js | 300 ++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 stylelint-polaris/plugins/coverage/customMessages.js diff --git a/stylelint-polaris/plugins/coverage/customMessages.js b/stylelint-polaris/plugins/coverage/customMessages.js new file mode 100644 index 00000000000..932b4e76875 --- /dev/null +++ b/stylelint-polaris/plugins/coverage/customMessages.js @@ -0,0 +1,300 @@ +module.exports = { + colors: { + 'color-named': { + message: (name) => + `(color-named): Replace named color "${name}" with a Polaris color token`, + }, + 'color-no-hex': { + message: (hex) => + `(color-no-hex): Replace hex code "${hex}" with a Polaris color token`, + }, + 'declaration-property-value-disallowed-list': { + message: (prop, value) => + `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris color token`, + args: ['prop', 'value'], + }, + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list): Replace function "${func}" with a Polaris color token`, + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris color token`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy mixin map-get data + '$polaris-colors': 'Sass variable', + '$color-filter-palette-data': 'Sass variable', + '$color-palette-data': 'Sass variable', + // Legacy custom properties + '--p-override-transparent': 'CSS custom property', + '--p-badge-mix-blend-mode': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris color token`; + }, + }, + }, + motion: { + 'at-rule-disallowed-list': { + message: (atRule) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "${atRule}" with a Polaris motion token`, + }, + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris motion token`, + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris motion token`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + '$duration-data': 'Sass variable', + '$polaris-duration-map': 'Sass variable', + '$skeleton-shimmer-duration': 'Sass variable', + '$easing-data': 'Sass variable', + // Legacy custom properties + '--p-range-slider-thumb-scale': 'CSS custom property', + '--p-duration-1-0-0': 'CSS custom property', + '--p-duration-1-5-0': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris motion token`; + }, + }, + }, + font: { + 'declaration-property-value-disallowed-list': { + message: (prop, value) => + `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris font token`, + args: ['prop', 'value'], + }, + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris font token`, + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris font token`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + '$typography-condensed': 'Sass variable', + '$base-font-size': 'Sass variable', + '$line-height-data': 'Sass variable', + '$font-family-data': 'Sass variable', + '$font-size-data': 'Sass variable', + '$default-browser-font-size': 'Sass variable', + // Legacy custom properties + '--p-button-font': 'CSS custom property', + '--p-badge-font': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris font token`; + }, + }, + }, + layout: { + 'declaration-property-value-disallowed-list': { + message: (prop, value) => + `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris layout component if possible`, + args: ['prop', 'value'], + }, + 'property-disallowed-list': { + message: (prop) => + `(declaration-property-value-disallowed-list) - Replace ${prop} with a Polaris layout component if possible`, + }, + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a px or rem length`, + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}" with a Polaris component`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy map variables + '$layout-width-data': 'Sass variable', + '$navigation-width': 'Sass variable', + '$small-thumbnail-size': 'Sass variable', + '$large-thumbnail-size': 'Sass variable', + '$medium-thumbnail-size': 'Sass variable', + '$thumbnail-sizes': 'Sass variable', + // Legacy custom properties + '--p-range-slider-thumb-size-base': 'CSS custom property', + '--p-range-slider-thumb-size-active': 'CSS custom property', + '--p-override-visible': 'CSS custom property', + '--p-icon-size': 'CSS custom property', + '--p-choice-size': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a px or rem length`; + }, + }, + }, + spacing: { + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris token`, + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy map variables + '$polaris-spacing': 'CSS custom property', + '$spacing-data': 'CSS custom property', + '$actions-vertical-spacing': 'CSS custom property', + // Legacy custom properties + '--p-button-group-item-spacing': 'CSS custom property', + '--p-choice-margin': 'CSS custom property', + '--p-text-field-spinner-offset': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris spacing token`; + }, + }, + }, + shape: { + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris shape token`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy map variables + '$border-radius-data': 'Sass variable', + '$border-width-data': 'Sass variable', + '$borders-data': 'Sass variable', + // Legacy custom properties + '--p-border-radius-base': 'CSS custom property', + '--p-border-radius-wide': 'CSS custom property', + '--p-border-radius-full': 'CSS custom property', + '--p-control-border-width': 'CSS custom property', + '--p-thin-border-subdued': 'CSS custom property', + '--p-banner-border-default': 'CSS custom property', + '--p-banner-border-success': 'CSS custom property', + '--p-banner-border-highlight': 'CSS custom property', + '--p-banner-border-warning': 'CSS custom property', + '--p-banner-border-critical': 'CSS custom property', + '--p-text-field-focus-ring-border-radius': 'CSS custom property', + '--p-text-field-focus-ring-offset': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris shape token`; + }, + }, + }, + depth: { + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris depth token`, + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy mixin map-get data + '$shadows-data': 'Sass variable', + '$fixed-element-stacking-order': 'Sass variable', + '$global-elements': 'Sass variable', + // Legacy custom properties + '--p-button-drop-shadow': 'CSS custom property', + '--p-button-inner-shadow': 'CSS custom property', + '--p-button-pressed-inner-shadow': 'CSS custom property', + '--p-card-shadow': 'CSS custom property', + '--p-popover-shadow': 'CSS custom property', + '--p-modal-shadow': 'CSS custom property', + '--p-top-bar-shadow': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris depth token`; + }, + }, + }, + 'z-index': { + 'declaration-property-value-disallowed-list': { + message: (property, value) => + `(declaration-property-value-disallowed-list) - Replace ${property} value "${value}" with a Polaris z-index token`, + }, + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris z-index token`, + }, + 'stylelint-polaris/global-disallowed-list': { + message: (value) => { + const legacyValueTypeMap = { + // Legacy mixin map-get data + '$fixed-element-stacking-order': 'Sass variable', + '$global-elements': 'Sass variable', + // Legacy custom properties + '--p-override-loading-z-index': 'CSS custom property', + }; + + return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris z-index token`; + }, + }, + }, + conventions: { + 'unit-disallowed-list': { + message: (unit) => + `(unit-disallowed-list) - Replace hard-coded value ${ + unit ? `"${unit}" ` : '' + }with a Polaris token`, + }, + 'stylelint-polaris/custom-properties-allowed-list': { + message: (value) => + `(custom-properties-allowed-list) - Unexpected CSS custom property ${value}`, + }, + }, + breakpoints: { + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris breakpoint token`, + }, + 'stylelint-polaris/media-queries-allowed-list': { + message: (name, params) => + `(media-query-allowed-list) - Replace media query params "${params}" with a Polaris breakpoint token`, + args: ['name', 'params'], + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}" with a Polaris breakpoint token`, + args: ['name', 'params'], + }, + }, + legacy: { + 'function-disallowed-list': { + message: (func) => + `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris token`, + }, + 'stylelint-polaris/at-rule-disallowed-list': { + message: (name, params) => + `(at-rule-disallowed-list) - Unexpected use of legacy Sass mixin "@${name} ${params}"`, + args: ['name', 'params'], + }, + 'stylelint-polaris/global-disallowed-list': { + message: (property) => { + const legacyCustomPropertyMap = { + '--p-override-none': `"none"`, + '--p-override-one': `"1"`, + '--p-override-zero': `"0"`, + '--p-non-null-content': `""`, + }; + + return `Replace legacy Polaris custom CSS property "${property}" with ${legacyCustomPropertyMap[property]}`; + }, + }, + }, +}; From fb90e2ee4c505e6e09f4c30b0989a44015be7e1f Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Mon, 21 Nov 2022 20:58:07 -0500 Subject: [PATCH 12/19] clean up config (add z-index category, add comments, map categories to token groups) --- stylelint-polaris/index.js | 75 +++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/stylelint-polaris/index.js b/stylelint-polaris/index.js index 9b1f1bdb2c4..5aca46fcb72 100644 --- a/stylelint-polaris/index.js +++ b/stylelint-polaris/index.js @@ -5,10 +5,11 @@ const stylelintPolarisCoverageOptions = { colors: { 'color-named': 'never', 'color-no-hex': true, - 'scss/function-color-relative': true, - 'declaration-property-value-disallowed-list': { - opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], - }, + 'declaration-property-value-disallowed-list': [ + { + opacity: [/(?!0|1)\d$|^\d{2,}|^[1-9]+\.|^\d+\.\d+\.|^0\.\d{3,}/], + }, + ], 'function-disallowed-list': [ // Include Sass namespace // https://regex101.com/r/UdW0oV/1 @@ -27,7 +28,7 @@ const stylelintPolarisCoverageOptions = { ], 'stylelint-polaris/at-rule-disallowed-list': { include: [ - // mixins + // Legacy mixins /([\w-]+\.)?color-icon($|\()/, /([\w-]+\.)?recolor-icon($|\()/, /([\w-]+\.)?control-backdrop($|\()/, @@ -35,6 +36,7 @@ const stylelintPolarisCoverageOptions = { ], }, 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$polaris-colors/, /\$color-filter-palette-data/, /\$color-palette-data/, @@ -54,6 +56,7 @@ const stylelintPolarisCoverageOptions = { include: [/([\w-]+\.)?skeleton-shimmer($|\()/], }, 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$duration-data/, /\$polaris-duration-map/, /\$skeleton-shimmer-duration/, @@ -64,7 +67,7 @@ const stylelintPolarisCoverageOptions = { /--p-duration-1-5-0/, ], }, - typography: { + font: { 'declaration-property-value-disallowed-list': { 'font-weight': [/(\$.*|[0-9]+)/], }, @@ -94,7 +97,7 @@ const stylelintPolarisCoverageOptions = { ], }, 'stylelint-polaris/global-disallowed-list': [ - /\$typography-condensed/, + // Legacy mixin map-get data /\$typography-condensed/, /\$base-font-size/, /\$line-height-data/, @@ -110,19 +113,18 @@ const stylelintPolarisCoverageOptions = { 'declaration-property-value-disallowed-list': [ { display: ['grid', 'flex'], + position: ['sticky'], top: [/(?!var\(--p-).+$/], bottom: [/(?!var\(--p-).+$/], left: [/(?!var\(--p-).+$/], right: [/(?!var\(--p-).+$/], width: [/(?!var\(--p-).+$/], height: [/(?!var\(--p-).+$/], - 'z-index': [/(?!var\(--p-).+$/], }, {severity: 'warning'}, ], 'property-disallowed-list': [ [ - 'position', 'grid', 'flex', 'flex-grow', @@ -141,7 +143,6 @@ const stylelintPolarisCoverageOptions = { 'grid-template-rows', 'grid-template-columns', 'grid-area', - 'display', ], {severity: 'warning'}, ], @@ -153,19 +154,19 @@ const stylelintPolarisCoverageOptions = { /([\w-]+\.)?thumbnail-size/, /([\w-]+\.)?icon-size($|\()/, /([\w-]+\.)?top-bar-height/, - /([\w-]+\.)?z-index/, - /([\w-]+\.)?safe-area-for($|\()/, ], 'stylelint-polaris/at-rule-disallowed-list': { include: [ /([\w-]+\.)?hidden-when-printing($|\()/, /([\w-]+\.)?print-hidden($|\()/, /([\w-]+\.)?layout-flex-fix($|\()/, + /([\w-]+\.)?safe-area-for($|\()/, /([\w-]+\.)?skeleton-page-header-layout($|\()/, /([\w-]+\.)?skeleton-page-secondary-actions-layout($|\()/, ], }, 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$layout-width-data/, /\$navigation-width/, /\$small-thumbnail-size/, @@ -189,6 +190,7 @@ const stylelintPolarisCoverageOptions = { /([\w-]+\.)?spacing/, ], 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$polaris-spacing/, /\$spacing-data/, /\$actions-vertical-spacing/, @@ -212,6 +214,7 @@ const stylelintPolarisCoverageOptions = { ], }, 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$border-radius-data/, /\$border-width-data/, /\$borders-data/, @@ -233,6 +236,7 @@ const stylelintPolarisCoverageOptions = { depth: { 'function-disallowed-list': [/([\w-]+\.)?shadow/], 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data /\$shadows-data/, /\$fixed-element-stacking-order/, /\$global-elements/, @@ -247,6 +251,22 @@ const stylelintPolarisCoverageOptions = { /--p-override-loading-z-index/, ], }, + 'z-index': { + 'declaration-property-value-allowed-list': [ + { + 'z-index': [/--p-z-\b([1-9]|1[0-2])\b/], + }, + {severity: 'warning'}, + ], + 'function-disallowed-list': [/([\w-]+\.)?z-index/], + 'stylelint-polaris/global-disallowed-list': [ + // Legacy mixin map-get data + /\$fixed-element-stacking-order/, + /\$global-elements/, + // Legacy custom properties + /--p-override-loading-z-index/, + ], + }, conventions: { 'unit-disallowed-list': [ // TODO: Should 's' and 'ms' move to `motion`? @@ -268,16 +288,9 @@ const stylelintPolarisCoverageOptions = { ], }, }, - 'stylelint-polaris/global-disallowed-list': [ - / \* \$/, - // Legacy custom properties - /--p-override-none/, - /--p-override-one/, - /--p-override-zero/, - /--p-non-null-content/, - ], }, - mediaQueries: { + breakpoints: { + // Legacy functions 'function-disallowed-list': [ /([\w-]+\.)?breakpoint/, /([\w-]+\.)?layout-width/, @@ -292,6 +305,7 @@ const stylelintPolarisCoverageOptions = { /^\$p-breakpoints-(xs|sm|md|lg|xl)-(up|down|only)$/, ], }, + // Legacy mixins 'stylelint-polaris/at-rule-disallowed-list': { include: [ /([\w-]+\.)?after-topbar-sheet($|\()/, @@ -326,7 +340,8 @@ const stylelintPolarisCoverageOptions = { ], }, }, - legacySass: { + legacy: { + // Legacy mixins 'stylelint-polaris/at-rule-disallowed-list': { include: [ /([\w-]+\.)?base-button-disabled($|\()/, @@ -335,6 +350,8 @@ const stylelintPolarisCoverageOptions = { /([\w-]+\.)?button-full-width($|\()/, /([\w-]+\.)?button-outline-disabled($|\()/, /([\w-]+\.)?button-outline($|\()/, + /([\w-]+\.)?control-backdrop($|\()/, + /([\w-]+\.)?list-selected-indicator($|\()/, /([\w-]+\.)?plain-button-backdrop($|\()/, /([\w-]+\.)?unstyled-button($|\()/, /([\w-]+\.)?skeleton-content($|\()/, @@ -343,15 +360,23 @@ const stylelintPolarisCoverageOptions = { /([\w-]+\.)?unstyled-list($|\()/, /([\w-]+\.)?range-thumb-selectors($|\()/, /([\w-]+\.)?range-track-selectors($|\()/, + /([\w-]+\.)?state($|\()/, /([\w-]+\.)?visually-hidden($|\()/, ], }, + // Legacy functions 'function-disallowed-list': [ /([\w-]+\.)?available-names/, /([\w-]+\.)?map-extend/, - /([\w-]+\.)?control-backdrop($|\()/, - /([\w-]+\.)?list-selected-indicator($|\()/, - /([\w-]+\.)?state($|\()/, + ], + 'stylelint-polaris/global-disallowed-list': [ + // Legacy variables + / \* \$/, + // Legacy custom properties + /--p-override-none/, + /--p-override-one/, + /--p-override-zero/, + /--p-non-null-content/, ], }, }; From 3575833a338f370db691e541abe5d3a569f330ab Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Mon, 21 Nov 2022 21:09:37 -0500 Subject: [PATCH 13/19] Add rule metadata --- .../plugins/coverage/ruleMetadata.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 stylelint-polaris/plugins/coverage/ruleMetadata.js diff --git a/stylelint-polaris/plugins/coverage/ruleMetadata.js b/stylelint-polaris/plugins/coverage/ruleMetadata.js new file mode 100644 index 00000000000..481912c4bd6 --- /dev/null +++ b/stylelint-polaris/plugins/coverage/ruleMetadata.js @@ -0,0 +1,45 @@ +module.exports = { + colors: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#colors', + url: 'https://polaris.shopify.com/tokens/colors', + }, + motion: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#motion', + url: 'https://polaris.shopify.com/tokens/motion', + }, + font: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#font', + url: 'https://polaris.shopify.com/tokens/font', + }, + layout: { + url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#layout', + }, + spacing: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#spacing', + url: 'https://polaris.shopify.com/tokens/spacing', + }, + shape: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#shape', + url: 'https://polaris.shopify.com/tokens/shape', + }, + depth: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#depth', + url: 'https://polaris.shopify.com/tokens/depth', + }, + 'z-index': { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#z-index', + url: 'https://polaris.shopify.com/tokens/z-index', + }, + conventions: { + url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#conventions', + }, + breakpoints: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#breakpoints', + url: 'https://polaris.shopify.com/tokens/breakpoints', + }, + legacy: { + // url: 'https://github.com/Shopify/polaris/tree/main/stylelint-polaris/plugins/coverage/README.md#legacy', + url: 'https://polaris.shopify.com/tokens/legacy', + deprecated: true, + }, +}; From f8ed8e86873d1b54d4cd64dd0fedc46f30b478b2 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Tue, 22 Nov 2022 12:19:25 -0500 Subject: [PATCH 14/19] fix VSCode metadata URL only being hyperlinked for 1 rule at a time --- stylelint-polaris/plugins/coverage/index.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index 44306770256..b2fa4e28a4b 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -79,13 +79,8 @@ module.exports = stylelint.createPlugin( (nodeProp) => warning?.node?.[nodeProp], ); // Stylelint's VS Code extension only looks for custom messages and metadata on the `customMessages` and `ruleMetadata` properties of the stylelint postcss result, otherwise it uses the `messages` and `meta` values set on the built in stylelint rule functions. - result.stylelint.customMessages = { - [ruleName]: message(...messageArgs), - }; - - result.stylelint.ruleMetadata = { - [ruleName]: metadata, - }; + result.stylelint.customMessages[ruleName] = message(...messageArgs); + result.stylelint.ruleMetadata[ruleName] = metadata; stylelint.utils.report({ result, From efbc3ad75b62c7a92c98451217f5e9940a0798ab Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Tue, 22 Nov 2022 12:20:08 -0500 Subject: [PATCH 15/19] fix up custom messages (WIP) --- .../plugins/coverage/customMessages.js | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/stylelint-polaris/plugins/coverage/customMessages.js b/stylelint-polaris/plugins/coverage/customMessages.js index 932b4e76875..321652071e3 100644 --- a/stylelint-polaris/plugins/coverage/customMessages.js +++ b/stylelint-polaris/plugins/coverage/customMessages.js @@ -19,7 +19,7 @@ module.exports = { }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris color token`, + `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params} with a Polaris color token`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -104,13 +104,25 @@ module.exports = { }, layout: { 'declaration-property-value-disallowed-list': { - message: (prop, value) => - `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris layout component if possible`, + message: (prop, value) => { + const messageMap = { + display: `Replace use of "${prop}: ${value}" with a Polaris layout component if possible`, + top: `Replace "${prop}" value "${value}" with a px or rem length value`, + bottom: `Replace "${prop}" value "${value}" with a px or rem length value`, + left: `Replace "${prop}" value "${value}" with a px or rem length value`, + right: `Replace "${prop}" value "${value}" with a px or rem length value`, + width: `Replace "${prop}" value "${value}" with a px or rem length value`, + height: `Replace "${prop}" value "${value}" with a px or rem length value`, + }; + + return `(declaration-property-value-disallowed-list) - ${messageMap[prop]}`; + }, args: ['prop', 'value'], }, 'property-disallowed-list': { message: (prop) => - `(declaration-property-value-disallowed-list) - Replace ${prop} with a Polaris layout component if possible`, + `(property-disallowed-list) - Replace use of "${prop}" with a Polaris layout component if possible`, + args: ['prop'], }, 'function-disallowed-list': { message: (func) => @@ -179,20 +191,20 @@ module.exports = { '$border-width-data': 'Sass variable', '$borders-data': 'Sass variable', // Legacy custom properties - '--p-border-radius-base': 'CSS custom property', - '--p-border-radius-wide': 'CSS custom property', - '--p-border-radius-full': 'CSS custom property', - '--p-control-border-width': 'CSS custom property', - '--p-thin-border-subdued': 'CSS custom property', - '--p-banner-border-default': 'CSS custom property', - '--p-banner-border-success': 'CSS custom property', - '--p-banner-border-highlight': 'CSS custom property', - '--p-banner-border-warning': 'CSS custom property', - '--p-banner-border-critical': 'CSS custom property', - '--p-text-field-focus-ring-border-radius': 'CSS custom property', - '--p-text-field-focus-ring-offset': 'CSS custom property', + 'var(--p-border-radius-base)': 'CSS custom property', + 'var(--p-border-radius-wide)': 'CSS custom property', + 'var(--p-border-radius-full)': 'CSS custom property', + 'var(--p-control-border-width)': 'CSS custom property', + 'var(--p-thin-border-subdued)': 'CSS custom property', + 'var(--p-banner-border-default)': 'CSS custom property', + 'var(--p-banner-border-success)': 'CSS custom property', + 'var(--p-banner-border-highlight)': 'CSS custom property', + 'var(--p-banner-border-warning)': 'CSS custom property', + 'var(--p-banner-border-critical)': 'CSS custom property', + 'var(--p-text-field-focus-ring-border-radius)': 'CSS custom property', + 'var(--p-text-field-focus-ring-offset)': 'CSS custom property', }; - + legacyValueTypeMap[value]; return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris shape token`; }, }, @@ -224,9 +236,10 @@ module.exports = { }, }, 'z-index': { - 'declaration-property-value-disallowed-list': { - message: (property, value) => - `(declaration-property-value-disallowed-list) - Replace ${property} value "${value}" with a Polaris z-index token`, + 'declaration-property-value-allowed-list': { + message: (prop, value) => + `(declaration-property-value-allowed-list) - Replace "${prop}" value "${value}" with a Polaris z-index token`, + args: ['prop', 'value'], }, 'function-disallowed-list': { message: (func) => @@ -239,7 +252,7 @@ module.exports = { '$fixed-element-stacking-order': 'Sass variable', '$global-elements': 'Sass variable', // Legacy custom properties - '--p-override-loading-z-index': 'CSS custom property', + 'var(--p-override-loading-z-index)': 'CSS custom property', }; return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris z-index token`; From 8accaa3b4a3da66b91486927b6c68351203ef366 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Wed, 30 Nov 2022 14:11:29 -0500 Subject: [PATCH 16/19] [stylelint-polaris] Move README content to package root --- stylelint-polaris/README.md | 365 +++++++- stylelint-polaris/plugins/coverage/README.md | 855 ------------------- 2 files changed, 353 insertions(+), 867 deletions(-) delete mode 100644 stylelint-polaris/plugins/coverage/README.md diff --git a/stylelint-polaris/README.md b/stylelint-polaris/README.md index 3248f353184..a3895ad34e4 100644 --- a/stylelint-polaris/README.md +++ b/stylelint-polaris/README.md @@ -1,22 +1,27 @@ -# Stylelint Polaris +# @shopify/stylelint-polaris + +Disallow CSS declarations that break conventions or lower Polaris coverage of colors, font, shape, spacing, depth, motion, z-index, breakpoints, and layout in your project. + +> Code is considered "covered" when it is using Polaris tokens and/or components without overriding or providing custom styles ## Package structure ``` stylelint-polaris/ |-- plugins/ -| | # Custom plugin for categorizing built-in and custom rules -| |-- coverage.js -| | # Additional custom rules -| |-- custom-properties-allowed-list.js -| | # Plugins entry point -| | # (See advanced config example below) -| |__ index.js -| # Main stylelint-polaris config +| | # Plugin for categorizing and reporting built-in and custom rules +| |-- coverage/ +| | # Custom rules +| |-- at-rule-disallowed-list/ +| |-- custom-properties-allowed-list/ +| |-- global-disallowed-list/ +| |-- media-queries-allowed-list/ +|-- utils/ +| # Config |__ index.js ``` -### Usage +## Usage ### Basic @@ -63,8 +68,344 @@ yarn build -- --filter=@shopify/polaris^... > Note: Remove the `^` character if you do want to build `@shopify/polaris` -3. Run `stylelint` on `polaris-react` +3. Run `stylelint` in `polaris-react` + +All files + +```sh +yarn turbo run lint:styles --filter=@shopify/polaris +``` + +Specific file ```sh -cd polaris-react && yarn lint:styles +yarn run stylelint path/to/component.scss + +// yarn run stylelint polaris-react/src/components/TopBar/TopBar.scss +``` + +## Rules + +### Conventions + +#### unit-disallowed-list + +```diff +- font-size: 12px; +- line-height: 1.5rem +- transition-duration: 200ms; +``` + +```diff ++ font-size: var(--p-font-size-75); ++ line-height: var(--p-font-line-height-3); ++ transition-duration: var(--p-duration-200); +``` + +### Colors + +#### color-named + +```diff +- color: black; +- fill: dimgray; +``` + +```diff ++ color: var(--p-text); ++ fill: var(--p-icon) +``` + +#### color-no-hex + +```diff +- color: #202223; +- fill: #5c5f62; +``` + +```diff ++ color: var(--p-text); ++ fill: var(--p-icon) +``` + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Font + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Shape + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Spacing + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Depth + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Motion + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Breakpoints + +#### function-disallowed-list + +```diff + +``` + +#### stylelint-polaris/media-queries-allowed-list + +```diff + +``` + +#### stylelint-polaris/at-rule-disallowed-list + +```diff + +``` + +### Z-Index + +#### declaration-property-value-allowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### stylelint-polaris/global-disallowed-list + +```diff + +``` + +### Layout + +#### declaration-property-value-disallowed-list + +```diff + +``` + +#### declaration-property-unit-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### at-rule-disallowed-list + +```diff + +``` + +#### property-disallowed-list + +```diff + +``` + +### Legacy + +#### stylelint-polaris/at-rule-disallowed-list + +```diff + +``` + +#### function-disallowed-list + +```diff + +``` + +#### stylelint-polaris/global-disallowed-list + +```diff + ``` diff --git a/stylelint-polaris/plugins/coverage/README.md b/stylelint-polaris/plugins/coverage/README.md deleted file mode 100644 index 1328e09a96a..00000000000 --- a/stylelint-polaris/plugins/coverage/README.md +++ /dev/null @@ -1,855 +0,0 @@ -# stylelint-polaris/coverage - -Disallows CSS declarations that lower Polaris coverage of colors, typography, layout, shape, spacing, depth, motion, and breakpoints in your project. - -## Conventions - -### unit-disallowed-list - -```diff -- font-size: 12px; -- line-height: 1.5rem -- transition-duration: 200ms; -``` - -```diff -+ font-size: var(--p-font-size-75); -+ line-height: var(--p-font-line-height-3); -+ transition-duration: var(--p-duration-200); -``` - -## Colors - -### color-no-hex - -```diff -- color: #202223; -- fill: #5c5f62; -``` - -```diff -+ color: var(--p-text); -+ fill: var(--p-icon) -``` - -### color-named - -```diff -- color: black; -- fill: dimgray; -``` - -```diff -+ color: var(--p-text); -+ fill: var(--p-icon) -``` - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Font - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Layout - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -## Shape - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Spacing - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Depth - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Motion - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Breakpoints - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Z-Index - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` - -## Legacy - -### declaration-property-value-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### declaration-property-unit-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### function-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### at-rule-disallowed-list - -```diff - -``` - -```diff - -``` - -```diff - -``` - -```diff - -``` - -### property-disallowed-list - -```diff - -``` From 0ef0cf43830cb3e716b7a8d07736cc8665c6cd7b Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Wed, 30 Nov 2022 14:12:07 -0500 Subject: [PATCH 17/19] [stylelint-polaris] Reconfigure categorized rules with secondary options structure --- stylelint-polaris/plugins/coverage/index.js | 82 ++++++++++++--------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index b2fa4e28a4b..6b66a40cda4 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -1,16 +1,21 @@ const stylelint = require('stylelint'); -const {isPlainObject} = require('../../utils'); +// const {isPlainObject} = require('../../utils'); const customMessages = require('./customMessages'); -const ruleMetadata = require('./ruleMetadata'); const coverageRuleName = 'stylelint-polaris/coverage'; /** * @typedef {{ - * [category: string]: import('stylelint').ConfigRules - * }} PrimaryOptions + * [category: string]: import('stylelint').ConfigRules | [ + * import('stylelint').ConfigRules, + * { + * defaultMessage: import('stylelint').RuleMessage, + * meta: import('stylelint').RuleMeta + * } + * ] + * }} CategorizedRules */ // Setting `line` to an invalid line number forces the warning to be reported and the `report({node})` option is used to display the location information: https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 @@ -18,24 +23,28 @@ const forceReport = {line: -1}; module.exports = stylelint.createPlugin( coverageRuleName, - /** @param {PrimaryOptions} primaryOptions */ - (primaryOptions, secondaryOptions, context) => { - const isPrimaryOptionsValid = validatePrimaryOptions(primaryOptions); + /** + * @param {CategorizedRules} categorizedRules - Configured Stylelint rules grouped by Polaris coverage category + */ + (categorizedRules, _, context) => { + // const isPrimaryOptionsValid = validatePrimaryOptions(categorizedRules); const rules = []; - for (const [categoryName, categoryConfigRules] of Object.entries( - primaryOptions, + for (const [category, [primaryOptions, secondaryOptions]] of Object.entries( + categorizedRules, )) { for (const [stylelintRuleName, ruleSettings] of Object.entries( - categoryConfigRules, + primaryOptions, )) { rules.push({ - ruleName: `${coverageRuleName}/${categoryName}`, + ruleName: `${coverageRuleName}/${category}`, stylelintRuleName, ruleSettings, - customMessage: customMessages[categoryName][stylelintRuleName], - metadata: ruleMetadata[categoryName], + customMessage: customMessages[category][stylelintRuleName] || { + message: secondaryOptions.defaultMessage, + }, + metadata: secondaryOptions.meta, severity: ruleSettings?.[1]?.severity, fix: context.fix && !ruleSettings?.[1]?.disableFix, }); @@ -43,16 +52,6 @@ module.exports = stylelint.createPlugin( } return (root, result) => { - const validOptions = stylelint.utils.validateOptions( - result, - coverageRuleName, - { - actual: isPrimaryOptionsValid, - }, - ); - - if (!validOptions) return; - for (const rule of rules) { const { ruleName, @@ -73,20 +72,35 @@ module.exports = stylelint.createPlugin( result, }, (warning) => { - const {message, args = ['value']} = customMessage; + /* tl;dr: Instead of reporting all problems under a single "stylelint-polaris/coverage" rule, the coverage plugin reports problems by category (e.g., "stylelint-polaris/coverage/colors") and augments the Stylelint PostCSS `result` with category specific metadata and an actionable message before reporting. + + Through its Plugin API, Stylelint supports creating custom rules complete with user defined messages and metadata. However, the API does not lend itself to the specificity that we want to give our users when indicating coverage problems out of the box. + + Stylelint's VS Code extension renders either: + 1) the `messages` and `meta` values set on the built-in stylelint rules _or_ + 2) the custom messages and metadata found on the `customMessages` and `ruleMetadata` properties of the Stylelint PostCSS `result` + + The `customMessages` and `ruleMetadata` properties of the Stylelint PostCSS `result` are derived from the optional `messages` and `meta` properties that can be set on a plugin's rule function. The coverage plugin reports several rules instead of one single rule, as each coverage category is reported as a rule (e.g., stylelint-polaris/coverage/colors). This means we need to set the custom message and metatdata URL directly onto the `customMessages` and `ruleMetadata` properties of the `result` using the rule name set on the problem reported, so that Stylelint's VS Code extension and CLI are able to link the reported coverage problem with a custom message and metadata URL + **See the relevant lines of source code permalinked below for more context: + + Stylelint `StylelintPostCSSResult` type => https://github.com/stylelint/stylelint/blob/11b3c1e5b446f39c9d90b9da55bd2353fe6d3210/types/stylelint/index.d.ts#L98) + + Stylelint `report` utility => https://github.com/stylelint/stylelint/blob/2290f557cb028242d978b06465dc108aa04a0c3a/lib/utils/report.js#L113 + + Stylelint VS Code `warningToDiagnostic` function => https://github.com/stylelint/vscode-stylelint/blob/5b3b4f6b04f2eacbf7cacc7694025dbe96abd128/src/utils/stylelint/warning-to-diagnostic.ts#L44 */ + + const {message, args = ['value']} = customMessage; const messageArgs = args.map( (nodeProp) => warning?.node?.[nodeProp], ); - // Stylelint's VS Code extension only looks for custom messages and metadata on the `customMessages` and `ruleMetadata` properties of the stylelint postcss result, otherwise it uses the `messages` and `meta` values set on the built in stylelint rule functions. + result.stylelint.customMessages[ruleName] = message(...messageArgs); result.stylelint.ruleMetadata[ruleName] = metadata; stylelint.utils.report({ result, ruleName, - message, - messageArgs, severity: severity ?? result.stylelint.config?.defaultSeverity ?? 'error', // If `warning.node` is NOT present, the warning is referring to a misconfigured rule @@ -99,12 +113,12 @@ module.exports = stylelint.createPlugin( }, ); -function validatePrimaryOptions(primaryOptions) { - if (!isPlainObject(primaryOptions)) return false; +// function validatePrimaryOptions(primaryOptions) { +// if (!isPlainObject(primaryOptions)) return false; - for (const categoryConfigRules of Object.values(primaryOptions)) { - if (!isPlainObject(categoryConfigRules)) return false; - } +// for (const [options] of Object.values(primaryOptions)) { +// if (!isPlainObject(options)) return false; +// } - return true; -} +// return true; +// } From a7d3221f29bcbc388abb76f139b648d05109f0e8 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Thu, 1 Dec 2022 09:49:01 -0500 Subject: [PATCH 18/19] Remove stylelint rule name from custom message drafts --- .../plugins/coverage/customMessages.js | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/stylelint-polaris/plugins/coverage/customMessages.js b/stylelint-polaris/plugins/coverage/customMessages.js index 321652071e3..85d09fc3ab9 100644 --- a/stylelint-polaris/plugins/coverage/customMessages.js +++ b/stylelint-polaris/plugins/coverage/customMessages.js @@ -2,24 +2,23 @@ module.exports = { colors: { 'color-named': { message: (name) => - `(color-named): Replace named color "${name}" with a Polaris color token`, + `Replace named color "${name}" with a Polaris color token`, }, 'color-no-hex': { - message: (hex) => - `(color-no-hex): Replace hex code "${hex}" with a Polaris color token`, + message: (hex) => `Replace hex code "${hex}" with a Polaris color token`, }, 'declaration-property-value-disallowed-list': { message: (prop, value) => - `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris color token`, + `Replace ${prop} value "${value}" with a Polaris color token`, args: ['prop', 'value'], }, 'function-disallowed-list': { message: (func) => - `(function-disallowed-list): Replace function "${func}" with a Polaris color token`, + `Replace function "${func}" with a Polaris color token`, }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params} with a Polaris color token`, + `Replace legacy Sass mixin "@${name} ${params} with a Polaris color token`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -41,15 +40,15 @@ module.exports = { motion: { 'at-rule-disallowed-list': { message: (atRule) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "${atRule}" with a Polaris motion token`, + `Replace legacy Sass mixin "${atRule}" with a Polaris motion token`, }, 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris motion token`, + `Replace legacy Sass function "${func}" with a Polaris motion token`, }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris motion token`, + `Replace legacy Sass mixin "@${name} ${params}} with a Polaris motion token`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -65,23 +64,23 @@ module.exports = { '--p-duration-1-5-0': 'CSS custom property', }; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris motion token`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris motion token`; }, }, }, font: { 'declaration-property-value-disallowed-list': { message: (prop, value) => - `(declaration-property-value-disallowed-list) - Replace ${prop} value "${value}" with a Polaris font token`, + `Replace ${prop} value "${value}" with a Polaris font token`, args: ['prop', 'value'], }, 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris font token`, + `Replace legacy Sass function "${func}" with a Polaris font token`, }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris font token`, + `Replace legacy Sass mixin "@${name} ${params}} with a Polaris font token`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -115,22 +114,22 @@ module.exports = { height: `Replace "${prop}" value "${value}" with a px or rem length value`, }; - return `(declaration-property-value-disallowed-list) - ${messageMap[prop]}`; + return `${messageMap[prop]}`; }, args: ['prop', 'value'], }, 'property-disallowed-list': { message: (prop) => - `(property-disallowed-list) - Replace use of "${prop}" with a Polaris layout component if possible`, + `Replace use of "${prop}" with a Polaris layout component if possible`, args: ['prop'], }, 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a px or rem length`, + `Replace legacy Sass function "${func}" with a px or rem length`, }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}" with a Polaris component`, + `Replace legacy Sass mixin "@${name} ${params}" with a Polaris component`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -151,14 +150,14 @@ module.exports = { '--p-choice-size': 'CSS custom property', }; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a px or rem length`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a px or rem length`; }, }, }, spacing: { 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris token`, + `Replace legacy Sass function "${func}" with a Polaris token`, }, 'stylelint-polaris/global-disallowed-list': { message: (value) => { @@ -173,14 +172,14 @@ module.exports = { '--p-text-field-spinner-offset': 'CSS custom property', }; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris spacing token`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris spacing token`; }, }, }, shape: { 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}} with a Polaris shape token`, + `Replace legacy Sass mixin "@${name} ${params}} with a Polaris shape token`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { @@ -205,14 +204,14 @@ module.exports = { 'var(--p-text-field-focus-ring-offset)': 'CSS custom property', }; legacyValueTypeMap[value]; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris shape token`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris shape token`; }, }, }, depth: { 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris depth token`, + `Replace legacy Sass function "${func}" with a Polaris depth token`, }, 'stylelint-polaris/global-disallowed-list': { message: (value) => { @@ -231,19 +230,19 @@ module.exports = { '--p-top-bar-shadow': 'CSS custom property', }; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris depth token`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris depth token`; }, }, }, 'z-index': { 'declaration-property-value-allowed-list': { message: (prop, value) => - `(declaration-property-value-allowed-list) - Replace "${prop}" value "${value}" with a Polaris z-index token`, + `Replace "${prop}" value "${value}" with a Polaris z-index token`, args: ['prop', 'value'], }, 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris z-index token`, + `Replace legacy Sass function "${func}" with a Polaris z-index token`, }, 'stylelint-polaris/global-disallowed-list': { message: (value) => { @@ -255,46 +254,45 @@ module.exports = { 'var(--p-override-loading-z-index)': 'CSS custom property', }; - return `(global-disallowed-list) - Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris z-index token`; + return `Replace legacy ${legacyValueTypeMap[value]} "${value}" with a Polaris z-index token`; }, }, }, conventions: { 'unit-disallowed-list': { message: (unit) => - `(unit-disallowed-list) - Replace hard-coded value ${ + `Replace hard-coded value ${ unit ? `"${unit}" ` : '' }with a Polaris token`, }, 'stylelint-polaris/custom-properties-allowed-list': { - message: (value) => - `(custom-properties-allowed-list) - Unexpected CSS custom property ${value}`, + message: (value) => `Unexpected CSS custom property ${value}`, }, }, breakpoints: { 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris breakpoint token`, + `Replace legacy Sass function "${func}" with a Polaris breakpoint token`, }, 'stylelint-polaris/media-queries-allowed-list': { message: (name, params) => - `(media-query-allowed-list) - Replace media query params "${params}" with a Polaris breakpoint token`, + `Replace media query params "${params}" with a Polaris breakpoint token`, args: ['name', 'params'], }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Replace legacy Sass mixin "@${name} ${params}" with a Polaris breakpoint token`, + `Replace legacy Sass mixin "@${name} ${params}" with a Polaris breakpoint token`, args: ['name', 'params'], }, }, legacy: { 'function-disallowed-list': { message: (func) => - `(function-disallowed-list) - Replace legacy Sass function "${func}" with a Polaris token`, + `Replace legacy Sass function "${func}" with a Polaris token`, }, 'stylelint-polaris/at-rule-disallowed-list': { message: (name, params) => - `(at-rule-disallowed-list) - Unexpected use of legacy Sass mixin "@${name} ${params}"`, + `Unexpected use of legacy Sass mixin "@${name} ${params}"`, args: ['name', 'params'], }, 'stylelint-polaris/global-disallowed-list': { From d5207f7328ee6b01b00635118b155c0c6387f58a Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Thu, 1 Dec 2022 13:19:29 -0500 Subject: [PATCH 19/19] WIP stash --- stylelint-polaris/plugins/coverage/index.js | 117 ++++++++++---------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/stylelint-polaris/plugins/coverage/index.js b/stylelint-polaris/plugins/coverage/index.js index 6b66a40cda4..3daa63c1d82 100644 --- a/stylelint-polaris/plugins/coverage/index.js +++ b/stylelint-polaris/plugins/coverage/index.js @@ -1,64 +1,77 @@ const stylelint = require('stylelint'); -// const {isPlainObject} = require('../../utils'); - -const customMessages = require('./customMessages'); +const {isPlainObject} = require('../../utils'); const coverageRuleName = 'stylelint-polaris/coverage'; +/* The stylelint-polaris/coverage rule is configured by categorizing Stylelint rules in order to enable reporting of problems by coverage category + +(e.g., Unexpected named color "blue" (color-named) Stylelint(stylelint-polaris/coverage/colors") */ + +/** + * @typedef {import('stylelint').ConfigRules} StylelintRules + */ + +/** + * @typedef {object} CategorySettings + * @property {string} [message] - Category message appended to the warning + * @property {import('stylelint').RuleMeta} [meta] - Category documentation URL hyperlinked to the reported rule in the VS Code diagnostic + */ + /** * @typedef {{ - * [category: string]: import('stylelint').ConfigRules | [ - * import('stylelint').ConfigRules, - * { - * defaultMessage: import('stylelint').RuleMessage, - * meta: import('stylelint').RuleMeta - * } - * ] + * [category: string]: StylelintRuleConfig | [ + * StylelintRuleConfig, CategorySettings + * ] * }} CategorizedRules */ -// Setting `line` to an invalid line number forces the warning to be reported and the `report({node})` option is used to display the location information: https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 -const forceReport = {line: -1}; - module.exports = stylelint.createPlugin( coverageRuleName, /** * @param {CategorizedRules} categorizedRules - Configured Stylelint rules grouped by Polaris coverage category */ - (categorizedRules, _, context) => { - // const isPrimaryOptionsValid = validatePrimaryOptions(categorizedRules); + (categorizedRules) => { + const isPrimaryOptionsValid = validatePrimaryOptions(categorizedRules); - const rules = []; + const coverageRules = []; - for (const [category, [primaryOptions, secondaryOptions]] of Object.entries( - categorizedRules, - )) { + for (const [category, categoryConfig] of Object.entries(categorizedRules)) { + const [stylelintRules, categorySettings] = + normalizeConfig(categoryConfig); for (const [stylelintRuleName, ruleSettings] of Object.entries( - primaryOptions, + stylelintRules, )) { - rules.push({ + coverageRules.push({ ruleName: `${coverageRuleName}/${category}`, stylelintRuleName, ruleSettings, - customMessage: customMessages[category][stylelintRuleName] || { - message: secondaryOptions.defaultMessage, - }, - metadata: secondaryOptions.meta, + message: ruleSettings?.[1]?.message || categorySettings?.message, + metadata: categorySettings?.meta, severity: ruleSettings?.[1]?.severity, - fix: context.fix && !ruleSettings?.[1]?.disableFix, + fix: !ruleSettings?.[1]?.disableFix, }); } } return (root, result) => { - for (const rule of rules) { + const validOptions = stylelint.utils.validateOptions( + result, + coverageRuleName, + { + actual: isPrimaryOptionsValid, + }, + ); + + if (!validOptions) return; + + for (const rule of coverageRules) { const { ruleName, stylelintRuleName, ruleSettings, - customMessage, - metadata, + message, + ruleMetadata, severity, fix, } = rule; @@ -72,35 +85,15 @@ module.exports = stylelint.createPlugin( result, }, (warning) => { - /* tl;dr: Instead of reporting all problems under a single "stylelint-polaris/coverage" rule, the coverage plugin reports problems by category (e.g., "stylelint-polaris/coverage/colors") and augments the Stylelint PostCSS `result` with category specific metadata and an actionable message before reporting. - - Through its Plugin API, Stylelint supports creating custom rules complete with user defined messages and metadata. However, the API does not lend itself to the specificity that we want to give our users when indicating coverage problems out of the box. - - Stylelint's VS Code extension renders either: - 1) the `messages` and `meta` values set on the built-in stylelint rules _or_ - 2) the custom messages and metadata found on the `customMessages` and `ruleMetadata` properties of the Stylelint PostCSS `result` - - The `customMessages` and `ruleMetadata` properties of the Stylelint PostCSS `result` are derived from the optional `messages` and `meta` properties that can be set on a plugin's rule function. The coverage plugin reports several rules instead of one single rule, as each coverage category is reported as a rule (e.g., stylelint-polaris/coverage/colors). This means we need to set the custom message and metatdata URL directly onto the `customMessages` and `ruleMetadata` properties of the `result` using the rule name set on the problem reported, so that Stylelint's VS Code extension and CLI are able to link the reported coverage problem with a custom message and metadata URL - - **See the relevant lines of source code permalinked below for more context: + // Setting `line` to an invalid line number forces the warning to be reported and the `report({node})` option is used to display the location information: https://github.com/stylelint/stylelint/blob/57cbcd4eb0ee809006a1e3d2ccfe73af48744ad5/lib/utils/report.js#L49-L52 + const forceReport = {line: -1}; - Stylelint `StylelintPostCSSResult` type => https://github.com/stylelint/stylelint/blob/11b3c1e5b446f39c9d90b9da55bd2353fe6d3210/types/stylelint/index.d.ts#L98) - - Stylelint `report` utility => https://github.com/stylelint/stylelint/blob/2290f557cb028242d978b06465dc108aa04a0c3a/lib/utils/report.js#L113 - - Stylelint VS Code `warningToDiagnostic` function => https://github.com/stylelint/vscode-stylelint/blob/5b3b4f6b04f2eacbf7cacc7694025dbe96abd128/src/utils/stylelint/warning-to-diagnostic.ts#L44 */ - - const {message, args = ['value']} = customMessage; - const messageArgs = args.map( - (nodeProp) => warning?.node?.[nodeProp], - ); - - result.stylelint.customMessages[ruleName] = message(...messageArgs); - result.stylelint.ruleMetadata[ruleName] = metadata; + result.stylelint.ruleMetadata[ruleName] = ruleMetadata; stylelint.utils.report({ result, ruleName, + message: `${warning.text} ${message ? ` - ${message}` : ''}`, severity: severity ?? result.stylelint.config?.defaultSeverity ?? 'error', // If `warning.node` is NOT present, the warning is referring to a misconfigured rule @@ -113,12 +106,16 @@ module.exports = stylelint.createPlugin( }, ); -// function validatePrimaryOptions(primaryOptions) { -// if (!isPlainObject(primaryOptions)) return false; +function normalizeConfig(config) { + return Array.isArray(config) ? config : [config, {}]; +} + +function validatePrimaryOptions(primaryOptions) { + if (!isPlainObject(primaryOptions)) return false; -// for (const [options] of Object.values(primaryOptions)) { -// if (!isPlainObject(options)) return false; -// } + for (const categoryConfigRules of Object.values(primaryOptions)) { + if (!isPlainObject(categoryConfigRules)) return false; + } -// return true; -// } + return true; +}