-3

I am working in a monorepo, using bolt to manage package installation. I also use preconstruct in order to build exports which are compatible with both module and common js. With preconstruct I am using also babel. The monorepo folder structure looks a little something like this:

root
├─apps
│ ├─someApp1
│ │ └─index.ts
│ └─someApp2
│   └─index.ts
└─packages
  ├─ui
  │ ├─src
  │ │ ├─components
  │ │ ├─pages
  │ │ │ └─index.tsx
  │ │ └─index.ts
  │ ├─package.json
  │ └─tsconfig.json
  └─utils
    ├─src
    │ └─index.ts
    ├─package.json
    └─tsconfig.json

I am building a package with several utility functions built around the fs module in node.js.

In another package in the same monorepo I am building React components and using Next to build the app.

If I use fs in the index.tsx file of a somePage in the Next folder within the component library, everything is fine.

If I import the modules I am building it gives me an error: can't resolve fs..

I think it has to do with Next trying to pull the dependency from the package while it shall be imported from the client node_modules.

probably there is some configuration settings or something to be done.

I will try to make a minimal reproducible example:

The file package.json in the root folder looks like this:

/package.json

// package
{
  "name": "somename",
  "version": "0.0.1",
  "private": true,
  "bolt": {
    "workspaces": [
      "apps/*",
      "packages/*"
    ]
  },
  "scripts": {
    "postinstall": "manypkg check",
    "clean": "bolt ws clean",
    "build": "sh bin/build.working.sh",
    "typecheck": "bolt ws typecheck"
  },
}...

then I have 2 packages in the packages folder : /packages/utils and /packages/ui. These are packages meant to be consumed in front end applications (browser and CLI) which sit in the /apps workspaces folder.

The utils package is meant to be a library for commonly used functions (arrays, string, file-treatment).

The ui package is meant to be a library of React components and, for ease of testing, I am running a Next application within that project. This applications is not exported with the library files of the package, as it can be noted in the tsconfig file.

they both inherit from a common tsconfig file which sits in the root of the package:

/tsconfig.base.json

{
  "compilerOptions": {
    "allowJs": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "lib": ["DOM", "DOM.Iterable", "ES2019"],
    "module": "CommonJS",
    "moduleResolution": "node",
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "ES2017"
  },
  "exclude": ["node_modules"]
}

their respective package.jsons and tsconfig files look like this:

/packages/utils/package.json

{
  "name": "@somename/utils",
  "version": "0.0.1",
  "private": true,
  "description": "Somename utils",
  "source": "src/index.ts",
  "main": "dist/somename-utils.cjs.js",
  "module": "dist/somename-utils.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "preconstruct": {
    "entrypoint": [
      "src/index.ts"
    ]
  },
  "scripts": {
    "clean": "rm -rf dist",
    "build": "yarn preconstruct build && run-p build:*",
    "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
    "typecheck": "tsc --noEmit"
  },
} ....

/packages/utils/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "include": ["src"],
  "exclude": ["dist", "node_modules"]
}

/packags/ui/package.json

{
  "name": "@somename/ui",
  "version": "0.0.1",
  "private": true,
  "source": "src/index.ts",
  "main": "dist/somename-ui.cjs.js",
  "module": "dist/somename-ui.esm.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "scripts": {
    "clean": "rm -rf dist",
    "build": "yarn preconstruct build && run-p build:*",
    "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
    "typecheck": "tsc --noEmit",
    "next_dev": "next dev",
    "next_build": "next build",
    "next_prod": "next start"
  },
} ....

/packages/ui/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "include": ["src"],
  "exclude": ["dist", "src/pages", "src/somefolder"],
  "compilerOptions": {
    "incremental": true,
    "jsx": "preserve",
    "isolatedModules": true,
    "noEmit": true
  }
}

note that in this ts config I exclude the src/pages folder as this package is meant to be exporting only the components, not the application which is built within it which is meant for testing purposes.

Then I have a function which sits in the utils package which looks something like this.

/packages/utils/index.ts

import fs from "fs"
// node/readDirSync.ts
export const readDirSync(...path:string[]){
  return fs.readdirsync(...path)
}

that I wish to use in the ui package, like this:

/packages/ui/pages/index.tsx


import { readDirSync } from "@somename/utils"; // my package
...

// Next getStatic
export async function getStaticProps() {
  const path = process.cwd();
  // retrieves a list of .md files
  console.log(readDirSync(path));
  return { props: { result: "should go here" } };
}

...

ps. I wrote this snippets here, I didn't actually test it myself, I hope it is enough to clearify the issue I am facing. If needed I can do a repo which reproduces the issue.

So, it seems clear to me that has something to do with the transpiler, or the Next compiler which needs to be told to grab the fs module from the node_modules folder within the ui package.

Anyone knows what to do? =)

  • Assuming based on your use of Next.js that you’re building a client-side (browser-based) application, why would you expect `fs` to be even remotely usable in such a context? Browsers do not grant arbitrary access to users’ file systems, and for very good reason (would you like it if any site you visited (or any frame/script running on that site, for that matter) could arbitrarily traverse your local filesystem?) – esqew Nov 25 '22 at 23:47
  • You assume wrong – Giacomo Gagliano Nov 25 '22 at 23:48
  • Ok, then can you provide additional details on your use case that lead you to believe `fs`' functionality should be available in your execution context? – esqew Nov 25 '22 at 23:52
  • 2
    Can you share some of the code snippets where you are trying to import `fs`? – DreamBold Nov 25 '22 at 23:58
  • I mean, your comment is out of the world. There are hundreds of reasons why one would want to do that. It is simply to read some files from my fs and show them on the browser. then again, even if I would build an API for that, in the Next context, I would still need to load that function from the pakcage... I maybe be ignorant, but your comment seems a little to prenteding – Giacomo Gagliano Nov 25 '22 at 23:59
  • 1
    @DreamBold I added it in the question – Giacomo Gagliano Nov 26 '22 at 00:07
  • i probably explained myself in a wrong way cause the allegedly good answer seems not to solve my problem ... – Giacomo Gagliano Nov 26 '22 at 09:25
  • I know how to use getStaticProps ... I think i clearly stated that I can use `fs` from within the app folder, but when I export the function in another package and import it as if it was an external package downloaded with npm, it would throw error as it pretends the dependency to be coming from the imported package, while it should simply look for it in its own node_modules .... – Giacomo Gagliano Nov 26 '22 at 09:28

1 Answers1

0

If you're using fs in a Next.js app, you'll need to import it as an ESM module using import ... from 'package' instead of using the CommonJS syntax require().

You can check out the docs for more info.

tiffanywhitedev
  • 829
  • 1
  • 6
  • 15
  • 1
    I am using js in the package I am building and importing in the application. The function works just fine, its a simple `readdirsync` on one of my folders. But when I pull it from the package which does a double export, in common and es. So I should be able to simply import that from my package, but the fs module shall be loaded on the client not from the package .. I think there is some sort of babel config to setup, but I have not much experience =) – Giacomo Gagliano Nov 26 '22 at 00:02