{"id":13532467,"url":"https://github.com/FormidableLabs/lank","last_synced_at":"2025-04-01T20:32:12.640Z","repository":{"id":18706854,"uuid":"85118612","full_name":"FormidableLabs/lank","owner":"FormidableLabs","description":"Link and control a bunch of repositories.","archived":true,"fork":false,"pushed_at":"2022-03-03T22:32:13.000Z","size":266,"stargazers_count":49,"open_issues_count":10,"forks_count":2,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-10T07:36:07.452Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FormidableLabs.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-15T20:38:37.000Z","updated_at":"2023-01-28T02:38:06.000Z","dependencies_parsed_at":"2022-08-07T09:00:53.984Z","dependency_job_id":null,"html_url":"https://github.com/FormidableLabs/lank","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FormidableLabs%2Flank","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FormidableLabs%2Flank/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FormidableLabs%2Flank/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FormidableLabs%2Flank/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FormidableLabs","download_url":"https://codeload.github.com/FormidableLabs/lank/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246709923,"owners_count":20821297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T07:01:11.166Z","updated_at":"2025-04-01T20:32:07.626Z","avatar_url":"https://github.com/FormidableLabs.png","language":"JavaScript","readme":"[![Travis Status][trav_img]][trav_site]\n[![Maintenance Status][maintenance-image]](#maintenance-status)\n\n\nlank\n====\n\nWorking on multiple repositories that depend on each other can be a real pain.\n`npm link`-ing often has limitations. Watching and copying files from one\nproject to another has lag and is often wonky.\n\nLank enters this space with a simple proposition: **do nothing**.\n\nMore specifically:\n\n1. Take a nested, interrelated dependency tree and delete any projects that you\n   would like to simultaneously edit from `node_modules` in each project.\n2. Add `NODE_PATH` enhancements to allow\n   [Node.js `require` resolution](https://nodejs.org/api/modules.html) to\n   resolve to the actual checked out projects _instead_ of installed\n   `node_modules` dependencies.\n  \n\n## Installation\n\nInstall `lank` globally:\n\n```sh\n$ npm install -g lank\n$ yarn global add lank\n```\n\n## Configuration\n\nThe first basic step with `lank` is \"linking\" several projects together. This\nreally means:\n\n1. `lank` has some means of knowing _which_ projects are connected --\n   accomplished by a `.lankrc.js` file.\n2. `lank` then provides helper tasks to delete linked projects from withing\n   each project's `node_modules` dependency tree.\n3. Things are now set up file-wise. The final aspect is that `lank` provides\n   execution helpers to set up `NODE_PATH` for proper inter-project resolution\n   while invoking (1) multi / \"all project\" tasks and (2) central \"project under\n   development\" tasks. This last step is optional as a user can just mutate\n   `NODE_PATH` on their own and run manual steps.\n\n### Configuration File\n\nLet's start with the first step - configuring project linking. To link projects,\nyou needs to create a `.lankrc.js` file. For an example set up of:\n\n```sh\nPROJECTS/\n  one/\n  two/\n  three/\n```\n\nSay that you wish to run tasks from `PROJECTS/one`. You can create a\nconfiguration file in either the controlling project directory, or one level\nabove, so:\n\nIf you would like to link the projects and run tasks from `PROJECTS`, then you\nshould create a:\n\n```sh\nPROJECTS/\n  one/.lankrc.js\n```\n\n_or_\n\n```sh\nPROJECTS/.lankrc.js\n```\n\nOn initialization, `lank` will look for a `.lankrc.js` in the current working\ndirectory. If there is none, then `lank` will secondarily look up one directory\nif it finds a `.lankrc.js` file. In either case, the project directories for all\nlinked files will be checked for presence, otherwise `lank` will throw a\nconfiguration error.\n\n#### Simple Version\n\nThe `.lankrc.js` file is simply an array of strings where each string\ncorresponds to the published `package.json:name` of a package _and_ is the name\nof a directory at the same level as all other project directories controlled by\n`lank`. For example:\n\n```js\n// `.lankrc.json`\n[\n  \"one\",\n  \"two\",\n  \"three\"\n]\n\n// `.lankrc.js`\nmodule.exports = [\n  \"one\",\n  \"two\",\n  \"three\"\n];\n```\n\n#### Advanced Version\n\nThe `.lankrc.js` file can also be an object supporting the following fields:\n\n- `module`: The string name of the module at issue.\n- `tags`: A string for single tag or array of strings for multiple tags. Tags\n  are used to filter multi-project commands.\n\n`lank` supports two formats for advanced configuration objects - a longhand one:\n\n```js\n// `.lankrc.js`\nmodule.exports = [\n  { module: \"one\" },\n  { module: \"two\", tags: \"foo\" },           // tags can be strings ...\n  { module: \"three\", tags: [\"foo\", \"bar\"] } // ... or an array of strings\n];\n```\n\nand a shorthand object form:\n\n```js\n// `.lankrc.js`\nmodule.exports = {\n  one: {},\n  two: { tags: \"foo\" },\n  three: { tags: [\"foo\", \"bar\"] }\n};\n```\n\n#### Scoped Modules\n\nIf your project has a scoped published name like `@org/foo`, then your directory\nstructure on disk must reflect that as well. So our example above may look\nsomething instead like:\n\n```sh\nPROJECTS/\n  @org/\n    one/\n    two/\n    three/\n```\n\nwith a `.lankrc.js` file of:\n\n```js\nmodule.exports = [\n  \"@org/one\",\n  \"@org/two\",\n  \"@org/three\"\n];\n```\n\nUnfortunately, the directory path of the actual module name is required, `@`\nsymbol and all.\n\nAlso note that for `.lankrc.js` resolution, the rules would be something like:\n\n```sh\n# If we're in `one`, look in CWD first.\nPROJECTS/@org/one/.lankrc.js\n\n# If not, look **two** levels down since we're in a scoped project.\nPROJECTS/.lankrc.js\n```\n\nThis scheme allows you to link both scoped and non-scoped projects in the same\ndirectory structure.\n\n## Running a Project\n\nWorkflows with `lank` circle around a root \"controlling\" project that you will\nchange directory into and run:\n\n```sh\n$ cd /PATH/TO/PROJECTS/one\n$ lank \u003ccommands\u003e\n```\n\n`lank` will base working directory paths for all linked projects off this\nassumption, and it will error out if it does not find a directory structure\nthat matches the configuration files.\n\n**Side Note -- What is a \"controlling\" project?**: We use `lank` to control\n2+ interrelated projects that need simultaneous changes. The controlling project\nis usually the \"most upstream\" one that is not depended on by any other project.\nFor example, this would likely be your application that then \"controls\" many\nlinked library projects. Ultimately, it doesn't really matter that much as long\nas the `lank` commands are executed from within the root project directory of\n_any_ linked project.\n\nAs we learned above, that project must have a `.lankrc` file in one of:\n\n```sh\n/PATH/TO/PROJECTS/one/.lankrc.js\n/PATH/TO/PROJECTS/.lankrc.js\n```\n\nSo now, turning to the projects at hand, the core issue is cross-project\ndependencies. In our example, they might look like this:\n\n```\none/\n  node_modules/\n    two\n      node_modules/\n        three           # this dependency may be flattened to higher level\n    three\ntwo/\n  node_modules/\n    three\n```\n\nSo, in this case `three` has no dependencies on the other projects, `two`\ndepends on `three`, and `one` (our control project) depends on at least `two`\nand `three` and possibly a _second_ `three` as a transitive dependency of `two`.\nYowza.\n\n### Linking Projects\n\nSo how do we \"link\" these projects together such that we can make a change in\n`three` and see it reflected live in `one` and `two`? There are a couple of\napproaches in the current ecosystem:\n\n1. `npm link` the changed projects to each other.\n2. Manually watch and recursively copy files from one project to another.\n3. Manually `npm pack` and `npm install` packed tarballs on each change.\n\nAll of these approaches have drawbacks of some sort -- either relying on\nsymlinks, file watching / delayed copying, and/or manual steps. This adds up\nto incorrect behavior or irksome developer workflows when changing things across\nmultiple project simultaneously.\n\n`lank` takes a very different approach to this problem:\n\n1. Find and delete all of the `npm install`-ed cross-project dependencies from\n   each project's `node_modules`\n2. Provide an execution wrapper that enhances the process environment such that\n   the Node dependencies resolve to the \"linked\" live projects.\n\nWith this scheme in place, changes across projects are _instantaneous_ because\nthe real code in the linked project is used at the real path -- no symlinks or\nfile copying!\n\nWith that long introduction in place, we introduce `lank link`. What this\ncommand basically does is find and delete all of the cross-project dependencies.\nSo, in our example, we first do a dry run to show what we would delete without\nactually deleting:\n\n```sh\n$ lank link --dry-run\n[lank:link]              Found 4 directories to delete:\n[lank:link]              - /PATH/TO/PROJECTS/one/node_modules/two\n[lank:link]              - /PATH/TO/PROJECTS/one/node_modules/two/node_modules/three\n[lank:link]              - /PATH/TO/PROJECTS/one/node_modules/three\n[lank:link]              - /PATH/TO/PROJECTS/two/node_modules/three\n```\n\n`lank` has traversed the dependency trees in all linked projects and found the\ncross-dependencies. We then run `lank link` to actually perform the deletion.\n\nIf you want to _undo_ the linking, simply reinstall all projects' dependencies\nwith:\n\n```sh\n# Concurrent yarn installs are finicky, so install one at a time in serial.\n# Also \"force\" the install as manually deleted packages from `lank` won't be\n# necessarily reinstalled on a vanilla `yarn install` alone.\n$ lank exec -s -- yarn install --force\n# ... OR ...\n$ lank exec -s -- npm install\n```\n\n### Shell Commands\n\nOnce you have `lank link`-ed a project, all projects effectively have \"holes\"\nfor cross-dependencies. We can use this to have the linked projects resolve to\neach other in source by running any commands in one project with an environment\nvariables `NODE_PATH` that includes the value `..` which means \"look one\ndirectory below CWD to find additional dependencies\". (See our section on Node\n`require` resolution for a further explanation of this.)\n\nTo help with this environment enhancement and for similar multi-repository\nworkflows, `lank` provides the `exec` command, which runs the same command in\nall linked projects:\n\n```sh\n$ lank exec -- SHELL_COMMAND\n```\n\nHere are some basic examples:\n\n```sh\n# Print CWD\n$ lank exec -- pwd\n\n# Git status\n$ lank exec -- git status\n\n# Install deps\n$ lank exec -s -- yarn install --force\n$ lank exec -s -- npm install\n```\n\nSometimes, you only want to exec a command in some projects. This is where the\n`-t, --tags \u003ctags\u003e` flag comes in handy to run based on arbitrary tags and the\n`-m, --modules \u003cmodules\u003e` flag is useful to limit to a list of named projects.\n\n```sh\n# Exec in projects configured with a (1) \"foo\" tag, (2) a \"foo\" or \"bar\" tag.\n$ lank exec -t foo -- pwd\n$ lank exec -t foo,bar -- pwd\n\n# Exec in specifically named projects\n$ lank exec -m one -- pwd\n$ lank exec -m one,two -- pwd\n```\n\n#### Output Buffering\n\nBy default, `lank` buffers all output and displays it once the underyling\nprocesses end. This is nice for processes that _do_ end since you don't get\nrandom process output crossing streams in your terminal during execution. But,\nthis scheme doesn't really work well for `exec`'s that are meant to be\nlong-lived or persistent processes, such as a file build watch.\n\nIn these cases, use the `-u` / `--unbuffered` flag to just have output\nsplattered to stdout/stderr as it happens, with some helpful prefixes to\nindicate which project the output came from. For example:\n\n```sh\n$ lank exec -u -- npm run watch-files\n```\n\n### Keeping Package Dependencies in Sync\n\nMultiple repositories generally ends up with dependency skews across projects.\n`lank` provides a very convenient manner of harmonizing dependencies across all\nlinked projects with:\n\n```sh\n$ lank deps -d  # Check with a dry run first.\n$ lank deps\n```\n\n`lank` uses a simplistic algorithm of:\n\n1. Only looking at deps that are of the forms `1.2.3`, `~1.2.3`, and `^1.2.3`\n2. If 2+ different versions exist in a `package.json`, `lank` chooses the latest\n   or highest version string to win.\n\n`lank` then writes out updates to actual project `package.json` files where\napplicable.\n\nFor a usual workflow, you'll want to update deps in linked projects, re-install\ndependencies (and new ones), then re-link the projects. Something like:\n\n```sh\n# Update\n$ lank deps\n\n# Reinstall\n$ lank exec -s -- yarn install --force\n$ lank exec -s -- npm install\n\n# Re-link\n$ lank link\n```\n\n## Notes, Tips, and Tricks\n\n### Miscellaneous\n\n* The name of the Node modules must correspond to the directory name of the\n  project on disk. For example, if you are linking the `foo` project normally\n  found in `node_modules/foo`, it now _must_ be named `foo` on the local\n  file system relative to the directories that `lank` controls.\n\n### Node `require` Resolution\n\n`lank` depends on the actual details of Node's `require` resolution, which is\na bit complicated and described in full detail at:\n[https://nodejs.org/api/modules.html](https://nodejs.org/api/modules.html)\n\nHere's an abbreviated example for us:\n\n```js\n# two/index.js\nmodule.exports = require(\"three\");\n```\n\n```js\n# one/index.js\nconst two = require(\"two\");\n\ntwo(\"the transitive THREE command!\");\n```\n\nThe resolution of `three` from `two` has a large set of paths to traverse until\nit finds a match.\n\n```sh\n# First, try `${dirname}/node_modules` and up file system\n/home/user/projects/one/node_modules/two/node_modules/three\n/home/user/projects/one/node_modules/three\n/home/user/projects/node_modules/three\n/home/user/node_modules/three\n/home/node_modules/three\n/node_modules/three\n\n# Now, look to `NODE_PATH`\n${NODE_PATH}/three\n\n# Finally, the global installs\n${HOME}/.node_modules/three\n${HOME}/.node_libraries/three\n${PREFIX}/lib/node/three\n```\n\nWhat `lank` does in `link`-ing is to just delete the normal projects from\n`node_modules` and the in `exec` and related commands enhance the `NODE_PATH`\nvariable so that lookup up until `NODE_PATH` fails to find the cross-referenced\nproject. That then leaves `NODE_PATH` to get the live `link`-ed project instead\nand **presto!** we have first class Node `require` integration with our custom\nprojects instead of what's installed via `yarn|npm install`.\n\n### TODO(INITIAL) Additional Sections\n\n* TODO(INITIAL): Document - webpack\n* TODO(INITIAL): eslint\n\n### Maintenance Status\n\n**Archived:** This project is no longer maintained by Formidable. We are no longer responding to issues or pull requests unless they relate to security concerns. We encourage interested developers to fork this project and make it their own!\n","funding_links":[],"categories":["仓库管理工具"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFormidableLabs%2Flank","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFormidableLabs%2Flank","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFormidableLabs%2Flank/lists"}