diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index a928418..f42d368 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -1,16 +1,16 @@ // Importing env files here to validate on build -import "./src/env.mjs"; +import './src/env.mjs' import withBundleAnalyzer from '@next/bundle-analyzer' import pkg from './next-i18next.config.js' -import withPWAInit from "@ducanh2912/next-pwa"; -import {withAxiom} from "next-axiom"; +import withPWAInit from '@ducanh2912/next-pwa' +import { withAxiom } from 'next-axiom' const withPWA = withPWAInit({ dest: 'public', fallbacks: { - document: '/_offline', + document: '/_offline' } -}); +}) const { i18n } = pkg @@ -25,16 +25,15 @@ const withMyBundleAnalyzer = withBundleAnalyzer({ */ !process.env.SKIP_ENV_VALIDATION && (await import('./src/env.mjs')) - /** @type {import("next").NextConfig} */ const config = withMyBundleAnalyzer(withPWA( - withAxiom({ - reactStrictMode: true, + withAxiom({ + reactStrictMode: true, - /** Enables hot reloading for local packages without a build step */ - transpilePackages: ["@weatherio/api", "@weatherio/ui", "@weatherio/types"], - i18n, - })) + /** Enables hot reloading for local packages without a build step */ + transpilePackages: ['@weatherio/api', '@weatherio/ui', '@weatherio/types'], + i18n + })) ) -export default config; +export default config diff --git a/apps/web/postcss.config.cjs b/apps/web/postcss.config.cjs index 12a703d..85f717c 100644 --- a/apps/web/postcss.config.cjs +++ b/apps/web/postcss.config.cjs @@ -1,6 +1,6 @@ module.exports = { plugins: { tailwindcss: {}, - autoprefixer: {}, - }, -}; + autoprefixer: {} + } +} diff --git a/apps/web/src/env.mjs b/apps/web/src/env.mjs index b570ce9..dd3ea0e 100644 --- a/apps/web/src/env.mjs +++ b/apps/web/src/env.mjs @@ -1,29 +1,29 @@ -import {createEnv} from "@t3-oss/env-nextjs"; -import {z} from "zod"; +import { createEnv } from '@t3-oss/env-nextjs' +import { z } from 'zod' export const env = createEnv({ shared: { NODE_ENV: z - .enum(["development", "production", "test"]) - .default("development"), - VERCEL_ENV: z.enum(["development", "preview", "production"]).optional(), + .enum(['development', 'production', 'test']) + .default('development'), + VERCEL_ENV: z.enum(['development', 'preview', 'production']).optional(), VERCEL_URL: z .string() .optional() .transform((v) => (v ? `https://${v}` : undefined)), - PORT: z.coerce.number().default(3000), + PORT: z.coerce.number().default(3000) }, /** * Destructure all variables from `process.env` to make sure they aren't tree-shaken away. */ runtimeEnv: { PORT: process.env.PORT, - VERCEL_URL: process.env.VERCEL_URL, + VERCEL_URL: process.env.VERCEL_URL // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, skipValidation: !!process.env.CI || !!process.env.SKIP_ENV_VALIDATION || - process.env.npm_lifecycle_event === "lint", -}); + process.env.npm_lifecycle_event === 'lint' +}) diff --git a/packages/api/env.mjs b/packages/api/env.mjs index 910bdeb..aa2cf4d 100644 --- a/packages/api/env.mjs +++ b/packages/api/env.mjs @@ -1,5 +1,5 @@ -import {createEnv} from "@t3-oss/env-nextjs"; -import {z} from "zod"; +import { createEnv } from '@t3-oss/env-nextjs' +import { z } from 'zod' export const env = createEnv({ server: { @@ -11,8 +11,8 @@ export const env = createEnv({ UPSTASH_RATELIMITER_TIME_INTERVAL: z.string().min(1), RESEND_API_KEY: z.string().min(1), QWEATHER_API_KEY: z.string().min(1), - API_NINJA_API_KEY: z.string().min(1), - }, + API_NINJA_API_KEY: z.string().min(1) + }, client: {}, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, @@ -20,10 +20,10 @@ export const env = createEnv({ UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN, UPSTASH_RATELIMITER_TOKENS_PER_TIME: process.env.UPSTASH_RATELIMITER_TOKENS_PER_TIME, - UPSTASH_RATELIMITER_TIME_INTERVAL: process.env.UPSTASH_RATELIMITER_TIME_INTERVAL, + UPSTASH_RATELIMITER_TIME_INTERVAL: process.env.UPSTASH_RATELIMITER_TIME_INTERVAL, RESEND_API_KEY: process.env.RESEND_API_KEY, QWEATHER_API_KEY: process.env.QWEATHER_API_KEY, - API_NINJA_API_KEY: process.env.API_NINJA_API_KEY, + API_NINJA_API_KEY: process.env.API_NINJA_API_KEY }, - skipValidation: !!process.env.CI || !!process.env.SKIP_ENV_VALIDATION, -}); \ No newline at end of file + skipValidation: !!process.env.CI || !!process.env.SKIP_ENV_VALIDATION +}) diff --git a/packages/city-data/convert.mjs b/packages/city-data/convert.mjs index 75095ea..211d24a 100644 --- a/packages/city-data/convert.mjs +++ b/packages/city-data/convert.mjs @@ -1,80 +1,80 @@ -import fs from "fs"; -import readline from "readline"; -import jsonfile from "jsonfile"; -import {z} from "zod"; -import {citySchema} from "./renamejsonproperties.mjs"; +import fs from 'fs' +import readline from 'readline' +import jsonfile from 'jsonfile' +import { z } from 'zod' +import { citySchema } from './renamejsonproperties.mjs' export const germanNamesSchema = z.array( - z.object({ - geonameid: z.string(), - isolanguage: z.string(), - alternateName: z.string(), - isPreferredName: z.string(), - }), -); + z.object({ + geonameid: z.string(), + isolanguage: z.string(), + alternateName: z.string(), + isPreferredName: z.string() + }) +) const removeDoubleQuotes = (/** @type {string} */ value) => - value.replaceAll('"', ""); + value.replaceAll('"', '') const txtToJson = ( /** @type {string} */ filename, columnNameMapping = {}, - /** @type {jsonfile.Path} */ jsonFilePath, + /** @type {jsonfile.Path} */ jsonFilePath ) => { return new Promise((resolve, reject) => { - console.log(`Converting ${filename}.txt to ${jsonFilePath}`); - const txtFilePath = `./${filename}.txt`; - jsonFilePath = jsonFilePath || `./${filename}.json`; - // @ts-ignore - let entries = [], - i = 0, - lineValues; + console.log(`Converting ${filename}.txt to ${jsonFilePath}`) + const txtFilePath = `./${filename}.txt` + jsonFilePath = jsonFilePath || `./${filename}.json` + // @ts-ignore + const entries = [] + let i = 0 + let lineValues const mappedColumnIndexes = Object.keys(columnNameMapping).map((index) => - parseInt(index), - ); + parseInt(index) + ) readline .createInterface({ input: fs.createReadStream(txtFilePath), output: process.stdout, - terminal: false, + terminal: false }) - .on("line", function (line) { - lineValues = line.split("\t"); + .on('line', function (line) { + lineValues = line.split('\t') if (i !== 0) { entries.push( lineValues.reduce((entry, value, valueIndex) => { if (mappedColumnIndexes.includes(valueIndex)) { - // @ts-ignore + // @ts-ignore entry[columnNameMapping[valueIndex]] = - removeDoubleQuotes(value); + removeDoubleQuotes(value) } - return entry; - }, {}), - ); + return entry + }, {}) + ) } - i++; + i++ }) - .on("close", function () { - console.log(`Writing ${i} entries to ${jsonFilePath}`); + .on('close', function () { + console.log(`Writing ${i} entries to ${jsonFilePath}`) jsonfile.writeFile( jsonFilePath, - // @ts-ignore + // @ts-ignore entries, { spaces: 2 }, function (err) { if (err) { - console.error(err); - reject(err); + console.error(err) + reject(err) } else { - resolve(undefined); + resolve(undefined) } - }, - ); - }); - }); -}; + } + ) + }) + }) +} // geonameid : integer id of record in geonames database // name : name of geographical point (utf8) varchar(200) @@ -97,83 +97,83 @@ const txtToJson = ( // modification date : date of last modification in yyyy-MM-dd format Promise.all([ txtToJson( - "cities1000", + 'cities1000', { - 0: "id", - 8: "country", - 1: "name", - 4: "lat", - 5: "lng", - 10: "admin1", - 11: "admin2", + 0: 'id', + 8: 'country', + 1: 'name', + 4: 'lat', + 5: 'lng', + 10: 'admin1', + 11: 'admin2' }, - "./cities.json", + './cities.json' ), txtToJson( - "admin1CodesASCII", + 'admin1CodesASCII', { - 0: "code", - 1: "name", + 0: 'code', + 1: 'name' }, - "./admin1.json", + './admin1.json' ), txtToJson( - "admin2Codes", + 'admin2Codes', { - 0: "code", - 1: "name", + 0: 'code', + 1: 'name' }, - "./admin2.json", + './admin2.json' ), txtToJson( - "DE", + 'DE', { - 0: "alternateNameId", - 1: "geonameid", - 2: "isolanguage", - 3: "alternateName", - 4: "isPreferredName", - 5: "isShortName", - 6: "isColloquial", - 7: "isHistoric", - 8: "from", - 9: "to", + 0: 'alternateNameId', + 1: 'geonameid', + 2: 'isolanguage', + 3: 'alternateName', + 4: 'isPreferredName', + 5: 'isShortName', + 6: 'isColloquial', + 7: 'isHistoric', + 8: 'from', + 9: 'to' }, - "./DE.json", - ), + './DE.json' + ) ]).then(() => { - console.log("Done creating JSON files"); + console.log('Done creating JSON files') - console.log("Starting to add German names to cities.json"); + console.log('Starting to add German names to cities.json') - const cities = JSON.parse(fs.readFileSync("./cities.json", "utf8")); - const germanNames = JSON.parse(fs.readFileSync("./DE.json", "utf8")); - const parsedCities = citySchema.parse(cities); - const parsedGermanNames = germanNamesSchema.parse(germanNames); + const cities = JSON.parse(fs.readFileSync('./cities.json', 'utf8')) + const germanNames = JSON.parse(fs.readFileSync('./DE.json', 'utf8')) + const parsedCities = citySchema.parse(cities) + const parsedGermanNames = germanNamesSchema.parse(germanNames) // Add the German names to the cities parsedCities.forEach((city) => { const germanNameEntry = parsedGermanNames.find( (entry) => entry.geonameid === city.id && - entry.isolanguage === "de" && - entry.isPreferredName === "1", - ); + entry.isolanguage === 'de' && + entry.isPreferredName === '1' + ) if (germanNameEntry) { // console.debug(germanNameEntry); const germanCity = { ...city, - name: germanNameEntry.alternateName, - }; - parsedCities.push(germanCity); + name: germanNameEntry.alternateName + } + parsedCities.push(germanCity) } - }); + }) // Write the updated cities back to the cities.json file - fs.writeFileSync("./cities.json", JSON.stringify(parsedCities, null, 2), "utf8"); + fs.writeFileSync('./cities.json', JSON.stringify(parsedCities, null, 2), 'utf8') - console.log("Done adding German names to cities.json"); -}); + console.log('Done adding German names to cities.json') +}) diff --git a/packages/city-data/download.mjs b/packages/city-data/download.mjs index 046c342..2ff0dd2 100644 --- a/packages/city-data/download.mjs +++ b/packages/city-data/download.mjs @@ -1,63 +1,63 @@ -import http from "https"; // or 'https' for https:// URLs -import fs from "fs"; -import yauzl from "yauzl"; +import http from 'https' // or 'https' for https:// URLs +import fs from 'fs' +import yauzl from 'yauzl' -const baseURL = "https://download.geonames.org/export/dump/"; +const baseURL = 'https://download.geonames.org/export/dump/' const downloadGeonameFile = (/** @type string */ filename) => { - const writeStream = fs.createWriteStream(filename); - const fileURL = `${baseURL}${filename}`; + const writeStream = fs.createWriteStream(filename) + const fileURL = `${baseURL}${filename}` - console.log(`Downloading ${fileURL}`); + console.log(`Downloading ${fileURL}`) http.get(fileURL, (response) => { - response.pipe(writeStream); - writeStream.on("finish", () => { - writeStream.close(); - console.log(`Download complete: ${filename}`); - }); - }); -}; + response.pipe(writeStream) + writeStream.on('finish', () => { + writeStream.close() + console.log(`Download complete: ${filename}`) + }) + }) +} -downloadGeonameFile("admin1CodesASCII.txt"); -downloadGeonameFile("admin2Codes.txt"); +downloadGeonameFile('admin1CodesASCII.txt') +downloadGeonameFile('admin2Codes.txt') -const txtFilenames = ["cities1000.txt", "DE.txt"]; -const zipFilenames = ["cities1000.zip", "alternatenames/DE.zip"]; -const localeZipFilenames = ["cities1000.zip", "DE.zip"]; +const txtFilenames = ['cities1000.txt', 'DE.txt'] +const zipFilenames = ['cities1000.zip', 'alternatenames/DE.zip'] +const localeZipFilenames = ['cities1000.zip', 'DE.zip'] zipFilenames.forEach((zipFilename, index) => { - const localeZipFilename = localeZipFilenames[index]; + const localeZipFilename = localeZipFilenames[index] if (localeZipFilename === undefined) { - throw new Error(`No locale zip filename found at index ${index}`); + throw new Error(`No locale zip filename found at index ${index}`) } - const zipFile = fs.createWriteStream(localeZipFilename); + const zipFile = fs.createWriteStream(localeZipFilename) http.get(`${baseURL}${zipFilename}`, (response) => { - response.pipe(zipFile); + response.pipe(zipFile) - zipFile.on("finish", () => { - zipFile.close(); - console.log(`Download of ${baseURL}${zipFilename} Completed`); + zipFile.on('finish', () => { + zipFile.close() + console.log(`Download of ${baseURL}${zipFilename} Completed`) yauzl.open(localeZipFilename, { lazyEntries: true }, (err, zipfile) => { - if (err) throw err; - zipfile.readEntry(); - zipfile.on("entry", (entry) => { + if (err) throw err + zipfile.readEntry() + zipfile.on('entry', (entry) => { if (entry.fileName === txtFilenames[index]) { - const txtFile = fs.createWriteStream(entry.fileName); + const txtFile = fs.createWriteStream(entry.fileName) zipfile.openReadStream(entry, (err, readStream) => { if (err) { - throw err; + throw err } - readStream.on("end", function () { - zipfile.readEntry(); - }); - readStream.pipe(txtFile); - }); - console.log(`Extracted ${entry.fileName}`); + readStream.on('end', function () { + zipfile.readEntry() + }) + readStream.pipe(txtFile) + }) + console.log(`Extracted ${entry.fileName}`) } else { - zipfile.readEntry(); + zipfile.readEntry() } - }); - }); - }); - }); -}); + }) + }) + }) + }) +}) diff --git a/packages/city-data/renamejsonproperties.mjs b/packages/city-data/renamejsonproperties.mjs index ef91f6b..3c6c0c4 100644 --- a/packages/city-data/renamejsonproperties.mjs +++ b/packages/city-data/renamejsonproperties.mjs @@ -1,9 +1,9 @@ -import fs from "fs"; -import {z} from "zod"; +import fs from 'fs' +import { z } from 'zod' // Replace 'input.json' with the path to your JSON file. -const filePath = "cities.json"; -const newFilePath = "city-list.json"; +const filePath = 'cities.json' +const newFilePath = 'city-list.json' export const citySchema = z.array( z.object({ @@ -13,47 +13,47 @@ export const citySchema = z.array( admin1: z.string(), admin2: z.string(), lat: z.string(), - lng: z.string(), - }), -); + lng: z.string() + }) +) // Read the JSON file. -fs.readFile(filePath, "utf8", (err, data) => { +fs.readFile(filePath, 'utf8', (err, data) => { if (err) { - console.error("Error reading the file:", err); - return; + console.error('Error reading the file:', err) + return } try { // Parse the JSON data. - const jsonData = JSON.parse(data); - const parsedJsonData = citySchema.parse(jsonData); + const jsonData = JSON.parse(data) + const parsedJsonData = citySchema.parse(jsonData) // Make the lat and lng into coord and numbers and parse the id to a number. And delete lat and lng const updatedJsonData = parsedJsonData.map((city) => { - const { lat, lng, ...rest } = city; + const { lat, lng, ...rest } = city return { ...rest, id: parseInt(city.id), coord: { lat: parseFloat(city.lat), - lon: parseFloat(city.lng), - }, - }; - }); + lon: parseFloat(city.lng) + } + } + }) // Convert the updated JSON data back to a string. - const updatedJson = JSON.stringify(updatedJsonData, null, 2); + const updatedJson = JSON.stringify(updatedJsonData, null, 2) // Write the updated JSON back to the file. - fs.writeFile(newFilePath, updatedJson, "utf8", (err) => { + fs.writeFile(newFilePath, updatedJson, 'utf8', (err) => { if (err) { - console.error("Error writing to the file:", err); + console.error('Error writing to the file:', err) } else { - console.log(`Successfully wrote to the file: '${newFilePath}'`); + console.log(`Successfully wrote to the file: '${newFilePath}'`) } - }); + }) } catch (error) { - console.error("Error parsing JSON:", error); + console.error('Error parsing JSON:', error) } -}); +}) diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js index e450573..b63f692 100644 --- a/tooling/eslint/base.js +++ b/tooling/eslint/base.js @@ -1,44 +1,44 @@ /** @type {import("eslint").Linter.Config} */ const config = { extends: [ - "turbo", - "eslint:recommended", - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - "prettier", + 'turbo', + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + 'prettier' ], env: { es2022: true, - node: true, + node: true }, - parser: "@typescript-eslint/parser", + parser: '@typescript-eslint/parser', parserOptions: { project: true }, - plugins: ["@typescript-eslint", "import"], + plugins: ['@typescript-eslint', 'import'], rules: { - "turbo/no-undeclared-env-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + 'turbo/no-undeclared-env-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } ], - "@typescript-eslint/consistent-type-imports": [ - "warn", - { prefer: "type-imports", fixStyle: "separate-type-imports" }, + '@typescript-eslint/consistent-type-imports': [ + 'warn', + { prefer: 'type-imports', fixStyle: 'separate-type-imports' } ], - "@typescript-eslint/no-misused-promises": [ + '@typescript-eslint/no-misused-promises': [ 2, - { checksVoidReturn: { attributes: false } }, + { checksVoidReturn: { attributes: false } } ], - "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'] }, ignorePatterns: [ - "**/*.config.js", - "**/*.config.cjs", - "**/.eslintrc.cjs", - ".next", - "dist", - "pnpm-lock.yaml", + '**/*.config.js', + '**/*.config.cjs', + '**/.eslintrc.cjs', + '.next', + 'dist', + 'pnpm-lock.yaml' ], - reportUnusedDisableDirectives: true, -}; + reportUnusedDisableDirectives: true +} -module.exports = config; +module.exports = config diff --git a/tooling/eslint/nextjs.js b/tooling/eslint/nextjs.js index 4b2bedd..0950aeb 100644 --- a/tooling/eslint/nextjs.js +++ b/tooling/eslint/nextjs.js @@ -1,10 +1,10 @@ /** @type {import('eslint').Linter.Config} */ const config = { - extends: ["plugin:@next/next/recommended"], + extends: ['plugin:@next/next/recommended'], rules: { - "@next/next/no-html-link-for-pages": "off", - "@typescript-eslint/require-await": "off", - }, -}; + '@next/next/no-html-link-for-pages': 'off', + '@typescript-eslint/require-await': 'off' + } +} -module.exports = config; +module.exports = config diff --git a/tooling/eslint/react.js b/tooling/eslint/react.js index 618e181..bf314c4 100644 --- a/tooling/eslint/react.js +++ b/tooling/eslint/react.js @@ -1,24 +1,24 @@ /** @type {import('eslint').Linter.Config} */ const config = { extends: [ - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:jsx-a11y/recommended", + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:jsx-a11y/recommended' ], rules: { - "react/prop-types": "off", + 'react/prop-types': 'off' }, globals: { - React: "writable", + React: 'writable' }, settings: { react: { - version: "detect", - }, + version: 'detect' + } }, env: { - browser: true, - }, -}; + browser: true + } +} -module.exports = config; +module.exports = config diff --git a/tooling/prettier/index.js b/tooling/prettier/index.js index 9bb9c37..8cbb741 100644 --- a/tooling/prettier/index.js +++ b/tooling/prettier/index.js @@ -1,4 +1,4 @@ -import { fileURLToPath } from "url"; +import { fileURLToPath } from 'url' /** @typedef {import("prettier").Config} PrettierConfig */ /** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */ @@ -7,29 +7,29 @@ import { fileURLToPath } from "url"; /** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ const config = { plugins: [ - "@ianvs/prettier-plugin-sort-imports", - "prettier-plugin-tailwindcss", + '@ianvs/prettier-plugin-sort-imports', + 'prettier-plugin-tailwindcss' ], tailwindConfig: fileURLToPath( - new URL("../../tooling/tailwind/index.ts", import.meta.url), + new URL('../../tooling/tailwind/index.ts', import.meta.url) ), - tailwindFunctions: ["cn", "cva"], + tailwindFunctions: ['cn', 'cva'], importOrder: [ - "", - "^(react/(.*)$)|^(react$)|^(react-native(.*)$)", - "^(next/(.*)$)|^(next$)", - "", - "", - "^@weatherio", - "^@weatherio/(.*)$", - "", - "^[.|..|~]", - "^~/", - "^[../]", - "^[./]", + '', + '^(react/(.*)$)|^(react$)|^(react-native(.*)$)', + '^(next/(.*)$)|^(next$)', + '', + '', + '^@weatherio', + '^@weatherio/(.*)$', + '', + '^[.|..|~]', + '^~/', + '^[../]', + '^[./]' ], - importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], - importOrderTypeScriptVersion: "4.4.0", -}; + importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'], + importOrderTypeScriptVersion: '4.4.0' +} -export default config; +export default config