Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generatePackageJson on nx 15 generates the the dependencies and versions differently than 14 #12675

Closed
JoA-MoS opened this issue Oct 17, 2022 · 71 comments · Fixed by #12851 or #12905
Closed
Assignees
Labels

Comments

@JoA-MoS
Copy link
Contributor

JoA-MoS commented Oct 17, 2022

Current Behavior

npm ci of a a copied apps dist directory breaks after update to nx 15 because the root package-lock.json dependency version does not match the generated package.json dependency version.

With nx 14 when using the "executor": "@nrwl/node:webpack", with "generatePackageJson": true the version of the dependency in the generated package.json matched the root package.json including the range.

With nx 15 and the same config the the generated package.json dependency appears to be the evaluated exact version without the range.

before nx 15

{
  "scripts": {
    "start": "node main.js",
    "release": "semantic-release-plus"
  },
  "dependencies": {
    "axios": "^0.26.0",
    "dayjs": "^1.10.7",
    "jsonc-parser": "^3.0.0",
    "nodemailer": "^6.7.2",
    "qs": "^6.10.3",
    "tslib": "^2.0.0"
  },
  "main": "main.js"
}

after nx 15 update

{
  "scripts": {
    "start": "node main.js",
    "release": "semantic-release-plus"
  },
  "dependencies": {
    "axios": "0.26.1",
    "call-bind": "1.0.2",
    "dayjs": "1.11.5",
    "follow-redirects": "1.15.2",
    "function-bind": "1.1.1",
    "get-intrinsic": "1.1.3",
    "has": "1.0.3",
    "has-symbols": "1.0.3",
    "jsonc-parser": "3.2.0",
    "nodemailer": "6.8.0",
    "object-inspect": "1.12.2",
    "qs": "6.11.0",
    "side-channel": "1.0.4",
    "tslib": "2.4.0"
  },
  "main": "main.js"
}

Expected Behavior

the versions in the generated package.json should be exactly the same including range of the root package.json

Steps to Reproduce

Working Branch using nx 14 - https://github.com/JoA-MoS/garage/

Bad branch PR using nx 15 - JoA-MoS/garage#81

Run build on both and compare generated package.json

nx build campsite-watcher
nx docker-build campsite-watcher

Failure Logs

Environment

>  NX   Report complete - copy this into the issue template

   Node : 16.18.0
   OS   : darwin x64
   npm  : 8.19.2
   
   nx : 15.0.0
   @nrwl/angular : 15.0.0
   @nrwl/cypress : 15.0.0
   @nrwl/detox : Not Found
   @nrwl/devkit : 15.0.0
   @nrwl/esbuild : Not Found
   @nrwl/eslint-plugin-nx : 15.0.0
   @nrwl/expo : Not Found
   @nrwl/express : 15.0.0
   @nrwl/jest : 15.0.0
   @nrwl/js : 15.0.0
   @nrwl/linter : 15.0.0
   @nrwl/nest : 15.0.0
   @nrwl/next : Not Found
   @nrwl/node : 15.0.0
   @nrwl/nx-cloud : 14.7.0
   @nrwl/nx-plugin : Not Found
   @nrwl/react : 15.0.0
   @nrwl/react-native : Not Found
   @nrwl/rollup : 15.0.0
   @nrwl/schematics : Not Found
   @nrwl/storybook : 15.0.0
   @nrwl/web : 15.0.0
   @nrwl/webpack : 15.0.0
   @nrwl/workspace : 15.0.0
   typescript : 4.8.4
   ---------------------------------------
   Local workspace plugins:
   ---------------------------------------
   Community plugins:
         @semantic-release-plus/nx-tools: 1.0.0-alpha.6
         @storybook/angular: 6.5.12
@yharaskrik
Copy link
Contributor

yharaskrik commented Oct 17, 2022

Also experiencing this. Ours looks like it is pulling in every dep, theres like 495 different packages in the dists package.json, we only have 341 in our root package.json

@fahminlb33
Copy link

I'm also experiencing this issue. The number of peerDependencies is too much.

Before 15

  "peerDependencies": {
    "multer": "1.4.5-lts.1",
    "@logeect/core": "2.0.3",
    "@elastic/ecs-winston-format": "^1.3.1",
    "crypto-random-string": "3.3.1",
    "otplib": "^12.0.1",
    "winston-transport": "^4.5.0",
    "axios": "^1.0.0",
    "date-fns": "^2.29.3",
    "date-fns-tz": "^1.3.7",
    "winston": "^3.8.2",
    "express": "^4.18.1",
    "tslib": "^2.3.0"
  },

