0x80 / isolate-package

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

Fails to handle spaces in file paths #26

Closed alexeigs closed 11 months ago

alexeigs commented 1 year ago

I'm working with a standard turbo/yarn workspaces setup as can be found in https://github.com/vercel/turbo/tree/main/examples/with-tailwind and want to use isolate-package to isolate apps/functions (firebase functions setup as outlined in the docs here).

Even after I gave it several tries and went over my whole setup to make sure I'm in line with both the standard turbo/yarn workspaces as well as isolate-package setup, I do not manage though to understand what's going wrong when running isolate, so let me share some code here.

First the actual error which occurs when I run isolate. The problem occurs with the common package (the only package that is needed from ./packages/*) and it seems as the path includes some parts twice with a weird cut or missing piece where usually is a whitespace (in Own Projects):

⋊> ~/L/D/O/l/a/functions on main ⨯ isolate                                                                                                                                     20:02:31
debug Running isolate-package version 1.3.3
debug Found tsconfig at: ./tsconfig.json
debug Workspace root resolved to /Users/<username>/Own Projects/<project>
debug Isolate target package (root)/apps/functions
debug Isolate output directory (root)/apps/functions/isolate
debug Cleaned the existing isolate output directory
debug Detected package manager yarn 3.6.4
debug Override workspace packages via config: packages/*,apps/*
debug Registering package ./packages/ui-interactive
debug Registering package ./packages/ui
debug Registering package ./packages/tsconfig
debug Registering package ./packages/tailwind-config
debug Registering package ./packages/eslint-config-custom
debug Registering package ./packages/common
debug Registering package ./apps/web
debug Registering package ./apps/functions
error Error: Command failed: npm pack --pack-destination /Users/<username>/Own Projects/<project>/apps/functions/isolate/__tmp
npm WARN Ignoring workspaces for specified package(s) 
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /Users/<username>/Own Projects/<project>/packages/common/Projects/<project>/apps/functions/isolate/__tmp/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/Users/<username>/Own Projects/<project>/packages/common/Projects/<project>/apps/functions/isolate/__tmp/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/<username>/.npm/_logs/2023-10-21T18_02_37_406Z-debug-0.log

    at ChildProcess.exithandler (node:child_process:419:12)
    at ChildProcess.emit (node:events:513:28)
    at maybeClose (node:internal/child_process:1091:16)
    at Socket.<anonymous> (node:internal/child_process:449:11)
    at Socket.emit (node:events:513:28)
    at Pipe.<anonymous> (node:net:322:12)

... the invalid path is the following: /Users/<username>/Own Projects/<project>/packages/common/Projects/<project>/apps/functions/isolate/__tmp/package.json which is a weird combination of /Users/<username>/Own Projects/<project>/packages/common/ and Projects/<project>/apps/functions/isolate/__tmp/package.json where the latter path lacks the beginning and the Own in Own Projects

Now let me share my setup:

./package.json:

{
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "build": "turbo build",
  },
  "prettier": "@vercel/style-guide/prettier",
  "devDependencies": {
    "@types/node": "^20.8.7",
    "@types/react": "^18.2.29",
    "@types/react-dom": "^18.2.14",
    "prettier": "^3.0.3",
    "prettier-plugin-tailwindcss": "^0.5.3",
    "tsconfig": "*",
    "turbo": "latest",
    "typescript": "^5.2.2"
  },
  "packageManager": "yarn@3.6.4"
}

apps/functions/package.json:

{
  "name": "functions",
  "version": "0.0.0",
  "scripts": {
    "build": "tsc -b tsconfig.build.json",
  },
  "engines": {
    "node": "18"
  },
  "main": "lib/index.js",
  "dependencies": {
    "common": "*",
    "firebase-admin": "^11.8.0",
    "firebase-functions": "^4.3.1"
  },
  "devDependencies": {
    "firebase-tools": "latest",
    "isolate-package": "^1.3.3",
    "typescript": "^4.9.0"
  },
  "private": true
}

apps/functions/isolate.config.json:

{
  "workspaceRoot": "../..",
  "workspacePackages": ["packages/*", "apps/*"],
  "logLevel": "debug"
}

apps/functions/tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  },
  "compileOnSave": true,
  "include": [
    "src",
    "./*.d.ts"
  ]
}

apps/functions/tsconfig.build.json:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDir": "src"
  },
}

packages/common/package.json:

{
  "name": "common",
  "version": "0.0.0",
  "private": true,
  "license": "MIT",
  "exports": {
    ".": "./dist"
  },
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "lint": "eslint src/",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "react": "^18.2.0",
    "tsconfig": "*",
    "tsup": "^6.1.3",
    "typescript": "^5.1.6"
  }
}

packages/common/tsconfig.json:

{
  "extends": "tsconfig/react-library.json",
  "include": ["."],
  "exclude": ["dist", "build", "node_modules"]
}

packages/common/tsup.config.ts:

import { defineConfig, Options } from 'tsup';

export default defineConfig((options: Options) => ({
  treeshake: true,
  splitting: true,
  entry: ['src/**/*.ts'],
  outDir: 'dist',
  format: ['esm'],
  dts: true,
  minify: true,
  clean: true,
  external: ['react'],
  ...options,
}));
alexeigs commented 1 year ago

