244 lines
6.7 KiB
TypeScript

import {
addProjectConfiguration,
convertNxGenerator,
formatFiles,
generateFiles,
getWorkspaceLayout,
joinPathFragments,
names,
NxJsonProjectConfiguration,
offsetFromRoot,
ProjectConfiguration,
readWorkspaceConfiguration,
TargetConfiguration,
Tree,
updateWorkspaceConfiguration,
} from '@nrwl/devkit';
import { join } from 'path';
import { webInitGenerator } from '../init/init';
import { cypressProjectGenerator } from '@nrwl/cypress';
import { Linter, lintProjectGenerator } from '@nrwl/linter';
import { jestProjectGenerator } from '@nrwl/jest';
import { WebBuildBuilderOptions } from '../../builders/build/build.impl';
import { Schema } from './schema';
interface NormalizedSchema extends Schema {
projectName: string;
appProjectRoot: string;
e2eProjectName: string;
e2eProjectRoot: string;
parsedTags: string[];
}
function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
...options,
...names(options.name),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
});
if (options.unitTestRunner === 'none') {
tree.delete(join(options.appProjectRoot, './src/app/app.element.spec.ts'));
}
}
function addBuildTarget(
project: ProjectConfiguration,
options: NormalizedSchema
): ProjectConfiguration {
const buildOptions: WebBuildBuilderOptions = {
outputPath: joinPathFragments('dist', options.appProjectRoot),
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
main: joinPathFragments(options.appProjectRoot, 'src/main.ts'),
polyfills: joinPathFragments(options.appProjectRoot, 'src/polyfills.ts'),
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
assets: [
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
joinPathFragments(options.appProjectRoot, 'src/assets'),
],
styles: [
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
],
scripts: [],
};
const productionBuildOptions: Partial<WebBuildBuilderOptions> = {
fileReplacements: [
{
replace: joinPathFragments(
options.appProjectRoot,
`src/environments/environment.ts`
),
with: joinPathFragments(
options.appProjectRoot,
`src/environments/environment.prod.ts`
),
},
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
budgets: [
{
type: 'initial',
maximumWarning: '2mb',
maximumError: '5mb',
},
],
};
return {
...project,
targets: {
...project.targets,
build: {
executor: '@nrwl/web:build',
outputs: ['{options.outputPath}'],
options: buildOptions,
configurations: {
production: productionBuildOptions,
},
},
},
};
}
function addServeTarget(
project: ProjectConfiguration,
options: NormalizedSchema
) {
const serveTarget: TargetConfiguration = {
executor: '@nrwl/web:dev-server',
options: {
buildTarget: `${options.projectName}:build`,
},
configurations: {
production: {
buildTarget: `${options.projectName}:build:production`,
},
},
};
return {
...project,
targets: {
...project.targets,
serve: serveTarget,
},
};
}
function addProject(tree: Tree, options: NormalizedSchema) {
const targets: Record<string, TargetConfiguration> = {};
let project: ProjectConfiguration & NxJsonProjectConfiguration = {
projectType: 'application',
root: options.appProjectRoot,
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
tags: options.parsedTags,
targets,
};
project = addBuildTarget(project, options);
project = addServeTarget(project, options);
addProjectConfiguration(tree, options.projectName, project);
const workspace = readWorkspaceConfiguration(tree);
if (!workspace.defaultProject) {
workspace.defaultProject = options.projectName;
updateWorkspaceConfiguration(tree, workspace);
}
}
export async function applicationGenerator(host: Tree, schema: Schema) {
const options = normalizeOptions(host, schema);
let installTask = await webInitGenerator(host, {
...options,
skipFormat: true,
});
createApplicationFiles(host, options);
addProject(host, options);
const lintInstallTask = await lintProjectGenerator(host, {
linter: options.linter,
project: options.projectName,
tsConfigPaths: [
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
],
eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`],
skipFormat: true,
});
installTask = lintInstallTask || installTask;
if (options.e2eTestRunner === 'cypress') {
const cypressInstallTask = await cypressProjectGenerator(host, {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName,
});
installTask = cypressInstallTask || installTask;
}
if (options.unitTestRunner === 'jest') {
const jestInstallTask = await jestProjectGenerator(host, {
project: options.projectName,
skipSerializers: true,
setupFile: 'web-components',
babelJest: options.babelJest,
});
installTask = jestInstallTask || installTask;
}
if (!schema.skipFormat) {
await formatFiles(host);
}
return installTask;
}
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const appDirectory = options.directory
? `${names(options.directory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;
const { appsDir, npmScope: defaultPrefix } = getWorkspaceLayout(host);
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
const e2eProjectName = `${appProjectName}-e2e`;
const appProjectRoot = `${appsDir}/${appDirectory}`;
const e2eProjectRoot = `${appsDir}/${appDirectory}-e2e`;
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
options.style = options.style || 'css';
options.linter = options.linter || Linter.EsLint;
options.unitTestRunner = options.unitTestRunner || 'jest';
options.e2eTestRunner = options.e2eTestRunner || 'cypress';
return {
...options,
prefix: options.prefix ? options.prefix : defaultPrefix,
name: names(options.name).fileName,
projectName: appProjectName,
appProjectRoot,
e2eProjectRoot,
e2eProjectName,
parsedTags,
};
}
export default applicationGenerator;
export const applicationSchematic = convertNxGenerator(applicationGenerator);