{"version":3,"file":"PackageMetadataManager.js","sourceRoot":"","sources":["../../src/analyzer/PackageMetadataManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport path from 'path';\nimport semver from 'semver';\n\nimport {\n type PackageJsonLookup,\n FileSystem,\n JsonFile,\n type NewlineKind,\n type INodePackageJson,\n type JsonObject,\n type IPackageJsonExports\n} from '@rushstack/node-core-library';\nimport { Extractor } from '../api/Extractor';\nimport type { MessageRouter } from '../collector/MessageRouter';\nimport { ConsoleMessageId } from '../api/ConsoleMessageId';\n\n/**\n * Represents analyzed information for a package.json file.\n * This object is constructed and returned by PackageMetadataManager.\n */\nexport class PackageMetadata {\n /**\n * The absolute path to the package.json file being analyzed.\n */\n public readonly packageJsonPath: string;\n /**\n * The parsed contents of package.json. Note that PackageJsonLookup\n * only includes essential fields.\n */\n public readonly packageJson: INodePackageJson;\n /**\n * If true, then the package's documentation comments can be assumed\n * to contain API Extractor compatible TSDoc tags.\n */\n public readonly aedocSupported: boolean;\n\n public constructor(packageJsonPath: string, packageJson: INodePackageJson, aedocSupported: boolean) {\n this.packageJsonPath = packageJsonPath;\n this.packageJson = packageJson;\n this.aedocSupported = aedocSupported;\n }\n}\n\nconst TSDOC_METADATA_FILENAME: 'tsdoc-metadata.json' = 'tsdoc-metadata.json';\n\n/**\n * 1. If package.json a `\"tsdocMetadata\": \"./path1/path2/tsdoc-metadata.json\"` field\n * then that takes precedence. This convention will be rarely needed, since the other rules below generally\n * produce a good result.\n */\nfunction _tryResolveTsdocMetadataFromTsdocMetadataField({\n tsdocMetadata\n}: INodePackageJson): string | undefined {\n return tsdocMetadata;\n}\n\n/**\n * 2. If package.json contains a `\"exports\": { \".\": { \"types\": \"./path1/path2/index.d.ts\" } }` field,\n * then we look for the file under \"./path1/path2/tsdoc-metadata.json\"\n *\n * This always looks for a \".\" and then a \"*\" entry in the exports field, and then evaluates for\n * a \"types\" field in that entry.\n */\n\nfunction _tryResolveTsdocMetadataFromExportsField({ exports }: INodePackageJson): string | undefined {\n switch (typeof exports) {\n case 'string': {\n return `${path.dirname(exports)}/${TSDOC_METADATA_FILENAME}`;\n }\n\n case 'object': {\n if (Array.isArray(exports)) {\n const [firstExport] = exports;\n // Take the first entry in the array\n if (firstExport) {\n return `${path.dirname(exports[0])}/${TSDOC_METADATA_FILENAME}`;\n }\n } else {\n const rootExport: IPackageJsonExports | string | null | undefined = exports['.'] ?? exports['*'];\n switch (typeof rootExport) {\n case 'string': {\n return `${path.dirname(rootExport)}/${TSDOC_METADATA_FILENAME}`;\n }\n\n case 'object': {\n let typesExport: IPackageJsonExports | string | undefined = rootExport?.types;\n while (typesExport) {\n switch (typeof typesExport) {\n case 'string': {\n return `${path.dirname(typesExport)}/${TSDOC_METADATA_FILENAME}`;\n }\n\n case 'object': {\n typesExport = typesExport?.types;\n break;\n }\n }\n }\n }\n }\n }\n break;\n }\n }\n}\n\n/**\n * 3. If package.json contains a `typesVersions` field, look for the version\n * matching the highest minimum version that either includes a \".\" or \"*\" entry.\n */\nfunction _tryResolveTsdocMetadataFromTypesVersionsField({\n typesVersions\n}: INodePackageJson): string | undefined {\n if (typesVersions) {\n let highestMinimumMatchingSemver: semver.SemVer | undefined;\n let latestMatchingPath: string | undefined;\n for (const [version, paths] of Object.entries(typesVersions)) {\n let range: semver.Range;\n try {\n range = new semver.Range(version);\n } catch {\n continue;\n }\n\n const minimumMatchingSemver: semver.SemVer | null = semver.minVersion(range);\n if (\n minimumMatchingSemver &&\n (!highestMinimumMatchingSemver || semver.gt(minimumMatchingSemver, highestMinimumMatchingSemver))\n ) {\n const pathEntry: string[] | undefined = paths['.'] ?? paths['*'];\n const firstPath: string | undefined = pathEntry?.[0];\n if (firstPath) {\n highestMinimumMatchingSemver = minimumMatchingSemver;\n latestMatchingPath = firstPath;\n }\n }\n }\n\n if (latestMatchingPath) {\n return `${path.dirname(latestMatchingPath)}/${TSDOC_METADATA_FILENAME}`;\n }\n }\n}\n\n/**\n * 4. If package.json contains a `\"types\": \"./path1/path2/index.d.ts\"` or a `\"typings\": \"./path1/path2/index.d.ts\"`\n * field, then we look for the file under \"./path1/path2/tsdoc-metadata.json\".\n *\n * @remarks\n * `types` takes precedence over `typings`.\n */\nfunction _tryResolveTsdocMetadataFromTypesOrTypingsFields({\n typings,\n types\n}: INodePackageJson): string | undefined {\n const typesField: string | undefined = types ?? typings;\n if (typesField) {\n return `${path.dirname(typesField)}/${TSDOC_METADATA_FILENAME}`;\n }\n}\n\n/**\n * 5. If package.json contains a `\"main\": \"./path1/path2/index.js\"` field, then we look for the file under\n * \"./path1/path2/tsdoc-metadata.json\".\n */\nfunction _tryResolveTsdocMetadataFromMainField({ main }: INodePackageJson): string | undefined {\n if (main) {\n return `${path.dirname(main)}/${TSDOC_METADATA_FILENAME}`;\n }\n}\n\n/**\n * This class maintains a cache of analyzed information obtained from package.json\n * files. It is built on top of the PackageJsonLookup class.\n *\n * @remarks\n *\n * IMPORTANT: Don't use PackageMetadataManager to analyze source files from the current project:\n * 1. Files such as tsdoc-metadata.json may not have been built yet, and thus may contain incorrect information.\n * 2. The current project is not guaranteed to have a package.json file at all. For example, API Extractor can\n * be invoked on a bare .d.ts file.\n *\n * Use ts.program.isSourceFileFromExternalLibrary() to test source files before passing the to PackageMetadataManager.\n */\nexport class PackageMetadataManager {\n public static tsdocMetadataFilename: string = TSDOC_METADATA_FILENAME;\n\n private readonly _packageJsonLookup: PackageJsonLookup;\n private readonly _messageRouter: MessageRouter;\n private readonly _packageMetadataByPackageJsonPath: Map = new Map<\n string,\n PackageMetadata\n >();\n\n public constructor(packageJsonLookup: PackageJsonLookup, messageRouter: MessageRouter) {\n this._packageJsonLookup = packageJsonLookup;\n this._messageRouter = messageRouter;\n }\n\n /**\n * This feature is still being standardized: https://github.com/microsoft/tsdoc/issues/7\n * In the future we will use the @microsoft/tsdoc library to read this file.\n */\n private static _resolveTsdocMetadataPathFromPackageJson(\n packageFolder: string,\n packageJson: INodePackageJson\n ): string {\n const tsdocMetadataRelativePath: string =\n _tryResolveTsdocMetadataFromTsdocMetadataField(packageJson) ??\n _tryResolveTsdocMetadataFromExportsField(packageJson) ??\n _tryResolveTsdocMetadataFromTypesVersionsField(packageJson) ??\n _tryResolveTsdocMetadataFromTypesOrTypingsFields(packageJson) ??\n _tryResolveTsdocMetadataFromMainField(packageJson) ??\n // As a final fallback, place the file in the root of the package.\n TSDOC_METADATA_FILENAME;\n\n // Always resolve relative to the package folder.\n const tsdocMetadataPath: string = path.resolve(\n packageFolder,\n // This non-null assertion is safe because the last entry in TSDOC_METADATA_RESOLUTION_FUNCTIONS\n // returns a non-undefined value.\n tsdocMetadataRelativePath!\n );\n return tsdocMetadataPath;\n }\n\n /**\n * @param tsdocMetadataPath - An explicit path that can be configured in api-extractor.json.\n * If this parameter is not an empty string, it overrides the normal path calculation.\n * @returns the absolute path to the TSDoc metadata file\n */\n public static resolveTsdocMetadataPath(\n packageFolder: string,\n packageJson: INodePackageJson,\n tsdocMetadataPath?: string\n ): string {\n if (tsdocMetadataPath) {\n return path.resolve(packageFolder, tsdocMetadataPath);\n }\n\n return PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson(packageFolder, packageJson);\n }\n\n /**\n * Writes the TSDoc metadata file to the specified output file.\n */\n public static writeTsdocMetadataFile(tsdocMetadataPath: string, newlineKind: NewlineKind): void {\n const fileObject: JsonObject = {\n tsdocVersion: '0.12',\n toolPackages: [\n {\n packageName: '@microsoft/api-extractor',\n packageVersion: Extractor.version\n }\n ]\n };\n\n const fileContent: string =\n '// This file is read by tools that parse documentation comments conforming to the TSDoc standard.\\n' +\n '// It should be published with your NPM package. It should not be tracked by Git.\\n' +\n JsonFile.stringify(fileObject);\n\n FileSystem.writeFile(tsdocMetadataPath, fileContent, {\n convertLineEndings: newlineKind,\n ensureFolderExists: true\n });\n }\n\n /**\n * Finds the package.json in a parent folder of the specified source file, and\n * returns a PackageMetadata object. If no package.json was found, then undefined\n * is returned. The results are cached.\n */\n public tryFetchPackageMetadata(sourceFilePath: string): PackageMetadata | undefined {\n const packageJsonFilePath: string | undefined =\n this._packageJsonLookup.tryGetPackageJsonFilePathFor(sourceFilePath);\n if (!packageJsonFilePath) {\n return undefined;\n }\n let packageMetadata: PackageMetadata | undefined =\n this._packageMetadataByPackageJsonPath.get(packageJsonFilePath);\n\n if (!packageMetadata) {\n const packageJson: INodePackageJson = this._packageJsonLookup.loadNodePackageJson(packageJsonFilePath);\n\n const packageJsonFolder: string = path.dirname(packageJsonFilePath);\n\n let aedocSupported: boolean = false;\n\n const tsdocMetadataPath: string = PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson(\n packageJsonFolder,\n packageJson\n );\n\n if (FileSystem.exists(tsdocMetadataPath)) {\n this._messageRouter.logVerbose(\n ConsoleMessageId.FoundTSDocMetadata,\n 'Found metadata in ' + tsdocMetadataPath\n );\n // If the file exists at all, assume it was written by API Extractor\n aedocSupported = true;\n }\n\n packageMetadata = new PackageMetadata(packageJsonFilePath, packageJson, aedocSupported);\n this._packageMetadataByPackageJsonPath.set(packageJsonFilePath, packageMetadata);\n }\n\n return packageMetadata;\n }\n\n /**\n * Returns true if the source file is part of a package whose .d.ts files support AEDoc annotations.\n */\n public isAedocSupportedFor(sourceFilePath: string): boolean {\n const packageMetadata: PackageMetadata | undefined = this.tryFetchPackageMetadata(sourceFilePath);\n if (!packageMetadata) {\n return false;\n }\n return packageMetadata.aedocSupported;\n }\n}\n"]}