├── .npmrc ├── examples ├── nuxt │ ├── .npmrc │ ├── static │ │ ├── favicon.ico │ │ └── README.md │ ├── components │ │ ├── README.md │ │ ├── Feature.vue │ │ └── Logo.vue │ ├── .editorconfig │ ├── layouts │ │ ├── README.md │ │ └── default.vue │ ├── pages │ │ ├── README.md │ │ ├── typography.vue │ │ └── index.vue │ ├── assets │ │ └── README.md │ ├── plugins │ │ └── README.md │ ├── middleware │ │ └── README.md │ ├── README.md │ ├── store │ │ └── README.md │ ├── package.json │ └── nuxt.config.js ├── vue │ ├── .npmrc │ ├── babel.config.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── main.js │ │ ├── App.vue │ │ ├── components │ │ │ ├── Feature.vue │ │ │ └── HelloWorld.vue │ │ └── assets │ │ │ └── logo.svg │ ├── .gitignore │ ├── README.md │ ├── vue.config.js │ └── package.json └── nuxt-ca-ts │ ├── .npmrc │ ├── tailwind.config.js │ ├── layouts │ └── default.vue │ ├── tsconfig.json │ ├── nuxt.config.ts │ ├── pages │ ├── nested.vue │ └── index.vue │ └── package.json ├── pnpm-workspace.yaml ├── .gitattributes ├── .gitignore ├── src ├── tsconfig.json ├── interfaces.ts ├── utils.ts ├── parser.ts └── index.ts ├── tsconfig.json ├── .github └── workflows │ └── publish.yml ├── LICENSE ├── package.json ├── docs └── using-tailwind-configuration.md ├── rollup.config.js ├── README.md └── pnpm-lock.yaml /.npmrc: -------------------------------------------------------------------------------- 1 | shared-workspace-lockfile=false -------------------------------------------------------------------------------- /examples/nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /examples/vue/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - examples/* -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | example/** -linguist-detectable 2 | site/** -linguist-detectable -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /public/build/ 3 | 4 | /src/**/*.js 5 | /src/**/*.d.ts 6 | 7 | .DS_Store 8 | .nuxt/ 9 | dist -------------------------------------------------------------------------------- /examples/vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('windicss/plugin/scroll-snap')], 3 | } 4 | -------------------------------------------------------------------------------- /examples/nuxt/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windicss/vue-windicss-preprocess/HEAD/examples/nuxt/static/favicon.ico -------------------------------------------------------------------------------- /examples/vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windicss/vue-windicss-preprocess/HEAD/examples/vue/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "declaration": true, 6 | "declarationDir": "../dist/types" 7 | }, 8 | "include": ["."] 9 | } 10 | -------------------------------------------------------------------------------- /examples/nuxt/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /examples/vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /examples/nuxt/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /examples/nuxt/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /examples/nuxt/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /examples/nuxt/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /examples/nuxt/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /examples/vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "target": "es2017", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "lib": ["dom", "esnext", "dom.iterable"], 8 | "strict": true, 9 | "strictNullChecks": true, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "resolveJsonModule": true 13 | }, 14 | "exclude": ["**/dist", "**/examples"] 15 | } 16 | -------------------------------------------------------------------------------- /examples/nuxt/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /examples/vue/README.md: -------------------------------------------------------------------------------- 1 | # vuets 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /examples/nuxt/README.md: -------------------------------------------------------------------------------- 1 | # nuxt_example 2 | 3 | ## Build Setup 4 | 5 | ```bash 6 | # install dependencies 7 | $ npm install 8 | 9 | # serve with hot reload at localhost:3000 10 | $ npm run dev 11 | 12 | # build for production and launch server 13 | $ npm run build 14 | $ npm run start 15 | 16 | # generate static project 17 | $ npm run generate 18 | ``` 19 | 20 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 21 | -------------------------------------------------------------------------------- /examples/nuxt/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /examples/nuxt/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "lib": ["ESNext", "ESNext.AsyncIterable", "DOM"], 7 | "esModuleInterop": true, 8 | "allowJs": true, 9 | "sourceMap": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "baseUrl": ".", 13 | "paths": { 14 | "~/*": ["./*"], 15 | "@/*": ["./*"] 16 | }, 17 | "types": ["@types/node", "@nuxt/types"] 18 | }, 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt_example", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "cross-env DEBUG='nuxt:*' nuxt --port 3333", 7 | "build": "nuxt build", 8 | "start": "nuxt start --port 3333", 9 | "generate": "nuxt generate" 10 | }, 11 | "dependencies": { 12 | "core-js": "^3.6.5", 13 | "nuxt": "^2.14.6" 14 | }, 15 | "devDependencies": { 16 | "cross-env": "^7.0.3", 17 | "vue-windicss-preprocess": "workspace:../../dist", 18 | "windicss": "^2.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/nuxt/components/Feature.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtConfig } from '@nuxtjs/composition-api' 2 | 3 | export default defineNuxtConfig({ 4 | buildModules: ['@nuxt/typescript-build', '@nuxtjs/composition-api'], 5 | build: { 6 | extend(config) { 7 | config.module?.rules.push({ 8 | test: /\.vue$/, 9 | loader: 'vue-windicss-preprocess', 10 | options: { 11 | config: 'tailwind.config.js', 12 | compile: process.env.NODE_ENV === 'production', 13 | globalPreflight: true, 14 | globalUtility: true, 15 | }, 16 | }) 17 | }, 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /examples/vue/vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | module.exports = { 3 | configureWebpack: (config) => { 4 | config.resolve.symlinks = true 5 | config.module.rules.push({ 6 | test: /\.vue$/, 7 | use: [{ 8 | loader: 'vue-windicss-preprocess', 9 | options: { 10 | // config: 'tailwind.config.js', 11 | compile: false, 12 | globalPreflight: true, 13 | globalUtility: true, 14 | prefix: 'windi-', 15 | } 16 | }] 17 | }); 18 | } 19 | } -------------------------------------------------------------------------------- /examples/vue/src/components/Feature.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/pages/nested.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 35 | -------------------------------------------------------------------------------- /examples/vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface Node { 2 | start: number, 3 | end: number, 4 | type: string, 5 | value?: Text | Expression | ( Text | Expression)[], 6 | } 7 | 8 | export interface Attribute extends Node { 9 | type: 'Attribute', 10 | name: string, 11 | value: Text | Expression | ( Text | Expression)[], 12 | } 13 | 14 | export interface Directive extends Node { 15 | type: 'Directive', 16 | name: string, 17 | value: Text | Expression | ( Text | Expression)[], 18 | } 19 | 20 | export interface Expression extends Node { 21 | type: 'Expression', 22 | data: string 23 | } 24 | 25 | export interface Text extends Node { 26 | type: 'Text', 27 | data: string 28 | } 29 | 30 | export interface Tag { 31 | start: number, 32 | end: number, 33 | name: string, 34 | value: (Attribute | Directive)[] 35 | } -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | publish-npm: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | registry-url: https://registry.npmjs.org/ 17 | - run: mkdir dist && cp package.json dist/package.json 18 | - run: npm i -g pnpm 19 | - run: pnpm install --frozen-lockfile 20 | - run: pnpm build:prod 21 | - run: pnpm test --if-present 22 | - run: cd dist && npm publish 23 | env: 24 | NODE_AUTH_TOKEN: ${{secrets.NPM_SECRET}} 25 | - run: npx conventional-github-releaser -p angular 26 | env: 27 | CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-windicss-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt build" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "nuxt": "^2.15.0" 14 | }, 15 | "devDependencies": { 16 | "@nuxt/types": "^2.15.0", 17 | "@nuxt/typescript-build": "^2.0.4", 18 | "@nuxt/vue-app": "^2.15.2", 19 | "@nuxtjs/composition-api": "^0.20.2", 20 | "css-loader": "^5.0.2", 21 | "fs-extra": "^9.1.0", 22 | "postcss": "^8.2.6", 23 | "postcss-import": "^13.0.0", 24 | "postcss-loader": "^4.2.0", 25 | "postcss-nested": "^5.0.3", 26 | "postcss-url": "^10.1.1", 27 | "vue-windicss-preprocess": "workspace:../../dist", 28 | "windicss": "^2.1.13" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue_example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.9.0", 12 | "vue": "^3.0.5" 13 | }, 14 | "devDependencies": { 15 | "@babel/eslint-parser": "^7.13.4", 16 | "@vue/cli-plugin-babel": "^4.5.11", 17 | "@vue/cli-plugin-eslint": "^4.5.11", 18 | "@vue/cli-service": "^4.5.11", 19 | "@vue/compiler-sfc": "^3.0.5", 20 | "eslint": "^7.20.0", 21 | "eslint-plugin-vue": "^7.6.0" 22 | }, 23 | "eslintConfig": { 24 | "root": true, 25 | "env": { 26 | "node": true 27 | }, 28 | "extends": [ 29 | "plugin:vue/vue3-essential", 30 | "eslint:recommended" 31 | ], 32 | "parser": "@babel/eslint-parser", 33 | "rules": {} 34 | }, 35 | "browserslist": [ 36 | "> 1%", 37 | "last 2 versions", 38 | "not dead" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Veritas Raven 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. -------------------------------------------------------------------------------- /examples/nuxt/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 63 | -------------------------------------------------------------------------------- /examples/nuxt/pages/typography.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-windicss-preprocess", 3 | "version": "2.2.0", 4 | "description": "A vue loader to compile tailwindcss at build time based on windicss compiler.", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "browser": "browser.js", 8 | "directories": { 9 | "examples": "examples" 10 | }, 11 | "scripts": { 12 | "dev": "rollup -cw", 13 | "tsd": "tsc -p ./src --emitDeclarationOnly", 14 | "build": "rollup -c && npm run tsd", 15 | "build:prod": "PUBLISH=true rollup -c && npm run tsd", 16 | "release": "npx git-ensure -a && npx bumpp --commit --tag --push" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/voorjaar/vue-windicss-preprocess.git" 21 | }, 22 | "keywords": [ 23 | "vue", 24 | "vue-loader", 25 | "tailwindcss", 26 | "css", 27 | "compiler" 28 | ], 29 | "author": "Veritas Raven", 30 | "license": "MIT", 31 | "homepage": "https://github.com/voorjaar/vue-windicss-preprocess", 32 | "dependencies": { 33 | "loader-utils": "^2.0.0", 34 | "magic-string": "^0.25.7", 35 | "windicss": "^2.3.0" 36 | }, 37 | "devDependencies": { 38 | "@rollup/plugin-node-resolve": "^11.2.0", 39 | "@rollup/plugin-replace": "^2.4.1", 40 | "@rollup/plugin-sucrase": "^3.1.0", 41 | "@rollup/plugin-typescript": "^8.2.0", 42 | "@types/loader-utils": "^2.0.1", 43 | "@types/node": "^14.14.32", 44 | "rollup": "^2.40.0", 45 | "rollup-plugin-terser": "^7.0.2", 46 | "tslib": "^2.1.0", 47 | "typescript": "^4.2.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/nuxt/nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Global page headers (https://go.nuxtjs.dev/config-head) 3 | head: { 4 | title: 'nuxt_example', 5 | meta: [ 6 | { charset: 'utf-8' }, 7 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 8 | { hid: 'description', name: 'description', content: '' } 9 | ], 10 | link: [ 11 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 12 | ] 13 | }, 14 | 15 | // Global CSS (https://go.nuxtjs.dev/config-css) 16 | css: [ 17 | ], 18 | 19 | // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) 20 | plugins: [ 21 | ], 22 | 23 | // Auto import components (https://go.nuxtjs.dev/config-components) 24 | components: true, 25 | 26 | // Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules) 27 | buildModules: [ 28 | ], 29 | 30 | // Modules (https://go.nuxtjs.dev/config-modules) 31 | modules: [ 32 | ], 33 | 34 | // Build Configuration (https://go.nuxtjs.dev/config-build) 35 | build: { 36 | extend(config) { 37 | const { buildContext } = this 38 | config.module.rules.push({ 39 | test: /\.vue$/, 40 | loader: 'vue-windicss-preprocess', 41 | options: { 42 | config: { 43 | plugins: [ 44 | require('windicss/plugin/typography') 45 | ] 46 | }, 47 | compile: !buildContext.nuxt.options.dev, // false: interpretation mode; true: compilation mode 48 | globalPreflight: true, // preflight style is global or scoped 49 | globalUtility: true, // utility style is global or scoped 50 | // prefix: 'windi-', 51 | } 52 | }) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/nuxt-ca-ts/pages/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 49 | 50 | 55 | -------------------------------------------------------------------------------- /docs/using-tailwind-configuration.md: -------------------------------------------------------------------------------- 1 | # Using tailwind configuration 2 | 3 | ## Create configuration file 4 | 5 | Basically it is a simple tailwind configuration file, you need to replace tailwindcss with windicss, if you need to call the default colors. All tailwindcss v2.0 utilities should be customizable, except for variants and plugins which are not currently supported. For detailed configuration, please refer to the [tailwindcss documentation](https://tailwindcss.com/docs/configuration). 6 | 7 | ```js 8 | // tailwind.config.js 9 | const colors = require('windicss/colors') 10 | 11 | module.exports = { 12 | theme: { 13 | screens: { 14 | sm: '480px', 15 | md: '768px', 16 | lg: '976px', 17 | xl: '1440px', 18 | }, 19 | colors: { 20 | gray: colors.coolGray, 21 | blue: colors.lightBlue, 22 | red: colors.rose, 23 | pink: colors.fuchsia, 24 | }, 25 | fontFamily: { 26 | sans: ['Graphik', 'sans-serif'], 27 | serif: ['Merriweather', 'serif'], 28 | }, 29 | extend: { 30 | spacing: { 31 | '128': '32rem', 32 | '144': '36rem', 33 | }, 34 | borderRadius: { 35 | '4xl': '2rem', 36 | } 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | ## Add your config file to windicss 43 | 44 | Add `config` option to your `rollup.config.js`/`svelte.config.js`. 45 | 46 | ```js 47 | const windicss = require('svelte-windicss-preprocess'); 48 | // ... 49 | plugins: [ 50 | svelte({ 51 | preprocess: { 52 | markup: windicss.preprocess({ 53 | config: 'tailwind.config.js', // things like ./src/tailwind.config.js also works 54 | compile: false, 55 | prefix: 'windi-', 56 | globalPreflight: true, 57 | globalUtility: true, 58 | }), 59 | }, 60 | // ... 61 | }), 62 | // ... 63 | ] 64 | ``` 65 | 66 | Now you can test whether your configuration file is working properly, and welcome feedback if you find any problems. -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'windicss/utils/style'; 2 | import type { loader } from "webpack"; 3 | import { Config as WindicssConfig } from "windicss/types/interfaces"; 4 | 5 | export function searchNotEscape(text:string, char = "{") { 6 | if (text.charAt(0) === char) return 0; 7 | const index = text.search(new RegExp(String.raw`([^\\]${char})`)); 8 | if (index === -1) return -1; 9 | return index + 1; 10 | } 11 | 12 | export function searchGroup(text: string) { 13 | let level = 1; 14 | let index = 0; 15 | let endBracket = searchNotEscape(text, "}"); 16 | while (endBracket !== -1) { 17 | let nextBracket = searchNotEscape(text.slice(index,), "{"); 18 | if (endBracket < nextBracket || nextBracket === -1) { 19 | level--; 20 | index = endBracket + 1; 21 | if (level == 0) return endBracket; 22 | } else { 23 | level++; 24 | index = nextBracket + 1; 25 | } 26 | endBracket = searchNotEscape(text.slice(index,), "}"); 27 | } 28 | return -1; 29 | } 30 | 31 | export function combineStyleList(stylesheets:StyleSheet[]) { 32 | return stylesheets.reduce((previousValue, currentValue) => previousValue.extend(currentValue), new StyleSheet()).combine();//.sort(); 33 | } 34 | 35 | export function writeFileSync(path: string, data: string) { 36 | if (!process.env.BROWSER) return require('fs').writeFileSync(path, data); 37 | } 38 | 39 | /** 40 | * Resolve the WindicssConfig. 41 | * @param config File path to tailwind.config.js or object of WindicssConfig 42 | */ 43 | export function resolveConfig(config?: string | WindicssConfig) { 44 | // handle invalid config 45 | if (!config) { 46 | return undefined 47 | } 48 | /* 49 | * A type of string implies a file path to the tailwind.config.js. We check if it's a browser 50 | * context otherwise the load may fail. 51 | */ 52 | if (typeof config === 'string' && !process.env.BROWSER) { 53 | return loadConfigFile(config) 54 | } 55 | // if it's already an object we can just return it as is 56 | return config 57 | } 58 | 59 | export function loadConfigFile(config: string) { 60 | return require(require('path').resolve(config)); 61 | } 62 | 63 | export function getOptions(root:loader.LoaderContext) { 64 | if (process.env.BROWSER) return {}; 65 | return require("loader-utils").getOptions(root); 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 46 | 47 | 61 | -------------------------------------------------------------------------------- /examples/vue/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /examples/vue/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 75 | 76 | -------------------------------------------------------------------------------- /examples/nuxt/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 32 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | import sucrase from "@rollup/plugin-sucrase"; 5 | import replace from "@rollup/plugin-replace"; 6 | import typescript from "@rollup/plugin-typescript"; 7 | import { terser } from "rollup-plugin-terser"; 8 | 9 | const output_dir = "./dist"; 10 | 11 | const is_publish = !!process.env.PUBLISH; 12 | 13 | const ts_plugin = is_publish 14 | ? typescript({ 15 | target: "es5", 16 | include: "src/**", 17 | outDir: output_dir, 18 | typescript: require("typescript"), 19 | }) 20 | : sucrase({ 21 | exclude: ['node_modules/**'], 22 | transforms: ["typescript"], 23 | }); 24 | 25 | const dump = (file) => path.join(output_dir, file); 26 | 27 | const copy = (files) => 28 | files.forEach((file) => fs.copyFileSync(file, dump(file))); 29 | 30 | const rmdir = (dir) => { 31 | if (fs.existsSync(dir)) { 32 | const files = fs.readdirSync(dir); 33 | 34 | if (files.length > 0) { 35 | files.forEach((file) => { 36 | if (fs.statSync(path.join(dir, file)).isDirectory()) { 37 | rmdir(dir + "/" + file); 38 | } else { 39 | fs.unlinkSync(path.join(dir, file)); 40 | } 41 | }); 42 | } 43 | } 44 | }; 45 | 46 | const mkdir = (dir) => !(fs.existsSync(dir) && fs.statSync(dir).isDirectory()) && fs.mkdirSync(dir); 47 | 48 | 49 | const types = (dest = "index.d.ts", src = "../types/index", module = "*") => { 50 | return { 51 | writeBundle() { 52 | fs.writeFileSync(dump(dest), `export ${module} from "${src}";`); 53 | }, 54 | }; 55 | }; 56 | 57 | const main = { 58 | input: "src/index.ts", 59 | output: [ 60 | { 61 | exports: "default", 62 | file: dump("index.js"), 63 | format: "cjs" 64 | } 65 | ], 66 | plugins: [ 67 | ts_plugin, 68 | resolve(), 69 | replace({ "process.env.BROWSER": false }), 70 | rmdir(output_dir), 71 | mkdir(output_dir), 72 | copy(["package.json", "README.md", "LICENSE"]), 73 | types("index.d.ts", "./types/index", "*"), 74 | ], 75 | external: [ 76 | "loader-utils", 77 | "magic-string", 78 | "windicss/lib", 79 | "windicss/utils/parser", 80 | "windicss/utils/style", 81 | "postcss", 82 | "postcss-nested" 83 | ] 84 | }; 85 | 86 | const browser = { 87 | input: "src/index.ts", 88 | output: [ 89 | { 90 | name: "windicss", 91 | file: dump("browser.js"), 92 | format: "umd", 93 | } 94 | ], 95 | plugins: [ 96 | ts_plugin, 97 | resolve({ browser: true }), 98 | terser({ module: true, output: { comments: 'some' } }), 99 | replace({ 100 | "process.env.BROWSER": true, 101 | "process.env.NODE_ENV": `"publish"` 102 | }) 103 | ], 104 | external: [ 105 | "postcss", 106 | "postcss-nested" 107 | ] 108 | }; 109 | 110 | export default is_publish? [ main, browser ] : main; -------------------------------------------------------------------------------- /src/parser.ts: -------------------------------------------------------------------------------- 1 | import { searchGroup, searchNotEscape } from "./utils"; 2 | import type { Tag, Text, Expression } from "./interfaces"; 3 | 4 | export default class Parser { 5 | content: string; 6 | index = 0; 7 | constructor(content: string) { 8 | this.content = content; 9 | } 10 | 11 | _parseTag(): Tag["value"] { 12 | const nodes: Tag["value"] = []; 13 | 14 | while (!this.end) { 15 | const attr = this.food.match(/\S+\s*=\s*/); 16 | const endTag = searchNotEscape(this.food, ">"); 17 | 18 | if (!attr || attr.index === undefined) break; 19 | if (endTag === -1 || endTag < attr.index) break; 20 | 21 | const start = this.index + attr.index; 22 | this.eatTo(attr.index + attr[0].length); 23 | 24 | let name = attr[0].replace(/\s*=\s*$/, ""); 25 | let type = /^class:\S+/.test(name) 26 | ? "Directive" 27 | : ("Attribute" as "Directive" | "Attribute"); 28 | if (type === "Directive") name = name.replace(/^class:/, ""); 29 | 30 | const startChar = this.first; 31 | switch (startChar) { 32 | case `"`: 33 | case `'`: 34 | this.eat(startChar); 35 | this.eatSpace(); 36 | const children: (Text | Expression)[] = []; 37 | 38 | while (!this.end) { 39 | const nextBracket = searchNotEscape(this.food, "{"); 40 | const nextQuote = searchNotEscape(this.food, startChar); 41 | 42 | if (nextQuote < nextBracket || nextBracket === -1) { 43 | let value = this.spitSpace(this.eatTo(nextQuote)); 44 | if (value.data) children.push({ ...value, type: "Text" }); 45 | break; 46 | } 47 | 48 | // text before group 49 | const value = this.spitSpace(this.eatTo(nextBracket)); 50 | if (value.data) children.push({ ...value, type: "Text" }); 51 | 52 | // handle group 53 | this.eat("{"); 54 | const groupEnd = searchGroup(this.food); 55 | const group = this.eatTo(groupEnd); 56 | this.eat("}"); 57 | this.eatSpace(); 58 | children.push({ ...group, type: "Expression" }); 59 | } 60 | 61 | const endChar = this.eat(startChar); // eat end quote 62 | nodes.push({ start, end: endChar.end, type, name, value: children }); 63 | break; 64 | 65 | case "{": 66 | this.eat(startChar); 67 | const groupEnd = searchGroup(this.food); 68 | const value = this.eatTo(groupEnd); 69 | this.eat(startChar); // eat end bracket 70 | nodes.push({ 71 | start, 72 | end: value.end + 1, 73 | type, 74 | name, 75 | value: { ...value, type: "Expression" }, 76 | }); 77 | break; 78 | 79 | default: 80 | const one = this.food.match(/^[^\s>]+/)?.[0]; 81 | if (one) { 82 | const value = this.eat(one); 83 | nodes.push({ 84 | start, 85 | end: value.end, 86 | type, 87 | name, 88 | value: { ...value, type: "Text" }, 89 | }); 90 | } 91 | break; 92 | } 93 | } 94 | return nodes; 95 | } 96 | 97 | parse(): Tag[] { 98 | const output: Tag[] = []; 99 | while (!this.end) { 100 | const tag = this.food.match(/<[^\s/]+/); 101 | if (!tag || tag.index === undefined) break; 102 | const decl = this.eat(tag[0]); 103 | const value = this._parseTag(); 104 | this.eat(">"); 105 | output.push({ 106 | start: decl.start, 107 | end: this.index, 108 | name: tag[0].slice(1), 109 | value, 110 | }); 111 | } 112 | return output; 113 | } 114 | 115 | slice(start?: number, end?: number): string { 116 | return this.content.slice(start, end); 117 | } 118 | 119 | eat(str: string) { 120 | const start = this.index; 121 | this.index += str.length; 122 | return { 123 | start, 124 | end: this.index, 125 | data: str, 126 | }; 127 | } 128 | 129 | eatSpace() { 130 | const spaces = this.food.match(/^\s+/); 131 | if (spaces) this.index += spaces[0].length; 132 | } 133 | 134 | spitSpace({ 135 | start, 136 | end, 137 | data, 138 | }: { 139 | start: number; 140 | end: number; 141 | data: string; 142 | }) { 143 | const spaces = data.match(/\s+$/); 144 | if (spaces) { 145 | data = data.slice(0, spaces.index); 146 | end -= spaces[0].length; 147 | } 148 | return { start, end, data }; 149 | } 150 | 151 | eatTo(end: number) { 152 | const start = this.index; 153 | const food = this.food.slice(0, end); 154 | this.index += end; 155 | return { 156 | start, 157 | end: this.index, 158 | data: food, 159 | }; 160 | } 161 | 162 | get food() { 163 | return this.content.slice(this.index); 164 | } 165 | 166 | get first() { 167 | return this.content.charAt(this.index); 168 | } 169 | 170 | get end() { 171 | return this.index === this.content.length; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import MagicString from "magic-string"; 2 | import { Processor } from "windicss/lib"; 3 | import { CSSParser } from "windicss/utils/parser"; 4 | import { StyleSheet } from "windicss/utils/style"; 5 | import { default as HTMLParser } from "./parser"; 6 | import { getOptions, resolveConfig, combineStyleList } from "./utils"; 7 | import type { loader } from "webpack"; 8 | import type { Config as WindicssConfig } from "windicss/types/interfaces" 9 | 10 | const OPTIONS: { 11 | config?: string | WindicssConfig; 12 | compile?: boolean; 13 | prefix?: string; 14 | bundle?: string; 15 | globalPreflight?: boolean; 16 | globalUtility?: boolean; 17 | } = { 18 | compile: false, 19 | prefix: "windi-", 20 | globalPreflight: true, 21 | globalUtility: true, 22 | }; 23 | 24 | const REGEXP = { 25 | matchStyle: /]*?(\/|(>([\s\S]*?)<\/style))>/g, 26 | matchTemplate: /