How to add included Pug files to Vite module graph - vue.js

I wrote a Rollup plugin to import Pug as an HTML string:
// Rollup plugin imported to Vite config
import { render } from 'pug';
export default function pug() {
return {
name: 'rollup-plugin-pug-html',
transform(src, id) {
if (id.endsWith('.pug')) {
const html = render(src, { filename: id });
const code = `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}
I'm using it in Vite to create templates for Vue components, as in this reduced example:
// ProofOfConceptSFC.vue
<script>
import { compile } from 'vue/dist/vue.esm-bundler.js';
import template from './template.pug';
export default {
render: compile(template)
};
</script>
The HMR is working great when I edit template.pug. The new template appears and the latest reactive values persist.
My problem is that template.pug may depend on other Pug files with include:
//- template.pug
include ./header.pug
p Hello {{ name }}
include ./footer.pug
The Vite server doesn't know about those files, and nothing happens if I touch them. Ideally I could invalidate template.pug when any Pug file is changed.
I'm guessing I want my plugin to update the ViteDevServer's server.moduleGraph. Is there a supported way to do that?

Huge thanks to the friendly Vite chat on Discord for setting me in the right direction.
The two keys I was missing:
Use Pug compile to create a render method that has render.dependencies, as done by Parcel
Use virtual import statements to attach the dependencies to the transform hook result, as done by vite-plugin-svelte.
Here is the working plugin:
import { compile } from 'pug';
export default function pluginPug() {
return {
name: 'vite-plugin-pug',
transform(src, id) {
if (id.endsWith('.pug')) {
const render = compile(src, { filename: id });
const html = render();
let code = '';
for (let dep of render.dependencies) {
code += `import ${JSON.stringify(dep)};\n`;
}
code += `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}

Related

Sources tab in chrome dev tools is not displaying the right files

Im trying to debug and set breakpoints in my code, but whenever I navigate to the Sources tab, it displays files that are not the actual code, always formatted like:
import { render, staticRenderFns } from "./Index.vue?vue&type=template&id=e99f4d94&scoped=true&"
import script from "./Index.vue?vue&type=script&lang=js&"
export * from "./Index.vue?vue&type=script&lang=js&"
/* normalize component */
import normalizer from "!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js"
var component = normalizer(
script,
render,
staticRenderFns,
false,
null,
"e99f4d94",
null
)
/* vuetify-loader */
import installComponents from "!../../../node_modules/vuetify-loader/lib/runtime/installComponents.js"
import { VCol } from 'vuetify/lib/components/VGrid';
import { VRow } from 'vuetify/lib/components/VGrid';
installComponents(component, {VCol,VRow})
/* hot reload */
if (module.hot) {
var api = require("D:\\vue-hot-reload-api\\dist\\index.js")
api.install(require('vue'))
if (api.compatible) {
module.hot.accept()
if (!api.isRecorded('e99f4d94')) {
api.createRecord('e99f4d94', component.options)
} else {
api.reload('e99f4d94', component.options)
}
module.hot.accept("./Index.vue?vue&type=template&id=e99f4d94&scoped=true&", function () {
api.rerender('e99f4d94', {
render: render,
staticRenderFns: staticRenderFns
})
})
}
}
component.options.__file = "src/views/adas/Index.vue"
export default component.exports
No matter what I do, I cannot find the actual Vue files that I am trying to debug
I have tried setting a debugger in my Vue code, have tried clicking the link to the screen in the Console tab that redirects to the above code still, and tried searching for the file in dev tools and still directing me to the above code.
You need to activate sourcemap.
If you are using vue-cli, you can do it like this.
// vue.config.js
const { defineConfig } = require('#vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
devtool: 'source-map',
}
})

Get config value in vue file

possibly a very simple question: I need to get a configuration value from within an html block in a vue file.
I have this simple config.js
const env = process.env.NODE_ENV
const development = {
images: {
server: "http://localhost:4001",
}
}
const production = {
images: {
server: "http://someimageserver.com",
}
}
const config = {
development,
production,
}
module.exports = config[env]
And this simple vue.js
<template>
<div>
<img :src="`${config.images.server}/images/someimage.jpg`"/>
</div>
</template>
At run time, the above throws
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'images')
What should I do to make this work ?
Thanks in advance
Note: I can get configuration values from within the script block using, this works perfectly, for example
import config from "../../config"
...
var c = config.images.server
UPDATE:
Using vue 3, one can easily achieve this by adding
import config from "../config"
app.config.globalProperties.$config = config
to the main.js file. From there on, $config can be used in templates and scripts across all files. Source: https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties
In Vue, you need to initiate a variable and assign what you imported to it, and eventually return this variable. It looks like below:
Vue2:
import config from "../../config"
export default {
data() {
return {
config: config
}
}
}
Vue3:
import config from "../../config"
export default {
setup() {
return {
config
}
}
}
Then the url in the template should work fine.
-------------------------updates-----------------------
If you want to use config globally, you can register it as a Plugin.
Create plugin.js
import config from "../../config"
export const Config = {
install(Vue, options) {
Vue.prototype.$config = function() {
return config
}
}
}
Then, in your main.js, add below code
import * as Plugins from '#/plugin.js'
Vue.use(Plugins.Config.install)
Then you can use $config within templetes like $route without any other import. Surely you can write other global functions in plugin.js and register each of them in main.js.

