trouble using next-transpile-modules in #nrwl/nextjs monorepo - next.js

My understanding is that I fall into Group 1 as those who are;
running a [nextjs] monorepo and therefore they want to be able to import their other packages from node_modules.
And running into an error similar to this:
../../node_modules/#waweb/base-ui.theme.brand-definition/dist/brand-definition.module.scss
CSS Modules cannot be imported from within node_modules. Read more:
https://nextjs.org/docs/messages/css-modules-npm Location:
../../node_modules/#waweb/base-ui.theme.brand-definition/dist/index.js
The official solution is next-transpile-modules, but as soon as I add any packages to the list of modules, I start getting errors in CSS modules in local source.
../../libs/ui/src/lib/contact.module.css
CSS Modules cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-modules-npm
Location: ../../libs/ui/src/lib/learn-more.tsx
Import trace for requested module:
../../libs/ui/src/lib/learn-more.tsx
../../libs/ui/src/lib/home.tsx
./pages/index.tsx
This is repeated for all components that were previously working.
I have prepared a branch in a public repo that has a full ci/cd and gitpod dev env configured that demonstrates the critical change.
Let's assume the sources to the components I am attempting to transpile are located in the correct node_modules dir, and I am using the following next config:
// eslint-disable-next-line #typescript-eslint/no-var-requires
const withNx = require('#nrwl/next/plugins/with-nx');
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')(
[
'#waweb/base-ui.theme.colors',
'#waweb/base-ui.theme.color-definition',
'#waweb/base-ui.theme.size-definition',
'#waweb/base-ui.theme.shadow-definition',
'#waweb/base-ui.theme.brand-definition',
'#waweb/base-ui.theme.theme-provider',
],
{ debug: true }
);
const withPWA = require('next-pwa');
/**
* #type {import('#nrwl/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {
// Set this to true if you would like to to use SVGR
// See: https://github.com/gregberge/svgr
svgr: true,
},
images: {
domains: [
'www.datocms-assets.com',
'a.storyblok.com',
'images.ctfassets.net',
'images.prismic.io',
'cdn.aglty.io',
'localhost', // For Strapi
],
imageSizes: [24, 64, 300],
},
};
const pwaConfig = {};
const plugins = [[withNx], [withPWA, pwaConfig]];
module.exports = withTM(withPlugins([...plugins], nextConfig));
Any idea what's wrong with my setup here?
Thank you all for any thoughts as to what I'm doing wrong here.
Cheers!
edit
For some additional context, I have tried many different variations, and the one I ended up on (shown above) is what got the module transpilation to actually work, according to the debug statements. Only now do I have the reported errors in modules that are actually source components, not node_modules. The usage of the plugin at all seems to break unrelated functionality.

It looks odd to me that you are wrapping withPuglins inside of withTM...
withTM is a plugin so I would imagine it should be more this format:
module.exports = withPlugins([
withTM
], nextConfig);
This seems to be what's expected when looking at the docs:
https://www.npmjs.com/package/next-transpile-modules
https://www.npmjs.com/package/next-compose-plugins

Related

File path in NextJS api route not resolving

I'm trying to resolve a file path in NextJS.
I understand that API routes are working a little bit differently when deployed to Vercel. In order to create a correct path to the file I assumed I had to do this:
const svg = fs.readFileSync(
path.join(process.cwd(), "img", "file.svg"),
"utf-8",
);
// ENOENT: no such file or directory
But I cannot make it work. The file cannot be found under that path.
How can I find the correct path for a file in NextJS api routes?
I've followed the documentation of this.
Next version is: 11.1.3
When logging the path, it is giving /var/task/packages/project-root/img/file.svg
Try using
path.resolve("img", "file.svg")
Maybe it should help.
Pretty sure you'll find the file if you serve it as a static file - Next.js documentation here
I'm thinking it's not bundled in the deployment, but whatever you have in /public will definitely be deployed.
Good luck πŸ’ͺ🏻
I manage to create a small sandbox that will clarify your issue. Open it using StackBlitz
Project Structure
.
β”œβ”€β”€ pages
| β”œβ”€β”€ api
| | β”œβ”€β”€ hello.js
| β”œβ”€β”€ _app.js
| β”œβ”€β”€ index.js
β”œβ”€β”€ public
| β”œβ”€β”€ 1.txt --> this is a demonstration file
I reproduce your code in the hello api for testing purposes
const { readFileSync } = require('fs');
const { join } = require('path');
export default (req, res) => {
const path = join(process.cwd(), '/public/1.txt');
const value = readFileSync(path, { encoding: 'utf-8' });
res.status(200).json({ value });
};
This API entry is called from the index.js file
import Head from 'next/head';
import { useEffect, useState } from 'react';
export default function Home() {
const [value, setValue ] = useState('');
useEffect(() => {
fetch('/api/hello')
.then((res) => res.json())
.then(data => setValue(data.value));
});
return (
<div>
<Head>
<title>Create Next App</title>
</Head>
<main>
<h1>{value}</h1>
</main>
</div>
);
}
Yes, this is a very simplified version (for testing purposes only.. I assume we won't use readFileSync in production) - but - it reproduces your code.
Unfortunately, it works perfectly fine in dev mode and in production mode (npm run build + npm start), which means:
You either misconfigured your img folder
Perhaps you are lacking read permissions for the path you are using. For instance if you deploy your work to a remote machine, most directories will have limited access and therefore prevent you from reading the file (for testing this theory please read this post and execute it on your deployed machine)
For anyone coming across this, I actually opened a ticket at Vercel to ask them about this.
It turns out it was a caching issue that is caused by using Yarn 3
The support redirected me to this page explaining that they would have issues with anything above Yarn 1.
According to them there is nothing really they can do about right now but suggest us to use a different package manager.
I'm using Yarn 1.22, but still have this issue. The reason is because files are not generated during build and run times, so they are never found. The way to get around this is to create a separate .js file that to wrap around the said static files (html, txt, etc). Export this JS object which contains the files, and Vercel will generate them. I'm using this to generate email templates.
//account_verify.js
import path from 'path';
import { promises as fs } from 'fs';
import { prefixPath } from './constants';
// TODO: force this to conform to a typescript type
export default {
subject: 'Confirm Your Account',
data: {
email_verification_link: '{{email_verification_link}}',
first_name: '{{first_name}}'
},
templates: {
txt: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.txt'), 'utf8'),
html: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.html'), 'utf8'),
}
};

Nextjs with Jest---No tests found, exiting with code 1 Run with `--passWithNoTests` to exit with code 0

import nextJest from 'next/jest'
const createJestConfig = nextJest({
dir: './',
})
// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
modulePathIgnorePatterns: ['./cypress'],
// testMatch: ['<rootDir>/**/*.test.js', '<rootDir>/**/*.test.jsx'],
}
module.exports = createJestConfig(customJestConfig)
In my project, we use Nextjs application with both Cypress and Jest. The latest jest.config.ts which is recommended is shown above.
If you are now owned this problem. you can maybe try to check your modulePathIgnorePatterns.
I added a ./ to ['cypress'], then it works well. So, I think maybe it just cann't recognize the path.

