Skip to content

feat: ssg rspress example #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 12, 2025
Merged

feat: ssg rspress example #102

merged 5 commits into from
Jun 12, 2025

Conversation

fiorin
Copy link
Contributor

@fiorin fiorin commented Apr 24, 2025

add an example using Rspress for server static site generator

to make Rspress to work with Zephyr is needed need a custom plugin, but enabling SSG triggers multiple times the build, so it is needed to proper check with thread is the proper one, so a tweak in the plugin does the work:

const zephyrRsbuildPlugin = () => ({
  name: "zephyr-rsbuild-plugin",
  setup(api: { modifyRspackConfig: (config: any) => Promise<void> }) {
    api.modifyRspackConfig(async (config: any) => {
      // this is important to avoid multiple zephyr build triggers
      config.name === "web" && (await withZephyr()(config));
    });
  },
});

@arthurfiorette
Copy link
Member

Please also create a PR on zephyr-packages to add this example to the templates for create-zephyr-apps.

@fiorin
Copy link
Contributor Author

fiorin commented Apr 28, 2025

Please also create a PR on zephyr-packages to add this example to the templates for create-zephyr-apps.

ZephyrCloudIO/zephyr-packages#98

@VladyslavZinchenko
Copy link

@fiorin Have an errors in console, not sure is it an issue,
Also the flow from readme is not working for me
image

artem-sht
artem-sht previously approved these changes Apr 29, 2025
@fiorin fiorin changed the title feat: ssr rspress example feat: ssg rspress example Apr 30, 2025
@fiorin
Copy link
Contributor Author

fiorin commented Apr 30, 2025

Discussed already with @VladyslavZinchenko, but here some context on the PR.

The example is the default implementation from Rspress documentation: here with a Zephyr custom plugin and the SSG flag enabled on the config file:

  ...
  ssg: true,
  builderConfig: {
    plugins: [zephyrRsbuildPlugin()],
  },

The issue with SSG flag was multiple Zephyr triggering on build:

Screenshot from 2025-04-29 18-55-21
Multiple authentication requirement

Screenshot from 2025-04-29 18-55-47
Multiple asset upload

Screenshot from 2025-04-29 18-55-57
Multiple served URL

And after upload the assets, multiple served URL generated, but only the last one proper loading.

image
Wrongly served URL