After 15

  "peerDependencies": {
    "multer": "1.4.5-lts.1",
    "address": "1.2.0",
    "agentkeepalive": "3.5.2",
    "humanize-ms": "1.2.1",
    "ms": "2.1.3",
    "bowser": "1.9.4",
    "copy-to": "2.0.1",
    "dateformat": "2.2.0",
    "debug": "2.6.9",
    "supports-color": "5.5.0",
    "has-flag": "3.0.0",
    "destroy": "1.2.0",
    "end-or-error": "1.0.1",
    "get-ready": "1.0.0",
    "is-type-of": "1.2.1",
    "core-util-is": "1.0.3",
    "is-class-hotfix": "0.0.6",
    "isstream": "0.1.2",
    "js-base64": "2.6.4",
    "jstoxml": "2.2.9",
    "merge-descriptors": "1.0.1",
    "mime": "2.6.0",
    "mz-modules": "2.1.0",
    "glob": "7.2.3",
    "fs.realpath": "1.0.0",
    "inflight": "1.0.6",
    "once": "1.4.0",
    "wrappy": "1.0.2",
    "inherits": "2.0.4",
    "minimatch": "3.1.2",
    "brace-expansion": "1.1.11",
    "balanced-match": "1.0.2",
    "concat-map": "0.0.1",
    "path-is-absolute": "1.0.1",
    "ko-sleep": "1.1.4",
    "mkdirp": "0.5.6",
    "minimist": "1.2.6",
    "pump": "3.0.0",
    "end-of-stream": "1.4.4",
    "rimraf": "2.7.1",
    "platform": "1.3.6",
    "sdk-base": "2.0.1",
    "stream-http": "2.8.2",
    "builtin-status-codes": "3.0.0",
    "readable-stream": "2.3.7",
    "isarray": "1.0.0",
    "process-nextick-args": "2.0.1",
    "safe-buffer": "5.1.2",
    "string_decoder": "1.1.1",
    "util-deprecate": "1.0.2",
    "to-arraybuffer": "1.0.1",
    "xtend": "4.0.2",
    "stream-wormhole": "1.1.0",
    "urllib": "2.38.1",
    "any-promise": "1.3.0",
    "content-type": "1.0.4",
    "default-user-agent": "1.0.0",
    "os-name": "1.0.3",
    "osx-release": "1.1.0",
    "win-release": "1.1.1",
    "semver": "5.7.1",
    "digest-header": "0.0.1",
    "utility": "0.1.11",
    "ee-first": "1.1.1",
    "formstream": "1.1.1",
    "pause-stream": "0.0.11",
    "through": "2.3.8",
    "iconv-lite": "0.4.24",
    "safer-buffer": "2.1.2",
    "ip": "1.1.8",
    "proxy-agent": "5.0.0",
    "agent-base": "6.0.2",
    "http-proxy-agent": "4.0.1",
    "@tootallnate/once": "1.1.2",
    "https-proxy-agent": "5.0.1",
    "lru-cache": "5.1.1",
    "yallist": "3.1.1",
    "pac-proxy-agent": "5.0.0",
    "get-uri": "3.0.2",
    "data-uri-to-buffer": "3.0.1",
    "file-uri-to-path": "2.0.0",
    "fs-extra": "8.1.0",
    "graceful-fs": "4.2.10",
    "jsonfile": "4.0.0",
    "universalify": "0.1.2",
    "ftp": "0.3.10",
    "xregexp": "2.0.0",
    "pac-resolver": "5.0.1",
    "degenerator": "3.0.2",
    "ast-types": "0.13.4",
    "tslib": "2.4.0",
    "escodegen": "1.14.3",
    "esprima": "4.0.1",
    "estraverse": "4.3.0",
    "esutils": "2.0.3",
    "optionator": "0.8.3",
    "deep-is": "0.1.4",
    "fast-levenshtein": "2.0.6",
    "levn": "0.3.0",
    "prelude-ls": "1.1.2",
    "type-check": "0.3.2",
    "word-wrap": "1.2.3",
    "vm2": "3.9.10",
    "acorn": "8.8.0",
    "acorn-walk": "8.2.0",
    "netmask": "2.0.2",
    "raw-body": "2.5.1",
    "bytes": "3.1.2",
    "http-errors": "2.0.0",
    "depd": "2.0.0",
    "setprototypeof": "1.2.0",
    "statuses": "2.0.1",
    "toidentifier": "1.0.1",
    "unpipe": "1.0.0",
    "socks-proxy-agent": "5.0.1",
    "socks": "2.7.0",
    "smart-buffer": "4.2.0",
    "proxy-from-env": "1.1.0",
    "qs": "6.11.0",
    "side-channel": "1.0.4",
    "call-bind": "1.0.2",
    "function-bind": "1.1.1",
    "get-intrinsic": "1.1.3",
    "has": "1.0.3",
    "has-symbols": "1.0.3",
    "object-inspect": "1.12.2",
    "escape-html": "1.0.3",
    "mz": "2.7.0",
    "object-assign": "4.1.1",
    "thenify-all": "1.6.0",
    "thenify": "3.3.1",
    "unescape": "1.0.1",
    "extend-shallow": "2.0.1",
    "is-extendable": "0.1.1",
    "xml2js": "0.4.23",
    "sax": "1.2.4",
    "xmlbuilder": "11.0.1",
    "async": "3.2.4",
    "block-stream2": "2.1.0",
    "browser-or-node": "1.3.0",
    "buffer-crc32": "0.2.13",
    "crypto-browserify": "3.12.0",
    "browserify-cipher": "1.0.1",
    "browserify-aes": "1.2.0",
    "buffer-xor": "1.0.3",
    "cipher-base": "1.0.4",
    "create-hash": "1.2.0",
    "md5.js": "1.3.5",
    "hash-base": "3.1.0",
    "ripemd160": "2.0.2",
    "sha.js": "2.4.11",
    "evp_bytestokey": "1.0.3",
    "browserify-des": "1.0.2",
    "des.js": "1.0.1",
    "minimalistic-assert": "1.0.1",
    "browserify-sign": "4.2.1",
    "bn.js": "5.2.1",
    "browserify-rsa": "4.1.0",
    "randombytes": "2.1.0",
    "create-hmac": "1.1.7",
    "elliptic": "6.5.4",
    "brorand": "1.1.0",
    "hash.js": "1.1.7",
    "hmac-drbg": "1.0.1",
    "minimalistic-crypto-utils": "1.0.1",
    "parse-asn1": "5.1.6",
    "asn1.js": "5.4.1",
    "pbkdf2": "3.1.2",
    "create-ecdh": "4.0.4",
    "diffie-hellman": "5.0.3",
    "miller-rabin": "4.0.1",
    "public-encrypt": "4.0.3",
    "randomfill": "1.0.4",
    "es6-error": "4.1.1",
    "fast-xml-parser": "3.21.1",
    "strnum": "1.0.5",
    "ipaddr.js": "2.0.1",
    "json-stream": "1.0.0",
    "lodash": "4.17.21",
    "mime-types": "2.1.35",
    "mime-db": "1.52.0",
    "query-string": "7.1.1",
    "decode-uri-component": "0.2.0",
    "filter-obj": "1.1.0",
    "split-on-first": "1.1.0",
    "strict-uri-encode": "2.0.0",
    "through2": "3.0.2",
    "web-encoding": "1.1.5",
    "util": "0.12.4",
    "is-arguments": "1.1.1",
    "has-tostringtag": "1.0.0",
    "is-generator-function": "1.0.10",
    "is-typed-array": "1.1.9",
    "available-typed-arrays": "1.0.5",
    "es-abstract": "1.20.3",
    "es-to-primitive": "1.2.1",
    "is-callable": "1.2.7",
    "is-date-object": "1.0.5",
    "is-symbol": "1.0.4",
    "function.prototype.name": "1.1.5",
    "define-properties": "1.1.4",
    "has-property-descriptors": "1.0.0",
    "object-keys": "1.1.1",
    "functions-have-names": "1.2.3",
    "get-symbol-description": "1.0.0",
    "internal-slot": "1.0.3",
    "is-negative-zero": "2.0.2",
    "is-regex": "1.1.4",
    "is-shared-array-buffer": "1.0.2",
    "is-string": "1.0.7",
    "is-weakref": "1.0.2",
    "object.assign": "4.1.4",
    "regexp.prototype.flags": "1.4.3",
    "safe-regex-test": "1.0.0",
    "string.prototype.trimend": "1.0.5",
    "string.prototype.trimstart": "1.0.5",
    "unbox-primitive": "1.0.2",
    "has-bigints": "1.0.2",
    "which-boxed-primitive": "1.0.2",
    "is-bigint": "1.0.4",
    "is-boolean-object": "1.1.2",
    "is-number-object": "1.0.7",
    "for-each": "0.3.3",
    "which-typed-array": "1.1.8",
    "xml": "1.0.1",
    "append-field": "1.0.0",
    "busboy": "1.6.0",
    "streamsearch": "1.1.0",
    "concat-stream": "1.6.2",
    "buffer-from": "1.1.2",
    "typedarray": "0.0.6",
    "type-is": "1.6.18",
    "media-typer": "0.3.0",
    "@logeect/core": "2.1.0",
    "@elastic/ecs-winston-format": "1.3.1",
    "@elastic/ecs-helpers": "1.1.0",
    "fast-json-stringify": "2.7.13",
    "ajv": "6.12.6",
    "fast-deep-equal": "3.1.3",
    "fast-json-stable-stringify": "2.1.0",
    "json-schema-traverse": "0.4.1",
    "uri-js": "4.4.1",
    "punycode": "2.1.1",
    "deepmerge": "4.2.2",
    "rfdc": "1.3.0",
    "string-similarity": "4.0.4",
    "winston-transport": "4.5.0",
    "logform": "2.4.2",
    "@colors/colors": "1.5.0",
    "fecha": "4.2.3",
    "safe-stable-stringify": "2.3.1",
    "triple-beam": "1.3.0",
    "axios": "1.1.3",
    "follow-redirects": "1.15.2",
    "form-data": "4.0.0",
    "asynckit": "0.4.0",
    "combined-stream": "1.0.8",
    "delayed-stream": "1.0.0",
    "date-fns": "2.29.3",
    "date-fns-tz": "1.3.7",
    "winston": "3.8.2",
    "@dabh/diagnostics": "2.0.3",
    "colorspace": "1.1.4",
    "color": "3.2.1",
    "color-convert": "1.9.3",
    "color-name": "1.1.3",
    "color-string": "1.9.1",
    "simple-swizzle": "0.2.2",
    "is-arrayish": "0.3.2",
    "text-hex": "1.0.0",
    "enabled": "2.0.0",
    "kuler": "2.0.0",
    "is-stream": "2.0.1",
    "one-time": "1.0.0",
    "fn.name": "1.1.0",
    "stack-trace": "0.0.10",
    "express": "4.17.3",
    "accepts": "1.3.8",
    "negotiator": "0.6.3",
    "array-flatten": "1.1.1",
    "body-parser": "1.19.2",
    "on-finished": "2.3.0",
    "content-disposition": "0.5.4",
    "cookie": "0.4.2",
    "cookie-signature": "1.0.6",
    "encodeurl": "1.0.2",
    "etag": "1.8.1",
    "finalhandler": "1.1.2",
    "parseurl": "1.3.3",
    "fresh": "0.5.2",
    "methods": "1.1.2",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "2.0.7",
    "forwarded": "0.2.0",
    "range-parser": "1.2.1",
    "send": "0.17.2",
    "serve-static": "1.14.2",
    "utils-merge": "1.0.1",
    "vary": "1.1.2"
  },