This relative module was not found: * ../services/Repository, Vue error

[SOLVED] services/ folder was at the same level than src/ , putting it inside src, following Vue.js Style guide, solved the error. Thanks guys.
When I run npm run serve I get that error, reading over (a lot) SO similar questions and trying everything didn't work, I don´t even know where that line ../services/Repository comes from (I changed the import path in BrandList.vue and nothing changes).
Here is my Repository.js
import http from "../http-common";
class Repository {
getAll() {
return http.get("/index");
}
}
export default new Repository();
This is where I import it, components/BrandList.vue
<script>
import Repository from "../services/Repository";
export default {
name: "brands",
data() {
return {
brands: []
};
},
methods: {
retrieveTutorials() {
Repository.getAll()
.then(response => {
this.brands = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
},
mounted() {
this.retrieveTutorials();
}
};
</script>
Project tree
It should be ../../services/Repository as it's two steps up in the file tree.
From your screenshot you can immediately notice that the services directory is on the same level as the src. A simple fix would be moving the services directory into the src directory.
If you do not want to do that, you can change the import path from "../services/Repository" to "../../services/Repository".
Furthermore, if your project was created with the Vue CLI then the # import only works for files and directories in src/. You could update that if you wanted to.

Can't import .obj files into my Nuxt components

in my Nuxt project I have a background scene made with Three.js.
Now I want to load an .obj into this scene. So the model has to loaded via the component.
my index.vue component:
export default {
name: 'scene',
data () {
return {
}
},
mounted () {
if(!this.scene) this.scene = new Scene({
$canvas: this.$refs.canvas,
});
}
}
In my .js file(Inside the components, where the .vue is as well):
import * as THREE from "three";
import Common from "../Common";
import { OBJLoader2 } from '~/node_modules/three/examples/jsm/loaders/OBJLoader2.js';
const Model = require('#/assets/models/background_1.obj')
export default class Model_1{
constructor(){
this.init();
}
init(){
var loader = new OBJLoader2();
console.log(Model)
loader.load(Model, (root) => {
Common.scene.add(root);
});
}
}
The Nuxt config:
export default {
mode: 'universal',
build: {
vendor: ['hammerjs'],
extend (config, ctx) {
config.module.rules.push(
{
test: /\.(obj|gltf)$/i,
loader: 'file-loader',
}
);
}
}
}
Following error appears:
Cannot find module '#/assets/models/background_1.obj'
I though that the .vue component will look into the assets folder, gets the obj and just reflects the url. But it's looking for a module, which I don't really understand 🤷‍♂️
The .obj file is located in the assets/models folder.
Ok, although every doc said that if the file is in static/file you'll have to use the src static/file. Now that did not work for me, but just using file as source worked. It's working, but can someone please clarify this since I've been on this problem since 2 days and many migraines 😐
Transfer the file to static folder example static/images/background.png
Then omit the #/assets/
var textureURL = "images/background.png";
var texture = textureLoader.load(textureURL);

How to use own JS as a plugin using Nuxt.js

I am using nuxt.js. I have a helper.js script inside plugins folder which has a simple Test() function. Now how can I can call the Test() method inside pages which is in helper.js file.
helper.js file:
export default function Test() {
return 'This is test'
}
to access your global methods entire application:
1-create ./plugins/helpers.js .
2-edit ./plugins/helpers.js :
import Vue from 'vue'
Vue.mixin({
methods:{
mySpecialMethod(value){
console.log(value)
},
}
})
3-edit ./nuxt.config.js :
plugins: [
...
{ src: '~/plugins/helpers' },
...
],
now you can access your global method by:
this.mySpecialMethod()
Using the inject method
There is actually an easy way to do this by using the 'inject' method.
As described in the docs...
The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use Vue.use(), you should create a file in plugins/ and add its path to plugins in nuxt.config.js.
in your plugin simply use inject like this:
export default ({ app }, inject) => {
inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}
and in your components you can use it as follows:
export default {
mounted(){
this.$myInjectedFunction('works in mounted')
},
asyncData(context){
context.app.$myInjectedFunction('works with context')
}
}
"Manual" injection
If you plan on injecting something yourself check out the Vue Docs on Adding Instance properties
There may be data/utilities you’d like to use in many components, but you don’t want to pollute the global scope. In these cases, you can make them available to each Vue instance by defining them on the prototype
Vue.prototype.$appName = 'My App'
And prefix these injected properties with '$'...
$ is a convention Vue uses for properties that are available to all instances. This avoids conflicts with any defined data, computed properties, or methods.
If you just want to use the code in your components (pages), you only need to import and use the method:
TestPage.vue
<template>
<div>
<h1>{{ getTest }}</h1>
</div>
</template>
<script>
import test from '~/plugins/helper.js'
export default {
computed: {
getTest () {
return test()
}
}
}
</script>
Hello you can inject the function globally into Vue doing the following:
./plugins/myPluging.js
import Vue from 'vue'
Vue.prototype.$nameOfMyPlugin = (args) => {
// Code here
}
Them in all your components you can access it this way:
./components/myComponent.vue
<script>
export default {
name: 'c',
mounted () {
this.$nameOfMyPlugin('something useful')
}
}
</script>
And that's it :) hope this helps.
-- Reference: https://nuxtjs.org/guide/plugins/#inject-in-root-amp-context
Below is a a custom js plugin that I have used in one of my nuxt projects.
create your file inside the plugins folder, and make your own function as below
export default (context, inject) => {
const formatDate = (dateTime) => {
if (typeof(dateTime) === 'undefined' || dateTime === null) {
return null;
}
let tempDate = new Date(dateTime);
tempDate.setMinutes(tempDate.getMinutes() -
tempDate.getTimezoneOffset());
tempDate = tempDate.toISOString().slice(0, 16);
return tempDate;
}
// Inject $hello(msg) in Vue, context and store.
inject('formatDate', formatDate)
// For Nuxt <= 2.12, also add 👇
context.$formatDate = formatDate
}
Add the plugin to nuxt.config.js and you will be able to use it globally.
myPlugin.js
export default (_, inject) => {
const myFuncA = value => return value;
const myFuncB = value => return myFuncA(1) + value;
inject('myPlugin', { myFuncA, myFuncB }
)
nuxt.config.js
plugins[
'#/plugin/myPlugin.js'
]
myComponent.vue
created(){
console.log( this.$myPlugin.funcA(2) );
}
in myPlugin.js, instead of "_" can use some public nuxt variables like {$config}