feat(remix): generate remix vite application (#28555)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> We currently still generate Remix Classic applications ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> We should generate Remix Vite applications ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
a9dbc71e9d
commit
af9d980f34
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,9 @@
|
||||
"tslib": "^2.3.1",
|
||||
"@phenomnomnominal/tsquery": "~5.0.1"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"peerDependencies": {
|
||||
"@remix-run/dev": "^2.13.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@ -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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -48,18 +39,21 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -120,18 +105,21 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Remix Application Integrated Repo --js should create the application correctly 3`] = `
|
||||
"import NxWelcome from '../nx-welcome';
|
||||
export default function Index() {
|
||||
return (
|
||||
<div>
|
||||
<NxWelcome title={'test'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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`] = `
|
||||
"/// <reference types='vitest' />
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -488,18 +357,21 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Remix Application Standalone Project Repo --js should create the application correctly 3`] = `
|
||||
"import NxWelcome from '../nx-welcome';
|
||||
export default function Index() {
|
||||
return (
|
||||
<div>
|
||||
<NxWelcome title={'test'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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: ['<rootDir>/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`] = `
|
||||
"/// <reference types='vitest' />
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -827,18 +580,21 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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"],
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
<StrictMode>
|
||||
<RemixBrowser />
|
||||
</StrictMode>
|
||||
);
|
||||
});
|
||||
@ -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(
|
||||
<RemixServer
|
||||
context={remixContext}
|
||||
url={request.url}
|
||||
abortDelay={ABORT_DELAY}
|
||||
/>,
|
||||
{
|
||||
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(
|
||||
<RemixServer
|
||||
context={remixContext}
|
||||
url={request.url}
|
||||
abortDelay={ABORT_DELAY}
|
||||
/>,
|
||||
{
|
||||
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);
|
||||
});
|
||||
}
|
||||
@ -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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -22,11 +34,14 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
@ -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),
|
||||
};
|
||||
@ -1,2 +0,0 @@
|
||||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node" />
|
||||
@ -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",
|
||||
|
||||
@ -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": [],
|
||||
|
||||
@ -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(),
|
||||
],
|
||||
});
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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`)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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';
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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',
|
||||
},
|
||||
[
|
||||
|
||||
@ -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",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
@ -7,7 +7,6 @@ export interface NormalizedSchema extends RemixGeneratorSchema {
|
||||
parsedTags: string[];
|
||||
unitTestRunner?: 'jest' | 'none' | 'vitest';
|
||||
e2eTestRunner?: 'cypress' | 'none';
|
||||
js?: boolean;
|
||||
}
|
||||
|
||||
export function normalizeOptions(
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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 (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -136,35 +75,16 @@ export default function App() {
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
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 <Outlet />;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@ -1 +0,0 @@
|
||||
export * from './update-remix-config';
|
||||
@ -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']
|
||||
};"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -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);
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
export interface SetupTailwindSchema {
|
||||
project: string;
|
||||
js?: boolean;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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',
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -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',
|
||||
};
|
||||
}
|
||||
|
||||
@ -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,14 +58,24 @@ export function getRemixConfigPathFromProjectRoot(
|
||||
const _remixConfigCache: Record<string, AppConfig> = {};
|
||||
|
||||
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) {
|
||||
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()}")`
|
||||
@ -55,6 +84,7 @@ export async function getRemixConfigValues(tree: Tree, projectName: string) {
|
||||
} catch {
|
||||
appConfig = require(remixConfigPath);
|
||||
}
|
||||
}
|
||||
_remixConfigCache[cacheKey] = appConfig;
|
||||
}
|
||||
|
||||
|
||||
@ -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'
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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';
|
||||
|
||||
59
pnpm-lock.yaml
generated
59
pnpm-lock.yaml
generated
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user