Error while moving component from application to a library - vue.js

I am having troubles while creating a library of components in top of Vuetify 2.x.
I have created a simple navigation-drawer wrapper:
<template>
<v-navigation-drawer app v-model="drawer">
<v-list-item>
<v-list-item-content>
<v-list-item-title class="title">
Application
</v-list-item-title>
<v-list-item-subtitle>
subtext
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-navigation-drawer>
</template>
<script>
export default {
name: 'MySidebar',
data: () => ({
drawer: null,
}),
};
</script>
When this component is placed inside my application, it works as expected. However, when the same component is on a library, the application crashes.
<template>
<v-app>
<my-sidebar/>
</v-app>
</template>
<script>
import MySidebar from 'my-library/src/components/MySidebar.vue'; // Error
import MySidebar from './src/components/MySidebar.vue'; // Works
export default {
name: 'App',
components: {
MySidebar,
},
};
</script>
On the first import, the application fails to get the this.$vuetify object, but when I console.log(this.$vuetify), it is there.
Some of the console messages:
TypeError: Cannot read property 'breakpoint' of undefined
at VueComponent.isMobile (VNavigationDrawer.ts?6ca0:193)
at Watcher.get (vue.runtime.esm.js?f9ee:4473)
...
[Vue warn]: Error in getter for watcher "isMobile": "TypeError: Cannot read property 'breakpoint' of undefined"
found in
---> <VNavigationDrawer>
<MySidebar> at my-library/src/components/MySidebar.vue
<VApp>
<App> at src/App.vue
<Root>
...
TypeError: Cannot read property 'breakpoint' of undefined
at VueComponent.isMobile (VNavigationDrawer.ts?6ca0:193)
at Watcher.get (vue.runtime.esm.js?f9ee:4473)
...
[Vue warn]: Error in created hook: "TypeError: Cannot read property 'application' of undefined"
found in
---> <VNavigationDrawer>
<MySidebar> at my-library/src/components/MySidebar.vue
<VApp>
<App> at src/App.vue
<Root>
Additional Information:
My Library has vuetify as dev dependency for development proposes
I am using the library via npm link

With some help from the Vuetify team, I found out that the problem was that Webpack was using Vuetify from the library instead of using from the app. This was caused by the use of npm link on my-app to use my-library together with Vuetify installed as dev-dependency on my-library.
Some possible solutions:
On my-library, use npm install --only=prod instead of npm install. This will prevent the conflict by not installing Vuetify on the library.
On my-app, add the following lines to vue.config.js:
const path = require('path');
module.exports = {
publicPath: process.env.VUE_APP_PUBLIC_PATH,
configureWebpack: {
resolve: {
alias: {
/* Use vuetify from my-app, not from my-library */
vuetify: path.resolve(__dirname, 'node_modules/vuetify'),
},
},
},
};
This will tell explicitly to Webpack to chose the Vuetify installation from my-app.
Other solutions that did not fit my requirements:
Stop using npm link
Do not install Vuetify on my-library, install it only on my-app

I had the exact same issue and solved it by deleting the node_modules folder in the component library folder (i.e. rm -rf my-library/node_modules in your case).

Related

How to create a library exposing a single Vue component that can be consumed by the distributed .mjs file?

I want to create a single Vue component that gets bundled into a single .mjs file. Another Vue project can fetch this .mjs file via HTTP and consume the component. Installing the pluggable component via npm is not possible, because the other application tries to fetch it based on a configuration during runtime.
Things to consider for the pluggable component
Might be using sub components from another UI framework / library
Might be using custom CSS
Might rely on other files e.g. images
Reproducing the library
I created a new Vuetify project via npm create vuetify
I deleted everything from the src folder except vite-env.d.ts , created a component Renderer.vue
<script setup lang="ts">
import { VContainer } from "vuetify/components"
defineProps<{ value: unknown }>()
</script>
<template>
<v-container>
<span class="red-text">Value is: {{ JSON.stringify(value, null, 2) }}</span>
</v-container>
</template>
<style>
.red-text { color: red; }
</style>
and an index.ts file
import Renderer from "./Renderer.vue";
export { Renderer };
I added the library mode to the vite.config.ts
build: {
lib: {
entry: resolve(__dirname, "./src/index.ts"),
name: "Renderer",
fileName: "renderer",
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
},
and extended the package.json file with
"files": ["dist"],
"main": "./dist/renderer.umd.cjs",
"module": "./dist/renderer.js",
"exports": {
".": {
"import": "./dist/renderer.js",
"require": "./dist/renderer.umd.cjs"
}
},
Since I'm using custom CSS Vite would generate a styles.css file but I have to inject the styles into the .mjs file. Based on this issue I'm using the plugin vite-plugin-css-injected-by-js.
When building I'm getting the desired .mjs file containing my custom CSS
Consuming the component
I created a new Vue project via npm create vue
and for testing purposes I copied the generated .mjs file right into the src directory of the new project and changed the App.vue file to
<script setup lang="ts">
import { onMounted, type Ref, ref } from "vue";
const ComponentToConsume: Ref = ref(null);
onMounted(async () => {
try {
const { Renderer } = await import("./renderer.mjs"); // fetch the component during runtime
ComponentToConsume.value = Renderer;
} catch (e) {
console.log(e);
} finally {
console.log("done...");
}
});
</script>
<template>
<div>Imported component below:</div>
<div v-if="ComponentToConsume === null">"still loading..."</div>
<component-to-consume v-else :value="123" />
</template>
Unfortunately I'm getting the following warnings and errors
[Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef instead of ref.
[Vue warn]: injection "Symbol(vuetify:defaults)" not found.
[Vue warn]: Unhandled error during execution of setup function
[Vue warn]: Unhandled error during execution of scheduler flush.
Uncaught (in promise) Error: [Vuetify] Could not find defaults instance
Does someone know what I'm missing or how to fix it?
Vuetify doesn't provide isolated components and requires the plugin to be initialized, you need to do this in main app:
app.use(Vuetify)
Make sure vuetify isn't duplicated in project deps, so the lib and main app use the same copy.
The lib should use vuetify as dev dependency and specify it in Rollup external, in order to prevent the things that are global to the project from being bundled with the lib:
external: ["vue", "vuetify"]

Vue3 use component from node_modules

I'm trying to use a component from a node module I installed in Vue3, but it won't work.
I used:
npm install --save vue-slick-carousel
my component
<template>
<div>
<VueSlickCarousel :arrows="true" :dots="true">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</VueSlickCarousel>
</div>
</template>
<script>
import VueSlickCarousel from 'vue-slick-carousel'
import 'vue-slick-carousel/dist/vue-slick-carousel.css'
export default {
name: 'MyComponent',
components: {
VueSlickCarousel
}
}
</script>
It seems like components from the node_modules folder won't work in general, because I have tryed to use an other library. My own components from the src/components folder work perfectly.
I also tryed to register the component globally in my main.js file, but it also won't work.
In my browsers console I always get this error:
Uncaught TypeError: this.$scopedSlots is undefined
Can anyone help?

How can I snapshot test a Vue SFC page in Nuxt that only contains a layout using jest

How can I snapshot test a Vue SFC page in Nuxt that only contains a layout using jest.
For example:
<script>
export default {
layout: 'some-layout-name'
};
</script>
I get an error because it is lacking the template part, I could not make it generate a snapshot in snapshot testing in jest.
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Anonymous>
<Root>
at warn (node_modules/vue/dist/vue.runtime.common.dev.js:621:15)
at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4024:9)
at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8392:10)
at init (node_modules/vue/dist/vue.runtime.common.dev.js:3112:13)
at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:5958:9)
at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5905:9)
at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6455:7)
at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3933:19)
You need to ensure you have a <template> element in your Single File Component SFC.
Example.
<template>
<div/>
</template>
<script>
export default {
layout: 'some-layout-name'
};
</script>
N.B. Without the <template> element in your SFC, you'll continue to get the error experienced.
render function or template not defined in the component.

