prettier-plugin-bootstrap 0.2.1 → 0.3.1

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.
@@ -0,0 +1,315 @@
1
+ //#region src/class-order.ts
2
+ const BREAKPOINTS = [
3
+ "sm",
4
+ "md",
5
+ "lg",
6
+ "xl",
7
+ "xxl"
8
+ ];
9
+ const RESPONSIVE_RE = new RegExp(`^(.+?)-(${BREAKPOINTS.join("|")})-(.+)$`);
10
+ const CLASS_ORDER = [
11
+ "container-fluid",
12
+ "container-sm",
13
+ "container-md",
14
+ "container-lg",
15
+ "container-xl",
16
+ "container-xxl",
17
+ "container",
18
+ "row",
19
+ "row-cols-",
20
+ "col-auto",
21
+ "col-1",
22
+ "col-2",
23
+ "col-3",
24
+ "col-4",
25
+ "col-5",
26
+ "col-6",
27
+ "col-7",
28
+ "col-8",
29
+ "col-9",
30
+ "col-10",
31
+ "col-11",
32
+ "col-12",
33
+ "col",
34
+ "offset-",
35
+ "g-",
36
+ "gx-",
37
+ "gy-",
38
+ "h1",
39
+ "h2",
40
+ "h3",
41
+ "h4",
42
+ "h5",
43
+ "h6",
44
+ "lead",
45
+ "display-",
46
+ "list-unstyled",
47
+ "list-inline",
48
+ "list-inline-item",
49
+ "initialism",
50
+ "blockquote",
51
+ "blockquote-footer",
52
+ "img-fluid",
53
+ "img-thumbnail",
54
+ "figure",
55
+ "figure-img",
56
+ "figure-caption",
57
+ "table",
58
+ "table-",
59
+ "caption-top",
60
+ "form-label",
61
+ "col-form-label",
62
+ "form-text",
63
+ "form-control",
64
+ "form-control-",
65
+ "form-select",
66
+ "form-select-",
67
+ "form-check",
68
+ "form-check-",
69
+ "form-switch",
70
+ "form-floating",
71
+ "form-range",
72
+ "input-group",
73
+ "input-group-",
74
+ "valid-feedback",
75
+ "valid-tooltip",
76
+ "invalid-feedback",
77
+ "invalid-tooltip",
78
+ "was-validated",
79
+ "btn",
80
+ "btn-",
81
+ "btn-close",
82
+ "btn-close-",
83
+ "fade",
84
+ "collapse",
85
+ "collapsing",
86
+ "show",
87
+ "dropdown",
88
+ "dropdown-",
89
+ "dropup",
90
+ "dropend",
91
+ "dropstart",
92
+ "btn-group",
93
+ "btn-group-",
94
+ "btn-toolbar",
95
+ "nav",
96
+ "nav-",
97
+ "tab-content",
98
+ "tab-pane",
99
+ "navbar",
100
+ "navbar-",
101
+ "card",
102
+ "card-",
103
+ "accordion",
104
+ "accordion-",
105
+ "breadcrumb",
106
+ "breadcrumb-item",
107
+ "pagination",
108
+ "pagination-",
109
+ "page-item",
110
+ "page-link",
111
+ "badge",
112
+ "alert",
113
+ "alert-",
114
+ "progress",
115
+ "progress-",
116
+ "progress-bar",
117
+ "progress-bar-",
118
+ "list-group",
119
+ "list-group-",
120
+ "toast",
121
+ "toast-",
122
+ "modal",
123
+ "modal-",
124
+ "tooltip",
125
+ "tooltip-",
126
+ "popover",
127
+ "popover-",
128
+ "carousel",
129
+ "carousel-",
130
+ "spinner-border",
131
+ "spinner-border-",
132
+ "spinner-grow",
133
+ "spinner-grow-",
134
+ "offcanvas",
135
+ "offcanvas-",
136
+ "placeholder",
137
+ "placeholder-",
138
+ "clearfix",
139
+ "link-",
140
+ "icon-link",
141
+ "icon-link-",
142
+ "ratio",
143
+ "ratio-",
144
+ "fixed-top",
145
+ "fixed-bottom",
146
+ "sticky-top",
147
+ "sticky-bottom",
148
+ "hstack",
149
+ "vstack",
150
+ "stretched-link",
151
+ "text-truncate",
152
+ "vr",
153
+ "visually-hidden",
154
+ "visually-hidden-focusable",
155
+ "align-",
156
+ "float-",
157
+ "object-fit-",
158
+ "opacity-",
159
+ "overflow-",
160
+ "d-",
161
+ "shadow",
162
+ "shadow-",
163
+ "focus-ring",
164
+ "focus-ring-",
165
+ "position-",
166
+ "top-",
167
+ "bottom-",
168
+ "start-",
169
+ "end-",
170
+ "translate-middle",
171
+ "translate-middle-",
172
+ "border",
173
+ "border-",
174
+ "w-",
175
+ "mw-",
176
+ "vw-",
177
+ "min-vw-",
178
+ "h-",
179
+ "mh-",
180
+ "vh-",
181
+ "min-vh-",
182
+ "flex-",
183
+ "justify-content-",
184
+ "align-items-",
185
+ "align-content-",
186
+ "align-self-",
187
+ "order-",
188
+ "m-",
189
+ "mx-",
190
+ "my-",
191
+ "mt-",
192
+ "me-",
193
+ "mb-",
194
+ "ms-",
195
+ "p-",
196
+ "px-",
197
+ "py-",
198
+ "pt-",
199
+ "pe-",
200
+ "pb-",
201
+ "ps-",
202
+ "gap-",
203
+ "row-gap-",
204
+ "column-gap-",
205
+ "font-monospace",
206
+ "fs-",
207
+ "fst-",
208
+ "fw-",
209
+ "lh-",
210
+ "text-decoration-",
211
+ "text-",
212
+ "text-opacity-",
213
+ "link-opacity-",
214
+ "link-offset-",
215
+ "link-underline",
216
+ "link-underline-",
217
+ "bg-",
218
+ "bg-opacity-",
219
+ "bg-gradient",
220
+ "user-select-",
221
+ "pe-none",
222
+ "pe-auto",
223
+ "rounded",
224
+ "rounded-",
225
+ "visible",
226
+ "invisible",
227
+ "z-"
228
+ ];
229
+ function buildOrderMap() {
230
+ const map = /* @__PURE__ */ new Map();
231
+ for (const [index, prefix] of CLASS_ORDER.entries()) map.set(prefix, index);
232
+ return map;
233
+ }
234
+ const ORDER_MAP = buildOrderMap();
235
+ function classKey(className) {
236
+ let base = className;
237
+ let breakpointIdx = 0;
238
+ const match = className.match(RESPONSIVE_RE);
239
+ if (match) {
240
+ base = `${match[1]}-${match[3]}`;
241
+ breakpointIdx = BREAKPOINTS.indexOf(match[2]) + 1;
242
+ }
243
+ let bestIdx = -1;
244
+ let bestLen = 0;
245
+ for (const [prefix, idx] of ORDER_MAP) if (base === prefix || prefix.endsWith("-") && base.startsWith(prefix)) {
246
+ if (prefix.length > bestLen) {
247
+ bestLen = prefix.length;
248
+ bestIdx = idx;
249
+ }
250
+ }
251
+ return [bestIdx === -1 ? Infinity : bestIdx, breakpointIdx];
252
+ }
253
+ function sortClasses(classes) {
254
+ const annotated = classes.map((cls, i) => ({
255
+ cls,
256
+ key: classKey(cls),
257
+ orig: i
258
+ }));
259
+ annotated.sort((a, b) => {
260
+ if (a.key[0] !== b.key[0]) return a.key[0] - b.key[0];
261
+ if (a.key[1] !== b.key[1]) return a.key[1] - b.key[1];
262
+ return a.orig - b.orig;
263
+ });
264
+ return annotated.map((entry) => entry.cls);
265
+ }
266
+ //#endregion
267
+ //#region src/sorting.ts
268
+ function sortClassString(value, options) {
269
+ if (!value || typeof value !== "string") return value;
270
+ const trimmed = value.trim();
271
+ if (!trimmed) return value;
272
+ const classes = trimmed.split(/\s+/);
273
+ if (classes.length <= 1) return value;
274
+ const leadingWs = value.match(/^\s*/)[0];
275
+ const trailingWs = value.match(/\s*$/)[0];
276
+ let toSort = classes;
277
+ if (options?.preserveDuplicates === false) toSort = classes.filter((c, i) => classes.indexOf(c) === i);
278
+ const sorted = sortClasses(toSort);
279
+ if (options?.preserveWhitespace) {
280
+ const separators = trimmed.split(/\S+/).slice(1, -1);
281
+ return `${leadingWs}${sorted.map((cls, i) => i < separators.length ? cls + separators[i] : cls).join("")}${trailingWs}`;
282
+ }
283
+ return `${leadingWs}${sorted.join(" ")}${trailingWs}`;
284
+ }
285
+ //#endregion
286
+ Object.defineProperty(exports, "BREAKPOINTS", {
287
+ enumerable: true,
288
+ get: function() {
289
+ return BREAKPOINTS;
290
+ }
291
+ });
292
+ Object.defineProperty(exports, "CLASS_ORDER", {
293
+ enumerable: true,
294
+ get: function() {
295
+ return CLASS_ORDER;
296
+ }
297
+ });
298
+ Object.defineProperty(exports, "classKey", {
299
+ enumerable: true,
300
+ get: function() {
301
+ return classKey;
302
+ }
303
+ });
304
+ Object.defineProperty(exports, "sortClassString", {
305
+ enumerable: true,
306
+ get: function() {
307
+ return sortClassString;
308
+ }
309
+ });
310
+ Object.defineProperty(exports, "sortClasses", {
311
+ enumerable: true,
312
+ get: function() {
313
+ return sortClasses;
314
+ }
315
+ });
@@ -0,0 +1,286 @@
1
+ //#region src/class-order.ts
2
+ const BREAKPOINTS = [
3
+ "sm",
4
+ "md",
5
+ "lg",
6
+ "xl",
7
+ "xxl"
8
+ ];
9
+ const RESPONSIVE_RE = new RegExp(`^(.+?)-(${BREAKPOINTS.join("|")})-(.+)$`);
10
+ const CLASS_ORDER = [
11
+ "container-fluid",
12
+ "container-sm",
13
+ "container-md",
14
+ "container-lg",
15
+ "container-xl",
16
+ "container-xxl",
17
+ "container",
18
+ "row",
19
+ "row-cols-",
20
+ "col-auto",
21
+ "col-1",
22
+ "col-2",
23
+ "col-3",
24
+ "col-4",
25
+ "col-5",
26
+ "col-6",
27
+ "col-7",
28
+ "col-8",
29
+ "col-9",
30
+ "col-10",
31
+ "col-11",
32
+ "col-12",
33
+ "col",
34
+ "offset-",
35
+ "g-",
36
+ "gx-",
37
+ "gy-",
38
+ "h1",
39
+ "h2",
40
+ "h3",
41
+ "h4",
42
+ "h5",
43
+ "h6",
44
+ "lead",
45
+ "display-",
46
+ "list-unstyled",
47
+ "list-inline",
48
+ "list-inline-item",
49
+ "initialism",
50
+ "blockquote",
51
+ "blockquote-footer",
52
+ "img-fluid",
53
+ "img-thumbnail",
54
+ "figure",
55
+ "figure-img",
56
+ "figure-caption",
57
+ "table",
58
+ "table-",
59
+ "caption-top",
60
+ "form-label",
61
+ "col-form-label",
62
+ "form-text",
63
+ "form-control",
64
+ "form-control-",
65
+ "form-select",
66
+ "form-select-",
67
+ "form-check",
68
+ "form-check-",
69
+ "form-switch",
70
+ "form-floating",
71
+ "form-range",
72
+ "input-group",
73
+ "input-group-",
74
+ "valid-feedback",
75
+ "valid-tooltip",
76
+ "invalid-feedback",
77
+ "invalid-tooltip",
78
+ "was-validated",
79
+ "btn",
80
+ "btn-",
81
+ "btn-close",
82
+ "btn-close-",
83
+ "fade",
84
+ "collapse",
85
+ "collapsing",
86
+ "show",
87
+ "dropdown",
88
+ "dropdown-",
89
+ "dropup",
90
+ "dropend",
91
+ "dropstart",
92
+ "btn-group",
93
+ "btn-group-",
94
+ "btn-toolbar",
95
+ "nav",
96
+ "nav-",
97
+ "tab-content",
98
+ "tab-pane",
99
+ "navbar",
100
+ "navbar-",
101
+ "card",
102
+ "card-",
103
+ "accordion",
104
+ "accordion-",
105
+ "breadcrumb",
106
+ "breadcrumb-item",
107
+ "pagination",
108
+ "pagination-",
109
+ "page-item",
110
+ "page-link",
111
+ "badge",
112
+ "alert",
113
+ "alert-",
114
+ "progress",
115
+ "progress-",
116
+ "progress-bar",
117
+ "progress-bar-",
118
+ "list-group",
119
+ "list-group-",
120
+ "toast",
121
+ "toast-",
122
+ "modal",
123
+ "modal-",
124
+ "tooltip",
125
+ "tooltip-",
126
+ "popover",
127
+ "popover-",
128
+ "carousel",
129
+ "carousel-",
130
+ "spinner-border",
131
+ "spinner-border-",
132
+ "spinner-grow",
133
+ "spinner-grow-",
134
+ "offcanvas",
135
+ "offcanvas-",
136
+ "placeholder",
137
+ "placeholder-",
138
+ "clearfix",
139
+ "link-",
140
+ "icon-link",
141
+ "icon-link-",
142
+ "ratio",
143
+ "ratio-",
144
+ "fixed-top",
145
+ "fixed-bottom",
146
+ "sticky-top",
147
+ "sticky-bottom",
148
+ "hstack",
149
+ "vstack",
150
+ "stretched-link",
151
+ "text-truncate",
152
+ "vr",
153
+ "visually-hidden",
154
+ "visually-hidden-focusable",
155
+ "align-",
156
+ "float-",
157
+ "object-fit-",
158
+ "opacity-",
159
+ "overflow-",
160
+ "d-",
161
+ "shadow",
162
+ "shadow-",
163
+ "focus-ring",
164
+ "focus-ring-",
165
+ "position-",
166
+ "top-",
167
+ "bottom-",
168
+ "start-",
169
+ "end-",
170
+ "translate-middle",
171
+ "translate-middle-",
172
+ "border",
173
+ "border-",
174
+ "w-",
175
+ "mw-",
176
+ "vw-",
177
+ "min-vw-",
178
+ "h-",
179
+ "mh-",
180
+ "vh-",
181
+ "min-vh-",
182
+ "flex-",
183
+ "justify-content-",
184
+ "align-items-",
185
+ "align-content-",
186
+ "align-self-",
187
+ "order-",
188
+ "m-",
189
+ "mx-",
190
+ "my-",
191
+ "mt-",
192
+ "me-",
193
+ "mb-",
194
+ "ms-",
195
+ "p-",
196
+ "px-",
197
+ "py-",
198
+ "pt-",
199
+ "pe-",
200
+ "pb-",
201
+ "ps-",
202
+ "gap-",
203
+ "row-gap-",
204
+ "column-gap-",
205
+ "font-monospace",
206
+ "fs-",
207
+ "fst-",
208
+ "fw-",
209
+ "lh-",
210
+ "text-decoration-",
211
+ "text-",
212
+ "text-opacity-",
213
+ "link-opacity-",
214
+ "link-offset-",
215
+ "link-underline",
216
+ "link-underline-",
217
+ "bg-",
218
+ "bg-opacity-",
219
+ "bg-gradient",
220
+ "user-select-",
221
+ "pe-none",
222
+ "pe-auto",
223
+ "rounded",
224
+ "rounded-",
225
+ "visible",
226
+ "invisible",
227
+ "z-"
228
+ ];
229
+ function buildOrderMap() {
230
+ const map = /* @__PURE__ */ new Map();
231
+ for (const [index, prefix] of CLASS_ORDER.entries()) map.set(prefix, index);
232
+ return map;
233
+ }
234
+ const ORDER_MAP = buildOrderMap();
235
+ function classKey(className) {
236
+ let base = className;
237
+ let breakpointIdx = 0;
238
+ const match = className.match(RESPONSIVE_RE);
239
+ if (match) {
240
+ base = `${match[1]}-${match[3]}`;
241
+ breakpointIdx = BREAKPOINTS.indexOf(match[2]) + 1;
242
+ }
243
+ let bestIdx = -1;
244
+ let bestLen = 0;
245
+ for (const [prefix, idx] of ORDER_MAP) if (base === prefix || prefix.endsWith("-") && base.startsWith(prefix)) {
246
+ if (prefix.length > bestLen) {
247
+ bestLen = prefix.length;
248
+ bestIdx = idx;
249
+ }
250
+ }
251
+ return [bestIdx === -1 ? Infinity : bestIdx, breakpointIdx];
252
+ }
253
+ function sortClasses(classes) {
254
+ const annotated = classes.map((cls, i) => ({
255
+ cls,
256
+ key: classKey(cls),
257
+ orig: i
258
+ }));
259
+ annotated.sort((a, b) => {
260
+ if (a.key[0] !== b.key[0]) return a.key[0] - b.key[0];
261
+ if (a.key[1] !== b.key[1]) return a.key[1] - b.key[1];
262
+ return a.orig - b.orig;
263
+ });
264
+ return annotated.map((entry) => entry.cls);
265
+ }
266
+ //#endregion
267
+ //#region src/sorting.ts
268
+ function sortClassString(value, options) {
269
+ if (!value || typeof value !== "string") return value;
270
+ const trimmed = value.trim();
271
+ if (!trimmed) return value;
272
+ const classes = trimmed.split(/\s+/);
273
+ if (classes.length <= 1) return value;
274
+ const leadingWs = value.match(/^\s*/)[0];
275
+ const trailingWs = value.match(/\s*$/)[0];
276
+ let toSort = classes;
277
+ if (options?.preserveDuplicates === false) toSort = classes.filter((c, i) => classes.indexOf(c) === i);
278
+ const sorted = sortClasses(toSort);
279
+ if (options?.preserveWhitespace) {
280
+ const separators = trimmed.split(/\S+/).slice(1, -1);
281
+ return `${leadingWs}${sorted.map((cls, i) => i < separators.length ? cls + separators[i] : cls).join("")}${trailingWs}`;
282
+ }
283
+ return `${leadingWs}${sorted.join(" ")}${trailingWs}`;
284
+ }
285
+ //#endregion
286
+ export { sortClasses as a, classKey as i, BREAKPOINTS as n, CLASS_ORDER as r, sortClassString as t };
package/dist/sorting.d.ts CHANGED
@@ -1 +1,2 @@
1
- export declare function sortClassString(value: string): string;
1
+ import type { SortOptions } from './types';
2
+ export declare function sortClassString(value: string, options?: SortOptions): string;
@@ -1,3 +1,4 @@
1
- export declare function processHtmlAst(ast: any, targetAttrs: string[], _targetFunctions?: string[]): any;
2
- export declare function processJsxAst(ast: any, targetAttrs: string[], targetFunctions?: string[]): any;
3
- export declare function processSvelteAst(ast: any, targetAttrs: string[], _targetFunctions?: string[]): any;
1
+ import type { SortOptions } from './types';
2
+ export declare function processHtmlAst(ast: any, attrMatcher: ((name: string) => boolean) | string[], _targetFunctions?: string[], sortOptions?: SortOptions): any;
3
+ export declare function processJsxAst(ast: any, attrMatcher: ((name: string) => boolean) | string[], targetFunctions?: string[], sortOptions?: SortOptions): any;
4
+ export declare function processSvelteAst(ast: any, attrMatcher: ((name: string) => boolean) | string[], _targetFunctions?: string[], sortOptions?: SortOptions): any;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import type { Options } from 'prettier';
2
+ export interface SortOptions {
3
+ preserveWhitespace?: boolean;
4
+ preserveDuplicates?: boolean;
5
+ }
2
6
  export interface BootstrapPluginOptions extends Options {
3
7
  bootstrapAttributes?: string[];
4
8
  bootstrapFunctions?: string[];
9
+ bootstrapPreserveWhitespace?: boolean;
10
+ bootstrapPreserveDuplicates?: boolean;
11
+ bootstrapVersion?: number;
5
12
  }
