@adamlui/geolocate 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/cli/index.js +40 -0
- package/dist/cli/lib/color.js +31 -0
- package/dist/cli/lib/data.js +30 -0
- package/dist/cli/lib/init.js +43 -0
- package/dist/cli/lib/jsdelivr.js +11 -0
- package/dist/cli/lib/language.js +92 -0
- package/dist/cli/lib/log.js +172 -0
- package/dist/cli/lib/pkg.js +78 -0
- package/dist/cli/lib/settings.js +131 -0
- package/dist/cli/lib/string.js +3 -0
- package/dist/data/messages.json +17 -7
- package/dist/data/package-data.json +1 -1
- package/dist/geolocate.min.js +1 -1
- package/docs/README.md +10 -8
- package/package.json +8 -4
- package/dist/cli/index.min.js +0 -8
- package/dist/cli/lib/data.min.js +0 -6
- package/dist/cli/lib/init.min.js +0 -6
- package/dist/cli/lib/jsdelivr.min.js +0 -6
- package/dist/cli/lib/language.min.js +0 -7
- package/dist/cli/lib/log.min.js +0 -23
- package/dist/cli/lib/pkg.min.js +0 -6
- package/dist/cli/lib/settings.min.js +0 -7
package/README.md
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
<img height=31 src="https://img.shields.io/npm/dm/@adamlui/geolocate?logo=npm&color=af68ff&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
9
9
|
<a href="#%EF%B8%8F-mit-license">
|
|
10
10
|
<img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
11
|
-
<a href="https://github.com/adamlui/js-utils/releases/tag/geolocate-2.
|
|
12
|
-
<img height=31 src="https://img.shields.io/badge/Latest_Build-2.
|
|
11
|
+
<a href="https://github.com/adamlui/js-utils/releases/tag/geolocate-2.3.0">
|
|
12
|
+
<img height=31 src="https://img.shields.io/badge/Latest_Build-2.3.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/@adamlui/geolocate?activeTab=code">
|
|
14
14
|
<img height=31 src="https://img.shields.io/npm/unpacked-size/%40adamlui%2Fgeolocate?style=for-the-badge&logo=ebox&logoColor=white&labelColor=464646&color=blue"></a>
|
|
15
|
-
<a href="
|
|
16
|
-
<img height=31 src="https://img.shields.io/
|
|
15
|
+
<a href="#">
|
|
16
|
+
<img height=31 src="https://img.shields.io/bundlejs/size/%40adamlui%2Fgeolocate%402.2.1?label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
|
|
17
17
|
<a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:geolocate/src/geolocate.js">
|
|
18
18
|
<img height=31 src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fsonarcloud.io%2Fapi%2Fmeasures%2Fcomponent%3Fcomponent%3Dadamlui_js-utils%3Ageolocate%2Fsrc%2Fgeolocate.js%26metricKeys%3Dvulnerabilities&query=%24.component.measures.0.value&style=for-the-badge&logo=sonarcloud&logoColor=white&labelColor=464646&label=Vulnerabilities&color=gold"></a>
|
|
19
19
|
<a href="https://github.com/toolleeo/cli-apps#networking">
|
|
@@ -77,6 +77,8 @@ Commands:
|
|
|
77
77
|
-i, --init Create config file (in project root).
|
|
78
78
|
-h, --help Display help screen.
|
|
79
79
|
-v, --version Show version number.
|
|
80
|
+
--stats Show npm stats.
|
|
81
|
+
--debug [targetKey] Show debug logs.
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
#
|
|
@@ -124,14 +126,14 @@ const geo = require('@adamlui/geolocate')
|
|
|
124
126
|
#### <> HTML script tag:
|
|
125
127
|
|
|
126
128
|
```html
|
|
127
|
-
<script src="https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
129
|
+
<script src="https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js"></script>
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
#### ES6:
|
|
131
133
|
|
|
132
134
|
```js
|
|
133
135
|
(async () => {
|
|
134
|
-
await import('https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
136
|
+
await import('https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js')
|
|
135
137
|
// Your code here...
|
|
136
138
|
})()
|
|
137
139
|
```
|
|
@@ -140,7 +142,7 @@ const geo = require('@adamlui/geolocate')
|
|
|
140
142
|
|
|
141
143
|
```js
|
|
142
144
|
...
|
|
143
|
-
// @require https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
145
|
+
// @require https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js
|
|
144
146
|
// ==/UserScript==
|
|
145
147
|
|
|
146
148
|
// Your code here...
|
|
@@ -148,7 +150,7 @@ const geo = require('@adamlui/geolocate')
|
|
|
148
150
|
|
|
149
151
|
<br>
|
|
150
152
|
|
|
151
|
-
**📝 Note:** To always import the latest version (not recommended in production!) remove the `@2.
|
|
153
|
+
**📝 Note:** To always import the latest version (not recommended in production!) remove the `@2.3.0` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/@adamlui/geolocate/dist/geolocate.min.js`
|
|
152
154
|
|
|
153
155
|
<br>
|
|
154
156
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
'use strict'
|
|
5
|
+
|
|
6
|
+
globalThis.env = {
|
|
7
|
+
args: process.argv.slice(2),
|
|
8
|
+
modes: { dev: /[\\/]src(?:[\\/]|$)/i.test(__dirname) },
|
|
9
|
+
paths: { lib: './lib' }
|
|
10
|
+
}
|
|
11
|
+
env.modes.debug = env.args.some(arg => /^--?(?:V|debug(?:[-_]?mode)?)$/.test(arg))
|
|
12
|
+
|
|
13
|
+
// Import LIBS
|
|
14
|
+
globalThis.log = require(`${env.paths.lib}/log`)
|
|
15
|
+
const clipboardy = require('node-clipboardy'),
|
|
16
|
+
geo = require(`../geolocate${ env.modes.dev ? '' : '.min' }.js`),
|
|
17
|
+
init = require(`${env.paths.lib}/init`)
|
|
18
|
+
|
|
19
|
+
await init.cli()
|
|
20
|
+
|
|
21
|
+
// Exec CMD arg if passed
|
|
22
|
+
if (cli.config.init) return init.configFile()
|
|
23
|
+
else if (cli.config.help) return log.help()
|
|
24
|
+
else if (cli.config.version) return log.version()
|
|
25
|
+
else if (cli.config.stats) return log.stats()
|
|
26
|
+
|
|
27
|
+
// Process IP args
|
|
28
|
+
const validIPs = []
|
|
29
|
+
for (const arg of env.args) if (!arg.startsWith('-')) // load IP from leading-dash-less arg
|
|
30
|
+
validIPs.push(arg.replace(/[[\]]/g, '')) // strip outer '[]' in case copied from docs
|
|
31
|
+
|
|
32
|
+
// Log/copy GEO result(s)
|
|
33
|
+
log.break()
|
|
34
|
+
const geoResults = await geo.locate(validIPs, { verbose: !cli.config.quietMode })
|
|
35
|
+
if (!geoResults) process.exit(1)
|
|
36
|
+
if (!cli.config.quietMode && geoResults.length == 1) log.geoData(geoResults[0])
|
|
37
|
+
log.ifNotQuiet(`${cli.msgs.info_copyingToClip}...`)
|
|
38
|
+
clipboardy.writeSync(JSON.stringify(geoResults, null, 2))
|
|
39
|
+
|
|
40
|
+
})()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const color = module.exports = {
|
|
2
|
+
nc: '\x1b[0m',
|
|
3
|
+
hex: {
|
|
4
|
+
br: '#ff0000', by: '#ffff00', bo: '#ffa500', bg: '#00ff00',
|
|
5
|
+
bw: '#ffffff', gry: '#808080', blk: '#000000', tlBG: '#008080'
|
|
6
|
+
},
|
|
7
|
+
schemes: {
|
|
8
|
+
get default() {
|
|
9
|
+
return [
|
|
10
|
+
'#00e5bc', '#18c8ae', '#30ac9f', '#488f91', '#607383',
|
|
11
|
+
'#775674', '#8f3966', '#a71d57', '#bf0049', '#9a1b5e'
|
|
12
|
+
].map(color.hexToANSI)
|
|
13
|
+
},
|
|
14
|
+
get rainbow() {
|
|
15
|
+
return [
|
|
16
|
+
'#e41a1c', '#ff7f00', '#ffff33', '#4daf4a', '#377eb8',
|
|
17
|
+
'#984ea3', '#f781bf', '#999999', '#a65628', '#d95f02'
|
|
18
|
+
].map(color.hexToANSI)
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
hexToANSI(hex) {
|
|
23
|
+
const r = parseInt(hex.slice(1,3), 16),
|
|
24
|
+
g = parseInt(hex.slice(3,5), 16),
|
|
25
|
+
b = parseInt(hex.slice(5,7), 16)
|
|
26
|
+
return `\x1b[38;2;${r};${g};${b}m`
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const hexKey of Object.keys(color.hex)) // add color[hexKey] getters that return ANSI
|
|
31
|
+
Object.defineProperty(color, hexKey, { get: () => color.hexToANSI(color.hex[hexKey]) })
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
|
|
3
|
+
atomicWrite(filePath, data, encoding = 'utf8') { // to prevent TOCTOU
|
|
4
|
+
const fs = require('fs'),
|
|
5
|
+
path = require('path'),
|
|
6
|
+
tmpPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.tmp`)
|
|
7
|
+
fs.writeFileSync(tmpPath, data, encoding) ; fs.renameSync(tmpPath, filePath)
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
fetch(url) { // to support Node.js < v21
|
|
11
|
+
return typeof fetch == 'undefined' ? new Promise((resolve, reject) => { // using https?.get()
|
|
12
|
+
const protocol = url.match(/^([^:]+):\/\//)[1]
|
|
13
|
+
if (!/^https?$/.test(protocol))
|
|
14
|
+
reject(new Error(`${cli.msgs.error_invalidURL}.`))
|
|
15
|
+
require(protocol).get(url, resp => {
|
|
16
|
+
let rawData = ''
|
|
17
|
+
resp.on('data', chunk => rawData += chunk)
|
|
18
|
+
resp.on('end', () => resolve({ json: () => JSON.parse(rawData), text: () => rawData }))
|
|
19
|
+
}).on('error', reject)
|
|
20
|
+
}) : fetch(url) // using Node.js fetch()
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
flatten(json, { key = 'message' } = {}) { // eliminate need to ref nested keys
|
|
24
|
+
const flatObj = {}
|
|
25
|
+
for (const jsonKey in json) flatObj[jsonKey] =
|
|
26
|
+
typeof json[jsonKey] == 'object' && key in json[jsonKey] ? json[jsonKey][key]
|
|
27
|
+
: json[jsonKey]
|
|
28
|
+
return flatObj
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const language = require('./language'),
|
|
2
|
+
settings = require('./settings')
|
|
3
|
+
|
|
4
|
+
const dataPath = `../../${ env.modes.dev ? '../' : 'data/' }`
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
|
|
8
|
+
async cli() {
|
|
9
|
+
Object.assign(globalThis.cli ??= {}, require(`${dataPath}package-data.json`))
|
|
10
|
+
cli.msgs = await language.getMsgs('en')
|
|
11
|
+
cli.msgs = await language.getMsgs(cli.lang = settings.load('uiLang') || (
|
|
12
|
+
env.modes.debug ? language.generateRandomLang({ excludes: ['en'] }) : language.getSysLang() ))
|
|
13
|
+
cli.urls.cliDocs ||= `${cli.urls.docs}/#-command-line-usage`
|
|
14
|
+
settings.load() // all keys to cli.config
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
async configFile(filename = settings.configFilename) {
|
|
18
|
+
const fs = require('fs'),
|
|
19
|
+
path = require('path'),
|
|
20
|
+
paths = { target: path.resolve(process.cwd(), filename) }
|
|
21
|
+
|
|
22
|
+
if (fs.existsSync(paths.target)) // use existing config file
|
|
23
|
+
return log.warn(`${cli.msgs.warn_configFileExists}:`, paths.target)
|
|
24
|
+
if (fs.existsSync(paths.src = path.resolve(__dirname, `${dataPath}${filename}`)))
|
|
25
|
+
fs.copyFileSync(paths.src, paths.target) // use found template
|
|
26
|
+
|
|
27
|
+
else { // use jsDelivr copy
|
|
28
|
+
const jsdURL = `${require('./jsdelivr').pkgVerURL()}/${filename}/`
|
|
29
|
+
log.data(`${cli.msgs.info_fetchingRemoteConfigFrom} ${jsdURL}...`)
|
|
30
|
+
try {
|
|
31
|
+
const data = require('./data'),
|
|
32
|
+
resp = await data.fetch(jsdURL)
|
|
33
|
+
if (resp.ok) data.atomicWrite(paths.target, await resp.text())
|
|
34
|
+
else return log.warn(`${cli.msgs.warn_remoteConfigNotFound}: ${jsdURL} (${resp.status})`)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return log.warn(`${cli.msgs.warn_remoteConfigFailed}: ${jsdURL} ${err.message}`) }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
log.success(`${cli.msgs.info_configFileCreated}: ${paths.target}\n`)
|
|
40
|
+
log.tip(`${cli.msgs.tip_editToSetDefaults}.`)
|
|
41
|
+
log.tip(`${cli.msgs.tip_cliArgsPrioritized}.`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
|
|
3
|
+
pkgVerURL(version) {
|
|
4
|
+
version ||= cli.version ||= require('./pkg').getVer('local') || 'none'
|
|
5
|
+
const pkgName = cli.name.split('/')[1],
|
|
6
|
+
verTag = !/^\d+\.\d+\.\d+$/.test(version) ? 'latest' : `${pkgName}-${version}`
|
|
7
|
+
return `${cli.urls.jsdelivr}@${verTag}/${pkgName}`
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
commitURL(hash = 'latest') { return `${cli.urls.jsdelivr}@${hash}/${cli.name.split('/')[1]}` }
|
|
11
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const data = require('./data')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
|
|
5
|
+
formatCode(langCode) { // to match locale dir name
|
|
6
|
+
return langCode.replace(
|
|
7
|
+
/([a-z]{2,8})[-_]([a-z]{2})/i, (_, lang, region) =>`${lang.toLowerCase()}_${region.toUpperCase()}`) },
|
|
8
|
+
|
|
9
|
+
generateRandomLang({ includes = [], excludes = [] } = {}) {
|
|
10
|
+
const fs = require('fs'),
|
|
11
|
+
path = require('path')
|
|
12
|
+
|
|
13
|
+
let locales = includes.length ? includes : (() => {
|
|
14
|
+
|
|
15
|
+
// Read cache if found
|
|
16
|
+
const cacheDir = path.join(__dirname, '..', '.cache'),
|
|
17
|
+
localeCache = path.join(cacheDir, 'locales.json')
|
|
18
|
+
if (fs.existsSync(localeCache))
|
|
19
|
+
try { return JSON.parse(fs.readFileSync(localeCache, 'utf8')) } catch (err) {}
|
|
20
|
+
|
|
21
|
+
// Discover pkg _locales
|
|
22
|
+
const localesDir = path.resolve(process.cwd(), '_locales')
|
|
23
|
+
if (!fs.existsSync(localesDir)) return ['en']
|
|
24
|
+
const locales = fs.readdirSync(localesDir, { withFileTypes: true })
|
|
25
|
+
.filter(entry => entry.isDirectory()).map(entry => entry.name)
|
|
26
|
+
.filter(name => /^\w{2}[-_]?\w{0,2}$/.test(name))
|
|
27
|
+
|
|
28
|
+
// Cache result
|
|
29
|
+
fs.mkdirSync(cacheDir, { recursive: true })
|
|
30
|
+
data.atomicWrite(localeCache, JSON.stringify(locales, null, 2))
|
|
31
|
+
|
|
32
|
+
return locales
|
|
33
|
+
})()
|
|
34
|
+
|
|
35
|
+
// Filter out excludes
|
|
36
|
+
const excludeSet = new Set(excludes)
|
|
37
|
+
locales = locales.filter(locale => !excludeSet.has(locale))
|
|
38
|
+
|
|
39
|
+
// Get random language
|
|
40
|
+
let randomLang = 'en'
|
|
41
|
+
if (locales.length)
|
|
42
|
+
randomLang = locales[Math.floor(Math.random() * locales.length)]
|
|
43
|
+
log.debug(`Random language: ${randomLang}`)
|
|
44
|
+
|
|
45
|
+
return randomLang
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async getMsgs(langCode = 'en') {
|
|
49
|
+
langCode = module.exports.formatCode(langCode)
|
|
50
|
+
if (env.msgs && langCode == cli.lang) return env.msgs // don't re-fetch same msgs
|
|
51
|
+
|
|
52
|
+
let msgs = data.flatten( // local ones
|
|
53
|
+
require(`../../${ env.modes.dev ? '../_locales/en/' : 'data/' }messages.json`))
|
|
54
|
+
|
|
55
|
+
if (!langCode.startsWith('en')) { // fetch non-English msgs from jsDelivr
|
|
56
|
+
const msgHostURL = `${require('./jsdelivr').commitURL(cli.commitHashes.locales)}/_locales/`
|
|
57
|
+
let msgHref = `${msgHostURL}${langCode}/messages.json`, msgFetchesTried = 0
|
|
58
|
+
while (msgFetchesTried < 3)
|
|
59
|
+
try { // fetch remote msgs
|
|
60
|
+
msgs = data.flatten(await (await data.fetch(msgHref)).json())
|
|
61
|
+
break
|
|
62
|
+
} catch (err) { // retry up to 2X (region-stripped + EN)
|
|
63
|
+
msgFetchesTried++ ; if (msgFetchesTried >= 3) break
|
|
64
|
+
log.debug(msgHref = langCode.includes('-') && msgFetchesTried == 1 ?
|
|
65
|
+
msgHref.replace(/([^_]*)_[^/]*(\/.*)/, '$1$2') // strip region before retrying
|
|
66
|
+
: `${msgHostURL}en/messages.json` // else use EN msgs
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return msgs
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
getSysLang() {
|
|
75
|
+
try {
|
|
76
|
+
if (process.platform == 'win32')
|
|
77
|
+
return require('child_process').execSync(
|
|
78
|
+
'(Get-Culture).TwoLetterISOLanguageName', { shell: 'powershell', encoding: 'utf-8' }
|
|
79
|
+
).trim()
|
|
80
|
+
else { // macOS/Linux
|
|
81
|
+
const pe = process.env
|
|
82
|
+
return (pe.LANG || pe.LANGUAGE || pe.LC_ALL || pe.LC_MESSAGES || pe.LC_NAME)
|
|
83
|
+
.split('.')[0] // strip encoding e.g. .UTF-8
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
log.error(`${cli.msgs.error_failedToFetchSysLang}:`, err.message)
|
|
87
|
+
return 'en'
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
validateLangCode(code) { return typeof code != 'string' ? false : /^[a-z]{2,8}(?:[-_][a-z]{2,3})?$/i.test(code) }
|
|
92
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const colors = require('./color'),
|
|
2
|
+
{ getDownloads, getVer } = require('./pkg'),
|
|
3
|
+
string = require('./string')
|
|
4
|
+
|
|
5
|
+
const nextMajVer = require('../../../package.json').version.replace(/^(\d+)\..*/, (_, major) => `${ +major +1 }.0.0`)
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
colors,
|
|
9
|
+
|
|
10
|
+
configURL() { this.info(`${cli.msgs.info_exampleValidConfigFile}: ${cli.urls.config}`) },
|
|
11
|
+
configURLandExit(...args) { this.error(...args); this.configURL(); process.exit(1) },
|
|
12
|
+
data(msg) { console.log(`\n${colors.bw}${msg}${colors.nc}`) },
|
|
13
|
+
dim(msg) { console.log(`${colors.gry}${msg}${colors.nc}`) },
|
|
14
|
+
error(...args) { console.error(`\n${colors.br}ERROR:`, ...args, colors.nc) },
|
|
15
|
+
errorAndExit(...args) { this.error(...args); this.helpDocsCmdsDocsURL(); process.exit(1) },
|
|
16
|
+
ifNotQuiet(msg) { if (!cli.config.quietMode) this.info(msg) },
|
|
17
|
+
info(msg) { console.info(`\n${colors.schemes.default[0]}${msg}${colors.nc}`) },
|
|
18
|
+
break() { console.log() },
|
|
19
|
+
tip(msg) { console.info(`${colors.by}TIP: ${msg}${colors.nc}`) },
|
|
20
|
+
success(msg) { console.log(`\n${colors.bg}${msg}${colors.nc}`) },
|
|
21
|
+
warn(...args) { console.warn(`\n${colors.bo}WARNING:`, ...args, colors.nc) },
|
|
22
|
+
|
|
23
|
+
geoData(data) {
|
|
24
|
+
console.info([
|
|
25
|
+
`\nIP: ${this.colors.bw}${data.ip}${this.colors.nc}`,
|
|
26
|
+
`${cli.msgs.geoLabel_country}: ${this.colors.bw}${data.country}${this.colors.nc}`,
|
|
27
|
+
`${cli.msgs.geoLabel_region}: ${this.colors.bw}${data.regionName}${this.colors.nc}`,
|
|
28
|
+
`${cli.msgs.geoLabel_city}: ${this.colors.bw}${data.city}${this.colors.nc}`,
|
|
29
|
+
`${cli.msgs.geoLabel_zip}: ${this.colors.bw}${data.zip}${this.colors.nc}`,
|
|
30
|
+
`${cli.msgs.geoLabel_lat}: ${this.colors.bw}${data.lat}${this.colors.nc}`,
|
|
31
|
+
`${cli.msgs.geoLabel_lon}: ${this.colors.bw}${data.lon}${this.colors.nc}`,
|
|
32
|
+
`${cli.msgs.geoLabel_timeZone}: ${
|
|
33
|
+
this.colors.bw}${data.timezone.replace(/_/g, ' ').replace(/\//g, ' / ')}${this.colors.nc}`,
|
|
34
|
+
`ISP: ${this.colors.bw}${data.isp}${this.colors.nc}`
|
|
35
|
+
].join('\n'))
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
argDoesNothing(arg) {
|
|
39
|
+
this.warn(`${cli.msgs.warn_option} ${arg} ${cli.msgs.warn_noLongerHasAnyEffect} ${
|
|
40
|
+
cli.msgs.warn_andWillBeRemoved} @ v${nextMajVer}`)
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
configKeyReplacedBy(oldKey, newKey, oldVal) {
|
|
44
|
+
if (!this[`${oldKey}Warned`]) {
|
|
45
|
+
this.warn(
|
|
46
|
+
`${cli.msgs.info_configFile} ${cli.msgs.warn_option.toLowerCase()} '${oldKey}: ${oldVal}' ${
|
|
47
|
+
cli.msgs.warn_hasBeenReplacedBy} '${
|
|
48
|
+
newKey}: ${ isNegKey(oldKey) != isNegKey(newKey) ? !oldVal : oldVal }' ${
|
|
49
|
+
cli.msgs.warn_andWillBeRemoved} @ v${nextMajVer}`
|
|
50
|
+
)
|
|
51
|
+
this[`${oldKey}Warned`] = true
|
|
52
|
+
function isNegKey(key) { return /^(?:no|disable|exclude)[A-Z]/.test(key) }
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
debug(msg) {
|
|
57
|
+
if (!env.modes.debug) return
|
|
58
|
+
this.argIdx ??= env.args.findIndex(arg => /^--?(?:V|debug(?:[-_]?mode)?)$/.test(arg))
|
|
59
|
+
if (this.argIdx +1 < env.args.length && !env.args[this.argIdx +1].startsWith('-')) // use --debug [targetKey]
|
|
60
|
+
this.key ??= env.args[this.argIdx +1].replace('-', '_')
|
|
61
|
+
if (this.key)
|
|
62
|
+
this.val = cli.config[this.key] || `cli.config key "${this.key}" ${cli.msgs.warn_notFound.toLowerCase()}`
|
|
63
|
+
else
|
|
64
|
+
this.val = cli.config
|
|
65
|
+
msg += `\n${colors.gry}${JSON.stringify(this.val)}${colors.nc}`
|
|
66
|
+
console.debug(`\n${colors.by}DEBUG:`, msg, colors.nc)
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
help(includeSections = ['header', 'usage', 'params', 'flags', 'cmds']) {
|
|
70
|
+
cli.prefix = `${this.colors.tlBG}${this.colors.blk} ${cli.name.replace(/^@[^/]+\//, '')} ${this.colors.nc} `
|
|
71
|
+
const helpSections = {
|
|
72
|
+
header: [
|
|
73
|
+
`\n├ ${cli.prefix}${cli.msgs.pkg_copyright}.`,
|
|
74
|
+
`${cli.prefix}${cli.msgs.prefix_source}: ${cli.urls.src}`
|
|
75
|
+
],
|
|
76
|
+
usage: [
|
|
77
|
+
`\n${this.colors.bw}o ${cli.msgs.helpSection_usage}:${this.colors.nc}`,
|
|
78
|
+
` ${this.colors.bw}» ${this.colors.bg}${cli.cmdFormat}${this.colors.nc}`
|
|
79
|
+
],
|
|
80
|
+
params: [
|
|
81
|
+
`\n${this.colors.bw}o ${cli.msgs.helpSection_params}:${this.colors.nc}`,
|
|
82
|
+
` --ui-lang="code" ${cli.msgs.optionDesc_uiLang}.`,
|
|
83
|
+
` --config="path/to/file" ${cli.msgs.optionDesc_config}.`
|
|
84
|
+
],
|
|
85
|
+
flags: [
|
|
86
|
+
`\n${this.colors.bw}o ${cli.msgs.helpSection_flags}:${this.colors.nc}`,
|
|
87
|
+
` -q, --quiet ${cli.msgs.optionDesc_quiet}.`
|
|
88
|
+
],
|
|
89
|
+
cmds: [
|
|
90
|
+
`\n${this.colors.bw}o ${cli.msgs.helpSection_cmds}:${this.colors.nc}`,
|
|
91
|
+
` -i, --init ${cli.msgs.optionDesc_init}.`,
|
|
92
|
+
` -h, --help ${cli.msgs.optionDesc_help}.`,
|
|
93
|
+
` -v, --version ${cli.msgs.optionDesc_version}.`,
|
|
94
|
+
` -v, --stats ${cli.msgs.optionDesc_stats}.`,
|
|
95
|
+
` -V, --debug ${cli.msgs.optionDesc_debug}.`
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
includeSections.forEach(section => // print valid arg elems
|
|
99
|
+
helpSections[section]?.forEach(line => printHelpMsg(line, /header|usage/.test(section) ? 1 : 29)))
|
|
100
|
+
console.info(`\n${cli.msgs.info_moreHelp}, ${
|
|
101
|
+
cli.msgs.info_visit}: ${this.colors.bw}${cli.urls.cliDocs}${this.colors.nc}`)
|
|
102
|
+
|
|
103
|
+
function printHelpMsg(msg, indent) { // wrap msg + indent 2nd+ lines
|
|
104
|
+
const terminalWidth = process.stdout.columns || 80,
|
|
105
|
+
words = msg.match(/\S+|\s+/g),
|
|
106
|
+
lines = [], prefix = '| '
|
|
107
|
+
|
|
108
|
+
// Split msg into lines of appropriate lengths
|
|
109
|
+
let currentLine = ''
|
|
110
|
+
words.forEach(word => {
|
|
111
|
+
const lineLength = terminalWidth -( !lines.length ? 0 : indent )
|
|
112
|
+
if (currentLine.length + prefix.length + word.length > lineLength) { // cap/store it
|
|
113
|
+
lines.push(!lines.length ? currentLine : currentLine.trimStart())
|
|
114
|
+
currentLine = ''
|
|
115
|
+
}
|
|
116
|
+
currentLine += word
|
|
117
|
+
})
|
|
118
|
+
lines.push(!lines.length ? currentLine : currentLine.trimStart())
|
|
119
|
+
|
|
120
|
+
// Print formatted msg
|
|
121
|
+
lines.forEach((line, idx) => console.info(prefix +(
|
|
122
|
+
idx == 0 ? line // print 1st line unindented
|
|
123
|
+
: ' '.repeat(indent) + line // print subsequent lines indented
|
|
124
|
+
)))
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
helpDocsCmdsDocsURL() {
|
|
129
|
+
console.info(`\n${
|
|
130
|
+
cli.msgs.info_moreHelp}, ${cli.msgs.info_type} ${
|
|
131
|
+
colors.bw}${cli.name.split('/')[1]} --<docs|help>${colors.nc} ${
|
|
132
|
+
cli.msgs.info_or} ${cli.msgs.info_visit}\n${colors.by}${cli.urls.docs}${colors.nc}`
|
|
133
|
+
)
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
initCmd(invalidKey) {
|
|
137
|
+
if (invalidKey)
|
|
138
|
+
this.warn(
|
|
139
|
+
`${cli.msgs.error_invalidKey} '${invalidKey}' ${cli.msgs.error_foundIn}\n`
|
|
140
|
+
+ `${log.colors.gry}${cli.configPath}`
|
|
141
|
+
)
|
|
142
|
+
if (!this.initTipped) {
|
|
143
|
+
this.break()
|
|
144
|
+
this.tip(`${
|
|
145
|
+
string.toTitleCase(cli.msgs.info_type)} '${cli.name} init' ${
|
|
146
|
+
cli.msgs.info_toCreateDefaultConfig}`
|
|
147
|
+
)
|
|
148
|
+
this.initTipped = true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
invalidConfigKey(key) { if (!this[`${key}Tipped`]) { this.initCmd(key) ; this[`${key}Tipped`] = true } },
|
|
153
|
+
|
|
154
|
+
async stats(pkgName = cli.name, options = { ecosystem: 'npm', maxDays: 8, maxVers: 5, scheme: 'default' }) {
|
|
155
|
+
const pkgStats = await getDownloads(pkgName, options),
|
|
156
|
+
schemeData = colors.schemes[options.scheme]
|
|
157
|
+
if (!schemeData) return this.error(`Scheme '${options.scheme}' not found!`)
|
|
158
|
+
const colorMap = Object.fromEntries(schemeData.map((hex, idx) => [`c${idx}`, hex])),
|
|
159
|
+
statsTable = new (require('console-table-printer').Table)({ colorMap })
|
|
160
|
+
pkgStats.forEach((row, idx) => // build colored rows
|
|
161
|
+
statsTable.addRow(row, { color: `c${Math.floor(idx / pkgStats.length * schemeData.length)}` }))
|
|
162
|
+
statsTable.printTable()
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
version() {
|
|
166
|
+
this.info(cli.name)
|
|
167
|
+
this.data(`${
|
|
168
|
+
cli.msgs.prefix_globalVer}: ${ getVer('global') || 'none' }\n${
|
|
169
|
+
cli.msgs.prefix_localVer }: ${ getVer('local') || 'none' }`
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const data = require('./data')
|
|
2
|
+
|
|
3
|
+
const endpoints = {
|
|
4
|
+
npmjsDLs: 'https://api.npmjs.org/downloads',
|
|
5
|
+
pepyProjects: 'https://pepy.tech/projects'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
|
|
10
|
+
async getDownloads(
|
|
11
|
+
pkgName, // e.g. some-npm-pkg, npm:@adamlui/minify.js, pypi:translate-messages
|
|
12
|
+
{ ecosystem = 'npm', maxDays = 10, maxVers = 10 } = {}
|
|
13
|
+
) {
|
|
14
|
+
if (pkgName.includes(':')) [ecosystem, pkgName] = pkgName.split(':')
|
|
15
|
+
|
|
16
|
+
if (/npm|node/i.test(ecosystem)) { // fetch from endpoints.npmjsDLs
|
|
17
|
+
function formatDate(date) { return date.toISOString().split('T')[0] }
|
|
18
|
+
const dates = { end: new Date(), start: new Date() }
|
|
19
|
+
dates.start.setMonth(dates.end.getMonth() -3)
|
|
20
|
+
const npmjsURL = `${endpoints.npmjsDLs}/range/${formatDate(dates.start)}:${formatDate(dates.end)}/${pkgName}`
|
|
21
|
+
log.info(`Fetching npm stats for ${pkgName}${
|
|
22
|
+
env.modes.debug ? ` from\n${log.colors.bw}${npmjsURL}` : '' }...\n`)
|
|
23
|
+
return (await (await data.fetch(npmjsURL)).json()).downloads // { downloads: [{ day, downloads }] }
|
|
24
|
+
.sort((a, b) => new Date(b.day) - new Date(a.day)) // new ⇅ old
|
|
25
|
+
.slice(0, maxDays) // cap rows
|
|
26
|
+
.map(({ day: date, downloads }) => ({ date, downloads }))
|
|
27
|
+
|
|
28
|
+
} else if (/^py/i.test(ecosystem)) { // fetch from endpoints.pepyProjects
|
|
29
|
+
let rows = []
|
|
30
|
+
const pepyURL = `${endpoints.pepyProjects}/${pkgName}`
|
|
31
|
+
log.info(`Fetching PyPI/mirror stats for ${pkgName}${
|
|
32
|
+
env.modes.debug ? ` from\n${log.colors.bw}${pepyURL}` : '' }...\n`)
|
|
33
|
+
const respText = await (await data.fetch(pepyURL)).text(),
|
|
34
|
+
rePush = /self\.__next_f\.push\(\[\d+,\s*"((?:\\.|[^"\\])*)"\]\)/g
|
|
35
|
+
let downloads = {}, match
|
|
36
|
+
while ((match = rePush.exec(respText))) {
|
|
37
|
+
try { // extract project.downloads
|
|
38
|
+
const inner = JSON.parse(`"${match[1]}"`),
|
|
39
|
+
data = JSON.parse(inner.substring(inner.indexOf(':') +1))
|
|
40
|
+
if (data[3]?.project) downloads = data[3].project.downloads
|
|
41
|
+
} catch (err) {}
|
|
42
|
+
}
|
|
43
|
+
rows = Object.entries(downloads)
|
|
44
|
+
.sort(([a], [b]) => new Date(b) - new Date(a)) // new ⇅ old
|
|
45
|
+
.slice(0, maxDays) // cap rows
|
|
46
|
+
.map(([date, data]) => ({ date, ...data }))
|
|
47
|
+
const activeVers = new Set()
|
|
48
|
+
rows.forEach(row => Object.keys(row).forEach(key => {
|
|
49
|
+
if (key != 'date' && row[key] > 0) activeVers.add(key) }))
|
|
50
|
+
const finalVers = [...activeVers]
|
|
51
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true })) // new ⇆ old
|
|
52
|
+
.slice(0, maxVers) // cap columns
|
|
53
|
+
return rows.map(row => {
|
|
54
|
+
const result = { date: row.date }
|
|
55
|
+
finalVers.forEach(ver => result[ver] = row[ver] || 0)
|
|
56
|
+
return result
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
} else return log.debug(`${ecosystem} not supported.`)
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
getVer(type = 'any') { // or <'global'|'local'>
|
|
63
|
+
let pkgVer
|
|
64
|
+
if (type != 'global')
|
|
65
|
+
try { // get local ver
|
|
66
|
+
const localManifestPath = require('path').resolve(
|
|
67
|
+
process.cwd(), 'node_modules', cli.name, 'package.json')
|
|
68
|
+
pkgVer = require(localManifestPath).version
|
|
69
|
+
} catch (err) { log.debug(`${cli.msgs.error_readingLocalPkgVer}: ${err.message}`) }
|
|
70
|
+
if (type == 'global' || type == 'all' && !pkgVer)
|
|
71
|
+
try { // get global ver
|
|
72
|
+
pkgVer = require('child_process').execSync(
|
|
73
|
+
`npm view ${JSON.stringify(cli.name)} version`
|
|
74
|
+
).toString().trim()
|
|
75
|
+
} catch (err) { log.debug(`${cli.msgs.error_failedToFetchGlobalVer}: ${err.message}`) }
|
|
76
|
+
return pkgVer
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const fs = require('fs'),
|
|
2
|
+
path = require('path')
|
|
3
|
+
|
|
4
|
+
;(globalThis.cli ??= {}).config = {}
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
configFilename: 'geolocate.config.mjs',
|
|
8
|
+
|
|
9
|
+
controls: {
|
|
10
|
+
uiLang: { type: 'param', valType: 'langCode', regex: /^--?ui[-_]?lang(?:[=\s].*|$)/ },
|
|
11
|
+
config: { type: 'param', valType: 'filepath', regex: /^--?config(?:[=\s].*|$)/ },
|
|
12
|
+
quietMode: { type: 'flag', regex: /^--?q(?:uiet)?(?:[-_]?mode)?$/ },
|
|
13
|
+
init: { type: 'cmd', regex: /^-{0,2}i(?:nit)?$/ },
|
|
14
|
+
help: { type: 'cmd', regex: /^--?h(?:elp)?$/ },
|
|
15
|
+
version: { type: 'cmd', regex: /^--?ve?r?s?i?o?n?$/ },
|
|
16
|
+
stats: { type: 'cmd', regex: /^--?stats?$/ }
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
load(ctrlKeys = Object.keys(this.controls)) {
|
|
20
|
+
const inputCtrlKeys = [].concat(ctrlKeys) // force array
|
|
21
|
+
|
|
22
|
+
if (!cli.defaultsSet && !arguments.length) { // init all defaults on arg-less load()
|
|
23
|
+
inputCtrlKeys.forEach(key => {
|
|
24
|
+
const ctrl = this.controls[key] ; if (ctrl.mode || ctrl.type == 'legacy') return
|
|
25
|
+
cli.config[key] ??= ctrl.defaultVal ?? ( ctrl.type == 'param' ? '' : false )
|
|
26
|
+
})
|
|
27
|
+
cli.defaultsSet = true
|
|
28
|
+
log.debug('All cli.config default vals set!')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!cli.configPathTried) { // init config file path
|
|
32
|
+
const configArg = env.args.find(arg => this.controls.config.regex.test(arg))
|
|
33
|
+
|
|
34
|
+
if (configArg) { // resolve input path, then validate
|
|
35
|
+
if (!/=/.test(configArg))
|
|
36
|
+
log.errorAndExit(`[${configArg}] ${cli.msgs.error_mustIncludePath}`)
|
|
37
|
+
const inputPath = configArg.split('=')[1]
|
|
38
|
+
cli.configPath = path.isAbsolute(inputPath) ? inputPath : path.resolve(process.cwd(), inputPath)
|
|
39
|
+
if (!fs.existsSync(cli.configPath))
|
|
40
|
+
log.configURLandExit(`${cli.msgs.error_configFileNotFound}:`, cli.configPath)
|
|
41
|
+
|
|
42
|
+
} else // auto-discover .config.[mc]?js file
|
|
43
|
+
for (const configExt of ['.mjs', '.cjs', '.js']) {
|
|
44
|
+
const autoPath = path.resolve(process.cwd(), this.configFilename.replace(/\.[^.]+$/, configExt))
|
|
45
|
+
if (fs.existsSync(autoPath)) { cli.configPath = autoPath ; break }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
cli.configPathTried = true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (cli.configPath) // load from config file
|
|
52
|
+
try {
|
|
53
|
+
const mod = require(cli.configPath), fileConfig = mod?.default ?? mod
|
|
54
|
+
if (!fileConfig || typeof fileConfig != 'object')
|
|
55
|
+
log.configURLandExit(`${cli.msgs.error_invalidConfigFile}.`)
|
|
56
|
+
;(arguments.length ? inputCtrlKeys : Object.keys(fileConfig)).forEach(key => {
|
|
57
|
+
if (!(key in fileConfig)) return
|
|
58
|
+
const val = fileConfig[key], ctrl = this.controls[key]
|
|
59
|
+
if (!ctrl) {
|
|
60
|
+
if (this.configFileKeyWhitelist && !this.configFileKeyWhitelist.includes(key))
|
|
61
|
+
log.invalidConfigKey(key)
|
|
62
|
+
return
|
|
63
|
+
} else if (ctrl.type == 'legacy' && ctrl.replacedBy) {
|
|
64
|
+
if (key.toLowerCase().includes('no') != ctrl.replacedBy.toLowerCase().includes('no'))
|
|
65
|
+
cli.config[ctrl.replacedBy] = !val // assign opposite val to current key
|
|
66
|
+
else // assign direct val to current key
|
|
67
|
+
cli.config[ctrl.replacedBy] = val
|
|
68
|
+
return log.configKeyReplacedBy(key, ctrl.replacedBy, val)
|
|
69
|
+
}
|
|
70
|
+
cli.config[key] = val
|
|
71
|
+
})
|
|
72
|
+
if (!arguments.length) log.debug('Config file loaded!')
|
|
73
|
+
} catch (err) {
|
|
74
|
+
log.configURLandExit(`${cli.msgs.error_failedToLoadConfigFile}:`, cli.configPath, `\n${err.message}`) }
|
|
75
|
+
|
|
76
|
+
for (let i = 0 ; i < env.args.length ; i++) { // load from CLI arg (overriding config file loads)
|
|
77
|
+
const arg = env.args[i]
|
|
78
|
+
if (/^[^-]|--?(?:config|debug)/.test(arg) && arg != 'init') continue
|
|
79
|
+
const ctrlKey = Object.keys(this.controls).find(key => this.controls[key]?.regex?.test(arg))
|
|
80
|
+
if (!ctrlKey && cli.msgs) log.errorAndExit(`[${arg}] ${cli.msgs.error_notRecognized}.`)
|
|
81
|
+
if (!inputCtrlKeys.includes(ctrlKey)) return // don't process env.args when load() specific keys
|
|
82
|
+
const ctrl = this.controls[ctrlKey]
|
|
83
|
+
if (ctrl.type == 'legacy') { log.argDoesNothing(arg) ; continue }
|
|
84
|
+
if (ctrl.mode) // set cli.config.mode to mode name
|
|
85
|
+
cli.config.mode = ctrlKey.replace(/mode$/i, '').toLowerCase()
|
|
86
|
+
else { // init flag/param/cmd cli.config[ctrlKey] val
|
|
87
|
+
if (ctrl.type == 'param')
|
|
88
|
+
cli.config[ctrlKey] =
|
|
89
|
+
arg.includes('=') ? arg.split('=')[1]?.trim() || '' // =val
|
|
90
|
+
: (i +1 < env.args.length && !env.args[i +1].startsWith('-')) ? env.args[++i] // dashless val
|
|
91
|
+
: '' // val-less --param passed
|
|
92
|
+
else // flag/cmd
|
|
93
|
+
cli.config[ctrlKey] = true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!arguments.length) log.debug('Args parsed!')
|
|
98
|
+
|
|
99
|
+
this.parseValidateConfig(inputCtrlKeys)
|
|
100
|
+
if (!arguments.length) log.debug('All cli.config vals parsed/validated!')
|
|
101
|
+
|
|
102
|
+
return inputCtrlKeys.length == 1 ? cli.config[inputCtrlKeys[0]] : cli.config
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
parseValidateConfig(ctrlKeys = Object.keys(this.controls)) {
|
|
106
|
+
const language = require('./language')
|
|
107
|
+
for (const key of [].concat(ctrlKeys)) {
|
|
108
|
+
const ctrl = this.controls[key], configVal = cli.config[key]
|
|
109
|
+
|
|
110
|
+
if (ctrl.parser && !ctrl.parsed) {
|
|
111
|
+
cli.config[key] = ctrl.parser(configVal) ; ctrl.parsed = true }
|
|
112
|
+
|
|
113
|
+
if (ctrl.valType) ({
|
|
114
|
+
positiveInt() {
|
|
115
|
+
const numVal = parseInt(configVal, 10)
|
|
116
|
+
if (isNaN(numVal) || numVal < 1)
|
|
117
|
+
log.errorAndExit(`[${key}] ${cli.msgs.error_nonPositiveNum}: ${configVal}`)
|
|
118
|
+
cli.config[key] = numVal
|
|
119
|
+
},
|
|
120
|
+
filepath() {
|
|
121
|
+
if (configVal && !fs.existsSync(configVal))
|
|
122
|
+
log.errorAndExit(`[${key}] ${cli.msgs.error_invalidFilepath}: ${configVal}`)
|
|
123
|
+
},
|
|
124
|
+
langCode() {
|
|
125
|
+
if (configVal && !language.validateLangCode(configVal))
|
|
126
|
+
log.errorAndExit(`[${key}] ${cli.msgs.error_invalidLangCode}: ${configVal}`)
|
|
127
|
+
}
|
|
128
|
+
})[ctrl.valType]()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
package/dist/data/messages.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"appCopyright": { "message": "© 2024–2026 Adam Lui under the MIT license" },
|
|
2
|
+
"pkg_copyright": { "message": "© 2024–2026 Adam Lui under the MIT license" },
|
|
4
3
|
"prefix_globalVer": { "message": "Global version" },
|
|
5
4
|
"prefix_localVer": { "message": "Local version" },
|
|
6
5
|
"prefix_source": { "message": "Source" },
|
|
@@ -11,6 +10,8 @@
|
|
|
11
10
|
"error_invalidLangCode": { "message": "is an invalid language code" },
|
|
12
11
|
"error_invalidURL": { "message": "Invalid URL" },
|
|
13
12
|
"error_invalidConfigFile": { "message": "Config file must export an object" },
|
|
13
|
+
"error_invalidKey": { "message": "Invalid key" },
|
|
14
|
+
"error_foundIn": { "message": "found in" },
|
|
14
15
|
"error_configFileNotFound": { "message": "Config file not found" },
|
|
15
16
|
"error_failedToLoadConfigFile": { "message": "Failed to load config file" },
|
|
16
17
|
"error_failedToFetchGlobalVer": { "message": "Failed to fetch global version" },
|
|
@@ -18,13 +19,20 @@
|
|
|
18
19
|
"error_mustIncludePath": { "message": "must include =path" },
|
|
19
20
|
"warn_configFileExists": { "message": "Config file already exists" },
|
|
20
21
|
"warn_remoteConfigNotFound": { "message": "Remote config file not found" },
|
|
21
|
-
"warn_remoteConfigFailed": { "message": "Failed to fetch remote config file" },
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
22
|
+
"warn_remoteConfigFailed": { "message": "Failed to fetch remote config file" },
|
|
23
|
+
"warn_option": { "message": "Option" },
|
|
24
|
+
"warn_noLongerHasAnyEffect": { "message": "no longer has any effect" },
|
|
25
|
+
"warn_hasBeenReplacedBy": { "message": "has been replaced by" },
|
|
26
|
+
"warn_andWillBeRemoved": { "message": "and will be removed" },
|
|
27
|
+
"warn_notFound": { "message": "Not found" },
|
|
28
|
+
"info_configFile": { "message": "Config file" },
|
|
29
|
+
"info_exampleValidConfigFile": { "message": "Example valid config file" },
|
|
30
|
+
"info_copyingToClip": { "message": "Copying to clipboard" },
|
|
31
|
+
"info_moreHelp": { "message": "For more help" },
|
|
25
32
|
"info_type": { "message": "type" },
|
|
26
33
|
"info_or": { "message": "or" },
|
|
27
34
|
"info_visit": { "message": "visit" },
|
|
35
|
+
"info_toCreateDefaultConfig": { "message": "to create default config file" },
|
|
28
36
|
"info_configFileCreated": { "message": "Config file created" },
|
|
29
37
|
"info_fetchingRemoteConfigFrom": { "message": "Fetching remote config file from" },
|
|
30
38
|
"tip_editToSetDefaults": { "message": "Edit this file to customize defaults" },
|
|
@@ -45,5 +53,7 @@
|
|
|
45
53
|
"optionDesc_quiet": { "message": "Suppress all logging except errors" },
|
|
46
54
|
"optionDesc_init": { "message": "Create config file (in project root)" },
|
|
47
55
|
"optionDesc_help": { "message": "Display help screen" },
|
|
48
|
-
"optionDesc_version": { "message": "Show version number" }
|
|
56
|
+
"optionDesc_version": { "message": "Show version number" },
|
|
57
|
+
"optionDesc_stats": { "message": "Show npm stats" },
|
|
58
|
+
"optionDesc_debug": { "message": "Show debug logs" }
|
|
49
59
|
}
|
package/dist/geolocate.min.js
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
4
|
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
5
|
*/
|
|
6
|
-
async function geolocate(e,n={}){var t
|
|
6
|
+
async function geolocate(e,n={}){var o,t={verbose:!0};_log.prefix="geolocate()",(e=[].concat(e))[0]||=await getOwnIP();for(o of e){n.verbose&&_log.info(`Validating ${o}...`);let t;try{t=require("generate-ip").ipv4.validate}catch(e){await import("https://cdn.jsdelivr.net/npm/generate-ip/dist/generate-ip.min.js"),t=window.ipv4.validate}if(t&&!t(o,{verbose:!1}))return _log.error(o+" is not a valid IPv4 address.")}if(_validateOptions({options:n,defaultOptions:t,helpURL:"https://github.com/adamlui/js-utils/tree/main/geolocate/docs/#locateips-options",exampleCall:"geolocate('8.8.8.8', { verbose: false })"})){n={...t,...n};try{var s,l=[];for(s of e){n.verbose&&_log.info(`Fetching geolocation data for ${s}...`);let e=await fetchData("http://ip-api.com/json/"+s),{status:t,org:o,as:r,query:i,...a}=await e.json();l.push({ip:s,...a})}return n.verbose&&"undefined"!=typeof window&&_log.info("Success!","Check returned array."),l}catch(e){_log.error(e.message)}}}function fetchData(r){return"undefined"==typeof fetch?new Promise((o,t)=>{try{var e=r.match(/^([^:]+):\/\//)[1];/^https?$/.test(e)||t(new Error("Invalid fetchData() URL.")),require(e).get(r,e=>{let t="";e.on("data",e=>t+=e),e.on("end",()=>o({json:()=>JSON.parse(t),text:()=>t}))}).on("error",e=>t(new Error(e.message)))}catch(e){t(new Error("Environment not supported."))}}):fetch(r)}async function getOwnIP(){return fetchData("https://ifconfig.me/ip").then(e=>e.text()).catch(()=>fetchData("http://ip-api.com/json/").then(e=>e.json()).then(e=>e.query)).catch(async()=>{try{var e=require("child_process").exec,t=require("util").promisify,{stdout:o,stderr:r}=await t(e)("curl -s ifconfig.me");return r?_log.error(r):o.trim()}catch(e){_log.error(e.message)}})}function _validateOptions({options:e,defaultOptions:t,helpURL:o,exampleCall:r}){var i,a,n=Object.keys(t).filter(e=>"boolean"==typeof t[e]),s=Object.keys(t).filter(e=>Number.isInteger(t[e]));if("object"!=typeof e)return i=r.split(",").findIndex(e=>e.trim().startsWith("{"))+1,i+=["st","nd","rd"][i-1]||"th",_log.error(`${"0th"==i?"[O":i+" arg [o"}ptions] can only be an object of key/vals.`),_log.info("Example valid call:",r),_log.validOptions(t),_log.helpURL(o),!1;for(a in e){if(!Object.prototype.hasOwnProperty.call(t,a))return _log.error(`\`${a}\` is an invalid option.`),_log.validOptions(t),_log.helpURL(o),!1;if(n.includes(a)&&"boolean"!=typeof e[a])return _log.error(`[${a}] option can only be \`true\` or \`false\`.`),_log.helpURL(o),!1;if(s.includes(a)&&(e[a]=parseInt(e[a],10),isNaN(e[a])||e[a]<1))return _log.error(`[${a}] option can only be an integer > 0.`),_log.helpURL(o),!1}return!0}Object.assign(globalThis.api??={},{name:"geolocate",regex:{geolocate:/^(?:geo)?locate$/i}});let _log={prefix:api.name,error(...e){console.error(this.prefix+" » ERROR:",...e)},helpURL(e=api.urls?.docs){this.info("For more help, please visit",e)},info(...e){console.info(this.prefix+" »",...e)},validOptions(e){var t=Object.keys(e).join(", "),e=JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:").replace(/"/g,"'").replace(/\n\s*/g," ");this.info(`Valid options: [${t}]`),this.info("If omitted, default settings are: "+e)}};api.exports=new Proxy({geolocate:geolocate},{get(e,t){for(var[o,r]of Object.entries(api.regex))if(r.test(t))return e[o]}});try{module.exports=api.exports}catch(e){}try{Object.assign(window,api.exports)}catch(e){}
|
package/docs/README.md
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
<img height=31 src="https://img.shields.io/npm/dm/@adamlui/geolocate?logo=npm&color=af68ff&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
9
9
|
<a href="#%EF%B8%8F-mit-license">
|
|
10
10
|
<img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
11
|
-
<a href="https://github.com/adamlui/js-utils/releases/tag/geolocate-2.
|
|
12
|
-
<img height=31 src="https://img.shields.io/badge/Latest_Build-2.
|
|
11
|
+
<a href="https://github.com/adamlui/js-utils/releases/tag/geolocate-2.3.0">
|
|
12
|
+
<img height=31 src="https://img.shields.io/badge/Latest_Build-2.3.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/@adamlui/geolocate?activeTab=code">
|
|
14
14
|
<img height=31 src="https://img.shields.io/npm/unpacked-size/%40adamlui%2Fgeolocate?style=for-the-badge&logo=ebox&logoColor=white&labelColor=464646&color=blue"></a>
|
|
15
|
-
<a href="
|
|
16
|
-
<img height=31 src="https://img.shields.io/
|
|
15
|
+
<a href="#">
|
|
16
|
+
<img height=31 src="https://img.shields.io/bundlejs/size/%40adamlui%2Fgeolocate%402.2.1?label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
|
|
17
17
|
<a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:geolocate/src/geolocate.js">
|
|
18
18
|
<img height=31 src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fsonarcloud.io%2Fapi%2Fmeasures%2Fcomponent%3Fcomponent%3Dadamlui_js-utils%3Ageolocate%2Fsrc%2Fgeolocate.js%26metricKeys%3Dvulnerabilities&query=%24.component.measures.0.value&style=for-the-badge&logo=sonarcloud&logoColor=white&labelColor=464646&label=Vulnerabilities&color=gold"></a>
|
|
19
19
|
<a href="https://github.com/toolleeo/cli-apps#networking">
|
|
@@ -77,6 +77,8 @@ Commands:
|
|
|
77
77
|
-i, --init Create config file (in project root).
|
|
78
78
|
-h, --help Display help screen.
|
|
79
79
|
-v, --version Show version number.
|
|
80
|
+
--stats Show npm stats.
|
|
81
|
+
--debug [targetKey] Show debug logs.
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
#
|
|
@@ -124,14 +126,14 @@ const geo = require('@adamlui/geolocate')
|
|
|
124
126
|
#### <> HTML script tag:
|
|
125
127
|
|
|
126
128
|
```html
|
|
127
|
-
<script src="https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
129
|
+
<script src="https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js"></script>
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
#### ES6:
|
|
131
133
|
|
|
132
134
|
```js
|
|
133
135
|
(async () => {
|
|
134
|
-
await import('https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
136
|
+
await import('https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js')
|
|
135
137
|
// Your code here...
|
|
136
138
|
})()
|
|
137
139
|
```
|
|
@@ -140,7 +142,7 @@ const geo = require('@adamlui/geolocate')
|
|
|
140
142
|
|
|
141
143
|
```js
|
|
142
144
|
...
|
|
143
|
-
// @require https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.
|
|
145
|
+
// @require https://cdn.jsdelivr.net/npm/@adamlui/geolocate@2.3.0/dist/geolocate.min.js
|
|
144
146
|
// ==/UserScript==
|
|
145
147
|
|
|
146
148
|
// Your code here...
|
|
@@ -148,7 +150,7 @@ const geo = require('@adamlui/geolocate')
|
|
|
148
150
|
|
|
149
151
|
<br>
|
|
150
152
|
|
|
151
|
-
**📝 Note:** To always import the latest version (not recommended in production!) remove the `@2.
|
|
153
|
+
**📝 Note:** To always import the latest version (not recommended in production!) remove the `@2.3.0` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/@adamlui/geolocate/dist/geolocate.min.js`
|
|
152
154
|
|
|
153
155
|
<br>
|
|
154
156
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adamlui/geolocate",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Fetch IP geolocation data from the CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Adam Lui",
|
|
@@ -33,13 +33,14 @@
|
|
|
33
33
|
"docs/"
|
|
34
34
|
],
|
|
35
35
|
"bin": {
|
|
36
|
-
"geolocate": "dist/cli/index.
|
|
36
|
+
"geolocate": "dist/cli/index.js"
|
|
37
37
|
},
|
|
38
38
|
"directories": {
|
|
39
39
|
"lib": "./dist",
|
|
40
40
|
"doc": "./docs"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
+
"dev": "npm run build && npm i -g && geolocate --help",
|
|
43
44
|
"build": "node utils/build",
|
|
44
45
|
"build:js": "node utils/build --js",
|
|
45
46
|
"build:data": "node utils/build --data",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"translate": "translate-messages",
|
|
49
50
|
"bump:patch": "bash utils/bump.sh patch",
|
|
50
51
|
"bump:minor": "bash utils/bump.sh minor",
|
|
52
|
+
"bump:feat": "npm run bump:minor",
|
|
51
53
|
"bump:major": "bash utils/bump.sh major"
|
|
52
54
|
},
|
|
53
55
|
"repository": {
|
|
@@ -72,7 +74,9 @@
|
|
|
72
74
|
"node-clipboardy": "^1.0.3"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
75
|
-
"@adamlui/minify.js": "^2.3.
|
|
76
|
-
"
|
|
77
|
+
"@adamlui/minify.js": "^2.3.1",
|
|
78
|
+
"console-table-printer": "^2.15.0",
|
|
79
|
+
"copyfiles": "^2.4.1",
|
|
80
|
+
"generate-ip": "^2.8.2"
|
|
77
81
|
}
|
|
78
82
|
}
|
package/dist/cli/index.min.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
4
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
5
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
6
|
-
*/
|
|
7
|
-
(async()=>{globalThis.env={args:process.argv.slice(2),devMode:/[\\/]src(?:[\\/]|$)/i.test(__dirname)},env.debugMode=env.args.some(e=>/^--?debug(?:-?mode)?$/.test(e)),env.modExt=`${env.devMode?"":".min"}.js`;var e=require("node-clipboardy"),i=require("../geolocate"+env.modExt),o=require("./lib/init"+env.modExt),r=require("./lib/log"+env.modExt);if(await o.cli(),cli.config.init)return o.configFile();if(cli.config.help)return r.help();if(cli.config.version)return r.version();var n,t=[];for(n of env.args)n.startsWith("-")||t.push(n.replace(/[[\]]/g,""));o=await i.locate(t,{verbose:!cli.config.quietMode});o||process.exit(1),cli.config.quietMode||1!=o.length||r.geoData(o[0]),r.ifNotQuiet(`
|
|
8
|
-
${cli.msgs.info_copyingToClip}...`),e.writeSync(JSON.stringify(o,null,2))})();
|
package/dist/cli/lib/data.min.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
module.exports={atomicWrite(e,r,t="utf8"){var n=require("fs"),a=require("path"),a=a.join(a.dirname(e),`.${a.basename(e)}.tmp`);n.writeFileSync(a,r,t),n.renameSync(a,e)},fetch(n){return"undefined"==typeof fetch?new Promise((t,e)=>{var r=n.match(/^([^:]+):\/\//)[1];/^https?$/.test(r)||e(new Error(cli.msgs.error_invalidURL+".")),require(r).get(n,e=>{let r="";e.on("data",e=>r+=e),e.on("end",()=>t({json:()=>JSON.parse(r)}))}).on("error",e)}):fetch(n)},flatten(e,{key:r="message"}={}){var t,n={};for(t in e)n[t]="object"==typeof e[t]&&r in e[t]?e[t][r]:e[t];return n}};
|
package/dist/cli/lib/init.min.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
let language=require("./language"+env.modExt),log=require("./log"+env.modExt),settings=require("./settings"+env.modExt),dataPath="../../"+(env.devMode?"../":"data/");module.exports={async cli(){Object.assign(globalThis.cli??={},require(dataPath+"package-data.json")),cli.lang=settings.load("uiLang")||(env.debugMode?language.generateRandomLang({excludes:["en"]}):language.getSysLang()),cli.msgs=await language.getMsgs(cli.lang),cli.urls.cliDocs||=cli.urls.docs+"/#-command-line-usage",settings.load()},async configFile(e=settings.configFilename){var t=require("fs"),a=require("path"),i={target:a.resolve(process.cwd(),e)};if(t.existsSync(i.target))return log.warn(cli.msgs.warn_configFileExists+":",i.target);if(t.existsSync(i.src=a.resolve(__dirname,""+dataPath+e)))t.copyFileSync(i.src,i.target);else{a=require("./jsdelivr"+env.modExt).pkgVerURL+`/${e}/`;log.data(cli.msgs.info_fetchingRemoteConfigFrom+` ${a}...`);try{var s=require("./data"+env.modExt),g=await s.fetch(a);if(!g.ok)return log.warn(`${cli.msgs.warn_remoteConfigNotFound}: ${a} (${g.status})`);s.atomicWrite(i.target,await g.text())}catch(e){return log.warn(cli.msgs.warn_remoteConfigFailed+`: ${a} `+e.message)}}log.success(`${cli.msgs.info_configFileCreated}: ${i.target}\n`),log.tip(cli.msgs.tip_editToSetDefaults+"."),log.tip(cli.msgs.tip_cliArgsPrioritized+".")}};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
module.exports={pkgVerURL(e){e||=cli.version||=require("./pkg"+env.modExt).getVer("local")||"none";var l=cli.name.split("/")[1],e=/^\d+\.\d+\.\d+$/.test(e)?l+"-"+e:"latest";return cli.urls.jsdelivr+`@${e}/`+l},commitURL(e="latest"){return cli.urls.jsdelivr+`@${e}/`+cli.name.split("/")[1]}};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
let data=require("./data"+env.modExt),log=require("./log"+env.modExt);module.exports={formatCode(e){return e.replace(/([a-z]{2,8})[-_]([a-z]{2})/i,(e,r,t)=>r.toLowerCase()+"_"+t.toUpperCase())},generateRandomLang({includes:e=[],excludes:r=[]}={}){let a=require("fs"),s=require("path"),t=e.length?e:(()=>{var e=s.join(__dirname,"..",".cache"),r=s.join(e,"locales.json");if(a.existsSync(r))try{return JSON.parse(a.readFileSync(r,"utf8"))}catch(e){}var t=s.resolve(process.cwd(),"_locales");return a.existsSync(t)?(t=a.readdirSync(t,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).filter(e=>/^\w{2}[-_]?\w{0,2}$/.test(e)),a.mkdirSync(e,{recursive:!0}),data.atomicWrite(r,JSON.stringify(t,null,2)),t):["en"]})(),n=new Set(r),l="en";return(t=t.filter(e=>!n.has(e))).length&&(l=t[Math.floor(Math.random()*t.length)]),log.debug(`Random language: ${l}
|
|
7
|
-
`),l},async getMsgs(a="en"){if(a=module.exports.formatCode(a),env.msgs&&a==cli.lang)return env.msgs;let e=data.flatten(require(`../../${env.devMode?"../_locales/en/":"data/"}messages.json`));if(!a.startsWith("en")){var s=require("./jsdelivr"+env.modExt).commitURL(cli.commitHashes.locales)+"/_locales/";let r=s+a+"/messages.json",t=0;for(;t<3;)try{e=data.flatten(await(await data.fetch(r)).json());break}catch(e){if(3<=++t)break;log.debug(r=a.includes("-")&&1==t?r.replace(/([^_]*)_[^/]*(\/.*)/,"$1$2"):s+"en/messages.json")}}return e},getSysLang(){try{var e;return"win32"==process.platform?require("child_process").execSync("(Get-Culture).TwoLetterISOLanguageName",{shell:"powershell",encoding:"utf-8"}).trim():((e=process.env).LANG||e.LANGUAGE||e.LC_ALL||e.LC_MESSAGES||e.LC_NAME).split(".")[0]}catch(e){return log.error(cli.msgs.error_failedToFetchSysLang+":",e.message),"en"}},validateLangCode(e){return"string"==typeof e&&/^[a-z]{2,8}(?:[-_][a-z]{2,3})?$/i.test(e)}};
|
package/dist/cli/lib/log.min.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
module.exports={colors:{nc:"[0m",br:"[1;91m",by:"[1;33m",bo:"[38;5;214m",bg:"[1;92m",bw:"[1;97m",gry:"[90m",blk:"[30m",tlBG:"[106m"},configURL(){this.info(`
|
|
7
|
-
${cli.msgs.info_exampleValidConfigFile}: `+cli.urls.config)},configURLandExit(...o){this.error(...o),this.configURL(),process.exit(1)},data(o){console.log(`
|
|
8
|
-
`+this.colors.bw+o+this.colors.nc)},debug(o){env.debugMode&&console.debug(`
|
|
9
|
-
${this.colors.bo}DEBUG:`,o,this.colors.nc,"\n")},dim(o){console.log(""+this.colors.gry+o+this.colors.nc)},error(...o){console.error(`
|
|
10
|
-
${this.colors.br}ERROR:`,...o,this.colors.nc)},errorAndExit(...o){this.error(...o),this.helpCmdAndDocURL(),process.exit(1)},ifNotQuiet(o){cli.config.quietMode||console.info(o)},info(o){console.info(`
|
|
11
|
-
`+this.colors.by+o+this.colors.nc)},tip(o){console.info(this.colors.by+"TIP: "+o+this.colors.nc)},success(o){console.log(`
|
|
12
|
-
`+this.colors.bg+o+this.colors.nc)},warn(...o){console.warn(`
|
|
13
|
-
${this.colors.bo}WARNING:`,...o,this.colors.nc)},geoData(o){console.info([`
|
|
14
|
-
IP: `+this.colors.bw+o.ip+this.colors.nc,cli.msgs.geoLabel_country+": "+this.colors.bw+o.country+this.colors.nc,cli.msgs.geoLabel_region+": "+this.colors.bw+o.regionName+this.colors.nc,cli.msgs.geoLabel_city+": "+this.colors.bw+o.city+this.colors.nc,cli.msgs.geoLabel_zip+": "+this.colors.bw+o.zip+this.colors.nc,cli.msgs.geoLabel_lat+": "+this.colors.bw+o.lat+this.colors.nc,cli.msgs.geoLabel_lon+": "+this.colors.bw+o.lon+this.colors.nc,cli.msgs.geoLabel_timeZone+": "+this.colors.bw+o.timezone.replace(/_/g," ").replace(/\//g," / ")+this.colors.nc,"ISP: "+this.colors.bw+o.isp+this.colors.nc].join("\n"))},help(o=["header","usage","params","flags","cmds"]){cli.prefix=""+this.colors.tlBG+this.colors.blk+` ${cli.name.replace(/^@[^/]+\//,"")} ${this.colors.nc} `;let s={header:[`
|
|
15
|
-
├ ${cli.prefix}${cli.msgs.appCopyright}.`,""+cli.prefix+cli.msgs.prefix_source+": "+cli.urls.src],usage:[`
|
|
16
|
-
${this.colors.bw}o ${cli.msgs.helpSection_usage}:`+this.colors.nc,` ${this.colors.bw}» `+this.colors.bg+cli.cmdFormat+this.colors.nc],params:[`
|
|
17
|
-
${this.colors.bw}o ${cli.msgs.helpSection_params}:`+this.colors.nc,` --ui-lang="code" ${cli.msgs.optionDesc_uiLang}.`,` --config="path/to/file" ${cli.msgs.optionDesc_config}.`],flags:[`
|
|
18
|
-
${this.colors.bw}o ${cli.msgs.helpSection_flags}:`+this.colors.nc,` -q, --quiet ${cli.msgs.optionDesc_quiet}.`],cmds:[`
|
|
19
|
-
${this.colors.bw}o ${cli.msgs.helpSection_cmds}:`+this.colors.nc,` -i, --init ${cli.msgs.optionDesc_init}.`,` -h, --help ${cli.msgs.optionDesc_help}.`,` -v, --version ${cli.msgs.optionDesc_version}.`]};o.forEach(r=>s[r]?.forEach(s=>{{var e=/header|usage/.test(r)?1:29;let i=process.stdout.columns||80,o=s.match(/\S+|\s+/g),c=[],l="";o.forEach(o=>{var s=i-(c.length?e:0);l.length+"| ".length+o.length>s&&(c.push(c.length?l.trimStart():l),l=""),l+=o}),c.push(c.length?l.trimStart():l),c.forEach((o,s)=>console.info("| "+(0==s?o:" ".repeat(e)+o)))}})),console.info(`
|
|
20
|
-
${cli.msgs.info_moreHelp}, ${cli.msgs.info_visit}: `+this.colors.bw+cli.urls.cliDocs+this.colors.nc)},helpCmdAndDocURL(){console.info(`
|
|
21
|
-
${cli.msgs.info_moreHelp}, ${cli.msgs.info_type} ${cli.name.split("/")[1]} --help' ${cli.msgs.info_or} ${cli.msgs.info_visit}
|
|
22
|
-
`+this.colors.bw+cli.urls.docs+this.colors.nc)},version(){var o=require("./pkg"+env.modExt).getVer;this.info(cli.name),this.data(`${cli.msgs.prefix_globalVer}: ${o("global")||"none"}
|
|
23
|
-
${cli.msgs.prefix_localVer}: `+(o("local")||"none"))}};
|
package/dist/cli/lib/pkg.min.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
let log=require("./log"+env.modExt);module.exports={getVer(e="any"){let r;if("global"!=e)try{var l=require("path").resolve(process.cwd(),"node_modules",cli.name,"package.json");r=require(l).version}catch(e){log.debug(cli.msgs.error_readingLocalPkgVer+": "+e.message)}if("global"==e||"all"==e&&!r)try{r=require("child_process").execSync(`npm view ${JSON.stringify(cli.name)} version`).toString().trim()}catch(e){log.debug(cli.msgs.error_failedToFetchGlobalVer+": "+e.message)}return r}};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* © 2024–2026 Adam Lui & contributors under the MIT license.
|
|
3
|
-
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
|
|
4
|
-
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
|
|
5
|
-
*/
|
|
6
|
-
let fs=require("fs"),log=require("./log"+env.modExt),path=require("path");(globalThis.cli??={}).config={},module.exports={configFilename:"geolocate.config.mjs",controls:{uiLang:{type:"param",valType:"langCode",regex:/^--?ui-?lang(?:=.*|$)/},config:{type:"param",valType:"filepath",regex:/^--?config(?:=.*|$)/},quietMode:{type:"flag",regex:/^--?q(?:uiet)?(?:-?mode)?$/},init:{type:"cmd",regex:/^-{0,2}i(?:nit)?$/},help:{type:"cmd",regex:/^--?h(?:elp)?$/},version:{type:"cmd",regex:/^--?ve?r?s?i?o?n?$/}},load(e=Object.keys(this.controls)){let t=[].concat(e);if(cli.defaultsSet||arguments.length||(t.forEach(e=>{var i=this.controls[e];i.mode||(cli.config[e]??=i.defaultVal??("param"==i.type&&""))}),cli.defaultsSet=!0),!cli.configPathTried){e=env.args.find(e=>this.controls.config.regex.test(e));if(e){/=/.test(e)||log.errorAndExit(`[${e}] `+cli.msgs.error_mustIncludePath);e=e.split("=")[1];cli.configPath=path.isAbsolute(e)?e:path.resolve(process.cwd(),e),fs.existsSync(cli.configPath)||log.configURLandExit(cli.msgs.error_configFileNotFound+":",cli.configPath)}else for(var i of[".mjs",".cjs",".js"]){i=path.resolve(process.cwd(),this.configFilename.replace(/\.[^.]+$/,i));if(fs.existsSync(i)){cli.configPath=i;break}}cli.configPathTried=!0}if(cli.configPath)try{let e=require(cli.configPath),o=e?.default??e;o&&"object"==typeof o||log.configURLandExit(cli.msgs.error_invalidConfigFile+"."),Object.assign(cli.config,arguments.length?t.reduce((e,i)=>o[i]?{...e,[i]:o[i]}:e,{}):o)}catch(e){log.configURLandExit(cli.msgs.error_failedToLoadConfigFile+":",cli.configPath,`
|
|
7
|
-
`+e.message)}return env.args.forEach(i=>{var e,o;/^[^-]|--?(?:config|debug)/.test(i)&&"init"!=i||(!(e=Object.keys(this.controls).find(e=>this.controls[e]?.regex?.test(i)))&&cli.msgs&&log.errorAndExit(`[${i}] ${cli.msgs.error_notRecognized}.`),t.includes(e)&&((o=this.controls[e]).mode?cli.config.mode=e.replace(/mode$/i,"").toLowerCase():cli.config[e]="param"!=o.type||(i.split("=")[1]?.trim()??"")))}),this.parseValidateConfig(t),1==t.length?cli.config[t[0]]:cli.config},parseValidateConfig(e=Object.keys(this.controls)){let t=require("./language"+env.modExt);for(let o of[].concat(e)){let e=this.controls[o],i=cli.config[o];e.parser&&!e.parsed&&(cli.config[o]=e.parser(i),e.parsed=!0),e.valType&&{positiveInt(){var e=parseInt(i,10);(isNaN(e)||e<1)&&log.errorAndExit(`[${o}] ${cli.msgs.error_nonPositiveNum}: `+i),cli.config[o]=e},filepath(){i&&!fs.existsSync(i)&&log.errorAndExit(`[${o}] ${cli.msgs.error_invalidFilepath}: `+i)},langCode(){i&&!t.validateLangCode(i)&&log.errorAndExit(`[${o}] ${cli.msgs.error_invalidLangCode}: `+i)}}[e.valType]()}}};
|