feat(nx): implement next support

This commit is contained in:
Victor Savkin 2019-09-06 19:04:44 -04:00
parent c3f2132436
commit 09a94b8958
120 changed files with 4519 additions and 1254 deletions

View File

@ -0,0 +1,33 @@
# init
Initialize the @nrwl/express plugin
## Usage
```bash
ng generate init ...
```
By default, Nx will search for `init` in the default collection provisioned in `angular.json`.
You can specify the collection explicitly as follows:
```bash
ng g @nrwl/express:init ...
```
Show what will be generated without writing to disk:
```bash
ng g init ... --dry-run
```
## Options
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files

View File

@ -0,0 +1,5 @@
# build
Build a Next.js app
Builder properties can be configured in angular.json when defining the builder, or when invoking it.

View File

@ -0,0 +1,45 @@
# dev-server
Serve a Next.js app
Builder properties can be configured in angular.json when defining the builder, or when invoking it.
## Properties
### buildTarget
Type: `string`
Target which builds the application
### dev
Default: `true`
Type: `boolean`
Serve the application in the dev mode
### port
Default: `4200`
Type: `number`
Port to listen on.
### quiet
Default: `false`
Type: `boolean`
Hide error messages containing server information.
### staticMarkup
Default: `false`
Type: `boolean`
Static markup.

View File

@ -0,0 +1,5 @@
# export
Export a Next.js app
Builder properties can be configured in angular.json when defining the builder, or when invoking it.

View File

@ -0,0 +1,143 @@
# application
Create a Next.js application
## Usage
```bash
ng generate application ...
```
```bash
ng g app ... # same
```
By default, Nx will search for `application` in the default collection provisioned in `angular.json`.
You can specify the collection explicitly as follows:
```bash
ng g @nrwl/next:application ...
```
Show what will be generated without writing to disk:
```bash
ng g application ... --dry-run
```
### Examples
Generate apps/myorg/myapp and apps/myorg/myapp-e2e:
```bash
ng g app myapp --directory=myorg
```
Use class components instead of functional components:
```bash
ng g app myapp --classComponent
```
## Options
### classComponent
Alias(es): C
Default: `false`
Type: `boolean`
Use class components instead of functional component
### directory
Alias(es): d
Type: `string`
The directory of the new application.
### e2eTestRunner
Default: `cypress`
Type: `string`
Possible values: `cypress`, `none`
Test runner to use for end to end (e2e) tests
### linter
Default: `tslint`
Type: `string`
Possible values: `eslint`, `tslint`
The tool to use for running lint checks.
### name
Type: `string`
The name of the application.
### pascalCaseFiles
Alias(es): P
Default: `false`
Type: `boolean`
Use pascal case component file name (e.g. App.tsx)
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files
### skipWorkspaceJson
Default: `false`
Type: `boolean`
Skip updating workspace.json with default schematic options based on values provided to this app (e.g. babel, style)
### style
Alias(es): s
Default: `css`
Type: `string`
Possible values: `css`, `scss`, `styl`, `less`, `styled-components`, `@emotion/styled`
The file extension to be used for style files.
### tags
Alias(es): t
Type: `string`
Add tags to the application (used for linting)
### unitTestRunner
Default: `jest`
Type: `string`
Possible values: `jest`, `none`
Test runner to use for unit tests

View File

@ -1 +1,11 @@
["cypress", "express", "jest", "linter", "nest", "node", "web", "workspace"]
[
"cypress",
"express",
"jest",
"linter",
"nest",
"next",
"node",
"web",
"workspace"
]

View File

@ -4,6 +4,7 @@
"express",
"jest",
"nest",
"next",
"node",
"react",
"web",

View File

@ -0,0 +1,33 @@
# init
Initialize the @nrwl/express plugin
## Usage
```bash
nx generate init ...
```
By default, Nx will search for `init` in the default collection provisioned in `workspace.json`.
You can specify the collection explicitly as follows:
```bash
nx g @nrwl/express:init ...
```
Show what will be generated without writing to disk:
```bash
nx g init ... --dry-run
```
## Options
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files

View File

@ -0,0 +1,6 @@
# build
Build a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/react/guides/cli.

View File

@ -0,0 +1,46 @@
# dev-server
Serve a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/react/guides/cli.
## Properties
### buildTarget
Type: `string`
Target which builds the application
### dev
Default: `true`
Type: `boolean`
Serve the application in the dev mode
### port
Default: `4200`
Type: `number`
Port to listen on.
### quiet
Default: `false`
Type: `boolean`
Hide error messages containing server information.
### staticMarkup
Default: `false`
Type: `boolean`
Static markup.

View File

@ -0,0 +1,6 @@
# export
Export a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/react/guides/cli.

View File

@ -0,0 +1,143 @@
# application
Create a Next.js application
## Usage
```bash
nx generate application ...
```
```bash
nx g app ... # same
```
By default, Nx will search for `application` in the default collection provisioned in `workspace.json`.
You can specify the collection explicitly as follows:
```bash
nx g @nrwl/next:application ...
```
Show what will be generated without writing to disk:
```bash
nx g application ... --dry-run
```
### Examples
Generate apps/myorg/myapp and apps/myorg/myapp-e2e:
```bash
nx g app myapp --directory=myorg
```
Use class components instead of functional components:
```bash
nx g app myapp --classComponent
```
## Options
### classComponent
Alias(es): C
Default: `false`
Type: `boolean`
Use class components instead of functional component
### directory
Alias(es): d
Type: `string`
The directory of the new application.
### e2eTestRunner
Default: `cypress`
Type: `string`
Possible values: `cypress`, `none`
Test runner to use for end to end (e2e) tests
### linter
Default: `tslint`
Type: `string`
Possible values: `eslint`, `tslint`
The tool to use for running lint checks.
### name
Type: `string`
The name of the application.
### pascalCaseFiles
Alias(es): P
Default: `false`
Type: `boolean`
Use pascal case component file name (e.g. App.tsx)
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files
### skipWorkspaceJson
Default: `false`
Type: `boolean`
Skip updating workspace.json with default schematic options based on values provided to this app (e.g. babel, style)
### style
Alias(es): s
Default: `css`
Type: `string`
Possible values: `css`, `scss`, `styl`, `less`, `styled-components`, `@emotion/styled`
The file extension to be used for style files.
### tags
Alias(es): t
Type: `string`
Add tags to the application (used for linting)
### unitTestRunner
Default: `jest`
Type: `string`
Possible values: `jest`, `none`
Test runner to use for unit tests

View File

@ -1 +1,11 @@
["cypress", "express", "jest", "linter", "nest", "node", "web", "workspace"]
[
"cypress",
"express",
"jest",
"linter",
"nest",
"next",
"node",
"web",
"workspace"
]

View File

@ -4,6 +4,7 @@
"express",
"jest",
"nest",
"next",
"node",
"react",
"web",

View File

@ -0,0 +1,33 @@
# init
Initialize the @nrwl/express plugin
## Usage
```bash
nx generate init ...
```
By default, Nx will search for `init` in the default collection provisioned in `workspace.json`.
You can specify the collection explicitly as follows:
```bash
nx g @nrwl/express:init ...
```
Show what will be generated without writing to disk:
```bash
nx g init ... --dry-run
```
## Options
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files

View File

@ -0,0 +1,6 @@
# build
Build a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/web/guides/cli.

View File

@ -0,0 +1,46 @@
# dev-server
Serve a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/web/guides/cli.
## Properties
### buildTarget
Type: `string`
Target which builds the application
### dev
Default: `true`
Type: `boolean`
Serve the application in the dev mode
### port
Default: `4200`
Type: `number`
Port to listen on.
### quiet
Default: `false`
Type: `boolean`
Hide error messages containing server information.
### staticMarkup
Default: `false`
Type: `boolean`
Static markup.

View File

@ -0,0 +1,6 @@
# export
Export a Next.js app
Builder properties can be configured in workspace.json when defining the builder, or when invoking it.
Read more about how to use builders and the CLI here: https://nx.dev/web/guides/cli.

View File