@skrtheboss
Copy link
Contributor

This should have the same root cause as #12660

@charsleysa
Copy link

Hi @JoA-Mo,

Your docker build process is copying the root package-json.lock along with the generated package.json file.
I assume you did this so that the install of the generated package.json files used the same versions as your root project due to the generated package.json file only including top level dependencies and using version ranges.

This should no longer be needed as the generated package.json should now include all dependencies in the dependency tree with fixed versions.

@yharaskrik
Copy link
Contributor

Hi @JoA-Mo,

Your docker build process is copying the root package-json.lock along with the generated package.json file.
I assume you did this so that the install of the generated package.json files used the same versions as your root project due to the generated package.json file only including top level dependencies and using version ranges.

This should no longer be needed as the generated package.json should now include all dependencies in the dependency tree with fixed versions.

Question about this though. Our lock file has all the versions that the apps were built and tested with. But when we copy it in and use it with the generated package JSON the frozen lockfile flag causes it to fail. You said we shouldn't be copying in the lockfile anymore as everything is pinned but aren't we introducing risk because the generate package JSON is being generated with different versions from the lockfile (thus we would be running In production different versions than it was built/tested with).

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 19, 2022

@charsleysa - for docker images that might work, but there are also additional consequences related to using generatePackageJson when building and publishing an npm package. It will inflate the number of direct dependencies that your package depends on as shown in NPM.

