From 666bc48a8fc2ea7d1e7ea0d7e868dbbfbd57c4c3 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Wed, 31 Oct 2018 22:36:11 -0700 Subject: [PATCH 1/8] Add tslint dependency and initial rules --- packages/react-scripts/config/tslint.json | 28 +++++++++++++++++++ .../config/webpack.config.dev.js | 1 + .../config/webpack.config.prod.js | 1 + packages/react-scripts/package.json | 1 + 4 files changed, 31 insertions(+) create mode 100644 packages/react-scripts/config/tslint.json diff --git a/packages/react-scripts/config/tslint.json b/packages/react-scripts/config/tslint.json new file mode 100644 index 00000000000..24cb0075891 --- /dev/null +++ b/packages/react-scripts/config/tslint.json @@ -0,0 +1,28 @@ +{ + "defaultSeverity": "warning", + "rules": { + "await-promise": true, + "new-parens": true, + "no-angle-bracket-type-assertion": true, + "no-conditional-assignment": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-duplicate-switch-case": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-floating-promises": true, + "no-for-in-array": true, + "no-invalid-template-strings": true, + "no-invalid-this": true, + "no-namespace": true, + "no-sparse-arrays": true, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-unused-expression": [true, "allow-fast-null-checks"], + "no-unused-variable": true, + "triple-equals": true, + "use-isnan": true + // "no-implicit-dependencies": true, + // TODO: Add tslint-microsoft-contrib for a11y rules + } +} diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index f8b7c64c40e..07ad3263b7b 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -424,6 +424,7 @@ module.exports = { async: false, checkSyntacticErrors: true, tsconfig: paths.appTsConfig, + tslint: path.resolve(__dirname, 'tslint.json'), compilerOptions: { module: 'esnext', moduleResolution: 'node', diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 5165f1a9842..354a7f3a0cf 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -544,6 +544,7 @@ module.exports = { async: false, checkSyntacticErrors: true, tsconfig: paths.appTsConfig, + tslint: path.resolve(__dirname, 'tslint.json'), compilerOptions: { module: 'esnext', moduleResolution: 'node', diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 05d784169f5..39596a9ca34 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -66,6 +66,7 @@ "sass-loader": "7.1.0", "style-loader": "0.23.0", "terser-webpack-plugin": "1.1.0", + "tslint": "5.11.0", "url-loader": "1.1.1", "webpack": "4.19.1", "webpack-dev-server": "3.1.9", From c7df1bb016be5f0bfd88440942e5c7a2714b366f Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Thu, 1 Nov 2018 21:01:20 -0700 Subject: [PATCH 2/8] Add react-a11y tslint rules --- packages/react-scripts/config/paths.js | 23 +++++++++++-------- packages/react-scripts/config/tslint.json | 22 ++++++++++++++---- packages/react-scripts/package.json | 1 + .../template-typescript/src/serviceWorker.ts | 1 + 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index b719054583b..17b840dc61d 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -126,30 +126,33 @@ const reactScriptsLinked = fs.lstatSync(reactScriptsPath).isSymbolicLink(); // config before publish: we're in ./packages/react-scripts/config/ + if ( !reactScriptsLinked && __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1 ) { + const template = 'template'; + module.exports = { - dotenv: resolveOwn('template/.env'), + dotenv: resolveOwn(`${template}/.env`), appPath: resolveApp('.'), appBuild: resolveOwn('../../build'), - appPublic: resolveOwn('template/public'), - appHtml: resolveOwn('template/public/index.html'), - appIndexJs: resolveModule(resolveOwn, 'template/src/index'), + appPublic: resolveOwn(`${template}/public`), + appHtml: resolveOwn(`${template}/public/index.html`), + appIndexJs: resolveModule(resolveOwn, `${template}/src/index`), appPackageJson: resolveOwn('package.json'), - appSrc: resolveOwn('template/src'), - appTsConfig: resolveOwn('template/tsconfig.json'), - yarnLockFile: resolveOwn('template/yarn.lock'), - testsSetup: resolveModule(resolveOwn, 'template/src/setupTests'), - proxySetup: resolveOwn('template/src/setupProxy.js'), + appSrc: resolveOwn(`${template}/src`), + appTsConfig: resolveOwn(`${template}/tsconfig.json`), + yarnLockFile: resolveOwn(`${template}/yarn.lock`), + testsSetup: resolveModule(resolveOwn, `${template}/src/setupTests`), + proxySetup: resolveOwn(`${template}/src/setupProxy.js`), appNodeModules: resolveOwn('node_modules'), publicUrl: getPublicUrl(resolveOwn('package.json')), servedPath: getServedPath(resolveOwn('package.json')), // These properties only exist before ejecting: ownPath: resolveOwn('.'), ownNodeModules: resolveOwn('node_modules'), - appTypeDeclarations: resolveOwn('template/src/react-app-env.d.ts'), + appTypeDeclarations: resolveOwn(`${template}/src/react-app-env.d.ts`), ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'), }; } diff --git a/packages/react-scripts/config/tslint.json b/packages/react-scripts/config/tslint.json index 24cb0075891..9e3e3f8ce0a 100644 --- a/packages/react-scripts/config/tslint.json +++ b/packages/react-scripts/config/tslint.json @@ -1,6 +1,8 @@ { "defaultSeverity": "warning", + "rulesDirectory": "tslint-microsoft-contrib", "rules": { + // https://palantir.github.io/tslint/rules/ "await-promise": true, "new-parens": true, "no-angle-bracket-type-assertion": true, @@ -12,6 +14,7 @@ "no-eval": true, "no-floating-promises": true, "no-for-in-array": true, + "no-implicit-dependencies": true, "no-invalid-template-strings": true, "no-invalid-this": true, "no-namespace": true, @@ -19,10 +22,21 @@ "no-string-throw": true, "no-switch-case-fall-through": true, "no-unused-expression": [true, "allow-fast-null-checks"], - "no-unused-variable": true, + // DEPRECATED. Recommended to use TS 'noUnusedLocals' for now + // "no-unused-variable": true, "triple-equals": true, - "use-isnan": true - // "no-implicit-dependencies": true, - // TODO: Add tslint-microsoft-contrib for a11y rules + "use-isnan": true, + + // https://github.com/Microsoft/tslint-microsoft-contrib + "react-a11y-anchors": true, + "react-a11y-aria-unsupported-elements": true, + "react-a11y-event-has-role": true, + "react-a11y-image-button-has-alt": true, + "react-a11y-img-has-alt": true, + "react-a11y-props": true, + "react-a11y-proptypes": true, + "react-a11y-role": true, + "react-a11y-role-has-required-aria-props": true, + "react-a11y-role-supports-aria-props": true } } diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 39596a9ca34..f3168eed256 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -67,6 +67,7 @@ "style-loader": "0.23.0", "terser-webpack-plugin": "1.1.0", "tslint": "5.11.0", + "tslint-microsoft-contrib": "5.2.1", "url-loader": "1.1.1", "webpack": "4.19.1", "webpack-dev-server": "3.1.9", diff --git a/packages/react-scripts/template-typescript/src/serviceWorker.ts b/packages/react-scripts/template-typescript/src/serviceWorker.ts index c0b13105198..57be14ec62d 100644 --- a/packages/react-scripts/template-typescript/src/serviceWorker.ts +++ b/packages/react-scripts/template-typescript/src/serviceWorker.ts @@ -1,3 +1,4 @@ +// tslint:disable // This optional code is used to register a service worker. // register() is not called by default. From 36ef7579b9f44eb55d82d9e22973b89823dfd9ac Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sat, 3 Nov 2018 11:54:13 -0700 Subject: [PATCH 3/8] WIP --- packages/react-scripts/config/paths.js | 23 ++++++++++------------- packages/react-scripts/config/tslint.json | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 17b840dc61d..b719054583b 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -126,33 +126,30 @@ const reactScriptsLinked = fs.lstatSync(reactScriptsPath).isSymbolicLink(); // config before publish: we're in ./packages/react-scripts/config/ - if ( !reactScriptsLinked && __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1 ) { - const template = 'template'; - module.exports = { - dotenv: resolveOwn(`${template}/.env`), + dotenv: resolveOwn('template/.env'), appPath: resolveApp('.'), appBuild: resolveOwn('../../build'), - appPublic: resolveOwn(`${template}/public`), - appHtml: resolveOwn(`${template}/public/index.html`), - appIndexJs: resolveModule(resolveOwn, `${template}/src/index`), + appPublic: resolveOwn('template/public'), + appHtml: resolveOwn('template/public/index.html'), + appIndexJs: resolveModule(resolveOwn, 'template/src/index'), appPackageJson: resolveOwn('package.json'), - appSrc: resolveOwn(`${template}/src`), - appTsConfig: resolveOwn(`${template}/tsconfig.json`), - yarnLockFile: resolveOwn(`${template}/yarn.lock`), - testsSetup: resolveModule(resolveOwn, `${template}/src/setupTests`), - proxySetup: resolveOwn(`${template}/src/setupProxy.js`), + appSrc: resolveOwn('template/src'), + appTsConfig: resolveOwn('template/tsconfig.json'), + yarnLockFile: resolveOwn('template/yarn.lock'), + testsSetup: resolveModule(resolveOwn, 'template/src/setupTests'), + proxySetup: resolveOwn('template/src/setupProxy.js'), appNodeModules: resolveOwn('node_modules'), publicUrl: getPublicUrl(resolveOwn('package.json')), servedPath: getServedPath(resolveOwn('package.json')), // These properties only exist before ejecting: ownPath: resolveOwn('.'), ownNodeModules: resolveOwn('node_modules'), - appTypeDeclarations: resolveOwn(`${template}/src/react-app-env.d.ts`), + appTypeDeclarations: resolveOwn('template/src/react-app-env.d.ts'), ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'), }; } diff --git a/packages/react-scripts/config/tslint.json b/packages/react-scripts/config/tslint.json index 9e3e3f8ce0a..5925543c034 100644 --- a/packages/react-scripts/config/tslint.json +++ b/packages/react-scripts/config/tslint.json @@ -14,7 +14,7 @@ "no-eval": true, "no-floating-promises": true, "no-for-in-array": true, - "no-implicit-dependencies": true, + "no-implicit-dependencies": [true, "dev"], "no-invalid-template-strings": true, "no-invalid-this": true, "no-namespace": true, From c0e172fa058aa5233c9f0eaa65f1f39b7d60a11d Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sat, 3 Nov 2018 12:24:34 -0700 Subject: [PATCH 4/8] Refactor serviceWorker.ts/js to use async/await --- .../template-typescript/src/index.tsx | 1 + .../template-typescript/src/serviceWorker.ts | 150 +++++++++--------- .../template/src/serviceWorker.js | 145 ++++++++--------- 3 files changed, 143 insertions(+), 153 deletions(-) diff --git a/packages/react-scripts/template-typescript/src/index.tsx b/packages/react-scripts/template-typescript/src/index.tsx index 0c5e75da1cd..d2da559ee47 100644 --- a/packages/react-scripts/template-typescript/src/index.tsx +++ b/packages/react-scripts/template-typescript/src/index.tsx @@ -9,4 +9,5 @@ ReactDOM.render(, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: http://bit.ly/CRA-PWA +// tslint:disable-next-line:no-floating-promises serviceWorker.unregister(); diff --git a/packages/react-scripts/template-typescript/src/serviceWorker.ts b/packages/react-scripts/template-typescript/src/serviceWorker.ts index 57be14ec62d..cf27862c6de 100644 --- a/packages/react-scripts/template-typescript/src/serviceWorker.ts +++ b/packages/react-scripts/template-typescript/src/serviceWorker.ts @@ -1,4 +1,3 @@ -// tslint:disable // This optional code is used to register a service worker. // register() is not called by default. @@ -40,105 +39,100 @@ export function register(config?: Config) { return; } - window.addEventListener('load', () => { + window.addEventListener('load', async () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); + await checkValidServiceWorker(swUrl, config); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit http://bit.ly/CRA-PWA' - ); - }); + await navigator.serviceWorker.ready; + + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA' + ); } else { // Is not localhost. Just register service worker - registerValidSW(swUrl, config); + await registerValidSW(swUrl, config); } }); } } -function registerValidSW(swUrl: string, config?: Config) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' - ); - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } +async function registerValidSW(swUrl: string, config?: Config) { + try { + const registration = await navigator.serviceWorker.register(swUrl); + + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (!installingWorker) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); } } - }; + } }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); + }; + } catch (error) { + console.error('Error during service worker registration:', error); + } } -function checkValidServiceWorker(swUrl: string, config?: Config) { +async function checkValidServiceWorker(swUrl: string, config?: Config) { // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); + try { + const response = await fetch(swUrl); + + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + await unregister(); + window.location.reload(); + } else { + // Service worker found. Proceed as normal.as + await registerValidSW(swUrl, config); + } + } catch { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + } } -export function unregister() { +export async function unregister() { if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); + const registration = await navigator.serviceWorker.ready; + await registration.unregister(); } } diff --git a/packages/react-scripts/template/src/serviceWorker.js b/packages/react-scripts/template/src/serviceWorker.js index 2283ff9ced1..e43723e3c51 100644 --- a/packages/react-scripts/template/src/serviceWorker.js +++ b/packages/react-scripts/template/src/serviceWorker.js @@ -31,105 +31,100 @@ export function register(config) { return; } - window.addEventListener('load', () => { + window.addEventListener('load', async () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); + await checkValidServiceWorker(swUrl, config); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit http://bit.ly/CRA-PWA' - ); - }); + await navigator.serviceWorker.ready; + + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA' + ); } else { // Is not localhost. Just register service worker - registerValidSW(swUrl, config); + await registerValidSW(swUrl, config); } }); } } -function registerValidSW(swUrl, config) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' - ); +async function registerValidSW(swUrl, config) { + try { + const registration = await navigator.serviceWorker.register(swUrl); + + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (!installingWorker) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' + ); - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); } } - }; + } }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); + }; + } catch (error) { + console.error('Error during service worker registration:', error); + } } -function checkValidServiceWorker(swUrl, config) { +async function checkValidServiceWorker(swUrl, config) { // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); + try { + const response = await fetch(swUrl); + + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + await unregister(); + window.location.reload(); + } else { + // Service worker found. Proceed as normal. + await registerValidSW(swUrl, config); + } + } catch { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + } } -export function unregister() { +export async function unregister() { if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); + const registration = await navigator.serviceWorker.ready; + await registration.unregister(); } } From c154fa6d6008c4f4cb648d455edcb9e18aa2c770 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sat, 3 Nov 2018 12:29:31 -0700 Subject: [PATCH 5/8] Typo --- packages/react-scripts/template-typescript/src/serviceWorker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/template-typescript/src/serviceWorker.ts b/packages/react-scripts/template-typescript/src/serviceWorker.ts index cf27862c6de..5bead095adc 100644 --- a/packages/react-scripts/template-typescript/src/serviceWorker.ts +++ b/packages/react-scripts/template-typescript/src/serviceWorker.ts @@ -120,7 +120,7 @@ async function checkValidServiceWorker(swUrl: string, config?: Config) { await unregister(); window.location.reload(); } else { - // Service worker found. Proceed as normal.as + // Service worker found. Proceed as normal. await registerValidSW(swUrl, config); } } catch { From 6b2ce5c2fba773d5cc058f64c3812884e66b1d65 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sun, 4 Nov 2018 19:43:00 -0800 Subject: [PATCH 6/8] Move TSLint config into separate package --- packages/react-scripts/config/tslint.json | 42 --------------- .../config/webpack.config.dev.js | 2 +- .../config/webpack.config.prod.js | 2 +- packages/react-scripts/package.json | 1 + .../template-typescript/tslint.json | 3 ++ packages/tslint-config-react-app/index.js | 51 +++++++++++++++++++ packages/tslint-config-react-app/package.json | 16 ++++++ 7 files changed, 73 insertions(+), 44 deletions(-) delete mode 100644 packages/react-scripts/config/tslint.json create mode 100644 packages/react-scripts/template-typescript/tslint.json create mode 100644 packages/tslint-config-react-app/index.js create mode 100644 packages/tslint-config-react-app/package.json diff --git a/packages/react-scripts/config/tslint.json b/packages/react-scripts/config/tslint.json deleted file mode 100644 index 5925543c034..00000000000 --- a/packages/react-scripts/config/tslint.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "defaultSeverity": "warning", - "rulesDirectory": "tslint-microsoft-contrib", - "rules": { - // https://palantir.github.io/tslint/rules/ - "await-promise": true, - "new-parens": true, - "no-angle-bracket-type-assertion": true, - "no-conditional-assignment": true, - "no-debugger": true, - "no-duplicate-super": true, - "no-duplicate-switch-case": true, - "no-duplicate-variable": true, - "no-eval": true, - "no-floating-promises": true, - "no-for-in-array": true, - "no-implicit-dependencies": [true, "dev"], - "no-invalid-template-strings": true, - "no-invalid-this": true, - "no-namespace": true, - "no-sparse-arrays": true, - "no-string-throw": true, - "no-switch-case-fall-through": true, - "no-unused-expression": [true, "allow-fast-null-checks"], - // DEPRECATED. Recommended to use TS 'noUnusedLocals' for now - // "no-unused-variable": true, - "triple-equals": true, - "use-isnan": true, - - // https://github.com/Microsoft/tslint-microsoft-contrib - "react-a11y-anchors": true, - "react-a11y-aria-unsupported-elements": true, - "react-a11y-event-has-role": true, - "react-a11y-image-button-has-alt": true, - "react-a11y-img-has-alt": true, - "react-a11y-props": true, - "react-a11y-proptypes": true, - "react-a11y-role": true, - "react-a11y-role-has-required-aria-props": true, - "react-a11y-role-supports-aria-props": true - } -} diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 07ad3263b7b..d3ab2c4e1e0 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -424,7 +424,7 @@ module.exports = { async: false, checkSyntacticErrors: true, tsconfig: paths.appTsConfig, - tslint: path.resolve(__dirname, 'tslint.json'), + tslint: require.resolve('tslint-config-react-app'), compilerOptions: { module: 'esnext', moduleResolution: 'node', diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 354a7f3a0cf..1a72cee05a3 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -544,7 +544,7 @@ module.exports = { async: false, checkSyntacticErrors: true, tsconfig: paths.appTsConfig, - tslint: path.resolve(__dirname, 'tslint.json'), + tslint: require.resolve('tslint-config-react-app'), compilerOptions: { module: 'esnext', moduleResolution: 'node', diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index f3168eed256..ef3e783bdc3 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -67,6 +67,7 @@ "style-loader": "0.23.0", "terser-webpack-plugin": "1.1.0", "tslint": "5.11.0", + "tslint-config-react-app": "^1.0.0", "tslint-microsoft-contrib": "5.2.1", "url-loader": "1.1.1", "webpack": "4.19.1", diff --git a/packages/react-scripts/template-typescript/tslint.json b/packages/react-scripts/template-typescript/tslint.json new file mode 100644 index 00000000000..376922a39fe --- /dev/null +++ b/packages/react-scripts/template-typescript/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "tslint-config-react-app" +} \ No newline at end of file diff --git a/packages/tslint-config-react-app/index.js b/packages/tslint-config-react-app/index.js new file mode 100644 index 00000000000..c12b806ecf6 --- /dev/null +++ b/packages/tslint-config-react-app/index.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +module.exports = { + defaultSeverity: 'warning', + rulesDirectory: 'tslint-microsoft-contrib', + rules: { + // https://palantir.github.io/tslint/rules/ + 'await-promise': true, + 'new-parens': true, + 'no-angle-bracket-type-assertion': true, + 'no-conditional-assignment': true, + 'no-debugger': true, + 'no-duplicate-super': true, + 'no-duplicate-switch-case': true, + 'no-duplicate-variable': true, + 'no-eval': true, + 'no-floating-promises': true, + 'no-for-in-array': true, + 'no-implicit-dependencies': [true, 'dev'], + 'no-invalid-template-strings': true, + 'no-invalid-this': true, + 'no-namespace': true, + 'no-sparse-arrays': true, + 'no-string-throw': true, + 'no-switch-case-fall-through': true, + 'no-unused-expression': [true, 'allow-fast-null-checks'], + // DEPRECATED. Recommended to use TS 'noUnusedLocals' for now + // "no-unused-variable": true, + 'triple-equals': true, + 'use-isnan': true, + + // https://github.com/Microsoft/tslint-microsoft-contrib + 'react-a11y-anchors': true, + 'react-a11y-aria-unsupported-elements': true, + 'react-a11y-event-has-role': true, + 'react-a11y-image-button-has-alt': true, + 'react-a11y-img-has-alt': true, + 'react-a11y-props': true, + 'react-a11y-proptypes': true, + 'react-a11y-role': true, + 'react-a11y-role-has-required-aria-props': true, + 'react-a11y-role-supports-aria-props': true, + }, +}; diff --git a/packages/tslint-config-react-app/package.json b/packages/tslint-config-react-app/package.json new file mode 100644 index 00000000000..4faacee2b79 --- /dev/null +++ b/packages/tslint-config-react-app/package.json @@ -0,0 +1,16 @@ +{ + "name": "tslint-config-react-app", + "version": "1.0.0", + "description": "TSLint configuration used by Create React App", + "repository": "facebook/create-react-app", + "license": "MIT", + "bugs": { + "url": "https://github.com/facebook/create-react-app/issues" + }, + "main": "index.js", + "peerDependencies": { + "tslint": "5.x", + "tslint-microsoft-contrib": "5.x" + } + } + \ No newline at end of file From 9b45c83ab66385a4f07141c15e8b596643b72071 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sun, 4 Nov 2018 19:44:39 -0800 Subject: [PATCH 7/8] Whitespace --- packages/react-scripts/template-typescript/tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/template-typescript/tslint.json b/packages/react-scripts/template-typescript/tslint.json index 376922a39fe..74931915488 100644 --- a/packages/react-scripts/template-typescript/tslint.json +++ b/packages/react-scripts/template-typescript/tslint.json @@ -1,3 +1,3 @@ { "extends": "tslint-config-react-app" -} \ No newline at end of file +} From baf40247a45302eff1be491b0b188139d6c7c1b4 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Sun, 4 Nov 2018 19:54:27 -0800 Subject: [PATCH 8/8] Whitespace --- packages/tslint-config-react-app/package.json | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/tslint-config-react-app/package.json b/packages/tslint-config-react-app/package.json index 4faacee2b79..4fb5b10aa7f 100644 --- a/packages/tslint-config-react-app/package.json +++ b/packages/tslint-config-react-app/package.json @@ -1,16 +1,15 @@ { - "name": "tslint-config-react-app", - "version": "1.0.0", - "description": "TSLint configuration used by Create React App", - "repository": "facebook/create-react-app", - "license": "MIT", - "bugs": { - "url": "https://github.com/facebook/create-react-app/issues" - }, - "main": "index.js", - "peerDependencies": { - "tslint": "5.x", - "tslint-microsoft-contrib": "5.x" - } + "name": "tslint-config-react-app", + "version": "1.0.0", + "description": "TSLint configuration used by Create React App", + "repository": "facebook/create-react-app", + "license": "MIT", + "bugs": { + "url": "https://github.com/facebook/create-react-app/issues" + }, + "main": "index.js", + "peerDependencies": { + "tslint": "5.x", + "tslint-microsoft-contrib": "5.x" } - \ No newline at end of file +}