Use babel transform with create-react-app - babeljs

I am working on a javaScript / react playground (something like very simple codesandbox.io) and I'm trying to figure out how to transpile the code. I was thinking of using Babel transform but the app itself is built using create-react-app so I do not have access to Babel. My question is, if I do something like the following and install Babel, will it also override how create-rect-app currently transpiles the code for the app?
// transpile.js
const babelOptions = {
presets: [ "react", ["es2015", { "modules": false }]]
}
export default function preprocess(str) {
const { code } = Babel.transform(str, babelOptions);
return code;
}
EDIT:
I've since learned that I can use Babel standalone for exactly this use case! Now it's just to figure out how to configure it. I would still appreciate help but if I find a solution first I will post for others :)

Ok so I have figured this out but it is not straight forward. I will try to add some details here in case anyone else finds it helpful.
I first needed to load Babel standalone and so I used this answer to create a custom hook to load a script:
import { useEffect } from 'react';
const useScript = url => {
useEffect(() => {
const script = document.createElement('script');
script.src = url;
script.async = true;
document.body.appendChild(script);
console.log(`${url} script loaded`);
return () => {
document.body.removeChild(script);
console.log(`${url} script removed`);
}
}, [url]);
};
export default useScript;
then I used it in my component like this:
import useScript from '../../../hooks/useScript';
useScript("https://unpkg.com/#babel/standalone/babel.min.js");
then I later use the code I wrote in the initial question to transpile my code.

Related

NEXTJS fix window is not defined on import [duplicate]