this info:
image

@enchorb
Copy link

enchorb commented Oct 19, 2022

Doing the below code in the Dockerfile of our server and now getting error Your lockfile needs to be updated, but yarn was run with --frozen-lockfile when deploying after upgrading to v15, assuming this issue is the root cause.

I agree with the other commenters points, won't this change introduce additional risk since the versions we develop/test with locally can differ from what the docker image builds with? Reverting back to v14 until more clarification on this.

# Copy Built Code
COPY yarn.lock .
COPY dist/apps/${BUILD_APP} .

# Install Dependecies
RUN yarn install --frozen-lockfile --non-interactive --production

@yharaskrik
Copy link
Contributor

We do the same process as @enchorb with the frozen lockfile flag, if the generate package json were truely correct and we didn't need out lockfile then we should be able to have our lockfile there and be able to use --frozen-lockfile successfully.

@charsleysa
Copy link

@JoA-MoS for publishing packages, one option could be to use this workaround posted in another issue #12660 (comment) though I do think that the previous functionality should be made available using a config option for those who are publishing packages.

@enchorb @yharaskrik the generated package.json file uses version numbers from the package-lock.json file so the versions you develop/test with should be the same as the version in the docker build. You can cross check between the files to confirm that is the case. I believe the reason the lockfile doesn't work is that the version number string differs from the original package.json, e.g. original file version is ^10.2.1 generated file version is 10.3.6.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 20, 2022

@charsleysa - thanks for providing some options, I can delay the update to 15, but wanted to report this to the nx team as I don't think this is intended behavior.

@yharaskrik
Copy link
Contributor

Ok so this is really weird, curious if anyone has some input here, no idea if it's related to Nx or not but Nx was the only part that I updated here in this PR.

When I build my docker container now (I am not copying yarn.lock anymore) it fails when running the yarn install step

Step 5/13 : RUN yarn install --frozen-lockfile --production && yarn cache clean --all
 ---> [Warning] Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
 ---> Running in 7510889edd02
yarn install v1.22.17
warning package.json: No license field
info No lockfile found.
warning bullet@0.0.1: No license field
[1/4] Resolving packages...
warning @aws-sdk/client-s3 > @aws-sdk/eventstream-serde-browser > @aws-sdk/eventstream-marshaller@3.78.0: The package @aws-sdk/eventstream-marshaller has been renamed to @aws-sdk/eventstream-codec. Please install the renamed package.
warning @aws-sdk/client-s3 > @aws-sdk/eventstream-serde-node > @aws-sdk/eventstream-marshaller@3.78.0: The package @aws-sdk/eventstream-marshaller has been renamed to @aws-sdk/eventstream-codec. Please install the renamed package.
warning @aws-sdk/client-s3 > @aws-sdk/eventstream-serde-browser > @aws-sdk/eventstream-serde-universal > @aws-sdk/eventstream-marshaller@3.78.0: The package @aws-sdk/eventstream-marshaller has been renamed to @aws-sdk/eventstream-codec. Please install the renamed package.
warning @aws-sdk/eventstream-marshaller@3.78.0: The package @aws-sdk/eventstream-marshaller has been renamed to @aws-sdk/eventstream-codec. Please install the renamed package.
warning @nestjs/graphql > subscriptions-transport-ws@0.11.0: The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws    For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md
warning querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning subscriptions-transport-ws@0.11.0: The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws    For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md
warning url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning uuid@3.3.2: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > @firebase/database-compat@0.1.5" has unmet peer dependency "@firebase/app-compat@0.x".
warning "firebase-admin > @firebase/database-compat@0.1.8" has unmet peer dependency "@firebase/app-compat@0.x".

<--- Last few GCs --->

[9:0x7f9979fed2e0]    95315 ms: Scavenge (reduce) 253.6 (256.5) -> 253.5 (258.0) MB, 2.7 / 0.0 ms  (average mu = 0.313, current mu = 0.059) task 
[9:0x7f9979fed2e0]    95646 ms: Mark-sweep (reduce) 254.2 (257.3) -> 252.6 (257.8) MB, 268.1 / 0.0 ms  (+ 38.4 ms in 21 steps since start of marking, biggest step 20.7 ms, walltime since start of marking 340 ms) (average mu = 0.312, current mu = 0.310) al

