`netlify dev` with `--cwd` from outside the project: every function 500s with "module is not defined in ES module scope" (`detectZisiBuilder` passes a string to `readPackageUp`)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.9k
- Forks
- 474
- Avg merge
- 23h 30m
- Merged PRs (30d)
- 53
Description
Describe the bug
detectZisiBuilder determines whether the project's package.json has "type": "module" like this:
// @ts-expect-error(serhalp) -- We seem to be incorrectly using this function, but it seems to work... Investigate.
const packageJson = await readPackageUp(func.mainFile)
const hasTypeModule = packageJson?.packageJson.type === 'module'
read-package-up expects an options object ({ cwd }), not a file path string. The string is silently ignored (find-up reads options.cwd, which is undefined), so the lookup always starts from process.cwd() instead of the function's directory. The @ts-expect-error TODO on that line is exactly this bug — it only "seems to work" because process.cwd() is usually inside the project.
It stops working when netlify dev is run with --cwd <project> from a directory outside the project: the CLI honors --cwd for config resolution but never calls process.chdir(), so readPackageUp searches upward from the invocation directory. If no package.json exists there or in its ancestors, hasTypeModule is false, and buildFunction skips writing the {"type":"commonjs"} marker into each function's .netlify/functions-serve/<name>/ directory:
In a project whose root package.json has "type": "module", Node then parses every zisi/esbuild CJS function bundle as ESM, and every function invocation 500s with:
ReferenceError: module is not defined in ES module scope
Running the identical command from inside the project works (the buggy fallback happens to resolve the right package.json), which is presumably why this is rarely reported. Editors/wrappers that spawn netlify dev --cwd <project> with a different working directory hit it every time.
A secondary effect of the same line: mustUseEsbuild = hasTypeModule || mustTranspile is also computed from the wrong package.json, so a "type": "module" project with plain .js functions and no explicit node_bundler wouldn't get esbuild auto-selected either.
Suggested fix
const packageJson = await readPackageUp({ cwd: path.dirname(func.mainFile) })
This is exactly what @netlify/functions-dev already does in its equivalent code path (its dist/main.js: readPackageUp({ cwd: path.dirname(func.mainFile) })), and it lets you drop the @ts-expect-error. Happy to open a PR.
Steps to reproduce
- Create a project with:
- root
package.jsoncontaining"type": "module" netlify.tomlwith[functions] node_bundler = "esbuild"and a v1 TypeScript function (e.g.netlify/functions/hello.ts)
- root
- From a directory outside the project that has no
package.jsonin itself or any ancestor (e.g.C:\or a fresh temp dir), run:netlify dev --cwd /path/to/project curl http://localhost:8888/.netlify/functions/hello→ 500, terminal showsReferenceError: module is not defined in ES module scope, and.netlify/functions-serve/hello/contains nopackage.jsonmarker.- Run the same
netlify devfrom inside the project directory → the function works and the{"type":"commonjs"}marker is written.
Verified on netlify-cli 26.0.1; the code is unchanged in v27.1.1 and current main (85c0113). Patching the installed dist/lib/functions/runtimes/js/builders/zisi.js with the one-line fix above resolves it.
Configuration
[build]
publish = "dist"
[functions]
node_bundler = "esbuild"
Environment
System:
OS: Windows 11 10.0.26200
CPU: (8) x64 Intel(R) Core(TM) Ultra 7 258V
Memory: 8.57 GB / 31.49 GB
Binaries:
Node: 24.15.0 - C:\Program Files\nodejs\node.EXE
npm: 11.12.1 - C:\Program Files\nodejs\npm.CMD
npmGlobalPackages:
netlify-cli: 26.0.1
(Reproduced on 26.0.1; bug confirmed present in 27.1.1 and main by source inspection. Not OS-specific — the lookup falls back to process.cwd() on any platform.)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/lib/functions/runtimes/js/builders/zisi.ts at detectZisiBuilder and buildFunction, then inspect how readPackageUp is called and how the function package marker is written. Reproduce with netlify dev --cwd /path/to/project from outside the project, and confirm that functions no longer return the module-scope error and the expected marker is created.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100