Skip to content

Commit

Permalink
feat(rsbuild): add rsbuild configuration generator (#29321)
Browse files Browse the repository at this point in the history
## Current Behavior
Nx currently does not offer a generator to help scaffold configuration
for an Rsbuild project

## Expected Behavior
Add a `configuration` generator to the `@nx/rsbuild` package to help
scaffold a configuration for a basic app
  • Loading branch information
Coly010 authored Dec 12, 2024
1 parent 22cec78 commit 36eaafd
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 0 deletions.
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -9902,6 +9902,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "configuration",
"path": "/nx-api/rsbuild/generators/configuration",
"name": "configuration",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -2909,6 +2909,15 @@
"originalFilePath": "/packages/rsbuild/src/generators/init/schema.json",
"path": "/nx-api/rsbuild/generators/init",
"type": "generator"
},
"/nx-api/rsbuild/generators/configuration": {
"description": "Add an Rsbuild configuration for the provided project.",
"file": "generated/packages/rsbuild/generators/configuration.json",
"hidden": false,
"name": "configuration",
"originalFilePath": "/packages/rsbuild/src/generators/configuration/schema.json",
"path": "/nx-api/rsbuild/generators/configuration",
"type": "generator"
}
},
"path": "/nx-api/rsbuild"
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2878,6 +2878,15 @@
"originalFilePath": "/packages/rsbuild/src/generators/init/schema.json",
"path": "rsbuild/generators/init",
"type": "generator"
},
{
"description": "Add an Rsbuild configuration for the provided project.",
"file": "generated/packages/rsbuild/generators/configuration.json",
"hidden": false,
"name": "configuration",
"originalFilePath": "/packages/rsbuild/src/generators/configuration/schema.json",
"path": "rsbuild/generators/configuration",
"type": "generator"
}
],
"githubRoot": "https://github.com/nrwl/nx/blob/master",
Expand Down
50 changes: 50 additions & 0 deletions docs/generated/packages/rsbuild/generators/configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "configuration",
"factory": "./src/generators/configuration/configuration",
"schema": {
"$schema": "http://json-schema.org/schema",
"$id": "Rsbuild",
"title": "Nx Rsbuild Configuration Generator",
"description": "Rsbuild configuration generator.",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "argv", "index": 0 },
"x-dropdown": "project",
"x-prompt": "What is the name of the project to set up a Rsbuild for?",
"x-priority": "important"
},
"entry": {
"type": "string",
"description": "Path relative to the workspace root for the entry file. Defaults to '<projectRoot>/src/index.ts'.",
"x-priority": "important"
},
"tsConfig": {
"type": "string",
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'.",
"x-priority": "important"
},
"target": {
"type": "string",
"description": "Target platform for the build, same as the Rsbuild output.target config option.",
"enum": ["node", "web", "web-worker"],
"default": "web"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
}
},
"presets": []
},
"description": "Add an Rsbuild configuration for the provided project.",
"implementation": "/packages/rsbuild/src/generators/configuration/configuration.ts",
"aliases": [],
"hidden": false,
"path": "/packages/rsbuild/src/generators/configuration/schema.json",
"type": "generator"
}
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@
- [rsbuild](/nx-api/rsbuild)
- [generators](/nx-api/rsbuild/generators)
- [init](/nx-api/rsbuild/generators/init)
- [configuration](/nx-api/rsbuild/generators/configuration)
- [rspack](/nx-api/rspack)
- [documents](/nx-api/rspack/documents)
- [Overview](/nx-api/rspack/documents/overview)
Expand Down
5 changes: 5 additions & 0 deletions packages/rsbuild/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
"description": "Initialize the `@nx/rsbuild` plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"configuration": {
"factory": "./src/generators/configuration/configuration",
"schema": "./src/generators/configuration/schema.json",
"description": "Add an Rsbuild configuration for the provided project."
}
}
}
170 changes: 170 additions & 0 deletions packages/rsbuild/src/generators/configuration/configuration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { type Tree } from '@nx/devkit';
import configurationGenerator from './configuration';

jest.mock('@nx/devkit', () => {
const original = jest.requireActual('@nx/devkit');
return {
...original,
createProjectGraphAsync: jest.fn().mockResolvedValue({
dependencies: {},
nodes: {
myapp: {
name: 'myapp',
type: 'app',
data: {
root: 'apps/myapp',
sourceRoot: 'apps/myapp/src',
targets: {},
},
},
},
}),
};
});

describe('Rsbuild configuration generator', () => {
let tree: Tree;

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
tree.write(
'apps/myapp/project.json',
JSON.stringify({
name: 'myapp',
projectType: 'application',
root: 'apps/myapp',
sourceRoot: 'apps/myapp/src',
targets: {},
})
);
tree.write(
'apps/myapp/src/index.ts',
'export function main() { console.log("Hello world"); }'
);
});

