Skip to content

Commit 7623ba6

Browse files
authored
chore(tooling): add script for getting nested dependency data (#9734)
Debugging issues like those in https://community.redwoodjs.com/t/you-must-register-a-usemutation-hook-via-the-graphglhooksprovider/5604 is extremely time consuming if you don't know what to look for and if you don't have the proper tooling. Because of my prior experience with Babel, I've written tooling that I use locally to detect how many versions of a dependency are in node_modules. (Spoiler: it's usually more than one.) I think it's worth keeping track of what we can consider "normal" nesting vs abnormal. So to start, I'm adding a script that identifies all nested dependencies and their versions to a degree (yes, they can be nested twice). Right now, all this script does is generate a JSON file like the one below: <details> <summary>Get ready to scroll</summary> ```json { "version": "6.5.1", "hoistedNodeModules": { "jest-runtime": { "strip-bom": "4.0.0" }, "browserify-rsa": { "bn.js": "5.2.1" }, "@types/testing-library__jest-dom": { "@types/jest": "29.5.11" }, "@types/react-dom": { "@types/react": "18.2.42" }, "@types/serve-static": { "@types/mime": "3.0.4" }, "multimatch": { "arrify": "2.0.1" }, "stack-utils": { "escape-string-regexp": "2.0.0" }, "supports-hyperlinks": { "supports-color": "7.2.0" }, "jest-worker": { "supports-color": "8.1.1" }, "dotenv-defaults": { "dotenv": "14.3.2" }, "watchpack-chokidar2": { "is-extendable": "1.0.1", "normalize-path": "2.1.1", "anymatch": "2.0.0", "chokidar": "2.1.8", "is-binary-path": "1.0.1", "readdirp": "2.2.1", "fill-range": "4.0.0", "binary-extensions": "1.13.1", "to-regex-range": "2.1.1", "define-property": "2.0.2", "extend-shallow": "3.0.2", "micromatch": "3.1.10", "braces": "2.3.2", "fsevents": "1.2.13", "is-glob": "3.1.0", "glob-parent": "3.1.0" }, "make-dir": { "semver": "5.7.2" }, "tsconfig-paths": { "json5": "1.0.2" }, "fast-json-stringify": { "ajv-formats": "2.1.1", "ajv": "8.12.0" }, "snapdragon-node": { "define-property": "1.0.0" }, "unset-value": { "has-values": "0.1.4", "isobject": "2.1.0", "has-value": "0.3.1", "isarray": "1.0.0" }, "webpack-manifest-plugin": { "webpack-sources": "2.3.1" }, "@pmmmwh/react-refresh-webpack-plugin": { "p-locate": "5.0.0", "find-up": "5.0.0", "schema-utils": "3.3.0", "source-map": "0.7.4", "locate-path": "6.0.0" }, "mixin-deep": { "is-extendable": "1.0.1" }, "cacheable-request": { "lowercase-keys": "2.0.0", "get-stream": "5.2.0" }, "optimism": { "@wry/trie": "0.4.3" }, "@whatwg-node/server": { "@whatwg-node/fetch": "0.9.14" }, "@whatwg-node/fetch": { "@whatwg-node/node-fetch": "0.3.6", "@whatwg-node/node-fetch/cjs": "null", "@whatwg-node/events": "0.0.3", "urlpattern-polyfill": "8.0.2" }, "@jest/reporters": { "istanbul-lib-instrument": "6.0.1" }, "@jest/transform": { "write-file-atomic": "4.0.2" }, "cssstyle": { "cssom": "0.3.8" }, "bl": { "readable-stream": "3.6.2", "buffer": "5.7.1" }, "minipass-pipeline": { "minipass": "3.3.6" }, "eslint-scope": { "estraverse": "4.3.0" }, "rimraf": { "minimatch/dist/mjs": "null", "minimatch/dist/cjs": "null", "minimatch": "9.0.3", "glob/dist/esm": "null", "glob/dist/commonjs": "null", "glob": "10.3.10" }, "crc32-stream": { "readable-stream": "3.6.2" }, "node-fetch": { "whatwg-url": "5.0.0", "webidl-conversions": "3.0.1" }, "@ts-morph/common": { "minimatch": "5.1.6", "mkdirp": "1.0.4" }, "eslint-plugin-import": { "semver": "6.3.1", "debug": "3.2.7" }, "extglob": { "define-property": "1.0.0" }, "@eslint/eslintrc": { "globals": "13.23.0" }, "concurrently": { "supports-color": "8.1.1" }, "line-column": { "isobject": "2.1.0", "isarray": "1.0.0" }, "babel-plugin-module-resolver": { "minimatch": "5.1.6", "glob": "8.1.0" }, "vscode-languageserver-protocol": { "vscode-languageserver-types": "3.17.5", "vscode-languageserver-types/lib/esm": "null" }, "touch": { "abbrev": "1.1.1", "nopt": "1.0.10" }, "express": { "ms": "2.0.0", "merge-descriptors": "1.0.1", "array-flatten": "1.1.1", "qs": "6.11.0", "path-to-regexp": "0.1.7", "debug": "2.6.9" }, "cli-truncate": { "strip-ansi": "7.1.0", "ansi-regex": "6.0.1", "string-width": "5.1.2" }, "configstore": { "make-dir": "1.3.0", "pify": "3.0.0" }, "@ardatan/relay-compiler": { "wrap-ansi": "6.2.0", "yargs-parser": "18.1.3", "cliui": "6.0.0", "yargs": "15.4.1", "camelcase": "5.3.1" }, "copy-webpack-plugin": { "slash": "4.0.0", "globby": "13.2.2", "glob-parent": "6.0.2" }, "@redwoodjs/internal": { "source-map": "0.7.4" }, "@redwoodjs/cli": { "decamelize": "5.0.1" }, "@redwoodjs/testing": { "@types/node": "18.18.9" }, "@redwoodjs/api": { "@whatwg-node/fetch": "0.9.14" }, "@redwoodjs/telemetry": { "@whatwg-node/fetch": "0.9.14" }, "@redwoodjs/prerender": { "@whatwg-node/fetch": "0.9.14" }, "@redwoodjs/structure": { "lru-cache": "7.18.3" }, "@redwoodjs/graphql-server": { "@graphql-tools/utils": "10.0.11", "@graphql-tools/utils/cjs": "null", "@graphql-tools/schema": "10.0.2", "@graphql-tools/schema/cjs": "null" }, "graphql-yoga": { "@whatwg-node/fetch": "0.9.14", "@graphql-tools/utils": "10.0.11", "@graphql-tools/utils/cjs": "null", "@graphql-tools/schema": "10.0.2", "@graphql-tools/schema/cjs": "null" }, "node-libs-browser": { "punycode": "1.4.1", "util": "0.11.1", "inherits": "2.0.3", "path-browserify": "0.0.1", "isarray": "1.0.0", "buffer": "4.9.2" }, "log-update": { "strip-ansi": "7.1.0", "type-fest": "1.4.0", "wrap-ansi": "8.1.0", "ansi-regex": "6.0.1", "ansi-styles": "6.2.1", "ansi-escapes": "5.0.0", "string-width": "5.1.2", "cli-cursor": "4.0.0" }, "has-values": { "kind-of": "4.0.0" }, "to-regex": { "is-extendable": "1.0.1", "extend-shallow": "3.0.2", "define-property": "2.0.2" }, "listr2": { "strip-ansi": "7.1.0", "wrap-ansi": "8.1.0", "ansi-regex": "6.0.1", "ansi-styles": "6.2.1", "string-width": "5.1.2" }, "minipass-flush": { "minipass": "3.3.6" }, "compress-commons": { "readable-stream": "3.6.2" }, "flat-cache": { "rimraf": "3.0.2", "keyv": "4.5.4", "json-buffer": "3.0.1" }, "split-string": { "is-extendable": "1.0.1", "extend-shallow": "3.0.2" }, "ansi-diff-stream": { "ansi-regex": "2.1.1" }, "p-locate": { "p-limit": "2.3.0" }, "eslint-import-resolver-node": { "debug": "3.2.7" }, "terser-webpack-plugin": { "jest-worker": "27.5.1", "supports-color": "8.1.1", "schema-utils": "3.3.0" }, "whatwg-url": { "tr46": "3.0.0" }, "csso": { "css-tree": "2.2.1", "mdn-data": "2.0.28" }, "ts-node": { "arg": "4.1.3" }, "url-loader": { "schema-utils": "3.3.0" }, "archiver": { "readable-stream": "3.6.2" }, "got": { "get-stream": "4.1.0" }, "minipass-sized": { "minipass": "3.3.6" }, "p-retry": { "retry": "0.13.1" }, "eslint-module-utils": { "debug": "3.2.7" }, "@cspotcode/source-map-support": { "@jridgewell/trace-mapping": "0.3.9" }, "encoding": { "iconv-lite": "0.6.3" }, "@isaacs/cliui": { "strip-ansi": "7.1.0", "wrap-ansi": "8.1.0", "ansi-regex": "6.0.1", "ansi-styles": "6.2.1", "string-width": "5.1.2" }, "expand-brackets": { "ms": "2.0.0", "debug": "2.6.9" }, "spdy-transport": { "readable-stream": "3.6.2" }, "cacache": { "minimatch/dist/mjs": "null", "minimatch/dist/cjs": "null", "minimatch": "9.0.3", "glob/dist/esm": "null", "glob/dist/commonjs": "null", "glob": "10.3.10" }, "null-loader": { "schema-utils": "3.3.0" }, "pkg-up": { "find-up": "3.0.0" }, "eslint-plugin-jsx-a11y": { "aria-query": "5.3.0" }, "find-up": { "p-locate": "4.1.0", "p-limit": "2.3.0", "locate-path": "5.0.0" }, "chalk": { "supports-color": "7.2.0" }, "assert": { "util": "0.10.4", "inherits": "2.0.3" }, "fast-url-parser": { "punycode": "1.4.1" }, "sockjs": { "uuid": "8.3.2" }, "url": { "punycode": "1.4.1" }, "supports-color": { "has-flag": "3.0.0" }, "webpack-cli": { "commander": "10.0.1" }, "istanbul-lib-report": { "make-dir": "4.0.0", "supports-color": "7.2.0" }, "snapdragon": { "ms": "2.0.0", "source-map": "0.5.7", "debug": "2.6.9" }, "foreground-child": { "signal-exit/dist/mjs": "null", "signal-exit/dist/cjs": "null", "signal-exit": "4.1.0" }, "schema-utils": { "ajv-formats": "2.1.1", "ajv-keywords": "5.1.0", "ajv": "8.12.0" }, "jsdom": { "agent-base": "6.0.2", "https-proxy-agent": "5.0.1", "http-proxy-agent": "5.0.0" }, "svgo": { "commander": "7.2.0" }, "jest-runner": { "source-map-support": "0.5.13" }, "@graphql-codegen/typescript-operations": { "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/core": { "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/plugin-helpers": { "change-case-all": "1.0.15", "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/typescript": { "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/cli": { "cli-truncate": "2.1.0", "wrap-ansi": "6.2.0", "slice-ansi": "3.0.0", "log-update": "4.0.0", "listr2": "4.0.5" }, "@graphql-codegen/add": { "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/typescript-resolvers": { "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/typescript-react-apollo": { "tslib": "2.6.2", "tslib/modules": "null", "@graphql-codegen/plugin-helpers": "2.7.2", "@graphql-codegen/plugin-helpers/cjs": "null", "@graphql-codegen/visitor-plugin-common": "2.13.1", "@graphql-codegen/visitor-plugin-common/cjs": "null", "@graphql-tools/utils": "8.13.1", "@graphql-tools/utils/cjs": "null" }, "@graphql-codegen/visitor-plugin-common": { "change-case-all": "1.0.15", "tslib": "2.5.3", "tslib/modules": "null" }, "@graphql-codegen/schema-ast": { "tslib": "2.5.3", "tslib/modules": "null" }, "webpack": { "webpack-sources": "3.2.3", "schema-utils": "3.3.0", "@webassemblyjs/ast": "1.11.6" }, "package-json": { "semver": "6.3.1" }, "dotenv-webpack": { "dotenv-defaults": "2.0.2", "dotenv": "8.6.0" }, "resolve-url-loader": { "convert-source-map": "1.9.0" }, "minizlib": { "minipass": "3.3.6" }, "semver": { "lru-cache": "6.0.0" }, "regjsparser": { "jsesc": "0.5.0" }, "vite": { "esbuild": "0.18.20", "@esbuild/darwin-arm64": "0.18.20" }, "@sdl-codegen/node": { "ts-morph": "18.0.0", "@ts-morph/common": "0.19.0", "code-block-writer/esm": "null", "code-block-writer/script": "null", "code-block-writer": "12.0.0", "minimatch/dist/mjs": "null", "minimatch/dist/cjs": "null", "minimatch": "7.4.6", "mkdirp/dist/mjs": "null", "mkdirp/dist/cjs": "2.1.6", "mkdirp": "2.1.6" }, "minimatch": { "brace-expansion": "1.1.11" }, "webpack-dev-server": { "rimraf": "3.0.2", "ipaddr.js": "2.1.0" }, "find-cache-dir": { "pkg-dir": "3.0.0", "find-up": "3.0.0" }, "regex-not": { "is-extendable": "1.0.1", "extend-shallow": "3.0.2" }, "unixify": { "normalize-path": "2.1.1" }, "browserify-sign": { "bn.js": "5.2.1", "readable-stream": "3.6.2" }, "humanize-string": { "decamelize": "2.0.0" }, "renderkid": { "htmlparser2": "6.1.0", "dom-serializer": "1.4.1", "dom-serializer/lib/esm": "null", "css-select": "4.3.0", "domutils": "2.8.0", "entities": "2.2.0", "domhandler": "4.3.1" }, "body-parser": { "ms": "2.0.0", "qs": "6.11.0", "raw-body": "2.5.1", "debug": "2.6.9" }, "graphql-config": { "jiti": "1.17.1", "brace-expansion": "1.1.11", "minimatch": "4.2.3", "cosmiconfig": "8.0.0", "@graphql-tools/merge": "8.4.2", "@graphql-tools/merge/cjs": "null" }, "@babel/preset-env": { "semver": "6.3.1" }, "@babel/core": { "semver": "6.3.1" }, "@babel/plugin-transform-runtime": { "semver": "6.3.1" }, "@babel/highlight": { "color-name": "1.1.3", "chalk": "2.4.2", "color-convert": "1.9.3", "ansi-styles": "3.2.1" }, "@babel/cli": { "commander": "4.1.1", "slash": "2.0.0" }, "@babel/helper-compilation-targets": { "lru-cache": "5.1.1", "semver": "6.3.1", "yallist": "3.1.1" }, "@babel/code-frame": { "color-name": "1.1.3", "chalk": "2.4.2", "color-convert": "1.9.3", "ansi-styles": "3.2.1" }, "@babel/eslint-parser": { "eslint-visitor-keys": "2.1.0", "semver": "6.3.1" }, "@babel/helper-create-regexp-features-plugin": { "semver": "6.3.1" }, "@babel/helper-create-class-features-plugin": { "semver": "6.3.1" }, "is-number": { "kind-of": "3.2.2" }, "msw": { "type-fest": "2.19.0", "strict-event-emitter": "0.4.6", "cookie": "0.4.2" }, "compression": { "ms": "2.0.0", "bytes": "3.0.0", "safe-buffer": "5.1.2", "debug": "2.6.9" }, "whatwg-encoding": { "iconv-lite": "0.6.3" }, "tsutils": { "tslib/test/validateModuleExportsMatchCommonJS": "null", "tslib": "1.14.1", "tslib/modules": "null" }, "copy-concurrently": { "rimraf": "2.7.1" }, "@webassemblyjs/wasm-parser": { "@webassemblyjs/ast": "1.11.6" }, "@webassemblyjs/wasm-gen": { "@webassemblyjs/ast": "1.11.6" }, "@webassemblyjs/helper-wasm-section": { "@webassemblyjs/ast": "1.11.6" }, "@webassemblyjs/wasm-edit": { "@webassemblyjs/wast-printer": "1.11.6", "@webassemblyjs/ast": "1.11.6" }, "@webassemblyjs/wast-parser": { "@webassemblyjs/floating-point-hex-parser": "1.9.0", "@webassemblyjs/helper-api-error": "1.9.0" }, "@webassemblyjs/ast": { "@webassemblyjs/helper-wasm-bytecode": "1.9.0" }, "@webassemblyjs/wasm-opt": { "@webassemblyjs/ast": "1.11.6" }, "pumpify": { "pump": "2.0.1" }, "node-gyp": { "minimatch/dist/mjs": "null", "minimatch/dist/cjs": "null", "minimatch": "9.0.3", "glob/dist/esm": "null", "glob/dist/commonjs": "null", "glob": "10.3.10", "which": "4.0.0", "isexe/dist/mjs": "null", "isexe/dist/cjs": "null", "isexe": "3.1.1" }, "zip-stream": { "readable-stream": "3.6.2" }, "slice-ansi": { "is-fullwidth-code-point": "4.0.0", "ansi-styles": "6.2.1" }, "webpack-bundle-analyzer": { "escape-string-regexp": "4.0.0", "commander": "7.2.0", "is-plain-object": "5.0.0", "ws": "7.5.9" }, "to-object-path": { "kind-of": "3.2.2" }, "ansi-escapes": { "type-fest": "0.21.3" }, "istanbul-lib-instrument": { "semver": "6.3.1" }, "babel-plugin-polyfill-corejs2": { "semver": "6.3.1" }, "yargs": { "y18n": "5.0.8" }, "move-concurrently": { "rimraf": "2.7.1" }, "eslint": { "globals": "13.23.0", "escape-string-regexp": "4.0.0", "eslint-scope": "7.2.2", "p-locate": "5.0.0", "find-up": "5.0.0", "doctrine": "3.0.0", "locate-path": "6.0.0", "glob-parent": "6.0.2" }, "import-fresh": { "resolve-from": "4.0.0" }, "to-regex-range": { "is-number": "7.0.0" }, "babel-timing": { "tapable": "1.1.3", "is-extendable": "1.0.1", "ssri": "6.0.2", "lru-cache": "5.1.1", "eslint-scope": "4.0.3", "chownr": "1.1.4", "commander": "2.20.3", "rimraf": "2.7.1", "path-exists": "3.0.0", "watchpack": "1.7.5", "acorn": "8.11.2", "serialize-javascript": "4.0.0", "unique-filename": "1.1.1", "find-babel-config": "1.2.0", "pkg-dir": "3.0.0", "find-cache-dir": "3.3.2", "terser-webpack-plugin": "1.4.5", "cacache": "12.0.4", "find-up": "3.0.0", "memory-fs": "0.5.0", "enhanced-resolve": "4.5.0", "schema-utils": "2.7.1", "fill-range": "4.0.0", "loader-utils": "1.4.2", "json5": "0.5.1", "webpack": "4.47.0", "unique-slug": "2.0.2", "semver": "6.3.1", "estraverse": "4.3.0", "make-dir": "3.1.0", "@webassemblyjs/wasm-parser": "1.9.0", "@webassemblyjs/wasm-gen": "1.9.0", "@webassemblyjs/helper-wasm-section": "1.9.0", "@webassemblyjs/utf8": "1.9.0", "@webassemblyjs/wasm-edit": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/ieee754": "1.9.0", "@webassemblyjs/helper-buffer": "1.9.0", "@webassemblyjs/helper-api-error": "1.9.0", "@webassemblyjs/wasm-opt": "1.9.0", "@webassemblyjs/leb128": "1.9.0", "to-regex-range": "2.1.1", "define-property": "2.0.2", "yallist": "3.1.1", "terser": "4.8.1", "extend-shallow": "3.0.2", "micromatch": "3.1.10", "babel-loader": "8.3.0", "braces": "2.3.2", "loader-runner": "2.4.0" }, "pretty-format": { "react-is": "18.2.0", "ansi-styles": "5.2.0" }, "object-copy": { "kind-of": "3.2.2" }, "define-property": { "is-descriptor": "0.1.7" }, "inquirer": { "wrap-ansi": "6.2.0" }, "@fastify/static": { "minimatch": "5.1.6", "glob": "8.1.0" }, "@fastify/ajv-compiler": { "ajv-formats": "2.1.1", "ajv": "8.12.0" }, "string-width": { "emoji-regex": "8.0.0" }, "@testing-library/dom": { "react-is": "17.0.2", "ansi-styles": "5.2.0", "pretty-format": "27.5.1" }, "@testing-library/react": { "react-is": "17.0.2", "ansi-styles": "5.2.0", "pretty-format": "27.5.1", "@testing-library/dom": "9.3.3" }, "@testing-library/jest-dom": { "aria-query": "5.3.0", "chalk": "3.0.0", "supports-color": "7.2.0" }, "@istanbuljs/load-nyc-config": { "argparse": "1.0.10", "js-yaml": "3.14.1", "camelcase": "5.3.1" }, "hash-base": { "readable-stream": "3.6.2" }, "eslint-plugin-react": { "resolve/test/module_dir/zmodules/bbb": "null", "resolve/test/resolver/empty_main": "null", "resolve/test/resolver/symlinked/package": "null", "resolve/test/resolver/dot_main": "null", "resolve/test/resolver/invalid_main": "null", "resolve/test/resolver/multirepo": "0.0.0", "resolve/test/resolver/multirepo/packages/package-b": "0.0.0", "resolve/test/resolver/multirepo/packages/package-a": "0.0.0", "resolve/test/resolver/null_main": "null", "resolve/test/resolver/missing_index": "null", "resolve/test/resolver/dot_slash_main": "null", "resolve/test/resolver/missing_main": "null", "resolve/test/resolver/baz": "null", "resolve/test/resolver/browser_field": "null", "resolve/test/resolver/nested_symlinks/mylib": "0.0.0", "resolve/test/resolver/incorrect_main": "null", "resolve/test/resolver/false_main": "null", "resolve": "2.0.0-next.5", "semver": "6.3.1" }, "archiver-utils": { "minimatch": "5.1.6", "glob": "8.1.0", "readable-stream": "3.6.2" }, "babel-loader": { "pkg-dir": "7.0.0", "path-exists": "5.0.0", "p-locate": "6.0.0", "p-limit": "4.0.0", "find-up": "6.3.0", "find-cache-dir": "4.0.0", "yocto-queue": "1.0.0", "locate-path": "7.2.0" }, "send": { "mime": "1.6.0", "ms": "2.0.0", "debug": "2.6.9" }, "safe-regex": { "ret": "0.1.15" }, "finalhandler": { "ms": "2.0.0", "debug": "2.6.9" }, "serve-index": { "ms": "2.0.0", "depd": "1.1.2", "inherits": "2.0.3", "http-errors": "1.6.3", "statuses": "1.5.0", "debug": "2.6.9", "setprototypeof": "1.1.0" }, "html-minifier-terser": { "commander": "8.3.0" }, "rc": { "strip-json-comments": "2.0.1" }, "cli-cursor": { "restore-cursor": "3.1.0" }, "tar": { "minipass": "5.0.0", "fs-minipass": "2.1.0", "mkdirp": "1.0.4" }, "pino-abstract-transport": { "readable-stream": "4.4.2" }, "portfinder": { "async": "2.6.4", "debug": "3.2.7" }, "http-proxy": { "eventemitter3": "4.0.7" }, "tough-cookie": { "universalify": "0.2.0" }, "@graphql-tools/prisma-loader": { "https-proxy-agent": "6.2.1", "http-proxy-agent": "6.1.1" }, "@graphql-tools/executor-legacy-ws": { "isomorphic-ws": "5.0.0", "ws": "8.13.0" }, "@graphql-tools/executor-graphql-ws": { "isomorphic-ws": "5.0.0", "ws": "8.13.0", "@repeaterjs/repeater": "3.0.4", "@repeaterjs/repeater/cjs": "null" }, "@graphql-tools/merge": { "@graphql-tools/utils": "10.0.11", "@graphql-tools/utils/cjs": "null" }, "@graphql-tools/delegate": { "@graphql-tools/executor": "0.0.20", "@graphql-tools/executor/cjs": "null" }, "@graphql-tools/schema": { "@graphql-tools/merge": "8.4.2", "@graphql-tools/merge/cjs": "null" }, "@graphql-tools/executor": { "@graphql-tools/utils": "10.0.11", "@graphql-tools/utils/cjs": "null" }, "readdir-glob": { "minimatch": "5.1.6" }, "string-width-cjs": { "emoji-regex": "8.0.0" }, "ajv": { "json-schema-traverse": "0.4.1" }, "base": { "define-property": "1.0.0", "pascalcase": "0.1.1" }, "open": { "is-wsl": "2.2.0" }, "readable-stream": { "string_decoder": "1.1.1", "safe-buffer": "5.1.2", "isarray": "1.0.0" }, "locate-path": { "path-exists": "3.0.0" }, "snapdragon-util": { "kind-of": "3.2.2" }, "lodash-decorators": { "tslib/test/validateModuleExportsMatchCommonJS": "null", "tslib": "1.14.1", "tslib/modules": "null" }, "jest-watch-typeahead": { "strip-ansi": "7.1.0", "type-fest": "3.13.1", "char-regex": "2.0.1", "string-length": "5.0.1", "chalk": "5.3.0", "ansi-regex": "6.0.1", "slash": "5.1.0", "ansi-escapes": "6.2.0" }, "debug": { "ms": "2.1.2" }, "nanomatch": { "is-extendable": "1.0.1", "extend-shallow": "3.0.2", "define-property": "2.0.2" } } } ``` </details> If you want to know why a dependency is being nested, use `yarn why` in CRWA: ```bash yarn create redwood-app nested_modules -y cd nested_modules # Why is `source-map` nested in `@redwoodjs/internal`? 🤔 yarn why source-map ├─ @pmmmwh/react-refresh-webpack-plugin@npm:0.5.11 │ └─ source-map@npm:0.7.4 (via npm:^0.7.3) │ ├─ @pmmmwh/react-refresh-webpack-plugin@npm:0.5.11 [5af1a] │ └─ source-map@npm:0.7.4 (via npm:^0.7.3) │ ├─ @redwoodjs/internal@npm:6.5.1 │ └─ source-map@npm:0.7.4 (via npm:0.7.4) │ ├─ clean-css@npm:5.3.3 │ └─ source-map@npm:0.6.1 (via npm:~0.6.0) │ ├─ escodegen@npm:2.1.0 │ └─ source-map@npm:0.6.1 (via npm:~0.6.1) │ ├─ get-source@npm:2.0.12 │ └─ source-map@npm:0.6.1 (via npm:^0.6.1) │ ├─ istanbul-lib-source-maps@npm:4.0.1 │ └─ source-map@npm:0.6.1 (via npm:^0.6.1) │ ├─ resolve-url-loader@npm:5.0.0 │ └─ source-map@npm:0.6.1 (via npm:0.6.1) │ ├─ snapdragon@npm:0.8.2 │ └─ source-map@npm:0.5.7 (via npm:^0.5.6) │ ├─ source-map-support@npm:0.5.13 │ └─ source-map@npm:0.6.1 (via npm:^0.6.0) │ ├─ source-map-support@npm:0.5.21 │ └─ source-map@npm:0.6.1 (via npm:^0.6.0) │ ├─ terser-webpack-plugin@npm:1.4.5 │ └─ source-map@npm:0.6.1 (via npm:^0.6.1) │ ├─ terser-webpack-plugin@npm:1.4.5 [04527] │ └─ source-map@npm:0.6.1 (via npm:^0.6.1) │ ├─ terser@npm:4.8.1 │ └─ source-map@npm:0.6.1 (via npm:~0.6.1) │ ├─ webpack-sources@npm:1.4.3 │ └─ source-map@npm:0.6.1 (via npm:~0.6.1) │ └─ webpack-sources@npm:2.3.1 └─ source-map@npm:0.6.1 (via npm:^0.6.1) # Oh, it's because most of CRWA's dependencies are asking for `0.6.1` # while `@redwoodjs/internal` is asking for `0.7.4`. That's probably because we use renovate to upgrade # all our dependencies while (I'm guessing) a lot of these packages aren't releasing new versions anymore. ``` I paired with @Josh-Walker-GM and we worked on a basic visualization: ![image](https://github.com/redwoodjs/redwood/assets/32992335/0cc222a5-24f3-4186-8cf0-20baf50ade72) Once we have a second version under our belt, we'll work on a diff visualization so that it's easier to detect anomalies.
1 parent ae3a356 commit 7623ba6

File tree

5 files changed

+1292
-0
lines changed

5 files changed

+1292
-0
lines changed

tasks/nmHoisting/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Nested `node_modules`
2+
3+
When packages request different versions of the same dependency, Yarn tries to
4+
make both packages happy by installing two copies of that dependency. With a
5+
little bit of hand-waving here, for the `node-modules` linker, since dependencies
6+
have to exist at a single place on disk, Yarn has to decide which one gets
7+
hoisted to the top level (i.e. `node_modules/dependency`) and which one has to
8+
be nested
9+
(`node_modules/package-requesting-different-version/node_modules/dependency`).
10+
As far as I can tell, it does this in a more or less sensible way by hoisting
11+
the version that's requested the most.
12+
13+
But when our own `@redwoodjs` packages become nested, it's usually a (painful)
14+
problem for us and for our users. This script identifies which of CRWA's
15+
dependencies have a lot of nested dependencies:
16+
17+
```bash
18+
yarn node ./tasks/nmHoisting/nmHoisting.mjs
19+
```
20+
21+
Here's a snippet of the nested dependencies for the `@redwoodjs` packages as of
22+
version `v6.5.1`:
23+
24+
```json5
25+
{
26+
"hoistedNodeModules": {
27+
// ...
28+
"@redwoodjs/internal": {
29+
"source-map": "0.7.4"
30+
},
31+
"@redwoodjs/cli": {
32+
"decamelize": "5.0.1"
33+
},
34+
"@redwoodjs/testing": {
35+
"@types/node": "18.18.9"
36+
},
37+
"@redwoodjs/api": {
38+
"@whatwg-node/fetch": "0.9.14"
39+
},
40+
"@redwoodjs/telemetry": {
41+
"@whatwg-node/fetch": "0.9.14"
42+
},
43+
"@redwoodjs/prerender": {
44+
"@whatwg-node/fetch": "0.9.14"
45+
},
46+
"@redwoodjs/structure": {
47+
"lru-cache": "7.18.3"
48+
},
49+
"@redwoodjs/graphql-server": {
50+
"@graphql-tools/utils": "10.0.11",
51+
"@graphql-tools/utils/cjs": "null",
52+
"@graphql-tools/schema": "10.0.2",
53+
"@graphql-tools/schema/cjs": "null"
54+
},
55+
// ...
56+
}
57+
}
58+
```
59+
60+
You can also see a visualization by opening [nmHoistingVisualize.html](./nmHoistingVisualize.html):

tasks/nmHoisting/nmHoisting.mjs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env node
2+
/* eslint-env node */
3+
// @ts-check
4+
5+
import { cd, fs, os, path, within, $ } from 'zx'
6+
7+
async function main() {
8+
const TMP_DIR = os.tmpdir()
9+
const TIMESTAMP = (await $`date +%Y%m%d_%H%M%S`).stdout.trim()
10+
let CRWA_DIR = path.join(`crwa_${TIMESTAMP}`)
11+
12+
const projectProvided = !!process.argv[2]
13+
14+
if (projectProvided) {
15+
CRWA_DIR = process.argv[2]
16+
}
17+
18+
let data = {
19+
version: '',
20+
node_modules: [],
21+
}
22+
23+
await within(async () => {
24+
cd(TMP_DIR)
25+
26+
if (!projectProvided) {
27+
await $`yarn create redwood-app ${CRWA_DIR} -y`
28+
}
29+
30+
cd(CRWA_DIR)
31+
32+
data.version = (
33+
await $`jq -r '.devDependencies."@redwoodjs/core"' < package.json`
34+
).stdout.trim()
35+
36+
let stdout = (
37+
await $`find ./node_modules -mindepth 2 -maxdepth 3 -type d -name node_modules`
38+
).stdout
39+
.trim()
40+
.split('\n')
41+
42+
data.node_modules = await batchLines(stdout)
43+
})
44+
45+
const dataS = `\
46+
47+
rawData.push(\`${JSON.stringify(data, null, 2)}\`)
48+
`
49+
50+
await fs.appendFile(new URL(`nmHoistingData.js`, import.meta.url), dataS)
51+
}
52+
53+
main()
54+
55+
// ------------------------
56+
// Helpers
57+
// ------------------------
58+
59+
function batchLines(lines) {
60+
return lines.reduce(async (objP, line) => {
61+
const obj = await objP
62+
63+
let depLines = (await $`find ${line} -name 'package.json'`).stdout
64+
.trim()
65+
.split('\n')
66+
67+
const name = line.match(
68+
/^\.\/node_modules\/(?<package>.+)\/node_modules$/
69+
)[1]
70+
obj[name] = await batchDepLines(depLines)
71+
72+
return obj
73+
}, Promise.resolve({}))
74+
}
75+
76+
function batchDepLines(depLines) {
77+
return depLines.reduce(async (objP, depLine) => {
78+
const obj = await objP
79+
80+
const version = (await $`cat ${depLine} | jq -r .version`).stdout.trim()
81+
82+
const name = depLine.match(
83+
/[^\.]+\/node_modules\/(?<package>.+)\/package.json$/
84+
)[1]
85+
obj[name] = version
86+
87+
return obj
88+
}, Promise.resolve({}))
89+
}

0 commit comments

Comments
 (0)