diff --git a/pynecone/.templates/web/bun.lockb b/pynecone/.templates/web/bun.lockb index e2ee60e5e..1da69bbaa 100755 Binary files a/pynecone/.templates/web/bun.lockb and b/pynecone/.templates/web/bun.lockb differ diff --git a/pynecone/.templates/web/package.json b/pynecone/.templates/web/package.json index 03cf67e84..a8f7ab18b 100644 --- a/pynecone/.templates/web/package.json +++ b/pynecone/.templates/web/package.json @@ -2,7 +2,7 @@ "name": "pynecone", "scripts": { "dev": "next dev", - "export": "next build && next export -o _static", + "export": "next build && next-sitemap && next export -o _static", "prod": "next start" }, "dependencies": { @@ -17,6 +17,7 @@ "gridjs": "^4.0.0", "gridjs-react": "^4.0.0", "next": "^12.1.0", + "next-sitemap": "^3.1.52", "plotly.js": "2.6.4", "prettier": "^2.8.1", "react": "^17.0.2", diff --git a/pynecone/compiler/templates.py b/pynecone/compiler/templates.py index 95a9aa098..c70f4424b 100644 --- a/pynecone/compiler/templates.py +++ b/pynecone/compiler/templates.py @@ -193,3 +193,6 @@ SOCKET = "const socket = useRef(null)" # Color toggle COLORTOGGLE = f"const {{ {constants.COLOR_MODE}, {constants.TOGGLE_COLOR_MODE} }} = {constants.USE_COLOR_MODE}()" + +# Sitemap config file. +SITEMAP_CONFIG = "module.exports = {config}".format diff --git a/pynecone/config.py b/pynecone/config.py index 3be9a522c..dfea09482 100644 --- a/pynecone/config.py +++ b/pynecone/config.py @@ -21,6 +21,9 @@ class Config(Base): # The backend API url. api_url: str = constants.API_URL + # The deploy url. + deploy_url: Optional[str] = None + # The database url. db_url: Optional[str] = constants.DB_URL @@ -30,8 +33,8 @@ class Config(Base): # Telemetry opt-in. telemetry_enabled: bool = True - # The deploy url. - deploy_url: Optional[str] = None + # The pcdeploy url. + pcdeploy_url: Optional[str] = None # The environment mode. env: constants.Env = constants.Env.DEV diff --git a/pynecone/constants.py b/pynecone/constants.py index 58eae5ab1..444d191d9 100644 --- a/pynecone/constants.py +++ b/pynecone/constants.py @@ -50,6 +50,8 @@ WEB_STATIC_DIR = os.path.join(WEB_DIR, "_static") WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR) # The directory where the assets are located. WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public") +# The sitemap config file. +SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js") # The node modules directory. NODE_MODULES = "node_modules" # The package lock file. diff --git a/pynecone/pc.py b/pynecone/pc.py index 5635a3957..436ecf147 100644 --- a/pynecone/pc.py +++ b/pynecone/pc.py @@ -129,14 +129,14 @@ def deploy(dry_run: bool = typer.Option(False, help="Whether to run a dry run.") config.api_url = utils.get_production_backend_url() # Check if the deploy url is set. - if config.deploy_url is None: + if config.pcdeploy_url is None: typer.echo("This feature is coming soon!") return # Compile the app in production mode. typer.echo("Compiling production app") app = utils.get_app().app - utils.export_app(app, zip=True) + utils.export_app(app, zip=True, deploy_url=config.deploy_url) # Exit early if this is a dry run. if dry_run: @@ -144,7 +144,7 @@ def deploy(dry_run: bool = typer.Option(False, help="Whether to run a dry run.") # Deploy the app. data = {"userId": config.username, "projectId": config.app_name} - original_response = httpx.get(config.deploy_url, params=data) + original_response = httpx.get(config.pcdeploy_url, params=data) response = original_response.json() frontend = response["frontend_resources_url"] backend = response["backend_resources_url"] @@ -175,15 +175,22 @@ def export( ), ): """Export the app to a zip file.""" + config = utils.get_config() + if for_pc_deploy: # Get the app config and modify the api_url base on username and app_name. - config = utils.get_config() config.api_url = utils.get_production_backend_url() # Compile the app in production mode and export it. utils.console.rule("[bold]Compiling production app and preparing for export.") app = utils.get_app().app - utils.export_app(app, backend=backend, frontend=frontend, zip=zipping) + utils.export_app( + app, + backend=backend, + frontend=frontend, + zip=zipping, + deploy_url=config.deploy_url, + ) # Post a telemetry event. pynecone_telemetry("export", utils.get_config().telemetry_enabled) diff --git a/pynecone/utils.py b/pynecone/utils.py index ac7b34e74..379222eef 100644 --- a/pynecone/utils.py +++ b/pynecone/utils.py @@ -493,8 +493,32 @@ def set_pynecone_project_hash(): json.dump(pynecone_json, f, ensure_ascii=False) +def generate_sitemap(deploy_url: str): + """Generate the sitemap config file. + + Args: + deploy_url: The URL of the deployed app. + """ + # Import here to avoid circular imports. + from pynecone.compiler import templates + + config = json.dumps( + { + "siteUrl": deploy_url, + "generateRobotsTxt": True, + } + ) + + with open(constants.SITEMAP_CONFIG_FILE, "w") as f: + f.write(templates.SITEMAP_CONFIG(config=config)) + + def export_app( - app: App, backend: bool = True, frontend: bool = True, zip: bool = False + app: App, + backend: bool = True, + frontend: bool = True, + zip: bool = False, + deploy_url: Optional[str] = None, ): """Zip up the app for deployment. @@ -503,6 +527,7 @@ def export_app( backend: Whether to zip up the backend app. frontend: Whether to zip up the frontend app. zip: Whether to zip the app. + deploy_url: The URL of the deployed app. """ # Force compile the app. app.compile(force_compile=True) @@ -510,6 +535,10 @@ def export_app( # Remove the static folder. rm(constants.WEB_STATIC_DIR) + # Generate the sitemap file. + if deploy_url is not None: + generate_sitemap(deploy_url) + # Export the Next app. subprocess.run([get_package_manager(), "run", "export"], cwd=constants.WEB_DIR) diff --git a/pyproject.toml b/pyproject.toml index 1a52f3ae1..55720fc57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pynecone" -version = "0.1.14" +version = "0.1.16" description = "Web apps in pure Python." license = "Apache-2.0" authors = [