Skip to content

Commit

Permalink
Make CodeExample block SSRd for better SEO. (#26829)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
salazarm authored Jan 4, 2025
1 parent 18c3c18 commit 151d6a9
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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/).

<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/example_setup.py" language="Python" title="Example setup.py" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/example_setup.py" language="Python" title="Example setup.py" />

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:

Expand Down Expand Up @@ -48,18 +48,18 @@ The default Python version for Dagster+ Serverless is Python 3.9. Python version
<Tabs groupId="method">
<TabItem value="GitHub" label="GitHub">
In your `.github/workflows/deploy.yml` file, update the `PYTHON_VERSION` environment variable with your desired Python version:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/github_python_version.yaml" language="yaml" title="Updating the Python version in deploy.yml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/github_python_version.yaml" language="yaml" title="Updating the Python version in deploy.yml" />

</TabItem>
<TabItem value="GitLab" label="GitLab">
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:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/gitlab_template.yaml" language="yaml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/gitlab_template.yaml" language="yaml" />

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

<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/gitlab_python_version.yaml" language="yaml" title="Updating the Python version in .gitlab-ci.yml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/gitlab_python_version.yaml" language="yaml" title="Updating the Python version in .gitlab-ci.yml" />

</TabItem>
<TabItem value="CLI" label="CLI">
Expand Down Expand Up @@ -101,7 +101,7 @@ Setting a custom base image isn't supported for GitLab CI/CD workflows out of th
<Tabs groupId="method">
<TabItem value="GitHub" label="GitHub">
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:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/github_base_image.yaml" language="yaml" title="Setting a custom base image in deploy.yml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/github_base_image.yaml" language="yaml" title="Setting a custom base image in deploy.yml" />
</TabItem>
Expand Down Expand Up @@ -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:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/data_files_setup.py" language="Python" title="Loading data files in setup.py" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/data_files_setup.py" language="Python" title="Loading data files in setup.py" />
## Disable PEX deploys
Expand All @@ -141,18 +141,18 @@ You have the option to disable PEX-based deploys and deploy using a Docker image
<Tabs groupId="method">
<TabItem value="GitHub" label="GitHub">
In your `.github/workflows/deploy.yml` file, update the `ENABLE_FAST_DEPLOYS` environment variable to `false`:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/github_disable_pex.yaml" language="yaml" title="Disable PEX deploys in deploy.yml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/github_disable_pex.yaml" language="yaml" title="Disable PEX deploys in deploy.yml" />
</TabItem>
<TabItem value="GitLab" label="GitLab">
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:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/gitlab_template.yaml" language="yaml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/gitlab_template.yaml" language="yaml" />
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`
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/gitlab_disable_pex.yaml" language="yaml" title="Disable PEX deploys in .gitlab-ci.yml" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/gitlab_disable_pex.yaml" language="yaml" title="Disable PEX deploys in .gitlab-ci.yml" />
</TabItem>
<TabItem value="CLI" label="CLI">
Expand Down Expand Up @@ -186,7 +186,7 @@ Setting a custom base image isn't supported for GitLab CI/CD workflows out of th
<Tabs groupId="method">
<TabItem value="GitHub" label="GitHub">
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:
<CodeExample filePath="dagster-plus/deployment/deployment-types/serverless/runtime-environment/github_no_pex_custom_base_image.yaml" language="yaml" title="Setting a custom base image in `deploy.yml`" />
<CodeExample filePath="dagster-plus/deployment/serverless/runtime-environment/github_no_pex_custom_base_image.yaml" language="yaml" title="Setting a custom base image in `deploy.yml`" />

</TabItem>
<TabItem value="CLI" label="CLI">
Expand Down
85 changes: 60 additions & 25 deletions docs/docs-beta/src/components/CodeExample.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {Suspense} from 'react';
import CodeBlock from '@theme/CodeBlock';

interface CodeExampleProps {
Expand All @@ -10,7 +10,6 @@ interface CodeExampleProps {
pathPrefix?: string;
}


/**
* Removes content below the `if __name__` block for the given `lines`.
*/
Expand All @@ -28,7 +27,39 @@ function filterNoqaComments(lines: string[]): string[] {
});
}

const CodeExample: React.FC<CodeExampleProps> = ({
const contentCache: Record<string, {content?: string; error?: string | null}> = {};

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<CodeExampleProps> = ({...props}) => {
return (
<Suspense>
<CodeExampleInner {...props} />
</Suspense>
);
};

const CodeExampleInner: React.FC<CodeExampleProps> = ({
filePath,
title,
lineStart,
Expand All @@ -37,31 +68,35 @@ const CodeExample: React.FC<CodeExampleProps> = ({
pathPrefix = 'docs_beta_snippets/docs_beta_snippets',
...props
}) => {
const [content, setContent] = React.useState<string>('');
const [error, setError] = React.useState<string | null>(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 <div style={{color: 'red', padding: '1rem', border: '1px solid red'}}>{error}</div>;
Expand Down

1 comment on commit 151d6a9

@github-actions
Copy link

@github-actions github-actions bot commented on 151d6a9 Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for dagster-docs-beta ready!

✅ Preview
https://dagster-docs-beta-7a7m7b40g-elementl.vercel.app

Built with commit 151d6a9.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.