├── src
├── types.ts
├── debug.ts
├── utils.ts
├── constants.ts
├── options.ts
└── index.ts
├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── .eslintignore
├── .npmrc
├── .gitignore
├── pnpm-workspace.yaml
├── .eslintrc.json
├── example
├── src
│ ├── main.css
│ ├── main.ts
│ └── App.vue
├── tailwind.config.js
├── index.html
├── vite.config.ts
└── package.json
├── .vscode
└── settings.json
├── tsconfig.json
├── LICENSE
├── package.json
├── README.md
└── pnpm-lock.yaml
/src/types.ts:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: antfu
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ignore-workspace-root-check=true
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - example/
3 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@antfu/eslint-config",
3 | "rules": {
4 | "@typescript-eslint/no-unused-vars": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/src/main.css:
--------------------------------------------------------------------------------
1 | .button {
2 | @apply p-4 bg-blue-400 text-white rounded hover:bg-blue-600 cursor-pointer;
3 | margin: 5px;
4 | }
5 |
--------------------------------------------------------------------------------
/example/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from '/src/App.vue'
3 | import 'windi.css'
4 | import './main.css'
5 |
6 | createApp(App).mount('#app')
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "Windi",
4 | "Windicss"
5 | ],
6 | "typescript.tsdk": "node_modules/typescript/lib",
7 | "volar.tsPlugin": true
8 | }
--------------------------------------------------------------------------------
/example/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('windicss/plugin/typography'),
4 | ],
5 | darkMode: 'class',
6 | theme: {
7 | extend: {
8 | colors: {
9 | teal: {
10 | 100: '#096',
11 | },
12 | },
13 | },
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vite App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - run: npx conventional-github-releaser -p angular
14 | env:
15 | CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}
16 |
--------------------------------------------------------------------------------
/example/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { UserConfig } from 'vite'
2 | import Vue from '@vitejs/plugin-vue'
3 | import WindiCSS from 'vite-plugin-windicss'
4 |
5 | const config: UserConfig = {
6 | plugins: [
7 | Vue({
8 | include: [/\.vue$/, /\.md$/],
9 | }),
10 | ...WindiCSS({
11 | safelist: 'shadow shadow-xl',
12 | }),
13 | ],
14 | }
15 |
16 | export default config
17 |
--------------------------------------------------------------------------------
/example/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello World
4 |
5 |
8 |
9 | foo
10 |
11 |
12 | Hello
13 |
14 |
15 |
16 |
17 |
22 |
--------------------------------------------------------------------------------
/src/debug.ts:
--------------------------------------------------------------------------------
1 | import _debug from 'debug'
2 |
3 | export const debug = {
4 | config: _debug('vite-plugin-windicss:config'),
5 | css: _debug('vite-plugin-windicss:css'),
6 | debug: _debug('vite-plugin-windicss:debug'),
7 | compile: _debug('vite-plugin-windicss:compile'),
8 | glob: _debug('vite-plugin-windicss:glob'),
9 | detect: _debug('vite-plugin-windicss:detect'),
10 | hmr: _debug('vite-plugin-windicss:hmr'),
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "es2017",
5 | "lib": ["ESNext", "DOM"],
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "strictNullChecks": true,
9 | "moduleResolution": "Node",
10 | "resolveJsonModule": true,
11 | "skipLibCheck": true
12 | },
13 | "exclude": [
14 | "**/dist",
15 | "**/node_modules",
16 | "**/test"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | export function toArray(v: T | T[]): T[] {
2 | if (Array.isArray(v))
3 | return v
4 | return [v]
5 | }
6 |
7 | export function kebabCase(str: string) {
8 | return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
9 | }
10 |
11 | export function include(set: Set, v: T[] | Set) {
12 | for (const i of v)
13 | set.add(i)
14 | }
15 |
16 | export function exclude(set: Set, v: T[] | Set) {
17 | for (const i of v)
18 | set.delete(i)
19 | }
20 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fixture",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "nodemon --watch ../dist/index.js -x \"cross-env DEBUG=vite-plugin-windicss:* vite\"",
7 | "build": "cross-env DEBUG=vite-plugin-windicss:* vite build"
8 | },
9 | "dependencies": {
10 | "vue": "^3.0.5"
11 | },
12 | "devDependencies": {
13 | "@vitejs/plugin-vue": "^1.1.4",
14 | "@vue/compiler-sfc": "^3.0.5",
15 | "cross-env": "^7.0.3",
16 | "nodemon": "^2.0.7",
17 | "typescript": "^4.1.5",
18 | "vite": "^2.0.0-beta.69",
19 | "vite-plugin-windicss": "workspace:*"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Anthony Fu
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.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-plugin-windicss",
3 | "version": "0.1.7",
4 | "main": "dist/index.js",
5 | "module": "dist/index.mjs",
6 | "types": "dist/index.d.ts",
7 | "license": "MIT",
8 | "author": "antfu ",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/antfu/vite-plugin-windicss"
12 | },
13 | "homepage": "https://github.com/antfu/vite-plugin-windicss",
14 | "bugs": "https://github.com/antfu/vite-plugin-windicss/issues",
15 | "files": [
16 | "dist"
17 | ],
18 | "scripts": {
19 | "dev": "npm run build -- --watch",
20 | "example:dev": "npm -C example run dev",
21 | "example:build": "npm -C example run build",
22 | "build": "tsup src/index.ts --dts --format cjs,esm",
23 | "prepublishOnly": "npm run build",
24 | "release": "npx bumpp --commit --tag --push && npm publish"
25 | },
26 | "dependencies": {
27 | "fast-glob": "^3.2.5",
28 | "windicss": "^2.1.6"
29 | },
30 | "peerDependencies": {
31 | "vite": "^2.0.0-beta.69"
32 | },
33 | "devDependencies": {
34 | "@antfu/eslint-config": "^0.4.3",
35 | "@types/debug": "^4.1.5",
36 | "@types/node": "^14.14.28",
37 | "@typescript-eslint/eslint-plugin": "^4.15.1",
38 | "debug": "^4.3.2",
39 | "eslint": "^7.20.0",
40 | "rollup": "^2.39.0",
41 | "tsup": "^3.12.1",
42 | "typescript": "^4.1.5",
43 | "vite": "^2.0.0-beta.69"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-use-before-define */
2 |
3 | export const MODULE_ID = 'windi.css'
4 | export const MODULE_ID_VIRTUAL = `/@windicss/${MODULE_ID}`
5 |
6 | export const regexQuotedString = /(["'`])((?:\\\1|(?:(?!\1)).)*?)\1/g
7 | export const regexClassCheck = /^[a-z-]+[a-z0-9:\-/\\]*\.?[a-z0-9]$/
8 | export const regexHtmlTag = /<([\w-]+)/g
9 |
10 | export const defaultAlias: Record = {
11 | 'router-link': 'a',
12 | }
13 |
14 | export const preflightTags = ['html', 'body', 'div']
15 | export const htmlTags = [
16 | 'html',
17 | 'body',
18 | 'div',
19 | 'a',
20 | 'abbr',
21 | 'address',
22 | 'area',
23 | 'article',
24 | 'aside',
25 | 'audio',
26 | 'base',
27 | 'basefont',
28 | 'bdo',
29 | 'blink',
30 | 'blockquote',
31 | 'br',
32 | 'button',
33 | 'canvas',
34 | 'caption',
35 | 'center',
36 | 'col',
37 | 'colgroup',
38 | 'command',
39 | 'comment',
40 | 'datalist',
41 | 'dd',
42 | 'del',
43 | 'details',
44 | 'dir',
45 | 'dl',
46 | 'dt',
47 | 'embed',
48 | 'fieldset',
49 | 'figure',
50 | 'b',
51 | 'big',
52 | 'i',
53 | 'small',
54 | 'tt',
55 | 'font',
56 | 'footer',
57 | 'form',
58 | 'frame',
59 | 'frameset',
60 | 'head',
61 | 'header',
62 | 'hgroup',
63 | 'h1',
64 | 'h2',
65 | 'h3',
66 | 'h4',
67 | 'h5',
68 | 'h6',
69 | 'hr',
70 | 'isindex',
71 | 'iframe',
72 | 'ilayer',
73 | 'img',
74 | 'input',
75 | 'ins',
76 | 'keygen',
77 | 'keygen',
78 | 'label',
79 | 'layer',
80 | 'legend',
81 | 'li',
82 | 'link',
83 | 'map',
84 | 'mark',
85 | 'marquee',
86 | 'menu',
87 | 'meta',
88 | 'meter',
89 | 'multicol',
90 | 'nav',
91 | 'nobr',
92 | 'noembed',
93 | 'noframes',
94 | 'noscript',
95 | 'object',
96 | 'ol',
97 | 'optgroup',
98 | 'option',
99 | 'output',
100 | 'p',
101 | 'param',
102 | 'cite',
103 | 'code',
104 | 'dfn',
105 | 'em',
106 | 'kbd',
107 | 'samp',
108 | 'strong',
109 | 'var',
110 | 'plaintext',
111 | 'pre',
112 | 'progress',
113 | 'q',
114 | 'ruby',
115 | 'script',
116 | 'section',
117 | 'select',
118 | 'spacer',
119 | 'span',
120 | 's',
121 | 'strike',
122 | 'style',
123 | 'sub',
124 | 'sup',
125 | 'svg',
126 | 'table',
127 | 'tbody',
128 | 'td',
129 | 'textarea',
130 | 'tfoot',
131 | 'th',
132 | 'thead',
133 | 'time',
134 | 'title',
135 | 'tr',
136 | 'u',
137 | 'ul',
138 | 'video',
139 | 'wbr',
140 | 'wbr',
141 | 'xmp',
142 | ] as const
143 |
144 | export type TagNames = (typeof htmlTags)[number]
145 |
--------------------------------------------------------------------------------
/src/options.ts:
--------------------------------------------------------------------------------
1 | import type { Config as WindiCssOptions } from 'windicss/types/interfaces'
2 | import { defaultAlias, TagNames } from './constants'
3 | import { kebabCase } from './utils'
4 |
5 | export { WindiCssOptions }
6 |
7 | export interface UserOptions {
8 | /**
9 | * Options for windicss/tailwindcss.
10 | * Also accepts string as config file path.
11 | *
12 | * @default 'tailwind.config.js'
13 | */
14 | windicssOptions?: WindiCssOptions | string
15 |
16 | /**
17 | * Enabled windicss preflight (a.k.a TailwindCSS style reset)
18 | *
19 | * @default true
20 | */
21 | preflight?: boolean | {
22 | /**
23 | * Safelist to always included
24 | */
25 | safelist?: string | string[]
26 |
27 | /**
28 | * Alias for resolving preflight
29 | */
30 | alias?: Record
31 |
32 | /**
33 | * @default true
34 | */
35 | includeBase?: boolean
36 |
37 | /**
38 | * @default true
39 | */
40 | includeGlobal?: boolean
41 |
42 | /**
43 | * @default true
44 | */
45 | includePlugin?: boolean
46 | }
47 |
48 | /**
49 | * Directories to search for classnames
50 | *
51 | * @default 'src'
52 | */
53 | searchDirs?: string[]
54 |
55 | /**
56 | * File extension to search for classnames
57 | *
58 | * @default 'html', 'vue'
59 | */
60 | searchExtensions?: string[]
61 |
62 | /**
63 | * Transform CSS for `@apply` directive
64 | *
65 | * @default true
66 | */
67 | transformCSS?: boolean
68 |
69 | /**
70 | * Sort the genrate utilities
71 | *
72 | * @default true
73 | */
74 | sortUtilities?: boolean
75 |
76 | /**
77 | * Safe class names to be always included.
78 | */
79 | safelist?: string | string[]
80 | }
81 |
82 | export function resolveOptions(options: UserOptions) {
83 | const {
84 | windicssOptions = 'tailwind.config.js',
85 | searchExtensions = ['html', 'vue', 'pug', 'jsx', 'tsx', 'svelte'],
86 | searchDirs = ['src'],
87 | preflight = true,
88 | transformCSS = true,
89 | sortUtilities = true,
90 | } = options
91 |
92 | const preflightOptions = Object.assign(
93 | {
94 | includeBase: true,
95 | includeGlobal: true,
96 | includePlugin: true,
97 | alias: {},
98 | },
99 | typeof preflight === 'boolean' ? {} : preflight,
100 | )
101 |
102 | preflightOptions.alias = Object.fromEntries(
103 | Object.entries({
104 | ...defaultAlias,
105 | ...preflightOptions.alias,
106 | }).filter(([k, v]) => [kebabCase(k), v]),
107 | )
108 |
109 | return {
110 | windicssOptions,
111 | searchExtensions,
112 | searchDirs,
113 | transformCSS,
114 | preflight: Boolean(preflight),
115 | preflightOptions,
116 | sortUtilities,
117 | }
118 | }
119 |
120 | export type ResolvedOptions = ReturnType
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | vite-plugin-windicss
2 |
3 | Windicss for Vite
4 | a.k.a On-demand TailwindCSS
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## Features
20 |
21 | - ⚡️ **It's FAST** - about 15~20 times faster than Tailwind on Vite
22 | - 🧩 On-demand CSS utilities (Compatible with Tailwind CSS v2)
23 | - 📦 On-demand native elements style reseting
24 | - 🔥 Hot module replacement (HMR)
25 | - 🍃 Load configurations from `tailwind.config.js`
26 | - 🤝 Framework-agnostic - Vue, React, Svelte and vanilla!
27 | - 📄 CSS `@apply` / `@screen` directives transforms (also works for Vue SFC's `