@nx/remix 20.0.10 → 20.1.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 (39) hide show
  1. package/migrations.json +29 -0
  2. package/package.json +7 -5
  3. package/src/generators/application/__snapshots__/application.impl.spec.ts.snap +126 -343
  4. package/src/generators/application/application.impl.js +10 -48
  5. package/src/generators/application/files/common/app/entry.client.tsx__tmpl__ +18 -0
  6. package/src/generators/application/files/common/app/entry.server.tsx__tmpl__ +140 -0
  7. package/src/generators/application/files/common/app/root.tsx__tmpl__ +20 -5
  8. package/src/generators/application/files/common/tsconfig.app.json__tmpl__ +5 -2
  9. package/src/generators/application/files/common/tsconfig.json__tmpl__ +6 -3
  10. package/src/generators/application/files/common/vite.config.ts__tmpl__ +25 -0
  11. package/src/generators/application/files/integrated/package.json__tmpl__ +3 -4
  12. package/src/generators/application/lib/add-e2e.js +1 -1
  13. package/src/generators/application/lib/add-vite-temp-files-to-gitignore.d.ts +2 -0
  14. package/src/generators/application/lib/add-vite-temp-files-to-gitignore.js +18 -0
  15. package/src/generators/application/lib/index.d.ts +1 -0
  16. package/src/generators/application/lib/index.js +1 -0
  17. package/src/generators/application/schema.d.ts +0 -1
  18. package/src/generators/application/schema.json +0 -5
  19. package/src/generators/convert-to-inferred/convert-to-inferred.js +1 -1
  20. package/src/generators/preset/lib/normalize-options.d.ts +0 -1
  21. package/src/generators/preset/preset.impl.js +0 -1
  22. package/src/generators/setup-tailwind/__snapshots__/setup-tailwind.impl.spec.ts.snap +32 -112
  23. package/src/generators/setup-tailwind/files/postcss.config.js__tpl__ +6 -0
  24. package/src/generators/setup-tailwind/schema.d.ts +0 -1
  25. package/src/generators/setup-tailwind/schema.json +0 -5
  26. package/src/generators/setup-tailwind/setup-tailwind.impl.js +2 -8
  27. package/src/generators/utils/update-dependencies.js +2 -0
  28. package/src/plugins/plugin.js +5 -3
  29. package/src/utils/remix-config.d.ts +1 -0
  30. package/src/utils/remix-config.js +37 -6
  31. package/src/utils/remix-route-utils.js +4 -1
  32. package/src/utils/versions.d.ts +4 -1
  33. package/src/utils/versions.js +5 -2
  34. package/src/generators/application/files/common/remix.config.js__tmpl__ +0 -17
  35. package/src/generators/application/files/common/remix.env.d.ts__tmpl__ +0 -2
  36. package/src/generators/setup-tailwind/lib/index.d.ts +0 -1
  37. package/src/generators/setup-tailwind/lib/index.js +0 -4
  38. package/src/generators/setup-tailwind/lib/update-remix-config.d.ts +0 -2
  39. package/src/generators/setup-tailwind/lib/update-remix-config.js +0 -34
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.remixApplicationGenerator = remixApplicationGenerator;
4
4
  exports.remixApplicationGeneratorInternal = remixApplicationGeneratorInternal;
5
5
  const devkit_1 = require("@nx/devkit");
6
- const target_defaults_utils_1 = require("@nx/devkit/src/generators/target-defaults-utils");
7
6
  const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-project-command");
8
7
  const js_1 = require("@nx/js");
9
8
  const create_ts_config_1 = require("@nx/js/src/utils/typescript/create-ts-config");
@@ -17,65 +16,29 @@ const update_dependencies_1 = require("../utils/update-dependencies");
17
16
  const lib_1 = require("./lib");
18
17
  function remixApplicationGenerator(tree, options) {
19
18
  return remixApplicationGeneratorInternal(tree, {
20
- addPlugin: false,
19
+ addPlugin: true,
21
20
  ...options,
22
21
  });
23
22
  }
