Skip to content

Commit

Permalink
feat: Add a shared Rsbuild configuration to build cozy application
Browse files Browse the repository at this point in the history
  • Loading branch information
cballevre committed Nov 28, 2024
1 parent 2a328d3 commit 103ea5d
Show file tree
Hide file tree
Showing 7 changed files with 1,400 additions and 67 deletions.
21 changes: 21 additions & 0 deletions config/rsbuild-config-cozy-app/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017-present, Cozy Cloud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
95 changes: 95 additions & 0 deletions config/rsbuild-config-cozy-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<h1 align="center">Rsbuild Config Cozy App</h1>

## Migrating from cozy-scripts

### Install dependencies

Run the following command to install the necessary dependencies:
```bash
yarn add @rsbuild/core rsbuild-config-cozy-app --dev
```

### Create a configuration file

Create a file named `rsbuild.config.mjs` in your project directory with the following content:
```javascript
import { defineConfig } from '@rsbuild/core'
import { getRsbuildConfig } from 'rsbuild-config-cozy-app'

const config = getRsbuildConfig({
title: 'Your application name',
hasServices: true
})

export default defineConfig(config)
```

### Update Scripts in package.json

Modify the `scripts` section in your `package.json` file as follows:
```json
"scripts": {
"build": "rsbuild build",
"watch": "rsbuild build --watch",
"analyze": "RSDOCTOR=true yarn build"
}
```

### Migrate Assets

Move assets that are not used in the React build to the `/public` directory. This directory will be copied to a public folder. For example, move assets from `/src/targets/vendor/assets` to `/public`. Ensure the `/public` directory exists. By default, Rsbuild copies the content of the public directory to the `distPath`. To comply with cozy-stack constraints, disable this behavior and copy the public directory to a separate folder called `assets` so it can be shared between public and private.

### Update index.ejs

Remove all references to webpack in your `index.ejs` file. Here is an example of an updated `index.ejs`:
```html
<!DOCTYPE html>
<html lang="{{.Locale}}">
<head>
<meta charset="utf-8">
<title><%= htmlPlugin.options.title %></title>
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/assets/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/assets/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/assets/manifest.json" crossorigin="use-credentials">
<link rel="mask-icon" href="/assets/safari-pinned-tab.svg" color="#297EF2">
<meta name="color-scheme" content="light dark" />
<meta name="theme-color" content="#ffffff">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, viewport-fit=cover">
<link rel="stylesheet" type="text/css" href="//{{.Domain}}/assets/fonts/fonts.css">
{{.ThemeCSS}}
</head>
<body>
<div role="application" data-cozy="{{.CozyData}}"></div>
</body>
</html>
```

### Step-by-Step Changes

1. Change `<title><%= htmlWebpackPlugin.options.title %></title>` to `<title><%= htmlPlugin.options.title %></title>`.
2. Remove `_.forEach` for importing CSS and JS files, as RSBuild handles this directly.
3. Import fonts using `<link rel="stylesheet" type="text/css" href="//{{.Domain}}/assets/fonts/fonts.css">`.
4. Prefix assets import with `/assets`

### Define Aliases

Ensure all aliases are defined in the `tsconfig.json`.

### Handling public targets

