From 151d6a91a3557119eabd1762efae8c1054e9c740 Mon Sep 17 00:00:00 2001 From: Marco polo Date: Fri, 3 Jan 2025 21:25:28 -0500 Subject: [PATCH] Make CodeExample block SSRd for better SEO. (#26829) ## Summary & Motivation We want to make sure that the CodeExample block is included in the statically generated HTML for the site so that it's included in SEO. Currently since we use a `useEffect` we only fetch the content on the client. To fix this we need to make sure we have the content on the server. To this end I added a check to see if we're on the server and if we are then we synchronously require the content for the codeblock inline during render and use it. Otherwise, if we're on the client, we import the content dynamically and rely on Suspense to avoid unmounting the SSR'd content during the initial render. ## How I Tested These Changes `yarn build` `yarn serve` Looked at the source in `view-source:http://localhost:3002/` and confirmed the HTML includes the codeblocks. There's a brief flash of the light theme but confirmed on the docs-preview site that this is in an existing issue and unrelated to my approach. https://github.com/user-attachments/assets/b760a941-1b77-4c46-b693-d0a79dff234e --- .../serverless/runtime-environment.md | 20 ++--- docs/docs-beta/src/components/CodeExample.tsx | 85 +++++++++++++------ 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/docs/docs-beta/docs/dagster-plus/deployment/deployment-types/serverless/runtime-environment.md b/docs/docs-beta/docs/dagster-plus/deployment/deployment-types/serverless/runtime-environment.md index ae13107a69f9b..00d2ba5c1e50a 100644 --- a/docs/docs-beta/docs/dagster-plus/deployment/deployment-types/serverless/runtime-environment.md +++ b/docs/docs-beta/docs/dagster-plus/deployment/deployment-types/serverless/runtime-environment.md @@ -17,7 +17,7 @@ By default, Dagster+ Serverless will package your code as PEX files and deploys You can add dependencies by including the corresponding Python libraries in your Dagster project's `setup.py` file. These should follow [PEP 508](https://peps.python.org/pep-0508/). - + You can also use a tarball to install a dependency, such as if `pip` is unable to resolve a package using `dependency_links`. For example, `soda` and `soda-snowflake` provide tarballs that you can include in the `install_requires` section: @@ -48,18 +48,18 @@ The default Python version for Dagster+ Serverless is Python 3.9. Python version In your `.github/workflows/deploy.yml` file, update the `PYTHON_VERSION` environment variable with your desired Python version: - + 1. Open your `.gitlab-ci.yml` file. If your `.gitlab-ci.yml` contains an `include` with a link to a Dagster provided CI/CD template: - + Follow the link and replace the contents of your `.gitlab-ci.yml` with the YAML document at the link address. Otherwise, continue to the next step. 3. Update the `PYTHON_VERSION` environment variable with your desired Python version - + @@ -101,7 +101,7 @@ Setting a custom base image isn't supported for GitLab CI/CD workflows out of th In your `.github/workflows/deploy.yml` file, add the `SERVERLESS_BASE_IMAGE_TAG` environment variable and set it to the tag printed out in the previous step: - + @@ -132,7 +132,7 @@ To add data files to your deployment, use the [Data Files Support](https://setup ``` If you want to include the data folder, modify your `setup.py` to add the `package_data` line: - + ## Disable PEX deploys @@ -141,18 +141,18 @@ You have the option to disable PEX-based deploys and deploy using a Docker image In your `.github/workflows/deploy.yml` file, update the `ENABLE_FAST_DEPLOYS` environment variable to `false`: - + 1. Open your `.gitlab-ci.yml` file. If your `.gitlab-ci.yml` contains an `include` with a link to a Dagster provided CI/CD template: - + Follow the link and replace the contents of your `.gitlab-ci.yml` with the YAML document at the link address. Otherwise, continue to the next step. 3. Update the `DISABLE_FAST_DEPLOYS` variable to `true` - + @@ -186,7 +186,7 @@ Setting a custom base image isn't supported for GitLab CI/CD workflows out of th In your `.github/workflows/deploy.yml` file, add the `SERVERLESS_BASE_IMAGE_TAG` environment variable and set it to the tag printed out in the previous step: - + diff --git a/docs/docs-beta/src/components/CodeExample.tsx b/docs/docs-beta/src/components/CodeExample.tsx index 1ac75e331960e..8c7c944b6af2e 100644 --- a/docs/docs-beta/src/components/CodeExample.tsx +++ b/docs/docs-beta/src/components/CodeExample.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Suspense} from 'react'; import CodeBlock from '@theme/CodeBlock'; interface CodeExampleProps { @@ -10,7 +10,6 @@ interface CodeExampleProps { pathPrefix?: string; } - /** * Removes content below the `if __name__` block for the given `lines`. */ @@ -28,7 +27,39 @@ function filterNoqaComments(lines: string[]): string[] { }); } -const CodeExample: React.FC = ({ +const contentCache: Record = {}; + +function processModule({ + module, + lineStart, + lineEnd, + path, +}: { + path: string; + lineEnd?: number; + lineStart?: number; + module: any; +}) { + var lines = module.default.split('\n'); + + const sliceStart = lineStart && lineStart > 0 ? lineStart : 0; + const sliceEnd = lineEnd && lineEnd <= lines.length ? lineEnd : lines.length; + lines = lines.slice(sliceStart, sliceEnd); + + lines = filterNoqaComments(lines); + lines = trimMainBlock(lines); + contentCache[path] = {content: lines.join('\n')}; +} + +const CodeExample: React.FC = ({...props}) => { + return ( + + + + ); +}; + +const CodeExampleInner: React.FC = ({ filePath, title, lineStart, @@ -37,31 +68,35 @@ const CodeExample: React.FC = ({ pathPrefix = 'docs_beta_snippets/docs_beta_snippets', ...props }) => { - const [content, setContent] = React.useState(''); - const [error, setError] = React.useState(null); - - React.useEffect(() => { - import(`!!raw-loader!/../../examples/${pathPrefix}/${filePath}`) + const path = pathPrefix + '/' + filePath; + const isServer = typeof window === 'undefined'; + if (isServer) { + /** + * Note: Remove the try/catch to cause a hard error on build once all of the bad code examples are cleaned up. + */ + try { + const module = require(`!!raw-loader!/../../examples/${path}`); + processModule({module, lineStart, lineEnd, path}); + } catch (e) { + console.error(e); + contentCache[path] = {error: e.toString()}; + } + } + if (!contentCache[path]) { + /** + * We only reach this path on the client. + * Throw a promise to suspend in order to avoid un-rendering the codeblock that we SSR'd + */ + throw import(`!!raw-loader!/../../examples/${path}`) .then((module) => { - var lines = module.default.split('\n'); - - const sliceStart = lineStart && lineStart > 0 ? lineStart : 0; - const sliceEnd = lineEnd && lineEnd <= lines.length ? lineEnd : lines.length; - lines = lines.slice(sliceStart, sliceEnd); - - lines = filterNoqaComments(lines); - lines = trimMainBlock(lines); - - setContent(lines.join('\n')); - setError(null); + processModule({module, lineStart, lineEnd, path}); }) - .catch((error) => { - console.error(`Error loading file: ${filePath}`, error); - setError( - `Failed to load file: ${filePath}. Please check if the file exists and the path is correct.`, - ); + .catch((e) => { + contentCache[filePath] = {error: e.toString()}; }); - }, [filePath]); + } + + const {content, error} = contentCache[path]; if (error) { return
{error}
;