diff --git a/docs/generated/packages/remix/generators/application.json b/docs/generated/packages/remix/generators/application.json
index 431fe31f8d..2ddb29ae33 100644
--- a/docs/generated/packages/remix/generators/application.json
+++ b/docs/generated/packages/remix/generators/application.json
@@ -20,11 +20,6 @@
"description": "The name of the application.",
"x-priority": "important"
},
- "js": {
- "type": "boolean",
- "description": "Generate JavaScript files rather than TypeScript files.",
- "default": false
- },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
diff --git a/docs/generated/packages/remix/generators/setup-tailwind.json b/docs/generated/packages/remix/generators/setup-tailwind.json
index 215eca3cdc..2b41acb0af 100644
--- a/docs/generated/packages/remix/generators/setup-tailwind.json
+++ b/docs/generated/packages/remix/generators/setup-tailwind.json
@@ -21,11 +21,6 @@
"x-prompt": "What project would you like to add Tailwind to?",
"pattern": "^[a-zA-Z].*$"
},
- "js": {
- "type": "boolean",
- "description": "Generate a JavaScript config file instead of a TypeScript config file",
- "default": false
- },
"skipFormat": {
"type": "boolean",
"description": "Skip formatting files after generator runs",
diff --git a/e2e/nx/src/workspace.test.ts b/e2e/nx/src/workspace.test.ts
index 2da91e6a12..14f853054d 100644
--- a/e2e/nx/src/workspace.test.ts
+++ b/e2e/nx/src/workspace.test.ts
@@ -32,10 +32,10 @@ describe('@nx/workspace:infer-targets', () => {
// default case, everything is generated with crystal, everything should be skipped
const remixApp = uniq('remix');
runCLI(
- `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
+ `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
);
- const output = runCLI(`generate infer-targets --no-interactive`);
+ const output = runCLI(`generate infer-targets --no-interactive --verbose`);
expect(output).toContain('@nx/remix:convert-to-inferred - Skipped');
expect(output).toContain('@nx/playwright:convert-to-inferred - Skipped');
@@ -60,7 +60,7 @@ describe('@nx/workspace:infer-targets', () => {
return json;
});
- const output2 = runCLI(`generate infer-targets --no-interactive`);
+ const output2 = runCLI(`generate infer-targets --no-interactive --verbose`);
expect(output2).toContain('@nx/remix:convert-to-inferred - Success');
expect(output2).toContain('@nx/eslint:convert-to-inferred - Success');
@@ -70,7 +70,7 @@ describe('@nx/workspace:infer-targets', () => {
// default case, everything is generated with crystal, relevant plugins should be skipped
const remixApp = uniq('remix');
runCLI(
- `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
+ `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
);
const output = runCLI(
@@ -116,7 +116,7 @@ describe('@nx/workspace:infer-targets', () => {
// even if we make sure there are executors for remix & remix-e2e, only remix conversions will run with --project option
const remixApp = uniq('remix');
runCLI(
- `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
+ `generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
);
updateJson('nx.json', (json) => {
diff --git a/e2e/remix/src/nx-remix.test.ts b/e2e/remix/src/nx-remix.test.ts
index edb4e7f245..d96addeb6f 100644
--- a/e2e/remix/src/nx-remix.test.ts
+++ b/e2e/remix/src/nx-remix.test.ts
@@ -61,7 +61,7 @@ describe('Remix E2E Tests', () => {
const result = runCLI(`build ${plugin}`);
expect(result).toContain('Successfully ran target build');
- checkFilesExist(`subdir/build/index.js`);
+ checkFilesExist(`subdir/build/server/index.js`);
}, 120000);
});
diff --git a/package.json b/package.json
index ed8db25190..6a29f515ad 100644
--- a/package.json
+++ b/package.json
@@ -93,8 +93,8 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@pnpm/lockfile-types": "^6.0.0",
"@reduxjs/toolkit": "1.9.0",
- "@remix-run/dev": "^2.8.1",
- "@remix-run/node": "^2.8.1",
+ "@remix-run/dev": "^2.13.1",
+ "@remix-run/node": "^2.13.1",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-image": "^3.0.3",
diff --git a/packages/remix/migrations.json b/packages/remix/migrations.json
index 093b4f237d..6dd420c0ae 100644
--- a/packages/remix/migrations.json
+++ b/packages/remix/migrations.json
@@ -131,6 +131,35 @@
"alwaysAddToPackageJson": true
}
}
+ },
+ "20.1.0": {
+ "version": "20.1.0-beta.0",
+ "packages": {
+ "@remix-run/node": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ },
+ "@remix-run/react": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ },
+ "@remix-run/serve": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ },
+ "@remix-run/dev": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ },
+ "@remix-run/css-bundle": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ },
+ "@remix-run/eslint-config": {
+ "version": "^2.13.1",
+ "alwaysAddToPackageJson": true
+ }
+ }
}
}
}
diff --git a/packages/remix/package.json b/packages/remix/package.json
index dbb01fe77e..b98b902165 100644
--- a/packages/remix/package.json
+++ b/packages/remix/package.json
@@ -35,7 +35,9 @@
"tslib": "^2.3.1",
"@phenomnomnominal/tsquery": "~5.0.1"
},
- "peerDependencies": {},
+ "peerDependencies": {
+ "@remix-run/dev": "^2.13.1"
+ },
"publishConfig": {
"access": "public"
},
diff --git a/packages/remix/src/generators/application/__snapshots__/application.impl.spec.ts.snap b/packages/remix/src/generators/application/__snapshots__/application.impl.spec.ts.snap
index 80f44ab280..23338adb44 100644
--- a/packages/remix/src/generators/application/__snapshots__/application.impl.spec.ts.snap
+++ b/packages/remix/src/generators/application/__snapshots__/application.impl.spec.ts.snap
@@ -1,36 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Remix Application Integrated Repo --directory should create the application correctly 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo --directory should create the application correctly 2`] = `
-"import type { MetaFunction } from '@remix-run/node';
-import {
+"import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
+import type { MetaFunction, LinksFunction } from '@remix-run/node';
export const meta: MetaFunction = () => [
{
@@ -38,7 +16,20 @@ export const meta: MetaFunction = () => [
},
];
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -48,18 +39,21 @@ export default function App() {
-
+ {children}
-
);
}
+
+export default function App() {
+ return ;
+}
"
`;
-exports[`Remix Application Integrated Repo --directory should create the application correctly 3`] = `
+exports[`Remix Application Integrated Repo --directory should create the application correctly 2`] = `
"import NxWelcome from '../nx-welcome';
export default function Index() {
@@ -73,36 +67,14 @@ export default function Index() {
`;
exports[`Remix Application Integrated Repo --directory should extract the layout directory from the directory options if it exists 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo --directory should extract the layout directory from the directory options if it exists 2`] = `
-"import type { MetaFunction } from '@remix-run/node';
-import {
+"import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
+import type { MetaFunction, LinksFunction } from '@remix-run/node';
export const meta: MetaFunction = () => [
{
@@ -110,7 +82,20 @@ export const meta: MetaFunction = () => [
},
];
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -120,18 +105,21 @@ export default function App() {
-
+ {children}
-
);
}
+
+export default function App() {
+ return ;
+}
"
`;
-exports[`Remix Application Integrated Repo --directory should extract the layout directory from the directory options if it exists 3`] = `
+exports[`Remix Application Integrated Repo --directory should extract the layout directory from the directory options if it exists 2`] = `
"import NxWelcome from '../nx-welcome';
export default function Index() {
@@ -239,96 +227,7 @@ export default defineConfig({
"
`;
-exports[`Remix Application Integrated Repo --js should create the application correctly 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo --js should create the application correctly 2`] = `
-"import {
- Links,
- LiveReload,
- Meta,
- Outlet,
- Scripts,
- ScrollRestoration,
-} from '@remix-run/react';
-export const meta = () => [
- {
- title: 'New Remix App',
- },
-];
-export default function App() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-"
-`;
-
-exports[`Remix Application Integrated Repo --js should create the application correctly 3`] = `
-"import NxWelcome from '../nx-welcome';
-export default function Index() {
- return (
-
-
-
- );
-}
-"
-`;
-
exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using jest 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using jest 2`] = `
"export default {
displayName: 'test',
preset: '../jest.preset.js',
@@ -341,7 +240,7 @@ exports[`Remix Application Integrated Repo --unitTestRunner should generate the
"
`;
-exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using jest 3`] = `
+exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using jest 2`] = `
"import { installGlobals } from '@remix-run/node';
import '@testing-library/jest-dom/matchers';
installGlobals();
@@ -349,27 +248,6 @@ installGlobals();
`;
exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 2`] = `
"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
@@ -400,14 +278,14 @@ export default defineConfig({
"
`;
-exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 3`] = `
+exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 2`] = `
"import { installGlobals } from '@remix-run/node';
import '@testing-library/jest-dom/matchers';
installGlobals();
"
`;
-exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 4`] = `
+exports[`Remix Application Integrated Repo --unitTestRunner should generate the correct files for testing using vitest 3`] = `
"{
"extends": "./tsconfig.json",
"compilerOptions": {
@@ -441,36 +319,14 @@ exports[`Remix Application Integrated Repo --unitTestRunner should generate the
`;
exports[`Remix Application Integrated Repo should create the application correctly 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Integrated Repo should create the application correctly 2`] = `
-"import type { MetaFunction } from '@remix-run/node';
-import {
+"import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
+import type { MetaFunction, LinksFunction } from '@remix-run/node';
export const meta: MetaFunction = () => [
{
@@ -478,7 +334,20 @@ export const meta: MetaFunction = () => [
},
];
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -488,18 +357,21 @@ export default function App() {
-
+ {children}
-
);
}
+
+export default function App() {
+ return ;
+}
"
`;
-exports[`Remix Application Integrated Repo should create the application correctly 3`] = `
+exports[`Remix Application Integrated Repo should create the application correctly 2`] = `
"import NxWelcome from '../nx-welcome';
export default function Index() {
@@ -534,96 +406,7 @@ export default defineConfig({
"
`;
-exports[`Remix Application Standalone Project Repo --js should create the application correctly 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Standalone Project Repo --js should create the application correctly 2`] = `
-"import {
- Links,
- LiveReload,
- Meta,
- Outlet,
- Scripts,
- ScrollRestoration,
-} from '@remix-run/react';
-export const meta = () => [
- {
- title: 'New Remix App',
- },
-];
-export default function App() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-"
-`;
-
-exports[`Remix Application Standalone Project Repo --js should create the application correctly 3`] = `
-"import NxWelcome from '../nx-welcome';
-export default function Index() {
- return (
-
-
-
- );
-}
-"
-`;
-
exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 2`] = `
"export default {
setupFilesAfterEnv: ['/test-setup.ts'],
displayName: 'test',
@@ -640,14 +423,14 @@ exports[`Remix Application Standalone Project Repo --unitTestRunner should gener
"
`;
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 3`] = `
+exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 2`] = `
"import { installGlobals } from '@remix-run/node';
import '@testing-library/jest-dom/matchers';
installGlobals();
"
`;
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 4`] = `
+exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using jest 3`] = `
"import { createRemixStub } from '@remix-run/testing';
import { render, screen, waitFor } from '@testing-library/react';
import Index from '../../app/routes/_index';
@@ -668,27 +451,6 @@ test('renders loader data', async () => {
`;
exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 2`] = `
"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
@@ -719,7 +481,7 @@ export default defineConfig({
"
`;
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 3`] = `
+exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 2`] = `
"import { createRemixStub } from '@remix-run/testing';
import { render, screen, waitFor } from '@testing-library/react';
import Index from '../../app/routes/_index';
@@ -739,7 +501,7 @@ test('renders loader data', async () => {
"
`;
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 4`] = `
+exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 3`] = `
"{
"extends": "./tsconfig.json",
"compilerOptions": {
@@ -772,7 +534,7 @@ exports[`Remix Application Standalone Project Repo --unitTestRunner should gener
"
`;
-exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 5`] = `
+exports[`Remix Application Standalone Project Repo --unitTestRunner should generate the correct files for testing using vitest 4`] = `
"import { installGlobals } from '@remix-run/node';
import '@testing-library/jest-dom/matchers';
installGlobals();
@@ -780,36 +542,14 @@ installGlobals();
`;
exports[`Remix Application Standalone Project Repo should create the application correctly 1`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
-exports[`Remix Application Standalone Project Repo should create the application correctly 2`] = `
-"import type { MetaFunction } from '@remix-run/node';
-import {
+"import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
+import type { MetaFunction, LinksFunction } from '@remix-run/node';
export const meta: MetaFunction = () => [
{
@@ -817,7 +557,20 @@ export const meta: MetaFunction = () => [
},
];
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -827,18 +580,21 @@ export default function App() {
-
+ {children}
-
);
}
+
+export default function App() {
+ return ;
+}
"
`;
-exports[`Remix Application Standalone Project Repo should create the application correctly 3`] = `
+exports[`Remix Application Standalone Project Repo should create the application correctly 2`] = `
"import NxWelcome from '../nx-welcome';
export default function Index() {
@@ -851,7 +607,7 @@ export default function Index() {
"
`;
-exports[`Remix Application Standalone Project Repo should create the application correctly 4`] = `
+exports[`Remix Application Standalone Project Repo should create the application correctly 3`] = `
"import { createRemixStub } from '@remix-run/testing';
import { render, screen, waitFor } from '@testing-library/react';
import Index from '../../app/routes/_index';
@@ -871,9 +627,36 @@ test('renders loader data', async () => {
"
`;
-exports[`Remix Application Standalone Project Repo should create the application correctly 5`] = `null`;
+exports[`Remix Application Standalone Project Repo should create the application correctly 4`] = `
+"import { vitePlugin as remix } from '@remix-run/dev';
+import { defineConfig } from 'vite';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-exports[`Remix Application Standalone Project Repo should create the application correctly 6`] = `
+declare module '@remix-run/node' {
+ interface Future {
+ v3_singleFetch: true;
+ }
+}
+
+export default defineConfig({
+ root: __dirname,
+ plugins: [
+ remix({
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ v3_singleFetch: true,
+ v3_lazyRouteDiscovery: true,
+ },
+ }),
+ nxViteTsPaths(),
+ ],
+});
+"
+`;
+
+exports[`Remix Application Standalone Project Repo should create the application correctly 5`] = `
"{
"root": true,
"ignorePatterns": ["!**/*", "build", "public/build"],
diff --git a/packages/remix/src/generators/application/application.impl.spec.ts b/packages/remix/src/generators/application/application.impl.spec.ts
index 8ba7f29cd7..7a41eefd1b 100644
--- a/packages/remix/src/generators/application/application.impl.spec.ts
+++ b/packages/remix/src/generators/application/application.impl.spec.ts
@@ -30,7 +30,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, '.');
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
+ expect(tree.exists('remix.config.js')).toBeFalsy();
expect(tree.read('app/root.tsx', 'utf-8')).toMatchSnapshot();
expect(tree.read('app/routes/_index.tsx', 'utf-8')).toMatchSnapshot();
expect(
@@ -40,29 +40,6 @@ describe('Remix Application', () => {
expect(tree.read('.eslintrc.json', 'utf-8')).toMatchSnapshot();
});
- describe(`--js`, () => {
- it('should create the application correctly', async () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace();
-
- // ACT
- await applicationGenerator(tree, {
- name: 'test',
- directory: '.',
- js: true,
- rootProject: true,
- addPlugin: true,
- });
-
- // ASSERT
- expectTargetsToBeCorrect(tree, '.');
-
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
- expect(tree.read('app/root.js', 'utf-8')).toMatchSnapshot();
- expect(tree.read('app/routes/_index.js', 'utf-8')).toMatchSnapshot();
- });
- });
-
describe('--unitTestRunner', () => {
it('should generate the correct files for testing using vitest', async () => {
// ARRANGE
@@ -80,7 +57,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, '.');
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
+ expect(tree.exists('remix.config.js')).toBeFalsy();
expect(tree.read('vitest.config.ts', 'utf-8')).toMatchSnapshot();
expect(
tree.read('tests/routes/_index.spec.tsx', 'utf-8')
@@ -105,7 +82,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, '.');
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
+ expect(tree.exists('remix.config.js')).toBeFalsy();
expect(tree.read('jest.config.ts', 'utf-8')).toMatchSnapshot();
expect(tree.read('test-setup.ts', 'utf-8')).toMatchSnapshot();
expect(
@@ -186,37 +163,13 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, appDir);
- expect(tree.read(`${appDir}/remix.config.js`, 'utf-8')).toMatchSnapshot();
+ expect(tree.exists(`${appDir}/remix.config.js`)).toBeFalsy();
expect(tree.read(`${appDir}/app/root.tsx`, 'utf-8')).toMatchSnapshot();
expect(
tree.read(`${appDir}/app/routes/_index.tsx`, 'utf-8')
).toMatchSnapshot();
});
- describe('--js', () => {
- it('should create the application correctly', async () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
-
- // ACT
- await applicationGenerator(tree, {
- directory: 'test',
- js: true,
- addPlugin: true,
- });
-
- // ASSERT
- expectTargetsToBeCorrect(tree, appDir);
-
- expect(
- tree.read(`${appDir}/remix.config.js`, 'utf-8')
- ).toMatchSnapshot();
- expect(tree.read(`${appDir}/app/root.js`, 'utf-8')).toMatchSnapshot();
- expect(
- tree.read(`${appDir}/app/routes/_index.js`, 'utf-8')
- ).toMatchSnapshot();
- });
- });
describe('--directory', () => {
it('should create the application correctly', async () => {
// ARRANGE
@@ -233,9 +186,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, newAppDir);
- expect(
- tree.read(`${newAppDir}/remix.config.js`, 'utf-8')
- ).toMatchSnapshot();
+ expect(tree.exists(`${newAppDir}/remix.config.js`)).toBeFalsy();
expect(
tree.read(`${newAppDir}/app/root.tsx`, 'utf-8')
).toMatchSnapshot();
@@ -259,9 +210,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, newAppDir);
- expect(
- tree.read(`${newAppDir}/remix.config.js`, 'utf-8')
- ).toMatchSnapshot();
+ expect(tree.exists(`${newAppDir}/remix.config.js`)).toBeFalsy();
expect(
tree.read(`${newAppDir}/app/root.tsx`, 'utf-8')
).toMatchSnapshot();
@@ -286,9 +235,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, appDir);
- expect(
- tree.read(`${appDir}/remix.config.js`, 'utf-8')
- ).toMatchSnapshot();
+ expect(tree.exists(`${appDir}/remix.config.js`)).toBeFalsy();
expect(
tree.read(`${appDir}/vitest.config.ts`, 'utf-8')
).toMatchSnapshot();
@@ -312,9 +259,7 @@ describe('Remix Application', () => {
// ASSERT
expectTargetsToBeCorrect(tree, appDir);
- expect(
- tree.read(`${appDir}/remix.config.js`, 'utf-8')
- ).toMatchSnapshot();
+ expect(tree.exists(`${appDir}/remix.config.js`)).toBeFalsy();
expect(
tree.read(`${appDir}/jest.config.ts`, 'utf-8')
).toMatchSnapshot();
diff --git a/packages/remix/src/generators/application/application.impl.ts b/packages/remix/src/generators/application/application.impl.ts
index ad6acdf540..c56633bf63 100644
--- a/packages/remix/src/generators/application/application.impl.ts
+++ b/packages/remix/src/generators/application/application.impl.ts
@@ -4,19 +4,16 @@ import {
formatFiles,
generateFiles,
GeneratorCallback,
- getPackageManagerCommand,
joinPathFragments,
offsetFromRoot,
readJson,
readProjectConfiguration,
runTasksInSerial,
- toJS,
Tree,
updateJson,
updateProjectConfiguration,
visitNotIgnoredFiles,
} from '@nx/devkit';
-import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
import { initGenerator as jsInitGenerator } from '@nx/js';
import { extractTsConfigBase } from '@nx/js/src/utils/typescript/create-ts-config';
@@ -37,10 +34,16 @@ import {
typescriptVersion,
typesReactDomVersion,
typesReactVersion,
+ viteVersion,
} from '../../utils/versions';
import initGenerator from '../init/init';
import { updateDependencies } from '../utils/update-dependencies';
-import { addE2E, normalizeOptions, updateUnitTestConfig } from './lib';
+import {
+ addE2E,
+ normalizeOptions,
+ updateUnitTestConfig,
+ addViteTempFilesToGitIgnore,
+} from './lib';
import { NxRemixGeneratorSchema } from './schema';
export function remixApplicationGenerator(
@@ -48,7 +51,7 @@ export function remixApplicationGenerator(
options: NxRemixGeneratorSchema
) {
return remixApplicationGeneratorInternal(tree, {
- addPlugin: false,
+ addPlugin: true,
...options,
});
}
@@ -60,62 +63,26 @@ export async function remixApplicationGeneratorInternal(
assertNotUsingTsSolutionSetup(tree, 'remix', 'application');
const options = await normalizeOptions(tree, _options);
+ if (!options.addPlugin) {
+ 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.`
+ );
+ }
+
const tasks: GeneratorCallback[] = [
await initGenerator(tree, {
skipFormat: true,
- addPlugin: options.addPlugin,
+ addPlugin: true,
}),
await jsInitGenerator(tree, { skipFormat: true }),
];
- addBuildTargetDefaults(tree, '@nx/remix:build');
-
addProjectConfiguration(tree, options.projectName, {
root: options.projectRoot,
sourceRoot: `${options.projectRoot}`,
projectType: 'application',
tags: options.parsedTags,
- targets: !options.addPlugin
- ? {
- build: {
- executor: '@nx/remix:build',
- outputs: ['{options.outputPath}'],
- options: {
- outputPath: joinPathFragments('dist', options.projectRoot),
- },
- },
- serve: {
- executor: `@nx/remix:serve`,
- options: {
- command: `${
- getPackageManagerCommand().exec
- } remix-serve build/index.js`,
- manual: true,
- port: 4200,
- },
- },
- start: {
- dependsOn: ['build'],
- command: `remix-serve build/index.js`,
- options: {
- cwd: options.projectRoot,
- },
- },
- ['serve-static']: {
- dependsOn: ['build'],
- command: `remix-serve build/index.js`,
- options: {
- cwd: options.projectRoot,
- },
- },
- typecheck: {
- command: `tsc --project tsconfig.app.json`,
- options: {
- cwd: options.projectRoot,
- },
- },
- }
- : {},
+ targets: {},
});
const installTask = updateDependencies(tree);
@@ -142,6 +109,7 @@ export async function remixApplicationGeneratorInternal(
typesReactDomVersion,
eslintVersion,
typescriptVersion,
+ viteVersion,
};
generateFiles(
@@ -186,7 +154,7 @@ export async function remixApplicationGeneratorInternal(
skipFormat: true,
testEnvironment: 'jsdom',
skipViteConfig: true,
- addPlugin: options.addPlugin,
+ addPlugin: true,
});
createOrEditViteConfig(
tree,
@@ -216,7 +184,7 @@ export async function remixApplicationGeneratorInternal(
skipSerializers: false,
skipPackageJson: false,
skipFormat: true,
- addPlugin: options.addPlugin,
+ addPlugin: true,
});
const projectConfig = readProjectConfiguration(tree, options.projectName);
if (projectConfig.targets['test']?.options) {
@@ -267,10 +235,6 @@ export async function remixApplicationGeneratorInternal(
]);
}
- if (options.js) {
- toJS(tree);
- }
-
if (options.rootProject && tree.exists('tsconfig.base.json')) {
// If this is a standalone project, merge tsconfig.json and tsconfig.base.json.
const tsConfigBaseJson = readJson(tree, 'tsconfig.base.json');
@@ -371,6 +335,7 @@ export default {...nxPreset};
}
}
+ addViteTempFilesToGitIgnore(tree);
if (!options.skipFormat) {
await formatFiles(tree);
}
diff --git a/packages/remix/src/generators/application/files/common/app/entry.client.tsx__tmpl__ b/packages/remix/src/generators/application/files/common/app/entry.client.tsx__tmpl__
new file mode 100644
index 0000000000..94d5dc0de0
--- /dev/null
+++ b/packages/remix/src/generators/application/files/common/app/entry.client.tsx__tmpl__
@@ -0,0 +1,18 @@
+/**
+ * By default, Remix will handle hydrating your app on the client for you.
+ * 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` ✨
+ * For more information, see https://remix.run/file-conventions/entry.client
+ */
+
+import { RemixBrowser } from "@remix-run/react";
+import { startTransition, StrictMode } from "react";
+import { hydrateRoot } from "react-dom/client";
+
+startTransition(() => {
+ hydrateRoot(
+ document,
+
+
+
+ );
+});
diff --git a/packages/remix/src/generators/application/files/common/app/entry.server.tsx__tmpl__ b/packages/remix/src/generators/application/files/common/app/entry.server.tsx__tmpl__
new file mode 100644
index 0000000000..45db3229c6
--- /dev/null
+++ b/packages/remix/src/generators/application/files/common/app/entry.server.tsx__tmpl__
@@ -0,0 +1,140 @@
+/**
+ * By default, Remix will handle generating the HTTP Response for you.
+ * 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` ✨
+ * For more information, see https://remix.run/file-conventions/entry.server
+ */
+
+import { PassThrough } from "node:stream";
+
+import type { AppLoadContext, EntryContext } from "@remix-run/node";
+import { createReadableStreamFromReadable } from "@remix-run/node";
+import { RemixServer } from "@remix-run/react";
+import { isbot } from "isbot";
+import { renderToPipeableStream } from "react-dom/server";
+
+const ABORT_DELAY = 5_000;
+
+export default function handleRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext,
+ // This is ignored so we can keep it in the template for visibility. Feel
+ // free to delete this parameter in your app if you're not using it!
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ loadContext: AppLoadContext
+) {
+ return isbot(request.headers.get("user-agent") || "")
+ ? handleBotRequest(
+ request,
+ responseStatusCode,
+ responseHeaders,
+ remixContext
+ )
+ : handleBrowserRequest(
+ request,
+ responseStatusCode,
+ responseHeaders,
+ remixContext
+ );
+}
+
+function handleBotRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext
+) {
+ return new Promise((resolve, reject) => {
+ let shellRendered = false;
+ const { pipe, abort } = renderToPipeableStream(
+ ,
+ {
+ onAllReady() {
+ shellRendered = true;
+ const body = new PassThrough();
+ const stream = createReadableStreamFromReadable(body);
+
+ responseHeaders.set("Content-Type", "text/html");
+
+ resolve(
+ new Response(stream, {
+ headers: responseHeaders,
+ status: responseStatusCode,
+ })
+ );
+
+ pipe(body);
+ },
+ onShellError(error: unknown) {
+ reject(error);
+ },
+ onError(error: unknown) {
+ responseStatusCode = 500;
+ // Log streaming rendering errors from inside the shell. Don't log
+ // errors encountered during initial shell rendering since they'll
+ // reject and get logged in handleDocumentRequest.
+ if (shellRendered) {
+ console.error(error);
+ }
+ },
+ }
+ );
+
+ setTimeout(abort, ABORT_DELAY);
+ });
+}
+
+function handleBrowserRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext
+) {
+ return new Promise((resolve, reject) => {
+ let shellRendered = false;
+ const { pipe, abort } = renderToPipeableStream(
+ ,
+ {
+ onShellReady() {
+ shellRendered = true;
+ const body = new PassThrough();
+ const stream = createReadableStreamFromReadable(body);
+
+ responseHeaders.set("Content-Type", "text/html");
+
+ resolve(
+ new Response(stream, {
+ headers: responseHeaders,
+ status: responseStatusCode,
+ })
+ );
+
+ pipe(body);
+ },
+ onShellError(error: unknown) {
+ reject(error);
+ },
+ onError(error: unknown) {
+ responseStatusCode = 500;
+ // Log streaming rendering errors from inside the shell. Don't log
+ // errors encountered during initial shell rendering since they'll
+ // reject and get logged in handleDocumentRequest.
+ if (shellRendered) {
+ console.error(error);
+ }
+ },
+ }
+ );
+
+ setTimeout(abort, ABORT_DELAY);
+ });
+}
diff --git a/packages/remix/src/generators/application/files/common/app/root.tsx__tmpl__ b/packages/remix/src/generators/application/files/common/app/root.tsx__tmpl__
index 7b97b10c54..f2bcdcbcb3 100644
--- a/packages/remix/src/generators/application/files/common/app/root.tsx__tmpl__
+++ b/packages/remix/src/generators/application/files/common/app/root.tsx__tmpl__
@@ -1,18 +1,30 @@
-import type { MetaFunction } from "@remix-run/node";
import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
+import type { MetaFunction, LinksFunction } from "@remix-run/node";
export const meta: MetaFunction = () => ([{
title: "New Remix App",
}]);
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: "preconnect", href: "https://fonts.googleapis.com" },
+ {
+ rel: "preconnect",
+ href: "https://fonts.gstatic.com",
+ crossOrigin: "anonymous",
+ },
+ {
+ rel: "stylesheet",
+ href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -22,11 +34,14 @@ export default function App() {
-
+ {children}
-
);
}
+
+export default function App() {
+ return ;
+}
diff --git a/packages/remix/src/generators/application/files/common/remix.config.js__tmpl__ b/packages/remix/src/generators/application/files/common/remix.config.js__tmpl__
deleted file mode 100644
index eb50539c38..0000000000
--- a/packages/remix/src/generators/application/files/common/remix.config.js__tmpl__
+++ /dev/null
@@ -1,17 +0,0 @@
-import {createWatchPaths} from '@nx/remix';
-import {dirname} from 'path';
-import {fileURLToPath} from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- ignoredRouteFiles: ["**/.*"],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
diff --git a/packages/remix/src/generators/application/files/common/remix.env.d.ts__tmpl__ b/packages/remix/src/generators/application/files/common/remix.env.d.ts__tmpl__
deleted file mode 100644
index dcf8c45e1d..0000000000
--- a/packages/remix/src/generators/application/files/common/remix.env.d.ts__tmpl__
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
diff --git a/packages/remix/src/generators/application/files/common/tsconfig.app.json__tmpl__ b/packages/remix/src/generators/application/files/common/tsconfig.app.json__tmpl__
index 2d7e289f96..72fbe340e2 100644
--- a/packages/remix/src/generators/application/files/common/tsconfig.app.json__tmpl__
+++ b/packages/remix/src/generators/application/files/common/tsconfig.app.json__tmpl__
@@ -1,11 +1,14 @@
{
"extends": "./tsconfig.json",
"include": [
- "remix.env.d.ts",
"app/**/*.ts",
"app/**/*.tsx",
"app/**/*.js",
- "app/**/*.jsx"
+ "app/**/*.jsx",
+ "**/.server/**/*.ts",
+ "**/.server/**/*.tsx",
+ "**/.client/**/*.ts",
+ "**/.client/**/*.tsx"
],
"exclude": [
"tests/**/*.spec.ts",
diff --git a/packages/remix/src/generators/application/files/common/tsconfig.json__tmpl__ b/packages/remix/src/generators/application/files/common/tsconfig.json__tmpl__
index 99c99823c3..2166bde140 100644
--- a/packages/remix/src/generators/application/files/common/tsconfig.json__tmpl__
+++ b/packages/remix/src/generators/application/files/common/tsconfig.json__tmpl__
@@ -2,16 +2,19 @@
"extends": "<%= offsetFromRoot %>tsconfig.base.json",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
+ "types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
- "moduleResolution": "node",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
"resolveJsonModule": true,
- "target": "ES2019",
+ "target": "ES2022",
"strict": true,
"allowJs": true,
+ "skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
- // Remix takes care of building everything in `remix build`.
+ // Vite takes care of building everything.
"noEmit": true
},
"include": [],
diff --git a/packages/remix/src/generators/application/files/common/vite.config.ts__tmpl__ b/packages/remix/src/generators/application/files/common/vite.config.ts__tmpl__
new file mode 100644
index 0000000000..a79433a590
--- /dev/null
+++ b/packages/remix/src/generators/application/files/common/vite.config.ts__tmpl__
@@ -0,0 +1,25 @@
+import { vitePlugin as remix } from '@remix-run/dev';
+import { defineConfig } from 'vite';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+
+declare module '@remix-run/node' {
+ interface Future {
+ v3_singleFetch: true;
+ }
+}
+
+export default defineConfig({
+ root: __dirname,
+ plugins: [
+ remix({
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ v3_singleFetch: true,
+ v3_lazyRouteDiscovery: true,
+ },
+ }),
+ nxViteTsPaths(),
+ ],
+});
diff --git a/packages/remix/src/generators/application/files/integrated/package.json__tmpl__ b/packages/remix/src/generators/application/files/integrated/package.json__tmpl__
index 8b3736be14..ea1aab3d4d 100644
--- a/packages/remix/src/generators/application/files/integrated/package.json__tmpl__
+++ b/packages/remix/src/generators/application/files/integrated/package.json__tmpl__
@@ -1,8 +1,6 @@
{
"private": true,
"name": "<%= projectName %>",
- "description": "",
- "license": "",
"scripts": {},
"type": "module",
"dependencies": {
@@ -18,10 +16,11 @@
"@types/react": "<%= typesReactVersion %>",
"@types/react-dom": "<%= typesReactDomVersion %>",
"eslint": "<%= eslintVersion %>",
- "typescript": "<%= typescriptVersion %>"
+ "typescript": "<%= typescriptVersion %>",
+ "vite": "<%= viteVersion %>",
},
"engines": {
- "node": ">=14"
+ "node": ">=20"
},
"sideEffects": false
}
diff --git a/packages/remix/src/generators/application/lib/add-e2e.ts b/packages/remix/src/generators/application/lib/add-e2e.ts
index 303a271bc3..f496b1fd94 100644
--- a/packages/remix/src/generators/application/lib/add-e2e.ts
+++ b/packages/remix/src/generators/application/lib/add-e2e.ts
@@ -83,10 +83,7 @@ export async function addE2E(tree: Tree, options: NormalizedSchema) {
tree,
'@nx/cypress/plugin',
buildTarget,
- joinPathFragments(
- options.e2eProjectRoot,
- `cypress.config.${options.js ? 'js' : 'ts'}`
- )
+ joinPathFragments(options.e2eProjectRoot, `cypress.config.ts`)
);
}
diff --git a/packages/remix/src/generators/application/lib/add-vite-temp-files-to-gitignore.ts b/packages/remix/src/generators/application/lib/add-vite-temp-files-to-gitignore.ts
new file mode 100644
index 0000000000..a84c07e79b
--- /dev/null
+++ b/packages/remix/src/generators/application/lib/add-vite-temp-files-to-gitignore.ts
@@ -0,0 +1,16 @@
+import { stripIndents, Tree } from '@nx/devkit';
+
+export function addViteTempFilesToGitIgnore(tree: Tree) {
+ let newGitIgnoreContents = `**/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*`;
+ if (tree.exists('.gitignore')) {
+ const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
+ if (!gitIgnoreContents.includes(newGitIgnoreContents)) {
+ newGitIgnoreContents = stripIndents`${gitIgnoreContents}
+ ${newGitIgnoreContents}`;
+
+ tree.write('.gitignore', newGitIgnoreContents);
+ }
+ } else {
+ tree.write('.gitignore', newGitIgnoreContents);
+ }
+}
diff --git a/packages/remix/src/generators/application/lib/index.ts b/packages/remix/src/generators/application/lib/index.ts
index 713c924e66..b1769741cc 100644
--- a/packages/remix/src/generators/application/lib/index.ts
+++ b/packages/remix/src/generators/application/lib/index.ts
@@ -1,3 +1,4 @@
export * from './normalize-options';
export * from './update-unit-test-config';
export * from './add-e2e';
+export * from './add-vite-temp-files-to-gitignore';
diff --git a/packages/remix/src/generators/application/schema.d.ts b/packages/remix/src/generators/application/schema.d.ts
index e3a31477a1..ddecf25896 100644
--- a/packages/remix/src/generators/application/schema.d.ts
+++ b/packages/remix/src/generators/application/schema.d.ts
@@ -4,7 +4,6 @@ export interface NxRemixGeneratorSchema {
directory: string;
name?: string;
tags?: string;
- js?: boolean;
linter?: Linter | LinterType;
unitTestRunner?: 'vitest' | 'jest' | 'none';
e2eTestRunner?: 'cypress' | 'playwright' | 'none';
diff --git a/packages/remix/src/generators/application/schema.json b/packages/remix/src/generators/application/schema.json
index 4992be7de5..0a13344175 100644
--- a/packages/remix/src/generators/application/schema.json
+++ b/packages/remix/src/generators/application/schema.json
@@ -20,11 +20,6 @@
"description": "The name of the application.",
"x-priority": "important"
},
- "js": {
- "type": "boolean",
- "description": "Generate JavaScript files rather than TypeScript files.",
- "default": false
- },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
diff --git a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts
index 0506b309b8..37e0ed1b82 100644
--- a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts
+++ b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts
@@ -25,7 +25,7 @@ export async function convertToInferred(tree: Tree, options: Schema) {
buildTargetName: 'build',
devTargetName: 'dev',
startTargetName: 'start',
- staticServeTargetName: 'static-serve',
+ serveStaticTargetName: 'serve-static',
typecheckTargetName: 'typecheck',
},
[
diff --git a/packages/remix/src/generators/init/init.spec.ts b/packages/remix/src/generators/init/init.spec.ts
index b75cc510ed..8fc1d44e83 100644
--- a/packages/remix/src/generators/init/init.spec.ts
+++ b/packages/remix/src/generators/init/init.spec.ts
@@ -18,13 +18,13 @@ describe('Remix Init Generator', () => {
const pkgJson = readJson(tree, 'package.json');
expect(pkgJson.dependencies).toMatchInlineSnapshot(`
{
- "@remix-run/serve": "^2.8.1",
+ "@remix-run/serve": "^2.13.1",
}
`);
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
{
"@nx/web": "0.0.1",
- "@remix-run/dev": "^2.8.1",
+ "@remix-run/dev": "^2.13.1",
}
`);
@@ -70,13 +70,13 @@ describe('Remix Init Generator', () => {
const pkgJson = readJson(tree, 'package.json');
expect(pkgJson.dependencies).toMatchInlineSnapshot(`
{
- "@remix-run/serve": "^2.8.1",
+ "@remix-run/serve": "^2.13.1",
}
`);
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
{
"@nx/web": "0.0.1",
- "@remix-run/dev": "^2.8.1",
+ "@remix-run/dev": "^2.13.1",
}
`);
});
diff --git a/packages/remix/src/generators/preset/lib/normalize-options.ts b/packages/remix/src/generators/preset/lib/normalize-options.ts
index 7ebd7f5447..79bee819d6 100644
--- a/packages/remix/src/generators/preset/lib/normalize-options.ts
+++ b/packages/remix/src/generators/preset/lib/normalize-options.ts
@@ -7,7 +7,6 @@ export interface NormalizedSchema extends RemixGeneratorSchema {
parsedTags: string[];
unitTestRunner?: 'jest' | 'none' | 'vitest';
e2eTestRunner?: 'cypress' | 'none';
- js?: boolean;
}
export function normalizeOptions(
diff --git a/packages/remix/src/generators/preset/preset.impl.ts b/packages/remix/src/generators/preset/preset.impl.ts
index c023fe203a..3a303053b5 100644
--- a/packages/remix/src/generators/preset/preset.impl.ts
+++ b/packages/remix/src/generators/preset/preset.impl.ts
@@ -26,7 +26,6 @@ export default async function (tree: Tree, _options: RemixGeneratorSchema) {
rootProject: true,
unitTestRunner: options.unitTestRunner ?? 'vitest',
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
- js: options.js ?? false,
addPlugin: addPluginDefault,
});
tasks.push(appGenTask);
diff --git a/packages/remix/src/generators/setup-tailwind/__snapshots__/setup-tailwind.impl.spec.ts.snap b/packages/remix/src/generators/setup-tailwind/__snapshots__/setup-tailwind.impl.spec.ts.snap
index 3d9139aa13..899be3df97 100644
--- a/packages/remix/src/generators/setup-tailwind/__snapshots__/setup-tailwind.impl.spec.ts.snap
+++ b/packages/remix/src/generators/setup-tailwind/__snapshots__/setup-tailwind.impl.spec.ts.snap
@@ -1,86 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`setup-tailwind generator should add a js tailwind config to an application correctly 1`] = `
-"import { createGlobPatternsForDependencies } from '@nx/react/tailwind';
-export default {
- content: [
- './app/**/*.{js,jsx,ts,tsx}',
- ...createGlobPatternsForDependencies(__dirname),
- ],
- theme: {
- extend: {},
- },
- plugins: [],
-};
-"
-`;
-
-exports[`setup-tailwind generator should add a js tailwind config to an application correctly 2`] = `
-"@tailwind base;
-@tailwind components;
-@tailwind utilities;
-"
-`;
-
-exports[`setup-tailwind generator should add a js tailwind config to an application correctly 3`] = `
-"import {
- Links,
- LiveReload,
- Meta,
- Outlet,
- Scripts,
- ScrollRestoration,
-} from '@remix-run/react';
-import twStyles from './tailwind.css';
-export const links = () => [{ rel: 'stylesheet', href: twStyles }];
-export const meta = () => [
- {
- title: 'New Remix App',
- },
-];
-export default function App() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-"
-`;
-
-exports[`setup-tailwind generator should add a js tailwind config to an application correctly 4`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- tailwind: true,
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
-"
-`;
-
exports[`setup-tailwind generator should add a tailwind config to an application correctly 1`] = `
"import type { Config } from 'tailwindcss';
import { createGlobPatternsForDependencies } from '@nx/react/tailwind';
@@ -99,26 +18,32 @@ export default {
`;
exports[`setup-tailwind generator should add a tailwind config to an application correctly 2`] = `
+"export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
+"
+`;
+
+exports[`setup-tailwind generator should add a tailwind config to an application correctly 3`] = `
"@tailwind base;
@tailwind components;
@tailwind utilities;
"
`;
-exports[`setup-tailwind generator should add a tailwind config to an application correctly 3`] = `
-"import type { MetaFunction, LinksFunction } from '@remix-run/node';
-import {
+exports[`setup-tailwind generator should add a tailwind config to an application correctly 4`] = `
+"import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
+import type { MetaFunction, LinksFunction } from '@remix-run/node';
import twStyles from './tailwind.css';
-export const links: LinksFunction = () => [
- { rel: 'stylesheet', href: twStyles },
-];
export const meta: MetaFunction = () => [
{
@@ -126,7 +51,21 @@ export const meta: MetaFunction = () => [
},
];
-export default function App() {
+export const links: LinksFunction = () => [
+ { rel: 'stylesheet', href: twStyles },
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -136,35 +75,16 @@ export default function App() {
-
+ {children}
-
);
}
-"
-`;
-
-exports[`setup-tailwind generator should add a tailwind config to an application correctly 4`] = `
-"import { createWatchPaths } from '@nx/remix';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-/**
- * @type {import('@remix-run/dev').AppConfig}
- */
-export default {
- tailwind: true,
- ignoredRouteFiles: ['**/.*'],
- // appDirectory: "app",
- // assetsBuildDirectory: "public/build",
- // serverBuildPath: "build/index.js",
- // publicPath: "/build/",
- watchPaths: () => createWatchPaths(__dirname),
-};
+
+export default function App() {
+ return ;
+}
"
`;
diff --git a/packages/remix/src/generators/setup-tailwind/files/postcss.config.js__tpl__ b/packages/remix/src/generators/setup-tailwind/files/postcss.config.js__tpl__
new file mode 100644
index 0000000000..2aa7205d4b
--- /dev/null
+++ b/packages/remix/src/generators/setup-tailwind/files/postcss.config.js__tpl__
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/packages/remix/src/generators/setup-tailwind/lib/index.ts b/packages/remix/src/generators/setup-tailwind/lib/index.ts
deleted file mode 100644
index f614834a1b..0000000000
--- a/packages/remix/src/generators/setup-tailwind/lib/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './update-remix-config';
diff --git a/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.spec.ts b/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.spec.ts
deleted file mode 100644
index 5589194eba..0000000000
--- a/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.spec.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { stripIndents } from '@nx/devkit';
-import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
-import { updateRemixConfig } from './update-remix-config';
-
-describe('updateRemixConfig', () => {
- it('should add tailwind property to an existing config that doesnt have it', () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace();
- tree.write(
- `remix.config.js`,
- stripIndents`module.exports = {
- ignoredRouteFiles: ['**/.*'],
- watchPaths: ['../../libs']
- };`
- );
-
- // ACT
- updateRemixConfig(tree, '.');
-
- // ASSERT
- expect(tree.read('remix.config.js', 'utf-8')).toMatchInlineSnapshot(`
- "module.exports = {
- tailwind: true,
- ignoredRouteFiles: ['**/.*'],
- watchPaths: ['../../libs']
- };"
- `);
- });
-
- it('should update tailwind property if the config has it and set to false', () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace();
- tree.write(
- `remix.config.js`,
- stripIndents`module.exports = {
- ignoredRouteFiles: ['**/.*'],
- tailwind: false,
- watchPaths: ['../../libs']
- };`
- );
-
- // ACT
- updateRemixConfig(tree, '.');
-
- // ASSERT
- expect(tree.read('remix.config.js', 'utf-8')).toMatchInlineSnapshot(`
- "module.exports = {
- ignoredRouteFiles: ['**/.*'],
- tailwind: true,
- watchPaths: ['../../libs']
- };"
- `);
- });
-
- it('should not update tailwind property if the config has it and set to true', () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace();
- tree.write(
- `remix.config.js`,
- stripIndents`module.exports = {
- ignoredRouteFiles: ['**/.*'],
- tailwind: true,
- watchPaths: ['../../libs']
- };`
- );
-
- // ACT
- updateRemixConfig(tree, '.');
-
- // ASSERT
- expect(tree.read('remix.config.js', 'utf-8')).toMatchInlineSnapshot(`
- "module.exports = {
- ignoredRouteFiles: ['**/.*'],
- tailwind: true,
- watchPaths: ['../../libs']
- };"
- `);
- });
-});
diff --git a/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.ts b/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.ts
deleted file mode 100644
index 22189d9358..0000000000
--- a/packages/remix/src/generators/setup-tailwind/lib/update-remix-config.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { joinPathFragments, type Tree } from '@nx/devkit';
-import { tsquery } from '@phenomnomnominal/tsquery';
-import { getRemixConfigPathFromProjectRoot } from '../../../utils/remix-config';
-
-export function updateRemixConfig(tree: Tree, projectRoot: string) {
- const pathToRemixConfig = getRemixConfigPathFromProjectRoot(
- tree,
- projectRoot
- );
- const fileContents = tree.read(pathToRemixConfig, 'utf-8');
-
- const REMIX_CONFIG_OBJECT_SELECTOR = 'ObjectLiteralExpression';
- const ast = tsquery.ast(fileContents);
-
- const nodes = tsquery(ast, REMIX_CONFIG_OBJECT_SELECTOR, {
- visitAllChildren: true,
- });
- if (nodes.length === 0) {
- throw new Error(`Remix Config is not valid, unable to update the file.`);
- }
-
- const configObjectNode = nodes[0];
-
- const propertyNodes = tsquery(configObjectNode, 'PropertyAssignment', {
- visitAllChildren: true,
- });
-
- for (const propertyNode of propertyNodes) {
- const nodeText = propertyNode.getText();
- if (nodeText.includes('tailwind') && nodeText.includes('true')) {
- return;
- } else if (nodeText.includes('tailwind') && nodeText.includes('false')) {
- const updatedFileContents = `${fileContents.slice(
- 0,
- propertyNode.getStart()
- )}tailwind: true${fileContents.slice(propertyNode.getEnd())}`;
- tree.write(pathToRemixConfig, updatedFileContents);
- return;
- }
- }
-
- const updatedFileContents = `${fileContents.slice(
- 0,
- configObjectNode.getStart() + 1
- )}\ntailwind: true,${fileContents.slice(configObjectNode.getStart() + 1)}`;
-
- tree.write(pathToRemixConfig, updatedFileContents);
-}
diff --git a/packages/remix/src/generators/setup-tailwind/schema.d.ts b/packages/remix/src/generators/setup-tailwind/schema.d.ts
index 2a098d99ba..a15c80d58d 100644
--- a/packages/remix/src/generators/setup-tailwind/schema.d.ts
+++ b/packages/remix/src/generators/setup-tailwind/schema.d.ts
@@ -1,5 +1,4 @@
export interface SetupTailwindSchema {
project: string;
- js?: boolean;
skipFormat?: boolean;
}
diff --git a/packages/remix/src/generators/setup-tailwind/schema.json b/packages/remix/src/generators/setup-tailwind/schema.json
index 01827985c5..9b49050ae7 100644
--- a/packages/remix/src/generators/setup-tailwind/schema.json
+++ b/packages/remix/src/generators/setup-tailwind/schema.json
@@ -20,11 +20,6 @@
"x-prompt": "What project would you like to add Tailwind to?",
"pattern": "^[a-zA-Z].*$"
},
- "js": {
- "type": "boolean",
- "description": "Generate a JavaScript config file instead of a TypeScript config file",
- "default": false
- },
"skipFormat": {
"type": "boolean",
"description": "Skip formatting files after generator runs",
diff --git a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts
index 1a5fc07d7f..d4d1472cc5 100644
--- a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts
+++ b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts
@@ -21,35 +21,11 @@ describe('setup-tailwind generator', () => {
// ASSERT
expect(tree.exists('tailwind.config.ts')).toBeTruthy();
expect(tree.read('tailwind.config.ts', 'utf-8')).toMatchSnapshot();
+ expect(tree.exists('postcss.config.js')).toBeTruthy();
+ expect(tree.read('postcss.config.js', 'utf-8')).toMatchSnapshot();
expect(tree.exists('app/tailwind.css')).toBeTruthy();
expect(tree.read('app/tailwind.css', 'utf-8')).toMatchSnapshot();
expect(tree.read('app/root.tsx', 'utf-8')).toMatchSnapshot();
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
- expect(
- readJson(tree, 'package.json').dependencies['tailwindcss']
- ).toBeTruthy();
- });
-
- it('should add a js tailwind config to an application correctly', async () => {
- // ARRANGE
- const tree = createTreeWithEmptyWorkspace();
- await applicationGenerator(tree, {
- name: 'test',
- directory: '.',
- js: true,
- rootProject: true,
- });
-
- // ACT
- await setupTailwind(tree, { project: 'test', js: true });
-
- // ASSERT
- expect(tree.exists('tailwind.config.js')).toBeTruthy();
- expect(tree.read('tailwind.config.js', 'utf-8')).toMatchSnapshot();
- expect(tree.exists('app/tailwind.css')).toBeTruthy();
- expect(tree.read('app/tailwind.css', 'utf-8')).toMatchSnapshot();
- expect(tree.read('app/root.js', 'utf-8')).toMatchSnapshot();
- expect(tree.read('remix.config.js', 'utf-8')).toMatchSnapshot();
expect(
readJson(tree, 'package.json').dependencies['tailwindcss']
).toBeTruthy();
diff --git a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.ts b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.ts
index 7cf86afb6e..666e41127b 100644
--- a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.ts
+++ b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.ts
@@ -5,13 +5,15 @@ import {
installPackagesTask,
joinPathFragments,
readProjectConfiguration,
- toJS,
type Tree,
} from '@nx/devkit';
import { upsertLinksFunction } from '../../utils/upsert-links-function';
-import { tailwindVersion } from '../../utils/versions';
-import { updateRemixConfig } from './lib';
+import {
+ autoprefixerVersion,
+ postcssVersion,
+ tailwindVersion,
+} from '../../utils/versions';
import type { SetupTailwindSchema } from './schema';
export default async function setupTailwind(
@@ -25,18 +27,10 @@ export default async function setupTailwind(
);
}
- updateRemixConfig(tree, project.root);
-
generateFiles(tree, joinPathFragments(__dirname, 'files'), project.root, {
tpl: '',
});
- if (options.js) {
- tree.rename(
- joinPathFragments(project.root, 'app/root.js'),
- joinPathFragments(project.root, 'app/root.tsx')
- );
- }
const pathToRoot = joinPathFragments(project.root, 'app/root.tsx');
upsertLinksFunction(
tree,
@@ -50,14 +44,12 @@ export default async function setupTailwind(
tree,
{
tailwindcss: tailwindVersion,
+ postcss: postcssVersion,
+ autoprefixer: autoprefixerVersion,
},
{}
);
- if (options.js) {
- toJS(tree);
- }
-
if (!options.skipFormat) {
await formatFiles(tree);
}
diff --git a/packages/remix/src/generators/style/style.impl.spec.ts b/packages/remix/src/generators/style/style.impl.spec.ts
index f08e872a28..3033d88a94 100644
--- a/packages/remix/src/generators/style/style.impl.spec.ts
+++ b/packages/remix/src/generators/style/style.impl.spec.ts
@@ -9,7 +9,7 @@ import presetGenerator from '../preset/preset.impl';
import routeGenerator from '../route/route.impl';
import styleGenerator from './style.impl';
-describe('route', () => {
+describe('style', () => {
let tree: Tree;
beforeEach(() => {
@@ -63,21 +63,10 @@ describe('route', () => {
it('should place styles correctly when app dir is changed', async () => {
await applicationGenerator(tree, { name: 'demo', directory: 'apps/demo' });
- tree.write(
- 'apps/demo/remix.config.js',
- `
- /**
- * @type {import('@remix-run/dev').AppConfig}
- */
- module.exports = {
- ignoredRouteFiles: ["**/.*"],
- appDirectory: "my-custom-dir",
- };`
- );
(remixConfigUtils.getRemixConfigValues as jest.Mock) = jest.fn(() =>
Promise.resolve({
ignoredRouteFiles: ['**/.*'],
- appDirectory: 'my-custom-dir',
+ appDirectory: 'apps/demo/my-custom-dir',
})
);
diff --git a/packages/remix/src/generators/utils/update-dependencies.ts b/packages/remix/src/generators/utils/update-dependencies.ts
index 3ca39271b3..ff9dcdb20e 100644
--- a/packages/remix/src/generators/utils/update-dependencies.ts
+++ b/packages/remix/src/generators/utils/update-dependencies.ts
@@ -2,12 +2,14 @@ import { type Tree, addDependenciesToPackageJson } from '@nx/devkit';
import {
eslintVersion,
isbotVersion,
+ nxVersion,
reactDomVersion,
reactVersion,
remixVersion,
typescriptVersion,
typesReactDomVersion,
typesReactVersion,
+ viteVersion,
} from '../../utils/versions';
export function updateDependencies(tree: Tree) {
@@ -25,6 +27,8 @@ export function updateDependencies(tree: Tree) {
'@types/react-dom': typesReactDomVersion,
eslint: eslintVersion,
typescript: typescriptVersion,
+ vite: viteVersion,
+ '@nx/vite': nxVersion,
}
);
}
diff --git a/packages/remix/src/plugins/plugin.ts b/packages/remix/src/plugins/plugin.ts
index b84e171e5d..1bff582ded 100644
--- a/packages/remix/src/plugins/plugin.ts
+++ b/packages/remix/src/plugins/plugin.ts
@@ -338,17 +338,22 @@ async function getBuildPaths(
// do nothing
}
const { resolveConfig } = await loadViteDynamicImport();
- const viteBuildConfig = await resolveConfig(
+ const viteBuildConfig = (await resolveConfig(
{
configFile: configPath,
mode: 'development',
},
'build'
- );
+ )) as any;
return {
buildDirectory: viteBuildConfig.build?.outDir ?? 'build',
- serverBuildPath: viteBuildConfig.build?.outDir ?? 'build',
+ serverBuildPath: viteBuildConfig.build?.outDir
+ ? join(
+ dirname(viteBuildConfig.build?.outDir),
+ `server/${viteBuildConfig.__remixPluginContext?.remixConfig.serverBuildFile}`
+ )
+ : 'build',
assetsBuildDirectory: 'build/client',
};
}
diff --git a/packages/remix/src/utils/remix-config.ts b/packages/remix/src/utils/remix-config.ts
index 2e97e6dc2d..a9916bb1fb 100644
--- a/packages/remix/src/utils/remix-config.ts
+++ b/packages/remix/src/utils/remix-config.ts
@@ -5,7 +5,26 @@ import {
workspaceRoot,
} from '@nx/devkit';
import type { AppConfig } from '@remix-run/dev';
-import { createContext, SourceTextModule } from 'vm';
+import { loadViteDynamicImport } from './executor-utils';
+
+export function getRemixConfigPathDetails(tree: Tree, projectName: string) {
+ const project = readProjectConfiguration(tree, projectName);
+ if (!project) throw new Error(`Project does not exist: ${projectName}`);
+
+ for (const ext of ['.mjs', '.cjs', '.js', '.mts', '.cts', '.ts']) {
+ const configPath = joinPathFragments(project.root, `vite.config${ext}`);
+ if (tree.exists(configPath)) {
+ return [configPath, 'vite'];
+ }
+ }
+
+ for (const ext of ['.mjs', '.cjs', '.js']) {
+ const configPath = joinPathFragments(project.root, `remix.config${ext}`);
+ if (tree.exists(configPath)) {
+ return [configPath, 'classic'];
+ }
+ }
+}
export function getRemixConfigPath(tree: Tree, projectName: string) {
const project = readProjectConfiguration(tree, projectName);
@@ -39,21 +58,32 @@ export function getRemixConfigPathFromProjectRoot(
const _remixConfigCache: Record = {};
export async function getRemixConfigValues(tree: Tree, projectName: string) {
- const remixConfigPath = joinPathFragments(
- workspaceRoot,
- getRemixConfigPath(tree, projectName)
- );
+ const [configPath, configType] = getRemixConfigPathDetails(tree, projectName);
+ const remixConfigPath = joinPathFragments(workspaceRoot, configPath);
const cacheKey = `${projectName}/${remixConfigPath}`;
let appConfig = _remixConfigCache[cacheKey];
+ let resolvedConfig: any;
if (!appConfig) {
- try {
- const importedConfig = await Function(
- `return import("${remixConfigPath}?t=${Date.now()}")`
- )();
- appConfig = (importedConfig?.default || importedConfig) as AppConfig;
- } catch {
- appConfig = require(remixConfigPath);
+ if (configType === 'vite') {
+ const { resolveConfig } = await loadViteDynamicImport();
+ const viteBuildConfig = (await resolveConfig(
+ {
+ configFile: configPath,
+ mode: 'development',
+ },
+ 'build'
+ )) as any;
+ appConfig = viteBuildConfig.__remixPluginContext?.remixConfig;
+ } else {
+ try {
+ const importedConfig = await Function(
+ `return import("${remixConfigPath}?t=${Date.now()}")`
+ )();
+ appConfig = (importedConfig?.default || importedConfig) as AppConfig;
+ } catch {
+ appConfig = require(remixConfigPath);
+ }
}
_remixConfigCache[cacheKey] = appConfig;
}
diff --git a/packages/remix/src/utils/remix-route-utils.ts b/packages/remix/src/utils/remix-route-utils.ts
index 36da8f6c2d..ac4ba4c9c8 100644
--- a/packages/remix/src/utils/remix-route-utils.ts
+++ b/packages/remix/src/utils/remix-route-utils.ts
@@ -5,6 +5,7 @@ import {
Tree,
} from '@nx/devkit';
import { getRemixConfigValues } from './remix-config';
+import { relative } from 'path';
/**
*
@@ -91,5 +92,10 @@ export async function resolveRemixAppDirectory(
const project = readProjectConfiguration(tree, projectName);
const remixConfig = await getRemixConfigValues(tree, projectName);
- return joinPathFragments(project.root, remixConfig.appDirectory ?? 'app');
+ return joinPathFragments(
+ project.root,
+ remixConfig.appDirectory
+ ? relative(project.root, remixConfig.appDirectory)
+ : 'app'
+ );
}
diff --git a/packages/remix/src/utils/versions.ts b/packages/remix/src/utils/versions.ts
index c2e2ae4901..b399a2828f 100644
--- a/packages/remix/src/utils/versions.ts
+++ b/packages/remix/src/utils/versions.ts
@@ -2,7 +2,7 @@ import { readJson, Tree } from '@nx/devkit';
export const nxVersion = require('../../package.json').version;
-export const remixVersion = '^2.8.1';
+export const remixVersion = '^2.13.1';
export const isbotVersion = '^4.4.0';
export const reactVersion = '^18.2.0';
export const reactDomVersion = '^18.2.0';
@@ -11,10 +11,13 @@ export const typesReactDomVersion = '^18.2.0';
export const eslintVersion = '^8.56.0';
export const typescriptVersion = '~5.5.2';
export const tailwindVersion = '^3.3.0';
+export const postcssVersion = '^8.4.38';
+export const autoprefixerVersion = '^10.4.19';
export const testingLibraryReactVersion = '^14.1.2';
// TODO(colum): Unpin this when @testing-library/jest-dom pushes a fix
export const testingLibraryJestDomVersion = '6.4.2';
export const testingLibraryUserEventsVersion = '^14.5.2';
+export const viteVersion = '^5.0.0';
export function getRemixVersion(tree: Tree): string {
return getPackageVersion(tree, '@remix-run/dev') ?? remixVersion;
diff --git a/packages/vite/src/utils/versions.ts b/packages/vite/src/utils/versions.ts
index f121493edb..c107ddc895 100644
--- a/packages/vite/src/utils/versions.ts
+++ b/packages/vite/src/utils/versions.ts
@@ -1,4 +1,5 @@
export const nxVersion = require('../../package.json').version;
+// Also update @nx/remix/utils/versions when changing vite version
export const viteVersion = '^5.0.0';
export const vitestVersion = '^1.3.1';
export const vitePluginReactVersion = '^4.2.0';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e0235722aa..58436781d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -368,11 +368,11 @@ importers:
specifier: 1.9.0
version: 1.9.0(react-redux@8.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1))(react@18.3.1)
'@remix-run/dev':
- specifier: ^2.8.1
- version: 2.12.0(@remix-run/react@2.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.5.4))(typescript@5.5.4)(vite@5.0.8(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6))
+ specifier: ^2.13.1
+ version: 2.13.1(@remix-run/react@2.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.5.4))(typescript@5.5.4)(vite@5.0.8(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6))
'@remix-run/node':
- specifier: ^2.8.1
- version: 2.12.0(typescript@5.5.4)
+ specifier: ^2.13.1
+ version: 2.13.1(typescript@5.5.4)
'@rollup/plugin-babel':
specifier: ^6.0.4
version: 6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.22.0)
@@ -5322,13 +5322,13 @@ packages:
react-redux:
optional: true
- '@remix-run/dev@2.12.0':
- resolution: {integrity: sha512-/87YQORdlJg5YChd7nVBM/hRXHZA4GfUjhKbZyNrh03bazCQBF+6EsXbzpJ6cCFOpZgecsN0Xv648Qw0VuJjwg==}
+ '@remix-run/dev@2.13.1':
+ resolution: {integrity: sha512-7+06Dail6zMyRlRvgrZ4cmQjs2gUb+M24iP4jbmql+0B7VAAPwzCRU0x+BF5z8GSef13kDrH3iXv/BQ2O2yOgw==}
engines: {node: '>=18.0.0'}
hasBin: true
peerDependencies:
- '@remix-run/react': ^2.12.0
- '@remix-run/serve': ^2.12.0
+ '@remix-run/react': ^2.13.1
+ '@remix-run/serve': ^2.13.1
typescript: ^5.1.0
vite: ^5.1.0
wrangler: ^3.28.2
@@ -5342,8 +5342,8 @@ packages:
wrangler:
optional: true
- '@remix-run/node@2.12.0':
- resolution: {integrity: sha512-83Jaoc6gpSuD4e6rCk7N5ZHAXNmDw4fJC+kPeDCsd6+wLtTLSi7u9Zo9/Q7moLZ3oyH+aR+LGdkxLULYv+Q6Og==}
+ '@remix-run/node@2.13.1':
+ resolution: {integrity: sha512-2ly7bENj2n2FNBdEN60ZEbNCs5dAOex/QJoo6EZ8RNFfUQxVKAZkMwfQ4ETV2SLWDgkRLj3Jo5n/dx7O2ZGhGw==}
engines: {node: '>=18.0.0'}
peerDependencies:
typescript: ^5.1.0
@@ -5366,6 +5366,10 @@ packages:
resolution: {integrity: sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==}
engines: {node: '>=14.0.0'}
+ '@remix-run/router@1.20.0':
+ resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==}
+ engines: {node: '>=14.0.0'}
+
'@remix-run/server-runtime@2.12.0':
resolution: {integrity: sha512-o9ukOr3XKmyY8UufTrDdkgD3fiy+z+f4qEzvCQnvC0+EasCyN9hb1Vbui6Koo/5HKvahC4Ga8RcWyvhykKrG3g==}
engines: {node: '>=18.0.0'}
@@ -5375,6 +5379,15 @@ packages:
typescript:
optional: true
+ '@remix-run/server-runtime@2.13.1':
+ resolution: {integrity: sha512-2DfBPRcHKVzE4bCNsNkKB50BhCCKF73x+jiS836OyxSIAL+x0tguV2AEjmGXefEXc5AGGzoxkus0AUUEYa29Vg==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ typescript: ^5.1.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@remix-run/web-blob@3.1.0':
resolution: {integrity: sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g==}
@@ -22716,7 +22729,7 @@ snapshots:
react: 18.3.1
react-redux: 8.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1)
- '@remix-run/dev@2.12.0(@remix-run/react@2.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.5.4))(typescript@5.5.4)(vite@5.0.8(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6))':
+ '@remix-run/dev@2.13.1(@remix-run/react@2.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.5.4))(typescript@5.5.4)(vite@5.0.8(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6))':
dependencies:
'@babel/core': 7.25.2
'@babel/generator': 7.25.6
@@ -22728,10 +22741,10 @@ snapshots:
'@babel/types': 7.25.6
'@mdx-js/mdx': 2.3.0
'@npmcli/package-json': 4.0.1
- '@remix-run/node': 2.12.0(typescript@5.5.4)
+ '@remix-run/node': 2.13.1(typescript@5.5.4)
'@remix-run/react': 2.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)
- '@remix-run/router': 1.19.2
- '@remix-run/server-runtime': 2.12.0(typescript@5.5.4)
+ '@remix-run/router': 1.20.0
+ '@remix-run/server-runtime': 2.13.1(typescript@5.5.4)
'@types/mdx': 2.0.13
'@vanilla-extract/integration': 6.5.0(@types/node@20.16.10)(less@4.1.3)(sass@1.55.0)(stylus@0.59.0)(terser@5.31.6)
arg: 5.0.2
@@ -22791,9 +22804,9 @@ snapshots:
- ts-node
- utf-8-validate
- '@remix-run/node@2.12.0(typescript@5.5.4)':
+ '@remix-run/node@2.13.1(typescript@5.5.4)':
dependencies:
- '@remix-run/server-runtime': 2.12.0(typescript@5.5.4)
+ '@remix-run/server-runtime': 2.13.1(typescript@5.5.4)
'@remix-run/web-fetch': 4.4.2
'@web3-storage/multipart-parser': 1.0.0
cookie-signature: 1.2.1
@@ -22817,6 +22830,8 @@ snapshots:
'@remix-run/router@1.19.2': {}
+ '@remix-run/router@1.20.0': {}
+
'@remix-run/server-runtime@2.12.0(typescript@5.5.4)':
dependencies:
'@remix-run/router': 1.19.2
@@ -22829,6 +22844,18 @@ snapshots:
optionalDependencies:
typescript: 5.5.4
+ '@remix-run/server-runtime@2.13.1(typescript@5.5.4)':
+ dependencies:
+ '@remix-run/router': 1.20.0
+ '@types/cookie': 0.6.0
+ '@web3-storage/multipart-parser': 1.0.0
+ cookie: 0.6.0
+ set-cookie-parser: 2.7.0
+ source-map: 0.7.3
+ turbo-stream: 2.4.0
+ optionalDependencies:
+ typescript: 5.5.4
+
'@remix-run/web-blob@3.1.0':
dependencies:
'@remix-run/web-stream': 1.1.0