-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add parcel-link and parcel-unlink dev CLIs (#8618)
* Link script * Lint * Scaffold atlassian-parcel-link package * Make link and unlink executable * Move binaries from src/ to bin/ * Parse args and scaffold logging * Factor in original link implementation * Extract mapAtlassianPackageAliases util * Use log, not console.log * Implement unlink * Export link and unlink from module I guess in case they would be useful in another script... * Allow configuration of the packageRoot for linking * Force install after unlink * Parametrize the namespace and node_modules globs This gets us a step closer to a more generic solution that can be published publicly by replacing all explicit references to the "@atlassian" namespace. * Improve namespaced config rewrites This should both expand to capture any entries in the root package.json that configure a namespaced package while also avoiding rewriting dependencies. * Add namespace and nodeModulesGlobs options This makes it so that the default behavior of link/unlink works for @parcel packages in any standard Parcel project, but allow configuring custom package namespace (e.g., for forks of Parcel) and custom node_modules locations for more complex setups. * Remove references to atlassian from parcel-link * Update README * Fix multi option parsing * Fix unlink arguments * Unify CLI and create submcommands `link` is the default subcommand and can be omitted, so `parcel-link [packageRoot]` still works. Now, unlinking is done via subcommand: `parcel-link unlink` * Lint/nits * Extract ParcelLinkConfig * Extract command to factory * Interface with @parcel/fs * Fix default command * Make command configurable * [WIP] tests * Throw instead of exit * Improve app root detection * toJSON not toJson * Validate fs operations before performing them This is really meant to avoid logging actions that actually sliently fail, like trying to remove a file that doesn't exist. * Add createFS test util * Improve logged messages * Naming nit * Add descriptive error messages * Rename parcel-link util to utils utils is the convention in the monorepo * Lint * Use globSync from @parcel/utils More testable * Use `withFileTypes` readdir option * Use CopyOnWriteToMemoryFS in tests * Use OverlayFS in tests * Reverse direction of symlink message * Add tests for link with default and common options * Add tests, fixes for linking with a custom namespace * Add tests, fixes for custom node_modules globs * Remove old unlink options * Fix link --dry-run * Add unlink tests * Use fsFixture in parcel-link tests * Fix missing bin link for namespaced links * Update version * lint * Fix package versions * Fix parcel-link tests * Extract link and unlink commands * Update @babel/core dep * Update readme * Fix parcel-link tests * skip tests failing on windows these tests are for the '--namespace' feature, which is is only useful if you're testing a fork of Parcel, so seems safe enough to skip. * Fix package versions --------- Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
- Loading branch information
Showing
10 changed files
with
1,339 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# `parcel-link` | ||
|
||
A CLI for linking a dev version of Parcel into a project. | ||
|
||
## Installation | ||
|
||
Clone and run `yarn`, then `cd packages/dev/parcel-link && yarn link` | ||
to make the `parcel-link` binary globally available. | ||
|
||
## Usage | ||
|
||
In an Parcel project root: | ||
|
||
```sh | ||
$ parcel-link [options] [packageRoot] | ||
``` | ||
|
||
### Specifying `packageRoot` | ||
|
||
```sh | ||
$ parcel-link /path/to/parcel/packages | ||
``` | ||
|
||
By default, `parcel-link` will link to packages in the same | ||
location where `parcel-link` is found. But it is common | ||
to want to link other worktrees of Parcel, and it's not fun | ||
to have to first re-link `parcel-link` to a new location. | ||
|
||
For this reason, `parcel-link` accepts a `packageRoot` argument, | ||
which specifies a path to a Parcel `packages` directory. | ||
Links will then be made to packages in that location instead | ||
of the default. | ||
|
||
### Specifying a `namespace` | ||
|
||
```sh | ||
$ parcel-link --namespace @my-parcel-fork | ||
``` | ||
|
||
When linking into a project that uses a fork of Parcel, | ||
the published packages may have a different namespace from | ||
Parcel, so `parcel-link` allows specifying a namespace. | ||
|
||
If defined to someting other than `"@parcel"`, | ||
`parcel-link` will do some extra work to adjust | ||
namespaced packages to reference linked packages instead. | ||
|
||
### Linking into a monorepo | ||
|
||
```sh | ||
$ parcel-link --node-modules-globs build-tools/*/node_modules build-tools/parcel/*/node_modules | ||
``` | ||
|
||
In a monorepo, there may be multiple locations where | ||
Parcel packages are installed. For this, `parcel-link` | ||
allows specifying globs of locations where packages should be linked. | ||
|
||
Note that specifying any value here will override the default of `node_modules`, | ||
so if you want to preserve the default behavior, be sure to include `node_modules` | ||
in the list of globs: | ||
|
||
```sh | ||
$ parcel-link -g build-tools/*/node_modules -g build-tools/parcel/*/node_modules -g node_modules | ||
``` | ||
|
||
## Cleanup | ||
|
||
To restore the project to its default Parcel install: | ||
|
||
```sh | ||
$ parcel-link unlink [options] [packageRoot] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#! /usr/bin/env node | ||
|
||
// @flow strict-local | ||
/* eslint-disable no-console */ | ||
|
||
'use strict'; | ||
|
||
// $FlowFixMe[untyped-import] | ||
require('@parcel/babel-register'); | ||
|
||
let program = require('./src/cli').createProgram(); | ||
|
||
(async function main() { | ||
try { | ||
await program.parseAsync(); | ||
} catch (e) { | ||
console.error(e); | ||
process.exit(1); | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "@parcel/link", | ||
"description": "A CLI for linking a dev version of Parcel into a project", | ||
"version": "2.10.2", | ||
"private": true, | ||
"bin": { | ||
"parcel-link": "bin.js" | ||
}, | ||
"scripts": { | ||
"test": "cd ../../.. && yarn test:integration --grep @parcel/link" | ||
}, | ||
"main": "src/index.js", | ||
"dependencies": { | ||
"@babel/core": "^7.22.11", | ||
"@parcel/babel-register": "2.10.2", | ||
"@parcel/fs": "2.10.2", | ||
"@parcel/utils": "2.10.2", | ||
"commander": "^7.0.0", | ||
"nullthrows": "^1.1.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// @flow | ||
|
||
import type {FileSystem} from '@parcel/fs'; | ||
|
||
import {globSync} from '@parcel/utils'; | ||
|
||
import assert from 'assert'; | ||
import nullthrows from 'nullthrows'; | ||
import path from 'path'; | ||
|
||
const LOCK_FILE_NAMES = ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']; | ||
const SCM_FILE_NAMES = ['.git', '.hg']; | ||
|
||
export class ParcelLinkConfig { | ||
fs: FileSystem; | ||
appRoot: string; | ||
packageRoot: string; | ||
namespace: string = '@parcel'; | ||
nodeModulesGlobs: string[] = ['node_modules']; | ||
filename: string = '.parcel-link'; | ||
|
||
static load( | ||
appRoot: string, | ||
{fs, filename = '.parcel-link'}: {|fs: FileSystem, filename?: string|}, | ||
): ParcelLinkConfig { | ||
let manifest = JSON.parse( | ||
fs.readFileSync(path.join(appRoot, filename), 'utf8'), | ||
); | ||
return new ParcelLinkConfig({...manifest, fs}); | ||
} | ||
|
||
constructor(options: {| | ||
fs: FileSystem, | ||
appRoot: string, | ||
packageRoot: string, | ||
namespace?: string, | ||
nodeModulesGlobs?: string[], | ||
filename?: string, | ||
|}) { | ||
this.fs = nullthrows(options.fs, 'fs is required'); | ||
this.appRoot = nullthrows(options.appRoot, 'appRoot is required'); | ||
this.packageRoot = nullthrows( | ||
options.packageRoot, | ||
'packageRoot is required', | ||
); | ||
this.namespace = options.namespace ?? this.namespace; | ||
this.nodeModulesGlobs = options.nodeModulesGlobs ?? this.nodeModulesGlobs; | ||
this.filename = options.filename ?? this.filename; | ||
} | ||
|
||
save(): Promise<void> { | ||
return this.fs.writeFile( | ||
path.join(this.appRoot, this.filename), | ||
JSON.stringify(this, null, 2), | ||
); | ||
} | ||
|
||
delete(): Promise<void> { | ||
return this.fs.rimraf(path.join(this.appRoot, this.filename)); | ||
} | ||
|
||
validateAppRoot() { | ||
assert( | ||
[...LOCK_FILE_NAMES, ...SCM_FILE_NAMES].some(filename => | ||
this.fs.existsSync(path.join(this.appRoot, filename)), | ||
), | ||
`Not a project root: '${this.appRoot}'`, | ||
); | ||
} | ||
|
||
validatePackageRoot() { | ||
assert( | ||
this.fs.existsSync(path.join(this.packageRoot, 'core/core')), | ||
`Not a package root: '${this.packageRoot}'`, | ||
); | ||
} | ||
|
||
validate(): void { | ||
this.validateAppRoot(); | ||
this.validatePackageRoot(); | ||
} | ||
|
||
getNodeModulesPaths(): string[] { | ||
return this.nodeModulesGlobs.reduce( | ||
(matches, pattern) => [ | ||
...matches, | ||
...globSync(pattern, this.fs, {cwd: this.appRoot, onlyFiles: false}), | ||
], | ||
[], | ||
); | ||
} | ||
|
||
toJSON(): {| | ||
appRoot: string, | ||
packageRoot: string, | ||
namespace: string, | ||
nodeModulesGlobs: string[], | ||
|} { | ||
return { | ||
appRoot: this.appRoot, | ||
packageRoot: this.packageRoot, | ||
namespace: this.namespace, | ||
nodeModulesGlobs: this.nodeModulesGlobs, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// @flow strict-local | ||
/* eslint-disable no-console */ | ||
|
||
import type {LinkCommandOptions} from './link'; | ||
import type {UnlinkCommandOptions} from './unlink'; | ||
|
||
// $FlowFixMe[untyped-import] | ||
import {version} from '../package.json'; | ||
import {createLinkCommand} from './link'; | ||
import {createUnlinkCommand} from './unlink'; | ||
|
||
import commander from 'commander'; | ||
|
||
export type ProgramOptions = {|...LinkCommandOptions, ...UnlinkCommandOptions|}; | ||
|
||
// $FlowFixMe[invalid-exported-annotation] | ||
export function createProgram(opts?: ProgramOptions): commander.Command { | ||
let {fs, log = console.log, link, unlink} = opts ?? {}; | ||
return new commander.Command() | ||
.version(version, '-V, --version') | ||
.description('A tool for linking a dev copy of Parcel into an app') | ||
.addHelpText('after', `\nThe link command is the default command.`) | ||
.addCommand(createLinkCommand({fs, log, link}), {isDefault: true}) | ||
.addCommand(createUnlinkCommand({fs, log, unlink})); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// @flow strict-local | ||
|
||
export type {ProgramOptions} from './cli'; | ||
export type {LinkOptions} from './link'; | ||
export type {UnlinkOptions} from './unlink'; | ||
|
||
export {createProgram} from './cli'; | ||
export {link} from './link'; | ||
export {unlink} from './unlink'; | ||
export {ParcelLinkConfig} from './ParcelLinkConfig'; |
Oops, something went wrong.