SSR build says "document is not defined" for a Vuejs cli3 SPA app using a Vue cli3 library - vue.js

I have a fairly complex SPA Vue.js app created with Vue CLI v3. I want to transform this app to benefit from SSR (although I might end up using only preprendering...)
My app is using custom Vuejs libraries created with the CLI v3 as well. These libs embbed CSS, and I would like to continue to do so.
I've probably read everything that the world has produced on Vue cli, vue-loader, vue-style-loader, vue.config.js, extract css etc, but I keep failing.
Here is the error message of my running app when I load a given route:
ReferenceError: document is not defined
at addStyle (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:699:22)
at addStylesToDom (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:683:20)
at addStylesClient (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:637:3)
at Object.3351 (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:500:14)
at __webpack_require__ (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:21:30)
at Object.d12e (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:3590:380)
at __webpack_require__ (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:21:30)
at Module.fae3 (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:3859:51)
at __webpack_require__ (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:21:30)
at /Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:85:18
at Object. (/Users/onekiloparsec/code/my-app/node_modules/vue-custom-lib/dist/vue-custom-lib.common.js:88:10)
at Module._compile (internal/modules/cjs/loader.js:701:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
When clicking the first line of the error, I get the following code from the commonjs file of my lib:
function addStyle (obj /* StyleObjectPart */) {
var update, remove
var styleElement = document.querySelector('style[' + ssrIdKey + '~="' + obj.id + '"]')
...
I don't know if I need to change things in my lib (I think so) or in my app, or both.
Here is the vue.config.js of my lib:
module.exports = {
css: {
extract: false
}
}
But I also tried extract: true and (a lot) more sophisticated stuff.... I get always the same error.
Is there a way to integrate Vue cli 3 lib with CSS into a SSR Vue app??? Thanks a lot.

You experienced this error because when the application is building process is done on the server-side where no document exists. You can append an element to document styles by finding an element with querySelector:
var styleElement = document.querySelector('style[' + ssrIdKey + '~="' + obj.id + '"]')
only when JavaScript append to the page is interpreted by the browser (no node js).
You have two options in this case:
Manage styling on the server-side (you can't use `document)
Manage styling on the client-side (you can use document)
In option 1 - server-side you will probably resign from using your vue-custom-lib package or create a fork that checks if the environment is browser or node js. Eg.:
if(process) { you are in node js }
if(window) { you are in the browser - you can use the document.querySelector }
In option 2 I recommend you check Nuxt.
You can add a package with document in head.script property in nuxt.config.js
export default {
head: {
script: [
{ src: '...' }
]
},
you should also read about detecting server and client-side in the Nuxt context
https://nuxtjs.org/api/context/
Configuration build that has also the property isServer:
https://nuxtjs.org/api/configuration-build
Appending your custom library can be done also in renderAndGetWindow function
https://nuxtjs.org/api/nuxt-render-and-get-window

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 prerender a Vue3 application?

I try without success to apply a prerendering (or a SSG) to my Vue3 application to make it more SEO friendly.
I found the vue-cli-plugin-prerender-spa, and when I try it with the command line: vue add prerender-spa I have the error:
ERROR TypeError: Cannot read properties of undefined (reading 'endsWith')
After that I tried prerender-spa-plugin but I have an error when I make a npm run build:
[prerender-spa-plugin] Unable to prerender all routes!
ERROR Error: Build failed with errors.
Error: Build failed with errors.
at /Users/myusername/Workspace/myproject/node_modules/#vue/cli-service/lib/commands/build/index.js:207:23
at /Users/myusername/Workspace/myproject/node_modules/webpack/lib/webpack.js:148:8
at /Users/myusername/Workspace/myproject/node_modules/webpack/lib/HookWebpackError.js:68:3
What do you think about this? Do you have any idea?
Nuxt3 is a really powerful meta-framework with a lot of features and huge ecosystem. Meanwhile, it's in RC2 right now so not 100% stable (may still work perfectly fine).
If your project is aiming for something simpler, I'd recommend using Vitesse. It may be a bit more stable and it's probably powerful enough (check what's coming with it to help you decide).
Some solutions like Prerender also exist but it's paid and not as good as some real SSG (/SSR). Also, it's more of a freemium.
I struggled with the same error output until I found the prerender-spa-plugin-next. Then I notice the latest version of prerender-spa-plugin was published 4 years ago and prerender-spa-plugin-next is continually updating. It seems like that prerender-spa-plugin-next is a new version of prerender-spa-plugin with the same functions. So I use prerender-spa-plugin-next instead of prerender-spa-plugin then everything works fine!
Here is my step:
install the package
npm i -D prerender-spa-plugin-next
modify vue.config.js like
const plugins = [];
if (process.env.NODE_ENV === 'production') {
const { join } = require('path');
const PrerenderPlugin = require('prerender-spa-plugin-next');
plugins.unshift(
new PrerenderPlugin({
staticDir: join(__dirname, 'dist'),
routes: ['/'], //the page route you want to prerender
})
);
}
module.exports = {
transpileDependencies: true,
configureWebpack(config) {
config.plugins = [...config.plugins, ...plugins];
},
};
build
npm run build
Then check the index.html under the dist folder you can see the page is prerendered.
Further usage refers to the homepage of prerender-spa-plugin-next
Found and fix about the scss files to import.
In nuxt.config.ts use :
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: `
#import "#/assets/scss/_variables.scss";
#import "#/assets/scss/my-style.scss";
`
}
},
},
}
Now my 2 mains issue are : how to install vuetify properly, currently syles and components seems working but the JS not, for example, accordions don't expands on click.
And second topic is to have a i18n module, it seems that vue-i18N no longer works.
Thanks.

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 js cannot parse css codes in components

I am using vue js + laravel to create a project. But I am facing an issue when I run npm run watch command.
Vue js throws the error below and the project does not build.
ERROR in ./node_modules/sweetalert2/dist/sweetalert2.min.css (./node_modules/css-loader/dist/cjs.js??clonedRuleSet-8[0].rules[0].use[1]!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-8[0].rules[0].use[2]!./node_modules/sweetalert2/dist/sweetalert2.min.css)
Module build failed (from ./node_modules/css-loader/dist/cjs.js):
ValidationError: Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options.url should be one of these:
boolean | object { filter? }
-> Allows to enables/disables `url()`/`image-set()` functions handling (https://github.com/webpack-contrib/css-loader#url).
Details:
* options.url should be a boolean.
* options.url should be an object:
object { filter? }
at validate (/var/www/liaratech/node_modules/webpack/node_modules/schema-utils/dist/validate.js:105:11)
at Object.getOptions (/var/www/liaratech/node_modules/webpack/lib/NormalModule.js:527:19)
at Object.loader (/var/www/liaratech/node_modules/css-loader/dist/index.js:31:27)
I realized that vue js cannot parse css codes in vue js component files.
How can i fix that?
The issue here is a version mismatch for css-loader.
Using css-loader#^3.0.0 fixes the problem (laravel-mix and sweetalert2 seem to use different versions I guess).
"devDependencies": {
"css-loader": "^3.0.0"
}

Vue.js 3 extension breaks while using "vue-cli-service build" due to unsafe-eval

I am developing a chrome extension using vue 3, vue-router and vuex based on Kocal's project which uses vue-cli under the hood. I used whenever possible Single File Components with extensive use of vue bindings.
Everything works perfect on development mode but I recently tried to build the application for production and I encountered this error with partial rendering:
chunk-vendors.f6de00a6.js:11 EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
After a few days of digging, my understanding is that either webpack or vue compiling system is messing with CSP by referring/injecting code through eval scripts. As I am fairly new to this, it's hard for me to read to distinguish what I can do.
I tried different approaches:
defining $vue alias to a runtime only build in vue.config.js (supposedly removing unsafe eval by having code compiled before runtime but ending with a new error: Uncaught TypeError: Object(...) is not a function for o=Object(n["withScopeId"])("data-v-21ae70c6");)
using render() function at root
adding "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", to manifest.json
switching a component to render() to see if I have better chance with the partial rendering, but ending up with nothing being displayed although having console.log from render being executed.
Adding a config to chainWebpack splitting manifest and inline manifest on vue.config
What puzzles me is that I can't shake off the unsafe-eval, with at best a partial display, at worst a blank page. Bindings seem to be shaken off regardless and using a router-link to change page will give a blank page.
Edit: After digging through compiled code from webpack and setting minimize opt to false, it seems the error comes from a vendor: vue-i18n
The eval is likely coming from Webpack, due to an issue with global scoping.
see link for more detail https://mathiasbynens.be/notes/globalthis
Could you try adding this configuration to vue.config.js
module.exports = {
configureWebpack: {
node: {
global: false
},
plugins: [
new webpack.DefinePlugin({
global: "window"
})
]
}
};
tl;dr: Check your dependencies/packages, including those you wouldn't think they use unsafe-eval.
After digging into webpack internals and components building for vue3, here are the takeaways:
using Single File Components and default vue-cli config is ok as it will indeed just need vue runtime, so no unsolicited unsafe-eval
webpack config as below works:
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
global: "window" // Placeholder for global used in any node_modules
})
]
},
...
};
// Note that this plugin definition would break if you are using "unit-mocha" module for vue-cli
In the end, the issue was a dependency I was using for i18n vue-i18n#next, after removing it and switching to chrome's i18n way, it's now working.