Qt Web Assembly application and Next.JS: handle is undefined - qt

I am trying to embed a Qt 5.15.2 Web Assembly application in a Next.js app. The Qt app is running fine when loaded by qtloader.js into a simple HTML page, but it crashes when built as an ES6 module and loaded into a React component.
The Qt app WASM loads fine and the app boots. It crashes when Qt calls back the stringToUTF16 javacript function exposed by Emscripten (v1.39.8):
exception thrown: TypeError: handle is undefined,__emval_get_property#webpack-internal:///./pages/MyApp.js:912:1085
QWasmString::toQString(emscripten::val const&)#http://localhost:8000/_next/static/94c486d8c8725ebf2f964854fb22d0f4.wasm
QWasmScreen::canvasId() const#http://localhost:8000/_next/static/94c486d8c8725ebf2f964854fb22d0f4.wasm
The Qt app is built with these Emscripten flags:
wasm {
QMAKE_LFLAGS += '-s USE_ES6_IMPORT_META=0 -s EXPORT_ES6=1 -s MODULARIZE=1 -s ENVIRONMENT="web"'
}
And loaded into the React component like this:
import React, { useRef, useEffect } from 'react'
import MyApp from './MyApp'
import MyAppWASM from './MyApp.wasm'
export default function MyAppCanvas({ props }) {
const canvasRef = useRef(null)
useEffect(() => {
MyApp({
qtCanvasElements: [canvasRef.current],
locateFile: () => {
return MyAppWASM
},
}).then((instance) => {
console.log('MyApp Loaded')
})
})
return (
<canvas
id="qtcanvas"
contentEditable="true"
onContextMenu={(e) => e.preventDefault()}
width="100%"
height="100%"
ref={canvasRef}
{...props}
></canvas>
)
}
Any hints about why the undefined handle to stringToUTF16 would be very welcome! Thanks

I've encountered exactly this problem, searched for the origin of the bug, and then I found out that the bug is already fixed in Git by this commit.
The fix should be released in Qt 6.3.0, due on April 12, 2022.

Related

Lazy loading fontawesome icons in vue3 + vite not working in DEV

