feat(nest): Add strict option (#16371)

This commit is contained in:
Nicholas Cunningham 2023-04-18 12:47:02 -06:00 committed by GitHub
parent a4ef9596d9
commit ce4a76a975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 72 additions and 35 deletions

View File

@ -68,6 +68,11 @@
"type": "boolean", "type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.", "description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false "default": false
},
"strict": {
"type": "boolean",
"description": "Adds strictNullChecks, noImplicitAny, strictBindCallApply, forceConsistentCasingInFileNames and noFallthroughCasesInSwitch to tsconfig.",
"default": false
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@ -110,7 +110,7 @@
"strict": { "strict": {
"description": "Whether to enable tsconfig strict mode or not.", "description": "Whether to enable tsconfig strict mode or not.",
"type": "boolean", "type": "boolean",
"default": false "default": true
}, },
"standaloneConfig": { "standaloneConfig": {
"description": "Split the project configuration into <projectRoot>/project.json rather than including it inside workspace.json", "description": "Split the project configuration into <projectRoot>/project.json rather than including it inside workspace.json",

View File

@ -511,8 +511,8 @@ describe('nest libraries', function () {
`libs/${nestlib}/src/lib/foo.model.ts`, `libs/${nestlib}/src/lib/foo.model.ts`,
` `
export class FooModel { export class FooModel {
foo: string; foo?: string;
bar: number; bar?: number;
}` }`
); );
@ -527,7 +527,7 @@ exports.FooModel = void 0;
const openapi = require("@nestjs/swagger"); const openapi = require("@nestjs/swagger");
class FooModel { class FooModel {
static _OPENAPI_METADATA_FACTORY() { static _OPENAPI_METADATA_FACTORY() {
return { foo: { required: true, type: () => String }, bar: { required: true, type: () => Number } }; return { foo: { required: false, type: () => String }, bar: { required: false, type: () => Number } };
} }
} }
exports.FooModel = FooModel; exports.FooModel = FooModel;

View File

@ -59,6 +59,22 @@ describe('application generator', () => {
]); ]);
}); });
it('should add strict checks with --strict', async () => {
await applicationGenerator(tree, { name: appName, strict: true });
const tsConfig = devkit.readJson(
tree,
`apps/${appDirectory}/tsconfig.app.json`
);
expect(tsConfig.compilerOptions.strictNullChecks).toBeTruthy();
expect(tsConfig.compilerOptions.noImplicitAny).toBeTruthy();
expect(tsConfig.compilerOptions.strictBindCallApply).toBeTruthy();
expect(
tsConfig.compilerOptions.forceConsistentCasingInFileNames
).toBeTruthy();
expect(tsConfig.compilerOptions.noFallthroughCasesInSwitch).toBeTruthy();
});
describe('--skipFormat', () => { describe('--skipFormat', () => {
it('should format files', async () => { it('should format files', async () => {
jest.spyOn(devkit, 'formatFiles'); jest.spyOn(devkit, 'formatFiles');

View File

@ -25,6 +25,7 @@ export function normalizeOptions(
return { return {
...options, ...options,
strict: options.strict ?? false,
appProjectRoot, appProjectRoot,
linter: options.linter ?? Linter.EsLint, linter: options.linter ?? Linter.EsLint,
unitTestRunner: options.unitTestRunner ?? 'jest', unitTestRunner: options.unitTestRunner ?? 'jest',

View File

@ -9,6 +9,16 @@ export function updateTsConfig(tree: Tree, options: NormalizedOptions): void {
(json) => { (json) => {
json.compilerOptions.emitDecoratorMetadata = true; json.compilerOptions.emitDecoratorMetadata = true;
json.compilerOptions.target = 'es2015'; json.compilerOptions.target = 'es2015';
if (options.strict) {
json.compilerOptions = {
...json.compilerOptions,
strictNullChecks: true,
noImplicitAny: true,
strictBindCallApply: true,
forceConsistentCasingInFileNames: true,
noFallthroughCasesInSwitch: true,
};
}
return json; return json;
} }
); );

View File

@ -13,6 +13,7 @@ export interface ApplicationGeneratorOptions {
e2eTestRunner?: 'jest' | 'none'; e2eTestRunner?: 'jest' | 'none';
setParserOptionsProject?: boolean; setParserOptionsProject?: boolean;
rootProject?: boolean; rootProject?: boolean;
strict?: boolean;
} }
interface NormalizedOptions extends ApplicationGeneratorOptions { interface NormalizedOptions extends ApplicationGeneratorOptions {

View File

@ -68,6 +68,11 @@
"type": "boolean", "type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.", "description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false "default": false
},
"strict": {
"type": "boolean",
"description": "Adds strictNullChecks, noImplicitAny, strictBindCallApply, forceConsistentCasingInFileNames and noFallthroughCasesInSwitch to tsconfig.",
"default": false
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@ -32,7 +32,13 @@ export default {
exports[`lib --unit-test-runner none should not generate test configuration 1`] = ` exports[`lib --unit-test-runner none should not generate test configuration 1`] = `
{ {
"compilerOptions": { "compilerOptions": {
"forceConsistentCasingInFileNames": true,
"module": "commonjs", "module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"strict": true,
}, },
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"files": [], "files": [],
@ -62,7 +68,13 @@ exports[`lib --unit-test-runner none should not generate test configuration 2`]
exports[`lib nested should create a local tsconfig.json 1`] = ` exports[`lib nested should create a local tsconfig.json 1`] = `
{ {
"compilerOptions": { "compilerOptions": {
"forceConsistentCasingInFileNames": true,
"module": "commonjs", "module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"strict": true,
}, },
"extends": "../../../tsconfig.base.json", "extends": "../../../tsconfig.base.json",
"files": [], "files": [],
@ -94,7 +106,13 @@ export class MyLibModule {}
exports[`lib not nested should create a local tsconfig.json 1`] = ` exports[`lib not nested should create a local tsconfig.json 1`] = `
{ {
"compilerOptions": { "compilerOptions": {
"forceConsistentCasingInFileNames": true,
"module": "commonjs", "module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"strict": true,
}, },
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"files": [], "files": [],

View File

@ -28,6 +28,7 @@ export function normalizeOptions(
const normalized: NormalizedOptions = { const normalized: NormalizedOptions = {
...options, ...options,
strict: options.strict ?? true,
controller: options.controller ?? false, controller: options.controller ?? false,
fileName, fileName,
global: options.global ?? false, global: options.global ?? false,

View File

@ -10,9 +10,10 @@ export function updateTsConfig(tree: Tree, options: NormalizedOptions): void {
if (options.strict) { if (options.strict) {
json.compilerOptions = { json.compilerOptions = {
...json.compilerOptions, ...json.compilerOptions,
strictNullChecks: true,
noImplicitAny: true,
strictBindCallApply: true,
forceConsistentCasingInFileNames: true, forceConsistentCasingInFileNames: true,
strict: true,
noImplicitReturns: true,
noFallthroughCasesInSwitch: true, noFallthroughCasesInSwitch: true,
}; };
} }

View File

@ -247,35 +247,14 @@ describe('lib', () => {
it('should update the projects tsconfig with strict true', async () => { it('should update the projects tsconfig with strict true', async () => {
await libraryGenerator(tree, { name: libName, strict: true }); await libraryGenerator(tree, { name: libName, strict: true });
const tsconfigJson = readJson( const tsConfig = readJson(tree, `/libs/${libFileName}/tsconfig.lib.json`);
tree, expect(tsConfig.compilerOptions.strictNullChecks).toBeTruthy();
`/libs/${libFileName}/tsconfig.lib.json` expect(tsConfig.compilerOptions.noImplicitAny).toBeTruthy();
); expect(tsConfig.compilerOptions.strictBindCallApply).toBeTruthy();
expect(tsconfigJson.compilerOptions.strict).toBe(true);
expect( expect(
tsconfigJson.compilerOptions.forceConsistentCasingInFileNames tsConfig.compilerOptions.forceConsistentCasingInFileNames
).toBe(true); ).toBeTruthy();
expect(tsconfigJson.compilerOptions.noImplicitReturns).toBe(true); expect(tsConfig.compilerOptions.noFallthroughCasesInSwitch).toBeTruthy();
expect(tsconfigJson.compilerOptions.noFallthroughCasesInSwitch).toBe(
true
);
});
it('should default to strict false', async () => {
await libraryGenerator(tree, { name: libName });
const tsconfigJson = readJson(
tree,
`/libs/${libFileName}/tsconfig.lib.json`
);
expect(tsconfigJson.compilerOptions.strict).not.toBeDefined();
expect(
tsconfigJson.compilerOptions.forceConsistentCasingInFileNames
).not.toBeDefined();
expect(tsconfigJson.compilerOptions.noImplicitReturns).not.toBeDefined();
expect(
tsconfigJson.compilerOptions.noFallthroughCasesInSwitch
).not.toBeDefined();
}); });
}); });

View File

@ -110,7 +110,7 @@
"strict": { "strict": {
"description": "Whether to enable tsconfig strict mode or not.", "description": "Whether to enable tsconfig strict mode or not.",
"type": "boolean", "type": "boolean",
"default": false "default": true
}, },
"standaloneConfig": { "standaloneConfig": {
"description": "Split the project configuration into <projectRoot>/project.json rather than including it inside workspace.json", "description": "Split the project configuration into <projectRoot>/project.json rather than including it inside workspace.json",