Icons not shown for controls [duplicate] - vue.js

I have an application that uses UIkit, Less for local styling, and Vite for frontend tooling (bundling and whatnot). I'm not sure that this is relevant, but this is a Vue 2/Webpack application that I'm upgrading to Vue 3/Vite.
Per UIkit's Less documentation, we import UIkit's uikit.theme.less file in our project's base less file. UIkit's stylesheets have some relative paths to SVG files that get run through less's data-uri function (examples below). That worked fine with Webpack, but with Vite it's not quite working. As I understand it, for small files, data-uri will UTF-8 encode the asset and essentially inline it--at least that's what we got in our Webpack bundles. But with our Vite build, it seems these relative image paths aren't being resolved, data-uri hence falls back to a url(), and we get 404s for the images.
For example, in UIkit's code the relative path to a spinner image is defined here; it's used in a select.uk-select. And here that path is passed to a .svg-fill mixin (here). When we bundle the code using Vite, or run a local dev server, the result is:
background-image: url(../../images/backgrounds/form-select.svg);
That, of course, doesn't load because the "images" directory is relative to the form.less file. In Webpack, the output was as expected:
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23001c30' d='M12 1 9 6h6zM12 13 9 8h6z'/%3E%3C/svg%3E");
For this type of question, I would normally include an HTML/CSS/JS snippet; however, I don't think SO supports Vite. As such, I'm including a small Stackblitz that minimally demonstrates the issue: https://stackblitz.com/edit/vite-esqmqc?file=main.js Please see main.js, style.less, and note that there's a 404 error in the console complaining about the aforementioned form-select.svg file.
In case the question isn't clear, how can I get Vite to resolve images which are relative to a dependency in node_modules?
Thanks in advance.

A workaround is to configure resolve.alias to point ../../images to uikit/src/images (which resolves to the uikit package under node_modules/). This tells Vite how to resolve the problematic relative image paths.
The resolve.alias config is passed to #rollup/plugin-alias as entries. Each entry can have a custom resolver that can be used to only replace imports from UIKit. However, it requires that the import of uikit.theme.less be in its own file so that the custom resolver can correctly identify the importer in order to determine when to replace the import.
Put the import of uikit.theme.less in its own file, and import that from main.js (not from style.less):
// style.less
// #import './node_modules/uikit/src/less/uikit.theme.less'; ❌ move to own file
// my-uikit.less
#import './node_modules/uikit/src/less/uikit.theme.less';
// main.js
import './style.less';
import './my-uikit.less';
Create vite.config.js with the following configuration:
// vite.config.js
import { defineConfig } from 'vite';
import { fileURLToPath } from 'url';
import { basename } from 'path';
export default defineConfig({
resolve: {
alias: [
{
find: '../../images',
replacement: '',
customResolver(updatedId, importer, resolveOptions) {
// don't replace if importer is not our my-uikit.less
if (basename(importer) !== 'my-uikit.less') {
return '../../images';
}
return fileURLToPath(
new URL(
'./node_modules/uikit/src/images' + updatedId,
import.meta.url
)
);
},
},
],
},
})
demo

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,
],
});

How to bundle tailwind css inside a Vue Component Package

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.

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 v3 - How does it know what JS files should be chunked in a different file(s)

