Netlify Vue Build Failing To Find Export Modules - javascript

So I have a couple of modules that I am importing in to my main.js and store.js file that are not being found when building in Netlify and I cannot understand why. When I run my build locally there is no issues.
So the 2 files are ability.js and storage.js
This is the alarm from the build in netlify
12:48:10 PM: These relative modules were not found:
12:48:10 PM: * ./utils/ability.js in ./src/main.js, ./src/store.js
12:48:10 PM: * ./utils/storage.js in ./src/store.js
12:48:10 PM: ERROR Build failed with errors.
Here is the ability.js file
import { Ability } from '#casl/ability'
export const ability = new Ability()
export const abilityPlugin = (store) => {
ability.update(store.state.rules)
const rules = store.subscribe((mutation) => {
switch (mutation.type) {
case 'createSession':
ability.update(mutation.payload[0])
break
case 'destroySession':
ability.update([{ actions: '', subject: '' }])
break
}
})
return rules
}
here is the storage.js file
export default (options) => (store) => {
if (localStorage.state) {
const storedState = JSON.parse(localStorage.state)
store.replaceState(Object.assign(store.state, storedState))
}
return store.subscribe((mutation, state) => {
if (options.destroyOn && options.destroyOn.indexOf(mutation.type) !== -1) {
return localStorage.removeItem('state')
}
const newState = options.storedKeys.reduce((map, key) => {
map[key] = state[key]
return map
}, {})
localStorage.state = JSON.stringify(newState)
})
}
and here are the 2 files where I import these modules
main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import { abilitiesPlugin } from '#casl/vue';
import { ability } from './utils/ability.js';
Vue.use(abilitiesPlugin, ability);
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import { abilityPlugin, ability as appAbility } from './utils/ability.js'
import storage from './utils/storage.js'
export const ability = appAbility
Vue.use(Vuex)
axios.defaults.baseURL = 'https://aewcpa.traxit.pro/api'
axios.defaults.headers.common['header1'] = {
'X-Requested-With': 'XMLHttpRequest',
}
export default new Vuex.Store({
plugins: [
storage({
storedKeys: ['rules', 'token'],
destroyOn: ['destroySession']
}),
abilityPlugin
],
})
The directory structure with issues
The directory structure that currently works

Is Utils in the root of your project? Are you using the Vue Webpack Template?
If so the # resolver is configured for you (ES6 import using at ('#') sign in path in a vue.js project using Webpack)
If so change:
from './utils/ability.js' to from '#/utils/storage.js'
and
from './utils/storage.js' to from '#/utils/storage.js'

Related

Issue with Dependency cycle via in Vue.js

I'm having an issue with a linting error in a vue.js project. The error that I get looks like this:
/Users/mikecuddy/Desktop/coding/data_science_projects/statues/client/src/store/modules/common.js
4:1 error Dependency cycle via #/store/index:4 import/no-cycle
I have no idea how to get rid of this error. I tried renaming files, using this.$router and this.$store with no luck. Here is some of my code:
router -> index.js:
The data path is the main one I want to get to. Notice that I have the store import files commented out - that does get rid of the dependency error but then I have issues with doing something like:
this.$store.state.common.loginFlag
as opposed as importing the store and doing this:
store.state.common.loginFlag
import Vue from 'vue';
import VueRouter from 'vue-router';
// import store from '../store/index.js';
// import store from '#/store/index';
import Home from '../views/Home.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/data',
name: 'Data',
component: () => import('../views/Data.vue'),
beforeEnter: (to, from, next) => {
if (this.$store.state.common.loginFlag === false) {
next('/login');
} else {
next();
}
},
beforeRouteLeave: (to, from, next) => {
if (this.$store.state.common.loginFlag === false) {
next('/login');
} else {
next();
}
},
},
];
const router = new VueRouter({
routes,
});
export default router;
store/modules/common.js:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
import router from '../../router';
Vue.use(Vuex);
const data = {
userNotFound: false,
passwordNoMatch: false,
loginFlag: false,
};
const getters = {
userNotFound: (state) => state.userNotFound,
passwordNoMatch: (state) => state.passwordNoMatch,
loginFlag: (state) => state.loginFlag,
};
const actions = {
login: ({ commit }, { payload }) => {
const path = 'http://localhost:5000/login';
axios.post(path, payload)
.then((res) => {
if (res.data.login_flag) {
commit('session/setUserObject', res.data.user, { root: true });
commit('setLoginFlag', res.data.login_flag);
// Tried this:
router.push{ name: 'Data' }
// As well as trying this:
this.$router.push({ name: 'Data' });
}
commit('setNoPasswordMatch', res.data.Password_no_match);
commit('setUserNotFound', res.data.Not_found);
})
.catch((error) => {
console.log(error);
});
},
};
// I have mutations but did not think they'd be needed
const mutations = {};
export default {
namespaced: true,
state: data,
getters,
actions,
mutations,
};
In the common.js file I've tried commenting out:
import router from '../../router';
and that seemed to work - got the Dependency cycle error to go away and in the router/index.js file I was able to get to the route but had an issue with this.$store.state.common.loginFlag when I commented out import store from '#/store/index'; If I leave in the import of: import store from '#/store/index';
then I get the dependency cycle error.
I've also found some help at these other stack pages:
TypeError: Cannot read properties of undefined (reading '$router') vuejs
dependency cycle detected import/no-cycle
I will say that I hate using linters and that's what's giving me the problem here.
Here is the code for store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import common from './modules/common';
import session from './modules/session';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
common,
session,
},
});
Looks like the reason for the dependency cycle here is when you are importing router setup in the store module, and the router in turn imports the whole store. It's okay to use store in router, but try to move routing/redirect logic (these lines):
// Tried this:
router.push{ name: 'Data' }
// As well as trying this:
this.$router.push({ name: 'Data' });
from /modules/common.js to the component or global router hook level, so you avoid router import in the store module.

