Gatsby window not available during server side rendering build error - javascript

I am trying to build my Gatsby site to deploy to production, but I have run into an issue which I believe is related to one line of code within my application where I use window to redirect to an external url that is retrieve from a GET request made on a button click. I tried to read through the documentation on this issue and attempted to use their window check conditional, but my redirect either didn't work or I received an error related to Can't resolve 'module'. Is there an alternative route to either replacing the use of window all together or fixing why this runs during build and causing the error?
Error:
failed Building static HTML for pages - 2.611s
ERROR #95312
"window" is not available during server side rendering.
Error for code checking for window:
WebpackError: ReferenceError: window is not defined
Code:
authClick(e) {
e.preventDefault();
this.setState({ authorizing: true }, ()=> {
axios.get(auth_url)
.then((res) => {
this.setState({
authorizing: false
})
window.location.replace(res.data) // Window call
// Attempt to fix the issue based on documentation
// if (typeof window !== `undefined`){
// const module = require("module")
// window.location.replace(res.data)
// }
}).catch((err) => {
console.log(err)
})
});
}
// Component
<button className="inline-flex items-center" onClick={this.authClick} disabled={this.state.authorizing}>
Auth
</button>

const isBrowser = () => typeof window !== "undefined"
isBrowser() && window.location.replace(res.data)
If you want to use window.location then you have to check first then use it isBrowser() && window.location.replace(res.data) instade of window.location.replace(res.data)

If you don't need to require any module based on the window availability, don't do it. In your case, you only need to make a redirection, not to importing any module.
Just:
authClick(e) {
e.preventDefault();
this.setState({ authorizing: true }, ()=> {
axios.get(auth_url)
.then((res) => {
this.setState({
authorizing: false
})
if (typeof window !== `undefined`){
window.location = res.data
}
}).catch((err) => {
console.log(err)
})
});
}
Note the change of making a redirection with window.location but you can also use your window.location.replace if you want.

The link to the documentation is correct the window is not available during the build process so you must define a check.
The term module in the documentation would be replaced by the actual name of the module you are referencing. Then you would wrap the import/require with the window check as mentioned in the documentation.
For your purposes the code would look like this:
...
if (typeof window !== `undefined`){
window.location.replace(res.data) // Window call
}
...
You would not need the require module since you are not importing a module.
Hope that clarifies.

Tip: put typeof window !== 'undefined' && window.whaterver-you-need to solve this.

Related

"process is not defined" when used in a function (Vue/Quasar)

The following snippet represents a Pinia store in my Vue 3 / Quasar 2 application. This store uses the environment variable VUE_APP_BACKEND_API_URL which shall be read from either the window object or process.env.
However I don't understand why the first variant is wokring but the second is not. Using the getEnv function always results in a Uncaught (in promise) ReferenceError: process is not defined error.
import { defineStore } from 'pinia';
function getEnv(name) {
return window?.appConfig?.[name] || process.env[name];
}
// 1. this is working
const backendApiUrl = window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL;
// 2. this is NOT working
const backendApiUrl = getEnv('VUE_APP_BACKEND_API_URL');
export const useAppConfigStore = defineStore('appConfig', {
state: () => ({
authorizationUrl: new URL(
'/oauth2/authorization/keycloak',
backendApiUrl,
).toString(),
logoutUrl: new URL('/logout', backendApiUrl).toString(),
backendApiUrl: new URL(backendApiUrl).toString(),
}),
});
NodeJS-specific stuff like process doesn't exist in the browser environments. Both Webpack and Vite implementations work by replacing process.env.XYZ expressions with their values on build time. So, just process.env, or process.env[name] will not be replaced, which will lead to the errors you are experiencing. See the caveats section and related Webpack/Vite docs and resources. So, unfortunately, the only easy way seems to be the first long and repetitive way you've tried(const backendApiUrl = window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL;). You can try embedding this logic in a single object, then use the function to access it.
const config = {
VUE_APP_BACKEND_API_URL: window?.appConfig?.VUE_APP_BACKEND_API_URL || process.env.VUE_APP_BACKEND_API_URL
}
export function getEnv(name) {
return config[name];
}
This way it will be longer and more repetitive to define it the first time, but at least you will be able to use it easily through the code base.
This is late, but it might help someone, I was able to resolve this by adding below to my quasar.conf.js
build: {
vueRouterMode: 'hash', // available values: 'hash', 'history'
env: {
API_ENDPOINT: process.env.API_ENDPOINT ? process.env.API_ENDPOINT : 'http://stg.....com',
API_ENDPOINT_PORT: process.env.API_ENDPOINT_PORT ? process.env.API_ENDPOINT_PORT : '0000',
...env
},
}
For more information ge here: https://github.com/quasarframework/quasar/discussions/9967

