How to bundle tailwind css inside a Vue Component Package - vue.js

In one of my projects, I build a nice vue3 component that could be useful to several other projects. So I decided to publish it as an NPM package and share it with everyone.
I wrote the isolate component, build it and publish BUT I use Tailwind css to make the style.
When I publish and install the component everything is working BUT without the beauty of the css part.
I tried several configurations and alternative tools to generate the package that automatically add the tailwind as an inner dependency to my package.
Does someone have experience with this? how can build/bundle my component by adding the tailwind CSS instructions into it?

You're almost there
Since you've got your component working, the majority of the part has been done.
For configuring the styling of the component you need to identify the Tailwind CSS classes being used by your Vue component package and retain them in the final CSS that is generated by the Tailwind engine in your project.
Follow below steps in the project where you want to use your tailwind vue component package.
For Tailwind CSS V3
// tailwind.config.js
module.exports = [
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
]
For Tailwind CSS V2
// tailwind.config.js
module.exports = [
//...
purge: {
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
//...
}
]
The content property in the tailwind.config.js file defines file path pattern that the tailwind engine should look into, for generating the final CSS file.
For Pro users
You may also try to automate the above setup by writing an install script for your npm package to add this configuration to the tailwind.config.js file
References
Tailwind Docs - 3rd party integration

It's a bit difficult for someone to answer your question as you've not really shared the source code, but thankfully (and a bit incorrectly), you've published the src directory to npm.
The core issue here is that when you're building a component library, you are running npm run build:npm which translates to vue-cli-service build --target lib --name getjvNumPad src/index.js.
The index.js reads as follows:
import component from './components/numeric-pad.vue'
// Declare install function executed by Vue.use()
export function install (Vue) {
if (install.installed) return
install.installed = true
Vue.component('getjv-num-pad', component)
}
// Create module definition for Vue.use()
const plugin = {
install
}
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null
if (typeof window !== 'undefined') {
GlobalVue = window.Vue
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue
}
if (GlobalVue) {
GlobalVue.use(plugin)
}
// To allow use as module (npm/webpack/etc.) export component
export default component
There is no mention of importing any CSS, hence no CSS included in the built version.
The simplest solution would be to include the index.css import in your index.js or the src/components/numeric-pad.vue file under the <style> section.
Lastly, I'm a bit rusty on how components are built, but you might find that Vue outputs the CSS as a separate file. In that case, you would also need to update your package.json to include an exports field.

Related

VUE: SFC - use markdown in <docs> tag - vite throwing eror

