@myelophone/nuxt 0.18.2

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.
Files changed (259) hide show
  1. package/.devcontainer/Dockerfile +7 -0
  2. package/.devcontainer/devcontainer.json +21 -0
  3. package/.editorconfig +43 -0
  4. package/.github/workflows/package.yml +73 -0
  5. package/.nuxtignore +6 -0
  6. package/.releaserc.js +132 -0
  7. package/.yarn/releases/yarn-4.18.0.cjs +1000 -0
  8. package/.yarnrc.yml +8 -0
  9. package/CHANGELOG.md +141 -0
  10. package/LICENSE +77 -0
  11. package/README.md +3004 -0
  12. package/app/app.vue +455 -0
  13. package/app/assets/css/animation.css +81 -0
  14. package/app/assets/css/backgrounds.css +57 -0
  15. package/app/assets/css/base.css +182 -0
  16. package/app/assets/css/index.css +9 -0
  17. package/app/assets/css/links.css +12 -0
  18. package/app/assets/css/multimedia.css +102 -0
  19. package/app/assets/css/preloader.css +68 -0
  20. package/app/assets/css/print.css +40 -0
  21. package/app/assets/css/tailwind.css +20 -0
  22. package/app/assets/css/theme.css +29 -0
  23. package/app/components/consent/GoogleMap.vue +59 -0
  24. package/app/components/consent/Youtube.vue +27 -0
  25. package/app/components/cookie/Banner.vue +70 -0
  26. package/app/components/cookie/ConsentWrapper.vue +71 -0
  27. package/app/components/cookie/PrivacyPolicy.vue +179 -0
  28. package/app/components/cookie/SettingsModal.vue +387 -0
  29. package/app/components/grid/Col.vue +262 -0
  30. package/app/components/grid/Container.vue +236 -0
  31. package/app/components/grid/Row.vue +248 -0
  32. package/app/components/grid/Stack.vue +233 -0
  33. package/app/components/myelophone/Copyright.vue +5 -0
  34. package/app/components/myelophone/Welcome.vue +107 -0
  35. package/app/components/page/FullscreenPreloader.vue +177 -0
  36. package/app/components/page/Preloader.vue +18 -0
  37. package/app/components/page/ReadyLocalized.vue +19 -0
  38. package/app/components/ready/.gitkeep +0 -0
  39. package/app/components/safe/Email.vue +52 -0
  40. package/app/components/safe/ImgWithLoader.vue +180 -0
  41. package/app/components/safe/NuxtImg.vue +25 -0
  42. package/app/components/safe/NuxtPicture.vue +29 -0
  43. package/app/components/section/.gitkeep +0 -0
  44. package/app/components/seo/BrandInfo.vue +76 -0
  45. package/app/components/seo/ContentNoIndex.vue +35 -0
  46. package/app/components/seo/NoIndex.vue +22 -0
  47. package/app/components/seo/OrgInfo.vue +130 -0
  48. package/app/components/seo/ProfileInfo.vue +137 -0
  49. package/app/components/ui/Accordion.vue +163 -0
  50. package/app/components/ui/Alert.vue +179 -0
  51. package/app/components/ui/Avatar.vue +88 -0
  52. package/app/components/ui/Badge.vue +150 -0
  53. package/app/components/ui/Banner.vue +133 -0
  54. package/app/components/ui/Button.vue +175 -0
  55. package/app/components/ui/Card.vue +66 -0
  56. package/app/components/ui/Checkbox.vue +134 -0
  57. package/app/components/ui/Chip.vue +136 -0
  58. package/app/components/ui/CommandPalette.vue +434 -0
  59. package/app/components/ui/Console.vue +583 -0
  60. package/app/components/ui/CursorCreative.vue +148 -0
  61. package/app/components/ui/FeatureAccordion.vue +165 -0
  62. package/app/components/ui/FileUpload.vue +705 -0
  63. package/app/components/ui/GeoDependent.vue +72 -0
  64. package/app/components/ui/Heading.vue +51 -0
  65. package/app/components/ui/Icon.vue +42 -0
  66. package/app/components/ui/Icon8.vue +39 -0
  67. package/app/components/ui/Input.vue +88 -0
  68. package/app/components/ui/LangLink.vue +34 -0
  69. package/app/components/ui/LangVisible.vue +33 -0
  70. package/app/components/ui/LanguageSelect.vue +202 -0
  71. package/app/components/ui/LightBox.vue +155 -0
  72. package/app/components/ui/LocalTime.vue +170 -0
  73. package/app/components/ui/Modal.vue +161 -0
  74. package/app/components/ui/Popup.vue +200 -0
  75. package/app/components/ui/PostContent.server.vue +23 -0
  76. package/app/components/ui/ProtectedEmail.vue +52 -0
  77. package/app/components/ui/RelativeTime.vue +141 -0
  78. package/app/components/ui/ScheduledContent.vue +150 -0
  79. package/app/components/ui/ScrollToTop.client.vue +48 -0
  80. package/app/components/ui/SegmentedControl.vue +148 -0
  81. package/app/components/ui/SiteSearch.vue +1191 -0
  82. package/app/components/ui/Skeleton.vue +91 -0
  83. package/app/components/ui/SmartContrast.vue +74 -0
  84. package/app/components/ui/SnapContainer.vue +222 -0
  85. package/app/components/ui/SnapSection.vue +294 -0
  86. package/app/components/ui/SonnerStyle.vue +7 -0
  87. package/app/components/ui/SplitSection.vue +99 -0
  88. package/app/components/ui/Steps.vue +858 -0
  89. package/app/components/ui/StickyWrapper.vue +46 -0
  90. package/app/components/ui/Table.vue +251 -0
  91. package/app/components/ui/Tabs.vue +451 -0
  92. package/app/components/ui/TextColumn.vue +36 -0
  93. package/app/components/ui/Textarea.vue +251 -0
  94. package/app/components/ui/Toggler.vue +40 -0
  95. package/app/components/ui/Tooltip.vue +524 -0
  96. package/app/components/ui/TruncateText.vue +105 -0
  97. package/app/components/ui/ViewportSpacer.vue +72 -0
  98. package/app/components/view/AnimatedCounter.vue +121 -0
  99. package/app/components/view/AnnouncementBar.vue +104 -0
  100. package/app/components/view/AvatarGroup.vue +104 -0
  101. package/app/components/view/Breadcrumbs.vue +97 -0
  102. package/app/components/view/ContentWithToc.vue +152 -0
  103. package/app/components/view/Copyright.vue +68 -0
  104. package/app/components/view/CountdownTimer.vue +184 -0
  105. package/app/components/view/CreativeCTA.vue +143 -0
  106. package/app/components/view/CurrencySelect.vue +254 -0
  107. package/app/components/view/FeatureBoxGrid.vue +142 -0
  108. package/app/components/view/HorizontalMenu.vue +97 -0
  109. package/app/components/view/ImageCompare.vue +141 -0
  110. package/app/components/view/ImgSlider.vue +333 -0
  111. package/app/components/view/InfiniteMarquee.vue +147 -0
  112. package/app/components/view/InfoBar.vue +86 -0
  113. package/app/components/view/LogoGrid.vue +106 -0
  114. package/app/components/view/LogoSlider.vue +83 -0
  115. package/app/components/view/NotFound.vue +81 -0
  116. package/app/components/view/PartialBackground.vue +313 -0
  117. package/app/components/view/QuoteBig.vue +111 -0
  118. package/app/components/view/ResponsiveMenu.vue +219 -0
  119. package/app/components/view/ReviewsSlider.vue +230 -0
  120. package/app/components/view/SocialBar.vue +78 -0
  121. package/app/components/view/StickyHeadingContent.vue +158 -0
  122. package/app/components/view/TallyForm.vue +295 -0
  123. package/app/components/view/WarningOutdated.vue +113 -0
  124. package/app/composables/useApi.ts +88 -0
  125. package/app/composables/useAppSeo.ts +78 -0
  126. package/app/composables/useBreadcrumbs.ts +106 -0
  127. package/app/composables/useChangeUrl.ts +73 -0
  128. package/app/composables/useCommand.ts +28 -0
  129. package/app/composables/useCookieControl.ts +111 -0
  130. package/app/composables/useCookieModal.ts +9 -0
  131. package/app/composables/useDevice.ts +109 -0
  132. package/app/composables/useGeoFetch.ts +139 -0
  133. package/app/composables/useLoadScript.ts +36 -0
  134. package/app/composables/useModalState.ts +37 -0
  135. package/app/composables/useMultiLang.ts +278 -0
  136. package/app/composables/useNetwork.ts +49 -0
  137. package/app/composables/useNuxtVersion.ts +5 -0
  138. package/app/composables/usePiniaStore.ts +5 -0
  139. package/app/composables/usePopupControl.ts +57 -0
  140. package/app/composables/useQuery.ts +307 -0
  141. package/app/composables/useReadyPage.ts +182 -0
  142. package/app/composables/useSafeList.ts +90 -0
  143. package/app/composables/useScriptLoader.ts +221 -0
  144. package/app/composables/useSiteSearch.ts +32 -0
  145. package/app/composables/useSnapScroll.ts +24 -0
  146. package/app/composables/useStatic.ts +5 -0
  147. package/app/composables/useStorage.ts +252 -0
  148. package/app/composables/useStoreBroadcast.ts +146 -0
  149. package/app/composables/useThemeSync.ts +81 -0
  150. package/app/constants/layout.ts +109 -0
  151. package/app/constants/predefinedCookieScripts.ts +1222 -0
  152. package/app/constants/ui-events.ts +1 -0
  153. package/app/error.vue +169 -0
  154. package/app/layouts/default-blog.vue +5 -0
  155. package/app/layouts/default.vue +3 -0
  156. package/app/locales/en/commands.json +5 -0
  157. package/app/locales/en/cookies.json +182 -0
  158. package/app/locales/en/error.json +4 -0
  159. package/app/locales/en/interface.json +33 -0
  160. package/app/locales/en/myelophone.json +8 -0
  161. package/app/locales/en/outdated.json +5 -0
  162. package/app/locales/en/times.json +18 -0
  163. package/app/locales/pl/commands.json +5 -0
  164. package/app/locales/pl/cookies.json +182 -0
  165. package/app/locales/pl/error.json +4 -0
  166. package/app/locales/pl/interface.json +33 -0
  167. package/app/locales/pl/myelophone.json +8 -0
  168. package/app/locales/pl/outdated.json +5 -0
  169. package/app/locales/pl/times.json +18 -0
  170. package/app/locales/ru/commands.json +5 -0
  171. package/app/locales/ru/cookies.json +182 -0
  172. package/app/locales/ru/error.json +4 -0
  173. package/app/locales/ru/interface.json +33 -0
  174. package/app/locales/ru/myelophone.json +8 -0
  175. package/app/locales/ru/outdated.json +5 -0
  176. package/app/locales/ru/times.json +18 -0
  177. package/app/middleware/blog-layout.ts +28 -0
  178. package/app/middleware/i18n.global.ts +17 -0
  179. package/app/middleware/not-found-status.ts +11 -0
  180. package/app/modules/default-blog/index.ts +39 -0
  181. package/app/modules/default-blog/posts.ts +80 -0
  182. package/app/modules/default-sitemap/index.ts +116 -0
  183. package/app/modules/default-sitemap/runtime/sitemap.get.ts +42 -0
  184. package/app/modules/fullscreen-preloader/module.ts +43 -0
  185. package/app/modules/htaccess/.htaccess +38 -0
  186. package/app/modules/htaccess/index.ts +23 -0
  187. package/app/modules/i18n-shaker.mjs +221 -0
  188. package/app/modules/multi18n/module.ts +71 -0
  189. package/app/modules/myelophone-config/module.ts +241 -0
  190. package/app/pages/[...all].vue +9 -0
  191. package/app/pages/index.vue +3 -0
  192. package/app/pages/post/[...slug].vue +30 -0
  193. package/app/plugins/bfcache-refresh.client.ts +9 -0
  194. package/app/plugins/canonical.ts +30 -0
  195. package/app/plugins/csp-nonce.server.ts +41 -0
  196. package/app/plugins/multi18n.client.ts +21 -0
  197. package/app/plugins/multi18n.server.ts +15 -0
  198. package/app/plugins/nuxt-version.client.ts +4 -0
  199. package/app/plugins/restore.client.ts +21 -0
  200. package/app/plugins/reveal.ts +89 -0
  201. package/app/plugins/url-helper.client.ts +66 -0
  202. package/app/server/api/exchange-rates.get.ts +216 -0
  203. package/app/server/api/healthz.ts +1 -0
  204. package/app/server/api/logout.post.ts +6 -0
  205. package/app/server/middleware/normalize-url.ts +14 -0
  206. package/app/server/middleware/security-headers.ts +147 -0
  207. package/app/server/middleware/url-helper.ts +43 -0
  208. package/app/server/plugins/clean-html-spaces.ts +15 -0
  209. package/app/server/plugins/clean-styles.ts +94 -0
  210. package/app/server/plugins/compression.ts +122 -0
  211. package/app/server/plugins/css-preload.ts +26 -0
  212. package/app/server/plugins/early-hints.ts +31 -0
  213. package/app/server/plugins/force-noindex.ts +43 -0
  214. package/app/server/plugins/html-one-line.ts +17 -0
  215. package/app/server/plugins/inline-style.ts +7 -0
  216. package/app/server/routes/healthz.get.ts +1 -0
  217. package/app/server/tsconfig.json +3 -0
  218. package/app/stores/cart.ts +945 -0
  219. package/app/stores/commands.ts +43 -0
  220. package/app/stores/device.ts +156 -0
  221. package/app/stores/querySync.ts +5 -0
  222. package/app/stores/settings.ts +145 -0
  223. package/app/stores/user.ts +607 -0
  224. package/app/types/cookie.ts +89 -0
  225. package/app/types/myelophone.d.ts +87 -0
  226. package/app/types/site-search.ts +110 -0
  227. package/app/utils/asyncRefresh.ts +4 -0
  228. package/app/utils/blog.server.ts +142 -0
  229. package/app/utils/jsonLd.ts +56 -0
  230. package/app/utils/myelophoneConfig.ts +4 -0
  231. package/app/utils/sortBy.ts +18 -0
  232. package/app/utils/transliterate.ts +75 -0
  233. package/app/utils/ytVideo.ts +37 -0
  234. package/app/workers/site-search.worker.ts +661 -0
  235. package/biome.json +94 -0
  236. package/commitlint.config.js +3 -0
  237. package/nuxt.config.ts +539 -0
  238. package/package.json +101 -0
  239. package/playground/content/.gitkeep +0 -0
  240. package/playground/locales/.gitkeep +0 -0
  241. package/playground/myelophone.dev.ts +45 -0
  242. package/playground/nuxt.config.ts +105 -0
  243. package/playground/package.json +5 -0
  244. package/playground/pages/.gitkeep +0 -0
  245. package/playground/tsconfig.json +3 -0
  246. package/playwright.config.ts +23 -0
  247. package/public/browsers/chrome.svg +77 -0
  248. package/public/browsers/edge.svg +98 -0
  249. package/public/browsers/firefox.svg +300 -0
  250. package/public/favicon.ico +0 -0
  251. package/public/myelophone_eng.png +0 -0
  252. package/public/myelophone_eng_white.png +0 -0
  253. package/spa-loading-template.html +72 -0
  254. package/tests/home.spec.ts +9 -0
  255. package/tests/utils.ts +23 -0
  256. package/tsconfig.json +16 -0
  257. package/vite/plugins/clean-css.ts +190 -0
  258. package/vite/plugins/postcss-add-viewportunits.ts +33 -0
  259. package/vite/plugins/postcss-viewport-fallback.ts +49 -0
