Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(node): update CNW to support generating a node server with a framework #14313

Merged
merged 1 commit into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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) => {
jaysoo marked this conversation as resolved.
Show resolved Hide resolved
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