So the main goal of the implementation was solve the multiple trigger issue, so the tweak in the custom Zephyr plugin:
```
api.modifyRspackConfig(async (config: any) => {
// this is important to avoid multiple zephyr build triggers
config.name === "web" && (await withZephyr()(config));
});


That line assure only the web targeted pipeline will build the application using Zephyr:
![Screenshot from 2025-04-29 18-56-30](https://github.com/user-attachments/assets/b32eb1f9-6aff-464f-a17b-996b8149daba)

As a future next step, it is planned to extract the custom plugin on a shared Rsbuild plugin on the packages repo, to be used along the Rs implementations.

So far, @VladyslavZinchenko pointed some console log errors on the build, and they seem to be related to brand icon on superior navbar for the Rspress default example:
![Screenshot from 2025-04-29 18-56-50](https://github.com/user-attachments/assets/b7c3d413-d8ae-4072-8cdf-344dde499705)
_Top left logo missing on builded example_

If you serve local the logo path seems to be resolved:
![Screenshot from 2025-04-29 18-57-02](https://github.com/user-attachments/assets/49703e96-4f76-4e5e-b547-3ef87e65edcc)
_Top left logo missing on dev served example_

After some exploration, the path confusion seems to be related to the expected public path for the logo in the navbar component, but all the page was built using .md and config files instead a regular .js/.ts code, so it is a edge specific case implementation.

Based on that, porting that implementation to a vanilla web code would solve the issue, instead fixing the rspress example components pre-setted config behavior. Although even that was not worthy the effort since the real goal for this example was to solve the multiple pipeline triggering.

Said that, it is proposed moving forward with this minor case, and keep as future step the Rsbuild shared plugin to be easily incorporated to Rs pipe instead using a custom plugin definition. And keep in sight possible other scenarios for path confusion like this one.

Copy link

claude bot commented Jun 9, 2025

Claude finished @fiorin's task in 1m 56s —— View job


Code Review: Rspress SSG Example

Purpose: Adds an Rspress SSG example with a custom Rsbuild plugin that prevents multiple Zephyr build triggers during SSG compilation.

Todo List:

  • Read and analyze core configuration files
  • Review custom Zephyr plugin implementation
  • Check documentation and setup files
  • Analyze package dependencies and security
  • Review file structure and organization
  • Provide comprehensive review with recommendations

Review Findings
Severity Line(s) Issue Recommendation
Critical N/A Missing test coverage for custom plugin Add unit tests for zephyrRsbuildPlugin to verify it only triggers on web config
High rspress.config.ts:7-10 Type safety violations with any types Define proper TypeScript interfaces for the API and config parameters
Medium rspress.config.ts:5-13 Custom plugin should be extracted to separate module Move plugin to plugins/zephyr-rsbuild-plugin.ts for reusability and maintainability
Medium rspress.config.ts:6 Generic plugin name could cause conflicts Use more specific name like zephyr-rspress-ssg-plugin
Medium rspress.config.ts:10 String literal check lacks type safety Use type guards or enums for config name validation
Low README.md:1-30 Documentation lacks Zephyr-specific setup details Add section explaining SSG integration and why custom plugin is needed
Low package.json:12 Uses catalog: dependency pattern Verify this pattern is standard in the monorepo setup

Specific Code Improvements

1. Type Safety Fix (rspress.config.ts:5-13):

interface RsbuildAPI {
  modifyRspackConfig: (callback: (config: RspackConfig) => Promise<void>) => void;
}

interface RspackConfig {
  name?: string;
  [key: string]: any;
}

const zephyrRsbuildPlugin = () => ({
  name: "zephyr-rspress-ssg-plugin",
  setup(api: RsbuildAPI) {
    api.modifyRspackConfig(async (config: RspackConfig) => {
      // Only apply Zephyr to web builds to prevent multiple SSG triggers
      if (config.name === "web") {
        await withZephyr()(config);
      }
    });
  },
});

2. Extract Plugin to Separate File:

// plugins/zephyr-rsbuild-plugin.ts
import { withZephyr } from "zephyr-rspack-plugin";

export interface RsbuildAPI {
  modifyRspackConfig: (callback: (config: RspackConfig) => Promise<void>) => void;
}

export interface RspackConfig {
  name?: string;
  [key: string]: any;
}

export const createZephyrRsbuildPlugin = () => ({
  name: "zephyr-rspress-ssg-plugin",
  setup(api: RsbuildAPI) {
    api.modifyRspackConfig(async (config: RspackConfig) => {
      if (config.name === "web") {
        await withZephyr()(config);
      }
    });
  },
});

3. Enhanced Documentation (README.md):

# Rspress SSG with Zephyr Integration

This example demonstrates how to integrate Zephyr with Rspress SSG (Static Site Generation).

## Zephyr Integration

Due to Rspress SSG creating multiple build pipelines, a custom Rsbuild plugin is used to prevent multiple Zephyr deployments:

```typescript
// The plugin only applies Zephyr to the 'web' build configuration
config.name === "web" && (await withZephyr()(config));

This prevents the SSG from triggering multiple authentication prompts and asset uploads.

Setup

...existing setup instructions...

Known Issues

  • Logo paths may not resolve correctly in built SSG output (see PR discussion)

**4. Add Basic Tests**:
```typescript
// __tests__/zephyr-plugin.test.ts
import { createZephyrRsbuildPlugin } from '../plugins/zephyr-rsbuild-plugin';

describe('zephyrRsbuildPlugin', () => {
  it('should only apply withZephyr to web configs', async () => {
    const mockWithZephyr = jest.fn();
    const plugin = createZephyrRsbuildPlugin();
    
    const mockApi = {
      modifyRspackConfig: jest.fn()
    };
    
    plugin.setup(mockApi);
    
    const callback = mockApi.modifyRspackConfig.mock.calls[0][0];
    
    // Test web config
    await callback({ name: 'web' });
    expect(mockWithZephyr).toHaveBeenCalled();
    
    // Test non-web config  
    mockWithZephyr.mockClear();
    await callback({ name: 'server' });
    expect(mockWithZephyr).not.toHaveBeenCalled();
  });
});

Overall Quality Score: 6/10

Strengths:

  • ✅ Solves a real technical problem (multiple build triggers)
  • ✅ Clean file structure and organization
  • ✅ Follows Rspress conventions correctly
  • ✅ Addresses core functionality effectively

Areas for Improvement:

  • ❌ Lacks type safety (extensive use of any)
  • ❌ No test coverage for critical custom plugin
  • ❌ Plugin should be extracted for reusability
  • ❌ Documentation needs Zephyr-specific details

Top 3 Next Steps:

  1. Add comprehensive tests for the custom Zephyr plugin to ensure it works correctly and handles edge cases
  2. Improve type safety by defining proper interfaces instead of using any types throughout the plugin
  3. Extract the plugin to a separate, reusable module that can be shared across other Rsbuild-based examples

@fiorin fiorin merged commit b88fd71 into main Jun 12, 2025
5 checks passed
@fiorin fiorin deleted the feat-ssg-support branch June 12, 2025 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants