From 8a43c11461c8265c4d95fcb947e7fcba578ecff6 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Tue, 21 Mar 2023 09:52:50 -0700 Subject: [PATCH] Enable Flow in server and serializer tests Summary: Changelog: [Internal] Enables Flow in `Server-test`, `baseBytecodeBundle-test`, `baseJSBundle-test` and `getRamBundleInfo-test`, to unblock additional changes to the serializer in support of [lazy bundling](https://github.com/react-native-community/discussions-and-proposals/pull/605). Reviewed By: jacdebug Differential Revision: D44184128 fbshipit-source-id: 53981514e17c901f351b3d2bb40b7ffaf5e875a6 --- .../__tests__/baseBytecodeBundle-test.js | 62 ++++++++-- .../__tests__/baseJSBundle-test.js | 112 ++++++++++++++--- .../__tests__/getRamBundleInfo-test.js | 113 +++++++++++++----- .../metro/src/Server/__tests__/Server-test.js | 11 +- 4 files changed, 237 insertions(+), 61 deletions(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseBytecodeBundle-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseBytecodeBundle-test.js index 55af9ee9d4..88e0944fab 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseBytecodeBundle-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseBytecodeBundle-test.js @@ -4,21 +4,40 @@ * 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 * @oncall react_native */ 'use strict'; +import type {Module, TransformInputOptions} from '../../types.flow'; + +import CountingSet from '../../../lib/CountingSet'; + const baseBytecodeBundle = require('../baseBytecodeBundle'); const {compile, validateBytecodeModule} = require('metro-hermes-compiler'); const path = require('path'); +const transformOptions: TransformInputOptions = { + customTransformOptions: {}, + dev: true, + hot: true, + minify: true, + platform: 'web', + runtimeBytecodeVersion: 900, + type: 'module', + unstable_transformProfile: 'default', +}; + const polyfillCode = '__d(function() {/* code for polyfill */});'; const polyfillBytecode = compile(polyfillCode, { sourceURL: 'polyfill-source', }).bytecode; -const polyfill = { +const polyfill: Module<> = { + path: '/polyfill', + dependencies: new Map(), + inverseDependencies: new CountingSet(), output: [ { type: 'js/script', @@ -36,9 +55,18 @@ const fooModuleCode = '__d(function() {/* code for foo */});'; const fooModuleBytecode = compile(fooModuleCode, { sourceURL: 'foo-source', }).bytecode; -const fooModule = { +const fooModule: Module<> = { path: '/root/foo', - dependencies: new Map([['./bar', {absolutePath: '/root/bar', data: {}}]]), + dependencies: new Map([ + [ + './bar', + { + absolutePath: '/root/bar', + data: {data: {asyncType: null, locs: [], key: './bar'}, name: './bar'}, + }, + ], + ]), + inverseDependencies: new CountingSet(), output: [ { type: 'js/module', @@ -62,9 +90,10 @@ const barModuleCode = '__d(function() {/* code for bar */});'; const barModuleBytecode = compile(barModuleCode, { sourceURL: 'bar-source', }).bytecode; -const barModule = { +const barModule: Module<> = { path: '/root/bar', dependencies: new Map(), + inverseDependencies: new CountingSet(['/root/foo']), output: [ { type: 'js/module', @@ -84,7 +113,7 @@ const barModule = { getSource: () => Buffer.from('bar-source'), }; -const getRunModuleStatement = moduleId => +const getRunModuleStatement = (moduleId: number | string) => `require(${JSON.stringify(moduleId)});`; it('should generate a bundle', () => { @@ -96,17 +125,25 @@ it('should generate a bundle', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: [], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ); @@ -137,18 +174,25 @@ it('does not add polyfills when `modulesOnly` is used', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['foo'], + entryPoints: new Set(['foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, modulesOnly: true, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: [], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ); diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js index c3754e2d23..7cd63a9430 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js @@ -4,17 +4,25 @@ * 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 * @oncall react_native */ 'use strict'; +import type {Module, TransformInputOptions} from '../../types.flow'; + +import CountingSet from '../../../lib/CountingSet'; + const createModuleIdFactory = require('../../../lib/createModuleIdFactory'); const baseJSBundle = require('../baseJSBundle'); const path = require('path'); -const polyfill = { +const polyfill: Module<> = { + path: '/polyfill', + dependencies: new Map(), + inverseDependencies: new CountingSet(), output: [ { type: 'js/script', @@ -24,9 +32,18 @@ const polyfill = { getSource: () => Buffer.from('polyfill-source'), }; -const fooModule = { +const fooModule: Module<> = { path: '/root/foo', - dependencies: new Map([['./bar', {absolutePath: '/root/bar', data: {}}]]), + dependencies: new Map([ + [ + './bar', + { + absolutePath: '/root/bar', + data: {data: {asyncType: null, locs: [], key: './bar'}, name: './bar'}, + }, + ], + ]), + inverseDependencies: new CountingSet(), output: [ { type: 'js/module', @@ -40,9 +57,10 @@ const fooModule = { getSource: () => Buffer.from('foo-source'), }; -const barModule = { +const barModule: Module<> = { path: '/root/bar', dependencies: new Map(), + inverseDependencies: new CountingSet(['/root/foo']), output: [ { type: 'js/module', @@ -56,9 +74,20 @@ const barModule = { getSource: () => Buffer.from('bar-source'), }; -const getRunModuleStatement = moduleId => +const getRunModuleStatement = (moduleId: number | string) => `require(${JSON.stringify(moduleId)});`; +const transformOptions: TransformInputOptions = { + customTransformOptions: {}, + dev: true, + hot: true, + minify: true, + platform: 'web', + runtimeBytecodeVersion: null, + type: 'module', + unstable_transformProfile: 'default', +}; + it('should generate a very simple bundle', () => { expect( baseJSBundle( @@ -69,17 +98,25 @@ it('should generate a very simple bundle', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: [], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ), ).toMatchInlineSnapshot(` @@ -111,17 +148,25 @@ it('should add runBeforeMainModule statements if found in the graph', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['/root/foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: ['/root/bar', 'non-existant'], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ).post, ).toMatchInlineSnapshot(` @@ -141,17 +186,24 @@ it('should handle numeric module ids', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['/root/foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', createModuleId: createModuleIdFactory(), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: ['/root/bar', 'non-existant'], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ).modules, ).toMatchInlineSnapshot(` @@ -178,17 +230,26 @@ it('outputs custom runModule statements', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['/root/foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement: moduleId => `export default require(${JSON.stringify(moduleId)}).default;`, + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: ['/root/bar'], runModule: true, + serverRoot: '/root', + sourceMapUrl: null, + sourceUrl: null, }, ).post, ).toMatchInlineSnapshot(` @@ -206,17 +267,25 @@ it('should add an inline source map to a very simple bundle', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: true, + modulesOnly: false, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: [], runModule: true, - inlineSourceMap: true, + serverRoot: '/root', + sourceMapUrl: null, + sourceUrl: null, }, ); expect(bundle.post.slice(0, bundle.post.lastIndexOf('base64'))).toEqual( @@ -248,18 +317,25 @@ it('does not add polyfills when `modulesOnly` is used', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), - entryPoints: ['foo'], + entryPoints: new Set(['/root/foo']), + transformOptions, }, { - processModuleFilter: () => true, + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? createModuleId: filePath => path.basename(filePath), dev: true, getRunModuleStatement, + includeAsyncPaths: false, + inlineSourceMap: false, modulesOnly: true, + processModuleFilter: () => true, projectRoot: '/root', runBeforeMainModule: [], runModule: true, + serverRoot: '/root', sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, }, ), ).toMatchInlineSnapshot(` diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js index bd6e7f6d4c..925cbcee64 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js @@ -4,15 +4,25 @@ * 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 * @oncall react_native */ 'use strict'; +import type {Module, TransformInputOptions} from '../../types.flow'; +import type {JsOutput} from 'metro-transform-worker'; + +import CountingSet from '../../../lib/CountingSet'; + const getRamBundleInfo = require('../getRamBundleInfo'); -function createModule(name, dependencies, type = 'js/module') { +function createModule( + name: string, + dependencies: $ReadOnlyArray, + type: JsOutput['type'] = 'js/module', +): [string, Module<>] { return [ `/root/${name}.js`, { @@ -20,9 +30,14 @@ function createModule(name, dependencies, type = 'js/module') { dependencies: new Map( dependencies.map(dep => [ dep, - {absolutePath: `/root/${dep}.js`, data: {isAsync: false, name: dep}}, + { + absolutePath: `/root/${dep}.js`, + data: {data: {asyncType: null, locs: [], key: dep}, name: dep}, + }, ]), ), + // FIXME: Populate inverseDependencies correctly. + inverseDependencies: new CountingSet(), getSource: () => Buffer.from(`source of ${name}`), output: [ {type, data: {code: `__d(function() {${name}()});`, lineCount: 1}}, @@ -31,6 +46,17 @@ function createModule(name, dependencies, type = 'js/module') { ]; } +const transformOptions: TransformInputOptions = { + customTransformOptions: {}, + dev: true, + hot: true, + minify: true, + platform: 'web', + runtimeBytecodeVersion: 900, + type: 'module', + unstable_transformProfile: 'default', +}; + const graph = { dependencies: new Map([ createModule('entry', ['foo', 'entry2']), @@ -41,30 +67,44 @@ const graph = { createModule('bar', []), createModule('qux', []), ]), + transformOptions, }; const pre = [createModule('pre', [], 'js/script')[1]]; -const getRunModuleStatement = moduleId => +const getRunModuleStatement = (moduleId: string | number) => `require(${JSON.stringify(moduleId)});`; it('should return the RAM bundle info', async () => { expect( - await getRamBundleInfo('/root/entry.js', pre, graph, { - processModuleFilter: module => true, - createModuleId: path => path, - excludeSource: false, - getRunModuleStatement, - getTransformOptions: () => ({ - preloadedModules: {}, - ramGroups: [], - }), - dev: true, - projectRoot: '/root', - runBeforeMainModule: [], - runModule: true, - sourceMapUrl: 'http://localhost/bundle.map', - }), + await getRamBundleInfo( + '/root/entry.js', + pre, + {...graph, entryPoints: new Set(['/root/entry.js'])}, + { + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? + createModuleId: path => path, + dev: true, + excludeSource: false, + getRunModuleStatement, + getTransformOptions: async () => ({ + preloadedModules: {}, + ramGroups: [], + }), + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + platform: null, + processModuleFilter: module => true, + projectRoot: '/root', + runBeforeMainModule: [], + runModule: true, + serverRoot: '/root', + sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, + }, + ), ).toMatchSnapshot(); }); @@ -74,18 +114,31 @@ it('should use the preloadedModules and ramGroup configs to build a RAM bundle', ramGroups: ['/root/foo.js'], }); - const bundleInfo = await getRamBundleInfo('/root/entry.js', pre, graph, { - processModuleFilter: module => true, - createModuleId: path => path, - excludeSource: false, - getRunModuleStatement, - getTransformOptions, - dev: true, - projectRoot: '/root', - runBeforeMainModule: [], - runModule: true, - sourceMapUrl: 'http://localhost/bundle.map', - }); + const bundleInfo = await getRamBundleInfo( + '/root/entry.js', + pre, + {...graph, entryPoints: new Set(['/root/entry.js'])}, + { + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-call] createModuleId assumes numeric IDs - is this too strict? + createModuleId: path => path, + dev: true, + excludeSource: false, + getRunModuleStatement, + getTransformOptions, + includeAsyncPaths: false, + inlineSourceMap: null, + modulesOnly: false, + platform: null, + processModuleFilter: module => true, + projectRoot: '/root', + runBeforeMainModule: [], + runModule: true, + serverRoot: '/root', + sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, + }, + ); expect(bundleInfo.startupModules.map(({id}) => id)).toEqual([ '/root/pre.js', diff --git a/packages/metro/src/Server/__tests__/Server-test.js b/packages/metro/src/Server/__tests__/Server-test.js index eb5593ecc1..47b830355e 100644 --- a/packages/metro/src/Server/__tests__/Server-test.js +++ b/packages/metro/src/Server/__tests__/Server-test.js @@ -33,6 +33,7 @@ const { validateBytecodeModule, } = require('metro-hermes-compiler'); const path = require('path'); +import CountingSet from '../../lib/CountingSet'; jest .mock('jest-worker', () => ({})) @@ -195,22 +196,24 @@ describe('processRequest', () => { resolverOptions: mixed, otherOptions: mixed, ) => { - // $FlowFixMe[prop-missing] dependencies = new Map>([ [ '/root/mybundle.js', { path: '/root/mybundle.js', - // $FlowFixMe[prop-missing] dependencies: new Map([ [ 'foo', { absolutePath: '/root/foo.js', - data: {isAsync: false, name: 'foo'}, + data: { + data: {asyncType: null, key: 'foo', locs: []}, + name: 'foo', + }, }, ], ]), + inverseDependencies: new CountingSet([]), getSource: () => Buffer.from('code-mybundle'), output: [ { @@ -234,10 +237,10 @@ describe('processRequest', () => { ], ]); if (!options.shallow) { - // $FlowFixMe[prop-missing] dependencies.set('/root/foo.js', { path: '/root/foo.js', dependencies: new Map(), + inverseDependencies: new CountingSet(['/root/mybundle.js']), getSource: () => Buffer.from('code-foo'), output: [ {