Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(remix): add option to create-nx-workspace #22334

Merged
merged 1 commit into from Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/generated/cli/create-nx-workspace.md
Expand Up @@ -145,7 +145,7 @@ Prefix to use for Angular component and directive selectors.

Type: `string`

Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "vue-monorepo", "vue-standalone", "nuxt", "nuxt-standalone", "next", "nextjs-standalone", "react-native", "expo", "nest", "express", "react", "vue", "angular", "node-standalone", "node-monorepo", "ts-standalone"]. To build your own see https://nx.dev/extending-nx/recipes/create-preset
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "vue-monorepo", "vue-standalone", "nuxt", "nuxt-standalone", "next", "nextjs-standalone", "remix-monorepo", "remix-standalone", "react-native", "expo", "nest", "express", "react", "vue", "angular", "node-standalone", "node-monorepo", "ts-standalone"]. To build your own see https://nx.dev/extending-nx/recipes/create-preset

### routing

Expand Down
Expand Up @@ -145,7 +145,7 @@ Prefix to use for Angular component and directive selectors.

Type: `string`

Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "vue-monorepo", "vue-standalone", "nuxt", "nuxt-standalone", "next", "nextjs-standalone", "react-native", "expo", "nest", "express", "react", "vue", "angular", "node-standalone", "node-monorepo", "ts-standalone"]. To build your own see https://nx.dev/extending-nx/recipes/create-preset
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "vue-monorepo", "vue-standalone", "nuxt", "nuxt-standalone", "next", "nextjs-standalone", "remix-monorepo", "remix-standalone", "react-native", "expo", "nest", "express", "react", "vue", "angular", "node-standalone", "node-monorepo", "ts-standalone"]. To build your own see https://nx.dev/extending-nx/recipes/create-preset

### routing

