@lucca/stylelint-config-prisme 22.0.0-rc.4 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,68 @@
1
1
  // WARNING!
2
2
  // Always check for variables with regular expressions. A string will match all the value, not part of it.
3
+ // {string} --token -> --token ✓ | var(--token) ✗
4
+ //
5
+ // Boundary convention:
6
+ // - Each pattern ends with `(?![\w-])`: matches the exact token only; the guard blocks a trailing word char OR `-`.
7
+ // --token ✓ | --token-x ✗ | --tokenX ✗
3
8
 
4
9
  export default [
5
10
  {
6
- objectPattern: /--commons-elevations-elevation-[1-6]]/,
11
+ // SEE https://regex101.com/r/Qkq3ok.
12
+ objectPattern: /--commons-elevations-elevation-[1-6](?![\w-])/,
7
13
  versionDeprecated: '17.3.0',
8
14
  versionDeleted: '19.1.0',
9
15
  },
10
16
  {
11
- objectPattern: /--commons-boxShadow-X*(S|M|L)/,
17
+ // SEE https://regex101.com/r/Y00E16.
18
+ objectPattern: /--commons-boxShadow-X*(S|M|L)(?![\w-])/,
12
19
  versionDeprecated: '17.3.0',
13
20
  versionDeleted: '19.1.0',
14
21
  },
15
22
  {
16
- objectPattern: /--palettes-(grey|primary|secondary|lucca)-[0-9]{2,3}/,
23
+ // SEE https://regex101.com/r/BRi8Yi.
24
+ objectPattern: /--palettes-(grey|primary|secondary|lucca)-(25|50|100|200|300|400|500|600|700|800|900)(?![\w-])/,
17
25
  versionDeprecated: '17.3.0',
18
- versionDeleted: '21.1.0',
26
+ versionDeleted: '22.0.0',
27
+ actions: `
28
+ * Remplacer \`grey\` par \`neutral\`.
29
+ * Remplacer \`primary\` & \`secondary\` par \`product\`.
30
+ * Remplacer \`lucca\` par \`brand\`.
31
+ `,
32
+ urls: {
33
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/b/15c256',
34
+ },
19
35
  },
20
36
  {
21
- objectPattern: /--spacings-X*(S|M|L)/,
37
+ // SEE https://regex101.com/r/WqLllN.
38
+ objectPattern: /--spacings-X*(S|M|L)(?![\w-])/,
22
39
  versionDeprecated: '17.4.0',
23
40
  versionDeleted: '19.1.0',
24
41
  },
25
42
  {
26
- objectPattern: /--colors-(black|white)-color/,
43
+ // SEE https://regex101.com/r/iMddaz.
44
+ objectPattern: /--colors-(black|white)-color(?![\w-])/,
27
45
  versionDeprecated: '18.2.0',
28
- versionDeleted: '21.1.0',
46
+ versionDeleted: '22.0.0',
47
+ actions: `
48
+ * Remplacer \`--colors-white-color\` par \`--palettes-neutral-0\` ou \`--pr-t-elevation-surface-raised\` selon si la couleur en question est considérée comme une couleur ou une surface.
49
+ * Remplacer \`--colors-black-color\` par \`--palettes-neutral-900\`.
50
+ `,
29
51
  },
30
52
  {
31
- objectPattern: /--commons-navSide-compact-width/,
53
+ // SEE https://regex101.com/r/xx7vjW.
54
+ objectPattern: /--commons-navSide-compact-width(?![\w-])/,
32
55
  versionDeprecated: '18.3.0',
33
56
  versionDeleted: '20.1.0',
34
57
  },
35
58
  {
36
- objectPattern: /--commons-borderRadius-(M|L|XL|full)/,
59
+ // SEE https://regex101.com/r/1P70OB.
60
+ objectPattern: /--commons-borderRadius-(M|L|XL|full)(?![\w-])/,
37
61
  versionDeprecated: '20.2.0',
62
+ versionDeleted: '22.0.0',
63
+ actions: 'Remplacer par \`--pr-t-border-radius-XXX\`',
64
+ urls: {
65
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/t/page-40c515-88288181-15c256-0',
66
+ },
38
67
  },
39
68
  ];