<--- JS stacktrace --->

FATAL ERROR: MarkCompactCollector: young object promotion failed Allocation failed - JavaScript heap out of memory
Aborted (core dumped)
The command '/bin/sh -c yarn install --frozen-lockfile --production && yarn cache clean --all' returned a non-zero code: 134

Exited with code exit status 134

Looks like a memory issue, again, the only thing that change between this build and a previous one is that I updated Nx and the generated package json changed. Our other apps successfully build it's just this one fails.

The one failing (now) has 702 ish packages in the package.json, the one that succeeds only has like 65. This was working before so I don't realllly want to crank the resources up on CI to get past this. Any docker experts around here that can point me in the direction of what I can do so the new way Nx generates the package.json will work for us?

@yharaskrik
Copy link
Contributor

yharaskrik commented Oct 20, 2022

Not sure if the container will run after this (i don't see why not) but this little script that is ran after the build seems to have solved my memory issue woes.

const { readFileSync, writeFileSync } = require('fs');
const { join } = require('path');

/**
 * @description Trim dependencies from the generated built package.json so it's only the first level of
 * dependencies and not everything.
 *
 * @type {any}
 */

const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), { encoding: 'utf8' }));

const distPackageJson = JSON.parse(
    readFileSync(join(__dirname, '../dist/apps/<app>/package.json'), { encoding: 'utf8' })
);

Object.keys(distPackageJson['dependencies']).forEach((key) => {
    if (!packageJson['dependencies'][key]) {
        distPackageJson['dependencies'][key] = undefined;
    }
});

writeFileSync(join(__dirname, '../dist/apps/<app>/package.json'), JSON.stringify(distPackageJson));

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 21, 2022

@yharaskrik - I am curious do you get this error outside of the container? IE copy the /dist/apps/<app> directory to some location outside of your repo and run the same command from the docker file in that directory?

@yharaskrik
Copy link
Contributor

@yharaskrik - I am curious do you get this error outside of the container? IE copy the /dist/apps/<app> directory to some location outside of your repo and run the same command from the docker file in that directory?

I can try it but I doubt it. I have a brand new MacBook pro M2 with a ton of RAM.

@yharaskrik
Copy link
Contributor

@yharaskrik - I am curious do you get this error outside of the container? IE copy the /dist/apps/<app> directory to some location outside of your repo and run the same command from the docker file in that directory?

Installs fine on my local machine.

@yharaskrik
Copy link
Contributor

Hi @JoA-Mo,

Your docker build process is copying the root package-json.lock along with the generated package.json file. I assume you did this so that the install of the generated package.json files used the same versions as your root project due to the generated package.json file only including top level dependencies and using version ranges.

This should no longer be needed as the generated package.json should now include all dependencies in the dependency tree with fixed versions.

Something isn't working right. Our root package.json defines nanoid as ^3.1.20 but the generated package json for our app used 2.1.11 which breaks our server once deployed.

Is there any plans to revert this generate package json change? If not I will need to write some more scripts to fix this ourselves as I don't think I can trust the generated one.

@leon
Copy link
Contributor

leon commented Oct 24, 2022

Any news on this?

After updating to nx 15 all my express apps using generatePackageJson: true now contain a whole bunch of dependencies which where not getting added when using v14

When comparing the old output and the new, It looks like v15 is including all the peer dependencies as top level dependencies instead.

So I'm getting a lot of:

"@firebase/analytics": "0.8.3",
    "@firebase/analytics-compat": "0.1.16",
    "@firebase/analytics-types": "0.7.0",
    "@firebase/app": "0.8.2",
    "@firebase/app-check": "0.5.15",
    "@firebase/app-check-compat": "0.2.15",
    "@firebase/app-check-interop-types": "0.1.0",
    "@firebase/app-check-types": "0.4.0",
    "@firebase/app-compat": "0.1.37",
    "@firebase/app-types": "0.8.0",
    "@firebase/auth": "0.20.10",
    "@firebase/auth-compat": "0.2.23",
    "@firebase/auth-interop-types": "0.1.6",
    "@firebase/auth-types": "0.11.0",
    "@firebase/component": "0.5.20",
    "@firebase/database": "0.13.9",
    "@firebase/database-compat": "0.2.9",
    "@firebase/database-types": "0.9.16",
    "@firebase/firestore": "3.7.1",
    "@firebase/firestore-compat": "0.2.1",
    "@firebase/firestore-types": "2.5.0",
    "@firebase/functions": "0.8.7",
    "@firebase/functions-compat": "0.2.7",
    "@firebase/functions-types": "0.5.0",
    "@firebase/installations": "0.5.15",
    "@firebase/installations-compat": "0.1.15",
    "@firebase/installations-types": "0.4.0",

@pmosconi
Copy link

Hi,
in my case generatePackageJson: true is also adding the wrong dependency of uuid package:
in root package.json: "uuid": "^9.0.0",
in dist/apps/api/package.json: "uuid": "^8.0.0"

There migh be other packages, but in this case the application is using a function that was deployed in uuid version 8.3.0, so it is cashing.

Interim solution: added yarn add uuid@^9.0.0 --production --no-lockfile to deployment script

Hope this helps.

@yharaskrik
Copy link
Contributor

