Skip to content

Commit 24b8c6f

Browse files
authored
typescript classy implementation (#81)
1 parent 183d191 commit 24b8c6f

File tree

13 files changed

+637
-166
lines changed

13 files changed

+637
-166
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
index.d.ts
12
node_modules
23
npm-debug.log

.npmignore

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
# examples
21
examples
3-
4-
# unit tests
52
test
6-
7-
# build info
83
.travis.yml
4+
.prettierrc
5+
src
6+
!index.js
7+
!index.d.ts

.prettierignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.prettierrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
singleQuote: true

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2018 Damon Oehlman <damon.oehlman@gmail.com>
3+
Copyright (c) 2019 Damon Oehlman <damon.oehlman@gmail.com>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,6 @@ a semver compatible format) using a navigator useragent in a browser or
88

99
[![stable](https://img.shields.io/badge/stability-stable-green.svg)](https://github.com/dominictarr/stability#stable) [![Build Status](https://api.travis-ci.org/DamonOehlman/detect-browser.svg?branch=master)](https://travis-ci.org/DamonOehlman/detect-browser) [![Maintainability](https://api.codeclimate.com/v1/badges/84947fce3f3b06da69d0/maintainability)](https://codeclimate.com/github/DamonOehlman/detect-browser/maintainability)
1010

11-
## NOTE: Migrating from 1.x to 2.x or higher
12-
13-
Release 2.0 introduced a breaking API change (hence the major release)
14-
which requires invocation of a `detect` function rather than just inclusion of
15-
the module. PR [#46](https://github.com/DamonOehlman/detect-browser/pull/46)
16-
provides more context as to why this change has been made.
17-
1811
## Example Usage
1912

2013
```js
@@ -29,10 +22,6 @@ if (browser) {
2922
}
3023
```
3124

32-
[![view on requirebin](http://requirebin.com/badge.png)](http://requirebin.com/?gist=DamonOehlman/a96b45bc4614b23671eac8721b781deb)
33-
34-
[![Try detect-browser on RunKit](https://badge.runkitcdn.com/detect-browser.svg)](https://runkit.com/damonoehlman/detect-browser-node-example)
35-
3625
Or you can use a switch statement:
3726

3827
```js
@@ -55,6 +44,20 @@ switch (browser && browser.name) {
5544
}
5645
```
5746

47+
## NOTE: Upgrading to 4.x
48+
49+
If you have been using `3.x` the main thing to be aware of is that the `bot` boolean flag
50+
has been removed from all but the `BotInfo` results (yes, `4.x` introduces some
51+
lightweight classes for identifying the type of result you have received).
52+
53+
In all but a few cases this change will be transparent to most users (given common JS
54+
coding pattern), but it's worth checking the [release](/releases/tag/v4.0.0)
55+
notes to get details on those edge cases.
56+
57+
Additionally, with the source now being written in TypeScript the package
58+
ships with type declarations so you shouldn't need the `@types/detect-browser`
59+
package anymore (thanks to those that put the effort into creating it though).
60+
5861
## Adding additional browser support
5962

6063
The current list of browsers that can be detected by `detect-browser` is
@@ -83,7 +86,7 @@ captures the version number of the browser.
8386

8487
The MIT License (MIT)
8588

86-
Copyright (c) 2018 Damon Oehlman <damon.oehlman@gmail.com>
89+
Copyright (c) 2019 Damon Oehlman <damon.oehlman@gmail.com>
8790

8891
Permission is hereby granted, free of charge, to any person obtaining a copy
8992
of this software and associated documentation files (the "Software"), to deal

index.js

Lines changed: 149 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,150 @@
1-
function detect() {
2-
if (typeof navigator !== 'undefined') {
3-
return parseUserAgent(navigator.userAgent);
4-
}
5-
6-
return getNodeVersion();
7-
}
8-
9-
function detectOS(userAgentString) {
10-
var rules = getOperatingSystemRules();
11-
var detected = rules.filter(function (os) {
12-
return os.rule && os.rule.test(userAgentString);
13-
})[0];
14-
15-
return detected ? detected.name : null;
16-
}
17-
18-
function getNodeVersion() {
19-
var isNode = typeof process !== 'undefined' && process.version;
20-
return isNode && {
21-
name: 'node',
22-
version: process.version.slice(1),
23-
os: process.platform
24-
};
25-
}
26-
27-
function parseUserAgent(userAgentString) {
28-
var browsers = getBrowserRules();
29-
if (!userAgentString) {
30-
return null;
31-
}
32-
33-
var detected = browsers.map(function(browser) {
34-
var match = browser.rule.exec(userAgentString);
35-
var version = match && match[1].split(/[._]/).slice(0,3);
36-
37-
if (version && version.length < 3) {
38-
version = version.concat(version.length == 1 ? [0, 0] : [0]);
1+
(function (factory) {
2+
if (typeof module === "object" && typeof module.exports === "object") {
3+
var v = factory(require, exports);
4+
if (v !== undefined) module.exports = v;
395
}
40-
41-
return match && {
42-
name: browser.name,
43-
version: version.join('.')
44-
};
45-
}).filter(Boolean)[0] || null;
46-
47-
if (detected) {
48-
detected.os = detectOS(userAgentString);
49-
}
50-
51-
if (/alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/i.test(userAgentString)) {
52-
detected = detected || {};
53-
detected.bot = true;
54-
}
55-
56-
return detected;
57-
}
58-
59-
function getBrowserRules() {
60-
return buildRules([
61-
[ 'aol', /AOLShield\/([0-9\._]+)/ ],
62-
[ 'edge', /Edge\/([0-9\._]+)/ ],
63-
[ 'yandexbrowser', /YaBrowser\/([0-9\._]+)/ ],
64-
[ 'vivaldi', /Vivaldi\/([0-9\.]+)/ ],
65-
[ 'kakaotalk', /KAKAOTALK\s([0-9\.]+)/ ],
66-
[ 'samsung', /SamsungBrowser\/([0-9\.]+)/ ],
67-
[ 'chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/ ],
68-
[ 'phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/ ],
69-
[ 'crios', /CriOS\/([0-9\.]+)(:?\s|$)/ ],
70-
[ 'firefox', /Firefox\/([0-9\.]+)(?:\s|$)/ ],
71-
[ 'fxios', /FxiOS\/([0-9\.]+)/ ],
72-
[ 'opera', /Opera\/([0-9\.]+)(?:\s|$)/ ],
73-
[ 'opera', /OPR\/([0-9\.]+)(:?\s|$)$/ ],
74-
[ 'ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/ ],
75-
[ 'ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/ ],
76-
[ 'ie', /MSIE\s(7\.0)/ ],
77-
[ 'bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/ ],
78-
[ 'android', /Android\s([0-9\.]+)/ ],
79-
[ 'ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/ ],
80-
[ 'safari', /Version\/([0-9\._]+).*Safari/ ],
81-
[ 'facebook', /FBAV\/([0-9\.]+)/],
82-
[ 'instagram', /Instagram\s([0-9\.]+)/],
83-
[ 'ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/]
84-
]);
85-
}
86-
87-
function getOperatingSystemRules() {
88-
return buildRules([
89-
[ 'iOS', /iP(hone|od|ad)/ ],
90-
[ 'Android OS', /Android/ ],
91-
[ 'BlackBerry OS', /BlackBerry|BB10/ ],
92-
[ 'Windows Mobile', /IEMobile/ ],
93-
[ 'Amazon OS', /Kindle/ ],
94-
[ 'Windows 3.11', /Win16/ ],
95-
[ 'Windows 95', /(Windows 95)|(Win95)|(Windows_95)/ ],
96-
[ 'Windows 98', /(Windows 98)|(Win98)/ ],
97-
[ 'Windows 2000', /(Windows NT 5.0)|(Windows 2000)/ ],
98-
[ 'Windows XP', /(Windows NT 5.1)|(Windows XP)/ ],
99-
[ 'Windows Server 2003', /(Windows NT 5.2)/ ],
100-
[ 'Windows Vista', /(Windows NT 6.0)/ ],
101-
[ 'Windows 7', /(Windows NT 6.1)/ ],
102-
[ 'Windows 8', /(Windows NT 6.2)/ ],
103-
[ 'Windows 8.1', /(Windows NT 6.3)/ ],
104-
[ 'Windows 10', /(Windows NT 10.0)/ ],
105-
[ 'Windows ME', /Windows ME/ ],
106-
[ 'Open BSD', /OpenBSD/ ],
107-
[ 'Sun OS', /SunOS/ ],
108-
[ 'Linux', /(Linux)|(X11)/ ],
109-
[ 'Mac OS', /(Mac_PowerPC)|(Macintosh)/ ],
110-
[ 'QNX', /QNX/ ],
111-
[ 'BeOS', /BeOS/ ],
112-
[ 'OS/2', /OS\/2/ ],
113-
[ 'Search Bot', /(nuhk)|(Googlebot)|(Yammybot)|(Openbot)|(Slurp)|(MSNBot)|(Ask Jeeves\/Teoma)|(ia_archiver)/ ]
114-
]);
115-
}
116-
117-
function buildRules(ruleTuples) {
118-
return ruleTuples.map(function(tuple) {
119-
return {
120-
name: tuple[0],
121-
rule: tuple[1]
122-
};
123-
});
124-
}
125-
126-
module.exports = {
127-
detect: detect,
128-
detectOS: detectOS,
129-
getNodeVersion: getNodeVersion,
130-
parseUserAgent: parseUserAgent
131-
};
6+
else if (typeof define === "function" && define.amd) {
7+
define(["require", "exports"], factory);
8+
}
9+
})(function (require, exports) {
10+
"use strict";
11+
Object.defineProperty(exports, "__esModule", { value: true });
12+
var BrowserInfo = /** @class */ (function () {
13+
function BrowserInfo(name, version, os) {
14+
this.name = name;
15+
this.version = version;
16+
this.os = os;
17+
}
18+
return BrowserInfo;
19+
}());
20+
exports.BrowserInfo = BrowserInfo;
21+
var NodeInfo = /** @class */ (function () {
22+
function NodeInfo(version) {
23+
this.version = version;
24+
this.name = 'node';
25+
this.os = process.platform;
26+
}
27+
return NodeInfo;
28+
}());
29+
exports.NodeInfo = NodeInfo;
30+
var BotInfo = /** @class */ (function () {
31+
function BotInfo() {
32+
this.bot = true; // NOTE: deprecated test name instead
33+
this.name = 'bot';
34+
this.version = null;
35+
this.os = null;
36+
}
37+
return BotInfo;
38+
}());
39+
exports.BotInfo = BotInfo;
40+
// tslint:disable-next-line:max-line-length
41+
var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
42+
var SEARCHBOT_OS_REGEX = /(nuhk)|(Googlebot)|(Yammybot)|(Openbot)|(Slurp)|(MSNBot)|(Ask Jeeves\/Teoma)|(ia_archiver)/;
43+
var REQUIRED_VERSION_PARTS = 3;
44+
var userAgentRules = [
45+
['aol', /AOLShield\/([0-9\._]+)/],
46+
['edge', /Edge\/([0-9\._]+)/],
47+
['yandexbrowser', /YaBrowser\/([0-9\._]+)/],
48+
['vivaldi', /Vivaldi\/([0-9\.]+)/],
49+
['kakaotalk', /KAKAOTALK\s([0-9\.]+)/],
50+
['samsung', /SamsungBrowser\/([0-9\.]+)/],
51+
['chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
52+
['phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/],
53+
['crios', /CriOS\/([0-9\.]+)(:?\s|$)/],
54+
['firefox', /Firefox\/([0-9\.]+)(?:\s|$)/],
55+
['fxios', /FxiOS\/([0-9\.]+)/],
56+
['opera', /Opera\/([0-9\.]+)(?:\s|$)/],
57+
['opera', /OPR\/([0-9\.]+)(:?\s|$)$/],
58+
['ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
59+
['ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
60+
['ie', /MSIE\s(7\.0)/],
61+
['bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/],
62+
['android', /Android\s([0-9\.]+)/],
63+
['ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/],
64+
['safari', /Version\/([0-9\._]+).*Safari/],
65+
['facebook', /FBAV\/([0-9\.]+)/],
66+
['instagram', /Instagram\s([0-9\.]+)/],
67+
['ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/],
68+
['searchbot', SEARCHBOX_UA_REGEX],
69+
];
70+
var operatingSystemRules = [
71+
['iOS', /iP(hone|od|ad)/],
72+
['Android OS', /Android/],
73+
['BlackBerry OS', /BlackBerry|BB10/],
74+
['Windows Mobile', /IEMobile/],
75+
['Amazon OS', /Kindle/],
76+
['Windows 3.11', /Win16/],
77+
['Windows 95', /(Windows 95)|(Win95)|(Windows_95)/],
78+
['Windows 98', /(Windows 98)|(Win98)/],
79+
['Windows 2000', /(Windows NT 5.0)|(Windows 2000)/],
80+
['Windows XP', /(Windows NT 5.1)|(Windows XP)/],
81+
['Windows Server 2003', /(Windows NT 5.2)/],
82+
['Windows Vista', /(Windows NT 6.0)/],
83+
['Windows 7', /(Windows NT 6.1)/],
84+
['Windows 8', /(Windows NT 6.2)/],
85+
['Windows 8.1', /(Windows NT 6.3)/],
86+
['Windows 10', /(Windows NT 10.0)/],
87+
['Windows ME', /Windows ME/],
88+
['Open BSD', /OpenBSD/],
89+
['Sun OS', /SunOS/],
90+
['Linux', /(Linux)|(X11)/],
91+
['Mac OS', /(Mac_PowerPC)|(Macintosh)/],
92+
['QNX', /QNX/],
93+
['BeOS', /BeOS/],
94+
['OS/2', /OS\/2/],
95+
['Search Bot', SEARCHBOT_OS_REGEX],
96+
];
97+
function detect() {
98+
if (typeof navigator !== 'undefined') {
99+
return parseUserAgent(navigator.userAgent);
100+
}
101+
return getNodeVersion();
102+
}
103+
exports.detect = detect;
104+
function parseUserAgent(ua) {
105+
// opted for using reduce here rather than Array#first with a regex.test call
106+
// this is primarily because using the reduce we only perform the regex
107+
// execution once rather than once for the test and for the exec again below
108+
// probably something that needs to be benchmarked though
109+
var matchedRule = ua !== '' &&
110+
userAgentRules.reduce(function (matched, _a) {
111+
var browser = _a[0], regex = _a[1];
112+
if (matched) {
113+
return matched;
114+
}
115+
var uaMatch = regex.exec(ua);
116+
return !!uaMatch && [browser, uaMatch];
117+
}, false);
118+
if (!matchedRule) {
119+
return null;
120+
}
121+
var name = matchedRule[0], match = matchedRule[1];
122+
if (name === 'searchbot') {
123+
return new BotInfo();
124+
}
125+
var version = match[1] && match[1].split(/[._]/).slice(0, 3);
126+
if (version) {
127+
if (version.length < REQUIRED_VERSION_PARTS) {
128+
version = version.concat(new Array(REQUIRED_VERSION_PARTS - version.length).fill('0'));
129+
}
130+
}
131+
else {
132+
version = [];
133+
}
134+
return new BrowserInfo(name, version.join('.'), detectOS(ua));
135+
}
136+
exports.parseUserAgent = parseUserAgent;
137+
function detectOS(ua) {
138+
var match = operatingSystemRules.find(function (_a) {
139+
var _ = _a[0], regex = _a[1];
140+
return regex.test(ua);
141+
});
142+
return match ? match[0] : null;
143+
}
144+
exports.detectOS = detectOS;
145+
function getNodeVersion() {
146+
var isNode = typeof process !== 'undefined' && process.version;
147+
return isNode ? new NodeInfo(process.version.slice(1)) : null;
148+
}
149+
exports.getNodeVersion = getNodeVersion;
150+
});

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
"description": "Unpack a browser type and version from the useragent string",
55
"main": "index.js",
66
"scripts": {
7+
"pretest": "tsc",
78
"test": "node test",
8-
"prepublish": "npm run test",
9+
"lint": "tslint -c tslint.json src/**",
10+
"compile-and-size": "yarn tsc && cat index.js | gzip | wc -c",
11+
"prepare": "npm run test && npm run lint",
912
"patch-release": "npm version patch && npm publish && npm run postpublish",
1013
"minor-release": "npm version minor && npm publish && npm run postpublish",
1114
"major-release": "npm version major && npm publish && npm run postpublish",
@@ -29,8 +32,11 @@
2932
},
3033
"homepage": "https://github.com/DamonOehlman/detect-browser",
3134
"devDependencies": {
35+
"@types/node": "^10.12.12",
3236
"embellish-readme": "1.3.2",
3337
"semver": "^5.0.3",
34-
"tape": "^4.2.2"
38+
"tape": "^4.2.2",
39+
"tslint": "^5.11.0",
40+
"typescript": "^3.2.1"
3541
}
3642
}

0 commit comments

Comments
 (0)