Skip to content

Commit

Permalink
rewrite the client in typescript (#951)
Browse files Browse the repository at this point in the history
* initial work to rewrite the client in typescript

* minor fixes

* fix js tests

* misc fixes

* rewrite event-to-object

* rewrite event-to-object tests

* finish tests

* improve typescript configuration

* show npm version

* workspace order

* minor fixes

* rework docs extension + misc changes

* build before check types

* fix typos

* move client under namespace dir

* fix types in noxfile
  • Loading branch information
rmorshea committed Mar 23, 2023
1 parent 6a4c947 commit 82a8bd6
Show file tree
Hide file tree
Showing 51 changed files with 2,947 additions and 3,712 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ repos:
- id: isort
name: isort
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.4
rev: v3.0.0-alpha.6
hooks:
- id: prettier
55 changes: 29 additions & 26 deletions docs/source/_custom_js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/source/_custom_js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"rollup": "^2.35.1"
},
"dependencies": {
"@reactpy/client": "file:../../../src/client/packages/client"
"@reactpy/client": "file:../../../src/client/packages/@reactpy/client"
}
}
26 changes: 13 additions & 13 deletions docs/source/_custom_js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { mountWithLayoutServer, LayoutServerInfo } from "@reactpy/client";
import { SimpleReactPyClient, mount } from "@reactpy/client";

let didMountDebug = false;

export function mountWidgetExample(
mountID,
viewID,
reactpyServerHost,
useActivateButton
useActivateButton,
) {
let reactpyHost, reactpyPort;
if (reactpyServerHost) {
Expand All @@ -16,27 +16,27 @@ export function mountWidgetExample(
reactpyPort = window.location.port;
}

const serverInfo = new LayoutServerInfo({
host: reactpyHost,
port: reactpyPort,
path: "/_reactpy/",
query: `view_id=${viewID}`,
secure: window.location.protocol == "https:",
const client = new SimpleReactPyClient({
serverLocation: {
url: `${window.location.protocol}//${reactpyHost}:${reactpyPort}`,
route: "/",
query: `?view_id=${viewID}`,
},
});

const mountEl = document.getElementById(mountID);
let isMounted = false;
triggerIfInViewport(mountEl, () => {
if (!isMounted) {
activateView(mountEl, serverInfo, useActivateButton);
activateView(mountEl, client, useActivateButton);
isMounted = true;
}
});
}

function activateView(mountEl, serverInfo, useActivateButton) {
function activateView(mountEl, client, useActivateButton) {
if (!useActivateButton) {
mountWithLayoutServer(mountEl, serverInfo);
mount(mountEl, client);
return;
}

Expand All @@ -51,7 +51,7 @@ function activateView(mountEl, serverInfo, useActivateButton) {
mountEl.setAttribute("class", "interactive widget-container");
mountWithLayoutServer(mountEl, serverInfo);
}
})
}),
);

function fadeOutElementThenCallback(element, callback) {
Expand Down Expand Up @@ -87,7 +87,7 @@ function triggerIfInViewport(element, callback) {
{
root: null,
threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport
}
},
);

observer.observe(element);
Expand Down
110 changes: 68 additions & 42 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def __call__(self, session: Session) -> Callable[[bool], None]:
SRC_DIR = ROOT_DIR / "src"
CLIENT_DIR = SRC_DIR / "client"
REACTPY_DIR = SRC_DIR / "reactpy"
LANGUAGE_TYPES: list[LanguageName] = ["py", "js"]
TAG_PATTERN = re.compile(
# start
r"^"
Expand All @@ -46,7 +45,6 @@ def __call__(self, session: Session) -> Callable[[bool], None]:
# end
r"$"
)
print(TAG_PATTERN.pattern)
REMAINING_ARGS = Option(nargs=REMAINDER, type=str)


Expand All @@ -60,6 +58,7 @@ def __call__(self, session: Session) -> Callable[[bool], None]:
def setup_checks(session: Session) -> None:
session.install("--upgrade", "pip")
session.run("pip", "--version")
session.run("npm", "--version", external=True)


@group.setup("check-javascript")
Expand Down Expand Up @@ -88,6 +87,12 @@ def format(session: Session) -> None:
session.run("npm", "run", "format", external=True)


@group.session
def tsc(session: Session) -> None:
session.chdir(CLIENT_DIR)
session.run("npx", "tsc", "-b", "-w", "packages/app", external=True)


@group.session
def example(session: Session) -> None:
"""Run an example"""
Expand Down Expand Up @@ -232,21 +237,24 @@ def check_docs(session: Session) -> None:


@group.session
def check_javascript_suite(session: Session) -> None:
"""Run the Javascript-based test suite and ensure it bundles succesfully"""
session.run("npm", "run", "test", external=True)
def check_javascript_tests(session: Session) -> None:
session.run("npm", "run", "check:tests", external=True)


@group.session
def check_javascript_build(session: Session) -> None:
"""Run the Javascript-based test suite and ensure it bundles succesfully"""
session.run("npm", "run", "test", external=True)
def check_javascript_format(session: Session) -> None:
session.run("npm", "run", "check:format", external=True)