24
23
  async function remixApplicationGeneratorInternal(tree, _options) {
25
24
  (0, ts_solution_setup_1.assertNotUsingTsSolutionSetup)(tree, 'remix', 'application');
26
25
  const options = await (0, lib_1.normalizeOptions)(tree, _options);
26
+ if (!options.addPlugin) {
27
+ throw new Error(`To generate a new Remix Vite application, you must use Inference Plugins. Check you do not have NX_ADD_PLUGINS=false or useInferencePlugins: false in your nx.json.`);
28
+ }
27
29
  const tasks = [
28
30
  await (0, init_1.default)(tree, {
29
31
  skipFormat: true,
30
- addPlugin: options.addPlugin,
32
+ addPlugin: true,
31
33
  }),
32
34
  await (0, js_1.initGenerator)(tree, { skipFormat: true }),
33
35
  ];
34
- (0, target_defaults_utils_1.addBuildTargetDefaults)(tree, '@nx/remix:build');
35
36
  (0, devkit_1.addProjectConfiguration)(tree, options.projectName, {
36
37
  root: options.projectRoot,
37
38
  sourceRoot: `${options.projectRoot}`,
38
39
  projectType: 'application',
39
40
  tags: options.parsedTags,
40
- targets: !options.addPlugin
41
- ? {
42
- build: {
43
- executor: '@nx/remix:build',
44
- outputs: ['{options.outputPath}'],
45
- options: {
46
- outputPath: (0, devkit_1.joinPathFragments)('dist', options.projectRoot),
47
- },
48
- },
49
- serve: {
50
- executor: `@nx/remix:serve`,
51
- options: {
52
- command: `${(0, devkit_1.getPackageManagerCommand)().exec} remix-serve build/index.js`,
53
- manual: true,
54
- port: 4200,
55
- },
56
- },
57
- start: {
58
- dependsOn: ['build'],
59
- command: `remix-serve build/index.js`,
60
- options: {
61
- cwd: options.projectRoot,
62
- },
63
- },
64
- ['serve-static']: {
65
- dependsOn: ['build'],
66
- command: `remix-serve build/index.js`,
67
- options: {
68
- cwd: options.projectRoot,
69
- },
70
- },
71
- typecheck: {
72
- command: `tsc --project tsconfig.app.json`,
73
- options: {
74
- cwd: options.projectRoot,
75
- },
76
- },
77
- }
78
- : {},
41
+ targets: {},
79
42
  });
80
43
  const installTask = (0, update_dependencies_1.updateDependencies)(tree);
81
44
  tasks.push(installTask);
@@ -94,6 +57,7 @@ async function remixApplicationGeneratorInternal(tree, _options) {
94
57
  typesReactDomVersion: versions_1.typesReactDomVersion,
95
58
  eslintVersion: versions_1.eslintVersion,
96
59
  typescriptVersion: versions_1.typescriptVersion,
60
+ viteVersion: versions_1.viteVersion,
97
61
  };
98
62
  (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files/common'), options.projectRoot, vars);
99
63
  (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, './files/nx-welcome', onBoardingStatus), options.projectRoot, { ...vars, connectCloudUrl });
@@ -115,7 +79,7 @@ async function remixApplicationGeneratorInternal(tree, _options) {
115
79
  skipFormat: true,
116
80
  testEnvironment: 'jsdom',
117
81
  skipViteConfig: true,
118
- addPlugin: options.addPlugin,
82
+ addPlugin: true,
119
83
  });
120
84
  createOrEditViteConfig(tree, {
121
85
  project: options.projectName,
@@ -136,7 +100,7 @@ async function remixApplicationGeneratorInternal(tree, _options) {
136
100
  skipSerializers: false,
137
101
  skipPackageJson: false,
138
102
  skipFormat: true,
139
- addPlugin: options.addPlugin,
103
+ addPlugin: true,
140
104
  });
141
105
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.projectName);
142
106
  if (projectConfig.targets['test']?.options) {
@@ -171,9 +135,6 @@ async function remixApplicationGeneratorInternal(tree, _options) {
171
135
  'public/build',
172
136
  ]);
173
137
  }
174
- if (options.js) {
175
- (0, devkit_1.toJS)(tree);
176
- }
177
138
  if (options.rootProject && tree.exists('tsconfig.base.json')) {
178
139
  // If this is a standalone project, merge tsconfig.json and tsconfig.base.json.
179
140
  const tsConfigBaseJson = (0, devkit_1.readJson)(tree, 'tsconfig.base.json');
@@ -247,6 +208,7 @@ export default {...nxPreset};
247
208
  });
248
209
  }
249
210
  }
