Skip to content

Commit

Permalink
feat(node): support nodejs frameworks to handle scaffolding using --f…
Browse files Browse the repository at this point in the history
…ramework
  • Loading branch information
ndcunningham committed Jan 6, 2023
1 parent d0d6e85 commit 5a7431a
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 4 deletions.
16 changes: 16 additions & 0 deletions docs/generated/packages/node/generators/application.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
},
"bundler": {
"description": "Bundler which is used to package the application",
"type": "string",
"enum": ["esbuild", "webpack"],
"default": "esbuild"
},
"framework": {
"description": "Generate the node application using a framework",
"type": "string",
"enum": ["express", "nest", "koa", "fastify", "connect"]
},
"port": {
"description": "The port which the server will be run on",
"type": "number",
"default": 3000
}
},
"required": [],
Expand Down
136 changes: 133 additions & 3 deletions packages/node/src/generators/application/application.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
addDependenciesToPackageJson,
addProjectConfiguration,
convertNxGenerator,
extractLayoutDirectory,
Expand All @@ -15,6 +16,7 @@ import {
TargetConfiguration,
toJS,
Tree,
updateJson,
updateProjectConfiguration,
updateTsConfigsToJs,
} from '@nrwl/devkit';
Expand All @@ -28,18 +30,30 @@ import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-ser
import { Schema } from './schema';
import { initGenerator } from '../init/init';
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
import {
connectTypingsVersion,
connectVersion,
esbuildVersion,
expressTypingsVersion,
expressVersion,
fastifyVersion,
koaTypingsVersion,
koaVersion,
nxVersion,
} from '../../utils/versions';
import { prompt } from 'enquirer';

export interface NormalizedSchema extends Schema {
appProjectRoot: string;
parsedTags: string[];
}

