Closed mxmzb closed 1 year ago
When you do:
import * as faceapi from "@vladmandic/face-api/dist/face-api.esm.js";
NextJS
will ALSO try to import it server-side as it is primarily SSR framework.
And since you're importing browser ESM, name space faceapi.tf.util
does not exist as its not needed in browser (since browsers implements methods like TextEncoder
natively).
A good rule for NextJS
and any other server-side framework is to explicitly load library client-side only.
Example:
'use client';
import { useState, useEffect } from 'react';
import * as Faceapi from '@vladmandic/face-api/types/face-api'; // import typedefs only
export default function FaceAPI() {
const [faceapi, setFaceapi] = useState<typeof Faceapi>(); // create state variable with strong typing
const [options, setOptions] = useState<Faceapi.SsdMobilenetv1Options>(); // create state variable with strong typing
const modelPath = 'https://vladmandic.github.io/face-api/model/' // can also be local path
useEffect(() => {
const initFaceAPI = async () => {
if (options) return; // already initialized
const instance = await import('@vladmandic/face-api/dist/face-api.esm'); // actual dynamic import that happens client-side only even if its actually part of webpack (there is no extra fetch request)
setFaceapi(instance); // assign import to state variable so it can be reused
if (!faceapi) return; // safety check if import faIled
await faceapi.tf.setBackend('webgl');
await faceapi.tf.ready();
console.log({ faceapi: faceapi.version, tfjs: faceapi.tf.version_core, backend: faceapi.tf.getBackend(), flags: faceapi.tf.ENV['flags'], platform: faceapi.tf.ENV['platformName'] });
await faceapi.nets.ssdMobilenetv1.load(modelPath);
await faceapi.nets.faceLandmark68Net.load(modelPath);
await faceapi.nets.faceRecognitionNet.load(modelPath);
await faceapi.nets.faceExpressionNet.load(modelPath);
const opt = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.1, maxResults: 1 }); // create options well as we wont need multiple instances anyhow
setOptions(opt); // assign options object to state variable
};
initFaceAPI();
}, [faceapi, options]);
return (
<div>
<p>FaceAPI version {faceapi?.version}</p>
</div>
);
}
@vladmandic thank you, much appreciated! This was super helpful and accurate! 🙏🙏🙏🙏🙏🤠
@vladmandic The code works in development mode, but I've realized there is an issue with when building for production.
Here is what I'm getting in browser console after production build:
useEffect(() => {
const initFaceAPI = async () => {
try {
// 🚨🚨🚨 this is what fails 👇
const instance = await import('@vladmandic/face-api/dist/face-api.esm');
} catch(e) {
console.log({ e })
}
};
initFaceAPI();
}, [faceapi]);
Do you have any idea what could be going wrong (not too sure yet if this is more an issue with Next.js or face-api, still looking into it)? Thank you and merry Christmas! 🎄
FYI: I found the following solution (for now):
// next.config.js
webpack: (config) => {
config.optimization.minimize = false;
return config;
},
Issue Description
I am trying to build with the new
appDir
feature of Next.js, and I'm getting this:Dev environment works absolutely perfectly.
Steps to Reproduce
Create a page in
/app
directory where you:Expected Behavior
It builds
**Environment
Additional
I found https://stackoverflow.com/questions/65562209/this-util-textencoder-is-not-a-constructor-only-in-electron-app-works-in-chrome which seems absolutely related to me but haven't looked how to fix the package, yet.