@nx/react 20.6.0-beta.0 → 20.6.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/package.json +6 -6
  2. package/router-plugin.d.ts +1 -0
  3. package/router-plugin.js +5 -0
  4. package/src/generators/application/application.js +42 -4
  5. package/src/generators/application/files/react-router-ssr/common/app/app-nav.tsx__tmpl__ +15 -0
  6. package/src/generators/application/files/react-router-ssr/common/app/entry.client.tsx__tmpl__ +18 -0
  7. package/src/generators/application/files/react-router-ssr/common/app/entry.server.tsx__tmpl__ +74 -0
  8. package/src/generators/application/files/react-router-ssr/common/app/root.tsx__tmpl__ +51 -0
  9. package/src/generators/application/files/react-router-ssr/common/app/routes/about.tsx__tmpl__ +7 -0
  10. package/src/generators/application/files/react-router-ssr/common/app/routes.tsx__tmpl__ +6 -0
  11. package/src/generators/application/files/react-router-ssr/common/public/favicon.ico +0 -0
  12. package/src/generators/application/files/react-router-ssr/common/react-router.config.ts__tmpl__ +5 -0
  13. package/src/generators/application/files/react-router-ssr/common/tests/routes/_index.spec.tsx__tmpl__ +16 -0
  14. package/src/generators/application/files/react-router-ssr/common/tsconfig.app.json__tmpl__ +23 -0
  15. package/src/generators/application/files/react-router-ssr/common/tsconfig.json__tmpl__ +27 -0
  16. package/src/generators/application/files/react-router-ssr/non-root/.gitignore__tmpl__ +5 -0
  17. package/src/generators/application/files/react-router-ssr/non-root/package.json__tmpl__ +24 -0
  18. package/src/generators/application/files/react-router-ssr/nx-welcome/claimed/app/nx-welcome.tsx__tmpl__ +866 -0
  19. package/src/generators/application/files/react-router-ssr/nx-welcome/not-configured/app/nx-welcome.tsx__tmpl__ +866 -0
  20. package/src/generators/application/files/react-router-ssr/nx-welcome/unclaimed/app/nx-welcome.tsx__tmpl__ +864 -0
  21. package/src/generators/application/files/react-router-ssr/ts-solution/package.json__tmpl__ +24 -0
  22. package/src/generators/application/files/react-router-ssr/ts-solution/tsconfig.app.json__tmpl__ +39 -0
  23. package/src/generators/application/lib/add-e2e.js +4 -2
  24. package/src/generators/application/lib/add-linting.d.ts +1 -0
  25. package/src/generators/application/lib/add-linting.js +38 -0
  26. package/src/generators/application/lib/add-project.js +12 -1
  27. package/src/generators/application/lib/add-routing.js +1 -1
  28. package/src/generators/application/lib/bundlers/add-vite.js +15 -6
  29. package/src/generators/application/lib/create-application-files.js +40 -3
  30. package/src/generators/application/lib/install-common-dependencies.js +13 -2
  31. package/src/generators/application/lib/normalize-options.js +3 -0
  32. package/src/generators/application/schema.d.ts +1 -0
  33. package/src/generators/application/schema.json +6 -1
  34. package/src/generators/init/init.js +23 -0
  35. package/src/generators/init/schema.d.ts +2 -0
  36. package/src/generators/library/library.js +3 -3
  37. package/src/plugins/router-plugin.d.ts +10 -0
  38. package/src/plugins/router-plugin.js +219 -0
  39. package/src/utils/ast-utils.d.ts +1 -1
  40. package/src/utils/ast-utils.js +2 -2
  41. package/src/utils/versions.d.ts +3 -1
  42. package/src/utils/versions.js +6 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "20.6.0-beta.0",
3
+ "version": "20.6.0-beta.1",
4
4
  "private": false,
5
5
  "description": "The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Vitest, Playwright, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
