diff --git a/.gitignore b/.gitignore index 535e615ce..92534dae0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ src/reactpy/static/*.js* src/reactpy/static/morphdom/ src/reactpy/static/pyscript/ +src/js/**/*.tgz +src/js/**/LICENSE # --- Jupyter --- *.ipynb_checkpoints diff --git a/pyproject.toml b/pyproject.toml index dcbeed297..6ba2f2f83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -210,15 +210,15 @@ build_client = ['hatch run "src/build_scripts/build_js_client.py" {args}'] build_app = ['hatch run "src/build_scripts/build_js_app.py" {args}'] publish_event_to_object = [ 'hatch run javascript:build_event_to_object', - # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun - # Ref: https://github.com/oven-sh/bun/issues/15601 - 'cd "src/js/packages/event-to-object" && bunx npm@11.8.0 publish --provenance --access public', + # FIXME: This is a temporary workaround. We are using `bun pm pack`->`npm publish` to fix missing "Trusted Publishing" support in `bun publish` + # See the following ticket https://github.com/oven-sh/bun/issues/15601 + 'cd "src/js/packages/event-to-object" && bun pm pack --filename "packages/event-to-object/dist.tgz" && bunx npm@11.8.0 publish dist.tgz --provenance --access public', ] publish_client = [ 'hatch run javascript:build_client', - # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun - # Ref: https://github.com/oven-sh/bun/issues/15601 - 'cd "src/js/packages/@reactpy/client" && bunx npm@11.8.0 publish --provenance --access public', + # FIXME: This is a temporary workaround. We are using `bun pm pack`->`npm publish` to fix missing "Trusted Publishing" support in `bun publish` + # See the following ticket https://github.com/oven-sh/bun/issues/15601 + 'cd "src/js/packages/@reactpy/client" && bun pm pack --filename "packages/@reactpy/client/dist.tgz" && bunx npm@11.8.0 publish dist.tgz --provenance --access public', ] ######################### diff --git a/src/build_scripts/build_js_client.py b/src/build_scripts/build_js_client.py index 5fab9440d..27c33e6ee 100644 --- a/src/build_scripts/build_js_client.py +++ b/src/build_scripts/build_js_client.py @@ -3,11 +3,18 @@ # dependencies = [] # /// import pathlib +import shutil import subprocess import sys dev_mode = "--dev" in sys.argv root_dir = pathlib.Path(__file__).parent.parent.parent + +# Copy LICENSE file +shutil.copyfile( + root_dir / "LICENSE", root_dir / "src/js/packages/@reactpy/client/LICENSE" +) + build_commands = [ [ "bun", diff --git a/src/build_scripts/build_js_event_to_object.py b/src/build_scripts/build_js_event_to_object.py index 88f4af64f..af8532bc0 100644 --- a/src/build_scripts/build_js_event_to_object.py +++ b/src/build_scripts/build_js_event_to_object.py @@ -3,11 +3,18 @@ # dependencies = [] # /// import pathlib +import shutil import subprocess import sys dev_mode = "--dev" in sys.argv root_dir = pathlib.Path(__file__).parent.parent.parent + +# Copy LICENSE file +shutil.copyfile( + root_dir / "LICENSE", root_dir / "src/js/packages/event-to-object/LICENSE" +) + build_commands = [ [ "bun", diff --git a/src/build_scripts/clean_js_dir.py b/src/build_scripts/clean_js_dir.py index f6b2b000f..0ab11b706 100644 --- a/src/build_scripts/clean_js_dir.py +++ b/src/build_scripts/clean_js_dir.py @@ -23,6 +23,12 @@ with contextlib.suppress(FileNotFoundError): shutil.rmtree(dist_dir) +# Delete all `*.tgz` files in `packages/**` +dist_tgz_files = glob.glob(str(js_src_dir / "**/*.tgz"), recursive=True) +for dist_tgz_file in dist_tgz_files: + with contextlib.suppress(FileNotFoundError): + os.remove(dist_tgz_file) + # Delete all `node_modules` folders node_modules_dirs = glob.glob(str(js_src_dir / "**/node_modules"), recursive=True) for node_modules_dir in node_modules_dirs: diff --git a/src/js/packages/@reactpy/app/tsconfig.json b/src/js/packages/@reactpy/app/tsconfig.json index de3ec6378..5a293d57e 100644 --- a/src/js/packages/@reactpy/app/tsconfig.json +++ b/src/js/packages/@reactpy/app/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { - "composite": true, "outDir": "dist", "rootDir": "src", + "composite": true, + "noEmit": false, "esModuleInterop": true }, "extends": "../../../tsconfig.json", diff --git a/src/js/packages/@reactpy/client/package.json b/src/js/packages/@reactpy/client/package.json index d8e2fcefd..6ca29ca8d 100644 --- a/src/js/packages/@reactpy/client/package.json +++ b/src/js/packages/@reactpy/client/package.json @@ -9,6 +9,11 @@ "event-to-object": "catalog:" }, "description": "A client for ReactPy implemented in React", + "files": [ + "dist", + "src", + "LICENSE" + ], "devDependencies": { "@types/json-pointer": "catalog:", "typescript": "catalog:" @@ -31,5 +36,5 @@ "checkTypes": "tsc --noEmit" }, "type": "module", - "version": "1.0.2" + "version": "1.0.3" } diff --git a/src/js/packages/@reactpy/client/tsconfig.json b/src/js/packages/@reactpy/client/tsconfig.json index b6bb436d0..c0f6f27d8 100644 --- a/src/js/packages/@reactpy/client/tsconfig.json +++ b/src/js/packages/@reactpy/client/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { - "composite": true, "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "composite": true, + "noEmit": false }, "extends": "../../../tsconfig.json", "include": ["src"], diff --git a/src/js/packages/event-to-object/package.json b/src/js/packages/event-to-object/package.json index 85c6b1b2c..62b828f3b 100644 --- a/src/js/packages/event-to-object/package.json +++ b/src/js/packages/event-to-object/package.json @@ -7,6 +7,11 @@ "json-pointer": "catalog:" }, "description": "Converts a JavaScript events to JSON serializable objects.", + "files": [ + "dist", + "src", + "LICENSE" + ], "devDependencies": { "happy-dom": "^15.0.0", "lodash": "^4.17.21", @@ -31,5 +36,5 @@ "checkTypes": "tsc --noEmit" }, "type": "module", - "version": "1.0.1" + "version": "2.0.0" } diff --git a/src/js/packages/event-to-object/tsconfig.json b/src/js/packages/event-to-object/tsconfig.json index 4fec695a9..4e3b040c6 100644 --- a/src/js/packages/event-to-object/tsconfig.json +++ b/src/js/packages/event-to-object/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { - "composite": true, "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "composite": true, + "noEmit": false }, "extends": "../../tsconfig.json", "include": ["src"] diff --git a/src/js/tsconfig.json b/src/js/tsconfig.json index e3c400628..ada0272d5 100644 --- a/src/js/tsconfig.json +++ b/src/js/tsconfig.json @@ -13,7 +13,7 @@ "module": "Preserve", "moduleDetection": "force", "moduleResolution": "bundler", - "noEmitOnError": true, + "noEmit": true, "noUnusedLocals": true, "resolveJsonModule": true, "skipLibCheck": true, diff --git a/tests/__init__.py b/tests/__init__.py index e69de29bb..e76aa7ffd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +import pytest + +from reactpy.testing import GITHUB_ACTIONS + +pytestmark = [pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1)] diff --git a/tests/test_asgi/test_middleware.py b/tests/test_asgi/test_middleware.py index 2c0a5ec58..8a801b758 100644 --- a/tests/test_asgi/test_middleware.py +++ b/tests/test_asgi/test_middleware.py @@ -15,6 +15,8 @@ from reactpy.executors.asgi.middleware import ReactPyMiddleware from reactpy.testing import BackendFixture, DisplayFixture +from .. import pytestmark # noqa: F401 + @pytest.fixture(scope="module") async def display(browser): diff --git a/tests/test_asgi/test_pyscript.py b/tests/test_asgi/test_pyscript.py index 9a86a9592..78b4d4c62 100644 --- a/tests/test_asgi/test_pyscript.py +++ b/tests/test_asgi/test_pyscript.py @@ -12,6 +12,8 @@ from reactpy.executors.asgi.pyscript import ReactPyCsr from reactpy.testing import BackendFixture, DisplayFixture +from .. import pytestmark # noqa: F401 + @pytest.fixture(scope="module") async def display(browser): diff --git a/tests/test_asgi/test_standalone.py b/tests/test_asgi/test_standalone.py index 2d4baa544..bebbad22d 100644 --- a/tests/test_asgi/test_standalone.py +++ b/tests/test_asgi/test_standalone.py @@ -13,6 +13,8 @@ from reactpy.testing.common import REACTPY_TESTS_DEFAULT_TIMEOUT from reactpy.types import Connection, Location +from .. import pytestmark # noqa: F401 + async def test_display_simple_hello_world(display: DisplayFixture): @reactpy.component diff --git a/tests/test_reactjs/test_modules_from_npm.py b/tests/test_reactjs/test_modules_from_npm.py index 4216ef672..1b537633b 100644 --- a/tests/test_reactjs/test_modules_from_npm.py +++ b/tests/test_reactjs/test_modules_from_npm.py @@ -4,7 +4,9 @@ import reactpy from reactpy import html from reactpy.reactjs import component_from_npm, import_reactjs -from reactpy.testing import GITHUB_ACTIONS, BackendFixture, DisplayFixture +from reactpy.testing import BackendFixture, DisplayFixture + +from .. import pytestmark # noqa: F401 MISSING_IMPORT_MAP_MSG = "ReactPy did not detect a suitable JavaScript import map" @@ -17,7 +19,6 @@ async def display(browser): yield new_display -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_react_bootstrap(display: DisplayFixture, caplog): Button = component_from_npm("react-bootstrap", "Button", version="2") @@ -39,7 +40,6 @@ def App(): assert MISSING_IMPORT_MAP_MSG not in " ".join(x.msg for x in caplog.records) -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_react_bootstrap_with_local_framework(browser, caplog): Button = component_from_npm("react-bootstrap", "Button", version="2") @@ -65,7 +65,6 @@ def App(): assert MISSING_IMPORT_MAP_MSG not in " ".join(x.msg for x in caplog.records) -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_without_explicit_reactjs_import(browser, caplog): Button = component_from_npm("react-bootstrap", "Button", version="2") @@ -89,7 +88,6 @@ def App(): assert MISSING_IMPORT_MAP_MSG in " ".join(x.msg for x in caplog.records) -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_material_ui(display: DisplayFixture): Button = component_from_npm("@mui/material", "Button", version="7") @@ -105,7 +103,6 @@ def App(): await expect(button).to_contain_class("MuiButton-root") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_antd(display: DisplayFixture): Button = component_from_npm("antd", "Button", version="6") @@ -119,7 +116,6 @@ def App(): await expect(button).to_contain_class("ant-btn") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_chakra_ui(display: DisplayFixture): ChakraProvider, _, Button = _get_chakra_components() @@ -135,7 +131,6 @@ def App(): await expect(button).to_contain_class("chakra-button") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_semantic_ui_react(browser): # Semantic UI is deprecated and doesn't get updates. Thus, it is incompatible with the # latest React versions. We use this as an opportunity to test Preact here. @@ -156,7 +151,6 @@ def App(): await expect(button).to_contain_class("button") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_mantine(display: DisplayFixture): MantineProvider, Button = component_from_npm( "@mantine/core", ["MantineProvider", "Button"], version="8" @@ -172,7 +166,6 @@ def App(): await expect(button).to_contain_class("mantine-Button-root") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_fluent_ui(display: DisplayFixture): PrimaryButton = component_from_npm("@fluentui/react", "PrimaryButton", version="8") @@ -186,7 +179,6 @@ def App(): await expect(button).to_contain_class("ms-Button") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_blueprint(display: DisplayFixture): Button = component_from_npm("@blueprintjs/core", "Button", version="6") @@ -200,7 +192,6 @@ def App(): await expect(button).to_contain_class("bp6-button") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_grommet(display: DisplayFixture): Grommet, Button = component_from_npm("grommet", ["Grommet", "Button"], version="2") @@ -215,7 +206,6 @@ def App(): await expect(button).to_have_text("Click me") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_evergreen(display: DisplayFixture): Button = component_from_npm("evergreen-ui", "Button", version="7") @@ -228,7 +218,6 @@ def App(): await expect(button).to_have_text("Click me") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_component_from_npm_react_spinners(display: DisplayFixture): ClipLoader = component_from_npm("react-spinners", "ClipLoader", version="0.17.0") @@ -251,7 +240,6 @@ def App(): await expect(loader).to_be_visible() -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_nested_npm_components(display: DisplayFixture): ChakraProvider, Box, _ = _get_chakra_components() BootstrapButton = component_from_npm("react-bootstrap", "Button", version="2") @@ -283,7 +271,6 @@ def App(): await expect(button).to_contain_class("btn") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_interleaved_npm_and_server_components(display: DisplayFixture): Card = component_from_npm("antd", "Card", version="6") Button = component_from_npm("@mui/material", "Button", version="7") @@ -316,7 +303,6 @@ def App(): await expect(button).to_have_css("text-transform", "uppercase") -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_complex_nested_material_ui(display: DisplayFixture): mui_components = component_from_npm( "@mui/material", diff --git a/tests/test_widgets.py b/tests/test_widgets.py index e734b91fb..0c089d0cd 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1,12 +1,12 @@ from base64 import b64encode from pathlib import Path -import pytest - import reactpy -from reactpy.testing import GITHUB_ACTIONS, DisplayFixture, poll +from reactpy.testing import DisplayFixture, poll from tests.tooling.common import DEFAULT_TYPE_DELAY +from . import pytestmark # noqa: F401 + HERE = Path(__file__).parent @@ -110,7 +110,6 @@ def SomeComponent(): await poll_value.until_equals(12) -@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_use_linked_inputs_ignore_empty(display: DisplayFixture): value = reactpy.Ref(None)