Skip to content

Commit

Permalink
feat(node): update CNW to support generating a node server with a fra…
Browse files Browse the repository at this point in the history
…mework (#14313)
  • Loading branch information
ndcunningham committed Jan 13, 2023
1 parent 5faef5d commit 00caf6a
Show file tree
Hide file tree
Showing 23 changed files with 210 additions and 26 deletions.
8 changes: 7 additions & 1 deletion docs/generated/cli/create-nx-workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ Default: `main`

Default base to use for new projects

### framework

Type: `string`

Framework option to be used when the node-server preset is selected

### help

Type: `boolean`
Expand Down Expand Up @@ -113,7 +119,7 @@ Package manager to use

Type: `string`

Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular"]. To build your own see https://nx.dev/packages/nx-plugin#preset
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-server"]. To build your own see https://nx.dev/packages/nx-plugin#preset

### skipGit

Expand Down
6 changes: 6 additions & 0 deletions docs/generated/packages/node/generators/application.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
"description": "The port which the server will be run on",
"type": "number",
"default": 3000
},
"rootProject": {
"description": "Create node application at the root of the workspace",
"type": "boolean",
"default": false,
"hidden": true
}
},
"required": [],
Expand Down
8 changes: 7 additions & 1 deletion docs/generated/packages/nx/documents/create-nx-workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ Default: `main`

Default base to use for new projects

### framework

Type: `string`

Framework option to be used when the node-server preset is selected

### help

Type: `boolean`
Expand Down Expand Up @@ -113,7 +119,7 @@ Package manager to use

Type: `string`

Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular"]. To build your own see https://nx.dev/packages/nx-plugin#preset
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-server"]. To build your own see https://nx.dev/packages/nx-plugin#preset

### skipGit