Typescript + webpack: Check for variable that is not defined in Node, but defined in Browser

I am trying to write a package for server and client use with as little modification as needed.
Some libs are part of Node but not included in a browser, others are available in a browser but not in Node.
Using https://github.com/lmaccherone/node-localstorage for example, I want to require the library when on Node, but in a browser, localStorage is already available.
I would like to check whether localStorage is available with a declare statement found in https://stackoverflow.com/a/65755447/2875404 so it looks like that:
declare var localStorage: any | undefined;
if (typeof localStorage === "undefined" || localStorage === null) {
console.log("need to use Node localStorage")
var LocalStorage = require('node-localstorage').LocalStorage;
var localStorage = new LocalStorage('./localStorage');
} else {
console.log("using Browser localStorage")
}
After wrapping up the package in webpack and, for a test, running it in a Chrome snippet, the "need to use Node localStorage" message pops up in console. If I manually edit the webpack'd code to console.log(localStorage) it actually does print the localStorage (so it must be there) - additionally, when I remove the whole if (typeof... block, the code accessing localStorage seems to run just fine.
What exactly do I need to do to make this "hybrid decision" function work? Can this have to do with webpack somehow putting things into a "deeper" scope that doesn't have access to window variables such as localStorage? But why is it possible to print and interact with the class then? I'm confused.
As I said in the comment, you could use the global window to check if you're in browser context:
declare var localStorage: any | undefined;
if (typeof window === 'undefined') {
console.log("need to use Node localStorage")
var LocalStorage = require('node-localstorage').LocalStorage;
var localStorage = new LocalStorage('./localStorage');
} else {
console.log("using Browser localStorage")
}

Not counting DOM elements in React site with Cypress?

I can't count the number of DOM elements on a site written in React.
/// <reference types="cypress" />
context('Checking all components', () => {
beforeEach(() => {
cy.visit('https://news-israel.com');
});
it('Checking posts', () => {
cy.get('.posts-wrapper').find('a').should('exist');
cy.get('.posts-wrapper').find('a').its('length').should('be.gte', 100);
});
});
In this case, it doesn't find the "a" tags because React rendering them asynchronously and dynamically.
The "post-wrapper" class finds, followed by an exception:
The following error originated from your application code, not from Cypress.
Cannot read property 'substr' of undefined
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
How to correctly count the number of elements in this case, so that you can "wait for the elements"?
The site I'm testing is in production - https://news-israel.com
The error is coming from the app itself, and ultimately should be fixed in the app source.
But see this note in the log
This behavior is configurable, and you can choose to turn this off by listening to the uncaught:exception event.
This links to an event handler you can use to debug. Add this to the top of the test to suppress the test failing when the error occurs.
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})
Now the test works, provide you use the correct class posts-wrapper not post-wrapper.
If you are able to fix the source, the error comes from the react-typed library, which is used in BreakingNews.js at line 75
<Typed
strings={posts}
typeSpeed={15}
backSpeed={10}
backDelay={5000}
loop
/>
the posts variable is initially undefined, so you need a fallback value, e.g strings={posts || []}
To globally Handle Uncaught exceptions, Go to cypress/support/index.js and write:
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
Now to count the number of elements you do it via each() or by using Cypress.$
Using each():
cy.get('div.post-title').each((ele, list) => {}).then((list) => {
cy.log(list.length)
})
Using Cypress.$
cy.get('div').find('.post-title').then(ele => {
cy.log(Cypress.$(ele).length)
})
OR, As suggested by #Hiram
cy.get('div.post-title').then(ele => {
cy.log(Cypress.$(ele).length)
})

