Skip to content

Commit

Permalink
feat(nx): add eslint support
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry authored and vsavkin committed Jul 31, 2019
1 parent 2abe13d commit 448233b
Show file tree
Hide file tree
Showing 23 changed files with 816 additions and 55 deletions.
8 changes: 8 additions & 0 deletions docs/api-angular/schematics/application.md
Expand Up @@ -53,6 +53,14 @@ Type: `boolean`

Specifies if the template will be in the ts file.

### linter

Default: `tslint`

Type: `string`

The tool to use for running lint checks.

### name

Type: `string`
Expand Down
8 changes: 8 additions & 0 deletions docs/api-express/schematics/application.md
Expand Up @@ -23,6 +23,14 @@ Type: `string`

Frontend project that needs to access this application. This sets up proxy configuration.

### linter

Default: `tslint`

Type: `string`

The tool to use for running lint checks.

### name

Type: `string`
Expand Down
8 changes: 8 additions & 0 deletions docs/api-nest/schematics/application.md
Expand Up @@ -23,6 +23,14 @@ Type: `string`

Frontend project that needs to access this application. This sets up proxy configuration.

### linter

Default: `tslint`

Type: `string`

The tool to use for running lint checks.

### name

Type: `string`
Expand Down
15 changes: 11 additions & 4 deletions e2e/cypress.test.ts
Expand Up @@ -11,13 +11,18 @@ import {
supportUi
} from './utils';