So I inherited an old (Vue 2) app that uses Styleguidist for creating style guide and documenting components...
It was running extra slow so my first task was to upgrade to using vite instead of webpack. Almost there... fixed almost all the issue, the one is outstanding though... this app uses this format of *.vue components
<template>...</template>
<script>...</script>
<style>...</style>
<docs>
Example of usage
```jsx
<MyComponent>...</MyComponent>
</docs>
where content inside is markdown, so one can write nicer documentation with code example
Now, vite is complaining that I am trying to use jsx (where I am not)...
this is the error
3:36:36 PM [vite] Internal server error: Failed to parse source for
import analysis because the content contains invalid JS syntax. If you
are using JSX, make sure to name the file with the .jsx or .tsx
extension. Plugin: vite:import-analysis
So what am I to do? How do I tell VITE to ignore that part?
The solution, as posted here, is to create a small Vite plugin that ignores the <docs> blocks.
Add this to vite.config.js:
const vueDocsPlugin = {
name: 'vue-docs',
transform(code, id) {
if (!/vue&type=docs/.test(id))
return;
return `export default ''`;
}
};
Then add the plugin to the plugins array:
export default defineConfig({
plugins: [
// vue() will be here...
vueDocsPlugin,
],
});

Vue: icons are not displayed when css.extract: false

When building a Vue library (component), according to Vue docs, you can set css.extract: false in vue.config.js to avoid the users having to import the CSS manually when they import the library into an app:
vue.config.js
module.exports = {
css: {
extract: false
}
}
However, when you do that, the icons are not displayed in the production build.
In this case I'm using #mdi/font and weather-icons. Neither of them load:
To reproduce
You can reproduce this with this Vue library (component):
Create new Vue project with vue create test
Clone the repo and put in the same directory as the Vue test project
In vue-open-weather-widget set css.extract: false in vue.config.js;
And comment out CSS import:
import 'vue-open-weather-widget/dist/vue-open-weather-widget.css'
Build vue-open-weather-widget with yarn build
Import it into the test Vue app with yarn add "../vue-open-weather-widget";
Serve the test app yarn serve
I have looked at your lib (nice component BTW). I created a build with css: { extract: false } and first looked at the behavior when importing vue-open-weather-widget.umd.js directly into an HTML file. And that worked without any problems.
The thing is that the fonts remain external in the dist after the build. And it seems that there is a problem to find the fonts when your component is loaded in a Webpack project (in our case Vue CLI project). I don't know why the fonts are not referenced correctly. But I have found another, and possibly a better solution.
As it is stated in the MDI docs, the use of the web fonts can negatively affect the page performance. When importing only one icon, all of them are imported, which in turn increases the bundle size. In such a small component this is more than suboptimal, especially for the component users. Therefore here is the alternative solution, also suggested by MDI:
Use #mdi/js instead of #mdi/font
Remove all #mdi/font references in your code and install deps:
npm install #mdi/js #jamescoyle/vue-icon
Replace all icons with SVG(e.g. in MainView.vue). Note that on this way only icons are included in the bundle that are used in your components:
...
<span #click="state.settings.view = 'settings'">
<svg-icon type="mdi" :path="mdiCogOutline"></svg-icon>
</span>
...
import SvgIcon from '#jamescoyle/vue-icon'
import { mdiCogOutline } from '#mdi/js'
...
components: {
SvgIcon
},
data () {
return {
mdiCogOutline: mdiCogOutline
}
},
Adjust vue.config.js:
module.exports = {
css: {
extract: false
}
}
Build component:
# i would also include --formats umd-min
vue-cli-service build --target lib --formats umd-min --name vue-open-weather-widget src/main.js
Now your dist contains only 192.68 KiB vue-open-weather-widget.umd.min.js and the component is ready to use over CDN or in a Vue CLI Project, without importing any CSS or fonts. I have tested both cases. Here is how it looks like:
Hope it helps you! Feel free to ask if you have further questions.

Vue CLI plugin CSS preprocessor (sass) transpile in parent

We have a pretty standard Vue CLI environment. It currently ingests a vue plugin we created via the install method. The plugin is also a Vue CLI environment and lives as a git submodule in the repo.
Currently the parent uses sass and sass-loader packages to transpile. It's configured in the vue.config.js settings, like so:
module.exports = {
// Other properties and settings removed to simplify
css: {
loaderOptions: {
sass: {
data: `
#import "#/styles/global.scss";
`
}
}
}
The global.scss just houses all our style includes.
The plugin is set up in a similar way, but none of the code is getting ingested into the parent. Which totally makes sense, as there is nothing importing/building the plugins style files. Anyone know how to import and transpile plugin sass style sheets? Thank you!

How to add tailwindcss to KotlinJS

I am unable to add the tailwindcss library to my KotlinJS project. I tried multiple things.
I have multiple dependencies defined in my build.gradle.kts
implementation(npm("postcss", "latest"))
implementation(npm("postcss-loader", "latest"))
implementation(npm("tailwindcss", "1.8.10"))
I tried creating a tailwindcss.js in my webpack.config.d with this content
config.module.rules.push({
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'tailwindcss'
],
],
},
},
}
]
}
);
But that doesn't do anything. I also tried modifying this with multiple options, but I was never able to get tailwindcss to compile. I also tried disabling and enabling the KotlinJS CSS support in build.gradle.kts
I can't find any info on how to add postcss to KotlinJS project.
Thank you for any help.
A basic integration can be achieved with the node-gradle plugin.
In your build.gradle.kts:
plugins {
id("com.github.node-gradle.node") version "3.0.0-rc2"
}
Also in build.gradle.kts define a task called "tailwindcss" that calls the tailwind CLI via npx. For example:
val tailwindCss = tasks.register<com.github.gradle.node.npm.task.NpxTask>("tailwindcss") {
// Output CSS location
val generatedFile = "build/resources/main/static/css/tailwind-generated.css"
// Location of the tailwind config file
val tailwindConfig = "css/tailwind.css"
command.set("tailwind")
args.set(listOf("build", tailwindConfig, "-o", generatedFile))
dependsOn(tasks.npmInstall)
// The location of the source files which Tailwind scans when running ```purgecss```
inputs.dir("src/main/kotlin/path/to/your/presentation/files")
inputs.file(tailwindConfig)
outputs.file(generatedFile)
}
Finally, in build.gradle.kts bind the task to your processResources step, so that it runs automatically. Note you may want to refine this later, because running tailwind every time the processResources step is invoked will slow down your dev cycle.
tasks.processResources {
dependsOn(tailwindCss)
}
Now we need a minimal package.json in the root of your project. For example:
{
"name": "MyProject",
"devDependencies": {
"tailwindcss": "^1.7.0"
}
}
Finally, we configure our tailwind config in the location defined by our NpxTask, in the example ```css/tailwind.css"
#tailwind base;
#tailwind components;
#tailwind utilities;
So now after the processResource step is run, gradle will invoke the Tailwind npx task, consume your source and write the CSS to the location you specified.
The accepted answer seems to not work anymore. Also, using the Node Gradle plugin is sub-optimal (KotlinJS already maintains its own package.json and yarn installation).
I managed to get Tailwind to work with KotlinJS thanks for this repository (GitHub) with a few small updates that you can find here (GitLab).
The linked I posted is the answer, the whole repository. It is not just a part of it
If you instead want me to copy/paste the whole repository instead here you're
= Kotlin/JS + Tailwind CSS =
This is a small sample repository to show the idiomatic way of
configuring these two systems together.
== Running it ==
. Run `./gradlew run`.
. Open `http://localhost:8080/` in your browser.
. 🎉 Notice we're using Tailwind CSS classes successfully.
== How To ==
Steps taken to make this work:
=== Dependencies ===
Add the following dependencies to your JS target (`jsMain` dependencies) in your Gradle file:
[source,kotlin]
----
implementation("org.jetbrains:kotlin-extensions:1.0.1-pre.148-kotlin-1.4.21")
implementation(npm("postcss", "8.2.6"))
implementation(npm("postcss-loader", "4.2.0")) // 5.0.0 seems not to work
implementation(npm("autoprefixer", "10.2.4"))
implementation(npm("tailwindcss", "2.0.3"))
----
* `kotlin-extensions` is necessary to get the JavaScript link:https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-extensions/src/main/kotlin/kotlinext/js/CommonJS.kt#L20[`require`] function.
** Make sure the version number matches your version of the Kotlin multiplatform plugin at the top of your Gradle file.
** Kotlin Multiplatform 1.4.30 gave me `No descriptor found for library` errors. Try 1.4.21.
** Find the latest versions link:https://bintray.com/kotlin/kotlin-js-wrappers/kotlin-extensions[here].
* `postcss` and `autoprefixer` are link:https://tailwindcss.com/docs/installation#install-tailwind-via- npm[dependencies] as mentioned in the Tailwind CSS docs.
* `postcss-loader` is required because Kotlin/JS is built on top of Webpack.
** Note that while 5.0.0 is out, using it gave me build errors. The latest 4.x seems to work.
* `tailwindcss` is obviously what we're here for.
=== Add Tailwind as a PostCSS plugin ===
Just do link:https://tailwindcss.com/docs/installation#add-tailwind-as-a-post-css-plugin[this step].
If unsure, create this file in your project root:
[source,javascript]
----
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
----
=== Create your configuration file (optional) ===
link:https://tailwindcss.com/docs/installation#create-your-configuration-file[Official documentation].
Creating the `tailwind.config.js` file is a little tricky because simply `npx` won't work, as we haven't installed any
`node_modules`. Fortunately, Kotlin/JS has already done this for us.
Run the following:
[source,shell]
----
$ ./gradlew kotlinNpmInstall
$ ( cd build/js/ && npx tailwindcss init && mv tailwind.config.js ../../ )
----
This generates `tailwind.config.js` in the `build/js/` directory and then moves it up two directories to the project
root. Kotlin/JS generates the node modules into build/js/node_modules when the kotlinNpmInstall task runs.
This assumes your JavaScript module is `js`. If it's not, you'll need to change the `cd build/js/` part. If you're not
sure where your node_modules directory is, run find . -maxdepth 3 -name node_modules.
You should now have all your dependencies set up and config files created.
=== Create and Reference a Regular CSS File ===
_If you already have a CSS file that you're loading in your app, you can skip this step._
Create `app.css` in your `jsMain/resources/` directory. Put something obvious in there so you know
when it's loaded:
[source,css]
----
body {
background-color: red;
}
----
This file will get copied into the same folder as your transpiled JavaScript files.
In your JavaScript file (`client.kt` in this package), add:
[source,javascript]
----
kotlinext.js.require("./app.css")
----
to your main method. You can of course import the require method if you prefer.
If you run `./gradlew run`, you should be able to see a red page at `http://localhost:8080/`.
We're almost there, but we have two more steps: tell Webpack to use PostCSS and to finally inject Tailwind CSS.
=== Using PostCSS with Webpack ===
We want to "monkeypatch" the Webpack configuration that Kotlin/JS generates for us. This hook is
documented in the link:https://kotlinlang.org/docs/js-project-setup.html#webpack-bundling[webpack bundling] section. Basically, if we create .js files in webpack.config.d/, they'll be automatically
merged into build/js/packages/projectName/webpack.config.js, which exists after a build and you can go inspect.
The "problem", if you have `cssSupport.enabled = true` in your Gradle file (which you should!), is that this line
generates a webpack rule matching /\.css$/. We can't simply create another rule matching the same files...that
won't work.
So, we need to find the original rule and modify it. Create the following file relative to your project root:
[source,javascript]
----
// in webpack.config.d/postcss-loader.config.js
(() => {
const cssRule = config.module.rules.find(r => "test.css".match(r.test));
if (!cssRule) {
throw new Error("Could not resolve webpack rule matching .css files.");
}
cssRule.use.push({
loader: "postcss-loader",
options: {}
});
})();
----
We use an IIFE so that our new variable doesn't potentially interfere with other unseen variables.
Now PostCSS is working!
With PostCSS configured and the `tailwindcss` npm module in our dependencies, all that's left now
is to use it.
=== Importing Tailwind CSS ===
We're basically smooth sailing from here. Follow the link:https://tailwindcss.com/docs/installation#include-tailwind-in-your-css[Include Tailwind in your CSS] directions.
Just stick the following in your `app.css`:
[source,css]
----
#tailwind base;
#tailwind components;
#tailwind utilities;
----
If you start the server again, it should **Just Work**! It's a bit hard to tell, but if you check the devtools,
you should see the tw classes loading and massive js.js file being loaded (9.20mb!) which contains all of Tailwind CSS.
== Areas for Improvement ==
=== Modifications to app.css ===
Changes made to app.css don't get picked up unless you do a full `./gradlew clean` first, which is painful.
Adding the following line to build.gradle.kts seems to fix this:
[source,kotlin]
----
tasks.withType(KotlinWebpack::class.java).forEach { t ->
t.inputs.files(fileTree("src/jsMain/resources"))
}
----
=== Getting --continuous working ===
Even with the above fix, --continuous doesn't seem to work. 🤷
== Future Topics ==
* link:https://tailwindcss.com/docs/installation#building-for-production[Building for Production]