@group.session
def check_javascript_format(session: Session) -> None:
"""Check that Javascript style guidelines are being followed"""
session.run("npm", "run", "check-format", external=True)
def check_javascript_types(session: Session) -> None:
session.run("npm", "run", "build", external=True)
session.run("npm", "run", "check:types", external=True)


@group.session
def check_javascript_build(session: Session) -> None:
session.run("npm", "run", "build", external=True)


@group.session
Expand All @@ -266,16 +274,38 @@ def build_python(session: Session) -> None:


@group.session
def publish(session: Session, dry_run: bool = False) -> None:
def publish(
session: Session,
publish_dry_run: Annotated[
bool,
Option(help="whether to test the release process"),
] = False,
publish_fake_tags: Annotated[
Sequence[str],
Option(nargs="*", type=str, help="fake tags to use for a dry run release"),
] = (),
) -> None:
packages = get_packages(session)

release_prep: dict[LanguageName, ReleasePrepFunc] = {
"js": prepare_javascript_release,
"py": prepare_python_release,
}

if publish_fake_tags and not publish_dry_run:
session.error("Cannot specify --publish-fake-tags without --publish-dry-run")

parsed_tags: list[TagInfo] = []
for tag in publish_fake_tags or get_current_tags(session):
tag_info = parse_tag(tag)
if tag_info is None:
session.error(
f"Invalid tag {tag} - must be of the form <package>-<language>-<version>"
)
parsed_tags.append(tag_info) # type: ignore

publishers: list[tuple[Path, Callable[[bool], None]]] = []
for tag, tag_pkg, tag_ver in get_current_tags(session):
for tag, tag_pkg, tag_ver in parsed_tags:
if tag_pkg not in packages:
session.error(f"Tag {tag} references package {tag_pkg} that does not exist")

Expand All @@ -293,7 +323,7 @@ def publish(session: Session, dry_run: bool = False) -> None:
for pkg_path, publish in publishers:
session.log(f"Publishing {pkg_path}...")
session.chdir(pkg_path)
publish(dry_run)
publish(publish_dry_run)


# --- Utilities ------------------------------------------------------------------------
Expand Down Expand Up @@ -386,23 +416,28 @@ def get_packages(session: Session) -> dict[str, PackageInfo]:
}

# collect javascript packages
for pkg in (CLIENT_DIR / "packages").glob("*"):
pkg_json_file = pkg / "package.json"
if not pkg_json_file.exists():
session.error(f"package.json not found in {pkg}")
js_package_paths: list[Path] = []
for maybed_pkg in (CLIENT_DIR / "packages").glob("*"):
if not (maybed_pkg / "package.json").exists():
for nmaybe_namespaced_pkg in maybed_pkg.glob("*"):
if (nmaybe_namespaced_pkg / "package.json").exists():
js_package_paths.append(nmaybe_namespaced_pkg)
else:
js_package_paths.append(maybed_pkg)

# get javascript package info
for pkg in js_package_paths:
pkg_json_file = pkg / "package.json" # we already know this exists

pkg_json = json.loads(pkg_json_file.read_text())

pkg_name = pkg_json.get("name")
pkg_version = pkg_json.get("version")

if pkg_version is None:
session.log(f"Skipping - {pkg_name} has no name or version in package.json")
if pkg_version is None or pkg_name is None:
session.log(f"Skipping - {pkg_name} has no name/version in package.json")
continue

if pkg_name is None:
session.error(f"Package {pkg} has no name in package.json")

if pkg_name in packages:
session.error(f"Duplicate package name {pkg_name}")

Expand All @@ -417,7 +452,7 @@ class PackageInfo(NamedTuple):
version: str


def get_current_tags(session: Session) -> list[TagInfo]:
def get_current_tags(session: Session) -> list[str]:
"""Get tags for the current commit"""
# check if unstaged changes
try:
Expand Down Expand Up @@ -465,24 +500,16 @@ def get_current_tags(session: Session) -> list[TagInfo]:
if not tags:
session.error("No tags found for current commit")

parsed_tags: list[TagInfo] = []
for tag in tags:
match = TAG_PATTERN.match(tag)
if not match:
session.error(
f"Invalid tag {tag} - must be of the form <package>-<language>-<version>"
)
parsed_tags.append(
TagInfo(
tag,
match["name"], # type: ignore[index]
match["version"], # type: ignore[index]
)
)
session.log(f"Found tags: {tags}")

return tags

session.log(f"Found tags: {[info.tag for info in parsed_tags]}")

return parsed_tags
def parse_tag(tag: str) -> TagInfo | None:
match = TAG_PATTERN.match(tag)
if not match:
return None
return TagInfo(tag, match["name"], match["version"])


class TagInfo(NamedTuple):
Expand All @@ -506,5 +533,4 @@ def get_reactpy_package_version(session: Session) -> str: # type: ignore[return
# remove the quotes
[1:-1]
)
else:
session.error(f"No version found in {pkg_root_init_file}")
session.error(f"No version found in {pkg_root_init_file}")
2 changes: 2 additions & 0 deletions src/client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tsconfig.tsbuildinfo
packages/**/package-lock.json
Loading

0 comments on commit 82a8bd6

Please sign in to comment.