`Module not found: Can't resolve 'fs'` when using near-api-js in Next.js

I have a Next.js app using near-api-js, and I'm getting this error.
It's confusing since I'm not using unencrypted_file_system_keystore anywhere.
error - ./node_modules/near-api-js/lib/key_stores/unencrypted_file_system_keystore.js:7:0
Module not found: Can't resolve 'fs'
Import trace for requested module:
./node_modules/near-api-js/lib/key_stores/index.js
P.S. This question is specifically about using near-api-js. I'll update the answer when I learn of a better solution to this particular problem since there are known bugs in that library and my current answer below feels like a wonky workaround.
Changing the contents of /next.config.js to the following seemed to fix it for me:
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
future: {
webpack5: true, // By default, if you customize webpack config, they switch back to version 4. (backward compatibility?)
},
webpack(config) {
// eslint-disable-next-line no-param-reassign
config.resolve.fallback = {
...config.resolve.fallback,
fs: false, // https://stackoverflow.com/a/67478653/470749
};
return config;
},
};
Thanks to https://stackoverflow.com/a/67478653/470749!
But I bet there is a bug in near-api-js. I doubt this step by me should have been necessary.

Rails (6.0.3.4) Webpacker (5.2.1) package import error: `Can't import the named export from non EcmaScript module`

