diff --git a/.changeset/all-cloths-sleep.md b/.changeset/all-cloths-sleep.md
new file mode 100644
index 0000000000..d5a06ec895
--- /dev/null
+++ b/.changeset/all-cloths-sleep.md
@@ -0,0 +1,5 @@
+---
+'@swisspost/design-system-documentation': patch
+---
+
+Added information in the migration guide regarding the update on the gutter utility classes.
diff --git a/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts b/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
index 75beb88793..2ceaf98532 100644
--- a/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
+++ b/packages/documentation/src/stories/misc/migration-guide/migrationv9-10.component.ts
@@ -177,6 +177,7 @@ export class MigrationV99Component extends LitElement {
+ 🪄 migration rule
The gutter classes naming (g-*
, gx-*
,
gy-*
) has changed to pixel-based names
diff --git a/packages/eslint/docs/rules/html/migrations/no-deprecated-gutter-utilities.md b/packages/eslint/docs/rules/html/migrations/no-deprecated-gutter-utilities.md
new file mode 100644
index 0000000000..a32471d3f0
--- /dev/null
+++ b/packages/eslint/docs/rules/html/migrations/no-deprecated-gutter-utilities.md
@@ -0,0 +1,36 @@
+# `no-deprecated-gutter-utilities`
+
+Flags all deprecated bootstrap gutter utility classes and replaces them with the new ones.
+
+- Type: problem
+- 🔧 Supports autofix (--fix)
+
+## Class list
+
+The classes that are handled by this rule are the following ones:
+
+- Starts with one of those gutter class names: `g-*`, `gx-*`, `gy-*`
+- Then, has either no breakpoint or one of the following breakpoint: `*sm-*`,`*md-*`,`*lg-*`, `*xl-*`
+- Ends with one of the following class values: `*1`, `*2`, `*3`, `*4`, `*5`
+
+## Rule Options
+
+This rule does not have any configuration options.
+
+## Example
+
+### ❌ Invalid Code
+
+```html
+Content
+Content
+Content
+```
+
+### ✅ Valid Code
+
+```html
+Content
+Content
+Content
+```
diff --git a/packages/eslint/src/rules/html/migrations/index.ts b/packages/eslint/src/rules/html/migrations/index.ts
index fbca75862d..d2daaa988c 100644
--- a/packages/eslint/src/rules/html/migrations/index.ts
+++ b/packages/eslint/src/rules/html/migrations/index.ts
@@ -3,19 +3,9 @@ import noDeprecatedLoaderRule, { name as noDeprecatedLoaderRuleName } from './no
import noUnnumberedBorderRadiusRule, {
name as noUnnumberedBorderRadiusRuleName,
} from './no-unnumbered-border-radius';
-import {
- rulePhase1 as noDeprecatedSpacingUtilitiesRulePhase1,
- rulePhase2 as noDeprecatedSpacingUtilitiesRulePhase2,
- namePhase1 as noDeprecatedSpacingUtilitiesRulePhase1Name,
- namePhase2 as noDeprecatedSpacingUtilitiesRulePhase2Name,
-} from './no-deprecated-spacing-utilities';
-
-import {
- rulePhase1 as noDeprecatedSizingUtilitiesRulePhase1,
- rulePhase2 as noDeprecatedSizingUtilitiesRulePhase2,
- namePhase1 as noDeprecatedSizingUtilitiesRulePhase1Name,
- namePhase2 as noDeprecatedSizingUtilitiesRulePhase2Name,
-} from './no-deprecated-sizing-utilities';
+import { rules as noDeprecatedSpacingUtilities } from './no-deprecated-spacing-utilities';
+import { rules as noDeprecatedGutterUtilities } from './no-deprecated-gutter-utilities';
+import { rules as noDeprecatedSizingUtilities } from './no-deprecated-sizing-utilities';
import noFormTextRule, { name as noFormTextRuleName } from './no-form-text';
import noDeprecatedFontWeightRule, {
name as noDeprecatedFontWeightRuleName,
@@ -40,12 +30,14 @@ export const htmlMigrationRules = {
[noDeprecatedBtnRgRuleName]: noDeprecatedBtnRgRule,
[noDeprecatedLoaderRuleName]: noDeprecatedLoaderRule,
[noUnnumberedBorderRadiusRuleName]: noUnnumberedBorderRadiusRule,
- [noDeprecatedSpacingUtilitiesRulePhase1Name]: noDeprecatedSpacingUtilitiesRulePhase1,
- [noDeprecatedSpacingUtilitiesRulePhase2Name]: noDeprecatedSpacingUtilitiesRulePhase2,
- [noDeprecatedSizingUtilitiesRulePhase1Name]: noDeprecatedSizingUtilitiesRulePhase1,
- [noDeprecatedSizingUtilitiesRulePhase2Name]: noDeprecatedSizingUtilitiesRulePhase2,
+ [noDeprecatedSpacingUtilities[0].name]: noDeprecatedSpacingUtilities[0].rule,
+ [noDeprecatedSpacingUtilities[1].name]: noDeprecatedSpacingUtilities[1].rule,
+ [noDeprecatedSizingUtilities[0].name]: noDeprecatedSizingUtilities[0].rule,
+ [noDeprecatedSizingUtilities[0].name]: noDeprecatedSizingUtilities[1].rule,
[noFormTextRuleName]: noFormTextRule,
[noDeprecatedFontWeightRuleName]: noDeprecatedFontWeightRule,
+ [noDeprecatedGutterUtilities[0].name]: noDeprecatedGutterUtilities[0].rule,
+ [noDeprecatedGutterUtilities[1].name]: noDeprecatedGutterUtilities[1].rule,
[noDeprecatedShadowUtilitiesRuleName]: noDeprecatedShadowUtilitiesRule,
[noDeprecatedHClearfixName]: noDeprecatedHClearfix,
[noDeprecatedHVisuallyhiddenRuleName]: noDeprecatedHVisuallyhiddenRule,
diff --git a/packages/eslint/src/rules/html/migrations/no-deprecated-gutter-utilities.ts b/packages/eslint/src/rules/html/migrations/no-deprecated-gutter-utilities.ts
new file mode 100644
index 0000000000..815a578463
--- /dev/null
+++ b/packages/eslint/src/rules/html/migrations/no-deprecated-gutter-utilities.ts
@@ -0,0 +1,32 @@
+import { bootstrapSizeMap } from '../../../utils/common-data';
+import {
+ arrayToMap,
+ createTwoPhasesClassUpdateRule,
+ setUpClassesMutations,
+ TwoPhasesData,
+} from '../../../utils/two-phases-classes-update';
+
+// Class names
+const classNames = ['g-', 'gx-', 'gy-'];
+
+export const data: TwoPhasesData = setUpClassesMutations(
+ arrayToMap(classNames),
+ bootstrapSizeMap,
+ 'deprecatedGutterUtilities',
+);
+
+export const rules = createTwoPhasesClassUpdateRule({
+ name: 'no-deprecated-gutter-utilities',
+ phases: [
+ {
+ ...data.phases[0],
+ description:
+ 'Flags deprecated bootstrap gutter utility classes and replaces them with final ones with a temporary name (phase 1).',
+ },
+ {
+ ...data.phases[1],
+ description:
+ 'Flags deprecated bootstrap gutter utility classes and replaces the temporary class names with the final ones.',
+ },
+ ],
+});
diff --git a/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts b/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts
index afed11dbf6..da0abf6029 100644
--- a/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts
+++ b/packages/eslint/src/rules/html/migrations/no-deprecated-sizing-utilities.ts
@@ -1,5 +1,9 @@
-import { createClassUpdateRule } from '../../../utils/create-class-update-rule';
-import { setUpClassesMutations, TwoPhasesData } from '../../../utils/two-phases-classes-update';
+import { bootstrapSizeMap } from '../../../utils/common-data';
+import {
+ createTwoPhasesClassUpdateRule,
+ setUpClassesMutations,
+ TwoPhasesData,
+} from '../../../utils/two-phases-classes-update';
// Class names
const classNamesMap: Record = {
@@ -11,11 +15,7 @@ const classNamesMap: Record = {
// Previous values mapped to the new values
const classValuesMap: Record = {
- '1': 4,
- '2': 8,
- '4': 24,
- '3': 16,
- '5': 48,
+ ...bootstrapSizeMap,
'hair': 1,
'line': 2,
'micro': 4,
@@ -42,23 +42,18 @@ export const data: TwoPhasesData = setUpClassesMutations(
'deprecatedSizingUtilities',
);
-export const namePhase1 = 'no-deprecated-sizing-utilities-phase-1';
-export const namePhase2 = 'no-deprecated-sizing-utilities-phase-2';
-
-export const rulePhase1 = createClassUpdateRule({
- name: namePhase1,
- type: 'problem',
- description:
- 'Flags deprecated sizing utility classes and replaces them with the new ones with a temporary name (phase 1).',
- messages: data.messagesPhase1,
- mutations: data.mutationsPhase1,
-});
-
-export const rulePhase2 = createClassUpdateRule({
- name: namePhase2,
- type: 'problem',
- description:
- 'Flags deprecated sizing utility classes and replaces the temporary class names with the final ones.',
- messages: data.messagesPhase2,
- mutations: data.mutationsPhase2,
+export const rules = createTwoPhasesClassUpdateRule({
+ name: 'no-deprecated-sizing-utilities',
+ phases: [
+ {
+ ...data.phases[0],
+ description:
+ 'Flags deprecated sizing utility classes and replaces them with the new ones with a temporary name (phase 1).',
+ },
+ {
+ ...data.phases[1],
+ description:
+ 'Flags deprecated sizing utility classes and replaces the temporary class names with the final ones.',
+ },
+ ],
});
diff --git a/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts b/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts
index f2efdcaf20..dc7c5112c8 100644
--- a/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts
+++ b/packages/eslint/src/rules/html/migrations/no-deprecated-spacing-utilities.ts
@@ -1,6 +1,7 @@
-import { createClassUpdateRule } from '../../../utils/create-class-update-rule';
+import { bootstrapSizeMap } from '../../../utils/common-data';
import {
arrayToMap,
+ createTwoPhasesClassUpdateRule,
setUpClassesMutations,
TwoPhasesData,
} from '../../../utils/two-phases-classes-update';
@@ -25,11 +26,7 @@ const classNames = [
// Previous values mapped to the new values
const classValuesMap: Record = {
- '1': 4,
- '2': 8,
- '4': 24,
- '3': 16,
- '5': 48,
+ ...bootstrapSizeMap,
'hair': 1,
'line': 2,
'micro': 4,
@@ -53,23 +50,18 @@ export const data: TwoPhasesData = setUpClassesMutations(
'deprecatedSpacingUtilities',
);
-export const namePhase1 = 'no-deprecated-spacing-utilities-phase-1';
-export const namePhase2 = 'no-deprecated-spacing-utilities-phase-2';
-
-export const rulePhase1 = createClassUpdateRule({
- name: namePhase1,
- type: 'problem',
- description:
- 'Flags deprecated named and numbered spacing utility classes and replaces them with pixel ones with a temporary name (phase 1).',
- messages: data.messagesPhase1,
- mutations: data.mutationsPhase1,
-});
-
-export const rulePhase2 = createClassUpdateRule({
- name: namePhase2,
- type: 'problem',
- description:
- 'Flags deprecated named and numbered spacing utility classes and replaces the temporary class names with the final ones.',
- messages: data.messagesPhase2,
- mutations: data.mutationsPhase2,
+export const rules = createTwoPhasesClassUpdateRule({
+ name: 'no-deprecated-spacing-utilities',
+ phases: [
+ {
+ ...data.phases[0],
+ description:
+ 'Flags deprecated named and numbered spacing utility classes and replaces them with pixel ones with a temporary name (phase 1).',
+ },
+ {
+ ...data.phases[1],
+ description:
+ 'Flags deprecated named and numbered spacing utility classes and replaces the temporary class names with the final ones.',
+ },
+ ],
});
diff --git a/packages/eslint/src/utils/common-data.ts b/packages/eslint/src/utils/common-data.ts
new file mode 100644
index 0000000000..c1706855fe
--- /dev/null
+++ b/packages/eslint/src/utils/common-data.ts
@@ -0,0 +1,7 @@
+export const bootstrapSizeMap: Record = {
+ '1': 4,
+ '2': 8,
+ '4': 24,
+ '3': 16,
+ '5': 48,
+};
diff --git a/packages/eslint/src/utils/create-class-update-rule.ts b/packages/eslint/src/utils/create-class-update-rule.ts
index 71eff6b38c..b541e4037d 100644
--- a/packages/eslint/src/utils/create-class-update-rule.ts
+++ b/packages/eslint/src/utils/create-class-update-rule.ts
@@ -2,49 +2,59 @@ import { createRule } from './create-rule';
import { HtmlNode } from '../parsers/html/html-node';
import { Rule } from 'eslint';
-interface RuleConfig{
+type RuleType = Rule.RuleMetaData['type'];
+
+export interface RuleConfigBase {
name: string;
+ type?: RuleType;
+}
+
+export interface PhaseConfig {
description: string;
messages: T;
- mutations: Record;
- type?: Rule.RuleMetaData['type'];
+ mutations: Record;
}
+type SinglePhaseRuleConfig = RuleConfigBase & PhaseConfig;
+
export const createClassUpdateRule = >(
- config: RuleConfig
-) => createRule({
- name: config.name,
- meta: {
- docs: {
- dir: 'html',
- description: config.description,
+ config: SinglePhaseRuleConfig,
+) =>
+ createRule({
+ name: config.name,
+ meta: {
+ docs: {
+ dir: 'html',
+ description: config.description,
+ },
+ messages: config.messages,
+ type: config.type || 'problem',
+ fixable: 'code',
+ schema: [],
},
- messages: config.messages,
- type: config.type || 'problem',
- fixable: 'code',
- schema: [],
- },
- defaultOptions: [],
- create(context) {
- return {
- tag(node: HtmlNode) {
- const $node = node.toCheerio();
+ defaultOptions: [],
+ create(context) {
+ return {
+ tag(node: HtmlNode) {
+ const $node = node.toCheerio();
- Object.entries(config.mutations).forEach(([messageId, [oldClass, newClass]]) => {
- if ($node.hasClass(oldClass)) {
- context.report({
- messageId,
- loc: node.loc,
- ... newClass ? {
- fix(fixer) {
- const fixedNode = $node.removeClass(oldClass).addClass(newClass);
- return fixer.replaceTextRange(node.range, fixedNode.toString());
- }
- } : {},
- });
- }
- });
- },
- };
- },
-});
+ Object.entries(config.mutations).forEach(([messageId, [oldClass, newClass]]) => {
+ if ($node.hasClass(oldClass)) {
+ context.report({
+ messageId,
+ loc: node.loc,
+ ...(newClass
+ ? {
+ fix(fixer) {
+ const fixedNode = $node.removeClass(oldClass).addClass(newClass);
+ return fixer.replaceTextRange(node.range, fixedNode.toString());
+ },
+ }
+ : {}),
+ });
+ }
+ });
+ },
+ };
+ },
+ });
diff --git a/packages/eslint/src/utils/two-phases-classes-update.ts b/packages/eslint/src/utils/two-phases-classes-update.ts
index 9375aa3f71..3163cfbf0c 100644
--- a/packages/eslint/src/utils/two-phases-classes-update.ts
+++ b/packages/eslint/src/utils/two-phases-classes-update.ts
@@ -1,11 +1,16 @@
+import { createClassUpdateRule, PhaseConfig, RuleConfigBase } from './create-class-update-rule';
+
+type RuleMessages = Record;
+
+interface TwoPhaseRuleConfig extends RuleConfigBase {
+ phases: [PhaseConfig, PhaseConfig];
+}
+
export interface TwoPhasesData {
- messagesPhase1: Record;
- mutationsPhase1: Record;
- messagesPhase2: Record;
- mutationsPhase2: Record;
+ phases: [PhaseConfig>, PhaseConfig>];
}
-// Empty string means no middle part
+// Empty string means no breakpoint
const breakpoints = ['sm-', 'md-', 'lg-', 'xl-', ''];
export function arrayToMap(array: Array): Record {
@@ -28,12 +33,10 @@ export function setUpClassesMutations(
classValuesMap: Record,
messageId: string,
): TwoPhasesData {
- const returnData: TwoPhasesData = {
- messagesPhase1: {},
- mutationsPhase1: {},
- messagesPhase2: {},
- mutationsPhase2: {},
- };
+ const messagesPhase1: Record = {};
+ const mutationsPhase1: Record = {};
+ const messagesPhase2: Record = {};
+ const mutationsPhase2: Record = {};
let index = 0;
@@ -52,24 +55,56 @@ export function setUpClassesMutations(
const keyPhase1 = `${messageId}Phase1_${index}`;
- returnData.messagesPhase1[
+ messagesPhase1[
keyPhase1
] = `The "${oldClass}" class is deprecated. Please replace it with "${finalNewClass}".`;
// Mutate from `oldClass` to `_tmp-newClass`
- returnData.mutationsPhase1[keyPhase1] = [oldClass, tempClass];
+ mutationsPhase1[keyPhase1] = [oldClass, tempClass];
const keyPhase2 = `${messageId}Phase2_${index}`;
- returnData.messagesPhase2[
+ messagesPhase2[
keyPhase2
] = `The "${oldClass}" class is deprecated. Please replace it with "${finalNewClass}".`;
// Mutate from `_tmp-newClass` to `newClass`
- returnData.mutationsPhase2[keyPhase2] = [tempClass, finalNewClass];
+ mutationsPhase2[keyPhase2] = [tempClass, finalNewClass];
index++;
}
}
}
-
- return returnData;
+ return {
+ phases: [
+ {
+ description: '',
+ messages: messagesPhase1,
+ mutations: mutationsPhase1,
+ },
+ {
+ description: '',
+ messages: messagesPhase2,
+ mutations: mutationsPhase2,
+ },
+ ],
+ };
}
+
+export const createTwoPhasesClassUpdateRule = (
+ config: TwoPhaseRuleConfig,
+) => {
+ const makePhaseRule = (phaseIndex: 0 | 1) => {
+ const phase = config.phases[phaseIndex];
+ return createClassUpdateRule({
+ name: `${config.name}-phase-${phaseIndex + 1}`,
+ type: config.type || 'problem',
+ description: phase.description,
+ messages: phase.messages,
+ mutations: phase.mutations,
+ });
+ };
+
+ return [
+ { name: `${config.name}-phase-1`, rule: makePhaseRule(0) },
+ { name: `${config.name}-phase-2`, rule: makePhaseRule(1) },
+ ];
+};
diff --git a/packages/eslint/test/rules/html/migrations/no-deprecated-gutter-utilities.spec.ts b/packages/eslint/test/rules/html/migrations/no-deprecated-gutter-utilities.spec.ts
new file mode 100644
index 0000000000..b6ccc9ff7d
--- /dev/null
+++ b/packages/eslint/test/rules/html/migrations/no-deprecated-gutter-utilities.spec.ts
@@ -0,0 +1,7 @@
+import { rules, data } from '../../../../src/rules/html/migrations/no-deprecated-gutter-utilities';
+import { generatedDataTester } from '../../../utils/generated-data-tester';
+
+const validClasses = ['g-sm-16', 'gx-md-48', 'gy-md-24'];
+
+generatedDataTester(rules[0].name, rules[0].rule, data.phases[0].mutations, validClasses);
+generatedDataTester(rules[1].name, rules[1].rule, data.phases[1].mutations, validClasses);
diff --git a/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts b/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts
index cd03f27e74..136d9bc1b1 100644
--- a/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts
+++ b/packages/eslint/test/rules/html/migrations/no-deprecated-sizing-utilities.spec.ts
@@ -1,13 +1,7 @@
-import {
- rulePhase1,
- namePhase1,
- rulePhase2,
- namePhase2,
- data,
-} from '../../../../src/rules/html/migrations/no-deprecated-sizing-utilities';
+import { rules, data } from '../../../../src/rules/html/migrations/no-deprecated-sizing-utilities';
import { generatedDataTester } from '../../../utils/generated-data-tester';
-const validData = ['w-sm-16', 'h-md-48', 'h-md-three-quarters'];
+const validClasses = ['w-sm-16', 'h-md-48', 'h-md-three-quarters'];
-generatedDataTester(namePhase1, rulePhase1, data.mutationsPhase1, validData);
-generatedDataTester(namePhase2, rulePhase2, data.mutationsPhase2, validData);
+generatedDataTester(rules[0].name, rules[0].rule, data.phases[0].mutations, validClasses);
+generatedDataTester(rules[1].name, rules[1].rule, data.phases[1].mutations, validClasses);
diff --git a/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts b/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts
index 41b77e3456..9d37efa120 100644
--- a/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts
+++ b/packages/eslint/test/rules/html/migrations/no-deprecated-spacing-utilities.spec.ts
@@ -1,13 +1,7 @@
-import {
- rulePhase1,
- namePhase1,
- rulePhase2,
- namePhase2,
- data,
-} from '../../../../src/rules/html/migrations/no-deprecated-spacing-utilities';
+import { rules, data } from '../../../../src/rules/html/migrations/no-deprecated-spacing-utilities';
import { generatedDataTester } from '../../../utils/generated-data-tester';
-const validData = ['mt-sm-16', 'pb-md-48'];
+const validClasses = ['mt-sm-16', 'pb-md-48'];
-generatedDataTester(namePhase1, rulePhase1, data.mutationsPhase1, validData);
-generatedDataTester(namePhase2, rulePhase2, data.mutationsPhase2, validData);
+generatedDataTester(rules[0].name, rules[0].rule, data.phases[0].mutations, validClasses);
+generatedDataTester(rules[1].name, rules[1].rule, data.phases[1].mutations, validClasses);