@react-native-windows/codegen
Version:
Generators for react-native-codegen targeting react-native-windows
227 lines • 11.1 kB
JavaScript
"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