nx/packages/remix/src/generators/application/application.impl.ts

304 lines
8.0 KiB
TypeScript

import {
addDependenciesToPackageJson,
addProjectConfiguration,
ensurePackage,
formatFiles,
generateFiles,
GeneratorCallback,
getPackageManagerCommand,
joinPathFragments,
offsetFromRoot,
readJson,
readProjectConfiguration,
runTasksInSerial,
toJS,
Tree,
updateJson,
updateProjectConfiguration,
} from '@nx/devkit';
import { extractTsConfigBase } from '@nx/js/src/utils/typescript/create-ts-config';
import {
eslintVersion,
getPackageVersion,
isbotVersion,
reactDomVersion,
reactVersion,
remixVersion,
typescriptVersion,
typesReactDomVersion,
typesReactVersion,
} from '../../utils/versions';
import {
NormalizedSchema,
normalizeOptions,
updateUnitTestConfig,
} from './lib';
import { NxRemixGeneratorSchema } from './schema';
export default async function (tree: Tree, _options: NxRemixGeneratorSchema) {
const options = await normalizeOptions(tree, _options);
const tasks: GeneratorCallback[] = [];
addProjectConfiguration(tree, options.projectName, {
root: options.projectRoot,
sourceRoot: `${options.projectRoot}`,
projectType: 'application',
tags: options.parsedTags,
targets: {
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,
},
},
typecheck: {
command: `tsc`,
options: {
cwd: options.projectRoot,
},
},
},
});
const installTask = addDependenciesToPackageJson(
tree,
{
'@remix-run/node': remixVersion,
'@remix-run/react': remixVersion,
'@remix-run/serve': remixVersion,
isbot: isbotVersion,
react: reactVersion,
'react-dom': reactDomVersion,
},
{
'@remix-run/dev': remixVersion,
'@remix-run/eslint-config': remixVersion,
'@types/react': typesReactVersion,
'@types/react-dom': typesReactDomVersion,
eslint: eslintVersion,
typescript: typescriptVersion,
}
);
tasks.push(installTask);
const vars = {
...options,
tmpl: '',
offsetFromRoot: offsetFromRoot(options.projectRoot),
remixVersion,
isbotVersion,
reactVersion,
reactDomVersion,
typesReactVersion,
typesReactDomVersion,
eslintVersion,
typescriptVersion,
};
generateFiles(
tree,
joinPathFragments(__dirname, 'files/common'),
options.projectRoot,
vars
);
if (options.rootProject) {
const gitignore = tree.read('.gitignore', 'utf-8');
tree.write(
'.gitignore',
`${gitignore}\n.cache\nbuild\npublic/build\n.env\n`
);
} else {
generateFiles(
tree,
joinPathFragments(__dirname, 'files/integrated'),
options.projectRoot,
vars
);
}
if (options.unitTestRunner !== 'none') {
if (options.unitTestRunner === 'vitest') {
const { vitestGenerator, createOrEditViteConfig } = ensurePackage<
typeof import('@nx/vite')
>('@nx/vite', getPackageVersion(tree, 'nx'));
const vitestTask = await vitestGenerator(tree, {
uiFramework: 'react',
project: options.projectName,
coverageProvider: 'v8',
inSourceTests: false,
skipFormat: true,
testEnvironment: 'jsdom',
skipViteConfig: true,
});
createOrEditViteConfig(
tree,
{
project: options.projectName,
includeLib: false,
includeVitest: true,
testEnvironment: 'jsdom',
imports: [`import react from '@vitejs/plugin-react';`],
plugins: [`react()`],
},
true,
undefined,
true
);
tasks.push(vitestTask);
} else {
const { configurationGenerator: jestConfigurationGenerator } =
ensurePackage<typeof import('@nx/jest')>(
'@nx/jest',
getPackageVersion(tree, 'nx')
);
const jestTask = await jestConfigurationGenerator(tree, {
project: options.projectName,
setupFile: 'none',
supportTsx: true,
skipSerializers: false,
skipPackageJson: false,
skipFormat: true,
});
const projectConfig = readProjectConfiguration(tree, options.projectName);
projectConfig.targets['test'].options.passWithNoTests = true;
updateProjectConfiguration(tree, options.projectName, projectConfig);
tasks.push(jestTask);
}
const pkgInstallTask = updateUnitTestConfig(
tree,
options.projectRoot,
options.unitTestRunner
);
tasks.push(pkgInstallTask);
} else {
tree.delete(
joinPathFragments(options.projectRoot, `tests/routes/_index.spec.tsx`)
);
}
if (options.linter !== 'none') {
const { lintProjectGenerator } = ensurePackage<typeof import('@nx/eslint')>(
'@nx/eslint',
getPackageVersion(tree, 'nx')
);
const eslintTask = await lintProjectGenerator(tree, {
linter: options.linter,
project: options.projectName,
tsConfigPaths: [
joinPathFragments(options.projectRoot, 'tsconfig.app.json'),
],
unitTestRunner: options.unitTestRunner,
skipFormat: true,
rootProject: options.rootProject,
});
tasks.push(eslintTask);
}
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');
updateJson(tree, 'tsconfig.json', (json) => {
delete json.extends;
json.compilerOptions = {
...tsConfigBaseJson.compilerOptions,
...json.compilerOptions,
// Taken from remix default setup
// https://github.com/remix-run/remix/blob/68c8982/templates/remix/tsconfig.json#L15-L17
paths: {
'~/*': ['./app/*'],
},
};
json.include = [
...(tsConfigBaseJson.include ?? []),
...(json.include ?? []),
];
json.exclude = [
...(tsConfigBaseJson.exclude ?? []),
...(json.exclude ?? []),
];
return json;
});
tree.delete('tsconfig.base.json');
} else {
// Otherwise, extract the tsconfig.base.json from tsconfig.json so we can share settings.
extractTsConfigBase(tree);
}
if (options.e2eTestRunner === 'cypress') {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', getPackageVersion(tree, 'nx'));
addFileServerTarget(tree, options, 'serve-static');
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
tasks.push(
await configurationGenerator(tree, {
project: options.e2eProjectName,
directory: 'src',
skipFormat: true,
devServerTarget: `${options.projectName}:serve:development`,
baseUrl: 'http://localhost:4200',
})
);
}
if (!options.skipFormat) {
await formatFiles(tree);
}
return runTasksInSerial(...tasks);
}
function addFileServerTarget(
tree: Tree,
options: NormalizedSchema,
targetName: string
) {
addDependenciesToPackageJson(
tree,
{},
{ '@nx/web': getPackageVersion(tree, 'nx') }
);
const projectConfig = readProjectConfiguration(tree, options.projectName);
projectConfig.targets[targetName] = {
executor: '@nx/web:file-server',
options: {
buildTarget: `${options.projectName}:build`,
port: 4200,
},
};
updateProjectConfiguration(tree, options.projectName, projectConfig);
}