Hi, in my case generatePackageJson: true is also adding the wrong dependency of uuid package: in root package.json: "uuid": "^9.0.0", in dist/apps/api/package.json: "uuid": "^8.0.0"

There migh be other packages, but in this case the application is using a function that was deployed in uuid version 8.3.0, so it is cashing.

Interim solution: added yarn add uuid@^9.0.0 --production --no-lockfile to deployment script

Hope this helps.

Yes, I was also getting incorrect versions of packages used.

for everyone here, after you run build with the flag you can use this flag to fix your package.json

const { readFileSync, writeFileSync } = require('fs');
const { join } = require('path');

const app = process.argv[2];

const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), { encoding: 'utf8' }));

const distPackageJson = JSON.parse(
    readFileSync(join(__dirname, `../dist/apps/${app}/package.json`), { encoding: 'utf8' })
);

Object.keys(distPackageJson['dependencies']).forEach((key) => {
    const version = packageJson['dependencies'][key];
    if (!version) {
        distPackageJson['dependencies'][key] = undefined;
    } else {
        distPackageJson['dependencies'][key] = version;
    }
});

writeFileSync(join(__dirname, `../dist/apps/${app}/package.json`), JSON.stringify(distPackageJson));

then run it with node filename.js <appname>

@PointSingularity
Copy link

Had the same thing happen with a version of axios. It might be adding packages NX itself is using.

@capJavert
Copy link

capJavert commented Oct 26, 2022

@FrozenPandaz @meeroslav is this something that is gonna be fixed/reverted to the 14.x behaviour or are you keeping it? I just think we all need some feedback because currently there is no valid solution to this. Thanks!

If I am looking at it correctly this change in behaviour was made in #12185

@JoA-MoS JoA-MoS changed the title generatePackageJson on nx 15 generates the the dependency versions differently than 14 generatePackageJson on nx 15 generates the the dependencies and versions differently than 14 Oct 26, 2022
@meeroslav
Copy link
Contributor

meeroslav commented Oct 27, 2022

Hey @capJavert,

We will not revert the code to v14, but we are working on the solution for this case.

In v14 we were keeping the version ranges as they were in package.json, which oftentimes caused unexpected errors when versions changed. The v15 uses fixed versions.

The additional dependencies issue is something we will investigate and will get back to you as soon as possible.

@yharaskrik
Copy link
Contributor

Hey @meeroslav thanks for your reply. One thing I will note is that there is one other issue more than the additional deps issue. There are cases where the version in the generated package json is wrong compared to the root package.json (and thus the lock file).

#12675 (comment)

#12675 (comment)

For the one that happened to me nanoid once I deployed my server it failed to start up as the version of nanoid that was installed was behind the version the app was built and tested with (by a major) so it just crashed. Which I believe is a bug based on how we were told it should work here: #12675 (comment)

@meeroslav
Copy link
Contributor

Hmm... code looks fine. Is there some minimal repo where I can debug this?

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 28, 2022

@meeroslav

I was able to make the issue recur locally after updating axios to v 1.1.3. JoA-MoS/garage#85

Root package.json

"axios": "^1.1.3",

Generated package.json

"axios": "0.21.4",

nxdeps.json

  {
        "source": "campsite-watcher",
        "target": "npm:axios@0.21.4",
        "type": "static"
      },

node_modules/axios/package.json

"version": "1.1.3",

Is there a bug in how nxdeps.json is being generated?

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 29, 2022

I am actually looking at https://github.com/nrwl/nx/blob/master/packages/nx/src/project-graph/build-project-graph.spec.ts to see if I can add this test case but not that familiar with the code so I am getting oriented. Let me know if you have decided this is not the correct path.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 29, 2022

@meeroslav - Here is a very simple repro library. I added the latest chalk 5.1.2 and would expect that to be included in the package.json when generated but the version is set to 2.4.2.

https://github.com/JoA-MoS/repro-nx-generate-package-json

pull the repo and run

npm ci
nx build example

Then check dist/apps/example/package.json to see the chalk version

@charsleysa
Copy link

@JoA-MoS I've checked out your repro and confirmed the issue.

@meeroslav I've debugged the issue and traced it down to this function https://github.com/nrwl/nx/blob/master/packages/nx/src/utils/target-project-locator.ts#L132

From the context of the repro, what looks like is happening is that there are multiple versions of chalk installed (due to different used by dependencies) and the graph contains all those versions (since it reads the entire lock file) but the listed dependency version does not come first in the list. The findNpmPackage function doesn't distinguish package versions so it returns the first package where the name matches regardless of version (which happens to be chalk@2.4.2).

A solution I came up with (which may not be correct) is to add the rootVersion property to the data during mapLockFileDataToPartialGraph https://github.com/nrwl/nx/blob/master/packages/nx/src/utils/lock-file/lock-file.ts#L133
and updating the findNpmPackage function by replacing

      const pkg = this.npmProjects.find(
        (pkg) =>
          npmImport === pkg.data.packageName ||
          npmImport.startsWith(`${pkg.data.packageName}/`)
      );

with

      const pkg = this.npmProjects.find(
        (pkg) =>
          (npmImport === pkg.data.packageName ||
          npmImport.startsWith(`${pkg.data.packageName}/`)) &&
          pkg.data.rootVersion === true
      ) ??  this.npmProjects.find(
        (pkg) =>
          npmImport === pkg.data.packageName ||
          npmImport.startsWith(`${pkg.data.packageName}/`)
      );

@yharaskrik
Copy link
Contributor