6
13
  export type SortKey = [categoryIndex: number, breakpointIndex: number];
package/package.json CHANGED
@@ -1,15 +1,22 @@
1
1
  {
2
2
  "name": "prettier-plugin-bootstrap",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "A Prettier plugin for automatic Bootstrap class sorting",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": {
9
+ "types": "./dist/index.d.ts",
9
10
  "import": "./dist/index.mjs",
10
- "types": "./dist/index.d.ts"
11
+ "require": "./dist/index.cjs"
12
+ },
13
+ "./sorter": {
14
+ "types": "./dist/sorter.d.ts",
15
+ "import": "./dist/sorter.mjs",
16
+ "require": "./dist/sorter.cjs"
11
17
  }
12
18
  },
19
+ "main": "./dist/index.cjs",
13
20
  "files": [
14
21
  "dist"
15
22
  ],
@@ -39,13 +46,14 @@
39
46
  },
40
47
  "devDependencies": {
41
48
  "@eslint/js": "^10.0.1",
42
- "@vitest/coverage-v8": "^3.2.4",
49
+ "@types/node": "^25.6.0",
50
+ "@vitest/coverage-v8": "^4.1.0",
43
51
  "eslint": "^10.3.0",
44
52
  "prettier": "^3.5.0",
45
53
  "tsdown": "^0.21.10",
46
54
  "typescript": "^5.8.0",
47
55
  "typescript-eslint": "^8.59.1",
48
- "vitest": "^3.1.0"
56
+ "vitest": "^4.1.0"
49
57
  },
50
58
  "scripts": {
51
59
  "build": "tsdown && tsc -p tsconfig.build.json",