I'm trying to import a package into my Rails 6 project – I'm on Rails 6.0.3.4 and webpacker 5.2.1 gem.
The package is installed yarn add #shopify/react-form
It's imported into the file import {useForm, useField} from '#shopify/react-form';
Now after running ./bin/webpack-dev-server
I get this:
ERROR in ./node_modules/#shopify/react-form/build/esm/validation/validator.mjs 35:25-32
Can't import the named export 'isEmpty' from non EcmaScript module (only default export is available)
And few more of the same error but different file names.
I read on another issue that adding a new rule to webpack would fix the issue, so I followed the instructions on the webpacker README:
The thing is I don't have /config/webpack/base.js, I created on anyway and created a rules directory in side the /config/webpack/ directory, so my base.js looks like this:
// /config/webpack/base.js
const { webpackConfig, merge } = require('#rails/webpacker')
const fixConfig = require('./rules/fix')
module.exports = merge(webpackConfig, fixConfig)
and the fix looks like this:
// /config/webpack/rules/fix.js
module.exports = {
module: {
rules: [
{
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
}
]
}
}
I need help/guidance to figure out hot to solve this. TIA!
I managed to fix the error, this article guided me to figure out how to merge a new rule into webpack config.
Here is what environment.js looks right now:
// /config/webpack/environment.js
const { environment } = require('#rails/webpacker')
environment.config.merge({
module: {
rules: [
{
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
}
]
}
})
module.exports = environment
Before it was:
const { environment } = require('#rails/webpacker')
module.exports = environment
I ended up deleting base.js and fix.js.

Next-offline and WorkboxOpts: Error: "runtimeCaching" is not a supported parameter. How to enable "runtimeCaching" in next.config.js

I'm just getting started with next-offline and found the section regarding workbox integration and its recipes.
According to the docs:
If you're new to workbox, I'd recommend reading this quick guide --
anything inside of workboxOpts will be passed to
workbox-webpack-plugin.
Define a workboxOpts object in your next.config.js and it will gets
passed to workbox-webpack-plugin. Workbox is what next-offline uses
under the hood to generate the service worker, you can learn more
about it here.
After digging around, I found this great section.
Essentially it gives a suggestion to use two different options:
GenerateSW or InjectManifest
I would like to use the InjectManifest, however when I try to implement that in my next.config.js file. I get this error:
"runtimeCaching" is not a supported parameter.
This is my next.config.js:
const withCSS = require('#zeit/next-css');
const withSass = require('#zeit/next-sass');
const withImages = require('next-images');
const optimizedImages = require('next-optimized-images');
const withOffline = require('next-offline');
module.exports = withOffline(
withImages(
optimizedImages(
withCSS(
withSass({
// useFileSystemPublicRoutes: false,
// generateSw: false, // this allows all your workboxOpts to be passed in injectManifest
generateInDevMode: true,
workboxOpts: {
swDest: './service-worker.js', // this is the important part,
exclude: [/.+error\.js$/, /\.map$/, /\.(?:png|jpg|jpeg|svg)$/],
runtimeCaching: [
{
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
handler: 'CacheFirst',
options: {
cacheName: 'hillfinder-images'
}
},
{
urlPattern: /^https?.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'hillfinder-https-calls',
networkTimeoutSeconds: 15,
expiration: {
maxEntries: 150,
maxAgeSeconds: 30 * 24 * 60 * 60 // 1 month
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
},
dontAutoRegisterSw: false,
env: {
MAPBOX_ACCESS_TOKEN: process.env.MAPBOX_ACCESS_TOKEN,
useFileSystemPublicRoutes: false
},
webpack(config, options) {
config.module.rules.push({
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
target: 'serverless'
}
}
});
return config;
}
})
)
)
)
);
Also when I check the Application pane, in devTools I see this:
You'll notice what appears to me a duplication of fields i.e. https-calls and hillfinder-https-calls and images and hillfinder-images.
I thought the cacheName field in the options: {} in each was allowing one to include a custom name?
Just wondering if anyone has had experience setting this up?
Thank you in advance!
(These comments apply to the basic Workbox build tools, not specifically to the next-offline wrapper, but I think they're still accurate.)
If you're using InjectManifest mode, the idea is that you write all of your service worker logic, using the underlying pieces of Workbox that you need, following a model that's similar to what's described in the Getting Started guide. You should include a call to precacheAndRoute(self.__WB_MANIFEST) somewhere in your service worker, and then the InjectManifest build tool is responsible for swapping out self.__WB_MANIFEST with an array containing the list of URLs to precache, along with revision information for each URL.
The runtimeCaching parameter is not compatible with InjectManifest. It's a parameter that can be used in GenerateSW mode, in with the Workbox build tool creates an entire service worker for you (including runtime caching routes). The GenerateSW mode takes in a declarative configuration and spits out the code for service worker based on that configuration. If that sounds goodβ€”if you'd just like to configure some build options and get a complete service worker as a resultβ€”then using GenerateSW is the right choice.

Resources