it('should generate Rsbuild configuration files', async () => {
await configurationGenerator(tree, {
project: 'myapp',
skipFormat: true,
});

expect(tree.exists('apps/myapp/rsbuild.config.ts')).toBeTruthy();
expect(tree.read('apps/myapp/rsbuild.config.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import { defineConfig } from '@rsbuild/core';
export default defineConfig({
source: {
entry: {
index: './src/index.ts'
},
},
output: {
target: 'web',
distPath: {
root: 'dist',
},
}
});
"
`);
});

it('should generate Rsbuild configuration with custom entry file', async () => {
tree.write(
'apps/myapp/src/main.ts',
'export function main() { console.log("Hello world"); }'
);
await configurationGenerator(tree, {
project: 'myapp',
entry: 'src/main.ts',
skipFormat: true,
});

expect(tree.exists('apps/myapp/rsbuild.config.ts')).toBeTruthy();
expect(tree.read('apps/myapp/rsbuild.config.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import { defineConfig } from '@rsbuild/core';
export default defineConfig({
source: {
entry: {
index: './src/main.ts'
},
},
output: {
target: 'web',
distPath: {
root: 'dist',
},
}
});
"
`);
});

it('should generate Rsbuild configuration with custom entry file with project root path', async () => {
tree.write(
'apps/myapp/src/main.ts',
'export function main() { console.log("Hello world"); }'
);
await configurationGenerator(tree, {
project: 'myapp',
entry: 'apps/myapp/src/main.ts',
skipFormat: true,
});

expect(tree.exists('apps/myapp/rsbuild.config.ts')).toBeTruthy();
expect(tree.read('apps/myapp/rsbuild.config.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import { defineConfig } from '@rsbuild/core';
export default defineConfig({
source: {
entry: {
index: './src/main.ts'
},
},
output: {
target: 'web',
distPath: {
root: 'dist',
},
}
});
"
`);
});

it('should generate Rsbuild configuration with custom tsconfig file', async () => {
await configurationGenerator(tree, {
project: 'myapp',
tsConfig: 'apps/myapp/tsconfig.json',
skipFormat: true,
});

expect(tree.exists('apps/myapp/rsbuild.config.ts')).toBeTruthy();
expect(tree.read('apps/myapp/rsbuild.config.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import { defineConfig } from '@rsbuild/core';
export default defineConfig({
source: {
entry: {
index: './src/index.ts'
},
tsconfigPath: './tsconfig.json',
},
output: {
target: 'web',
distPath: {
root: 'dist',
},
}
});
"
`);
});
});
66 changes: 66 additions & 0 deletions packages/rsbuild/src/generators/configuration/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
addDependenciesToPackageJson,
createProjectGraphAsync,
generateFiles,
GeneratorCallback,
readProjectConfiguration,
readProjectsConfigurationFromProjectGraph,
runTasksInSerial,
type Tree,
} from '@nx/devkit';
import { type Schema } from './schema';
import { normalizeOptions } from './lib';
import { initGenerator as jsInitGenerator } from '@nx/js';
import { initGenerator } from '../init/init';
import { rsbuildVersion } from '../../utils/versions';
import { join } from 'path';

export async function configurationGenerator(tree: Tree, schema: Schema) {
const projectGraph = await createProjectGraphAsync();
const projects = readProjectsConfigurationFromProjectGraph(projectGraph);
const project = projects.projects[schema.project];
if (!project) {
throw new Error(
`Could not find project '${schema.project}'. Please choose a project that exists in the Nx Workspace.`
);
}

const options = await normalizeOptions(tree, schema, project);
const tasks: GeneratorCallback[] = [];

const jsInitTask = await jsInitGenerator(tree, {
...schema,
skipFormat: true,
tsConfigName:
options.projectRoot === '.' ? 'tsconfig.json' : 'tsconfig.base.json',
});
tasks.push(jsInitTask);
const initTask = await initGenerator(tree, { skipFormat: true });
tasks.push(initTask);

if (options.skipValidation) {
const projectJson = readProjectConfiguration(tree, project.name);
if (projectJson.targets['build']) {
delete projectJson.targets['build'];
}
if (projectJson.targets['serve']) {
delete projectJson.targets['serve'];
}
if (projectJson.targets['dev']) {
delete projectJson.targets['dev'];
}
}

tasks.push(
addDependenciesToPackageJson(tree, {}, { '@rsbuild/core': rsbuildVersion })
);

generateFiles(tree, join(__dirname, 'files'), options.projectRoot, {
...options,
tpl: '',
});

return runTasksInSerial(...tasks);
}

export default configurationGenerator;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
source: {
entry: {
index: '<%= entry %>'
},<% if (tsConfig) { %>
tsconfigPath: '<%= tsConfig %>',<% } %>
},
output: {
target: '<%= target %>',
distPath: {
root: 'dist',
},
}
});
1 change: 1 addition & 0 deletions packages/rsbuild/src/generators/configuration/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './normalize-options';
Loading

0 comments on commit 36eaafd

Please sign in to comment.