Skip to content

Commit

Permalink
Merge branch 'v2' into jlane2/chunk-large-blogs-to-disk
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeLane authored Nov 3, 2023
2 parents c5efb53 + 506e762 commit a10370f
Show file tree
Hide file tree
Showing 10 changed files with 1,339 additions and 0 deletions.
555 changes: 555 additions & 0 deletions packages/core/integration-tests/test/parcel-link.js

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions packages/dev/parcel-link/README.md
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]
```
20 changes: 20 additions & 0 deletions packages/dev/parcel-link/bin.js
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);
}
})();
21 changes: 21 additions & 0 deletions packages/dev/parcel-link/package.json
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"
}
}
106 changes: 106 additions & 0 deletions packages/dev/parcel-link/src/ParcelLinkConfig.js
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,
};
}
}
25 changes: 25 additions & 0 deletions packages/dev/parcel-link/src/cli.js
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}));
}
10 changes: 10 additions & 0 deletions packages/dev/parcel-link/src/index.js
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';
Loading

0 comments on commit a10370f

Please sign in to comment.