@ -0,0 +1,143 @@
# application
Create a Next.js application
## Usage
```bash
nx generate application ...
```
```bash
nx g app ... # same
```
By default, Nx will search for `application` in the default collection provisioned in `workspace.json`.
You can specify the collection explicitly as follows:
```bash
nx g @nrwl/next:application ...
```
Show what will be generated without writing to disk:
```bash
nx g application ... --dry-run
```
### Examples
Generate apps/myorg/myapp and apps/myorg/myapp-e2e:
```bash
nx g app myapp --directory=myorg
```
Use class components instead of functional components:
```bash
nx g app myapp --classComponent
```
## Options
### classComponent
Alias(es): C
Default: `false`
Type: `boolean`
Use class components instead of functional component
### directory
Alias(es): d
Type: `string`
The directory of the new application.
### e2eTestRunner
Default: `cypress`
Type: `string`
Possible values: `cypress`, `none`
Test runner to use for end to end (e2e) tests
### linter
Default: `tslint`
Type: `string`
Possible values: `eslint`, `tslint`
The tool to use for running lint checks.
### name
Type: `string`
The name of the application.
### pascalCaseFiles
Alias(es): P
Default: `false`
Type: `boolean`
Use pascal case component file name (e.g. App.tsx)
### skipFormat
Default: `false`
Type: `boolean`
Skip formatting files
### skipWorkspaceJson
Default: `false`
Type: `boolean`
Skip updating workspace.json with default schematic options based on values provided to this app (e.g. babel, style)
### style
Alias(es): s
Default: `css`
Type: `string`
Possible values: `css`, `scss`, `styl`, `less`, `styled-components`, `@emotion/styled`
The file extension to be used for style files.
### tags
Alias(es): t
Type: `string`
Add tags to the application (used for linting)
### unitTestRunner
Default: `jest`
Type: `string`
Possible values: `jest`, `none`
Test runner to use for unit tests

View File

@ -1 +1,11 @@
["cypress", "express", "jest", "linter", "nest", "node", "web", "workspace"]
[
"cypress",
"express",
"jest",
"linter",
"nest",
"next",
"node",
"web",
"workspace"
]

View File

@ -4,6 +4,7 @@
"express",
"jest",
"nest",
"next",
"node",
"react",
"web",

133
e2e/next.test.ts Normal file
View File

@ -0,0 +1,133 @@
import { capitalize } from '@nrwl/workspace/src/utils/strings';
import * as http from 'http';
import {
checkFilesExist,
ensureProject,
forEachCli,
readFile,
runCLI,
runCLIAsync,
uniq,
updateFile
} from './utils';
import treeKill = require('tree-kill');
forEachCli('nx', () => {
describe('Next.js Applications', () => {
it('should generate a Nest.jx app that consumes a react lib', async () => {
ensureProject();
const appName = uniq('app');
const libName = uniq('lib');
runCLI(
`generate @nrwl/next:app ${appName} --no-interactive --linter=eslint`
);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
const mainPath = `apps/${appName}/pages/index.tsx`;
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
updateFile(
`apps/${appName}/next.config.js`,
`
module.exports = {
generateBuildId: function () {
return 'fixed';
}
};
`
);
await checkApp(appName, { checkLint: true });
// check that the configuration was consumed
expect(readFile(`dist/apps/${appName}/BUILD_ID`)).toEqual('fixed');
}, 120000);
it('should generate a Next.js app dynamically loading a lib', async () => {
ensureProject();
const appName = uniq('app');
const libName = uniq('lib');
runCLI(
`generate @nrwl/next:app ${appName} --no-interactive --linter=eslint`
);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
const mainPath = `apps/${appName}/pages/index.tsx`;
updateFile(
mainPath,
`
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(
() => import('@proj/${libName}').then(d => d.${capitalize(libName)})
);
` + readFile(mainPath)
);
await checkApp(appName, { checkLint: false });
}, 120000);
});
});
async function checkApp(appName: string, opts: { checkLint: boolean }) {
if (opts.checkLint) {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
}
const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
// const server = fork(
// `./node_modules/@nrwl/cli/bin/nx.js`,
// [`serve`, appName],
// {
// cwd: tmpProjPath(),
// silent: true
// }
// );
// expect(server).toBeTruthy();
// await new Promise(resolve => {
// setTimeout(() => {
// getPage().then(page => {
// expect(page).toContain(`Here are some things you can do with Nx`);
// treeKill(server.pid, 'SIGTERM', err => {
// expect(err).toBeFalsy();
// resolve();
// });
// });
// }, 5000);
// });
// if (supportUi()) {
// const e2eResults = runCLI(`e2e ${appName}-e2e`);
// expect(e2eResults).toContain('All specs passed!');
// }
const buildResult = runCLI(`build ${appName}`);
expect(buildResult).toContain(`Compiled successfully`);
checkFilesExist(`dist/apps/${appName}/build-manifest.json`);
const exportResult = runCLI(`export ${appName}`);
checkFilesExist(`dist/apps/${appName}/exported/index.html`);
}
function getPage(): Promise<string> {
return new Promise(resolve => {
http.get('http://localhost:4200/', res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.once('end', () => {
resolve(data);
});
});
});
}
forEachCli('angular', () => {
describe('next', () => {
it('is not supported', () => {});
});
});

View File

@ -148,15 +148,7 @@ export function ensureProject(): void {
}
export function supportUi() {
// powershell => wsl => no ui for now
try {
execSync(`powershell.exe echo 1`, {
stdio: ['ignore', 'ignore', 'ignore']
});
return false;
} catch (e) {
return true;
}
return !process.env.NO_CHROME;
}
export function copyMissingPackages(): void {
@ -216,6 +208,8 @@ export function copyMissingPackages(): void {
'eslint-plugin-react',
'eslint-plugin-react-hooks',
'next',
'next-server',
'document-register-element'
];
modulesToCopy.forEach(m => copyNodeModule(m));

View File

@ -147,7 +147,12 @@
"webpack-node-externals": "^1.7.2",
"yargs": "^11.0.0",
"yargs-parser": "10.0.0",
"zone.js": "^0.9.0"
"zone.js": "^0.9.0",
"next": "9.0.5",
"@zeit/next-css": "1.0.1",
"@zeit/next-sass": "1.0.1",
"@zeit/next-less": "1.0.1",
"@zeit/next-stylus": "1.0.1"
},
"author": "Victor Savkin",
"license": "MIT",

View File

