UNPKG

@graphql-mesh/migrate-config-cli

Version:
441 lines (440 loc) • 18.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.run = run; const tslib_1 = require("tslib"); // eslint-disable-next-line import/no-nodejs-modules const node_fs_1 = require("node:fs"); // eslint-disable-next-line import/no-nodejs-modules const node_path_1 = require("node:path"); const lodash_kebabcase_1 = tslib_1.__importDefault(require("lodash.kebabcase")); const prettier_1 = require("prettier"); const cli_1 = require("@graphql-mesh/cli"); const compose_cli_1 = require("@graphql-mesh/compose-cli"); const config_1 = require("@graphql-mesh/config"); const utils_1 = require("@graphql-mesh/utils"); async function run() { const cwd = process.argv[2] || process.cwd(); const { config: legacyConfig, filepath, isEmpty, } = await (0, cli_1.findConfig)({ initialLoggerPrefix: 'Mesh Config Migrate', dir: cwd, }); if (isEmpty) { console.error('No config file found'); process.exit(1); } if (!legacyConfig) { console.error('No config found'); process.exit(1); } console.log(`Found config at ${filepath}`); // Create compose configuration const importMap = new Map(); const addedPackages = new Set(['@graphql-hive/gateway', '@graphql-mesh/compose-cli']); const removedPackages = new Set(['@graphql-mesh/cli']); // Prepare Compose Configuration const subgraphConfigList = new Set(); for (const legacySource of legacyConfig.sources) { const handlerName = Object.keys(legacySource.handler)[0]; if (handlerName === 'supergraph') { console.error(`If you use "supergraph" handler, you probably don't need to use Mesh Compose, check Hive Gateway's docs to consume a supergraph.`); process.exit(1); } const handlerConfig = legacySource.handler[handlerName]; const handlerInfo = handlerInfoMap[handlerName]; if (!handlerInfo) { console.error(`Handler ${handlerName} is not supported currently in Mesh Migrate CLI, please do the migration manually.`); process.exit(1); } addImport(importMap, handlerInfo.packageName, handlerInfo.importName); removedPackages.add(handlerInfo.oldPackageName); addedPackages.add(handlerInfo.packageName); const subgraphConfigContent = new Set(); subgraphConfigContent.add(` sourceHandler: ${handlerInfo.importName}(${JSON.stringify(legacySource.name)}, ${JSON.stringify(handlerConfig)} ) `); if (legacySource.transforms?.length) { subgraphConfigContent.add(`transforms: [ ${legacySource.transforms.map(transform => handleTransformConfiguration(importMap, transform)).join(',\n')} ]`); } subgraphConfigList.add(`{ ${Array.from(subgraphConfigContent).join(',\n')} }`); } const subgraphsConfig = ` subgraphs: [ ${Array.from(subgraphConfigList).join(',\n')} ] `; const composeConfigList = new Set(); composeConfigList.add(subgraphsConfig); if (legacyConfig.additionalTypeDefs) { composeConfigList.add(`additionalTypeDefs: ${JSON.stringify(legacyConfig.additionalTypeDefs)},`); } if (legacyConfig.transforms) { console.error('Root level transforms are not supported in Mesh Compose CLI! Please source level transforms instead!'); process.exit(1); } if (legacyConfig.customFetch) { const [packageName, importName = 'default'] = legacyConfig.customFetch.split('#'); addImport(importMap, packageName, `${importName} as customFetch`); composeConfigList.add(`fetch: customFetch,`); } addImport(importMap, '@graphql-mesh/compose-cli', 'defineConfig as defineComposeConfig'); const configList = new Set(); const composeConfig = ` export const composeConfig = defineComposeConfig({ ${Array.from(composeConfigList).join(',\n')} }); `; // Serve Configuration const serveConfigList = new Set(); const pluginList = new Set(); if (legacyConfig.additionalEnvelopPlugins) { const [packageName, importName = 'default'] = legacyConfig.additionalEnvelopPlugins.split('#'); addImport(importMap, packageName, `${importName} as additionalEnvelopPlugins`); pluginList.add(`...additionalEnvelopPlugins`); } if (legacyConfig.additionalResolvers) { const additionalResolversNewConfig = new Set(); const additionalResolversConfigs = Array.isArray(legacyConfig.additionalResolvers) ? legacyConfig.additionalResolvers : [legacyConfig.additionalResolvers]; for (const additionalResolversConfigIndex in additionalResolversConfigs) { const additionalResolversConfig = additionalResolversConfigs[additionalResolversConfigIndex]; if (typeof additionalResolversConfig === 'string') { const importNameAlias = `additionalResolvers$${additionalResolversConfigIndex}`; const [packageName, importName = 'default'] = additionalResolversConfig.split('#'); addImport(importMap, packageName, `${importName} as ${importNameAlias}`); additionalResolversNewConfig.add(importNameAlias); } else if (typeof additionalResolversConfig === 'object') { additionalResolversNewConfig.add(JSON.stringify(additionalResolversConfig)); } } serveConfigList.add(`additionalResolvers: [ ${Array.from(additionalResolversNewConfig).join(',\n')} ]`); } if (legacyConfig.cache) { const cacheName = Object.keys(legacyConfig.cache)[0].toString(); const packageName = `@graphql-mesh/cache-${(0, lodash_kebabcase_1.default)(cacheName)}`; const cacheConfig = legacyConfig.cache[cacheName]; addImport(importMap, packageName, 'default as Cache'); serveConfigList.add(`cache: new Cache(${JSON.stringify(cacheConfig)})`); } if (legacyConfig.codegen) { console.warn('Codegen is not available in Mesh Compose CLI! Please use GraphQL Codegen directly to generate types!'); } if (legacyConfig.customFetch) { const [packageName, importName = 'default'] = legacyConfig.customFetch.split('#'); addImport(importMap, packageName, `${importName} as customFetch`); serveConfigList.add(`fetchAPI: { fetch: customFetch }`); } if (legacyConfig.logger) { const [packageName, importName = 'default'] = legacyConfig.logger.split('#'); addImport(importMap, packageName, `${importName} as logger`); serveConfigList.add(`logger`); } if (legacyConfig.pubsub) { console.warn('PubSub configuration cannot be migrated right now. Please check docs for PubSub configuration in Mesh Compose CLI!'); } if (legacyConfig.persistedOperations) { console.warn('Persisted Operations configuration cannot be migrated right now. Please check docs for Persisted Operations configuration in Mesh Compose CLI!'); } if (legacyConfig.plugins) { for (const legacyPluginConfig of legacyConfig.plugins) { const legacyPluginName = Object.keys(legacyPluginConfig)[0]; if (legacyPluginName === 'maskedErrors') { const maskedErrorsConfig = legacyPluginConfig[legacyPluginName]; if (typeof maskedErrorsConfig === 'boolean') { serveConfigList.add(`maskedErrors: ${maskedErrorsConfig}`); } else { serveConfigList.add(`maskedErrors: ${JSON.stringify(maskedErrorsConfig)}`); } } else if (legacyPluginName === 'immediateIntrospection') { addImport(importMap, '@envelop/core', 'useImmediateIntrospection'); pluginList.add('useImmediateIntrospection()'); } else if (legacyPluginName === 'hive') { // eslint-disable-next-line camelcase const { experimental__persistedDocuments, ...hiveConfig } = legacyPluginConfig[legacyPluginName]; serveConfigList.add(` reporting: ${JSON.stringify({ type: 'hive', ...hiveConfig, })} `); // eslint-disable-next-line camelcase if (experimental__persistedDocuments) { serveConfigList.add(`persistedOperations: ${JSON.stringify({ type: 'hive', // eslint-disable-next-line camelcase ...experimental__persistedDocuments, })}`); } } else if (legacyPluginName === 'prometheus') { serveConfigList.add(`prometheus: ${JSON.stringify(legacyPluginConfig[legacyPluginName])}`); } else if (legacyPluginName === 'rateLimit') { serveConfigList.add(`rateLimiting: ${JSON.stringify(legacyPluginConfig[legacyPluginName])}`); } else if (legacyPluginName === 'responseCache') { serveConfigList.add(`responseCaching: ${JSON.stringify(legacyPluginConfig[legacyPluginName])}`); } else { const { resolved: possiblePluginFactory, moduleName } = await (0, config_1.getPackage)({ name: legacyPluginName.toString(), type: 'plugin', importFn: utils_1.defaultImportFn, cwd, additionalPrefixes: ['@envelop/', '@graphql-yoga/plugin-', '@escape.tech/graphql-armor-'], }); let importName; let fnName; if (typeof possiblePluginFactory === 'function') { fnName = (0, compose_cli_1.camelCase)(`use_${legacyPluginName}`); importName = `default as ${fnName}`; } else { importName = Object.keys(possiblePluginFactory) .find(iName => (iName.toString().startsWith('use') || iName.toString().endsWith('Plugin')) && typeof possiblePluginFactory[iName] === 'function') .toString(); fnName = importName; } addImport(importMap, moduleName, importName); pluginList.add(`${fnName}({ ...ctx, ${JSON.stringify(legacyPluginConfig[legacyPluginName]).slice(1, -1)}, })`); } } } if (legacyConfig.pollingInterval) { serveConfigList.add(`pollingInterval: ${legacyConfig.pollingInterval}`); } if (legacyConfig.require) { for (const requiredPackage of legacyConfig.require) { configList.add(`import '${requiredPackage}';`); } } if (legacyConfig.sdk) { console.warn('Mesh no longer generates SDKs. Please use GraphQL Codegen to generate types and SDK!'); } if (legacyConfig.serve) { if (legacyConfig.serve.batchingLimit) { serveConfigList.add(`batching: { limit: ${legacyConfig.serve.batchingLimit} }`); } if (legacyConfig.serve.cors) { serveConfigList.add(`cors: ${JSON.stringify(legacyConfig.serve.cors)}`); } if (legacyConfig.serve.endpoint) { serveConfigList.add(`graphqlEndpoint: ${JSON.stringify(legacyConfig.serve.endpoint)}`); } if (legacyConfig.serve.extraParamNames) { console.warn('Extra Param Names configuration cannot be migrated right now. Please check docs for Extra Param Names configuration in Mesh Compose CLI!'); } if (legacyConfig.serve.fork) { serveConfigList.add(`fork: ${legacyConfig.serve.fork}`); } if (legacyConfig.serve.healthCheckEndpoint) { serveConfigList.add(`healthCheckEndpoint: ${JSON.stringify(legacyConfig.serve.healthCheckEndpoint)}`); } if (legacyConfig.serve.hostname) { serveConfigList.add(`host: ${JSON.stringify(legacyConfig.serve.hostname)}`); } if (legacyConfig.serve.playground === false) { serveConfigList.add(`graphiql: false`); } if (legacyConfig.serve.playgroundTitle) { serveConfigList.add(`graphiql: { title: ${JSON.stringify(legacyConfig.serve.playgroundTitle)} }`); } if (legacyConfig.serve.port) { serveConfigList.add(`port: ${legacyConfig.serve.port}`); } if (legacyConfig.serve.sslCredentials) { serveConfigList.add(`sslCredentials: ${JSON.stringify(legacyConfig.serve.sslCredentials)}`); } if (legacyConfig.serve.staticFiles) { addImport(importMap, '@graphql-hive/gateway', 'useStaticFiles'); pluginList.add(`useStaticFiles(${JSON.stringify(legacyConfig.serve.staticFiles)})`); } } if (legacyConfig.skipSSLValidation) { configList.add(`process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';`); } serveConfigList.add(`plugins: ctx => ([ ${Array.from(pluginList).join(',\n')} ])`); addImport(importMap, '@graphql-hive/gateway', 'defineConfig as defineGatewayConfig'); const serveConfig = ` export const gatewayConfig = defineGatewayConfig({ ${Array.from(serveConfigList).join(',\n')} }); `; for (const [packageName, imports] of importMap) { configList.add(`import { ${Array.from(imports).join(', ')} } from '${packageName}';`); } configList.add(composeConfig); configList.add(serveConfig); const newConfigPath = (0, node_path_1.join)(cwd, 'mesh.config.ts'); let configContent = Array.from(configList).join('\n'); configContent = await (0, prettier_1.format)(configContent, { parser: 'typescript', }); (0, node_fs_1.writeFileSync)(newConfigPath, configContent); console.log('Migration successful!'); console.log(' '); console.log(`New config file created at ${newConfigPath}`); console.log(' '); console.log('Please make sure to install the following packages in package.json:'); for (const packageName of addedPackages) { console.log(`- ${packageName}`); } console.log(' '); console.log('Please make sure to remove the following packages in package.json:'); for (const packageName of removedPackages) { console.log(`- ${packageName}`); } console.log(' '); console.log(`Then run "npx mesh-compose -o supergraph.graphql" to generate the supergraph schema!`); console.log(`Finally, run "npx hive-gateway supergraph" to start the gateway server!`); } function addImport(importMap, packageName, importName) { let set = importMap.get(packageName); if (!set) { set = new Set(); importMap.set(packageName, set); } set.add(importName); } const handlerInfoMap = { graphql: { packageName: '@graphql-mesh/compose-cli', oldPackageName: '@graphql-mesh/graphql', importName: 'loadGraphQLHTTPSubgraph', }, grpc: { packageName: '@omnigraph/grpc', oldPackageName: '@graphql-mesh/grpc', importName: 'loadGRPCSubgraph', }, jsonSchema: { packageName: '@omnigraph/json-schema', oldPackageName: '@graphql-mesh/json-schema', importName: 'loadJSONSchemaSubgraph', }, mongoose: { packageName: '@omnigraph/mongoose', oldPackageName: '@graphql-mesh/mongoose', importName: 'loadMongooseSubgraph', }, mysql: { packageName: '@omnigraph/mysql', oldPackageName: '@graphql-mesh/mysql', importName: 'loadMySQLSubgraph', }, neo4j: { packageName: '@omnigraph/neo4j', oldPackageName: '@graphql-mesh/neo4j', importName: 'loadNeo4jSubgraph', }, odata: { packageName: '@omnigraph/odata', oldPackageName: '@graphql-mesh/odata', importName: 'loadODataSubgraph', }, postgraphile: { packageName: '@omnigraph/postgresql', oldPackageName: '@graphql-mesh/postgraphile', importName: 'loadPostgreSQLSubgraph', }, raml: { packageName: '@omnigraph/raml', oldPackageName: '@graphql-mesh/raml', importName: 'loadRAMLSubgraph', }, openapi: { packageName: '@omnigraph/openapi', oldPackageName: '@graphql-mesh/openapi', importName: 'loadOpenAPISubgraph', }, soap: { packageName: '@omnigraph/soap', oldPackageName: '@graphql-mesh/soap', importName: 'loadSOAPSubgraph', }, thrift: { packageName: '@omnigraph/thrift', oldPackageName: '@graphql-mesh/thrift', importName: 'loadThriftSubgraph', }, tuql: { packageName: '@omnigraph/sqlite', oldPackageName: '@graphql-mesh/tuql', importName: 'loadSQLiteSubgraph', }, }; const transformInfoMap = { cache: { deprecated: 'Cache Transform has been deprecated, please use response caching plugin instead!', }, encapsulate: { fnName: 'createEncapsulateTransform', }, extend: { deprecated: 'Extend Transform has been deprecated, please use additionalTypeDefs instead!', }, federation: { fnName: 'createFederationTransform', }, filterSchema: { fnName: 'createFilterTransform', }, hoistField: { fnName: 'createHoistFieldTransform', }, namingConvention: { fnName: 'createNamingConventionTransform', }, prefix: { fnName: 'createPrefixTransform', }, rateLimit: { deprecated: 'RateLimit Transform has been deprecated, please use rate limiting plugin instead!', }, rename: { fnName: 'createRenameTransform', }, replaceField: { deprecated: 'ReplaceField Transform has been deprecated, please check other alternatives like Hoist Field Transform!', }, resolversComposition: { deprecated: 'ResolversComposition Transform has been deprecated, please use alternatives or additionalResolvers instead!', }, typeMerging: { deprecated: 'TypeMerging Transform has been deprecated, please use Federation Transform instead!', }, }; function handleTransformConfiguration(importMap, transformConfiguration) { const transformName = Object.keys(transformConfiguration)[0]; const transformConfig = transformConfiguration[transformName]; const transformInfo = transformInfoMap[transformName]; if (transformInfo.deprecated) { console.error(transformInfo.deprecated); process.exit(1); } addImport(importMap, '@graphql-mesh/compose-cli', transformInfo.fnName); return `${transformInfo.fnName}(${JSON.stringify(transformConfig)})`; }