UNPKG

nitro-codegen

Version:

The code-generator for react-native-nitro-modules.

166 lines (165 loc) 7.96 kB
import { Project } from 'ts-morph'; import { extendsHybridObject, isHybridView, getHybridObjectPlatforms, getHybridViewPlatforms, } from './getPlatformSpecs.js'; import { generatePlatformFiles } from './createPlatformSpec.js'; import path from 'path'; import { prettifyDirectory } from './prettifyDirectory.js'; import { capitalizeName, errorToString, filterDuplicateFiles, indent, } from './utils.js'; import { writeFile } from './writeFile.js'; import chalk from 'chalk'; import { groupByPlatform } from './syntax/SourceFile.js'; import { Logger } from './Logger.js'; import { NitroConfig } from './config/NitroConfig.js'; import { createIOSAutolinking } from './autolinking/createIOSAutolinking.js'; import { createAndroidAutolinking } from './autolinking/createAndroidAutolinking.js'; import { createGitAttributes } from './createGitAttributes.js'; export async function runNitrogen({ baseDirectory, outputDirectory, }) { let targetSpecs = 0; let generatedSpecs = 0; // Create the TS project const project = new Project({ compilerOptions: { strict: true, strictNullChecks: true, noUncheckedIndexedAccess: true, }, }); const ignorePaths = NitroConfig.getIgnorePaths(); const globPattern = [path.join(baseDirectory, '**', '*.nitro.ts')]; ignorePaths.forEach((ignorePath) => { globPattern.push('!' + path.join(baseDirectory, ignorePath)); }); project.addSourceFilesAtPaths(globPattern); // Loop through all source files to log them Logger.info(chalk.reset(`🚀 Nitrogen runs at ${chalk.underline(prettifyDirectory(baseDirectory))}`)); for (const dir of project.getDirectories()) { const specs = dir.getSourceFiles().length; const relativePath = prettifyDirectory(dir.getPath()); Logger.info(` 🔍 Nitrogen found ${specs} spec${specs === 1 ? '' : 's'} in ${chalk.underline(relativePath)}`); } // If no source files are found, we can exit if (project.getSourceFiles().length === 0) { const searchDir = prettifyDirectory(path.join(path.resolve(baseDirectory), '**', '*.nitro.ts')); Logger.error(`❌ Nitrogen didn't find any spec files in ${chalk.underline(searchDir)}! ` + `To create a Nitro Module, create a TypeScript file with the "${chalk.underline('.nitro.ts')}" suffix ` + 'and export an interface that extends HybridObject<T>.'); process.exit(); } const usedPlatforms = []; const filesAfter = []; const writtenFiles = []; for (const sourceFile of project.getSourceFiles()) { Logger.info(`⏳ Parsing ${sourceFile.getBaseName()}...`); const startedWithSpecs = generatedSpecs; // Find all interfaceDeclarations in the given file const declarations = [ ...sourceFile.getInterfaces(), ...sourceFile.getTypeAliases(), ]; for (const declaration of declarations) { let typeName = declaration.getName(); try { let platformSpec; if (isHybridView(declaration.getType())) { // Hybrid View Props const targetPlatforms = getHybridViewPlatforms(declaration); if (targetPlatforms == null) { // It does not extend HybridView, continue.. continue; } platformSpec = targetPlatforms; } else if (extendsHybridObject(declaration.getType(), true)) { // Hybrid View const targetPlatforms = getHybridObjectPlatforms(declaration); if (targetPlatforms == null) { // It does not extend HybridObject, continue.. continue; } platformSpec = targetPlatforms; } else { continue; } const platforms = Object.keys(platformSpec); if (platforms.length === 0) { Logger.warn(`⚠️ ${typeName} does not declare any platforms in HybridObject<T> - nothing can be generated.`); continue; } targetSpecs++; Logger.info(` ⚙️ Generating specs for HybridObject "${chalk.bold(typeName)}"...`); // Create all files and throw it into a big list const allFiles = platforms .flatMap((p) => { usedPlatforms.push(p); const language = platformSpec[p]; return generatePlatformFiles(declaration.getType(), language); }) .filter(filterDuplicateFiles); // Group the files by platform ({ ios: [], android: [], shared: [] }) const filesPerPlatform = groupByPlatform(allFiles); // Loop through each platform one by one so that it has some kind of order (per-platform) for (const [p, files] of Object.entries(filesPerPlatform)) { const platform = p; const language = platform === 'shared' ? 'c++' : platformSpec[platform]; if (language == null) { // if the language was never specified in the spec, skip it continue; } if (files.length === 0) { // if no files exist on this platform, skip it continue; } Logger.info(` ${chalk.dim(platform)}: Generating ${capitalizeName(language)} code...`); // Write the actual files for this specific platform. for (const file of files) { const basePath = path.join(outputDirectory, file.platform, file.language); const actualPath = await writeFile(basePath, file); filesAfter.push(actualPath); writtenFiles.push(file); } } // Done! generatedSpecs++; } catch (error) { const message = indent(errorToString(error), ' '); Logger.error(chalk.redBright(` ❌ Failed to generate spec for ${typeName}! ${message}`)); process.exitCode = 1; } } if (generatedSpecs === startedWithSpecs) { Logger.error(chalk.redBright(` ❌ No specs found in ${sourceFile.getBaseName()}!`)); } } // Autolinking Logger.info(`⛓️ Setting up build configs for autolinking...`); const autolinkingFiles = []; if (usedPlatforms.includes('ios')) { autolinkingFiles.push(createIOSAutolinking()); } if (usedPlatforms.includes('android')) { autolinkingFiles.push(createAndroidAutolinking(writtenFiles)); } for (const autolinking of autolinkingFiles) { Logger.info(` Creating autolinking build setup for ${chalk.dim(autolinking.platform)}...`); for (const file of autolinking.sourceFiles) { const basePath = path.join(outputDirectory, file.platform); const actualPath = await writeFile(basePath, file); filesAfter.push(actualPath); } } try { // write a .gitattributes file const markAsGenerated = NitroConfig.getGitAttributesGeneratedFlag(); const file = await createGitAttributes(markAsGenerated, outputDirectory); filesAfter.push(file); } catch { Logger.error(`❌ Failed to write ${chalk.dim(`.gitattributes`)}!`); } return { generatedFiles: filesAfter, targetSpecsCount: targetSpecs, generatedSpecsCount: generatedSpecs, }; }