diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 657bbb4404..9b51d35922 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -44,7 +44,10 @@ "ts-node": "^10.0.0" }, "devDependencies": { - "@types/is-glob": "^4.0.2" + "@backstage/cli": "workspace:^", + "@types/is-glob": "^4.0.2", + "@types/mock-fs": "^4.13.0", + "mock-fs": "^5.1.0" }, "files": [ "bin", diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index d1e8d86240..b2fab40ca5 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -65,7 +65,7 @@ import { } from '@microsoft/api-documenter/lib/markdown/CustomMarkdownEmitter'; import { IMarkdownEmitterContext } from '@microsoft/api-documenter/lib/markdown/MarkdownEmitter'; import { AstDeclaration } from '@microsoft/api-extractor/lib/analyzer/AstDeclaration'; -import { paths as cliPaths } from '../../lib/paths'; +import { paths as cliPaths, resolvePackagePath } from '../../lib/paths'; import g from 'glob'; import isGlob from 'is-glob'; @@ -226,25 +226,6 @@ ApiReportGenerator.generateReviewFileContent = }); }; -export async function resolvePackagePath( - packagePath: string, -): Promise { - const fullPackageDir = cliPaths.resolveTargetRoot(packagePath); - - const stat = await fs.stat(fullPackageDir); - if (!stat.isDirectory()) { - return undefined; - } - - try { - const packageJsonPath = join(fullPackageDir, 'package.json'); - await fs.access(packageJsonPath); - } catch (_) { - return undefined; - } - return relativePath(cliPaths.targetRoot, fullPackageDir); -} - export async function findPackageDirs(selectedPaths: string[]) { const packageDirs = new Array(); for (const packageRoot of selectedPaths) { diff --git a/packages/repo-tools/src/lib/paths.test.ts b/packages/repo-tools/src/lib/paths.test.ts new file mode 100644 index 0000000000..90d84aec3c --- /dev/null +++ b/packages/repo-tools/src/lib/paths.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import mockFs from 'mock-fs'; +import { resolve as resolvePath } from 'path'; +import { resolvePackagePath, paths } from './paths'; + +describe('paths', () => { + jest.spyOn(paths, 'targetRoot', 'get').mockReturnValue('/root'); + jest.spyOn(paths, 'resolveTargetRoot').mockImplementation((...path) => { + return resolvePath('/root', ...path); + }); + + beforeEach(() => { + mockFs({ + [paths.targetRoot]: { + 'package.json': JSON.stringify({ name: 'test' }), + packages: { + 'package-a': { + 'package.json': '{}', + }, + 'package-b': { + 'package.json': '{}', + }, + 'package-c': {}, + 'README.md': 'Hello World', + }, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + describe('resolvePackagePath', () => { + it('should return undefined if the package does not exist or does not contain a package.json', async () => { + expect(await resolvePackagePath('packages/package-d')).toBeUndefined(); + expect(await resolvePackagePath('packages/package-c')).toBeUndefined(); + }); + it('should return the path to the package if it exists and has a package.json', async () => { + expect(await resolvePackagePath('packages/package-a')).toBe( + 'packages/package-a', + ); + expect(await resolvePackagePath('packages/package-b')).toBe( + 'packages/package-b', + ); + }); + it('should return undefined if the pat is not a directory', async () => { + expect(await resolvePackagePath('packages/README.md')).toBeUndefined(); + }); + }); +}); diff --git a/packages/repo-tools/src/lib/paths.ts b/packages/repo-tools/src/lib/paths.ts index 2c658c27b3..f387e9be51 100644 --- a/packages/repo-tools/src/lib/paths.ts +++ b/packages/repo-tools/src/lib/paths.ts @@ -15,6 +15,29 @@ */ import { findPaths } from '@backstage/cli-common'; +import { relative as relativePath, join } from 'path'; +import fs from 'fs-extra'; /* eslint-disable-next-line no-restricted-syntax */ export const paths = findPaths(__dirname); + +export async function resolvePackagePath( + packagePath: string, +): Promise { + const fullPackageDir = paths.resolveTargetRoot(packagePath); + + try { + const stat = await fs.stat(fullPackageDir); + if (!stat.isDirectory()) { + return undefined; + } + + const packageJsonPath = join(fullPackageDir, 'package.json'); + + await fs.access(packageJsonPath); + } catch (e) { + console.log(`folder omitted: ${fullPackageDir}, cause: ${e}`); + return undefined; + } + return relativePath(paths.targetRoot, fullPackageDir); +} diff --git a/yarn.lock b/yarn.lock index c9a9d1daea..e92df381ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8467,6 +8467,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/repo-tools@workspace:packages/repo-tools" dependencies: + "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@backstage/errors": "workspace:^" "@manypkg/get-packages": ^1.1.3 @@ -8475,10 +8476,12 @@ __metadata: "@microsoft/api-extractor-model": ^7.17.2 "@microsoft/tsdoc": 0.14.1 "@types/is-glob": ^4.0.2 + "@types/mock-fs": ^4.13.0 chalk: ^4.0.0 commander: ^9.1.0 fs-extra: 10.1.0 is-glob: ^4.0.3 + mock-fs: ^5.1.0 ts-node: ^10.0.0 bin: backstage-repo-tools: bin/backstage-repo-tools