Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,13 @@ fix_*.patch
/private/react-native-fantom/.out/
/private/react-native-fantom/tester/build/

# [Experimental] Generated TS type definitions
/packages/**/types_generated/
# JS build output
/packages/*/dist/
/packages/*/dist_noflow/
/packages/debugger-shell/build/

# Generated TS type definitions
/packages/*/types_generated/

# Python
__pycache__/
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
"exports": {
".": {
"react-native-strict-api": "./types_generated/index.d.ts",
"react-native-noflow": "./dist_noflow/index.js",
"types": "./types/index.d.ts",
"default": "./index.js"
},
"./*": {
"react-native-strict-api": null,
"react-native-noflow": "./dist_noflow/*.js",
"types": "./*.d.ts",
"default": "./*.js"
},
Expand Down Expand Up @@ -75,6 +77,7 @@
"files": [
"build.gradle.kts",
"cli.js",
"dist_noflow",
"flow",
"gradle.properties",
"gradle/libs.versions.toml",
Expand Down
3 changes: 3 additions & 0 deletions packages/virtualized-lists/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@
"exports": {
".": {
"react-native-strict-api": "./types_generated/index.d.ts",
"react-native-noflow": "./dist_noflow/index.js",
"types": "./index.d.ts",
"default": "./index.js"
},
"./*": {
"types": null,
"react-native-noflow": "./dist_noflow/*.js",
"default": "./*.js"
},
"./package.json": "./package.json"
},
"files": [
"dist_noflow",
"index.js",
"index.d.ts",
"Lists",
Expand Down
26 changes: 26 additions & 0 deletions scripts/build/babel/noflow.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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 strict-local
* @format
*/

// A config which strips and transforms Flow features, to be received by either
// Flow/JS compatible Babel presets.

// IMPORTANT: We've given no public guarantee that we will preserve this
// transform. The app/frameworks requirement is still to apply all of
// @react-native/babel-preset.

import type {BabelCoreOptions} from '@babel/core';

const config: BabelCoreOptions = {
sourceMaps: true,
presets: [require.resolve('@babel/preset-flow')],
plugins: [require.resolve('babel-plugin-syntax-hermes-parser')],
};

module.exports = config;
110 changes: 105 additions & 5 deletions scripts/build/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

require('../shared/babelRegister').registerForScript();

/*::
import type {BuildOptions} from './config';
*/