function getBuildConfig(
function getWebpackBuildConfig(
project: ProjectConfiguration,
options: NormalizedSchema
): TargetConfiguration {
return {
executor: '@nrwl/webpack:webpack',
executor: `@nrwl/webpack:webpack`,
outputs: ['{options.outputPath}'],
options: {
target: 'node',
Expand Down Expand Up @@ -74,6 +88,42 @@ function getBuildConfig(
};
}

function getEsBuildConfig(
project: ProjectConfiguration,
options: NormalizedSchema
): TargetConfiguration {
return {
executor: '@nrwl/esbuild:esbuild',
outputs: ['{options.outputPath}'],
options: {
outputPath: joinPathFragments('dist', options.appProjectRoot),
format: ['cjs'],
main: joinPathFragments(
project.sourceRoot,
'main' + (options.js ? '.js' : '.ts')
),
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
assets: [joinPathFragments(project.sourceRoot, 'assets')],
},
configurations: {
production: {
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',
Expand All @@ -96,7 +146,10 @@ function addProject(tree: Tree, options: NormalizedSchema) {
targets: {},
tags: options.parsedTags,
};
project.targets.build = getBuildConfig(project, options);
project.targets.build =
options.bundler === 'esbuild'
? getEsBuildConfig(project, options)
: getWebpackBuildConfig(project, options);
project.targets.serve = getServeConfig(options);

addProjectConfiguration(
Expand All @@ -109,6 +162,7 @@ function addProject(tree: Tree, options: NormalizedSchema) {

function addAppFiles(tree: Tree, options: NormalizedSchema) {
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
...options,
tmpl: '',
name: options.name,
root: options.appProjectRoot,
Expand Down Expand Up @@ -189,18 +243,91 @@ export async function addLintingToApplication(
return lintTask;
}

function addProjectDependencies(
tree: Tree,
options: NormalizedSchema
): GeneratorCallback {
const bundlers = {
webpack: {
'@nrwl/webpack': nxVersion,
},
esbuild: {
'@nrwl/esbuild': nxVersion,
esbuild: esbuildVersion,
},
};

const frameworkDependencies = {
express: {
express: expressVersion,
'@types/express': expressTypingsVersion,
},
koa: {
koa: koaVersion,
'@types/koa': koaTypingsVersion,
},
fastify: {
fastify: fastifyVersion,
},
connect: {
connect: connectVersion,
'@types/connect': connectTypingsVersion,
},
};
return addDependenciesToPackageJson(
tree,
{},
{
...frameworkDependencies[options.framework],
...bundlers[options.bundler],
}
);
}

function updateTsConfigOptions(tree: Tree, options: NormalizedSchema) {
// updatae tsconfig.app.json to typecheck default exports https://www.typescriptlang.org/tsconfig#esModuleInterop
updateJson(tree, `${options.appProjectRoot}/tsconfig.app.json`, (json) => ({
...json,
compilerOptions: {
...json.compilerOptions,
esModuleInterop: true,
},
}));
}

export async function applicationGenerator(tree: Tree, schema: Schema) {
// Prompt for bundler webpack / esbuild
schema.bundler = (
await prompt<{ bundler: 'esbuild' | 'webpack' }>([
{
message: 'What bundler would you like to use?',
type: 'select',
name: 'bundler',
choices: ['esbuild', 'webpack'],
},
])
).bundler;

const options = normalizeOptions(tree, schema);

if (schema?.framework === 'nest') {
// TODO (nicholas): Updatae this temporary solution after the interaction is streamlined
const { framework, ...rest } = options;
const { applicationGenerator } = require('@nrwl/nest');
return applicationGenerator(tree, rest);
}

const tasks: GeneratorCallback[] = [];
const initTask = await initGenerator(tree, {
...options,
skipFormat: true,
});
tasks.push(initTask);

addProjectDependencies(tree, options);
addAppFiles(tree, options);
addProject(tree, options);
updateTsConfigOptions(tree, options);

if (options.linter !== Linter.None) {
const lintTask = await addLintingToApplication(tree, {
Expand Down Expand Up @@ -252,6 +379,8 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {

const appProjectRoot = joinPathFragments(appsDir, appDirectory);

options.bundler = options.bundler ?? 'esbuild';

const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
Expand All @@ -266,6 +395,7 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
parsedTags,
linter: options.linter ?? Linter.EsLint,
unitTestRunner: options.unitTestRunner ?? 'jest',
port: options.port ?? 3000,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
console.log('Hello World!');
<% if(framework === 'express') {%>
import * as express from 'express';
const app = express();


app.get('/', (req, res) => {
res.send('Hello from Nrwl 🐳 API');
});

app.listen(<%= port %>, () => {
// Server is running
});
<%}%>
10 changes: 10 additions & 0 deletions packages/node/src/generators/application/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,14 @@ export interface Schema {
pascalCaseFiles?: boolean;
setParserOptionsProject?: boolean;
standaloneConfig?: boolean;
bundler?: 'esbuild' | 'webpack';
framework?: NodeJsFrameWorks;
port?: number;
}

export type NodeJsFrameWorks =
| 'express'
| 'nest'
| 'koa'
| 'fastify'
| 'connect';
16 changes: 16 additions & 0 deletions packages/node/src/generators/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
},
"bundler": {
"description": "Bundler which is used to package the application",
"type": "string",
"enum": ["esbuild", "webpack"],
"default": "esbuild"
},
"framework": {
"description": "Generate the node application using a framework",
"type": "string",
"enum": ["express", "nest", "koa", "fastify", "connect"]
},
"port": {
"description": "The port which the server will be run on",
"type": "number",
"default": 3000
}
},
"required": []
Expand Down
13 changes: 13 additions & 0 deletions packages/node/src/utils/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,16 @@ export const nxVersion = require('../../package.json').version;
export const tslibVersion = '^2.3.0';

export const typesNodeVersion = '18.7.1';

export const esbuildVersion = '^0.15.7';

export const expressVersion = '^4.18.1';
export const expressTypingsVersion = '4.17.13';

export const koaVersion = '2.14.1';
export const koaTypingsVersion = '2.13.5';

export const fastifyVersion = '4.11.0';

export const connectVersion = '3.7.0';
export const connectTypingsVersion = '3.4.35';

0 comments on commit 5a7431a

Please sign in to comment.