Incrementally Add New Packages (#1607)
This commit is contained in:
parent
7d7b7901a9
commit
fed75ea7f8
@ -7,7 +7,6 @@
|
|||||||
"prod": "next start"
|
"prod": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/icons": "^2.0.19",
|
|
||||||
"@chakra-ui/react": "^2.6.0",
|
"@chakra-ui/react": "^2.6.0",
|
||||||
"@chakra-ui/system": "^2.5.6",
|
"@chakra-ui/system": "^2.5.6",
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
@ -15,28 +14,13 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"chakra-react-select": "^4.6.0",
|
"chakra-react-select": "^4.6.0",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "^10.12.4",
|
|
||||||
"gridjs": "^6.0.6",
|
|
||||||
"gridjs-react": "^6.0.1",
|
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"next": "^13.3.1",
|
"next": "^13.3.1",
|
||||||
"next-sitemap": "^4.1.8",
|
"next-sitemap": "^4.1.8",
|
||||||
"plotly.js": "^2.22.0",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-debounce-input": "^3.3.0",
|
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
|
||||||
"react-markdown": "^8.0.7",
|
|
||||||
"react-player": "^2.12.0",
|
|
||||||
"react-plotly.js": "^2.6.0",
|
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
|
||||||
"rehype-katex": "^6.0.3",
|
|
||||||
"rehype-raw": "^6.1.1",
|
|
||||||
"remark-gfm": "^3.0.1",
|
|
||||||
"remark-math": "^5.1.1",
|
|
||||||
"socket.io-client": "^4.6.1",
|
"socket.io-client": "^4.6.1",
|
||||||
"universal-cookie": "^4.0.4",
|
"universal-cookie": "^4.0.4"
|
||||||
"victory": "^36.6.8"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
@ -47,7 +47,7 @@ from reflex.route import (
|
|||||||
verify_route_validity,
|
verify_route_validity,
|
||||||
)
|
)
|
||||||
from reflex.state import DefaultState, State, StateManager, StateUpdate
|
from reflex.state import DefaultState, State, StateManager, StateUpdate
|
||||||
from reflex.utils import console, format, types
|
from reflex.utils import console, format, prerequisites, types
|
||||||
|
|
||||||
# Define custom types.
|
# Define custom types.
|
||||||
ComponentCallable = Callable[[], Component]
|
ComponentCallable = Callable[[], Component]
|
||||||
@ -483,6 +483,27 @@ class App(Base):
|
|||||||
|
|
||||||
admin.mount_to(self.api)
|
admin.mount_to(self.api)
|
||||||
|
|
||||||
|
def get_frontend_packages(self, imports: Dict[str, str]):
|
||||||
|
"""Gets the frontend packages to be installed and filters out the unnecessary ones.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
imports: A dictionary containing the imports used in the current page.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> get_frontend_packages({"react": "16.14.0", "react-dom": "16.14.0"})
|
||||||
|
"""
|
||||||
|
page_imports = [
|
||||||
|
i
|
||||||
|
for i in imports
|
||||||
|
if i not in compiler.DEFAULT_IMPORTS.keys()
|
||||||
|
and i != "focus-visible/dist/focus-visible"
|
||||||
|
and "next" not in i
|
||||||
|
and not i.startswith("/")
|
||||||
|
and i != ""
|
||||||
|
]
|
||||||
|
page_imports.extend(get_config().frontend_packages)
|
||||||
|
prerequisites.install_frontend_packages(page_imports)
|
||||||
|
|
||||||
def compile(self):
|
def compile(self):
|
||||||
"""Compile the app and output it to the pages folder."""
|
"""Compile the app and output it to the pages folder."""
|
||||||
if os.environ.get(constants.SKIP_COMPILE_ENV_VAR) == "yes":
|
if os.environ.get(constants.SKIP_COMPILE_ENV_VAR) == "yes":
|
||||||
@ -493,12 +514,13 @@ class App(Base):
|
|||||||
MofNCompleteColumn(),
|
MofNCompleteColumn(),
|
||||||
TimeElapsedColumn(),
|
TimeElapsedColumn(),
|
||||||
)
|
)
|
||||||
task = progress.add_task("Compiling: ", total=len(self.pages))
|
|
||||||
|
|
||||||
# TODO: include all work done in progress indicator, not just self.pages
|
|
||||||
for render, kwargs in DECORATED_PAGES:
|
for render, kwargs in DECORATED_PAGES:
|
||||||
self.add_page(render, **kwargs)
|
self.add_page(render, **kwargs)
|
||||||
|
|
||||||
|
task = progress.add_task("Compiling: ", total=len(self.pages))
|
||||||
|
# TODO: include all work done in progress indicator, not just self.pages
|
||||||
|
|
||||||
# Get the env mode.
|
# Get the env mode.
|
||||||
config = get_config()
|
config = get_config()
|
||||||
|
|
||||||
@ -509,6 +531,7 @@ class App(Base):
|
|||||||
custom_components = set()
|
custom_components = set()
|
||||||
# TODO Anecdotally, processes=2 works 10% faster (cpu_count=12)
|
# TODO Anecdotally, processes=2 works 10% faster (cpu_count=12)
|
||||||
thread_pool = ThreadPool()
|
thread_pool = ThreadPool()
|
||||||
|
all_imports = {}
|
||||||
with progress:
|
with progress:
|
||||||
for route, component in self.pages.items():
|
for route, component in self.pages.items():
|
||||||
# TODO: this progress does not reflect actual threaded task completion
|
# TODO: this progress does not reflect actual threaded task completion
|
||||||
@ -524,8 +547,12 @@ class App(Base):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# add component.get_imports() to all_imports
|
||||||
|
all_imports.update(component.get_imports())
|
||||||
|
|
||||||
# Add the custom components from the page to the set.
|
# Add the custom components from the page to the set.
|
||||||
custom_components |= component.get_custom_components()
|
custom_components |= component.get_custom_components()
|
||||||
|
|
||||||
thread_pool.close()
|
thread_pool.close()
|
||||||
thread_pool.join()
|
thread_pool.join()
|
||||||
|
|
||||||
@ -537,7 +564,11 @@ class App(Base):
|
|||||||
# Compile the custom components.
|
# Compile the custom components.
|
||||||
compile_results.append(compiler.compile_components(custom_components))
|
compile_results.append(compiler.compile_components(custom_components))
|
||||||
|
|
||||||
# Compile the root document with base styles and fonts.
|
# Iterate through all the custom components and add their imports to the all_imports
|
||||||
|
for component in custom_components:
|
||||||
|
all_imports.update(component.get_imports())
|
||||||
|
|
||||||
|
# Compile the root document with base styles and fonts
|
||||||
compile_results.append(compiler.compile_document_root(self.stylesheets))
|
compile_results.append(compiler.compile_document_root(self.stylesheets))
|
||||||
|
|
||||||
# Compile the theme.
|
# Compile the theme.
|
||||||
@ -556,6 +587,9 @@ class App(Base):
|
|||||||
# Empty the .web pages directory
|
# Empty the .web pages directory
|
||||||
compiler.purge_web_pages_dir()
|
compiler.purge_web_pages_dir()
|
||||||
|
|
||||||
|
# install frontend packages
|
||||||
|
self.get_frontend_packages(all_imports)
|
||||||
|
|
||||||
# Write the pages at the end to trigger the NextJS hot reload only once.
|
# Write the pages at the end to trigger the NextJS hot reload only once.
|
||||||
thread_pool = ThreadPool()
|
thread_pool = ThreadPool()
|
||||||
for output_path, code in compile_results:
|
for output_path, code in compile_results:
|
||||||
|
@ -25,7 +25,7 @@ from reflex.components.component import Component, ComponentStyle, CustomCompone
|
|||||||
from reflex.state import Cookie, LocalStorage, State
|
from reflex.state import Cookie, LocalStorage, State
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils import format, imports, path_ops
|
from reflex.utils import format, imports, path_ops
|
||||||
from reflex.vars import ImportVar
|
from reflex.vars import ImportVar, NoRenderImportVar
|
||||||
|
|
||||||
# To re-export this function.
|
# To re-export this function.
|
||||||
merge_imports = imports.merge_imports
|
merge_imports = imports.merge_imports
|
||||||
@ -42,6 +42,9 @@ def compile_import_statement(fields: Set[ImportVar]) -> Tuple[str, Set[str]]:
|
|||||||
default: default library. When install "import def from library".
|
default: default library. When install "import def from library".
|
||||||
rest: rest of libraries. When install "import {rest1, rest2} from library"
|
rest: rest of libraries. When install "import {rest1, rest2} from library"
|
||||||
"""
|
"""
|
||||||
|
# ignore the NoRenderImportVar fields during compilation
|
||||||
|
fields = {field for field in fields if not isinstance(field, NoRenderImportVar)}
|
||||||
|
|
||||||
# Check for default imports.
|
# Check for default imports.
|
||||||
defaults = {field for field in fields if field.is_default}
|
defaults = {field for field in fields if field.is_default}
|
||||||
assert len(defaults) < 2
|
assert len(defaults) < 2
|
||||||
@ -72,7 +75,8 @@ def validate_imports(imports: imports.ImportDict):
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Can not compile, the tag {import_name} is used multiple time from {lib} and {used_tags[import_name]}"
|
f"Can not compile, the tag {import_name} is used multiple time from {lib} and {used_tags[import_name]}"
|
||||||
)
|
)
|
||||||
used_tags[import_name] = lib
|
if import_name is not None:
|
||||||
|
used_tags[import_name] = lib
|
||||||
|
|
||||||
|
|
||||||
def compile_imports(imports: imports.ImportDict) -> List[dict]:
|
def compile_imports(imports: imports.ImportDict) -> List[dict]:
|
||||||
@ -87,6 +91,10 @@ def compile_imports(imports: imports.ImportDict) -> List[dict]:
|
|||||||
import_dicts = []
|
import_dicts = []
|
||||||
for lib, fields in imports.items():
|
for lib, fields in imports.items():
|
||||||
default, rest = compile_import_statement(fields)
|
default, rest = compile_import_statement(fields)
|
||||||
|
# prevent all NoRenderImportVar from being rendered on the page
|
||||||
|
if any({f for f in fields if isinstance(f, NoRenderImportVar)}): # type: ignore
|
||||||
|
continue
|
||||||
|
|
||||||
if not lib:
|
if not lib:
|
||||||
assert not default, "No default field allowed for empty library."
|
assert not default, "No default field allowed for empty library."
|
||||||
assert rest is not None and len(rest) > 0, "No fields to import."
|
assert rest is not None and len(rest) > 0, "No fields to import."
|
||||||
@ -94,6 +102,11 @@ def compile_imports(imports: imports.ImportDict) -> List[dict]:
|
|||||||
import_dicts.append(get_import_dict(module))
|
import_dicts.append(get_import_dict(module))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# remove the version before rendering the package imports
|
||||||
|
lib, at, version = lib.rpartition("@")
|
||||||
|
if not lib:
|
||||||
|
lib = at + version
|
||||||
|
|
||||||
import_dicts.append(get_import_dict(lib, default, rest))
|
import_dicts.append(get_import_dict(lib, default, rest))
|
||||||
return import_dicts
|
return import_dicts
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ from reflex.event import (
|
|||||||
)
|
)
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils import format, imports, types
|
from reflex.utils import format, imports, types
|
||||||
from reflex.vars import BaseVar, ImportVar, Var
|
from reflex.vars import BaseVar, ImportVar, NoRenderImportVar, Var
|
||||||
|
|
||||||
|
|
||||||
class Component(Base, ABC):
|
class Component(Base, ABC):
|
||||||
@ -40,6 +40,9 @@ class Component(Base, ABC):
|
|||||||
# The library that the component is based on.
|
# The library that the component is based on.
|
||||||
library: Optional[str] = None
|
library: Optional[str] = None
|
||||||
|
|
||||||
|
# List here the non-react dependency needed by `library`
|
||||||
|
lib_dependencies: List[str] = []
|
||||||
|
|
||||||
# The tag to use when rendering the component.
|
# The tag to use when rendering the component.
|
||||||
tag: Optional[str] = None
|
tag: Optional[str] = None
|
||||||
|
|
||||||
@ -515,9 +518,12 @@ class Component(Base, ABC):
|
|||||||
return code
|
return code
|
||||||
|
|
||||||
def _get_imports(self) -> imports.ImportDict:
|
def _get_imports(self) -> imports.ImportDict:
|
||||||
|
imports = {}
|
||||||
if self.library is not None and self.tag is not None:
|
if self.library is not None and self.tag is not None:
|
||||||
return {self.library: {self.import_var}}
|
imports[self.library] = {self.import_var}
|
||||||
return {}
|
for dep in self.lib_dependencies:
|
||||||
|
imports[dep] = {NoRenderImportVar()} # type: ignore
|
||||||
|
return imports
|
||||||
|
|
||||||
def get_imports(self) -> imports.ImportDict:
|
def get_imports(self) -> imports.ImportDict:
|
||||||
"""Get all the libraries and fields that are used by the component.
|
"""Get all the libraries and fields that are used by the component.
|
||||||
@ -833,11 +839,26 @@ class NoSSRComponent(Component):
|
|||||||
"""A dynamic component that is not rendered on the server."""
|
"""A dynamic component that is not rendered on the server."""
|
||||||
|
|
||||||
def _get_imports(self):
|
def _get_imports(self):
|
||||||
return {"next/dynamic": {ImportVar(tag="dynamic", is_default=True)}}
|
imports = {"next/dynamic": {ImportVar(tag="dynamic", is_default=True)}}
|
||||||
|
|
||||||
|
for dep in [self.library, *self.lib_dependencies]:
|
||||||
|
imports[dep] = {NoRenderImportVar()} # type: ignore
|
||||||
|
|
||||||
|
return imports
|
||||||
|
|
||||||
def _get_custom_code(self) -> str:
|
def _get_custom_code(self) -> str:
|
||||||
opts_fragment = ", { ssr: false });"
|
opts_fragment = ", { ssr: false });"
|
||||||
library_import = f"const {self.tag} = dynamic(() => import('{self.library}')"
|
|
||||||
|
# extract the correct import name from library name
|
||||||
|
if self.library is None:
|
||||||
|
raise ValueError("Undefined library for NoSSRComponent")
|
||||||
|
|
||||||
|
import_name_parts = [p for p in self.library.rpartition("@") if p != ""]
|
||||||
|
import_name = (
|
||||||
|
import_name_parts[0] if import_name_parts[0] != "@" else self.library
|
||||||
|
)
|
||||||
|
|
||||||
|
library_import = f"const {self.tag} = dynamic(() => import('{import_name}')"
|
||||||
mod_import = (
|
mod_import = (
|
||||||
# https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-named-exports
|
# https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-named-exports
|
||||||
f".then((mod) => mod.{self.tag})"
|
f".then((mod) => mod.{self.tag})"
|
||||||
|
@ -19,7 +19,7 @@ PRISM_STYLES_PATH = "/styles/code/prism"
|
|||||||
class CodeBlock(Component):
|
class CodeBlock(Component):
|
||||||
"""A code block."""
|
"""A code block."""
|
||||||
|
|
||||||
library = "react-syntax-highlighter"
|
library = "react-syntax-highlighter@^15.5.0"
|
||||||
|
|
||||||
tag = "Prism"
|
tag = "Prism"
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ from reflex.vars import BaseVar, ComputedVar, ImportVar, Var
|
|||||||
class Gridjs(Component):
|
class Gridjs(Component):
|
||||||
"""A component that wraps a nivo bar component."""
|
"""A component that wraps a nivo bar component."""
|
||||||
|
|
||||||
library = "gridjs-react"
|
library = "gridjs-react@^6.0.1"
|
||||||
|
|
||||||
|
lib_dependencies: List[str] = ["gridjs@^6.0.6"]
|
||||||
|
|
||||||
|
|
||||||
class DataTable(Gridjs):
|
class DataTable(Gridjs):
|
||||||
|
@ -16,7 +16,7 @@ class DebounceInput(Component):
|
|||||||
is experiencing high latency.
|
is experiencing high latency.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
library = "react-debounce-input"
|
library = "react-debounce-input@^3.3.0"
|
||||||
tag = "DebounceInput"
|
tag = "DebounceInput"
|
||||||
|
|
||||||
# Minimum input characters before triggering the on_change event
|
# Minimum input characters before triggering the on_change event
|
||||||
|
@ -21,7 +21,7 @@ clear_selected_files = BaseVar(name="_e => setFiles((files) => [])", type_=Event
|
|||||||
class Upload(Component):
|
class Upload(Component):
|
||||||
"""A file upload component."""
|
"""A file upload component."""
|
||||||
|
|
||||||
library = "react-dropzone"
|
library = "react-dropzone@^14.2.3"
|
||||||
|
|
||||||
tag = "ReactDropzone"
|
tag = "ReactDropzone"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Component for displaying a plotly graph."""
|
"""Component for displaying a plotly graph."""
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict, List
|
||||||
|
|
||||||
from plotly.graph_objects import Figure
|
from plotly.graph_objects import Figure
|
||||||
|
|
||||||
@ -12,7 +12,9 @@ from reflex.vars import Var
|
|||||||
class PlotlyLib(NoSSRComponent):
|
class PlotlyLib(NoSSRComponent):
|
||||||
"""A component that wraps a plotly lib."""
|
"""A component that wraps a plotly lib."""
|
||||||
|
|
||||||
library = "react-plotly.js"
|
library = "react-plotly.js@^2.6.0"
|
||||||
|
|
||||||
|
lib_dependencies: List[str] = ["plotly.js@^2.22.0"]
|
||||||
|
|
||||||
|
|
||||||
class Plotly(PlotlyLib):
|
class Plotly(PlotlyLib):
|
||||||
|
@ -334,7 +334,7 @@ def data(graph: str, x: List, y: Optional[List] = None, **kwargs) -> List:
|
|||||||
class Victory(Component):
|
class Victory(Component):
|
||||||
"""A component that wraps a victory lib."""
|
"""A component that wraps a victory lib."""
|
||||||
|
|
||||||
library = "victory"
|
library = "victory@^36.6.8"
|
||||||
|
|
||||||
# The data to display.
|
# The data to display.
|
||||||
data: Var[List[Dict]]
|
data: Var[List[Dict]]
|
||||||
|
@ -11,7 +11,7 @@ class ReactPlayerComponent(NoSSRComponent):
|
|||||||
reference: https://github.com/cookpete/react-player.
|
reference: https://github.com/cookpete/react-player.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
library = "react-player/lazy"
|
library = "react-player@^2.12.0"
|
||||||
|
|
||||||
tag = "ReactPlayer"
|
tag = "ReactPlayer"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from reflex.utils import format
|
|||||||
class ChakraIconComponent(Component):
|
class ChakraIconComponent(Component):
|
||||||
"""A component that wraps a Chakra icon component."""
|
"""A component that wraps a Chakra icon component."""
|
||||||
|
|
||||||
library = "@chakra-ui/icons"
|
library = "@chakra-ui/icons@^2.0.19"
|
||||||
|
|
||||||
|
|
||||||
class Icon(ChakraIconComponent):
|
class Icon(ChakraIconComponent):
|
||||||
|
@ -32,7 +32,14 @@ components_by_tag: Dict[str, Callable] = {
|
|||||||
class Markdown(Component):
|
class Markdown(Component):
|
||||||
"""A markdown component."""
|
"""A markdown component."""
|
||||||
|
|
||||||
library = "react-markdown"
|
library = "react-markdown@^8.0.7"
|
||||||
|
|
||||||
|
lib_dependencies: List[str] = [
|
||||||
|
"rehype-katex@^6.0.3",
|
||||||
|
"remark-math@^5.1.1",
|
||||||
|
"rehype-raw@^6.1.1",
|
||||||
|
"remark-gfm@^3.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
tag = "ReactMarkdown"
|
tag = "ReactMarkdown"
|
||||||
|
|
||||||
|
@ -127,6 +127,9 @@ def run(
|
|||||||
frontend = True
|
frontend = True
|
||||||
backend = True
|
backend = True
|
||||||
|
|
||||||
|
if not frontend and backend:
|
||||||
|
_skip_compile()
|
||||||
|
|
||||||
# Check that the app is initialized.
|
# Check that the app is initialized.
|
||||||
prerequisites.check_initialized(frontend=frontend)
|
prerequisites.check_initialized(frontend=frontend)
|
||||||
|
|
||||||
|
@ -223,9 +223,6 @@ def setup_frontend(
|
|||||||
root: The root path of the project.
|
root: The root path of the project.
|
||||||
disable_telemetry: Whether to disable the Next telemetry.
|
disable_telemetry: Whether to disable the Next telemetry.
|
||||||
"""
|
"""
|
||||||
# Install frontend packages.
|
|
||||||
prerequisites.install_frontend_packages()
|
|
||||||
|
|
||||||
# Copy asset files to public folder.
|
# Copy asset files to public folder.
|
||||||
path_ops.cp(
|
path_ops.cp(
|
||||||
src=str(root / constants.APP_ASSETS_DIR),
|
src=str(root / constants.APP_ASSETS_DIR),
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import psutil
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
@ -25,20 +28,78 @@ def start_watching_assets_folder(root):
|
|||||||
asset_watch.start()
|
asset_watch.start()
|
||||||
|
|
||||||
|
|
||||||
|
def detect_package_change(json_file_path: str) -> str:
|
||||||
|
"""Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_file_path (str): The path to the JSON file to be hashed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The SHA-256 hash of the JSON file as a hexadecimal string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> detect_package_change("package.json")
|
||||||
|
'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'
|
||||||
|
"""
|
||||||
|
with open(json_file_path, "r") as file:
|
||||||
|
json_data = json.load(file)
|
||||||
|
|
||||||
|
# Calculate the hash
|
||||||
|
json_string = json.dumps(json_data, sort_keys=True)
|
||||||
|
hash_object = hashlib.sha256(json_string.encode())
|
||||||
|
return hash_object.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def kill(proc_pid: int):
|
||||||
|
"""Kills a process and all its child processes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
proc_pid (int): The process ID of the process to be killed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> kill(1234)
|
||||||
|
"""
|
||||||
|
process = psutil.Process(proc_pid)
|
||||||
|
for proc in process.children(recursive=True):
|
||||||
|
proc.kill()
|
||||||
|
process.kill()
|
||||||
|
|
||||||
|
|
||||||
def run_process_and_launch_url(run_command: list[str]):
|
def run_process_and_launch_url(run_command: list[str]):
|
||||||
"""Run the process and launch the URL.
|
"""Run the process and launch the URL.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
run_command: The command to run.
|
run_command: The command to run.
|
||||||
"""
|
"""
|
||||||
process = processes.new_process(
|
json_file_path = os.path.join(constants.WEB_DIR, "package.json")
|
||||||
run_command, cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS
|
last_hash = detect_package_change(json_file_path)
|
||||||
)
|
process = None
|
||||||
|
first_run = True
|
||||||
|
|
||||||
for line in processes.stream_logs("Starting frontend", process):
|
while True:
|
||||||
if "ready started server on" in line:
|
if process is None:
|
||||||
url = line.split("url: ")[-1].strip()
|
process = processes.new_process(
|
||||||
console.print(f"App running at: [bold green]{url}")
|
run_command, cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS
|
||||||
|
)
|
||||||
|
if process.stdout:
|
||||||
|
for line in processes.stream_logs("Starting frontend", process):
|
||||||
|
if "ready started server on" in line:
|
||||||
|
if first_run:
|
||||||
|
url = line.split("url: ")[-1].strip()
|
||||||
|
console.print(f"App running at: [bold green]{url}")
|
||||||
|
first_run = False
|
||||||
|
else:
|
||||||
|
console.print(f"New packages detected updating app...")
|
||||||
|
else:
|
||||||
|
console.debug(line)
|
||||||
|
new_hash = detect_package_change(json_file_path)
|
||||||
|
if new_hash != last_hash:
|
||||||
|
last_hash = new_hash
|
||||||
|
kill(process.pid)
|
||||||
|
process = None
|
||||||
|
break # for line in process.stdout
|
||||||
|
if process is not None:
|
||||||
|
break # while True
|
||||||
|
|
||||||
|
|
||||||
def run_frontend(root: Path, port: str):
|
def run_frontend(root: Path, port: str):
|
||||||
|
@ -14,7 +14,7 @@ import zipfile
|
|||||||
from fileinput import FileInput
|
from fileinput import FileInput
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import typer
|
import typer
|
||||||
@ -376,25 +376,32 @@ def install_bun():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def install_frontend_packages():
|
def install_frontend_packages(packages: List[str]):
|
||||||
"""Installs the base and custom frontend packages."""
|
"""Installs the base and custom frontend packages.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages (List[str]): A list of package names to be installed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> install_frontend_packages(["react", "react-dom"])
|
||||||
|
"""
|
||||||
# Install the base packages.
|
# Install the base packages.
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.WEB_DIR,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
processes.show_status("Installing base frontend packages", process)
|
processes.show_status("Installing base frontend packages", process)
|
||||||
|
|
||||||
# Install the app packages.
|
# Install the custom packages, if any.
|
||||||
packages = get_config().frontend_packages
|
|
||||||
if len(packages) > 0:
|
if len(packages) > 0:
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[get_install_package_manager(), "add", *packages],
|
[get_install_package_manager(), "add", *packages],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.WEB_DIR,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_status("Installing custom frontend packages", process)
|
processes.show_status("Installing frontend packages for components", process)
|
||||||
|
|
||||||
|
|
||||||
def check_initialized(frontend: bool = True):
|
def check_initialized(frontend: bool = True):
|
||||||
|
@ -1372,6 +1372,12 @@ class ImportVar(Base):
|
|||||||
return hash((self.tag, self.is_default, self.alias))
|
return hash((self.tag, self.is_default, self.alias))
|
||||||
|
|
||||||
|
|
||||||
|
class NoRenderImportVar(ImportVar):
|
||||||
|
"""A import that doesn't need to be rendered."""
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
def get_local_storage(key: Optional[Union[Var, str]] = None) -> BaseVar:
|
def get_local_storage(key: Optional[Union[Var, str]] = None) -> BaseVar:
|
||||||
"""Provide a base var as payload to get local storage item(s).
|
"""Provide a base var as payload to get local storage item(s).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user