Repository: facebook/react
Pull request: Only build react-html in experimental channel

Even though the whole package is private right now. Once we publish it, it'll likely be just the experimental channel first before upgrading to stable.

This means it gets excluded from the built packages.

Changed files:
filename: packages/react-html/src/ReactHTMLClient.stable.js
patch: @@ -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');

filename: packages/react-html/src/ReactHTMLServer.stable.js
patch: @@ -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');

filename: packages/react-html/src/__tests__/ReactHTMLClient-test.js
patch: @@ -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 <div>hello world</div>;
-    }
+    it('should be able to render a simple component', async () => {
+      function Component() {
+        return <div>hello world</div>;
+      }
 
-    const html = await ReactHTML.renderToMarkup(<Component />);
-    expect(html).toBe('<div>hello world</div>');
-  });
+      const html = await ReactHTML.renderToMarkup(<Component />);
+      expect(html).toBe('<div>hello world</div>');
+    });
 
-  it('should prefix html tags with a doctype', async () => {
-    const html = await ReactHTML.renderToMarkup(
-      <html>
-        <body>hello</body>
-      </html>,
-    );
-    expect(html).toBe(
-      '<!DOCTYPE html><html><head></head><body>hello</body></html>',
-    );
-  });
+    it('should prefix html tags with a doctype', async () => {
+      const html = await ReactHTML.renderToMarkup(
+        <html>
+          <body>hello</body>
+        </html>,
+      );
+      expect(html).toBe(
+        '<!DOCTYPE html><html><head></head><body>hello</body></html>',
+      );
+    });
 
-  it('should error on useState', async () => {
-    function Component() {
-      const [state] = React.useState('hello');
-      return <div>{state}</div>;
-    }
+    it('should error on useState', async () => {
+      function Component() {
+        const [state] = React.useState('hello');
+        return <div>{state}</div>;
+      }
 
-    await expect(async () => {
-      await ReactHTML.renderToMarkup(<Component />);
-    }).rejects.toThrow();
-  });
+      await expect(async () => {
+        await ReactHTML.renderToMarkup(<Component />);
+      }).rejects.toThrow();
+    });
 
-  it('should error on refs passed to host components', async () => {
-    function Component() {
-      const ref = React.createRef();
-      return <div ref={ref} />;
-    }
+    it('should error on refs passed to host components', async () => {
+      function Component() {
+        const ref = React.createRef();
+        return <div ref={ref} />;
+      }
 
-    await expect(async () => {
-      await ReactHTML.renderToMarkup(<Component />);
-    }).rejects.toThrow();
-  });
+      await expect(async () => {
+        await ReactHTML.renderToMarkup(<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.
+        }
+        return <div onClick={onClick} />;
       }
-      return <div onClick={onClick} />;
-    }
 
-    await expect(async () => {
-      await ReactHTML.renderToMarkup(<Component />);
-    }).rejects.toThrow();
-  });
+      await expect(async () => {
+        await ReactHTML.renderToMarkup(<Component />);
+      }).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(<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'),
-    );
-  });
+      const html = await ReactHTML.renderToMarkup(<Component />);
+      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 (
-        <div>
-          {a}
-          {b}
-        </div>
+      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 (
+          <div>
+            {a}
+            {b}
+          </div>
+        );
+      }
 
-    const html = await ReactHTML.renderToMarkup(<Component />);
-    expect(html).toBe('<div>01</div>');
+      const html = await ReactHTML.renderToMarkup(<Component />);
+      expect(html).toBe('<div>01</div>');
+    });
   });
-});
+}

filename: packages/react-html/src/__tests__/ReactHTMLServer-test.js
patch: @@ -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('<div>hello world</div>');
-  });
+    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(
-      '<!DOCTYPE html><html><head></head><body>hello</body></html>',
-    );
-  });
+      const html = await ReactHTML.renderToMarkup(
+        React.createElement(Component),
+      );
+      expect(html).toBe('<div>hello world</div>');
+    });
 
-  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(
+        '<!DOCTYPE html><html><head></head><body>hello</body></html>',
+      );
+    });
 
-    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('<div>00</div>');
+    // @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('<div>00</div>');
+    });
   });
-});
+}

filename: scripts/rollup/bundles.js
patch: @@ -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',