@@ -1,182 +1,264 @@
1
+ // Deprecated selectors — used by Stylelint's selector-disallowed-list (see stylelint.config.mjs).
2
+ // Each `objectPattern` is matched with `.test()` against a selector.
3
+ //
4
+ // WARNING!
5
+ // Always match selectors with regular expressions. A string will match the whole selector, not part of it.
6
+ // {string} .token -> .token ✓ | .foo .token ✗ | .token.mod-x ✗
7
+ //
8
+ // Boundary conventions:
9
+ // - Exact token — `\.token(?![\w-])`: matches the token only; the guard blocks a trailing word char OR `-`.
10
+ // .token ✓ | .token-x ✗ | .tokenX ✗
11
+ // - Component root — `\.token\b`: also flags hyphen-children (`\b` counts `-` as a boundary); not camelCase or plural.
12
+ // .token ✓ | .token-child ✓ | .tokens ✗
13
+ // - Enumerated children — `\.token(-(a|b))?(?![\w-])`: base plus only its known children (avoids a broad root).
14
+ // .token ✓ | .token-a ✓ | .token-c ✗
15
+ // - Order-independent combo — `(?=\S*\.a(?![\w-]))(?=\S*\.b(?![\w-]))\S*`: both tokens, any order; `\S*` never crosses whitespace (no descendant match).
16
+ // .a.b ✓ | .b.a ✓ | .a .b ✗
17
+ // - Catch-all — `\.token.*`: base plus any suffix.
18
+ // .token ✓ | .tokenX ✓ | .other ✗
1
19
  export default [
2
20
  // Any occurrence of one of these selectors in any part of a selector
3
21
  // SEE https://regex101.com/r/OTnSEg
4
22
  {
5
23
  objectPattern: [
6
- /\.active/,
7
- /\.disabled/,
8
- /\.error/,
9
- /\.label/,
10
- /\.mod-delete/,
11
- /\.mod-link/,
12
- /\.mod-outline\b/,
13
- /\.success/,
14
- /\.u-textLight/,
24
+ /\.active(?![\w-])/,
25
+ /\.disabled(?![\w-])/,
26
+ /\.error(?![\w-])/,
27
+ /\.label(?![\w-])/,
28
+ /\.label-icon(?![\w-])/,
29
+ /\.mod-delete(?![\w-])/,
30
+ /\.mod-link(?![\w-])/,
31
+ /\.mod-outline(?![\w-])/,
32
+ /\.success(?![\w-])/,
33
+ /\.u-textLight(?![\w-])/,
15
34
  ],
16
35
  },
17
36
  {
18
37
  // Any combination of .button and .mod-counter, with any non-whitespace character between
19
38
  // SEE https://regex101.com/r/9WOlXc.
20
- objectPattern: /(?=\S*\.\bbutton\b)(?=\S*\.\bmod-counter\b)\S*/,
39
+ objectPattern: /(?=\S*\.button(?![\w-]))(?=\S*\.mod-counter(?![\w-]))\S*/,
21
40
  versionDeleted: '18.1.0',
22
41
  },
23
42
  {
24
- objectPattern: [/\.button-counter/, /\.navSide-item-alert/, /\.textfield-actionClear/, /\.lu-select-value .label/],
43
+ // SEE https://regex101.com/r/AUlf21.
44
+ objectPattern: [
45
+ /\.button-counter(?![\w-])/,
46
+ /\.navSide-item-alert(?![\w-])/,
47
+ /\.textfield-actionClear(?![\w-])/,
48
+ /\.lu-select-value \.label(?![\w-])/,
49
+ ],
25
50
  versionDeleted: '18.1.0',
26
51
  },
27
52
  {
28
53
  // Any combination of .callout and .mod-tiny, with any non-whitespace character between
29
54
  // SEE https://regex101.com/r/rW039S.
30
- objectPattern: /(?=\S*\.\bcallout\b)(?=\S*\.\bmod-tiny\b)\S*/,
55
+ objectPattern: /(?=\S*\.callout(?![\w-]))(?=\S*\.mod-tiny(?![\w-]))\S*/,
31
56
  versionDeleted: '18.1.0',
32
57
  },
33
- // Any occurrence of one of these selectors in any part of a selector
34
- // SEE https://regex101.com/r/VHfdte.
35
58
  {
36
- objectPattern: [/\.user-info/, /\.user-tile(-(title|label|footnote))?/, /\.picture/],
59
+ // Any occurrence of one of these selectors in any part of a selector
60
+ // SEE https://regex101.com/r/VHfdte.
61
+ objectPattern: [/\.user-info\b/, /\.user-tile(-(title|label|footnote))?(?![\w-])/, /\.picture(?![\w-])/],
37
62
  versionDeleted: '20.1.0',
38
63
  },
39
64
  {
40
65
  // Any combination of .button and .mod-icon, with any non-whitespace character between
41
66
  // SEE https://regex101.com/r/6yQzje.
42
- objectPattern: /(\.button|\.mod-icon)[\S]*(\.button|\.mod-icon)/,
67
+ objectPattern: /(?=\S*\.button(?![\w-]))(?=\S*\.mod-icon(?![\w-]))\S*/,
43
68
  versionDeprecated: '17.2.0',
44
69
  versionDeleted: '19.1.0',
45
70
  },
46
71
  {
47
- objectPattern: [/\.u-comma/, /\.u-unit/],
72
+ // SEE https://regex101.com/r/VGtzuM.
73
+ objectPattern: [/\.u-comma(?![\w-])/, /\.u-unit(?![\w-])/],
48
74
  versionDeprecated: '17.3.0',
49
75
  versionDeleted: '19.1.0',
50
76
  },
51
77
  {
52
- objectPattern: /\.palette-(grey|primary|secondary|lucca)/,
78
+ // SEE https://regex101.com/r/nPMyQZ.
79
+ objectPattern: /\.palette-(grey|primary|secondary|lucca)(?![\w-])/,
53
80
  versionDeprecated: '17.3.0',
54
81
  versionDeleted: '22.0.0',
82
+ actions: `
83
+ * Remplacer \`grey\` par \`neutral\`.
84
+ * Remplacer \`primary\` & \`secondary\` par \`product\`.
85
+ * Remplacer \`lucca\` par \`brand\`.
86
+ `,
87
+ urls: {
88
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/b/15c256',
89
+ },
55
90
  },
56
91
  {
57
- objectPattern: /\.u-(padding|margin|gap)X*(S|M|L)/,
92
+ // Old t-shirt sized utilities
93
+ // - Sizes: 0, XXS-XXL, Auto (margins only).
94
+ // - Directions: physical, Inline, Block. Gaps: gap, rowGap, columnGap.
95
+ // - Excluded: `.u-{margin|padding}{Inline|Block}0`, still shipping (SEE next entry).
96
+ // SEE https://regex101.com/r/YCDDc8.
97
+ objectPattern:
98
+ /\.u-((margin|padding)(Top|Right|Bottom|Left)?(Auto|0|X{1,2}[SL]|[SML])|(margin|padding)(Inline|Block)(Auto|X{1,2}[SL]|[SML])|(columnGap|rowGap|gap)(0|X{1,2}[SL]|[SML]))(?![\w-])/,
58
99
  versionDeprecated: '17.4.0',
59
100
  versionDeleted: '19.1.0',
60
101
  },
61
102
  {
62
- objectPattern: [/\.u-textLeft/, /\.u-textCenter/, /\.u-textRight/],
103
+ // Zero spacing utilities with the deprecated `.u-` prefix, renamed `.pr-u-` in 20.2.0
104
+ // - Box models: margin, padding, inset. Directions: Inline, Block, and their `-start`/`-end` children.
105
+ // - Names are generated `.u-{box}{capitalize(direction)}0`, so directional children stay hyphenated
106
+ // and lower-cased (`.u-marginInline-start0`, not `.u-marginInlineStart0`); bare `.u-inset0` has no direction.
107
+ // SEE https://github.com/LuccaSA/lucca-front/pull/3814.
108
+ // SEE https://regex101.com/r/GwCS3W.
109
+ objectPattern: /\.u-(inset0|(margin|padding|inset)(Inline|Block)(-(start|end))?0)(?![\w-])/,
110
+ versionDeprecated: '20.2.0',
111
+ },
112
+ {
113
+ // SEE https://regex101.com/r/y2kYBk.
114
+ objectPattern: [/\.u-textLeft(?![\w-])/, /\.u-textCenter(?![\w-])/, /\.u-textRight(?![\w-])/],
63
115
  versionDeprecated: '18.1.0',
64
116
  versionDeleted: '22.0.0',
117
+ actions: `Doublon. Remplacer par : \`.u-textAlignLeft\`, \`.u-textAlignCenter\` & \`.u-textAlignRight\`.`,
65
118
  },
66
119
  {
67
- objectPattern: /\.mod-columnSticky/,
120
+ // SEE https://regex101.com/r/HoanMW.
121
+ objectPattern: /\.mod-columnSticky(?![\w-])/,
68
122
  versionDeprecated: '18.2.0',
69
123
  versionDeleted: '20.1.0',
70
124
  },
71
125
  // SEE https://regex101.com/r/1EfDam
72
126
  {
73
- objectPattern: [/\.(indexT|t)able-head-row-cell-sortableButton/],
127
+ objectPattern: [/\.(indexT|t)able-head-row-cell-sortableButton(?![\w-])/],
74
128
  versionDeprecated: '18.2.0',
75
129
  versionDeleted: '20.1.0',
76
130
  },
77
131
  {
78
- // Any combination of .table-head-row-cell and .mod-sortable, .sortedAscending or .sortedAscending, with any non-whitespace character between
132
+ // Any combination of .table-head-row-cell and .mod-sortable, .sortedAscending or .sortedDescending, with any non-whitespace character between
79
133
  // SEE https://regex101.com/r/NFrjBF.
80
- objectPattern: /(?=\S*\.\btable-head-row-cell\b)(?=\S*\.\b(mod-sortable|sortedAscending|sortedDescending)\b)\S*/,
134
+ objectPattern: /(?=\S*\.table-head-row-cell(?![\w-]))(?=\S*\.(mod-sortable|sortedAscending|sortedDescending)(?![\w-]))\S*/,
81
135
  versionDeprecated: '18.2.0',
82
136
  versionDeleted: '20.1.0',
83
137
  },
84
138
  {
85
- objectPattern: /\.u-text(Left|Center|Right)/,
86
- versionDeprecated: '18.2.0',
87
- },
88
- {
89
- objectPattern: [/\.comment-content-textContainer/, /\.mod-withMenuCompact/],
139
+ // SEE https://regex101.com/r/8f4B4g.
140
+ objectPattern: [/\.comment-content-textContainer(?![\w-])/, /\.mod-withMenuCompact(?![\w-])/],
90
141
  versionDeprecated: '18.3.0',
91
142
  versionDeleted: '20.1.0',
143
+ urls: {
144
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/b/95175f',
145
+ },
92
146
  },
93
147
  {
94
- objectPattern: [/\.dialog-form/, /\.dialog-formOptional/],
148
+ // SEE https://regex101.com/r/Ndh0bQ.
149
+ objectPattern: [/\.dialog-form(?![\w-])/, /\.dialog-formOptional(?![\w-])/],
95
150
  versionDeprecated: '18.3.0',
96
151
  versionDeleted: '22.0.0',
152
+ actions: `Remplacer par la classe unique \`.dialog-inside-formOptional\`.`,
97
153
  },
98
154
  {
155
+ // SEE https://regex101.com/r/43OspP.
99
156
  objectPattern: /\.u-elevate.*/,
100
157
  versionDeprecated: '19.1.0',
101
158
  versionDeleted: '19.1.0',
102
159
  },
103
160
  {
104
- objectPattern: /\.lu-dropdown-(content|options|options-item|options-item-action)/,
161
+ // SEE https://regex101.com/r/7wO4Oc.
162
+ objectPattern: /\.lu-dropdown-(content|options|options-item|options-item-action)(?![\w-])/,
105
163
  versionDeprecated: '19.2.0',
106
164
  versionDeleted: '22.0.0',
165
+ actions: `Remplacer par le nouveau DOM du composant [Dropdown](https://prisme.lucca.io/94310e217/p/557682-dropdown).`,
107
166
  },
108
167
  {
109
168
  // SEE https://regex101.com/r/VqPdDw.
110
- objectPattern: /\.filterBarDeprecated-?/,
169
+ objectPattern: /\.filterBarDeprecated\b/,
111
170
  versionDeprecated: '19.2.0',
112
- versionDeleted: '21.1.0',
171
+ versionDeleted: '22.0.0',
113
172
  },
114
173
  {
115
- objectPattern: /\.menu-?/,
174
+ // SEE https://regex101.com/r/1eWJ0d.
175
+ objectPattern: /\.menu\b/,
116
176
  versionDeprecated: '19.3.0',
117
177
  versionDeleted: '22.0.0',
178
+ actions: `Remplacer par [Horizontal navigation](https://prisme.lucca.io/94310e217/p/29aaef-horizontal-navigation).`,
118
179
  },
119
180
  {
120
181
  // SEE https://regex101.com/r/rOqMxE.
121
- objectPattern: /\.u-text(X?S|M|X{0,3}L)/,
182
+ objectPattern: /\.u-text(X?S|M|X{0,3}L)(?![\w-])/,
122
183
  versionDeprecated: '20.1.0',
123
184
  versionDeleted: '22.0.0',
185
+ actions: `
186
+ Remplacer par les classes \`.pr-u-bodyXS\`, \`.pr-u-bodyS\`, \`.pr-u-bodyM\`.
187
+
188
+ Les utilitaires L ~ XXXL peuvent être remplacés par un utilitaire de titre \`.pr-u-hx\` ou le [token typographie](https://prisme.lucca.io/94310e217/p/73bd2f-typographie/b/23f311) correspondant.
189
+
190
+ [Plus d’informations sur le sujet](https://www.notion.so/luccasoftware/Tokens-Typo-1ebd278ab26e808a9b58d1017514ecb9).
191
+ `,
124
192
  },
125
193
  {
126
194
  // Any combination of .button and .mod-text, .mod-deleted or .loading, with any non-whitespace character between
127
195
  // SEE https://regex101.com/r/5qB2gm.
128
- objectPattern: /(?=\S*\.\bbutton\b)(?=\S*\.\b(mod-text|mod-deleted|loading)\b)\S*/,
196
+ objectPattern: /(?=\S*\.button(?![\w-]))(?=\S*\.(mod-text|mod-deleted|loading)(?![\w-]))\S*/,
129
197
  versionDeprecated: '20.2.0',
130
198
  versionDeleted: '22.0.0',
199
+ actions: `Remplacer par les classes \`.mod-ghost\` & \`.mod-critical\` et les inputs Angular \`critical\`, \`ghost\` & \`ghost-invert\``,
131
200
  },
132
201
  {
133
202
  // Utilitaires renommés très utilisés
203
+ // SEE https://regex101.com/r/TdUmb8.
134
204
  objectPattern: [
135
- /\.pr-u-textPrimary/,
136
- /\.pr-u-textProduct/,
137
- /\.pr-u-textSecondary/,
138
- /\.pr-u-textBrand/,
139
- /\.pr-u-textCritical/,
140
- /\.pr-u-textDefault/,
141
- /\.pr-u-textError/,
142
- /\.pr-u-textGrey/,
143
- /\.pr-u-textLight/,
144
- /\.pr-u-textLucca/,
145
- /\.pr-u-textNeutral/,
146
- /\.pr-u-textPlaceholder/,
147
- /\.pr-u-textSuccess/,
148
- /\.pr-u-textWarning/,
205
+ /\.pr-u-textPrimary(?![\w-])/,
206
+ /\.pr-u-textProduct(?![\w-])/,
207
+ /\.pr-u-textSecondary(?![\w-])/,
208
+ /\.pr-u-textBrand(?![\w-])/,
209
+ /\.pr-u-textCritical(?![\w-])/,
210
+ /\.pr-u-textDefault(?![\w-])/,
211
+ /\.pr-u-textError(?![\w-])/,
212
+ /\.pr-u-textGrey(?![\w-])/,
213
+ /\.pr-u-textLight(?![\w-])/,
214
+ /\.pr-u-textLucca(?![\w-])/,
215
+ /\.pr-u-textNeutral(?![\w-])/,
216
+ /\.pr-u-textPlaceholder(?![\w-])/,
217
+ /\.pr-u-textSuccess(?![\w-])/,
218
+ /\.pr-u-textWarning(?![\w-])/,
149
219
  ],
150
220
  versionDeprecated: '21.0.0',
221
+ actions: `
222
+ Remplacer par les [nouvelles classes](https://prisme.lucca.io/94310e217/p/21a286-utilitaires) liées aux tokens (\`.pr-u-color\`…)
223
+ `,
224
+ urls: {
225
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/t/17d7fdfdaf',
226
+ },
151
227
  },
152
228
  {
153
229
  // Utilitaires renommés peu ou pas utilisés
230
+ // SEE https://regex101.com/r/Dk4cFs.
154
231
  objectPattern: [
155
- /\.pr-u-textSuccessContrasted/,
156
- /\.pr-u-textWarningContrasted/,
157
- /\.pr-u-textBrandContrasted/,
158
- /\.pr-u-textNavigation/,
159
- /\.pr-u-textAI/,
160
- /\.pr-u-textProduct/,
161
- /\.pr-u-textPagga/,
162
- /\.pr-u-textPoplee/,
163
- /\.pr-u-textCoreHR/,
164
- /\.pr-u-textTimmi/,
165
- /\.pr-u-textCleemy/,
166
- /\.pr-u-textCc/,
167
- /\.pr-u-textKiwi/,
168
- /\.pr-u-textLime/,
169
- /\.pr-u-textCucumber/,
170
- /\.pr-u-textMint/,
171
- /\.pr-u-textGlacier/,
172
- /\.pr-u-textLagoon/,
173
- /\.pr-u-textBlueberry/,
174
- /\.pr-u-textLavender/,
175
- /\.pr-u-textGrape/,
176
- /\.pr-u-textWatermelon/,
177
- /\.pr-u-textPumpkin/,
178
- /\.pr-u-textPineapple/,
232
+ /\.pr-u-textSuccessContrasted(?![\w-])/,
233
+ /\.pr-u-textWarningContrasted(?![\w-])/,
234
+ /\.pr-u-textBrandContrasted(?![\w-])/,
235
+ /\.pr-u-textNavigation(?![\w-])/,
236
+ /\.pr-u-textAI(?![\w-])/,
237
+ /\.pr-u-textPagga(?![\w-])/,
238
+ /\.pr-u-textPoplee(?![\w-])/,
239
+ /\.pr-u-textCoreHR(?![\w-])/,
240
+ /\.pr-u-textTimmi(?![\w-])/,
241
+ /\.pr-u-textCleemy(?![\w-])/,
242
+ /\.pr-u-textCc(?![\w-])/,
243
+ /\.pr-u-textKiwi(?![\w-])/,
244
+ /\.pr-u-textLime(?![\w-])/,
245
+ /\.pr-u-textCucumber(?![\w-])/,
246
+ /\.pr-u-textMint(?![\w-])/,
247
+ /\.pr-u-textGlacier(?![\w-])/,
248
+ /\.pr-u-textLagoon(?![\w-])/,
249
+ /\.pr-u-textBlueberry(?![\w-])/,
250
+ /\.pr-u-textLavender(?![\w-])/,
251
+ /\.pr-u-textGrape(?![\w-])/,
252
+ /\.pr-u-textWatermelon(?![\w-])/,
253
+ /\.pr-u-textPumpkin(?![\w-])/,
254
+ /\.pr-u-textPineapple(?![\w-])/,
179
255
  ],
180
256
  versionDeprecated: '21.0.0',
257
+ actions: `
258
+ Remplacer par les [nouvelles classes](https://prisme.lucca.io/94310e217/p/21a286-utilitaires) liées aux tokens (\`.pr-u-color\`…)
259
+ `,
260
+ urls: {
261
+ schematics: 'https://prisme.lucca.io/94310e217/p/40c515-cycle-de-vie-des-composants/t/17d7fdfdaf',
262
+ },
181
263
  },
182
264
  ];
