webpack/vue/nuxt - Map import if file exists (overwrite) - vue.js

In my current project I am trying to implement the following:
Let's assume the following file structure:
/src/
/src/components/MyComponent.vue
/src/overwrites/components/MyComponent.vue
(in any *.vue file):
import MyComponent from '#/components/MyComponent.vue'.
This import should, if the same file exists in the parallel existing directory "overwrites", import it instead of the one from /src/components/.
To benefit from hot reloading etc, I want to solve this via webpack.
However, how to extend the vue-loader or get in front of it I haven't figured out.

You can use require.context to crawl directory for components without a webpack or vue-loader involved.
For instance you have structure:
.src
+-- components
| +-- MyComponent.vue
+-- overwrites
| +-- components
| | +-- MyComponent.vue
You will put index.js file in every components folder with content:
const requireModule = require.context('./', false, /\.vue$/)
requireModule.keys().forEach(fileName => {
if (fileName === './index.js') return;
const moduleName = pascalCase(
fileName.replace(/(\.\/|\.vue)/g, '')
);
components[moduleName] = requireModule(fileName)
});
export default components;
Then you will be able to import those index.js files in root component file like:
import components from './components/index.js';
export default {
components: components,
// ...
}
It will load all vue files from components directory. It will solve the issue of hot reloading.
Though, you will have to manage loading of duplicated components on your own, probably with Object.assing like:
import components from './components/index.js';
import {components as overwrites} from './overwrites/components/index.js';
export default {
components: Object.assign({}, components, overwrites),
// ...
}
Duplicated components will be substituted.

I solved the problem with a custom webpack resolver. Similar to this one:
https://github.com/hollandThomas/webpack-custom-resolver/blob/master/StyleResolverPlugin.js
However, I check for ".vue" instead of ".js" and then I substitute the path instead of the filename.

Related

Icons not shown for controls [duplicate]

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

Path aliases not working in vue script block

I'm trying to completely understand the path aliases with Vue and Vite.
Outside of the <script> block (e.g. <style> and <template> or other .js files) absolute paths with ~ or the # alias, for getting the root directory are working. But inside I still need to use ../../../ for getting back to the root. If I try using ~ or # I get errors for files not being found.
Also wouldn't # and ~ do the same in that case?
EDIT:
// Somehow working cause it's no component but a mere .js file
import {
filterwords
} from '#/services/signup/filterwords.js';
// Working
import passwordMeter from '../../utility/PasswordMeter.vue';
// Not working
import passwordMeter from '~/utility/PasswordMeter.vue';
import passwordMeter from '#/utility/PasswordMeter.vue';
Do proper changes in vite.config.js. Tested this locally and it solves it.
Add '~' if you also want to use that.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
const path = require('path')
// https://vitejs.dev/config/
export default defineConfig({
resolve:{
alias:{
'#' : path.resolve(__dirname, './src')
},
},
plugins: [vue()]
})
Credit to this article: https://vueschool.io/articles/vuejs-tutorials/import-aliases-in-vite/

How to import js file from cdn?

How do we import js file from cdn?
I am building a custom component (a wrapper component) to view the pdf files. and for this I need to use pdf.js file from cdn and I am unable to import the file?
following does not work
import "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js";
To import the JS file from CDN and use in vue.js component, you can use mounted lifecycle and there you need to append your script into DOM.
Then it will be available into your window global variable.
Working code example:
mounted () {
let pdfJS = document.createElement('script')
pdfJS.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js')
document.head.appendChild(pdfJS)
// for checking whether it's loaded in windows are not, I am calling the below function.
this.checkPDFJSLib()
},
methods: {
checkPDFJSLib() {
console.log(window.pdfjsLib)
}
}
You can normally include it in your index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>
Alternatively, you can use an npm library e.g vue-pdf
install it into your modules
npm install --save vue-pdf
and use it
import pdf from 'vue-pdf'

Loading vuetify in a package that i use in a vuetify project

What is the correct way of loading vuetify into a package that i use in a vuetify project?
When serving projects it all seems to work fine but when i build the project i've got some issues with the css/sass
things i've tried:
With vuetify loader: the css is loaded twice so i can't overwrite sass variables
Without vuetify loader: the package doesn't have the vuetify css, so it looks horrible
Without vuetify loader with vuetify.min.css: the css is loaded twice so i can't overwrite sass variables, and the loaded css is all the css so it's huge
My package is called vuetify-resource, and this is the source code of the index.js (without the vuetify loader) At this point everything works on npm run serve But when i build the package doesn't have "access" to the vuetify css.
import Vue from 'vue';
import Vuetify from 'vuetify';
import VuetifyResourceComponent from './VuetifyResource.vue';
Vue.use(Vuetify);
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
To solve my issue i had to do a couple of things.
Make peer dependencies of vuetify and vue
add vuetify to the webpack externals, so when someone uses the package, the package uses that projects vuetify
not longer import vue and vuetify in the index.js it's not needed, the project that uses the package imports that
import the specific components that you use in every .vue file
for example:
Vue.config.js
module.exports = {
configureWebpack: {
externals: {'vuetify/lib': 'vuetify/lib'},
},
};
index.js
import VuetifyResourceComponent from './VuetifyResource.vue';
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
part of the component.vue
import { VDataTable } from 'vuetify/lib';
export default {
name: 'vuetify-resource',
components: {
VDataTable
},
Step 4 in Ricardo's answer is not needed if you use vuetify-loader, it will do the job for you.
And I would modify step 2 to also exclude Vuetify's styles/css from your bundle. If you don't exclude them you can run into styling issues when the Vuetify version differ between your library and your application.
Use a regular expression in vue.config.js like this: configureWebpack: { externals: /^vuetify\// }. That way, only your own styles are included in the library bundle.

bootstrap-vue's `b-tabs` renders differently depending on the Vue build that I import

I'm using bootstrap-vue and I've noticed b-tabs renders differently depending on the Vue build that I import:
If I import vue it renders correctly:
https://codesandbox.io/s/vue-template-77mzg
But if I import vue/dist/vue.common or vue/dist/vue It renders wrongly:
https://codesandbox.io/s/vue-template-y0t15
Also, it doesn't happen with other components, like b-navbar-nav. They render correctly regardless of the vue build I import.
I'd like to understand why does it happen, since I need to import a vue version that includes the compiler because some components need it.
Thanks!
When importing a specific variant of Vue (i.e. commonjs vs ES), you need to set up an alias in webpack to ensure that BootstrapVue (and other dependants such as PortalVue) use the same build of Vue (as BootstrapVue also imports from vue).
See the docs on setting up aliases (so you can just import Vue from 'vue'):
https://bootstrap-vue.js.org/docs#aliasing-vue-import
i.e. for Webpack config
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
}
}