mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat: support custom library paths in react-native.config.js for codegen on iOS (#34580)
Summary:
Currently for codegen to work for a library on iOS, it needs to be located
inside `node_modules`. This patch adds support for libraries defined in
`react-native.config.js`.
This is useful when developing libraries as well as monorepos where the library
may exist outside of the `node_modules`.
Example:
```js
// react-native.config.js
const path = require('path');
module.exports = {
dependencies: {
'react-native-library-name': {
root: path.join(__dirname, '..'),
},
},
};
```
## Changelog
[Internal] [Added] - Support custom library paths in `react-native.config.js` for codegen on iOS
Pull Request resolved: https://github.com/facebook/react-native/pull/34580
Test Plan:
Tested on a test application and ensured that codegen finds the library specified in `react-native.config.js`
https://user-images.githubusercontent.com/1174278/188141056-bce03730-2a13-4648-8889-9727aaf2c3c4.mp4
I have also added a basic test case for this scenario.
Reviewed By: jacdebug, cortinico
Differential Revision: D39257919
Pulled By: cipolleschi
fbshipit-source-id: 131189f1941128a59b9b1e28af61a9038eb4536b
This commit is contained in:
committed by
Dmitry Rykun
parent
ad582f5e8a
commit
f0ffd2291c
@@ -13,13 +13,20 @@
|
||||
const underTest = require('../generate-artifacts-executor');
|
||||
const fixtures = require('../__test_fixtures__/fixtures');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const child_process = require('child_process');
|
||||
|
||||
const codegenConfigKey = 'codegenConfig';
|
||||
const reactNativeDependencyName = 'react-native';
|
||||
const rootPath = path.join(__dirname, '../../..');
|
||||
|
||||
describe('generateCode', () => {
|
||||
it('executeNodes with the right arguents', () => {
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('executeNodes with the right arguments', () => {
|
||||
// Define variables and expected values
|
||||
const iosOutputDir = 'app/ios/build/generated/ios';
|
||||
const library = {config: {name: 'library', type: 'all'}};
|
||||
@@ -32,46 +39,32 @@ describe('generateCode', () => {
|
||||
const tmpOutDir = path.join(tmpDir, 'out');
|
||||
|
||||
// mock used functions
|
||||
let mkdirSyncInvocationCount = 0;
|
||||
jest.mock('fs', () => ({
|
||||
mkdirSync: (location, config) => {
|
||||
if (mkdirSyncInvocationCount === 0) {
|
||||
expect(location).toEqual(tmpOutDir);
|
||||
}
|
||||
if (mkdirSyncInvocationCount === 1) {
|
||||
expect(location).toEqual(iosOutputDir);
|
||||
}
|
||||
|
||||
mkdirSyncInvocationCount += 1;
|
||||
},
|
||||
}));
|
||||
|
||||
let execSyncInvocationCount = 0;
|
||||
jest.mock('child_process', () => ({
|
||||
execSync: command => {
|
||||
if (execSyncInvocationCount === 0) {
|
||||
const expectedCommand = `${node} ${path.join(
|
||||
rnRoot,
|
||||
'generate-specs-cli.js',
|
||||
)} \
|
||||
--platform ios \
|
||||
--schemaPath ${pathToSchema} \
|
||||
--outputDir ${tmpOutDir} \
|
||||
--libraryName ${library.config.name} \
|
||||
--libraryType ${libraryType}`;
|
||||
expect(command).toEqual(expectedCommand);
|
||||
}
|
||||
|
||||
if (execSyncInvocationCount === 1) {
|
||||
expect(command).toEqual(`cp -R ${tmpOutDir}/* ${iosOutputDir}`);
|
||||
}
|
||||
|
||||
execSyncInvocationCount += 1;
|
||||
},
|
||||
}));
|
||||
jest.spyOn(fs, 'mkdirSync').mockImplementation();
|
||||
jest.spyOn(child_process, 'execSync').mockImplementation();
|
||||
|
||||
underTest._generateCode(iosOutputDir, library, tmpDir, node, pathToSchema);
|
||||
expect(mkdirSyncInvocationCount).toBe(2);
|
||||
|
||||
const expectedCommand = `${node} ${path.join(
|
||||
rnRoot,
|
||||
'generate-specs-cli.js',
|
||||
)} --platform ios --schemaPath ${pathToSchema} --outputDir ${tmpOutDir} --libraryName ${
|
||||
library.config.name
|
||||
} --libraryType ${libraryType}`;
|
||||
|
||||
expect(child_process.execSync).toHaveBeenCalledTimes(2);
|
||||
expect(child_process.execSync).toHaveBeenNthCalledWith(1, expectedCommand);
|
||||
expect(child_process.execSync).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
`cp -R ${tmpOutDir}/* ${iosOutputDir}`,
|
||||
);
|
||||
|
||||
expect(fs.mkdirSync).toHaveBeenCalledTimes(2);
|
||||
expect(fs.mkdirSync).toHaveBeenNthCalledWith(1, tmpOutDir, {
|
||||
recursive: true,
|
||||
});
|
||||
expect(fs.mkdirSync).toHaveBeenNthCalledWith(2, iosOutputDir, {
|
||||
recursive: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,6 +195,83 @@ describe('extractLibrariesFromJSON', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findCodegenEnabledLibraries', () => {
|
||||
const mock = require('mock-fs');
|
||||
const {
|
||||
_findCodegenEnabledLibraries: findCodegenEnabledLibraries,
|
||||
} = require('../generate-artifacts-executor');
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('returns libraries defined in react-native.config.js', () => {
|
||||
const projectDir = path.join(__dirname, '../../../../test-project');
|
||||
const baseCodegenConfigFileDir = path.join(__dirname, '../../..');
|
||||
const baseCodegenConfigFilePath = path.join(
|
||||
baseCodegenConfigFileDir,
|
||||
'package.json',
|
||||
);
|
||||
|
||||
mock({
|
||||
[baseCodegenConfigFilePath]: `
|
||||
{
|
||||
"codegenConfig": {}
|
||||
}
|
||||
`,
|
||||
[projectDir]: {
|
||||
app: {
|
||||
'package.json': `{
|
||||
"name": "my-app"
|
||||
}`,
|
||||
'react-native.config.js': '',
|
||||
},
|
||||
'library-foo': {
|
||||
'package.json': `{
|
||||
"name": "react-native-foo",
|
||||
"codegenConfig": {
|
||||
"name": "RNFooSpec",
|
||||
"type": "modules",
|
||||
"jsSrcsDir": "src"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock(path.join(projectDir, 'app', 'react-native.config.js'), () => ({
|
||||
dependencies: {
|
||||
'react-native-foo': {
|
||||
root: path.join(projectDir, 'library-foo'),
|
||||
},
|
||||
'react-native-bar': {
|
||||
root: path.join(projectDir, 'library-bar'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const libraries = findCodegenEnabledLibraries(
|
||||
`${projectDir}/app`,
|
||||
baseCodegenConfigFileDir,
|
||||
`package.json`,
|
||||
'codegenConfig',
|
||||
);
|
||||
|
||||
expect(libraries).toEqual([
|
||||
{
|
||||
library: 'react-native',
|
||||
config: {},
|
||||
libraryPath: baseCodegenConfigFileDir,
|
||||
},
|
||||
{
|
||||
library: 'react-native-foo',
|
||||
config: {name: 'RNFooSpec', type: 'modules', jsSrcsDir: 'src'},
|
||||
libraryPath: path.join(projectDir, 'library-foo'),
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete empty files and folders', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
@@ -204,6 +204,55 @@ function handleThirdPartyLibraries(
|
||||
});
|
||||
}
|
||||
|
||||
function handleLibrariesFromReactNativeConfig(
|
||||
libraries,
|
||||
codegenConfigKey,
|
||||
codegenConfigFilename,
|
||||
appRootDir,
|
||||
) {
|
||||
const rnConfigFileName = 'react-native.config.js';
|
||||
|
||||
console.log(
|
||||
`\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${rnConfigFileName}`,
|
||||
);
|
||||
|
||||
const rnConfigFilePath = path.join(appRootDir, rnConfigFileName);
|
||||
|
||||
if (fs.existsSync(rnConfigFilePath)) {
|
||||
const rnConfig = require(rnConfigFilePath);
|
||||
|
||||
if (rnConfig.dependencies != null) {
|
||||
Object.keys(rnConfig.dependencies).forEach(name => {
|
||||
const dependencyConfig = rnConfig.dependencies[name];
|
||||
|
||||
if (dependencyConfig.root) {
|
||||
const codegenConfigFileDir = path.resolve(
|
||||
appRootDir,
|
||||
dependencyConfig.root,
|
||||
);
|
||||
const configFilePath = path.join(
|
||||
codegenConfigFileDir,
|
||||
codegenConfigFilename,
|
||||
);
|
||||
const pkgJsonPath = path.join(codegenConfigFileDir, 'package.json');
|
||||
|
||||
if (fs.existsSync(configFilePath)) {
|
||||
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath));
|
||||
const configFile = JSON.parse(fs.readFileSync(configFilePath));
|
||||
extractLibrariesFromJSON(
|
||||
configFile,
|
||||
libraries,
|
||||
codegenConfigKey,
|
||||
pkgJson.name,
|
||||
codegenConfigFileDir,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleInAppLibraries(
|
||||
libraries,
|
||||
pkgJson,
|
||||
@@ -362,6 +411,39 @@ function createComponentProvider(
|
||||
}
|
||||
}
|
||||
|
||||
function findCodegenEnabledLibraries(
|
||||
appRootDir,
|
||||
baseCodegenConfigFileDir,
|
||||
codegenConfigFilename,
|
||||
codegenConfigKey,
|
||||
) {
|
||||
const pkgJson = readPackageJSON(appRootDir);
|
||||
const dependencies = {...pkgJson.dependencies, ...pkgJson.devDependencies};
|
||||
const libraries = [];
|
||||
|
||||
handleReactNativeCodeLibraries(
|
||||
libraries,
|
||||
codegenConfigFilename,
|
||||
codegenConfigKey,
|
||||
);
|
||||
handleThirdPartyLibraries(
|
||||
libraries,
|
||||
baseCodegenConfigFileDir,
|
||||
dependencies,
|
||||
codegenConfigFilename,
|
||||
codegenConfigKey,
|
||||
);
|
||||
handleLibrariesFromReactNativeConfig(
|
||||
libraries,
|
||||
codegenConfigKey,
|
||||
codegenConfigFilename,
|
||||
appRootDir,
|
||||
);
|
||||
handleInAppLibraries(libraries, pkgJson, codegenConfigKey, appRootDir);
|
||||
|
||||
return libraries;
|
||||
}
|
||||
|
||||
// It removes all the empty files and empty folders
|
||||
// it finds, starting from `filepath`, recursively.
|
||||
//
|
||||
@@ -429,23 +511,12 @@ function execute(
|
||||
}
|
||||
|
||||
try {
|
||||
const pkgJson = readPackageJSON(appRootDir);
|
||||
const dependencies = {...pkgJson.dependencies, ...pkgJson.devDependencies};
|
||||
const libraries = [];
|
||||
|
||||
handleReactNativeCodeLibraries(
|
||||
libraries,
|
||||
codegenConfigFilename,
|
||||
codegenConfigKey,
|
||||
);
|
||||
handleThirdPartyLibraries(
|
||||
libraries,
|
||||
const libraries = findCodegenEnabledLibraries(
|
||||
appRootDir,
|
||||
baseCodegenConfigFileDir,
|
||||
dependencies,
|
||||
codegenConfigFilename,
|
||||
codegenConfigKey,
|
||||
);
|
||||
handleInAppLibraries(libraries, pkgJson, codegenConfigKey, appRootDir);
|
||||
|
||||
if (libraries.length === 0) {
|
||||
console.log('[Codegen] No codegen-enabled libraries found.');
|
||||
@@ -482,6 +553,7 @@ module.exports = {
|
||||
execute: execute,
|
||||
// exported for testing purposes only:
|
||||
_extractLibrariesFromJSON: extractLibrariesFromJSON,
|
||||
_findCodegenEnabledLibraries: findCodegenEnabledLibraries,
|
||||
_executeNodeScript: executeNodeScript,
|
||||
_generateCode: generateCode,
|
||||
_cleanupEmptyFilesAndFolders: cleanupEmptyFilesAndFolders,
|
||||
|
||||
Reference in New Issue
Block a user