@ -1,12 +1,13 @@
{
"name": "nx/angular",
"name": "Nx Angular",
"version": "0.1",
"extends": ["@schematics/angular", "@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add @nrwl/angular to a project",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/angular plugin",
"aliaes": ["ng-add"],
"hidden": true
},

View File

@ -29,7 +29,7 @@ import {
addLintFiles
} from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
import {
addImportToModule,
addImportToTestBed,
@ -657,7 +657,7 @@ export default function(schema: Schema): Rule {
: `${options.name}/e2e`;
return chain([
ngAdd({
init({
...options,
skipFormat: true
}),

View File

@ -3,7 +3,7 @@ import { runSchematic, callRule } from '../../utils/testing';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
describe('ng-add', () => {
describe('init', () => {
let appTree: Tree;
beforeEach(() => {
@ -11,7 +11,7 @@ describe('ng-add', () => {
});
it('should add angular dependencies', async () => {
const tree = await runSchematic('ng-add', {}, appTree);
const tree = await runSchematic('init', {}, appTree);
const { dependencies, devDependencies } = readJsonInTree(
tree,
'package.json'
@ -36,7 +36,7 @@ describe('ng-add', () => {
describe('karma', () => {
it('should add karma dependencies', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'karma'
},
@ -57,7 +57,7 @@ describe('ng-add', () => {
it('should add karma configuration', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'karma'
},
@ -68,7 +68,7 @@ describe('ng-add', () => {
it('should set defaults', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'karma'
},
@ -87,7 +87,7 @@ describe('ng-add', () => {
describe('jest', () => {
it('should add jest dependencies', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'jest'
},
@ -101,7 +101,7 @@ describe('ng-add', () => {
it('should add jest configuration', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'jest'
},
@ -112,7 +112,7 @@ describe('ng-add', () => {
it('should set defaults', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'jest'
},
@ -133,7 +133,7 @@ describe('ng-add', () => {
describe('cypress', () => {
it('should add cypress dependencies', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
unitTestRunner: 'none',
e2eTestRunner: 'cypress'
@ -147,7 +147,7 @@ describe('ng-add', () => {
it('should set defaults', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
e2eTestRunner: 'cypress'
},
@ -163,7 +163,7 @@ describe('ng-add', () => {
describe('protractor', () => {
it('should add protractor dependencies', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
e2eTestRunner: 'protractor'
},
@ -179,7 +179,7 @@ describe('ng-add', () => {
it('should set defaults', async () => {
const tree = await runSchematic(
'ng-add',
'init',
{
e2eTestRunner: 'protractor'
},
@ -195,7 +195,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, appTree);
const result = await runSchematic('init', {}, appTree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});
@ -211,7 +211,7 @@ describe('ng-add', () => {
}),
appTree
);
const result = await runSchematic('ng-add', {}, appTree);
const result = await runSchematic('init', {}, appTree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});
@ -227,7 +227,7 @@ describe('ng-add', () => {
}),
appTree
);
const result = await runSchematic('ng-add', {}, appTree);
const result = await runSchematic('init', {}, appTree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
});

View File

@ -67,7 +67,7 @@ export function addUnitTestRunner(
}
return externalSchematic(
'@nrwl/jest',
'ng-add',
'init',
{},
{
interactive: false

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsNxNgAdd",
"id": "SchematicsAngularModuleInit",
"title": "Add Nx Schematics to Project and Convert Workspace",
"description": "NOTE: Does not work in the --dry-run mode",
"type": "object",

View File

@ -36,7 +36,7 @@ import {
toPropertyName,
updateJsonInTree
} from '@nrwl/workspace';
import { addUnitTestRunner } from '../ng-add/ng-add';
import { addUnitTestRunner } from '../init/init';
import { addImportToModule, addRoute } from '../../utils/ast-utils';
import { insertImport } from '@nrwl/workspace/src/utils/ast-utils';

View File

@ -2,10 +2,11 @@
"name": "Nx Cypress",
"version": "0.1",
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add Cypress configuration to the workspace",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/cypress plugin",
"aliases": ["ng-add"],
"hidden": true
},
"cypress-project": {

View File

@ -5,7 +5,7 @@ import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { runSchematic } from '../../utils/testing';
describe('ng-add', () => {
describe('init', () => {
let appTree: Tree;
beforeEach(() => {
@ -14,7 +14,7 @@ describe('ng-add', () => {
});
it('should add dependencies into `package.json` file', async () => {
const tree = await runSchematic('ng-add', {}, appTree);
const tree = await runSchematic('init', {}, appTree);
const packageJson = readJsonInTree(tree, 'package.json');
expect(packageJson.devDependencies.cypress).toBeDefined();

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "Nx Cypress Project Schematics Schema",
"id": "NxCypressInit",
"title": "Add Cypress Configuration to the workspace",
"type": "object",
"properties": {}

View File

@ -1,13 +1,13 @@
{
"name": "nx/express",
"name": "Nx Express",
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add @nrwl/express to a project",
"hidden": true
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/express plugin",
"alias": ["ng-add"]
},
"application": {

View File

@ -9,7 +9,7 @@ import { join, normalize, Path } from '@angular-devkit/core';
import { Schema } from './schema';
import { updateJsonInTree } from '@nrwl/workspace';
import { toFileName, formatFiles } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -54,7 +54,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd({ skipFormat: true }),
init({ skipFormat: true }),
externalSchematic('@nrwl/node', 'application', schema),
addMainFile(options),
addTypes(options),

View File

@ -5,7 +5,7 @@ import { join } from 'path';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
import { callRule, runSchematic } from '../../utils/testing';
describe('ng-add', () => {
describe('init', () => {
let tree: Tree;
let testRunner: SchematicTestRunner;
@ -20,7 +20,7 @@ describe('ng-add', () => {
it('should add dependencies', async () => {
const result = await testRunner
.runSchematicAsync('ng-add', {}, tree)
.runSchematicAsync('init', {}, tree)
.toPromise();
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/express']).toBeUndefined();
@ -31,7 +31,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/express');
});
@ -47,7 +47,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/express');
});
@ -63,7 +63,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});

View File

@ -2,7 +2,7 @@ import { Rule, chain } from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
updateJsonInTree,
addPackageWithNgAdd,
addPackageWithInit,
updateWorkspace,
formatFiles
} from '@nrwl/workspace';
@ -53,8 +53,8 @@ function setDefault(): Rule {
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithNgAdd('@nrwl/node'),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithInit('@nrwl/node'),
addPackageWithInit('@nrwl/jest'),
addDependencies(),
moveDependency(),
formatFiles(schema)

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxExpressNgAdd",
"id": "NxExpressInit",
"title": "Add Nx Express Schematics",
"type": "object",
"properties": {

View File

@ -2,10 +2,11 @@
"name": "Nx Jest",
"version": "0.1",
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add Jest configuration to the workspace",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/jest plugin",
"aliases": ["ng-add"],
"hidden": true
},
"jest-project": {

View File

@ -12,7 +12,7 @@ describe('jest', () => {
});
it('should generate files', async () => {
const resultTree = await runSchematic('ng-add', {}, appTree);
const resultTree = await runSchematic('init', {}, appTree);
expect(resultTree.exists('jest.config.js')).toBeTruthy();
});
@ -23,7 +23,7 @@ describe('jest', () => {
});
it('should add dependencies', async () => {
const resultTree = await runSchematic('ng-add', {}, appTree);
const resultTree = await runSchematic('init', {}, appTree);
const packageJson = readJsonInTree(resultTree, 'package.json');
expect(packageJson.devDependencies.jest).toBeDefined();
expect(packageJson.devDependencies['@nrwl/jest']).toBeDefined();

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxJestNgAdd",
"id": "NxJestInit",
"title": "Add Jest Configuration to a workspace",
"type": "object",
"properties": {},

View File

@ -19,7 +19,7 @@ import {
import { getProjectConfig, addDepsToPackageJson } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
export interface JestProjectSchema {
project: string;
@ -125,7 +125,7 @@ function normalizeOptions(options: JestProjectSchema): JestProjectSchema {
export default function(options: JestProjectSchema): Rule {
options = normalizeOptions(options);
return chain([
ngAdd(),
init(),
check(options),
generateFiles(options),
updateTsConfig(options),

View File

@ -3,10 +3,11 @@
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add @nrwl/nest to a project",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/nest plugin",
"aliases": ["ng-add"],
"hidden": true
},

View File

@ -13,7 +13,7 @@ import {
import { join, normalize, Path } from '@angular-devkit/core';
import { Schema } from './schema';
import { toFileName } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -65,7 +65,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd({
init({
skipFormat: true
}),
externalSchematic('@nrwl/node', 'application', schema),

View File

@ -3,7 +3,7 @@ import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
import { runSchematic, callRule } from '../../utils/testing';
describe('ng-add', () => {
describe('init', () => {
let tree: Tree;
beforeEach(() => {
@ -12,7 +12,7 @@ describe('ng-add', () => {
});
it('should add dependencies', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/nest']).toBeUndefined();
@ -22,7 +22,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/nest');
});
@ -38,7 +38,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/nest');
});
@ -54,7 +54,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});

View File

@ -1,7 +1,7 @@
import { chain, Rule } from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
addPackageWithNgAdd,
addPackageWithInit,
formatFiles,
updateJsonInTree,
updateWorkspace
@ -57,8 +57,8 @@ function setDefault(): Rule {
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithNgAdd('@nrwl/node'),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithInit('@nrwl/node'),
addPackageWithInit('@nrwl/jest'),
addDependencies(),
moveDependency(),
formatFiles(schema)

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxNestNgAdd",
"id": "NxNestInit",
"title": "Add Nx Nest Schematics",
"type": "object",
"properties": {

View File

@ -0,0 +1,20 @@
{
"$schema": "@angular-devkit/architect/src/builders-schema.json",
"builders": {
"build": {
"implementation": "./src/builders/build/build.impl",
"schema": "./src/builders/build/schema.json",
"description": "Build a Next.js app"
},
"dev-server": {
"implementation": "./src/builders/dev-server/dev-server.impl",
"schema": "./src/builders/dev-server/schema.json",
"description": "Serve a Next.js app"
},
"export": {
"implementation": "./src/builders/export/export.impl",
"schema": "./src/builders/export/schema.json",
"description": "Export a Next.js app"
}
}
}

View File

@ -0,0 +1,20 @@
{
"name": "nx/nest",
"version": "0.1",
"extends": ["@nrwl/react"],
"schematics": {
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/next plugin",
"hidden": true
},
"application": {
"factory": "./src/schematics/application/application",
"schema": "./src/schematics/application/schema.json",
"aliases": ["app"],
"description": "Create a Next.js application"
}
}
}

View File

@ -0,0 +1,3 @@
{
"schematics": {}
}

View File

@ -0,0 +1,38 @@
{
"name": "@nrwl/next",
"version": "0.0.1",
"description": "Next.js Plugin for Nx",
"repository": {
"type": "git",
"url": "git+https://github.com/nrwl/nx.git"
},
"keywords": [
"Monorepo",
"Node",
"Next",
"Jest",
"Cypress",
"CLI"
],
"main": "index.js",
"types": "index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://nx.dev",
"schematics": "./collection.json",
"builders": "./builders.json",
"ng-update": {
"requirements": {},
"migrations": "./migrations.json"
},
"peerDependencies": {
"@nrwl/workspace": "*"
},
"dependencies": {
"@nrwl/react": "*",
"@angular-devkit/schematics": "8.1.1"
}
}

View File

@ -0,0 +1,37 @@
import {
BuilderContext,
BuilderOutput,
createBuilder
} from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { PHASE_PRODUCTION_BUILD } from 'next-server/constants';
import build from 'next/dist/build';
import * as path from 'path';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { prepareConfig } from '../../utils/config';
try {
require('dotenv').config();
} catch (e) {}
export interface NextBuildBuilderOptions extends JsonObject {
root: string;
outputPath: string;
}
export default createBuilder<NextBuildBuilderOptions>(run);
function run(
options: NextBuildBuilderOptions,
context: BuilderContext
): Observable<BuilderOutput> {
const root = path.resolve(context.workspaceRoot, options.root);
const config = prepareConfig(
context.workspaceRoot,
options.root,
options.outputPath,
PHASE_PRODUCTION_BUILD
);
return from(build(root, config as any)).pipe(map(() => ({ success: true })));
}

View File

@ -0,0 +1,7 @@
{
"title": "Next Build",
"description": "Build a Next.js app",
"type": "object",
"properties": {},
"required": []
}

View File

@ -0,0 +1,72 @@
import {
BuilderContext,
BuilderOutput,
createBuilder,
targetFromTargetString,
scheduleTargetAndForget
} from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import {
PHASE_DEVELOPMENT_SERVER,
PHASE_PRODUCTION_SERVER
} from 'next-server/constants';
import startServer from 'next/dist/server/lib/start-server';
import * as path from 'path';
import { from, Observable, of } from 'rxjs';
import { switchMap, concatMap } from 'rxjs/operators';
import { prepareConfig } from '../../utils/config';
try {
require('dotenv').config();
} catch (e) {}
export interface NextBuildBuilderOptions extends JsonObject {
dev: boolean;
port: number;
staticMarkup: boolean;
quiet: boolean;
buildTarget: string;
}
export default createBuilder<NextBuildBuilderOptions>(run);
function run(
options: NextBuildBuilderOptions,
context: BuilderContext
): Observable<BuilderOutput> {
const buildTarget = targetFromTargetString(options.buildTarget);
const build$ = !options.dev
? scheduleTargetAndForget(context, buildTarget)
: of({ success: true });
return build$.pipe(
concatMap(r => {
if (!r.success) return of(r);
return from(context.getTargetOptions(buildTarget)).pipe(
concatMap((buildOptions: any) => {
const root = path.resolve(context.workspaceRoot, buildOptions.root);
const config = prepareConfig(
context.workspaceRoot,
buildOptions.root,
buildOptions.outputPath,
options.dev ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_SERVER
);
return from(
startServer(
{
dev: options.dev,
dir: root,
staticMarkup: options.staticMarkup,
quiet: options.quiet,
conf: config
} as any,
options.port
).then(app => app.prepare())
).pipe(switchMap(e => new Observable<BuilderOutput>(obs => {})));
})
);
})
);
}

View File

@ -0,0 +1,32 @@
{
"title": "Next Serve",
"description": "Serve a Next.js app",
"type": "object",
"properties": {
"dev": {
"type": "boolean",
"description": "Serve the application in the dev mode",
"default": true
},
"buildTarget": {
"type": "string",
"description": "Target which builds the application"
},
"port": {
"type": "number",
"description": "Port to listen on.",
"default": 4200
},
"staticMarkup": {
"type": "boolean",
"description": "Static markup.",
"default": false
},
"quiet": {
"type": "boolean",
"description": "Hide error messages containing server information.",
"default": false
}
},
"required": []
}

View File

@ -0,0 +1,64 @@
import {
BuilderContext,
BuilderOutput,
createBuilder,
targetFromTargetString,
scheduleTargetAndForget
} from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { PHASE_EXPORT } from 'next-server/constants';
import exportApp from 'next/dist/export';
import * as path from 'path';
import { from, Observable, of } from 'rxjs';
import { map, concatMap } from 'rxjs/operators';
import { prepareConfig } from '../../utils/config';
try {
require('dotenv').config();
} catch (e) {}
export interface NextBuildBuilderOptions extends JsonObject {
buildTarget: string;
silent: boolean;
threads: number;
concurrency: number;
}
export default createBuilder<NextBuildBuilderOptions>(run);
function run(
options: NextBuildBuilderOptions,
context: BuilderContext
): Observable<BuilderOutput> {
const buildTarget = targetFromTargetString(options.buildTarget);
const build$ = scheduleTargetAndForget(context, buildTarget);
return build$.pipe(
concatMap(r => {
if (!r.success) return of(r);
return from(context.getTargetOptions(buildTarget)).pipe(
concatMap((buildOptions: any) => {
const root = path.resolve(context.workspaceRoot, buildOptions.root);
const config = prepareConfig(
context.workspaceRoot,
buildOptions.root,
buildOptions.outputPath,
PHASE_EXPORT
);
return from(
exportApp(
root,
{
silent: options.silent,
threads: options.threads,
concurrency: options.concurrency,
outdir: `${buildOptions.outputPath}/exported`
} as any,
config
)
).pipe(map(() => ({ success: true })));
})
);
})
);
}

View File

@ -0,0 +1,7 @@
{
"title": "Next Export",
"description": "Export a Next.js app",
"type": "object",
"properties": {},
"required": []
}

View File

@ -0,0 +1,289 @@
import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { readJsonInTree, NxJson } from '@nrwl/workspace';
import { runSchematic } from '../../utils/testing';
describe('app', () => {
let appTree: Tree;
beforeEach(() => {
appTree = Tree.empty();
appTree = createEmptyWorkspace(appTree);
});
describe('not nested', () => {
it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app');
expect(workspaceJson.projects['my-app-e2e'].root).toEqual(
'apps/my-app-e2e'
);
expect(workspaceJson.defaultProject).toEqual('my-app');
});
it('should update nx.json', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', tags: 'one,two' },
appTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({
npmScope: 'proj',
projects: {
'my-app': {
tags: ['one', 'two']
},
'my-app-e2e': {
tags: []
}
}
});
});
it('should generate files', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree);
expect(tree.exists('apps/my-app/tsconfig.json')).toBeTruthy();
expect(tree.exists('apps/my-app/pages/index.tsx')).toBeTruthy();
expect(tree.exists('apps/my-app/specs/index.spec.tsx')).toBeTruthy();
});
});
describe('--style scss', () => {
it('should generate scss styles', async () => {
const result = await runSchematic(
'app',
{ name: 'myApp', style: 'scss' },
appTree
);
expect(result.exists('apps/my-app/pages/index.scss')).toEqual(true);
});
});
it('should setup jest with tsx support', async () => {
const tree = await runSchematic(
'app',
{
name: 'my-app'
},
appTree
);
expect(tree.readContent('apps/my-app/jest.config.js')).toContain(
`moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],`
);
});
it('should set up the nrwl next build builder', async () => {
const tree = await runSchematic(
'app',
{
name: 'my-app'
},
appTree
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.build.builder).toEqual('@nrwl/next:build');
expect(architectConfig.build.options).toEqual({
root: 'apps/my-app',
outputPath: 'dist/apps/my-app'
});
});
it('should set up the nrwl next dev exportr builder', async () => {
const tree = await runSchematic(
'app',
{
name: 'my-app'
},
appTree
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.serve.builder).toEqual('@nrwl/next:dev-server');
expect(architectConfig.serve.options).toEqual({
buildTarget: 'my-app:build',
dev: true
});
expect(architectConfig.serve.configurations).toEqual({
production: { dev: false }
});
});
it('should set up the nrwl next export builder', async () => {
const tree = await runSchematic(
'app',
{
name: 'my-app'
},
appTree
);
const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.export.builder).toEqual('@nrwl/next:export');
expect(architectConfig.export.options).toEqual({
buildTarget: 'my-app:build'
});
});
describe('--unit-test-runner none', () => {
it('should not generate test configuration', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', unitTestRunner: 'none' },
appTree
);
expect(tree.exists('apps/my-app/specs/index.spec.tsx')).toBeFalsy();
});
});
describe('--e2e-test-runner none', () => {
it('should not generate test configuration', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', e2eTestRunner: 'none' },
appTree
);
expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(workspaceJson.projects['my-app-e2e']).toBeUndefined();
});
});
describe('--pascalCaseFiles', () => {
it('should use upper case app file', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', pascalCaseFiles: true },
appTree
);
expect(tree.exists('apps/my-app/pages/Index.tsx')).toBeTruthy();
expect(tree.exists('apps/my-app/specs/Index.spec.tsx')).toBeTruthy();
});
});
it('should generate functional components by default', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const appContent = tree.read('apps/my-app/pages/index.tsx').toString();
expect(appContent).not.toMatch(/extends Component/);
});
describe('--class-component', () => {
it('should generate class components', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', classComponent: true },
appTree
);
const appContent = tree.read('apps/my-app/pages/index.tsx').toString();
expect(appContent).toMatch(/extends Component/);
});
});
// uncomment
// describe('--style styled-components', () => {
// it('should use styled-components as the styled API library', async () => {
// const tree = await runSchematic(
// 'app',
// { name: 'myApp', style: 'styled-components' },
// appTree
// );
// expect(
// tree.exists('apps/my-app/src/app/app.styled-components')
// ).toBeFalsy();
// expect(tree.exists('apps/my-app/src/app/app.tsx')).toBeTruthy();
// const content = tree.read('apps/my-app/src/app/app.tsx').toString();
// expect(content).toContain('styled-component');
// expect(content).toContain('<StyledApp>');
// });
// it('should add dependencies to package.json', async () => {
// const tree = await runSchematic(
// 'app',
// { name: 'myApp', style: 'styled-components' },
// appTree
// );
// const packageJSON = readJsonInTree(tree, 'package.json');
// expect(packageJSON.dependencies['styled-components']).toBeDefined();
// });
// });
// describe('--style @emotion/styled', () => {
// it('should use @emotion/styled as the styled API library', async () => {
// const tree = await runSchematic(
// 'app',
// { name: 'myApp', style: '@emotion/styled' },
// appTree
// );
// expect(
// tree.exists('apps/my-app/src/app/app.@emotion/styled')
// ).toBeFalsy();
// expect(tree.exists('apps/my-app/src/app/app.tsx')).toBeTruthy();
// const content = tree.read('apps/my-app/src/app/app.tsx').toString();
// expect(content).toContain('@emotion/styled');
// expect(content).toContain('<StyledApp>');
// });
// it('should exclude styles from workspace.json', async () => {
// const tree = await runSchematic(
// 'app',
// { name: 'myApp', style: '@emotion/styled' },
// appTree
// );
// const workspaceJson = readJsonInTree(tree, 'workspace.json');
// expect(
// workspaceJson.projects['my-app'].architect.build.options.styles
// ).toEqual([]);
// });
// it('should add dependencies to package.json', async () => {
// const tree = await runSchematic(
// 'app',
// { name: 'myApp', style: '@emotion/styled' },
// appTree
// );
// const packageJSON = readJsonInTree(tree, 'package.json');
// expect(packageJSON.dependencies['@emotion/core']).toBeDefined();
// expect(packageJSON.dependencies['@emotion/styled']).toBeDefined();
// });
// });
describe('--linter=eslint', () => {
it('should add .eslintrc and dependencies', async () => {
const tree = await runSchematic(
'app',
{ name: 'myApp', linter: 'eslint' },
appTree
);
const eslintJson = readJsonInTree(tree, '/apps/my-app/.eslintrc');
const packageJson = readJsonInTree(tree, '/package.json');
expect(eslintJson.plugins).toEqual(
expect.arrayContaining(['react', 'react-hooks'])
);
expect(packageJson).toMatchObject({
devDependencies: {
'eslint-plugin-react': expect.anything(),
'eslint-plugin-react-hooks': expect.anything()
}
});
});
});
});

View File

@ -0,0 +1,259 @@
import { join, JsonObject, normalize, Path } from '@angular-devkit/core';
import {
apply,
chain,
externalSchematic,
filter,
mergeWith,
move,
noop,
Rule,
SchematicContext,
template,
Tree,
url
} from '@angular-devkit/schematics';
import {
assertValidStyle,
extraEslintDependencies,
reactEslintJson,
CSS_IN_JS_DEPENDENCIES
} from '@nrwl/react';
import {
addLintFiles,
formatFiles,
generateProjectLint,
names,
NxJson,
offsetFromRoot,
toFileName,
updateJsonInTree,
updateWorkspace
} from '@nrwl/workspace';
import {
updateWorkspaceInTree,
addDepsToPackageJson
} from '@nrwl/workspace/src/utils/ast-utils';
import init from '../init/init';
import { Schema } from './schema';
interface NormalizedSchema extends Schema {
projectName: string;
appProjectRoot: Path;
e2eProjectName: string;
e2eProjectRoot: Path;
parsedTags: string[];
fileName: string;
styledModule: null | string;
}
export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(host, schema);
return chain([
init({
skipFormat: true
}),
addLintFiles(options.appProjectRoot, options.linter, {
localConfig: reactEslintJson,
extraPackageDeps: extraEslintDependencies
}),
createApplicationFiles(options),
updateNxJson(options),
addProject(options),
addCypress(options),
addJest(options),
addStyledModuleDependencies(options),
setDefaults(options),
formatFiles(options)
]);
};
}
function createApplicationFiles(options: NormalizedSchema): Rule {
return mergeWith(
apply(url(`./files`), [
template({
...names(options.name),
...options,
tmpl: '',
offsetFromRoot: offsetFromRoot(options.appProjectRoot)
}),
options.styledModule
? filter(file => !file.endsWith(`.${options.style}`))
: noop(),
options.unitTestRunner === 'none'
? filter(file => file !== `/specs/index.spec.tsx`)
: noop(),
move(options.appProjectRoot)
])
);
}
function updateNxJson(options: NormalizedSchema): Rule {
return updateJsonInTree<NxJson>('nx.json', json => {
json.projects[options.projectName] = { tags: options.parsedTags };
return json;
});
}
function addProject(options: NormalizedSchema): Rule {
return updateWorkspaceInTree(json => {
const architect: { [key: string]: any } = {};
architect.build = {
builder: '@nrwl/next:build',
options: {
root: options.appProjectRoot,
outputPath: join(normalize('dist'), options.appProjectRoot)
}
};
architect.serve = {
builder: '@nrwl/next:dev-server',
options: {
buildTarget: `${options.projectName}:build`,
dev: true
},
configurations: {
production: {
dev: false
}
}
};
architect.export = {
builder: '@nrwl/next:export',
options: {
buildTarget: `${options.projectName}:build`
}
};
architect.lint = generateProjectLint(
normalize(options.appProjectRoot),
join(normalize(options.appProjectRoot), 'tsconfig.json'),
options.linter
);
json.projects[options.projectName] = {
root: options.appProjectRoot,
sourceRoot: options.appProjectRoot,
projectType: 'application',
schematics: {},
architect
};
json.defaultProject = json.defaultProject || options.projectName;
return json;
});
}
function addCypress(options: NormalizedSchema): Rule {
return options.e2eTestRunner === 'cypress'
? externalSchematic('@nrwl/cypress', 'cypress-project', {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName
})
: noop();
}
function addJest(options: NormalizedSchema): Rule {
return options.unitTestRunner === 'jest'
? chain([
externalSchematic('@nrwl/jest', 'jest-project', {
project: options.projectName,
supportTsx: true,
skipSerializers: true,
setupFile: 'none'
}),
updateJsonInTree(
`${options.appProjectRoot}/tsconfig.spec.json`,
json => {
json.compilerOptions.jsx = 'react';
return json;
}
)
])
: noop();
}
function addStyledModuleDependencies(options: NormalizedSchema): Rule {
const extraDependencies = CSS_IN_JS_DEPENDENCIES[options.styledModule];
return extraDependencies
? addDepsToPackageJson(
extraDependencies.dependencies,
extraDependencies.devDependencies
)
: noop();
}
function setDefaults(options: NormalizedSchema): Rule {
return options.skipWorkspaceJson
? noop()
: updateWorkspace(workspace => {
workspace.extensions.schematics = jsonIdentity(
workspace.extensions.schematics || {}
);
workspace.extensions.schematics['@nrwl/next'] =
workspace.extensions.schematics['@nrwl/next'] || {};
const prev = jsonIdentity(
workspace.extensions.schematics['@nrwl/next']
);
workspace.extensions.schematics = {
...workspace.extensions.schematics,
'@nrwl/next': {
...prev,
application: {
style: options.style,
linter: options.linter,
...jsonIdentity(prev.application)
}
}
};
});
}
function jsonIdentity(x: any): JsonObject {
return x as JsonObject;
}
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const appDirectory = options.directory
? `${toFileName(options.directory)}/${toFileName(options.name)}`
: toFileName(options.name);
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
const e2eProjectName = `${appProjectName}-e2e`;
const appProjectRoot = normalize(`apps/${appDirectory}`);
const e2eProjectRoot = normalize(`apps/${appDirectory}-e2e`);
const parsedTags = options.tags
? options.tags.split(',').map(s => s.trim())
: [];
const fileName = options.pascalCaseFiles ? 'Index' : 'index';
const styledModule = /^(css|scss|less|styl)$/.test(options.style)
? null
: options.style;
assertValidStyle(options.style);
return {
...options,
name: toFileName(options.name),
projectName: appProjectName,
appProjectRoot,
e2eProjectRoot,
e2eProjectName,
parsedTags,
fileName,
styledModule
};
}

View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

View File

@ -0,0 +1,132 @@
/*
* Remove template code below
*/
.app {
font-family: sans-serif;
min-width: 300px;
max-width: 600px;
margin: 50px auto;
}
.app .gutter-left {
margin-left: 9px;
}
.app .col-span-2 {
grid-column: span 2;
}
.app .flex {
display: flex;
align-items: center;
justify-content: center;
}
.app header {
background-color: #143055;
color: white;
padding: 5px;
border-radius: 3px;
}
.app main {
padding: 0 36px;
}
.app p {
text-align: center;
}
.app h1 {
text-align: center;
margin-left: 18px;
font-size: 24px;
}
.app h2 {
text-align: center;
font-size: 20px;
margin: 40px 0 10px 0;
}
.app .resources {
text-align: center;
list-style: none;
padding: 0;
display: grid;
grid-gap: 9px;
grid-template-columns: 1fr 1fr;
}
.app .resource {
color: #0094ba;
height: 36px;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
padding: 3px 9px;
text-decoration: none;
}
.app .resource:hover {
background-color: rgba(68, 138, 255, 0.04);
}
.app pre {
padding: 9px;
border-radius: 4px;
background-color: black;
color: #eee;
}
.app details {
border-radius: 4px;
color: #333;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
padding: 3px 9px;
margin-bottom: 9px;
}
.app summary {
outline: none;
height: 36px;
line-height: 36px;
}
.app .github-star-container {
margin-top: 12px;
line-height: 20px;
}
.app .github-star-container a {
display: flex;
align-items: center;
text-decoration: none;
color: #333;
}
.app .github-star-badge {
color: #24292e;
display: flex;
align-items: center;
font-size: 12px;
padding: 3px 10px;
border: 1px solid rgba(27,31,35,.2);
border-radius: 3px;
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
margin-left: 4px;
font-weight: 600;
}
.app .github-star-badge:hover {
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
border-color: rgba(27,31,35,.35);
background-position: -.5em;
}
.app .github-star-badge .material-icons {
height: 16px;
width: 16px;
margin-right: 4px;
}

View File

@ -0,0 +1,265 @@
<% if (classComponent) { %>
import React, { Component } from 'react';
<% } else { %>
import React from 'react';
<% } %>
<% if (styledModule) {
var wrapper = 'StyledApp';
%>import styled from '<%= styledModule %>';<% } else {
var wrapper = 'div';
%>
import './<%= fileName %>.<%= style %>';
<% }
%>
<% if (styledModule) { %>
const StyledApp = styled.div`
/*
* Remove template code below
*/
font-family: sans-serif;
min-width: 300px;
max-width: 600px;
margin: 50px auto;
.gutter-left {
margin-left: 9px;
}
.col-span-2 {
grid-column: span 2;
}
.flex {
display: flex;
align-items: center;
justify-content: center;
}
header {
background-color: #143055;
color: white;
padding: 5px;
border-radius: 3px;
}
main {
padding: 0 36px;
}
p {
text-align: center;
}
h1 {
text-align: center;
margin-left: 18px;
font-size: 24px;
}
h2 {
text-align: center;
font-size: 20px;
margin: 40px 0 10px 0;
}
.resources {
text-align: center;
list-style: none;
padding: 0;
display: grid;
grid-gap: 9px;
grid-template-columns: 1fr 1fr;
}
.resource {
color: #0094ba;
height: 36px;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
padding: 3px 9px;
text-decoration: none;
}
.resource:hover {
background-color: rgba(68, 138, 255, 0.04);
}
pre {
padding: 9px;
border-radius: 4px;
background-color: black;
color: #eee;
}
details {
border-radius: 4px;
color: #333;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
padding: 3px 9px;
margin-bottom: 9px;
}
summary {
outline: none;
height: 36px;
line-height: 36px;
}
.github-star-container {
margin-top: 12px;
line-height: 20px;
}
.github-star-container a {
display: flex;
align-items: center;
text-decoration: none;
color: #333;
}
.github-star-badge {
color: #24292e;
display: flex;
align-items: center;
font-size: 12px;
padding: 3px 10px;
border: 1px solid rgba(27,31,35,.2);
border-radius: 3px;
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
margin-left: 4px;
font-weight: 600;
}
.github-star-badge:hover {
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
border-color: rgba(27,31,35,.35);
background-position: -.5em;
}
.github-star-badge .material-icons {
height: 16px;
width: 16px;
margin-right: 4px;
}
`;
<% }%>
<%
var innerJsx = `
<header className="flex">
<img
alt=""
width="75"
src="https://nx.dev/assets/images/nx-logo-white.svg"
/>
<h1>Welcome to ${name}!</h1>
</header>
<main>
<h2>Resources &amp; Tools</h2>
<p>
Thank you for using and showing some ♥ for Nx.
</p>
<div className="flex github-star-container">
<a href="https://github.com/nrwl/nx" target="_blank" rel="noopener noreferrer"> If you like Nx, please give it a star:
<div className="github-star-badge">
<svg className="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
Star
</div>
</a>
</div>
<p>
Here are some links to help you get started.
</p>
<ul className="resources">
<li className="col-span-2">
<a
className="resource flex"
href="https://nx.dev/react/getting-started/what-is-nx"
>
Nx video tutorial
</a>
</li>
<li className="col-span-2">
<a
className="resource flex"
href="https://nx.dev/react/tutorial/01-create-application"
>
Interactive tutorial
</a>
</li>
<li className="col-span-2">
<a className="resource flex" href="https://connect.nrwl.io/">
<img
height="36"
alt="Nrwl Connect"
src="https://connect.nrwl.io/assets/img/CONNECT_ColorIcon.png"
/>
<span className="gutter-left">
Nrwl Connect
</span>
</a>
</li>
</ul>
<h2>Next Steps</h2>
<p>
Here are some things you can do with Nx.
</p>
<details open>
<summary>Add UI library</summary>
<pre>{\`# Generate UI lib
nx g @nrwl/react:lib ui
# Add a component
nx g @nrwl/react:component xyz --project ui\`}</pre>
</details>
<details>
<summary>View dependency graph</summary>
<pre>{\`nx dep-graph\`}</pre>
</details>
<details>
<summary>Run affected commands</summary>
<pre>{\`# see what's been affected by changes
nx affected:dep-graph
# run tests for current changes
nx affected:test
# run e2e tests for current changes
nx affected:e2e
\`}</pre>
</details>
</main>
`;
%>
<% if (classComponent) { %>
export class Index extends Component {
render() {
return (
<<%= wrapper %>>
<%= innerJsx %>
</<%= wrapper %>>
);
}
}
<% } else { %>
export const Index = () => {
/*
* Replace the elements below with your own.
*
* Note: The corresponding styles are in the ./${fileName}.${style} file.
*/
return (
<<%= wrapper %><% if (!styledModule) {%> className="app"<% } %>>
<%= innerJsx %>
</<%= wrapper %>>
);
};
<% } %>
export default Index;

View File

@ -0,0 +1,18 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import Index from '../pages/index';
describe('Index', () => {
afterEach(cleanup);
it('should render successfully', () => {
const { baseElement } = render(<Index />);
expect(baseElement).toBeTruthy();
});
it('should have a greeting as the title', () => {
const { getByText } = render(<Index />);
expect(getByText('Welcome to <%= projectName %>!')).toBeTruthy();
});
});

View File

@ -0,0 +1,16 @@
{
"extends": "<%= offsetFromRoot %>tsconfig.json",
"compilerOptions": {
"jsx": "preserve",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["node", "jest"],
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["**/*.ts", "**/*.tsx", "next-env.d.ts"]
}

View File

@ -0,0 +1,15 @@
import { Linter } from '@nrwl/workspace';
export interface Schema {
name: string;
style?: string;
skipFormat: boolean;
directory?: string;
tags?: string;
unitTestRunner: 'jest' | 'none';
e2eTestRunner: 'cypress' | 'none';
linter: Linter;
pascalCaseFiles?: boolean;
skipWorkspaceJson?: boolean;
classComponent: boolean;
}

View File

@ -0,0 +1,111 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxNextApp",
"title": "Create a Next.js Application for Nx",
"examples": [
{
"command": "g app myapp --directory=myorg",
"description": "Generate apps/myorg/myapp and apps/myorg/myapp-e2e"
},
{
"command": "g app myapp --classComponent",
"description": "Use class components instead of functional components"
}
],
"type": "object",
"properties": {
"name": {
"description": "The name of the application.",
"type": "string",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the application?"
},
"directory": {
"description": "The directory of the new application.",
"type": "string",
"alias": "d"
},
"style": {
"description": "The file extension to be used for style files.",
"type": "string",
"default": "css",
"alias": "s",
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{
"value": "scss",
"label": "SASS(.scss) [ http://sass-lang.com ]"
},
{
"value": "styl",
"label": "Stylus(.styl) [ http://stylus-lang.com ]"
},
{
"value": "less",
"label": "LESS [ http://lesscss.org ]"
},
{
"value": "styled-components",
"label": "styled-components [ https://styled-components.com ]"
},
{
"value": "@emotion/styled",
"label": "emotion [ https://emotion.sh ]"
}
]
}
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"skipFormat": {
"description": "Skip formatting files",
"type": "boolean",
"default": false
},
"skipWorkspaceJson": {
"description": "Skip updating workspace.json with default schematic options based on values provided to this app (e.g. babel, style)",
"type": "boolean",
"default": false
},
"unitTestRunner": {
"type": "string",
"enum": ["jest", "none"],
"description": "Test runner to use for unit tests",
"default": "jest"
},
"e2eTestRunner": {
"type": "string",
"enum": ["cypress", "none"],
"description": "Test runner to use for end to end (e2e) tests",
"default": "cypress"
},
"tags": {
"type": "string",
"description": "Add tags to the application (used for linting)",
"alias": "t"
},
"pascalCaseFiles": {
"type": "boolean",
"description": "Use pascal case component file name (e.g. App.tsx)",
"alias": "P",
"default": false
},
"classComponent": {
"type": "boolean",
"description": "Use class components instead of functional component",
"alias": "C",
"default": false
}
},
"required": []
}

View File

@ -0,0 +1,64 @@
import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { readJsonInTree } from '@nrwl/workspace';
import { updateJsonInTree } from '@nrwl/workspace';
import { runSchematic, callRule } from '../../utils/testing';
describe('init', () => {
let tree: Tree;
beforeEach(() => {
tree = Tree.empty();
tree = createEmptyWorkspace(tree);
});
it('should add react dependencies', async () => {
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/next']).toBeUndefined();
expect(packageJson.dependencies['@nrwl/react']).toBeUndefined();
expect(packageJson.dependencies['next']).toBeDefined();
});
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/next');
});
it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule(
updateJsonInTree('workspace.json', json => {
json.cli = {
defaultCollection: '@nrwl/workspace'
};
return json;
}),
tree
);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/next');
});
it('should not be set if something else was set before', async () => {
tree = await callRule(
updateJsonInTree('workspace.json', json => {
json.cli = {
defaultCollection: '@nrwl/angular'
};
json.schematics = {};
return json;
}),
tree
);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});
});
});

View File

@ -0,0 +1,47 @@
import { JsonObject } from '@angular-devkit/core';
import { chain, Rule } from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
addPackageWithInit,
updateWorkspace
} from '@nrwl/workspace';
import { frameworkVersion, pluginVersion } from '../../utils/versions';
import { Schema } from './schema';
export function addDependencies(): Rule {
return addDepsToPackageJson(
{
next: frameworkVersion,
'@zeit/next-css': pluginVersion,
'@zeit/next-sass': pluginVersion,
'@zeit/next-less': pluginVersion,
'@zeit/next-stylus': pluginVersion
},
{}
);
}
function setDefault(): Rule {
return updateWorkspace(workspace => {
// Set workspace default collection to 'react' if not already set.
workspace.extensions.cli = workspace.extensions.cli || {};
const defaultCollection: string =
workspace.extensions.cli &&
((workspace.extensions.cli as JsonObject).defaultCollection as string);
if (!defaultCollection || defaultCollection === '@nrwl/workspace') {
(workspace.extensions.cli as JsonObject).defaultCollection = '@nrwl/next';
}
});
}
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithInit('@nrwl/jest'),
addPackageWithInit('@nrwl/cypress'),
addPackageWithInit('@nrwl/web'),
addPackageWithInit('@nrwl/react'),
addDependencies()
]);
}

View File

@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxNextNgInit",
"title": "Add Nx Next Schematics",
"type": "object",
"properties": {
"skipFormat": {
"description": "Skip formatting files",
"type": "boolean",
"default": false
}
},
"required": []
}

View File

@ -0,0 +1,45 @@
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import loadConfig from 'next-server/dist/server/config';
import { offsetFromRoot } from '@nrwl/workspace';
import * as path from 'path';
import * as withCSS from '@zeit/next-css';
import * as withLESS from '@zeit/next-less';
import * as withSASS from '@zeit/next-sass';
import * as withSTYLUS from '@zeit/next-stylus';
function createWebpackConfig(root: string) {
return function webpackConfig(config, { defaultLoaders }) {
const mainFields = ['es2015', 'module', 'main'];
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
config.resolve.plugins = [
new TsConfigPathsPlugin({
configFile: path.resolve(root, 'tsconfig.json'),
extensions,
mainFields
})
];
config.module.rules.push({
test: /\.tsx/,
use: [defaultLoaders.babel]
});
return config;
};
}
export function prepareConfig(
workspaceRoot: string,
root: string,
outputPath: string,
mode: string
) {
const absoluteRoot = path.resolve(workspaceRoot, root);
const config = withSTYLUS(
withSASS(withLESS(withCSS(loadConfig(mode, root, null))))
);
config.distDir = `${offsetFromRoot(root)}${outputPath}`;
config.outdir = `${offsetFromRoot(root)}${outputPath}`;
const userWebpack = config.webpack;
config.webpack = (a, b) =>
createWebpackConfig(absoluteRoot)(userWebpack(a, b), b);
return config;
}

View File

@ -0,0 +1,35 @@
import { join } from 'path';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { Rule, Tree } from '@angular-devkit/schematics';
import { names } from '@nrwl/workspace/src/utils/name-utils';
import { updateWorkspace } from '@nrwl/workspace/src/utils/workspace';
const testRunner = new SchematicTestRunner(
'@nrwl/next',
join(__dirname, '../../collection.json')
);
export function runSchematic(schematicName: string, options: any, tree: Tree) {
return testRunner.runSchematicAsync(schematicName, options, tree).toPromise();
}
export function callRule(rule: Rule, tree: Tree) {
return testRunner.callRule(rule, tree).toPromise();
}
export function createApp(tree: Tree, appName: string): Promise<Tree> {
const { fileName } = names(appName);
return callRule(
updateWorkspace(workspace => {
workspace.projects.add({
name: fileName,
root: `apps/${fileName}`,
projectType: 'application',
sourceRoot: `apps/${fileName}/src`,
targets: {}
});
}),
tree
);
}

View File

@ -0,0 +1,3 @@
export const nxVersion = '*';
export const frameworkVersion = '9.0.5';
export const pluginVersion = '1.0.1';

View File

@ -3,10 +3,11 @@
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add @nrwl/node to a project",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/node plugin",
"aliases": ["ng-add"],
"hidden": true
},

View File

@ -22,7 +22,7 @@ import {
import { toFileName } from '@nrwl/workspace';
import { getProjectConfig } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -148,7 +148,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd({
init({
skipFormat: true
}),
addLintFiles(options.appProjectRoot, options.linter),

View File

@ -3,7 +3,7 @@ import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
import { callRule, runSchematic } from '../../utils/testing';
describe('ng-add', () => {
describe('init', () => {
let tree: Tree;
beforeEach(() => {
@ -12,7 +12,7 @@ describe('ng-add', () => {
});
it('should add dependencies', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/node']).toBeUndefined();
expect(packageJson.devDependencies['@nrwl/node']).toBeDefined();
@ -20,7 +20,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
});
@ -36,7 +36,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
});
@ -52,7 +52,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});

View File

@ -2,7 +2,7 @@ import { Rule, chain } from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
updateJsonInTree,
addPackageWithNgAdd,
addPackageWithInit,
updateWorkspace,
formatFiles
} from '@nrwl/workspace';
@ -45,7 +45,7 @@ function setDefault(): Rule {
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithInit('@nrwl/jest'),
addDependencies(),
moveDependency(),
formatFiles(schema)

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxNodeNgAdd",
"id": "NxNodeInit",
"title": "Add Nx Node Schematics",
"type": "object",
"properties": {

View File

@ -1,12 +1,13 @@
{
"name": "nx/react",
"name": "Nx React",
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"description": "Add @nrwl/react to a project",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/react plugin",
"aliases": ["ng-add"],
"hidden": true
},

View File

@ -0,0 +1,3 @@
export { extraEslintDependencies, reactEslintJson } from './src/utils/lint';
export { CSS_IN_JS_DEPENDENCIES } from './src/utils/styled';
export { assertValidStyle } from './src/utils/assertion';

View File

@ -30,7 +30,7 @@ import {
addDepsToPackageJson,
updateWorkspaceInTree
} from '@nrwl/workspace/src/utils/ast-utils';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
import * as ts from 'typescript';
import { Schema } from './schema';
@ -59,7 +59,7 @@ export default function(schema: Schema): Rule {
const options = normalizeOptions(host, schema);
return chain([
ngAdd({
init({
skipFormat: true
}),
addLintFiles(options.appProjectRoot, options.linter, {

View File

@ -4,7 +4,7 @@ import { readJsonInTree } from '@nrwl/workspace';
import { updateJsonInTree } from '@nrwl/workspace';
import { runSchematic, callRule } from '../../utils/testing';
describe('ng-add', () => {
describe('init', () => {
let tree: Tree;
beforeEach(() => {
@ -13,7 +13,7 @@ describe('ng-add', () => {
});
it('should add react dependencies', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/react']).toBeUndefined();
expect(packageJson.dependencies['react']).toBeDefined();
@ -26,7 +26,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
expect(workspaceJson.schematics['@nrwl/react'].application.babel).toBe(
@ -45,7 +45,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
expect(workspaceJson.schematics['@nrwl/react'].application.babel).toBe(
@ -66,7 +66,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});

View File

@ -2,7 +2,7 @@ import { chain, Rule } from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
updateJsonInTree,
addPackageWithNgAdd,
addPackageWithInit,
updateWorkspace
} from '@nrwl/workspace';
import { Schema } from './schema';
@ -76,9 +76,9 @@ function jsonIdentity(x: any): JsonObject {
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithNgAdd('@nrwl/cypress'),
addPackageWithNgAdd('@nrwl/web'),
addPackageWithInit('@nrwl/jest'),
addPackageWithInit('@nrwl/cypress'),
addPackageWithInit('@nrwl/web'),
addDependencies(),
moveDependency()
]);

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxReactNgAdd",
"id": "NxReactNgInit",
"title": "Add Nx React Schematics",
"type": "object",
"properties": {

View File

@ -1,6 +1,6 @@
import * as ts from 'typescript';
import { join, Path, strings } from '@angular-devkit/core';
import '@nrwl/tao/src/compat/angular-cli-compat';
import '@nrwl/tao/src/compat/compat';
import {
addDepsToPackageJson,
addGlobal,

View File

@ -1,5 +1,5 @@
#!/usr/bin/env node
import './src/compat/angular-cli-compat';
import './src/compat/compat';
export async function invokeCommand(
command: string,

View File

@ -3,9 +3,9 @@
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"ng-add": {
"factory": "./src/schematics/ng-add/ng-add",
"schema": "./src/schematics/ng-add/schema.json",
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Add @nrwl/web to a project",
"hidden": true
},

View File

@ -26,7 +26,7 @@ import {
generateProjectLint,
addLintFiles
} from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
import init from '../init/init';
interface NormalizedSchema extends Schema {
projectName: string;
@ -150,7 +150,7 @@ export default function(schema: Schema): Rule {
const options = normalizeOptions(host, schema);
return chain([
ngAdd({
init({
skipFormat: true
}),
addLintFiles(options.appProjectRoot, options.linter),

View File

@ -4,7 +4,7 @@ import { readJsonInTree } from '@nrwl/workspace';
import { callRule, runSchematic } from '../../utils/testing';
import { updateJsonInTree } from '@nrwl/workspace/src/utils/ast-utils';
describe('ng-add', () => {
describe('init', () => {
let tree: Tree;
beforeEach(() => {
@ -13,7 +13,7 @@ describe('ng-add', () => {
});
it('should add web dependencies', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const packageJson = readJsonInTree(result, 'package.json');
expect(packageJson.dependencies['@nrwl/web']).toBeUndefined();
expect(packageJson.dependencies['document-register-element']).toBeDefined();
@ -22,7 +22,7 @@ describe('ng-add', () => {
describe('defaultCollection', () => {
it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
});
@ -38,7 +38,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
});
@ -54,7 +54,7 @@ describe('ng-add', () => {
}),
tree
);
const result = await runSchematic('ng-add', {}, tree);
const result = await runSchematic('init', {}, tree);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
});

View File

@ -1,7 +1,7 @@
import { Rule, chain } from '@angular-devkit/schematics';
import {
updateJsonInTree,
addPackageWithNgAdd,
addPackageWithInit,
formatFiles
} from '@nrwl/workspace';
import { addDepsToPackageJson } from '@nrwl/workspace';
@ -50,8 +50,8 @@ function setDefault(): Rule {
export default function(schema: Schema) {
return chain([
setDefault(),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithNgAdd('@nrwl/cypress'),
addPackageWithInit('@nrwl/jest'),
addPackageWithInit('@nrwl/cypress'),
addDependencies(),
moveDependency(),
formatFiles(schema)

Some files were not shown because too many files have changed in this diff Show More