yharaskrik commented Oct 29, 2022 via email

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 29, 2022

Hoisting occurs in npm, pnpm, yarn, and lerna workspaces where there are multiple package.json for each project. In this situation the dependency version in the package.json of the project if defined should be used. If it is not defined it should take the root package.json. All of these tools already have a way of figuring this out so I think it would be ideal to utilize the package manager resolution of the dependency.

Now nx without workspaces nx is responsible for figuring out this and I think it should just rely on the closest package.json dependency version. If it is defined in the project's package.json use that version. If it is defined at the root use that version.

If you use npm workspaces your package lock looks like following

{
    "name": "@org/example",
    "version": "1.0.0",
    "lockfileVersion": 2,
    "requires": true,
    "packages": {
        "": {
         ... copy of the root package.json with deps and dev deps
         },
         "workspace1": {
         ... copy of workspace1's package.json with deps and dev deps
         }
         "project1/node_modules/<dependency>: {
           "version": "9.4.1",
              "resolved": "",
              "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
              "engines": {
                  "node": "^12.20.0 || >=14"
              }
         },
         this repeats for workspace1 specific dependencies that shouldn't be hoisted,
        "workspace2":{
          ... copy of workspace2's package.json with deps and dev deps
          }
      }
 }

The difficulty is that with nx without package manager workspaces this isn't defined like this.