If your app have a public target, you need to declare the assets as public route inside your manifest.webapp :
```javascript
"/assets": {
"folder": "/assets",
"public": true
}
```
**Note :** You can found more info into the cozy-stack [documentation](https://github.com/cozy/cozy-stack/blob/2cbb312271732663e18802079870747e3a03d03e/docs/apps.md?plain=1#L51)

## Best Practices

- Keep file extensions, especially for `.styl` files.
- Place any files that need to be included in the build package but are not used in React inside the root `public` folder. These files will be copied to the public folder, avoiding duplicated assets for public builds.
- Use a general alias like `@/`.

183 changes: 183 additions & 0 deletions config/rsbuild-config-cozy-app/getRsbuildConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import fs from 'fs'
import path from 'path'

import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'
import { pluginReact } from '@rsbuild/plugin-react'
import { pluginStylus } from '@rsbuild/plugin-stylus'
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin'
import { pluginEjs } from 'rsbuild-plugin-ejs'

import { getServicesEntries } from './helpers'

/**
* Generates the configuration object for the Rsbuild tool.
*
* @param {Object} options - The options object.
* @param {string} options.title - The title of the app.
* @param {boolean} [options.hasPublic] - Indicates if the app has a public target.
* @param {boolean} [options.hasServices] - Indicates if the app has services target.
* @param {boolean} [options.hasIntents] - Indicates if the app has a intents target.
* @returns {Object} The configuration object for Rsbuild.
*/
function getRsbuildConfig({
hasServices = false,
hasPublic = false,
hasIntents = false,
title
} = {}) {
const appPath = fs.realpathSync(process.cwd())

return {
plugins: [
pluginEjs(),
pluginNodePolyfill(),
pluginReact(),
pluginStylus({
stylusOptions: {
// To resolve import from cozy-ui inside stylus files
paths: [path.resolve(appPath, 'node_modules', 'cozy-ui', 'stylus')]
}
})
],
output: {
cleanDistPath: true,
filename: {
html: 'index.html'
},
cssModules: {
// By default, only .module.styl files are considered as CSS modules by Rsbuild.
// This option forces all .styl files to be resolved as CSS modules.
auto: resource => resource.endsWith('.styl')
}
},
performance: {
chunkSplit: {
forceSplitting: {
cozy: /node_modules[\\/]cozy*/
}
}
},
html: {
title
},
tools: {
rspack: {
module: {
rules: [
{
test: /\.webapp$/i,
type: 'json'
}
]
},
plugins: [
// Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time.
process.env.RSDOCTOR && new RsdoctorRspackPlugin()
].filter(Boolean)
}
},
server: {
publicDir: {
copyOnBuild: false
}
},
environments: {
main: {
html: {
template: './src/targets/browser/index.ejs'
},
source: {
entry: {
main: './src/targets/browser/index.jsx'
}
},
output: {
target: 'web',
distPath: {
root: 'build'
},
copy: [
{
from: 'manifest.webapp'
},
{
from: 'README.md'
},
{
from: 'LICENSE'
},
{
from: 'public',
to: 'assets'
}
]
}
},
...(hasPublic && {
public: {
html: {
template: './src/targets/public/index.ejs'
},
source: {
entry: {
public: './src/targets/public/index.jsx'
}
},
output: {
target: 'web',
distPath: {
root: 'build/public'
},
assetPrefix: '/public'
}
}
}),
...(hasIntents && {
intents: {
html: {
template: './src/targets/intents/index.ejs'
},
source: {
entry: {
intents: './src/targets/intents/index.jsx'
}
},
output: {
target: 'web',
distPath: {
root: 'build/intents'
},
assetPrefix: '/intents'
}
}
}),
...(hasServices && {
services: {
source: {
entry: getServicesEntries(appPath)
},
output: {
target: 'node',
distPath: {
root: 'build/services'
}
},
tools: {
rspack: {
module: {
rules: [
{
test: /\.hbs$/, // Only for services with notification
type: 'asset/source'
}
]
}
}
}
}
})
}
}
}

export { getRsbuildConfig }
34 changes: 34 additions & 0 deletions config/rsbuild-config-cozy-app/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import fs from 'fs'
import path from 'path'

/**
* Retrieves the entries for services target in the specified application path.
*
* This function reads the contents of the 'src/targets/services' directory within the given application path,
* filters for JavaScript (.js) and TypeScript (.ts) files, and returns an object mapping each service name
* (derived from the file name) to its relative path.
*
* Example:
* ```
* {
* service1: 'src/targets/services/service1.js',
* service2: 'src/targets/services/service2.ts'
* }
* ```
* @param {string} appPath - The root path of the application.
* @returns {Object} An object where the keys are service names and the values are the relative paths to the service files.
*/
export const getServicesEntries = appPath => {
const servicesDir = path.resolve(appPath, 'src', 'targets', 'services')

return fs
.readdirSync(servicesDir)
.filter(file => file.endsWith('.js') || file.endsWith('.ts'))
.reduce(
(current, file) => ({
...current,
[file.slice(0, -3)]: `./src/targets/services/${file}`
}),
{}
)
}
1 change: 1 addition & 0 deletions config/rsbuild-config-cozy-app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getRsbuildConfig } from './getRsbuildConfig'
34 changes: 34 additions & 0 deletions config/rsbuild-config-cozy-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "rsbuild-config-cozy-app",
"version": "0.0.0",
"description": "Rsbuild config for Cozy Application (Cozy Cloud)",
"author": "Cozy Cloud",
"repository": {
"type": "git",
"url": "git+https://github.com/cozy/cozy-libs.git"
},
"homepage": "https://github.com/cozy/cozy-libs/tree/master/packages/rsbuild-config-cozy-app",
"license": "MIT",
"bugs": {
"url": "https://github.com/cozy/cozy-libs/issues"
},
"files": [
"index.js",
"getRsbuildConfig.js"
],
"devDependencies": {
"@rsbuild/core": "1.1.2",
"cozy-ui": "113.2.0"
},
"dependencies": {
"@rsbuild/plugin-node-polyfill": "1.2.0",
"@rsbuild/plugin-react": "1.0.7",
"@rsbuild/plugin-stylus": "^1.0.6",
"@rsdoctor/rspack-plugin": "^0.4.8",
"rsbuild-plugin-ejs": "1.0.1"
},
"peerDependencies": {
"@rsbuild/core": ">= 1.1.2",
"cozy-ui": ">= 113.2.0"
}
}
Loading

0 comments on commit 103ea5d

Please sign in to comment.