@nestjs/schematics
Version:
Nest - modern, fast, powerful node.js web framework (@schematics)
148 lines (147 loc) • 6.01 kB
JavaScript
import { join, strings } from '@angular-devkit/core';
import { classify } from '@angular-devkit/core/src/utils/strings';
import { apply, branchAndMerge, chain, filter, mergeWith, move, noop, SchematicsException, template, url, } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';
import pluralize from 'pluralize';
import { ModuleDeclarator, ModuleFinder, } from '../../index.js';
import { addPackageJsonDependency, getPackageJsonDependency, NodeDependencyType, } from '../../utils/dependencies.utils.js';
import { formatFiles } from '../../utils/format-files.rule.js';
import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
import { NameParser } from '../../utils/name.parser.js';
import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
export function main(options) {
options = transform(options);
return (tree, context) => {
options.isEsm = isEsmProject(tree);
return branchAndMerge(chain([
addMappedTypesDependencyIfApplies(options),
mergeSourceRoot(options),
addDeclarationToModule(options),
mergeWith(generate(options)),
options.format === true ? formatFiles() : noop(),
]))(tree, context);
};
}
function transform(options) {
const target = Object.assign({}, options);
if (!target.name) {
throw new SchematicsException('Option (name) is required.');
}
target.metadata = 'imports';
const location = new NameParser().parse(target);
target.name = normalizeToKebabOrSnakeCase(location.name);
target.path = normalizeToKebabOrSnakeCase(location.path);
target.language = target.language !== undefined ? target.language : 'ts';
if (target.language === 'js') {
throw new Error('The "resource" schematic does not support JavaScript language (only TypeScript is supported).');
}
target.specFileSuffix = normalizeToKebabOrSnakeCase(options.specFileSuffix || 'spec');
target.path = target.flat
? target.path
: join(target.path, target.name);
target.isSwaggerInstalled = options.isSwaggerInstalled ?? false;
return target;
}
function generate(options) {
return (context) => apply(url(join('./files', options.language)), [
filter((path) => {
if (path.endsWith('.dto.ts')) {
return (options.type !== 'graphql-code-first' &&
options.type !== 'graphql-schema-first' &&
!!options.crud);
}
if (path.endsWith('.input.ts')) {
return ((options.type === 'graphql-code-first' ||
options.type === 'graphql-schema-first') &&
!!options.crud);
}
if (path.endsWith('.resolver.ts') ||
path.endsWith('.resolver.__specFileSuffix__.ts')) {
return (options.type === 'graphql-code-first' ||
options.type === 'graphql-schema-first');
}
if (path.endsWith('.graphql')) {
return options.type === 'graphql-schema-first' && !!options.crud;
}
if (path.endsWith('controller.ts') ||
path.endsWith('.controller.__specFileSuffix__.ts')) {
return options.type === 'microservice' || options.type === 'rest';
}
if (path.endsWith('.gateway.ts') ||
path.endsWith('.gateway.__specFileSuffix__.ts')) {
return options.type === 'ws';
}
if (path.includes('@ent')) {
return !!options.crud;
}
return true;
}),
options.spec
? noop()
: filter((path) => {
const suffix = `.__specFileSuffix__.ts`;
return !path.endsWith(suffix);
}),
template({
...strings,
...options,
lowercased: (name) => {
const classifiedName = classify(name);
return (classifiedName.charAt(0).toLowerCase() + classifiedName.slice(1));
},
singular: (name) => pluralize.singular(name),
ent: (name) => name + '.entity',
}),
move(options.path),
])(context);
}
function addDeclarationToModule(options) {
return (tree) => {
if (options.skipImport !== undefined && options.skipImport) {
return tree;
}
options.module =
new ModuleFinder(tree).find({
name: options.name,
path: options.path,
}) ?? undefined;
if (!options.module) {
return tree;
}
const content = tree.read(options.module).toString();
const declarator = new ModuleDeclarator();
tree.overwrite(options.module, declarator.declare(content, {
...options,
type: 'module',
isEsm: isEsmProject(tree),
}));
return tree;
};
}
function addMappedTypesDependencyIfApplies(options) {
return (host, context) => {
try {
if (options.type === 'graphql-code-first') {
return;
}
if (options.type === 'rest') {
const nodeDependencyRef = getPackageJsonDependency(host, '@nestjs/swagger');
if (nodeDependencyRef) {
options.isSwaggerInstalled = true;
return;
}
}
const nodeDependencyRef = getPackageJsonDependency(host, '@nestjs/mapped-types');
if (!nodeDependencyRef) {
addPackageJsonDependency(host, {
type: NodeDependencyType.Default,
name: '@nestjs/mapped-types',
version: '*',
});
context.addTask(new NodePackageInstallTask());
}
}
catch {
}
};
}