How to use i18n in the Vuex store

I have a project where I need to do translations inside the Vuex store. But I keep on getting an error when trying to translate using i18n inside the store.
I have tried to import and instance of i18n inside the store using the following import statement. But I then I get an error Uncaught TypeError: _i18n__WEBPACK_IMPORTED_MODULE_3__.default.t is not a function
import i18n from '#/i18n';
In the main.js file of my Vue project I import and use the i18n file:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { store } from './store';
import i18n from './i18n';
createApp(App).use(i18n).use(store).use(router).mount('#app');
This is my i18n.js file that is located inside the src folder:
import { createI18n } from 'vue-i18n';
function loadLocaleMessages() {
const locales = require.context(
'./locales',
true,
/[A-Za-z0-9-_,\s]+\.json$/i
);
const messages = {};
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
messages[locale] = locales(key);
}
});
return messages;
}
export default createI18n({
legacy: false,
locale: localStorage.locale ?? 'nl',
globalInjection: true,
messages: loadLocaleMessages(),
});
For Vue 3 guys out there struggling with usage of i18n in the Vuex store, I was able to achieve it like this:
translations/index.js
with basic setup
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
fallbackLocale: 'en',
globalInjection: true,
messages: messages
})
export default i18n
main.js
Import store and i18n and use them in Vue app instance
import i18n from './translations'
import store from './store'
const app = createApp(App)
app.use(store)
app.use(i18n)
app.mount('#app')
Vuex store module file with getter example:
import i18n from './translations'
const getters = {
getNotification: (state) => {
...
notification.title = i18n.global.t('notification.title')
...
}
}
I used vue-i18n in Vuex. Maybe it helps to you.
Create vue-i18n.js file like this;
import Vue from "vue";
import VueI18n from "vue-i18n";
// Localisation language list
import { locale as en } from "#/core/config/i18n/en.js";
import { locale as ch } from "#/core/config/i18n/ch.js";
Vue.use(VueI18n);
let messages = {};
messages = { ...messages, en, ch };
// get current selected language
const lang = localStorage.getItem("language") || "en";
// Create VueI18n instance with options
const i18n = new VueI18n({
locale: lang, // set locale
messages // set locale messages
});
export default i18n;
and import it to Vue in main.js file;
import i18n from "#/core/plugins/vue-i18n";
new Vue({
router,
store,
i18n,
render: h => h(App),
}).$mount('#app')
import it inside your store or modules ( i imported in my vuex module);
import i18n from "#/core/plugins/vue-i18n";
then use it wherever you want (action, mutation, setter or getter);
const sample = i18n.t('ERRORS.NETWORKERROR');
en.js file;
export const locale = {
LOGIN: {
OPERATORID: "Operator ID",
SIGNIN:"Sign In",
SCANCARD: "Scan Card"
},
ERRORS: {
NETWORKERROR: "Network error occurred!",
UNAUTHUSERERROR: "Unauthorized user!",
}
};

