mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat(iOS): add colors to codegen output (#46557)
Summary: This PR is a quality-of-life improvement, adds colors to codegen output:  Makes it a lot easier to grasp what's going on in the actions taken by codegen. ## Changelog: [GENERAL] [ADDED] - Add color formatting to codegen output Pull Request resolved: https://github.com/facebook/react-native/pull/46557 Test Plan: 1. Run codegen 2. Expect colored output Reviewed By: christophpurrer Differential Revision: D63031248 Pulled By: cipolleschi fbshipit-source-id: e86bc72f16582562dc9f1e3b55dc69af4c7838f5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
9e2e8c1a27
commit
1747f57c67
@@ -61,10 +61,10 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
CodegenUtils.set_react_codegen_podspec_generated(true)
|
||||
|
||||
# Act
|
||||
CodegenUtils.new().generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: FileMock)
|
||||
CodegenUtils.new().generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: FileMock, logger: Pod::UI)
|
||||
|
||||
# Assert
|
||||
assert_equal(Pod::UI.collected_messages, ["[Codegen] Skipping ReactCodegen podspec generation."])
|
||||
assert_equal(Pod::UI.collected_messages, ["Skipping ReactCodegen podspec generation."])
|
||||
assert_equal(Pathname.pwd_invocation_count, 0)
|
||||
assert_equal(Pod::Executable.executed_commands, [])
|
||||
assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 0)
|
||||
@@ -77,13 +77,13 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
codegen_output_dir = "build"
|
||||
|
||||
# Act
|
||||
CodegenUtils.new().generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: FileMock)
|
||||
CodegenUtils.new().generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: FileMock, logger: Pod::UI)
|
||||
|
||||
# Assert
|
||||
assert_equal(Pathname.pwd_invocation_count, 1)
|
||||
assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 1)
|
||||
assert_equal(Pod::Executable.executed_commands, [{ "command" => 'mkdir', "arguments" => ["-p", "~/app/ios/build"]}])
|
||||
assert_equal(Pod::UI.collected_messages, ["[Codegen] Generating ~/app/ios/build/ReactCodegen.podspec.json"])
|
||||
assert_equal(Pod::UI.collected_messages, ["Generating ~/app/ios/build/ReactCodegen.podspec.json"])
|
||||
assert_equal(FileMock.open_files_with_mode["~/app/ios/build/ReactCodegen.podspec.json"], 'w')
|
||||
assert_equal(FileMock.open_files[0].collected_write, ['{"name":"Test Podspec"}'])
|
||||
assert_equal(FileMock.open_files[0].fsync_invocation_count, 1)
|
||||
@@ -103,12 +103,13 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
'package.json',
|
||||
:hermes_enabled => true,
|
||||
:script_phases => "echo Test Script Phase",
|
||||
:file_manager => FileMock
|
||||
:file_manager => FileMock,
|
||||
:logger => Pod::UI
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert_equal(podspec, get_podspec_fabric_and_script_phases("echo Test Script Phase"))
|
||||
assert_equal(Pod::UI.collected_messages, ["[Codegen] Adding script_phases to ReactCodegen."])
|
||||
assert_equal(Pod::UI.collected_messages, ["Adding script_phases to ReactCodegen."])
|
||||
end
|
||||
|
||||
def testGetReactCodegenSpec_whenUseFrameworksAndNewArch_generatesAPodspec
|
||||
@@ -238,10 +239,10 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
|
||||
# Act
|
||||
assert_raises() {
|
||||
CodegenUtils.new().get_react_codegen_script_phases(nil, file_manager: FileMock)
|
||||
CodegenUtils.new().get_react_codegen_script_phases(nil, file_manager: FileMock, logger: Pod::UI)
|
||||
}
|
||||
# Assert
|
||||
assert_equal(Pod::UI.collected_warns, ["[Codegen] error: app_path is required to use codegen discovery."])
|
||||
assert_equal(Pod::UI.collected_warns, ["error: app_path is required to use codegen discovery."])
|
||||
end
|
||||
|
||||
def testGetReactCodegenScriptPhases_returnTheScriptObject
|
||||
@@ -308,11 +309,11 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
CodegenUtils.set_react_codegen_discovery_done(true)
|
||||
|
||||
# Act
|
||||
CodegenUtils.new().use_react_native_codegen_discovery!(false, nil, file_manager: FileMock)
|
||||
CodegenUtils.new().use_react_native_codegen_discovery!(false, nil, file_manager: FileMock, logger: Pod::UI)
|
||||
|
||||
# Assert
|
||||
assert_true(CodegenUtils.react_codegen_discovery_done())
|
||||
assert_equal(Pod::UI.collected_messages, ["[Codegen] Skipping use_react_native_codegen_discovery."])
|
||||
assert_equal(Pod::UI.collected_messages, ["Skipping use_react_native_codegen_discovery."])
|
||||
assert_equal(Pod::UI.collected_warns, [])
|
||||
end
|
||||
|
||||
@@ -321,15 +322,15 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
|
||||
# Act
|
||||
assert_raises(){
|
||||
CodegenUtils.new().use_react_native_codegen_discovery!(false, nil, file_manager: FileMock)
|
||||
CodegenUtils.new().use_react_native_codegen_discovery!(false, nil, file_manager: FileMock, logger: Pod::UI)
|
||||
}
|
||||
|
||||
# Assert
|
||||
assert_false(CodegenUtils.react_codegen_discovery_done())
|
||||
assert_equal(Pod::UI.collected_messages, [])
|
||||
assert_equal(Pod::UI.collected_warns, [
|
||||
'[Codegen] Error: app_path is required for use_react_native_codegen_discovery.',
|
||||
'[Codegen] If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.'
|
||||
'Error: app_path is required for use_react_native_codegen_discovery.',
|
||||
'If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.'
|
||||
])
|
||||
end
|
||||
|
||||
@@ -349,13 +350,14 @@ class CodegenUtilsTests < Test::Unit::TestCase
|
||||
false,
|
||||
app_path,
|
||||
:codegen_utils => codegen_utils_mock,
|
||||
:file_manager => FileMock
|
||||
:file_manager => FileMock,
|
||||
:logger => Pod::UI
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert_true(CodegenUtils.react_codegen_discovery_done())
|
||||
assert_equal(Pod::UI.collected_warns, [
|
||||
'[Codegen] warn: using experimental new codegen integration'
|
||||
'warn: using experimental new codegen integration'
|
||||
])
|
||||
assert_equal(codegen_utils_mock.get_react_codegen_script_phases_params, [{
|
||||
:app_path => app_path,
|
||||
|
||||
@@ -39,11 +39,11 @@ class CodegenUtils
|
||||
# - spec: the cocoapod specs
|
||||
# - codegen_output_dir: the output directory for the codegen
|
||||
# - file_manager: a class that implements the `File` interface. Defaults to `File`, the Dependency can be injected for testing purposes.
|
||||
def generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: File)
|
||||
def generate_react_codegen_podspec!(spec, codegen_output_dir, file_manager: File, logger: CodegenUtils::UI)
|
||||
# This podspec file should only be create once in the session/pod install.
|
||||
# This happens when multiple targets are calling use_react_native!.
|
||||
if @@REACT_CODEGEN_PODSPEC_GENERATED
|
||||
Pod::UI.puts "[Codegen] Skipping ReactCodegen podspec generation."
|
||||
logger.puts("Skipping ReactCodegen podspec generation.")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ class CodegenUtils
|
||||
Pod::Executable.execute_command("mkdir", ["-p", output_dir]);
|
||||
|
||||
podspec_path = file_manager.join(output_dir, 'ReactCodegen.podspec.json')
|
||||
Pod::UI.puts "[Codegen] Generating #{podspec_path}"
|
||||
logger.puts("Generating #{podspec_path}")
|
||||
|
||||
file_manager.open(podspec_path, 'w') do |f|
|
||||
f.write(spec.to_json)
|
||||
@@ -69,7 +69,7 @@ class CodegenUtils
|
||||
# - hermes_enabled: whether hermes is enabled or not.
|
||||
# - script_phases: whether we want to add some build script phases or not.
|
||||
# - file_manager: a class that implements the `File` interface. Defaults to `File`, the Dependency can be injected for testing purposes.
|
||||
def get_react_codegen_spec(package_json_file, folly_version: get_folly_config()[:version], hermes_enabled: true, script_phases: nil, file_manager: File)
|
||||
def get_react_codegen_spec(package_json_file, folly_version: get_folly_config()[:version], hermes_enabled: true, script_phases: nil, file_manager: File, logger: CodegenUtils::UI)
|
||||
package = JSON.parse(file_manager.read(package_json_file))
|
||||
version = package['version']
|
||||
use_frameworks = ENV['USE_FRAMEWORKS'] != nil
|
||||
@@ -157,7 +157,7 @@ class CodegenUtils
|
||||
end
|
||||
|
||||
if script_phases
|
||||
Pod::UI.puts "[Codegen] Adding script_phases to ReactCodegen."
|
||||
logger.puts("Adding script_phases to ReactCodegen.")
|
||||
spec[:'script_phases'] = script_phases
|
||||
end
|
||||
|
||||
@@ -231,10 +231,11 @@ class CodegenUtils
|
||||
config_key: 'codegenConfig',
|
||||
codegen_utils: CodegenUtils.new(),
|
||||
script_phase_extractor: CodegenScriptPhaseExtractor.new(),
|
||||
file_manager: File
|
||||
file_manager: File,
|
||||
logger: CodegenUtils::UI
|
||||
)
|
||||
if !app_path
|
||||
Pod::UI.warn '[Codegen] error: app_path is required to use codegen discovery.'
|
||||
logger.warn("error: app_path is required to use codegen discovery.")
|
||||
abort
|
||||
end
|
||||
|
||||
@@ -281,22 +282,23 @@ class CodegenUtils
|
||||
config_key: 'codegenConfig',
|
||||
folly_version: get_folly_config()[:version],
|
||||
codegen_utils: CodegenUtils.new(),
|
||||
file_manager: File
|
||||
file_manager: File,
|
||||
logger: CodegenUtils::UI
|
||||
)
|
||||
return if codegen_disabled
|
||||
|
||||
if CodegenUtils.react_codegen_discovery_done()
|
||||
Pod::UI.puts "[Codegen] Skipping use_react_native_codegen_discovery."
|
||||
logger.puts("Skipping use_react_native_codegen_discovery.")
|
||||
return
|
||||
end
|
||||
|
||||
if !app_path
|
||||
Pod::UI.warn '[Codegen] Error: app_path is required for use_react_native_codegen_discovery.'
|
||||
Pod::UI.warn '[Codegen] If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.'
|
||||
logger.warn("Error: app_path is required for use_react_native_codegen_discovery.")
|
||||
logger.warn("If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.")
|
||||
abort
|
||||
end
|
||||
|
||||
Pod::UI.warn '[Codegen] warn: using experimental new codegen integration'
|
||||
logger.warn("warn: using experimental new codegen integration")
|
||||
relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
|
||||
|
||||
# Generate ReactCodegen podspec here to add the script phases.
|
||||
@@ -362,4 +364,24 @@ class CodegenUtils
|
||||
abort
|
||||
end
|
||||
end
|
||||
|
||||
class UI
|
||||
# ANSI escape codes for colors and formatting
|
||||
CYAN = "\e[36m"
|
||||
YELLOW = "\e[33m"
|
||||
BOLD = "\e[1m"
|
||||
RESET = "\e[0m"
|
||||
|
||||
class << self
|
||||
def puts(text, info: false)
|
||||
prefix = "#{CYAN}#{BOLD}[Codegen]#{RESET}"
|
||||
message = info ? "#{YELLOW}#{text}#{RESET}" : text
|
||||
Pod::UI.puts "#{prefix} #{message}"
|
||||
end
|
||||
|
||||
def warn(text)
|
||||
puts(text, info: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,6 +76,17 @@ const MODULES_PROTOCOLS_MM_TEMPLATE_PATH = path.join(
|
||||
'RCTModulesConformingToProtocolsProviderMM.template',
|
||||
);
|
||||
|
||||
const codegenLog = (text, info = false) => {
|
||||
// ANSI escape codes for colors and formatting
|
||||
const reset = '\x1b[0m';
|
||||
const cyan = '\x1b[36m';
|
||||
const yellow = '\x1b[33m';
|
||||
const bold = '\x1b[1m';
|
||||
|
||||
const color = info ? yellow : '';
|
||||
console.log(`${cyan}${bold}[Codegen]${reset} ${color}${text}${reset}`);
|
||||
};
|
||||
|
||||
// HELPERS
|
||||
|
||||
function pkgJsonIncludesGeneratedCode(pkgJson) {
|
||||
@@ -98,11 +109,11 @@ function printDeprecationWarningIfNeeded(dependency) {
|
||||
if (dependency === REACT_NATIVE) {
|
||||
return;
|
||||
}
|
||||
console.log(`[Codegen] CodegenConfig Deprecated Setup for ${dependency}.
|
||||
codegenLog(`CodegenConfig Deprecated Setup for ${dependency}.
|
||||
The configuration file still contains the codegen in the libraries array.
|
||||
If possible, replace it with a single object.
|
||||
`);
|
||||
console.debug(`BEFORE:
|
||||
codegenLog(`BEFORE:
|
||||
{
|
||||
// ...
|
||||
"codegenConfig": {
|
||||
@@ -146,7 +157,7 @@ function extractLibrariesFromJSON(configFile, dependencyPath) {
|
||||
if (configFile.codegenConfig == null) {
|
||||
return [];
|
||||
}
|
||||
console.log(`[Codegen] Found ${configFile.name}`);
|
||||
codegenLog(`Found ${configFile.name}`);
|
||||
if (configFile.codegenConfig.libraries == null) {
|
||||
const config = configFile.codegenConfig;
|
||||
return [
|
||||
@@ -172,7 +183,7 @@ function getCocoaPodsPlatformKey(platformName) {
|
||||
}
|
||||
|
||||
function extractSupportedApplePlatforms(dependency, dependencyPath) {
|
||||
console.log('[Codegen] Searching for podspec in the project dependencies.');
|
||||
codegenLog('Searching for podspec in the project dependencies.', true);
|
||||
const podspecs = glob.sync('*.podspec', {cwd: dependencyPath});
|
||||
|
||||
if (podspecs.length === 0) {
|
||||
@@ -214,8 +225,8 @@ function extractSupportedApplePlatforms(dependency, dependencyPath) {
|
||||
);
|
||||
|
||||
if (supportedPlatformsList.length > 0) {
|
||||
console.log(
|
||||
`[Codegen] Supported Apple platforms: ${supportedPlatformsList.join(
|
||||
codegenLog(
|
||||
`Supported Apple platforms: ${supportedPlatformsList.join(
|
||||
', ',
|
||||
)} for ${dependency}`,
|
||||
);
|
||||
@@ -231,8 +242,9 @@ function findExternalLibraries(pkgJson, projectRoot) {
|
||||
...pkgJson.peerDependencies,
|
||||
};
|
||||
// Determine which of these are codegen-enabled libraries
|
||||
console.log(
|
||||
'[Codegen] Searching for codegen-enabled libraries in the project dependencies.',
|
||||
codegenLog(
|
||||
'Searching for codegen-enabled libraries in the project dependencies.',
|
||||
true,
|
||||
);
|
||||
// Handle third-party libraries
|
||||
return Object.keys(dependencies).flatMap(dependency => {
|
||||
@@ -253,8 +265,9 @@ function findExternalLibraries(pkgJson, projectRoot) {
|
||||
function findLibrariesFromReactNativeConfig(projectRoot) {
|
||||
const rnConfigFileName = 'react-native.config.js';
|
||||
|
||||
console.log(
|
||||
`\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${rnConfigFileName}`,
|
||||
codegenLog(
|
||||
`Searching for codegen-enabled libraries in ${rnConfigFileName}`,
|
||||
true,
|
||||
);
|
||||
|
||||
const rnConfigFilePath = path.resolve(projectRoot, rnConfigFileName);
|
||||
@@ -289,17 +302,18 @@ function findLibrariesFromReactNativeConfig(projectRoot) {
|
||||
}
|
||||
|
||||
function findProjectRootLibraries(pkgJson, projectRoot) {
|
||||
console.log('[Codegen] Searching for codegen-enabled libraries in the app.');
|
||||
codegenLog('Searching for codegen-enabled libraries in the app.', true);
|
||||
|
||||
if (pkgJson.codegenConfig == null) {
|
||||
console.log(
|
||||
'[Codegen] The "codegenConfig" field is not defined in package.json. Assuming there is nothing to generate at the app level.',
|
||||
codegenLog(
|
||||
'The "codegenConfig" field is not defined in package.json. Assuming there is nothing to generate at the app level.',
|
||||
true,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (typeof pkgJson.codegenConfig !== 'object') {
|
||||
throw '[Codegen] The "codegenConfig" field must be an Object.';
|
||||
throw 'The "codegenConfig" field must be an Object.';
|
||||
}
|
||||
|
||||
return extractLibrariesFromJSON(pkgJson, projectRoot);
|
||||
@@ -316,7 +330,7 @@ function buildCodegenIfNeeded() {
|
||||
if (fs.existsSync(libPath) && fs.readdirSync(libPath).length > 0) {
|
||||
return;
|
||||
}
|
||||
console.log('[Codegen] Building react-native-codegen package.');
|
||||
codegenLog('Building react-native-codegen package.', true);
|
||||
execSync('yarn install', {
|
||||
cwd: CODEGEN_REPO_PATH,
|
||||
stdio: 'inherit',
|
||||
@@ -394,7 +408,7 @@ function generateSchemaInfo(library, platform) {
|
||||
library.libraryPath,
|
||||
library.config.jsSrcsDir,
|
||||
);
|
||||
console.log(`[Codegen] Processing ${library.config.name}`);
|
||||
codegenLog(`Processing ${library.config.name}`);
|
||||
|
||||
const supportedApplePlatforms = extractSupportedApplePlatforms(
|
||||
library.config.name,
|
||||
@@ -436,8 +450,9 @@ function shouldSkipGenerationForRncore(schemaInfo, platform) {
|
||||
|
||||
function generateCode(outputPath, schemaInfo, includesGeneratedCode, platform) {
|
||||
if (shouldSkipGenerationForRncore(schemaInfo, platform)) {
|
||||
console.log(
|
||||
codegenLog(
|
||||
'[Codegen - rncore] Skipping iOS code generation for rncore as it has been generated already.',
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -447,9 +462,7 @@ function generateCode(outputPath, schemaInfo, includesGeneratedCode, platform) {
|
||||
const tmpOutputDir = path.join(tmpDir, 'out');
|
||||
fs.mkdirSync(tmpOutputDir, {recursive: true});
|
||||
|
||||
console.log(
|
||||
`[Codegen] Generating Native Code for ${libraryName} - ${platform}`,
|
||||
);
|
||||
codegenLog(`Generating Native Code for ${libraryName} - ${platform}`);
|
||||
const useLocalIncludePaths = includesGeneratedCode;
|
||||
generateSpecsCLIExecutor.generateSpecFromInMemorySchema(
|
||||
platform,
|
||||
@@ -466,7 +479,7 @@ function generateCode(outputPath, schemaInfo, includesGeneratedCode, platform) {
|
||||
reactNativeCoreLibraryOutputPath(libraryName, platform) ?? outputPath;
|
||||
fs.mkdirSync(outputDir, {recursive: true});
|
||||
fs.cpSync(tmpOutputDir, outputDir, {recursive: true});
|
||||
console.log(`[Codegen] Generated artifacts: ${outputDir}`);
|
||||
codegenLog(`Generated artifacts: ${outputDir}`);
|
||||
}
|
||||
|
||||
function generateSchemaInfos(libraries) {
|
||||
@@ -506,7 +519,7 @@ function mustGenerateNativeCode(includeLibraryPath, schemaInfo) {
|
||||
}
|
||||
|
||||
function createComponentProvider(schemas, supportedApplePlatforms) {
|
||||
console.log('[Codegen] Creating component provider.');
|
||||
codegenLog('Creating component provider.', true);
|
||||
const outputDir = path.join(
|
||||
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
|
||||
'React',
|
||||
@@ -523,7 +536,7 @@ function createComponentProvider(schemas, supportedApplePlatforms) {
|
||||
generators: ['providerIOS'],
|
||||
},
|
||||
);
|
||||
console.log(`[Codegen] Generated provider in: ${outputDir}`);
|
||||
codegenLog(`Generated provider in: ${outputDir}`);
|
||||
}
|
||||
|
||||
function findCodegenEnabledLibraries(pkgJson, projectRoot) {
|
||||
@@ -652,9 +665,7 @@ function generateRNCoreComponentsIOS(projectRoot /*: string */) /*: void*/ {
|
||||
*/
|
||||
function execute(projectRoot, targetPlatform, baseOutputPath) {
|
||||
try {
|
||||
console.log(
|
||||
`[Codegen] Analyzing ${path.join(projectRoot, 'package.json')}`,
|
||||
);
|
||||
codegenLog(`Analyzing ${path.join(projectRoot, 'package.json')}`);
|
||||
|
||||
const supportedPlatforms = ['android', 'ios'];
|
||||
if (
|
||||
@@ -675,7 +686,7 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
|
||||
const libraries = findCodegenEnabledLibraries(pkgJson, projectRoot);
|
||||
|
||||
if (libraries.length === 0) {
|
||||
console.log('[Codegen] No codegen-enabled libraries found.');
|
||||
codegenLog('No codegen-enabled libraries found.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -718,11 +729,11 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
|
||||
cleanupEmptyFilesAndFolders(outputPath);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
codegenLog(err);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
console.log('[Codegen] Done.');
|
||||
codegenLog('Done.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user