feat(nx): add full stack preset for react/express
This commit is contained in:
parent
8c7c0b7721
commit
bb858e4db9
@ -23,20 +23,20 @@ const presetOptions = [
|
||||
value: 'angular',
|
||||
name: 'angular [a workspace with a single Angular application]'
|
||||
},
|
||||
{
|
||||
value: 'angular-nest',
|
||||
name:
|
||||
'angular-nest [a workspace with a full stack application (Angular + Nest)]'
|
||||
},
|
||||
{
|
||||
value: 'react',
|
||||
name: 'react [a workspace with a single React application]'
|
||||
},
|
||||
{
|
||||
value: 'nest-angular',
|
||||
value: 'react-express',
|
||||
name:
|
||||
'angular-nest [a workspace with a full stack application (Angular + Nest)]'
|
||||
'react-express [a workspace with a full stack application (React + Express)]'
|
||||
}
|
||||
// {
|
||||
// value: 'react-express',
|
||||
// name:
|
||||
// 'react-express [a workspace with a full stack application (React + Express)]'
|
||||
// }
|
||||
];
|
||||
|
||||
const tsVersion = 'TYPESCRIPT_VERSION';
|
||||
|
||||
@ -5,5 +5,6 @@ module.exports = {
|
||||
},
|
||||
resolver: '@nrwl/jest/plugins/resolver',
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageReporters: ['html']
|
||||
coverageReporters: ['html'],
|
||||
passWithNoTests: true
|
||||
};
|
||||
|
||||
@ -13,6 +13,7 @@ export function getBabelWebpackConfig(config: Configuration) {
|
||||
{
|
||||
// Allow importing core-js in entrypoint and use browserlist to select polyfills
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3,
|
||||
modules: false,
|
||||
// Exclude transforms that make all code slower
|
||||
exclude: ['transform-typeof-symbol']
|
||||
|
||||
@ -55,9 +55,9 @@ describe('preset', () => {
|
||||
);
|
||||
expect(tree.exists('/apps/proj/src/app/app.component.ts')).toBe(true);
|
||||
expect(tree.exists('/apps/api/src/app/app.controller.ts')).toBe(true);
|
||||
expect(tree.exists('/libs/api-interface/src/lib/interfaces.ts')).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
tree.exists('/libs/api-interfaces/src/lib/api-interfaces.ts')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with unnormalized names', async () => {
|
||||
@ -69,9 +69,36 @@ describe('preset', () => {
|
||||
|
||||
expect(tree.exists('/apps/my-proj/src/app/app.component.ts')).toBe(true);
|
||||
expect(tree.exists('/apps/api/src/app/app.controller.ts')).toBe(true);
|
||||
expect(tree.exists('/libs/api-interface/src/lib/interfaces.ts')).toBe(
|
||||
true
|
||||
expect(
|
||||
tree.exists('/libs/api-interfaces/src/lib/api-interfaces.ts')
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--preset react-express', () => {
|
||||
it('should create files', async () => {
|
||||
const tree = await runSchematic(
|
||||
'preset',
|
||||
{ name: 'proj', preset: 'react-express' },
|
||||
projectTree
|
||||
);
|
||||
expect(tree.exists('/apps/proj/src/app/app.tsx')).toBe(true);
|
||||
expect(
|
||||
tree.exists('/libs/api-interfaces/src/lib/api-interfaces.ts')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with unnormalized names', async () => {
|
||||
const tree = await runSchematic(
|
||||
'preset',
|
||||
{ name: 'myProj', preset: 'react-express' },
|
||||
projectTree
|
||||
);
|
||||
|
||||
expect(tree.exists('/apps/my-proj/src/app/app.tsx')).toBe(true);
|
||||
expect(
|
||||
tree.exists('/libs/api-interfaces/src/lib/api-interfaces.ts')
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,32 +93,49 @@ function createPreset(options: Schema): Rule {
|
||||
},
|
||||
{ interactive: false }
|
||||
),
|
||||
schematic(
|
||||
'library',
|
||||
{ name: 'api-interface', framework: 'none' },
|
||||
{ interactive: false }
|
||||
),
|
||||
schematic('library', { name: 'api-interfaces' }, { interactive: false }),
|
||||
setDefaultCollection('@nrwl/angular'),
|
||||
connectFrontendAndApi(options)
|
||||
connectAngularAndNest(options)
|
||||
]);
|
||||
} else if (options.preset === 'react-express') {
|
||||
throw new Error(`Not implemented yet`);
|
||||
return chain([
|
||||
externalSchematic(
|
||||
'@nrwl/react',
|
||||
'application',
|
||||
{
|
||||
name: options.name,
|
||||
style: options.style,
|
||||
babel: true,
|
||||
linter
|
||||
},
|
||||
{ interactive: false }
|
||||
),
|
||||
externalSchematic(
|
||||
'@nrwl/express',
|
||||
'application',
|
||||
{
|
||||
name: 'api',
|
||||
frontendProject: options.name
|
||||
},
|
||||
{ interactive: false }
|
||||
),
|
||||
schematic('library', { name: 'api-interfaces' }, { interactive: false }),
|
||||
setDefaultCollection('@nrwl/react'),
|
||||
setDefaultLinter(linter),
|
||||
connectReactAndExpress(options)
|
||||
]);
|
||||
} else {
|
||||
throw new Error(`Invalid preset ${options.preset}`);
|
||||
}
|
||||
}
|
||||
|
||||
function connectFrontendAndApi(options: Schema) {
|
||||
function connectAngularAndNest(options: Schema) {
|
||||
const addImportToModule = require('@nrwl/angular/src/utils/ast-utils')
|
||||
.addImportToModule;
|
||||
return (host: Tree) => {
|
||||
host.create(
|
||||
'libs/api-interface/src/lib/interfaces.ts',
|
||||
`export interface Message { message: string }`
|
||||
);
|
||||
host.overwrite(
|
||||
'libs/api-interface/src/index.ts',
|
||||
`export * from './lib/interfaces';`
|
||||
'libs/api-interfaces/src/lib/api-interfaces.ts',
|
||||
`export interface Message { message: string }`
|
||||
);
|
||||
|
||||
const modulePath = `apps/${options.name}/src/app/app.module.ts`;
|
||||
@ -148,7 +165,7 @@ function connectFrontendAndApi(options: Schema) {
|
||||
`apps/${options.name}/src/app/app.component.ts`,
|
||||
`import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Message } from '@${scope}/api-interface';
|
||||
import { Message } from '@${scope}/api-interfaces';
|
||||
|
||||
@Component({
|
||||
selector: '${scope}-root',
|
||||
@ -203,7 +220,7 @@ describe('AppComponent', () => {
|
||||
`apps/api/src/app/app.controller.ts`,
|
||||
`import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { Message } from '@${scope}/api-interface';
|
||||
import { Message } from '@${scope}/api-interfaces';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@ -222,7 +239,7 @@ export class AppController {
|
||||
host.overwrite(
|
||||
`apps/api/src/app/app.service.ts`,
|
||||
`import { Injectable } from '@nestjs/common';
|
||||
import { Message } from '@${scope}/api-interface';
|
||||
import { Message } from '@${scope}/api-interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@ -235,6 +252,98 @@ export class AppService {
|
||||
};
|
||||
}
|
||||
|
||||
function connectReactAndExpress(options: Schema) {
|
||||
return (host: Tree) => {
|
||||
const scope = options.npmScope;
|
||||
const style = options.style ? options.style : 'css';
|
||||
host.overwrite(
|
||||
'libs/api-interfaces/src/lib/api-interfaces.ts',
|
||||
`export interface Message { message: string }`
|
||||
);
|
||||
|
||||
host.overwrite(
|
||||
`apps/${options.name}/src/app/app.tsx`,
|
||||
`import React, { useEffect, useState } from 'react';
|
||||
import { Message } from '@${scope}/api-interfaces';
|
||||
|
||||
import './app.${style}';
|
||||
|
||||
export const App = () => {
|
||||
const [m, setMessage] = useState<Message>({ message: '' });
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api')
|
||||
.then(r => r.json())
|
||||
.then(setMessage);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<h1>Welcome to ${options.name}!</h1>
|
||||
<img
|
||||
width="450"
|
||||
src="https://raw.githubusercontent.com/nrwl/nx/master/nx-logo.png"
|
||||
/>
|
||||
</div>
|
||||
<div>{m.message}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
`
|
||||
);
|
||||
|
||||
host.overwrite(
|
||||
`apps/${options.name}/src/app/app.spec.tsx`,
|
||||
`import { cleanup, getByText, render, wait } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import App from './app';
|
||||
|
||||
describe('App', () => {
|
||||
afterEach(() => {
|
||||
delete global['fetch'];
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('should render successfully', async () => {
|
||||
global['fetch'] = jest.fn().mockResolvedValueOnce({
|
||||
json: () => ({
|
||||
message: 'my message'
|
||||
})
|
||||
});
|
||||
|
||||
const { baseElement } = render(<App />);
|
||||
await wait(() => getByText(baseElement, 'my message'));
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
host.overwrite(
|
||||
`apps/api/src/main.ts`,
|
||||
`import * as express from 'express';
|
||||
import { Message } from '@${scope}/api-interfaces';
|
||||
|
||||
const app = express();
|
||||
|
||||
const greeting: Message = { message: 'Welcome to api!' };
|
||||
|
||||
app.get('/api', (req, res) => {
|
||||
res.send(greeting);
|
||||
});
|
||||
|
||||
const port = process.env.port || 3333;
|
||||
const server = app.listen(port, () => {
|
||||
console.log('Listening at http://localhost:' + port + '/api');
|
||||
});
|
||||
server.on('error', console.error);
|
||||
`
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function setDefaultCollection(defaultCollection: string) {
|
||||
return updateWorkspaceInTree(json => {
|
||||
if (!json.cli) {
|
||||
|
||||
@ -117,6 +117,14 @@ export function sharedNew(cli: string, options: Schema): Rule {
|
||||
function addDependencies(options: Schema) {
|
||||
if (options.preset === 'empty') {
|
||||
return noop();
|
||||
} else if (options.preset === 'web-components') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
'@nrwl/web': nxVersion
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'angular') {
|
||||
return addDepsToPackageJson(
|
||||
{
|
||||
@ -125,6 +133,16 @@ function addDependencies(options: Schema) {
|
||||
{},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'angular-nest') {
|
||||
return addDepsToPackageJson(
|
||||
{
|
||||
'@nrwl/angular': nxVersion
|
||||
},
|
||||
{
|
||||
'@nrwl/nest': nxVersion
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'react') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
@ -133,24 +151,17 @@ function addDependencies(options: Schema) {
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'web-components') {
|
||||
} else if (options.preset === 'react-express') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
'@nrwl/web': nxVersion
|
||||
'@nrwl/react': nxVersion,
|
||||
'@nrwl/express': nxVersion
|
||||
},
|
||||
false
|
||||
);
|
||||
} else {
|
||||
return addDepsToPackageJson(
|
||||
{
|
||||
'@nrwl/angular': nxVersion
|
||||
},
|
||||
{
|
||||
'@nrwl/nest': nxVersion
|
||||
},
|
||||
false
|
||||
);
|
||||
return noop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user