Well, I actually answered it myself when writing the issue I guess, and could resolve it by getting rid of the white space in my local path in Own Projects -> own_projects. As it may still be an issue users could ran into, and you might want to fix it, I leave it open but feel free to just close it otherwise.

alexeigs commented 1 year ago

Given I already shared all the code above, I will still add the following issue I'm facing in this thread; in case you have a helpful input that would be much appreciated!

The isolate output is not as expected but for when I deploy the functions folder, my setup seems to result in the common package to be looked for in node_modules instead of ./packages/common:

[2023-10-21T18:51:33.785Z] > [functions] package.json contents: {
  "name": "functions",
  "version": "0.0.0",
  "scripts": {
    "build": "tsc -b tsconfig.build.json",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "18"
  },
  "main": "lib/index.js",
  "dependencies": {
    "common": "file:./packages/common",
    "firebase-admin": "^11.8.0",
    "firebase-functions": "^4.3.1"
  },
  "private": true
}
[2023-10-21T18:51:33.785Z] Building nodejs source
i  functions: Loading and analyzing source code for codebase default to determine what to deploy 
[2023-10-21T18:51:33.786Z] Could not find functions.yaml. Must use http discovery
[2023-10-21T18:51:33.798Z] Found firebase-functions binary at '/Users/<user>/Local/Dev/own_projects/<project>/node_modules/.bin/firebase-functions'
Serving at port 8243

[2023-10-21T18:51:34.086Z] Got response code 400; body Failed to generate manifest from function source: Error: Cannot find module '/Users/<user>/Local/Dev/own_projects/<project>/node_modules/common/dist'
shutdown requested via /__/quitquitquit
0x80 commented 1 year ago

Hi Alexei,

Thanks for bringing the spaces in paths issue to my attention. That is an oversight. I think programmers traditionally avoid spaces exactly for this reason but modern software should of course just handle them correctly.

As for your remaining issue. I'm not sure what you mean with

my setup seems to result in the common package to be looked for in node_modules instead of ./packages/common:

The package json you posted is from the isolate output, right? The path there correctly points to "file:./packages/common", so I'm not sure what part of your setup you are referring to.

One thing I can think of is that your build script from package.json interferes with the deploy process.

At first I omitted the scripts, but then got a request to include them, which I did in v1.3.2. But then recently a user told me that the build script is being picked up by the firebase deploy pipeline, which it shouldn't really.

So I think in the next version I am going to at least create a configuration option to include/exclude the package scripts from the isolate output manifest.

Could you try to use v1.3.1, which still omits the scripts property? If that works then we know where the problem is...

0x80 commented 1 year ago

Also, I wonder why you have these configurations set:

  "workspaceRoot": "../..",
  "workspacePackages": ["packages/*", "apps/*"],

If you are using a typical monorepo setup, I think they should not be required.

0x80 commented 11 months ago

@alexeigs I've created an example that might find useful https://github.com/0x80/mono-ts

0x80 commented 11 months ago

@alexeigs The spaces problem has been fixed in the latest version

Also, more importantly, isolated lockfiles are not generated for NPM and all other package managers.