how to override vue cli-service entry settings

I'm trying to integrate a vue project that I built with the vue cli into an existing .net app. I'm very new to vue, so I'm trying to follow guides and such, but am left with lots of questions.
While trying to compile this, I found that the vue cli-service node module has the following for setting the main.js file located in it's base.js file.
webpackConfig
.mode('development')
.context(api.service.context)
.entry('app')
.add('./src/main.js')
.end()
.output
.path(api.resolve(options.outputDir))
.filename(isLegacyBundle ? '[name]-legacy.js' : '[name].js')
.publicPath(options.publicPath)
I need to override this since my .net app doesn't have a src directory and the usage of this vue app won't follow that path structure. I'm not seeing a way to do it in my vue.config.js file. I would expect that if I can override it, that would be the spot.
I could overwrite the base.js file where this exists, but when a co-worker runs npm install, they would get the default value rather than what I have. The only option I see there is checking in all the node modules to git which we really don't want to do.
For anyone in a similar situation, I found what worked for me. It's not the ideal solution due to the fact that it forces you to build into a js folder. That resulted in the file being put in Scripts\build\vue\js. Would be nice to be able to just dump it in the vue folder, but at least this works. Code below.
vue.config.js
module.exports = {
publicPath : "/",
outputDir: "Scripts/build/vue", //where to put the files
// Modify Webpack config
// https://cli.vuejs.org/config/#chainwebpack
chainWebpack: config => {
// Not naming bundle 'app'
config.entryPoints.delete('app'); //removes what base.js added
},
// Overriding webpack config
configureWebpack: {
// Naming bundle 'bundleName'
entry: {
quote: './Scripts/Quote/index.js' //where to get the main vue app js file
},
optimization: {
splitChunks: false
}
},
filenameHashing: false,
pages: {
quoteApp: { //by using pages, it allowed me to name the output file quoteApp.js
entry: './Scripts/Quote/index.js',
filename: 'index.html'
}
}
}