postcss-clampwind 0.0.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,461 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/clampwind.js
30
+ var clampwind_exports = {};
31
+ __export(clampwind_exports, {
32
+ default: () => clampwind_default
33
+ });
34
+ module.exports = __toCommonJS(clampwind_exports);
35
+ var import_postcss = __toESM(require("postcss"), 1);
36
+
37
+ // src/screens.js
38
+ var defaultScreens = {
39
+ sm: "40rem",
40
+ // 640px
41
+ md: "48rem",
42
+ // 768px
43
+ lg: "64rem",
44
+ // 1024px
45
+ xl: "80rem",
46
+ // 1280px
47
+ "2xl": "96rem"
48
+ // 1536px
49
+ };
50
+ var defaultContainerScreens = {
51
+ "@3xs": "16rem",
52
+ // 256px
53
+ "@2xs": "18rem",
54
+ // 288px
55
+ "@xs": "20rem",
56
+ // 320px
57
+ "@sm": "24rem",
58
+ // 384px
59
+ "@md": "28rem",
60
+ // 448px
61
+ "@lg": "32rem",
62
+ // 512px
63
+ "@xl": "36rem",
64
+ // 576px
65
+ "@2xl": "42rem",
66
+ // 672px
67
+ "@3xl": "48rem",
68
+ // 768px
69
+ "@4xl": "56rem",
70
+ // 896px
71
+ "@5xl": "64rem",
72
+ // 1024px
73
+ "@6xl": "72rem",
74
+ // 1152px
75
+ "@7xl": "80rem"
76
+ // 1280px
77
+ };
78
+ var convertSortScreens = (screens, rootFontSize = 16) => {
79
+ const convertedScreens = Object.entries(screens).reduce((acc, [key, value]) => {
80
+ if (value.includes("px")) {
81
+ const pxValue = parseFloat(value);
82
+ acc[key] = `${pxValue / rootFontSize}rem`;
83
+ } else {
84
+ acc[key] = value;
85
+ }
86
+ return acc;
87
+ }, {});
88
+ const sortedKeys = Object.keys(convertedScreens).sort((a, b) => {
89
+ const aValue = parseFloat(convertedScreens[a]);
90
+ const bValue = parseFloat(convertedScreens[b]);
91
+ return aValue - bValue;
92
+ });
93
+ return sortedKeys.reduce((acc, key) => {
94
+ acc[key] = convertedScreens[key];
95
+ return acc;
96
+ }, {});
97
+ };
98
+
99
+ // src/utils.js
100
+ var extractTwoValidClampArgs = (value) => {
101
+ const m = value.match(/\bclamp\s*\(\s*(var\([^()]+\)|[^,()]+)\s*,\s*(var\([^()]+\)|[^,()]+)\s*\)$/);
102
+ return m ? [m[1].trim(), m[2].trim()] : null;
103
+ };
104
+ var extractUnit = (value) => {
105
+ const trimmedValue = value.replace(/\s+/g, "");
106
+ if (trimmedValue.includes("--")) {
107
+ const match = trimmedValue.replace(/var\(([^,)]+)[^)]*\)/, "$1");
108
+ return match ? match : null;
109
+ } else {
110
+ const match = trimmedValue.match(/\D+$/);
111
+ return match ? match[0] : null;
112
+ }
113
+ };
114
+ var formatProperty = (value) => {
115
+ const trimmedValue = value.replace(/\s+/g, "");
116
+ return trimmedValue.replace(/var\(([^,)]+)[^)]*\)/, "$1");
117
+ };
118
+ var convertToRem = (value, rootFontSize, spacingSize, customProperties = {}) => {
119
+ const unit = extractUnit(value);
120
+ const formattedProperty = formatProperty(value);
121
+ const fallbackValue = value.includes("var(") && value.includes(",") ? value.replace(/var\([^,]+,\s*([^)]+)\)/, "$1") : null;
122
+ if (!unit) {
123
+ const spacingSizeInt = parseFloat(spacingSize);
124
+ const spacingUnit = extractUnit(spacingSize);
125
+ if (spacingUnit === "px") {
126
+ return `${parseFloat(value * spacingSizeInt / rootFontSize).toFixed(4)}rem`;
127
+ }
128
+ if (spacingUnit === "rem") {
129
+ return `${parseFloat(value * spacingSizeInt).toFixed(4)}rem`;
130
+ }
131
+ }
132
+ if (unit === "px") {
133
+ return `${parseFloat(value.replace("px", "") / rootFontSize).toFixed(4)}rem`;
134
+ }
135
+ if (unit === "rem") {
136
+ return value;
137
+ }
138
+ if (customProperties[formattedProperty]) {
139
+ return `${customProperties[formattedProperty]}rem`;
140
+ }
141
+ if (formattedProperty && !customProperties[formattedProperty] && fallbackValue) {
142
+ const fallbackUnit = extractUnit(fallbackValue);
143
+ if (!fallbackUnit) {
144
+ return `${parseFloat(fallbackValue * spacingSize).toFixed(4)}rem`;
145
+ }
146
+ if (fallbackUnit === "px") {
147
+ return `${parseFloat(fallbackValue.replace("px", "") / rootFontSize).toFixed(4)}rem`;
148
+ }
149
+ if (fallbackUnit === "rem") {
150
+ return fallbackValue;
151
+ }
152
+ }
153
+ return null;
154
+ };
155
+ var generateClamp = (lower, upper, minScreen, maxScreen, rootFontSize = 16, spacingSize = "1px", containerQuery = false) => {
156
+ const maxScreenInt = parseFloat(
157
+ convertToRem(maxScreen, rootFontSize, spacingSize)
158
+ );
159
+ const minScreenInt = parseFloat(
160
+ convertToRem(minScreen, rootFontSize, spacingSize)
161
+ );
162
+ const lowerInt = parseFloat(lower);
163
+ const upperInt = parseFloat(upper);
164
+ const isDescending = lowerInt > upperInt;
165
+ const min = isDescending ? upper : lower;
166
+ const max = isDescending ? lower : upper;
167
+ const widthUnit = containerQuery ? `100cqw` : `100vw`;
168
+ const slopeInt = parseFloat(((upperInt - lowerInt) / (maxScreenInt - minScreenInt)).toFixed(4));
169
+ const clamp = `clamp(${min}, calc(${lower} + ${slopeInt} * (${widthUnit} - ${minScreen})), ${max})`;
170
+ return clamp;
171
+ };
172
+
173
+ // src/clampwind.js
174
+ var clampwind = (opts = {}) => {
175
+ return {
176
+ postcssPlugin: "clampwind",
177
+ prepare() {
178
+ let rootFontSize = 16;
179
+ let spacingSize = "1px";
180
+ let customProperties = {};
181
+ let screens = defaultScreens || {};
182
+ let containerScreens = defaultContainerScreens || {};
183
+ const config = {
184
+ defaultLayerBreakpoints: {},
185
+ defaultLayerContainerBreakpoints: {},
186
+ themeLayerBreakpoints: {},
187
+ themeLayerContainerBreakpoints: {},
188
+ rootElementBreakpoints: {},
189
+ rootElementContainerBreakpoints: {},
190
+ configCollected: false,
191
+ configReady: false
192
+ };
193
+ const collectConfig = (root) => {
194
+ if (config.configCollected) return;
195
+ root.walkDecls((decl) => {
196
+ if (decl.parent?.selector === ":root") {
197
+ if (decl.prop === "font-size" && decl.value.includes("px")) {
198
+ rootFontSize = parseFloat(decl.value);
199
+ }
200
+ if (decl.prop === "--text-base" && decl.value.includes("px")) {
201
+ rootFontSize = parseFloat(decl.value);
202
+ }
203
+ }
204
+ });
205
+ root.walkDecls((decl) => {
206
+ if (decl.parent?.selector === ":root") {
207
+ if (decl.prop.startsWith("--breakpoint-")) {
208
+ const key = decl.prop.replace("--breakpoint-", "");
209
+ config.rootElementBreakpoints[key] = decl.value;
210
+ }
211
+ if (decl.prop.startsWith("--container-")) {
212
+ const key = decl.prop.replace("--container-", "@");
213
+ config.rootElementContainerBreakpoints[key] = decl.value;
214
+ }
215
+ if (decl.prop === "--spacing") {
216
+ spacingSize = decl.value;
217
+ }
218
+ if (decl.prop.startsWith("--")) {
219
+ const value = parseFloat(convertToRem(decl.value, rootFontSize, spacingSize, customProperties));
220
+ if (value) customProperties[decl.prop] = value;
221
+ }
222
+ }
223
+ });
224
+ root.walkAtRules("layer", (atRule) => {
225
+ if (atRule.params === "theme") {
226
+ atRule.walkDecls((decl) => {
227
+ if (decl.prop === "--text-base" && decl.value.includes("px")) {
228
+ rootFontSize = parseFloat(decl.value);
229
+ }
230
+ });
231
+ }
232
+ });
233
+ root.walkAtRules("layer", (atRule) => {
234
+ if (atRule.params === "default") {
235
+ if (!Object.keys(config.defaultLayerBreakpoints).length) {
236
+ atRule.walkDecls((decl) => {
237
+ if (decl.prop.startsWith("--breakpoint-")) {
238
+ const key = decl.prop.replace("--breakpoint-", "");
239
+ config.defaultLayerBreakpoints[key] = decl.value;
240
+ }
241
+ if (decl.prop.startsWith("--container-")) {
242
+ const key = decl.prop.replace("--container-", "@");
243
+ config.defaultLayerContainerBreakpoints[key] = decl.value;
244
+ }
245
+ });
246
+ }
247
+ }
248
+ if (atRule.params === "theme") {
249
+ atRule.walkDecls((decl) => {
250
+ if (decl.prop.startsWith("--breakpoint-")) {
251
+ const key = decl.prop.replace("--breakpoint-", "");
252
+ config.themeLayerBreakpoints[key] = decl.value;
253
+ }
254
+ if (decl.prop.startsWith("--container-")) {
255
+ const key = decl.prop.replace("--container-", "@");
256
+ config.themeLayerContainerBreakpoints[key] = decl.value;
257
+ }
258
+ if (decl.prop === "--spacing") {
259
+ spacingSize = decl.value;
260
+ }
261
+ if (decl.prop.startsWith("--")) {
262
+ const value = parseFloat(convertToRem(decl.value, rootFontSize, spacingSize, customProperties));
263
+ if (value) customProperties[decl.prop] = value;
264
+ }
265
+ });
266
+ }
267
+ });
268
+ config.configCollected = true;
269
+ };
270
+ const finalizeConfig = () => {
271
+ if (config.configReady) return;
272
+ screens = Object.assign(
273
+ {},
274
+ screens,
275
+ config.defaultLayerBreakpoints,
276
+ config.rootElementBreakpoints,
277
+ config.themeLayerBreakpoints
278
+ );
279
+ screens = convertSortScreens(screens, rootFontSize);
280
+ containerScreens = Object.assign(
281
+ {},
282
+ containerScreens,
283
+ config.defaultLayerContainerBreakpoints,
284
+ config.rootElementContainerBreakpoints,
285
+ config.themeLayerContainerBreakpoints
286
+ );
287
+ containerScreens = convertSortScreens(containerScreens, rootFontSize);
288
+ config.configReady = true;
289
+ };
290
+ const processClampDeclaration = (decl, minScreen, maxScreen, isContainer = false) => {
291
+ const args = extractTwoValidClampArgs(decl.value);
292
+ const [lower, upper] = args.map((val) => convertToRem(val, rootFontSize, spacingSize, customProperties));
293
+ if (!args || !lower || !upper) {
294
+ console.warn("Invalid clamp() values", { node: decl });
295
+ decl.value = ` ${decl.value} /* Invalid clamp() values */`;
296
+ return true;
297
+ }
298
+ const clamp = generateClamp(lower, upper, minScreen, maxScreen, rootFontSize, spacingSize, isContainer);
299
+ decl.value = clamp;
300
+ return true;
301
+ };
302
+ return {
303
+ // Use OnceExit to ensure Tailwind has generated its content
304
+ OnceExit(root, { result }) {
305
+ collectConfig(root);
306
+ finalizeConfig();
307
+ root.walkAtRules("media", (atRule) => {
308
+ const isNested = atRule.parent?.type === "atrule";
309
+ const isSameAtRule = atRule.parent?.name === atRule.name;
310
+ const clampDecls = [];
311
+ atRule.walkDecls((decl) => {
312
+ if (extractTwoValidClampArgs(decl.value)) {
313
+ clampDecls.push(decl);
314
+ }
315
+ });
316
+ if (!clampDecls.length) return;
317
+ if (isNested && isSameAtRule) {
318
+ const parentParams = atRule.parent.params;
319
+ const currentParams = atRule.params;
320
+ let minScreen = null;
321
+ let maxScreen = null;
322
+ if (parentParams.includes(">")) {
323
+ const match = parentParams.match(/>=?\s*([^)]+)/);
324
+ if (match) minScreen = match[1].trim();
325
+ }
326
+ if (currentParams.includes(">") && !minScreen) {
327
+ const match = currentParams.match(/>=?\s*([^)]+)/);
328
+ if (match) minScreen = match[1].trim();
329
+ }
330
+ if (parentParams.includes("<")) {
331
+ const match = parentParams.match(/<\s*([^)]+)/);
332
+ if (match) maxScreen = match[1].trim();
333
+ }
334
+ if (currentParams.includes("<") && !maxScreen) {
335
+ const match = currentParams.match(/<\s*([^)]+)/);
336
+ if (match) maxScreen = match[1].trim();
337
+ }
338
+ if (minScreen && maxScreen) {
339
+ clampDecls.forEach((decl) => {
340
+ processClampDeclaration(decl, minScreen, maxScreen, false);
341
+ });
342
+ }
343
+ return;
344
+ }
345
+ if (isNested && !isSameAtRule) {
346
+ clampDecls.forEach((decl) => {
347
+ decl.value = ` ${decl.value} /* Invalid nested @media rules */`;
348
+ });
349
+ return;
350
+ }
351
+ const screenValues = Object.values(screens);
352
+ clampDecls.forEach((decl) => {
353
+ if (atRule.params.includes(">")) {
354
+ const match = atRule.params.match(/>=?\s*([^)]+)/);
355
+ if (match) {
356
+ const minScreen = match[1].trim();
357
+ const maxScreen = screenValues[screenValues.length - 1];
358
+ processClampDeclaration(decl, minScreen, maxScreen, false);
359
+ }
360
+ } else if (atRule.params.includes("<")) {
361
+ const match = atRule.params.match(/<\s*([^)]+)/);
362
+ if (match) {
363
+ const minScreen = screenValues[0];
364
+ const maxScreen = match[1].trim();
365
+ processClampDeclaration(decl, minScreen, maxScreen, false);
366
+ }
367
+ }
368
+ });
369
+ });
370
+ root.walkAtRules("container", (atRule) => {
371
+ const isNested = atRule.parent?.type === "atrule";
372
+ const isSameAtRule = atRule.parent?.name === atRule.name;
373
+ const clampDecls = [];
374
+ atRule.walkDecls((decl) => {
375
+ if (extractTwoValidClampArgs(decl.value)) {
376
+ clampDecls.push(decl);
377
+ }
378
+ });
379
+ if (!clampDecls.length) return;
380
+ if (isNested && isSameAtRule) {
381
+ const parentParams = atRule.parent.params;
382
+ const currentParams = atRule.params;
383
+ let minContainer = null;
384
+ let maxContainer = null;
385
+ if (parentParams.includes(">")) {
386
+ const match = parentParams.match(/>=?\s*([^)]+)/);
387
+ if (match) minContainer = match[1].trim();
388
+ }
389
+ if (currentParams.includes(">") && !minContainer) {
390
+ const match = currentParams.match(/>=?\s*([^)]+)/);
391
+ if (match) minContainer = match[1].trim();
392
+ }
393
+ if (parentParams.includes("<")) {
394
+ const match = parentParams.match(/<\s*([^)]+)/);
395
+ if (match) maxContainer = match[1].trim();
396
+ }
397
+ if (currentParams.includes("<") && !maxContainer) {
398
+ const match = currentParams.match(/<\s*([^)]+)/);
399
+ if (match) maxContainer = match[1].trim();
400
+ }
401
+ if (minContainer && maxContainer) {
402
+ clampDecls.forEach((decl) => {
403
+ processClampDeclaration(decl, minContainer, maxContainer, true);
404
+ });
405
+ }
406
+ return;
407
+ }
408
+ if (isNested && !isSameAtRule) {
409
+ clampDecls.forEach((decl) => {
410
+ decl.value = ` ${decl.value} /* Invalid nested @container rules */`;
411
+ });
412
+ return;
413
+ }
414
+ const screenValues = Object.values(containerScreens);
415
+ clampDecls.forEach((decl) => {
416
+ if (atRule.params.includes(">")) {
417
+ const match = atRule.params.match(/>=?\s*([^)]+)/);
418
+ if (match) {
419
+ const minContainer = match[1].trim();
420
+ const maxContainer = screenValues[screenValues.length - 1];
421
+ processClampDeclaration(decl, minContainer, maxContainer, true);
422
+ }
423
+ } else if (atRule.params.includes("<")) {
424
+ const match = atRule.params.match(/<\s*([^)]+)/);
425
+ if (match) {
426
+ const minContainer = screenValues[0];
427
+ const maxContainer = match[1].trim();
428
+ processClampDeclaration(decl, minContainer, maxContainer, true);
429
+ }
430
+ }
431
+ });
432
+ });
433
+ root.walkRules((rule) => {
434
+ let parent = rule.parent;
435
+ while (parent) {
436
+ if (parent.type === "atrule" && (parent.name === "media" || parent.name === "container")) {
437
+ return;
438
+ }
439
+ parent = parent.parent;
440
+ }
441
+ const clampDecls = [];
442
+ rule.walkDecls((decl) => {
443
+ if (extractTwoValidClampArgs(decl.value)) {
444
+ clampDecls.push(decl);
445
+ }
446
+ });
447
+ if (clampDecls.length === 0) return;
448
+ const screenValues = Object.values(screens);
449
+ const minScreen = screenValues[0];
450
+ const maxScreen = screenValues[screenValues.length - 1];
451
+ clampDecls.forEach((decl) => {
452
+ processClampDeclaration(decl, minScreen, maxScreen, false);
453
+ });
454
+ });
455
+ }
456
+ };
457
+ }
458
+ };
459
+ };
460
+ clampwind.postcss = true;
461
+ var clampwind_default = clampwind;