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',