UNPKG

@rushstack/heft

Version:

Build all your JavaScript projects the same way: A way that works.

153 lines 8.57 kB
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. import * as path from 'node:path'; import { ProjectConfigurationFile, InheritanceType, PathResolutionMethod } from '@rushstack/heft-config-file'; import { Import, PackageJsonLookup, InternalError } from '@rushstack/node-core-library'; import { Constants } from './Constants'; export class CoreConfigFiles { /** * Returns the loader for the `config/heft.json` config file. */ static async loadHeftConfigurationFileForProjectAsync(terminal, projectPath, rigConfig) { var _a, _b; if (!CoreConfigFiles._heftConfigFileLoader) { let heftPluginPackageFolder; const pluginPackageResolver = (options) => { const { propertyValue, configurationFilePath } = options; if (propertyValue === Constants.heftPackageName) { // If the value is "@rushstack/heft", then resolve to the Heft package that is // installed in the project folder. This avoids issues with mismatched versions // between the project and the globally installed Heft. Use the PackageJsonLookup // class to find the package folder to avoid hardcoding the path for compatibility // with bundling. if (!heftPluginPackageFolder) { heftPluginPackageFolder = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); } if (!heftPluginPackageFolder) { // This should never happen throw new InternalError('Unable to find the @rushstack/heft package folder'); } return heftPluginPackageFolder; } else { const configurationFileDirectory = path.dirname(configurationFilePath); return Import.resolvePackage({ packageName: propertyValue, baseFolderPath: configurationFileDirectory, allowSelfReference: true }); } }; const schemaObject = await import('../schemas/heft.schema.json'); CoreConfigFiles._heftConfigFileLoader = new ProjectConfigurationFile({ projectRelativeFilePath: CoreConfigFiles.heftConfigurationProjectRelativeFilePath, jsonSchemaObject: schemaObject, propertyInheritanceDefaults: { array: { inheritanceType: InheritanceType.append }, object: { inheritanceType: InheritanceType.merge } }, jsonPathMetadata: { // Use a custom resolver for the plugin packages, since the NodeResolve algorithm will resolve to the // package.json exports/module property, which may or may not exist. '$.heftPlugins.*.pluginPackage': { pathResolutionMethod: PathResolutionMethod.custom, customResolver: pluginPackageResolver }, // Use a custom resolver for the plugin packages, since the NodeResolve algorithm will resolve to the // package.json exports/module property, which may or may not exist. '$.phasesByName.*.tasksByName.*.taskPlugin.pluginPackage': { pathResolutionMethod: PathResolutionMethod.custom, customResolver: pluginPackageResolver } } }); } const heftConfigFileLoader = CoreConfigFiles._heftConfigFileLoader; let configurationFile; try { configurationFile = await heftConfigFileLoader.loadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); } catch (e) { if (!(e instanceof Error) || !e.message.startsWith('Resolved configuration object does not match schema')) { throw e; } try { // If the config file doesn't match the schema, then we should check to see if it does // match the legacy schema. We don't need to worry about the resulting object, we just // want to see if it parses. We will use the ConfigurationFile class to load it to ensure // that we follow the "extends" chain for the entire config file. const legacySchemaObject = await import('../schemas/heft-legacy.schema.json'); const legacyConfigFileLoader = new ProjectConfigurationFile({ projectRelativeFilePath: CoreConfigFiles.heftConfigurationProjectRelativeFilePath, jsonSchemaObject: legacySchemaObject }); await legacyConfigFileLoader.loadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); } catch (e2) { // It doesn't match the legacy schema either. Throw the original error. throw e; } // Matches the legacy schema, so throw a more helpful error. throw new Error("This project's Heft configuration appears to be using an outdated schema.\n\n" + 'Heft 0.51.0 introduced a major breaking change for Heft configuration files. ' + 'Your project appears to be using the older file format. You will need to ' + 'migrate your project to the new format. Follow these instructions: ' + 'https://rushstack.io/link/heft-0.51'); } // The pluginPackage field was resolved to the root of the package, but we also want to have // the original plugin package name in the config file. function getUpdatedPluginSpecifier(rawSpecifier) { const pluginPackageName = heftConfigFileLoader.getPropertyOriginalValue({ parentObject: rawSpecifier, propertyName: 'pluginPackage' }); const newSpecifier = { ...rawSpecifier, pluginPackageRoot: rawSpecifier.pluginPackage, pluginPackage: pluginPackageName }; return newSpecifier; } const phasesByName = {}; const normalizedConfigurationFile = { ...configurationFile, heftPlugins: (_b = (_a = configurationFile.heftPlugins) === null || _a === void 0 ? void 0 : _a.map(getUpdatedPluginSpecifier)) !== null && _b !== void 0 ? _b : [], phasesByName }; for (const [phaseName, phase] of Object.entries(configurationFile.phasesByName || {})) { const tasksByName = {}; phasesByName[phaseName] = { ...phase, tasksByName }; for (const [taskName, task] of Object.entries(phase.tasksByName || {})) { if (task.taskPlugin) { tasksByName[taskName] = { ...task, taskPlugin: getUpdatedPluginSpecifier(task.taskPlugin) }; } else { tasksByName[taskName] = task; } } } return normalizedConfigurationFile; } static async tryLoadNodeServiceConfigurationFileAsync(terminal, projectPath, rigConfig) { if (!CoreConfigFiles._nodeServiceConfigurationLoader) { const schemaObject = await import('../schemas/node-service.schema.json'); CoreConfigFiles._nodeServiceConfigurationLoader = new ProjectConfigurationFile({ projectRelativeFilePath: CoreConfigFiles.nodeServiceConfigurationProjectRelativeFilePath, jsonSchemaObject: schemaObject }); } const configurationFile = await CoreConfigFiles._nodeServiceConfigurationLoader.tryLoadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); return configurationFile; } } CoreConfigFiles.heftConfigurationProjectRelativeFilePath = `${Constants.projectConfigFolderName}/${Constants.heftConfigurationFilename}`; CoreConfigFiles.nodeServiceConfigurationProjectRelativeFilePath = `${Constants.projectConfigFolderName}/${Constants.nodeServiceConfigurationFilename}`; //# sourceMappingURL=CoreConfigFiles.js.map