React build - Reference error: Window is not Defined - How to only run on client side

I've got a p5.js sketch that is included in my gatsby.js project. When I push and build my project on netlify.com it thows:
error "window" is not available during server side rendering.
on: window.requestAnimationFrame = (function() {
WebpackError: ReferenceError: window is not defined
Ok, I understand why. During the build the window DOM is not available. So I tried to work around it with two ways, as seen in my code example.
First is to use react loadable componant
Second to check if window is undefined (on build) and if so return something else than the main code.
my code example:
import loadable from '#loadable/component'
import sketch from "./p5_app";
class GetP5Wrapper extends React.Component {
render() {
if (typeof window === 'undefined')
return <span>loading...</span>
const P5Wrapper = loadable(() => import('react-p5-wrapper')) //loadable help is not to run on build
return <P5Wrapper sketch={sketch}/>
}
}
the question:
How do I get my sketch to only run on client side.
I finally figured it out. I've switched from react-p5-wrapper to react-p5. It kept giving problems, even with the solution. So I switched. Also I've moved the check if window exists into the component. Which is much cleaner
render() {
if (typeof window !== 'undefined') {
const Sketch = loadable(() => import('react-p5'));
return <Sketch setup={this.setup} draw={this.draw}/>
} else { // if window does not exist
return null;
}
}

Protractor testing, access and modify Window object properties

I'm trying to write a simple e2e test for the authentication we use in our project, Authentication is based on a json web token which is set into window.localStorage.satellizer_token .
To set it i use the code below, but for what i see it doesn't really set the real localStorage property of the window object.
describe('login', function () {
it('should set the satellizer token and be allowed to get panel', function () {
browser.driver.get('http://example.com/');
browser.driver.executeScript(function () {
return window.localStorage;
}).then(function (localStorage) {
expect(localStorage.satellizer_token).toBe(undefined);
localStorage.satellizer_token = "eyJ0fdaccKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjE3MjUzIiwiaWF0IjoxNDM0Mzc1NjU3LCJleHAiOjE0NjU5Mjk2NTd9.VbhdWQ_gOb7X8pmOGLDjBKURxcaWQlIXQGvLRansQCphk";
expect(localStorage.satellizer_token).toBe("eyJ0fdaccKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjE3MjUzIiwiaWF0IjoxNDM0Mzc1NjU3LCJleHAiOjE0NjU5Mjk2NTd9.VbhdWQ_gOb7X8pmOGLDjBKURxcaWQlIXQGvLRansQCphk");
browser.driver.get('http://example.com/panel');
expect(browser.driver.getTitle()).toEqual('http://example.com/panel');
expect(browser.driver.getCurrentUrl()).toEqual('http://example.com/panel');
});
});
});
I know there is already something similar here and here but all the examples i can find are about access-only, i need also to modify window properties.
What is the correct way to interact with the window object in protractor tests?
Working solution:
browser.executeScript(function () {
window.localStorage.satellizer_token = "eyJ0eXAiOiJKV1QiLCJhbGasdsOiJIUzI1NiJ9.eyJpZCI6IjE3MjUzIiwiaWF0IjoxNDM0Mzc1NjU3LCJleHAiOjE0NjU5Mjk2NTd9.VbhdWQ_gOb7X8pmOGLDjBKURQUQlcAfGSGvLRansQCphk";
});
browser.driver.manage().window()
Seen here: https://github.com/bolshchikov-public/protractor-best-practices/blob/master/Practices.md#set-screen-size

Categories