feat(angular): add an option to add strict type checking (#3465)
* feat(angular): add an option to add strict type checking Adding support for strict type checking to Angular application and library generate schematics. E.g. `nx generate application myapp --strict` `nx generate lib mylib --strict` Closes #3383. Performs the following configuration changes: - Enables strict mode in TypeScript, as well as other strictness flags recommended by the TypeScript team. Specifically, forceConsistentCasingInFileNames, noImplicitReturns, noFallthroughCasesInSwitch. - Turns on strict Angular compiler flags strictTemplates and strictInjectionParameters These match the flags used in the standard CLI strict mode. * cleanup(misc): updating import path Co-authored-by: Ashley Hunter <ashley.hunter@hotmail.co.uk>
This commit is contained in:
parent
86b4f4e7b8
commit
10911e25c2
@ -136,6 +136,14 @@ Type: `boolean`
|
|||||||
|
|
||||||
Skip creating spec files.
|
Skip creating spec files.
|
||||||
|
|
||||||
|
### strict
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Creates an application with stricter type checking and build optimization options.
|
||||||
|
|
||||||
### style
|
### style
|
||||||
|
|
||||||
Default: `css`
|
Default: `css`
|
||||||
|
|||||||
@ -142,6 +142,14 @@ Type: `boolean`
|
|||||||
|
|
||||||
Do not update tsconfig.json for development experience.
|
Do not update tsconfig.json for development experience.
|
||||||
|
|
||||||
|
### strict
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Creates a library with stricter type checking and build optimization options.
|
||||||
|
|
||||||
### style
|
### style
|
||||||
|
|
||||||
Default: `css`
|
Default: `css`
|
||||||
|
|||||||
@ -136,6 +136,14 @@ Type: `boolean`
|
|||||||
|
|
||||||
Skip creating spec files.
|
Skip creating spec files.
|
||||||
|
|
||||||
|
### strict
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Creates an application with stricter type checking and build optimization options.
|
||||||
|
|
||||||
### style
|
### style
|
||||||
|
|
||||||
Default: `css`
|
Default: `css`
|
||||||
|
|||||||
@ -142,6 +142,14 @@ Type: `boolean`
|
|||||||
|
|
||||||
Do not update tsconfig.json for development experience.
|
Do not update tsconfig.json for development experience.
|
||||||
|
|
||||||
|
### strict
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Creates a library with stricter type checking and build optimization options.
|
||||||
|
|
||||||
### style
|
### style
|
||||||
|
|
||||||
Default: `css`
|
Default: `css`
|
||||||
|
|||||||
@ -556,4 +556,43 @@ describe('app', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('--strict', () => {
|
||||||
|
it('should enable strict type checking', async () => {
|
||||||
|
const tree = await runSchematic(
|
||||||
|
'app',
|
||||||
|
{ name: 'my-app', strict: true },
|
||||||
|
appTree
|
||||||
|
);
|
||||||
|
|
||||||
|
// define all the tsconfig files to update
|
||||||
|
const configFiles = [
|
||||||
|
'apps/my-app/tsconfig.json',
|
||||||
|
'apps/my-app-e2e/tsconfig.e2e.json',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const configFile of configFiles) {
|
||||||
|
const { compilerOptions, angularCompilerOptions } = JSON.parse(
|
||||||
|
tree.readContent(configFile)
|
||||||
|
);
|
||||||
|
|
||||||
|
// check that the TypeScript compiler options have been updated
|
||||||
|
expect(compilerOptions.forceConsistentCasingInFileNames).toBe(true);
|
||||||
|
expect(compilerOptions.strict).toBe(true);
|
||||||
|
expect(compilerOptions.noImplicitReturns).toBe(true);
|
||||||
|
expect(compilerOptions.noFallthroughCasesInSwitch).toBe(true);
|
||||||
|
|
||||||
|
// check that the Angular Template options have been updated
|
||||||
|
expect(angularCompilerOptions.strictInjectionParameters).toBe(true);
|
||||||
|
expect(angularCompilerOptions.strictTemplates).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if the workspace configuration has been updated to use strict
|
||||||
|
// mode by default in future applications
|
||||||
|
const workspaceJson = readJsonInTree(tree, 'workspace.json');
|
||||||
|
expect(workspaceJson.schematics['@nrwl/angular:application'].strict).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -680,6 +680,66 @@ function addProxyConfig(options: NormalizedSchema): Rule {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableStrictTypeChecking(schema: Schema): Rule {
|
||||||
|
return (host) => {
|
||||||
|
const options = normalizeOptions(host, schema);
|
||||||
|
|
||||||
|
// define all the tsconfig files to update
|
||||||
|
const configFiles = [
|
||||||
|
`${options.appProjectRoot}/tsconfig.json`,
|
||||||
|
`${options.e2eProjectRoot}/tsconfig.e2e.json`,
|
||||||
|
];
|
||||||
|
|
||||||
|
const rules: Rule[] = [];
|
||||||
|
|
||||||
|
// iterate each config file, if it exists then update it
|
||||||
|
for (const configFile of configFiles) {
|
||||||
|
if (!host.exists(configFile)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the settings in the tsconfig.app.json to enable strict type checking.
|
||||||
|
// This matches the settings defined by the Angular CLI https://angular.io/guide/strict-mode
|
||||||
|
const rule = updateJsonInTree(configFile, (json) => {
|
||||||
|
// update the TypeScript settings
|
||||||
|
json.compilerOptions = {
|
||||||
|
...(json.compilerOptions ?? {}),
|
||||||
|
forceConsistentCasingInFileNames: true,
|
||||||
|
strict: true,
|
||||||
|
noImplicitReturns: true,
|
||||||
|
noFallthroughCasesInSwitch: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// update Angular Template Settings
|
||||||
|
json.angularCompilerOptions = {
|
||||||
|
...(json.angularCompilerOptions ?? {}),
|
||||||
|
strictInjectionParameters: true,
|
||||||
|
strictTemplates: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
rules.push(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the default so future applications will default to strict mode
|
||||||
|
// unless the user has previously set this to false by default
|
||||||
|
const updateAngularWorkspace = updateWorkspace((workspace) => {
|
||||||
|
workspace.extensions.schematics = workspace.extensions.schematics || {};
|
||||||
|
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:application'] =
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:application'] || {};
|
||||||
|
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:application'].strict =
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:application'].strict ??
|
||||||
|
options.strict;
|
||||||
|
});
|
||||||
|
|
||||||
|
return chain([...rules, updateAngularWorkspace]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default function (schema: Schema): Rule {
|
export default function (schema: Schema): Rule {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
const options = normalizeOptions(host, schema);
|
const options = normalizeOptions(host, schema);
|
||||||
@ -762,6 +822,7 @@ export default function (schema: Schema): Rule {
|
|||||||
})
|
})
|
||||||
: noop(),
|
: noop(),
|
||||||
options.backendProject ? addProxyConfig(options) : noop(),
|
options.backendProject ? addProxyConfig(options) : noop(),
|
||||||
|
options.strict ? enableStrictTypeChecking(options) : noop(),
|
||||||
formatFiles(options),
|
formatFiles(options),
|
||||||
])(host, context);
|
])(host, context);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,4 +19,5 @@ export interface Schema {
|
|||||||
unitTestRunner: UnitTestRunner;
|
unitTestRunner: UnitTestRunner;
|
||||||
e2eTestRunner: E2eTestRunner;
|
e2eTestRunner: E2eTestRunner;
|
||||||
backendProject?: string;
|
backendProject?: string;
|
||||||
|
strict?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,6 +120,11 @@
|
|||||||
"backendProject": {
|
"backendProject": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Backend project that provides data to this application. This sets up proxy.config.json."
|
"description": "Backend project that provides data to this application. This sets up proxy.config.json."
|
||||||
|
},
|
||||||
|
"strict": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates an application with stricter type checking and build optimization options.",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { NormalizedSchema } from './normalized-schema';
|
||||||
|
import { chain, Rule } from '@angular-devkit/schematics';
|
||||||
|
import { updateJsonInTree, updateWorkspace } from '@nrwl/workspace';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable Strict Mode in the library and spec TS Config
|
||||||
|
* */
|
||||||
|
export function enableStrictTypeChecking(options: NormalizedSchema): Rule {
|
||||||
|
return () => chain([updateTsConfig(options), updateAngularWorkspace()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTsConfig(options: NormalizedSchema): Rule {
|
||||||
|
return () => {
|
||||||
|
// Update the settings in the tsconfig.app.json to enable strict type checking.
|
||||||
|
// This matches the settings defined by the Angular CLI https://angular.io/guide/strict-mode
|
||||||
|
return updateJsonInTree(`${options.projectRoot}/tsconfig.json`, (json) => {
|
||||||
|
// update the TypeScript settings
|
||||||
|
json.compilerOptions = {
|
||||||
|
...(json.compilerOptions ?? {}),
|
||||||
|
forceConsistentCasingInFileNames: true,
|
||||||
|
strict: true,
|
||||||
|
noImplicitReturns: true,
|
||||||
|
noFallthroughCasesInSwitch: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// update Angular Template Settings
|
||||||
|
json.angularCompilerOptions = {
|
||||||
|
...(json.angularCompilerOptions ?? {}),
|
||||||
|
strictInjectionParameters: true,
|
||||||
|
strictTemplates: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAngularWorkspace(): Rule {
|
||||||
|
// set the default so future libraries will default to strict mode
|
||||||
|
// unless the user has previously set this to false by default
|
||||||
|
return updateWorkspace((workspace) => {
|
||||||
|
workspace.extensions.schematics = workspace.extensions.schematics || {};
|
||||||
|
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:library'] =
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:library'] || {};
|
||||||
|
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:library'].strict =
|
||||||
|
workspace.extensions.schematics['@nrwl/angular:library'].strict ?? true;
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1149,4 +1149,41 @@ describe('lib', () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('--strict', () => {
|
||||||
|
it('should enable strict type checking', async () => {
|
||||||
|
const tree = await runSchematic(
|
||||||
|
'lib',
|
||||||
|
{
|
||||||
|
name: 'myLib',
|
||||||
|
framework: 'angular',
|
||||||
|
publishable: true,
|
||||||
|
importPath: '@myorg/lib',
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
appTree
|
||||||
|
);
|
||||||
|
|
||||||
|
const { compilerOptions, angularCompilerOptions } = JSON.parse(
|
||||||
|
tree.readContent('libs/my-lib/tsconfig.json')
|
||||||
|
);
|
||||||
|
|
||||||
|
// check that the TypeScript compiler options have been updated
|
||||||
|
expect(compilerOptions.forceConsistentCasingInFileNames).toBe(true);
|
||||||
|
expect(compilerOptions.strict).toBe(true);
|
||||||
|
expect(compilerOptions.noImplicitReturns).toBe(true);
|
||||||
|
expect(compilerOptions.noFallthroughCasesInSwitch).toBe(true);
|
||||||
|
|
||||||
|
// check that the Angular Template options have been updated
|
||||||
|
expect(angularCompilerOptions.strictInjectionParameters).toBe(true);
|
||||||
|
expect(angularCompilerOptions.strictTemplates).toBe(true);
|
||||||
|
|
||||||
|
// check to see if the workspace configuration has been updated to use strict
|
||||||
|
// mode by default in future applications
|
||||||
|
const workspaceJson = readJsonInTree(tree, 'workspace.json');
|
||||||
|
expect(workspaceJson.schematics['@nrwl/angular:library'].strict).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { updateLibPackageNpmScope } from './lib/update-lib-package-npm-scope';
|
|||||||
import { updateProject } from './lib/update-project';
|
import { updateProject } from './lib/update-project';
|
||||||
import { updateTsConfig } from './lib/update-tsconfig';
|
import { updateTsConfig } from './lib/update-tsconfig';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
import { enableStrictTypeChecking } from './lib/enable-strict-type-checking';
|
||||||
|
|
||||||
export default function (schema: Schema): Rule {
|
export default function (schema: Schema): Rule {
|
||||||
return (host: Tree): Rule => {
|
return (host: Tree): Rule => {
|
||||||
@ -79,6 +80,7 @@ export default function (schema: Schema): Rule {
|
|||||||
? updateLibPackageNpmScope(options)
|
? updateLibPackageNpmScope(options)
|
||||||
: noop(),
|
: noop(),
|
||||||
addModule(options),
|
addModule(options),
|
||||||
|
options.strict ? enableStrictTypeChecking(options) : noop(),
|
||||||
formatFiles(options),
|
formatFiles(options),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export interface Schema {
|
|||||||
lazy?: boolean;
|
lazy?: boolean;
|
||||||
parentModule?: string;
|
parentModule?: string;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
|
strict?: boolean;
|
||||||
|
|
||||||
linter: Linter;
|
linter: Linter;
|
||||||
unitTestRunner: UnitTestRunner;
|
unitTestRunner: UnitTestRunner;
|
||||||
|
|||||||
@ -114,6 +114,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The library name used to import it, like @myorg/my-awesome-lib. Must be a valid npm name."
|
"description": "The library name used to import it, like @myorg/my-awesome-lib. Must be a valid npm name."
|
||||||
},
|
},
|
||||||
|
"strict": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Creates a library with stricter type checking and build optimization options.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user