Skip to content

Commit

Permalink
feat: enhance the issue formatter to get a customized function (#578)
Browse files Browse the repository at this point in the history
Adds option to pass a custom formatter function
  • Loading branch information
vanpipy authored Mar 18, 2021
1 parent fe34ce1 commit 3a2981d
Show file tree
Hide file tree
Showing 10 changed files with 16,651 additions and 18 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ you can place your configuration in the:

Options passed to the plugin constructor will overwrite options from the cosmiconfig (using [deepmerge](https://github.com/TehShrike/deepmerge)).

| Name | Type | Default value | Description |
| ----------------- | --------------------- | ------------------------------------------------------------------ | ----------- |
| `async` | `boolean` | `compiler.options.mode === 'development'` | If `true`, reports issues **after** webpack's compilation is done. Thanks to that it doesn't block the compilation. Used only in the `watch` mode. |
| `typescript` | `object` or `boolean` | `true` | If a `boolean`, it enables/disables TypeScript checker. If an `object`, see [TypeScript options](#typescript-options). |
| `eslint` | `object` | `undefined` | If `undefined`, it disables ESLint linter. If an `object`, see [ESLint options](#eslint-options). |
| `issue` | `object` | `{}` | See [Issues options](#issues-options). |
| `formatter` | `string` or `object` | `codeframe` | Available formatters are `basic` and `codeframe`. To [configure](https://babeljs.io/docs/en/babel-code-frame#options) `codeframe` formatter, pass object: `{ type: 'codeframe', options: { <coderame options> } }`. |
| Name | Type | Default value | Description |
| ----------------- | ---------------------------------- | ------------------------------------------------------------------ | ----------- |
| `async` | `boolean` | `compiler.options.mode === 'development'` | If `true`, reports issues **after** webpack's compilation is done. Thanks to that it doesn't block the compilation. Used only in the `watch` mode. |
| `typescript` | `object` or `boolean` | `true` | If a `boolean`, it enables/disables TypeScript checker. If an `object`, see [TypeScript options](#typescript-options). |
| `eslint` | `object` | `undefined` | If `undefined`, it disables ESLint linter. If an `object`, see [ESLint options](#eslint-options). |
| `issue` | `object` | `{}` | See [Issues options](#issues-options). |
| `formatter` | `string` or `object` or `function` | `codeframe` | Available formatters are `basic`, `codeframe` and a custom `function`. To [configure](https://babeljs.io/docs/en/babel-code-frame#options) `codeframe` formatter, pass object: `{ type: 'codeframe', options: { <coderame options> } }`. |
| `logger` | `object` | `{ infrastructure: 'silent', issues: 'console', devServer: true }` | Available loggers are `silent`, `console`, and `webpack-infrastructure`. Infrastructure logger prints additional information, issue logger prints `issues` in the `async` mode. If `devServer` is set to `false`, errors will not be reported to Webpack Dev Server. |

### TypeScript options
Expand Down
2 changes: 1 addition & 1 deletion src/formatter/FormatterConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type FormatterConfiguration = Formatter;

function createFormatterConfiguration(options: FormatterOptions | undefined) {
return createFormatter(
options ? (typeof options === 'string' ? options : options.type || 'codeframe') : 'codeframe',
options ? (typeof options === 'object' ? options.type || 'codeframe' : options) : 'codeframe',
options && typeof options === 'object' ? options.options || {} : {}
);
}
Expand Down
22 changes: 13 additions & 9 deletions src/formatter/FormatterFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Formatter } from './Formatter';
import { BabelCodeFrameOptions, createCodeFrameFormatter } from './CodeFrameFormatter';
import { createBasicFormatter } from './BasicFormatter';

type NotConfigurableFormatterType = undefined | 'basic';
type NotConfigurableFormatterType = undefined | 'basic' | Formatter;
type ConfigurableFormatterType = 'codeframe';
type FormatterType = NotConfigurableFormatterType | ConfigurableFormatterType;

Expand All @@ -25,17 +25,21 @@ function createFormatter<T extends ConfigurableFormatterType>(
function createFormatter<T extends FormatterType>(type: T, options?: object): Formatter;
// declare function implementation
function createFormatter(type?: FormatterType, options?: object): Formatter {
switch (type) {
case 'basic':
case undefined:
return createBasicFormatter();
if (typeof type === 'function') {
return type;
}

case 'codeframe':
return createCodeFrameFormatter(options);
if (typeof type === 'undefined' || type === 'basic') {
return createBasicFormatter();
}

default:
throw new Error(`Unknown "${type}" formatter. Available types are: basic, codeframe.`);
if (type === 'codeframe') {
return createCodeFrameFormatter(options);
}

throw new Error(
`Unknown "${type}" formatter. Available types are: "basic", "codeframe" or a custom function.`
);
}

export {
Expand Down
143 changes: 143 additions & 0 deletions test/e2e/TypeScriptContextAndFormatterOption.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { readFixture } from './sandbox/Fixture';
import { join } from 'path';
import os from 'os';
import { createSandbox, Sandbox } from './sandbox/Sandbox';
import {
createWebpackDevServerDriver,
WEBPACK_CLI_VERSION,
WEBPACK_DEV_SERVER_VERSION,
} from './sandbox/WebpackDevServerDriver';
import { FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION } from './sandbox/Plugin';

describe('TypeScript Context Option', () => {
let sandbox: Sandbox;

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

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

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

it.each([
{ async: true, typescript: '2.7.1' },
{ async: false, typescript: '~3.0.0' },
{ async: true, typescript: '~3.6.0' },
{ async: false, typescript: '~3.8.0' },
])(
'uses the custom formatter to format the error message for %p',
async ({ async, typescript }) => {
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('^7.0.0'),
TYPESCRIPT_VERSION: JSON.stringify(typescript),
WEBPACK_VERSION: JSON.stringify('^4.0.0'),
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')),
]);

// update sandbox to use context option
await sandbox.remove('tsconfig.json');
await sandbox.write(
'build/tsconfig.json',
JSON.stringify({
compilerOptions: {
target: 'es5',
module: 'commonjs',
lib: ['ES6', 'DOM'],
moduleResolution: 'node',
esModuleInterop: true,
skipLibCheck: true,
skipDefaultLibCheck: true,
strict: true,
baseUrl: './src',
outDir: './dist',
},
include: ['./src'],
exclude: ['node_modules'],
})
);
await sandbox.patch(
'webpack.config.js',
"entry: './src/index.ts',",
["entry: './src/index.ts',", 'context: path.resolve(__dirname),'].join('\n')
);
await sandbox.patch(
'webpack.config.js',
' logger: {',
[
' typescript: {',
' enabled: true,',
' configFile: path.resolve(__dirname, "build/tsconfig.json"),',
' context: __dirname,',
' },',
' logger: {',
].join('\n')
);
await sandbox.patch(
'webpack.config.js',
' logger: {',
[
' formatter: (issue) => {',
' return `It is the custom issue statement - ${issue.code}: ${issue.message}`',
' },',
' logger: {',
].join('\n')
);
await sandbox.patch(
'webpack.config.js',
' transpileOnly: true',
[
' transpileOnly: true,',
' configFile: path.resolve(__dirname, "build/tsconfig.json"),',
' context: __dirname,',
].join('\n')
);
// create additional directory for cwd test
await sandbox.write('foo/.gitignore', '');

const driver = createWebpackDevServerDriver(
sandbox.spawn(
`../node_modules/.bin/webpack-dev-server${os.platform() === 'win32' ? '.cmd' : ''}`,
{},
join(sandbox.context, 'foo')
),
async
);

// first compilation is successful
await driver.waitForNoErrors();

// then we introduce semantic error by removing "firstName" and "lastName" from the User model
await sandbox.patch(
'src/model/User.ts',
[' firstName?: string;', ' lastName?: string;'].join('\n'),
''
);

// we should receive 2 semantic errors
const errors = await driver.waitForErrors();
expect(errors).toEqual([
[
'ERROR in ../src/model/User.ts:11:16',
"It is the custom issue statement - TS2339: Property 'firstName' does not exist on type 'User'.",
].join('\n'),
[
'ERROR in ../src/model/User.ts:11:32',
"It is the custom issue statement - TS2339: Property 'lastName' does not exist on type 'User'.",
].join('\n'),
]);
}
);
});
Loading

0 comments on commit 3a2981d

Please sign in to comment.