npm dree folder structure to JSON

i am trying to use dree to get the folder structure as a json object.
i have installed the package following the link : https://www.npmjs.com/package/dree
To test i have given the path to my components folder in my project
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App "/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
const dree = require('dree');
const tree = dree.scan('./components');
console.log(tree);
export default {
name: 'app',
components: {
HelloWorld
}
}
</script>
If I console.log (dree) then it's giving me the result but console.log(tree);
is giving me the below error. Please help.
index.js?517d:325 Uncaught TypeError: Cannot set property 'sizeInBytes' of nullenter code here at Object.scan (index.js?517d:325)
Dree uses the node fs module, that is not supported in browsers. Hence, dree is not supported in browsers.
Here you can find useful pieces of information about the modules with no browser-support used in vuejs, maybe you could use it at build-time and not run-time.
Unable to require 'fs' with Vue CLI 3

How to use ag-grid in a Nuxt app

I'm building a Nuxt application and I'm trying to render an ag-grid in one of my pages
I created a plugin called ag-grid.js:
import * as agGridEnterpise from 'ag-grid-enterprise/main'
agGridEnterpise.LicenseManager.setLicenseKey([MY_LICENSE_KEY])
On nuxt.config.js i have registered the plugin:
plugins: [
//...
{
src: '~/plugins/ag-grid.js',
ssr: false
}
],
And in my page component i have:
<template>
<ag-grid-vue ref="table" class="ag-theme-material"
:pinnedTopRowData="pinnedRow ? [pinnedRow] : []" :gridOptions="gridOptions"
:columnDefs="columnDefs" :rowData="tableData" v-show="!loadingGridData"
:cellValueChanged="onCellValueChanged">
</ag-grid-vue>
</template>
<script>
import { AgGridVue } from 'ag-grid-vue'
export default {
// ....
components: {
'ag-grid-vue': AgGridVue
// ....
}
}
</script>
But when I'm rendering the page i get the following error:
TypeError: Cannot read property 'match' of undefined
at Environment.webpackJsonp../node_modules/ag-grid/dist/lib/environment.js.Environment.getTheme (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\environment.js:76)
at GridOptionsWrapper.webpackJsonp../node_modules/ag-grid/dist/lib/gridOptionsWrapper.js.GridOptionsWrapper.specialForNewMaterial (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\gridOptionsWrapper.js:636)
at GridOptionsWrapper.webpackJsonp../node_modules/ag-grid/dist/lib/gridOptionsWrapper.js.GridOptionsWrapper.getHeaderHeight (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\gridOptionsWrapper.js:352)
at GridOptionsWrapper.webpackJsonp../node_modules/ag-grid/dist/lib/gridOptionsWrapper.js.GridOptionsWrapper.getGroupHeaderHeight (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\gridOptionsWrapper.js:368)
at GridPanel.webpackJsonp../node_modules/ag-grid/dist/lib/gridPanel/gridPanel.js.GridPanel.setBodyAndHeaderHeights (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\gridPanel\gridPanel.js:1193)
at GridPanel.webpackJsonp../node_modules/ag-grid/dist/lib/gridPanel/gridPanel.js.GridPanel.init (C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\gridPanel\gridPanel.js:191)
at C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\context\context.js:215
at Array.forEach (<anonymous>)
at C:\xampp\htdocs\ate_crm_webapp\node_modules\ag-grid\dist\lib\context\context.js:215
at Array.forEach (<anonymous>)
And i get a Vue warning:
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
found in
---> <AgGridVue>
//....
Any clue what's going on?
The ag-grid failed to find the css. That's why you need these lines to your nuxt.config.js file:
module.exports = {
css: [
'#/node_modules/ag-grid/dist/styles/ag-grid.css',
'#/node_modules/ag-grid/dist/styles/ag-theme-material.css'
],
build: ect...
To resolve the other answerers problems (like MouseEvent error) put this to the build:
build: {
extend (config, context) {
config.resolve.alias['vue'] = 'vue/dist/vue.common'
}
}