mirror of
https://github.com/desktop/desktop
synced 2024-08-27 04:00:55 +00:00
Revert "ESLint for great justice: Episode I"
This commit is contained in:
parent
0c22a1fde5
commit
45e7b216ea
|
@ -1,87 +0,0 @@
|
|||
root: true
|
||||
parser: typescript-eslint-parser
|
||||
plugins:
|
||||
- typescript
|
||||
- babel
|
||||
- react
|
||||
- prettier
|
||||
|
||||
extends:
|
||||
- prettier
|
||||
- prettier/react
|
||||
|
||||
rules:
|
||||
##########
|
||||
# CUSTOM #
|
||||
##########
|
||||
insecure-random: error
|
||||
|
||||
###########
|
||||
# PLUGINS #
|
||||
###########
|
||||
# TYPESCRIPT
|
||||
typescript/interface-name-prefix:
|
||||
- error
|
||||
- always
|
||||
typescript/no-angle-bracket-type-assertion: error
|
||||
typescript/explicit-member-accessibility: error
|
||||
typescript/no-unused-vars: error
|
||||
typescript/no-use-before-define:
|
||||
- error
|
||||
- functions: false
|
||||
variables: false
|
||||
typedefs: false
|
||||
## blocked by https://github.com/nzakas/eslint-plugin-typescript/pull/23
|
||||
# typescript/member-ordering: error
|
||||
##
|
||||
## blocked by https://github.com/nzakas/eslint-plugin-typescript/issues/41
|
||||
# typescript/type-annotation-spacing: error
|
||||
##
|
||||
|
||||
# Babel
|
||||
babel/no-invalid-this: error
|
||||
|
||||
# React
|
||||
react/jsx-boolean-value:
|
||||
- error
|
||||
- always
|
||||
react/jsx-key: error
|
||||
react/jsx-no-bind: error
|
||||
react/no-string-refs: error
|
||||
|
||||
###########
|
||||
# BUILTIN #
|
||||
###########
|
||||
curly: error
|
||||
no-new-wrappers: error
|
||||
no-redeclare:
|
||||
- error
|
||||
- builtinGlobals: true
|
||||
no-eval: error
|
||||
no-sync: error
|
||||
no-unused-expressions: error
|
||||
no-var: error
|
||||
prefer-const: error
|
||||
eqeqeq:
|
||||
- error
|
||||
- smart
|
||||
|
||||
###########
|
||||
# SPECIAL #
|
||||
###########
|
||||
prettier/prettier:
|
||||
- error
|
||||
- singleQuote: true
|
||||
trailingComma: es5
|
||||
semi: false
|
||||
parser: typescript
|
||||
no-restricted-syntax:
|
||||
- error
|
||||
# no-default-export
|
||||
- selector: ExportDefaultDeclaration
|
||||
message: Use of default exports is forbidden
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaFeatures:
|
||||
jsx: true
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,5 +5,4 @@ npm-debug.log
|
|||
app/node_modules/
|
||||
.DS_Store
|
||||
.awcache
|
||||
.idea/
|
||||
.eslintcache
|
||||
.idea/
|
|
@ -20,7 +20,6 @@ cache:
|
|||
directories:
|
||||
- node_modules
|
||||
- $HOME/.electron
|
||||
- .eslintcache
|
||||
|
||||
install:
|
||||
- npm install -g npm@4.6.1
|
||||
|
@ -31,6 +30,7 @@ install:
|
|||
- npm ls --dev
|
||||
|
||||
script:
|
||||
- npm run check-prettiness
|
||||
- npm run lint
|
||||
- npm run build:prod
|
||||
- npm run test:setup
|
||||
|
|
162
app/npm-shrinkwrap.json
generated
162
app/npm-shrinkwrap.json
generated
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"accessibility-developer-tools": {
|
||||
"version": "2.12.0",
|
||||
"from": "accessibility-developer-tools@^2.11.0",
|
||||
"from": "accessibility-developer-tools@>=2.11.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accessibility-developer-tools/-/accessibility-developer-tools-2.12.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -25,9 +25,15 @@
|
|||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"from": "ansi-regex@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"app-path": {
|
||||
"version": "2.2.0",
|
||||
"from": "app-path@>=2.2.0 <3.0.0",
|
||||
"from": "app-path@latest",
|
||||
"resolved": "https://registry.npmjs.org/app-path/-/app-path-2.2.0.tgz"
|
||||
},
|
||||
"argparse": {
|
||||
|
@ -36,9 +42,9 @@
|
|||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
|
||||
},
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.5",
|
||||
"from": "asap@>=2.0.3 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
|
@ -95,7 +101,7 @@
|
|||
},
|
||||
"big.js": {
|
||||
"version": "3.1.3",
|
||||
"from": "big.js@^3.1.3",
|
||||
"from": "big.js@>=3.1.3 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -212,12 +218,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.8",
|
||||
"from": "debug@^2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.0.1",
|
||||
"from": "deep-equal@>=1.0.1 <2.0.0",
|
||||
|
@ -230,7 +230,7 @@
|
|||
},
|
||||
"devtron": {
|
||||
"version": "1.4.0",
|
||||
"from": "devtron@^1.4.0",
|
||||
"from": "devtron@>=1.4.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/devtron/-/devtron-1.4.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -266,33 +266,27 @@
|
|||
"optional": true
|
||||
},
|
||||
"electron-debug": {
|
||||
"version": "1.3.0",
|
||||
"from": "electron-debug@^1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-debug/-/electron-debug-1.3.0.tgz",
|
||||
"version": "1.1.0",
|
||||
"from": "electron-debug@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-debug/-/electron-debug-1.1.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"electron-devtools-installer": {
|
||||
"version": "2.2.0",
|
||||
"from": "electron-devtools-installer@^2.1.0",
|
||||
"from": "electron-devtools-installer@>=2.1.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-2.2.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"electron-is-accelerator": {
|
||||
"version": "0.1.2",
|
||||
"from": "electron-is-accelerator@^0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"electron-is-dev": {
|
||||
"version": "0.3.0",
|
||||
"from": "electron-is-dev@^0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
|
||||
"version": "0.1.2",
|
||||
"from": "electron-is-dev@>=0.1.0 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.1.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"electron-localshortcut": {
|
||||
"version": "2.0.2",
|
||||
"from": "electron-localshortcut@^2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-localshortcut/-/electron-localshortcut-2.0.2.tgz",
|
||||
"version": "0.6.1",
|
||||
"from": "electron-localshortcut@>=0.6.0 <0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-localshortcut/-/electron-localshortcut-0.6.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"electron-window-state": {
|
||||
|
@ -302,7 +296,7 @@
|
|||
},
|
||||
"emojis-list": {
|
||||
"version": "2.1.0",
|
||||
"from": "emojis-list@^2.0.0",
|
||||
"from": "emojis-list@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -342,13 +336,13 @@
|
|||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz"
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.14",
|
||||
"version": "0.8.12",
|
||||
"from": "fbjs@>=0.8.9 <0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz"
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.12.tgz"
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "0.0.2",
|
||||
"from": "file-uri-to-path@>=0.0.0 <1.0.0",
|
||||
"from": "file-uri-to-path@latest",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-0.0.2.tgz"
|
||||
},
|
||||
"forever-agent": {
|
||||
|
@ -376,6 +370,11 @@
|
|||
"from": "fs.realpath@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"from": "fstream@>=1.0.2 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz"
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"from": "getpass@>=0.1.1 <0.2.0",
|
||||
|
@ -414,9 +413,9 @@
|
|||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz"
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.12.0",
|
||||
"from": "highlight.js@^9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz",
|
||||
"version": "9.11.0",
|
||||
"from": "highlight.js@>=9.3.0 <10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.11.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"hoek": {
|
||||
|
@ -426,20 +425,25 @@
|
|||
},
|
||||
"html-entities": {
|
||||
"version": "1.2.1",
|
||||
"from": "html-entities@^1.2.0",
|
||||
"from": "html-entities@>=1.2.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1",
|
||||
"from": "http-signature@>=1.1.0 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz"
|
||||
},
|
||||
"humanize-plus": {
|
||||
"version": "1.8.2",
|
||||
"from": "humanize-plus@^1.8.1",
|
||||
"from": "humanize-plus@>=1.8.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.18",
|
||||
"version": "0.4.17",
|
||||
"from": "iconv-lite@>=0.4.13 <0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz"
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.17.tgz"
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
|
@ -477,9 +481,9 @@
|
|||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.1",
|
||||
"from": "js-tokens@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz"
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz"
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.8.3",
|
||||
|
@ -509,7 +513,7 @@
|
|||
},
|
||||
"json5": {
|
||||
"version": "0.5.1",
|
||||
"from": "json5@^0.5.0",
|
||||
"from": "json5@>=0.5.0 <0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -542,7 +546,7 @@
|
|||
},
|
||||
"loader-utils": {
|
||||
"version": "1.1.0",
|
||||
"from": "loader-utils@^1.0.2",
|
||||
"from": "loader-utils@>=1.0.2 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -591,21 +595,15 @@
|
|||
"from": "moment@>=2.17.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz"
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"from": "ms@2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.5.1",
|
||||
"from": "nan@2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz"
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.1",
|
||||
"version": "1.6.3",
|
||||
"from": "node-fetch@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz"
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz"
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "1.0.0",
|
||||
|
@ -663,9 +661,9 @@
|
|||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz"
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"version": "7.1.1",
|
||||
"from": "promise@>=7.1.1 <8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz"
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz"
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.5.10",
|
||||
|
@ -689,7 +687,7 @@
|
|||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"from": "querystring@^0.2.0",
|
||||
"from": "querystring@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -700,7 +698,7 @@
|
|||
},
|
||||
"react-addons-perf": {
|
||||
"version": "15.4.2",
|
||||
"from": "react-addons-perf@15.4.2",
|
||||
"from": "react-addons-perf@>=15.4.2 <16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-addons-perf/-/react-addons-perf-15.4.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -735,6 +733,11 @@
|
|||
"from": "regenerator-runtime@>=0.10.0 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz"
|
||||
},
|
||||
"request": {
|
||||
"version": "2.81.0",
|
||||
"from": "request@>=2.79.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz"
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.1",
|
||||
"from": "rimraf@>=2.5.4 <3.0.0",
|
||||
|
@ -751,9 +754,9 @@
|
|||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"from": "semver@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0||>=4.0.0 <5.0.0||>=5.0.0 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"version": "5.3.0",
|
||||
"from": "semver@>=5.3.0 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"setimmediate": {
|
||||
|
@ -803,6 +806,12 @@
|
|||
"from": "stringstream@>=0.0.4 <0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"from": "strip-ansi@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"from": "strip-eof@>=1.0.0 <2.0.0",
|
||||
|
@ -810,31 +819,24 @@
|
|||
},
|
||||
"style-loader": {
|
||||
"version": "0.13.2",
|
||||
"from": "style-loader@^0.13.2",
|
||||
"from": "style-loader@>=0.13.2 <0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"from": "tar@>=2.2.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
|
||||
"dependencies": {
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"from": "fstream@https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz"
|
||||
}
|
||||
}
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz"
|
||||
},
|
||||
"temp": {
|
||||
"version": "0.8.3",
|
||||
"from": "temp@^0.8.3",
|
||||
"from": "temp@>=0.8.3 <0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.2.8",
|
||||
"from": "rimraf@~2.2.6",
|
||||
"from": "rimraf@>=2.2.6 <2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
|
||||
"dev": true
|
||||
}
|
||||
|
@ -899,24 +901,10 @@
|
|||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz"
|
||||
},
|
||||
"webpack-hot-middleware": {
|
||||
"version": "2.18.2",
|
||||
"from": "webpack-hot-middleware@^2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.18.2.tgz",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"from": "ansi-regex@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"from": "strip-ansi@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
"version": "2.18.0",
|
||||
"from": "webpack-hot-middleware@>=2.10.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.18.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "2.0.3",
|
||||
|
|
|
@ -147,7 +147,7 @@ function skewInterval(): number {
|
|||
|
||||
// We don't need cryptographically secure random numbers for
|
||||
// the skew. Pseudo-random should be just fine.
|
||||
// eslint-disable-next-line insecure-random
|
||||
// tslint:disable-next-line:insecure-random
|
||||
const skew = Math.ceil(Math.random() * SkewUpperBound)
|
||||
_skewInterval = skew
|
||||
return skew
|
||||
|
|
16
app/src/lib/globals.d.ts
vendored
16
app/src/lib/globals.d.ts
vendored
|
@ -62,7 +62,7 @@ declare type DOMHighResTimeStamp = number
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline
|
||||
*/
|
||||
interface IIdleDeadline {
|
||||
interface IdleDeadline {
|
||||
readonly didTimeout: boolean
|
||||
readonly timeRemaining: () => DOMHighResTimeStamp
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ interface IIdleDeadline {
|
|||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
*/
|
||||
interface IIdleCallbackOptions {
|
||||
interface IdleCallbackOptions {
|
||||
/**
|
||||
* If timeout is specified and has a positive value, and the callback has not
|
||||
* already been called by the time timeout milliseconds have passed, the
|
||||
|
@ -99,8 +99,8 @@ interface IIdleCallbackOptions {
|
|||
* timeout:
|
||||
*/
|
||||
declare function requestIdleCallback(
|
||||
fn: (deadline: IIdleDeadline) => void,
|
||||
options?: IIdleCallbackOptions
|
||||
fn: (deadline: IdleDeadline) => void,
|
||||
options?: IdleCallbackOptions
|
||||
): number
|
||||
|
||||
interface IDesktopLogger {
|
||||
|
@ -177,7 +177,7 @@ declare const log: IDesktopLogger
|
|||
// these changes should be pushed into the Electron declarations
|
||||
|
||||
declare namespace NodeJS {
|
||||
// eslint-disable-next-line typescript/interface-name-prefix
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface Process extends EventEmitter {
|
||||
once(event: 'uncaughtException', listener: (error: Error) => void): this
|
||||
on(event: 'uncaughtException', listener: (error: Error) => void): this
|
||||
|
@ -187,7 +187,7 @@ declare namespace NodeJS {
|
|||
}
|
||||
|
||||
declare namespace Electron {
|
||||
// eslint-disable-next-line typescript/interface-name-prefix
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface MenuItem {
|
||||
readonly accelerator?: Electron.Accelerator
|
||||
readonly submenu?: Electron.Menu
|
||||
|
@ -195,7 +195,7 @@ declare namespace Electron {
|
|||
readonly type: 'normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'
|
||||
}
|
||||
|
||||
// eslint-disable-next-line typescript/interface-name-prefix
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface RequestOptions {
|
||||
readonly method: string
|
||||
readonly url: string
|
||||
|
@ -204,7 +204,7 @@ declare namespace Electron {
|
|||
|
||||
type AppleActionOnDoubleClickPref = 'Maximize' | 'Minimize' | 'None'
|
||||
|
||||
// eslint-disable-next-line typescript/interface-name-prefix
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface SystemPreferences {
|
||||
getUserDefault(
|
||||
key: 'AppleActionOnDoubleClick',
|
||||
|
|
|
@ -3,7 +3,7 @@ import { formatLogMessage } from '../format-log-message'
|
|||
|
||||
const g = global as any
|
||||
|
||||
g.log = {
|
||||
g.log = <IDesktopLogger>{
|
||||
error(message: string, error?: Error) {
|
||||
log('error', '[main] ' + formatLogMessage(message, error))
|
||||
},
|
||||
|
@ -16,4 +16,4 @@ g.log = {
|
|||
debug(message: string, error?: Error) {
|
||||
log('debug', '[main] ' + formatLogMessage(message, error))
|
||||
},
|
||||
} as IDesktopLogger
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ function log(level: LogLevel, message: string, error?: Error) {
|
|||
)
|
||||
}
|
||||
|
||||
g.log = {
|
||||
g.log = <IDesktopLogger>{
|
||||
error(message: string, error?: Error) {
|
||||
log('error', message, error)
|
||||
console.error(formatLogMessage(message, error))
|
||||
|
@ -34,4 +34,4 @@ g.log = {
|
|||
log('debug', message, error)
|
||||
console.debug(formatLogMessage(message, error))
|
||||
},
|
||||
} as IDesktopLogger
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as ChildProcess from 'child_process'
|
||||
import * as os from 'os'
|
||||
|
|
|
@ -48,13 +48,13 @@ function retrieveSourceMap(source: string) {
|
|||
// https://github.com/v8/v8/wiki/Stack-Trace-API#customizing-stack-traces
|
||||
// This happens on-demand when someone accesses the stack
|
||||
// property on an error object and has to be synchronous :/
|
||||
// eslint-disable-next-line no-sync
|
||||
// tslint:disable-next-line:no-sync-functions
|
||||
if (!Fs.existsSync(path)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-sync
|
||||
// tslint:disable-next-line:no-sync-functions
|
||||
const map = Fs.readFileSync(path, 'utf8')
|
||||
return { url: Path.basename(path), map }
|
||||
} catch (error) {
|
||||
|
@ -125,6 +125,7 @@ function sourceMappedStackTrace(error: Error): string | undefined {
|
|||
// in our weak map. In order to get around that we'll eagerly access the
|
||||
// stack, forcing our handler to run which should ensure that the native
|
||||
// frames are stored in our weak map.
|
||||
// tslint:disable-next-line:whitespace
|
||||
;(error.stack || '').toString()
|
||||
frames = stackFrameMap.get(error)
|
||||
}
|
||||
|
|
|
@ -6,45 +6,6 @@ import { assertNever } from '../lib/fatal-error'
|
|||
*/
|
||||
export const maximumDiffStringSize = 268435441
|
||||
|
||||
/**
|
||||
* A container for holding an image for display in the application
|
||||
*/
|
||||
export class Image {
|
||||
/**
|
||||
* The base64 encoded contents of the image
|
||||
*/
|
||||
public readonly contents: string
|
||||
|
||||
/**
|
||||
* The data URI media type, so the browser can render the image correctly
|
||||
*/
|
||||
public readonly mediaType: string
|
||||
}
|
||||
|
||||
/** each diff is made up of a number of hunks */
|
||||
export class DiffHunk {
|
||||
/** details from the diff hunk header about the line start and patch length */
|
||||
public readonly header: DiffHunkHeader
|
||||
/** the contents - context and changes - of the diff setion */
|
||||
public readonly lines: ReadonlyArray<DiffLine>
|
||||
/** the diff hunk's start position in the overall file diff */
|
||||
public readonly unifiedDiffStart: number
|
||||
/** the diff hunk's end position in the overall file diff */
|
||||
public readonly unifiedDiffEnd: number
|
||||
|
||||
public constructor(
|
||||
header: DiffHunkHeader,
|
||||
lines: ReadonlyArray<DiffLine>,
|
||||
unifiedDiffStart: number,
|
||||
unifiedDiffEnd: number
|
||||
) {
|
||||
this.header = header
|
||||
this.unifiedDiffStart = unifiedDiffStart
|
||||
this.unifiedDiffEnd = unifiedDiffEnd
|
||||
this.lines = lines
|
||||
}
|
||||
}
|
||||
|
||||
export enum DiffType {
|
||||
/** changes to a text file, which may be partially selected for commit */
|
||||
Text,
|
||||
|
@ -201,6 +162,45 @@ export class DiffHunkHeader {
|
|||
}
|
||||
}
|
||||
|
||||
/** each diff is made up of a number of hunks */
|
||||
export class DiffHunk {
|
||||
/** details from the diff hunk header about the line start and patch length */
|
||||
public readonly header: DiffHunkHeader
|
||||
/** the contents - context and changes - of the diff setion */
|
||||
public readonly lines: ReadonlyArray<DiffLine>
|
||||
/** the diff hunk's start position in the overall file diff */
|
||||
public readonly unifiedDiffStart: number
|
||||
/** the diff hunk's end position in the overall file diff */
|
||||
public readonly unifiedDiffEnd: number
|
||||
|
||||
public constructor(
|
||||
header: DiffHunkHeader,
|
||||
lines: ReadonlyArray<DiffLine>,
|
||||
unifiedDiffStart: number,
|
||||
unifiedDiffEnd: number
|
||||
) {
|
||||
this.header = header
|
||||
this.unifiedDiffStart = unifiedDiffStart
|
||||
this.unifiedDiffEnd = unifiedDiffEnd
|
||||
this.lines = lines
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A container for holding an image for display in the application
|
||||
*/
|
||||
export class Image {
|
||||
/**
|
||||
* The base64 encoded contents of the image
|
||||
*/
|
||||
public readonly contents: string
|
||||
|
||||
/**
|
||||
* The data URI media type, so the browser can render the image correctly
|
||||
*/
|
||||
public readonly mediaType: string
|
||||
}
|
||||
|
||||
export class FileSummary {
|
||||
/**
|
||||
* The number of lines added as part of this change.
|
||||
|
|
|
@ -13,7 +13,7 @@ import { LinkButton } from '../lib/link-button'
|
|||
import { PopupType } from '../../lib/app-state'
|
||||
import * as Path from 'path'
|
||||
|
||||
import untildify = require('untildify')
|
||||
const untildify: (str: string) => string = require('untildify')
|
||||
|
||||
interface IAddExistingRepositoryProps {
|
||||
readonly dispatcher: Dispatcher
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
IMenu,
|
||||
ISubmenuItem,
|
||||
findItemByAccessKey,
|
||||
itemIsSelectable,
|
||||
} from '../../models/app-menu'
|
||||
import { IMenu, ISubmenuItem } from '../../models/app-menu'
|
||||
import { AppMenuBarButton } from './app-menu-bar-button'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import { AppMenuFoldout, FoldoutType } from '../../lib/app-state'
|
||||
import { findItemByAccessKey, itemIsSelectable } from '../../models/app-menu'
|
||||
|
||||
interface IAppMenuBarProps {
|
||||
readonly appMenu: ReadonlyArray<IMenu>
|
||||
|
@ -31,7 +27,7 @@ interface IAppMenuBarProps {
|
|||
|
||||
/**
|
||||
* An optional function that's called when the menubar loses focus.
|
||||
*
|
||||
*
|
||||
* Note that this function will only be called once no descendant element
|
||||
* of the menu bar has keyboard focus. In other words this differs
|
||||
* from the traditional onBlur event.
|
||||
|
@ -51,7 +47,7 @@ interface IAppMenuBarState {
|
|||
* Creates menu bar state given props. This is intentionally not
|
||||
* an instance member in order to avoid mistakenly using any other
|
||||
* input data or state than the received props.
|
||||
*
|
||||
*
|
||||
* The state consists of a list of visible top-level menu items which have
|
||||
* child menus of their own (ie submenu items).
|
||||
*/
|
||||
|
@ -99,12 +95,12 @@ export class AppMenuBar extends React.Component<
|
|||
* element which had focus prior to the component receiving it. We do so in
|
||||
* order to be able to restore focus to that element when we decide to
|
||||
* _programmatically_ give up our focus.
|
||||
*
|
||||
*
|
||||
* A good example of this is when the user is focused on a text box and hits
|
||||
* the Alt key. Focus will then move to the first menu item in the menu bar.
|
||||
* If the user then hits Enter we relinquish our focus and return it back to
|
||||
* the text box again.
|
||||
*
|
||||
*
|
||||
* As long as we hold on to this reference we might be preventing GC from
|
||||
* collecting a potentially huge subtree of the DOM so we need to make sure
|
||||
* to clear it out as soon as we're done with it.
|
||||
|
|
|
@ -1285,11 +1285,9 @@ export class App extends React.Component<IAppProps, IAppState> {
|
|||
}
|
||||
|
||||
private onRepositoryDropdownStateChanged = (newState: DropdownState) => {
|
||||
if (newState === 'open') {
|
||||
this.props.dispatcher.showFoldout({ type: FoldoutType.Repository })
|
||||
} else {
|
||||
this.props.dispatcher.closeFoldout(FoldoutType.Repository)
|
||||
}
|
||||
newState === 'open'
|
||||
? this.props.dispatcher.showFoldout({ type: FoldoutType.Repository })
|
||||
: this.props.dispatcher.closeFoldout(FoldoutType.Repository)
|
||||
}
|
||||
|
||||
private renderRepositoryToolbarButton() {
|
||||
|
@ -1437,11 +1435,9 @@ export class App extends React.Component<IAppProps, IAppState> {
|
|||
}
|
||||
|
||||
private onBranchDropdownStateChanged = (newState: DropdownState) => {
|
||||
if (newState === 'open') {
|
||||
this.props.dispatcher.showFoldout({ type: FoldoutType.Branch })
|
||||
} else {
|
||||
this.props.dispatcher.closeFoldout(FoldoutType.Branch)
|
||||
}
|
||||
newState === 'open'
|
||||
? this.props.dispatcher.showFoldout({ type: FoldoutType.Branch })
|
||||
: this.props.dispatcher.closeFoldout(FoldoutType.Branch)
|
||||
}
|
||||
|
||||
private renderBranchToolbarButton(): JSX.Element | null {
|
||||
|
|
|
@ -4,12 +4,20 @@ import { IAutocompletionProvider } from './index'
|
|||
import { fatalError } from '../../lib/fatal-error'
|
||||
import * as classNames from 'classnames'
|
||||
|
||||
interface IPosition {
|
||||
readonly top: number
|
||||
readonly left: number
|
||||
}
|
||||
|
||||
interface IRange {
|
||||
readonly start: number
|
||||
readonly length: number
|
||||
}
|
||||
|
||||
import getCaretCoordinates = require('textarea-caret')
|
||||
const getCaretCoordinates: (
|
||||
element: HTMLElement,
|
||||
position: number
|
||||
) => IPosition = require('textarea-caret')
|
||||
|
||||
interface IAutocompletingTextInputProps<ElementType> {
|
||||
/**
|
||||
|
@ -124,7 +132,6 @@ export abstract class AutocompletingTextInput<
|
|||
const element = this.element!
|
||||
let coordinates = getCaretCoordinates(element, state.range.start)
|
||||
coordinates = {
|
||||
...coordinates,
|
||||
top: coordinates.top - element.scrollTop,
|
||||
left: coordinates.left - element.scrollLeft,
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ export class EmojiAutocompletionProvider
|
|||
}
|
||||
|
||||
public getRegExp(): RegExp {
|
||||
return /(?:^|\n| )(?::)([a-z\d\\+-][a-z\d_]*)?/g
|
||||
return /(?:^|\n| )(?::)([a-z0-9\\+\\-][a-z0-9_]*)?/g
|
||||
}
|
||||
|
||||
public async getAutocompletionItems(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { IAutocompletionProvider } from './index'
|
||||
import { Dispatcher, IssuesStore } from '../../lib/dispatcher'
|
||||
import { IssuesStore } from '../../lib/dispatcher'
|
||||
import { GitHubRepository } from '../../models/github-repository'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import { ThrottledScheduler } from '../lib/throttled-scheduler'
|
||||
|
||||
/** The interval we should use to throttle the issues update. */
|
||||
|
@ -44,7 +45,7 @@ export class IssuesAutocompletionProvider
|
|||
}
|
||||
|
||||
public getRegExp(): RegExp {
|
||||
return /(?:^|\n| )(?:#)([a-z\d\\+-][a-z\d_]*)?/g
|
||||
return /(?:^|\n| )(?:#)([a-z0-9\\+\\-][a-z0-9_]*)?/g
|
||||
}
|
||||
|
||||
public getAutocompletionItems(
|
||||
|
|
|
@ -30,7 +30,7 @@ export class UserAutocompletionProvider
|
|||
}
|
||||
|
||||
public getRegExp(): RegExp {
|
||||
return /(?:^|\n| )(?:@)([a-z\d\\+-][a-z\d_-]*)?/g
|
||||
return /(?:^|\n| )(?:@)([a-z0-9\\+\\-][a-z0-9_\-]*)?/g
|
||||
}
|
||||
|
||||
public async getAutocompletionItems(
|
||||
|
|
|
@ -11,8 +11,9 @@ import { DiffSelectionType } from '../../models/diff'
|
|||
import { CommitIdentity } from '../../models/commit-identity'
|
||||
import { Checkbox, CheckboxValue } from '../lib/checkbox'
|
||||
import { ICommitMessage } from '../../lib/app-state'
|
||||
import { Dispatcher, IGitHubUser } from '../../lib/dispatcher'
|
||||
import { IGitHubUser } from '../../lib/dispatcher'
|
||||
import { IAutocompletionProvider } from '../autocompletion'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { showContextualMenu, IMenuItem } from '../main-process-proxy'
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
} from '../autocompletion'
|
||||
import { CommitIdentity } from '../../models/commit-identity'
|
||||
import { ICommitMessage } from '../../lib/app-state'
|
||||
import { Dispatcher, IGitHubUser } from '../../lib/dispatcher'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import { IGitHubUser } from '../../lib/dispatcher'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { Button } from '../lib/button'
|
||||
import { Avatar } from '../lib/avatar'
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react'
|
|||
|
||||
import { ChangesList } from './changes-list'
|
||||
import { DiffSelectionType } from '../../models/diff'
|
||||
import { ICommitMessage, IChangesState, PopupType } from '../../lib/app-state'
|
||||
import { IChangesState, PopupType } from '../../lib/app-state'
|
||||
import { Repository } from '../../models/repository'
|
||||
import {
|
||||
Dispatcher,
|
||||
|
@ -20,6 +20,7 @@ import {
|
|||
IssuesAutocompletionProvider,
|
||||
UserAutocompletionProvider,
|
||||
} from '../autocompletion'
|
||||
import { ICommitMessage } from '../../lib/app-state'
|
||||
import { ClickSource } from '../list'
|
||||
import { WorkingDirectoryFileChange } from '../../models/status'
|
||||
import { CSSTransitionGroup } from 'react-transition-group'
|
||||
|
|
|
@ -192,6 +192,7 @@ export class Dialog extends React.Component<IDialogProps, IDialogState> {
|
|||
public componentDidMount() {
|
||||
// This cast to any is necessary since React doesn't know about the
|
||||
// dialog element yet.
|
||||
// tslint:disable-next-line:whitespace
|
||||
;(this.dialogElement as any).showModal()
|
||||
|
||||
this.setState({ isAppearing: true })
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import * as React from 'react'
|
||||
import * as CodeMirror from 'codemirror'
|
||||
|
||||
// Required for us to be able to customize the foreground color of selected text
|
||||
import 'codemirror/addon/selection/mark-selection'
|
||||
|
||||
if (__DARWIN__) {
|
||||
// This has to be required to support the `simple` scrollbar style.
|
||||
require('codemirror/addon/scroll/simplescrollbars')
|
||||
}
|
||||
|
||||
// Required for us to be able to customize the foreground color of selected text
|
||||
require('codemirror/addon/selection/mark-selection')
|
||||
|
||||
interface ICodeMirrorHostProps {
|
||||
/**
|
||||
* An optional class name for the wrapper element around the
|
||||
|
|
|
@ -510,7 +510,7 @@ export class Diff extends React.Component<IDiffProps, {}> {
|
|||
//
|
||||
// The only way to unsubscribe is to pass the exact same function given to the
|
||||
// 'on' function to the 'off' so we need a reference to ourselves, basically.
|
||||
let deleteHandler: () => void // eslint-disable-line prefer-const
|
||||
let deleteHandler: () => void
|
||||
|
||||
// Since we manually render a react component we have to take care of unmounting
|
||||
// it or else we'll leak memory. This disposable will unmount the component.
|
||||
|
|
|
@ -5,12 +5,13 @@ import { FileList } from './file-list'
|
|||
import { Repository } from '../../models/repository'
|
||||
import { FileChange } from '../../models/status'
|
||||
import { Commit } from '../../models/commit'
|
||||
import { Dispatcher, IGitHubUser } from '../../lib/dispatcher'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import {
|
||||
IHistoryState as IAppHistoryState,
|
||||
ImageDiffType,
|
||||
} from '../../lib/app-state'
|
||||
import { ThrottledScheduler } from '../lib/throttled-scheduler'
|
||||
import { IGitHubUser } from '../../lib/dispatcher'
|
||||
import { Resizable } from '../resizable'
|
||||
|
||||
// At some point we'll make index.tsx only be exports
|
||||
|
|
|
@ -81,7 +81,7 @@ export class Checkbox extends React.Component<ICheckboxProps, ICheckboxState> {
|
|||
const label = this.props.label
|
||||
const inputId = this.state.inputId
|
||||
|
||||
return label ? <label htmlFor={inputId}>{label}</label> : null
|
||||
return !!label ? <label htmlFor={inputId}>{label}</label> : null
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -47,7 +47,7 @@ export class Select extends React.Component<ISelectProps, ISelectState> {
|
|||
const label = this.props.label
|
||||
const inputId = this.state.inputId
|
||||
|
||||
return label ? <label htmlFor={inputId}>{label}</label> : null
|
||||
return !!label ? <label htmlFor={inputId}>{label}</label> : null
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -177,7 +177,7 @@ class UpdateStore {
|
|||
public quitAndInstallUpdate() {
|
||||
// This is synchronous so that we can ensure the app will let itself be quit
|
||||
// before we call the function to quit.
|
||||
// eslint-disable-next-line no-sync
|
||||
// tslint:disable-next-line:no-sync-functions
|
||||
sendWillQuitSync()
|
||||
autoUpdater.quitAndInstall()
|
||||
}
|
||||
|
|
|
@ -478,12 +478,14 @@ export class List extends React.Component<IListProps, IListState> {
|
|||
className={className}
|
||||
tabIndex={tabIndex}
|
||||
ref={ref}
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
// tslint:disable-next-line jsx-no-lambda
|
||||
onMouseOver={e => this.onRowMouseOver(rowIndex, e)}
|
||||
// tslint:disable-next-line jsx-no-lambda
|
||||
onMouseDown={e => this.handleMouseDown(rowIndex, e)}
|
||||
// tslint:disable-next-line jsx-no-lambda
|
||||
onClick={e => this.onRowClick(rowIndex, e)}
|
||||
// tslint:disable-next-line jsx-no-lambda
|
||||
onKeyDown={e => this.handleRowKeyDown(rowIndex, e)}
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
style={style}
|
||||
>
|
||||
{element}
|
||||
|
|
|
@ -39,7 +39,7 @@ export function showCertificateTrustDialog(
|
|||
* that would tell the app to quit.
|
||||
*/
|
||||
export function sendWillQuitSync() {
|
||||
// eslint-disable-next-line no-sync
|
||||
// tslint:disable-next-line:no-sync-functions
|
||||
ipcRenderer.sendSync('will-quit')
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,38 @@
|
|||
import * as React from 'react'
|
||||
|
||||
export interface IResizableProps extends React.Props<Resizable> {
|
||||
readonly width: number
|
||||
|
||||
/** The maximum width the panel can be resized to.
|
||||
*
|
||||
* @default 350
|
||||
*/
|
||||
readonly maximumWidth?: number
|
||||
|
||||
/**
|
||||
* The minimum width the panel can be resized to.
|
||||
*
|
||||
* @default 150
|
||||
*/
|
||||
readonly minimumWidth?: number
|
||||
|
||||
/** The optional ID for the root element. */
|
||||
readonly id?: string
|
||||
|
||||
/**
|
||||
* Handler called when the width of the component has changed
|
||||
* through an explicit resize event (dragging the handle).
|
||||
*/
|
||||
readonly onResize?: (newWidth: number) => void
|
||||
|
||||
/**
|
||||
* Handler called when the resizable component has been
|
||||
* reset (ie restored to its original width by double clicking
|
||||
* on the resize handle).
|
||||
*/
|
||||
readonly onReset?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Component abstracting a resizable panel.
|
||||
*
|
||||
|
@ -106,36 +139,3 @@ export class Resizable extends React.Component<IResizableProps, {}> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
export interface IResizableProps extends React.Props<Resizable> {
|
||||
readonly width: number
|
||||
|
||||
/** The maximum width the panel can be resized to.
|
||||
*
|
||||
* @default 350
|
||||
*/
|
||||
readonly maximumWidth?: number
|
||||
|
||||
/**
|
||||
* The minimum width the panel can be resized to.
|
||||
*
|
||||
* @default 150
|
||||
*/
|
||||
readonly minimumWidth?: number
|
||||
|
||||
/** The optional ID for the root element. */
|
||||
readonly id?: string
|
||||
|
||||
/**
|
||||
* Handler called when the width of the component has changed
|
||||
* through an explicit resize event (dragging the handle).
|
||||
*/
|
||||
readonly onResize?: (newWidth: number) => void
|
||||
|
||||
/**
|
||||
* Handler called when the resizable component has been
|
||||
* reset (ie restored to its original width by double clicking
|
||||
* on the resize handle).
|
||||
*/
|
||||
readonly onReset?: () => void
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import * as React from 'react'
|
||||
import { ToolbarButton, ToolbarButtonStyle } from './button'
|
||||
import { IAheadBehind, Progress } from '../../lib/app-state'
|
||||
import { ToolbarButton } from './button'
|
||||
import { ToolbarButtonStyle } from './button'
|
||||
import { IAheadBehind } from '../../lib/app-state'
|
||||
import { Dispatcher } from '../../lib/dispatcher'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { RelativeTime } from '../relative-time'
|
||||
import { Progress } from '../../lib/app-state'
|
||||
|
||||
interface IPushPullButtonProps {
|
||||
/**
|
||||
|
|
|
@ -54,11 +54,9 @@ export class TitleBar extends React.Component<ITitleBarProps, ITitleBarState> {
|
|||
|
||||
switch (actionOnDoubleClick) {
|
||||
case 'Maximize':
|
||||
if (mainWindow.isMaximized()) {
|
||||
mainWindow.unmaximize()
|
||||
} else {
|
||||
mainWindow.maximize()
|
||||
}
|
||||
mainWindow.isMaximized()
|
||||
? mainWindow.unmaximize()
|
||||
: mainWindow.maximize()
|
||||
break
|
||||
case 'Minimize':
|
||||
mainWindow.minimize()
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
rules:
|
||||
# throws with Chai
|
||||
no-unused-expressions: off
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as Path from 'path'
|
||||
import * as FSE from 'fs-extra'
|
||||
|
|
|
@ -16,9 +16,9 @@ g['__RELEASE_CHANNEL__'] = 'development'
|
|||
g['__UPDATES_URL__'] = ''
|
||||
g['__SHA__'] = 'test'
|
||||
|
||||
g['log'] = {
|
||||
g['log'] = <IDesktopLogger>{
|
||||
error: () => {},
|
||||
warn: () => {},
|
||||
info: () => {},
|
||||
debug: () => {},
|
||||
} as IDesktopLogger
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { IAppShell } from '../src/lib/dispatcher/app-shell'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as chai from 'chai'
|
||||
const expect = chai.expect
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as chai from 'chai'
|
||||
const expect = chai.expect
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as path from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { expect } from 'chai'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { GitError } from 'dugite'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as chai from 'chai'
|
||||
const expect = chai.expect
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { expect } from 'chai'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { expect } from 'chai'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import { expect } from 'chai'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as path from 'path'
|
||||
import * as Fs from 'fs'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as path from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as chai from 'chai'
|
||||
const expect = chai.expect
|
||||
|
|
|
@ -67,57 +67,59 @@ describe('GitProgressParser', () => {
|
|||
|
||||
expect(result.kind).to.equal('context')
|
||||
})
|
||||
})
|
||||
|
||||
describe('progress parser', () => {
|
||||
it('parses progress with no total', () => {
|
||||
const result = parse('remote: Counting objects: 167587')
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'remote: Counting objects',
|
||||
text: 'remote: Counting objects: 167587',
|
||||
value: 167587,
|
||||
done: false,
|
||||
percent: undefined,
|
||||
total: undefined,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it('parses final progress with no total', () => {
|
||||
const result = parse('remote: Counting objects: 167587, done.')
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'remote: Counting objects',
|
||||
text: 'remote: Counting objects: 167587, done.',
|
||||
value: 167587,
|
||||
done: true,
|
||||
percent: undefined,
|
||||
total: undefined,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it('parses progress with total', () => {
|
||||
const result = parse('remote: Compressing objects: 72% (16/22)')
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'remote: Compressing objects',
|
||||
text: 'remote: Compressing objects: 72% (16/22)',
|
||||
value: 16,
|
||||
done: false,
|
||||
percent: 72,
|
||||
total: 22,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it('parses final with total', () => {
|
||||
const result = parse('remote: Compressing objects: 100% (22/22), done.')
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'remote: Compressing objects',
|
||||
text: 'remote: Compressing objects: 100% (22/22), done.',
|
||||
value: 22,
|
||||
done: true,
|
||||
percent: 100,
|
||||
total: 22,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it('parses with total and throughput', () => {
|
||||
|
@ -125,14 +127,14 @@ describe('GitProgressParser', () => {
|
|||
'Receiving objects: 99% (166741/167587), 267.24 MiB | 2.40 MiB/s'
|
||||
)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'Receiving objects',
|
||||
text: 'Receiving objects: 99% (166741/167587), 267.24 MiB | 2.40 MiB/s',
|
||||
value: 166741,
|
||||
done: false,
|
||||
percent: 99,
|
||||
total: 167587,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it('parses final with total and throughput', () => {
|
||||
|
@ -140,7 +142,7 @@ describe('GitProgressParser', () => {
|
|||
'Receiving objects: 100% (167587/167587), 279.67 MiB | 2.43 MiB/s, done.'
|
||||
)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
expect(result).to.deep.equal(<IGitProgressInfo>{
|
||||
title: 'Receiving objects',
|
||||
text:
|
||||
'Receiving objects: 100% (167587/167587), 279.67 MiB | 2.43 MiB/s, done.',
|
||||
|
@ -148,7 +150,7 @@ describe('GitProgressParser', () => {
|
|||
done: true,
|
||||
percent: 100,
|
||||
total: 167587,
|
||||
} as IGitProgressInfo)
|
||||
})
|
||||
})
|
||||
|
||||
it("does not parse things that aren't progress", () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-sync */
|
||||
/* tslint:disable:no-sync-functions */
|
||||
|
||||
import * as chai from 'chai'
|
||||
const expect = chai.expect
|
||||
|
|
10
app/typings/textarea-caret/index.d.ts
vendored
10
app/typings/textarea-caret/index.d.ts
vendored
|
@ -1,10 +0,0 @@
|
|||
declare module 'textarea-caret' {
|
||||
interface ICaret {
|
||||
top: number
|
||||
left: number
|
||||
height: number
|
||||
}
|
||||
|
||||
function getCaretCoordinates(element: HTMLElement, position: number): ICaret
|
||||
export = getCaretCoordinates
|
||||
}
|
4
app/typings/untildify/index.d.ts
vendored
4
app/typings/untildify/index.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
declare module 'untildify' {
|
||||
function untildify(path: string): string
|
||||
export = untildify
|
||||
}
|
|
@ -7,7 +7,6 @@ environment:
|
|||
nodejs_version: "7"
|
||||
|
||||
cache:
|
||||
- .eslintcache
|
||||
- node_modules
|
||||
- '%USERPROFILE%\.electron'
|
||||
|
||||
|
@ -31,6 +30,7 @@ install:
|
|||
- npm ls --dev
|
||||
|
||||
build_script:
|
||||
- npm run check-prettiness
|
||||
- npm run lint
|
||||
- npm run build:prod
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ machine:
|
|||
|
||||
dependencies:
|
||||
cache_directories:
|
||||
- ".eslintcache"
|
||||
- "node_modules"
|
||||
- "~/.electron"
|
||||
|
||||
|
@ -31,6 +30,7 @@ dependencies:
|
|||
|
||||
compile:
|
||||
override:
|
||||
- npm run check-prettiness
|
||||
- npm run lint
|
||||
- npm run build:prod
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// strings from https://github.com/Microsoft/tslint-microsoft-contrib/blob/b720cd9/src/insecureRandomRule.ts
|
||||
|
||||
const MATH_FAIL_STRING =
|
||||
'Math.random produces insecure random numbers. ' +
|
||||
'Use crypto.randomBytes() or window.crypto.getRandomValues() instead'
|
||||
|
||||
const NODE_FAIL_STRING =
|
||||
'crypto.pseudoRandomBytes produces insecure random numbers. ' +
|
||||
'Use crypto.randomBytes() instead'
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Do not use insecure sources for random bytes',
|
||||
category: 'Best Practices',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const { callee } = node
|
||||
const isMemberExpression = callee.type === 'MemberExpression'
|
||||
if (
|
||||
isMemberExpression &&
|
||||
callee.object.name === 'Math' &&
|
||||
callee.property.name === 'random'
|
||||
) {
|
||||
context.report(node, MATH_FAIL_STRING)
|
||||
}
|
||||
if (
|
||||
(isMemberExpression &&
|
||||
callee.property.name === 'pseudoRandomBytes') ||
|
||||
callee.name === 'pseudoRandomBytes'
|
||||
) {
|
||||
context.report(node, NODE_FAIL_STRING)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
19
package.json
19
package.json
|
@ -21,15 +21,14 @@
|
|||
"package": "node script/package",
|
||||
"clean:tslint": "rimraf tslint-rules/*.js",
|
||||
"compile:tslint": "tsc -p tslint-rules",
|
||||
"lint": "npm run compile:tslint && npm run tslint && npm run eslint-check && npm run eslint",
|
||||
"tslint": "tslint ./tslint-rules/*.ts ./app/{src,typings,test}/**/*.{ts,tsx}",
|
||||
"eslint": "ts-node script/eslint.ts",
|
||||
"eslint:fix": "ts-node script/eslint.ts --fix",
|
||||
"eslint-check": "eslint --print-config .eslintrc.* | eslint-config-prettier-check",
|
||||
"lint": "npm run compile:tslint && tslint \"./app/{src,test}/**/*.{ts,tsx}\"",
|
||||
"check-prettiness": "node script/is-it-pretty",
|
||||
"publish": "node script/publish",
|
||||
"clean-slate": "rimraf out node_modules app/node_modules && npm install",
|
||||
"rebuild-hard:dev": "npm run clean-slate && npm run build:dev",
|
||||
"rebuild-hard:prod": "npm run clean-slate && npm run build:prod"
|
||||
"rebuild-hard:prod": "npm run clean-slate && npm run build:prod",
|
||||
"prettier:base": "prettier --single-quote --trailing-comma es5 --no-semi --write",
|
||||
"prettify": "npm run prettier:base \"{app/{src,test}/**/*.{ts,tsx,js},app/webpack.*.js,script/!(*.ps1|*.bat|setup-macos-keychain)}\""
|
||||
},
|
||||
"author": {
|
||||
"name": "GitHub, Inc.",
|
||||
|
@ -57,12 +56,6 @@
|
|||
"electron-mocha": "^4.0.0",
|
||||
"electron-packager": "^8.7.2",
|
||||
"electron-winstaller": "2.5.2",
|
||||
"eslint": "^4.3.0",
|
||||
"eslint-config-prettier": "^2.3.0",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
"eslint-plugin-prettier": "^2.1.2",
|
||||
"eslint-plugin-react": "^7.1.0",
|
||||
"eslint-plugin-typescript": "^0.4.0",
|
||||
"express": "^4.15.0",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"fs-extra": "^2.1.2",
|
||||
|
@ -85,7 +78,6 @@
|
|||
"tslint": "^4.5.1",
|
||||
"tslint-microsoft-contrib": "^4.0.1",
|
||||
"typescript": "2.5.2",
|
||||
"typescript-eslint-parser": "^8.0.0",
|
||||
"vrsource-tslint-rules": "^0.12.0",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-dev-middleware": "^1.12.0",
|
||||
|
@ -114,7 +106,6 @@
|
|||
"@types/ua-parser-js": "^0.7.30",
|
||||
"@types/uuid": "^3.4.0",
|
||||
"@types/winston": "^2.2.0",
|
||||
"eslint_d": "^5.1.0",
|
||||
"prettier": "~1.7.0",
|
||||
"tslint-config-prettier": "^1.1.0",
|
||||
"tslint-react": "~3.0.0"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
rules:
|
||||
unicorn/no-process-exit: off
|
||||
import/no-commonjs: off
|
||||
no-sync: off
|
|
@ -8,7 +8,6 @@ import * as packager from 'electron-packager'
|
|||
const legalEagle: LegalEagle = require('legal-eagle')
|
||||
|
||||
const distInfo = require('./dist-info')
|
||||
|
||||
const getReleaseChannel: () => string = distInfo.getReleaseChannel
|
||||
const getVersion: () => string = distInfo.getVersion
|
||||
const getExecutableName: () => string = distInfo.getExecutableName
|
||||
|
@ -48,6 +47,7 @@ updateLicenseDump(err => {
|
|||
|
||||
if (isPublishableBuild) {
|
||||
process.exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,14 +172,12 @@ function copyStaticResources() {
|
|||
}
|
||||
|
||||
function copyDependencies() {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const originalPackage: Package = require(path.join(
|
||||
projectRoot,
|
||||
'app',
|
||||
'package.json'
|
||||
))
|
||||
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const commonConfig = require(path.resolve(__dirname, '../app/webpack.common'))
|
||||
const externals = commonConfig.externals
|
||||
const oldDependencies = originalPackage.dependencies
|
||||
|
|
|
@ -5,7 +5,6 @@ const os = require('os')
|
|||
const fs = require('fs')
|
||||
|
||||
const projectRoot = path.join(__dirname, '..')
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const appPackage = require(path.join(projectRoot, 'app', 'package.json'))
|
||||
|
||||
function getDistPath() {
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env ts-node
|
||||
|
||||
interface IClient {
|
||||
// there’s other stuff; I’m not including it
|
||||
// because it’s not needed here.
|
||||
// If you want to add to it, here’s the source:
|
||||
// https://github.com/mantoni/eslint_d.js/blob/v5.1.0/lib/client.js
|
||||
lint(args: string[], text?: string): void
|
||||
}
|
||||
|
||||
const client: IClient = require('eslint_d/lib/client')
|
||||
|
||||
const ESLINT_ARGS = [
|
||||
'--cache',
|
||||
'--rulesdir=./eslint-rules',
|
||||
'./{script,eslint-rules}/**/*.{j,t}s?(x)',
|
||||
'./tslint-rules/**/*.ts',
|
||||
'./app/{src,typings,test}/**/*.{j,t}s?(x)',
|
||||
...process.argv.slice(2),
|
||||
]
|
||||
|
||||
client.lint(ESLINT_ARGS)
|
||||
|
||||
type ProcessOnExit = (cb: (code: number) => void) => void
|
||||
const onExit = process.on.bind(process, 'exit') as ProcessOnExit
|
||||
|
||||
onExit(code => {
|
||||
if (code && ESLINT_ARGS.indexOf('--fix') === -1) {
|
||||
console.error(
|
||||
'\x1b[1m\x1b[32m→ To fix some of these errors, run \x1b[4mnpm run eslint:fix\x1b[24\x1b[39m\x1b[22m\n'
|
||||
)
|
||||
}
|
||||
})
|
47
script/is-it-pretty.js
Normal file
47
script/is-it-pretty.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
'use strict'
|
||||
|
||||
const prettier = require('prettier')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs')
|
||||
const globPattern =
|
||||
'{app/{src,test}/**/*.{ts,tsx,js},app/webpack.*.js,script/!(*.ps1|*.bat|setup-macos-keychain)}'
|
||||
const prettierOptions = {
|
||||
parser: 'typescript',
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
semi: false,
|
||||
printWidth: 80,
|
||||
}
|
||||
|
||||
glob(globPattern, (err, matches) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const uglyFiles = []
|
||||
const matchCount = matches.length
|
||||
|
||||
for (const match of matches) {
|
||||
const fileContents = fs.readFileSync(match, 'utf8')
|
||||
const isPretty = prettier.check(fileContents, prettierOptions)
|
||||
|
||||
if (!isPretty) {
|
||||
uglyFiles.push(match)
|
||||
}
|
||||
}
|
||||
|
||||
if (uglyFiles.length === 0) {
|
||||
console.log('This is some pretty code')
|
||||
} else {
|
||||
console.log(
|
||||
`${uglyFiles.length} out of ${matchCount} code files are ugly. Please run 'npm run prettify' to make the following files pretty:`
|
||||
)
|
||||
|
||||
for (const file of uglyFiles) {
|
||||
console.log(`\t${file}`)
|
||||
}
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
const distInfo = require('./dist-info')
|
||||
|
||||
const getUserDataPath: () => string = distInfo.getUserDataPath
|
||||
|
||||
export function getLogFiles(): ReadonlyArray<string> {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import * as fs from 'fs'
|
||||
import * as cp from 'child_process'
|
||||
import { getLogFiles } from './review-logs'
|
||||
|
||||
const distInfo = require('./dist-info')
|
||||
|
||||
const getDistPath: () => string = distInfo.getDistPath
|
||||
|
|
|
@ -26,7 +26,18 @@
|
|||
import * as ts from 'typescript'
|
||||
import * as Lint from 'tslint'
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new ButtonGroupOrderWalker(sourceFile, this.getOptions()))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ButtonGroupOrderWalker extends Lint.RuleWalker {
|
||||
|
||||
/**
|
||||
* Visit the node and ensure any button children are in the correct order.
|
||||
*/
|
||||
|
@ -82,34 +93,26 @@ class ButtonGroupOrderWalker extends Lint.RuleWalker {
|
|||
}
|
||||
|
||||
const buttonsWithTypeAttr = buttons.map(b => {
|
||||
const typeAttr = b.attributes.properties.find(
|
||||
a =>
|
||||
a.kind === ts.SyntaxKind.JsxAttribute && a.name.getText() === 'type'
|
||||
const typeAttr = b.attributes.properties.find(a =>
|
||||
a.kind === ts.SyntaxKind.JsxAttribute && a.name.getText() === 'type'
|
||||
) as ts.JsxAttribute | undefined
|
||||
|
||||
let value = undefined
|
||||
|
||||
if (
|
||||
typeAttr &&
|
||||
typeAttr.initializer &&
|
||||
typeAttr.initializer.kind === ts.SyntaxKind.StringLiteral
|
||||
) {
|
||||
if (typeAttr && typeAttr.initializer && typeAttr.initializer.kind === ts.SyntaxKind.StringLiteral) {
|
||||
value = typeAttr.initializer.text
|
||||
}
|
||||
|
||||
return [b, value]
|
||||
return [ b, value ]
|
||||
})
|
||||
|
||||
const primaryButtonIx = buttonsWithTypeAttr.findIndex(
|
||||
x => x[1] === 'submit'
|
||||
)
|
||||
const primaryButtonIx = buttonsWithTypeAttr.findIndex(x => x[1] === 'submit')
|
||||
|
||||
if (primaryButtonIx !== -1 && primaryButtonIx !== 0) {
|
||||
const start = node.getStart()
|
||||
const width = node.getWidth()
|
||||
const error = `Wrong button order in ButtonGroup.`
|
||||
const explanation =
|
||||
'ButtonGroups should have the primary button as its first child'
|
||||
const explanation = 'ButtonGroups should have the primary button as its first child'
|
||||
|
||||
const message = `${error} ${explanation}`
|
||||
|
||||
|
@ -117,15 +120,3 @@ class ButtonGroupOrderWalker extends Lint.RuleWalker {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(
|
||||
new ButtonGroupOrderWalker(sourceFile, this.getOptions())
|
||||
)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
39
tslint-rules/noSyncFunctionsRule.ts
Normal file
39
tslint-rules/noSyncFunctionsRule.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* no-sync-functions
|
||||
*
|
||||
* Don't allow calling any functions that end in 'Sync'.
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript'
|
||||
import * as Lint from 'tslint'
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new NoSyncFunctionsWalker(sourceFile, this.getOptions()))
|
||||
}
|
||||
}
|
||||
|
||||
// The walker takes care of all the work.
|
||||
class NoSyncFunctionsWalker extends Lint.RuleWalker {
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
const functionName = this.getFunctionName(node)
|
||||
if (functionName && functionName.endsWith('Sync')) {
|
||||
const start = node.getStart()
|
||||
const width = node.getWidth()
|
||||
const error = `Synchronous functions shouldn't be used. Find an asynchronous alternative.`
|
||||
this.addFailure(this.createFailure(start, width, error))
|
||||
} else {
|
||||
super.visitCallExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from https://github.com/Microsoft/tslint-microsoft-contrib/blob/051abda5bafffd8068c42bdc9da7afc488cfab76/src/utils/AstUtils.ts#L16-L23.
|
||||
private getFunctionName(node: ts.CallExpression): string {
|
||||
const expression: ts.Expression = node.expression
|
||||
let functionName: string = (expression as any).text
|
||||
if (functionName === undefined && (expression as any).name) {
|
||||
functionName = (expression as any).name.text
|
||||
}
|
||||
return functionName
|
||||
}
|
||||
}
|
|
@ -22,8 +22,19 @@
|
|||
import * as ts from 'typescript'
|
||||
import * as Lint from 'tslint'
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new ReactNoUnboundDispatcherPropsWalker(sourceFile, this.getOptions()))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The walker takes care of all the work.
|
||||
class ReactNoUnboundDispatcherPropsWalker extends Lint.RuleWalker {
|
||||
|
||||
protected visitJsxElement(node: ts.JsxElement): void {
|
||||
this.visitJsxOpeningLikeElement(node.openingElement)
|
||||
super.visitJsxElement(node)
|
||||
|
@ -42,49 +53,40 @@ class ReactNoUnboundDispatcherPropsWalker extends Lint.RuleWalker {
|
|||
private visitJsxOpeningLikeElement(node: ts.JsxOpeningLikeElement): void {
|
||||
// create violations if the listener is a reference to a class method that was not bound to 'this' in the constructor
|
||||
node.attributes.properties.forEach(attributeLikeElement => {
|
||||
if (attributeLikeElement.kind !== ts.SyntaxKind.JsxAttribute) {
|
||||
return
|
||||
}
|
||||
|
||||
if (attributeLikeElement.kind !== ts.SyntaxKind.JsxAttribute) { return }
|
||||
|
||||
// This is some weak sauce, why doesn't JsxAttribute specify a literal kind
|
||||
// so that it can be narrowed automatically?
|
||||
const attribute: ts.JsxAttribute = attributeLikeElement
|
||||
const attribute: ts.JsxAttribute = <ts.JsxAttribute>attributeLikeElement
|
||||
|
||||
// This means that the attribute is an inferred boolean true value. See:
|
||||
//
|
||||
// https://github.com/Microsoft/TypeScript/blob/52ec508/src/compiler/types.ts#L1483
|
||||
// https://facebook.github.io/react/docs/jsx-in-depth.html#props-default-to-true
|
||||
if (!attribute.initializer) {
|
||||
return
|
||||
}
|
||||
if (!attribute.initializer) { return }
|
||||
|
||||
// This likely means that the attribute is a string literal
|
||||
// ie <foo className='foo' />
|
||||
if (attribute.initializer.kind !== ts.SyntaxKind.JsxExpression) {
|
||||
return
|
||||
}
|
||||
if (attribute.initializer.kind !== ts.SyntaxKind.JsxExpression) { return }
|
||||
|
||||
const jsxExpression: ts.JsxExpression = attribute.initializer
|
||||
|
||||
// We only care about property accesses, direct method invocation on
|
||||
// dispatcher is still okay. This excludes things like
|
||||
// <A foo={1} />, <B foo={this.method()} />, <C foo={{ foo: 'bar' }} etc.
|
||||
if (
|
||||
!jsxExpression.expression ||
|
||||
jsxExpression.expression.kind !== ts.SyntaxKind.PropertyAccessExpression
|
||||
) {
|
||||
if (!jsxExpression.expression || jsxExpression.expression.kind !== ts.SyntaxKind.PropertyAccessExpression) {
|
||||
return
|
||||
}
|
||||
|
||||
const propAccess: ts.PropertyAccessExpression = jsxExpression.expression as ts.PropertyAccessExpression
|
||||
const propAccess: ts.PropertyAccessExpression = <ts.PropertyAccessExpression>jsxExpression.expression
|
||||
const propAccessText = propAccess.getText()
|
||||
|
||||
if (/^this\.props\.dispatcher\./.test(propAccessText)) {
|
||||
const start = propAccess.getStart()
|
||||
const width = propAccess.getWidth()
|
||||
const error = `Use of unbound dispatcher method: ${propAccessText}.`
|
||||
const explanation =
|
||||
'Consider extracting the method call to a bound instance method.'
|
||||
const explanation = 'Consider extracting the method call to a bound instance method.'
|
||||
|
||||
const message = `${error} ${explanation}`
|
||||
|
||||
|
@ -93,15 +95,3 @@ class ReactNoUnboundDispatcherPropsWalker extends Lint.RuleWalker {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(
|
||||
new ReactNoUnboundDispatcherPropsWalker(sourceFile, this.getOptions())
|
||||
)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,17 +15,25 @@ interface IExpectedParameter {
|
|||
readonly type: string
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new ReactProperLifecycleMethodsWalker(sourceFile, this.getOptions()))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
||||
|
||||
private propsTypeName: string
|
||||
private stateTypeName: string
|
||||
|
||||
protected visitClassDeclaration(node: ts.ClassDeclaration): void {
|
||||
if (node.heritageClauses && node.heritageClauses.length) {
|
||||
for (const heritageClause of node.heritageClauses) {
|
||||
if (
|
||||
heritageClause.token === ts.SyntaxKind.ExtendsKeyword &&
|
||||
heritageClause.types
|
||||
) {
|
||||
if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword && heritageClause.types) {
|
||||
for (const type of heritageClause.types) {
|
||||
const inheritedName = type.expression.getText()
|
||||
|
||||
|
@ -46,10 +54,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
|
||||
protected visitMethodDeclaration(node: ts.MethodDeclaration): void {
|
||||
const methodName = node.name.getText()
|
||||
if (
|
||||
methodName.startsWith('component') ||
|
||||
methodName.startsWith('shouldComponent')
|
||||
) {
|
||||
if (methodName.startsWith('component') || methodName.startsWith('shouldComponent')) {
|
||||
switch (methodName) {
|
||||
case 'componentWillMount':
|
||||
case 'componentDidMount':
|
||||
|
@ -81,10 +86,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
}
|
||||
}
|
||||
|
||||
private verifyParameter(
|
||||
node: ts.ParameterDeclaration,
|
||||
expectedParameter: IExpectedParameter
|
||||
): boolean {
|
||||
private verifyParameter(node: ts.ParameterDeclaration, expectedParameter: IExpectedParameter): boolean {
|
||||
const parameterName = node.name.getText()
|
||||
|
||||
const parameterStart = node.getStart()
|
||||
|
@ -92,9 +94,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
|
||||
if (parameterName !== expectedParameter.name) {
|
||||
const message = `parameter should be named ${expectedParameter.name}.`
|
||||
this.addFailure(
|
||||
this.createFailure(parameterStart, parameterWidth, message)
|
||||
)
|
||||
this.addFailure(this.createFailure(parameterStart, parameterWidth, message))
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -102,19 +102,14 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
|
||||
if (parameterTypeName !== expectedParameter.type) {
|
||||
const message = `parameter should be of type ${expectedParameter.type}.`
|
||||
this.addFailure(
|
||||
this.createFailure(parameterStart, parameterWidth, message)
|
||||
)
|
||||
this.addFailure(this.createFailure(parameterStart, parameterWidth, message))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private verifyParameters(
|
||||
node: ts.MethodDeclaration,
|
||||
expectedParameters: ReadonlyArray<IExpectedParameter>
|
||||
): boolean {
|
||||
private verifyParameters(node: ts.MethodDeclaration, expectedParameters: ReadonlyArray<IExpectedParameter>): boolean {
|
||||
// It's okay to omit parameters
|
||||
for (let i = 0; i < node.parameters.length; i++) {
|
||||
const parameter = node.parameters[i]
|
||||
|
@ -125,9 +120,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
const parameterWidth = parameter.getWidth()
|
||||
const message = `unknown parameter ${parameterName}`
|
||||
|
||||
this.addFailure(
|
||||
this.createFailure(parameterStart, parameterWidth, message)
|
||||
)
|
||||
this.addFailure(this.createFailure(parameterStart, parameterWidth, message))
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -139,9 +132,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
// Remove trailing unused void parameters
|
||||
for (let i = node.parameters.length - 1; i >= 0; i--) {
|
||||
const parameter = node.parameters[i]
|
||||
const parameterTypeName = parameter.type
|
||||
? parameter.type.getText()
|
||||
: undefined
|
||||
const parameterTypeName = parameter.type ? parameter.type.getText() : undefined
|
||||
|
||||
if (parameterTypeName === 'void') {
|
||||
const parameterName = parameter.getText()
|
||||
|
@ -149,9 +140,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
const parameterWidth = parameter.getWidth()
|
||||
const message = `remove unused void parameter ${parameterName}.`
|
||||
|
||||
this.addFailure(
|
||||
this.createFailure(parameterStart, parameterWidth, message)
|
||||
)
|
||||
this.addFailure(this.createFailure(parameterStart, parameterWidth, message))
|
||||
return false
|
||||
} else {
|
||||
break
|
||||
|
@ -162,9 +151,7 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
}
|
||||
|
||||
private verifyComponentWillReceiveProps(node: ts.MethodDeclaration) {
|
||||
this.verifyParameters(node, [
|
||||
{ name: 'nextProps', type: this.propsTypeName },
|
||||
])
|
||||
this.verifyParameters(node, [ { name: 'nextProps', type: this.propsTypeName } ])
|
||||
}
|
||||
|
||||
private verifyComponentWillUpdate(node: ts.MethodDeclaration) {
|
||||
|
@ -192,21 +179,9 @@ class ReactProperLifecycleMethodsWalker extends Lint.RuleWalker {
|
|||
const start = node.name.getStart()
|
||||
const width = node.name.getWidth()
|
||||
|
||||
const message =
|
||||
'Method names starting with component or shouldComponent ' +
|
||||
const message = 'Method names starting with component or shouldComponent ' +
|
||||
'are prohibited since they can be confused with React lifecycle methods.'
|
||||
|
||||
this.addFailure(this.createFailure(start, width, message))
|
||||
}
|
||||
}
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(
|
||||
new ReactProperLifecycleMethodsWalker(sourceFile, this.getOptions())
|
||||
)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,25 +13,33 @@
|
|||
import * as ts from 'typescript'
|
||||
import * as Lint from 'tslint'
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(new ReactReadonlyPropsAndStateWalker(sourceFile, this.getOptions()))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The walker takes care of all the work.
|
||||
class ReactReadonlyPropsAndStateWalker extends Lint.RuleWalker {
|
||||
protected visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void {
|
||||
if (node.name.text.endsWith('Props')) {
|
||||
this.ensureReadOnly(node.members)
|
||||
}
|
||||
if (node.name.text.endsWith('Props')) {
|
||||
this.ensureReadOnly(node.members)
|
||||
}
|
||||
|
||||
if (node.name.text.endsWith('State')) {
|
||||
this.ensureReadOnly(node.members)
|
||||
}
|
||||
if (node.name.text.endsWith('State')) {
|
||||
this.ensureReadOnly(node.members)
|
||||
}
|
||||
|
||||
super.visitInterfaceDeclaration(node)
|
||||
super.visitInterfaceDeclaration(node)
|
||||
}
|
||||
|
||||
private ensureReadOnly(members: ts.NodeArray<ts.TypeElement>) {
|
||||
members.forEach(member => {
|
||||
if (member.kind !== ts.SyntaxKind.PropertySignature) {
|
||||
return
|
||||
}
|
||||
if (member.kind !== ts.SyntaxKind.PropertySignature) { return }
|
||||
|
||||
const propertySignature = member as ts.PropertySignature
|
||||
|
||||
|
@ -48,9 +56,7 @@ class ReactReadonlyPropsAndStateWalker extends Lint.RuleWalker {
|
|||
private isReadOnly(propertySignature: ts.PropertySignature): boolean {
|
||||
const modifiers = propertySignature.modifiers
|
||||
|
||||
if (!modifiers) {
|
||||
return false
|
||||
}
|
||||
if (!modifiers) { return false }
|
||||
|
||||
if (modifiers.find(m => m.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
||||
return true
|
||||
|
@ -59,15 +65,3 @@ class ReactReadonlyPropsAndStateWalker extends Lint.RuleWalker {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
|
||||
return this.applyWithWalker(
|
||||
new ReactReadonlyPropsAndStateWalker(sourceFile, this.getOptions())
|
||||
)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
tslint.json
62
tslint.json
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"extends": [
|
||||
"tslint-react",
|
||||
"tslint-config-prettier"
|
||||
],
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-react/rules",
|
||||
"node_modules/vrsource-tslint-rules/rules",
|
||||
"node_modules/tslint-microsoft-contrib/",
|
||||
"tslint-rules/"
|
||||
|
@ -11,13 +11,50 @@
|
|||
"rules": {
|
||||
"button-group-order": true,
|
||||
"class-name": true,
|
||||
"curly": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"insecure-random": [ true ],
|
||||
"interface-name": [ true, "always-prefix"],
|
||||
"jsdoc-format": true,
|
||||
"literal-spacing": [
|
||||
false,
|
||||
{
|
||||
"array": ["never"],
|
||||
"object": ["always"]
|
||||
}
|
||||
],
|
||||
"member-access": [
|
||||
true,
|
||||
"check-accessor",
|
||||
"check-constructor"
|
||||
],
|
||||
"member-ordering": [
|
||||
true,
|
||||
"static-before-instance",
|
||||
"variables-before-functions"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-default-export": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": true,
|
||||
"no-internal-module": true,
|
||||
"no-invalid-this": true,
|
||||
"no-stateless-class": true,
|
||||
"no-sync-functions": true,
|
||||
"no-trailing-whitespace": [ true, "ignore-comments", "ignore-jsdoc" ],
|
||||
"no-unused-expression": true,
|
||||
"no-unused-variable": [true, "react"],
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"promise-must-complete": true,
|
||||
"react-no-unbound-dispatcher-props": true,
|
||||
"react-proper-lifecycle-methods": true,
|
||||
|
@ -29,6 +66,21 @@
|
|||
}
|
||||
],
|
||||
"react-this-binding-issue": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"never"
|
||||
],
|
||||
"trailing-comma": [
|
||||
false,
|
||||
{
|
||||
"multiline": "always",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
|
@ -42,6 +94,14 @@
|
|||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue