Skip to content

Commit

Permalink
feat: add issue.scope option (#442)
Browse files Browse the repository at this point in the history
The plugin uses TypeScript settings regarding files that are included in the compilation. It can be a different set of files comparing to the webpack compilation. To not display issues that are related to files outside webpack compilation, an `issue.scope` option has been added.

BREAKING CHANGE: 🧨 Issues outside webpack compilation will not be reported by default. See `issue.scope` option.
  • Loading branch information
piotr-oles committed Jun 12, 2020
1 parent b865f1d commit 188d44d
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
@@ -1,5 +1,5 @@
name: CI/CD
on: [push]
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -195,6 +195,7 @@ Options for the issues filtering (`issues` option object).
| --------- | --------------------------------- | ------------- | ----------- |
| `include` | `object` or `function` or `array` | `undefined` | If `object`, defines issue properties that should be [matched](./src/issue/IssueMatch.ts). If `function`, acts as a predicate where `issue` is an argument. |
| `exclude` | `object` or `function` or `array` | `undefined` | Same as `include` but issues that match this predicate will be excluded. |
| `scope` | `'all'` or `'webpack'` | `'webpack'` | Defines issues scope to be reported. If `'webpack'`, reports errors only related to the webpack compilation. Reports all errors otherwise (like `tsc` and `eslint` command). |

## Vue.js

Expand Down
5 changes: 5 additions & 0 deletions src/ForkTsCheckerWebpackPluginOptions.json
Expand Up @@ -246,6 +246,11 @@
},
"exclude": {
"$ref": "#/definitions/IssuePredicateOption"
},
"scope": {
"type": "string",
"enum": ["all", "webpack"],
"description": "Defines issues scope to be reported. If 'webpack', reports errors only related to a given webpack compilation. Reports all errors otherwise."
}
}
},
Expand Down
8 changes: 8 additions & 0 deletions src/hooks/tapAfterCompileToGetIssues.ts
@@ -1,4 +1,5 @@
import webpack from 'webpack';
import path from 'path';
import { ForkTsCheckerWebpackPluginConfiguration } from '../ForkTsCheckerWebpackPluginConfiguration';
import { ForkTsCheckerWebpackPluginState } from '../ForkTsCheckerWebpackPluginState';
import { getForkTsCheckerWebpackPluginHooks } from './pluginHooks';
Expand Down Expand Up @@ -32,6 +33,13 @@ function tapAfterCompileToGetIssues(
return;
}

if (configuration.issue.scope === 'webpack') {
// exclude issues that are related to files outside webpack compilation
issues = issues.filter(
(issue) => !issue.file || compilation.fileDependencies.has(path.normalize(issue.file))
);
}

// filter list of issues by provided issue predicate
issues = issues.filter(configuration.issue.predicate);

Expand Down
10 changes: 9 additions & 1 deletion src/hooks/tapDoneToAsyncGetIssues.ts
@@ -1,4 +1,6 @@
import webpack from 'webpack';
import chalk from 'chalk';
import path from 'path';
import { ForkTsCheckerWebpackPluginConfiguration } from '../ForkTsCheckerWebpackPluginConfiguration';
import { ForkTsCheckerWebpackPluginState } from '../ForkTsCheckerWebpackPluginState';
import { getForkTsCheckerWebpackPluginHooks } from './pluginHooks';
Expand All @@ -7,7 +9,6 @@ import { Issue } from '../issue';
import { IssueWebpackError } from '../issue/IssueWebpackError';
import isPending from '../utils/async/isPending';
import wait from '../utils/async/wait';
import chalk from 'chalk';

function tapDoneToAsyncGetIssues(
compiler: webpack.Compiler,
Expand Down Expand Up @@ -50,6 +51,13 @@ function tapDoneToAsyncGetIssues(
return;
}

if (configuration.issue.scope === 'webpack') {
// exclude issues that are related to files outside webpack compilation
issues = issues.filter(
(issue) => !issue.file || stats.compilation.fileDependencies.has(path.normalize(issue.file))
);
}

// filter list of issues by provided issue predicate
issues = issues.filter(configuration.issue.predicate);

Expand Down
2 changes: 2 additions & 0 deletions src/issue/IssueConfiguration.ts
Expand Up @@ -9,6 +9,7 @@ import { IssuePredicateOption, IssueOptions } from './IssueOptions';

interface IssueConfiguration {
predicate: IssuePredicate;
scope: 'all' | 'webpack';
}

function createIssuePredicateFromOption(
Expand Down Expand Up @@ -47,6 +48,7 @@ function createIssueConfiguration(

return {
predicate: (issue) => include(issue) && !exclude(issue),
scope: options.scope || 'webpack',
};
}

Expand Down
1 change: 1 addition & 0 deletions src/issue/IssueOptions.ts
Expand Up @@ -6,6 +6,7 @@ type IssuePredicateOption = IssuePredicate | IssueMatch | (IssuePredicate | Issu
interface IssueOptions {
include?: IssuePredicateOption;
exclude?: IssuePredicateOption;
scope?: 'all' | 'webpack';
}

export { IssueOptions, IssuePredicateOption };
Expand Up @@ -215,7 +215,7 @@ function createControlledTypeScriptSystem(
invokeFileChanged(path: string) {
const normalizedPath = realFileSystem.normalizePath(path);

if (deletedFiles.get(normalizedPath) || !fileWatchersMap.has(path)) {
if (deletedFiles.get(normalizedPath) || !fileWatchersMap.has(normalizedPath)) {
invokeFileWatchers(path, ts.FileWatcherEventKind.Created);
invokeDirectoryWatchers(normalizedPath);

Expand Down
108 changes: 108 additions & 0 deletions test/e2e/WebpackIssueScope.spec.ts
@@ -0,0 +1,108 @@
import { readFixture } from './sandbox/Fixture';
import { join } from 'path';
import { createSandbox, FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION, Sandbox } from './sandbox/Sandbox';
import {
createWebpackDevServerDriver,
WEBPACK_CLI_VERSION,
WEBPACK_DEV_SERVER_VERSION,
} from './sandbox/WebpackDevServerDriver';

describe('Webpack Issue Scope', () => {
let sandbox: Sandbox;

beforeAll(async () => {
sandbox = await createSandbox();
});

beforeEach(async () => {
await sandbox.reset();
});

afterAll(async () => {
await sandbox.cleanup();
});

it.each([
{ webpack: '4.0.0', async: true, scope: 'webpack' },
{ webpack: '4.0.0', async: false, scope: 'all' },
{ webpack: '^4.0.0', async: false, scope: 'webpack' },
{ webpack: '^4.0.0', async: true, scope: 'all' },
{ webpack: '^5.0.0-beta.16', async: true, scope: 'webpack' },
{ webpack: '^5.0.0-beta.16', async: false, scope: 'all' },
])(
'reports errors only related to the given scope with %p',
async ({ webpack, async, scope }) => {
await sandbox.load([
await readFixture(join(__dirname, 'fixtures/environment/typescript-basic.fixture'), {
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION: JSON.stringify(
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION
),
TS_LOADER_VERSION: JSON.stringify('^5.0.0'),
TYPESCRIPT_VERSION: JSON.stringify('~3.8.0'),
WEBPACK_VERSION: JSON.stringify(webpack),
WEBPACK_CLI_VERSION: JSON.stringify(WEBPACK_CLI_VERSION),
WEBPACK_DEV_SERVER_VERSION: JSON.stringify(WEBPACK_DEV_SERVER_VERSION),
ASYNC: JSON.stringify(async),
}),
await readFixture(join(__dirname, 'fixtures/implementation/typescript-basic.fixture')),
]);

// add importsNotUsedAsValues which is supported from TypeScript 3.8.0+
// this option is required for proper watching of type-only files in the `transpileOnly: true` mode
await sandbox.patch(
'./tsconfig.json',
' "outDir": "./dist"',
[' "outDir": "./dist",', ' "importsNotUsedAsValues": "preserve"'].join('\n')
);

// update configuration
await sandbox.write(
'fork-ts-checker.config.js',
`module.exports = { issue: { scope: ${JSON.stringify(scope)} } };`
);
await sandbox.write('src/notUsedFile.ts', 'const x: number = "1";');

const driver = createWebpackDevServerDriver(
sandbox.spawn('npm run webpack-dev-server'),
async
);

// first compilation should be successful only if we use "webpack" scope
if (scope === 'webpack') {
await driver.waitForNoErrors();

// add reference to the file to include it to the compilation
await sandbox.patch(
'src/model/User.ts',
"import { Role } from './Role';",
"import { Role } from './Role';\nimport '../notUsedFile';"
);

const errors = await driver.waitForErrors();
expect(errors).toEqual([
[
'ERROR in src/notUsedFile.ts 1:7-8',
"TS2322: Type '\"1\"' is not assignable to type 'number'.",
' > 1 | const x: number = "1";',
' | ^',
].join('\n'),
]);

// remove reference to the file to exclude it from the compilation
await sandbox.patch('src/model/User.ts', "import '../notUsedFile';", '');

await driver.waitForNoErrors();
} else {
const errors = await driver.waitForErrors();
expect(errors).toEqual([
[
'ERROR in src/notUsedFile.ts 1:7-8',
"TS2322: Type '\"1\"' is not assignable to type 'number'.",
' > 1 | const x: number = "1";',
' | ^',
].join('\n'),
]);
}
}
);
});

0 comments on commit 188d44d

Please sign in to comment.