I'd like to build a Nuxt.js App, in this case, I'm using dynamic routes that to be generated by using config.
Well, I got an issue when I was trying to generate my web page using Nuxt & Firebase.
Here are my Nuxt Config JS code :
import * as firebase from 'firebase';
import 'firebase/auth';
import 'firebase/database';
var firebaseConfig = {
apiKey: "AIzaSyAOX6yNHPzWHWd30GnDagwlhgGv9iP8kLs",
authDomain: "musthofa-lapor.firebaseapp.com",
databaseURL: "https://musthofa-lapor.firebaseio.com",
projectId: "musthofa-lapor",
storageBucket: "musthofa-lapor.appspot.com",
messagingSenderId: "653288691711",
appId: "1:653288691711:web:e49daf72720bf99dc5f9ca",
measurementId: "G-0KW7CGZHL3"
};
var app = firebase.initializeApp(firebaseConfig);
var dbx = app.database();
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
],
script:[
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-auth.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-database.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-storage.js' },
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
],
/*
** Nuxt.js modules
*/
modules: [
],
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, { isDev, isClient }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
generate:{
routes(){
return dbx.ref('aspirasi').once("value",function(snap){
snap.forEach(function(snapshot){
var this_val = snapshot.val();
return {
route: '/admin/balas/' + this_val.id
}
})
})
}
}
}
It generated error as follows :
ERROR undefined 06:38:45
TypeError: Cannot read property '_normalized' of undefined
at normalizeLocation (/Volumes/DAKSA-HDD/PROJECTS/PRANANDA/MUSTHOFA LAPOR RAKYAT/PROJECTS/WEBSITE/MAIN/musthofa-web/node_modules/vue-router/dist/vue-router.common.js:1297:12)
at VueRouter.resolve (/Volumes/DAKSA-HDD/PROJECTS/PRANANDA/MUSTHOFA LAPOR RAKYAT/PROJECTS/WEBSITE/MAIN/musthofa-web/node_modules/vue-router/dist/vue-router.common.js:2627:18)
at st (server.js:1:31205)
at async e.default (server.js:1:32623)
Any helps will be appreciated. Thank you so much.
Best Regards
This is caused by returning null or undefined from the routes method, and indeed that is what's happening. There's 2 issues with your code:
You are not returning a value from your callback function.
Using return in a forEach does not return the value to the caller. forEach has a return type of undefined, so you should use map instead.
routes(){
return dbx.ref('aspirasi').once("value",function(snap){
return snap.map(function(snapshot){
var this_val = snapshot.val();
return {
route: '/admin/balas/' + this_val.id
}
})
})
}
Bonus: Your code could be cleaned up a lot by using ES6 syntax and async/await:
async routes() {
const snapshot = await dbx.ref("aspirasi").once("value")
return snapshot.map(snap => ({ route: "/admin/balas/" + snap.val().id }))
}
Related
I am using some utils in Nuxt 3. The vue reactivity system (ref, computed, ...) is also imported directly. However, it is not the case for the tests.
Running the spec file importing a ./useBusinessValidation composable throws the error ReferenceError: ref is not defined
Source file ./useBusinessValidation:
import { MaybeRef } from "#vueuse/core"
export const useBusinessValidation = <T>(rule: (payload: T) => true | string, payload: MaybeRef<T>) => {
const validation = computed(() => rule(unref(payload)))
const isValid = computed(() => validation.value === true)
const errorMessage = computed(() => isValid.value ? undefined : validation.value as string)
return {
isValid,
errorMessage
}
}
Spec file useBusinessValidation.spec.ts:
import { useBusinessValidation } from "./useBusinessValidation"
describe('useBusinessValidation', async () => {
it('should be valid with payload respecting the rule', () => {
const rule = (x: number) => x > 0 ? true : `invalid ${x} number. Expected ${x} to be greater than 0.`
const { isValid, errorMessage } = useBusinessValidation(rule, 0)
expect(isValid.value).toBe(true)
expect(errorMessage.value).toBe(undefined)
});
})
and the vitest.config.ts
{
resolve: {
alias: {
'~': '.',
'~~': './',
'##': '.',
'##/': './',
'assets': './assets',
'public': './public',
'public/': './public/'
}
},
test: {
globals: true,
setupFiles: './test/setupUnit.ts',
environment: 'jsdom',
deps: { inline: [/#nuxt\/test-utils-edge/] },
exclude: [
'test/**/**.spec.ts',
'**/node_modules/**',
'**/dist/**',
'**/cypress/**',
'**/.{idea,git,cache,output,temp}/**'
]
}
}
I also tried with the #vitejs/plugin-vue as
plugins: [Vue()]
in the vitest config. It didn't work out.
To auto-import in vitest, install the unplugin-auto-import.
Then, in the vitest.config.ts add:
import AutoImport from 'unplugin-auto-import/vite';
export default defineConfig({
...
plugins: [
AutoImport({
imports: [
'vue',
// could add 'vue-router' or 'vitest', whatever else you need.
],
}),
]
});
I have a Nuxt app in which everything works fine in middleware except when I use redirect.
When I comment the redirect('/admin') line it works fine even the state data is present when console logged. As soon as I uncomment the redirect line it makes the state null.
Please help if someone knows this issue. This exact code works in my other projects but not here.
This is my auth.js file in the middleware folder.
export default function ({ store, route, redirect }) {
const user = store.getters['user/user']
const blockRouteAdmin = /\/admin\/*/g
const blockRouteManager = /\/manager\/*/g
const path = ['/signup', '/login']
let value = path.includes(route.path)
if (user) {
if (user.isAdmin) {
if (!route.path.match(blockRouteAdmin)) {
redirect('/admin')
}
}
if (user.isManager) {
if (!route.path.match(blockRouteManager)) {
redirect('/manager')
}
}
if (user.isUser) {
if (
route.path.match(blockRouteAdmin) ||
route.path.match(blockRouteManager) ||
value
) {
console.log('isUser', user.isUser)
redirect('/')
}
}
}
if (!user) {
if (
route.path.match(blockRouteAdmin) ||
route.path.match(blockRouteManager)
) {
redirect('/')
} else {
redirect()
}
}
}
Here is my nuxt.config.js
export default {
// Target: https://go.nuxtjs.dev/config-target
target: 'static',
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'aitl',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' },
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: ['~/plugins/firebaseConfig.js'],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/buefy
'nuxt-buefy',
// https://go.nuxtjs.dev/pwa
'#nuxtjs/pwa',
// https://go.nuxtjs.dev/content
'#nuxt/content',
],
// PWA module configuration: https://go.nuxtjs.dev/pwa
pwa: {
manifest: {
lang: 'en',
},
},
// Content module configuration: https://go.nuxtjs.dev/config-content
content: {},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {},
}
My index.js inside store.
import { vuexfireMutations } from 'vuexfire'
import { getUserFromCookie } from '../helper/index.js'
export const mutations = {
...vuexfireMutations,
}
export const actions = {
async nuxtServerInit({ dispatch, commit }, { req }) {
try {
const user = getUserFromCookie(req)
if (user) {
await dispatch('user/setUSER', {
email: user.email,
isAdmin: user.admin,
isManager: user.manager,
isUser: user.user,
uid: user.user_id,
name: user.name,
})
}
} catch (err) {
console.log(err)
}
},
}
User.js in store folder
import { auth } from '../plugins/firebaseConfig'
import Cookies from 'js-cookie'
export const state = () => ({
user: null,
})
export const getters = {
user(state) {
return state.user
},
}
export const actions = {
async userlogin({ dispatch }, user) {
try {
const token = await auth.currentUser.getIdToken(true)
const userInfo = {
email: user.email,
isAdmin: user.admin,
isManager: user.manager,
isUser: user.user,
uid: user.uid,
name: user.displayName,
}
Cookies.set('access_token', token)
await dispatch('setUSER', userInfo)
} catch (err) {
console.log(err)
}
},
setUSER({ commit }, user) {
commit('setUSER', user)
},
}
export const mutations = {
setUSER(state, user) {
state.user = user
},
}
The issue was solved by going from target: 'static' to target: 'server', aka mirroring the settings of another working project.
im trying to setup nextjs 12 with ant design antd and in next.config.js when i try to setup withAntdLess it gives type error
Type '{}' is missing the following properties from type '{ esModule: boolean; sourceMap: boolean; modules: { mode: string; }; }': esModule, sourceMap, modules
although all props are optional according to next-plugin-antd-less docs
next.config.js file:
// #ts-check
// next.config.js
const withAntdLess = require('next-plugin-antd-less');
/**
* #type {import('next').NextConfig}
**/
module.exports =withAntdLess({
cssLoaderOptions: {},
// Other Config Here...
webpack(config) {
return config;
},
reactStrictMode: true,
});
I solved it using next-with-less https://github.com/elado/next-with-less
next.config.js
const withLess = require('next-with-less');
const lessToJS = require('less-vars-to-js');
const themeVariables = lessToJS(
fs.readFileSync(
path.resolve(__dirname, './public/styles/custom.less'),
'utf8'
)
);
module.exports = withLess({
...
lessLoaderOptions: {
lessOptions: {
javascriptEnabled: true,
modifyVars: themeVariables, // make your antd custom effective
localIdentName: '[path]___[local]___[hash:base64:5]',
},
},
...
})
Import your custom less file on top off the file _app.jsx
import 'public/styles/custom.less';
...
Import the default Antd less file on your custom less file: (in my case public/styles/custom.less)
#import "~antd/dist/antd.less";
....
Extra notes:
If you have an old implementation of Antd, you should remove the integration in your .babelrc
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "lib",
"style": true
}
],
If you have an old implementation of Antd, you should remove the integration in your webpack zone in your next.config.js
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
I use Nuxt (universal mode) with Firestore, I set rules properly but I still get this error:
I get Missing or insufficient permissions
when I reload the page. When ccessing the page through the router, I don't get any error, the rules re working as it's supposed to.
Firestore is called in the asyncData (as it's a SSR app).
If I check the req.user in asynData when loading the page, everything looks fine. I have the roles and all information. It seems that serverMiddleware is activated after the asyncData, when reloading the browser. Any ideas why?
My Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
function isApplicant() {
return request.auth.token.roles.applicant == true
}
match /tests/{test} {
allow read: if isApplicant();
}
}
}
serverMiddleware:
const admin = require('../services/firebase-admin-init.js')
const cookieParser = require('cookie-parser')();
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
module.exports = async function (req, res, next) {
await getIdTokenFromRequest(req, res).then(idToken => {
if (idToken) {
addDecodedIdTokenToRequest(idToken, req).then(() => {
next();
});
} else {
next();
}
});
}
function getIdTokenFromRequest(req, res) {
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
return Promise.resolve(req.headers.authorization.split('Bearer ')[1]);
}
return new Promise(function(resolve) {
cookieParser(req, res, () => {
if (req.cookies && req.cookies.__session) {
console.log("new")
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
resolve(req.cookies.__session);
} else {
resolve();
}
});
});
}
/**
* Returns a Promise with the Decoded ID Token and adds it to req.user.
*/
function addDecodedIdTokenToRequest(idToken, req) {
// console.log('Start addDecodedIdTokenToRequest')
return admin.auth().verifyIdToken(idToken).then(decodedIdToken => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
}).catch(error => {
console.error('Error while verifying Firebase ID token:', error);
});
}
Nuxt config files:
const path = require('path');
const environment = process.env.NODE_ENV || 'development';
const envSet = require(`./config/env.${environment}.js`);
module.exports = {
env: envSet,
mode: 'universal',
plugins: [
{ src: '~/plugins/fireinit.js', ssr: false, },
{ src: '~/plugins/auth-cookie.js', ssr: false },
{ src: '~/plugins/fireauth.js', ssr: false }
],
router: {
},
serverMiddleware: [
'~/serverMiddleware/validateFirebaseIdToken'
],
modules: [
'vue-scrollto/nuxt',
['nuxt-validate', {
lang: 'ja'
}],
'#nuxtjs/axios',
],
css: [
{src: '~/assets/scss/app.scss', lang: 'scss'}
],
/*
** Customize the progress bar color
*/
loading: { color: '#403a8f' },
resolve: {
extensions: ['.js', '.json', '.vue', '.ts'],
root: path.resolve(__dirname),
alias: {
'~': path.resolve(__dirname),
'#': path.resolve(__dirname)
},
},
/*
** Build configuration
*/
buildDir: '../cloud/front/',
build: {
publicPath: '/assets/',
extractCSS: true,
quiet: false,
terser: {
terserOptions: {
compress: {
drop_console: environment === 'production' ? true : false,
},
},
},
},
}
Warning: Possible Unhandled Promise Rejection (id: 0)
TypeError: Object is not a function (evaluating 'concreteComponentProvider()')
This is the Warning I get after adding React Redux in my App. It might some conflict about the React Redux and React Native Navigation (latest version) or also the React Native Vector Icons.
I think what causing the Error is in this code, the startMainTabs.js. This is where I code all my React Native Navigation.
Feel free to ask for more of my codes or any questions. Thank you!
These are my codes:
App.js
import {Provider} from 'react-redux';
import configureStore from './src/store/configureStore';
const store = configureStore();
//Register Screens
Navigation.registerComponent("Event.AuthScreen", () => AuthScreen);
Navigation.registerComponent("Event.Map", () => EventMap);
Navigation.registerComponent("EventCreator", () => EventCreator, store, Provider);
Navigation.registerComponent("EventHome", () => EventHome, store, Provider);
Navigation.setRoot({
root: {
stack: {
children: [{
component: {
name: "Event.AuthScreen",
}
}],
options: {
topBar: {
title: {
text: 'Welcome'
}
}
}
}
}
});
startMainTabs.js
const startTabs = () => {
Promise.all([
Icon.getImageSource("ios-home", 30),
Icon.getImageSource("ios-map", 30),
Icon.getImageSource("ios-share-alt", 30)
]).then(sources => {
Navigation.setRoot({
root: {
bottomTabs: {
children: [{
stack: {
children: [{
component: {
name: "Event.Map",
}
}],
options: {
bottomTab: {
icon: sources[1],
testID: 'FIRST_TAB_BAR_BUTTON'
}
}
}
},
{
stack: {
children: [{
component: {
name: "EventHome"
}
}],
options: {
bottomTab: {
icon: sources[0],
testID: 'SECOND_TAB_BAR_BUTTON'
}
}
}
},
{
component: {
name: "EventCreator",
options: {
bottomTab: {
icon: sources[2],
testID: 'THIRD_TAB_BAR_BUTTON'
}
}
}
}
]
}
}
});
})
}
in newest version of react native navigation from wix you should use
registerComponentWithRedux instead of registerComponent,
and set Provider before store
Navigation.registerComponentWithRedux("Event.AuthScreen", () => AuthScreen,Provider,store);
Navigation.registerComponentWithRedux("Event.Map", () => EventMap,Provider,store);
Navigation.registerComponentWithRedux("EventCreator", () => EventCreator,Provider,store);
Navigation.registerComponentWithRedux("EventHome", () => EventHome,Provider,store);
In the newest version of the RNN you should use registerComponentWithRedux instead of registerComponent and the Provider comes first then store, like the above code example