@maz-ui/vite-plugin 0.0.0-stage → 5.7.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +45 -2
- package/agent-docs/README.md +12 -0
- package/agent-docs/ecosystem/vite-plugin.md +167 -0
- package/agent-docs/manifest.json +1 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.mjs +220 -0
- package/dist/manifest.d.mts +26 -0
- package/dist/manifest.d.ts +26 -0
- package/dist/manifest.mjs +5 -0
- package/dist/shared/vite-plugin.Ce6HCqgJ.mjs +200 -0
- package/package.json +100 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 LouisMazel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @maz-ui/vite-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vite plugins for [Maz-UI](https://maz-ui.com). The first one, `MazUiPurgeCss`, removes from the CSS of maz-ui (`maz-ui/style.css`, `maz-ui/utilities.css`) the rules of the components, plugins, directives and utilities your app does not use. It runs at build time only.
|
|
4
|
+
|
|
5
|
+
With `@maz-ui/nuxt`, the module adds it for you (`css.purge`).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D @maz-ui/vite-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires `vite >=6 <9` and `maz-ui >= 5.7.0`.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { MazUiPurgeCss } from '@maz-ui/vite-plugin'
|
|
19
|
+
// vite.config.ts
|
|
20
|
+
import vue from '@vitejs/plugin-vue'
|
|
21
|
+
import { defineConfig } from 'vite'
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
plugins: [
|
|
25
|
+
vue(),
|
|
26
|
+
MazUiPurgeCss()
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
| Option | Description |
|
|
34
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `content` | Globs of the files that use maz-ui, relative to the project root (a package of your monorepo, a dependency in `node_modules`) |
|
|
36
|
+
| `entries` | Entries of maz-ui to keep: `MazBtn`, `vTooltip`, `plugins/toast`... |
|
|
37
|
+
| `safelist` | Classes to keep: `maz:` classes or public utilities (`text-title-1`, `/^rich-text/`) |
|
|
38
|
+
| `report` | Print the CSS saved by the purge (default: `true`) |
|
|
39
|
+
|
|
40
|
+
## Documentation
|
|
41
|
+
|
|
42
|
+
https://maz-ui.com/ecosystem/vite-plugin
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @maz-ui/vite-plugin documentation for AI agents
|
|
2
|
+
|
|
3
|
+
> Vite plugin that removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities the app does not use (build only).
|
|
4
|
+
|
|
5
|
+
These files document the **installed** version of @maz-ui/vite-plugin. Prefer them over what you remember from training data: APIs change between versions.
|
|
6
|
+
|
|
7
|
+
- Rules for the whole ecosystem: `node_modules/maz-ui/agent-docs/rules.md` when maz-ui is installed.
|
|
8
|
+
- `manifest.json` describes every page (symbols, imports, aliases).
|
|
9
|
+
|
|
10
|
+
## Ecosystem packages
|
|
11
|
+
|
|
12
|
+
- [Vite plugin](ecosystem/vite-plugin.md): @maz-ui/vite-plugin removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities your app does not use, at build time.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Vite plugin"
|
|
3
|
+
description: "@maz-ui/vite-plugin removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities your app does not use, at build time."
|
|
4
|
+
package: "@maz-ui/vite-plugin"
|
|
5
|
+
url: "https://maz-ui.com/ecosystem/vite-plugin"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Vite plugin
|
|
9
|
+
|
|
10
|
+
> @maz-ui/vite-plugin removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities your app does not use, at build time.
|
|
11
|
+
|
|
12
|
+
- Package: `@maz-ui/vite-plugin`
|
|
13
|
+
- Import: `import { MazUiPurgeCss } from '@maz-ui/vite-plugin'`
|
|
14
|
+
- Web: https://maz-ui.com/ecosystem/vite-plugin
|
|
15
|
+
|
|
16
|
+
`maz-ui/style.css` holds the styles of every component. Most apps use a few of them: the plugin keeps the rules of what your code uses and removes the rest, in the CSS of your production build. It is the first plugin of the package, others may come.
|
|
17
|
+
|
|
18
|
+
**Tip: Nuxt**
|
|
19
|
+
|
|
20
|
+
With `@maz-ui/nuxt`, you do not install anything: the module adds the plugin for you, see [`css.purge`](https://maz-ui.com/ecosystem/nuxt.md#css-configuration).
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
For Vue apps built with Vite (`vite >=6 <9`):
|
|
25
|
+
|
|
26
|
+
```bash [pnpm]
|
|
27
|
+
pnpm add -D @maz-ui/vite-plugin
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash [npm]
|
|
31
|
+
npm install -D @maz-ui/vite-plugin
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash [yarn]
|
|
35
|
+
yarn add -D @maz-ui/vite-plugin
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It requires `maz-ui >= 5.7.0`, which ships the manifest the plugin reads. Keep both on the same version.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// vite.config.ts
|
|
44
|
+
import vue from '@vitejs/plugin-vue'
|
|
45
|
+
import { MazUiPurgeCss } from '@maz-ui/vite-plugin'
|
|
46
|
+
import { defineConfig } from 'vite'
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
plugins: [
|
|
50
|
+
vue(),
|
|
51
|
+
MazUiPurgeCss(),
|
|
52
|
+
],
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Keep importing `maz-ui/style.css` as usual. The build prints a report:
|
|
57
|
+
|
|
58
|
+
```txt
|
|
59
|
+
[maz-ui:purge-css] 86.9 kB -> 32.8 kB of maz-ui CSS (-62%, 412 unused classes removed)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## How it works
|
|
63
|
+
|
|
64
|
+
1. At the build of maz-ui, `dist/css/purge-manifest.json` lists every class of `style.css`, the public utilities, and for each entry (`components/MazBtn`, `plugins/toast`, `directives/vTooltip`...) the classes it renders, with the entries it imports.
|
|
65
|
+
2. In your build, the plugin scans the source files of the project and finds the entries you use by their names: `MazBtn`, `<maz-btn>`, `LazyMazBtn`, `useToast`, `v-tooltip`, `ToastPlugin`... A namespace import of a barrel (`import * as components from 'maz-ui/components'`) keeps every component. It also finds the public utilities used (`text-title-1`, `padded-container`...).
|
|
66
|
+
3. When Vite transforms a CSS file, the plugin removes the rules of the unused entries and utilities, before the hash of the asset is computed, so the cache stays correct. It also removes the classes of `main.css` that no component renders.
|
|
67
|
+
4. At the end of the build, it checks the final JS and HTML: a purged class found there prints a warning, see Bundle check.
|
|
68
|
+
|
|
69
|
+
The plugin only touches the CSS of maz-ui. The CSS of your own app is never modified.
|
|
70
|
+
|
|
71
|
+
## Gains
|
|
72
|
+
|
|
73
|
+
Measured with `vite build`, the maz-ui preset, on the CSS assets of the app. Every Vue app imports `maz-ui/style.css`.
|
|
74
|
+
|
|
75
|
+
| App | Without purge | With purge | Saved |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| Landing (MazBtn, MazCard, MazBadge, MazAvatar, MazAccordion) | 103.2 kB (15.6 kB gzip) | 49.1 kB (8.8 kB gzip) | -52% |
|
|
78
|
+
| Sign in (MazInput, MazCheckbox, MazBtn, MazCard, MazLink, toast plugin) | 108.3 kB (16.0 kB gzip) | 64.7 kB (10.1 kB gzip) | -40% |
|
|
79
|
+
| Dashboard (MazTable, MazSelect, MazDatePicker, MazDialog, MazSidebar, MazTabs, MazDropdown, MazSwitch, toast, dialog, vTooltip...) | 156.1 kB (24.2 kB gzip) | 119.5 kB (19.4 kB gzip) | -23% |
|
|
80
|
+
| No Tailwind, with `maz-ui/utilities.css` (padded-container, text-display, rich-text, MazBtn, MazCard) | 118.8 kB (16.9 kB gzip) | 50.9 kB (8.4 kB gzip) | -57% |
|
|
81
|
+
| Every component | 212.0 kB (29.8 kB gzip) | 195.7 kB (28.0 kB gzip) | -8% |
|
|
82
|
+
|
|
83
|
+
The `style.css` of maz-ui alone goes from 86.9 kB to 32.8 kB (landing), 43.8 kB (sign in) and 50.3 kB (dashboard).
|
|
84
|
+
|
|
85
|
+
With Nuxt (2 pages using MazBtn, MazCard, MazBadge, LazyMazAccordion, MazInput, MazCheckbox, the toast plugin and vTooltip, `nuxi generate`), the CSS files go from 122.3 kB (21.0 kB gzip) to 78.6 kB (14.8 kB gzip) with the default `css.purge: true`, which is -36%.
|
|
86
|
+
|
|
87
|
+
**Warning: The numbers depend on your app**
|
|
88
|
+
|
|
89
|
+
They come from the demo apps above and this setup (preset, Tailwind or not, components used). The more components you use, the smaller the gain.
|
|
90
|
+
|
|
91
|
+
## Options
|
|
92
|
+
|
|
93
|
+
All options are optional.
|
|
94
|
+
|
|
95
|
+
| Option | Type | Description |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `content` | `string[]` | Globs of the files that use maz-ui, relative to the root of the project. Added to the defaults |
|
|
98
|
+
| `entries` | `string[]` | Entries of maz-ui to keep even if the plugin does not find them in the code: a component (`MazBtn`), a directive (`vTooltip`), a plugin (`plugins/toast`) |
|
|
99
|
+
| `safelist` | `(string \| RegExp)[]` | Classes to keep: `maz:` classes of the components or public utilities (`text-title-1`, `/^rich-text/`) |
|
|
100
|
+
| `report` | `boolean` | Print the CSS saved by the purge (default: `true`) |
|
|
101
|
+
|
|
102
|
+
By default, the plugin scans every source file of the project (`vue`, `js`, `jsx`, `ts`, `tsx`, `html`, `md`, `mdx`, `astro`, `svelte`...), and ignores `node_modules`, `dist`, `.output`, `.nuxt`, `coverage` and `.d.ts` files.
|
|
103
|
+
|
|
104
|
+
### Monorepo
|
|
105
|
+
|
|
106
|
+
When your app uses components written in another package of the monorepo, add its sources:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
MazUiPurgeCss({
|
|
110
|
+
content: ['../../packages/ui/src/**/*.vue'],
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Dependency built on maz-ui
|
|
115
|
+
|
|
116
|
+
The default scan ignores `node_modules`, the globs of `content` do not: add the files of a dependency that renders maz-ui components (a design system of your company), or list the entries it uses with `entries`:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
MazUiPurgeCss({
|
|
120
|
+
content: ['node_modules/@acme/ui/dist/**/*.js'],
|
|
121
|
+
// or
|
|
122
|
+
entries: ['MazBtn', 'MazDialog', 'plugins/toast', 'vTooltip'],
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Names built at runtime
|
|
127
|
+
|
|
128
|
+
A component resolved from a dynamic name, or a class that only appears in HTML loaded at runtime (a CMS, an API), is not found in the source. Keep it explicitly:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
MazUiPurgeCss({
|
|
132
|
+
entries: ['MazBtn', 'MazCard'],
|
|
133
|
+
safelist: ['text-title-1', /^rich-text/],
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Bundle check
|
|
138
|
+
|
|
139
|
+
At the end of the build, the plugin looks for the purged classes in the final JS and HTML. If one is found, a component of maz-ui is used by a file the plugin does not scan (a dependency, a package of your monorepo), and the build warns:
|
|
140
|
+
|
|
141
|
+
```txt
|
|
142
|
+
[maz-ui:purge-css] 3 purged classes are in the bundle: rounded-lg, flex, p-4
|
|
143
|
+
A component of maz-ui is probably used by a file the plugin does not scan (a dependency, a package of your monorepo).
|
|
144
|
+
Add its files to the `content` option, or the component to `entries`.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Fix it with `content` (the files that use the component) or `entries`, then rebuild.
|
|
148
|
+
|
|
149
|
+
## Projects without Tailwind
|
|
150
|
+
|
|
151
|
+
`maz-ui/utilities.css` contains the public utilities of maz-ui without prefix: the `text-*` and `type-*` type scale, `rich-text`, `padded-container`, `flex-center`, `glass`, `scrollbar-themed`, `disabled-*`, `cursor-interactive`, `cap-f` and `z-default-backdrop`. It weighs 19.7 kB (2.6 kB gzip) and the plugin purges it like `style.css`.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import 'maz-ui/style.css'
|
|
155
|
+
import 'maz-ui/utilities.css'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Warning: With Tailwind CSS**
|
|
159
|
+
|
|
160
|
+
Keep `@import 'maz-ui/tailwindcss/utilities.css'` in your Tailwind entry and do not import `maz-ui/utilities.css`: you would get duplicates, and no variants (`hover:`, `tab-m:`...). See [Tailwind CSS](https://maz-ui.com/guide/tailwind.md).
|
|
161
|
+
|
|
162
|
+
## Limitations
|
|
163
|
+
|
|
164
|
+
- Vite only for now (no webpack, Rollup alone or other bundlers).
|
|
165
|
+
- Build only: the dev server keeps the whole CSS.
|
|
166
|
+
- Entries are found by name in the files of the project: names built at runtime need `entries` or `safelist`.
|
|
167
|
+
- Only the CSS of maz-ui is purged, not the CSS of your app.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"schemaVersion":2,"package":"@maz-ui/vite-plugin","version":"5.7.0-beta.2","description":"Vite plugin that removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities the app does not use (build only).","homepage":"https://maz-ui.com/ecosystem/vite-plugin","group":"ecosystem","docs":[{"id":"ecosystem/vite-plugin","category":"ecosystem","slug":"vite-plugin","title":"Vite plugin","description":"@maz-ui/vite-plugin removes from the CSS of maz-ui the rules of the components, plugins, directives and utilities your app does not use, at build time.","package":"@maz-ui/vite-plugin","file":"ecosystem/vite-plugin.md","url":"https://maz-ui.com/ecosystem/vite-plugin","symbols":["MazUiPurgeCss"],"imports":["import { MazUiPurgeCss } from '@maz-ui/vite-plugin'"],"aliases":["vite plugin","purge css","purgecss","unused css","css size","reduce css","tree shaking css","bundle size","smaller css","mazuipurgecss"],"headings":["Installation","Usage","How it works","Gains","Options","Monorepo","Dependency built on maz-ui","Names built at runtime","Bundle check","Projects without Tailwind","Limitations"],"related":["ecosystem/nuxt","guide/vue"],"chars":7695}],"components":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface MazUiPurgeCssOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Globs of the files that use maz-ui, relative to the root of the project. The defaults scan every source file of
|
|
6
|
+
* the project (`node_modules`, `dist` and the build folders excluded). Add the files of a package of your monorepo,
|
|
7
|
+
* or of a dependency built on maz-ui: these globs can target `node_modules`.
|
|
8
|
+
*/
|
|
9
|
+
content?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Entries of maz-ui to keep even if the plugin does not find them in the code: a component (`MazBtn`), a directive
|
|
12
|
+
* (`vTooltip`), a plugin (`plugins/toast`)...
|
|
13
|
+
*/
|
|
14
|
+
entries?: string[];
|
|
15
|
+
/** Classes to keep, `maz:` classes of the components or public utilities (`text-title-1`, `/^rich-text/`) */
|
|
16
|
+
safelist?: (string | RegExp)[];
|
|
17
|
+
/**
|
|
18
|
+
* Print the CSS saved by the purge
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
report?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Remove from the CSS of maz-ui (`maz-ui/style.css`, `maz-ui/utilities.css`) the rules of the components, directives,
|
|
25
|
+
* plugins and utilities the app does not use. It runs at build time only: the dev server keeps the whole CSS.
|
|
26
|
+
*/
|
|
27
|
+
declare function MazUiPurgeCss(options?: MazUiPurgeCssOptions): Plugin;
|
|
28
|
+
|
|
29
|
+
export { MazUiPurgeCss };
|
|
30
|
+
export type { MazUiPurgeCssOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface MazUiPurgeCssOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Globs of the files that use maz-ui, relative to the root of the project. The defaults scan every source file of
|
|
6
|
+
* the project (`node_modules`, `dist` and the build folders excluded). Add the files of a package of your monorepo,
|
|
7
|
+
* or of a dependency built on maz-ui: these globs can target `node_modules`.
|
|
8
|
+
*/
|
|
9
|
+
content?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Entries of maz-ui to keep even if the plugin does not find them in the code: a component (`MazBtn`), a directive
|
|
12
|
+
* (`vTooltip`), a plugin (`plugins/toast`)...
|
|
13
|
+
*/
|
|
14
|
+
entries?: string[];
|
|
15
|
+
/** Classes to keep, `maz:` classes of the components or public utilities (`text-title-1`, `/^rich-text/`) */
|
|
16
|
+
safelist?: (string | RegExp)[];
|
|
17
|
+
/**
|
|
18
|
+
* Print the CSS saved by the purge
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
report?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Remove from the CSS of maz-ui (`maz-ui/style.css`, `maz-ui/utilities.css`) the rules of the components, directives,
|
|
25
|
+
* plugins and utilities the app does not use. It runs at build time only: the dev server keeps the whole CSS.
|
|
26
|
+
*/
|
|
27
|
+
declare function MazUiPurgeCss(options?: MazUiPurgeCssOptions): Plugin;
|
|
28
|
+
|
|
29
|
+
export { MazUiPurgeCss };
|
|
30
|
+
export type { MazUiPurgeCssOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { isAbsolute, relative } from 'node:path';
|
|
2
|
+
import { s as splitSelectorList, a as getRequiredClasses, c as containsName, b as containsToken, l as loadPurgeManifest } from './shared/vite-plugin.Ce6HCqgJ.mjs';
|
|
3
|
+
import postcss from 'postcss';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { glob } from 'tinyglobby';
|
|
6
|
+
import 'node:fs';
|
|
7
|
+
import 'node:module';
|
|
8
|
+
|
|
9
|
+
const PUBLIC_UTILITIES_LAYER = "maz";
|
|
10
|
+
function isInPublicLayer(rule) {
|
|
11
|
+
let node = rule.parent;
|
|
12
|
+
while (node && node.type !== "root") {
|
|
13
|
+
if (node.type === "atrule" && node.name === "layer" && node.params.trim() === PUBLIC_UTILITIES_LAYER)
|
|
14
|
+
return true;
|
|
15
|
+
node = node.parent;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function isInKeyframes(rule) {
|
|
20
|
+
return rule.parent?.type === "atrule" && /keyframes$/i.test(rule.parent.name);
|
|
21
|
+
}
|
|
22
|
+
function removeEmptyAtRules(container) {
|
|
23
|
+
for (const node of [...container.nodes]) {
|
|
24
|
+
if (node.type !== "atrule" || !node.nodes)
|
|
25
|
+
continue;
|
|
26
|
+
removeEmptyAtRules(node);
|
|
27
|
+
if (node.nodes.length === 0)
|
|
28
|
+
node.remove();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function purgeCss(css, decision) {
|
|
32
|
+
const root = postcss.parse(css);
|
|
33
|
+
const removed = /* @__PURE__ */ new Set();
|
|
34
|
+
let changed = false;
|
|
35
|
+
root.walkRules((rule) => {
|
|
36
|
+
if (isInKeyframes(rule))
|
|
37
|
+
return;
|
|
38
|
+
const inPublicLayer = isInPublicLayer(rule);
|
|
39
|
+
const selectors = splitSelectorList(rule.selector);
|
|
40
|
+
const kept = selectors.filter((selector) => {
|
|
41
|
+
const unused = getRequiredClasses(selector).filter((className) => decision.isManaged(className, inPublicLayer) && !decision.isUsed(className));
|
|
42
|
+
for (const className of unused)
|
|
43
|
+
removed.add(className);
|
|
44
|
+
return unused.length === 0;
|
|
45
|
+
});
|
|
46
|
+
if (kept.length === selectors.length)
|
|
47
|
+
return;
|
|
48
|
+
changed = true;
|
|
49
|
+
if (kept.length === 0)
|
|
50
|
+
rule.remove();
|
|
51
|
+
else
|
|
52
|
+
rule.selector = kept.join(",");
|
|
53
|
+
});
|
|
54
|
+
if (!changed)
|
|
55
|
+
return { css, removed };
|
|
56
|
+
removeEmptyAtRules(root);
|
|
57
|
+
root.raws.semicolon = true;
|
|
58
|
+
return {
|
|
59
|
+
css: root.toString(),
|
|
60
|
+
removed
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const DEFAULT_CONTENT = ["**/*.{vue,js,jsx,mjs,cjs,ts,tsx,mts,cts,html,md,mdx,astro,svelte}"];
|
|
65
|
+
const DEFAULT_IGNORE = [
|
|
66
|
+
"**/node_modules/**",
|
|
67
|
+
"**/dist/**",
|
|
68
|
+
"**/.output/**",
|
|
69
|
+
"**/.nuxt/**",
|
|
70
|
+
"**/.vercel/**",
|
|
71
|
+
"**/.netlify/**",
|
|
72
|
+
"**/.git/**",
|
|
73
|
+
"**/.vite/**",
|
|
74
|
+
"**/.vitepress/cache/**",
|
|
75
|
+
"**/.vitepress/dist/**",
|
|
76
|
+
"**/coverage/**",
|
|
77
|
+
"**/*.d.ts"
|
|
78
|
+
];
|
|
79
|
+
const NAMESPACE_IMPORT = /import\s*\*\s*as\s+[\w$]+\s+from\s*["']maz-ui\/(components|composables|directives|plugins)["']/g;
|
|
80
|
+
async function readContent(root, content, ignore = DEFAULT_IGNORE) {
|
|
81
|
+
if (content.length === 0)
|
|
82
|
+
return "";
|
|
83
|
+
const files = await glob(content, {
|
|
84
|
+
cwd: root,
|
|
85
|
+
absolute: true,
|
|
86
|
+
ignore,
|
|
87
|
+
dot: false
|
|
88
|
+
});
|
|
89
|
+
const sources = await Promise.all(files.map((file) => readFile(file, "utf8").catch(() => "")));
|
|
90
|
+
return sources.join("\n");
|
|
91
|
+
}
|
|
92
|
+
function matchesEntry(key, aliases, wanted) {
|
|
93
|
+
return key === wanted || key.endsWith(`/${wanted}`) || aliases.includes(wanted);
|
|
94
|
+
}
|
|
95
|
+
function resolveUsage(manifest, source, options) {
|
|
96
|
+
const entries = /* @__PURE__ */ new Set();
|
|
97
|
+
const namespaces = new Set([
|
|
98
|
+
...source.matchAll(NAMESPACE_IMPORT)
|
|
99
|
+
].map((match) => match[1]));
|
|
100
|
+
for (const [key, { aliases }] of Object.entries(manifest.entries)) {
|
|
101
|
+
const directory = key.split("/")[0];
|
|
102
|
+
if (namespaces.has(directory) || options.entries.some((wanted) => matchesEntry(key, aliases, wanted)) || aliases.some((alias) => containsName(source, alias))) {
|
|
103
|
+
entries.add(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const classes = /* @__PURE__ */ new Set();
|
|
107
|
+
for (const key of entries) {
|
|
108
|
+
for (const index of manifest.entries[key].classes)
|
|
109
|
+
classes.add(manifest.classes[index]);
|
|
110
|
+
}
|
|
111
|
+
for (const utility of manifest.utilities) {
|
|
112
|
+
if (containsToken(source, utility))
|
|
113
|
+
classes.add(utility);
|
|
114
|
+
}
|
|
115
|
+
const patterns = options.safelist.filter((item) => item instanceof RegExp);
|
|
116
|
+
const names = new Set(options.safelist.filter((item) => typeof item === "string"));
|
|
117
|
+
for (const className of [
|
|
118
|
+
...manifest.classes,
|
|
119
|
+
...manifest.utilities
|
|
120
|
+
]) {
|
|
121
|
+
if (names.has(className) || patterns.some((pattern) => {
|
|
122
|
+
pattern.lastIndex = 0;
|
|
123
|
+
return pattern.test(className);
|
|
124
|
+
})) {
|
|
125
|
+
classes.add(className);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { entries, classes };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const CSS_REQUEST = /\.(?:css|scss|sass|less|styl|stylus|pcss|postcss|sss)(?:$|\?)|[?&]lang\.css/;
|
|
132
|
+
const MANAGED_CLASS_MARKER = "maz\\:";
|
|
133
|
+
const BYTES_PER_KB = 1024;
|
|
134
|
+
function formatSize(bytes) {
|
|
135
|
+
return `${(bytes / BYTES_PER_KB).toFixed(1)} kB`;
|
|
136
|
+
}
|
|
137
|
+
function MazUiPurgeCss(options = {}) {
|
|
138
|
+
const { content = [], entries = [], safelist = [], report = true } = options;
|
|
139
|
+
let config;
|
|
140
|
+
let manifest;
|
|
141
|
+
let usage;
|
|
142
|
+
const removed = /* @__PURE__ */ new Set();
|
|
143
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
144
|
+
let manifestClasses = /* @__PURE__ */ new Set();
|
|
145
|
+
let manifestUtilities = /* @__PURE__ */ new Set();
|
|
146
|
+
function isManaged(className, inPublicLayer) {
|
|
147
|
+
if (className.startsWith("maz:"))
|
|
148
|
+
return manifestClasses.has(className);
|
|
149
|
+
return inPublicLayer && manifestUtilities.has(className);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
name: "maz-ui:purge-css",
|
|
153
|
+
apply: "build",
|
|
154
|
+
configResolved(resolvedConfig) {
|
|
155
|
+
config = resolvedConfig;
|
|
156
|
+
manifest = loadPurgeManifest(config.root);
|
|
157
|
+
if (!manifest) {
|
|
158
|
+
config.logger.warn("[maz-ui:purge-css] maz-ui/purge-manifest.json not found: the CSS of maz-ui is not purged (maz-ui >= 5.7.0 is required)");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
manifestClasses = new Set(manifest.classes);
|
|
162
|
+
manifestUtilities = new Set(manifest.utilities);
|
|
163
|
+
},
|
|
164
|
+
buildStart() {
|
|
165
|
+
if (!manifest)
|
|
166
|
+
return;
|
|
167
|
+
const loadedManifest = manifest;
|
|
168
|
+
const extraContent = content.map((glob) => isAbsolute(glob) ? relative(config.root, glob) : glob);
|
|
169
|
+
usage = Promise.all([
|
|
170
|
+
readContent(config.root, DEFAULT_CONTENT),
|
|
171
|
+
readContent(config.root, extraContent, [])
|
|
172
|
+
]).then((sources) => resolveUsage(loadedManifest, sources.join("\n"), { entries, safelist }));
|
|
173
|
+
},
|
|
174
|
+
async transform(code, id) {
|
|
175
|
+
if (!usage || !CSS_REQUEST.test(id))
|
|
176
|
+
return;
|
|
177
|
+
const hasManagedClasses = code.includes(MANAGED_CLASS_MARKER) || code.includes(`@layer ${PUBLIC_UTILITIES_LAYER}`);
|
|
178
|
+
if (!hasManagedClasses)
|
|
179
|
+
return;
|
|
180
|
+
const { classes } = await usage;
|
|
181
|
+
const result = purgeCss(code, {
|
|
182
|
+
isManaged,
|
|
183
|
+
isUsed: (className) => classes.has(className)
|
|
184
|
+
});
|
|
185
|
+
if (result.css === code)
|
|
186
|
+
return;
|
|
187
|
+
for (const className of result.removed)
|
|
188
|
+
removed.add(className);
|
|
189
|
+
sizes.set(id.split("?")[0], { before: code.length, after: result.css.length });
|
|
190
|
+
return { code: result.css, map: null };
|
|
191
|
+
},
|
|
192
|
+
generateBundle(_, bundle) {
|
|
193
|
+
if (removed.size === 0)
|
|
194
|
+
return;
|
|
195
|
+
const output = Object.values(bundle).map((file) => file.type === "chunk" ? file.code : typeof file.source === "string" ? file.source : "").join("\n");
|
|
196
|
+
const missing = [...removed].filter((className) => containsToken(output, className));
|
|
197
|
+
if (missing.length > 0) {
|
|
198
|
+
config.logger.warn([
|
|
199
|
+
`[maz-ui:purge-css] ${missing.length} purged classes are in the bundle: ${missing.slice(0, 10).join(", ")}${missing.length > 10 ? "..." : ""}`,
|
|
200
|
+
"A component of maz-ui is probably used by a file the plugin does not scan (a dependency, a package of your monorepo).",
|
|
201
|
+
"Add its files to the `content` option, or the component to `entries`."
|
|
202
|
+
].join("\n"));
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
closeBundle() {
|
|
206
|
+
if (!report || sizes.size === 0 || config.build.ssr)
|
|
207
|
+
return;
|
|
208
|
+
const before = [
|
|
209
|
+
...sizes.values()
|
|
210
|
+
].reduce((total, size) => total + size.before, 0);
|
|
211
|
+
const after = [
|
|
212
|
+
...sizes.values()
|
|
213
|
+
].reduce((total, size) => total + size.after, 0);
|
|
214
|
+
const ratio = Math.round((before - after) / before * 100);
|
|
215
|
+
config.logger.info(`[maz-ui:purge-css] ${formatSize(before)} -> ${formatSize(after)} of maz-ui CSS (-${ratio}%, ${removed.size} unused classes removed)`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { MazUiPurgeCss };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare const PURGE_MANIFEST_FILE = "purge-manifest.json";
|
|
2
|
+
interface PurgeManifestEntry {
|
|
3
|
+
/** Names that reveal the entry in the source code: `MazBtn`, `maz-btn`, `useToast`, `v-tooltip`... */
|
|
4
|
+
aliases: string[];
|
|
5
|
+
/** Indexes in `classes` of the classes the entry renders, with the entries it imports */
|
|
6
|
+
classes: number[];
|
|
7
|
+
}
|
|
8
|
+
interface PurgeManifest {
|
|
9
|
+
version: string;
|
|
10
|
+
/** Every `maz:` class of `maz-ui/style.css` */
|
|
11
|
+
classes: string[];
|
|
12
|
+
/** The public utilities of `maz-ui/utilities.css` (`text-title-1`, `padded-container`...) */
|
|
13
|
+
utilities: string[];
|
|
14
|
+
/** Keyed by the path of the entry in `maz-ui` (`components/MazBtn`, `plugins/toast`) */
|
|
15
|
+
entries: Record<string, PurgeManifestEntry>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Write `dist/css/purge-manifest.json` in the build of `maz-ui`: the `maz:` classes of `style.css`, the public
|
|
19
|
+
* utilities of `utilities.css`, and for every entry the classes it renders with the modules it imports.
|
|
20
|
+
*/
|
|
21
|
+
declare function generatePurgeManifest(distDir: string, version: string): PurgeManifest;
|
|
22
|
+
/** Read the manifest of the `maz-ui` installed in the project */
|
|
23
|
+
declare function loadPurgeManifest(root: string): PurgeManifest | undefined;
|
|
24
|
+
|
|
25
|
+
export { PURGE_MANIFEST_FILE, generatePurgeManifest, loadPurgeManifest };
|
|
26
|
+
export type { PurgeManifest, PurgeManifestEntry };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare const PURGE_MANIFEST_FILE = "purge-manifest.json";
|
|
2
|
+
interface PurgeManifestEntry {
|
|
3
|
+
/** Names that reveal the entry in the source code: `MazBtn`, `maz-btn`, `useToast`, `v-tooltip`... */
|
|
4
|
+
aliases: string[];
|
|
5
|
+
/** Indexes in `classes` of the classes the entry renders, with the entries it imports */
|
|
6
|
+
classes: number[];
|
|
7
|
+
}
|
|
8
|
+
interface PurgeManifest {
|
|
9
|
+
version: string;
|
|
10
|
+
/** Every `maz:` class of `maz-ui/style.css` */
|
|
11
|
+
classes: string[];
|
|
12
|
+
/** The public utilities of `maz-ui/utilities.css` (`text-title-1`, `padded-container`...) */
|
|
13
|
+
utilities: string[];
|
|
14
|
+
/** Keyed by the path of the entry in `maz-ui` (`components/MazBtn`, `plugins/toast`) */
|
|
15
|
+
entries: Record<string, PurgeManifestEntry>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Write `dist/css/purge-manifest.json` in the build of `maz-ui`: the `maz:` classes of `style.css`, the public
|
|
19
|
+
* utilities of `utilities.css`, and for every entry the classes it renders with the modules it imports.
|
|
20
|
+
*/
|
|
21
|
+
declare function generatePurgeManifest(distDir: string, version: string): PurgeManifest;
|
|
22
|
+
/** Read the manifest of the `maz-ui` installed in the project */
|
|
23
|
+
declare function loadPurgeManifest(root: string): PurgeManifest | undefined;
|
|
24
|
+
|
|
25
|
+
export { PURGE_MANIFEST_FILE, generatePurgeManifest, loadPurgeManifest };
|
|
26
|
+
export type { PurgeManifest, PurgeManifestEntry };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { join, basename, resolve, dirname } from 'node:path';
|
|
4
|
+
import postcss from 'postcss';
|
|
5
|
+
|
|
6
|
+
const IDENT_CHAR = /[\w-]/;
|
|
7
|
+
const HEX_DIGIT = /[\da-f]/i;
|
|
8
|
+
const WHITESPACE = /\s/;
|
|
9
|
+
function readIdentifier(selector, start) {
|
|
10
|
+
let value = "";
|
|
11
|
+
let index = start;
|
|
12
|
+
while (index < selector.length) {
|
|
13
|
+
const char = selector[index];
|
|
14
|
+
if (char === "\\") {
|
|
15
|
+
let hex = "";
|
|
16
|
+
let cursor = index + 1;
|
|
17
|
+
while (hex.length < 6 && cursor < selector.length && HEX_DIGIT.test(selector[cursor])) {
|
|
18
|
+
hex += selector[cursor];
|
|
19
|
+
cursor++;
|
|
20
|
+
}
|
|
21
|
+
if (hex) {
|
|
22
|
+
value += String.fromCodePoint(Number.parseInt(hex, 16));
|
|
23
|
+
index = cursor < selector.length && WHITESPACE.test(selector[cursor]) ? cursor + 1 : cursor;
|
|
24
|
+
} else {
|
|
25
|
+
value += selector[index + 1] ?? "";
|
|
26
|
+
index += 2;
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!IDENT_CHAR.test(char) && char.charCodeAt(0) < 128)
|
|
31
|
+
break;
|
|
32
|
+
value += char;
|
|
33
|
+
index++;
|
|
34
|
+
}
|
|
35
|
+
return { value, end: index };
|
|
36
|
+
}
|
|
37
|
+
function skipString(selector, start) {
|
|
38
|
+
const quote = selector[start];
|
|
39
|
+
let index = start + 1;
|
|
40
|
+
while (index < selector.length && selector[index] !== quote) {
|
|
41
|
+
index += selector[index] === "\\" ? 2 : 1;
|
|
42
|
+
}
|
|
43
|
+
return index;
|
|
44
|
+
}
|
|
45
|
+
function walkTopLevel(selector, visit) {
|
|
46
|
+
let depth = 0;
|
|
47
|
+
let index = 0;
|
|
48
|
+
while (index < selector.length) {
|
|
49
|
+
const char = selector[index];
|
|
50
|
+
if (char === "\\") {
|
|
51
|
+
index += 2;
|
|
52
|
+
} else if (char === '"' || char === "'") {
|
|
53
|
+
index = skipString(selector, index) + 1;
|
|
54
|
+
} else if (char === "(" || char === "[") {
|
|
55
|
+
depth++;
|
|
56
|
+
index++;
|
|
57
|
+
} else if (char === ")" || char === "]") {
|
|
58
|
+
depth--;
|
|
59
|
+
index++;
|
|
60
|
+
} else {
|
|
61
|
+
index = (depth === 0 ? visit(index) : void 0) ?? index + 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function splitSelectorList(selector) {
|
|
66
|
+
const selectors = [];
|
|
67
|
+
let start = 0;
|
|
68
|
+
walkTopLevel(selector, (index) => {
|
|
69
|
+
if (selector[index] === ",") {
|
|
70
|
+
selectors.push(selector.slice(start, index).trim());
|
|
71
|
+
start = index + 1;
|
|
72
|
+
}
|
|
73
|
+
return void 0;
|
|
74
|
+
});
|
|
75
|
+
selectors.push(selector.slice(start).trim());
|
|
76
|
+
return selectors.filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
function getRequiredClasses(selector) {
|
|
79
|
+
const classes = [];
|
|
80
|
+
walkTopLevel(selector, (index) => {
|
|
81
|
+
if (selector[index] !== ".")
|
|
82
|
+
return void 0;
|
|
83
|
+
const { value, end } = readIdentifier(selector, index + 1);
|
|
84
|
+
if (value)
|
|
85
|
+
classes.push(value);
|
|
86
|
+
return end;
|
|
87
|
+
});
|
|
88
|
+
return classes;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const PURGE_MANIFEST_FILE = "purge-manifest.json";
|
|
92
|
+
const ENTRY_DIRECTORIES = [
|
|
93
|
+
"components",
|
|
94
|
+
"composables",
|
|
95
|
+
"directives",
|
|
96
|
+
"plugins"
|
|
97
|
+
];
|
|
98
|
+
const IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?)["'](\.{1,2}\/[^"']+)["']/g;
|
|
99
|
+
const EXPORT_PATTERN = /export\s*\{([^}]*)\}/g;
|
|
100
|
+
const NAME_CHAR = /[\w$]/;
|
|
101
|
+
const CLASS_CHAR = /[\w-]/;
|
|
102
|
+
function toKebabCase(value) {
|
|
103
|
+
return value.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
104
|
+
}
|
|
105
|
+
function collectClasses(css, filter) {
|
|
106
|
+
const classes = /* @__PURE__ */ new Set();
|
|
107
|
+
postcss.parse(css).walkRules((rule) => {
|
|
108
|
+
for (const selector of splitSelectorList(rule.selector)) {
|
|
109
|
+
for (const className of getRequiredClasses(selector)) {
|
|
110
|
+
if (filter(className))
|
|
111
|
+
classes.add(className);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return [...classes].sort();
|
|
116
|
+
}
|
|
117
|
+
function containsToken(content, token, isTokenChar = (char) => CLASS_CHAR.test(char)) {
|
|
118
|
+
let index = content.indexOf(token);
|
|
119
|
+
while (index !== -1) {
|
|
120
|
+
const before = index === 0 ? "" : content[index - 1];
|
|
121
|
+
const after = content[index + token.length] ?? "";
|
|
122
|
+
const isDecimal = after === "." && /\d/.test(content[index + token.length + 1] ?? "");
|
|
123
|
+
if (!isTokenChar(before) && !isTokenChar(after) && after !== "!" && after !== "/" && !isDecimal)
|
|
124
|
+
return true;
|
|
125
|
+
index = content.indexOf(token, index + 1);
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
function containsName(content, name) {
|
|
130
|
+
return containsToken(content, name, (char) => NAME_CHAR.test(char) || char === "-");
|
|
131
|
+
}
|
|
132
|
+
function readModules(distDir, file, seen) {
|
|
133
|
+
if (seen.has(file) || !existsSync(file))
|
|
134
|
+
return "";
|
|
135
|
+
seen.add(file);
|
|
136
|
+
const code = readFileSync(file, "utf8");
|
|
137
|
+
let content = code;
|
|
138
|
+
for (const match of code.matchAll(IMPORT_PATTERN))
|
|
139
|
+
content += `
|
|
140
|
+
${readModules(distDir, resolve(dirname(file), match[1]), seen)}`;
|
|
141
|
+
return content;
|
|
142
|
+
}
|
|
143
|
+
function getAliases(directory, name, code) {
|
|
144
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
145
|
+
if (directory === "components") {
|
|
146
|
+
aliases.add(name);
|
|
147
|
+
aliases.add(toKebabCase(name));
|
|
148
|
+
aliases.add(`Lazy${name}`);
|
|
149
|
+
}
|
|
150
|
+
for (const match of code.matchAll(EXPORT_PATTERN)) {
|
|
151
|
+
for (const specifier of match[1].split(",")) {
|
|
152
|
+
const exported = specifier.trim().split(/\s+/).pop();
|
|
153
|
+
if (exported && exported !== "default")
|
|
154
|
+
aliases.add(exported);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (directory === "directives") {
|
|
158
|
+
for (const alias of [...aliases]) {
|
|
159
|
+
if (/^v[A-Z]/.test(alias) && !alias.endsWith("Install"))
|
|
160
|
+
aliases.add(toKebabCase(alias));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return [...aliases].sort();
|
|
164
|
+
}
|
|
165
|
+
function generatePurgeManifest(distDir, version) {
|
|
166
|
+
const classes = collectClasses(readFileSync(join(distDir, "css/main.css"), "utf8"), (className) => className.startsWith("maz:"));
|
|
167
|
+
const utilitiesFile = join(distDir, "css/utilities.css");
|
|
168
|
+
const utilities = existsSync(utilitiesFile) ? collectClasses(readFileSync(utilitiesFile, "utf8"), () => true) : [];
|
|
169
|
+
const entries = {};
|
|
170
|
+
for (const directory of ENTRY_DIRECTORIES) {
|
|
171
|
+
const folder = join(distDir, directory);
|
|
172
|
+
if (!existsSync(folder))
|
|
173
|
+
continue;
|
|
174
|
+
for (const file of readdirSync(folder)) {
|
|
175
|
+
if (!file.endsWith(".js") || file === "index.js")
|
|
176
|
+
continue;
|
|
177
|
+
const path = join(folder, file);
|
|
178
|
+
const name = basename(file, ".js");
|
|
179
|
+
const content = readModules(distDir, path, /* @__PURE__ */ new Set());
|
|
180
|
+
entries[`${directory}/${name}`] = {
|
|
181
|
+
aliases: getAliases(directory, name, readFileSync(path, "utf8")),
|
|
182
|
+
classes: classes.flatMap((className, index) => containsToken(content, className) ? [index] : [])
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const manifest = { version, classes, utilities, entries };
|
|
187
|
+
writeFileSync(join(distDir, "css", PURGE_MANIFEST_FILE), JSON.stringify(manifest));
|
|
188
|
+
return manifest;
|
|
189
|
+
}
|
|
190
|
+
function loadPurgeManifest(root) {
|
|
191
|
+
try {
|
|
192
|
+
const require = createRequire(join(root, "package.json"));
|
|
193
|
+
const file = require.resolve(`maz-ui/${PURGE_MANIFEST_FILE}`);
|
|
194
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
195
|
+
} catch {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export { PURGE_MANIFEST_FILE as P, getRequiredClasses as a, containsToken as b, containsName as c, generatePurgeManifest as g, loadPurgeManifest as l, splitSelectorList as s };
|
package/package.json
CHANGED
|
@@ -1,6 +1,103 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maz-ui/vite-plugin",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "5.7.0-beta.2",
|
|
5
|
+
"description": "Vite plugins for Maz-UI: purge the CSS of the components and utilities your app does not use",
|
|
6
|
+
"author": "Louis Mazel <me@loicmazuel.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://maz-ui.com/ecosystem/vite-plugin",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/LouisMazel/maz-ui.git",
|
|
12
|
+
"directory": "packages/vite-plugin"
|
|
13
|
+
},
|
|
14
|
+
"bugs": "https://github.com/LouisMazel/maz-ui/issues",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"vite",
|
|
17
|
+
"vite-plugin",
|
|
18
|
+
"maz-ui",
|
|
19
|
+
"purge",
|
|
20
|
+
"css",
|
|
21
|
+
"tree-shaking"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"monorepo:dev": "./src/index.ts",
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"default": "./dist/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./manifest": {
|
|
35
|
+
"monorepo:dev": "./src/manifest.ts",
|
|
36
|
+
"types": "./dist/manifest.d.mts",
|
|
37
|
+
"import": "./dist/manifest.mjs",
|
|
38
|
+
"default": "./dist/manifest.mjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"main": "./dist/index.mjs",
|
|
42
|
+
"types": "./dist/index.d.mts",
|
|
43
|
+
"files": [
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"README.md",
|
|
46
|
+
"agent-docs",
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"vite": ">=6.0.0 <9.0.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"postcss": "^8.5.32",
|
|
57
|
+
"tinyglobby": "^0.2.17"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@maz-ui/eslint-config": "^5.6.0-beta.0",
|
|
61
|
+
"@types/node": "^26.6.6",
|
|
62
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
63
|
+
"cross-env": "^10.1.0",
|
|
64
|
+
"eslint": "^10.14.0",
|
|
65
|
+
"typescript": "^6.0.3",
|
|
66
|
+
"unbuild": "^3.6.1",
|
|
67
|
+
"vite": "^8.3.7",
|
|
68
|
+
"vitest": "5.1.0"
|
|
69
|
+
},
|
|
70
|
+
"lint-staged": {
|
|
71
|
+
"*.{js,jsx,ts,tsx,mjs,mts,cjs,md,yml,json}": [
|
|
72
|
+
"eslint --fix"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"nx": {
|
|
76
|
+
"targets": {
|
|
77
|
+
"build": {
|
|
78
|
+
"inputs": [
|
|
79
|
+
"production",
|
|
80
|
+
"^production",
|
|
81
|
+
"{workspaceRoot}/tools/agent-docs/src/**/*",
|
|
82
|
+
"{workspaceRoot}/tools/agent-docs/content/**/*",
|
|
83
|
+
"{workspaceRoot}/apps/docs/src/**/*"
|
|
84
|
+
],
|
|
85
|
+
"outputs": [
|
|
86
|
+
"{projectRoot}/dist",
|
|
87
|
+
"{projectRoot}/agent-docs"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"scripts": {
|
|
93
|
+
"build": "unbuild && pnpm generate:agent-docs",
|
|
94
|
+
"generate:agent-docs": "pnpm -F agent-docs generate --package @maz-ui/vite-plugin",
|
|
95
|
+
"dev": "unbuild --stub",
|
|
96
|
+
"typecheck": "tsc --noEmit --skipLibCheck",
|
|
97
|
+
"lint": "cross-env NODE_ENV=production eslint .",
|
|
98
|
+
"lint:fix": "pnpm lint --fix",
|
|
99
|
+
"test:unit": "vitest run",
|
|
100
|
+
"test:unit:watch": "vitest watch",
|
|
101
|
+
"test:unit:coverage": "vitest run --coverage"
|
|
102
|
+
}
|
|
6
103
|
}
|