@@ -0,0 +1,300 @@
1
+ <svg
2
+ id="Layer_1"
3
+ data-name="Layer 1"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ xmlns:xlink="http://www.w3.org/1999/xlink"
6
+ width="953.37"
7
+ height="984"
8
+ viewBox="0 0 953.37 984"
9
+ >
10
+ <defs>
11
+ <radialGradient
12
+ id="radial-gradient"
13
+ cx="-14569.52"
14
+ cy="9284.3"
15
+ fx="-14598.28277817514"
16
+ r="450.88"
17
+ gradientTransform="matrix(0.76, 0.03, 0.05, -1.12, 11545.85, 11139.76)"
18
+ gradientUnits="userSpaceOnUse"
19
+ >
20
+ <stop offset="0.1" stop-color="#ffea00" />
21
+ <stop offset="0.17" stop-color="#ffde00" />
22
+ <stop offset="0.28" stop-color="#ffbf00" />
23
+ <stop offset="0.43" stop-color="#ff8e00" />
24
+ <stop offset="0.77" stop-color="#ff272d" />
25
+ <stop offset="0.87" stop-color="#e0255a" />
26
+ <stop offset="0.95" stop-color="#cc2477" />
27
+ <stop offset="1" stop-color="#c42482" />
28
+ </radialGradient>
29
+ <radialGradient
30
+ id="radial-gradient-2"
31
+ cx="-7540.92"
32
+ cy="8862.36"
33
+ r="791.23"
34
+ gradientTransform="matrix(1.23, 0, 0, -1.23, 9900.64, 11054.07)"
35
+ gradientUnits="userSpaceOnUse"
36
+ >
37
+ <stop offset="0" stop-color="#00ccda" />
38
+ <stop offset="0.22" stop-color="#0083ff" />
39
+ <stop offset="0.26" stop-color="#007af9" />
40
+ <stop offset="0.33" stop-color="#0060e8" />
41
+ <stop offset="0.33" stop-color="#005fe7" />
42
+ <stop offset="0.44" stop-color="#2639ad" />
43
+ <stop offset="0.52" stop-color="#401e84" />
44
+ <stop offset="0.57" stop-color="#4a1475" />
45
+ </radialGradient>
46
+ <linearGradient
47
+ id="linear-gradient"
48
+ x1="540.64"
49
+ y1="254.8"
50
+ x2="349.2"
51
+ y2="881.03"
52
+ gradientTransform="matrix(1, 0, 0, -1, 0, 984)"
53
+ gradientUnits="userSpaceOnUse"
54
+ >
55
+ <stop offset="0" stop-color="#000f43" stop-opacity="0.4" />
56
+ <stop offset="0.48" stop-color="#001962" stop-opacity="0.17" />
57
+ <stop offset="1" stop-color="#002079" stop-opacity="0" />
58
+ </linearGradient>
59
+ <radialGradient
60
+ id="radial-gradient-3"
61
+ cx="-8291.37"
62
+ cy="7446.88"
63
+ r="266.89"
64
+ gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10186.72, 10733.96)"
65
+ gradientUnits="userSpaceOnUse"
66
+ >
67
+ <stop offset="0" stop-color="#ffea00" />
68
+ <stop offset="0.5" stop-color="#ff272d" />
69
+ <stop offset="1" stop-color="#c42482" />
70
+ </radialGradient>
71
+ <radialGradient
72
+ id="radial-gradient-4"
73
+ cx="-8315.51"
74
+ cy="7702.79"
75
+ r="445.68"
76
+ gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10186.72, 10733.96)"
77
+ gradientUnits="userSpaceOnUse"
78
+ >
79
+ <stop offset="0" stop-color="#ffe900" />
80
+ <stop offset="0.16" stop-color="#ffaf0e" />
81
+ <stop offset="0.32" stop-color="#ff7a1b" />
82
+ <stop offset="0.47" stop-color="#ff4e26" />
83
+ <stop offset="0.62" stop-color="#ff2c2e" />
84
+ <stop offset="0.76" stop-color="#ff1434" />
85
+ <stop offset="0.89" stop-color="#ff0538" />
86
+ <stop offset="1" stop-color="#ff0039" />
87
+ </radialGradient>
88
+ <radialGradient
89
+ id="radial-gradient-5"
90
+ cx="-8252.47"
91
+ cy="7289.65"
92
+ r="408.96"
93
+ gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10186.72, 10733.96)"
94
+ gradientUnits="userSpaceOnUse"
95
+ >
96
+ <stop offset="0" stop-color="#ff272d" />
97
+ <stop offset="0.5" stop-color="#c42482" />
98
+ <stop offset="0.99" stop-color="#620700" />
99
+ </radialGradient>
100
+ <radialGradient
101
+ id="radial-gradient-6"
102
+ cx="715.19"
103
+ cy="589.96"
104
+ fx="743.1665035759816"
105
+ fy="576.1323859732546"
106
+ r="782.18"
107
+ gradientTransform="matrix(1, 0, 0, -1, 0, 984)"
108
+ gradientUnits="userSpaceOnUse"
109
+ >
110
+ <stop offset="0.16" stop-color="#ffea00" />
111
+ <stop offset="0.23" stop-color="#ffde00" />
112
+ <stop offset="0.37" stop-color="#ffbf00" />
113
+ <stop offset="0.54" stop-color="#ff8e00" />
114
+ <stop offset="0.76" stop-color="#ff272d" />
115
+ <stop offset="0.8" stop-color="#f92433" />
116
+ <stop offset="0.84" stop-color="#e91c45" />
117
+ <stop offset="0.89" stop-color="#cf0e62" />
118
+ <stop offset="0.94" stop-color="#b5007f" />
119
+ </radialGradient>
120
+ <radialGradient
121
+ id="radial-gradient-7"
122
+ cx="670.34"
123
+ cy="952.71"
124
+ r="891.45"
125
+ gradientTransform="matrix(1, 0, 0, -1, 0, 984)"
126
+ gradientUnits="userSpaceOnUse"
127
+ >
128
+ <stop offset="0.28" stop-color="#ffea00" />
129
+ <stop offset="0.4" stop-color="#fd0" />
130
+ <stop offset="0.63" stop-color="#ffba00" />
131
+ <stop offset="0.86" stop-color="#ff9100" />
132
+ <stop offset="0.93" stop-color="#ff6711" />
133
+ <stop offset="0.99" stop-color="#ff4a1d" />
134
+ </radialGradient>
135
+ <linearGradient
136
+ id="linear-gradient-2"
137
+ x1="-8977.08"
138
+ y1="7615.91"
139
+ x2="-8670.05"
140
+ y2="7694.17"
141
+ gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10186.72, 10733.96)"
142
+ gradientUnits="userSpaceOnUse"
143
+ >
144
+ <stop offset="0" stop-color="#c42482" stop-opacity="0.5" />
145
+ <stop offset="0.47" stop-color="#ff272d" stop-opacity="0.5" />
146
+ <stop offset="0.49" stop-color="#ff2c2c" stop-opacity="0.51" />
147
+ <stop offset="0.68" stop-color="#ff7a1a" stop-opacity="0.72" />
148
+ <stop offset="0.83" stop-color="#ffb20d" stop-opacity="0.87" />
149
+ <stop offset="0.94" stop-color="#ffd605" stop-opacity="0.96" />
150
+ <stop offset="1" stop-color="#ffe302" />
151
+ </linearGradient>
152
+ <linearGradient
153
+ id="linear-gradient-3"
154
+ x1="188.29"
155
+ y1="2056.08"
156
+ x2="133.72"
157
+ y2="2195.93"
158
+ gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, -247, 2281.31)"
159
+ gradientUnits="userSpaceOnUse"
160
+ >
161
+ <stop offset="0" stop-color="#891551" stop-opacity="0.6" />
162
+ <stop offset="1" stop-color="#c42482" stop-opacity="0" />
163
+ </linearGradient>
164
+ <linearGradient
165
+ id="linear-gradient-4"
166
+ x1="-39.01"
167
+ y1="252.89"
168
+ x2="55.1"
169
+ y2="146.01"
170
+ gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 232.13, 720.9)"
171
+ gradientUnits="userSpaceOnUse"
172
+ >
173
+ <stop offset="0.01" stop-color="#891551" stop-opacity="0.5" />
174
+ <stop offset="0.48" stop-color="#ff272d" stop-opacity="0.5" />
175
+ <stop offset="1" stop-color="#ff272d" stop-opacity="0" />
176
+ </linearGradient>
177
+ <linearGradient
178
+ id="linear-gradient-5"
179
+ x1="141.88"
180
+ y1="68.53"
181
+ x2="141.95"
182
+ y2="143.31"
183
+ gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 232.13, 720.9)"
184
+ gradientUnits="userSpaceOnUse"
185
+ >
186
+ <stop offset="0" stop-color="#c42482" />
187
+ <stop offset="0.08" stop-color="#c42482" stop-opacity="0.81" />
188
+ <stop offset="0.21" stop-color="#c42482" stop-opacity="0.57" />
189
+ <stop offset="0.33" stop-color="#c42482" stop-opacity="0.36" />
190
+ <stop offset="0.45" stop-color="#c42482" stop-opacity="0.2" />
191
+ <stop offset="0.56" stop-color="#c42482" stop-opacity="0.09" />
192
+ <stop offset="0.67" stop-color="#c42482" stop-opacity="0.02" />
193
+ <stop offset="0.77" stop-color="#c42482" stop-opacity="0" />
194
+ </linearGradient>
195
+ <linearGradient
196
+ id="linear-gradient-6"
197
+ x1="620.52"
198
+ y1="947.88"
199
+ x2="926.18"
200
+ y2="264.39"
201
+ gradientTransform="matrix(1, 0, 0, -1, 0, 984)"
202
+ gradientUnits="userSpaceOnUse"
203
+ >
204
+ <stop offset="0" stop-color="#fff14f" />
205
+ <stop offset="0.27" stop-color="#ffee4c" />
206
+ <stop offset="0.45" stop-color="#ffe643" />
207
+ <stop offset="0.61" stop-color="#ffd834" />
208
+ <stop offset="0.76" stop-color="#ffc41e" />
209
+ <stop offset="0.89" stop-color="#ffab02" />
210
+ <stop offset="0.9" stop-color="#ffa900" />
211
+ <stop offset="0.95" stop-color="#ffa000" />
212
+ <stop offset="1" stop-color="#ff9100" />
213
+ </linearGradient>
214
+ <linearGradient
215
+ id="linear-gradient-7"
216
+ x1="680.88"
217
+ y1="554.79"
218
+ x2="536.1"
219
+ y2="166.04"
220
+ gradientTransform="matrix(1, 0, 0, -1, 0, 984)"
221
+ gradientUnits="userSpaceOnUse"
222
+ >
223
+ <stop offset="0" stop-color="#ff8e00" />
224
+ <stop offset="0.04" stop-color="#ff8e00" stop-opacity="0.86" />
225
+ <stop offset="0.08" stop-color="#ff8e00" stop-opacity="0.73" />
226
+ <stop offset="0.13" stop-color="#ff8e00" stop-opacity="0.63" />
227
+ <stop offset="0.18" stop-color="#ff8e00" stop-opacity="0.56" />
228
+ <stop offset="0.23" stop-color="#ff8e00" stop-opacity="0.51" />
229
+ <stop offset="0.28" stop-color="#ff8e00" stop-opacity="0.5" />
230
+ <stop offset="0.39" stop-color="#ff8e00" stop-opacity="0.48" />
231
+ <stop offset="0.52" stop-color="#ff8e00" stop-opacity="0.42" />
232
+ <stop offset="0.68" stop-color="#ff8e00" stop-opacity="0.31" />
233
+ <stop offset="0.84" stop-color="#ff8e00" stop-opacity="0.17" />
234
+ <stop offset="1" stop-color="#ff8e00" stop-opacity="0" />
235
+ </linearGradient>
236
+ </defs>
237
+ <title>Firefox_Logo,_2017_2</title>
238
+ <g id="Layer_2" data-name="Layer 2">
239
+ <g id="Firefox_-_Large" data-name="Firefox - Large">
240
+ <path
241
+ d="M770.28,91.56c-24,27.88-35.1,90.64-10.82,154.26s61.5,49.8,84.7,114.67c30.62,85.6,16.37,200.59,16.37,200.59s36.81,106.61,62.47-6.63C979.79,341.74,770.28,143.94,770.28,91.56Z"
242
+ style="fill: url(#radial-gradient)"
243
+ />
244
+ <path
245
+ id="_Path_"
246
+ data-name=" Path "
247
+ d="M478.07,974.64c245.24,0,443.9-199.74,443.9-446s-198.66-446-443.66-446S34.65,282.32,34.65,528.61C34.18,775.14,233.07,974.64,478.07,974.64Z"
248
+ style="fill: url(#radial-gradient-2)"
249
+ />
250
+ <path
251
+ d="M810.67,803.64a246.89,246.89,0,0,1-30.12,18.18,704,704,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.6,257.6,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158-25.29,54.51-90.07,108.39-150.72,113.9-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a85.52,85.52,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.2,81.2,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.83,32.83,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.36,67.36,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a58,58,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.73,10.73,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.33,44.33,0,0,0,.27-5.4,84.48,84.48,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.36,334.36,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A483.61,483.61,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z"
252
+ style="fill: url(#linear-gradient)"
253
+ />
254
+ <path
255
+ d="M711.1,866.71c162.87-18.86,235-186.7,142.38-190C769.85,674,634,875.61,711.1,866.71Z"
256
+ style="fill: url(#radial-gradient-3)"
257
+ />
258
+ <path
259
+ d="M865.21,642.42C977.26,577.21,948,436.34,948,436.34s-43.25,50.24-72.62,130.32C846.4,646,797.84,681.81,865.21,642.42Z"
260
+ style="fill: url(#radial-gradient-4)"
261
+ />
262
+ <path
263
+ d="M509.47,950.06C665.7,999.91,800,876.84,717.21,835.74,642,798.68,435.32,926.49,509.47,950.06Z"
264
+ style="fill: url(#radial-gradient-5)"
265
+ />
266
+ <path
267
+ d="M876.85,702.23c3.8-5.36,8.94-22.53,13.48-30.21,27.58-44.52,27.78-80,27.78-80.84,16.66-83.22,15.15-117.2,4.9-180-8.25-50.6-44.32-123.09-75.57-158-32.2-36-9.51-24.25-40.69-50.52-27.33-30.29-53.82-60.29-68.25-72.36C634.22,43.09,636.57,24.58,638.58,21.42c-.34.37-.84.92-1.47,1.64C635.87,18.14,635,14,635,14s-57,57-69,152c-7.83,62,15.38,126.68,49,168a381.37,381.37,0,0,0,59,58h0c25.4,36.48,39.38,81.49,39.38,129.91,0,121.24-98.34,219.53-219.65,219.53a220.45,220.45,0,0,1-49.13-5.52c-57.24-10.92-90.3-39.8-106.78-59.41-9.45-11.23-13.46-19.42-13.46-19.42,51.28,18.37,108,14.53,142.47-4.52,34.75-19.26,55.77-33.55,72.84-27.92,16.82,5.61,30.21-10.67,18.2-27.54-11.77-16.85-42.4-41-87.88-34.29-34.79,5.07-66.66,29.76-112.24,5.84a96.32,96.32,0,0,1-8.55-5c-3-1.77,9.77,2.69,6.79.68-8.87-4.32-24.57-13.73-28.64-17.07-.68-.56,6.88,2.16,6.2,1.6-42.62-31.45-37.3-52.69-36-66,1.07-10.66,8.81-24.32,21.86-29.86,6.3,3.08,10.23,5.43,10.23,5.43s-2.69-4.92-4.14-7.51c.51-.19,1-.15,1.5-.34,5.16,2.23,16.58,8,22.59,11.57,7.83,5,10.32,9.36,10.32,9.36s2.06-1,.54-5.33c-.56-1.77-2.93-7.39-10.68-13.07h.48a90.85,90.85,0,0,1,13.13,8.17c2.19-7.12,6.12-14.56,5.25-27.86-.53-9.35-.28-11.78-2.12-15.39-1.65-3.1.92-4.31,3.78-1.09a30,30,0,0,0-2.44-7.34v-.24c3.57-11.14,75.53-40.12,80.77-43.51a70.31,70.31,0,0,0,21.17-20.63c4-5.72,7-13.73,7.75-25.89.25-5.48-1.44-9.82-20.5-14-11.44-2.49-29.14-4.91-56.43-7.47-19.9-1.76-31.58-14.68-38.21-26.6-1.21-2.57-2.45-4.9-3.68-7.22a53.11,53.11,0,0,1-2.83-8.36,158.47,158.47,0,0,1,61.28-76.06c1.6-1.31-6.4.33-4.8-1,1.87-1.52,14.06-5.93,16.37-6.92,2.81-1.19-12-6.84-25.16-5.47-13.36,1.35-16.19,2.78-23.32,5.49,3-2.64,12.37-6.1,10.16-6.08-14.4,2-32.3,9.48-47.6,18a9.68,9.68,0,0,1,.92-4.31c-7.13,2.71-24.64,13.67-29.73,23a39.49,39.49,0,0,0,.29-5.35,88.68,88.68,0,0,0-14.6,13.7l-.27.22C258.14,196,221.75,195,191,201.72c-6.74-6.06-17.57-15.23-32.89-45.4-1-1.82-1.6,3.75-2.4,2-6-13.81-9.55-36.44-9-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.87-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.37,12.62-1.38,4-3.32,6.27-4.56,11.29l-.29.46c-.1-1.48.37-6.08,0-5.14A236.1,236.1,0,0,0,95.34,186c-5.49,18-11.88,42.61-12.89,74.57-.24,2.42,0,5.14-.25,7.32-13,14.83-21.86,27.39-25.2,33.91-16.81,26-35.33,66.44-53.29,130.46a319.14,319.14,0,0,1,28.54-50C17.32,416.25,2.89,469.62,0,551.8a438.52,438.52,0,0,1,13.87-50.24C11.29,556.36,17.68,624.3,52.32,701c20.57,45,67.92,136.6,183.62,208h0s39.36,29.3,107,51.26c5,1.81,10.06,3.6,15.23,5.33q-2.43-1-4.71-2A484.88,484.88,0,0,0,492.27,984c175.18.15,226.85-70.2,226.85-70.2l-.51.38q3.71-3.49,7.14-7.26c-27.64,26.08-90.75,27.84-114.3,26,40.22-11.81,66.69-21.81,118.17-41.52q9-3.36,18.48-7.64l2-.94c1.25-.58,2.49-1.13,3.75-1.74a349.21,349.21,0,0,0,70.26-44c51.7-41.3,63-81.56,68.83-108.1-.82,2.54-3.37,8.47-5.17,12.32-13.31,28.48-42.84,46-74.91,61a689,689,0,0,0,42.38-62.44C865.77,729.39,869,713.15,876.85,702.23Z"
268
+ style="fill: url(#radial-gradient-6)"
269
+ />
270
+ <path
271
+ d="M813.92,801c21.08-23.24,40-49.82,54.35-80,36.9-77.58,94-206.58,49-341.31C881.77,273.22,833,215,771.11,158.12,670.56,65.76,642.48,24.52,642.48,0c0,0-116.09,129.41-65.74,264.38s153.46,130,221.68,270.87c80.27,165.74-65,346.61-185,397.24,7.35-1.63,267-60.38,280.61-208.88C893.68,726.34,887.83,767.41,813.92,801Z"
272
+ style="fill: url(#radial-gradient-7)"
273
+ />
274
+ <path
275
+ d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z"
276
+ style="fill: url(#linear-gradient-2)"
277
+ />
278
+ <path
279
+ d="M158.31,156.47c-1-1.82-1.6,3.75-2.4,2-6-13.81-9.58-36.2-8.72-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.86-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.35,12.38-1.65,4.24-3.35,6.52-4.61,11.77-.39,1.43.39-6.32.05-5.38C84.72,201.68,80.19,271,82.69,268,133.17,214.14,191,201.36,191,201.36c-6.15-4.53-19.53-17.63-32.7-44.89Z"
280
+ style="fill: url(#linear-gradient-3)"
281
+ />
282
+ <path
283
+ d="M349.84,720.1c-69.72-29.77-149-71.75-146-167.14C207.92,427.35,321,452.18,321,452.18c-4.27,1-15.68,9.16-19.72,17.82-4.27,10.83-12.07,35.28,11.55,60.9,37.09,40.19-76.2,95.36,98.66,199.57,4.41,2.4-41-1.43-61.64-10.36Z"
284
+ style="fill: url(#linear-gradient-4)"
285
+ />
286
+ <path
287
+ d="M325.07,657.5c49.44,17.21,107,14.19,141.52-4.86,23.09-12.85,52.7-33.43,70.92-28.35-15.78-6.24-27.73-9.15-42.1-9.86-2.45,0-5.38,0-8-.32a136.24,136.24,0,0,0-15.76.86c-8.9.82-18.77,6.43-27.74,5.53-.48,0,8.7-3.77,8-3.61-4.75,1-9.92,1.21-15.37,1.88-3.47.39-6.45.82-9.89,1-103,8.73-190-55.81-190-55.81-7.41,25,33.17,74.3,88.52,93.57Z"
288
+ style="fill: url(#linear-gradient-5)"
289
+ />
290
+ <path
291
+ d="M813.74,801.65c104.16-102.27,156.86-226.58,134.58-366,0,0,8.9,71.5-24.85,144.63,16.21-71.39,18.1-160.11-25-252C841,205.64,746.45,141.11,710.35,114.19,655.66,73.4,633,31.87,632.57,23.3c-16.34,33.48-65.77,148.2-5.31,247,56.64,92.56,145.86,120,208.33,205C950.67,631.67,813.74,801.65,813.74,801.65Z"
292
+ style="fill: url(#linear-gradient-6)"
293
+ />
294
+ <path
295
+ d="M798.81,535.55C762.41,460.35,717,427.55,674,392c5,7,6.23,9.47,9,14,37.83,40.32,93.61,138.66,53.11,262.11C659.88,900.48,355,791.06,323,760.32,335.93,894.81,561,959.16,707.6,872,791,793,858.47,658.79,798.81,535.55Z"
296
+ style="fill: url(#linear-gradient-7)"
297
+ />
298
+ </g>
299
+ </g>
300
+ </svg>
Binary file
Binary file
Binary file
@@ -0,0 +1,72 @@
1
+ <div id="spa-loader">
2
+ <svg class="spinner" viewBox="0 0 50 50">
3
+ <circle
4
+ class="path"
5
+ cx="25"
6
+ cy="25"
7
+ r="20"
8
+ fill="none"
9
+ stroke-width="3"
10
+ ></circle>
11
+ </svg>
12
+ </div>
13
+
14
+ <style>
15
+ :root {
16
+ --ui-bg: #121212;
17
+ --loader-fill-color: #ffffff;
18
+ }
19
+
20
+ @media (prefers-color-scheme: light) {
21
+ :root {
22
+ --ui-bg: #f7f7f7;
23
+ --loader-fill-color: #0082e6;
24
+ }
25
+ }
26
+
27
+ #spa-loader {
28
+ position: fixed;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ background: var(--ui-bg);
34
+ display: flex;
35
+ justify-content: center;
36
+ align-items: center;
37
+ z-index: 9999;
38
+ }
39
+
40
+ .spinner {
41
+ width: 35px;
42
+ height: 35px;
43
+ animation: rotate 1.5s linear infinite;
44
+ }
45
+
46
+ .path {
47
+ stroke: var(--loader-fill-color);
48
+ stroke-linecap: round;
49
+ animation: dash 1.2s ease-in-out infinite;
50
+ }
51
+
52
+ @keyframes rotate {
53
+ 100% {
54
+ transform: rotate(360deg);
55
+ }
56
+ }
57
+
58
+ @keyframes dash {
59
+ 0% {
60
+ stroke-dasharray: 1, 125;
61
+ stroke-dashoffset: 0;
62
+ }
63
+ 50% {
64
+ stroke-dasharray: 75, 125;
65
+ stroke-dashoffset: -30;
66
+ }
67
+ 100% {
68
+ stroke-dasharray: 75, 125;
69
+ stroke-dashoffset: -105;
70
+ }
71
+ }
72
+ </style>
@@ -0,0 +1,9 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { waitForNuxtApp } from './utils';
3
+
4
+ test('MyelophoneWelcome is rendered', async ({ page }) => {
5
+ await waitForNuxtApp(page, 'http://localhost:3000');
6
+
7
+ const body = page.locator('body');
8
+ await expect(body).toContainText('MyelophOne Nuxt', { timeout: 5000 });
9
+ });
package/tests/utils.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { Page } from '@playwright/test';
2
+
3
+ export async function waitForNuxtApp(
4
+ page: Page,
5
+ url: string,
6
+ selector = '#__nuxt',
7
+ attempts = 15,
8
+ delayMs = 3000,
9
+ ) {
10
+ for (let i = 1; i <= attempts; i++) {
11
+ try {
12
+ await page.goto(url, { waitUntil: 'domcontentloaded' });
13
+ const visible = await page.locator(selector).evaluate((el) => {
14
+ const rect = el.getBoundingClientRect();
15
+ return rect.width > 0 && rect.height > 0;
16
+ });
17
+ if (visible) return;
18
+ } catch {}
19
+ console.log(`Attempt ${i}/${attempts} failed, waiting ${delayMs}ms...`);
20
+ await page.waitForTimeout(delayMs);
21
+ }
22
+ throw new Error(`Nuxt ${url} is not ready after ${attempts} attempts`);
23
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./.nuxt/tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "lib": ["ESNext", "DOM"],
7
+ "moduleResolution": "Bundler",
8
+ "esModuleInterop": true,
9
+ "strict": true,
10
+ "resolveJsonModule": true,
11
+ "allowSyntheticDefaultImports": true,
12
+ "types": ["@nuxt/schema"],
13
+ },
14
+ "exclude": ["dist", "node_modules", ".output", "playground"],
15
+ "include": [".nuxt/**/*.d.ts", "**/*.ts", "**/*.vue"],
16
+ }
@@ -0,0 +1,190 @@
1
+ import type { Plugin } from 'vite';
2
+ import postcss, { type AcceptedPlugin } from 'postcss';
3
+ import { PurgeCSS } from 'purgecss';
4
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
5
+ import { basename, join } from 'node:path';
6
+ import { useLogger } from '@nuxt/kit';
7
+
8
+ interface CleanCssPluginOptions {
9
+ postcssPlugins?: AcceptedPlugin[];
10
+ purgeTailwindUtilities?: boolean;
11
+ }
12
+
13
+ export function cleanEmptyCssPlugin(
14
+ options: CleanCssPluginOptions = {},
15
+ ): Plugin {
16
+ const EMPTY_VAR_REGEX = /--[a-zA-Z0-9-_]+\s*:\s*;/g;
17
+ const sourceFiles = new Set<string>();
18
+ const reports: string[] = [];
19
+ const SOURCE_FILE_RE = /\.(?:[cm]?[jt]sx?|vue|json)$/i;
20
+ const extractTailwindCandidates = (content: string) =>
21
+ content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || [];
22
+ const logger = useLogger('@myelophone/nuxt');
23
+
24
+ function removeUnresolved(css: string) {
25
+ const root = postcss.parse(css);
26
+ root.walkRules((rule) => {
27
+ if (/\\\$/.test(rule.selector)) {
28
+ rule.remove();
29
+ }
30
+ });
31
+ return root.toString();
32
+ }
33
+
34
+ async function purgeTailwindUtilities(css: string, content: string) {
35
+ const root = postcss.parse(css);
36
+ const utilityLayers: postcss.AtRule[] = [];
37
+
38
+ root.walkAtRules('layer', (rule) => {
39
+ if (rule.params.trim() === 'utilities') {
40
+ utilityLayers.push(rule);
41
+ }
42
+ });
43
+
44
+ if (utilityLayers.length === 0) {
45
+ return { css, before: 0, after: 0 };
46
+ }
47
+
48
+ const before = utilityLayers.reduce(
49
+ (size, layer) => size + Buffer.byteLength(layer.toString(), 'utf8'),
50
+ 0,
51
+ );
52
+
53
+ const result = await new PurgeCSS().purge({
54
+ content: [{ raw: content, extension: 'js' }],
55
+ css: utilityLayers.map((layer) => ({ raw: layer.toString() })),
56
+ defaultExtractor: extractTailwindCandidates,
57
+ });
58
+
59
+ for (const [index, layer] of utilityLayers.entries()) {
60
+ const purgedLayer = postcss.parse(result[index]?.css || '');
61
+ let replacement: postcss.AtRule | undefined;
62
+
63
+ purgedLayer.walkAtRules('layer', (rule) => {
64
+ if (!replacement && rule.params.trim() === 'utilities') {
65
+ replacement = rule;
66
+ }
67
+ });
68
+
69
+ if (replacement) {
70
+ layer.replaceWith(replacement);
71
+ } else {
72
+ layer.remove();
73
+ }
74
+ }
75
+
76
+ const output = root.toString();
77
+ const outputRoot = postcss.parse(output);
78
+ let after = 0;
79
+ outputRoot.walkAtRules('layer', (rule) => {
80
+ if (rule.params.trim() === 'utilities') {
81
+ after += Buffer.byteLength(rule.toString(), 'utf8');
82
+ }
83
+ });
84
+
85
+ return {
86
+ css: output,
87
+ before,
88
+ after,
89
+ };
90
+ }
91
+
92
+ async function collectFiles(directory: string): Promise<string[]> {
93
+ const entries = await readdir(directory, { withFileTypes: true });
94
+ const files = await Promise.all(
95
+ entries.map(async (entry) => {
96
+ const file = join(directory, entry.name);
97
+ return entry.isDirectory() ? collectFiles(file) : [file];
98
+ }),
99
+ );
100
+
101
+ return files.flat();
102
+ }
103
+
104
+ return {
105
+ name: 'myelophone-vite-clean-css',
106
+ apply: 'build',
107
+ generateBundle(_, bundle) {
108
+ sourceFiles.clear();
109
+
110
+ for (const output of Object.values(bundle)) {
111
+ if (output.type !== 'chunk') {
112
+ continue;
113
+ }
114
+
115
+ for (const id of Object.keys(output.modules)) {
116
+ const sourceFile = id.split('?', 1)[0];
117
+ if (sourceFile && SOURCE_FILE_RE.test(sourceFile)) {
118
+ sourceFiles.add(sourceFile);
119
+ }
120
+ }
121
+ }
122
+ },
123
+ async writeBundle(outputOptions) {
124
+ reports.length = 0;
125
+
126
+ if (!outputOptions.dir) {
127
+ return;
128
+ }
129
+
130
+ const files = await collectFiles(outputOptions.dir);
131
+ const sourceContent = (
132
+ await Promise.all(
133
+ [...sourceFiles].map(async (file) => {
134
+ try {
135
+ return await readFile(file, 'utf8');
136
+ } catch {
137
+ return '';
138
+ }
139
+ }),
140
+ )
141
+ ).filter(Boolean);
142
+
143
+ for (const file of files.filter((file) => file.endsWith('.css'))) {
144
+ let css = await readFile(file, 'utf8');
145
+ const sizeBefore = Buffer.byteLength(css, 'utf8');
146
+ let utilitiesBefore = 0;
147
+ let utilitiesAfter = 0;
148
+
149
+ css = css.replace(EMPTY_VAR_REGEX, '');
150
+ css = removeUnresolved(css);
151
+
152
+ if (
153
+ options.purgeTailwindUtilities &&
154
+ sourceContent.length > 0
155
+ ) {
156
+ const purgeResult = await purgeTailwindUtilities(
157
+ css,
158
+ sourceContent.join('\n'),
159
+ );
160
+ css = purgeResult.css;
161
+ utilitiesBefore = purgeResult.before;
162
+ utilitiesAfter = purgeResult.after;
163
+ }
164
+
165
+ if (
166
+ options.postcssPlugins &&
167
+ options.postcssPlugins.length > 0
168
+ ) {
169
+ const result = await postcss(
170
+ options.postcssPlugins,
171
+ ).process(css, {
172
+ from: undefined,
173
+ });
174
+ css = result.css;
175
+ }
176
+
177
+ await writeFile(file, css);
178
+ reports.push(
179
+ `.../${basename(file)} (${sizeBefore} B → ${Buffer.byteLength(css, 'utf8')} B)`,
180
+ );
181
+ }
182
+ },
183
+ closeBundle() {
184
+ logger.info('Purging css...');
185
+ for (const report of reports) {
186
+ logger.info(report);
187
+ }
188
+ },
189
+ };
190
+ }
@@ -0,0 +1,33 @@
1
+ import type { PluginCreator, Rule } from 'postcss';
2
+
3
+ const dvUnitsMap: Record<string, { prop: string; value: string }> = {
4
+ 'h-screen': { prop: 'height', value: '100dvh' },
5
+ 'min-h-screen': { prop: 'min-height', value: '100dvh' },
6
+ 'w-screen': { prop: 'width', value: '100dvw' },
7
+ 'min-w-screen': { prop: 'min-width', value: '100dvw' },
8
+ };
9
+
10
+ const creator: PluginCreator<{}> = () => ({
11
+ postcssPlugin: 'postcss-add-dv-units-inline',
12
+
13
+ Rule(rule: Rule) {
14
+ const selector = rule.selector.trim();
15
+ const clsName = selector.startsWith('.') ? selector.slice(1) : '';
16
+ const config = dvUnitsMap[clsName];
17
+ if (!config) return;
18
+
19
+ const { prop, value } = config;
20
+
21
+ let exists = false;
22
+ rule.walkDecls(prop, (decl) => {
23
+ if (decl.value === value) exists = true;
24
+ });
25
+
26
+ if (!exists) {
27
+ rule.append({ prop, value });
28
+ }
29
+ },
30
+ });
31
+
32
+ creator.postcss = true;
33
+ export default creator;