211
+ (0, lib_1.addViteTempFilesToGitIgnore)(tree);
250
212
  if (!options.skipFormat) {
251
213
  await (0, devkit_1.formatFiles)(tree);
252
214
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * By default, Remix 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 remix reveal` ✨
4
+ * For more information, see https://remix.run/file-conventions/entry.client
5
+ */
6
+
7
+ import { RemixBrowser } from "@remix-run/react";
8
+ import { startTransition, StrictMode } from "react";
9
+ import { hydrateRoot } from "react-dom/client";
10
+
11
+ startTransition(() => {
12
+ hydrateRoot(
13
+ document,
14
+ <StrictMode>
15
+ <RemixBrowser />
16
+ </StrictMode>
17
+ );
18
+ });
@@ -0,0 +1,140 @@
1
+ /**
2
+ * By default, Remix 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://remix.run/file-conventions/entry.server
5
+ */
6
+
7
+ import { PassThrough } from "node:stream";
8
+
9
+ import type { AppLoadContext, EntryContext } from "@remix-run/node";
10
+ import { createReadableStreamFromReadable } from "@remix-run/node";
11
+ import { RemixServer } from "@remix-run/react";
12
+ import { isbot } from "isbot";
13
+ import { renderToPipeableStream } from "react-dom/server";
14
+
15
+ const ABORT_DELAY = 5_000;
16
+
17
+ export default function handleRequest(
18
+ request: Request,
19
+ responseStatusCode: number,
20
+ responseHeaders: Headers,
21
+ remixContext: EntryContext,
22
+ // This is ignored so we can keep it in the template for visibility. Feel
23
+ // free to delete this parameter in your app if you're not using it!
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ loadContext: AppLoadContext
26
+ ) {
27
+ return isbot(request.headers.get("user-agent") || "")
28
+ ? handleBotRequest(
29
+ request,
30
+ responseStatusCode,
31
+ responseHeaders,
32
+ remixContext
33
+ )
34
+ : handleBrowserRequest(
35
+ request,
36
+ responseStatusCode,
37
+ responseHeaders,
38
+ remixContext
39
+ );
40
+ }
41
+
42
+ function handleBotRequest(
43
+ request: Request,
44
+ responseStatusCode: number,
45
+ responseHeaders: Headers,
46
+ remixContext: EntryContext
47
+ ) {
48
+ return new Promise((resolve, reject) => {
49
+ let shellRendered = false;
50
+ const { pipe, abort } = renderToPipeableStream(
51
+ <RemixServer
52
+ context={remixContext}
53
+ url={request.url}
54
+ abortDelay={ABORT_DELAY}
55
+ />,
56
+ {
57
+ onAllReady() {
58
+ shellRendered = true;
59
+ const body = new PassThrough();
60
+ const stream = createReadableStreamFromReadable(body);
61
+
62
+ responseHeaders.set("Content-Type", "text/html");
63
+
64
+ resolve(
65
+ new Response(stream, {
66
+ headers: responseHeaders,
67
+ status: responseStatusCode,
68
+ })
69
+ );
70
+
71
+ pipe(body);
72
+ },
73
+ onShellError(error: unknown) {
74
+ reject(error);
75
+ },
76
+ onError(error: unknown) {
77
+ responseStatusCode = 500;
78
+ // Log streaming rendering errors from inside the shell. Don't log
79
+ // errors encountered during initial shell rendering since they'll
80
+ // reject and get logged in handleDocumentRequest.
81
+ if (shellRendered) {
82
+ console.error(error);
83
+ }
84
+ },
85
+ }
86
+ );
87
+
88
+ setTimeout(abort, ABORT_DELAY);
89
+ });
90
+ }
91
+
92
+ function handleBrowserRequest(
93
+ request: Request,
94
+ responseStatusCode: number,
95
+ responseHeaders: Headers,
96
+ remixContext: EntryContext
97
+ ) {
98
+ return new Promise((resolve, reject) => {
99
+ let shellRendered = false;
100
+ const { pipe, abort } = renderToPipeableStream(
101
+ <RemixServer
102
+ context={remixContext}
103
+ url={request.url}
104
+ abortDelay={ABORT_DELAY}
105
+ />,
106
+ {
107
+ onShellReady() {
108
+ shellRendered = true;
109
+ const body = new PassThrough();
110
+ const stream = createReadableStreamFromReadable(body);
111
+
112
+ responseHeaders.set("Content-Type", "text/html");
113
+
114
+ resolve(
115
+ new Response(stream, {
116
+ headers: responseHeaders,
117
+ status: responseStatusCode,
118
+ })
119
+ );
120
+
121
+ pipe(body);
122
+ },
123
+ onShellError(error: unknown) {
124
+ reject(error);
125
+ },
126
+ onError(error: unknown) {
127
+ responseStatusCode = 500;
128
+ // Log streaming rendering errors from inside the shell. Don't log
129
+ // errors encountered during initial shell rendering since they'll
130
+ // reject and get logged in handleDocumentRequest.
131
+ if (shellRendered) {
132
+ console.error(error);
133
+ }
134
+ },
135
+ }
136
+ );
137
+
138
+ setTimeout(abort, ABORT_DELAY);
139
+ });
140
+ }
@@ -1,18 +1,30 @@
1
- import type { MetaFunction } from "@remix-run/node";
2
1
  import {
3
2
  Links,
4
- LiveReload,
5
3
  Meta,
6
4
  Outlet,
7
5
  Scripts,
8
6
  ScrollRestoration,
9
7
  } from "@remix-run/react";
8
+ import type { MetaFunction, LinksFunction } from "@remix-run/node";
10
9
 
11
10
  export const meta: MetaFunction = () => ([{
12
11
  title: "New Remix App",
13
12
  }]);
14
13
 
15
- export default function App() {
14
+ export const links: LinksFunction = () => [
15
+ { rel: "preconnect", href: "https://fonts.googleapis.com" },
16
+ {
17
+ rel: "preconnect",
18
+ href: "https://fonts.gstatic.com",
19
+ crossOrigin: "anonymous",
20
+ },
21
+ {
22
+ rel: "stylesheet",
23
+ href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
24
+ },
25
+ ];
26
+
27
+ export function Layout({ children }: { children: React.ReactNode }) {
16
28
  return (
17
29
  <html lang="en">
18
30
  <head>
@@ -22,11 +34,14 @@ export default function App() {
22
34
  <Links />
23
35
  </head>
24
36
  <body>
25
- <Outlet />
37
+ {children}
26
38
  <ScrollRestoration />
27
39
  <Scripts />
28
- <LiveReload />
29
40
  </body>
30
41
  </html>
31
42
  );
32
43
  }
44
+
45
+ export default function App() {
46
+ return <Outlet />;
47
+ }
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
3
  "include": [
4
- "remix.env.d.ts",
5
4
  "app/**/*.ts",
6
5
  "app/**/*.tsx",
7
6
  "app/**/*.js",
8
- "app/**/*.jsx"
7
+ "app/**/*.jsx",
8
+ "**/.server/**/*.ts",
9
+ "**/.server/**/*.tsx",
10
+ "**/.client/**/*.ts",
11
+ "**/.client/**/*.tsx"
9
12
  ],
10
13
  "exclude": [
11
14
  "tests/**/*.spec.ts",
@@ -2,16 +2,19 @@
2
2
  "extends": "<%= offsetFromRoot %>tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "lib": ["DOM", "DOM.Iterable", "ES2019"],
5
+ "types": ["@remix-run/node", "vite/client"],
5
6
  "isolatedModules": true,
6
7
  "esModuleInterop": true,
7
8
  "jsx": "react-jsx",
8
- "moduleResolution": "node",
9
+ "module": "ESNext",
10
+ "moduleResolution": "Bundler",
9
11
  "resolveJsonModule": true,
10
- "target": "ES2019",
12
+ "target": "ES2022",
11
13
  "strict": true,
12
14
  "allowJs": true,
15
+ "skipLibCheck": true,
13
16
  "forceConsistentCasingInFileNames": true,
14
- // Remix takes care of building everything in `remix build`.
17
+ // Vite takes care of building everything.
15
18
  "noEmit": true
16
19
  },
17
20
  "include": [],
@@ -0,0 +1,25 @@
1
+ import { vitePlugin as remix } from '@remix-run/dev';
2
+ import { defineConfig } from 'vite';
3
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
4
+
5
+ declare module '@remix-run/node' {
6
+ interface Future {
7
+ v3_singleFetch: true;
8
+ }
9
+ }
10
+
11
+ export default defineConfig({
12
+ root: __dirname,
13
+ plugins: [
14
+ remix({
15
+ future: {
16
+ v3_fetcherPersist: true,
17
+ v3_relativeSplatPath: true,
18
+ v3_throwAbortReason: true,
19
+ v3_singleFetch: true,
20
+ v3_lazyRouteDiscovery: true,
21
+ },
22
+ }),
23
+ nxViteTsPaths(),
24
+ ],
25
+ });
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "private": true,
3
3
  "name": "<%= projectName %>",
4
- "description": "",
5
- "license": "",
6
4
  "scripts": {},
7
5
  "type": "module",
8
6
  "dependencies": {
@@ -18,10 +16,11 @@
18
16
  "@types/react": "<%= typesReactVersion %>",
19
17
  "@types/react-dom": "<%= typesReactDomVersion %>",
20
18
  "eslint": "<%= eslintVersion %>",
21
- "typescript": "<%= typescriptVersion %>"
19
+ "typescript": "<%= typescriptVersion %>",
20
+ "vite": "<%= viteVersion %>",
22
21
  },
23
22
  "engines": {
24
- "node": ">=14"
23
+ "node": ">=20"
25
24
  },
26
25
  "sideEffects": false
27
26
  }
@@ -50,7 +50,7 @@ async function addE2E(tree, options) {
50
50
  buildTarget = `^${matchingPlugin.options?.buildTargetName ?? 'build'}`;
51
51
  }
52
52
  }
53
- await (0, target_defaults_utils_1.addE2eCiTargetDefaults)(tree, '@nx/cypress/plugin', buildTarget, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, `cypress.config.${options.js ? 'js' : 'ts'}`));
53
+ await (0, target_defaults_utils_1.addE2eCiTargetDefaults)(tree, '@nx/cypress/plugin', buildTarget, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, `cypress.config.ts`));
54
54
  }
55
55
  return e2eTask;
56
56
  }
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export declare function addViteTempFilesToGitIgnore(tree: Tree): void;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addViteTempFilesToGitIgnore = addViteTempFilesToGitIgnore;
4
+ const devkit_1 = require("@nx/devkit");
5
+ function addViteTempFilesToGitIgnore(tree) {
6
+ let newGitIgnoreContents = `vite.config.*.timestamp*`;
7
+ if (tree.exists('.gitignore')) {
8
+ const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
9
+ if (!gitIgnoreContents.includes(newGitIgnoreContents)) {
10
+ newGitIgnoreContents = (0, devkit_1.stripIndents) `${gitIgnoreContents}
11
+ ${newGitIgnoreContents}`;
12
+ tree.write('.gitignore', newGitIgnoreContents);
13
+ }
14
+ }
15
+ else {
16
+ tree.write('.gitignore', newGitIgnoreContents);
17
+ }
18
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './normalize-options';
2
2
  export * from './update-unit-test-config';
3
3
  export * from './add-e2e';
4
+ export * from './add-vite-temp-files-to-gitignore';
@@ -4,3 +4,4 @@ const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./normalize-options"), exports);
5
5
  tslib_1.__exportStar(require("./update-unit-test-config"), exports);
6
6
  tslib_1.__exportStar(require("./add-e2e"), exports);
7
+ tslib_1.__exportStar(require("./add-vite-temp-files-to-gitignore"), exports);
@@ -4,7 +4,6 @@ export interface NxRemixGeneratorSchema {
4
4
  directory: string;
5
5
  name?: string;
6
6
  tags?: string;
7
- js?: boolean;
8
7
  linter?: Linter | LinterType;
9
8
  unitTestRunner?: 'vitest' | 'jest' | 'none';
10
9
  e2eTestRunner?: 'cypress' | 'playwright' | 'none';
@@ -20,11 +20,6 @@
20
20
  "description": "The name of the application.",
21
21
  "x-priority": "important"
22
22
  },
23
- "js": {
24
- "type": "boolean",
25
- "description": "Generate JavaScript files rather than TypeScript files.",
26
- "default": false
27
- },
28
23
  "linter": {
29
24
  "description": "The tool to use for running lint checks.",
30
25
  "type": "string",
@@ -14,7 +14,7 @@ async function convertToInferred(tree, options) {
14
14
  buildTargetName: 'build',
15
15
  devTargetName: 'dev',
16
16
  startTargetName: 'start',
17
- staticServeTargetName: 'static-serve',
17
+ serveStaticTargetName: 'serve-static',
18
18
  typecheckTargetName: 'typecheck',
19
19
  }, [
20
20
  {
@@ -6,6 +6,5 @@ export interface NormalizedSchema extends RemixGeneratorSchema {
6
6
  parsedTags: string[];
7
7
  unitTestRunner?: 'jest' | 'none' | 'vitest';
8
8
  e2eTestRunner?: 'cypress' | 'none';
9
- js?: boolean;
10
9
  }
11
10
  export declare function normalizeOptions(tree: Tree, options: RemixGeneratorSchema): NormalizedSchema;
@@ -22,7 +22,6 @@ async function default_1(tree, _options) {
22
22
  rootProject: true,
23
23
  unitTestRunner: options.unitTestRunner ?? 'vitest',
24
24
  e2eTestRunner: options.e2eTestRunner ?? 'cypress',
25
- js: options.js ?? false,
26
25
  addPlugin: addPluginDefault,
27
26
  });
28
27
  tasks.push(appGenTask);