Single-spa - is there a way to show loader while switching between micro frontends?

I am using single-spa for a project, it consists of one root-config and multiple vue micro-frontends, i need to show overlay from vuetify overlay, is there a way i can acheive from mount function??
root-config.js
import * as singleSpa from "single-spa";
let appUrl = ["/app1", "/app2"];
appUrl.forEach(url => {
singleSpa.registerApplication({
name: "#test" + url,
app: () => System.import("#test" + url),
activeWhen: [url],
});
})
/* here need to set loader */
main.js
import Vue from 'vue'
import singleSpaVue from 'single-spa-vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render (h) {
return h(App, {
props: {
}
})
},
router,
vuetify
}
})
export const bootstrap = [vueLifecycles.bootstrap,doneBootstrap]
export const mount = vueLifecycles.mount
export const unmount = vueLifecycles.unmount
function doneBootstrap() {
/* here need to unset loader */
return Promise.resolve();
}

Change I18n locale don't work for Vee-Validate - VueJS

I am using Vee-Validate plugin for form validation in my VueJS Application. So, my app has more than 1 language, for that, I am using I18n. All the plugins I am using are in separate files under plugins folder and then I am getting all files and registering all plugins in main.js, so in my Vee-Validate.js I have written:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import VeeValidate from 'vee-validate';
import enMessages from "./../locales/validation/en";
import urMessages from "./../locales/validation/ur";
Vue.use(VueI18n);
const i18n = new VueI18n();
i18n.locale = "en";
Vue.use(VeeValidate, {
errorBagName: 'vErrors',
i18nRootKey: 'validations',
i18n,
dictionary: {
en: {
messages: enMessages
},
ur: {
messages: urMessages
}
}
});
But on clicking the change locale button don't change this file locale,
My change locale function:
changeLocale () {
this.$i18n.locale == 'en' ? this.$i18n.locale = 'ur' : this.$i18n.locale = 'en'
this.$vuetify.rtl = this.$i18n.locale == 'ur' ? true : false;
}
Well, I'm not saying your configuration is wrong. I'm going just share mine that is working just fine.
1- vue.config.js
module.exports = {
transpileDependencies: [
'vuetify',
],
pluginOptions: {
i18n: {
locale: 'en',
fallbackLocale: 'en',
localeDir: 'locales',
enableInSFC: true,
},
},
};
2- i18n.ts
import Vue from 'vue';
import VueI18n, { LocaleMessages } from 'vue-i18n';
Vue.use(VueI18n);
function loadLocaleMessages(): LocaleMessages {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i);
const messages: LocaleMessages = {};
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
messages[locale] = locales(key);
}
});
return messages;
}
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages(),
});
3- main.ts
import Vue from 'vue';
import './registerServiceWorker';
import { sync } from 'vuex-router-sync';
import VueLodash from 'vue-lodash';
import Storage from 'vue-ls';
import vuetify from './plugins/vuetify';
import './utils/vee-validate';
// Components
import './components';
// Application imports
import App from './App.vue';
import router from '#/router';
import store from '#/store';
import i18n from './i18n';
// Sync store with router
sync(store, router);
const options = {
name: 'ls', // name variable Vue.[ls] or this.[$ls],
storage: 'local', // storage name session, local, memory
};
Vue.use(Storage, options);
Vue.config.productionTip = false;
Vue.use(VueLodash, { name: 'lodash' });
new Vue({
router,
store,
vuetify,
i18n,
render: h => h(App),
}).$mount('#app');
4- src/plugins/vuetify.ts
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
// import VueI18n from 'vue-i18n';
// import i18n from '#/i18n';
import en from '#/locales/en.json';
import jp from '#/locales/jp.json';
Vue.use(Vuetify);
export default new Vuetify({
lang: {
locales: { en, jp },
current: 'jp',
},
});
5- src/utils/vee-validate.js
/* eslint-disable no-underscore-dangle */
import { extend } from 'vee-validate';
import { required, email, confirmed } from 'vee-validate/dist/rules';
import i18n from '#/i18n';
extend('required', {
...required,
message: (_, values) => i18n.t('GENERAL_VALIDATION_MESSAGES_REQUIRED', values),
});
extend('email', {
...email,
message: (_, values) => i18n.t('LOGIN_FORM_EMAIL_VALID_MESSAGE', values),
});
extend('confirmed', {
...confirmed,
message: (_, values) => i18n.t('CHANGE_PASSWORD_FORM_CONFIRMATION_VALID_MESSAGE', values),
});
6- I use vuex so from my language store
import Vue from 'vue';
import { localize } from 'vee-validate';
import Vuetify from 'vuetify/lib';
import i18n from '#/i18n';
import en from '#/locales/en.json';
import jp from '#/locales/jp.json';
...
const mutations = {
SET_LANG(state, data) {
state.lang = data;
i18n.locale = data;
localize(data, jp);
},
SET_LANG_ERROR() {
window.$messageGlobal('Error switching languages');
},
};
Hope it helps