In my Next.js app I can't seem to access window:
Unhandled Rejection (ReferenceError): window is not defined
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
̶A̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶i̶s̶ ̶b̶y̶ ̶u̶s̶i̶n̶g̶ ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r ̶ ̶t̶o̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶ ̶d̶u̶r̶i̶n̶g̶ ̶r̶e̶n̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶.
But process object has been deprecated in Webpack5 and also NextJS, because it is a NodeJS variable for backend side only.
So we have to use back window object from the browser.
if (typeof window !== "undefined") {
// Client-side-only code
}
Other solution is by using react hook to replace componentDidMount:
useEffect(() => {
// Client-side-only code
})
Move the code from componentWillMount() to componentDidMount():
componentDidMount() {
console.log('window.innerHeight', window.innerHeight);
}
In Next.js, componentDidMount() is executed only on the client where window and other browser specific APIs will be available. From the Next.js wiki:
Next.js is universal, which means it executes code first server-side,
then client-side. The window object is only present client-side, so if
you absolutely need to have access to it in some React component, you
should put that code in componentDidMount. This lifecycle method will
only be executed on the client. You may also want to check if there
isn't some alternative universal library which may suit your needs.
Along the same lines, componentWillMount() will be deprecated in v17 of React, so it effectively will be potentially unsafe to use in the very near future.
If you use React Hooks you can move the code into the Effect Hook:
import * as React from "react";
export const MyComp = () => {
React.useEffect(() => {
// window is accessible here.
console.log("window.innerHeight", window.innerHeight);
}, []);
return (<div></div>)
}
The code inside useEffect is only executed on the client (in the browser), thus it has access to window.
With No SSR
https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home
The error occurs because window is not yet available, while component is still mounting. You can access window object after component is mounted.
You can create a very useful hook for getting dynamic window.innerHeight or window.innerWidth
const useDeviceSize = () => {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
const handleWindowResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}
useEffect(() => {
// component is mounted and window is available
handleWindowResize();
window.addEventListener('resize', handleWindowResize);
// unsubscribe from the event on component unmount
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
return [width, height]
}
export default useDeviceSize
Use case:
const [width, height] = useDeviceSize();
componentWillMount() lifecycle hook works both on server as well as client side. In your case server would not know about window or document during page serving, the suggestion is to move the code to either
Solution 1:
componentDidMount()
Or, Solution 2
In case it is something that you only want to perform in then you could write something like:
componentWillMount() {
if (typeof window !== 'undefined') {
console.log('window.innerHeight', window.innerHeight);
}
}
In the constructor of your class Component you can add
if (typeof window === 'undefined') {
global.window = {}
}
Example:
import React, { Component } from 'react'
class MyClassName extends Component {
constructor(props){
super(props)
...
if (typeof window === 'undefined') {
global.window = {}
}
}
This will avoid the error (in my case, the error would occur after I would click reload of the page).
global?.window && window.innerHeight
It's important to use the operator ?., otherwise the build command might crash.
Best solution ever
import dynamic from 'next/dynamic';
const Chart = dynamic(()=> import('react-apexcharts'), {
ssr:false,
})
A bit late but you could also consider using Dynamic Imports from next turn off SSR for that component.
You can warp the import for your component inside a dynamic function and then, use the returned value as the actual component.
import dynamic from 'next/dynamic'
const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
ssr: false,
})
<>
<BoardDynamic />
</>
I have to access the hash from the URL so I come up with this
const hash = global.window && window.location.hash;
Here's an easy-to-use workaround that I did.
const runOnClient = (func: () => any) => {
if (typeof window !== "undefined") {
if (window.document.readyState == "loading") {
window.addEventListener("load", func);
} else {
func();
}
}
};
Usage:
runOnClient(() => {
// access window as you like
})
// or async
runOnClient(async () => {
// remember to catch errors that might be raised in promises, and use the `await` keyword wherever needed
})
This is better than just typeof window !== "undefined", because if you just check that the window is not undefined, it won't work if your page was redirected to, it just works once while loading. But this workaround works even if the page was redirected to, not just once while loading.
I was facing the same problem when i was developing a web application in next.js This fixed my problem, you have to refer to refer the window object in a life cycle method or a react Hook. For example lets say i want to create a store variable with redux and in this store i want to use a windows object i can do it as follows:
let store
useEffect(()=>{
store = createStore(rootReducers, window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__())
}, [])
....
So basically, when you are working with window's object always use a hook to play around or componentDidMount() life cycle method
I wrapped the general solution (if (typeof window === 'undefined') return;) in a custom hook, that I am very pleased with. It has a similiar interface to reacts useMemo hook which I really like.
import { useEffect, useMemo, useState } from "react";
const InitialState = Symbol("initial");
/**
*
* #param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
* #param deps Factory function dependencies, just like in `useMemo`.
* #param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
*/
export function useClientSideMemo<T = any, K = T>(
clientFactory: () => T | Promise<T>,
deps: Parameters<typeof useMemo>["1"],
serverFactory?: () => K
) {
const [memoized, setMemoized] = useState<T | typeof InitialState>(
InitialState
);
useEffect(() => {
(async () => {
setMemoized(await clientFactory());
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return typeof window === "undefined" || memoized === InitialState
? serverFactory?.()
: memoized;
}
Usage Example:
I am using it to dynamically import libaries that are not compatible with SSR in next.js, since its own dynamic import is only compatible with components.
const renderer = useClientSideMemo(
async () =>
(await import("#/components/table/renderers/HighlightTextRenderer"))
.HighlightTextRendererAlias,
[],
() => "text"
);
As you can see I even implemented a fallback factory callback, so you may provide a result when initially rendering on the server aswell. In all other aspects this hook should behave similiar to reacts useMemo hook. Open to feedback.
For such cases, Next.js has Dynamic Import.
A module that includes a library that only works in the browser, it's suggested to use Dynamic Import. Refer
Date: 06/08/2021
Check if the window object exists or not and then follow the code along with it.
function getSelectedAddress() {
if (typeof window === 'undefined') return;
// Some other logic
}
For Next.js version 12.1.0, I find that we can use process.title to determine whether we are in browser or in node side. Hope it helps!
export default function Projects(props) {
console.log({ 'process?.title': process?.title });
return (
<div></div>
);
}
1. From the terminal, I receive { 'process?.title': 'node' }
2. From Chrome devtool, I revice { 'process?.title': 'browser' }
I had this same issue when refreshing the page (caused by an import that didn't work well with SSR).
What fixed it for me was going to pages where this was occurring and forcing the import to be dynamic:
import dynamic from 'next/dynamic';
const SomeComponent = dynamic(()=>{return import('../Components/SomeComponent')}, {ssr: false});
//import SomeComponent from '../Components/SomeComponent'
Commenting out the original import and importing the component dynamically forces the client-side rendering of the component.
The dynamic import is covered in Nextjs's documentation here:
https://nextjs.org/docs/advanced-features/dynamic-import
I got to this solution by watching the youtube video here:
https://www.youtube.com/watch?v=DA0ie1RPP6g
You can define a state var and use the window event handle to handle changes like so.
const [height, setHeight] = useState();
useEffect(() => {
if (!height) setHeight(window.innerHeight - 140);
window.addEventListener("resize", () => {
setHeight(window.innerHeight - 140);
});
}, []);
You can try the below code snippet for use-cases such as - to get current pathname (CurrentUrl Path)
import { useRouter } from "next/router";
const navigator = useRouter()
console.log(navigator.pathname);
For anyone who somehow cannot use hook (for example, function component):
Use setTimeout(() => yourFunctionWithWindow()); will allow it get the window instance. Guess it just need a little more time to load.
I want to leave this approach that I found interesting for future researchers. It's using a custom hook useEventListener that can be used in so many others needs.
Note that you will need to apply a little change in the originally posted one, like I suggest here.
So it will finish like this:
import { useRef, useEffect } from 'react'
export const useEventListener = (eventName, handler, element) => {
const savedHandler = useRef()
useEffect(() => {
savedHandler.current = handler
}, [handler])
useEffect(() => {
element = !element ? window : element
const isSupported = element && element.addEventListener
if (!isSupported) return
const eventListener = (event) => savedHandler.current(event)
element.addEventListener(eventName, eventListener)
return () => {
element.removeEventListener(eventName, eventListener)
}
}, [eventName, element])
}
If it is NextJS app and inside _document.js, use below:
<script dangerouslySetInnerHTML={{
__html: `
var innerHeight = window.innerHeight;
`
}} />

Import geojson in NextJS

I'm trying to import a GeoJSON file on NextJS but it says:
Module parse failed: Unexpected token (2:8)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
It worked fine when the project was in ReactJS with create-react-app but now that we migrate to NextJS it doesn't.
Maybe I need to configure some loaders on next.config.js but I don't know how to do it
Here is my next.config.js:
const withCSS = require("#zeit/next-css")
const withLess = require('#zeit/next-less');
const withImages = require('next-images')
module.exports = withCSS(withLess({
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
return config;
},
lessLoaderOptions: {
javascriptEnabled: true,
},
}));
Can someone help me achieve this?
Okay guys, I managed to do it!
I will try to explain what I wanted to accomplish, what was happening and what do I did.
I wanted to load a geojson data from a file into google maps api to load some layers, so I wanted to use it on map.data.loadGeoJson(imported_file.geojson)
First, I needed to make Nextjs load my file from the source so I installed json-loader
npm i --save-dev json-loader
And then added it to next.config.js
const withCSS = require("#zeit/next-css")
const withLess = require('#zeit/next-less');
const withImages = require('next-images')
module.exports = withCSS(withLess({
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
config.module.rules.push({
test: /\.geojson$/,
use: ["json-loader"]
});
return config;
},
lessLoaderOptions: {
javascriptEnabled: true,
},
}));
And then, no more error message importing the geojson file!
But now, another problem. The layers didn't load! So I read Google Maps API and tried another method to load the geojson.
map.data.addGeoJson(imported_file.geojson)
And it worked! Hope it can help who is in trouble.

Can I call Ionic 4 / Capacitor Electron code from the Ionic part of the application?

I am investigating using Ionic 4/ Capacitor to target Windows via the Electron option, for an application where I want to use SQLite.
Using the Ionic Native SQLite plugin, which wraps this Cordova plugin, out of the box, as far as I can see, the Windows support is for UWP, and not Desktop, which runs using Electron in Ionic Capacitor wrapper.
My plan, was to see if I could use Electron SQLite package, and then call this from my Ionic application by making a wrapper class for the Ionic native similar to what I used to get browser support by following this tutoral
If I can call the Electron code from my Ionic app, then I can't see why this wouldn't work.
So, my question here is, can I call code (I will add functions to use the SQlite) I add to the hosting Electron application from within the Ionic (web) code? And if so, how?
Thanks in advance for any help
[UPDATE1]
Tried the following...
From an Ionic page, I have a button click handler where I raise an event..
export class HomePage {
public devtools() : void {
let emit = new EventEmitter(true);
emit.emit('myEvent');
var evt = new CustomEvent('myEvent');
window.dispatchEvent(evt);
}
Then within the Electron projects index.js, I tried..
mainWindow.webContents.on('myEvent', () => {
mainWindow.openDevTools();
});
const ipc = require('electron').ipcMain
ipc.on('myEvent', (ev, arg) => {
mainWindow.openDevTools();
});
But neither worked.
I should mention I know very little about Electron. This is my first exposure to it (via Capacitor)
In case someone is interested, this is how I solved this.
Im am using Ionic 4 / Capacitor + Vue 3.
In my entry file (app.ts) I have declared a global interface called Window as follows:
// app.ts
declare global { interface Window { require: any; } }
Then, I have written the following class:
// electron.ts
import { isPlatform } from '#ionic/core';
export class Electron
{
public static isElectron = isPlatform(window, 'electron');
public static getElectron()
{
if (this.isElectron)
{
return window.require('electron');
}
else
{
return null;
}
}
public static getIpcRenderer()
{
if (this.isElectron)
{
return window.require('electron').ipcRenderer;
}
else
{
return null;
}
}
public static getOs()
{
if (this.isElectron)
{
return window.require('os');
}
else
{
return null;
}
}
}
And I use it like this:
//electronabout.ts
import { IAbout } from './iabout';
import { Plugins } from '#capacitor/core';
import { Electron } from '../utils/electron';
export class ElectronAbout implements IAbout
{
constructor() { }
public async getDeviceInfo()
{
let os = Electron.getOs();
let devInfo =
{
arch: os.arch(),
platform: os.platform(),
type: os.type(),
userInfo: os.userInfo()
};
return devInfo;
}
public async showDeviceInfo()
{
const devInfo = await this.getDeviceInfo();
await Plugins.Modals.alert({ title: 'Info from Electron', message: JSON.stringify(devInfo) });
}
}
This is working but, of course, I still need to refactor the Electron class (electron.ts). Probably using the singleton pattern is a better idea.
I hope this helps.
Update
You can communicate from the render process with your main process (index.js) like this:
//somefile.ts
if (Electron.isElectron)
{
let ipc = Electron.getIpcRenderer();
ipc.once('hide-menu-button', (event) => { this.isMenuButtonVisible = false; });
}
//index.js
let newWindow = new BrowserWindow(windowOptions);
newWindow.loadURL(`file://${__dirname}/app/index.html`);
newWindow.webContents.on('dom-ready', () => {
newWindow.webContents.send('hide-menu-button');
newWindow.show();
});
I dug into this yesterday and have an example for you using angular(this should apply to ionic too).
in your service declare require so we can use it
//Below your imports
declare function require(name:string);
Then in whatever function you want to use it in:
// Require the ipcRenderer so we can emit to the ipc to call a function
// Use ts-ignore or else angular wont compile
// #ts-ignore
const ipc = window.require('electron').ipcRenderer;
// Send a message to the ipc
// #ts-ignore
ipc.send('test', 'google');
Then in the created index.js within the electron folder
// Listening for the emitted event
ipc.addListener('test', (ev, arg) => {
// console.log('ev', ev);
console.log('arg', arg);
});
Its probably not the correct way to access it but its the best way i could find. From my understanding the ipcRenderer is used for when you have multiple browsers talking to each other within electron. so in our situation it enables our web layer to communicate with the electron stuff

Avoid making .babelrc file to use for testing with Jest

I realize that it is recommended to make a .babelrc file to run tests with Jest according to their docs. But is there any way I could load the babelrc config programmatically and therefore not have to create this file for every React project that I have? Also, I realize I could put something in my package.json file, but I don't want to have to do that either.
You can take advantage of Jest's scriptPreprocessor config setting. I created a file that looked like this and it worked:
const babel = require('babel-core')
const jestPreset = require('babel-preset-jest')
module.exports = {
process: function (src) {
const transformCfg = {
presets: ['es2015', 'react', 'stage-0', jestPreset],
retainLines: true
}
return babel.transform(src, transformCfg).code
}
}

Observe changes in TinyMCE from Dart

According to TinyMCE API, the following JavaScript code observe changes in TinyMCE editor:
tinyMCE.init({
...
setup : function(ed) {
ed.onChange.add(function(ed, l) {
console.debug('Editor contents was modified. Contents: ' + l.content);
});
}
});
However, I'm unable to run this code from Dart using the js Library. Help is appreciated.
UPDATE:
There is a problem in the JS code above. Alternatively, I found this working code in here:
var ed = new tinymce.Editor('textarea_id', {
init_setting_item: 1,
}, tinymce.EditorManager);
ed.on('change', function(e) {
var content = ed.getContent();
console.log(content);
});
ed.render();
I still need help running the code from Dart. And preferably storing its results in a Dart variable for subsequent processing.
Here's the same code called from Dart :
var ed = new js.Proxy(js.context.tinymce.Editor, 'textarea_id', js.map({
'init_setting_item': 1
}), js.context.tinymce.EditorManager);
js.retain(ed); // retain allows to use 'ed' in the following callback
ed.on('change', new js.Callback.many((e) {
var content = ed.getContent();
window.console.log(content);
}));
ed.render();