package/LFVersions.mjs CHANGED
@@ -4,6 +4,19 @@ import { join } from 'node:path';
4
4
 
5
5
  let LFVersions = null;
6
6
 
7
+ /**
8
+ * Normalise a version to a patch version.
9
+ * e.g. `22.0` → `22.0.0`
10
+ *
11
+ * Used on both sides: LFVersions keys (built from milestone titles) and lookups.
12
+ *
13
+ * @param {string} version - Dot-separated LF version
14
+ * @return {string}
15
+ */
16
+ export function normalizeVersion(version) {
17
+ return version.split('.').length === 2 ? `${version}.0` : version;
18
+ }
19
+
7
20
  // Check if the `showCachePath` parameter is present when executing the script.
8
21
  const showCachePath = process.argv.includes('showCachePath');
9
22
  const CACHE_FILE_PATH = join(os.tmpdir(), 'stylelint-LFVersions.json');
@@ -31,25 +44,36 @@ if (LFVersions === null) {
31
44
  console.info(`Fetching from Github to ${CACHE_FILE_PATH}…`);
32
45
  }
33
46
 
34
- const githubMilestones = await fetch('https://api.github.com/repos/LuccaSA/lucca-front/milestones?state=all&sort=due_on&direction=desc');
47
+ // The API caps page size at 100 and the repo already has >100 milestones, so paginate to the last
48
+ // page: every referenced version must resolve, not just the 100 most recent.
49
+ const PER_PAGE = 100;
50
+ const MAX_PAGES = 20; // Safety bound against an unbounded loop; far exceeds the milestone count.
51
+ let page = 1;
52
+ let hasMore = true;
35
53
 