Expand Down
5 changes: 5 additions & 0 deletions docs/generated/packages/workspace/generators/new.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
"description": "The package manager used to install dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm"]
},
"framework": {
"description": "The framework which the application is using",
"type": "string",
"enum": ["express", "koa", "fastify", "connect"]
}
},
"additionalProperties": true,
Expand Down
5 changes: 5 additions & 0 deletions docs/generated/packages/workspace/generators/preset.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
"description": "The package manager used to install dependencies.",
"type": "string",
"enum": ["npm", "yarn", "pnpm"]
},
"framework": {
"description": "The framework which the application is using",
"type": "string",
"enum": ["express", "koa", "fastify", "connect"]
}
},
"presets": []
Expand Down
4 changes: 2 additions & 2 deletions e2e/node/src/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ describe('Node Applications', () => {

it('should be able to generate an express application', async () => {
const nodeapp = uniq('nodeapp');
const originalEnvPort = process.env.port;
const originalEnvPort = process.env.PORT;
const port = 3333;
process.env.port = `${port}`;
process.env.PORT = `${port}`;

runCLI(`generate @nrwl/express:app ${nodeapp} --linter=eslint`);
const lintResults = runCLI(`lint ${nodeapp}`);
Expand Down
64 changes: 62 additions & 2 deletions packages/create-nx-workspace/bin/create-nx-workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Arguments = {
appName: string;
cli: string;
style: string;
framework: string;
nxCloud: boolean;
allPrompts: boolean;
packageManager: PackageManager;
Expand Down Expand Up @@ -62,6 +63,7 @@ enum Preset {
Express = 'express',
React = 'react',
Angular = 'angular',
NodeServer = 'node-server',
}

const presetOptions: { name: Preset; message: string }[] = [
Expand Down Expand Up @@ -96,6 +98,11 @@ const presetOptions: { name: Preset; message: string }[] = [
message:
'react-native [a monorepo with a single React Native application]',
},
{
name: Preset.NodeServer,
message:
'node [a standalone repo with a single Node Server e.g. Express]',
},
];

const nxVersion = require('../package.json').version;
Expand Down Expand Up @@ -145,6 +152,10 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
describe: chalk.dim`Style option to be used when a preset with pregenerated app is selected`,
type: 'string',
})
.option('framework', {
describe: chalk.dim`Framework option to be used when the node-server preset is selected`,
type: 'string',
})
.option('nxCloud', {
describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')),
type: 'boolean',
Expand Down Expand Up @@ -224,6 +235,7 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
ci,
skipGit,
commit,
framework,
} = parsedArgs;

output.log({
Expand All @@ -248,6 +260,7 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
style,
nxCloud,
defaultBase,
framework,
}
);

Expand Down Expand Up @@ -300,7 +313,7 @@ async function getConfiguration(
argv: yargs.Arguments<Arguments>
): Promise<void> {
try {
let name, appName, style, preset;
let name, appName, style, preset, framework;

output.log({
title:
Expand Down Expand Up @@ -343,6 +356,9 @@ async function getConfiguration(
} else {
name = await determineRepoName(argv);
appName = await determineAppName(preset, argv);
if (preset === Preset.NodeServer) {
framework = await determineFramework(preset, argv);
}
}
style = await determineStyle(preset, argv);
}
Expand All @@ -358,6 +374,7 @@ async function getConfiguration(
preset,
appName,
style,
framework,
cli,
nxCloud,
packageManager,
Expand Down Expand Up @@ -643,6 +660,47 @@ async function determineAppName(
});
}

async function determineFramework(
preset: Preset,
parsedArgs: yargs.Arguments<Arguments>
): Promise<string> {
if (preset !== Preset.NodeServer) {
return Promise.resolve('');
}

const frameworkChoices = ['express', 'koa', 'fastify', 'connect'];

if (!parsedArgs.framework) {
return enquirer
.prompt([
{
message: 'What framework should be used?',
type: 'select',
name: 'framework',
choices: frameworkChoices,
},
])
.then((a: { framework: string }) => a.framework);
}

const foundFramework = frameworkChoices.indexOf(parsedArgs.framework);

if (foundFramework < 0) {
output.error({
title: 'Invalid framwork',
bodyLines: [
`It must be one of the following:`,
'',
...frameworkChoices.map((choice) => choice),
],
});

process.exit(1);
}

return Promise.resolve(parsedArgs.framework);
}

function isValidCli(cli: string): cli is 'angular' | 'nx' {
return ['nx', 'angular'].indexOf(cli) !== -1;
}
Expand Down Expand Up @@ -685,7 +743,8 @@ async function determineStyle(
preset === Preset.Nest ||
preset === Preset.Express ||
preset === Preset.ReactNative ||
preset === Preset.Expo
preset === Preset.Expo ||
preset === Preset.NodeServer
) {
return Promise.resolve(null);
}
Expand Down Expand Up @@ -1182,6 +1241,7 @@ function pointToTutorialAndCourse(preset: Preset) {
});
break;
case Preset.Express:
case Preset.NodeServer:
output.addVerticalSeparator();
output.note({
title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ describe('app', () => {
} as Schema);

const mainFile = appTree.read('apps/my-node-app/src/main.ts').toString();
expect(mainFile).toContain(`import * as express from 'express';`);
expect(mainFile).toContain(`import express from 'express';`);

const tsconfig = readJson(appTree, 'apps/my-node-app/tsconfig.json');
expect(tsconfig).toMatchInlineSnapshot(`
Object {
"compilerOptions": Object {
"esModuleInterop": true,
},
"extends": "../../tsconfig.base.json",
"files": Array [],
"include": Array [],
Expand Down Expand Up @@ -111,12 +114,13 @@ Object {

expect(appTree.exists('apps/my-node-app/src/main.js')).toBeTruthy();
expect(appTree.read('apps/my-node-app/src/main.js').toString()).toContain(
`import * as express from 'express';`
`import express from 'express';`
);

const tsConfig = readJson(appTree, 'apps/my-node-app/tsconfig.json');
expect(tsConfig.compilerOptions).toEqual({
allowJs: true,
esModuleInterop: true,
});

const tsConfigApp = readJson(
Expand Down
4 changes: 2 additions & 2 deletions packages/express/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function addMainFile(tree: Tree, options: NormalizedSchema) {
* This is only a minimal backend to get started.
*/
import * as express from 'express';
import express from 'express';
import * as path from 'path';
const app = express();
Expand All @@ -48,7 +48,7 @@ app.get('/api', (req, res) => {
res.send({ message: 'Welcome to ${options.name}!' });
});
const port = process.env.port || 3333;
const port = process.env.PORT || 3333;
const server = app.listen(port, () => {
console.log(\`Listening at http://localhost:\${port}/api\`);
});
Expand Down
5 changes: 5 additions & 0 deletions packages/node/src/generators/application/application.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('app', () => {
await applicationGenerator(tree, {
name: 'myNodeApp',
standaloneConfig: false,
bundler: 'webpack',
});
const project = readProjectConfiguration(tree, 'my-node-app');
expect(project.root).toEqual('my-node-app');
Expand Down Expand Up @@ -116,6 +117,9 @@ describe('app', () => {
const tsconfig = readJson(tree, 'my-node-app/tsconfig.json');
expect(tsconfig).toMatchInlineSnapshot(`
Object {
"compilerOptions": Object {
"esModuleInterop": true,
},
"extends": "../tsconfig.base.json",
"files": Array [],
"include": Array [],
Expand Down Expand Up @@ -401,6 +405,7 @@ describe('app', () => {
const tsConfig = readJson(tree, 'my-node-app/tsconfig.json');
expect(tsConfig.compilerOptions).toEqual({
allowJs: true,
esModuleInterop: true,
});

const tsConfigApp = readJson(tree, 'my-node-app/tsconfig.app.json');
Expand Down
44 changes: 31 additions & 13 deletions packages/node/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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 { NodeJsFrameWorks, Schema } from './schema';
import { initGenerator } from '../init/init';
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
import {
Expand All @@ -43,6 +43,8 @@ import {
} from '../../utils/versions';
import { prompt } from 'enquirer';

import * as shared from '@nrwl/workspace/src/utils/create-ts-config';

export interface NormalizedSchema extends Schema {
appProjectRoot: string;
parsedTags: string[];
Expand Down Expand Up @@ -290,14 +292,27 @@ function addProjectDependencies(
}

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,
},
}));
updateJson(tree, `${options.appProjectRoot}/tsconfig.json`, (json) => {
if (options.rootProject) {
return {
compilerOptions: {
...shared.tsConfigBaseOptions,
...json.compilerOptions,
},
...json,
extends: undefined,
exclude: ['node_modules', 'tmp'],
};
} else {
return {
...json,
compilerOptions: {
...json.compilerOptions,
esModuleInterop: true,
},
};
}
});
}

export async function applicationGenerator(tree: Tree, schema: Schema) {
Expand All @@ -313,9 +328,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
addProjectDependencies(tree, options);
addAppFiles(tree, options);
addProject(tree, options);
if (options.framework && options?.bundler === 'esbuild') {
updateTsConfigOptions(tree, options);
}

updateTsConfigOptions(tree, options);

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

const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');

const appProjectRoot = joinPathFragments(appsDir, appDirectory);
const appProjectRoot = options.rootProject
? '.'
: joinPathFragments(appsDir, appDirectory);

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

const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
Expand Down
Loading

1 comment on commit 00caf6a

@vercel
Copy link

@vercel vercel bot commented on 00caf6a Jan 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-nrwl.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx.dev

Please sign in to comment.