6
6
  "repository": {
@@ -38,11 +38,11 @@
38
38
  "minimatch": "9.0.3",
39
39
  "picocolors": "^1.1.0",
40
40
  "tslib": "^2.3.0",
41
- "@nx/devkit": "20.6.0-beta.0",
42
- "@nx/js": "20.6.0-beta.0",
43
- "@nx/eslint": "20.6.0-beta.0",
44
- "@nx/web": "20.6.0-beta.0",
45
- "@nx/module-federation": "20.6.0-beta.0",
41
+ "@nx/devkit": "20.6.0-beta.1",
42
+ "@nx/js": "20.6.0-beta.1",
43
+ "@nx/eslint": "20.6.0-beta.1",
44
+ "@nx/web": "20.6.0-beta.1",
45
+ "@nx/module-federation": "20.6.0-beta.1",
46
46
  "express": "^4.21.2",
47
47
  "http-proxy-middleware": "^3.0.3",
48
48
  "semver": "^7.6.3"
@@ -0,0 +1 @@
1
+ export { createNodesV2, ReactRouterPluginOptions, } from './src/plugins/router-plugin';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNodesV2 = void 0;
4
+ var router_plugin_1 = require("./src/plugins/router-plugin");
5
+ Object.defineProperty(exports, "createNodesV2", { enumerable: true, get: function () { return router_plugin_1.createNodesV2; } });
@@ -26,6 +26,7 @@ const add_rspack_1 = require("./lib/bundlers/add-rspack");
26
26
  const add_rsbuild_1 = require("./lib/bundlers/add-rsbuild");
27
27
  const add_vite_1 = require("./lib/bundlers/add-vite");
28
28
  const sort_fields_1 = require("@nx/js/src/utils/package-json/sort-fields");
29
+ const prompt_1 = require("@nx/devkit/src/generators/prompt");
29
30
  async function applicationGenerator(tree, schema) {
30
31
  return await applicationGeneratorInternal(tree, {
31
32
  addPlugin: false,
@@ -45,6 +46,25 @@ async function applicationGeneratorInternal(tree, schema) {
45
46
  });
46
47
  tasks.push(jsInitTask);
47
48
  const options = await (0, normalize_options_1.normalizeOptions)(tree, schema);
49
+ options.useReactRouter = options.routing
50
+ ? options.useReactRouter ??
51
+ (await (0, prompt_1.promptWhenInteractive)({
52
+ name: 'response',
53
+ message: 'Would you like to use react-router for server-side rendering?',
54
+ type: 'autocomplete',
55
+ choices: [
56
+ {
57
+ name: 'Yes',
58
+ message: 'I want to use react-router [ https://reactrouter.com/start/framework/routing ]',
59
+ },
60
+ {
61
+ name: 'No',
62
+ message: 'I do not want to use react-router for server-side rendering',
63
+ },
64
+ ],
65
+ initial: 0,
66
+ }, { response: 'No' }).then((r) => r.response === 'Yes'))
67
+ : false;
48
68
  (0, show_possible_warnings_1.showPossibleWarnings)(tree, options);
49
69
  const initTask = await (0, init_1.default)(tree, {
50
70
  ...options,
@@ -113,16 +133,34 @@ async function applicationGeneratorInternal(tree, schema) {
113
133
  }
114
134
  // Handle tsconfig.spec.json for jest or vitest
115
135
  (0, update_jest_config_1.updateSpecConfig)(tree, options);
116
- const stylePreprocessorTask = await (0, install_common_dependencies_1.installCommonDependencies)(tree, options);
117
- tasks.push(stylePreprocessorTask);
136
+ const commonDependencyTask = await (0, install_common_dependencies_1.installCommonDependencies)(tree, options);
137
+ tasks.push(commonDependencyTask);
118
138
  const styledTask = (0, add_styled_dependencies_1.addStyledModuleDependencies)(tree, options);
119
139
  tasks.push(styledTask);
120
- const routingTask = (0, add_routing_1.addRouting)(tree, options);
121
- tasks.push(routingTask);
140
+ if (!options.useReactRouter) {
141
+ const routingTask = (0, add_routing_1.addRouting)(tree, options);
142
+ tasks.push(routingTask);
143
+ }
122
144
  (0, set_defaults_1.setDefaults)(tree, options);
123
145
  if (options.bundler === 'rspack' && options.style === 'styled-jsx') {
124
146
  (0, add_rspack_1.handleStyledJsxForRspack)(tasks, tree, options);
125
147
  }
148
+ if (options.useReactRouter) {
149
+ (0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'tsconfig.json'), (json) => {
150
+ const types = new Set(json.compilerOptions?.types || []);
151
+ types.add('@react-router/node');
152
+ return {
153
+ ...json,
154
+ compilerOptions: {
155
+ ...json.compilerOptions,
156
+ jsx: 'react-jsx',
157
+ moduleResolution: 'bundler',
158
+ types: Array.from(types),
159
+ },
160
+ };
161
+ });
162
+ }
163
+ // Only for the new TS solution
126
164
  (0, ts_solution_setup_1.updateTsconfigFiles)(tree, options.appProjectRoot, 'tsconfig.app.json', {
127
165
  jsx: 'react-jsx',
128
166
  module: 'esnext',
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ import { NavLink } from "react-router";
3
+
4
+ export function AppNav() {
5
+ return (
6
+ <nav>
7
+ <NavLink to="/" end>
8
+ Home
9
+ </NavLink>
10
+ <NavLink to="/about" end>
11
+ About
12
+ </NavLink>
13
+ </nav>
14
+ );
15
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * By default, React Router will handle hydrating your app on the client for you.
3
+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx react-router reveal` ✨
4
+ * For more information, see https://reactrouter.com/explanation/special-files#entryclienttsx
5
+ */
6
+
7
+ import { HydratedRouter } from 'react-router/dom';
8
+ import { startTransition, StrictMode } from "react";
9
+ import { hydrateRoot } from "react-dom/client";
10
+
11
+ startTransition(() => {
12
+ hydrateRoot(
13
+ document,
14
+ <StrictMode>
15
+ <HydratedRouter />
16
+ </StrictMode>
17
+ );
18
+ });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * By default, React Router will handle generating the HTTP Response for you.
3
+ * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4
+ * For more information, see https://reactrouter.com/explanation/special-files#entryservertsx
5
+ */
6
+
7
+ import { PassThrough } from "node:stream";
8
+
9
+ import type { AppLoadContext, EntryContext } from "react-router";
10
+ import { createReadableStreamFromReadable } from "@react-router/node";
11
+ import { ServerRouter } from "react-router";
12
+ import { isbot } from "isbot";
13
+ import type { RenderToPipeableStreamOptions } from "react-dom/server";
14
+ import { renderToPipeableStream } from "react-dom/server";
15
+
16
+ export const streamTimeout = 5_000;
17
+
18
+ export default function handleRequest(
19
+ request: Request,
20
+ responseStatusCode: number,
21
+ responseHeaders: Headers,
22
+ routerContext: EntryContext,
23
+ loadContext: AppLoadContext
24
+ ) {
25
+ return new Promise((resolve, reject) => {
26
+ let shellRendered = false;
27
+ const userAgent = request.headers.get("user-agent");
28
+
29
+ // Ensure requests from bots and SPA Mode renders wait for all content to load before responding
30
+ // https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
31
+ const readyOption: keyof RenderToPipeableStreamOptions =
32
+ (userAgent && isbot(userAgent)) || routerContext.isSpaMode
33
+ ? "onAllReady"
34
+ : "onShellReady";
35
+
36
+ const { pipe, abort } = renderToPipeableStream(
37
+ <ServerRouter context={routerContext} url={request.url} />,
38
+ {
39
+ [readyOption]() {
40
+ shellRendered = true;
41
+ const body = new PassThrough();
42
+ const stream = createReadableStreamFromReadable(body);
43
+
44
+ responseHeaders.set("Content-Type", "text/html");
45
+
46
+ resolve(
47
+ new Response(stream, {
48
+ headers: responseHeaders,
49
+ status: responseStatusCode,
50
+ })
51
+ );
52
+
53
+ pipe(body);
54
+ },
55
+ onShellError(error: unknown) {
56
+ reject(error);
57
+ },
58
+ onError(error: unknown) {
59
+ responseStatusCode = 500;
60
+ // Log streaming rendering errors from inside the shell. Don't log
61
+ // errors encountered during initial shell rendering since they'll
62
+ // reject and get logged in handleDocumentRequest.
63
+ if (shellRendered) {
64
+ console.error(error);
65
+ }
66
+ },
67
+ }
68
+ );
69
+
70
+ // Abort the rendering stream after the `streamTimeout` so it has time to
71
+ // flush down the rejected boundaries
72
+ setTimeout(abort, streamTimeout + 1000);
73
+ });
74
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ Links,
3
+ Meta,
4
+ Outlet,
5
+ Scripts,
6
+ ScrollRestoration,
7
+ type MetaFunction,
8
+ type LinksFunction
9
+ } from "react-router";
10
+
11
+ import { AppNav } from './app-nav'
12
+
13
+ export const meta: MetaFunction = () => ([{
14
+ title: "New Nx React Router App",
15
+ }]);
16
+
17
+ export const links: LinksFunction = () => [
18
+ { rel: "preconnect", href: "https://fonts.googleapis.com" },
19
+ {
20
+ rel: "preconnect",
21
+ href: "https://fonts.gstatic.com",
22
+ crossOrigin: "anonymous",
23
+ },
24
+ {
25
+ rel: "stylesheet",
26
+ href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
27
+ },
28
+ ];
29
+
30
+ export function Layout({ children }: { children: React.ReactNode }) {
31
+ return (
32
+ <html lang="en">
33
+ <head>
34
+ <meta charSet="utf-8" />
35
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
36
+ <Meta />
37
+ <Links />
38
+ </head>
39
+ <body>
40
+ <AppNav />
41
+ {children}
42
+ <ScrollRestoration />
43
+ <Scripts />
44
+ </body>
45
+ </html>
46
+ );
47
+ }
48
+
49
+ export default function App() {
50
+ return <Outlet />;
51
+ }
@@ -0,0 +1,7 @@
1
+ export default function AboutComponent() {
2
+ return (
3
+ <div>
4
+ <h1>About!!!</h1>
5
+ </div>
6
+ );
7
+ }
@@ -0,0 +1,6 @@
1
+ import { type RouteConfig, index, route } from "@react-router/dev/routes";
2
+
3
+ export default [
4
+ index('<%- js ? `./${fileName}.jsx` : `./${fileName}.tsx` %>'),
5
+ route('about', './routes/about.tsx')
6
+ ] satisfies RouteConfig;
@@ -0,0 +1,5 @@
1
+ import type { Config } from "@react-router/dev/config";
2
+
3
+ export default {
4
+ ssr: true,
5
+ } satisfies Config;
@@ -0,0 +1,16 @@
1
+ import { createRoutesStub } from 'react-router';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import App from '../../app/app';
4
+
5
+ test('renders loader data', async () => {
6
+ const ReactRouterStub = createRoutesStub([
7
+ {
8
+ path: '/',
9
+ Component: App,
10
+ },
11
+ ]);
12
+
13
+ render(<ReactRouterStub />);
14
+
15
+ await waitFor(() => screen.findByText('Hello there,'));
16
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": [
4
+ "app/**/*.ts",
5
+ "app/**/*.tsx",
6
+ "app/**/*.js",
7
+ "app/**/*.jsx",
8
+ "**/.server/**/*.ts",
9
+ "**/.server/**/*.tsx",
10
+ "**/.client/**/*.ts",
11
+ "**/.client/**/*.tsx"
12
+ ],
13
+ "exclude": [
14
+ "tests/**/*.spec.ts",
15
+ "tests/**/*.test.ts",
16
+ "tests/**/*.spec.tsx",
17
+ "tests/**/*.test.tsx",
18
+ "tests/**/*.spec.js",
19
+ "tests/**/*.test.js",
20
+ "tests/**/*.spec.jsx",
21
+ "tests/**/*.test.jsx"
22
+ ]
23
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "<%= offsetFromRoot %>tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "lib": ["DOM", "DOM.Iterable", "ES2019"],
5
+ "types": ["@react-router/node", "vite/client"],
6
+ "isolatedModules": true,
7
+ "esModuleInterop": true,
8
+ "jsx": "react-jsx",
9
+ "module": "ESNext",
10
+ "moduleResolution": "Bundler",
11
+ "resolveJsonModule": true,
12
+ "target": "ES2022",
13
+ "strict": true,
14
+ "allowJs": true,
15
+ "skipLibCheck": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ // Vite takes care of building everything.
18
+ "noEmit": true
19
+ },
20
+ "include": [],
21
+ "files": [],
22
+ "references": [
23
+ {
24
+ "path": "./tsconfig.app.json"
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,5 @@
1
+ .cache
2
+ build
3
+ public/build
4
+ .env
5
+ .react-router
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "<%= projectName %>",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {},
6
+ "dependencies": {
7
+ "@react-router/node": "<%= reactRouterVersion %>",
8
+ "@react-router/serve": "<%= reactRouterVersion %>",
9
+ "isbot": "<%= reactRouterIsBotVersion %>",
10
+ "react": "<%= reactVersion %>",
11
+ "react-dom": "<%= reactVersion %>",
12
+ "react-router": "<%= reactRouterVersion %>"
13
+ },
14
+ "devDependencies": {
15
+ "@react-router/dev": "<%= reactRouterVersion %>",
16
+ "@types/node": "<%= typesNodeVersion %>",
17
+ "@types/react": "<%= reactVersion %>",
18
+ "@types/react-dom": "<%= reactVersion %>"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "sideEffects": false
24
+ }