From d28a0bdab49f323cb37cde2a51e4fec8ba3e6d50 Mon Sep 17 00:00:00 2001 From: Thew Dhanat <26767607+ThewBear@users.noreply.github.com> Date: Mon, 2 Mar 2020 13:48:15 +0700 Subject: [PATCH] feat(gatsby-plugin-typescript): Supports linting (#18721) Co-authored-by: GatsbyJS Bot Co-authored-by: Ward Peeters --- packages/gatsby-plugin-typescript/README.md | 6 + .../gatsby-plugin-typescript/package.json | 8 +- .../src/__tests__/gatsby-node.js | 122 +++++++++++++++++- .../src/gatsby-node.js | 40 +++++- 4 files changed, 172 insertions(+), 4 deletions(-) diff --git a/packages/gatsby-plugin-typescript/README.md b/packages/gatsby-plugin-typescript/README.md index 622c21848e44a..60c05d7dcd1fc 100644 --- a/packages/gatsby-plugin-typescript/README.md +++ b/packages/gatsby-plugin-typescript/README.md @@ -77,3 +77,9 @@ Visual Studio Code is very good in this regard. In addition, you can see the instructions in [TypeScript-Babel-Starter](https://github.com/Microsoft/TypeScript-Babel-Starter) for setting up a `type-check` task. + +## ESLint + +This plugin supports linting TSX with [typescript-eslint](https://typescript-eslint.io) using [Gatsby's default ESLint config](https://www.gatsbyjs.org/docs/eslint/). To enable linting TSX, install `typescript`. + +`npm install typescript` diff --git a/packages/gatsby-plugin-typescript/package.json b/packages/gatsby-plugin-typescript/package.json index ff37ef20bf0ef..e54a951a94454 100644 --- a/packages/gatsby-plugin-typescript/package.json +++ b/packages/gatsby-plugin-typescript/package.json @@ -33,7 +33,13 @@ "license": "MIT", "main": "index.js", "peerDependencies": { - "gatsby": "^2.0.0" + "gatsby": "^2.0.0", + "typescript": "^3.2.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-typescript/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-typescript/src/__tests__/gatsby-node.js index 46426487607c3..af2fa62a1dee5 100644 --- a/packages/gatsby-plugin-typescript/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-typescript/src/__tests__/gatsby-node.js @@ -49,7 +49,22 @@ describe(`gatsby-plugin-typescript`, () => { const actions = { setWebpackConfig: jest.fn() } const jsLoader = {} const loaders = { js: jest.fn(() => jsLoader) } - onCreateWebpackConfig({ actions, loaders }) + const stage = `develop` + const eslintLoader = { loader: `eslint-loader` } + const webpackConfig = { + module: { + rules: [ + { + enforce: `pre`, + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + use: [eslintLoader], + }, + ], + }, + } + const getConfig = jest.fn(() => webpackConfig) + onCreateWebpackConfig({ actions, getConfig, loaders, stage }) expect(actions.setWebpackConfig).toHaveBeenCalledWith({ module: { rules: [ @@ -60,13 +75,116 @@ describe(`gatsby-plugin-typescript`, () => { ], }, }) + expect(actions.setWebpackConfig).toHaveBeenCalledWith({ + module: { + rules: [ + { + enforce: `pre`, + test: /\.tsx?$/, + exclude: /(node_modules|bower_components)/, + use: [eslintLoader], + }, + ], + }, + }) }) it(`does not set the webpack config if there isn't a js loader`, () => { const actions = { setWebpackConfig: jest.fn() } const loaders = { js: jest.fn() } - onCreateWebpackConfig({ actions, loaders }) + const stage = `develop` + const getConfig = jest.fn() + onCreateWebpackConfig({ actions, getConfig, loaders, stage }) expect(actions.setWebpackConfig).not.toHaveBeenCalled() }) + + it(`does not set the typescript-eslint webpack config if the built-in eslint-loader isn't set`, () => { + const actions = { setWebpackConfig: jest.fn() } + const jsLoader = {} + const loaders = { + js: jest.fn(() => jsLoader), + } + const stage = `develop` + const webpackConfig = { + module: { + rules: [ + { + enforce: `pre`, + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + use: [], + }, + ], + }, + } + const getConfig = jest.fn(() => webpackConfig) + onCreateWebpackConfig({ actions, getConfig, loaders, stage }) + expect(actions.setWebpackConfig).toHaveBeenCalledWith({ + module: { + rules: [ + { + test: /\.tsx?$/, + use: jsLoader, + }, + ], + }, + }) + expect(actions.setWebpackConfig).not.toHaveBeenCalledWith({ + module: { + rules: [ + { + enforce: `pre`, + test: /\.tsx?$/, + exclude: /(node_modules|bower_components)/, + use: [], + }, + ], + }, + }) + }) + + it(`set the typescript-eslint webpack config only if in develop stage`, () => { + const actions = { setWebpackConfig: jest.fn() } + const jsLoader = {} + const loaders = { js: jest.fn(() => jsLoader) } + const stage = `build-html` + const eslintLoader = { loader: `eslint-loader` } + const webpackConfig = { + module: { + rules: [ + { + enforce: `pre`, + test: /\.jsx?$/, + exclude: /(node_modules|bower_components)/, + use: [eslintLoader], + }, + ], + }, + } + const getConfig = jest.fn(() => webpackConfig) + onCreateWebpackConfig({ actions, getConfig, loaders, stage }) + expect(actions.setWebpackConfig).toHaveBeenCalledWith({ + module: { + rules: [ + { + test: /\.tsx?$/, + use: jsLoader, + }, + ], + }, + }) + expect(actions.setWebpackConfig).not.toHaveBeenCalledWith({ + module: { + rules: [ + { + enforce: `pre`, + test: /\.tsx?$/, + exclude: /(node_modules|bower_components)/, + use: [eslintLoader], + }, + ], + }, + }) + }) }) }) diff --git a/packages/gatsby-plugin-typescript/src/gatsby-node.js b/packages/gatsby-plugin-typescript/src/gatsby-node.js index 90e8b8c8de51f..fb6330cf935f0 100644 --- a/packages/gatsby-plugin-typescript/src/gatsby-node.js +++ b/packages/gatsby-plugin-typescript/src/gatsby-node.js @@ -16,7 +16,13 @@ function onCreateBabelConfig({ actions }, options) { }) } -function onCreateWebpackConfig({ actions, loaders }) { +function onCreateWebpackConfig({ + actions, + getConfig, + loaders, + stage, + reporter, +}) { const jsLoader = loaders.js() if (!jsLoader) { @@ -33,6 +39,38 @@ function onCreateWebpackConfig({ actions, loaders }) { ], }, }) + + if (stage === `develop`) { + let isTypescriptDepAvailable + try { + isTypescriptDepAvailable = require.resolve(`typescript`) + } catch (e) { + reporter.warn( + `"typescript" is not installed. Builtin ESLint won't be working on typescript files.` + ) + } + + if (isTypescriptDepAvailable) { + const builtInEslintRule = getConfig().module.rules.find(rule => { + if (rule.enforce === `pre`) { + return rule.use.some(use => /eslint-loader/.test(use.loader)) + } + return false + }) + + if (builtInEslintRule) { + const typescriptEslintRule = { + ...builtInEslintRule, + test: /\.tsx?$/, + } + actions.setWebpackConfig({ + module: { + rules: [typescriptEslintRule], + }, + }) + } + } + } } exports.resolvableExtensions = resolvableExtensions