36
- if (githubMilestones.ok) {
37
- const milestones = await githubMilestones.json();
54
+ while (hasMore && page <= MAX_PAGES) {
55
+ const githubMilestones = await fetch(
56
+ `https://api.github.com/repos/LuccaSA/lucca-front/milestones?state=all&sort=due_on&direction=desc&per_page=${PER_PAGE}&page=${page}`,
57
+ );
38
58
 
39
- for (const milestone of milestones) {
40
- let version = milestone.title;
59
+ if (!githubMilestones.ok) {
60
+ break;
61
+ }
41
62
 
42
- if (version) {
43
- const date = new Date(milestone.due_on);
63
+ const milestones = await githubMilestones.json();
44
64
 
45
- // If version doesn't have patch version, add it as .0
46
- if (version.split('.').length === 2) {
47
- version += '.0';
48
- }
65
+ for (const milestone of milestones) {
66
+ const version = milestone.title;
49
67
 
50
- LFVersions[version] = date.toLocaleDateString();
68
+ // due_on is already ISO 8601 UTC; store as-is (a locale string breaks new Date() outside en-US).
69
+ // Skip milestones with no due date so they don't resolve to the epoch.
70
+ if (version && milestone.due_on) {
71
+ LFVersions[normalizeVersion(version)] = milestone.due_on;
51
72
  }
52
73
  }
74
+
75
+ hasMore = milestones.length === PER_PAGE;
76
+ page++;
53
77
  }
54
78
 
55
79
  writeFileSync(CACHE_FILE_PATH, JSON.stringify({ LFVersions, createdAt: Date.now() }), 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucca/stylelint-config-prisme",
3
- "version": "22.0.0-rc.4",
3
+ "version": "22.0.0",
4
4
  "description": "Lucca Front stylelint configuration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,9 +23,9 @@
23
23
  },
24
24
  "homepage": "https://github.com/LuccaSA/lucca-front#readme",
25
25
  "peerDependencies": {
26
- "@stylistic/stylelint-config": "^4.0.0 || ^5.0.0",
27
- "@stylistic/stylelint-plugin": "^5.0.0",
28
- "stylelint": "^17.4.0",
26
+ "@stylistic/stylelint-config": "^5.0.0",
27
+ "@stylistic/stylelint-plugin": "^5.2.1",
28
+ "stylelint": "^17.14.1",
29
29
  "stylelint-config-standard-scss": "^17.0.0"
30
30
  }
31
31
  }
@@ -11,7 +11,7 @@ export default {
11
11
  files: ['**/*.scss'],
12
12
  rules: {
13
13
  // Disable for now because of bugs with SCSS files.
14
- // SEE https://github.com/stylelint-scss/stylelint-config-standard-scss/issues/252
14
+ // SEE https://github.com/stylelint-scss/stylelint-config-standard-scss/issues/269
15
15
  'no-invalid-position-declaration': null,
16
16
  },
17
17
  },
@@ -23,6 +23,12 @@ export default {
23
23
  },
24
24
  },
25
25
  ],
