Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions src/package-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { glob } from 'glob';
import * as fileUtils from './file-utils';
import {
getPackageManifest,
Expand All @@ -9,14 +10,7 @@ import {
validatePolyrepoPackageManifest,
} from './package-utils';

jest.mock('util', () => {
return {
promisify: jest.fn().mockImplementation(
// This is effectively the mock of the promisified glob main export
() => async (val: unknown, _options: Record<string, unknown>) => val,
),
};
});
jest.mock('glob');

describe('getPackageManifest', () => {
let readJsonFileMock: jest.SpyInstance;
Expand Down Expand Up @@ -164,11 +158,49 @@ describe('validatePolyrepoPackageManifest', () => {
});

describe('getWorkspaceLocations', () => {
const mockGlob = (value: string[]) => (
_pattern: string,
_options: unknown,
callback: (error: null, data: string[]) => void,
) => callback(null, value);

it('does the thing', async () => {
const workspaces = ['foo/bar', 'fizz/buzz'];
const rootDir = 'dir';
expect(await getWorkspaceLocations(workspaces, rootDir)).toStrictEqual([
...workspaces,
]);

(glob as jest.MockedFunction<any>)
.mockImplementationOnce(mockGlob(['foo/bar']))
.mockImplementationOnce(mockGlob(['fizz/buzz']));

expect(await getWorkspaceLocations(workspaces, rootDir)).toStrictEqual(
workspaces,
);
});

it('does the thing, but recursively', async () => {
(glob as jest.MockedFunction<any>)
.mockImplementationOnce(mockGlob(['foo/bar']))
.mockImplementationOnce(mockGlob(['baz']))
.mockImplementationOnce(mockGlob(['qux']));

jest
.spyOn(fileUtils, 'readJsonObjectFile')
.mockImplementationOnce(async () => ({
[ManifestFieldNames.Version]: '1.0.0',
[ManifestFieldNames.Private]: true,
[ManifestFieldNames.Workspaces]: ['baz'],
}))
.mockImplementationOnce(async () => ({
[ManifestFieldNames.Version]: '1.0.0',
[ManifestFieldNames.Private]: true,
[ManifestFieldNames.Workspaces]: ['qux'],
}))
.mockImplementation(async () => ({
[ManifestFieldNames.Version]: '1.0.0',
}));

expect(
await getWorkspaceLocations(['foo/bar'], 'dir', true),
).toStrictEqual(['foo/bar', 'foo/bar/baz', 'foo/bar/baz/qux']);
});
});
55 changes: 52 additions & 3 deletions src/package-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,63 @@ function getManifestErrorMessagePrefix(
*
* @param workspaces - The list of workspace patterns given in the root manifest.
* @param rootDir - The monorepo root directory.
* @param recursive - Whether to search recursively.
* @returns The location of each workspace directory relative to the root directory
*/
export async function getWorkspaceLocations(
workspaces: string[],
rootDir: string,
recursive = false,
prefix = '',
): Promise<string[]> {
const resolvedWorkspaces = await Promise.all(
workspaces.map((pattern) => glob(pattern, { cwd: rootDir })),
const resolvedWorkspaces = await workspaces.reduce<Promise<string[]>>(
async (promise, pattern) => {
const array = await promise;
const matches = (await glob(pattern, { cwd: rootDir })).map((match) =>
pathUtils.join(prefix, match),
);

return [...array, ...matches];
},
Promise.resolve([]),
);
return resolvedWorkspaces.flat();

if (recursive) {
// This reads all the package JSON files in each workspace, checks if they are a monorepo, and
// recursively calls `getWorkspaceLocations` if they are.
const resolvedSubWorkspaces = await resolvedWorkspaces.reduce<
Promise<string[]>
>(async (promise, workspacePath) => {
const array = await promise;

const rawManifest = await getPackageManifest(workspacePath);
if (ManifestFieldNames.Workspaces in rawManifest) {
const manifest = validatePackageManifestVersion(
rawManifest,
workspacePath,
);

const monorepoManifest = validateMonorepoPackageManifest(
manifest,
workspacePath,
);

return [
...array,
...(await getWorkspaceLocations(
monorepoManifest[ManifestFieldNames.Workspaces],
workspacePath,
recursive,
workspacePath,
)),
];
}

return array;
}, Promise.resolve(resolvedWorkspaces));

return resolvedSubWorkspaces;
}

return resolvedWorkspaces;
}