@fruitsbytes/country-list-ts
Version:
It contains country information including 2 and 3 character ISO codes, country and capital names, currency information, telephone calling codes, and provinces ( first-tier political subdivisions).
457 lines (398 loc) • 15.7 kB
JavaScript
#! /usr/bin/env node
import chalk from "chalk";
import {existsSync, mkdirSync, statSync, truncateSync, writeFileSync, appendFileSync, readFileSync} from "fs";
import shelljs from "shelljs";
import {parse} from "path";
import cliSpinners from "cli-spinners";
import ora from 'ora';
import camelCase from "lodash.camelcase";
import snakeCase from "lodash.snakecase";
import startCase from "lodash.startcase";
const {exec, exit, cp, rm, ls} = shelljs;
function pascalCase(str) {
return startCase(camelCase(str)).replace(/ /g, '');
}
console.log(chalk.blackBright('' +
' &&&&&&&&## \n' +
' &&#BBGGGBB#&& &B55Y555555YJ5 \n' +
' &#P5YYYYYYYYYYYYY5G#& PJ5GBGGPP55YJP \n' +
' #PYJJJJ??????JJJYY555YYYP55#BY?7777??JG \n' +
' #5JJ?7777777????????JY5YYYYY5Y77??????J??5B& \n' +
' GJ?77: .. ~7???????77. .YJ??777777?YYJ?77?Y# \n' +
' GJ??: :7!~. ~??!!!! J?777777777!!!7JP# \n' +
'#J???: ^J. ~??. .. YYJ?77?77?JYP#& \n' +
'5????: ^J^. ~???????7 Y555YP \n' +
'Y????: .^!J. ~???????7 Y5YYYP \n' +
'5????: ^J. ~???????7 J5PGBB& \n' +
'&????: .^!J. ~???????7 P#&###BB#& \n' +
' B7??!^. ... :^7???????7 7#J?Y5!5G775 \n' +
' B?7??!~~~~~~~7??????????!77BP.:~P.YP ::& \n' +
' &5?7??????????????????J5555B5YPGJPGJJG \n' +
' &GJ?77???????????JYYYYYPB &##BBB& \n' +
' &BPYJJ??JJJYY55PG#& \n' +
' &&&&&###&&& \n'));
const OK = "✅ ";
const ERROR = "❌ ";
function printError(message) {
console.log(ERROR + chalk.redBright(` ${message}`));
}
function printSuccess(message) {
console.log(OK + chalk.green(` ${message}`));
}
function printStatic(message) {
process.stdout.write(chalk.grey('- ' + message + '\r'));
}
function printLog(message) {
console.log(chalk.grey('- ' + message));
}
console.log(chalk.bgCyan.black.bold(' <FruitsBytes> '));
console.log("Country list - Building data and assets");
const countries_file = './src/data/countries.ts';
const path_to_dependency = "./node_modules/world-countries";
if (!existsSync(path_to_dependency)) {
const spinner = ora({
color: "blue",
spinner: cliSpinners.dots,
text: "Installing dependency " + chalk.bold("world-countries") + "...."
});
spinner.start();
if (exec('npm i -D mledoze/countries').code !== 0) {
printError("Could not install dependency: " + chalk.bold("world-countries") + ".");
spinner.stop();
exit(1);
}
spinner.stop();
}
function prepareFile(path) {
const parts = parse(path);
try {
statSync(path);
printLog(`Found ${path}...`);
truncateSync(path, 0);
printLog(`Cleared ${path}...`)
} catch (e) {
const dir = parts.dir;
if (!existsSync(dir)) {
mkdirSync(dir, '0744');
}
}
try {
writeFileSync(path, `// ${parts.name} file`);
} catch (e) {
printError(`Could not create ${parts.name} file.`);
exit(1);
}
}
try {
prepareFile(countries_file);
const interfaceCountry = `
export type Mapped< K extends string, V> = Partial<{ [k in K] : V }>
export interface Currency{ name : CurrencyName; symbol : CurrencySymbol; }
export interface Name {
common: string;
official: string;
native: Mapped<Lang , {official : string;common : string;}>;
}
export type LatLong = [number, number];
export interface InternationalDirectDialing{
root : string;
suffixes : string[];
}
export interface Country{
name: Name;
tld : string[];
cca2 : CCA2;
ccn3 : CCN3;
cca3 : CCA3;
cioc : CIOC;
independent : boolean;
status : CountryStatus;
unMember : boolean;
currencies : Mapped<CurrencyCode , Currency> | [];
idd : InternationalDirectDialing;
capital : Capital[];
altSpellings : string[];
region : Region;
subregion : SubRegion;
languages : Mapped<Lang,LanguageName>;
translations : Mapped<Lang, { official : string; common : string; }>;
latlng : LatLong;
demonyms : Mapped<Lang, {
f : string;
m : string;
}>;
landlocked : boolean;
borders : CCA3[];
area : number,
callingCodes : string[]
flag : string;
}`;
const dataSourcePath = "./src/data/countries-unescaped.json";
cp(path_to_dependency + "/dist/countries-unescaped.json", dataSourcePath);
let countriesString = readFileSync(dataSourcePath, 'utf8');
const countriesObject = JSON.parse(countriesString);
let lists = {
cca2: [],
tld: [],
ccn3: [],
cca3: [],
cioc: [],
countryStatus: [],
commonName: [],
officialName: [],
currencyCode: [],
currencyName: [],
currencySymbol: [],
region: [],
subRegion: [],
capital: [],
lang: [],
languageName: []
}
let maps = {
name: {},
tld: {},
ccn3: {},
cca3: {},
cioc: {},
independent: {},
idd: {},
countryStatus: {},
currencies: {},
region: {},
subRegion: {},
capitals: {},
languages: {},
translations: {},
latLng: {},
landlocked: {},
borders: {},
area: {},
flag: {},
demonyms: {},
callingCodes: {}
}
let fatList = {
name: [],
tld: [],
ccn3: [],
cca3: [],
cioc: [],
independent: [],
idd: [],
countryStatus: [],
currencies: [],
region: [],
subRegion: [],
capitals: [],
languages: [],
translations: [],
latLng: [],
landlocked: [],
borders: [],
area: [],
flag: [],
demonyms: [],
callingCodes: []
}
let listArraysAnInterface = "\n";
for (const c of countriesObject) {
lists.commonName.push(c.name.common);
lists.officialName.push(c.name.official);
lists.tld.push(...c.tld);
lists.cca2.push(c.cca2);
lists.ccn3.push(c.ccn3);
lists.cca3.push(c.cca3);
lists.cioc.push(c.cioc);
lists.countryStatus.push(c.status);
lists.region.push(c.region);
lists.subRegion.push(c.subregion);
lists.capital.push(...c.capital);
for (const [i, v] of Object.entries(c.currencies)) {
lists.currencyCode.push(i);
lists.currencyName.push(v.name);
lists.currencySymbol.push(v.symbol)
}
for (const [i, v] of Object.entries(c.languages)) {
lists.lang.push(i);
lists.languageName.push(v);
}
for (const [i] of Object.entries(c.translations)) {
lists.lang.push(i);
}
maps.name[c.cca2] = c.name;
maps.tld[c.cca2] = c.tld;
maps.ccn3[c.cca2] = c.ccn3;
maps.cca3[c.cca2] = c.cca3;
maps.cioc[c.cca2] = c.cioc;
maps.independent[c.cca2] = c.independent;
maps.idd[c.cca2] = c.idd;
maps.countryStatus[c.cca2] = c.status;
maps.currencies[c.cca2] = c.currencies;
maps.region[c.cca2] = c.region;
maps.subRegion[c.cca2] = c.subregion;
maps.capitals[c.cca2] = c.capital;
maps.languages[c.cca2] = c.languages;
maps.translations[c.cca2] = c.translations;
maps.latLng[c.cca2] = c.latlng;
maps.landlocked[c.cca2] = c.landlocked;
maps.borders[c.cca2] = c.borders;
maps.area[c.cca2] = c.area;
maps.flag[c.cca2] = c.flag;
maps.demonyms[c.cca2] = c.demonyms;
maps.callingCodes[c.cca2] = c.callingCodes;
fatList.name.push({name: c.name, cca2: c.cca2});
fatList.tld.push({tld: c.tld, cca2: c.cca2});
fatList.ccn3.push({ccn3: c.ccn3, cca2: c.cca2});
fatList.cca3.push({cca3: c.cca3, cca2: c.cca2});
fatList.cioc.push({cioc: c.cioc, cca2: c.cca2});
fatList.independent.push({independent: c.independent, cca2: c.cca2});
fatList.idd.push({idd: c.idd, cca2: c.cca2});
fatList.countryStatus.push({countryStatus: c.status, cca2: c.cca2});
fatList.currencies.push({currencies: c.currencies, cca2: c.cca2});
fatList.region.push({region: c.region, cca2: c.cca2});
fatList.subRegion.push({subRegion: c.subregion, cca2: c.cca2});
fatList.capitals.push({capitals: c.capital, cca2: c.cca2});
fatList.languages.push({languages: c.languages, cca2: c.cca2});
fatList.translations.push({translations: c.translations, cca2: c.cca2});
fatList.latLng.push({latLng: c.latlng, cca2: c.cca2});
fatList.landlocked.push({landlocked: c.landlocked, cca2: c.cca2});
fatList.borders.push({borders: c.borders, cca2: c.cca2});
fatList.area.push({area: c.area, cca2: c.cca2});
fatList.flag.push({flag: c.flag, cca2: c.cca2});
fatList.demonyms.push({demonyms: c.demonyms, cca2: c.cca2});
fatList.callingCodes.push({callingCodes: c.callingCodes, cca2: c.cca2});
}
listArraysAnInterface += `
export const NamesMap= ${JSON.stringify(maps.name)};
export const NamesFatList= ${JSON.stringify(fatList.name)};
export const TLDMap= ${JSON.stringify(maps.tld)};
export const TLDFatList= ${JSON.stringify(fatList.tld)};
export const CCN3Map= ${JSON.stringify(maps.ccn3)};
export const CCN3FatList= ${JSON.stringify(fatList.ccn3)};
export const CCA3Map= ${JSON.stringify(maps.cca3)};
export const CCA3FatList= ${JSON.stringify(fatList.cca3)};
export const CIOCMap= ${JSON.stringify(maps.cioc)};
export const CIOCFatList= ${JSON.stringify(fatList.cioc)};
export const IndependentMap= ${JSON.stringify(maps.independent)};
export const IndependentFatList= ${JSON.stringify(fatList.independent)};
export const IDDMap= ${JSON.stringify(maps.idd)};
export const IDDFatList= ${JSON.stringify(fatList.idd)};
export const CountryStatusMap= ${JSON.stringify(maps.countryStatus)};
export const CountryStatusFatList= ${JSON.stringify(fatList.countryStatus)};
export const CurrenciesMap= ${JSON.stringify(maps.currencies)};
export const CurrenciesFatList= ${JSON.stringify(fatList.currencies)};
export const RegionMap= ${JSON.stringify(maps.region)};
export const RegionFatList= ${JSON.stringify(fatList.region)};
export const SubRegionMap= ${JSON.stringify(maps.subRegion)};
export const SubRegionFatList= ${JSON.stringify(fatList.subRegion)};
export const CapitalsMap= ${JSON.stringify(maps.capitals)};
export const CapitalsFatList= ${JSON.stringify(fatList.capitals)};
export const LanguagesMap= ${JSON.stringify(maps.languages)};
export const LanguagesFatList= ${JSON.stringify(fatList.languages)};
export const TranslationsMap= ${JSON.stringify(maps.translations)};
export const TranslationsFatList= ${JSON.stringify(fatList.translations)};
export const LatLngMap= ${JSON.stringify(maps.latLng)};
export const LatLngFatList= ${JSON.stringify(fatList.latLng)};
export const LandlockedMap= ${JSON.stringify(maps.landlocked)};
export const LandlockedFatList= ${JSON.stringify(fatList.landlocked)};
export const BordersMap= ${JSON.stringify(maps.borders)};
export const BordersFatList= ${JSON.stringify(fatList.borders)};
export const AreaMap= ${JSON.stringify(maps.area)};
export const AreaFatList= ${JSON.stringify(fatList.area)};
export const DemonymsMap= ${JSON.stringify(maps.demonyms)};
export const DemonymsFatList= ${JSON.stringify(fatList.demonyms)};
export const FlagsMap= ${JSON.stringify(maps.flag)};
export const FlagsFatList= ${JSON.stringify(fatList.flag)};
export const CallingCodesMap= ${JSON.stringify(maps.callingCodes)};
export const CallingCodesFatList= ${JSON.stringify(fatList.callingCodes)};
`;
function removeDups(ar) {
return [...new Set(ar)];
}
let listable = "\n\n export type Listable = 'default'";
let list_types = "\n\n export type List = [] ";
let mappable = "\n\n export type Mappable = 'default'";
for (const listsKey in lists) {
listable += ` | '${listsKey}'`;
lists[listsKey] = removeDups(lists[listsKey]);
const arrayName = (snakeCase(listsKey) + "_ARRAY").toUpperCase();
const typeName = ['cca2', 'tld', 'ccn3', 'cca3', 'cioc', 'idd'].includes(listsKey) ? listsKey.toUpperCase() : pascalCase(listsKey);
listArraysAnInterface += `\n export const ${arrayName} = ${JSON.stringify(lists[listsKey])};`
listArraysAnInterface += `\n export type ${typeName} = typeof ${arrayName}[number];`;
list_types += ` | Array<${typeName}>`;
}
listable += ";"
list_types += ";"
for (const mapsKey in maps) {
mappable += ` | '${mapsKey}'`;
}
mappable += ";";
rm('-rf', dataSourcePath);
appendFileSync(countries_file,
listArraysAnInterface +
interfaceCountry +
listable +
list_types +
mappable +
"\n\n export const countries: Array<Country>=" + countriesString + ";"
);
printSuccess('Country Data file was created successfully.');
} catch
(e) {
// console.error(e)
printError('Could not fill countries file.');
exit(1);
}
try {
const flagsIndex = './src/data/flags/index.ts';
const topoIndex = './src/data/topo-json/index.ts';
const geoIndex = './src/data/geo-json/index.ts';
printLog("Indexing flags...");
prepareFile(flagsIndex);
cp(path_to_dependency + '/data/*.svg', "./src/data/flags");
ls('./src/data/flags/*.svg').forEach((file) => {
const {base, name} = parse(file);
appendFileSync(
flagsIndex,
`
import ${name} from "./${base}";
export const ${name}_flag = ${name};`
);
});
printSuccess(chalk.bold("flag") + " indexed.");
printLog("Indexing geo-json...");
prepareFile(geoIndex);
cp(path_to_dependency + '/data/*.geo.json', "./src/data/geo-json");
ls('./src/data/geo-json/*.geo.json').forEach((file) => {
const {base, name: _name} = parse(file);
const name = _name.replace(".geo","");
appendFileSync(
geoIndex,
`
import ${name} from "./${base}";
export const ${name}_geo_json = ${name};`
);
});
printSuccess(chalk.bold("geo-json") + " indexed.");
printLog("Indexing topo.json...");
prepareFile(topoIndex);
cp(path_to_dependency + '/data/*.topo.json', "./src/data/topo-json");
ls('./src/data/topo-json/*.topo.json').forEach((file) => {
const {base, name: _name} = parse(file);
const name = _name.replace(".topo","");
appendFileSync(
topoIndex,
`
import ${name} from "./${base}";
export const ${name}_topo_json = ${name};`
);
});
printSuccess(chalk.bold("topo-json") + " indexed.");
} catch (e) {
console.error(e)
printError('Could not fill index flags.');
exit(1);
}
console.log(chalk.bold.greenBright("\n✨ Done."));
console.log(chalk.bgCyan.black.bold(' </FruitsBytes> '));