Skip to content

Commit 0100840

Browse files
Adds to & from options in link script (#1917)
1 parent c15ee2a commit 0100840

File tree

10 files changed

+190
-117
lines changed

10 files changed

+190
-117
lines changed

tools/cli/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ cli
5050
cli
5151
.command('link')
5252
.description('Link local LeafyGreen packages to a destination app.')
53-
.argument('destination', 'The destination app path')
53+
.argument('[destination]', 'The destination app path')
54+
.option('--to <destination>', 'Alias for `destination`')
55+
.option(
56+
'--from <source>',
57+
'When running from a consuming application, defines the source of linked packages',
58+
)
5459
.option('-v --verbose', 'Prints additional information to the console', false)
5560
.option('--scope <name>', 'The NPM organization')
5661
.option(

tools/link/src/link.ts

Lines changed: 71 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,86 @@
11
/* eslint-disable no-console */
22
import { getLGConfig } from '@lg-tools/meta';
33
import chalk from 'chalk';
4-
import { spawn } from 'cross-spawn';
54
import fse from 'fs-extra';
6-
import { homedir } from 'os';
75
import path from 'path';
86

9-
import { formatLog } from './utils';
7+
import { createLinkFrom } from './utils/createLinkFrom';
8+
import { formatLog } from './utils/formatLog';
9+
import { yarnInstall } from './utils/install';
10+
import { linkPackageTo } from './utils/linkPackageTo';
11+
import { PackageDetails } from './utils/types';
1012

1113
interface LinkOptions {
1214
packages: Array<string>;
1315
scope: string;
1416
verbose: boolean;
17+
to?: string;
18+
from?: string;
1519
}
1620

1721
const ignorePackages = ['mongo-nav'];
1822

19-
export async function linkPackages(destination: string, opts: LinkOptions) {
20-
const { verbose, scope: scopeFlag, packages } = opts;
23+
export async function linkPackages(
24+
dest: string | undefined,
25+
opts: LinkOptions,
26+
) {
27+
const { verbose, scope: scopeFlag, packages, to, from } = opts;
28+
2129
const rootDir = process.cwd();
22-
const relativeDestination = path.relative(rootDir, destination);
30+
31+
if (!to && !dest && !from) {
32+
console.error('Error linking. Must provide either a destination or source');
33+
}
34+
35+
const destination = path.resolve(path.join(rootDir, dest || to || '.'));
36+
const source = path.resolve(from ? path.join(rootDir, from) : rootDir);
2337

2438
// Check if the destination exists
2539
if (
26-
!(fse.existsSync(destination) && fse.lstatSync(destination).isDirectory())
40+
!(
41+
destination &&
42+
fse.existsSync(destination) &&
43+
fse.lstatSync(destination).isDirectory()
44+
)
2745
) {
2846
throw new Error(
29-
`Can't find the directory ${formatLog.path(relativeDestination)}.`,
47+
`Can't find the directory ${formatLog.path(destination ?? '')}.`,
48+
);
49+
}
50+
51+
if (dest ?? to) {
52+
console.log(
53+
chalk.green(`Linking packages to ${formatLog.path(destination)} ...`),
3054
);
3155
}
3256

33-
console.log(
34-
chalk.green(
35-
`Linking packages to ${formatLog.path(relativeDestination)} ...`,
36-
),
37-
);
57+
if (from) {
58+
console.log(
59+
chalk.green(`Linking packages from ${formatLog.path(source)} ...`),
60+
);
61+
}
3862

39-
const { scopes: availableScopes } = getLGConfig();
63+
const { scopes: availableScopes } = getLGConfig(source);
64+
65+
verbose &&
66+
console.log({
67+
availableScopes,
68+
dest,
69+
to,
70+
from,
71+
destination,
72+
source,
73+
rootDir,
74+
});
4075

4176
const linkPromises: Array<Promise<void>> = [];
4277

4378
for (const [scopeName, scopePath] of Object.entries(availableScopes)) {
4479
if (!scopeFlag || scopeFlag.includes(scopeName)) {
4580
linkPromises.push(
4681
linkPackagesForScope(
47-
scopeName,
48-
scopePath as string,
82+
{ scopeName, scopePath },
83+
source,
4984
destination,
5085
packages,
5186
verbose,
@@ -60,8 +95,8 @@ export async function linkPackages(destination: string, opts: LinkOptions) {
6095
}
6196

6297
async function linkPackagesForScope(
63-
scopeName: string,
64-
scopePath: string,
98+
{ scopeName, scopePath }: Pick<PackageDetails, 'scopeName' | 'scopePath'>,
99+
source: string,
65100
destination: string,
66101
packages?: Array<string>,
67102
verbose?: boolean,
@@ -86,16 +121,23 @@ async function linkPackagesForScope(
86121
packages.some(pkgFlag => pkgFlag.includes(installedPkg))),
87122
);
88123

124+
/** Create links */
89125
console.log(
90126
chalk.gray(
91127
` Creating links to ${formatLog.scope(scopeName)} packages...`,
92128
),
93129
);
94130
await Promise.all(
95131
packagesToLink.map(pkg => {
96-
createYarnLinkForPackage(scopeName, scopePath, pkg, verbose);
132+
createLinkFrom(
133+
source,
134+
{ scopeName, scopePath, packageName: pkg },
135+
verbose,
136+
);
97137
}),
98138
);
139+
140+
/** Connect link */
99141
console.log(
100142
chalk.gray(
101143
` Connecting links for ${formatLog.scope(
@@ -105,7 +147,14 @@ async function linkPackagesForScope(
105147
);
106148
await Promise.all(
107149
packagesToLink.map((pkg: string) =>
108-
linkPackageToDestination(scopeName, pkg, destination, verbose),
150+
linkPackageTo(
151+
destination,
152+
{
153+
scopeName,
154+
packageName: pkg,
155+
},
156+
verbose,
157+
),
109158
),
110159
);
111160
} else {
@@ -124,102 +173,11 @@ async function linkPackagesForScope(
124173
// TODO: Prompt user to install instead of just running it
125174
await yarnInstall(destination);
126175
await linkPackagesForScope(
127-
scopeName,
128-
scopePath,
176+
{ scopeName, scopePath },
129177
destination,
178+
source,
130179
packages,
131180
verbose,
132181
);
133182
}
134183
}
135-
136-
/**
137-
* Runs the yarn link command in a leafygreen-ui package directory
138-
* @returns Promise that resolves when the yarn link command has finished
139-
*/
140-
function createYarnLinkForPackage(
141-
scopeName: string,
142-
scopePath: string,
143-
packageName: string,
144-
verbose?: boolean,
145-
): Promise<void> {
146-
const scopeSrc = scopePath;
147-
return new Promise<void>(resolve => {
148-
const packagesDirectory = findDirectory(process.cwd(), scopeSrc);
149-
150-
if (packagesDirectory) {
151-
verbose &&
152-
console.log(
153-
'Creating link for:',
154-
chalk.green(`${scopeName}/${packageName}`),
155-
);
156-
spawn('yarn', ['link'], {
157-
cwd: path.join(packagesDirectory, packageName),
158-
stdio: verbose ? 'inherit' : 'ignore',
159-
})
160-
.on('close', resolve)
161-
.on('error', () => {
162-
throw new Error(`Couldn't create link for package: ${packageName}`);
163-
});
164-
} else {
165-
throw new Error(
166-
`Can't find a ${scopeSrc} directory in ${process.cwd()} or any of its parent directories.`,
167-
);
168-
}
169-
});
170-
}
171-
172-
/**
173-
* Runs the yarn link <packageName> command in the destination directory
174-
* @returns Promise that resolves when the yarn link <packageName> command has finished
175-
*/
176-
function linkPackageToDestination(
177-
scopeName: string,
178-
packageName: string,
179-
destination: string,
180-
verbose?: boolean,
181-
): Promise<void> {
182-
const fullPackageName = `${scopeName}/${packageName}`;
183-
return new Promise<void>(resolve => {
184-
verbose && console.log('Linking package:', chalk.blue(fullPackageName));
185-
spawn('yarn', ['link', fullPackageName], {
186-
cwd: destination,
187-
stdio: verbose ? 'inherit' : 'ignore',
188-
})
189-
.on('close', resolve)
190-
.on('error', () => {
191-
throw new Error(`Couldn't link package: ${fullPackageName}`);
192-
});
193-
});
194-
}
195-
196-
function findDirectory(
197-
startDir: string,
198-
targetDir: string,
199-
): string | undefined {
200-
const testDir = path.join(startDir, targetDir);
201-
202-
if (fse.existsSync(testDir) && fse.lstatSync(testDir).isDirectory()) {
203-
return testDir;
204-
} else {
205-
const parentDir = path.join(startDir, '..');
206-
207-
// If we haven't reached the users home directory, recursively look for the packages directory
208-
if (parentDir !== homedir()) {
209-
return findDirectory(path.join(startDir, '..'), targetDir);
210-
}
211-
}
212-
}
213-
214-
function yarnInstall(path: string) {
215-
return new Promise((resolve, reject) => {
216-
spawn('yarn', ['install'], {
217-
cwd: path,
218-
stdio: 'ignore',
219-
})
220-
.on('close', resolve)
221-
.on('error', () => {
222-
throw new Error(`Error installing packages`);
223-
});
224-
});
225-
}

tools/link/src/unlink.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { spawn } from 'cross-spawn';
55
import fse from 'fs-extra';
66
import path from 'path';
77

8-
import { formatLog } from './utils';
8+
import { formatLog } from './utils/formatLog';
99

1010
interface UnlinkOpts {
1111
verbose: boolean;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable no-console */
2+
import chalk from 'chalk';
3+
import { spawn } from 'cross-spawn';
4+
import path from 'path';
5+
6+
import { findDirectory } from './findDirectory';
7+
import { PackageDetails } from './types';
8+
9+
/**
10+
* Runs the yarn link command in a leafygreen-ui package directory
11+
* @returns Promise that resolves when the yarn link command has finished
12+
*/
13+
export function createLinkFrom(
14+
source: string,
15+
{ scopeName, scopePath, packageName }: PackageDetails,
16+
verbose?: boolean,
17+
): Promise<void> {
18+
const scopeSrc = scopePath;
19+
return new Promise<void>(resolve => {
20+
const packagesDirectory = findDirectory(process.cwd(), scopeSrc);
21+
22+
if (packagesDirectory) {
23+
verbose &&
24+
console.log(
25+
'Creating link for:',
26+
chalk.green(`${scopeName}/${packageName}`),
27+
);
28+
spawn('yarn', ['link'], {
29+
cwd: path.join(packagesDirectory, packageName),
30+
stdio: verbose ? 'inherit' : 'ignore',
31+
})
32+
.on('close', resolve)
33+
.on('error', () => {
34+
throw new Error(`Couldn't create link for package: ${packageName}`);
35+
});
36+
} else {
37+
throw new Error(
38+
`Can't find a ${scopeSrc} directory in ${process.cwd()} or any of its parent directories.`,
39+
);
40+
}
41+
});
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import fse from 'fs-extra';
2+
import { homedir } from 'os';
3+
import path from 'path';
4+
5+
export function findDirectory(
6+
startDir: string,
7+
targetDir: string,
8+
): string | undefined {
9+
const testDir = path.join(startDir, targetDir);
10+
11+
if (fse.existsSync(testDir) && fse.lstatSync(testDir).isDirectory()) {
12+
return testDir;
13+
} else {
14+
const parentDir = path.join(startDir, '..');
15+
16+
// If we haven't reached the users home directory, recursively look for the packages directory
17+
if (parentDir !== homedir()) {
18+
return findDirectory(path.join(startDir, '..'), targetDir);
19+
}
20+
}
21+
}
File renamed without changes.

tools/link/src/utils/install.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { spawn } from 'cross-spawn';
2+
3+
export function yarnInstall(path: string) {
4+
return new Promise((resolve, reject) => {
5+
spawn('yarn', ['install'], {
6+
cwd: path,
7+
stdio: 'ignore',
8+
})
9+
.on('close', resolve)
10+
.on('error', () => {
11+
throw new Error(`Error installing packages`);
12+
});
13+
});
14+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import chalk from 'chalk';
2+
import { spawn } from 'cross-spawn';
3+
4+
import { PackageDetails } from './types';
5+
6+
/**
7+
* Runs the yarn link <packageName> command in the destination directory
8+
* @returns Promise that resolves when the yarn link <packageName> command has finished
9+
*/
10+
export function linkPackageTo(
11+
destination: string,
12+
{ scopeName, packageName }: Pick<PackageDetails, 'scopeName' | 'packageName'>,
13+
verbose?: boolean,
14+
): Promise<void> {
15+
const fullPackageName = `${scopeName}/${packageName}`;
16+
return new Promise<void>(resolve => {
17+
// eslint-disable-next-line no-console
18+
verbose && console.log('Linking package:', chalk.blue(fullPackageName));
19+
spawn('yarn', ['link', fullPackageName], {
20+
cwd: destination,
21+
stdio: verbose ? 'inherit' : 'ignore',
22+
})
23+
.on('close', resolve)
24+
.on('error', () => {
25+
throw new Error(`Couldn't link package: ${fullPackageName}`);
26+
});
27+
});
28+
}

tools/link/src/utils/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface PackageDetails {
2+
scopeName: string;
3+
packageName: string;
4+
scopePath: string;
5+
}

0 commit comments

Comments
 (0)