26
+ languageOptions: {
27
+ directionality: {
28
+ block: 'top-to-bottom',
29
+ inline: 'left-to-right',
30
+ },
31
+ },
26
32
  rules: {
27
33
  // SCSS specific
28
34
  // ============================================================================================
@@ -130,6 +136,14 @@ export default {
130
136
  severity: (property) => getDisallowedData(LFDeprecatedProperties, property).severity,
131
137
  },
132
138
  ],
139
+ 'property-layout-mappings': [
140
+ 'flow-relative',
141
+ {
142
+ // overflow-x / overflow-y logical equivalents (overflow-inline / overflow-block)
143
+ // are not yet Baseline widely available (Chrome 135+, Safari 26+).
144
+ ignoreProperties: ['overflow-x', 'overflow-y'],
145
+ },
146
+ ],
133
147
  'property-no-unknown': [
134
148
  true,
135
149
  {
@@ -157,13 +171,16 @@ export default {
157
171
  message: (selectorId) => `Expected "${selectorId}" to match pattern #foo(-bar(Baz)*)*`,
158
172
  },
159
173
  ],
174
+ 'selector-no-deprecated': true,
160
175
  'selector-pseudo-element-no-unknown': [
161
176
  true,
162
177
  {
163
178
  ignorePseudoElements: ['ng-deep'],
164
179
  },
165
180
  ],
181
+ 'unit-layout-mappings': 'flow-relative',
166
182
  'value-keyword-case': null,
183
+ 'value-keyword-layout-mappings': 'flow-relative',
167
184
 
168
185
  // Formatting with @stylistic
169
186
  // SEE: https://github.com/stylelint-stylistic/stylelint-stylistic/blob/main/docs/user-guide/rules.md
@@ -183,6 +200,7 @@ export default {
183
200
  alignQuotes: true,
184
201
  },
185
202
  ],