In my vue3+vite project I'm using the official fontawesome vue3 package (see use with vue).
In order to enable tree-shaking you need to statically load the necessary icons (or possibly all of them) in advance using library.add. See for instance the following App.vue
<script setup>
import { ref, computed } from "vue";
import { library } from "#fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "#fortawesome/vue-fontawesome";
import { definition } from "#fortawesome/free-solid-svg-icons/faTruck";
library.add(definition);
const icon = ref("");
const showIcon = () => { icon.value = `fa-solid fa-truck`; };
</script>
<template>
<button #click="showIcon">Show Truck Icon</button>
<div v-if="icon">
<font-awesome-icon :icon="icon" />
</div>
</template>
here we statically load the truck icon and when you click the button the icon shows up.
What I was trying to do is loading the icons on demand (in this case, only when the button is clicked), using the following code:
<script setup>
import { ref, computed } from "vue";
import { library } from "#fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "#fortawesome/vue-fontawesome";
const modules = import.meta.glob(
"../node_modules/#fortawesome/free-solid-svg-icons/faTruck.js",
{ eager: false, import: "definition" }
);
const icon = ref("");
const showIcon = () => {
Object.values(modules)[0]().then((elem) => {
library.add(elem);
icon.value = `fa-solid fa-truck`;
});
};
</script>
<template>
<button #click="showIcon">Show Truck Icon</button>
<div v-if="icon">
<font-awesome-icon :icon="icon" />
</div>
</template>
But this doesn't work in "develpment" (npm run dev):
it makes a call to http://localhost:5173/node_modules/#fortawesome/free-solid-svg-icons/faTruck.js
then raises an error: Uncaught (in promise) ReferenceError: exports is not defined
while it works fine when the bundle is built (npm run build then for example serve the dist folder with http-server)
I suspect the problem is related to the fact that in development mode faTruck.js module is used "as is", while it is transpiled in the build phase.
Is there a solution?
NOTE:
The example contains only the "truck" because is over-simplified, but actually any icon should be loaded; i.e. the actual path in import.meta.glob should be ../node_modules/#fortawesome/free-solid-svg-icons/fa*.js
Full steps to reproduce the issue:
npm create vue#3 # accepts all defaults
cd vue-project
npm i #fortawesome/fontawesome-svg-core #fortawesome/free-solid-svg-icons #fortawesome/vue-fontawesome
# replace src/App.vue with the one indicated above
# run in dev with
npm run dev
# or build for prod and then expose using http-server
npm run build
npx http-server dist
Explaination
According to the Vite pre-bundling docs:
Vite's dev serves all code as native ESM. Therefore, Vite must convert dependencies that are shipped as CommonJS or UMD into ESM first
But when you use glob import with dynamic variables, your modules will not be pre-bundled. Since #fortawesome/free-solid-svg-icons/faTruck.js is a CommonJS file, it can not be used directly in ESM. And you are right that Vite does transform the module on production build, so it works well on production.
You may think about the optimizeDeps.include option but unfortunately, it does not help in this situation. Even if you add your module to the include list, Vite does pre-bundle your module but it will not use that pre-bundled file for your dynamic import. It still uses the file in node_modules/#fortawesome/free-solid-svg-icons/ folder.
I'm afraid that there is no straightforward solution to your problem. See this issue
Workaround
Just make it work differently on dev and prod.
const showIcon = async () => {
let x = 'faTruck'
let definition
if (import.meta.env.PROD) {
const iconModule = await import(
`../node_modules/#fortawesome/free-solid-svg-icons/${x}.js`
)
definition = iconModule.definition
} else {
const iconModule = await import(`#fortawesome/free-solid-svg-icons`)
definition = iconModule[x]
}
library.add(definition)
icon.value = `fa-solid fa-truck`
}
With this code, you still have the benefit of lazy loading on production and a smooth dev server to work
Another approach
Hard-coding your import list like so:
const showIcon = async (iconName) => {
const listImport = {
faTruck: () => import(`#fortawesome/free-solid-svg-icons/faTruck`),
faWarning: () => import(`#fortawesome/free-solid-svg-icons/faWarning`),
}
const iconModule = await listImport[iconName]()
console.log('iconModule', iconModule)
library.add(iconModule.definition)
}
But I bet you have hundreds of icons in your list so it hardly is an option

Storybook fails to load story with npm link: "react is not defined" (using React 18)

I'm building a UI component library using React 18 and exporting a Button.tsx component in it.
Then, to test that I can use that Button component, i'm importing it into a different component, Test.tsx, within that same UI component library code package (using npm link) and creating a story for that component.
So Test.tsx looks like this:
import Button from 'my-component-library'
const Test = (): JSX.Element => {
return (
<>
<Button title='test' body='test' />
</>
)
}
export default Test
And this is the Story for Test.tsx:
import { Meta, Story } from '#storybook/react'
import React from 'react'
import Test from './Test'
export default {
title: 'Test',
component: Test,
} as Meta
export const Default: Story<any> = () => <Test />
But if I want to view this story, I'm getting the following error in the Storybook UI:
Couldn't find story matching 'test--default'.
- Are you sure a story with that id exists?
- Please check your stories field of your main.js config.
- Also check the browser console and terminal for error messages.
And the following error in the browser console:
Unexpected error while loading ./components/Test/Test.stories.tsx: react is not defined
ReferenceError: react is not defined
How can I fix this?

Next js and Next Auth overlapping react declarations