const {PACKAGES_DIR, REPO_ROOT} = require('../shared/consts');
const {
buildConfig,
Expand All @@ -30,8 +34,9 @@ const {parseArgs, styleText} = require('util');

const SRC_DIR = 'src';
const BUILD_DIR = 'dist';
const NOFLOW_BUILD_DIR = 'dist_noflow';
const JS_FILES_PATTERN = '**/*.js';
const IGNORE_PATTERN = '**/__{tests,mocks,fixtures}__/**';
const IGNORE_PATTERN = '**/__{tests,mocks,fixtures,flowtests}__/**';

const config = {
allowPositionals: true,
Expand Down Expand Up @@ -77,6 +82,13 @@ async function build() {

let ok = true;
for (const packageName of packagesToBuild) {
const {target} = getBuildOptions(packageName);

if (target === 'noflow') {
await buildNoFlowTarget(packageName);
continue;
}

await buildPackage(packageName, prepack);
}

Expand Down Expand Up @@ -163,8 +175,9 @@ async function buildFile(
) {
const {silent = false} = options;
const packageName = getPackageName(file);
const buildPath = getBuildPath(file);
const {emitFlowDefs, emitTypeScriptDefs} = getBuildOptions(packageName);
const buildOptions = getBuildOptions(packageName);
const {emitFlowDefs, emitTypeScriptDefs} = buildOptions;
const buildPath = getBuildPath(file, buildOptions);

const logResult = ({copied, desc} /*: {copied: boolean, desc?: string} */) =>
silent ||
Expand Down Expand Up @@ -226,6 +239,87 @@ async function buildFile(
logResult({copied: true});
}

async function buildNoFlowTarget(packageName /*: string */) {
try {
const buildOptions = getBuildOptions(packageName);
const {srcOverride} = buildOptions;

process.stdout.write(
`${packageName} ${styleText('yellow', '(noflow)')} ${styleText('dim', '.').repeat(63 - packageName.length)} `,
);

const files = glob.sync(
path.resolve(
PACKAGES_DIR,
packageName,
...(srcOverride != null
? ['{', srcOverride, '}']
: [SRC_DIR, '**/*.js']),
),
{
nodir: true,
ignore: [IGNORE_PATTERN],
},
);

for (const file of files) {
const filePath = path.normalize(file);
const buildPath = getBuildPath(filePath, buildOptions);
const prettierConfig = {parser: 'babel'};
const source = await fs.readFile(file, 'utf-8');

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

// If file contains `@noflow`, copy only
if (/@noflow/.test(source)) {
await fs.copyFile(file, buildPath);
continue;
}

// Apply Flow transforms
const babelResult = await babel.transformFileAsync(
filePath,
getBabelConfig(packageName),
);
const transformed = await prettier.format(
babelResult.code,
/* $FlowFixMe[incompatible-type] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
prettierConfig,
);

// Write transformed file with source map comment
const relativeSourcePath = path.relative(
path.dirname(buildPath),
filePath,
);
const sourceMapComment = `\n//# sourceMappingURL=${path.basename(buildPath)}.map`;
await fs.writeFile(buildPath, transformed + sourceMapComment);

// Write source map file
if (babelResult.map) {
const sourceMapPath = buildPath + '.map';
const sourceMap = {
...babelResult.map,
sources: [relativeSourcePath],
};
await fs.writeFile(sourceMapPath, JSON.stringify(sourceMap, null, 2));
}
}

process.stdout.write(
styleText(['reset', 'inverse', 'bold', 'green'], ' DONE '),
);
} catch (e) {
process.stdout.write(
styleText(['reset', 'inverse', 'bold', 'red'], ' FAIL ') + '\n',
);
throw e;
} finally {
process.stdout.write('\n');
}
}

/*::
type PackageJson = {
name: string,
Expand Down Expand Up @@ -356,13 +450,19 @@ function getPackageName(file /*: string */) /*: string */ {
return path.relative(PACKAGES_DIR, file).split(path.sep)[0];
}

function getBuildPath(file /*: string */) /*: string */ {
function getBuildPath(
file /*: string */,
{srcOverride, target} /*: BuildOptions */,
) /*: string */ {
const packageDir = path.join(PACKAGES_DIR, getPackageName(file));

return path.join(
packageDir,
file
.replace(path.join(packageDir, SRC_DIR), BUILD_DIR)
.replace(
path.join(packageDir, srcOverride ? '' : SRC_DIR),
target === 'noflow' ? NOFLOW_BUILD_DIR : BUILD_DIR,
)
.replace('.flow.js', '.js'),
);
}
Expand Down
24 changes: 23 additions & 1 deletion scripts/build/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ const {ModuleResolutionKind} = require('typescript');

export type BuildOptions = Readonly<{
// The target runtime to compile for.
target: 'node',
target:
| 'node'
// A special compile target aligning with the "react-native-noflow" exports
// condition. This entry point allows compatible native parsers (e.g. Bun
// and SWC) to consume React Native without Flow types. Output will be stored
// in `dist_noflow/`.
| 'noflow',

// Whether to emit Flow definition files (.js.flow) (default: true).
emitFlowDefs?: boolean,

// Whether to emit TypeScript definition files (.d.ts) (default: false).
emitTypeScriptDefs?: boolean,

// Source dir glob override (default: 'src/**/*'). This is intended to provide
// compatibility for the react-native package only. This setting is ignored
// unless using the 'noflow' compile target.
srcOverride?: string | null,
}>;

export type BuildConfig = Readonly<{
Expand Down Expand Up @@ -57,16 +68,25 @@ const buildConfig: BuildConfig = {
emitTypeScriptDefs: true,
target: 'node',
},
'react-native': {
srcOverride: 'Libraries/**/*.js,src/**/*.js,index.js',
target: 'noflow',
},
'react-native-compatibility-check': {
emitTypeScriptDefs: true,
target: 'node',
},
'virtualized-lists': {
srcOverride: 'Lists/**/*.js,Utilities/**/*.js,index.js',
target: 'noflow',
},
},
};

const defaultBuildOptions = {
emitFlowDefs: true,
emitTypeScriptDefs: false,
srcOverride: null,
};

function getBuildOptions(
Expand All @@ -86,6 +106,8 @@ function getBabelConfig(
switch (target) {
case 'node':
return require('./babel/node.config.js');
case 'noflow':
return require('./babel/noflow.config.js');
}
}

Expand Down
Loading