Skip to content

Commit

Permalink
Bootstrap experimental types build for main package (#48661)
Browse files Browse the repository at this point in the history
Summary:


Changelog: [Internal]

Differential Revision: D68102875
  • Loading branch information
j-piasecki authored and facebook-github-bot committed Jan 14, 2025
1 parent 85f9939 commit ab8120a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 5 deletions.
33 changes: 29 additions & 4 deletions scripts/build/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

const {PACKAGES_DIR, REPO_ROOT} = require('../consts');
const buildRNTypes = require('./buildRNTypes');
const {
buildConfig,
getBabelConfig,
Expand All @@ -35,15 +36,16 @@ const IGNORE_PATTERN = '**/__{tests,mocks,fixtures}__/**';
const config = {
allowPositionals: true,
options: {
help: {type: 'boolean'},
check: {type: 'boolean'},
experimentalBuildRNTypes: {type: 'boolean'},
help: {type: 'boolean'},
},
};

async function build() {
const {
positionals: packageNames,
values: {help, check},
values: {check, experimentalBuildRNTypes, help},
} = parseArgs(config);

if (help) {
Expand All @@ -54,6 +56,13 @@ async function build() {
By default, builds all packages defined in ./scripts/build/config.js. If a
a package list is provided, builds only those specified.
Options:
--check Validate that no build artifacts have been accidentally
committed.
--experimentalBuildRNTypes
[Experimental] Enable source code -> type translation
output for the react-native package.
`);
process.exitCode = 0;
return;
Expand All @@ -69,6 +78,16 @@ async function build() {

let ok = true;
for (const packageName of packagesToBuild) {
if (packageName === 'react-native' && !experimentalBuildRNTypes) {
if (packagesToBuild.length === 1) {
console.warn(
'Building the react-native package must be enabled using ' +
'--experimentalBuildRNTypes.',
);
}
continue;
}

if (check) {
ok &&= await checkPackage(packageName);
} else {
Expand All @@ -83,15 +102,21 @@ async function checkPackage(packageName /*: string */) /*: Promise<boolean> */ {
const artifacts = await exportedBuildArtifacts(packageName);
if (artifacts.length > 0) {
console.log(
`${chalk.bgRed(packageName)}: has been build and the ${chalk.bold('build artifacts')} committed to the repository. This will break Flow checks.`,
`${chalk.bgRed(packageName)}: has been built and the ${chalk.bold('build artifacts')} committed to the repository. This will break Flow checks.`,
);
return false;
}
return true;
}

async function buildPackage(packageName /*: string */) {
const {emitTypeScriptDefs} = getBuildOptions(packageName);
const {target, emitTypeScriptDefs} = getBuildOptions(packageName);

if (target === 'react-native-emit-types') {
await buildRNTypes();
return;
}

const entryPoints = await getEntryPoints(packageName);

const files = glob
Expand Down
67 changes: 67 additions & 0 deletions scripts/build/buildRNTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/

const {PACKAGES_DIR} = require('../consts');
const translate = require('flow-api-translator');
const {promises: fs} = require('fs');
const glob = require('glob');
const path = require('path');

const TYPES_DIR = 'new-types';
const PACKAGE_NAME = 'react-native';

// Start with Animated only as it using the export syntax.
const PATHS = ['Libraries/Animated'];

async function buildRNTypes() {
const files = PATHS.flatMap(src_path =>
glob.sync(path.resolve(PACKAGES_DIR, PACKAGE_NAME, src_path, '**/*.js'), {
nodir: true,
}),
);

console.log('Building RN types...');
for (const file of files) {
// Don't build tests
if (/\/__tests__\//.test(file)) {
continue;
}

const buildPath = getBuildPath(file);
const source = await fs.readFile(file, 'utf-8');
const prettierConfig = {parser: 'babel'};

await fs.mkdir(path.dirname(buildPath), {recursive: true});

try {
await fs.writeFile(
buildPath,
await translate.translateFlowToTSDef(source, prettierConfig),
);
} catch (e) {
console.error(`Failed to build ${file.replace(PACKAGES_DIR, '')}`);
}
}
}

function getBuildPath(file /*: string */) /*: string */ {
const packageDir = path.join(PACKAGES_DIR, PACKAGE_NAME);

return path.join(
packageDir,
file
.replace(packageDir, TYPES_DIR)
.replace(/\.flow\.js$/, '.js')
.replace(/\.js$/, '.d.ts'),
);
}

module.exports = buildRNTypes;
12 changes: 11 additions & 1 deletion scripts/build/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ const {ModuleResolutionKind} = require('typescript');
/*::
export type BuildOptions = $ReadOnly<{
// The target runtime to compile for.
target: 'node',
target:
// Node.js
'node' |
// [Experimental] react-native package only: Skip build, emit translated
// TypeScript types for 3P use.
'react-native-emit-types',
// Whether to emit Flow definition files (.js.flow) (default: true).
emitFlowDefs?: boolean,
Expand Down Expand Up @@ -58,6 +63,9 @@ const buildConfig /*: BuildConfig */ = {
emitTypeScriptDefs: true,
target: 'node',
},
'react-native': {
target: 'react-native-emit-types',
},
},
};

Expand All @@ -83,6 +91,8 @@ function getBabelConfig(
switch (target) {
case 'node':
return require('./babel/node.config.js');
case 'react-native-emit-types':
return {}; // stub
}
}

Expand Down

0 comments on commit ab8120a

Please sign in to comment.