Vue.js - Package app as Figma Plugin using Vite - vue.js

I have a Vue.js app that I created with Vite. This app successfully runs when I use npm run dev and visit the app in the browser. Now, I'm trying to bundle the app as a single code file so that I can use it as a plugin for Figma.
I know that the index.html page loads because I can edit the HTML and see the changes. However, the page isn't loading/running the Vue.js app itself. I can see the following warning in the console log:
<link rel=modulepreload> has no 'href' value
My project is structured like this:
/
/dist
/assets
index.a386f87b.css
index.dc441194.js
vendor.fbe8b50a.js
index.html
manifest.json
plugin.js
/src
/views
Index.vue
Items.vue
Calendar.vue
/res
/css
theme.css
/images
loading.gif
splash.jpeg
App.vue
main.js
index.html
When I look at the index.html file in the dist directory, I see:
<script type="module" crossorigin src="/assets/index.dc441194.js"></script>
<link rel="modulepreload" href="/assets/vendor.fbe8b50a.js">
<link rel="stylesheet" href="/assets/index.a386f87b.css">
<div id="app" class="position-absolute top-0 left-0 vh-100 vw-100"></div>
The references look correct. However, clearly something is wrong as the app is not loading as a plugin in Figma. Once again, I know I'm successfully loading the index.html file because if I manually edit it, the changes appear in the plugin. This leads me to bundling the app as a single code file. At this point, I'm stuck though. I don't see a way to accomplish this via Vite's built-in capabilities. I tried including the vite-plugin-singlefile plugin. Unfortunately that didn't work for me either. Currently, my vite.config.js file looks like this:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue';
export default defineConfig(({command, mode }) => {
return {
assetsDir: 'res',
plugins: [
vue(),
],
root: 'src',
build: {
emptyOutDir: true,
outDir: '../dist'
}
}
});
What am I doing wrong?

Figma ignores <script>.src and <link>.href, which aligns with the docs you linked, stating "all the code must be in one file".
Using vite-plugin-singlefile (as you mentioned) to inline all scripts and styles in index.html indeed seems to workaround the problem:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import { viteSingleFile } from 'vite-plugin-singlefile'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), viteSingleFile()],
build: {
cssCodeSplit: false,
assetsInlineLimit: 100000000,
rollupOptions: {
output: {
manualChunks: () => "everything.js",
},
},
}
})
demo

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"]

How to use min.js in vite2?

such as vue.min.js
I try to external the vue in build.rollupOptions,and import
//vite.config.js
rollupOptions: {
external: ['vue', 'vuetify']
}
// index.html
<script src="/lib/vue.min.js"></script>
<script src="/lib/vuetify.min.js"></script>
and download the vue.min.js and vuetify.min.js ,but it is not work.
the legacy vue.min.js is a umd file but vite need esm version.
so just download the vue.esm.browser.min.js to replace vue.min.js is Ok.
just add external like this:
rollupOptions: {
// 外部化vue 和 vuetify
external: ['vue']
}
and import vue in index.html like this:
...
<head>
...
<title><%= title %></title>
<!-- script -->
<script type="importmap">
{
"imports": {
"vue": "/lib/vue.esm.browser.min.js" //(where the file is)
}
}
</script>
...
</head>
Now you can run vite build in your project, and it works.
But I didnt find esm version of vuetify2.
The net give us some plugins to import external umd which should be pre-handled:
vite-plugin-external
vite-plugin-cdn-import
But i still not make them work,if you have an example successfully,pleace udpate a answer!!!

Unable to import and use vuejs single-file-component after build

