@develate/quasar-app-extension-quasar-ext-tailwind 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thorsten
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 ADDED
@@ -0,0 +1,53 @@
1
+ # Quasar App Extension "@develate/quasar-ext-tailwind"
2
+
3
+ Adds [Tailwind CSS v4](https://tailwindcss.com) to a Quasar (Vite) app.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ quasar ext add @develate/quasar-ext-tailwind
9
+ ```
10
+
11
+ This will:
12
+
13
+ - add `tailwindcss`, `@tailwindcss/vite` and `@tailwindcss/postcss` to your app's
14
+ devDependencies,
15
+ - create `/src/css/tailwind.css`,
16
+ - add `'tailwind.css'` to the `css` array of your quasar.config file.
17
+
18
+ On each dev/build, the extension registers the `@tailwindcss/vite` plugin, so
19
+ there is nothing else to configure.
20
+
21
+ ## Uninstall
22
+
23
+ ```bash
24
+ quasar ext remove @develate/quasar-ext-tailwind
25
+ ```
26
+
27
+ This removes `/src/css/tailwind.css` and its `css` array entry. The Tailwind
28
+ packages themselves are left in your package.json — uninstall them yourself if
29
+ nothing else needs them.
30
+
31
+ ## Info
32
+
33
+ `/src/css/tailwind.css` is yours to edit — that is where you import Tailwind and
34
+ declare your `@theme`. It ships with the `tw` prefix (`tw:flex`, `tw:p-4`, ...)
35
+ so that Tailwind utilities cannot collide with Quasar's own classes.
36
+
37
+ Tailwind's preflight is included by default and resets browser styles globally,
38
+ which changes how some Quasar components look. To keep Quasar's baseline, import
39
+ the Tailwind layers you want instead of the whole package:
40
+
41
+ ```css
42
+ @layer theme, base, components, utilities;
43
+ @import 'tailwindcss/theme.css' layer(theme) prefix(tw);
44
+ @import 'tailwindcss/utilities.css' layer(utilities) prefix(tw);
45
+ ```
46
+
47
+ `@tailwindcss/postcss` is installed for tooling that goes through PostCSS (and
48
+ for editor integrations that look for it); the app itself is built with the Vite
49
+ plugin.
50
+
51
+ ## Donate
52
+
53
+ If you appreciate the work that went into this App Extension, please consider [donating to Quasar](https://donate.quasar.dev).
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@develate/quasar-app-extension-quasar-ext-tailwind",
3
+ "version": "1.0.2",
4
+ "description": "Tailwind CSS v4 for Quasar",
5
+ "keywords": [
6
+ "quasar",
7
+ "quasar-app-extension",
8
+ "quasar-app-vite",
9
+ "quasar-cli",
10
+ "tailwind",
11
+ "tailwindcss",
12
+ "vue",
13
+ "vuejs"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "Thorsten <thorsten@develate.com>",
17
+ "homepage": "https://github.com/develate/quasar-ext-tailwind#readme",
18
+ "bugs": "https://github.com/develate/quasar-ext-tailwind/issues",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/develate/quasar-ext-tailwind.git",
22
+ "directory": "ae"
23
+ },
24
+ "type": "module",
25
+ "main": "src/index.js",
26
+ "files": [
27
+ "src"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "imports": {
33
+ "#q-app": "@quasar/app-vite"
34
+ },
35
+ "devDependencies": {
36
+ "@quasar/app-vite": "^3.3.0",
37
+ "quasar": "^2.23.3",
38
+ "vue": "^3.5.39"
39
+ },
40
+ "peerDependencies": {
41
+ "@quasar/app-vite": "^3.3.0"
42
+ },
43
+ "engines": {
44
+ "node": ">= 26 || ^24 || ^22.12"
45
+ }
46
+ }
package/src/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Quasar App Extension index/runner script
3
+ * (runs on each dev/build)
4
+ *
5
+ * Docs: https://quasar.dev/app-extensions/development-guide/index-api
6
+ */
7
+
8
+ import { defineIndexScript } from '#q-app'
9
+
10
+ // can be async
11
+ export default defineIndexScript(api => {
12
+ api.compatibleWith('quasar', '^2.0.0')
13
+ api.compatibleWith('@quasar/app-vite', '^3.3.0')
14
+
15
+ // not fatal: the packages are added by the install script, which runs
16
+ // before they are actually put into node_modules by the packager
17
+ if (api.hasPackage('tailwindcss', '^4.0.0') === false) {
18
+ api.logger.warn(
19
+ 'Tailwind CSS v4 is not installed. Run' +
20
+ ` "quasar ext invoke ${api.extId}" to set it up.`
21
+ )
22
+ }
23
+
24
+ api.extendQuasarConf(conf => {
25
+ conf.build.vitePlugins.push(['@tailwindcss/vite', {}])
26
+ })
27
+ })
package/src/install.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Quasar App Extension install script
3
+ * https://quasar.dev/app-extensions/development-guide/install-api
4
+ */
5
+
6
+ import { basename } from 'node:path'
7
+ import { defineInstallScript } from '#q-app'
8
+
9
+ import { addCssEntry } from './lib/quasar-config-css.js'
10
+
11
+ const tailwindVersion = '^4.3.3'
12
+ const cssFile = 'tailwind.css'
13
+ const cssTarget = `src/css/${cssFile}`
14
+
15
+ // can be async
16
+ export default defineInstallScript(api => {
17
+ api.compatibleWith('quasar', '^2.0.0')
18
+ api.compatibleWith('@quasar/app-vite', '^3.3.0')
19
+
20
+ api.extendPackageJson({
21
+ devDependencies: {
22
+ '@tailwindcss/postcss': tailwindVersion,
23
+ '@tailwindcss/vite': tailwindVersion,
24
+ tailwindcss: tailwindVersion
25
+ }
26
+ })
27
+
28
+ api.renderFile(`./templates/${cssFile}`, cssTarget)
29
+
30
+ const configFile = api.ctx.appPaths.quasarConfigFilename
31
+ const configName = basename(configFile)
32
+
33
+ let status
34
+
35
+ try {
36
+ status = addCssEntry(configFile, cssFile)
37
+ } catch (err) {
38
+ api.logger.warn(`Could not read/write ${configName}: ${err.message}`)
39
+ status = 'unsupported'
40
+ }
41
+
42
+ if (status === 'added') {
43
+ api.logger.log(`Added "${cssFile}" to ${configName} > css`)
44
+ } else if (status === 'already-present') {
45
+ api.logger.log(`"${cssFile}" is already in ${configName} > css`)
46
+ } else {
47
+ api.onExitLog(
48
+ `Could not locate the "css" array in ${configName}.` +
49
+ ` Please add "${cssFile}" to it yourself.`
50
+ )
51
+ }
52
+ })
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Helpers to add/remove an entry from the "css" array of the host app's
3
+ * quasar.config file.
4
+ *
5
+ * That file is user-owned source code (js or ts), so it gets edited as text
6
+ * rather than parsed: only the first "css: [ ... ]" array is touched and the
7
+ * surrounding formatting is preserved as much as possible.
8
+ */
9
+
10
+ import fs from 'node:fs'
11
+
12
+ const cssArrayRegex = /\bcss\s*:\s*\[([^\]]*)\]/
13
+
14
+ function escapeRegExp(str) {
15
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
16
+ }
17
+
18
+ function matchCssArray(source) {
19
+ const match = source.match(cssArrayRegex)
20
+
21
+ if (match === null) return null
22
+
23
+ const inner = match[1]
24
+ const start = match.index + match[0].indexOf('[') + 1
25
+
26
+ return { inner, start, end: start + inner.length }
27
+ }
28
+
29
+ function replaceInner(source, { start, end }, newInner) {
30
+ return source.slice(0, start) + newInner + source.slice(end)
31
+ }
32
+
33
+ /**
34
+ * @param {string} configFile (absolute path to the quasar.config file)
35
+ * @param {string} entry (example: 'tailwind.css')
36
+ * @return {'added' | 'already-present' | 'unsupported'}
37
+ */
38
+ export function addCssEntry(configFile, entry) {
39
+ const source = fs.readFileSync(configFile, 'utf8')
40
+ const cssArray = matchCssArray(source)
41
+
42
+ if (cssArray === null) return 'unsupported'
43
+
44
+ const { inner } = cssArray
45
+
46
+ if (new RegExp(`(["'])${escapeRegExp(entry)}\\1`).test(inner)) {
47
+ return 'already-present'
48
+ }
49
+
50
+ // follow whatever quote style the app is already using
51
+ const quote = inner.match(/["']/)?.[0] ?? "'"
52
+ const item = `${quote}${entry}${quote}`
53
+
54
+ let newInner
55
+
56
+ if (inner.trim() === '') {
57
+ newInner = item
58
+ } else if (inner.includes('\n')) {
59
+ const indent = inner.match(/\n([ \t]*)\S/)?.[1] ?? ' '
60
+ const tail = inner.match(/\n[ \t]*$/)?.[0] ?? '\n'
61
+ newInner = `${inner.replace(/,?\s*$/, '')},\n${indent}${item}${tail}`
62
+ } else {
63
+ newInner = `${inner.replace(/,\s*$/, '')}, ${item}`
64
+ }
65
+
66
+ fs.writeFileSync(configFile, replaceInner(source, cssArray, newInner), 'utf8')
67
+ return 'added'
68
+ }
69
+
70
+ /**
71
+ * @param {string} configFile (absolute path to the quasar.config file)
72
+ * @param {string} entry (example: 'tailwind.css')
73
+ * @return {'removed' | 'missing' | 'unsupported'}
74
+ */
75
+ export function removeCssEntry(configFile, entry) {
76
+ const source = fs.readFileSync(configFile, 'utf8')
77
+ const cssArray = matchCssArray(source)
78
+
79
+ if (cssArray === null) return 'unsupported'
80
+
81
+ const { inner } = cssArray
82
+ const itemRegex = new RegExp(
83
+ `[^\\S\\n]*(["'])${escapeRegExp(entry)}\\1[^\\S\\n]*,?`
84
+ )
85
+
86
+ if (itemRegex.test(inner) === false) return 'missing'
87
+
88
+ let newInner = inner
89
+ .replace(itemRegex, '')
90
+ // the removed item might have left an empty line behind
91
+ .replace(/\n[ \t]*(?=\n)/g, '')
92
+ // ...or a dangling comma, if it was the last item
93
+ .replace(/,([^\S\n]*(?:\n[ \t]*)?)$/, '$1')
94
+ // ...or a leading space, if it was the first one
95
+ .replace(/^[^\S\n]+(?=\S)/, '')
96
+
97
+ if (newInner.trim() === '') newInner = ''
98
+
99
+ fs.writeFileSync(configFile, replaceInner(source, cssArray, newInner), 'utf8')
100
+ return 'removed'
101
+ }
@@ -0,0 +1,12 @@
1
+ @import 'tailwindcss' prefix(tw);
2
+
3
+ @theme {
4
+ --font-sans:
5
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
6
+ 'Noto Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
7
+ 'Segoe UI Symbol', 'Noto Color Emoji';
8
+ --font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
9
+ --font-mono:
10
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
11
+ 'Courier New', monospace;
12
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Quasar App Extension uninstall script
3
+ * https://quasar.dev/app-extensions/development-guide/uninstall-api
4
+ */
5
+
6
+ import { basename } from 'node:path'
7
+ import { defineUninstallScript } from '#q-app'
8
+
9
+ import { removeCssEntry } from './lib/quasar-config-css.js'
10
+
11
+ const cssFile = 'tailwind.css'
12
+ const cssTarget = `src/css/${cssFile}`
13
+
14
+ // can be async
15
+ export default defineUninstallScript(api => {
16
+ const configFile = api.ctx.appPaths.quasarConfigFilename
17
+ const configName = basename(configFile)
18
+
19
+ let status
20
+
21
+ try {
22
+ status = removeCssEntry(configFile, cssFile)
23
+ } catch (err) {
24
+ api.logger.warn(`Could not read/write ${configName}: ${err.message}`)
25
+ status = 'unsupported'
26
+ }
27
+
28
+ if (status === 'removed') {
29
+ api.logger.log(`Removed "${cssFile}" from ${configName} > css`)
30
+ } else if (status === 'unsupported') {
31
+ api.onExitLog(
32
+ `Could not locate the "css" array in ${configName}.` +
33
+ ` Please remove "${cssFile}" from it yourself.`
34
+ )
35
+ }
36
+
37
+ api.removePath(cssTarget)
38
+
39
+ api.onExitLog(
40
+ 'The Tailwind packages (tailwindcss, @tailwindcss/vite,' +
41
+ ' @tailwindcss/postcss) are still in your package.json.' +
42
+ ' Uninstall them if nothing else needs them.'
43
+ )
44
+ })