0x80 / isolate-package

Isolate a monorepo package with its internal dependencies to form a self-contained directory with a pruned lockfile
MIT License
121 stars 13 forks source link

Error on including monorepo dependencies with import statements #8

Closed jjmountain closed 1 year ago

jjmountain commented 1 year ago

First of all, thank you for your work on this awesome project.

I'm trying to use my local package which is a Prisma (DB) client so I can use it in my firebase function (I'm starting based on this T3-Turbo template).

When npx isolate is run as part of the deploy, it copies over my package exactly as it is in my typescript project (it copies index.ts with import statements). However I believe I need my shared package to be transpiled to commonJS for it to run in the firebase cloud environment and it doesn't seem like there's a way to do this.

The error I get is SyntaxError: Cannot use import statement outside a module which seems to from db/index.ts where I have the line import { PrismaClient } from "@prisma/client"

Do you have any idea why this would happen?

Here's the last stage of my deploy where it fails:

debug Linking dependency @acme/db to file:packages/db
debug Copied lockfile to /Users/jjdevereux/code/reasily-monorepo/packages/functions/isolate/package-lock.json
debug Deleting temporary directory (root)/packages/functions/isolate/__tmp
debug Stored isolate output at /Users/jjdevereux/code/reasily-monorepo/packages/functions/isolate
info Isolate completed
✔  functions: Finished running predeploy script.
i  functions: preparing codebase default for deployment
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
i  artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
✔  functions: required API cloudbuild.googleapis.com is enabled
✔  artifactregistry: required API artifactregistry.googleapis.com is enabled
✔  functions: required API cloudfunctions.googleapis.com is enabled

Error: Failed to load function definition from source: Failed to generate manifest from function source: SyntaxError: Cannot use import statement outside a module
npm ERR! Lifecycle script `deploy` failed with error: 
npm ERR! Error: command failed 
npm ERR!   in workspace: functions@0.0.0 
npm ERR!   at location: /Users/jjdevereux/code/reasily-monorepo/packages/functions 
jjmountain commented 1 year ago

I realized that i needed to have "type": "module" set in my package.json and these fields in my tsconfig.json.

    "target": "es2017",
    "module": "esnext",
    "moduleResolution": "node"

However even with this I'm running into the problem that npx isolate is not transpiling the code from my shared package from typescript into javascript. According to the firebase function docs this is necessary.

Is there a way to make isolate-package transpile the code from your shared package (my prisma client in this case)?

Deploy output:

functions:deploy: Error: Failed to load function definition from source: Failed to generate manifest from function source: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/jjdevereux/code/reasily-monorepo/packages/db/index.ts
functions:deploy: npm ERR! Lifecycle script `deploy` failed with error: 
functions:deploy: npm ERR! Error: command failed 
functions:deploy: npm ERR!   in workspace: functions@0.0.0 
0x80 commented 1 year ago

@jjmountain isolate-package will not transpile anything for you. If your shared packages need some sort of compile/transpile step, this should be handled before the isolate process is triggered. The isolate in that sense works similarly to an npm publish. It will pick up the "to be released" files according to your package "files" setting. Since those typically point to a dist folder, you can have a build process do something to your source.

So the issues you are experiencing are not the responsibility of isolate-package.

That being said. I have successfully managed to deploy to firebase using ESM modules for both my shared packaes as the firebase functions source. So I don't think it is not true that functions need to be transpiled.

I was planning to make a pure ESM mono-repo example with firebase deployment, but haven't gotten around to it yet. The project I'm referring to is closed-source, but I will try to look up my configs later today. I'm on the road at the moment...

0x80 commented 1 year ago

Ah sorry I didn't read your text about transpiling correctly. I associate transpiling with js =>js like ESM to commonjs.

TS always needs to be compiled to JS because it is running on Node.js, so that is not specific to Firebase. The problem you're facing is that your code is compiled to ESM (which uses imports).

You could choose to compile to commonjs (using requires), but commonjs doesn't like requiring ESM. The other way around, importing a commonjs module in an esm module, is not a problem. So it's better to deploy your firebase code from ESM IMO.

This is a hairy subject, because there's a lot of settings you can mess up, and I have spent quite a lot of time on this recently. Like I said, it is out of the scope of isolate-package, but this is what I do:

The package that deploys to firebase has this

  "type": "module",
  "main": "dist/index.js",
  "module": "dist/index.js",
  "files": [
    "dist"
  ],
"compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "nodenext",
    "outDir": "dist",
    "rootDir": "src",
    "lib": ["esnext"],
    "sourceMap": true,
    "types": ["node"],
    "baseUrl": ".",
    "paths": {
      "~/*": ["src/*"]
    }

The shared package something like

  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
"compilerOptions": {
    "target": "esnext",
    "rootDir": "src",
    "lib": ["esnext"],
    "baseUrl": ".",
    "paths": {
      "~/*": ["src/*"]
    }
  },

Then compile the shared package with tsc so the dist folder contains js code. Or, I use tsup but that makes it more compicated to get go-to-definition to work in your editor, so that's for another story...

Make sure you're running ESM code using node18 on firebase (configured in firebase.json), otherwise I think ESM is not supported without setting a flag.

0x80 commented 1 year ago

Closing this because, as I mentioned, the issue is not the responsibility of isolate-package