forEachCli(() => {
forEachCli(currentCLIName => {
const linter = currentCLIName === 'angular' ? 'tslint' : 'eslint';
const nrwlPackageName = currentCLIName === 'angular' ? 'angular' : 'react';

describe('Cypress E2E Test runner', () => {
describe('project scaffolding', () => {
it('should generate an app with the Cypress as e2e test runner', () => {
ensureProject();
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`);
runCLI(
`generate @nrwl/${nrwlPackageName}:app ${myapp} --e2eTestRunner=cypress --linter=${linter}`
);

// Making sure the package.json file contains the Cypress dependency
const packageJson = readJson('package.json');
Expand All @@ -38,10 +43,12 @@ forEachCli(() => {

if (supportUi()) {
describe('running Cypress', () => {
fit('should execute e2e tests using Cypress', () => {
it('should execute e2e tests using Cypress', () => {
newProject();
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`);
runCLI(
`generate @nrwl/${nrwlPackageName}:app ${myapp} --e2eTestRunner=cypress --linter=${linter}`
);

expect(runCLI(`e2e ${myapp}-e2e --headless --no-watch`)).toContain(
'All specs passed!'
Expand Down
18 changes: 14 additions & 4 deletions e2e/node.test.ts
Expand Up @@ -31,12 +31,17 @@ function getData(): Promise<any> {
});
}

forEachCli(() => {
forEachCli(currentCLIName => {
const linter = currentCLIName === 'angular' ? 'tslint' : 'eslint';

describe('Node Applications', () => {
it('should be able to generate an express application', async done => {
ensureProject();
const nodeapp = uniq('nodeapp');
runCLI(`generate @nrwl/express:app ${nodeapp}`);

runCLI(`generate @nrwl/express:app ${nodeapp} --linter=${linter}`);
const lintResults = runCLI(`lint ${nodeapp}`);
expect(lintResults).toContain('All files pass linting.');

updateFile(
`apps/${nodeapp}/src/app/test.spec.ts`,
Expand Down Expand Up @@ -123,7 +128,9 @@ forEachCli(() => {
it('should be able to generate a nest application', async done => {
ensureProject();
const nestapp = uniq('nestapp');
runCLI(`generate @nrwl/nest:app ${nestapp}`);
runCLI(`generate @nrwl/nest:app ${nestapp} --linter=${linter}`);
const lintResults = runCLI(`lint ${nestapp}`);
expect(lintResults).toContain('All files pass linting.');

updateFile(`apps/${nestapp}/src/assets/file.txt`, ``);
const jestResult = await runCLIAsync(`test ${nestapp}`);
Expand Down Expand Up @@ -183,7 +190,10 @@ forEachCli(() => {
ensureProject();
const nodeapp = uniq('nodeapp');

runCLI(`generate @nrwl/node:app ${nodeapp}`);
runCLI(`generate @nrwl/node:app ${nodeapp} --linter=${linter}`);
const lintResults = runCLI(`lint ${nodeapp}`);
expect(lintResults).toContain('All files pass linting.');

updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`);
await runCLIAsync(`build ${nodeapp}`);

Expand Down
43 changes: 29 additions & 14 deletions e2e/react.test.ts
Expand Up @@ -14,14 +14,18 @@ import {
} from './utils';
import { serializeJson } from '@nrwl/workspace';

forEachCli(() => {
forEachCli(currentCLIName => {
const linter = currentCLIName === 'angular' ? 'tslint' : 'eslint';

describe('React Applications', () => {
it('should be able to generate a react app + lib', async () => {
ensureProject();
const appName = uniq('app');
const libName = uniq('lib');

runCLI(`generate @nrwl/react:app ${appName} --no-interactive --babel`);
runCLI(
`generate @nrwl/react:app ${appName} --no-interactive --babel --linter=${linter}`
);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);

const mainPath = `apps/${appName}/src/main.tsx`;
Expand All @@ -30,37 +34,42 @@ forEachCli(() => {
const libTestResults = await runCLIAsync(`test ${libName}`);
expect(libTestResults.stderr).toContain('Test Suites: 1 passed, 1 total');

await testGeneratedApp(appName, { checkStyles: true });
await testGeneratedApp(appName, { checkStyles: true, checkLinter: true });
}, 120000);

it('should generate app with routing', async () => {
ensureProject();
const appName = uniq('app');

runCLI(
`generate @nrwl/react:app ${appName} --routing --no-interactive --babel`
`generate @nrwl/react:app ${appName} --routing --no-interactive --babel --linter=${linter}`
);

await testGeneratedApp(appName, { checkStyles: true });
await testGeneratedApp(appName, { checkStyles: true, checkLinter: true });
}, 120000);

it('should generate app with styled-components', async () => {
ensureProject();
const appName = uniq('app');

runCLI(
`generate @nrwl/react:app ${appName} --style styled-components --no-interactive --babel`
`generate @nrwl/react:app ${appName} --style styled-components --no-interactive --babel --linter=${linter}`
);

await testGeneratedApp(appName, { checkStyles: false });
await testGeneratedApp(appName, {
checkStyles: false,
checkLinter: true
});
}, 120000);

it('should be able to use JSX', async () => {
ensureProject();
const appName = uniq('app');
const libName = uniq('lib');

runCLI(`generate @nrwl/react:app ${appName} --no-interactive --babel`);
runCLI(
`generate @nrwl/react:app ${appName} --no-interactive --babel --linter=${linter}`
);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);

renameFile(
Expand Down Expand Up @@ -92,12 +101,20 @@ forEachCli(() => {
const mainPath = `apps/${appName}/src/main.jsx`;
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));

await testGeneratedApp(appName, { checkStyles: true });
await testGeneratedApp(appName, {
checkStyles: true,
checkLinter: false
});
}, 30000);

async function testGeneratedApp(appName, opts: { checkStyles: boolean }) {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
async function testGeneratedApp(
appName,
opts: { checkStyles: boolean; checkLinter: boolean }
) {
if (opts.checkLinter) {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
}

runCLI(`build ${appName}`);
let filesToCheck = [
Expand Down Expand Up @@ -133,8 +150,6 @@ forEachCli(() => {

const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
const lintE2eResults = runCLI(`lint ${appName}-e2e`);
expect(lintE2eResults).toContain('All files pass linting.');

if (supportUi()) {
const e2eResults = runCLI(`e2e ${appName}-e2e`);
Expand Down
4 changes: 2 additions & 2 deletions e2e/utils.ts
Expand Up @@ -11,7 +11,7 @@ export function uniq(prefix: string) {

export function forEachCli(
selectedCliOrFunction: string | Function,
callback?: Function
callback?: (currentCLIName) => void
) {
let clis;
if (process.env.SELECTED_CLI && selectedCliOrFunction && callback) {
Expand All @@ -32,7 +32,7 @@ export function forEachCli(
beforeEach(() => {
cli = c;
});
cb();
cb(c);
});
});
}
Expand Down
7 changes: 5 additions & 2 deletions e2e/web.test.ts
Expand Up @@ -9,13 +9,16 @@ import {
supportUi
} from './utils';

forEachCli(() => {
forEachCli(currentCLIName => {
describe('Web Components Applications', () => {
it('should be able to generate a web app', async () => {
ensureProject();
const appName = uniq('app');

runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
const linter = currentCLIName === 'angular' ? 'tslint' : 'eslint';
runCLI(
`generate @nrwl/web:app ${appName} --no-interactive --linter=${linter}`
);

const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
Expand Down
5 changes: 5 additions & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"@angular-devkit/build-webpack": "0.801.1",
"@angular-devkit/core": "8.1.1",
"@angular-devkit/schematics": "8.1.1",
"@angular-eslint/builder": "0.0.1-alpha.16",
"@angular/cli": "8.1.1",
"@angular/common": "^8.0.0",
"@angular/compiler": "^8.0.0",
Expand Down Expand Up @@ -66,6 +67,8 @@
"@types/react-dom": "^16.8.2",
"@types/webpack": "^4.4.24",
"@types/yargs": "^11.0.0",
"@typescript-eslint/eslint-plugin": "2.0.0-alpha.4",
"@typescript-eslint/parser": "2.0.0-alpha.4",
"angular": "1.6.6",
"app-root-path": "^2.0.1",
"babel-loader": "8.0.6",
Expand All @@ -81,6 +84,8 @@
"document-register-element": "^1.13.1",
"dotenv": "6.2.0",
"express": "4.16.3",
"eslint": "6.1.0",
"eslint-config-prettier": "6.0.0",
"fork-ts-checker-webpack-plugin": "^0.4.9",
"fs-extra": "7.0.1",
"graphviz": "^0.0.8",
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/schematics/application/schema.d.ts
Expand Up @@ -14,6 +14,7 @@ export interface Schema {
skipTests?: boolean;
directory?: string;
tags?: string;
linter: string;
unitTestRunner: UnitTestRunner;
e2eTestRunner: E2eTestRunner;
}
6 changes: 6 additions & 0 deletions packages/angular/src/schematics/application/schema.json
Expand Up @@ -110,6 +110,12 @@
"tags": {
"type": "string",
"description": "Add tags to the application (used for linting)"
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["tslint"],
"default": "tslint"
}
},
"required": []
Expand Down
1 change: 1 addition & 0 deletions packages/express/src/schematics/application/schema.d.ts
Expand Up @@ -6,5 +6,6 @@ export interface Schema {
directory?: string;
unitTestRunner: UnitTestRunner;
tags?: string;
linter: string;
frontendProject?: string;
}
6 changes: 6 additions & 0 deletions packages/express/src/schematics/application/schema.json
Expand Up @@ -28,6 +28,12 @@
"default": false,
"description": "Do not add dependencies to package.json."
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"unitTestRunner": {
"type": "string",
"enum": ["jest", "none"],
Expand Down
3 changes: 2 additions & 1 deletion packages/linter/package.json
Expand Up @@ -30,6 +30,7 @@
"@angular/compiler": "8.1.2",
"@angular/compiler-cli": "8.1.2",
"@angular-devkit/build-angular": "0.801.1",
"@angular-devkit/architect": "0.801.1"
"@angular-devkit/architect": "0.801.1",
"@angular-eslint/builder": "0.0.1-alpha.16"
}
}
30 changes: 23 additions & 7 deletions packages/linter/src/builders/lint/lint.impl.ts
@@ -1,25 +1,41 @@
import {
BuilderContext,
createBuilder,
BuilderOutput
BuilderOutput,
createBuilder
} from '@angular-devkit/architect';
import { Observable, from } from 'rxjs';
import { concatMap, tap } from 'rxjs/operators';
import { from, Observable } from 'rxjs';
import { concatMap } from 'rxjs/operators';

function run(options: any, context: BuilderContext): Observable<BuilderOutput> {
if (options.linter === 'tslint') {
delete options.linter;
options.tslintConfig = options.config;
delete options.config;
context.logger;
return from(
context.scheduleBuilder('@angular-devkit/build-angular:tslint', options, {
logger: patchedLogger(context)
})
).pipe(concatMap(r => r.output));
} else {
throw new Error(`ESLint support hasn't been implemented yet.`);
}

if (options.linter === 'eslint') {
delete options.linter;
options.eslintConfig = options.config;
delete options.config;
// Use whatever the default formatter is
delete options.format;
return from(
context.scheduleBuilder('@angular-eslint/builder:lint', options, {
logger: patchedLogger(context)
})
).pipe(concatMap(r => r.output));
}

throw new Error(
`"${
options.linter
}" is not a supported linter option: use either eslint or tslint`
);
}

// remove once https://github.com/angular/angular-cli/issues/15053 is fixed
Expand Down
1 change: 1 addition & 0 deletions packages/nest/src/schematics/application/schema.d.ts
Expand Up @@ -6,5 +6,6 @@ export interface Schema {
directory?: string;
unitTestRunner: UnitTestRunner;
tags?: string;
linter: string;
frontendProject?: string;
}

0 comments on commit 448233b

Please sign in to comment.