2022-09-12 21:19:50 +01:00

279 lines
7.3 KiB
TypeScript

import {
addProjectConfiguration,
convertNxGenerator,
formatFiles,
generateFiles,
GeneratorCallback,
getWorkspaceLayout,
joinPathFragments,
logger,
names,
offsetFromRoot,
ProjectConfiguration,
readProjectConfiguration,
readWorkspaceConfiguration,
TargetConfiguration,
toJS,
Tree,
updateProjectConfiguration,
updateTsConfigsToJs,
updateWorkspaceConfiguration,
} from '@nrwl/devkit';
import { join } from 'path';
import { Linter, lintProjectGenerator } from '@nrwl/linter';
import { jestProjectGenerator } from '@nrwl/jest';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import { Schema } from './schema';
import { initGenerator } from '../init/init';
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
export interface NormalizedSchema extends Schema {
appProjectRoot: string;
parsedTags: string[];
}
function getBuildConfig(
project: ProjectConfiguration,
options: NormalizedSchema
): TargetConfiguration {
return {
executor: '@nrwl/webpack:webpack',
outputs: ['{options.outputPath}'],
options: {
target: 'node',
compiler: 'tsc',
outputPath: joinPathFragments('dist', options.appProjectRoot),
main: joinPathFragments(
project.sourceRoot,
'main' + (options.js ? '.js' : '.ts')
),
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
assets: [joinPathFragments(project.sourceRoot, 'assets')],
},
configurations: {
production: {
optimization: true,
extractLicenses: true,
inspect: false,
fileReplacements: [
{
replace: joinPathFragments(
project.sourceRoot,
'environments/environment' + (options.js ? '.js' : '.ts')
),
with: joinPathFragments(
project.sourceRoot,
'environments/environment.prod' + (options.js ? '.js' : '.ts')
),
},
],
},
},
};
}
function getServeConfig(options: NormalizedSchema): TargetConfiguration {
return {
executor: '@nrwl/js:node',
options: {
buildTarget: `${options.name}:build`,
},
configurations: {
production: {
buildTarget: `${options.name}:build:production`,
},
},
};
}
function addProject(tree: Tree, options: NormalizedSchema) {
const project: ProjectConfiguration = {
root: options.appProjectRoot,
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
projectType: 'application',
targets: {},
tags: options.parsedTags,
};
project.targets.build = getBuildConfig(project, options);
project.targets.serve = getServeConfig(options);
addProjectConfiguration(
tree,
options.name,
project,
options.standaloneConfig
);
const workspace = readWorkspaceConfiguration(tree);
if (!workspace.defaultProject) {
workspace.defaultProject = options.name;
updateWorkspaceConfiguration(tree, workspace);
}
}
function addAppFiles(tree: Tree, options: NormalizedSchema) {
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
tmpl: '',
name: options.name,
root: options.appProjectRoot,
offset: offsetFromRoot(options.appProjectRoot),
rootTsConfigPath: getRelativePathToRootTsConfig(
tree,
options.appProjectRoot
),
});
if (options.js) {
toJS(tree);
}
if (options.pascalCaseFiles) {
logger.warn('NOTE: --pascalCaseFiles is a noop');
}
}
function addProxy(tree: Tree, options: NormalizedSchema) {
const projectConfig = readProjectConfiguration(tree, options.frontendProject);
if (projectConfig.targets && projectConfig.targets.serve) {
const pathToProxyFile = `${projectConfig.root}/proxy.conf.json`;
projectConfig.targets.serve.options = {
...projectConfig.targets.serve.options,
proxyConfig: pathToProxyFile,
};
if (!tree.exists(pathToProxyFile)) {
tree.write(
pathToProxyFile,
JSON.stringify(
{
'/api': {
target: 'http://localhost:3333',
secure: false,
},
},
null,
2
)
);
} else {
//add new entry to existing config
const proxyFileContent = tree.read(pathToProxyFile).toString();
const proxyModified = {
...JSON.parse(proxyFileContent),
[`/${options.name}-api`]: {
target: 'http://localhost:3333',
secure: false,
},
};
tree.write(pathToProxyFile, JSON.stringify(proxyModified, null, 2));
}
updateProjectConfiguration(tree, options.frontendProject, projectConfig);
}
}
export async function addLintingToApplication(
tree: Tree,
options: NormalizedSchema
): Promise<GeneratorCallback> {
const lintTask = await lintProjectGenerator(tree, {
linter: options.linter,
project: options.name,
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
eslintFilePatterns: [
`${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
],
unitTestRunner: options.unitTestRunner,
skipFormat: true,
setParserOptionsProject: options.setParserOptionsProject,
});
return lintTask;
}
export async function applicationGenerator(tree: Tree, schema: Schema) {
const options = normalizeOptions(tree, schema);
const tasks: GeneratorCallback[] = [];
const initTask = await initGenerator(tree, {
...options,
skipFormat: true,
});
tasks.push(initTask);
addAppFiles(tree, options);
addProject(tree, options);
if (options.linter !== Linter.None) {
const lintTask = await addLintingToApplication(tree, {
...options,
skipFormat: true,
});
tasks.push(lintTask);
}
if (options.unitTestRunner === 'jest') {
const jestTask = await jestProjectGenerator(tree, {
...options,
project: options.name,
setupFile: 'none',
skipSerializers: true,
supportTsx: options.js,
babelJest: options.babelJest,
testEnvironment: 'node',
skipFormat: true,
});
tasks.push(jestTask);
}
if (options.js) {
updateTsConfigsToJs(tree, { projectRoot: options.appProjectRoot });
}
if (options.frontendProject) {
addProxy(tree, options);
}
if (!options.skipFormat) {
await formatFiles(tree);
}
return runTasksInSerial(...tasks);
}
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const { appsDir } = getWorkspaceLayout(host);
const appDirectory = options.directory
? `${names(options.directory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
const appProjectRoot = joinPathFragments(appsDir, appDirectory);
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
return {
...options,
name: names(appProjectName).fileName,
frontendProject: options.frontendProject
? names(options.frontendProject).fileName
: undefined,
appProjectRoot,
parsedTags,
linter: options.linter ?? Linter.EsLint,
unitTestRunner: options.unitTestRunner ?? 'jest',
};
}
export default applicationGenerator;
export const applicationSchematic = convertNxGenerator(applicationGenerator);