|
1 | 1 | #! /usr/bin/env node |
2 | 2 |
|
3 | | -const fs = require("fs"); |
4 | | -const path = require("path"); |
5 | | -const { load } = require("cheerio"); |
6 | | - |
7 | | -const mainAxisMap = { |
8 | | - start: "justify-start", |
9 | | - center: "justify-center", |
10 | | - end: "justify-end", |
11 | | - "space-around": "justify-around", |
12 | | - "space-between": "justify-between", |
13 | | - "space-evenly": "justify-evenly", |
14 | | -}; |
15 | | - |
16 | | -const crossAxisMap = { |
17 | | - start: "items-start", |
18 | | - center: "items-center", |
19 | | - end: "items-end", |
20 | | - baseline: "items-baseline", |
21 | | - stretch: "items-stretch", |
22 | | -}; |
23 | | - |
24 | | -const fxAttributes = ["fxFill", "fxLayout", "fxLayoutAlign", "fxGap", "fxFlex"]; |
25 | | - |
26 | | -function convertFlexLayoutToTailwind(filePath) { |
27 | | - const html = fs.readFileSync(filePath, "utf-8"); |
28 | | - return extractHtmlTags(html).reduce( |
29 | | - (html, tag) => html.replace(tag, convertTag(tag)), |
30 | | - html |
31 | | - ); |
32 | | -} |
33 | | - |
34 | | -function convertTag(tag) { |
35 | | - if (!fxAttributes.some((a) => tag.includes(a))) return tag; |
36 | | - |
37 | | - const $ = load(tag, { xmlMode: true, decodeEntities: false }); |
38 | | - |
39 | | - $("[fxLayout], [fxLayoutGap], [fxLayoutAlign]").each((_, element) => { |
40 | | - const $element = $(element); |
41 | | - |
42 | | - const fxLayout = $element.attr("fxLayout"); |
43 | | - const fxLayoutGap = $element.attr("fxLayoutGap"); |
44 | | - const fxLayoutAlign = $element.attr("fxLayoutAlign"); |
45 | | - |
46 | | - if (fxLayout) { |
47 | | - convertFxLayoutToTailwind($element, fxLayout); |
48 | | - } |
49 | | - |
50 | | - if (fxLayoutGap) { |
51 | | - convertFxLayoutGapToTailwind($element, fxLayout, fxLayoutGap); |
52 | | - } |
53 | | - |
54 | | - if (fxLayoutAlign) { |
55 | | - const [mainAxis, crossAxis] = fxLayoutAlign.split(" "); |
56 | | - |
57 | | - if (mainAxis !== "start" && crossAxis !== "start") { |
58 | | - $element |
59 | | - .addClass( |
60 | | - `${mainAxisMap[mainAxis] || ""} ${crossAxisMap[crossAxis] || ""}` |
61 | | - ) |
62 | | - .removeAttr("fxLayoutAlign"); |
63 | | - } else if (mainAxis !== "start") { |
64 | | - $element |
65 | | - .addClass(`${mainAxisMap[mainAxis] || ""}`) |
66 | | - .removeAttr("fxLayoutAlign"); |
67 | | - } else { |
68 | | - $element |
69 | | - .addClass(crossAxisMap[crossAxis] || "") |
70 | | - .removeAttr("fxLayoutAlign"); |
71 | | - } |
72 | | - } |
73 | | - }); |
74 | | - |
75 | | - $("[fxFlex]").each((_, elem) => { |
76 | | - let fxFlex = $(elem).attr("fxFlex"); |
77 | | - |
78 | | - if (!fxFlex) { |
79 | | - $(elem).addClass(`flex-1`).removeAttr("fxFlex"); |
80 | | - return; |
81 | | - } |
82 | | - |
83 | | - if (fxFlex === "*") { |
84 | | - $(elem).addClass(`flex-auto`).removeAttr("fxFlex"); |
85 | | - return; |
86 | | - } |
87 | | - |
88 | | - let widthClass = ""; |
89 | | - switch (+fxFlex) { |
90 | | - case 33: |
91 | | - widthClass = "1/3"; |
92 | | - break; |
93 | | - case 66: |
94 | | - widthClass = "2/3"; |
95 | | - break; |
96 | | - case 100: |
97 | | - widthClass = "full"; |
98 | | - break; |
99 | | - default: |
100 | | - widthClass = percentageToFraction(+fxFlex); |
101 | | - break; |
102 | | - } |
103 | | - |
104 | | - $(elem).addClass(`basis-${widthClass}`).removeAttr("fxFlex"); |
105 | | - }); |
106 | | - |
107 | | - $("[fxFill]").each((_, elem) => { |
108 | | - $(elem) |
109 | | - .addClass(`h-full w-full min-h-full min-w-full`) |
110 | | - .removeAttr("fxFill"); |
111 | | - }); |
112 | | - |
113 | | - let newTag = $.html(); |
114 | | - newTag = newTag.replace(/(\W\w+)=""/gm, "$1"); |
115 | | - |
116 | | - if (newTag.endsWith("/>") && tag.endsWith("/>")) { |
117 | | - return newTag; |
118 | | - } else { |
119 | | - return newTag.slice(0, -2) + ">"; |
120 | | - } |
121 | | -} |
122 | | - |
123 | | -function convertFxLayoutToTailwind($element, fxLayout) { |
124 | | - let [layout, other] = (fxLayout || "column").split(" "); |
125 | | - |
126 | | - let className = ""; |
127 | | - switch (layout) { |
128 | | - case "row": |
129 | | - className = "flex-row"; |
130 | | - break; |
131 | | - case "column": |
132 | | - className = "flex-col"; |
133 | | - break; |
134 | | - case "row-reverse": |
135 | | - className = "flex-row-reverse"; |
136 | | - break; |
137 | | - case "column-reverse": |
138 | | - className = "flex-col-reverse"; |
139 | | - break; |
140 | | - default: |
141 | | - return; |
142 | | - } |
143 | | - |
144 | | - $element.addClass(`flex ${className}`); |
145 | | - |
146 | | - if (other === "wrap") { |
147 | | - $element.addClass("flex-wrap"); |
148 | | - } |
149 | | - |
150 | | - if (other === "inline") { |
151 | | - $element.removeClass("flex"); |
152 | | - $element.addClass("inline-flex"); |
153 | | - } |
154 | | - |
155 | | - $element.removeAttr("fxLayout"); |
156 | | -} |
157 | | - |
158 | | -function convertFxLayoutGapToTailwind($element, fxLayout, fxLayoutGap) { |
159 | | - let [layout] = (fxLayout || "column").split(" "); |
160 | | - |
161 | | - if (fxLayoutGap === undefined) return; |
162 | | - |
163 | | - let spacing = 0; |
164 | | - spacing = Math.ceil(parseFloat(fxLayoutGap) / 4); // convert from px |
165 | | - |
166 | | - if (layout === "row") { |
167 | | - $element.addClass(`space-x-${spacing}`); |
168 | | - } else { |
169 | | - $element.addClass(`space-y-${spacing}`); |
170 | | - } |
171 | | - |
172 | | - $element.removeAttr("fxLayoutGap"); |
173 | | -} |
174 | | - |
175 | | -function gcd(a, b) { |
176 | | - if (!b) { |
177 | | - return a; |
178 | | - } |
179 | | - return gcd(b, a % b); |
180 | | -} |
181 | | - |
182 | | -function percentageToFraction(percentage) { |
183 | | - const denominator = 100; |
184 | | - const numerator = percentage; |
185 | | - const gcdValue = gcd(numerator, denominator); |
186 | | - const simplifiedNumerator = numerator / gcdValue; |
187 | | - const simplifiedDenominator = denominator / gcdValue; |
188 | | - return `${simplifiedNumerator}/${simplifiedDenominator}`; |
189 | | -} |
190 | | - |
191 | | -function extractHtmlTags(html) { |
192 | | - let openingTags = []; |
193 | | - let tag = ""; |
194 | | - let inTag = false; |
195 | | - let quote = null; |
196 | | - |
197 | | - for (const ch of [...html]) { |
198 | | - if (!inTag && ch === "<") { |
199 | | - inTag = true; |
200 | | - tag += ch; |
201 | | - } else if (inTag) { |
202 | | - tag += ch; |
203 | | - |
204 | | - if (quote === null && (ch === '"' || ch === "'")) { |
205 | | - quote = ch; |
206 | | - } else if (quote !== null && ch === quote) { |
207 | | - quote = null; |
208 | | - } else if (quote === null && ch === ">") { |
209 | | - openingTags.push(tag); |
210 | | - tag = ""; |
211 | | - inTag = false; |
212 | | - } |
213 | | - } |
214 | | - } |
215 | | - |
216 | | - return openingTags; |
217 | | -} |
218 | | - |
219 | | -function convertFile(filePath) { |
220 | | - const convertedData = convertFlexLayoutToTailwind(filePath); |
221 | | - fs.writeFileSync(filePath, convertedData, "utf-8"); |
222 | | - console.log(`File converted successfully: ${filePath}`); |
223 | | -} |
224 | | - |
225 | | -function processFiles(folderPath, processFile, processFolder, level = 0) { |
226 | | - if (fs.existsSync(folderPath)) { |
227 | | - // console.log(`folderPath: ${folderPath}`); |
228 | | - fs.readdirSync(folderPath).forEach((file) => { |
229 | | - const currentPath = path.join(folderPath, file); |
230 | | - // console.log(`currentPath: ${currentPath}`); |
231 | | - if (fs.lstatSync(currentPath).isDirectory()) { |
232 | | - if ( |
233 | | - currentPath.endsWith("node_modules") || |
234 | | - currentPath.endsWith("dist") |
235 | | - ) { |
236 | | - return; |
237 | | - } |
238 | | - |
239 | | - if (processFiles(currentPath, processFile, processFolder, level + 1)) { |
240 | | - processFolder?.(currentPath); |
241 | | - } |
242 | | - } else { |
243 | | - if (currentPath.endsWith(".html")) { |
244 | | - processFile(currentPath, level); |
245 | | - } |
246 | | - } |
247 | | - }); |
248 | | - return true; |
249 | | - } else { |
250 | | - console.log(`Could not find folderPath: ${folderPath}`); |
251 | | - return false; |
252 | | - } |
253 | | -} |
254 | | - |
255 | | -processFiles(".", convertFile); |
| 3 | +const fn = require("../src/functions"); |
| 4 | +fn.processFiles(".", fn.convertFile); |
0 commit comments