203
+ '@stylistic/no-multiple-whitespaces': null, // Allow people to align values as they wish.
186
204
  '@stylistic/string-quotes': 'single',
187
205
  },
188
206
  };
@@ -1,25 +1,51 @@
1
- import LFVersions from './LFVersions.mjs';
1
+ import LFVersions, { normalizeVersion } from './LFVersions.mjs';
2
2
  import currentLFVersion from './currentLFVersion.mjs';
3
3
 
4
+ /**
5
+ * @typedef {object} DisallowedObject - Object found in the list of disallowed objects
6
+ * @property {(RegExp | string)[] | RegExp | string} [objectPattern] - Pattern(s) matching the deprecated element
7
+ * @property {string} [versionDeprecated] - LF version deprecating the element
8
+ * @property {string} [versionDeleted] - LF version deleting the element
9
+ * @property {string} [actions] - Migration actions
10
+ * @property {object} [urls] - Related URLs
11
+ * @property {Date} [dateDeprecated] - Deprecation date, added by setDates()
12
+ * @property {Date} [dateDeleted] - Deletion date, added by setDates()
13
+ * @property {string} [faultyPattern] - Pattern reported by Stylelint, added by getDisallowedData()
14
+ */
15
+
4
16
  /**
5
17
  * Get all blacklisted elements from all versions.
6
18
  *
7
- * @return {Array[Regex | String]}
19
+ * @param {DisallowedObject[]} disallowedObjects - List of disallowed objects
20
+ * @return {(RegExp | string)[]}
8
21
  */