Vue CLI and Server-Side Rendering

I'm using Vue CLI which uses main.js to mount the app versus Server-Side Rendering (according to the tutorial I follow here) which uses app.js, entry-client.js and entry-server.js.
I tried to bypass main.js, but I'm having an error since its seems its required some how.
How can I work to keep main.js with app.js, entry-client.js and entry-server.js.
It might be a very basic question and I could maybe use the content of app.js in main.js, be I want to do things right.
main.js :
import Vue from 'vue'
import bootstrap from 'bootstrap'
import App from './App.vue'
import router from './router'
import store from './store'
import VueResource from 'vue-resource'
import BootstrapVue from 'bootstrap-vue'
import './registerServiceWorker'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.config.productionTip = false
Vue.use(VueResource)
Vue.use(BootstrapVue)
new Vue({
bootstrap,
router,
store,
render: h => h(App)
}).$mount('#app')
app.js :
import Vue from 'vue'
import App from './App.vue'
import bootstrap from 'bootstrap'
import { createRouter } from './router'
import store from './store'
import VueResource from 'vue-resource'
import BootstrapVue from 'bootstrap-vue'
import './registerServiceWorker'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.config.productionTip = false
Vue.use(VueResource)
Vue.use(BootstrapVue)
export function createApp () {
// create router instance
const router = createRouter()
const app = new Vue({
bootstrap,
router,
store,
render: h => h(App)
})
return { app, bootstrap, router, store }
}
server-client.js
import { createApp } from './app'
const { app, router } = createApp()
router.onReady(() => {
app.$mount('#app')
})
entry-server.js
import { createApp } from './app'
export default context => {
// since there could potentially be asynchronous route hooks or components,
// we will be returning a Promise so that the server can wait until
// everything is ready before rendering.
return new Promise((resolve, reject) => {
const { app, router } = createApp()
// set server-side router's location
router.push(context.url)
// wait until router has resolved possible async components and hooks
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// no matched routes, reject with 404
if (!matchedComponents.length) {
return reject({ code: 404 })
}
// the Promise should resolve to the app instance so it can be rendered
resolve(app)
}, reject)
})
}
Any help is greatly appreciated.
I just found how. By default, Vue CLI 3 comes with no vue.config.js. I had to create one at the root of the folder to override the entry which was set to /src/main.js.
I used the vue.config.js found on this github and onverwrittes the entry to ./src/entry-${target}
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const nodeExternals = require('webpack-node-externals')
const merge = require('lodash.merge')
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const createApiFile = TARGET_NODE
? './create-api-server.js'
: './create-api-client.js'
const target = TARGET_NODE
? 'server'
: 'client'
module.exports = {
configureWebpack: () => ({
entry: `./src/entry-${target}`,
target: TARGET_NODE ? 'node' : 'web',
node: TARGET_NODE ? undefined : false,
plugins: [
TARGET_NODE
? new VueSSRServerPlugin()
: new VueSSRClientPlugin()
],
externals: TARGET_NODE ? nodeExternals({
whitelist: /\.css$/
}) : undefined,
output: {
libraryTarget: TARGET_NODE
? 'commonjs2'
: undefined
},
optimization: {
splitChunks: undefined
},
resolve:{
alias: {
'create-api': createApiFile
}
}
}),
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options =>
merge(options, {
optimizeSSR: false
})
)
}
}

Categories

Resources