UNPKG

@react-native-windows/codegen

Version:

Generators for react-native-codegen targeting react-native-windows

227 lines 11.1 kB
"use strict"; /** * Copyright (c) Microsoft Corporation. * Licensed under the MIT License. * * @format */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runCodeGen = exports.generate = exports.combineSchemas = exports.parseFile = void 0; const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("@react-native-windows/fs")); const globby_1 = __importDefault(require("globby")); const GenerateNM2_1 = require("./generators/GenerateNM2"); const GenerateComponentWindows_1 = require("./generators/GenerateComponentWindows"); const GenerateTypeScript_1 = require("./generators/GenerateTypeScript"); // Load @react-native/codegen from react-native const rnPath = path_1.default.dirname(require.resolve('react-native/package.json')); const rncodegenPath = path_1.default.dirname(require.resolve('@react-native/codegen/package.json', { paths: [rnPath] })); function getParser(isTypeScript) { if (isTypeScript) { const fp = require(path_1.default.resolve(rncodegenPath, 'lib/parsers/typescript/parser')); return new fp.TypeScriptParser(); } else { const fp = require(path_1.default.resolve(rncodegenPath, 'lib/parsers/flow/parser')); return new fp.FlowParser(); } } const schemaValidator = require(path_1.default.resolve(rncodegenPath, 'lib/SchemaValidator')); function normalizeFileMap(map, outputDir, outMap) { for (const [fileName, contents] of map) { const location = path_1.default.join(outputDir, fileName); outMap.set(path_1.default.normalize(location), contents); } } function checkFilesForChanges(map, outputDir) { let hasChanges = false; outputDir = path_1.default.resolve(outputDir); const globbyDir = outputDir.replace(/\\/g, '/'); const allExistingFiles = globby_1.default .sync([`${globbyDir}/**`, `${globbyDir}/**/.*`], { absolute: true }) .map(_ => path_1.default.normalize(_)); const allGeneratedFiles = [...map.keys()].map(_ => path_1.default.normalize(_)).sort(); if (allExistingFiles.length !== allGeneratedFiles.length || !allGeneratedFiles.every(filepath => allExistingFiles.includes(path_1.default.normalize(path_1.default.resolve(process.cwd(), filepath))))) return true; for (const [fileName, contents] of map) { if (!fs_1.default.existsSync(fileName)) { hasChanges = true; continue; } const currentContents = fs_1.default.readFileSync(fileName, 'utf8'); if (currentContents !== contents) { console.log(`- ${fileName} has changed`); hasChanges = true; continue; } } return hasChanges; } function writeMapToFiles(map, outputDir) { let success = true; outputDir = path_1.default.resolve(outputDir); const globbyDir = outputDir.replace(/\\/g, '/'); // This ensures that we delete any generated files from modules that have been deleted const allExistingFiles = globby_1.default.sync([`${globbyDir}/**`, `${globbyDir}/**/.*`], { absolute: true }); const allGeneratedFiles = [...map.keys()].map(_ => path_1.default.normalize(_)).sort(); allExistingFiles.forEach(existingFile => { if (!allGeneratedFiles.includes(path_1.default.normalize(existingFile))) { console.log('Deleting ', existingFile); fs_1.default.unlinkSync(existingFile); } }); for (const [fileName, contents] of map) { try { fs_1.default.mkdirSync(path_1.default.dirname(fileName), { recursive: true }); if (fs_1.default.existsSync(fileName)) { const currentContents = fs_1.default.readFileSync(fileName, 'utf8'); // Don't update the files if there are no changes as this breaks incremental builds if (currentContents === contents) { continue; } } console.log('Writing ', fileName); fs_1.default.writeFileSync(fileName, contents); } catch (error) { success = false; console.error(`Failed to write ${fileName} to ${fileName}`, error); } } return success; } function parseFile(filename) { try { const isTypeScript = path_1.default.extname(filename) === '.ts' || path_1.default.extname(filename) === '.tsx'; const contents = fs_1.default.readFileSync(filename, 'utf8'); const schema = getParser(isTypeScript).parseString(contents, filename); // there will be at most one turbo module per file const moduleName = Object.keys(schema.modules)[0]; if (moduleName) { const spec = schema.modules[moduleName]; if (spec.type === 'NativeModule') { if (contents) { // This is a temporary implementation until such information is added to the schema in facebook/react-native if (contents.includes('TurboModuleRegistry.get<')) { (0, GenerateTypeScript_1.setOptionalTurboModule)(spec, true); } else if (contents.includes('TurboModuleRegistry.getEnforcing<')) { (0, GenerateTypeScript_1.setOptionalTurboModule)(spec, false); } } } } return schema; } catch (e) { if (e instanceof Error) { e.message = `(${filename}): ${e.message}`; } throw e; } } exports.parseFile = parseFile; function combineSchemas(files) { return files.reduce((merged, filename) => { const contents = fs_1.default.readFileSync(filename, 'utf8'); if (contents && (/export\s+default\s+\(?codegenNativeComponent</.test(contents) || contents.includes('extends TurboModule'))) { const schema = parseFile(filename); merged.modules = { ...merged.modules, ...schema.modules }; } return merged; }, { modules: {} }); } exports.combineSchemas = combineSchemas; function generate({ libraryName, methodOnly, modulesCxx, modulesTypeScriptTypes, modulesWindows, internalComponents, componentsWindows, namespace, outputDirectory, cppStringType, separateDataTypes, moduleSpecName, schema, }, { /*generators,*/ test }) { schemaValidator.validate(schema); const componentOutputdir = path_1.default.join(outputDirectory, 'react/components', libraryName); const generatedFiles = new Map(); generatedFiles.set(path_1.default.join(outputDirectory, '.clang-format'), 'DisableFormat: true\nSortIncludes: false'); const generateNM2 = (0, GenerateNM2_1.createNM2Generator)({ methodOnly, namespace, cppStringType, separateDataTypes, }); const generateJsiModuleH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/modules/GenerateModuleH')).generate; const generateJsiModuleCpp = require(path_1.default.resolve(rncodegenPath, 'lib/generators/modules/GenerateModuleCpp')).generate; const generatorPropsH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GeneratePropsH')).generate; const generatorPropsCPP = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GeneratePropsCpp')).generate; const generatorShadowNodeH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateShadowNodeH')).generate; const generatorShadowNodeCPP = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateShadowNodeCpp')).generate; const generatorComponentDescriptorH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateComponentDescriptorH')).generate; const generatorEventEmitterH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateEventEmitterH')).generate; const generatorEventEmitterCPP = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateEventEmitterCpp')).generate; const generatorStateCPP = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateStateCpp')).generate; const generatorStateH = require(path_1.default.resolve(rncodegenPath, 'lib/generators/components/GenerateStateH')).generate; const moduleGenerators = []; if (modulesWindows) { moduleGenerators.push(generateNM2); } if (modulesCxx) { moduleGenerators.push(generateJsiModuleH); moduleGenerators.push(generateJsiModuleCpp); } if (modulesTypeScriptTypes) { moduleGenerators.push(GenerateTypeScript_1.generateTypeScript); } moduleGenerators.forEach(generator => { const generated = generator(libraryName, schema, moduleSpecName); normalizeFileMap(generated, outputDirectory, generatedFiles); }); if (Object.keys(schema.modules).some(moduleName => schema.modules[moduleName].type === 'Component')) { const componentGenerators = []; if (internalComponents) { componentGenerators.push(generatorComponentDescriptorH, generatorEventEmitterCPP, generatorEventEmitterH, generatorPropsCPP, generatorPropsH, generatorShadowNodeCPP, generatorShadowNodeH, generatorStateCPP, generatorStateH); } if (componentsWindows) { const generateComponentWindows = (0, GenerateComponentWindows_1.createComponentGenerator)({ namespace, cppStringType, }); componentGenerators.push(generateComponentWindows); } componentGenerators.forEach(generator => { const generated = generator(libraryName, schema, moduleSpecName); normalizeFileMap(generated, componentOutputdir, generatedFiles); }); } if (test === true) { return checkFilesForChanges(generatedFiles, outputDirectory); } return writeMapToFiles(generatedFiles, outputDirectory); } exports.generate = generate; function runCodeGen(options) { if (!options.file && !options.files) throw new Error('Must specify file or files option'); const schema = options.file ? parseFile(options.file) : combineSchemas(globby_1.default.sync(options.files)); const libraryName = options.libraryName; const moduleSpecName = 'moduleSpecName'; const { methodOnly, modulesCxx, modulesTypeScriptTypes, modulesWindows, componentsWindows, internalComponents, namespace, outputDirectory, cppStringType, separateDataTypes, } = options; return generate({ libraryName, methodOnly, modulesCxx, modulesTypeScriptTypes, modulesWindows, componentsWindows, internalComponents, namespace, outputDirectory, cppStringType, separateDataTypes, moduleSpecName, schema, }, { generators: [], test: options.test }); } exports.runCodeGen = runCodeGen; //# sourceMappingURL=index.js.map