Expand Down
26 changes: 22 additions & 4 deletions packages/create-nx-workspace/bin/create-nx-workspace.ts
Expand Up @@ -43,7 +43,7 @@ interface ReactArguments extends BaseArguments {
stack: 'react';
workspaceType: 'standalone' | 'integrated';
appName: string;
framework: 'none' | 'next';
framework: 'none' | 'next' | 'remix';
style: string;
bundler: 'webpack' | 'vite' | 'rspack';
nextAppDir: boolean;
Expand Down Expand Up @@ -378,6 +378,8 @@ async function determineStack(
case Preset.ReactMonorepo:
case Preset.NextJs:
case Preset.NextJsStandalone:
case Preset.RemixStandalone:
case Preset.RemixMonorepo:
return 'react';
case Preset.Vue:
case Preset.VueStandalone:
Expand Down Expand Up @@ -520,7 +522,8 @@ async function determineReactOptions(
preset = parsedArgs.preset;
if (
preset === Preset.ReactStandalone ||
preset === Preset.NextJsStandalone
preset === Preset.NextJsStandalone ||
preset === Preset.RemixStandalone
) {
appName = parsedArgs.appName ?? parsedArgs.name;
} else {
Expand Down Expand Up @@ -548,6 +551,12 @@ async function determineReactOptions(
} else {
preset = Preset.NextJs;
}
} else if (framework === 'remix') {
if (workspaceType === 'standalone') {
preset = Preset.RemixStandalone;
} else {
preset = Preset.RemixMonorepo;
}
} else if (framework === 'react-native') {
preset = Preset.ReactNative;
} else if (framework === 'expo') {
Expand All @@ -568,6 +577,11 @@ async function determineReactOptions(
nextAppDir = await determineNextAppDir(parsedArgs);
nextSrcDir = await determineNextSrcDir(parsedArgs);
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
} else if (
preset === Preset.RemixMonorepo ||
preset === Preset.RemixStandalone
) {
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
}

if (parsedArgs.style) {
Expand Down Expand Up @@ -1017,9 +1031,9 @@ async function determineAppName(

async function determineReactFramework(
parsedArgs: yargs.Arguments<ReactArguments>
): Promise<'none' | 'nextjs' | 'expo' | 'react-native'> {
): Promise<'none' | 'nextjs' | 'remix' | 'expo' | 'react-native'> {
const reply = await enquirer.prompt<{
framework: 'none' | 'nextjs' | 'expo' | 'react-native';
framework: 'none' | 'nextjs' | 'remix' | 'expo' | 'react-native';
}>([
{
name: 'framework',
Expand All @@ -1035,6 +1049,10 @@ async function determineReactFramework(
name: 'nextjs',
message: 'Next.js [ https://nextjs.org/ ]',
},
{
name: 'remix',
message: 'Remix [ https://remix.run/ ]',
},
{
name: 'expo',
message: 'Expo [ https://expo.io/ ]',
Expand Down
2 changes: 2 additions & 0 deletions packages/create-nx-workspace/src/utils/preset/preset.ts
Expand Up @@ -15,6 +15,8 @@ export enum Preset {
NuxtStandalone = 'nuxt-standalone',
NextJs = 'next',
NextJsStandalone = 'nextjs-standalone',
RemixMonorepo = 'remix-monorepo',
RemixStandalone = 'remix-standalone',
ReactNative = 'react-native',
Expo = 'expo',
Nest = 'nest',
Expand Down
Expand Up @@ -1147,8 +1147,7 @@ export default {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: './coverage/test',
testMatch: [
'<rootDir>/src/**/__tests__/**/*.[jt]s?(x)',
'<rootDir>/src/**/*(*.)@(spec|test).[jt]s?(x)',
'<rootDir>/tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
],
};
"
Expand Down
23 changes: 23 additions & 0 deletions packages/remix/src/generators/application/application.impl.ts
Expand Up @@ -35,6 +35,7 @@ import initGenerator from '../init/init';
import { initGenerator as jsInitGenerator } from '@nx/js';
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/add-build-target-defaults';
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
import { updateJestTestMatch } from '../../utils/testing-config-utils';

export function remixApplicationGenerator(
tree: Tree,
Expand Down Expand Up @@ -267,6 +268,28 @@ export async function remixApplicationGeneratorInternal(
extractTsConfigBase(tree);
}

if (options.rootProject) {
updateJson(tree, `package.json`, (json) => {
json.type = 'module';
return json;
});

if (options.unitTestRunner === 'jest') {
tree.write(
'jest.preset.js',
`import { nxPreset } from '@nx/jest/preset/jest-preset.js';
export default {...nxPreset};
`
);

updateJestTestMatch(
tree,
'jest.config.ts',
'<rootDir>/tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'
);
}
}

tasks.push(await addE2E(tree, options));

if (!options.skipFormat) {
Expand Down
27 changes: 27 additions & 0 deletions packages/remix/src/utils/testing-config-utils.ts
Expand Up @@ -100,6 +100,33 @@ export function updateJestTestSetup(
}
}

export function updateJestTestMatch(
tree: Tree,
pathToJestConfig: string,
includesString: string
) {
if (!tsModule) {
tsModule = ensureTypescript();
}
const { tsquery } = require('@phenomnomnominal/tsquery');
const fileContents = tree.read(pathToJestConfig, 'utf-8');

const ast = tsquery.ast(fileContents);

const TEST_MATCH_SELECTOR =
'PropertyAssignment:has(Identifier[name=testMatch])';
const nodes = tsquery(ast, TEST_MATCH_SELECTOR, { visitAllChildren: true });

if (nodes.length !== 0) {
const updatedFileContents = stripIndents`${fileContents.slice(
0,
nodes[0].getStart()
)}testMatch: ["${includesString}"]${fileContents.slice(nodes[0].getEnd())}`;

tree.write(pathToJestConfig, updatedFileContents);
}
}

export function updateVitestTestIncludes(
tree: Tree,
pathToVitestConfig: string,
Expand Down
Expand Up @@ -1286,6 +1286,166 @@ It will show tasks that you can run with Nx.
"
`;

exports[`@nx/workspace:generateWorkspaceFiles README.md should be created for RemixMonorepo preset 1`] = `
"# Proj

<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>

✨ **This workspace has been generated by [Nx, Smart Monorepos · Fast CI.](https://nx.dev)** ✨

## Integrate with editors

Enhance your Nx experience by installing [Nx Console](https://nx.dev/nx-console) for your favorite editor. Nx Console
provides an interactive UI to view your projects, run tasks, generate code, and more! Available for VSCode, IntelliJ and
comes with a LSP for Vim users.

## Nx plugins and code generators

Add Nx plugins to leverage their code generators and automated, inferred tasks.

\`\`\`
# Add plugin
npx nx add @nx/react

# Use code generator
npx nx generate @nx/react:app demo

# Run development server
npx nx serve demo

# View project details
npx nx show project demo --web
\`\`\`

Run \`npx nx list\` to get a list of available plugins and whether they have generators. Then run \`npx nx list <plugin-name>\` to see what generators are available.

Learn more about [code generators](https://nx.dev/features/generate-code) and [inferred tasks](https://nx.dev/concepts/inferred-tasks) in the docs.

## Running tasks

To execute tasks with Nx use the following syntax:

\`\`\`
npx nx <target> <project> <...options>
\`\`\`

You can also run multiple targets:

\`\`\`
npx nx run-many -t <target1> <target2>
\`\`\`

..or add \`-p\` to filter specific projects

\`\`\`
npx nx run-many -t <target1> <target2> -p <proj1> <proj2>
\`\`\`

Targets can be defined in the \`package.json\` or \`projects.json\`. Learn more [in the docs](https://nx.dev/features/run-tasks).

## Set up CI!

Nx comes with local caching already built-in (check your \`nx.json\`). On CI you might want to go a step further.

- [Set up remote caching](https://nx.dev/features/share-your-cache)
- [Set up task distribution across multiple machines](https://nx.dev/nx-cloud/features/distribute-task-execution)
- [Learn more how to setup CI](https://nx.dev/recipes/ci)

## Explore the project graph

Run \`npx nx graph\` to show the graph of the workspace.
It will show tasks that you can run with Nx.

- [Learn more about Exploring the Project Graph](https://nx.dev/core-features/explore-graph)

## Connect with us!

- [Join the community](https://nx.dev/community)
- [Subscribe to the Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
- [Follow us on Twitter](https://twitter.com/nxdevtools)
"
`;

exports[`@nx/workspace:generateWorkspaceFiles README.md should be created for RemixStandalone preset 1`] = `
"# Proj

<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>

✨ **This workspace has been generated by [Nx, Smart Monorepos · Fast CI.](https://nx.dev)** ✨

## Integrate with editors

Enhance your Nx experience by installing [Nx Console](https://nx.dev/nx-console) for your favorite editor. Nx Console
provides an interactive UI to view your projects, run tasks, generate code, and more! Available for VSCode, IntelliJ and
comes with a LSP for Vim users.

## Nx plugins and code generators

Add Nx plugins to leverage their code generators and automated, inferred tasks.

\`\`\`
# Add plugin
npx nx add @nx/react

# Use code generator
npx nx generate @nx/react:app demo

# Run development server
npx nx serve demo

# View project details
npx nx show project demo --web
\`\`\`

Run \`npx nx list\` to get a list of available plugins and whether they have generators. Then run \`npx nx list <plugin-name>\` to see what generators are available.

Learn more about [code generators](https://nx.dev/features/generate-code) and [inferred tasks](https://nx.dev/concepts/inferred-tasks) in the docs.

## Running tasks

To execute tasks with Nx use the following syntax:

\`\`\`
npx nx <target> <project> <...options>
\`\`\`

You can also run multiple targets:

\`\`\`
npx nx run-many -t <target1> <target2>
\`\`\`

..or add \`-p\` to filter specific projects

\`\`\`
npx nx run-many -t <target1> <target2> -p <proj1> <proj2>
\`\`\`

Targets can be defined in the \`package.json\` or \`projects.json\`. Learn more [in the docs](https://nx.dev/features/run-tasks).

## Set up CI!

Nx comes with local caching already built-in (check your \`nx.json\`). On CI you might want to go a step further.

- [Set up remote caching](https://nx.dev/features/share-your-cache)
- [Set up task distribution across multiple machines](https://nx.dev/nx-cloud/features/distribute-task-execution)
- [Learn more how to setup CI](https://nx.dev/recipes/ci)

## Explore the project graph

Run \`npx nx graph\` to show the graph of the workspace.
It will show tasks that you can run with Nx.

- [Learn more about Exploring the Project Graph](https://nx.dev/core-features/explore-graph)

## Connect with us!

- [Join the community](https://nx.dev/community)
- [Subscribe to the Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
- [Follow us on Twitter](https://twitter.com/nxdevtools)
"
`;

exports[`@nx/workspace:generateWorkspaceFiles README.md should be created for TS preset 1`] = `
"# Proj

Expand Down
4 changes: 4 additions & 0 deletions packages/workspace/src/generators/new/generate-preset.ts
Expand Up @@ -122,6 +122,10 @@ function getPresetDependencies({
case Preset.NextJsStandalone:
return { dependencies: { '@nx/next': nxVersion }, dev: {} };

case Preset.RemixStandalone:
case Preset.RemixMonorepo:
return { dependencies: { '@nx/remix': nxVersion }, dev: {} };

case Preset.VueMonorepo:
case Preset.VueStandalone:
return {
Expand Down
Expand Up @@ -107,6 +107,7 @@ function createFiles(tree: Tree, options: NormalizedSchema) {
options.preset === Preset.NuxtStandalone ||
options.preset === Preset.NodeStandalone ||
options.preset === Preset.NextJsStandalone ||
options.preset === Preset.RemixStandalone ||
options.preset === Preset.TsStandalone
? './files-root-app'
: options.preset === Preset.NPM
Expand Down