Skip to content
Merged
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
19 changes: 19 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,25 @@ Type: `boolean`

Enable forwarding of `client_log` events (when client logs are [configured](https://github.com/facebook/metro/blob/614ad14a85b22958129ee94e04376b096f03ccb1/packages/metro/src/lib/createWebsocketServer.js#L20)) to the reporter. Defaults to `true`.

#### `tls`

Type: `false | object`

If not provided or is `false` Metro will start an HTTP server with WS WebSocket endpoints.

If an object, Metro will start an HTTPS server with WSS WebSocket endpoints using the passed TLS options:

```ts
ca?: string | Buffer, // Certificate authority (contents, not path)
cert?: string | Buffer, // Server certificate (contents, not path)
key?: string | Buffer, // Private key (contents, not path)
requestCert?: boolean, // Whether to authenticate the remote peer by requesting a certificate
```

Notice that when overriding the base config, object tls configs extend the base tls config, false overrides the base tls configs, and `null` and `undefined` are ignored.

When running Metro with `Metro.runServer` with the `secureServerOptions` property Metro will likewise start an HTTPS server merging with the `config.server.tls` object if provided, overriding it.

---

### Watcher Options
Expand Down
2 changes: 1 addition & 1 deletion packages/buck-worker-tool/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "buck-worker-tool",
"version": "0.83.4",
"version": "0.83.5",
"description": "Implementation of the Buck worker protocol for Node.js.",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-babel-register/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-babel-register",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 babel/register configuration for Metro.",
"main": "src/babel-register.js",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-babel-transformer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-babel-transformer",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 Base Babel transformer for Metro.",
"main": "src/index.js",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-cache-key/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-cache-key",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 Cache key utility.",
"main": "src/index.js",
"exports": {
Expand Down
4 changes: 2 additions & 2 deletions packages/metro-cache/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-cache",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 Cache layers for Metro.",
"main": "src/index.js",
"exports": {
Expand All @@ -21,7 +21,7 @@
"exponential-backoff": "^3.1.1",
"flow-enums-runtime": "^0.0.6",
"https-proxy-agent": "^7.0.5",
"metro-core": "0.83.4"
"metro-core": "0.83.5"
},
"devDependencies": {
"memfs": "^4.38.2"
Expand Down
12 changes: 6 additions & 6 deletions packages/metro-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-config",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 Config parser for Metro.",
"main": "src/index.js",
"exports": {
Expand All @@ -22,15 +22,15 @@
"connect": "^3.6.5",
"flow-enums-runtime": "^0.0.6",
"jest-validate": "^29.7.0",
"metro": "0.83.4",
"metro-cache": "0.83.4",
"metro-core": "0.83.4",
"metro-runtime": "0.83.4",
"metro": "0.83.5",
"metro-cache": "0.83.5",
"metro-core": "0.83.5",
"metro-runtime": "0.83.5",
"yaml": "^2.6.1"
},
"devDependencies": {
"@types/connect": "^3.4.35",
"metro-babel-register": "0.83.4",
"metro-babel-register": "0.83.5",
"pretty-format": "^29.7.0"
},
"engines": {
Expand Down
168 changes: 167 additions & 1 deletion packages/metro-config/src/__tests__/mergeConfig-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
* 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
* @flow
* @format
* @oncall react_native
*/

import type {InputConfigT} from '../types';

import {mergeConfig} from '../loadConfig';

describe('mergeConfig', () => {
Expand All @@ -26,4 +28,168 @@ describe('mergeConfig', () => {
},
});
});

describe('server.tls merging', () => {
describe('override IS applied when tls is false or object', () => {
test('override tls: object replaces base tls: false', () => {
const base: InputConfigT = {server: {tls: false}};
const override: InputConfigT = {
server: {tls: {key: 'key', cert: 'cert'}},
};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'});
});

test('override tls: false replaces base tls: object', () => {
const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}};
const override: InputConfigT = {server: {tls: false}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(false);
});

test('override tls: false sets tls when base is undefined', () => {
const base: InputConfigT = {server: {}};
const override: InputConfigT = {server: {tls: false}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(false);
});

test('override tls: object sets tls when base is undefined', () => {
const base: InputConfigT = {server: {}};
const override: InputConfigT = {
server: {tls: {key: 'key', cert: 'cert'}},
};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'});
});

test('override tls: object deep merges with base tls: object', () => {
const base: InputConfigT = {
server: {tls: {key: 'baseKey', cert: 'baseCert', ca: 'baseCa'}},
};
const override: InputConfigT = {
server: {tls: {key: 'newKey', cert: 'newCert'}},
};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({
key: 'newKey',
cert: 'newCert',
ca: 'baseCa',
});
});

test('override tls: object adds new properties to base tls: object', () => {
const base: InputConfigT = {
server: {tls: {key: 'baseKey', cert: 'baseCert'}},
};
const override: InputConfigT = {
server: {tls: {ca: 'newCa'}},
};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({
key: 'baseKey',
cert: 'baseCert',
ca: 'newCa',
});
});

test('override tls: object with same properties overrides base values', () => {
const base: InputConfigT = {
server: {tls: {key: 'baseKey', cert: 'baseCert'}},
};
const override: InputConfigT = {
server: {tls: {key: 'newKey', cert: 'newCert'}},
};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({
key: 'newKey',
cert: 'newCert',
});
});

test('other server properties are preserved when tls is overridden', () => {
const base: InputConfigT = {server: {port: 8081, tls: false}};
const override: InputConfigT = {
server: {tls: {key: 'key', cert: 'cert'}},
};
const result = mergeConfig(base, override);
expect(result.server).toStrictEqual({
port: 8081,
tls: {key: 'key', cert: 'cert'},
});
});

test('override tls: null replaces base tls: undefined', () => {
const base: InputConfigT = {server: {}};
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const override: InputConfigT = {server: {tls: null}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(null);
});
});

describe('override is NOT applied when tls is null or undefined', () => {
test('override tls: undefined keeps base tls: object', () => {
const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}};
const override: InputConfigT = {server: {}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'});
});

test('override tls: undefined (explicit) keeps base tls: object', () => {
const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}};
// $FlowExpectedError[incompatible-type] - testing explicit undefined
const override: InputConfigT = {server: {tls: undefined}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'});
});

test('override tls: undefined keeps base tls: false', () => {
const base: InputConfigT = {server: {tls: false}};
const override: InputConfigT = {server: {}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(false);
});

test('override tls: undefined (explicit) keeps base tls: false', () => {
const base: InputConfigT = {server: {tls: false}};
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const override: InputConfigT = {server: {tls: undefined}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(false);
});

test('override tls: null keeps base tls: object', () => {
const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}};
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const override: InputConfigT = {server: {tls: null}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'});
});

test('override tls: null keeps base tls: false', () => {
const base: InputConfigT = {server: {tls: false}};
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const override: InputConfigT = {server: {tls: null}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBe(false);
});

test('both tls undefined results in no tls property', () => {
const base: InputConfigT = {server: {}};
const override: InputConfigT = {server: {}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBeUndefined();
});

test('both tls undefined (explicit) results in no tls property', () => {
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const base: InputConfigT = {server: {tls: undefined}};
// $FlowExpectedError[incompatible-type] - testing untyped runtime behavior
const override: InputConfigT = {server: {tls: undefined}};
const result = mergeConfig(base, override);
expect(result.server?.tls).toBeUndefined();
});
});
});
});
1 change: 1 addition & 0 deletions packages/metro-config/src/defaults/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const getDefaultValues = (projectRoot: ?string): ConfigT => ({
unstable_serverRoot: null,
useGlobalHotkey: true,
verifyConnections: false,
tls: false,
},

symbolicator: {
Expand Down
15 changes: 14 additions & 1 deletion packages/metro-config/src/loadConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,21 @@ function mergeConfigObjects<T: InputConfigT>(
},
server: {
...base.server,
// $FlowFixMe[exponential-spread]
...overrides.server,
// $FlowFixMe[exponential-spread]
...(base.server?.tls != null ? {tls: base.server?.tls} : null),
// only override base tls config with false or an object
...(overrides.server?.tls === false
? {tls: false}
: overrides.server?.tls != null &&
typeof overrides.server?.tls === 'object'
? {
tls: {
...(base.server?.tls || {}),
...overrides.server?.tls,
},
}
: null),
},
symbolicator: {
...base.symbolicator,
Expand Down
8 changes: 8 additions & 0 deletions packages/metro-config/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ type ServerConfigT = {
unstable_serverRoot: ?string,
useGlobalHotkey: boolean,
verifyConnections: boolean,
tls:
| false
| {
ca?: string | Buffer,
cert?: string | Buffer,
key?: string | Buffer,
requestCert?: boolean,
},
};

type SymbolicatorConfigT = {
Expand Down
8 changes: 8 additions & 0 deletions packages/metro-config/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ type ServerConfigT = {
unstable_serverRoot: null | undefined | string;
useGlobalHotkey: boolean;
verifyConnections: boolean;
tls:
| false
| {
ca?: string | Buffer;
cert?: string | Buffer;
key?: string | Buffer;
requestCert?: boolean;
};
};
type SymbolicatorConfigT = {
customizeFrame: ($$PARAM_0$$: {
Expand Down
4 changes: 2 additions & 2 deletions packages/metro-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-core",
"version": "0.83.4",
"version": "0.83.5",
"description": "🚇 Metro's core package.",
"main": "src/index.js",
"exports": {
Expand All @@ -20,7 +20,7 @@
"dependencies": {
"flow-enums-runtime": "^0.0.6",
"lodash.throttle": "^4.1.1",
"metro-resolver": "0.83.4"
"metro-resolver": "0.83.5"
},
"license": "MIT",
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-file-map/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metro-file-map",
"version": "0.83.4",
"version": "0.83.5",
"description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
"main": "src/index.js",
"exports": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe('cacheManager', () => {
let pluginCacheKey = 'foo';
const plugin: FileMapPlugin<> = {
name: 'foo',
async bulkUpdate() {},
bulkUpdate() {},
async initialize() {},
assertValid() {},
getSerializableSnapshot() {
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-file-map/src/flow-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export interface FileMapPlugin<
initOptions: FileMapPluginInitOptions<SerializableState, PerFileData>,
): Promise<void>;
assertValid(): void;
bulkUpdate(delta: FileMapDelta<?PerFileData>): Promise<void>;
bulkUpdate(delta: FileMapDelta<?PerFileData>): void;
getSerializableSnapshot(): SerializableState;
onRemovedFile(relativeFilePath: string, pluginData: ?PerFileData): void;
onNewOrModifiedFile(relativeFilePath: string, pluginData: ?PerFileData): void;
Expand Down
Loading