Skip to content

Commit

Permalink
Extract link and unlink commands
Browse files Browse the repository at this point in the history
  • Loading branch information
lettertwo committed Aug 16, 2023
1 parent 2546295 commit f9843ea
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 107 deletions.
3 changes: 1 addition & 2 deletions packages/core/integration-tests/test/parcel-link.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow strict-local

import type {FileSystem} from '@parcel/fs';
import type {ProgramOptions} from '@parcel/link';

import {createProgram as _createProgram} from '@parcel/link';
Expand All @@ -11,7 +10,7 @@ import assert from 'assert';
import path from 'path';
import sinon from 'sinon';

function createProgram(opts: {|...ProgramOptions, fs: FileSystem|}) {
function createProgram(opts: ProgramOptions) {
let program = _createProgram(opts).exitOverride();

function cli(command: string = ''): Promise<void> {
Expand Down
112 changes: 11 additions & 101 deletions packages/dev/parcel-link/src/cli.js
Original file line number Diff line number Diff line change
@@ -1,115 +1,25 @@
// @flow strict-local
/* eslint-disable no-console */

import type {FileSystem} from '@parcel/fs';
import type {LinkCommandOptions} from './link';
import type {UnlinkCommandOptions} from './unlink';

// $FlowFixMe[untyped-import]
import {version} from '../package.json';
import {ParcelLinkConfig} from './ParcelLinkConfig';
import {link as linkAction} from './link';
import {unlink as unlinkAction} from './unlink';
import {NodeFS} from '@parcel/fs';
import {createLinkCommand} from './link';
import {createUnlinkCommand} from './unlink';

import commander from 'commander';
import path from 'path';

export type ProgramOptions = {|
+fs?: FileSystem,
+link?: typeof linkAction,
+unlink?: typeof unlinkAction,
|};
export type ProgramOptions = {|...LinkCommandOptions, ...UnlinkCommandOptions|};

// $FlowFixMe[invalid-exported-annotation]
export function createProgram(opts?: ProgramOptions): commander.Command {
const {
fs = new NodeFS(),
link = linkAction,
unlink = unlinkAction,
} = opts ?? {};

const program = new commander.Command();

program
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.`);

program
.command('link [packageRoot]', {isDefault: true})
.description('Link a dev copy of Parcel into an app', {
packageRoot:
'Path to the Parcel package root\nDefaults to the package root containing this package',
})
.option('-d, --dry-run', 'Do not write any changes')
.option('-n, --namespace <namespace>', 'Namespace for packages', '@parcel')
.option(
'-g, --node-modules-glob <glob>',
'Location where node_modules should be linked in the app.\nCan be repeated with multiple globs.',
(glob, globs) => globs.concat([glob.replace(/["']/g, '')]),
['node_modules'],
)
.action(async (packageRoot, options) => {
if (options.dryRun) console.log('Dry run...');
let appRoot = process.cwd();

let parcelLinkConfig;

try {
parcelLinkConfig = await ParcelLinkConfig.load(appRoot, {fs});
} catch (e) {
// boop!
}

if (parcelLinkConfig) {
throw new Error(
'A Parcel link already exists! Try `parcel-link unlink` to re-link.',
);
}

parcelLinkConfig = new ParcelLinkConfig({
fs,
appRoot,
packageRoot: packageRoot ?? path.join(__dirname, '../../../'),
namespace: options.namespace,
nodeModulesGlobs: options.nodeModulesGlob,
});

await link(parcelLinkConfig, {dryRun: options.dryRun, log: console.log});

if (!options.dryRun) await parcelLinkConfig.save();

console.log('🎉 Linking successful');
});

program
.command('unlink')
.description('Unlink a dev copy of Parcel into an app')
.option('-d, --dry-run', 'Do not write any changes')
.option('-f, --force-install', 'Force a reinstall after unlinking')
.action(async options => {
if (options.dryRun) console.log('Dry run...');
let appRoot = process.cwd();

let parcelLinkConfig;
try {
parcelLinkConfig = await ParcelLinkConfig.load(appRoot, {fs});
} catch (e) {
// boop!
}

if (parcelLinkConfig) {
await unlink(parcelLinkConfig, {
dryRun: options.dryRun,
forceInstall: options.forceInstall,
log: console.log,
});

if (!options.dryRun) await parcelLinkConfig.delete();
} else {
throw new Error('A Parcel link could not be found!');
}

console.log('🎉 Unlinking successful');
});

return program;
.addHelpText('after', `\nThe link command is the default command.`)
.addCommand(createLinkCommand({fs, log, link}), {isDefault: true})
.addCommand(createUnlinkCommand({fs, log, unlink}));
}
71 changes: 69 additions & 2 deletions packages/dev/parcel-link/src/link.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @flow strict-local

import type {ParcelLinkConfig} from './ParcelLinkConfig';
import type {CmdOptions} from './utils';
import type {FileSystem} from '@parcel/fs';

import {ParcelLinkConfig} from './ParcelLinkConfig';
import {
findParcelPackages,
mapNamespacePackageAliases,
Expand All @@ -14,15 +15,25 @@ import {

import nullthrows from 'nullthrows';
import path from 'path';
import {NodeFS} from '@parcel/fs';
import commander from 'commander';

export type LinkOptions = {|
dryRun?: boolean,
log?: (...data: mixed[]) => void,
|};

export type LinkCommandOptions = {|
+link?: typeof link,
+fs?: FileSystem,
+log?: (...data: mixed[]) => void,
|};

const NOOP: (...data: mixed[]) => void = () => {};

export async function link(
config: ParcelLinkConfig,
{dryRun = false, log = () => {}}: LinkOptions,
{dryRun = false, log = NOOP}: LinkOptions,
): Promise<void> {
config.validate();

Expand Down Expand Up @@ -128,3 +139,59 @@ export async function link(
}
}
}

export function createLinkCommand(
opts?: LinkCommandOptions,
// $FlowFixMe[invalid-exported-annotation]
): commander.Command {
let action = opts?.link ?? link;
let log = opts?.log ?? NOOP;
let fs = opts?.fs ?? new NodeFS();

return new commander.Command('link')
.arguments('[packageRoot]')
.description('Link a dev copy of Parcel into an app', {
packageRoot:
'Path to the Parcel package root\nDefaults to the package root containing this package',
})
.option('-d, --dry-run', 'Do not write any changes')
.option('-n, --namespace <namespace>', 'Namespace for packages', '@parcel')
.option(
'-g, --node-modules-glob <glob>',
'Location where node_modules should be linked in the app.\nCan be repeated with multiple globs.',
(glob, globs) => globs.concat([glob.replace(/["']/g, '')]),
['node_modules'],
)
.action(async (packageRoot, options) => {
if (options.dryRun) log('Dry run...');
let appRoot = process.cwd();

let parcelLinkConfig;

try {
parcelLinkConfig = await ParcelLinkConfig.load(appRoot, {fs});
} catch (e) {
// boop!
}

if (parcelLinkConfig) {
throw new Error(
'A Parcel link already exists! Try `parcel-link unlink` to re-link.',
);
}

parcelLinkConfig = new ParcelLinkConfig({
fs,
appRoot,
packageRoot: packageRoot ?? path.join(__dirname, '../../../'),
namespace: options.namespace,
nodeModulesGlobs: options.nodeModulesGlob,
});

await action(parcelLinkConfig, {dryRun: options.dryRun, log});

if (!options.dryRun) await parcelLinkConfig.save();

log('🎉 Linking successful');
});
}
54 changes: 52 additions & 2 deletions packages/dev/parcel-link/src/unlink.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @flow strict-local

import type {ParcelLinkConfig} from './ParcelLinkConfig';
import type {CmdOptions} from './utils';
import type {FileSystem} from '@parcel/fs';

import {ParcelLinkConfig} from './ParcelLinkConfig';
import {
cleanupBin,
cleanupNodeModules,
Expand All @@ -13,16 +14,26 @@ import {
} from './utils';

import path from 'path';
import {NodeFS} from '@parcel/fs';
import commander from 'commander';

export type UnlinkOptions = {|
dryRun?: boolean,
forceInstall?: boolean,
log?: (...data: mixed[]) => void,
|};

export type UnlinkCommandOptions = {|
+unlink?: typeof unlink,
+fs?: FileSystem,
+log?: (...data: mixed[]) => void,
|};

const NOOP: (...data: mixed[]) => void = () => {};

export async function unlink(
config: ParcelLinkConfig,
{dryRun = false, forceInstall = false, log = () => {}}: UnlinkOptions,
{dryRun = false, forceInstall = false, log = NOOP}: UnlinkOptions,
) {
config.validate();

Expand Down Expand Up @@ -117,3 +128,42 @@ export async function unlink(
log('Run `yarn install --force` (or similar) to restore dependencies');
}
}

export function createUnlinkCommand(
opts?: UnlinkCommandOptions,
// $FlowFixMe[invalid-exported-annotation]
): commander.Command {
let action = opts?.unlink ?? unlink;
let log = opts?.log ?? NOOP;
let fs = opts?.fs ?? new NodeFS();

return new commander.Command('unlink')
.description('Unlink a dev copy of Parcel from an app')
.option('-d, --dry-run', 'Do not write any changes')
.option('-f, --force-install', 'Force a reinstall after unlinking')
.action(async options => {
if (options.dryRun) log('Dry run...');
let appRoot = process.cwd();

let parcelLinkConfig;
try {
parcelLinkConfig = await ParcelLinkConfig.load(appRoot, {fs});
} catch (e) {
// boop!
}

if (parcelLinkConfig) {
await action(parcelLinkConfig, {
dryRun: options.dryRun,
forceInstall: options.forceInstall,
log,
});

if (!options.dryRun) await parcelLinkConfig.delete();
} else {
throw new Error('A Parcel link could not be found!');
}

log('🎉 Unlinking successful');
});
}

0 comments on commit f9843ea

Please sign in to comment.