feat(rspack): move rspack into main nx repo (#27969)
- feat: add rspack plugin (#143) - feat: add rspack plugin (#143) - feat(rspack): update to latest rspack version (#159) - feat(rspack): add missing features (less/sass/stylus, assets, etc.) (#160) - feat(rspack): add missing features (less/sass/stylus, assets, etc.) (#160) - feat(rspack): clean-up project setup (#161) - feat(rspack): clean-up project setup (#161) - fix(rspack): use correct app dir when generating non-root projects (#162) - fix(rspack): use correct app dir when generating non-root projects (#162) - chore(repo): migrate to Nx 15.8.5 (#169) - feat(rspack): update and pin rspack to 0.1.0 (#173) - fix(rspack): fix rspack build - chore(rspack): remove comment (#175) - feat(rspack): set mode in configuration and expose option (#177) - fix(rspack): handle existing stylePreprocessorOptions (#182) - fix(rspack): add dependency to ajv-keywords that match the version used by rspack (#187) - fix(rspack): pass devServer options to devServer (#193) - fix(rspack): set externals for target node (#194) - feat(rspack): install latest patch when configuring (#195) - fix(rspack): add withWeb if web app (#200) - chore(repo): fix release script (#202) - chore(repo): fix release script (#202) - feat(rspack): configuration generator better ux (#201) - feat(rspack): builder returns outfile (#207) - fix(rspack): use ensureTypescript before tsquery (#215) - feat(rspack): simplify app generator (#212) - feat(rspack): simplify app generator (#212) - fix(rspack): implement watch mode (#217) - fix(rspack): do not force cssmodules (#222) - fix(rspack): use builtin minify instead (#172) - fix(rspack): use built-in tsconfig paths support (#227) - fix(rspack): add back `resolve.alias` configuration since `resolve.tsConfigPaths` seem to be incorrect in some scenarios (#229) - feat(misc): update to Nx 16 and rescoped packages (#235) - feat(misc): update to Nx 16 and rescoped packages (#235) - fix(misc): replace missed references to @nrwl scope (#239) - chore(repo): add legacy packages for nx rescope (#238) - chore(repo): add legacy packages for nx rescope (#238) - fix(repo): fix publishing for legacy packages (#240) - fix(repo): fix publishing for legacy packages (#240) - fix(misc): target commonjs for legacy packages (#241) - fix(repo): add json files to assets (#243) - chore(repo): update to 16.0.3 (#244) - chore(repo): update to nx 16.2.1 (#271) - fix(rspack): lock version to 0.1.11 (#279) - fix(rspack): refine output filename patterns (#280) - chore(rspack): update to latest (#278) - feat(rspack): Add extractLicenses option to rspack's project configuration (#230) - feat(rspack): Add extractLicenses option to rspack's project configuration (#230) - fix(rspack): add missing license-webpack-plugin dependency (#301) - chore(repo): upgrade to nx 16.6.0 (#319) - fix(rspack): add fileReplacements support (#231) - chore(reop): update nx to 16.7.1 (#325) - chore(rspack): add jest babel config to e2e (#321) - chore: don't use rspack internal module (#328) - chore(repo): update nx to 16.8.1 (#335) - feat(rspack): add typecheck (#338) - chore(repo): update nx to 17.0.1 (#342) - feat(rspack): add generatePackageJson plugin (#341) - feat(rspack): add generatePackageJson plugin (#341) - feat: upgrade rspack to 0.4.4 (#352) - fix(rspack): Add missing peer dep (#372) - chore(repo): migrate to latest nx (#376) - chore(repo): migrate to latest nx (#376) - feat(rspack): update rspack to install the latest version (#379) - feat(rspack): add option to keep existing versions of packages for init generator (#378) - fix(rspack): do not depend directly on ajv to allow for correct hoisting (#384) - fix(rspack): ensure react-refresh is installed (#385) - fix(rspack): User port should be respected. (#387) - feat(rspack_: update rspack to install latest version (#389) - feat(rspack): support object configs (#402) - feat(rspack): add crystal plugin for inferring projects (#407) - feat(rspack): add crystal plugin for inferring projects (#407) - feat(rspack): bump to latest rspack (#412) - fix(rspack): add postcss-loader for css files (#415) - feat(rspack): add module federation support (#416) - feat(rspack): add module federation support (#416) - fix(rspack): add hook for dev server to log when compilation completed (#417) - feat(rspack): add module-federation-static-server (#418) - fix(rspack): ensure process is default import (#420) - chore(repo): move packages/rspack to packages/rspack to prepare to be imported - chore(repo): move packages-legacy/rspack to packages-legacy/rspack to prepare to be imported - chore(repo): move e2e/rspack-e2e to e2e/rspack to prepare to be imported - chore(repo): fix e2e setup - chore(repo): add rspack commit scope - chore(rspack): configure correctly - chore(rspack): final fixes - docs(rspack): add docs for rspack - chore(rspack): fix rspack e2e - chore(react): add rspack bundler test <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
commit
e76c7d1428
@ -9875,6 +9875,130 @@
|
|||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "rspack",
|
||||||
|
"path": "/nx-api/rspack",
|
||||||
|
"name": "rspack",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "documents",
|
||||||
|
"path": "/nx-api/rspack/documents",
|
||||||
|
"name": "documents",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"name": "Overview",
|
||||||
|
"path": "/nx-api/rspack/documents/overview",
|
||||||
|
"id": "overview",
|
||||||
|
"isExternal": false,
|
||||||
|
"children": [],
|
||||||
|
"disableCollapsible": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "executors",
|
||||||
|
"path": "/nx-api/rspack/executors",
|
||||||
|
"name": "executors",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "rspack",
|
||||||
|
"path": "/nx-api/rspack/executors/rspack",
|
||||||
|
"name": "rspack",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dev-server",
|
||||||
|
"path": "/nx-api/rspack/executors/dev-server",
|
||||||
|
"name": "dev-server",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssr-dev-server",
|
||||||
|
"path": "/nx-api/rspack/executors/ssr-dev-server",
|
||||||
|
"name": "ssr-dev-server",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "module-federation-dev-server",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-dev-server",
|
||||||
|
"name": "module-federation-dev-server",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "module-federation-ssr-dev-server",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-ssr-dev-server",
|
||||||
|
"name": "module-federation-ssr-dev-server",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "module-federation-static-server",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-static-server",
|
||||||
|
"name": "module-federation-static-server",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "generators",
|
||||||
|
"path": "/nx-api/rspack/generators",
|
||||||
|
"name": "generators",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "configuration",
|
||||||
|
"path": "/nx-api/rspack/generators/configuration",
|
||||||
|
"name": "configuration",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "init",
|
||||||
|
"path": "/nx-api/rspack/generators/init",
|
||||||
|
"name": "init",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "preset",
|
||||||
|
"path": "/nx-api/rspack/generators/preset",
|
||||||
|
"name": "preset",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "application",
|
||||||
|
"path": "/nx-api/rspack/generators/application",
|
||||||
|
"name": "application",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "storybook",
|
"id": "storybook",
|
||||||
"path": "/nx-api/storybook",
|
"path": "/nx-api/storybook",
|
||||||
|
|||||||
@ -2877,6 +2877,122 @@
|
|||||||
},
|
},
|
||||||
"path": "/nx-api/rollup"
|
"path": "/nx-api/rollup"
|
||||||
},
|
},
|
||||||
|
"rspack": {
|
||||||
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
"name": "rspack",
|
||||||
|
"packageName": "@nx/rspack",
|
||||||
|
"description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
|
||||||
|
"documents": {
|
||||||
|
"/nx-api/rspack/documents/overview": {
|
||||||
|
"id": "overview",
|
||||||
|
"name": "Overview",
|
||||||
|
"description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
|
||||||
|
"file": "generated/packages/rspack/documents/overview",
|
||||||
|
"itemList": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"path": "/nx-api/rspack/documents/overview",
|
||||||
|
"tags": [],
|
||||||
|
"originalFilePath": "shared/packages/rspack/rspack-plugin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "/packages/rspack",
|
||||||
|
"source": "/packages/rspack/src",
|
||||||
|
"executors": {
|
||||||
|
"/nx-api/rspack/executors/rspack": {
|
||||||
|
"description": "Run Rspack via an executor for a project.",
|
||||||
|
"file": "generated/packages/rspack/executors/rspack.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "rspack",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/rspack/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/rspack",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/executors/dev-server": {
|
||||||
|
"description": "Run @rspack/dev-server to serve a project.",
|
||||||
|
"file": "generated/packages/rspack/executors/dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/dev-server/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/executors/ssr-dev-server": {
|
||||||
|
"description": "Serve a SSR application.",
|
||||||
|
"file": "generated/packages/rspack/executors/ssr-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "ssr-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/ssr-dev-server/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/ssr-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/executors/module-federation-dev-server": {
|
||||||
|
"description": "Serve a host or remote application.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-dev-server/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/executors/module-federation-ssr-dev-server": {
|
||||||
|
"description": "Serve a host application along with it's known remotes.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-ssr-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-ssr-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-ssr-dev-server/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-ssr-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/executors/module-federation-static-server": {
|
||||||
|
"description": "Serve a host and its remotes statically.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-static-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-static-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-static-server/schema.json",
|
||||||
|
"path": "/nx-api/rspack/executors/module-federation-static-server",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"generators": {
|
||||||
|
"/nx-api/rspack/generators/configuration": {
|
||||||
|
"description": "Rspack configuration generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/configuration.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "configuration",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/configuration/schema.json",
|
||||||
|
"path": "/nx-api/rspack/generators/configuration",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/generators/init": {
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/init.json",
|
||||||
|
"hidden": true,
|
||||||
|
"name": "init",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/init/schema.json",
|
||||||
|
"path": "/nx-api/rspack/generators/init",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/generators/preset": {
|
||||||
|
"description": "React preset generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/preset.json",
|
||||||
|
"hidden": true,
|
||||||
|
"name": "preset",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/preset/schema.json",
|
||||||
|
"path": "/nx-api/rspack/generators/preset",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/rspack/generators/application": {
|
||||||
|
"description": "React application generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/application.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "application",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/application/schema.json",
|
||||||
|
"path": "/nx-api/rspack/generators/application",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "/nx-api/rspack"
|
||||||
|
},
|
||||||
"storybook": {
|
"storybook": {
|
||||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
"name": "storybook",
|
"name": "storybook",
|
||||||
|
|||||||
@ -2852,6 +2852,121 @@
|
|||||||
"root": "/packages/rollup",
|
"root": "/packages/rollup",
|
||||||
"source": "/packages/rollup/src"
|
"source": "/packages/rollup/src"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
|
||||||
|
"documents": [
|
||||||
|
{
|
||||||
|
"id": "overview",
|
||||||
|
"name": "Overview",
|
||||||
|
"description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
|
||||||
|
"file": "generated/packages/rspack/documents/overview",
|
||||||
|
"itemList": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"path": "rspack/documents/overview",
|
||||||
|
"tags": [],
|
||||||
|
"originalFilePath": "shared/packages/rspack/rspack-plugin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executors": [
|
||||||
|
{
|
||||||
|
"description": "Run Rspack via an executor for a project.",
|
||||||
|
"file": "generated/packages/rspack/executors/rspack.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "rspack",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/rspack/schema.json",
|
||||||
|
"path": "rspack/executors/rspack",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Run @rspack/dev-server to serve a project.",
|
||||||
|
"file": "generated/packages/rspack/executors/dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/dev-server/schema.json",
|
||||||
|
"path": "rspack/executors/dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Serve a SSR application.",
|
||||||
|
"file": "generated/packages/rspack/executors/ssr-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "ssr-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/ssr-dev-server/schema.json",
|
||||||
|
"path": "rspack/executors/ssr-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Serve a host or remote application.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-dev-server/schema.json",
|
||||||
|
"path": "rspack/executors/module-federation-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Serve a host application along with it's known remotes.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-ssr-dev-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-ssr-dev-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-ssr-dev-server/schema.json",
|
||||||
|
"path": "rspack/executors/module-federation-ssr-dev-server",
|
||||||
|
"type": "executor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Serve a host and its remotes statically.",
|
||||||
|
"file": "generated/packages/rspack/executors/module-federation-static-server.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "module-federation-static-server",
|
||||||
|
"originalFilePath": "/packages/rspack/src/executors/module-federation-static-server/schema.json",
|
||||||
|
"path": "rspack/executors/module-federation-static-server",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"generators": [
|
||||||
|
{
|
||||||
|
"description": "Rspack configuration generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/configuration.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "configuration",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/configuration/schema.json",
|
||||||
|
"path": "rspack/generators/configuration",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/init.json",
|
||||||
|
"hidden": true,
|
||||||
|
"name": "init",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/init/schema.json",
|
||||||
|
"path": "rspack/generators/init",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "React preset generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/preset.json",
|
||||||
|
"hidden": true,
|
||||||
|
"name": "preset",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/preset/schema.json",
|
||||||
|
"path": "rspack/generators/preset",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "React application generator.",
|
||||||
|
"file": "generated/packages/rspack/generators/application.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "application",
|
||||||
|
"originalFilePath": "/packages/rspack/src/generators/application/schema.json",
|
||||||
|
"path": "rspack/generators/application",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
"name": "rspack",
|
||||||
|
"packageName": "@nx/rspack",
|
||||||
|
"root": "/packages/rspack",
|
||||||
|
"source": "/packages/rspack/src"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "The Nx Plugin for Storybook contains executors and generators for allowing your workspace to use the powerful Storybook integration testing & documenting capabilities.",
|
"description": "The Nx Plugin for Storybook contains executors and generators for allowing your workspace to use the powerful Storybook integration testing & documenting capabilities.",
|
||||||
"documents": [
|
"documents": [
|
||||||
|
|||||||
98
docs/generated/packages/rspack/documents/overview.md
Normal file
98
docs/generated/packages/rspack/documents/overview.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
title: Overview of the Nx Rspack Plugin
|
||||||
|
description: The Nx Plugin for Rspack contains executors, generators, and utilities for managing Rspack projects in an Nx Workspace.
|
||||||
|
---
|
||||||
|
|
||||||
|
The Nx Plugin for Rspack contains executors, generators, and utilities for managing Rspack projects in an Nx Workspace.
|
||||||
|
|
||||||
|
## Setting Up @nx/rspack
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
{% callout type="note" title="Keep Nx Package Versions In Sync" %}
|
||||||
|
Make sure to install the `@nx/rspack` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/recipes/tips-n-tricks/keep-nx-versions-in-sync).
|
||||||
|
{% /callout %}
|
||||||
|
|
||||||
|
In any Nx workspace, you can install `@nx/rspack` by running the following command:
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab label="Nx 18+" %}
|
||||||
|
|
||||||
|
```shell {% skipRescope=true %}
|
||||||
|
nx add @nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install the correct version of `@nx/rspack`.
|
||||||
|
|
||||||
|
### How @nx/rspack Infers Tasks
|
||||||
|
|
||||||
|
The `@nx/rspack` plugin will create a task for any project that has a Rspack configuration file present. Any of the following files will be recognized as a Rspack configuration file:
|
||||||
|
|
||||||
|
- `rspack.config.js`
|
||||||
|
- `rspack.config.ts`
|
||||||
|
- `rspack.config.mjs`
|
||||||
|
- `rspack.config.mts`
|
||||||
|
- `rspack.config.cjs`
|
||||||
|
- `rspack.config.cts`
|
||||||
|
|
||||||
|
### View Inferred Tasks
|
||||||
|
|
||||||
|
To view inferred tasks for a project, open the [project details view](/concepts/inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line.
|
||||||
|
|
||||||
|
### @nx/rspack Configuration
|
||||||
|
|
||||||
|
The `@nx/rspack/plugin` is configured in the `plugins` array in `nx.json`.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/rspack/plugin",
|
||||||
|
"options": {
|
||||||
|
"buildTargetName": "build",
|
||||||
|
"previewTargetName": "preview",
|
||||||
|
"serveTargetName": "serve",
|
||||||
|
"serveStaticTargetName": "serve-static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `buildTargetName`, `previewTargetName`, `serveTargetName` and `serveStaticTargetName` options control the names of the inferred Rspack tasks. The default names are `build`, `preview`, `serve` and `serve-static`.
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% tab label="Nx < 18" %}
|
||||||
|
|
||||||
|
Install the `@nx/rspack` package with your package manager.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm add -D @nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% /tabs %}
|
||||||
|
|
||||||
|
## Using @nx/rspack
|
||||||
|
|
||||||
|
### Generate a new project using Rspack
|
||||||
|
|
||||||
|
You can generate a [React](/nx-api/react) application or library that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) and [`@nx/react:lib`](/nx-api/react/generators/library) generators accept the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||||
|
|
||||||
|
To generate a React application using Rspack, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nx/react:app my-app --bundler=rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate a React library using Rspack, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nx/react:lib my-lib --bundler=rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modify an existing React project to use Rspack
|
||||||
|
|
||||||
|
You can use the `@nx/rspack:configuration` generator to change your React to use Rspack. This generator will modify your project's configuration to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||||
|
|
||||||
|
You can read more about this generator on the [`@nx/rspack:configuration`](/nx-api/rspack/generators/configuration) generator page.
|
||||||
55
docs/generated/packages/rspack/executors/dev-server.json
Normal file
55
docs/generated/packages/rspack/executors/dev-server.json
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"name": "dev-server",
|
||||||
|
"implementation": "/packages/rspack/src/executors/dev-server/dev-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"version": 2,
|
||||||
|
"title": "Rspack dev-server executor",
|
||||||
|
"description": "Run @rspack/dev-server to serve a project.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The build target for rspack."
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to for the dev-server to listen on."
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Mode to run the server in.",
|
||||||
|
"enum": ["development", "production", "none"]
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using `HTTPS`.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["buildTarget"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Run @rspack/dev-server to serve a project.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/dev-server/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"name": "module-federation-dev-server",
|
||||||
|
"implementation": "/packages/rspack/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Rspack Module Federation Dev Server",
|
||||||
|
"description": "Serve a module federation application.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"devRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": { "type": "string" },
|
||||||
|
"configuration": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "List of remote applications to not automatically serve, either statically or in development mode. This will not remove the remotes from the `module-federation.config` file, and therefore the application may still try to fetch these remotes.\nThis option is useful if you have other means for serving the `remote` application(s).\n**NOTE:** Remotes that are not in the workspace will be skipped automatically.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Port to listen on.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using `HTTPS`.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"static": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to use a static file server instead of the rspack-dev-server. This should be used for remote applications that are also host applications."
|
||||||
|
},
|
||||||
|
"isInitialHost": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
|
"default": true,
|
||||||
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
|
},
|
||||||
|
"staticRemotesPort": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port at which to serve the file-server for the static remotes."
|
||||||
|
},
|
||||||
|
"pathToManifestFile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Serve a host or remote application.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/module-federation-dev-server/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"name": "module-federation-ssr-dev-server",
|
||||||
|
"implementation": "/packages/rspack/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Module Federation SSR Dev Server",
|
||||||
|
"description": "Serve a SSR host application along with its known remotes.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"browserTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the browser application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"serverTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the server application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to be set on `process.env.PORT` for use in the server.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"devRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"staticRemotesPort": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port at which to serve the file-server for the static remotes."
|
||||||
|
},
|
||||||
|
"pathToManifestFile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using HTTPS.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving HTTPS."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving HTTPS."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"isInitialHost": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
|
"default": true,
|
||||||
|
"x-priority": "internal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["browserTarget", "serverTarget"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Serve a host application along with it's known remotes.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/module-federation-ssr-dev-server/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "module-federation-static-server",
|
||||||
|
"implementation": "/packages/rspack/src/executors/module-federation-static-server/module-federation-static-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Module Federation Static Dev Server",
|
||||||
|
"description": "Serve a host application statically along with it's remotes.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": { "serveTarget": { "type": "string" } },
|
||||||
|
"required": ["serveTarget"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Serve a host and its remotes statically.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/module-federation-static-server/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
202
docs/generated/packages/rspack/executors/rspack.json
Normal file
202
docs/generated/packages/rspack/executors/rspack.json
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
{
|
||||||
|
"name": "rspack",
|
||||||
|
"implementation": "/packages/rspack/src/executors/rspack/rspack.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"version": 2,
|
||||||
|
"title": "Rspack build executor",
|
||||||
|
"description": "Run Rspack via an executor for a project.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The platform to target (e.g. web, node).",
|
||||||
|
"enum": ["web", "node"]
|
||||||
|
},
|
||||||
|
"main": { "type": "string", "description": "The main entry file." },
|
||||||
|
"outputPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The output path for the bundle."
|
||||||
|
},
|
||||||
|
"outputFileName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The main output entry file"
|
||||||
|
},
|
||||||
|
"tsConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The tsconfig file to build the project."
|
||||||
|
},
|
||||||
|
"typeCheck": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip the type checking."
|
||||||
|
},
|
||||||
|
"indexHtml": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The path to the index.html file."
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "HTML File which will be contain the application.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.html|.htm)"
|
||||||
|
},
|
||||||
|
"baseHref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Base url for the application being built."
|
||||||
|
},
|
||||||
|
"deployUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL where the application will be deployed."
|
||||||
|
},
|
||||||
|
"rspackConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The path to the rspack config file."
|
||||||
|
},
|
||||||
|
"optimization": {
|
||||||
|
"description": "Enables optimization of the build output.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"scripts": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables optimization of the scripts output.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables optimization of the styles output.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{ "type": "boolean" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sourceMap": {
|
||||||
|
"description": "Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment.",
|
||||||
|
"default": true,
|
||||||
|
"oneOf": [{ "type": "boolean" }, { "type": "string" }]
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of static application assets.",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"glob": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The pattern to match."
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The input directory path in which to apply 'glob'. Defaults to the project root."
|
||||||
|
},
|
||||||
|
"ignore": {
|
||||||
|
"description": "An array of globs to ignore.",
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Absolute path within the output."
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable re-building when files change.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["glob", "input", "output"]
|
||||||
|
},
|
||||||
|
{ "type": "string" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extractLicenses": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Extract all licenses in a separate file.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"fileReplacements": {
|
||||||
|
"description": "Replace files with other files in the build.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"replace": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to be replaced.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
},
|
||||||
|
"with": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to replace with.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["replace", "with"]
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Mode to run the build in.",
|
||||||
|
"enum": ["development", "production", "none"]
|
||||||
|
},
|
||||||
|
"generatePackageJson": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["target", "main", "outputPath", "tsConfig", "rspackConfig"],
|
||||||
|
"definitions": {
|
||||||
|
"assetPattern": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"glob": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The pattern to match."
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The input directory path in which to apply 'glob'. Defaults to the project root."
|
||||||
|
},
|
||||||
|
"ignore": {
|
||||||
|
"description": "An array of globs to ignore.",
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Absolute path within the output."
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable re-building when files change.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["glob", "input", "output"]
|
||||||
|
},
|
||||||
|
{ "type": "string" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Run Rspack via an executor for a project.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/rspack/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
46
docs/generated/packages/rspack/executors/ssr-dev-server.json
Normal file
46
docs/generated/packages/rspack/executors/ssr-dev-server.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "ssr-dev-server",
|
||||||
|
"implementation": "/packages/rspack/src/executors/ssr-dev-server/ssr-dev-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Rspack SSR Dev Server",
|
||||||
|
"description": "Serve a SSR application using rspack.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"browserTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the browser application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"serverTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the server application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to be set on `process.env.PORT` for use in the server.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"browserTargetOptions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Additional options to pass into the browser build target.",
|
||||||
|
"default": {}
|
||||||
|
},
|
||||||
|
"serverTargetOptions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Additional options to pass into the server build target.",
|
||||||
|
"default": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["browserTarget", "serverTarget"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Serve a SSR application.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/executors/ssr-dev-server/schema.json",
|
||||||
|
"type": "executor"
|
||||||
|
}
|
||||||
98
docs/generated/packages/rspack/generators/application.json
Normal file
98
docs/generated/packages/rspack/generators/application.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"name": "application",
|
||||||
|
"factory": "./src/generators/application/application",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Application",
|
||||||
|
"title": "Application generator for React + rspack",
|
||||||
|
"type": "object",
|
||||||
|
"description": "React + Rspack application generator.",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"command": "nx g app myapp --directory=myorg",
|
||||||
|
"description": "Generate `apps/myorg/myapp` and `apps/myorg/myapp-e2e`"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the application.",
|
||||||
|
"type": "string",
|
||||||
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
|
"x-prompt": "What name would you like to use for the application?",
|
||||||
|
"pattern": "^[a-zA-Z].*$",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework to use for the application.",
|
||||||
|
"x-prompt": "What framework do you want to use when generating this application?",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": "react"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"description": "The file extension to be used for style files.",
|
||||||
|
"type": "string",
|
||||||
|
"default": "css",
|
||||||
|
"alias": "s",
|
||||||
|
"x-prompt": {
|
||||||
|
"message": "Which stylesheet format would you like to use?",
|
||||||
|
"type": "list",
|
||||||
|
"items": [
|
||||||
|
{ "value": "css", "label": "CSS" },
|
||||||
|
{
|
||||||
|
"value": "scss",
|
||||||
|
"label": "SASS(.scss) [ http://sass-lang.com ]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "styl",
|
||||||
|
"label": "Stylus(.styl) [ http://stylus-lang.com ]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "less",
|
||||||
|
"label": "LESS [ http://lesscss.org ]"
|
||||||
|
},
|
||||||
|
{ "value": "none", "label": "None" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unitTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The unit test runner to use.",
|
||||||
|
"enum": ["none", "jest"],
|
||||||
|
"default": "jest"
|
||||||
|
},
|
||||||
|
"e2eTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The e2e test runner to use.",
|
||||||
|
"enum": ["none", "cypress"],
|
||||||
|
"default": "cypress"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The directory to nest the app under."
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Add tags to the application (used for linting).",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"monorepo": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates an integrated monorepo.",
|
||||||
|
"aliases": ["integrated"]
|
||||||
|
},
|
||||||
|
"rootProject": { "type": "boolean", "x-priority": "internal" }
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"aliases": ["app"],
|
||||||
|
"x-type": "application",
|
||||||
|
"description": "React application generator.",
|
||||||
|
"implementation": "/packages/rspack/src/generators/application/application.ts",
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/generators/application/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
78
docs/generated/packages/rspack/generators/configuration.json
Normal file
78
docs/generated/packages/rspack/generators/configuration.json
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"name": "configuration",
|
||||||
|
"factory": "./src/generators/configuration/configuration",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Rspack",
|
||||||
|
"title": "Nx Rspack Configuration Generator",
|
||||||
|
"description": "Rspack 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 rspack for?",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework used by the project.",
|
||||||
|
"x-prompt": "What framework is the project you want to convert using?",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.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 rspack config option.",
|
||||||
|
"enum": ["node", "web"],
|
||||||
|
"default": "web"
|
||||||
|
},
|
||||||
|
"devServer": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add a serve target to run a local rspack dev-server",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The style solution to use.",
|
||||||
|
"enum": ["none", "css", "scss", "less"]
|
||||||
|
},
|
||||||
|
"newProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Is this a new project?",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The build target of the project to be transformed to use the @nx/vite:build executor."
|
||||||
|
},
|
||||||
|
"serveTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The serve target of the project to be transformed to use the @nx/vite:dev-server and @nx/vite:preview-server executors."
|
||||||
|
},
|
||||||
|
"rootProject": { "type": "boolean", "x-priority": "internal" }
|
||||||
|
},
|
||||||
|
"required": ["project"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Rspack configuration generator.",
|
||||||
|
"implementation": "/packages/rspack/src/generators/configuration/configuration.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/rspack/src/generators/configuration/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
39
docs/generated/packages/rspack/generators/init.json
Normal file
39
docs/generated/packages/rspack/generators/init.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "init",
|
||||||
|
"factory": "./src/generators/init/init",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Init",
|
||||||
|
"title": "Nx Rspack Init Generator",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"properties": {
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The UI framework used by the project.",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"]
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The style solution to use.",
|
||||||
|
"enum": ["none", "css", "scss", "less", "styl"]
|
||||||
|
},
|
||||||
|
"rootProject": { "type": "boolean", "x-priority": "internal" },
|
||||||
|
"keepExistingVersions": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"description": "Keep existing dependencies versions",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"hidden": true,
|
||||||
|
"implementation": "/packages/rspack/src/generators/init/init.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"path": "/packages/rspack/src/generators/init/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
70
docs/generated/packages/rspack/generators/preset.json
Normal file
70
docs/generated/packages/rspack/generators/preset.json
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"name": "preset",
|
||||||
|
"factory": "./src/generators/preset/preset",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Preset",
|
||||||
|
"title": "Standalone React and rspack preset",
|
||||||
|
"description": "React + Rspack preset generator.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "",
|
||||||
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework to use for the application.",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": "react"
|
||||||
|
},
|
||||||
|
"less": { "type": "boolean", "description": "Use less for styling." },
|
||||||
|
"sass": { "type": "boolean", "description": "Use sass for styling." },
|
||||||
|
"stylus": { "type": "boolean", "description": "Use stylus for styling." },
|
||||||
|
"unitTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The unit test runner to use.",
|
||||||
|
"enum": ["none", "jest"],
|
||||||
|
"default": "jest"
|
||||||
|
},
|
||||||
|
"e2eTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The e2e test runner to use.",
|
||||||
|
"enum": ["none", "cypress"],
|
||||||
|
"default": "cypress"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The directory to nest the app under."
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Add tags to the project (used for linting).",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"monorepo": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates an integrated monorepo.",
|
||||||
|
"default": false,
|
||||||
|
"aliases": ["integrated"]
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "React preset generator.",
|
||||||
|
"hidden": true,
|
||||||
|
"implementation": "/packages/rspack/src/generators/preset/preset.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"path": "/packages/rspack/src/generators/preset/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -2559,6 +2559,19 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "rspack",
|
||||||
|
"id": "rspack",
|
||||||
|
"description": "Rspack package.",
|
||||||
|
"itemList": [
|
||||||
|
{
|
||||||
|
"name": "Overview",
|
||||||
|
"id": "overview",
|
||||||
|
"path": "/nx-api/rspack",
|
||||||
|
"file": "shared/packages/rspack/rspack-plugin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "detox",
|
"name": "detox",
|
||||||
"id": "detox",
|
"id": "detox",
|
||||||
|
|||||||
98
docs/shared/packages/rspack/rspack-plugin.md
Normal file
98
docs/shared/packages/rspack/rspack-plugin.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
title: Overview of the Nx Rspack Plugin
|
||||||
|
description: The Nx Plugin for Rspack contains executors, generators, and utilities for managing Rspack projects in an Nx Workspace.
|
||||||
|
---
|
||||||
|
|
||||||
|
The Nx Plugin for Rspack contains executors, generators, and utilities for managing Rspack projects in an Nx Workspace.
|
||||||
|
|
||||||
|
## Setting Up @nx/rspack
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
{% callout type="note" title="Keep Nx Package Versions In Sync" %}
|
||||||
|
Make sure to install the `@nx/rspack` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/recipes/tips-n-tricks/keep-nx-versions-in-sync).
|
||||||
|
{% /callout %}
|
||||||
|
|
||||||
|
In any Nx workspace, you can install `@nx/rspack` by running the following command:
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab label="Nx 18+" %}
|
||||||
|
|
||||||
|
```shell {% skipRescope=true %}
|
||||||
|
nx add @nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install the correct version of `@nx/rspack`.
|
||||||
|
|
||||||
|
### How @nx/rspack Infers Tasks
|
||||||
|
|
||||||
|
The `@nx/rspack` plugin will create a task for any project that has a Rspack configuration file present. Any of the following files will be recognized as a Rspack configuration file:
|
||||||
|
|
||||||
|
- `rspack.config.js`
|
||||||
|
- `rspack.config.ts`
|
||||||
|
- `rspack.config.mjs`
|
||||||
|
- `rspack.config.mts`
|
||||||
|
- `rspack.config.cjs`
|
||||||
|
- `rspack.config.cts`
|
||||||
|
|
||||||
|
### View Inferred Tasks
|
||||||
|
|
||||||
|
To view inferred tasks for a project, open the [project details view](/concepts/inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line.
|
||||||
|
|
||||||
|
### @nx/rspack Configuration
|
||||||
|
|
||||||
|
The `@nx/rspack/plugin` is configured in the `plugins` array in `nx.json`.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/rspack/plugin",
|
||||||
|
"options": {
|
||||||
|
"buildTargetName": "build",
|
||||||
|
"previewTargetName": "preview",
|
||||||
|
"serveTargetName": "serve",
|
||||||
|
"serveStaticTargetName": "serve-static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `buildTargetName`, `previewTargetName`, `serveTargetName` and `serveStaticTargetName` options control the names of the inferred Rspack tasks. The default names are `build`, `preview`, `serve` and `serve-static`.
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% tab label="Nx < 18" %}
|
||||||
|
|
||||||
|
Install the `@nx/rspack` package with your package manager.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm add -D @nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% /tabs %}
|
||||||
|
|
||||||
|
## Using @nx/rspack
|
||||||
|
|
||||||
|
### Generate a new project using Rspack
|
||||||
|
|
||||||
|
You can generate a [React](/nx-api/react) application or library that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) and [`@nx/react:lib`](/nx-api/react/generators/library) generators accept the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||||
|
|
||||||
|
To generate a React application using Rspack, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nx/react:app my-app --bundler=rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate a React library using Rspack, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nx/react:lib my-lib --bundler=rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modify an existing React project to use Rspack
|
||||||
|
|
||||||
|
You can use the `@nx/rspack:configuration` generator to change your React to use Rspack. This generator will modify your project's configuration to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||||
|
|
||||||
|
You can read more about this generator on the [`@nx/rspack:configuration`](/nx-api/rspack/generators/configuration) generator page.
|
||||||
@ -682,6 +682,21 @@
|
|||||||
- [init](/nx-api/rollup/generators/init)
|
- [init](/nx-api/rollup/generators/init)
|
||||||
- [configuration](/nx-api/rollup/generators/configuration)
|
- [configuration](/nx-api/rollup/generators/configuration)
|
||||||
- [convert-to-inferred](/nx-api/rollup/generators/convert-to-inferred)
|
- [convert-to-inferred](/nx-api/rollup/generators/convert-to-inferred)
|
||||||
|
- [rspack](/nx-api/rspack)
|
||||||
|
- [documents](/nx-api/rspack/documents)
|
||||||
|
- [Overview](/nx-api/rspack/documents/overview)
|
||||||
|
- [executors](/nx-api/rspack/executors)
|
||||||
|
- [rspack](/nx-api/rspack/executors/rspack)
|
||||||
|
- [dev-server](/nx-api/rspack/executors/dev-server)
|
||||||
|
- [ssr-dev-server](/nx-api/rspack/executors/ssr-dev-server)
|
||||||
|
- [module-federation-dev-server](/nx-api/rspack/executors/module-federation-dev-server)
|
||||||
|
- [module-federation-ssr-dev-server](/nx-api/rspack/executors/module-federation-ssr-dev-server)
|
||||||
|
- [module-federation-static-server](/nx-api/rspack/executors/module-federation-static-server)
|
||||||
|
- [generators](/nx-api/rspack/generators)
|
||||||
|
- [configuration](/nx-api/rspack/generators/configuration)
|
||||||
|
- [init](/nx-api/rspack/generators/init)
|
||||||
|
- [preset](/nx-api/rspack/generators/preset)
|
||||||
|
- [application](/nx-api/rspack/generators/application)
|
||||||
- [storybook](/nx-api/storybook)
|
- [storybook](/nx-api/storybook)
|
||||||
- [documents](/nx-api/storybook/documents)
|
- [documents](/nx-api/storybook/documents)
|
||||||
- [Overview](/nx-api/storybook/documents/overview)
|
- [Overview](/nx-api/storybook/documents/overview)
|
||||||
|
|||||||
@ -63,6 +63,41 @@ describe('React Applications', () => {
|
|||||||
}
|
}
|
||||||
}, 250_000);
|
}, 250_000);
|
||||||
|
|
||||||
|
it('should be able to use Rspack to build and test apps', async () => {
|
||||||
|
const appName = uniq('app');
|
||||||
|
const libName = uniq('lib');
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:app ${appName} --bundler=rspack --unit-test-runner=vitest --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:lib ${libName} --bundler=none --no-interactive --unit-test-runner=vitest --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Library generated with Vite
|
||||||
|
checkFilesExist(`${libName}/vite.config.ts`);
|
||||||
|
|
||||||
|
const mainPath = `${appName}/src/main.tsx`;
|
||||||
|
updateFile(
|
||||||
|
mainPath,
|
||||||
|
`
|
||||||
|
import '@${proj}/${libName}';
|
||||||
|
${readFile(mainPath)}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
runCLI(`build ${appName}`);
|
||||||
|
|
||||||
|
checkFilesExist(`dist/${appName}/index.html`);
|
||||||
|
|
||||||
|
if (runE2ETests()) {
|
||||||
|
// TODO(Colum): investigate why webkit is failing
|
||||||
|
const e2eResults = runCLI(`e2e ${appName}-e2e -- --project=chromium`);
|
||||||
|
expect(e2eResults).toContain('Successfully ran target e2e for project');
|
||||||
|
expect(await killPorts()).toBeTruthy();
|
||||||
|
}
|
||||||
|
}, 250_000);
|
||||||
|
|
||||||
it('should be able to generate a react app + lib (with CSR and SSR)', async () => {
|
it('should be able to generate a react app + lib (with CSR and SSR)', async () => {
|
||||||
const appName = uniq('app');
|
const appName = uniq('app');
|
||||||
const libName = uniq('lib');
|
const libName = uniq('lib');
|
||||||
|
|||||||
19
e2e/rspack/jest.config.ts
Normal file
19
e2e/rspack/jest.config.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'e2e-rspack',
|
||||||
|
preset: '../jest.preset.e2e.js',
|
||||||
|
maxWorkers: 1,
|
||||||
|
globals: {},
|
||||||
|
globalSetup: '../utils/global-setup.ts',
|
||||||
|
globalTeardown: '../utils/global-teardown.ts',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]s$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
|
coverageDirectory: '../../coverage/e2e/e2e-rspack',
|
||||||
|
};
|
||||||
10
e2e/rspack/project.json
Normal file
10
e2e/rspack/project.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "e2e-rspack",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"projectType": "application",
|
||||||
|
"sourceRoot": "e2e/rspack",
|
||||||
|
"// targets": "to see all targets run: nx show project e2e-rspack --web",
|
||||||
|
"targets": {},
|
||||||
|
"tags": [],
|
||||||
|
"implicitDependencies": ["rspack"]
|
||||||
|
}
|
||||||
145
e2e/rspack/tests/rspack.spec.ts
Normal file
145
e2e/rspack/tests/rspack.spec.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { getPackageManagerCommand } from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
checkFilesExist,
|
||||||
|
cleanupProject,
|
||||||
|
listFiles,
|
||||||
|
newProject,
|
||||||
|
tmpProjPath,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
runCLI,
|
||||||
|
runCommand,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
describe('rspack e2e', () => {
|
||||||
|
let proj: string;
|
||||||
|
|
||||||
|
// Setting up individual workspaces per
|
||||||
|
// test can cause e2e runs to take a long time.
|
||||||
|
// For this reason, we recommend each suite only
|
||||||
|
// consumes 1 workspace. The tests should each operate
|
||||||
|
// on a unique project in the workspace, such that they
|
||||||
|
// are not dependant on one another.
|
||||||
|
beforeAll(() => {
|
||||||
|
proj = newProject({ packages: ['@nx/rspack'] });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should create rspack root project and additional apps', async () => {
|
||||||
|
const project = uniq('myapp');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/rspack:preset ${project} --framework=react --unitTestRunner=jest --e2eTestRunner=cypress`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Added this so that the nx-ecosystem-ci tests don't throw jest error
|
||||||
|
writeFileSync(
|
||||||
|
join(tmpProjPath(), '.babelrc'),
|
||||||
|
`
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript",
|
||||||
|
[
|
||||||
|
"@nx/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": ["@babel/plugin-transform-runtime"]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const pm = getPackageManagerCommand();
|
||||||
|
runCommand(
|
||||||
|
pm.addDev +
|
||||||
|
' @babel/preset-react @babel/preset-env @babel/preset-typescript'
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = runCLI(`build ${project}`, {
|
||||||
|
env: { NODE_ENV: 'production' },
|
||||||
|
});
|
||||||
|
expect(result).toContain('Successfully ran target build');
|
||||||
|
// Make sure expected files are present.
|
||||||
|
expect(listFiles(`dist/${project}`)).toHaveLength(5);
|
||||||
|
|
||||||
|
result = runCLI(`test ${project}`);
|
||||||
|
expect(result).toContain('Successfully ran target test');
|
||||||
|
|
||||||
|
// TODO(Colum): re-enable when cypress issue is resolved
|
||||||
|
// result = runCLI(`e2e e2e`);
|
||||||
|
// expect(result.stdout).toContain('Successfully ran target e2e');
|
||||||
|
|
||||||
|
// Update app and make sure previous dist files are not present.
|
||||||
|
updateFile(`src/app/app.tsx`, (content) => {
|
||||||
|
return `${content}\nconsole.log('hello');
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
result = runCLI(`build ${project}`, {
|
||||||
|
env: { NODE_ENV: 'production' },
|
||||||
|
});
|
||||||
|
expect(result).toContain('Successfully ran target build');
|
||||||
|
expect(listFiles(`dist/${project}`)).toHaveLength(5); // same length as before
|
||||||
|
|
||||||
|
// Generate a new app and check that the files are correct
|
||||||
|
const app2 = uniq('app2');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/rspack:app ${app2} --framework=react --unitTestRunner=jest --e2eTestRunner=cypress --style=css`
|
||||||
|
);
|
||||||
|
checkFilesExist(`${app2}/project.json`, `${app2}-e2e/project.json`);
|
||||||
|
|
||||||
|
// Added this so that the nx-ecosystem-ci tests don't throw jest error
|
||||||
|
writeFileSync(
|
||||||
|
join(tmpProjPath(), app2, '.babelrc'),
|
||||||
|
`
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript",
|
||||||
|
[
|
||||||
|
"@nx/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": ["@babel/plugin-transform-runtime"]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
result = runCLI(`build ${app2}`, {
|
||||||
|
env: { NODE_ENV: 'production' },
|
||||||
|
});
|
||||||
|
expect(result).toContain('Successfully ran target build');
|
||||||
|
// Make sure expected files are present.
|
||||||
|
expect(listFiles(`dist/${app2}`)).toHaveLength(5);
|
||||||
|
|
||||||
|
result = runCLI(`test ${app2}`);
|
||||||
|
expect(result).toContain('Successfully ran target test');
|
||||||
|
|
||||||
|
// TODO(Colum): re-enable when cypress issue is resolved
|
||||||
|
// result = runCLI(`e2e ${app2}-e2e`);
|
||||||
|
// expect(result.stdout).toContain('Successfully ran target e2e');
|
||||||
|
|
||||||
|
// Generate a Nest app and verify build output
|
||||||
|
const app3 = uniq('app3');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/rspack:app ${app3} --framework=nest --unitTestRunner=jest --no-interactive`
|
||||||
|
);
|
||||||
|
checkFilesExist(`${app3}/project.json`);
|
||||||
|
|
||||||
|
result = runCLI(`build ${app3}`);
|
||||||
|
expect(result).toContain('Successfully ran target build');
|
||||||
|
// Make sure expected files are present.
|
||||||
|
expect(listFiles(`dist/${app3}`)).toHaveLength(2);
|
||||||
|
|
||||||
|
result = runCLI(`build ${app3} --generatePackageJson=true`);
|
||||||
|
expect(result).toContain('Successfully ran target build');
|
||||||
|
// Make sure expected files are present.
|
||||||
|
expect(listFiles(`dist/${app3}`)).toHaveLength(4);
|
||||||
|
}, 200_000);
|
||||||
|
});
|
||||||
13
e2e/rspack/tsconfig.json
Normal file
13
e2e/rspack/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node", "jest"]
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
9
e2e/rspack/tsconfig.spec.json
Normal file
9
e2e/rspack/tsconfig.spec.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
||||||
@ -56,6 +56,7 @@ const nxPackages = [
|
|||||||
`@nx/rollup`,
|
`@nx/rollup`,
|
||||||
`@nx/react`,
|
`@nx/react`,
|
||||||
`@nx/remix`,
|
`@nx/remix`,
|
||||||
|
`@nx/rspack`,
|
||||||
`@nx/storybook`,
|
`@nx/storybook`,
|
||||||
`@nx/vue`,
|
`@nx/vue`,
|
||||||
`@nx/vite`,
|
`@nx/vite`,
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { PackageSchemaSubList } from '@nx/nx-dev/feature-package-schema-viewer/src/lib/package-schema-sub-list';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function DocumentsIndex({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: { menu: Menu; package: ProcessedPackageMetadata } = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaSubList pkg={vm.package} type={'document'} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps(): Promise<{
|
|
||||||
props: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
};
|
|
||||||
}> {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
pkg,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { DocViewer } from '@nx/nx-dev/feature-doc-viewer';
|
|
||||||
import { ProcessedDocument, RelatedDocument } from '@nx/nx-dev/models-document';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { content } from '../../../../lib/rspack/content/overview';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function Overview({
|
|
||||||
document,
|
|
||||||
menu,
|
|
||||||
relatedDocuments,
|
|
||||||
widgetData,
|
|
||||||
}: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
widgetData: { githubStarsCount: number };
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: Menu;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
} = {
|
|
||||||
document,
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
relatedDocuments,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<DocViewer
|
|
||||||
document={vm.document}
|
|
||||||
relatedDocuments={vm.relatedDocuments}
|
|
||||||
widgetData={widgetData}
|
|
||||||
/>
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
const document = {
|
|
||||||
content: content,
|
|
||||||
description: '',
|
|
||||||
filePath: '',
|
|
||||||
id: 'overview',
|
|
||||||
name: 'Overview of the Nx Rspack Plugin',
|
|
||||||
relatedDocuments: {},
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
document,
|
|
||||||
widgetData: {
|
|
||||||
githubStarsCount: await fetchGithubStarCount(),
|
|
||||||
},
|
|
||||||
relatedDocuments: [],
|
|
||||||
menu: menusApi.getMenu('nx-api', ''),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { DocViewer } from '@nx/nx-dev/feature-doc-viewer';
|
|
||||||
import { ProcessedDocument, RelatedDocument } from '@nx/nx-dev/models-document';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { content } from '../../../../lib/rspack/content/rspack-config-setup';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function RspackConfigSetup({
|
|
||||||
document,
|
|
||||||
menu,
|
|
||||||
relatedDocuments,
|
|
||||||
widgetData,
|
|
||||||
}: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
widgetData: { githubStarsCount: number };
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: Menu;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
} = {
|
|
||||||
document,
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
relatedDocuments,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<DocViewer
|
|
||||||
document={vm.document}
|
|
||||||
relatedDocuments={vm.relatedDocuments}
|
|
||||||
widgetData={widgetData}
|
|
||||||
/>
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
const document = {
|
|
||||||
content: content,
|
|
||||||
description:
|
|
||||||
'A guide on how to configure Rspack on your Nx workspace, and instructions on how to customize your Rspack configuration.',
|
|
||||||
filePath: '',
|
|
||||||
id: 'rspack-plugins',
|
|
||||||
name: ' How to configure Rspack on your Nx workspace',
|
|
||||||
relatedDocuments: {},
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
document,
|
|
||||||
widgetData: {
|
|
||||||
githubStarsCount: await fetchGithubStarCount(),
|
|
||||||
},
|
|
||||||
relatedDocuments: [],
|
|
||||||
menu: menusApi.getMenu('nx-api', ''),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { DocViewer } from '@nx/nx-dev/feature-doc-viewer';
|
|
||||||
import { ProcessedDocument, RelatedDocument } from '@nx/nx-dev/models-document';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { content } from '../../../../lib/rspack/content/rspack-plugin';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function RspackPlugins({
|
|
||||||
document,
|
|
||||||
menu,
|
|
||||||
relatedDocuments,
|
|
||||||
widgetData,
|
|
||||||
}: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
widgetData: { githubStarsCount: number };
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
document: ProcessedDocument;
|
|
||||||
menu: Menu;
|
|
||||||
relatedDocuments: RelatedDocument[];
|
|
||||||
} = {
|
|
||||||
document,
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
relatedDocuments,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<DocViewer
|
|
||||||
document={vm.document}
|
|
||||||
relatedDocuments={vm.relatedDocuments}
|
|
||||||
widgetData={widgetData}
|
|
||||||
/>
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
const document = {
|
|
||||||
content: content,
|
|
||||||
description: 'Rspack plugins',
|
|
||||||
filePath: '',
|
|
||||||
id: 'rspack-plugins',
|
|
||||||
name: 'Rspack plugins',
|
|
||||||
relatedDocuments: {},
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
document,
|
|
||||||
widgetData: {
|
|
||||||
githubStarsCount: await fetchGithubStarCount(),
|
|
||||||
},
|
|
||||||
relatedDocuments: [],
|
|
||||||
menu: menusApi.getMenu('nx-api', ''),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { PackageSchemaViewer } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import {
|
|
||||||
ProcessedPackageMetadata,
|
|
||||||
SchemaMetadata,
|
|
||||||
} from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { schema } from '../../../../lib/rspack/schema/executors/dev-server';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function DevServerExecutor({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
menu: Menu;
|
|
||||||
package: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
} = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
schema: schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaViewer pkg={vm.package} schema={vm.schema} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { PackageSchemaSubList } from '@nx/nx-dev/feature-package-schema-viewer/src/lib/package-schema-sub-list';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function ExecutorsIndex({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: { menu: Menu; package: ProcessedPackageMetadata } = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaSubList pkg={vm.package} type={'executor'} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps(): Promise<{
|
|
||||||
props: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
};
|
|
||||||
}> {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
pkg,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { PackageSchemaViewer } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import {
|
|
||||||
ProcessedPackageMetadata,
|
|
||||||
SchemaMetadata,
|
|
||||||
} from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { schema } from '../../../../lib/rspack/schema/executors/rspack';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function RspackExecutor({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
menu: Menu;
|
|
||||||
package: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
} = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
schema: schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaViewer pkg={vm.package} schema={vm.schema} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { PackageSchemaViewer } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import {
|
|
||||||
ProcessedPackageMetadata,
|
|
||||||
SchemaMetadata,
|
|
||||||
} from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { schema } from '../../../../lib/rspack/schema/generators/application';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function ApplicationGenerator({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
menu: Menu;
|
|
||||||
package: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
} = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
schema: schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaViewer pkg={vm.package} schema={vm.schema} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { PackageSchemaViewer } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import {
|
|
||||||
ProcessedPackageMetadata,
|
|
||||||
SchemaMetadata,
|
|
||||||
} from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { schema } from '../../../../lib/rspack/schema/generators/configuration';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function ConfigurationGenerator({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
menu: Menu;
|
|
||||||
package: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
} = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
schema: schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaViewer pkg={vm.package} schema={vm.schema} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { PackageSchemaSubList } from '@nx/nx-dev/feature-package-schema-viewer/src/lib/package-schema-sub-list';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function GeneratorsIndex({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: { menu: Menu; package: ProcessedPackageMetadata } = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaSubList pkg={vm.package} type={'generator'} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps(): Promise<{
|
|
||||||
props: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
};
|
|
||||||
}> {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
pkg,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { PackageSchemaViewer } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import {
|
|
||||||
ProcessedPackageMetadata,
|
|
||||||
SchemaMetadata,
|
|
||||||
} from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
|
|
||||||
import { schema } from '../../../../lib/rspack/schema/generators/init';
|
|
||||||
import { pkg } from '../../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function InitGenerator({
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: {
|
|
||||||
menu: Menu;
|
|
||||||
package: ProcessedPackageMetadata;
|
|
||||||
schema: SchemaMetadata;
|
|
||||||
} = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
schema: schema,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaViewer pkg={vm.package} schema={vm.schema} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
pkg,
|
|
||||||
schema,
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import { PackageSchemaList } from '@nx/nx-dev/feature-package-schema-viewer';
|
|
||||||
import { getPackagesSections } from '@nx/nx-dev/data-access-menu';
|
|
||||||
import { sortCorePackagesFirst } from '@nx/nx-dev/data-access-packages';
|
|
||||||
import { Menu, MenuItem, MenuSection } from '@nx/nx-dev/models-menu';
|
|
||||||
import { ProcessedPackageMetadata } from '@nx/nx-dev/models-package';
|
|
||||||
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
|
|
||||||
import { menusApi } from '../../../lib/menus.api';
|
|
||||||
import { useNavToggle } from '../../../lib/navigation-toggle.effect';
|
|
||||||
import { content } from '../../../lib/rspack/content/overview';
|
|
||||||
import { pkg } from '../../../lib/rspack/pkg';
|
|
||||||
import { ScrollableContent } from '@nx/ui-scrollable-content';
|
|
||||||
|
|
||||||
export default function RspackIndex({
|
|
||||||
overview,
|
|
||||||
menu,
|
|
||||||
pkg,
|
|
||||||
}: {
|
|
||||||
menu: MenuItem[];
|
|
||||||
overview: string;
|
|
||||||
pkg: ProcessedPackageMetadata;
|
|
||||||
}): JSX.Element {
|
|
||||||
const { toggleNav, navIsOpen } = useNavToggle();
|
|
||||||
|
|
||||||
const vm: { menu: Menu; package: ProcessedPackageMetadata } = {
|
|
||||||
menu: {
|
|
||||||
sections: sortCorePackagesFirst<MenuSection>(
|
|
||||||
getPackagesSections(menu),
|
|
||||||
'id'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
package: pkg,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show either the docviewer or the package view depending on:
|
|
||||||
* - docviewer: it is a documentation document
|
|
||||||
* - packageviewer: it is package generated documentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="shell" className="flex h-full flex-col">
|
|
||||||
<div className="w-full flex-shrink-0">
|
|
||||||
<DocumentationHeader isNavOpen={navIsOpen} toggleNav={toggleNav} />
|
|
||||||
</div>
|
|
||||||
<main
|
|
||||||
id="main"
|
|
||||||
role="main"
|
|
||||||
className="flex h-full flex-1 overflow-y-hidden"
|
|
||||||
>
|
|
||||||
<SidebarContainer
|
|
||||||
menu={vm.menu}
|
|
||||||
navIsOpen={navIsOpen}
|
|
||||||
toggleNav={toggleNav}
|
|
||||||
/>
|
|
||||||
<ScrollableContent resetScrollOnNavigation={true}>
|
|
||||||
<PackageSchemaList pkg={vm.package} overview={overview} />
|
|
||||||
</ScrollableContent>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
menu: menusApi.getMenu('nx-api', 'nx-api'),
|
|
||||||
overview: content,
|
|
||||||
pkg,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -100,6 +100,10 @@
|
|||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-url": "^8.0.2",
|
"@rollup/plugin-url": "^8.0.2",
|
||||||
|
"@rspack/core": "1.0.5",
|
||||||
|
"@rspack/dev-server": "1.0.5",
|
||||||
|
"@rspack/plugin-minify": "^0.7.5",
|
||||||
|
"@rspack/plugin-react-refresh": "^1.0.0",
|
||||||
"@schematics/angular": "~18.2.0",
|
"@schematics/angular": "~18.2.0",
|
||||||
"@storybook/addon-essentials": "^8.2.8",
|
"@storybook/addon-essentials": "^8.2.8",
|
||||||
"@storybook/addon-interactions": "^8.2.8",
|
"@storybook/addon-interactions": "^8.2.8",
|
||||||
|
|||||||
11
packages-legacy/rspack/README.md
Normal file
11
packages-legacy/rspack/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
## @nrwl/rspack has been deprecated!
|
||||||
|
|
||||||
|
@nrwl/rspack has been deprecated in favor of [@nx/rspack](https://www.npmjs.com/package/@nx/rspack). Please use that instead.
|
||||||
|
|
||||||
|
@nrwl/rspack will no longer be published in Nx v17.
|
||||||
|
|
||||||
|
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
|
||||||
|
|
||||||
|
# Nx: Smart, Fast and Extensible Build System
|
||||||
|
|
||||||
|
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||||
15
packages-legacy/rspack/executors.json
Normal file
15
packages-legacy/rspack/executors.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"executors": {
|
||||||
|
"rspack": {
|
||||||
|
"implementation": "@nx/rspack/src/executors/rspack/rspack.impl",
|
||||||
|
"schema": "@nx/rspack/src/executors/rspack/schema.json",
|
||||||
|
"description": "rspack executor"
|
||||||
|
},
|
||||||
|
"dev-server": {
|
||||||
|
"implementation": "@nx/rspack/src/executors/dev-server/dev-server.impl",
|
||||||
|
"schema": "@nx/rspack/src/executors/dev-server/schema.json",
|
||||||
|
"description": "dev-server executor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages-legacy/rspack/generators.json
Normal file
4
packages-legacy/rspack/generators.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": ["@nx/rspack"],
|
||||||
|
"schematics": {}
|
||||||
|
}
|
||||||
1
packages-legacy/rspack/index.ts
Normal file
1
packages-legacy/rspack/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from '@nx/rspack';
|
||||||
27
packages-legacy/rspack/package.json
Normal file
27
packages-legacy/rspack/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "@nrwl/rspack",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "commonjs",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nrwl/nx-labs.git",
|
||||||
|
"directory": "packages-legacy/rspack"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"Monorepo",
|
||||||
|
"Next",
|
||||||
|
"Vercel"
|
||||||
|
],
|
||||||
|
"author": "Jack Hsu",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://nx.dev",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"generators": "./generators.json",
|
||||||
|
"executors": "./executors.json",
|
||||||
|
"dependencies": {
|
||||||
|
"@nx/rspack": "file:../../packages/rspack"
|
||||||
|
},
|
||||||
|
"nx-migrations": {
|
||||||
|
"migrations": "@nx/rspack/migrations.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
packages-legacy/rspack/project.json
Normal file
38
packages-legacy/rspack/project.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "rspack-legacy",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "packages-legacy/rspack",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"outputs": ["{workspaceRoot}/build/packages/{projectName}/README.md"],
|
||||||
|
"command": "node ./scripts/copy-readme.js rspack-legacy"
|
||||||
|
},
|
||||||
|
"build-base": {
|
||||||
|
"executor": "@nrwl/js:tsc",
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"options": {
|
||||||
|
"main": "packages-legacy/rspack/index.ts",
|
||||||
|
"tsConfig": "packages-legacy/rspack/tsconfig.json",
|
||||||
|
"outputPath": "build/packages/rspack-legacy",
|
||||||
|
"updateBuildableProjectDepsInPackageJson": false,
|
||||||
|
"assets": [
|
||||||
|
"packages-legacy/rspack/*.md",
|
||||||
|
{
|
||||||
|
"input": "packages-legacy/rspack",
|
||||||
|
"glob": "**/*.json",
|
||||||
|
"ignore": ["**/tsconfig*.json", "project.json"],
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages-legacy/rspack",
|
||||||
|
"glob": "**/*.d.ts",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
"LICENSE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
9
packages-legacy/rspack/tsconfig.json
Normal file
9
packages-legacy/rspack/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"],
|
||||||
|
"files": ["index.ts"]
|
||||||
|
}
|
||||||
@ -142,6 +142,8 @@
|
|||||||
"@nrwl/rollup",
|
"@nrwl/rollup",
|
||||||
"@nx/remix",
|
"@nx/remix",
|
||||||
"@nrwl/remix",
|
"@nrwl/remix",
|
||||||
|
"@nx/rspack",
|
||||||
|
"@nrwl/rspack",
|
||||||
"@nx/storybook",
|
"@nx/storybook",
|
||||||
"@nrwl/storybook",
|
"@nrwl/storybook",
|
||||||
"@nrwl/tao",
|
"@nrwl/tao",
|
||||||
|
|||||||
@ -85,6 +85,10 @@ export const CORE_PLUGINS: CorePlugin[] = [
|
|||||||
name: '@nx/rollup',
|
name: '@nx/rollup',
|
||||||
capabilities: 'executors,generators',
|
capabilities: 'executors,generators',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '@nx/rspack',
|
||||||
|
capabilities: 'executors,generators',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@nx/storybook',
|
name: '@nx/storybook',
|
||||||
capabilities: 'executors,generators',
|
capabilities: 'executors,generators',
|
||||||
|
|||||||
25
packages/rspack/.eslintrc.json
Normal file
25
packages/rspack/.eslintrc.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["./package.json", "./generators.json", "./executors.json"],
|
||||||
|
"parser": "jsonc-eslint-parser",
|
||||||
|
"rules": {
|
||||||
|
"@nx/nx-plugin-checks": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
67
packages/rspack/README.md
Normal file
67
packages/rspack/README.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<p style="text-align: center;">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
|
||||||
|
<img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
|
||||||
|
</picture>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{links}}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
# Nx: Smart Monorepos · Fast CI
|
||||||
|
|
||||||
|
Nx is a build system, optimized for monorepos, with plugins for popular frameworks and tools and advanced CI capabilities including caching and distribution.
|
||||||
|
|
||||||
|
This package is a [Rspack plugin for Nx](https://nx.dev/nx-api/rspack).
|
||||||
|
|
||||||
|
{{content}}
|
||||||
|
|
||||||
|
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
# Nx: Smart, Fast and Extensible Build System
|
||||||
|
|
||||||
|
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||||
|
|
||||||
|
This package is a Rspack plugin for Nx.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Use `--preset=@nx/rspack` when creating new workspace.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-nx-workspace@latest rspack-demo --preset=@nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, you can go into the `rspack-demo` folder and start development.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd rspack-demo
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also run lint, test, and e2e scripts for the project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
npm run test
|
||||||
|
npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
## Existing workspaces
|
||||||
|
|
||||||
|
You can add Rspack to any existing Nx workspace.
|
||||||
|
|
||||||
|
First, install the plugin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev @nx/rspack
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, r
|
||||||
|
|
||||||
|
**Note:** You must restart the server if you make any changes to your library.
|
||||||
35
packages/rspack/executors.json
Normal file
35
packages/rspack/executors.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"executors": {
|
||||||
|
"rspack": {
|
||||||
|
"implementation": "./src/executors/rspack/rspack.impl",
|
||||||
|
"schema": "./src/executors/rspack/schema.json",
|
||||||
|
"description": "Run Rspack via an executor for a project."
|
||||||
|
},
|
||||||
|
"dev-server": {
|
||||||
|
"implementation": "./src/executors/dev-server/dev-server.impl",
|
||||||
|
"schema": "./src/executors/dev-server/schema.json",
|
||||||
|
"description": "Run @rspack/dev-server to serve a project."
|
||||||
|
},
|
||||||
|
"ssr-dev-server": {
|
||||||
|
"implementation": "./src/executors/ssr-dev-server/ssr-dev-server.impl",
|
||||||
|
"schema": "./src/executors/ssr-dev-server/schema.json",
|
||||||
|
"description": "Serve a SSR application."
|
||||||
|
},
|
||||||
|
"module-federation-dev-server": {
|
||||||
|
"implementation": "./src/executors/module-federation-dev-server/module-federation-dev-server.impl",
|
||||||
|
"schema": "./src/executors/module-federation-dev-server/schema.json",
|
||||||
|
"description": "Serve a host or remote application."
|
||||||
|
},
|
||||||
|
"module-federation-ssr-dev-server": {
|
||||||
|
"implementation": "./src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl",
|
||||||
|
"schema": "./src/executors/module-federation-ssr-dev-server/schema.json",
|
||||||
|
"description": "Serve a host application along with it's known remotes."
|
||||||
|
},
|
||||||
|
"module-federation-static-server": {
|
||||||
|
"implementation": "./src/executors/module-federation-static-server/module-federation-static-server.impl",
|
||||||
|
"schema": "./src/executors/module-federation-static-server/schema.json",
|
||||||
|
"description": "Serve a host and its remotes statically."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
packages/rspack/generators.json
Normal file
31
packages/rspack/generators.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"name": "rspack",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"generators": {
|
||||||
|
"configuration": {
|
||||||
|
"factory": "./src/generators/configuration/configuration",
|
||||||
|
"schema": "./src/generators/configuration/schema.json",
|
||||||
|
"description": "Rspack configuration generator."
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"preset": {
|
||||||
|
"factory": "./src/generators/preset/preset",
|
||||||
|
"schema": "./src/generators/preset/schema.json",
|
||||||
|
"description": "React preset generator.",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"factory": "./src/generators/application/application",
|
||||||
|
"schema": "./src/generators/application/schema.json",
|
||||||
|
"aliases": ["app"],
|
||||||
|
"x-type": "application",
|
||||||
|
"description": "React application generator."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
packages/rspack/jest.config.ts
Normal file
16
packages/rspack/jest.config.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'rspack',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
globals: {},
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]s$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
|
coverageDirectory: '../../coverage/packages/rspack',
|
||||||
|
};
|
||||||
98
packages/rspack/migrations.json
Normal file
98
packages/rspack/migrations.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"generators": {
|
||||||
|
"update-16-0-0-add-nx-packages": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "16.0.0-beta.1",
|
||||||
|
"description": "Replace @nrwl/rspack with @nx/rspack",
|
||||||
|
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packageJsonUpdates": {
|
||||||
|
"16.1.3": {
|
||||||
|
"version": "16.1.3-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"version": "~0.1.12",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/dev-server": {
|
||||||
|
"version": "~0.1.12",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/plugin-minify": {
|
||||||
|
"version": "~0.1.12",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"18.1.0": {
|
||||||
|
"version": "18.1.0-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"version": "~0.5.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/dev-server": {
|
||||||
|
"version": "~0.5.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/plugin-minify": {
|
||||||
|
"version": "~0.5.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"18.1.3": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"packages": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"version": "^0.6.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/dev-server": {
|
||||||
|
"version": "^0.6.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/plugin-minify": {
|
||||||
|
"version": "^0.6.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"19.3.0": {
|
||||||
|
"version": "19.3.0-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"version": "^0.7.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/dev-server": {
|
||||||
|
"version": "^0.7.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/plugin-minify": {
|
||||||
|
"version": "^0.7.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"19.7.0": {
|
||||||
|
"version": "19.7.0-beta.1",
|
||||||
|
"packages": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"version": "^1.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/dev-server": {
|
||||||
|
"version": "^1.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@rspack/plugin-react-refresh": {
|
||||||
|
"version": "^1.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "0.1"
|
||||||
|
}
|
||||||
1
packages/rspack/module-federation.ts
Normal file
1
packages/rspack/module-federation.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './src/utils/module-federation/public-api';
|
||||||
48
packages/rspack/package.json
Normal file
48
packages/rspack/package.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "@nx/rspack",
|
||||||
|
"description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "commonjs",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nrwl/nx.git",
|
||||||
|
"directory": "packages/rspack"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nrwl/nx/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"Monorepo",
|
||||||
|
"Rspack",
|
||||||
|
"Bundling",
|
||||||
|
"Module Federation"
|
||||||
|
],
|
||||||
|
"author": "Jack Hsu",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://nx.dev",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"generators": "./generators.json",
|
||||||
|
"executors": "./executors.json",
|
||||||
|
"dependencies": {
|
||||||
|
"@nx/js": "file:../js",
|
||||||
|
"@nx/devkit": "file:../devkit",
|
||||||
|
"@nx/eslint": "file:../eslint",
|
||||||
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||||
|
"less-loader": "11.1.0",
|
||||||
|
"license-webpack-plugin": "^4.0.2",
|
||||||
|
"sass-loader": "^12.2.0",
|
||||||
|
"stylus-loader": "^7.1.0",
|
||||||
|
"postcss-loader": "^8.1.1",
|
||||||
|
"@rspack/core": "^1.0.4",
|
||||||
|
"@rspack/plugin-react-refresh": "^1.0.0",
|
||||||
|
"@rspack/plugin-minify": "^0.7.5",
|
||||||
|
"chalk": "~4.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@module-federation/enhanced": "~0.6.0",
|
||||||
|
"@module-federation/node": "~2.5.10"
|
||||||
|
},
|
||||||
|
"nx-migrations": {
|
||||||
|
"migrations": "./migrations.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/rspack/plugin.ts
Normal file
2
packages/rspack/plugin.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { createDependencies, createNodesV2 } from './src/plugins/plugin';
|
||||||
|
export type { RspackPluginOptions } from './src/plugins/plugin';
|
||||||
50
packages/rspack/project.json
Normal file
50
packages/rspack/project.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "rspack",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "packages/rspack/src",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"add-extra-dependencies": {
|
||||||
|
"outputs": ["{workspaceRoot}/build/packages/rspack"],
|
||||||
|
"command": "node ./scripts/add-dependency-to-build.js rspack @nrwl/rspack"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"outputs": ["{workspaceRoot}/build/packages/rspack"],
|
||||||
|
"options": {
|
||||||
|
"command": "node ./scripts/copy-readme.js rspack"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build-base": {
|
||||||
|
"dependsOn": ["^build-base"],
|
||||||
|
"executor": "@nx/js:tsc",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"options": {
|
||||||
|
"outputPath": "build/packages/rspack",
|
||||||
|
"main": "packages/rspack/src/index.ts",
|
||||||
|
"tsConfig": "packages/rspack/tsconfig.lib.json",
|
||||||
|
"assets": [
|
||||||
|
"packages/rspack/*.md",
|
||||||
|
{
|
||||||
|
"input": "./packages/rspack/src",
|
||||||
|
"glob": "**/!(*.ts)",
|
||||||
|
"output": "./src"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "./packages/rspack/src",
|
||||||
|
"glob": "**/*.d.ts",
|
||||||
|
"output": "./src"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "./packages/rspack",
|
||||||
|
"glob": "**.json",
|
||||||
|
"output": ".",
|
||||||
|
"ignore": ["**/tsconfig*.json", "project.json", ".eslintrc.json"]
|
||||||
|
},
|
||||||
|
"LICENSE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
80
packages/rspack/src/executors/dev-server/dev-server.impl.ts
Normal file
80
packages/rspack/src/executors/dev-server/dev-server.impl.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
logger,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import { Configuration } from '@rspack/core';
|
||||||
|
import { RspackDevServer } from '@rspack/dev-server';
|
||||||
|
import { createCompiler, isMultiCompiler } from '../../utils/create-compiler';
|
||||||
|
import { isMode } from '../../utils/mode-utils';
|
||||||
|
import { getDevServerOptions } from './lib/get-dev-server-config';
|
||||||
|
import { DevServerExecutorSchema } from './schema';
|
||||||
|
|
||||||
|
type DevServer = Configuration['devServer'];
|
||||||
|
export default async function* runExecutor(
|
||||||
|
options: DevServerExecutorSchema,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncIterableIterator<{ success: boolean; baseUrl?: string }> {
|
||||||
|
process.env.NODE_ENV ??= options.mode ?? 'development';
|
||||||
|
|
||||||
|
if (isMode(process.env.NODE_ENV)) {
|
||||||
|
options.mode = process.env.NODE_ENV;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildTarget = parseTargetString(
|
||||||
|
options.buildTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
|
|
||||||
|
const buildOptions = readTargetOptions(buildTarget, context);
|
||||||
|
|
||||||
|
let devServerConfig: DevServer = getDevServerOptions(
|
||||||
|
context.root,
|
||||||
|
options,
|
||||||
|
buildOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
const compiler = await createCompiler(
|
||||||
|
{ ...buildOptions, devServer: devServerConfig, mode: options.mode },
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use the first one if it's MultiCompiler
|
||||||
|
// https://webpack.js.org/configuration/dev-server/#root:~:text=Be%20aware%20that%20when%20exporting%20multiple%20configurations%20only%20the%20devServer%20options%20for%20the%20first%20configuration%20will%20be%20taken%20into%20account%20and%20used%20for%20all%20the%20configurations%20in%20the%20array.
|
||||||
|
const firstCompiler = isMultiCompiler(compiler)
|
||||||
|
? compiler.compilers[0]
|
||||||
|
: compiler;
|
||||||
|
devServerConfig = {
|
||||||
|
...devServerConfig,
|
||||||
|
...firstCompiler.options.devServer,
|
||||||
|
port: devServerConfig.port,
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseUrl = `http://localhost:${options.port ?? 4200}`;
|
||||||
|
|
||||||
|
return yield* createAsyncIterable(({ next }) => {
|
||||||
|
const server = new RspackDevServer(
|
||||||
|
{
|
||||||
|
...devServerConfig,
|
||||||
|
onListening: () => {
|
||||||
|
next({
|
||||||
|
success: true,
|
||||||
|
baseUrl,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
compiler
|
||||||
|
);
|
||||||
|
server.compiler.hooks.done.tap('NX Rspack Dev Server', (stats) => {
|
||||||
|
if (stats.hasErrors()) {
|
||||||
|
logger.error(`NX Compilation failed. See above for more details.`);
|
||||||
|
} else {
|
||||||
|
logger.info(`NX Server ready at ${baseUrl}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
import { logger } from '@nx/devkit';
|
||||||
|
import type { Configuration as RspackDevServerConfiguration } from '@rspack/dev-server';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { RspackExecutorSchema } from '../../rspack/schema';
|
||||||
|
import { DevServerExecutorSchema } from '../schema';
|
||||||
|
import { buildServePath } from './serve-path';
|
||||||
|
|
||||||
|
export function getDevServerOptions(
|
||||||
|
root: string,
|
||||||
|
serveOptions: DevServerExecutorSchema,
|
||||||
|
buildOptions: RspackExecutorSchema
|
||||||
|
): RspackDevServerConfiguration {
|
||||||
|
const servePath = buildServePath(buildOptions);
|
||||||
|
|
||||||
|
let scriptsOptimization: boolean;
|
||||||
|
let stylesOptimization: boolean;
|
||||||
|
if (typeof buildOptions.optimization === 'boolean') {
|
||||||
|
scriptsOptimization = stylesOptimization = buildOptions.optimization;
|
||||||
|
} else if (buildOptions.optimization) {
|
||||||
|
scriptsOptimization = buildOptions.optimization.scripts;
|
||||||
|
stylesOptimization = buildOptions.optimization.styles;
|
||||||
|
} else {
|
||||||
|
scriptsOptimization = stylesOptimization = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: RspackDevServerConfiguration = {
|
||||||
|
host: serveOptions.host,
|
||||||
|
port: serveOptions.port,
|
||||||
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
|
historyApiFallback: {
|
||||||
|
index:
|
||||||
|
buildOptions.index &&
|
||||||
|
`${servePath}${path.basename(buildOptions.index)}`,
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
onListening(server) {
|
||||||
|
const isHttps =
|
||||||
|
server.options.https ||
|
||||||
|
(server.options.server as { type: string })?.type === 'https';
|
||||||
|
logger.info(
|
||||||
|
`NX Web Development Server is listening at ${
|
||||||
|
isHttps ? 'https' : 'http'
|
||||||
|
}://${server.options.host}:${server.options.port}${buildServePath(
|
||||||
|
buildOptions
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
open: false,
|
||||||
|
static: false,
|
||||||
|
compress: scriptsOptimization || stylesOptimization,
|
||||||
|
devMiddleware: {
|
||||||
|
publicPath: servePath,
|
||||||
|
stats: false,
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
webSocketURL: serveOptions.publicHost,
|
||||||
|
overlay: {
|
||||||
|
errors: !(scriptsOptimization || stylesOptimization),
|
||||||
|
warnings: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hot: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (serveOptions.ssl) {
|
||||||
|
config.server = {
|
||||||
|
type: 'https',
|
||||||
|
};
|
||||||
|
if (serveOptions.sslKey && serveOptions.sslCert) {
|
||||||
|
config.server.options = getSslConfig(root, serveOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSslConfig(root: string, options: DevServerExecutorSchema) {
|
||||||
|
return {
|
||||||
|
key: readFileSync(path.resolve(root, options.sslKey), 'utf-8'),
|
||||||
|
cert: readFileSync(path.resolve(root, options.sslCert), 'utf-8'),
|
||||||
|
};
|
||||||
|
}
|
||||||
56
packages/rspack/src/executors/dev-server/lib/serve-path.ts
Normal file
56
packages/rspack/src/executors/dev-server/lib/serve-path.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type { RspackExecutorSchema } from '../../rspack/schema';
|
||||||
|
|
||||||
|
export function buildServePath(browserOptions: RspackExecutorSchema) {
|
||||||
|
let servePath =
|
||||||
|
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
||||||
|
'/';
|
||||||
|
if (servePath.endsWith('/')) {
|
||||||
|
servePath = servePath.slice(0, -1);
|
||||||
|
}
|
||||||
|
if (!servePath.startsWith('/')) {
|
||||||
|
servePath = `/${servePath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return servePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _findDefaultServePath(
|
||||||
|
baseHref?: string,
|
||||||
|
deployUrl?: string
|
||||||
|
): string | null {
|
||||||
|
if (!baseHref && !deployUrl) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
/^(\w+:)?\/\//.test(baseHref || '') ||
|
||||||
|
/^(\w+:)?\/\//.test(deployUrl || '')
|
||||||
|
) {
|
||||||
|
// If baseHref or deployUrl is absolute, unsupported by nx serve
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize baseHref
|
||||||
|
// for nx serve the starting base is always `/` so a relative
|
||||||
|
// and root relative value are identical
|
||||||
|
const baseHrefParts = (baseHref || '')
|
||||||
|
.split('/')
|
||||||
|
.filter((part) => part !== '');
|
||||||
|
if (baseHref && !baseHref.endsWith('/')) {
|
||||||
|
baseHrefParts.pop();
|
||||||
|
}
|
||||||
|
const normalizedBaseHref =
|
||||||
|
baseHrefParts.length === 0 ? '/' : `/${baseHrefParts.join('/')}/`;
|
||||||
|
|
||||||
|
if (deployUrl && deployUrl[0] === '/') {
|
||||||
|
if (baseHref && baseHref[0] === '/' && normalizedBaseHref !== deployUrl) {
|
||||||
|
// If baseHref and deployUrl are root relative and not equivalent, unsupported by nx serve
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return deployUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join together baseHref and deployUrl
|
||||||
|
return `${normalizedBaseHref}${deployUrl || ''}`;
|
||||||
|
}
|
||||||
12
packages/rspack/src/executors/dev-server/schema.d.ts
vendored
Normal file
12
packages/rspack/src/executors/dev-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { Mode } from '@rspack/core';
|
||||||
|
|
||||||
|
export interface DevServerExecutorSchema {
|
||||||
|
buildTarget: string;
|
||||||
|
mode?: Mode;
|
||||||
|
host?: string;
|
||||||
|
port?: number;
|
||||||
|
ssl?: boolean;
|
||||||
|
sslKey?: string;
|
||||||
|
sslCert?: string;
|
||||||
|
publicHost?: string;
|
||||||
|
}
|
||||||
45
packages/rspack/src/executors/dev-server/schema.json
Normal file
45
packages/rspack/src/executors/dev-server/schema.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"version": 2,
|
||||||
|
"title": "Rspack dev-server executor",
|
||||||
|
"description": "Run @rspack/dev-server to serve a project.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The build target for rspack."
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to for the dev-server to listen on."
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Mode to run the server in.",
|
||||||
|
"enum": ["development", "production", "none"]
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using `HTTPS`.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["buildTarget"]
|
||||||
|
}
|
||||||
@ -0,0 +1,317 @@
|
|||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
logger,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
runExecutor,
|
||||||
|
workspaceRoot,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
combineAsyncIterables,
|
||||||
|
createAsyncIterable,
|
||||||
|
} from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import fileServerExecutor from '@nx/web/src/executors/file-server/file-server.impl';
|
||||||
|
import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open';
|
||||||
|
import { cpSync, existsSync } from 'fs';
|
||||||
|
import { extname, join } from 'path';
|
||||||
|
import {
|
||||||
|
getModuleFederationConfig,
|
||||||
|
getRemotes,
|
||||||
|
} from '../../utils/module-federation';
|
||||||
|
import { buildStaticRemotes } from '../../utils/module-federation/build-static.remotes';
|
||||||
|
import {
|
||||||
|
parseStaticRemotesConfig,
|
||||||
|
type StaticRemotesConfig,
|
||||||
|
} from '../../utils/module-federation/parse-static-remotes-config';
|
||||||
|
import { startRemoteProxies } from '../../utils/module-federation/start-remote-proxies';
|
||||||
|
import devServerExecutor from '../dev-server/dev-server.impl';
|
||||||
|
import { ModuleFederationDevServerOptions } from './schema';
|
||||||
|
|
||||||
|
function getBuildOptions(buildTarget: string, context: ExecutorContext) {
|
||||||
|
const target = parseTargetString(buildTarget, context);
|
||||||
|
|
||||||
|
const buildOptions = readTargetOptions(target, context);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...buildOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function startStaticRemotesFileServer(
|
||||||
|
staticRemotesConfig: StaticRemotesConfig,
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: ModuleFederationDevServerOptions
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!staticRemotesConfig.remotes ||
|
||||||
|
staticRemotesConfig.remotes.length === 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let shouldMoveToCommonLocation = false;
|
||||||
|
let commonOutputDirectory: string;
|
||||||
|
for (const app of staticRemotesConfig.remotes) {
|
||||||
|
const remoteBasePath = staticRemotesConfig.config[app].basePath;
|
||||||
|
if (!commonOutputDirectory) {
|
||||||
|
commonOutputDirectory = remoteBasePath;
|
||||||
|
} else if (commonOutputDirectory !== remoteBasePath) {
|
||||||
|
shouldMoveToCommonLocation = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMoveToCommonLocation) {
|
||||||
|
commonOutputDirectory = join(workspaceRoot, 'tmp/static-remotes');
|
||||||
|
for (const app of staticRemotesConfig.remotes) {
|
||||||
|
const remoteConfig = staticRemotesConfig.config[app];
|
||||||
|
cpSync(
|
||||||
|
remoteConfig.outputPath,
|
||||||
|
join(commonOutputDirectory, remoteConfig.urlSegment),
|
||||||
|
{
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const staticRemotesIter = fileServerExecutor(
|
||||||
|
{
|
||||||
|
cors: true,
|
||||||
|
watch: false,
|
||||||
|
staticFilePath: commonOutputDirectory,
|
||||||
|
parallel: false,
|
||||||
|
spa: false,
|
||||||
|
withDeps: false,
|
||||||
|
host: options.host,
|
||||||
|
port: options.staticRemotesPort,
|
||||||
|
ssl: options.ssl,
|
||||||
|
sslCert: options.sslCert,
|
||||||
|
sslKey: options.sslKey,
|
||||||
|
cacheSeconds: -1,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
return staticRemotesIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startRemotes(
|
||||||
|
remotes: string[],
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: ModuleFederationDevServerOptions,
|
||||||
|
target: 'serve' | 'serve-static' = 'serve'
|
||||||
|
) {
|
||||||
|
const remoteIters: AsyncIterable<{ success: boolean }>[] = [];
|
||||||
|
|
||||||
|
for (const app of remotes) {
|
||||||
|
const remoteProjectServeTarget =
|
||||||
|
context.projectGraph.nodes[app].data.targets[target];
|
||||||
|
const isUsingModuleFederationDevServerExecutor =
|
||||||
|
remoteProjectServeTarget.executor.includes(
|
||||||
|
'module-federation-dev-server'
|
||||||
|
);
|
||||||
|
|
||||||
|
const configurationOverride = options.devRemotes?.find(
|
||||||
|
(
|
||||||
|
r
|
||||||
|
): r is {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
} => typeof r !== 'string' && r.remoteName === app
|
||||||
|
)?.configuration;
|
||||||
|
|
||||||
|
const defaultOverrides = {
|
||||||
|
...(options.host ? { host: options.host } : {}),
|
||||||
|
...(options.ssl ? { ssl: options.ssl } : {}),
|
||||||
|
...(options.sslCert ? { sslCert: options.sslCert } : {}),
|
||||||
|
...(options.sslKey ? { sslKey: options.sslKey } : {}),
|
||||||
|
};
|
||||||
|
const overrides =
|
||||||
|
target === 'serve'
|
||||||
|
? {
|
||||||
|
watch: true,
|
||||||
|
...(isUsingModuleFederationDevServerExecutor
|
||||||
|
? { isInitialHost: false }
|
||||||
|
: {}),
|
||||||
|
...defaultOverrides,
|
||||||
|
}
|
||||||
|
: { ...defaultOverrides };
|
||||||
|
|
||||||
|
remoteIters.push(
|
||||||
|
await runExecutor(
|
||||||
|
{
|
||||||
|
project: app,
|
||||||
|
target,
|
||||||
|
configuration: configurationOverride ?? context.configurationName,
|
||||||
|
},
|
||||||
|
overrides,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return remoteIters;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* moduleFederationDevServer(
|
||||||
|
options: ModuleFederationDevServerOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncIterableIterator<{ success: boolean; baseUrl?: string }> {
|
||||||
|
// Force Node to resolve to look for the nx binary that is inside node_modules
|
||||||
|
const nxBin = require.resolve('nx/bin/nx');
|
||||||
|
const currIter = options.static
|
||||||
|
? fileServerExecutor(
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
parallel: false,
|
||||||
|
withDeps: false,
|
||||||
|
spa: false,
|
||||||
|
cors: true,
|
||||||
|
cacheSeconds: -1,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
)
|
||||||
|
: devServerExecutor(options, context);
|
||||||
|
|
||||||
|
const p = context.projectsConfigurations.projects[context.projectName];
|
||||||
|
const buildOptions = getBuildOptions(options.buildTarget, context);
|
||||||
|
|
||||||
|
let pathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
p.sourceRoot,
|
||||||
|
'assets/module-federation.manifest.json'
|
||||||
|
);
|
||||||
|
if (options.pathToManifestFile) {
|
||||||
|
const userPathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
options.pathToManifestFile
|
||||||
|
);
|
||||||
|
if (!existsSync(userPathToManifestFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`
|
||||||
|
);
|
||||||
|
} else if (extname(options.pathToManifestFile) !== '.json') {
|
||||||
|
throw new Error(
|
||||||
|
`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathToManifestFile = userPathToManifestFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.isInitialHost) {
|
||||||
|
return yield* currIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleFederationConfig = getModuleFederationConfig(
|
||||||
|
buildOptions.tsConfig,
|
||||||
|
context.root,
|
||||||
|
p.root,
|
||||||
|
'react'
|
||||||
|
);
|
||||||
|
|
||||||
|
const remoteNames = options.devRemotes?.map((r) =>
|
||||||
|
typeof r === 'string' ? r : r.remoteName
|
||||||
|
);
|
||||||
|
|
||||||
|
const remotes = getRemotes(
|
||||||
|
remoteNames,
|
||||||
|
options.skipRemotes,
|
||||||
|
moduleFederationConfig,
|
||||||
|
{
|
||||||
|
projectName: context.projectName,
|
||||||
|
projectGraph: context.projectGraph,
|
||||||
|
root: context.root,
|
||||||
|
},
|
||||||
|
pathToManifestFile
|
||||||
|
);
|
||||||
|
options.staticRemotesPort ??= remotes.staticRemotePort;
|
||||||
|
|
||||||
|
// Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
|
||||||
|
process.env.NX_MF_DEV_REMOTES = JSON.stringify([
|
||||||
|
...(remotes.devRemotes.map((r) =>
|
||||||
|
typeof r === 'string' ? r : r.remoteName
|
||||||
|
) ?? []),
|
||||||
|
p.name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const staticRemotesConfig = parseStaticRemotesConfig(
|
||||||
|
[...remotes.staticRemotes, ...remotes.dynamicRemotes],
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const mappedLocationsOfStaticRemotes = await buildStaticRemotes(
|
||||||
|
staticRemotesConfig,
|
||||||
|
nxBin,
|
||||||
|
context,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
const devRemoteIters = await startRemotes(
|
||||||
|
remotes.devRemotes,
|
||||||
|
context,
|
||||||
|
options,
|
||||||
|
'serve'
|
||||||
|
);
|
||||||
|
|
||||||
|
const staticRemotesIter = startStaticRemotesFileServer(
|
||||||
|
staticRemotesConfig,
|
||||||
|
context,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
startRemoteProxies(
|
||||||
|
staticRemotesConfig,
|
||||||
|
mappedLocationsOfStaticRemotes,
|
||||||
|
options.ssl
|
||||||
|
? {
|
||||||
|
pathToCert: join(workspaceRoot, options.sslCert),
|
||||||
|
pathToKey: join(workspaceRoot, options.sslKey),
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
return yield* combineAsyncIterables(
|
||||||
|
currIter,
|
||||||
|
...devRemoteIters,
|
||||||
|
...(staticRemotesIter ? [staticRemotesIter] : []),
|
||||||
|
createAsyncIterable<{ success: true; baseUrl: string }>(
|
||||||
|
async ({ next, done }) => {
|
||||||
|
if (!options.isInitialHost) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (remotes.remotePorts.length === 0) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const host = options.host ?? 'localhost';
|
||||||
|
const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${
|
||||||
|
options.port
|
||||||
|
}`;
|
||||||
|
const portsToWaitFor = staticRemotesIter
|
||||||
|
? [options.staticRemotesPort, ...remotes.remotePorts]
|
||||||
|
: [...remotes.remotePorts];
|
||||||
|
await Promise.all(
|
||||||
|
portsToWaitFor.map((port) =>
|
||||||
|
waitForPortOpen(port, {
|
||||||
|
retries: 480,
|
||||||
|
retryDelay: 2500,
|
||||||
|
host: host,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`NX All remotes started, server ready at ${baseUrl}`);
|
||||||
|
next({ success: true, baseUrl: baseUrl });
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to start remotes. Check above for any errors.`
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
18
packages/rspack/src/executors/module-federation-dev-server/schema.d.ts
vendored
Normal file
18
packages/rspack/src/executors/module-federation-dev-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { DevServerExecutorSchema } from '../dev-server/schema';
|
||||||
|
|
||||||
|
export type ModuleFederationDevServerOptions = DevServerExecutorSchema & {
|
||||||
|
// Module Federation Specific Options
|
||||||
|
devRemotes?: (
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
}
|
||||||
|
)[];
|
||||||
|
skipRemotes?: string[];
|
||||||
|
static?: boolean;
|
||||||
|
isInitialHost?: boolean;
|
||||||
|
parallel?: number;
|
||||||
|
staticRemotesPort?: number;
|
||||||
|
pathToManifestFile?: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Rspack Module Federation Dev Server",
|
||||||
|
"description": "Serve a module federation application.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"devRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "List of remote applications to not automatically serve, either statically or in development mode. This will not remove the remotes from the `module-federation.config` file, and therefore the application may still try to fetch these remotes.\nThis option is useful if you have other means for serving the `remote` application(s).\n**NOTE:** Remotes that are not in the workspace will be skipped automatically.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Port to listen on.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using `HTTPS`.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving `HTTPS`."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"static": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to use a static file server instead of the rspack-dev-server. This should be used for remote applications that are also host applications."
|
||||||
|
},
|
||||||
|
"isInitialHost": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
|
"default": true,
|
||||||
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
|
},
|
||||||
|
"staticRemotesPort": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port at which to serve the file-server for the static remotes."
|
||||||
|
},
|
||||||
|
"pathToManifestFile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,406 @@
|
|||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
logger,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
runExecutor,
|
||||||
|
workspaceRoot,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { extname, join } from 'path';
|
||||||
|
import {
|
||||||
|
getModuleFederationConfig,
|
||||||
|
getRemotes,
|
||||||
|
} from '../../utils/module-federation';
|
||||||
|
import { RspackSsrDevServerOptions } from '../ssr-dev-server/schema';
|
||||||
|
import ssrDevServerExecutor from '../ssr-dev-server/ssr-dev-server.impl';
|
||||||
|
|
||||||
|
import {
|
||||||
|
combineAsyncIterables,
|
||||||
|
createAsyncIterable,
|
||||||
|
} from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import { fork } from 'child_process';
|
||||||
|
import { cpSync, createWriteStream, existsSync } from 'fs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseStaticSsrRemotesConfig,
|
||||||
|
type StaticRemotesConfig,
|
||||||
|
} from '../../utils/module-federation/parse-static-remotes-config';
|
||||||
|
|
||||||
|
import fileServerExecutor from '@nx/web/src/executors/file-server/file-server.impl';
|
||||||
|
import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open';
|
||||||
|
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||||
|
import { startSsrRemoteProxies } from '../../utils/module-federation/start-ssr-remote-proxies';
|
||||||
|
|
||||||
|
type ModuleFederationSsrDevServerOptions = RspackSsrDevServerOptions & {
|
||||||
|
devRemotes?: (
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
}
|
||||||
|
)[];
|
||||||
|
|
||||||
|
skipRemotes?: string[];
|
||||||
|
host: string;
|
||||||
|
pathToManifestFile?: string;
|
||||||
|
staticRemotesPort?: number;
|
||||||
|
parallel?: number;
|
||||||
|
ssl?: boolean;
|
||||||
|
sslKey?: string;
|
||||||
|
sslCert?: string;
|
||||||
|
isInitialHost?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeOptions(
|
||||||
|
options: ModuleFederationSsrDevServerOptions
|
||||||
|
): ModuleFederationSsrDevServerOptions {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
ssl: options.ssl ?? false,
|
||||||
|
sslCert: options.sslCert ? join(workspaceRoot, options.sslCert) : undefined,
|
||||||
|
sslKey: options.sslKey ? join(workspaceRoot, options.sslKey) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuildOptions(buildTarget: string, context: ExecutorContext) {
|
||||||
|
const target = parseTargetString(buildTarget, context);
|
||||||
|
|
||||||
|
const buildOptions = readTargetOptions(target, context);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...buildOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSsrStaticRemotesFileServer(
|
||||||
|
ssrStaticRemotesConfig: StaticRemotesConfig,
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: ModuleFederationSsrDevServerOptions
|
||||||
|
) {
|
||||||
|
if (ssrStaticRemotesConfig.remotes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The directories are usually generated with /browser and /server suffixes so we need to copy them to a common directory
|
||||||
|
const commonOutputDirectory = join(workspaceRoot, 'tmp/static-remotes');
|
||||||
|
for (const app of ssrStaticRemotesConfig.remotes) {
|
||||||
|
const remoteConfig = ssrStaticRemotesConfig.config[app];
|
||||||
|
|
||||||
|
cpSync(
|
||||||
|
remoteConfig.outputPath,
|
||||||
|
join(commonOutputDirectory, remoteConfig.urlSegment),
|
||||||
|
{
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const staticRemotesIter = fileServerExecutor(
|
||||||
|
{
|
||||||
|
cors: true,
|
||||||
|
watch: false,
|
||||||
|
staticFilePath: commonOutputDirectory,
|
||||||
|
parallel: false,
|
||||||
|
spa: false,
|
||||||
|
withDeps: false,
|
||||||
|
host: options.host,
|
||||||
|
port: options.staticRemotesPort,
|
||||||
|
ssl: options.ssl,
|
||||||
|
sslCert: options.sslCert,
|
||||||
|
sslKey: options.sslKey,
|
||||||
|
cacheSeconds: -1,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
return staticRemotesIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startRemotes(
|
||||||
|
remotes: string[],
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: ModuleFederationSsrDevServerOptions
|
||||||
|
) {
|
||||||
|
const remoteIters: AsyncIterable<{ success: boolean }>[] = [];
|
||||||
|
const target = 'serve';
|
||||||
|
for (const app of remotes) {
|
||||||
|
const remoteProjectServeTarget =
|
||||||
|
context.projectGraph.nodes[app].data.targets[target];
|
||||||
|
const isUsingModuleFederationSsrDevServerExecutor =
|
||||||
|
remoteProjectServeTarget.executor.includes(
|
||||||
|
'module-federation-ssr-dev-server'
|
||||||
|
);
|
||||||
|
|
||||||
|
const configurationOverride = options.devRemotes?.find(
|
||||||
|
(remote): remote is { remoteName: string; configuration: string } =>
|
||||||
|
typeof remote !== 'string' && remote.remoteName === app
|
||||||
|
)?.configuration;
|
||||||
|
{
|
||||||
|
const defaultOverrides = {
|
||||||
|
...(options.host ? { host: options.host } : {}),
|
||||||
|
...(options.ssl ? { ssl: options.ssl } : {}),
|
||||||
|
...(options.sslCert ? { sslCert: options.sslCert } : {}),
|
||||||
|
...(options.sslKey ? { sslKey: options.sslKey } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const overrides = {
|
||||||
|
watch: true,
|
||||||
|
...defaultOverrides,
|
||||||
|
...(isUsingModuleFederationSsrDevServerExecutor
|
||||||
|
? { isInitialHost: false }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
remoteIters.push(
|
||||||
|
await runExecutor(
|
||||||
|
{
|
||||||
|
project: app,
|
||||||
|
target,
|
||||||
|
configuration: configurationOverride ?? context.configurationName,
|
||||||
|
},
|
||||||
|
overrides,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remoteIters;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildSsrStaticRemotes(
|
||||||
|
staticRemotesConfig: StaticRemotesConfig,
|
||||||
|
nxBin,
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: ModuleFederationSsrDevServerOptions
|
||||||
|
) {
|
||||||
|
if (!staticRemotesConfig.remotes.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Nx is building ${staticRemotesConfig.remotes.length} static remotes...`
|
||||||
|
);
|
||||||
|
const mapLocationOfRemotes: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const remoteApp of staticRemotesConfig.remotes) {
|
||||||
|
mapLocationOfRemotes[remoteApp] = `http${options.ssl ? 's' : ''}://${
|
||||||
|
options.host
|
||||||
|
}:${options.staticRemotesPort}/${
|
||||||
|
staticRemotesConfig.config[remoteApp].urlSegment
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
const childProcess = fork(
|
||||||
|
nxBin,
|
||||||
|
[
|
||||||
|
'run-many',
|
||||||
|
'--target=server',
|
||||||
|
'--projects',
|
||||||
|
staticRemotesConfig.remotes.join(','),
|
||||||
|
...(context.configurationName
|
||||||
|
? [`--configuration=${context.configurationName}`]
|
||||||
|
: []),
|
||||||
|
...(options.parallel ? [`--parallel=${options.parallel}`] : []),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: context.root,
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add a listener to the child process to capture the build log
|
||||||
|
const remoteBuildLogFile = join(
|
||||||
|
workspaceDataDirectory,
|
||||||
|
// eslint-disable-next-line
|
||||||
|
`${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`
|
||||||
|
);
|
||||||
|
|
||||||
|
const remoteBuildLogStream = createWriteStream(remoteBuildLogFile);
|
||||||
|
|
||||||
|
childProcess.stdout.on('data', (data) => {
|
||||||
|
const ANSII_CODE_REGEX =
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||||
|
const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
|
||||||
|
remoteBuildLogStream.write(stdoutString);
|
||||||
|
|
||||||
|
// in addition to writing into the stdout stream, also show error directly in console
|
||||||
|
// so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
|
||||||
|
if (stdoutString.includes('ERROR in')) {
|
||||||
|
logger.log(stdoutString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdoutString.includes('Successfully ran target server')) {
|
||||||
|
childProcess.stdout.removeAllListeners('data');
|
||||||
|
logger.info(
|
||||||
|
`Nx Built ${staticRemotesConfig.remotes.length} static remotes.`
|
||||||
|
);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', () => childProcess.kill('SIGTERM'));
|
||||||
|
process.on('exit', () => childProcess.kill('SIGTERM'));
|
||||||
|
});
|
||||||
|
return mapLocationOfRemotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* moduleFederationSsrDevServer(
|
||||||
|
ssrDevServerOptions: ModuleFederationSsrDevServerOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const options = normalizeOptions(ssrDevServerOptions);
|
||||||
|
// Force Node to resolve to look for the nx binary that is inside node_modules
|
||||||
|
const nxBin = require.resolve('nx/bin/nx');
|
||||||
|
const iter = ssrDevServerExecutor(options, context);
|
||||||
|
const projectConfig =
|
||||||
|
context.projectsConfigurations.projects[context.projectName];
|
||||||
|
const buildOptions = getBuildOptions(options.browserTarget, context);
|
||||||
|
|
||||||
|
let pathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
projectConfig.sourceRoot,
|
||||||
|
'assets/module-federation.manifest.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.pathToManifestFile) {
|
||||||
|
const userPathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
options.pathToManifestFile
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!existsSync(userPathToManifestFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`
|
||||||
|
);
|
||||||
|
} else if (extname(userPathToManifestFile) !== '.json') {
|
||||||
|
throw new Error(
|
||||||
|
`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pathToManifestFile = userPathToManifestFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.isInitialHost) {
|
||||||
|
return yield* iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleFederationConfig = getModuleFederationConfig(
|
||||||
|
buildOptions.tsConfig,
|
||||||
|
context.root,
|
||||||
|
projectConfig.root,
|
||||||
|
'react'
|
||||||
|
);
|
||||||
|
|
||||||
|
const remoteNames = options.devRemotes?.map((remote) =>
|
||||||
|
typeof remote === 'string' ? remote : remote.remoteName
|
||||||
|
);
|
||||||
|
|
||||||
|
const remotes = getRemotes(
|
||||||
|
remoteNames,
|
||||||
|
options.skipRemotes,
|
||||||
|
moduleFederationConfig,
|
||||||
|
{
|
||||||
|
projectName: context.projectName,
|
||||||
|
projectGraph: context.projectGraph,
|
||||||
|
root: context.root,
|
||||||
|
},
|
||||||
|
pathToManifestFile
|
||||||
|
);
|
||||||
|
|
||||||
|
options.staticRemotesPort ??= remotes.staticRemotePort;
|
||||||
|
|
||||||
|
process.env.NX_MF_DEV_REMOTES = JSON.stringify([
|
||||||
|
...(remotes.devRemotes.map((r) =>
|
||||||
|
typeof r === 'string' ? r : r.remoteName
|
||||||
|
) ?? []),
|
||||||
|
projectConfig.name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const staticRemotesConfig = parseStaticSsrRemotesConfig(
|
||||||
|
[...remotes.staticRemotes, ...remotes.dynamicRemotes],
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
const mappedLocationsOfStaticRemotes = await buildSsrStaticRemotes(
|
||||||
|
staticRemotesConfig,
|
||||||
|
nxBin,
|
||||||
|
context,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
const devRemoteIters = await startRemotes(
|
||||||
|
remotes.devRemotes,
|
||||||
|
context,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
const staticRemotesIter = startSsrStaticRemotesFileServer(
|
||||||
|
staticRemotesConfig,
|
||||||
|
context,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
startSsrRemoteProxies(
|
||||||
|
staticRemotesConfig,
|
||||||
|
mappedLocationsOfStaticRemotes,
|
||||||
|
options.ssl
|
||||||
|
? {
|
||||||
|
pathToCert: options.sslCert,
|
||||||
|
pathToKey: options.sslKey,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
return yield* combineAsyncIterables(
|
||||||
|
iter,
|
||||||
|
...devRemoteIters,
|
||||||
|
...(staticRemotesIter ? [staticRemotesIter] : []),
|
||||||
|
createAsyncIterable<{ success: true; baseUrl: string }>(
|
||||||
|
async ({ next, done }) => {
|
||||||
|
if (!options.isInitialHost) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remotes.remotePorts.length === 0) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const host = options.host ?? 'localhost';
|
||||||
|
const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${
|
||||||
|
options.port
|
||||||
|
}`;
|
||||||
|
const portsToWaitFor = staticRemotesIter
|
||||||
|
? [options.staticRemotesPort, ...remotes.remotePorts]
|
||||||
|
: [...remotes.remotePorts];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
portsToWaitFor.map((port) =>
|
||||||
|
waitForPortOpen(port, {
|
||||||
|
retries: 480,
|
||||||
|
retryDelay: 2500,
|
||||||
|
host,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Nx all ssr remotes have started, server ready at ${baseUrl}`
|
||||||
|
);
|
||||||
|
next({ success: true, baseUrl });
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Nx failed to start ssr remotes. Check above for errors.`
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Module Federation SSR Dev Server",
|
||||||
|
"description": "Serve a SSR host application along with its known remotes.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"browserTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the browser application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"serverTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the server application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to be set on `process.env.PORT` for use in the server.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"devRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipRemotes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Host to listen on.",
|
||||||
|
"default": "localhost"
|
||||||
|
},
|
||||||
|
"staticRemotesPort": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port at which to serve the file-server for the static remotes."
|
||||||
|
},
|
||||||
|
"pathToManifestFile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Serve using HTTPS.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"sslKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL key to use for serving HTTPS."
|
||||||
|
},
|
||||||
|
"sslCert": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SSL certificate to use for serving HTTPS."
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"isInitialHost": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
|
"default": true,
|
||||||
|
"x-priority": "internal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["browserTarget", "serverTarget"]
|
||||||
|
}
|
||||||
@ -0,0 +1,394 @@
|
|||||||
|
import {
|
||||||
|
logger,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
Target,
|
||||||
|
workspaceRoot,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
combineAsyncIterables,
|
||||||
|
createAsyncIterable,
|
||||||
|
} from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import fileServerExecutor from '@nx/web/src/executors/file-server/file-server.impl';
|
||||||
|
import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open';
|
||||||
|
import { fork } from 'child_process';
|
||||||
|
import type { Express } from 'express';
|
||||||
|
import { cpSync, existsSync, readFileSync, rmSync } from 'fs';
|
||||||
|
import { ExecutorContext } from 'nx/src/config/misc-interfaces';
|
||||||
|
import { basename, extname, join } from 'path';
|
||||||
|
import {
|
||||||
|
getModuleFederationConfig,
|
||||||
|
getRemotes,
|
||||||
|
} from '../../utils/module-federation';
|
||||||
|
import { buildStaticRemotes } from '../../utils/module-federation/build-static.remotes';
|
||||||
|
import {
|
||||||
|
parseStaticRemotesConfig,
|
||||||
|
StaticRemotesConfig,
|
||||||
|
} from '../../utils/module-federation/parse-static-remotes-config';
|
||||||
|
import { ModuleFederationDevServerOptions } from '../module-federation-dev-server/schema';
|
||||||
|
import type { RspackExecutorSchema } from '../rspack/schema';
|
||||||
|
import { ModuleFederationStaticServerSchema } from './schema';
|
||||||
|
|
||||||
|
function getBuildAndServeOptionsFromServeTarget(
|
||||||
|
serveTarget: string,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const target = parseTargetString(serveTarget, context);
|
||||||
|
|
||||||
|
const serveOptions: ModuleFederationDevServerOptions = readTargetOptions(
|
||||||
|
target,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const buildTarget = parseTargetString(serveOptions.buildTarget, context);
|
||||||
|
|
||||||
|
const buildOptions: RspackExecutorSchema = readTargetOptions(
|
||||||
|
buildTarget,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
let pathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
context.projectGraph.nodes[context.projectName].data.sourceRoot,
|
||||||
|
'assets/module-federation.manifest.json'
|
||||||
|
);
|
||||||
|
if (serveOptions.pathToManifestFile) {
|
||||||
|
const userPathToManifestFile = join(
|
||||||
|
context.root,
|
||||||
|
serveOptions.pathToManifestFile
|
||||||
|
);
|
||||||
|
if (!existsSync(userPathToManifestFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`
|
||||||
|
);
|
||||||
|
} else if (extname(serveOptions.pathToManifestFile) !== '.json') {
|
||||||
|
throw new Error(
|
||||||
|
`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathToManifestFile = userPathToManifestFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
buildTarget,
|
||||||
|
buildOptions,
|
||||||
|
serveOptions,
|
||||||
|
pathToManifestFile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildHost(
|
||||||
|
nxBin: string,
|
||||||
|
buildTarget: Target,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
await new Promise<void>((res, rej) => {
|
||||||
|
const staticProcess = fork(
|
||||||
|
nxBin,
|
||||||
|
[
|
||||||
|
`run`,
|
||||||
|
`${buildTarget.project}:${buildTarget.target}${
|
||||||
|
buildTarget.configuration
|
||||||
|
? `:${buildTarget.configuration}`
|
||||||
|
: context.configurationName
|
||||||
|
? `:${context.configurationName}`
|
||||||
|
: ''
|
||||||
|
}`,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: context.root,
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
staticProcess.stdout.on('data', (data) => {
|
||||||
|
const ANSII_CODE_REGEX =
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||||
|
const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
|
||||||
|
|
||||||
|
// in addition to writing into the stdout stream, also show error directly in console
|
||||||
|
// so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
|
||||||
|
if (stdoutString.includes('ERROR in')) {
|
||||||
|
logger.log(stdoutString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdoutString.includes('Successfully ran target build')) {
|
||||||
|
staticProcess.stdout.removeAllListeners('data');
|
||||||
|
logger.info(`NX Built host`);
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
|
||||||
|
staticProcess.once('exit', (code) => {
|
||||||
|
staticProcess.stdout.removeAllListeners('data');
|
||||||
|
staticProcess.stderr.removeAllListeners('data');
|
||||||
|
if (code !== 0) {
|
||||||
|
rej(`Host failed to build. See above for details.`);
|
||||||
|
} else {
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
|
||||||
|
process.on('exit', () => staticProcess.kill('SIGTERM'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToTmpDirectory(
|
||||||
|
staticRemotesConfig: StaticRemotesConfig,
|
||||||
|
hostOutputPath: string,
|
||||||
|
hostUrlSegment: string
|
||||||
|
) {
|
||||||
|
const commonOutputDirectory = join(
|
||||||
|
workspaceRoot,
|
||||||
|
'tmp/static-module-federation'
|
||||||
|
);
|
||||||
|
for (const app of staticRemotesConfig.remotes) {
|
||||||
|
const remoteConfig = staticRemotesConfig.config[app];
|
||||||
|
cpSync(
|
||||||
|
remoteConfig.outputPath,
|
||||||
|
join(commonOutputDirectory, remoteConfig.urlSegment),
|
||||||
|
{
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cpSync(hostOutputPath, join(commonOutputDirectory, hostUrlSegment), {
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
rmSync(commonOutputDirectory, { force: true, recursive: true });
|
||||||
|
};
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
process.on('exit', () => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
return commonOutputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startProxies(
|
||||||
|
staticRemotesConfig: StaticRemotesConfig,
|
||||||
|
hostServeOptions: ModuleFederationDevServerOptions,
|
||||||
|
mappedLocationOfHost: string,
|
||||||
|
mappedLocationsOfRemotes: Record<string, string>,
|
||||||
|
sslOptions?: { pathToCert: string; pathToKey: string }
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const express = require('express');
|
||||||
|
let sslCert: Buffer;
|
||||||
|
let sslKey: Buffer;
|
||||||
|
if (sslOptions && sslOptions.pathToCert && sslOptions.pathToKey) {
|
||||||
|
if (existsSync(sslOptions.pathToCert) && existsSync(sslOptions.pathToKey)) {
|
||||||
|
sslCert = readFileSync(sslOptions.pathToCert);
|
||||||
|
sslKey = readFileSync(sslOptions.pathToKey);
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
`Encountered SSL options in project.json, however, the certificate files do not exist in the filesystem. Using http.`
|
||||||
|
);
|
||||||
|
logger.warn(
|
||||||
|
`Attempted to find '${sslOptions.pathToCert}' and '${sslOptions.pathToKey}'.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const http = require('http');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
logger.info(`NX Starting static remotes proxies...`);
|
||||||
|
for (const app of staticRemotesConfig.remotes) {
|
||||||
|
const expressProxy: Express = express();
|
||||||
|
expressProxy.use(
|
||||||
|
createProxyMiddleware({
|
||||||
|
target: mappedLocationsOfRemotes[app],
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: sslCert ? false : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const proxyServer = (sslCert ? https : http)
|
||||||
|
.createServer({ cert: sslCert, key: sslKey }, expressProxy)
|
||||||
|
.listen(staticRemotesConfig.config[app].port);
|
||||||
|
process.on('SIGTERM', () => proxyServer.close());
|
||||||
|
process.on('exit', () => proxyServer.close());
|
||||||
|
}
|
||||||
|
logger.info(`NX Static remotes proxies started successfully`);
|
||||||
|
logger.info(`NX Starting static host proxy...`);
|
||||||
|
const expressProxy: Express = express();
|
||||||
|
expressProxy.use(
|
||||||
|
createProxyMiddleware({
|
||||||
|
target: mappedLocationOfHost,
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: sslCert ? false : undefined,
|
||||||
|
pathRewrite: (path) => {
|
||||||
|
let pathRewrite = path;
|
||||||
|
for (const app of staticRemotesConfig.remotes) {
|
||||||
|
if (path.endsWith(app)) {
|
||||||
|
pathRewrite = '/';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathRewrite;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const proxyServer = (sslCert ? https : http)
|
||||||
|
.createServer({ cert: sslCert, key: sslKey }, expressProxy)
|
||||||
|
.listen(hostServeOptions.port);
|
||||||
|
process.on('SIGTERM', () => proxyServer.close());
|
||||||
|
process.on('exit', () => proxyServer.close());
|
||||||
|
logger.info('NX Static host proxy started successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* moduleFederationStaticServer(
|
||||||
|
schema: ModuleFederationStaticServerSchema,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
// Force Node to resolve to look for the nx binary that is inside node_modules
|
||||||
|
const nxBin = require.resolve('nx/bin/nx');
|
||||||
|
|
||||||
|
// Get the remotes from the module federation config
|
||||||
|
const p = context.projectsConfigurations.projects[context.projectName];
|
||||||
|
const options = getBuildAndServeOptionsFromServeTarget(
|
||||||
|
schema.serveTarget,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
const moduleFederationConfig = getModuleFederationConfig(
|
||||||
|
options.buildOptions.tsConfig,
|
||||||
|
context.root,
|
||||||
|
p.root,
|
||||||
|
'react'
|
||||||
|
);
|
||||||
|
|
||||||
|
const remotes = getRemotes(
|
||||||
|
[],
|
||||||
|
options.serveOptions.skipRemotes,
|
||||||
|
moduleFederationConfig,
|
||||||
|
{
|
||||||
|
projectName: context.projectName,
|
||||||
|
projectGraph: context.projectGraph,
|
||||||
|
root: context.root,
|
||||||
|
},
|
||||||
|
options.pathToManifestFile
|
||||||
|
);
|
||||||
|
|
||||||
|
const staticRemotesConfig = parseStaticRemotesConfig(
|
||||||
|
[...remotes.staticRemotes, ...remotes.dynamicRemotes],
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
options.serveOptions.staticRemotesPort ??= remotes.staticRemotePort;
|
||||||
|
const mappedLocationsOfStaticRemotes = await buildStaticRemotes(
|
||||||
|
staticRemotesConfig,
|
||||||
|
nxBin,
|
||||||
|
context,
|
||||||
|
options.serveOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build the host
|
||||||
|
const hostUrlSegment = basename(options.buildOptions.outputPath);
|
||||||
|
const mappedLocationOfHost = `http${options.serveOptions.ssl ? 's' : ''}://${
|
||||||
|
options.serveOptions.host
|
||||||
|
}:${options.serveOptions.staticRemotesPort}/${hostUrlSegment}`;
|
||||||
|
await buildHost(nxBin, options.buildTarget, context);
|
||||||
|
|
||||||
|
// Move to a temporary directory
|
||||||
|
const commonOutputDirectory = moveToTmpDirectory(
|
||||||
|
staticRemotesConfig,
|
||||||
|
options.buildOptions.outputPath,
|
||||||
|
hostUrlSegment
|
||||||
|
);
|
||||||
|
|
||||||
|
// File Serve the temporary directory
|
||||||
|
const staticFileServerIter = fileServerExecutor(
|
||||||
|
{
|
||||||
|
cors: true,
|
||||||
|
watch: false,
|
||||||
|
staticFilePath: commonOutputDirectory,
|
||||||
|
parallel: false,
|
||||||
|
spa: false,
|
||||||
|
withDeps: false,
|
||||||
|
host: options.serveOptions.host,
|
||||||
|
port: options.serveOptions.staticRemotesPort,
|
||||||
|
ssl: options.serveOptions.ssl,
|
||||||
|
sslCert: options.serveOptions.sslCert,
|
||||||
|
sslKey: options.serveOptions.sslKey,
|
||||||
|
cacheSeconds: -1,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
// express proxy all of it
|
||||||
|
startProxies(
|
||||||
|
staticRemotesConfig,
|
||||||
|
options.serveOptions,
|
||||||
|
mappedLocationOfHost,
|
||||||
|
mappedLocationsOfStaticRemotes,
|
||||||
|
options.serveOptions.ssl
|
||||||
|
? {
|
||||||
|
pathToCert: join(workspaceRoot, options.serveOptions.sslCert),
|
||||||
|
pathToKey: join(workspaceRoot, options.serveOptions.sslKey),
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
return yield* combineAsyncIterables(
|
||||||
|
staticFileServerIter,
|
||||||
|
createAsyncIterable<{ success: true; baseUrl: string }>(
|
||||||
|
async ({ next, done }) => {
|
||||||
|
const host = options.serveOptions.host ?? 'localhost';
|
||||||
|
const baseUrl = `http${options.serveOptions.ssl ? 's' : ''}://${host}:${
|
||||||
|
options.serveOptions.port
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (remotes.remotePorts.length === 0) {
|
||||||
|
const portsToWaitFor = [options.serveOptions.staticRemotesPort];
|
||||||
|
await Promise.all(
|
||||||
|
portsToWaitFor.map((port) =>
|
||||||
|
waitForPortOpen(port, {
|
||||||
|
retries: 480,
|
||||||
|
retryDelay: 2500,
|
||||||
|
host: host,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`NX Server ready at ${baseUrl}`);
|
||||||
|
next({ success: true, baseUrl: baseUrl });
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const portsToWaitFor = staticFileServerIter
|
||||||
|
? [options.serveOptions.staticRemotesPort, ...remotes.remotePorts]
|
||||||
|
: [...remotes.remotePorts];
|
||||||
|
await Promise.all(
|
||||||
|
portsToWaitFor.map((port) =>
|
||||||
|
waitForPortOpen(port, {
|
||||||
|
retries: 480,
|
||||||
|
retryDelay: 2500,
|
||||||
|
host: host,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`NX Server ready at ${baseUrl}`);
|
||||||
|
next({ success: true, baseUrl: baseUrl });
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Failed to start. Check above for any errors.`);
|
||||||
|
} finally {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
3
packages/rspack/src/executors/module-federation-static-server/schema.d.ts
vendored
Normal file
3
packages/rspack/src/executors/module-federation-static-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface ModuleFederationStaticServerSchema {
|
||||||
|
serveTarget: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Module Federation Static Dev Server",
|
||||||
|
"description": "Serve a host application statically along with it's remotes.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"serveTarget": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["serveTarget"]
|
||||||
|
}
|
||||||
146
packages/rspack/src/executors/rspack/rspack.impl.ts
Normal file
146
packages/rspack/src/executors/rspack/rspack.impl.ts
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { ExecutorContext, logger } from '@nx/devkit';
|
||||||
|
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import { printDiagnostics, runTypeCheck } from '@nx/js';
|
||||||
|
import { Compiler, MultiCompiler, MultiStats, Stats } from '@rspack/core';
|
||||||
|
import { rmSync } from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { createCompiler, isMultiCompiler } from '../../utils/create-compiler';
|
||||||
|
import { isMode } from '../../utils/mode-utils';
|
||||||
|
import { RspackExecutorSchema } from './schema';
|
||||||
|
|
||||||
|
export default async function* runExecutor(
|
||||||
|
options: RspackExecutorSchema,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
process.env.NODE_ENV ??= options.mode ?? 'production';
|
||||||
|
|
||||||
|
if (isMode(process.env.NODE_ENV)) {
|
||||||
|
options.mode = process.env.NODE_ENV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.typeCheck) {
|
||||||
|
await executeTypeCheck(options, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mimic --clean from webpack.
|
||||||
|
rmSync(path.join(context.root, options.outputPath), {
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const compiler = await createCompiler(options, context);
|
||||||
|
|
||||||
|
const iterable = createAsyncIterable<{
|
||||||
|
success: boolean;
|
||||||
|
outfile?: string;
|
||||||
|
}>(async ({ next, done }) => {
|
||||||
|
if (options.watch) {
|
||||||
|
const watcher = compiler.watch(
|
||||||
|
{},
|
||||||
|
async (err, stats: Stats | MultiStats) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
next({ success: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!compiler || !stats) {
|
||||||
|
logger.error(new Error('Compiler or stats not available'));
|
||||||
|
next({ success: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statsOptions = getStatsOptions(compiler);
|
||||||
|
const printedStats = stats.toString(statsOptions);
|
||||||
|
// Avoid extra empty line when `stats: 'none'`
|
||||||
|
if (printedStats) {
|
||||||
|
console.error(printedStats);
|
||||||
|
}
|
||||||
|
next({
|
||||||
|
success: !stats.hasErrors(),
|
||||||
|
outfile: path.resolve(context.root, options.outputPath, 'main.js'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
registerCleanupCallback(() => {
|
||||||
|
watcher.close(() => {
|
||||||
|
logger.info('Watcher closed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
compiler.run(async (err, stats: Stats | MultiStats) => {
|
||||||
|
compiler.close(() => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
next({ success: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!compiler || !stats) {
|
||||||
|
logger.error(new Error('Compiler or stats not available'));
|
||||||
|
next({ success: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statsOptions = getStatsOptions(compiler);
|
||||||
|
const printedStats = stats.toString(statsOptions);
|
||||||
|
// Avoid extra empty line when `stats: 'none'`
|
||||||
|
if (printedStats) {
|
||||||
|
console.error(printedStats);
|
||||||
|
}
|
||||||
|
next({
|
||||||
|
success: !stats.hasErrors(),
|
||||||
|
outfile: path.resolve(context.root, options.outputPath, 'main.js'),
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
yield* iterable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from packages/esbuild/src/executors/esbuild/esbuild.impl.ts
|
||||||
|
function registerCleanupCallback(callback: () => void) {
|
||||||
|
const wrapped = () => {
|
||||||
|
callback();
|
||||||
|
process.off('SIGINT', wrapped);
|
||||||
|
process.off('SIGTERM', wrapped);
|
||||||
|
process.off('exit', wrapped);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('SIGINT', wrapped);
|
||||||
|
process.on('SIGTERM', wrapped);
|
||||||
|
process.on('exit', wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeTypeCheck(
|
||||||
|
options: RspackExecutorSchema,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const projectConfiguration =
|
||||||
|
context.projectGraph.nodes[context.projectName].data;
|
||||||
|
const result = await runTypeCheck({
|
||||||
|
workspaceRoot: path.resolve(projectConfiguration.root),
|
||||||
|
tsConfigPath: options.tsConfig,
|
||||||
|
mode: 'noEmit',
|
||||||
|
});
|
||||||
|
|
||||||
|
await printDiagnostics(result.errors, result.warnings);
|
||||||
|
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
throw new Error('Found type errors. See above.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatsOptions(compiler: Compiler | MultiCompiler) {
|
||||||
|
return isMultiCompiler(compiler)
|
||||||
|
? {
|
||||||
|
children: compiler.compilers.map((compiler) =>
|
||||||
|
compiler.options ? compiler.options.stats : undefined
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: compiler.options
|
||||||
|
? compiler.options.stats
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
34
packages/rspack/src/executors/rspack/schema.d.ts
vendored
Normal file
34
packages/rspack/src/executors/rspack/schema.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import type { Mode } from '@rspack/core';
|
||||||
|
|
||||||
|
export interface RspackExecutorSchema {
|
||||||
|
target: 'web' | 'node';
|
||||||
|
main: string;
|
||||||
|
index?: string;
|
||||||
|
tsConfig: string;
|
||||||
|
typeCheck?: boolean;
|
||||||
|
outputPath: string;
|
||||||
|
outputFileName?: string;
|
||||||
|
indexHtml?: string;
|
||||||
|
mode?: Mode;
|
||||||
|
watch?: boolean;
|
||||||
|
baseHref?: string;
|
||||||
|
deployUrl?: string;
|
||||||
|
|
||||||
|
rspackConfig: string;
|
||||||
|
optimization?: boolean | OptimizationOptions;
|
||||||
|
sourceMap?: boolean | string;
|
||||||
|
assets?: any[];
|
||||||
|
extractLicenses?: boolean;
|
||||||
|
fileReplacements?: FileReplacement[];
|
||||||
|
generatePackageJson?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileReplacement {
|
||||||
|
replace: string;
|
||||||
|
with: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OptimizationOptions {
|
||||||
|
scripts: boolean;
|
||||||
|
styles: boolean;
|
||||||
|
}
|
||||||
177
packages/rspack/src/executors/rspack/schema.json
Normal file
177
packages/rspack/src/executors/rspack/schema.json
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"version": 2,
|
||||||
|
"title": "Rspack build executor",
|
||||||
|
"description": "Run Rspack via an executor for a project.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The platform to target (e.g. web, node).",
|
||||||
|
"enum": ["web", "node"]
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The main entry file."
|
||||||
|
},
|
||||||
|
"outputPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The output path for the bundle."
|
||||||
|
},
|
||||||
|
"outputFileName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The main output entry file"
|
||||||
|
},
|
||||||
|
"tsConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The tsconfig file to build the project."
|
||||||
|
},
|
||||||
|
"typeCheck": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip the type checking."
|
||||||
|
},
|
||||||
|
"indexHtml": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The path to the index.html file."
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "HTML File which will be contain the application.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.html|.htm)"
|
||||||
|
},
|
||||||
|
"baseHref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Base url for the application being built."
|
||||||
|
},
|
||||||
|
"deployUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URL where the application will be deployed."
|
||||||
|
},
|
||||||
|
"rspackConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The path to the rspack config file."
|
||||||
|
},
|
||||||
|
"optimization": {
|
||||||
|
"description": "Enables optimization of the build output.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"scripts": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables optimization of the scripts output.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables optimization of the styles output.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sourceMap": {
|
||||||
|
"description": "Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment.",
|
||||||
|
"default": true,
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of static application assets.",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/assetPattern"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extractLicenses": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Extract all licenses in a separate file.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"fileReplacements": {
|
||||||
|
"description": "Replace files with other files in the build.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"replace": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to be replaced.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
},
|
||||||
|
"with": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to replace with.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["replace", "with"]
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Mode to run the build in.",
|
||||||
|
"enum": ["development", "production", "none"]
|
||||||
|
},
|
||||||
|
"generatePackageJson": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["target", "main", "outputPath", "tsConfig", "rspackConfig"],
|
||||||
|
"definitions": {
|
||||||
|
"assetPattern": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"glob": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The pattern to match."
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The input directory path in which to apply 'glob'. Defaults to the project root."
|
||||||
|
},
|
||||||
|
"ignore": {
|
||||||
|
"description": "An array of globs to ignore.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Absolute path within the output."
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable re-building when files change.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["glob", "input", "output"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import * as net from 'net';
|
||||||
|
|
||||||
|
export function waitUntilServerIsListening(port: number): Promise<void> {
|
||||||
|
const allowedErrorCodes = ['ECONNREFUSED', 'ECONNRESET'];
|
||||||
|
const maxAttempts = 25;
|
||||||
|
let attempts = 0;
|
||||||
|
const client = new net.Socket();
|
||||||
|
const cleanup = () => {
|
||||||
|
client.removeAllListeners('connect');
|
||||||
|
client.removeAllListeners('error');
|
||||||
|
client.end();
|
||||||
|
client.destroy();
|
||||||
|
client.unref();
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const listen = () => {
|
||||||
|
client.once('connect', () => {
|
||||||
|
cleanup();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
client.on('error', (err) => {
|
||||||
|
if (
|
||||||
|
attempts > maxAttempts ||
|
||||||
|
!allowedErrorCodes.includes(err['code'])
|
||||||
|
) {
|
||||||
|
cleanup();
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
attempts++;
|
||||||
|
setTimeout(listen, 100 * attempts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.connect({ port, host: 'localhost' });
|
||||||
|
};
|
||||||
|
listen();
|
||||||
|
});
|
||||||
|
}
|
||||||
11
packages/rspack/src/executors/ssr-dev-server/schema.d.ts
vendored
Normal file
11
packages/rspack/src/executors/ssr-dev-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
interface TargetOptions {
|
||||||
|
[key: string]: string | boolean | number | TargetOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RspackSsrDevServerOptions {
|
||||||
|
browserTarget: string;
|
||||||
|
serverTarget: string;
|
||||||
|
port: number;
|
||||||
|
browserTargetOptions: TargetOptions;
|
||||||
|
serverTargetOptions: TargetOptions;
|
||||||
|
}
|
||||||
36
packages/rspack/src/executors/ssr-dev-server/schema.json
Normal file
36
packages/rspack/src/executors/ssr-dev-server/schema.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"outputCapture": "direct-nodejs",
|
||||||
|
"title": "Rspack SSR Dev Server",
|
||||||
|
"description": "Serve a SSR application using rspack.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"browserTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the browser application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"serverTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target which builds the server application.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port to be set on `process.env.PORT` for use in the server.",
|
||||||
|
"default": 4200,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"browserTargetOptions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Additional options to pass into the browser build target.",
|
||||||
|
"default": {}
|
||||||
|
},
|
||||||
|
"serverTargetOptions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Additional options to pass into the server build target.",
|
||||||
|
"default": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["browserTarget", "serverTarget"]
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
runExecutor,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { combineAsyncIterables } from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
import * as chalk from 'chalk';
|
||||||
|
|
||||||
|
import { RspackExecutorSchema } from '../rspack/schema';
|
||||||
|
import { waitUntilServerIsListening } from './lib/wait-until-server-is-listening';
|
||||||
|
import { RspackSsrDevServerOptions, TargetOptions } from './schema';
|
||||||
|
|
||||||
|
export async function* ssrDevServerExecutor(
|
||||||
|
options: RspackSsrDevServerOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const browserTarget = parseTargetString(
|
||||||
|
options.browserTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
|
const serverTarget = parseTargetString(options.serverTarget, context);
|
||||||
|
const browserOptions = readTargetOptions<RspackExecutorSchema>(
|
||||||
|
browserTarget,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const serverOptions = readTargetOptions<RspackExecutorSchema>(
|
||||||
|
serverTarget,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
const runBrowser = await runExecutor<{
|
||||||
|
success: boolean;
|
||||||
|
baseUrl?: string;
|
||||||
|
options: TargetOptions;
|
||||||
|
}>(
|
||||||
|
browserTarget,
|
||||||
|
{ ...browserOptions, ...options.browserTargetOptions },
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const runServer = await runExecutor<{
|
||||||
|
success: boolean;
|
||||||
|
baseUrl?: string;
|
||||||
|
options: TargetOptions;
|
||||||
|
}>(
|
||||||
|
serverTarget,
|
||||||
|
{ ...serverOptions, ...options.serverTargetOptions },
|
||||||
|
context
|
||||||
|
);
|
||||||
|
let browserBuilt = false;
|
||||||
|
let nodeStarted = false;
|
||||||
|
const combined = combineAsyncIterables(runBrowser, runServer);
|
||||||
|
|
||||||
|
for await (const output of combined) {
|
||||||
|
if (!output.success) throw new Error('Could not build application');
|
||||||
|
if (output.options?.target === 'node') {
|
||||||
|
nodeStarted = true;
|
||||||
|
} else if (output.options?.target === 'web') {
|
||||||
|
browserBuilt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStarted && browserBuilt) {
|
||||||
|
await waitUntilServerIsListening(options.port);
|
||||||
|
console.log(
|
||||||
|
`[ ${chalk.green('ready')} ] on http://localhost:${options.port}`
|
||||||
|
);
|
||||||
|
yield {
|
||||||
|
...output,
|
||||||
|
baseUrl: `http://localhost:${options.port}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ssrDevServerExecutor;
|
||||||
105
packages/rspack/src/generators/application/application.ts
Normal file
105
packages/rspack/src/generators/application/application.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { ensurePackage, formatFiles, runTasksInSerial, Tree } from '@nx/devkit';
|
||||||
|
import { version as nxVersion } from 'nx/package.json';
|
||||||
|
import configurationGenerator from '../configuration/configuration';
|
||||||
|
import rspackInitGenerator from '../init/init';
|
||||||
|
import { normalizeOptions } from './lib/normalize-options';
|
||||||
|
import { ApplicationGeneratorSchema } from './schema';
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
tree: Tree,
|
||||||
|
_options: ApplicationGeneratorSchema
|
||||||
|
) {
|
||||||
|
const tasks = [];
|
||||||
|
const initTask = await rspackInitGenerator(tree, {
|
||||||
|
..._options,
|
||||||
|
// TODO: Crystalize the default rspack.config.js file.
|
||||||
|
// The default setup isn't crystalized so don't add plugin.
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
tasks.push(initTask);
|
||||||
|
|
||||||
|
const options = normalizeOptions(tree, _options);
|
||||||
|
|
||||||
|
options.style ??= 'css';
|
||||||
|
|
||||||
|
if (options.framework === 'nest') {
|
||||||
|
const { applicationGenerator: nestAppGenerator } = ensurePackage(
|
||||||
|
'@nx/nest',
|
||||||
|
nxVersion
|
||||||
|
);
|
||||||
|
const createAppTask = await nestAppGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
skipFormat: true,
|
||||||
|
tags: options.tags ?? '',
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const convertAppTask = await configurationGenerator(tree, {
|
||||||
|
project: options.name,
|
||||||
|
target: 'node',
|
||||||
|
newProject: false,
|
||||||
|
buildTarget: 'build',
|
||||||
|
framework: 'nest',
|
||||||
|
});
|
||||||
|
|
||||||
|
tasks.push(createAppTask, convertAppTask);
|
||||||
|
} else if (options.framework === 'web') {
|
||||||
|
const { applicationGenerator: webAppGenerator } = ensurePackage(
|
||||||
|
'@nx/web',
|
||||||
|
nxVersion
|
||||||
|
);
|
||||||
|
const createAppTask = await webAppGenerator(tree, {
|
||||||
|
bundler: 'webpack',
|
||||||
|
name: options.name,
|
||||||
|
style: options.style,
|
||||||
|
directory: options.directory,
|
||||||
|
tags: options.tags ?? '',
|
||||||
|
unitTestRunner: options.unitTestRunner,
|
||||||
|
e2eTestRunner: options.e2eTestRunner,
|
||||||
|
rootProject: options.rootProject,
|
||||||
|
skipFormat: true,
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
const convertAppTask = await configurationGenerator(tree, {
|
||||||
|
project: options.name,
|
||||||
|
target: 'web',
|
||||||
|
newProject: false,
|
||||||
|
buildTarget: 'build',
|
||||||
|
serveTarget: 'serve',
|
||||||
|
framework: 'web',
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
tasks.push(createAppTask, convertAppTask);
|
||||||
|
} else {
|
||||||
|
// default to react
|
||||||
|
const { applicationGenerator: reactAppGenerator } = ensurePackage(
|
||||||
|
'@nx/react',
|
||||||
|
nxVersion
|
||||||
|
);
|
||||||
|
const createAppTask = await reactAppGenerator(tree, {
|
||||||
|
bundler: 'webpack',
|
||||||
|
name: options.name,
|
||||||
|
style: options.style,
|
||||||
|
directory: options.directory,
|
||||||
|
tags: options.tags ?? '',
|
||||||
|
unitTestRunner: options.unitTestRunner,
|
||||||
|
e2eTestRunner: options.e2eTestRunner,
|
||||||
|
rootProject: options.rootProject,
|
||||||
|
skipFormat: true,
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
const convertAppTask = await configurationGenerator(tree, {
|
||||||
|
project: options.name,
|
||||||
|
target: 'web',
|
||||||
|
newProject: false,
|
||||||
|
buildTarget: 'build',
|
||||||
|
serveTarget: 'serve',
|
||||||
|
framework: 'react',
|
||||||
|
});
|
||||||
|
tasks.push(createAppTask, convertAppTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { Tree, workspaceRoot, writeJson } from '@nx/devkit';
|
||||||
|
import { relative } from 'path';
|
||||||
|
import { Framework } from '../../init/schema';
|
||||||
|
|
||||||
|
export function editTsConfig(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
framework: Framework,
|
||||||
|
relativePathToRootTsConfig: string
|
||||||
|
) {
|
||||||
|
// Nx 15.8 moved util to @nx/js, but it is in @nx/workspace in 15.7
|
||||||
|
let shared: any;
|
||||||
|
try {
|
||||||
|
shared = require('@nx/js/src/utils/typescript/create-ts-config');
|
||||||
|
} catch {
|
||||||
|
shared = require('@nx/workspace/src/utils/create-ts-config');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framework === 'react') {
|
||||||
|
const json = {
|
||||||
|
compilerOptions: {
|
||||||
|
jsx: 'react-jsx',
|
||||||
|
allowJs: false,
|
||||||
|
esModuleInterop: false,
|
||||||
|
allowSyntheticDefaultImports: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
files: [],
|
||||||
|
include: [],
|
||||||
|
references: [
|
||||||
|
{
|
||||||
|
path: './tsconfig.app.json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// inline tsconfig.base.json into the project
|
||||||
|
if (projectIsRootProjectInStandaloneWorkspace(projectRoot)) {
|
||||||
|
json.compileOnSave = false;
|
||||||
|
json.compilerOptions = {
|
||||||
|
...shared.tsConfigBaseOptions,
|
||||||
|
...json.compilerOptions,
|
||||||
|
};
|
||||||
|
json.exclude = ['node_modules', 'tmp'];
|
||||||
|
} else {
|
||||||
|
json.extends = relativePathToRootTsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJson(tree, `${projectRoot}/tsconfig.json`, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectIsRootProjectInStandaloneWorkspace(projectRoot: string) {
|
||||||
|
return relative(workspaceRoot, projectRoot).length === 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { Tree } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { normalizeOptions } from './normalize-options';
|
||||||
|
|
||||||
|
describe('normalizeOptions', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set { rootProject: true } when --rootProject=true is passed', () => {
|
||||||
|
expect(
|
||||||
|
normalizeOptions(tree, {
|
||||||
|
name: 'demo',
|
||||||
|
style: 'css',
|
||||||
|
rootProject: true,
|
||||||
|
}).rootProject
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set { rootProject: false } when --rootProject=undefined is passed', () => {
|
||||||
|
expect(
|
||||||
|
normalizeOptions(tree, {
|
||||||
|
name: 'demo',
|
||||||
|
style: 'css',
|
||||||
|
}).rootProject
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set { rootProject: false } when --rootProject=false is passed', () => {
|
||||||
|
expect(
|
||||||
|
normalizeOptions(tree, {
|
||||||
|
name: 'demo',
|
||||||
|
style: 'css',
|
||||||
|
rootProject: false,
|
||||||
|
}).rootProject
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set { rootProject: false } when --monorepo=true and --rootProject=true is passed', () => {
|
||||||
|
expect(
|
||||||
|
normalizeOptions(tree, {
|
||||||
|
name: 'demo',
|
||||||
|
style: 'css',
|
||||||
|
monorepo: true,
|
||||||
|
rootProject: true,
|
||||||
|
}).rootProject
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
extractLayoutDirectory,
|
||||||
|
getWorkspaceLayout,
|
||||||
|
names,
|
||||||
|
normalizePath,
|
||||||
|
Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { ApplicationGeneratorSchema, NormalizedSchema } from '../schema';
|
||||||
|
|
||||||
|
export function normalizeDirectory(options: ApplicationGeneratorSchema) {
|
||||||
|
const { projectDirectory } = extractLayoutDirectory(options.directory);
|
||||||
|
return projectDirectory
|
||||||
|
? `${names(projectDirectory).fileName}/${names(options.name).fileName}`
|
||||||
|
: names(options.name).fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeProjectName(options: ApplicationGeneratorSchema) {
|
||||||
|
return normalizeDirectory(options).replace(new RegExp('/', 'g'), '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeOptions(
|
||||||
|
host: Tree,
|
||||||
|
options: ApplicationGeneratorSchema
|
||||||
|
): NormalizedSchema {
|
||||||
|
// --monorepo takes precedence over --rootProject
|
||||||
|
// This won't be needed once we add --bundler=rspack to the @nx/react:app preset
|
||||||
|
const rootProject = !options.monorepo && options.rootProject;
|
||||||
|
const appDirectory = normalizeDirectory(options);
|
||||||
|
const appProjectName = normalizeProjectName(options);
|
||||||
|
const e2eProjectName = options.rootProject
|
||||||
|
? 'e2e'
|
||||||
|
: `${names(options.name).fileName}-e2e`;
|
||||||
|
|
||||||
|
const { layoutDirectory } = extractLayoutDirectory(options.directory);
|
||||||
|
const appsDir = layoutDirectory ?? getWorkspaceLayout(host).appsDir;
|
||||||
|
const appProjectRoot = rootProject
|
||||||
|
? '.'
|
||||||
|
: normalizePath(`${appsDir}/${appDirectory}`);
|
||||||
|
|
||||||
|
const normalized = {
|
||||||
|
...options,
|
||||||
|
rootProject,
|
||||||
|
name: names(options.name).fileName,
|
||||||
|
projectName: appProjectName,
|
||||||
|
appProjectRoot,
|
||||||
|
e2eProjectName,
|
||||||
|
fileName: 'app',
|
||||||
|
} as NormalizedSchema;
|
||||||
|
|
||||||
|
normalized.unitTestRunner ??= 'jest';
|
||||||
|
normalized.e2eTestRunner ??= 'cypress';
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
16
packages/rspack/src/generators/application/schema.d.ts
vendored
Normal file
16
packages/rspack/src/generators/application/schema.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export interface ApplicationGeneratorSchema {
|
||||||
|
name: string;
|
||||||
|
framework?: Framework;
|
||||||
|
style: 'css' | 'scss' | 'less' | 'styl';
|
||||||
|
unitTestRunner?: 'none' | 'jest';
|
||||||
|
e2eTestRunner?: 'none' | 'cypress';
|
||||||
|
directory?: string;
|
||||||
|
tags?: string;
|
||||||
|
rootProject?: boolean;
|
||||||
|
monorepo?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NormalizedSchema extends ApplicationGeneratorSchema {
|
||||||
|
appProjectRoot: string;
|
||||||
|
e2eProjectName: string;
|
||||||
|
}
|
||||||
98
packages/rspack/src/generators/application/schema.json
Normal file
98
packages/rspack/src/generators/application/schema.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Application",
|
||||||
|
"title": "Application generator for React + rspack",
|
||||||
|
"type": "object",
|
||||||
|
"description": "React + Rspack application generator.",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"command": "nx g app myapp --directory=myorg",
|
||||||
|
"description": "Generate `apps/myorg/myapp` and `apps/myorg/myapp-e2e`"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the application.",
|
||||||
|
"type": "string",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-prompt": "What name would you like to use for the application?",
|
||||||
|
"pattern": "^[a-zA-Z].*$",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework to use for the application.",
|
||||||
|
"x-prompt": "What framework do you want to use when generating this application?",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": "react"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"description": "The file extension to be used for style files.",
|
||||||
|
"type": "string",
|
||||||
|
"default": "css",
|
||||||
|
"alias": "s",
|
||||||
|
"x-prompt": {
|
||||||
|
"message": "Which stylesheet format would you like to use?",
|
||||||
|
"type": "list",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "css",
|
||||||
|
"label": "CSS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "scss",
|
||||||
|
"label": "SASS(.scss) [ http://sass-lang.com ]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "styl",
|
||||||
|
"label": "Stylus(.styl) [ http://stylus-lang.com ]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "less",
|
||||||
|
"label": "LESS [ http://lesscss.org ]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "none",
|
||||||
|
"label": "None"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unitTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The unit test runner to use.",
|
||||||
|
"enum": ["none", "jest"],
|
||||||
|
"default": "jest"
|
||||||
|
},
|
||||||
|
"e2eTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The e2e test runner to use.",
|
||||||
|
"enum": ["none", "cypress"],
|
||||||
|
"default": "cypress"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The directory to nest the app under."
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Add tags to the application (used for linting).",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"monorepo": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates an integrated monorepo.",
|
||||||
|
"aliases": ["integrated"]
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
153
packages/rspack/src/generators/configuration/configuration.ts
Normal file
153
packages/rspack/src/generators/configuration/configuration.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
joinPathFragments,
|
||||||
|
offsetFromRoot,
|
||||||
|
readProjectConfiguration,
|
||||||
|
Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
addOrChangeBuildTarget,
|
||||||
|
addOrChangeServeTarget,
|
||||||
|
deleteWebpackConfig,
|
||||||
|
determineFrameworkAndTarget,
|
||||||
|
findExistingTargetsInProject,
|
||||||
|
handleUnknownExecutors,
|
||||||
|
handleUnsupportedUserProvidedTargets,
|
||||||
|
TargetFlags,
|
||||||
|
UserProvidedTargetName,
|
||||||
|
writeRspackConfigFile,
|
||||||
|
} from '../../utils/generator-utils';
|
||||||
|
import { editTsConfig } from '../application/lib/create-ts-config';
|
||||||
|
import rspackInitGenerator from '../init/init';
|
||||||
|
import { ConfigurationSchema } from './schema';
|
||||||
|
|
||||||
|
export async function configurationGenerator(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema
|
||||||
|
) {
|
||||||
|
const task = await rspackInitGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
// TODO: Crystalize the default rspack.config.js file.
|
||||||
|
// The default setup isn't crystalized so don't add plugin.
|
||||||
|
addPlugin: false,
|
||||||
|
});
|
||||||
|
const { targets, root, projectType } = readProjectConfiguration(
|
||||||
|
tree,
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
const { target, framework } = determineFrameworkAndTarget(
|
||||||
|
tree,
|
||||||
|
options,
|
||||||
|
root,
|
||||||
|
targets
|
||||||
|
);
|
||||||
|
options.framework = framework;
|
||||||
|
options.target = target;
|
||||||
|
|
||||||
|
let foundStylePreprocessorOptions: { includePaths?: string[] } | undefined;
|
||||||
|
|
||||||
|
let buildTargetName = 'build';
|
||||||
|
let serveTargetName = 'serve';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is for when we are converting an existing project
|
||||||
|
* to use the vite executors.
|
||||||
|
*/
|
||||||
|
let projectAlreadyHasRspackTargets: TargetFlags = {};
|
||||||
|
|
||||||
|
if (!options.newProject) {
|
||||||
|
const userProvidedTargetName: UserProvidedTargetName = {
|
||||||
|
build: options.buildTarget,
|
||||||
|
serve: options.serveTarget,
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
validFoundTargetName,
|
||||||
|
projectContainsUnsupportedExecutor,
|
||||||
|
userProvidedTargetIsUnsupported,
|
||||||
|
alreadyHasNxRspackTargets,
|
||||||
|
} = findExistingTargetsInProject(targets, userProvidedTargetName);
|
||||||
|
projectAlreadyHasRspackTargets = alreadyHasNxRspackTargets;
|
||||||
|
|
||||||
|
if (
|
||||||
|
alreadyHasNxRspackTargets.build &&
|
||||||
|
(alreadyHasNxRspackTargets.serve ||
|
||||||
|
projectType === 'library' ||
|
||||||
|
options.framework === 'nest')
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`The project ${options.project} is already configured to use the @nx/rspack executors.
|
||||||
|
Please try a different project, or remove the existing targets
|
||||||
|
and re-run this generator to reset the existing Rspack Configuration.
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validFoundTargetName.build && projectContainsUnsupportedExecutor) {
|
||||||
|
throw new Error(
|
||||||
|
`The project ${options.project} cannot be converted to use the @nx/rspack executors.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!projectContainsUnsupportedExecutor &&
|
||||||
|
!validFoundTargetName.build &&
|
||||||
|
!validFoundTargetName.serve
|
||||||
|
) {
|
||||||
|
await handleUnknownExecutors(options.project);
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleUnsupportedUserProvidedTargets(
|
||||||
|
userProvidedTargetIsUnsupported,
|
||||||
|
userProvidedTargetName,
|
||||||
|
validFoundTargetName,
|
||||||
|
options.framework
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once the user is at this stage, then they can go ahead and convert.
|
||||||
|
*/
|
||||||
|
|
||||||
|
buildTargetName = validFoundTargetName.build ?? buildTargetName;
|
||||||
|
serveTargetName = validFoundTargetName.serve ?? serveTargetName;
|
||||||
|
|
||||||
|
// Not needed atm
|
||||||
|
// if (projectType === 'application' && options.target !== 'node') {
|
||||||
|
// moveAndEditIndexHtml(tree, options, buildTargetName);
|
||||||
|
// }
|
||||||
|
|
||||||
|
foundStylePreprocessorOptions =
|
||||||
|
targets?.[buildTargetName]?.options?.stylePreprocessorOptions;
|
||||||
|
|
||||||
|
deleteWebpackConfig(
|
||||||
|
tree,
|
||||||
|
root,
|
||||||
|
targets?.[buildTargetName]?.options?.webpackConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
editTsConfig(
|
||||||
|
tree,
|
||||||
|
root,
|
||||||
|
options.framework,
|
||||||
|
joinPathFragments(offsetFromRoot(root), 'tsconfig.base.json')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectAlreadyHasRspackTargets.build) {
|
||||||
|
addOrChangeBuildTarget(tree, options, buildTargetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(options.framework !== 'none' || options.devServer) &&
|
||||||
|
options.framework !== 'nest' &&
|
||||||
|
!projectAlreadyHasRspackTargets.serve
|
||||||
|
) {
|
||||||
|
addOrChangeServeTarget(tree, options, serveTargetName);
|
||||||
|
}
|
||||||
|
writeRspackConfigFile(tree, options, foundStylePreprocessorOptions);
|
||||||
|
await formatFiles(tree);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default configurationGenerator;
|
||||||
12
packages/rspack/src/generators/configuration/schema.d.ts
vendored
Normal file
12
packages/rspack/src/generators/configuration/schema.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { InitGeneratorSchema } from '../init/schema';
|
||||||
|
|
||||||
|
export interface ConfigurationSchema extends InitGeneratorSchema {
|
||||||
|
project: string;
|
||||||
|
main?: string;
|
||||||
|
tsConfig?: string;
|
||||||
|
target?: 'node' | 'web';
|
||||||
|
skipValidation?: boolean;
|
||||||
|
newProject?: boolean;
|
||||||
|
buildTarget?: string;
|
||||||
|
serveTarget?: string;
|
||||||
|
}
|
||||||
73
packages/rspack/src/generators/configuration/schema.json
Normal file
73
packages/rspack/src/generators/configuration/schema.json
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Rspack",
|
||||||
|
"title": "Nx Rspack Configuration Generator",
|
||||||
|
"description": "Rspack 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 rspack for?",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework used by the project.",
|
||||||
|
"x-prompt": "What framework is the project you want to convert using?",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.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 rspack config option.",
|
||||||
|
"enum": ["node", "web"],
|
||||||
|
"default": "web"
|
||||||
|
},
|
||||||
|
"devServer": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add a serve target to run a local rspack dev-server",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The style solution to use.",
|
||||||
|
"enum": ["none", "css", "scss", "less"]
|
||||||
|
},
|
||||||
|
"newProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Is this a new project?",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"buildTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The build target of the project to be transformed to use the @nx/vite:build executor."
|
||||||
|
},
|
||||||
|
"serveTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The serve target of the project to be transformed to use the @nx/vite:dev-server and @nx/vite:preview-server executors."
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["project"]
|
||||||
|
}
|
||||||
113
packages/rspack/src/generators/init/init.ts
Normal file
113
packages/rspack/src/generators/init/init.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
convertNxGenerator,
|
||||||
|
createProjectGraphAsync,
|
||||||
|
GeneratorCallback,
|
||||||
|
readNxJson,
|
||||||
|
runTasksInSerial,
|
||||||
|
Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
|
||||||
|
import { initGenerator } from '@nx/js';
|
||||||
|
import { createNodesV2 } from '../../../plugin';
|
||||||
|
import {
|
||||||
|
lessLoaderVersion,
|
||||||
|
reactRefreshVersion,
|
||||||
|
rspackCoreVersion,
|
||||||
|
rspackDevServerVersion,
|
||||||
|
rspackPluginMinifyVersion,
|
||||||
|
rspackPluginReactRefreshVersion,
|
||||||
|
} from '../../utils/versions';
|
||||||
|
import { InitGeneratorSchema } from './schema';
|
||||||
|
|
||||||
|
export async function rspackInitGenerator(
|
||||||
|
tree: Tree,
|
||||||
|
schema: InitGeneratorSchema
|
||||||
|
) {
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
const addPluginDefault =
|
||||||
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||||
|
nxJson.useInferencePlugins !== false;
|
||||||
|
schema.addPlugin ??= addPluginDefault;
|
||||||
|
|
||||||
|
if (schema.addPlugin) {
|
||||||
|
await addPlugin(
|
||||||
|
tree,
|
||||||
|
await createProjectGraphAsync(),
|
||||||
|
'@nx/rspack/plugin',
|
||||||
|
createNodesV2,
|
||||||
|
{
|
||||||
|
buildTargetName: [
|
||||||
|
'build',
|
||||||
|
'rspack:build',
|
||||||
|
'build:rspack',
|
||||||
|
'rspack-build',
|
||||||
|
'build-rspack',
|
||||||
|
],
|
||||||
|
serveTargetName: [
|
||||||
|
'serve',
|
||||||
|
'rspack:serve',
|
||||||
|
'serve:rspack',
|
||||||
|
'rspack-serve',
|
||||||
|
'serve-rspack',
|
||||||
|
],
|
||||||
|
previewTargetName: [
|
||||||
|
'preview',
|
||||||
|
'rspack:preview',
|
||||||
|
'preview:rspack',
|
||||||
|
'rspack-preview',
|
||||||
|
'preview-rspack',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
schema.updatePackageScripts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsInitTask = await initGenerator(tree, {
|
||||||
|
...schema,
|
||||||
|
tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tasks.push(jsInitTask);
|
||||||
|
|
||||||
|
const devDependencies = {
|
||||||
|
'@rspack/core': rspackCoreVersion,
|
||||||
|
'@rspack/cli': rspackCoreVersion,
|
||||||
|
'@rspack/plugin-minify': rspackPluginMinifyVersion,
|
||||||
|
'@rspack/plugin-react-refresh': rspackPluginReactRefreshVersion,
|
||||||
|
'react-refresh': reactRefreshVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const version = require('../../../package.json').version;
|
||||||
|
if (version !== '0.0.1') {
|
||||||
|
// Ignored for local dev / e2e tests.
|
||||||
|
devDependencies['@nx/rspack'] = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.style === 'less') {
|
||||||
|
devDependencies['less-loader'] = lessLoaderVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.framework !== 'none' || schema.devServer) {
|
||||||
|
devDependencies['@rspack/dev-server'] = rspackDevServerVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
const installTask = addDependenciesToPackageJson(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
devDependencies,
|
||||||
|
undefined,
|
||||||
|
schema.keepExistingVersions
|
||||||
|
);
|
||||||
|
tasks.push(installTask);
|
||||||
|
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default rspackInitGenerator;
|
||||||
|
|
||||||
|
export const rspackInitSchematic = convertNxGenerator(rspackInitGenerator);
|
||||||
11
packages/rspack/src/generators/init/schema.d.ts
vendored
Normal file
11
packages/rspack/src/generators/init/schema.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export type Framework = 'none' | 'react' | 'web' | 'nest';
|
||||||
|
|
||||||
|
export interface InitGeneratorSchema {
|
||||||
|
addPlugin?: boolean;
|
||||||
|
devServer?: boolean;
|
||||||
|
framework?: Framework;
|
||||||
|
keepExistingVersions?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
|
style?: 'none' | 'css' | 'scss' | 'less' | 'styl';
|
||||||
|
updatePackageScripts?: boolean;
|
||||||
|
}
|
||||||
31
packages/rspack/src/generators/init/schema.json
Normal file
31
packages/rspack/src/generators/init/schema.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Init",
|
||||||
|
"title": "Nx Rspack Init Generator",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Rspack init generator.",
|
||||||
|
"properties": {
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The UI framework used by the project.",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"]
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The style solution to use.",
|
||||||
|
"enum": ["none", "css", "scss", "less", "styl"]
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"keepExistingVersions": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"description": "Keep existing dependencies versions",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
36
packages/rspack/src/generators/preset/preset.ts
Normal file
36
packages/rspack/src/generators/preset/preset.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Tree, updateJson } from '@nx/devkit';
|
||||||
|
|
||||||
|
import applicationGenerator from '../application/application';
|
||||||
|
import { PresetGeneratorSchema } from './schema';
|
||||||
|
|
||||||
|
export default async function (tree: Tree, options: PresetGeneratorSchema) {
|
||||||
|
const appTask = applicationGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
// Since `--style` is not passed down to custom preset, we're using individual flags for now.
|
||||||
|
style: options.sass
|
||||||
|
? 'scss'
|
||||||
|
: options.less
|
||||||
|
? 'less'
|
||||||
|
: options.stylus
|
||||||
|
? 'styl'
|
||||||
|
: 'css',
|
||||||
|
});
|
||||||
|
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.scripts ??= {};
|
||||||
|
json.scripts.build ??= 'npx nx build';
|
||||||
|
json.scripts.start ??= 'npx nx serve';
|
||||||
|
json.scripts.lint ??= 'npx nx lint';
|
||||||
|
json.scripts.test ??= 'npx nx test';
|
||||||
|
json.scripts.e2e ??= 'npx nx e2e e2e';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.rootProject) {
|
||||||
|
// Remove these folders so projects will be generated at the root.
|
||||||
|
tree.delete('apps');
|
||||||
|
tree.delete('libs');
|
||||||
|
}
|
||||||
|
|
||||||
|
return appTask;
|
||||||
|
}
|
||||||
18
packages/rspack/src/generators/preset/schema.d.ts
vendored
Normal file
18
packages/rspack/src/generators/preset/schema.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export interface PresetGeneratorSchema {
|
||||||
|
name: string;
|
||||||
|
framework?: Framework;
|
||||||
|
less?: boolean;
|
||||||
|
sass?: boolean;
|
||||||
|
stylus?: boolean;
|
||||||
|
unitTestRunner?: 'none' | 'jest';
|
||||||
|
e2eTestRunner?: 'none' | 'cypress';
|
||||||
|
directory?: string;
|
||||||
|
tags?: string;
|
||||||
|
rootProject?: boolean;
|
||||||
|
monorepo?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NormalizedSchema extends PresetGeneratorSchema {
|
||||||
|
appProjectRoot: string;
|
||||||
|
e2eProjectName: string;
|
||||||
|
}
|
||||||
71
packages/rspack/src/generators/preset/schema.json
Normal file
71
packages/rspack/src/generators/preset/schema.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "Preset",
|
||||||
|
"title": "Standalone React and rspack preset",
|
||||||
|
"description": "React + Rspack preset generator.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"framework": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The framework to use for the application.",
|
||||||
|
"enum": ["none", "react", "web", "nest"],
|
||||||
|
"alias": ["uiFramework"],
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": "react"
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use less for styling."
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use sass for styling."
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use stylus for styling."
|
||||||
|
},
|
||||||
|
"unitTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The unit test runner to use.",
|
||||||
|
"enum": ["none", "jest"],
|
||||||
|
"default": "jest"
|
||||||
|
},
|
||||||
|
"e2eTestRunner": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The e2e test runner to use.",
|
||||||
|
"enum": ["none", "cypress"],
|
||||||
|
"default": "cypress"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The directory to nest the app under."
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Add tags to the project (used for linting).",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"monorepo": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates an integrated monorepo.",
|
||||||
|
"default": false,
|
||||||
|
"aliases": ["integrated"]
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
6
packages/rspack/src/index.ts
Normal file
6
packages/rspack/src/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './generators/configuration/configuration';
|
||||||
|
export * from './generators/init/init';
|
||||||
|
export * from './utils/config';
|
||||||
|
export * from './utils/with-nx';
|
||||||
|
export * from './utils/with-react';
|
||||||
|
export * from './utils/with-web';
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { readJson, Tree, updateJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import replacePackage from './update-16-0-0-add-nx-packages';
|
||||||
|
|
||||||
|
describe('update-16-0-0-add-nx-packages', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.devDependencies['@nrwl/rspack'] = '16.0.0';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove the dependency on @nrwl/rspack', async () => {
|
||||||
|
await replacePackage(tree);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
readJson(tree, 'package.json').dependencies['@nrwl/rspack']
|
||||||
|
).not.toBeDefined();
|
||||||
|
expect(
|
||||||
|
readJson(tree, 'package.json').devDependencies['@nrwl/rspack']
|
||||||
|
).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a dependency on @nx/rspack', async () => {
|
||||||
|
await replacePackage(tree);
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
const newDependencyVersion =
|
||||||
|
packageJson.devDependencies['@nx/rspack'] ??
|
||||||
|
packageJson.dependencies['@nx/rspack'];
|
||||||
|
|
||||||
|
expect(newDependencyVersion).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { formatFiles, Tree } from '@nx/devkit';
|
||||||
|
import { replaceNrwlPackageWithNxPackage } from '@nx/devkit/src/utils/replace-package';
|
||||||
|
|
||||||
|
export default async function replacePackage(tree: Tree): Promise<void> {
|
||||||
|
await replaceNrwlPackageWithNxPackage(tree, '@nrwl/rspack', '@nx/rspack');
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
87
packages/rspack/src/plugins/generate-package-json-plugin.ts
Normal file
87
packages/rspack/src/plugins/generate-package-json-plugin.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
detectPackageManager,
|
||||||
|
serializeJson,
|
||||||
|
type ProjectGraph,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
HelperDependency,
|
||||||
|
createLockFile,
|
||||||
|
createPackageJson,
|
||||||
|
getHelperDependenciesFromProjectGraph,
|
||||||
|
getLockFileName,
|
||||||
|
readTsConfig,
|
||||||
|
} from '@nx/js';
|
||||||
|
import { type Compiler, type RspackPluginInstance } from '@rspack/core';
|
||||||
|
import { RawSource } from 'webpack-sources';
|
||||||
|
|
||||||
|
const pluginName = 'GeneratePackageJsonPlugin';
|
||||||
|
|
||||||
|
export class GeneratePackageJsonPlugin implements RspackPluginInstance {
|
||||||
|
private readonly projectGraph: ProjectGraph;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly options: { tsConfig: string; outputFileName: string },
|
||||||
|
private readonly context: ExecutorContext
|
||||||
|
) {
|
||||||
|
this.projectGraph = context.projectGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(compiler: Compiler): void {
|
||||||
|
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
||||||
|
compilation.hooks.processAssets.tap(
|
||||||
|
{
|
||||||
|
name: pluginName,
|
||||||
|
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
const helperDependencies = getHelperDependenciesFromProjectGraph(
|
||||||
|
this.context.root,
|
||||||
|
this.context.projectName,
|
||||||
|
this.projectGraph
|
||||||
|
);
|
||||||
|
|
||||||
|
const importHelpers = !!readTsConfig(this.options.tsConfig).options
|
||||||
|
.importHelpers;
|
||||||
|
const shouldAddHelperDependency =
|
||||||
|
importHelpers &&
|
||||||
|
helperDependencies.every(
|
||||||
|
(dep) => dep.target !== HelperDependency.tsc
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldAddHelperDependency) {
|
||||||
|
helperDependencies.push({
|
||||||
|
type: 'static',
|
||||||
|
source: this.context.projectName,
|
||||||
|
target: HelperDependency.tsc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJson = createPackageJson(
|
||||||
|
this.context.projectName,
|
||||||
|
this.projectGraph,
|
||||||
|
{
|
||||||
|
target: this.context.targetName,
|
||||||
|
root: this.context.root,
|
||||||
|
isProduction: true,
|
||||||
|
helperDependencies: helperDependencies.map((dep) => dep.target),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
packageJson.main = packageJson.main ?? this.options.outputFileName;
|
||||||
|
|
||||||
|
compilation.emitAsset(
|
||||||
|
'package.json',
|
||||||
|
new RawSource(serializeJson(packageJson))
|
||||||
|
);
|
||||||
|
const packageManager = detectPackageManager(this.context.root);
|
||||||
|
compilation.emitAsset(
|
||||||
|
getLockFileName(packageManager),
|
||||||
|
new RawSource(
|
||||||
|
createLockFile(packageJson, this.projectGraph, packageManager)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
231
packages/rspack/src/plugins/plugin.ts
Normal file
231
packages/rspack/src/plugins/plugin.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import {
|
||||||
|
CreateDependencies,
|
||||||
|
CreateNodesContext,
|
||||||
|
createNodesFromFiles,
|
||||||
|
CreateNodesV2,
|
||||||
|
detectPackageManager,
|
||||||
|
ProjectConfiguration,
|
||||||
|
readJsonFile,
|
||||||
|
workspaceRoot,
|
||||||
|
writeJsonFile,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||||
|
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||||
|
import { getLockFileName, getRootTsConfigPath } from '@nx/js';
|
||||||
|
import { existsSync, readdirSync } from 'fs';
|
||||||
|
import { hashObject } from 'nx/src/hasher/file-hasher';
|
||||||
|
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||||
|
import { dirname, isAbsolute, join, relative, resolve } from 'path';
|
||||||
|
import { readRspackOptions } from '../utils/read-rspack-options';
|
||||||
|
import { resolveUserDefinedRspackConfig } from '../utils/resolve-user-defined-rspack-config';
|
||||||
|
|
||||||
|
export interface RspackPluginOptions {
|
||||||
|
buildTargetName?: string;
|
||||||
|
serveTargetName?: string;
|
||||||
|
serveStaticTargetName?: string;
|
||||||
|
previewTargetName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RspackTargets = Pick<ProjectConfiguration, 'targets' | 'metadata'>;
|
||||||
|
|
||||||
|
function readTargetsCache(cachePath: string): Record<string, RspackTargets> {
|
||||||
|
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeTargetsToCache(
|
||||||
|
cachePath,
|
||||||
|
results?: Record<string, RspackTargets>
|
||||||
|
) {
|
||||||
|
writeJsonFile(cachePath, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createDependencies: CreateDependencies = () => {
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const rspackConfigGlob = '**/rspack.config.{js,ts,mjs,mts,cjs,cts}';
|
||||||
|
|
||||||
|
export const createNodesV2: CreateNodesV2<RspackPluginOptions> = [
|
||||||
|
rspackConfigGlob,
|
||||||
|
async (configFilePaths, options, context) => {
|
||||||
|
const optionsHash = hashObject(options);
|
||||||
|
const cachePath = join(
|
||||||
|
workspaceDataDirectory,
|
||||||
|
`rspack-${optionsHash}.hash`
|
||||||
|
);
|
||||||
|
const targetsCache = readTargetsCache(cachePath);
|
||||||
|
try {
|
||||||
|
return await createNodesFromFiles(
|
||||||
|
(configFile, options, context) =>
|
||||||
|
createNodesInternal(configFile, options, context, targetsCache),
|
||||||
|
configFilePaths,
|
||||||
|
options,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
writeTargetsToCache(cachePath, targetsCache);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function createNodesInternal(
|
||||||
|
configFilePath: string,
|
||||||
|
options: RspackPluginOptions,
|
||||||
|
context: CreateNodesContext,
|
||||||
|
targetsCache: Record<string, RspackTargets>
|
||||||
|
) {
|
||||||
|
const projectRoot = dirname(configFilePath);
|
||||||
|
// Do not create a project if package.json and project.json isn't there.
|
||||||
|
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
|
||||||
|
if (
|
||||||
|
!siblingFiles.includes('package.json') &&
|
||||||
|
!siblingFiles.includes('project.json')
|
||||||
|
) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedOptions = normalizeOptions(options);
|
||||||
|
|
||||||
|
// We do not want to alter how the hash is calculated, so appending the config file path to the hash
|
||||||
|
// to prevent vite/vitest files overwriting the target cache created by the other
|
||||||
|
const hash =
|
||||||
|
(await calculateHashForCreateNodes(
|
||||||
|
projectRoot,
|
||||||
|
normalizedOptions,
|
||||||
|
context,
|
||||||
|
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||||
|
)) + configFilePath;
|
||||||
|
|
||||||
|
targetsCache[hash] ??= await createRspackTargets(
|
||||||
|
configFilePath,
|
||||||
|
projectRoot,
|
||||||
|
normalizedOptions,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
const { targets, metadata } = targetsCache[hash];
|
||||||
|
|
||||||
|
return {
|
||||||
|
projects: {
|
||||||
|
[projectRoot]: {
|
||||||
|
root: projectRoot,
|
||||||
|
targets,
|
||||||
|
metadata,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRspackTargets(
|
||||||
|
configFilePath: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: RspackPluginOptions,
|
||||||
|
context: CreateNodesContext
|
||||||
|
): Promise<RspackTargets> {
|
||||||
|
const namedInputs = getNamedInputs(projectRoot, context);
|
||||||
|
|
||||||
|
const rspackConfig = resolveUserDefinedRspackConfig(
|
||||||
|
join(context.workspaceRoot, configFilePath),
|
||||||
|
getRootTsConfigPath(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const rspackOptions = await readRspackOptions(rspackConfig);
|
||||||
|
|
||||||
|
const outputPath = normalizeOutputPath(
|
||||||
|
rspackOptions.output?.path,
|
||||||
|
projectRoot
|
||||||
|
);
|
||||||
|
|
||||||
|
const targets = {};
|
||||||
|
|
||||||
|
targets[options.buildTargetName] = {
|
||||||
|
command: `rspack build`,
|
||||||
|
options: { cwd: projectRoot, args: ['--node-env=production'] },
|
||||||
|
cache: true,
|
||||||
|
dependsOn: [`^${options.buildTargetName}`],
|
||||||
|
inputs:
|
||||||
|
'production' in namedInputs
|
||||||
|
? [
|
||||||
|
'production',
|
||||||
|
'^production',
|
||||||
|
{
|
||||||
|
externalDependencies: ['@rspack/cli'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
'default',
|
||||||
|
'^default',
|
||||||
|
{
|
||||||
|
externalDependencies: ['@rspack/cli'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outputs: [outputPath],
|
||||||
|
};
|
||||||
|
|
||||||
|
targets[options.serveTargetName] = {
|
||||||
|
command: `rspack serve`,
|
||||||
|
options: {
|
||||||
|
cwd: projectRoot,
|
||||||
|
args: ['--node-env=development'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
targets[options.previewTargetName] = {
|
||||||
|
command: `rspack serve`,
|
||||||
|
options: {
|
||||||
|
cwd: projectRoot,
|
||||||
|
args: ['--node-env=production'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
targets[options.serveStaticTargetName] = {
|
||||||
|
executor: '@nx/web:file-server',
|
||||||
|
options: {
|
||||||
|
buildTarget: options.buildTargetName,
|
||||||
|
spa: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return { targets, metadata: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOptions(options: RspackPluginOptions): RspackPluginOptions {
|
||||||
|
options ??= {};
|
||||||
|
options.buildTargetName ??= 'build';
|
||||||
|
options.serveTargetName ??= 'serve';
|
||||||
|
options.previewTargetName ??= 'preview';
|
||||||
|
options.serveStaticTargetName ??= 'serve-static';
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOutputPath(
|
||||||
|
outputPath: string | undefined,
|
||||||
|
projectRoot: string
|
||||||
|
): string | undefined {
|
||||||
|
if (!outputPath) {
|
||||||
|
// If outputPath is undefined, use rspack's default `dist` directory.
|
||||||
|
if (projectRoot === '.') {
|
||||||
|
return `{projectRoot}/dist`;
|
||||||
|
} else {
|
||||||
|
return `{workspaceRoot}/dist/{projectRoot}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isAbsolute(outputPath)) {
|
||||||
|
/**
|
||||||
|
* If outputPath is absolute, we need to resolve it relative to the workspaceRoot first.
|
||||||
|
* After that, we can use the relative path to the workspaceRoot token {workspaceRoot} to generate the output path.
|
||||||
|
*/
|
||||||
|
return `{workspaceRoot}/${relative(
|
||||||
|
workspaceRoot,
|
||||||
|
resolve(workspaceRoot, outputPath)
|
||||||
|
)}`;
|
||||||
|
} else {
|
||||||
|
if (outputPath.startsWith('..')) {
|
||||||
|
return join('{workspaceRoot}', join(projectRoot, outputPath));
|
||||||
|
} else {
|
||||||
|
return join('{projectRoot}', outputPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
packages/rspack/src/utils/config.ts
Normal file
49
packages/rspack/src/utils/config.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import type { ExecutorContext } from '@nx/devkit';
|
||||||
|
import type { Configuration } from '@rspack/core';
|
||||||
|
|
||||||
|
import { SharedConfigContext } from './model';
|
||||||
|
|
||||||
|
export const nxRspackComposablePlugin = 'nxRspackComposablePlugin';
|
||||||
|
|
||||||
|
export function isNxRspackComposablePlugin(
|
||||||
|
a: unknown
|
||||||
|
): a is AsyncNxComposableRspackPlugin {
|
||||||
|
return a?.[nxRspackComposablePlugin] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NxRspackExecutionContext {
|
||||||
|
options: unknown;
|
||||||
|
context: ExecutorContext;
|
||||||
|
configuration?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NxComposableRspackPlugin {
|
||||||
|
(config: Configuration, ctx: NxRspackExecutionContext): Configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AsyncNxComposableRspackPlugin {
|
||||||
|
(config: Configuration, ctx: NxRspackExecutionContext):
|
||||||
|
| Configuration
|
||||||
|
| Promise<Configuration>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function composePlugins(...plugins: any[]) {
|
||||||
|
return Object.defineProperty(
|
||||||
|
async function combined(
|
||||||
|
config: Configuration,
|
||||||
|
ctx: SharedConfigContext
|
||||||
|
): Promise<Configuration> {
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
const fn = await plugin;
|
||||||
|
config = await fn(config, ctx);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
nxRspackComposablePlugin,
|
||||||
|
{
|
||||||
|
value: true,
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
51
packages/rspack/src/utils/create-compiler.ts
Normal file
51
packages/rspack/src/utils/create-compiler.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { ExecutorContext } from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
Compiler,
|
||||||
|
type Configuration,
|
||||||
|
MultiCompiler,
|
||||||
|
rspack,
|
||||||
|
} from '@rspack/core';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { RspackExecutorSchema } from '../executors/rspack/schema';
|
||||||
|
import { resolveUserDefinedRspackConfig } from './resolve-user-defined-rspack-config';
|
||||||
|
|
||||||
|
export async function createCompiler(
|
||||||
|
options: RspackExecutorSchema & {
|
||||||
|
devServer?: any;
|
||||||
|
},
|
||||||
|
context: ExecutorContext
|
||||||
|
): Promise<Compiler | MultiCompiler> {
|
||||||
|
const pathToConfig = path.join(context.root, options.rspackConfig);
|
||||||
|
let userDefinedConfig: any = {};
|
||||||
|
if (options.tsConfig) {
|
||||||
|
userDefinedConfig = resolveUserDefinedRspackConfig(
|
||||||
|
pathToConfig,
|
||||||
|
options.tsConfig
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
userDefinedConfig = await import(pathToConfig).then((x) => x.default || x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof userDefinedConfig.then === 'function') {
|
||||||
|
userDefinedConfig = await userDefinedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
let config: Configuration = {};
|
||||||
|
if (typeof userDefinedConfig === 'function') {
|
||||||
|
config = await userDefinedConfig(
|
||||||
|
{ devServer: options.devServer },
|
||||||
|
{ options, context }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
config = userDefinedConfig;
|
||||||
|
config.devServer ??= options.devServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rspack(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMultiCompiler(
|
||||||
|
compiler: Compiler | MultiCompiler
|
||||||
|
): compiler is MultiCompiler {
|
||||||
|
return 'compilers' in compiler;
|
||||||
|
}
|
||||||
619
packages/rspack/src/utils/generator-utils.ts
Normal file
619
packages/rspack/src/utils/generator-utils.ts
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
import {
|
||||||
|
joinPathFragments,
|
||||||
|
logger,
|
||||||
|
readProjectConfiguration,
|
||||||
|
TargetConfiguration,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||||
|
import { RspackExecutorSchema } from '../executors/rspack/schema';
|
||||||
|
import { ConfigurationSchema } from '../generators/configuration/schema';
|
||||||
|
import { Framework } from '../generators/init/schema';
|
||||||
|
|
||||||
|
export type Target = 'build' | 'serve';
|
||||||
|
export type TargetFlags = Partial<Record<Target, boolean>>;
|
||||||
|
export type UserProvidedTargetName = Partial<Record<Target, string>>;
|
||||||
|
export type ValidFoundTargetName = Partial<Record<Target, string>>;
|
||||||
|
|
||||||
|
export function findExistingTargetsInProject(
|
||||||
|
targets: {
|
||||||
|
[targetName: string]: TargetConfiguration;
|
||||||
|
},
|
||||||
|
userProvidedTargets?: UserProvidedTargetName
|
||||||
|
): {
|
||||||
|
validFoundTargetName: ValidFoundTargetName;
|
||||||
|
projectContainsUnsupportedExecutor: boolean;
|
||||||
|
userProvidedTargetIsUnsupported: TargetFlags;
|
||||||
|
alreadyHasNxRspackTargets: TargetFlags;
|
||||||
|
} {
|
||||||
|
const output: ReturnType<typeof findExistingTargetsInProject> = {
|
||||||
|
validFoundTargetName: {},
|
||||||
|
projectContainsUnsupportedExecutor: false,
|
||||||
|
userProvidedTargetIsUnsupported: {},
|
||||||
|
alreadyHasNxRspackTargets: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportedExecutors = {
|
||||||
|
build: [
|
||||||
|
'@nxext/vite:build',
|
||||||
|
'@nrwl/webpack:webpack',
|
||||||
|
'@nrwl/rollup:rollup',
|
||||||
|
'@nrwl/web:rollup',
|
||||||
|
'@nrwl/vite:build',
|
||||||
|
'@nx/webpack:webpack',
|
||||||
|
'@nx/rollup:rollup',
|
||||||
|
'@nx/web:rollup',
|
||||||
|
'@nx/vite:build',
|
||||||
|
],
|
||||||
|
serve: [
|
||||||
|
'@nxext/vite:dev',
|
||||||
|
'@nrwl/webpack:dev-server',
|
||||||
|
'@nrwl/vite:dev-server',
|
||||||
|
'@nx/webpack:dev-server',
|
||||||
|
'@nx/vite:dev-server',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsupportedExecutors = [
|
||||||
|
'@nx/js:babel',
|
||||||
|
'@nx/js:node',
|
||||||
|
'@nx/js:swc',
|
||||||
|
'@nx/react-native:run-ios',
|
||||||
|
'@nx/react-native:start',
|
||||||
|
'@nx/react-native:run-android',
|
||||||
|
'@nx/react-native:bundle',
|
||||||
|
'@nx/react-native:build-android',
|
||||||
|
'@nx/react-native:bundle',
|
||||||
|
'@nx/next:build',
|
||||||
|
'@nx/next:server',
|
||||||
|
'@nx/js:tsc',
|
||||||
|
'@nx/angular:ng-packagr-lite',
|
||||||
|
'@nx/angular:package',
|
||||||
|
'@nx/angular:webpack-browser',
|
||||||
|
'@nx/esbuild:esbuild',
|
||||||
|
'@nrwl/js:babel',
|
||||||
|
'@nrwl/js:node',
|
||||||
|
'@nrwl/js:swc',
|
||||||
|
'@nrwl/react-native:run-ios',
|
||||||
|
'@nrwl/react-native:start',
|
||||||
|
'@nrwl/react-native:run-android',
|
||||||
|
'@nrwl/react-native:bundle',
|
||||||
|
'@nrwl/react-native:build-android',
|
||||||
|
'@nrwl/react-native:bundle',
|
||||||
|
'@nrwl/next:build',
|
||||||
|
'@nrwl/next:server',
|
||||||
|
'@nrwl/js:tsc',
|
||||||
|
'@nrwl/angular:ng-packagr-lite',
|
||||||
|
'@nrwl/angular:package',
|
||||||
|
'@nrwl/angular:webpack-browser',
|
||||||
|
'@nrwl/esbuild:esbuild',
|
||||||
|
'@angular-devkit/build-angular:browser',
|
||||||
|
'@angular-devkit/build-angular:dev-server',
|
||||||
|
];
|
||||||
|
|
||||||
|
// First, we check if the user has provided a target
|
||||||
|
// If they have, we check if the executor the target is using is supported
|
||||||
|
// If it's not supported, then we set the unsupported flag to true for that target
|
||||||
|
|
||||||
|
function checkUserProvidedTarget(target: Target) {
|
||||||
|
if (userProvidedTargets?.[target]) {
|
||||||
|
if (
|
||||||
|
supportedExecutors[target].includes(
|
||||||
|
targets[userProvidedTargets[target]]?.executor
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
output.validFoundTargetName[target] = userProvidedTargets[target];
|
||||||
|
} else {
|
||||||
|
output.userProvidedTargetIsUnsupported[target] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkUserProvidedTarget('build');
|
||||||
|
checkUserProvidedTarget('serve');
|
||||||
|
|
||||||
|
// Returns early when we have a build, serve, and test targets.
|
||||||
|
if (output.validFoundTargetName.build && output.validFoundTargetName.serve) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We try to find the targets that are using the supported executors
|
||||||
|
// for build, serve and test, since these are the ones we will be converting
|
||||||
|
for (const target in targets) {
|
||||||
|
const executorName = targets[target].executor;
|
||||||
|
|
||||||
|
const hasRspackTargets = output.alreadyHasNxRspackTargets;
|
||||||
|
hasRspackTargets.build ||= executorName === '@nx/rspack:rspack';
|
||||||
|
hasRspackTargets.serve ||= executorName === '@nx/rspack:dev-server';
|
||||||
|
|
||||||
|
const foundTargets = output.validFoundTargetName;
|
||||||
|
if (
|
||||||
|
!foundTargets.build &&
|
||||||
|
supportedExecutors.build.includes(executorName)
|
||||||
|
) {
|
||||||
|
foundTargets.build = target;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!foundTargets.serve &&
|
||||||
|
supportedExecutors.serve.includes(executorName)
|
||||||
|
) {
|
||||||
|
foundTargets.serve = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.projectContainsUnsupportedExecutor ||=
|
||||||
|
unsupportedExecutors.includes(executorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addOrChangeBuildTarget(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
target: string
|
||||||
|
) {
|
||||||
|
const project = readProjectConfiguration(tree, options.project);
|
||||||
|
const assets = [];
|
||||||
|
if (
|
||||||
|
options.target === 'web' &&
|
||||||
|
tree.exists(joinPathFragments(project.root, 'src/favicon.ico'))
|
||||||
|
) {
|
||||||
|
assets.push(joinPathFragments(project.root, 'src/favicon.ico'));
|
||||||
|
}
|
||||||
|
if (tree.exists(joinPathFragments(project.root, 'src/assets'))) {
|
||||||
|
assets.push(joinPathFragments(project.root, 'src/assets'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildOptions: RspackExecutorSchema = {
|
||||||
|
target: options.target ?? 'web',
|
||||||
|
outputPath: joinPathFragments(
|
||||||
|
'dist',
|
||||||
|
// If standalone project then use the project's name in dist.
|
||||||
|
project.root === '.' ? project.name : project.root
|
||||||
|
),
|
||||||
|
main: determineMain(tree, options),
|
||||||
|
tsConfig: determineTsConfig(tree, options),
|
||||||
|
rspackConfig: joinPathFragments(project.root, 'rspack.config.js'),
|
||||||
|
assets,
|
||||||
|
};
|
||||||
|
|
||||||
|
project.targets ??= {};
|
||||||
|
|
||||||
|
project.targets[target] = {
|
||||||
|
executor: '@nx/rspack:rspack',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
|
options: buildOptions,
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
mode: 'development',
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
mode: 'production',
|
||||||
|
optimization: options.target === 'web' ? true : undefined,
|
||||||
|
sourceMap: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateProjectConfiguration(tree, options.project, project);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addOrChangeServeTarget(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
target: string
|
||||||
|
) {
|
||||||
|
const project = readProjectConfiguration(tree, options.project);
|
||||||
|
|
||||||
|
project.targets ??= {};
|
||||||
|
|
||||||
|
project.targets[target] = {
|
||||||
|
executor: '@nx/rspack:dev-server',
|
||||||
|
options: {
|
||||||
|
buildTarget: `${options.project}:build:development`,
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {},
|
||||||
|
production: {
|
||||||
|
buildTarget: `${options.project}:build:production`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateProjectConfiguration(tree, options.project, project);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeRspackConfigFile(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
stylePreprocessorOptions?: { includePaths?: string[] }
|
||||||
|
) {
|
||||||
|
const project = readProjectConfiguration(tree, options.project);
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
joinPathFragments(project.root, 'rspack.config.js'),
|
||||||
|
createConfig(options, stylePreprocessorOptions)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfig(
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
stylePreprocessorOptions?: { includePaths?: string[] }
|
||||||
|
) {
|
||||||
|
if (options.framework === 'react') {
|
||||||
|
return `
|
||||||
|
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
||||||
|
|
||||||
|
module.exports = composePlugins(withNx(), withReact(${
|
||||||
|
stylePreprocessorOptions
|
||||||
|
? `
|
||||||
|
{
|
||||||
|
stylePreprocessorOptions: ${JSON.stringify(stylePreprocessorOptions)},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}), (config) => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
} else if (options.framework === 'web' || options.target === 'web') {
|
||||||
|
return `
|
||||||
|
const { composePlugins, withNx, withWeb } = require('@nx/rspack');
|
||||||
|
|
||||||
|
module.exports = composePlugins(withNx(), withWeb(${
|
||||||
|
stylePreprocessorOptions
|
||||||
|
? `
|
||||||
|
{
|
||||||
|
stylePreprocessorOptions: ${JSON.stringify(stylePreprocessorOptions)},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}), (config) => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
} else if (options.framework === 'nest') {
|
||||||
|
return `
|
||||||
|
const { composePlugins, withNx } = require('@nx/rspack');
|
||||||
|
|
||||||
|
module.exports = composePlugins(withNx(), (config) => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
return `
|
||||||
|
const { composePlugins, withNx${
|
||||||
|
stylePreprocessorOptions ? ', withWeb' : ''
|
||||||
|
} } = require('@nx/rspack');
|
||||||
|
|
||||||
|
module.exports = composePlugins(withNx()${
|
||||||
|
stylePreprocessorOptions
|
||||||
|
? `,
|
||||||
|
withWeb({
|
||||||
|
stylePreprocessorOptions: ${JSON.stringify(stylePreprocessorOptions)},
|
||||||
|
})`
|
||||||
|
: ''
|
||||||
|
}, (config) => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteWebpackConfig(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
webpackConfigFilePath?: string
|
||||||
|
) {
|
||||||
|
const webpackConfigPath =
|
||||||
|
webpackConfigFilePath && tree.exists(webpackConfigFilePath)
|
||||||
|
? webpackConfigFilePath
|
||||||
|
: tree.exists(`${projectRoot}/webpack.config.js`)
|
||||||
|
? `${projectRoot}/webpack.config.js`
|
||||||
|
: tree.exists(`${projectRoot}/webpack.config.ts`)
|
||||||
|
? `${projectRoot}/webpack.config.ts`
|
||||||
|
: null;
|
||||||
|
if (webpackConfigPath) {
|
||||||
|
tree.delete(webpackConfigPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe add delete vite config?
|
||||||
|
|
||||||
|
export function moveAndEditIndexHtml(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
buildTarget: string
|
||||||
|
) {
|
||||||
|
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||||
|
|
||||||
|
let indexHtmlPath =
|
||||||
|
projectConfig.targets?.[buildTarget]?.options?.index ??
|
||||||
|
`${projectConfig.root}/src/index.html`;
|
||||||
|
let mainPath =
|
||||||
|
projectConfig.targets?.[buildTarget]?.options?.main ??
|
||||||
|
`${projectConfig.root}/src/main.ts${
|
||||||
|
options.framework === 'react' ? 'x' : ''
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (projectConfig.root !== '.') {
|
||||||
|
mainPath = mainPath.replace(projectConfig.root, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!tree.exists(indexHtmlPath) &&
|
||||||
|
tree.exists(`${projectConfig.root}/index.html`)
|
||||||
|
) {
|
||||||
|
indexHtmlPath = `${projectConfig.root}/index.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.exists(indexHtmlPath)) {
|
||||||
|
const indexHtmlContent = tree.read(indexHtmlPath, 'utf8');
|
||||||
|
if (
|
||||||
|
!indexHtmlContent.includes(
|
||||||
|
`<script type="module" src="${mainPath}"></script>`
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
tree.write(
|
||||||
|
`${projectConfig.root}/index.html`,
|
||||||
|
indexHtmlContent.replace(
|
||||||
|
'</body>',
|
||||||
|
`<script type="module" src="${mainPath}"></script>
|
||||||
|
</body>`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tree.exists(`${projectConfig.root}/src/index.html`)) {
|
||||||
|
tree.delete(`${projectConfig.root}/src/index.html`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tree.write(
|
||||||
|
`${projectConfig.root}/index.html`,
|
||||||
|
`<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="${mainPath}"></script>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeViteConfigFilePathWithTree(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
configFile?: string
|
||||||
|
): string {
|
||||||
|
return configFile && tree.exists(configFile)
|
||||||
|
? configFile
|
||||||
|
: tree.exists(joinPathFragments(`${projectRoot}/rspack.config.ts`))
|
||||||
|
? joinPathFragments(`${projectRoot}/rspack.config.ts`)
|
||||||
|
: tree.exists(joinPathFragments(`${projectRoot}/rspack.config.js`))
|
||||||
|
? joinPathFragments(`${projectRoot}/rspack.config.js`)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getViteConfigPathForProject(
|
||||||
|
tree: Tree,
|
||||||
|
projectName: string,
|
||||||
|
target?: string
|
||||||
|
) {
|
||||||
|
let viteConfigPath: string | undefined;
|
||||||
|
const { targets, root } = readProjectConfiguration(tree, projectName);
|
||||||
|
if (target) {
|
||||||
|
viteConfigPath = targets?.[target]?.options?.configFile;
|
||||||
|
} else {
|
||||||
|
const config = Object.values(targets).find(
|
||||||
|
(config) => config.executor === '@nx/rspack:build'
|
||||||
|
);
|
||||||
|
viteConfigPath = config?.options?.configFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeViteConfigFilePathWithTree(tree, root, viteConfigPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleUnsupportedUserProvidedTargets(
|
||||||
|
userProvidedTargetIsUnsupported: TargetFlags,
|
||||||
|
userProvidedTargetName: UserProvidedTargetName,
|
||||||
|
validFoundTargetName: ValidFoundTargetName,
|
||||||
|
framework: Framework
|
||||||
|
) {
|
||||||
|
if (userProvidedTargetIsUnsupported.build && validFoundTargetName.build) {
|
||||||
|
await handleUnsupportedUserProvidedTargetsErrors(
|
||||||
|
userProvidedTargetName.build,
|
||||||
|
validFoundTargetName.build,
|
||||||
|
'build',
|
||||||
|
'rspack'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
framework !== 'nest' &&
|
||||||
|
userProvidedTargetIsUnsupported.serve &&
|
||||||
|
validFoundTargetName.serve
|
||||||
|
) {
|
||||||
|
await handleUnsupportedUserProvidedTargetsErrors(
|
||||||
|
userProvidedTargetName.serve,
|
||||||
|
validFoundTargetName.serve,
|
||||||
|
'serve',
|
||||||
|
'dev-server'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUnsupportedUserProvidedTargetsErrors(
|
||||||
|
userProvidedTargetName: string,
|
||||||
|
validFoundTargetName: string,
|
||||||
|
target: Target,
|
||||||
|
executor: 'rspack' | 'dev-server'
|
||||||
|
) {
|
||||||
|
logger.warn(
|
||||||
|
`The custom ${target} target you provided (${userProvidedTargetName}) cannot be converted to use the @nx/rspack:${executor} executor.
|
||||||
|
However, we found the following ${target} target in your project that can be converted: ${validFoundTargetName}
|
||||||
|
|
||||||
|
Please note that converting a potentially non-compatible project to use Vite.js may result in unexpected behavior. Always commit
|
||||||
|
your changes before converting a project to use Vite.js, and test the converted project thoroughly before deploying it.
|
||||||
|
`
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { Confirm } = require('enquirer');
|
||||||
|
const prompt = new Confirm({
|
||||||
|
name: 'question',
|
||||||
|
message: `Should we convert the ${validFoundTargetName} target to use the @nx/rspack:${executor} executor?`,
|
||||||
|
initial: true,
|
||||||
|
});
|
||||||
|
const shouldConvert = await prompt.run();
|
||||||
|
if (!shouldConvert) {
|
||||||
|
throw new Error(
|
||||||
|
`The ${target} target ${userProvidedTargetName} cannot be converted to use the @nx/rspack:${executor} executor.
|
||||||
|
Please try again, either by providing a different ${target} target or by not providing a target at all (Nx will
|
||||||
|
convert the first one it finds, most probably this one: ${validFoundTargetName})
|
||||||
|
|
||||||
|
Please note that converting a potentially non-compatible project to use Vite.js may result in unexpected behavior. Always commit
|
||||||
|
your changes before converting a project to use Vite.js, and test the converted project thoroughly before deploying it.
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleUnknownExecutors(projectName: string) {
|
||||||
|
logger.warn(
|
||||||
|
`
|
||||||
|
We could not find any targets in project ${projectName} that use executors which
|
||||||
|
can be converted to the @nx/rspack executors.
|
||||||
|
|
||||||
|
This either means that your project may not have a target
|
||||||
|
for building, serving, or testing at all, or that your targets are
|
||||||
|
using executors that are not known to Nx.
|
||||||
|
|
||||||
|
If you still want to convert your project to use the @nx/rspack executors,
|
||||||
|
please make sure to commit your changes before running this generator.
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { Confirm } = require('enquirer');
|
||||||
|
const prompt = new Confirm({
|
||||||
|
name: 'question',
|
||||||
|
message: `Should Nx convert your project to use the @nx/rspack executors?`,
|
||||||
|
initial: true,
|
||||||
|
});
|
||||||
|
const shouldConvert = await prompt.run();
|
||||||
|
if (!shouldConvert) {
|
||||||
|
throw new Error(`
|
||||||
|
Nx could not verify that the executors you are using can be converted to the @nx/rspack executors.
|
||||||
|
Please try again with a different project.
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function determineFrameworkAndTarget(
|
||||||
|
tree: Tree,
|
||||||
|
options: ConfigurationSchema,
|
||||||
|
projectRoot: string,
|
||||||
|
targets: {
|
||||||
|
[targetName: string]: TargetConfiguration<any>;
|
||||||
|
}
|
||||||
|
): { target: 'node' | 'web'; framework?: Framework } {
|
||||||
|
ensureTypescript();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { tsquery } = require('@phenomnomnominal/tsquery');
|
||||||
|
|
||||||
|
// First try to infer if the target is node
|
||||||
|
if (options.target !== 'node') {
|
||||||
|
// Try to infer from jest config if the env is node
|
||||||
|
let jestConfigPath: string;
|
||||||
|
if (
|
||||||
|
targets?.test?.executor !== '@nx/jest:jest' &&
|
||||||
|
targets?.test?.options?.jestConfig
|
||||||
|
) {
|
||||||
|
jestConfigPath = targets?.test?.options?.jestConfig;
|
||||||
|
} else {
|
||||||
|
jestConfigPath = joinPathFragments(projectRoot, 'jest.config.ts');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tree.exists(jestConfigPath)) {
|
||||||
|
return { target: options.target, framework: options.framework };
|
||||||
|
}
|
||||||
|
const appFileContent = tree.read(jestConfigPath, 'utf-8');
|
||||||
|
const file = tsquery.ast(appFileContent);
|
||||||
|
// find testEnvironment: 'node' in jest config
|
||||||
|
const testEnvironment = tsquery(
|
||||||
|
file,
|
||||||
|
`PropertyAssignment:has(Identifier[name="testEnvironment"]) > StringLiteral[value="node"]`
|
||||||
|
);
|
||||||
|
if (testEnvironment.length > 0) {
|
||||||
|
return { target: 'node', framework: options.framework };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.exists(joinPathFragments(projectRoot, 'src/main.ts'))) {
|
||||||
|
const appFileContent = tree.read(
|
||||||
|
joinPathFragments(projectRoot, 'src/main.ts'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
const file = tsquery.ast(appFileContent);
|
||||||
|
const hasNestJsDependency = tsquery(
|
||||||
|
file,
|
||||||
|
`ImportDeclaration:has(StringLiteral[value="@nestjs/common"])`
|
||||||
|
);
|
||||||
|
if (hasNestJsDependency?.length > 0) {
|
||||||
|
return { target: 'node', framework: 'nest' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.framework === 'nest') {
|
||||||
|
return { target: 'node', framework: 'nest' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.framework !== 'react' && options.target === 'web') {
|
||||||
|
// Look if React is used in the project
|
||||||
|
let tsConfigPath = joinPathFragments(projectRoot, 'tsconfig.json');
|
||||||
|
if (!tree.exists(tsConfigPath)) {
|
||||||
|
tsConfigPath = determineTsConfig(tree, options);
|
||||||
|
}
|
||||||
|
const tsConfig = JSON.parse(tree.read(tsConfigPath).toString());
|
||||||
|
if (tsConfig?.compilerOptions?.jsx?.includes('react')) {
|
||||||
|
return { target: 'web', framework: 'react' };
|
||||||
|
} else {
|
||||||
|
return { target: options.target, framework: options.framework };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { target: options.target, framework: options.framework };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function determineMain(tree: Tree, options: ConfigurationSchema) {
|
||||||
|
if (options.main) return options.main;
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, options.project);
|
||||||
|
|
||||||
|
const mainTsx = joinPathFragments(project.root, 'src/main.tsx');
|
||||||
|
if (tree.exists(mainTsx)) return mainTsx;
|
||||||
|
|
||||||
|
return joinPathFragments(project.root, 'src/main.ts');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function determineTsConfig(tree: Tree, options: ConfigurationSchema) {
|
||||||
|
if (options.tsConfig) return options.tsConfig;
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, options.project);
|
||||||
|
|
||||||
|
const appJson = joinPathFragments(project.root, 'tsconfig.app.json');
|
||||||
|
if (tree.exists(appJson)) return appJson;
|
||||||
|
|
||||||
|
const libJson = joinPathFragments(project.root, 'tsconfig.lib.json');
|
||||||
|
if (tree.exists(libJson)) return libJson;
|
||||||
|
|
||||||
|
return joinPathFragments(project.root, 'tsconfig.json');
|
||||||
|
}
|
||||||
19
packages/rspack/src/utils/get-copy-patterns.ts
Normal file
19
packages/rspack/src/utils/get-copy-patterns.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export function getCopyPatterns(assets: any[]) {
|
||||||
|
return assets.map((asset) => {
|
||||||
|
return {
|
||||||
|
context: asset.input,
|
||||||
|
// Now we remove starting slash to make Webpack place it from the output root.
|
||||||
|
to: asset.output,
|
||||||
|
from: asset.glob,
|
||||||
|
globOptions: {
|
||||||
|
ignore: [
|
||||||
|
'.gitkeep',
|
||||||
|
'**/.DS_Store',
|
||||||
|
'**/Thumbs.db',
|
||||||
|
...(asset.ignore ?? []),
|
||||||
|
],
|
||||||
|
dot: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user