I am running Next js and Next Auth in multiple project, and all of a sudden all of them decided to crash with the same error.
Module parse failed: Identifier '_react' has already been declared (14:6)
File was processed with these loaders:
* ./node_modules/next/dist/build/webpack/loaders/next-swc-loader.js
You may need an additional loader to handle the result of these loaders.
| const _material = require("#mui/material");
| const _xDataGrid = require("#mui/x-data-grid");
> const _react = require("next-auth/react");
| const _reportTable = /*#__PURE__*/
a simple example that crashes looks like this...
As you can see from the example below. I am not importing react twice.
import React from "react";
import { Box } from "#mui/material";
import { DataGrid, GridColDef, GridRowsProp } from "#mui/x-data-grid";
import { getSession } from "next-auth/react";
import ReportTable from "../src/components/ReportTable";
export default function Home() {
const findSession = () => {
const session = getSession();
console.log(session);
return session;
};
return (
<Box>
<ReportTable title="Price Books">
<DataGrid
sx={{ border: "0" }}
rows={rows}
columns={columns}
headerHeight={40}
/>
</ReportTable>
</Box>
);
}
If I remove the getSession import at the top everything runs fine. The other developers on my team can run these project just fine, so I believe it's an environmental issue on my side.
Has anyone else run into this issue?
I have built the project and it works fine. The errors only occur in my dev environment.
I also cloned the repo on my personal machine and it worked fine there as well.
The problem was with a new plugin that came out today, "Code Ninja". If you are facing this issue, disable that VSCode extension.

Why is Vercel failing to build my Next.js tsx app?

When I run npm run build and npm start on my local machine it deploys perfectly to localhost but when I try to deploy the very same code to Vercel I get the following error:
08:28:16 Failed to compile.
08:28:16 ./pages/index.tsx:5:20
08:28:16 Type error: Cannot find module '../components/layout' or its corresponding type declarations.
It definitely seems like an issue with the Layout component, I switched around the order of the important and it always fails when trying to load the Layout component. Here's the code for the component:
import Alert from "./alert";
import Footer from "./footer";
import Meta from "./meta";
type Props = {
preview?: boolean;
children: React.ReactNode;
};
const Layout = ({ preview, children }: Props) => {
return (
<>
<Meta />
<div className="min-h-screen">
<Alert preview={preview} />
<main>{children}</main>
</div>
<Footer />
</>
);
};
export default Layout;
index.tsx line 5 looks like this import Layout from "../components/layout"; and I've confirmed that that is the correct path for the Layout component.
are you sure the file name is layout.tsx not Layout.tsx :-)
I went through the same thing.
Fix layout.tsx to Layout.tsx
The file name and component name must be the same.

How to integrate Material Ui with Meteor?

I am trying to integrate Material Ui with meteor and as a sample test tried executing the below but ended up with errors and no Idea how to resolve it. Anyone there to assist me in fixing this. Below are few detail to track.
How I installed ? --> meteor npm install #material-ui/core
How I Integrated code? Through Blaze React component
ExampleTest.js
Template.ExampleTest.helpers({
ExampleContainer() {
return ExampleContainer;
}
});
ExampleContainer.js
const ExampleContainer = withTracker(() => {
---------
})(Example);
Example.js
import React, { Component } from "react";
import { Button } from "#material-ui/core";
class Example extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Button color="primary">Hello World</Button>
</div>
);
}
}
export default Example;
What error did I receive ?
Error: In template "ExampleTest", call to `{{> React ... }}` missing `component` argument.
at Blaze.View.<anonymous> (react-template-helper.js?hash=3fb2a2954362a4acdee8150fb77f0f500dd28206:67)
at blaze.js?hash=cbd85c3fe14949f2d2b9a3b76334f5f0e96d553c:1934
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=cbd85c3fe14949f2d2b9a3b76334f5f0e96d553c:3769)
at blaze.js?hash=cbd85c3fe14949f2d2b9a3b76334f5f0e96d553c:1932............
Any assistance on this ?
It looks like you're using Blaze template engine. You should use React instead.
https://www.meteor.com/tutorials/react/components
Material UI is a UI framework for use with React. It doesn't work with Blaze, and I don't think there is any way to use both Blaze and React in the same page.
To add Material UI to a Meteor/React project, install the package from the command line:
npm install #material-ui/core
And include the Roboto font in the head of your HTML:
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
For me this just worked, with nothing special needed for Meteor.
More instructions here: https://material-ui.com/getting-started/installation/

Resources