I'm trying to reuse some of my components from one repository in another one. My component is a single file component. I setup webpack to create a .js bundle for that component and then I copy that file and drop it in the other repository's shared directory.
when I import that component and register it in the repository it was created in, there is no issue. Problem is when I import that component and register it on a parent component after I bundle it (separately) and move it to the other repository. I'm getting this error message.
Failed to mount component: template or render function not defined.
Export file
<template>
// template code
</template>
<script>
export default {
data() {
return {
data: 'data'
}
},
</script>
<style>
</style>
Import file:
import sharedComponent from '../../components/shared/sharedComponent.vue';
Vue.component('parent-component', {
components:{
'shared-component': sharedComponent,
},
})
Solution that worked for me:
The issue seemed to be happening because of the way webpack was bundling the file. Fixed by explicitly requesting umd build in the webpack configuration as such:
output: {
libraryTarget: 'umd'
},

Vue Js 2 / Vue-CLI 3 / When hosted shows empty blank page

Vue Js app working fine in dev mode, but as I upload it on the server, it simply displays a blank page. All the resources are showing up in the developer console. I am using vue-router package.
VueJs 2 / Vue-CLI-3
Below is my App.vue
<template>
<div id="app">
<m-nav/>
<router-view></router-view>
</div>
</template>
<script>
import Navbar from './components/Navbar.vue'
export default {
name: 'app',
components: {
'm-nav':Navbar
}
}
</script>
<style>
</style>
This is my main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import HomePage from './components/HomePage.vue'
import Brochure from './components/Brochure.vue'
import Register from './components/Register.vue'
Vue.config.productionTip = false
Vue.use(VueRouter);
const routes = [
{
path:'/',
component: HomePage
},
{
path:'/download',
component: Brochure
},
{
path:'/register',
component: Register
}
];
const router = new VueRouter({
routes,
mode: 'history'
});
new Vue({
router,
render: h => h(App),
}).$mount('#app');
after running npm run build, It shows a blank page, although I can see some stuff in the DOM <div id="app"></div>
http://prntscr.com/lvasvo
I am not getting, where I have made a mistake! The project is complete, but stuck on this part! :(
follow this tips:
use <%= BASE_URL %> for script and link tags which wrote on index.html
create vue.config.js in root of project and config your public path for production and development mode like:
module.exports = {
publicPath:
process.env.NODE_ENV === "production" ? "/" : "/"
}
};
just use relative path, to know more about it, read this link:
https://cli.vuejs.org/config/#publicpath
create config.vue.js
In my case the issue was in the Router.beforeEach function that I had in index.js file inside the router folder to add a route guard. I had to rewrite it after upgrading it from Vue 2 to Vue 3 to make it work.
Just encounter the same issue with vue-cli3 project.
And my main reason getting this issue is my app doesn't deploy on the root of the domain,instead on a sub-path.
There're several tricky configures:
vue.config.js (it's vue-cli3 exclusive): to set publicPath:/my-app/ ;
router.js (assuming your vue-router config file is this): to set base:/my-app/;
And beware of the history router mode:
this mode needs extra server side config, I'm using nginX, this is my config:
location /my-app {
alias /var/www/my-app;
try_files $uri /index.html last;
index index.html;
}
And beware of the different between alias and root.
this mode is not support relative path publicPath:./ in vue.config.js.

Components not showing up in Vue DevTools

Has anyone experienced this problem? I'm using Vue Devtools but can't inspect any components on a count of none are showing up. No Root component or anything. Pretty much just a blank DevTools. I'm new to Vue so I'm sure I'm missing something obvious. I'm using the webpack cli template and haven't implemented any Vue Router stuff yet. Nothing comes up when searching for components either. I'm assuming it's something in these 3 files?
main.js
import Vue from 'vue'
import App from './App'
var db = firebase.database();
var vm = new Vue({
el: '#app',
created: function() {
// Import firebase data
var quizzesRef = db.ref('quizzes');
quizzesRef.on('value', function(snapshot) {
console.log(snapshot.val());
vm.quizzes = snapshot.val();
});
},
data: function() {
return {
authenticated: false,
quizzes: {},
resources: []
}
},
template: '<App/>',
components: { App }
})
App.vue
<template>
<div id="app">
<navbar></navbar>
<resource-info></resource-info>
</div>
</template>
<script>
import Navbar from './components/Navbar'
import ResourceInfo from './components/ResourceInfo'
export default {
name: 'app',
components: {
Navbar,
ResourceInfo
}
}
</script>
<style>
</style>
Index.html (Omitted header)
<body>
<div id="app" class="container-fluid"></div>
<!-- built files will be auto injected -->
<script>
// Initialize Firebase
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
storageBucket: "",
messagingSenderId: ""
};
firebase.initializeApp(config);
</script>
</body>
I had the same issue and here's what fixed it for me:
Ensure Allow access to file URLs in the chrome extension settings (chrome://extensions) is enabled. Restart Chrome.
Open the Vue DevTools extension and click the Refresh button on the top right after opening a Vue component on the page.
I hope this helps someone.
I see that you are using vue-cli and I assume it is running in dev mode (npm run dev).
Obviously it will not work after you build the production app using npm run build and serve from the dist folder.
Assuming you have taken care of the above, did you install the Vue.js devtools recently in Chrome? If so, your browser might need a restart. I think I had to do it when I installed Vue devtools for the first time.
After all that, you should start seeing your components in "Vue" tab of developer tools. You might see Anonymous component for some components, but all you need is name: app which is something you are already doing in your App.vue component.
I had the same issue! Not much to find scratching around for help, but this worked for me:
change the webpack.config.js setting for NODE_ENV: '"production"' to NODE_ENV: '"development"'
Seems almost too obvious but the rendered Vue app(s) then appeared like magic showing all the object goodness!