270 lines
7.3 KiB
TypeScript
270 lines
7.3 KiB
TypeScript
import { logShowProjectCommand } from '@nx/devkit/internal';
|
|
import {
|
|
ensurePackage,
|
|
formatFiles,
|
|
GeneratorCallback,
|
|
joinPathFragments,
|
|
readProjectConfiguration,
|
|
runTasksInSerial,
|
|
Tree,
|
|
updateJson,
|
|
updateProjectConfiguration,
|
|
updateTsConfigsToJs,
|
|
} from '@nx/devkit';
|
|
import { configurationGenerator } from '@nx/jest';
|
|
import { initGenerator as jsInitGenerator } from '@nx/js';
|
|
import {
|
|
addProjectToTsSolutionWorkspace,
|
|
getTsConfigBaseOptions,
|
|
shouldConfigureTsSolutionSetup,
|
|
updateTsconfigFiles,
|
|
sortPackageJsonFields,
|
|
} from '@nx/js/internal';
|
|
import { assertSupportedFrameworkVersion } from '../../utils/assert-supported-framework-version';
|
|
import { nxVersion } from '../../utils/versions';
|
|
import { e2eProjectGenerator } from '../e2e-project/e2e-project';
|
|
import { initGenerator } from '../init/init';
|
|
import { setupDockerGenerator } from '../setup-docker/setup-docker';
|
|
import { Schema } from './schema';
|
|
import {
|
|
addAppFiles,
|
|
addLintingToApplication,
|
|
addProject,
|
|
addProjectDependencies,
|
|
addProxy,
|
|
normalizeOptions,
|
|
NormalizedSchema,
|
|
} from './lib';
|
|
|
|
function updateTsConfigOptions(tree: Tree, options: NormalizedSchema) {
|
|
if (options.isUsingTsSolutionConfig) {
|
|
return;
|
|
}
|
|
|
|
updateJson(tree, `${options.appProjectRoot}/tsconfig.json`, (json) => {
|
|
if (options.rootProject) {
|
|
return {
|
|
compilerOptions: {
|
|
...getTsConfigBaseOptions(tree),
|
|
...json.compilerOptions,
|
|
esModuleInterop: true,
|
|
},
|
|
...json,
|
|
extends: undefined,
|
|
exclude: ['node_modules', 'tmp'],
|
|
};
|
|
} else {
|
|
return {
|
|
...json,
|
|
compilerOptions: {
|
|
...json.compilerOptions,
|
|
esModuleInterop: true,
|
|
},
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
export async function applicationGenerator(tree: Tree, schema: Schema) {
|
|
return await applicationGeneratorInternal(tree, {
|
|
addPlugin: false,
|
|
useProjectJson: true,
|
|
...schema,
|
|
});
|
|
}
|
|
|
|
export async function applicationGeneratorInternal(tree: Tree, schema: Schema) {
|
|
assertSupportedFrameworkVersion(tree, schema.framework);
|
|
|
|
const tasks: GeneratorCallback[] = [];
|
|
|
|
const addTsPlugin = shouldConfigureTsSolutionSetup(
|
|
tree,
|
|
schema.addPlugin,
|
|
schema.useTsSolution
|
|
);
|
|
const jsInitTask = await jsInitGenerator(tree, {
|
|
...schema,
|
|
tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
|
|
skipFormat: true,
|
|
addTsPlugin,
|
|
});
|
|
tasks.push(jsInitTask);
|
|
|
|
const options = await normalizeOptions(tree, schema);
|
|
|
|
if (options.framework === 'nest') {
|
|
// nx-ignore-next-line
|
|
const { applicationGenerator } = ensurePackage('@nx/nest', nxVersion);
|
|
const nestTasks = await applicationGenerator(tree, {
|
|
...options,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(nestTasks);
|
|
|
|
if (options.docker) {
|
|
const dockerTask = await setupDockerGenerator(tree, {
|
|
...options,
|
|
project: options.name,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(dockerTask);
|
|
}
|
|
return runTasksInSerial(
|
|
...[
|
|
...tasks,
|
|
() => {
|
|
logShowProjectCommand(options.name);
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
const initTask = await initGenerator(tree, {
|
|
...schema,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(initTask);
|
|
|
|
const { installTask, frameworkDependencies } = addProjectDependencies(
|
|
tree,
|
|
options
|
|
);
|
|
tasks.push(installTask);
|
|
|
|
if (options.bundler === 'webpack') {
|
|
const { webpackInitGenerator } = ensurePackage<
|
|
typeof import('@nx/webpack')
|
|
>('@nx/webpack', nxVersion);
|
|
const webpackInitTask = await webpackInitGenerator(tree, {
|
|
skipPackageJson: options.skipPackageJson,
|
|
skipFormat: true,
|
|
addPlugin: options.addPlugin,
|
|
});
|
|
tasks.push(webpackInitTask);
|
|
if (!options.skipPackageJson) {
|
|
// Use CommonJS `require` rather than a dynamic ESM `import`:
|
|
// `ensurePackage` makes the on-demand-installed package available via
|
|
// `Module._initPaths`, which `require()` honors but ESM resolution does
|
|
// not. Under nodenext, a dynamic `import()` is preserved as a true ESM
|
|
// dynamic import, so it can't see the temp install.
|
|
const {
|
|
ensureDependencies,
|
|
}: typeof import('@nx/webpack/internal') = require('@nx/webpack/internal');
|
|
tasks.push(
|
|
ensureDependencies(tree, {
|
|
uiFramework: options.isNest ? 'none' : 'react',
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
addAppFiles(tree, options);
|
|
addProject(tree, options, frameworkDependencies);
|
|
|
|
// If we are using the new TS solution
|
|
// We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project
|
|
if (options.isUsingTsSolutionConfig) {
|
|
await addProjectToTsSolutionWorkspace(tree, options.appProjectRoot);
|
|
}
|
|
|
|
updateTsConfigOptions(tree, options);
|
|
|
|
if (options.linter === 'eslint') {
|
|
const lintTask = await addLintingToApplication(tree, options);
|
|
tasks.push(lintTask);
|
|
}
|
|
|
|
if (options.unitTestRunner === 'jest') {
|
|
const jestTask = await configurationGenerator(tree, {
|
|
...options,
|
|
project: options.name,
|
|
setupFile: 'none',
|
|
skipSerializers: true,
|
|
supportTsx: options.js,
|
|
testEnvironment: 'node',
|
|
compiler: options.swcJest ? 'swc' : 'tsc',
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(jestTask);
|
|
// There are no tests by default, so set `--passWithNoTests` to avoid test failure on new project.
|
|
const projectConfig = readProjectConfiguration(tree, options.name);
|
|
projectConfig.targets ??= {};
|
|
projectConfig.targets.test = {
|
|
...projectConfig.targets.test,
|
|
options: {
|
|
...projectConfig.targets.test?.options,
|
|
passWithNoTests: true,
|
|
},
|
|
};
|
|
updateProjectConfiguration(tree, options.name, projectConfig);
|
|
} else {
|
|
// No need for default spec file if unit testing is not setup.
|
|
tree.delete(
|
|
joinPathFragments(options.appProjectRoot, 'src/app/app.spec.ts')
|
|
);
|
|
}
|
|
|
|
if (options.e2eTestRunner === 'jest') {
|
|
const e2eTask = await e2eProjectGenerator(tree, {
|
|
...options,
|
|
projectType: options.framework === 'none' ? 'cli' : 'server',
|
|
name: options.rootProject ? 'e2e' : `${options.name}-e2e`,
|
|
directory: options.rootProject ? 'e2e' : `${options.appProjectRoot}-e2e`,
|
|
project: options.name,
|
|
port: options.port,
|
|
isNest: options.isNest,
|
|
skipFormat: true,
|
|
});
|
|
tasks.push(e2eTask);
|
|
}
|
|
|
|
if (options.js) {
|
|
updateTsConfigsToJs(tree, { projectRoot: options.appProjectRoot });
|
|
}
|
|
|
|
if (options.frontendProject) {
|
|
addProxy(tree, options);
|
|
}
|
|
|
|
if (options.docker) {
|
|
const dockerTask = await setupDockerGenerator(tree, {
|
|
...options,
|
|
project: options.name,
|
|
skipFormat: true,
|
|
skipDockerPlugin: options.skipDockerPlugin ?? false,
|
|
});
|
|
|
|
tasks.push(dockerTask);
|
|
}
|
|
|
|
if (options.isUsingTsSolutionConfig) {
|
|
updateTsconfigFiles(
|
|
tree,
|
|
options.appProjectRoot,
|
|
'tsconfig.app.json',
|
|
{
|
|
module: 'nodenext',
|
|
moduleResolution: 'nodenext',
|
|
},
|
|
options.linter === 'eslint'
|
|
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
|
: undefined
|
|
);
|
|
}
|
|
|
|
sortPackageJsonFields(tree, options.appProjectRoot);
|
|
|
|
if (!options.skipFormat) {
|
|
await formatFiles(tree);
|
|
}
|
|
|
|
tasks.push(() => {
|
|
logShowProjectCommand(options.name);
|
|
});
|
|
|
|
return runTasksInSerial(...tasks);
|
|
}
|
|
|
|
export default applicationGenerator;
|