UNPKG

@baseplate-dev/core-generators

Version:

Core generators for Baseplate

124 lines (120 loc) 5.86 kB
import { TEMPLATE_EXTRACTOR_GENERATED_DIRECTORY } from '@baseplate-dev/sync'; import { posixJoin } from '@baseplate-dev/utils/node'; import { isEqual } from 'es-toolkit'; import { getGeneratedTemplateConstantName, getGeneratedTemplateExportName, getGeneratedTemplateInterfaceName, getGeneratedTemplateProviderName, } from '#src/renderers/extractor/utils/index.js'; import { tsImportBuilder } from '#src/renderers/typescript/imports/index.js'; import { tsCodeFragment } from '#src/renderers/typescript/index.js'; import { renderTsCodeFileTemplate } from '#src/renderers/typescript/renderers/index.js'; import { TsCodeUtils, tsTemplate, } from '#src/renderers/typescript/utils/index.js'; export const GENERATED_RENDERERS_FILE_NAME = 'template-renderers.ts'; const GENERATED_RENDERERS_FILE_PATH = posixJoin(TEMPLATE_EXTRACTOR_GENERATED_DIRECTORY, GENERATED_RENDERERS_FILE_NAME); const GENERATED_RENDERERS_TEMPLATE = ` import { createProviderType } from '@baseplate-dev/sync'; export interface TPL_PROVIDER_TYPE_NAME { TPL_PROVIDER_TYPE_CONTENTS; } const TPL_PROVIDER_EXPORT_NAME = createProviderType<TPL_PROVIDER_TYPE_NAME>('TPL_PROVIDER_NAME'); const TPL_TASK_EXPORT_NAME = TPL_TASK_DECLARATION; export const TPL_EXPORT_NAME = { provider: TPL_PROVIDER_EXPORT_NAME, task: TPL_TASK_EXPORT_NAME, }; `; function getRenderersFileExportNames(generatorName) { return { interfaceName: getGeneratedTemplateInterfaceName(generatorName, 'renderers'), providerExportName: getGeneratedTemplateExportName(generatorName, 'renderers'), taskExportName: getGeneratedTemplateExportName(generatorName, 'renderers-task'), rootExportName: getGeneratedTemplateConstantName(generatorName, 'renderers'), }; } function createRenderersTask(generatorName, renderers, context, { providerExportName }) { // Collect all unique task dependencies from renderer definitions const allDependencies = new Map(); for (const renderer of renderers) { for (const dep of renderer.taskDependencies) { // Check if the dependency already exists const existingDep = allDependencies.get(dep.name); if (existingDep) { if (!isEqual(existingDep, dep)) { throw new Error(`Duplicate dependency name ${dep.name} with different providers: ` + `${existingDep.providerExpression ?? existingDep.providerImportName} (${existingDep.providerImportSpecifier}) ` + `and ${dep.providerExpression ?? dep.providerImportName} (${dep.providerImportSpecifier})`); } continue; } // Add the dependency allDependencies.set(dep.name, dep); } } // Create dependencies object from collected dependencies const dependencies = TsCodeUtils.mergeFragmentsAsObject({ ...Object.fromEntries([...allDependencies].map(([name, dep]) => [ name, tsCodeFragment(dep.providerExpression ?? dep.providerImportName, [ tsImportBuilder([dep.providerImportName]).from(dep.providerImportSpecifier), ]), ])), }); const taskExportKey = providerExportName.replace(/Provider$/, ''); // Create exports object const exports = TsCodeUtils.mergeFragmentsAsObject({ [taskExportKey]: `${providerExportName}.export()`, }); // Create renderer functions from definitions const rendererFunctions = TsCodeUtils.mergeFragmentsAsObject(Object.fromEntries(renderers.map((renderer) => [ renderer.name, tsTemplate `{ render: ${renderer.renderFunction} }`, ]))); // Generate run function with dynamic dependency names const dependencyNames = [...allDependencies.keys()].sort(); const run = tsTemplate ` function run({ ${dependencyNames.join(', ')} }) { return { providers: { ${providerExportName}: ${rendererFunctions} } } } `; const taskOptions = TsCodeUtils.mergeFragmentsAsObject({ dependencies, exports, run, }); return TsCodeUtils.templateWithImports([ tsImportBuilder(['createGeneratorTask']).from('@baseplate-dev/sync'), ]) `createGeneratorTask(${taskOptions});`; } export async function writeRenderersFile(generatorName, renderers, context) { const extractorConfig = context.configLookup.getExtractorConfigOrThrow(generatorName); const renderersFilePath = posixJoin(extractorConfig.generatorDirectory, GENERATED_RENDERERS_FILE_PATH); const fileExportNames = getRenderersFileExportNames(generatorName); const { interfaceName, providerExportName, taskExportName } = fileExportNames; const taskFragment = createRenderersTask(generatorName, renderers, context, fileExportNames); const renderersFileContents = renderTsCodeFileTemplate({ templateContents: GENERATED_RENDERERS_TEMPLATE, variables: { TPL_PROVIDER_TYPE_NAME: interfaceName, TPL_PROVIDER_TYPE_CONTENTS: TsCodeUtils.mergeFragmentsAsInterfaceContent(new Map(renderers.map((renderer) => [ renderer.name, tsTemplate `{ render: ${renderer.renderType} }`, ]))), TPL_PROVIDER_EXPORT_NAME: providerExportName, TPL_PROVIDER_NAME: getGeneratedTemplateProviderName(generatorName, 'renderers'), TPL_TASK_DECLARATION: taskFragment, TPL_TASK_EXPORT_NAME: taskExportName, TPL_EXPORT_NAME: fileExportNames.rootExportName, }, options: { importSortOptions: { internalPatterns: [/^#src/], }, }, }); await context.fileContainer.writeFile(renderersFilePath, renderersFileContents); return { exportName: fileExportNames.rootExportName, }; } //# sourceMappingURL=renderers-file.js.map