diff --git a/packages/react-html/src/ReactHTMLClient.stable.js b/packages/react-html/src/ReactHTMLClient.stable.js
new file mode 100644
index 0000000000000..0874c2b7af877
--- /dev/null
+++ b/packages/react-html/src/ReactHTMLClient.stable.js
@@ -0,0 +1,11 @@
+/**
+ * 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
+ */
+
+// eslint-disable-next-line react-internal/prod-error-codes
+throw new Error('react-html should not get built in stable');
diff --git a/packages/react-html/src/ReactHTMLServer.stable.js b/packages/react-html/src/ReactHTMLServer.stable.js
new file mode 100644
index 0000000000000..0874c2b7af877
--- /dev/null
+++ b/packages/react-html/src/ReactHTMLServer.stable.js
@@ -0,0 +1,11 @@
+/**
+ * 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
+ */
+
+// eslint-disable-next-line react-internal/prod-error-codes
+throw new Error('react-html should not get built in stable');
diff --git a/packages/react-html/src/__tests__/ReactHTMLClient-test.js b/packages/react-html/src/__tests__/ReactHTMLClient-test.js
index 36a44ebc6d21a..92fb762c8ca1b 100644
--- a/packages/react-html/src/__tests__/ReactHTMLClient-test.js
+++ b/packages/react-html/src/__tests__/ReactHTMLClient-test.js
@@ -12,141 +12,152 @@
let React;
let ReactHTML;
-describe('ReactHTML', () => {
- beforeEach(() => {
- jest.resetModules();
- React = require('react');
- ReactHTML = require('react-html');
+if (!__EXPERIMENTAL__) {
+ it('should not be built in stable', () => {
+ try {
+ require('react-html');
+ } catch (x) {
+ return;
+ }
+ throw new Error('Expected react-html not to exist in stable.');
});
+} else {
+ describe('ReactHTML', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactHTML = require('react-html');
+ });
- it('should be able to render a simple component', async () => {
- function Component() {
- return
hello world
;
- }
+ it('should be able to render a simple component', async () => {
+ function Component() {
+ return hello world
;
+ }
- const html = await ReactHTML.renderToMarkup();
- expect(html).toBe('hello world
');
- });
+ const html = await ReactHTML.renderToMarkup();
+ expect(html).toBe('hello world
');
+ });
- it('should prefix html tags with a doctype', async () => {
- const html = await ReactHTML.renderToMarkup(
-
- hello
- ,
- );
- expect(html).toBe(
- 'hello',
- );
- });
+ it('should prefix html tags with a doctype', async () => {
+ const html = await ReactHTML.renderToMarkup(
+
+ hello
+ ,
+ );
+ expect(html).toBe(
+ 'hello',
+ );
+ });
- it('should error on useState', async () => {
- function Component() {
- const [state] = React.useState('hello');
- return {state}
;
- }
+ it('should error on useState', async () => {
+ function Component() {
+ const [state] = React.useState('hello');
+ return {state}
;
+ }
- await expect(async () => {
- await ReactHTML.renderToMarkup();
- }).rejects.toThrow();
- });
+ await expect(async () => {
+ await ReactHTML.renderToMarkup();
+ }).rejects.toThrow();
+ });
- it('should error on refs passed to host components', async () => {
- function Component() {
- const ref = React.createRef();
- return ;
- }
+ it('should error on refs passed to host components', async () => {
+ function Component() {
+ const ref = React.createRef();
+ return ;
+ }
- await expect(async () => {
- await ReactHTML.renderToMarkup();
- }).rejects.toThrow();
- });
+ await expect(async () => {
+ await ReactHTML.renderToMarkup();
+ }).rejects.toThrow();
+ });
- it('should error on callbacks passed to event handlers', async () => {
- function Component() {
- function onClick() {
- // This won't be able to be called.
+ it('should error on callbacks passed to event handlers', async () => {
+ function Component() {
+ function onClick() {
+ // This won't be able to be called.
+ }
+ return ;
}
- return ;
- }
- await expect(async () => {
- await ReactHTML.renderToMarkup();
- }).rejects.toThrow();
- });
+ await expect(async () => {
+ await ReactHTML.renderToMarkup();
+ }).rejects.toThrow();
+ });
- it('supports the useId Hook', async () => {
- function Component() {
- const firstNameId = React.useId();
- const lastNameId = React.useId();
- return React.createElement(
- 'div',
- null,
- React.createElement(
- 'h2',
- {
- id: firstNameId,
- },
- 'First',
- ),
- React.createElement(
- 'p',
- {
- 'aria-labelledby': firstNameId,
- },
- 'Sebastian',
- ),
- React.createElement(
- 'h2',
- {
- id: lastNameId,
- },
- 'Last',
- ),
- React.createElement(
- 'p',
- {
- 'aria-labelledby': lastNameId,
- },
- 'Smith',
- ),
- );
- }
+ it('supports the useId Hook', async () => {
+ function Component() {
+ const firstNameId = React.useId();
+ const lastNameId = React.useId();
+ return React.createElement(
+ 'div',
+ null,
+ React.createElement(
+ 'h2',
+ {
+ id: firstNameId,
+ },
+ 'First',
+ ),
+ React.createElement(
+ 'p',
+ {
+ 'aria-labelledby': firstNameId,
+ },
+ 'Sebastian',
+ ),
+ React.createElement(
+ 'h2',
+ {
+ id: lastNameId,
+ },
+ 'Last',
+ ),
+ React.createElement(
+ 'p',
+ {
+ 'aria-labelledby': lastNameId,
+ },
+ 'Smith',
+ ),
+ );
+ }
- const html = await ReactHTML.renderToMarkup();
- const container = document.createElement('div');
- container.innerHTML = html;
-
- expect(container.getElementsByTagName('h2')[0].id).toBe(
- container.getElementsByTagName('p')[0].getAttribute('aria-labelledby'),
- );
- expect(container.getElementsByTagName('h2')[1].id).toBe(
- container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
- );
-
- // It's not the same id between them.
- expect(container.getElementsByTagName('h2')[0].id).not.toBe(
- container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
- );
- });
+ const html = await ReactHTML.renderToMarkup();
+ const container = document.createElement('div');
+ container.innerHTML = html;
- // @gate disableClientCache
- it('does NOT support cache yet because it is a client component', async () => {
- let counter = 0;
- const getCount = React.cache(() => {
- return counter++;
- });
- function Component() {
- const a = getCount();
- const b = getCount();
- return (
-
- {a}
- {b}
-
+ expect(container.getElementsByTagName('h2')[0].id).toBe(
+ container.getElementsByTagName('p')[0].getAttribute('aria-labelledby'),
);
- }
+ expect(container.getElementsByTagName('h2')[1].id).toBe(
+ container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
+ );
+
+ // It's not the same id between them.
+ expect(container.getElementsByTagName('h2')[0].id).not.toBe(
+ container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
+ );
+ });
+
+ // @gate disableClientCache
+ it('does NOT support cache yet because it is a client component', async () => {
+ let counter = 0;
+ const getCount = React.cache(() => {
+ return counter++;
+ });
+ function Component() {
+ const a = getCount();
+ const b = getCount();
+ return (
+
+ {a}
+ {b}
+
+ );
+ }
- const html = await ReactHTML.renderToMarkup();
- expect(html).toBe('01
');
+ const html = await ReactHTML.renderToMarkup();
+ expect(html).toBe('01
');
+ });
});
-});
+}
diff --git a/packages/react-html/src/__tests__/ReactHTMLServer-test.js b/packages/react-html/src/__tests__/ReactHTMLServer-test.js
index 1a9976aafbd49..f97b5aa92d60e 100644
--- a/packages/react-html/src/__tests__/ReactHTMLServer-test.js
+++ b/packages/react-html/src/__tests__/ReactHTMLServer-test.js
@@ -15,150 +15,178 @@ global.TextEncoder = require('util').TextEncoder;
let React;
let ReactHTML;
-describe('ReactHTML', () => {
- beforeEach(() => {
- jest.resetModules();
- // We run in the react-server condition.
- jest.mock('react', () => require('react/react.react-server'));
- jest.mock('react-html', () =>
- require('react-html/react-html.react-server'),
- );
-
- React = require('react');
- ReactHTML = require('react-html');
+if (!__EXPERIMENTAL__) {
+ it('should not be built in stable', () => {
+ try {
+ require('react-html');
+ } catch (x) {
+ return;
+ }
+ throw new Error('Expected react-html not to exist in stable.');
});
+} else {
+ describe('ReactHTML', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ // We run in the react-server condition.
+ jest.mock('react', () => require('react/react.react-server'));
+ if (__EXPERIMENTAL__) {
+ jest.mock('react-html', () =>
+ require('react-html/react-html.react-server'),
+ );
+ }
- it('should be able to render a simple component', async () => {
- function Component() {
- // We can't use JSX because that's client-JSX in our tests.
- return React.createElement('div', null, 'hello world');
- }
+ React = require('react');
+ if (__EXPERIMENTAL__) {
+ ReactHTML = require('react-html');
+ } else {
+ try {
+ require('react-html/react-html.react-server');
+ } catch (x) {
+ return;
+ }
+ throw new Error('Expected react-html not to exist in stable.');
+ }
+ });
- const html = await ReactHTML.renderToMarkup(React.createElement(Component));
- expect(html).toBe('hello world
');
- });
+ it('should be able to render a simple component', async () => {
+ function Component() {
+ // We can't use JSX because that's client-JSX in our tests.
+ return React.createElement('div', null, 'hello world');
+ }
- it('should prefix html tags with a doctype', async () => {
- const html = await ReactHTML.renderToMarkup(
- // We can't use JSX because that's client-JSX in our tests.
- React.createElement(
- 'html',
- null,
- React.createElement('body', null, 'hello'),
- ),
- );
- expect(html).toBe(
- 'hello',
- );
- });
+ const html = await ReactHTML.renderToMarkup(
+ React.createElement(Component),
+ );
+ expect(html).toBe('hello world
');
+ });
- it('should error on useState', async () => {
- function Component() {
- const [state] = React.useState('hello');
- // We can't use JSX because that's client-JSX in our tests.
- return React.createElement('div', null, state);
- }
+ it('should prefix html tags with a doctype', async () => {
+ const html = await ReactHTML.renderToMarkup(
+ // We can't use JSX because that's client-JSX in our tests.
+ React.createElement(
+ 'html',
+ null,
+ React.createElement('body', null, 'hello'),
+ ),
+ );
+ expect(html).toBe(
+ 'hello',
+ );
+ });
- await expect(async () => {
- await ReactHTML.renderToMarkup(React.createElement(Component));
- }).rejects.toThrow();
- });
+ it('should error on useState', async () => {
+ function Component() {
+ const [state] = React.useState('hello');
+ // We can't use JSX because that's client-JSX in our tests.
+ return React.createElement('div', null, state);
+ }
- it('should error on refs passed to host components', async () => {
- function Component() {
- const ref = React.createRef();
- // We can't use JSX because that's client-JSX in our tests.
- return React.createElement('div', {ref});
- }
+ await expect(async () => {
+ await ReactHTML.renderToMarkup(React.createElement(Component));
+ }).rejects.toThrow();
+ });
- await expect(async () => {
- await ReactHTML.renderToMarkup(React.createElement(Component));
- }).rejects.toThrow();
- });
+ it('should error on refs passed to host components', async () => {
+ function Component() {
+ const ref = React.createRef();
+ // We can't use JSX because that's client-JSX in our tests.
+ return React.createElement('div', {ref});
+ }
+
+ await expect(async () => {
+ await ReactHTML.renderToMarkup(React.createElement(Component));
+ }).rejects.toThrow();
+ });
- it('should error on callbacks passed to event handlers', async () => {
- function Component() {
- function onClick() {
- // This won't be able to be called.
+ it('should error on callbacks passed to event handlers', async () => {
+ function Component() {
+ function onClick() {
+ // This won't be able to be called.
+ }
+ // We can't use JSX because that's client-JSX in our tests.
+ return React.createElement('div', {onClick});
}
- // We can't use JSX because that's client-JSX in our tests.
- return React.createElement('div', {onClick});
- }
- await expect(async () => {
- await ReactHTML.renderToMarkup(React.createElement(Component));
- }).rejects.toThrow();
- });
+ await expect(async () => {
+ await ReactHTML.renderToMarkup(React.createElement(Component));
+ }).rejects.toThrow();
+ });
- it('supports the useId Hook', async () => {
- function Component() {
- const firstNameId = React.useId();
- const lastNameId = React.useId();
- // We can't use JSX because that's client-JSX in our tests.
- return React.createElement(
- 'div',
- null,
- React.createElement(
- 'h2',
- {
- id: firstNameId,
- },
- 'First',
- ),
- React.createElement(
- 'p',
- {
- 'aria-labelledby': firstNameId,
- },
- 'Sebastian',
- ),
- React.createElement(
- 'h2',
- {
- id: lastNameId,
- },
- 'Last',
- ),
- React.createElement(
- 'p',
- {
- 'aria-labelledby': lastNameId,
- },
- 'Smith',
- ),
+ it('supports the useId Hook', async () => {
+ function Component() {
+ const firstNameId = React.useId();
+ const lastNameId = React.useId();
+ // We can't use JSX because that's client-JSX in our tests.
+ return React.createElement(
+ 'div',
+ null,
+ React.createElement(
+ 'h2',
+ {
+ id: firstNameId,
+ },
+ 'First',
+ ),
+ React.createElement(
+ 'p',
+ {
+ 'aria-labelledby': firstNameId,
+ },
+ 'Sebastian',
+ ),
+ React.createElement(
+ 'h2',
+ {
+ id: lastNameId,
+ },
+ 'Last',
+ ),
+ React.createElement(
+ 'p',
+ {
+ 'aria-labelledby': lastNameId,
+ },
+ 'Smith',
+ ),
+ );
+ }
+
+ const html = await ReactHTML.renderToMarkup(
+ React.createElement(Component),
);
- }
+ const container = document.createElement('div');
+ container.innerHTML = html;
- const html = await ReactHTML.renderToMarkup(React.createElement(Component));
- const container = document.createElement('div');
- container.innerHTML = html;
-
- expect(container.getElementsByTagName('h2')[0].id).toBe(
- container.getElementsByTagName('p')[0].getAttribute('aria-labelledby'),
- );
- expect(container.getElementsByTagName('h2')[1].id).toBe(
- container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
- );
-
- // It's not the same id between them.
- expect(container.getElementsByTagName('h2')[0].id).not.toBe(
- container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
- );
- });
+ expect(container.getElementsByTagName('h2')[0].id).toBe(
+ container.getElementsByTagName('p')[0].getAttribute('aria-labelledby'),
+ );
+ expect(container.getElementsByTagName('h2')[1].id).toBe(
+ container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
+ );
- // @gate enableCache
- it('supports cache', async () => {
- let counter = 0;
- const getCount = React.cache(() => {
- return counter++;
+ // It's not the same id between them.
+ expect(container.getElementsByTagName('h2')[0].id).not.toBe(
+ container.getElementsByTagName('p')[1].getAttribute('aria-labelledby'),
+ );
});
- function Component() {
- const a = getCount();
- const b = getCount();
- return React.createElement('div', null, a, b);
- }
- const html = await ReactHTML.renderToMarkup(React.createElement(Component));
- expect(html).toBe('00
');
+ // @gate enableCache
+ it('supports cache', async () => {
+ let counter = 0;
+ const getCount = React.cache(() => {
+ return counter++;
+ });
+ function Component() {
+ const a = getCount();
+ const b = getCount();
+ return React.createElement('div', null, a, b);
+ }
+
+ const html = await ReactHTML.renderToMarkup(
+ React.createElement(Component),
+ );
+ expect(html).toBe('00
');
+ });
});
-});
+}
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index db0a306ffcefc..d17db8976390e 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -365,7 +365,7 @@ const bundles = [
/******* React HTML RSC *******/
{
- bundleTypes: [NODE_DEV, NODE_PROD],
+ bundleTypes: __EXPERIMENTAL__ ? [NODE_DEV, NODE_PROD] : [],
moduleType: RENDERER,
entry: 'react-html/src/ReactHTMLServer.js',
name: 'react-html.react-server',
@@ -378,7 +378,7 @@ const bundles = [
/******* React HTML Client *******/
{
- bundleTypes: [NODE_DEV, NODE_PROD],
+ bundleTypes: __EXPERIMENTAL__ ? [NODE_DEV, NODE_PROD] : [],
moduleType: RENDERER,
entry: 'react-html/src/ReactHTMLClient.js',
name: 'react-html',