My thought is that the generated package.json should use the package version defined in the package.json closest to the app/lib that uses it. If it is not defined in the project folder then move up to the root package.json. (There is the consideration of nested projects (which I don't think people should use) but the algorithm is still the same. If the package is not defined move up to the next package.json until you get to the root package.json.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 29, 2022

Now nx without workspaces nx is responsible for figuring out this and I think it should just rely on the closest package.json dependency version. If it is defined in the project's package.json use that version. If it is defined at the root use that version.

Correction, if we don't have npm/yarn workspaces the only version of a dependency you can have is the top root dependency, if you were to specify a different version of a package in a projects package.json it would never be installed.

The other method for multiple versions is npm aliases which sorts itself out.

npm alias

"dependencies": {
    "chalk": "^5.1.2",
    "chalk-4": "npm:chalk@^4.1.2",
    "tslib": "^2.3.0"
  }

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 30, 2022

@meeroslav, @FrozenPandaz - Can you explain why the generartePackageJson process was changed? Was it intentional or a side effect of something else?

What is the process for reopening this issue? do we reopen this issue or create a new one?

@electrofLy
Copy link

electrofLy commented Oct 31, 2022

Still experiencing this - packages that are supposed to be lenient (from the root package.json "whatever": "^1.0.0") are fixed to the installed version (in library generated package.json "whatever": "1.2.3"). Maybe this issue should be reopened?

@meeroslav
Copy link
Contributor

@JoA-MoS first of all, thank you for the repro. This is super helpful and allows me to pinpoint the source of the problem (kudos to @charsleysa for doing most of the work in locating it).

@meeroslav, @FrozenPandaz - Can you explain why the generartePackageJson process was changed? Was it intentional or a side effect of something else?

How we treat external dependencies (this also includes package json generation) was semi-broken for a very long time. The parsing of lock files finally allowed us to have full insight into which packages are actually used. This change helped us fix several issues that couldn't be fixed otherwise.
This generatePackageJson bug was an unfortunate result of the whole process. Again, sorry for the inconvenience. I understand this might be frustrating to you.

What is the process for reopening this issue? do we reopen this issue or create a new one?

No need to reopen it. I will create another fix in a bit and link it to this issue.

@meeroslav
Copy link
Contributor

@charsleysa the solution is a bit more complicated than that but you were on the right track. Detecting which of the versions is the root one is the key. I just need to make sure the solution doesn't impact the performance.

@meeroslav
Copy link
Contributor

@electrofLy

Still experiencing this - packages that are supposed to be lenient (from the root package.json "whatever": "^1.0.0") are fixed to the installed version (in library generated package.json "whatever": "1.2.3"). Maybe this issue should be reopened?

TLDR;

This is by design. We intentionally want to restrict generated package.json dependencies to those that we have in the lock file since that is the only version we can guarantee to work.

Long version

There are reasons why lock files exist:

  1. Speeds up the installation because the range e.g. “typescript: *^4.0.0” with be resolved to “typescript: 4.8.4" and the lock file will contain the exact URL to that package. Installation doesn’t have to check and recalculate the versions again, but can instead fetch them directly
  2. Resolves the multiple version confusion when you have dependencies requiring different versions of the same package. Some of those will be hoisted (usually the one defined in the package.json itself) and some will be nested.
  3. Prevents untested (and potentially vulnerable) new versions of the package from creeping in and ending up in your deployment pipeline. It's a sort of manifest of what version we have installed.
  4. Resolves the differences between dev/CI machines by providing the same set of dependencies (this might still differ if you have a different OS and/or node version major). No more "Works on my machine"

Reasons 2-4 (most importantly 3) are the reasons why we want to have a fixed version in the generated package.json. When you are deploying the app/package you want to deploy a set of dependencies you have been testing with and know to be working well together.

To achieve this, each npm install/yarn install/pnpm install should be run intentionally and only when upgrading dependencies. Each change to the lock file should be tested thoroughly.

In all the other scenarios we should run npm ci/yarn --frozen-lockfile/yarn --immutable/pnpm i --frozen-lockfile to make sure we get those exact versions.

The only reason we didn't have it done this way so far, was that we didn't have lock file parsing in place so we couldn't tell which versions you actually have.

We will soon add lock file pruning, so each generated package.json will be accompanied by a matching lock file.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 31, 2022

@meeroslav - thank you so much for your work on this, it is a wonderful tool.

I have a different understanding about how package managers and lock files work but haven't found it documented so I am wondering if there has been conversations about this with the npm / yarn / pnpm teams and confirmed that using the whole lock file with a subset package.json is not supported?

  1. Resolves the multiple version confusion when you have dependencies requiring different versions of the same package. Some of those will be hoisted (usually the one defined in the package.json itself) and some will be nested.
  2. Prevents untested (and potentially vulnerable) new versions of the package from creeping in and ending up in your deployment pipeline. It's a sort of manifest of what version we have installed.
  3. Resolves the differences between dev/CI machines by providing the same set of dependencies (this might still differ if you have a different OS and/or node version major). No more "Works on my machine"

2 - 4 above were solved in version 9 (I think) through 14's implementation of generatePackageJson by using the following process.

  1. Enable generatePackageJson
  2. Build your app
  3. Copy the lock file from the root to the apps dist directory
  4. Run npm ci in the app directory

CONFIRM: Run npm ls in the container or (directory) you will see the additional items in the lock file that were not included in the generated package json shown as unmet.

Usually the above is done in a Dockerfile

Maybe, you have confirmed that the above process is an unsupported process, I am wondering if the complexity of processing the different package managers lock files, in the different configurations (root only, workspaces), and evaluating the correct version to include in the generated package.json and pruned lock file adds value to the above process?

In my opinion, looking at the justification for this added functionality all the items were already solved in v14 generate package json algorithm it just wasn't documented well.

If it would help I can write up a full recipe for publishing apps / docker images / lambda with the exact dependencies using the root lock file.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Oct 31, 2022

Also, the new generated pacakge json may have adverse impacts for publishable npm packages as the range specified is important and needs to be included in the generated package json. This also impacts any publishable package using the @nrwl/js:tsc executor.

@meeroslav
Copy link
Contributor

meeroslav commented Nov 2, 2022

2 - 4 above were solved in version 9 (I think) through 14's implementation of generatePackageJson by using the following process.

This is more of a workaround than a proper solution. That's why we want to offer automated one. Lock file generation should be available in few weeks.

Maybe, you have confirmed that the above process is an unsupported process, I am wondering if the complexity of processing the different package managers lock files, in the different configurations (root only, workspaces), and evaluating the correct version to include in the generated package.json and pruned lock file adds value to the above process?

As mentioned earlier, parsing and pruning the lock files was absolutely must since it was blocking several issues and features. So it this was just a side win.

Also, the new generated pacakge json may have adverse impacts for publishable npm packages as the range specified is important and needs to be included in the generated package json. This also impacts any publishable package using the @nrwl/js:tsc executor.

For publishable packages we suggest creating manual package.json instead of using generated one, because it gives you the freedom to specify ranges as you see fit. For example my library could support @angular/core: >= 10 < 15 but my root package.json would have probably something like @angular/core: ^14.2.0. Using root package json ranges for publishable libs is bad practice, as they tend to be more restrictive than needed.

@yharaskrik
Copy link
Contributor

yharaskrik commented Nov 7, 2022

This seems to be fixed now in 15.0.10 from what I can tell.

Scratch that.

@meeroslav
Copy link
Contributor

@yharaskrik there were several fixes added recently for this logic and not all of them are rolled out yet.

This is included in the latest version:

This will be included in the upcoming version:

Hopefully, that should cover it.

@enchorb
Copy link

enchorb commented Nov 14, 2022

Has this been released yet? Still getting the Your lockfile needs to be updated error on 15.0.13

@meeroslav
Copy link
Contributor

@enchorb The error you see is not related to the included fixes, but rather to the upcoming implementation of lock file pruning. Unfortunately, you have to be a bit more patient with that.

@Nigelli
Copy link

Nigelli commented Nov 16, 2022

Hi, I've already moved on to the new version which fixed the issue with dependency version but I am now seeing an issue with using an aliased npm package. I'm not sure if this is related to this fix or a separate issue.

When I have a package installed using npm aliased-package-name@npm:mypackage@1.0.0 my package.json is now including a dependency for aliased-package-name:1.0.0 instead. Obviously this results in a package not found error during install.

Is the fix to look for the name property in the lock file? I notice the aliased package has the extra field.

"node_modules/aliased-package-name": {
"name": "mypackage",
"version": "1.0.0",
...,
}

@meeroslav
Copy link
Contributor

Thanks @Nigelli, can you please open a separate issue and tag me and this issue so I can follow up.

@JoA-MoS
Copy link
Contributor Author

JoA-MoS commented Dec 13, 2022

@meeroslav - Something occurred to me a while back but just wanted to add it to the thread. In regards to this comment.

For publishable packages we suggest creating manual package.json instead of using generated one, because it gives you the freedom to specify ranges as you see fit. For example my library could support @angular/core: >= 10 < 15 but my root package.json would have probably something like @angular/core: ^14.2.0. Using root package json ranges for publishable libs is bad practice, as they tend to be more restrictive than needed.

I believe specifying the range beyond the version used to build the package is what the peerDependencies section of the package.json is used for.

@meeroslav
Copy link
Contributor

Usually yes, but not exclusively.

Peer only means: "my package needs this to work, but I'm expecting other dependencies to have already installed it and will not install it myself."

@github-actions
Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.