9
22
  export function getDisallowedObjects(disallowedObjects) {
10
- return disallowedObjects.reduce((output, object) => {
11
- return output.concat(object.objectPattern);
12
- }, []);
23
+ return disallowedObjects.flatMap((object) => object.objectPattern).filter((pattern) => pattern != null);
13
24
  }
14
25
 
26
+ // Results are constant per (list, faultyPattern) within a run; Stylelint calls getDisallowedData once for
27
+ // the message and once for the severity of every warning, so memoise to compute each result only once.
28
+ const disallowedDataCache = new WeakMap();
29
+
15
30
  /**
16
31
  * Get data related to object.
17
32
  *
18
- * @param {Array[Object{objectPattern, versionDeprecated, versionDeleted}]} disallowedObjects - List of disallowed objects
19
- * @param {String} faultyPattern - Pattern provided by Stylelint, to check against list of custom patterns
20
- * @return {{message: String, severity: String}}
33
+ * @param {DisallowedObject[]} disallowedObjects - List of disallowed objects
34
+ * @param {string} faultyPattern - Pattern provided by Stylelint, to check against list of custom patterns
35
+ * @return {{message: string, severity: string}}
21
36
  */
22
37
  export function getDisallowedData(disallowedObjects, faultyPattern) {
38
+ let cache = disallowedDataCache.get(disallowedObjects);
39
+
40
+ if (!cache) {
41
+ cache = new Map();
42
+ disallowedDataCache.set(disallowedObjects, cache);
43
+ }
44
+
45
+ if (cache.has(faultyPattern)) {
46
+ return cache.get(faultyPattern);
47
+ }
48
+
23
49
  let objectData = disallowedObjects.find((haystack) => {
24
50
  // If the patterns are within an array, parse it.
25
51
  if (Array.isArray(haystack.objectPattern)) {
@@ -34,17 +60,21 @@ export function getDisallowedData(disallowedObjects, faultyPattern) {
34
60
  objectData = setDates(objectData);
35
61
  objectData.faultyPattern = faultyPattern;
36
62
 
37
- return {
63
+ const result = {
38
64
  message: getMessage(objectData),
39
65
  severity: getSeverity(objectData),
40
66
  };
67
+
68
+ cache.set(faultyPattern, result);
69
+
70
+ return result;
41
71
  }
42
72
 
43
73
  /**
44
74
  * Set deprecation and deletion dates for an object.
45
75
  *
46
- * @param {Object} objectData - Object found in the list of disallowed objects
47
- * @return {Object} - objectData with dates.
76
+ * @param {DisallowedObject} objectData
77
+ * @return {DisallowedObject} - objectData with dates.
48
78
  */
49
79
  function setDates(objectData) {
50
80
  return {
@@ -57,8 +87,8 @@ function setDates(objectData) {
57
87
  /**
58
88
  * Is the parameter a valid date?
59
89
  *
60
- * @param {*} date - Value to analise
61
- * @return boolean
90
+ * @param {*} date - Value to analyse
91
+ * @return {boolean}
62
92
  */
63
93
  function isValidDate(date) {
64
94
  return date instanceof Date && !isNaN(date);
@@ -71,8 +101,15 @@ function isValidDate(date) {
71
101
  * @return {Date|undefined}
72
102
  */
73
103
  function getDateForVersion(version) {
74
- if (version && version in LFVersions) {
75
- const date = new Date(LFVersions[version]);
104
+ if (!version) {
105
+ return;
106
+ }
107
+
108
+ // LFVersions keys are normalised; a non-normalised lookup would silently miss the map.
109
+ const normalizedVersion = normalizeVersion(version);
110
+
111
+ if (normalizedVersion in LFVersions) {
112
+ const date = new Date(LFVersions[normalizedVersion]);
76
113
 
77
114
  if (isValidDate(date)) {
78
115
  return date;
@@ -83,23 +120,33 @@ function getDateForVersion(version) {
83
120
  /**
84
121
  * Compare the faulty pattern with disallowed patterns.
85
122
  *
86
- * @param {String} faultyPattern - faultyPattern returned by Stylelint
87
- * @param {Array[RegExp | String] | RegExp | String} disallowedPattern - Custom pattern to match
88
- * @return boolean
123
+ * @param {string} faultyPattern - faultyPattern returned by Stylelint
124
+ * @param {(RegExp | string)[] | RegExp | string} disallowedPattern - Custom pattern to match
125
+ * @return {boolean}
89
126
  */
90
127
  function comparePatterns(faultyPattern, disallowedPattern) {
91
- return typeof disallowedPattern === 'string' ? faultyPattern === disallowedPattern : disallowedPattern.test(faultyPattern);
128
+ // A missing pattern never matches: this keeps a broken object from crashing Stylelint.
129
+ if (disallowedPattern == null) {
130
+ return false;
131
+ }
132
+
133
+ if (typeof disallowedPattern === 'string') {
134
+ return faultyPattern === disallowedPattern;
135
+ }
136
+
137
+ return disallowedPattern.test(faultyPattern);
92
138
  }
93
139
 
94
140
  /**
95
141
  * Format message based on versions criticity.
96
142
  *
97
- * @param {Object} objectData
98
- * @return {String}
143
+ * @param {DisallowedObject} objectData
144
+ * @return {string}
99
145
  */
100
146
  function getMessage(objectData) {
147
+ const status = isDeleted(objectData) ? 'deleted' : 'deprecated';
148
+
101
149
  let pattern = `${objectData.faultyPattern}`;
102
- let status = 'deprecated';
103
150
  let messageDeprecated = '';
104
151
  let messageDeleted = '';
105
152
  let messageLFVersionWarning = '';
@@ -119,10 +166,6 @@ function getMessage(objectData) {
119
166
  const daysLeft = Math.ceil((objectData.dateDeleted.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24));
120
167
  const dayString = new Intl.RelativeTimeFormat().format(daysLeft, 'day');
121
168
 
122
- if (daysLeft <= 0) {
123
- status = 'deleted';
124
- }
125
-
126
169
  messageDeleted = ` | until ${objectData.dateDeleted.toLocaleDateString()} (${dayString}, LF ${objectData.versionDeleted})`;
127
170
  }
128
171
 
@@ -130,23 +173,66 @@ function getMessage(objectData) {
130
173
  }
131
174
 
132
175
  /**
133
- * Get severity based on version used.
176
+ * Is the element deleted for the current LF version?
177
+ * Single source of truth for getMessage() and getSeverity().
178
+ * Compares dot-separated versions part by part so the most significant difference decides.
179
+ * A plain string comparison would misorder them (e.g. `9.0.0` > `22.0.0`).
134
180
  *
135
- * @param {String} objectData
136
- * @return {'warning'|'error'}
181
+ * @param {DisallowedObject} objectData
182
+ * @return {boolean} - true if the current LF version is at or past versionDeleted
137
183
  */
138
- function getSeverity(objectData) {
139
- if (!currentLFVersion) {
140
- return 'warning';
184
+ function isDeleted(objectData) {
185
+ if (!currentLFVersion || !objectData.versionDeleted) {
186
+ return false;
187
+ }
188
+
189
+ const [currentParts, currentPrerelease] = parseVersion(currentLFVersion);
190
+ const [deletedParts, deletedPrerelease] = parseVersion(objectData.versionDeleted);
191
+ const length = Math.max(currentParts.length, deletedParts.length);
192
+
193
+ for (let i = 0; i < length; i++) {
194
+ const diff = (parseInt(currentParts[i], 10) || 0) - (parseInt(deletedParts[i], 10) || 0);
195
+
196
+ if (diff < 0) {
197
+ return false;
198
+ }
199
+
200
+ if (diff > 0) {
201
+ return true;
202
+ }
141
203
  }
142
204
 
143
- if (!objectData.versionDeleted) {
144
- return 'warning';
205
+ // Equal release versions: per semver, a prerelease sorts before its release (`22.0.0-rc.1` < `22.0.0`).
206
+ if (currentPrerelease && !deletedPrerelease) {
207
+ return false;
145
208
  }
146
209
 
147
- if (currentLFVersion < objectData.versionDeleted) {
148
- return 'warning';
210
+ return true;
211
+ }
212
+
213
+ /**
214
+ * Split a version into its numeric release parts and its prerelease tag.
215
+ * e.g. `22.0.0-rc.1` → [['22', '0', '0'], 'rc.1']
216
+ *
217
+ * @param {string} version - semver formatted LF version
218
+ * @return {[string[], string]}
219
+ */
220
+ function parseVersion(version) {
221
+ const hyphenIndex = version.indexOf('-');
222
+
223
+ if (hyphenIndex === -1) {
224
+ return [version.split('.'), ''];
149
225
  }
150
226
 
151
- return 'error';
227
+ return [version.slice(0, hyphenIndex).split('.'), version.slice(hyphenIndex + 1)];
228
+ }
229
+
230
+ /**
231
+ * Get severity based on version used.
232
+ *
233
+ * @param {DisallowedObject} objectData
234
+ * @return {'warning'|'error'}
235
+ */
236
+ function getSeverity(objectData) {
237
+ return isDeleted(objectData) ? 'error' : 'warning';
152
238
  }