Skip to content

Commit

Permalink
feat(remix): add option to create-nx-workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed Mar 15, 2024
1 parent d4e47cc commit 5156658
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 11 deletions.
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
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
30 changes: 27 additions & 3 deletions packages/workspace/src/generators/preset/preset.ts
Expand Up @@ -21,9 +21,6 @@ async function createPreset(tree: Tree, options: Schema) {
process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false;

console.log('Add plugin', addPlugin);
console.log(options.preset, Preset.ReactNative);

if (options.preset === Preset.Apps) {
return;
} else if (options.preset === Preset.AngularMonorepo) {
Expand Down Expand Up @@ -93,6 +90,33 @@ async function createPreset(tree: Tree, options: Schema) {
unitTestRunner: options.bundler === 'vite' ? 'vitest' : 'jest',
addPlugin,
});
} else if (options.preset === Preset.RemixMonorepo) {
const { applicationGenerator: remixApplicationGenerator } = require('@nx' +
'/remix/generators');

return remixApplicationGenerator(tree, {
name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
unitTestRunner: 'vitest',
addPlugin,
});
} else if (options.preset === Preset.RemixStandalone) {
const { applicationGenerator: remixApplicationGenerator } = require('@nx' +
'/remix/generators');

return remixApplicationGenerator(tree, {
name: options.name,
directory: '.',
projectNameAndRootFormat: 'as-provided',
linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
rootProject: true,
unitTestRunner: 'vitest',
addPlugin,
});
} else if (options.preset === Preset.VueMonorepo) {
const { applicationGenerator: vueApplicationGenerator } = require('@nx' +
'/vue');
Expand Down
2 changes: 2 additions & 0 deletions packages/workspace/src/generators/utils/presets.ts
Expand Up @@ -15,6 +15,8 @@ export enum Preset {
ReactMonorepo = 'react-monorepo',
ReactStandalone = 'react-standalone',
NextJsStandalone = 'nextjs-standalone',
RemixMonorepo = 'remix-monorepo',
RemixStandalone = 'remix-standalone',
ReactNative = 'react-native',
VueMonorepo = 'vue-monorepo',
VueStandalone = 'vue-standalone',
Expand Down

0 comments on commit 5156658

Please sign in to comment.