I am having hard times wrapping my mind around new CLI and configuration.
In the official documentation, I couldn't really find anything about CSS and how to add it as an entry point and not import it directly into an component or main.js file.
I realized that some JS files are being chunked into separate file, from main.js and the rest gets compiled where supposed to - into the app.js.
I was wondering, how does it know in the background what should be stored as "vendor" for the JS, but when I try to import some "vendor" SASS files into main.js it does not and it merges all within a single app.css file.
Can anyone tell me, how does one create/modify the vue.config.js and tell the bundler that I also want app.scss to be an entry point and vendor.scss to be another entry point.
I am unsure what are best practices for such purpose, but I always did it this way with my own webpack config...
Partial example below:
entry: {
vendor: [
'./styles/vendor.scss',
'./scripts/vendor.js'
],
app: [
'./styles/app.scss',
'./scripts/app.js'
]
}
EDIT #1
I think I got the first one...
"How does it know what should be chunked in "vendor" files?
Whatever gets imported from node_modules, it is being chunked.
What I did not figure out yet is... What if I am having my personal assets/styles/vendor directory where I #import those SASS files from NPM and do some modifications of variables or whatever.
Importing this file to main.js does not get chunked in this case... So there must be a way to tell bundler that I want everything within that directory or everything within vendor.scss file where everything is being imported, to be chunked out.
EDIT #2
I figured I can use WebPack's magical comments to import the main vendor SCSS file, such as:
import(/* webpackChunkName: "vendor" */ './assets/styles/vendor.scss')
I don't have a problem with this, but apparently the bundler does. It generates an empty vendor.[hash].js file as well.
EDIT #3
I did further research and learned that there's a command vue inspect which would output the webpack configuration.
So when making tweaks to vue.config.js, we can look a the output with this command if there's a bug or something is not working as expected.
Further more, I learned that if we specify entry directly in our vue.config.js file, that we will get an error that entry cannot be specified within our configuration file.
The following is forbidden to do so, but it's what I actually want to achieve...
// vue.config.js
module.exports = {
entry: {
app: [
'./src/main.js',
'./src/assets/styles/app.scss'
],
vendor: [
'./src/assets/styles/vendor.scss'
]
}
}
The actual proper way to do this will be an answer to my own question...
The way to achieve this is by using WebPack's Chain API.
However, if I did everything correctly, I still see a problem of generated vendor.[hash].js file with some WebPack module boilerplate. This JS file is also being injected to the index.html template.
Which leads to the same outcome as the attempt of my EDIT #2, except that we're no longer importing our Sass files within main.js
To modify entry points for my purpose of this question, we can do it the following way:
// vue.config.js
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
}
}
Note
We're not specifying the app entry JS file, which would be main.js by default, because we're not overriding the current entry point. Instead, we're extending it, so everything works as expected.
UPDATE
Until WebPack resolves this in future major releases, I found a great package - fqborges/webpack-fix-style-only-entries. It solves this issue that I was having and I'd suggest you to use it.
Final configuration would look like this:
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}
UPDATE #2
After further investigation and use of such configuration for projects, I realized that I had to use !important in styles where I had a need to override anything vendor related.
This is simply because WebPack will inject app, before vendor (both JS and CSS) and it will cause such issue.
Even if we modify the configuration from above and move app entry, below the vendor entry, it will still fail. Reason being, because we're modifying the entry point which already exists by default within vue-cli config. We're adding more entries to the app and we're adding new vendor entry.
To fix this issue of ordering, we must delete the app entirely and then create it ourselves.
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config.entryPoints.delete('app')
config
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
.entry('app')
.add('./src/main.js')
.add('./src/assets/styles/app.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}

Server a static pdf - Vue CLI - Webpack - issue

I build a little front thanks to VueCLI (+ Vuetify).
I wanted to render a button where onClick open a new tab with a pdf located in the folder's tree of the project.
I have an error and after hours looking why, it's seems to be a webpack conf to modify.
I finally read this answer on S/O ; Serving static pdf with react webpack file loader
But I got an error saying include: paths -> paths is not defined
I have to admit that I have no clue how webpack works behind the scene so any help would be find.
You probably don't need Webpack for this: you can just put it in your static folder and link to it pdf. If you want to go the Webpack route, put it in your src/assets directory and import it and add it to data object:
import pdf from '../assets/mypdf.pdf'
//...
data: () => ({ pdf })
//...
Then use the link in your component:
<a :href="pdf">pdf</a>
You probably added include: paths to the loader config but you didn't define it, instead of modifying your image loader, add a new one for PDFs:
{
test: /\.(pdf)(\?.*)?$/,
loader: 'url-loader',
options: {
name: utils.assetsPath('[name].[hash:7].[ext]')
}
}
You can change [name].[hash:7].[ext] if you want, say you want to add a pdfs directory in assets instead of using the base assets directory you would use: pdfs/[name].[hash:7].[ext]