diff --git a/_api/.gitignore b/_api/.gitignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/_api/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/_api/README.md b/_api/README.md new file mode 100644 index 0000000..1b80694 --- /dev/null +++ b/_api/README.md @@ -0,0 +1,25 @@ +# Chain Registry Deployment Scripts + +### Overview + +This repository contains scripts to deploy static files from the `_api/dist` directory. The scripts perform the following tasks: + +1. Copy original files to the `dist` directory. +2. Update URLs in `chain.json` and `assetlist.json` files. +3. Aggregate `chain.json` files into a single `chains.json` file by extracting specific properties. + +### Usage + +Set the `ROOT_DIR` environment variable to specify the network type (e.g., `testnets`, `devnets`). + +Run the script: + +```sh +pnpm build +``` + +or + +```sh +ROOT_DIR=testnets pnpm build +``` diff --git a/_api/package.json b/_api/package.json new file mode 100644 index 0000000..4206ed9 --- /dev/null +++ b/_api/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "build": "tsx src/main.ts" + }, + "devDependencies": { + "@initia/initia-registry-types": "^0.0.17", + "@tsconfig/recommended": "^1.0.6", + "@types/node": "^20.14.8", + "@types/ramda": "^0.30.0", + "ramda": "^0.30.1", + "tsx": "^4.15.7" + } +} diff --git a/_api/pnpm-lock.yaml b/_api/pnpm-lock.yaml new file mode 100644 index 0000000..e7a0e3a --- /dev/null +++ b/_api/pnpm-lock.yaml @@ -0,0 +1,355 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@initia/initia-registry-types': + specifier: ^0.0.17 + version: 0.0.17 + '@tsconfig/recommended': + specifier: ^1.0.6 + version: 1.0.6 + '@types/node': + specifier: ^20.14.8 + version: 20.14.8 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.0 + ramda: + specifier: ^0.30.1 + version: 0.30.1 + tsx: + specifier: ^4.15.7 + version: 4.15.7 + +packages: + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@initia/initia-registry-types@0.0.17': + resolution: {integrity: sha512-5zBYKH1cU8I5vGUBmRYqAIbvKSJRehUOvUvDy4ohtcz5G7GjNRWvo7x7aXgMSDw5hnxTcqBCaJxsp4i8EcPIxg==} + + '@tsconfig/recommended@1.0.6': + resolution: {integrity: sha512-0IKu9GHYF1NGTJiYgfWwqnOQSlnE9V9R7YohHNNf0/fj/SyOZWzdd06JFr0fLpg1Mqw0kGbYg8w5xdkSqLKM9g==} + + '@types/node@20.14.8': + resolution: {integrity: sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==} + + '@types/ramda@0.30.0': + resolution: {integrity: sha512-DQtfqUbSB18iM9NHbQ++kVUDuBWHMr6T2FpW1XTiksYRGjq4WnNPZLt712OEHEBJs7aMyJ68Mf2kGMOP1srVVw==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.7.5: + resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} + + ramda@0.30.1: + resolution: {integrity: sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsx@4.15.7: + resolution: {integrity: sha512-u3H0iSFDZM3za+VxkZ1kywdCeHCn+8/qHQS1MNoO2sONDgD95HlWtt8aB23OzeTmFP9IU4/8bZUdg58Uu5J4cg==} + engines: {node: '>=18.0.0'} + hasBin: true + + types-ramda@0.30.0: + resolution: {integrity: sha512-oVPw/KHB5M0Du0txTEKKM8xZOG9cZBRdCVXvwHYuNJUVkAiJ9oWyqkA+9Bj2gjMsHgkkhsYevobQBWs8I2/Xvw==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@initia/initia-registry-types@0.0.17': + dependencies: + zod: 3.23.8 + + '@tsconfig/recommended@1.0.6': {} + + '@types/node@20.14.8': + dependencies: + undici-types: 5.26.5 + + '@types/ramda@0.30.0': + dependencies: + types-ramda: 0.30.0 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.7.5: + dependencies: + resolve-pkg-maps: 1.0.0 + + ramda@0.30.1: {} + + resolve-pkg-maps@1.0.0: {} + + ts-toolbelt@9.6.0: {} + + tsx@4.15.7: + dependencies: + esbuild: 0.21.5 + get-tsconfig: 4.7.5 + optionalDependencies: + fsevents: 2.3.3 + + types-ramda@0.30.0: + dependencies: + ts-toolbelt: 9.6.0 + + undici-types@5.26.5: {} + + zod@3.23.8: {} diff --git a/_api/src/aggregateChains.ts b/_api/src/aggregateChains.ts new file mode 100644 index 0000000..fe65d54 --- /dev/null +++ b/_api/src/aggregateChains.ts @@ -0,0 +1,36 @@ +import * as path from "path" +import { ascend, descend, pick, sortWith } from "ramda" +import { Chain } from "@initia/initia-registry-types" +import { readJsonFile, writeJsonFile } from "./utils" + +const selectedChainProperties: (keyof Chain)[] = [ + "chain_id", + "chain_name", + "pretty_name", + "description", + "website", + "apis", + "faucets", + "metadata", + "bech32_prefix", +] + +// Aggregates chain data from all chain.json files in the given directory +export function aggregateChainData(dirs: string[], outputFilePath: string) { + // Collect all chain.json paths + const chainJsonPaths = dirs.map((dir) => path.join(dir, "chain.json")) + + // Pick specific properties from each chain.json file + const chains: Chain[] = chainJsonPaths.map((chainPath) => { + const jsonContent = readJsonFile(chainPath) + return pick(selectedChainProperties, jsonContent) + }) + + // Sort chains: layer 1 first, then by op bridge id + const sortChains = sortWith([ + descend((chain) => chain.metadata?.is_l1 || false), + ascend((chain) => Number(chain.metadata?.op_bridge_id) || Number.MAX_SAFE_INTEGER), + ]) + + writeJsonFile(outputFilePath, sortChains(chains)) +} diff --git a/_api/src/main.ts b/_api/src/main.ts new file mode 100644 index 0000000..c9e066c --- /dev/null +++ b/_api/src/main.ts @@ -0,0 +1,27 @@ +/** + * Step 1: Update URLs in chain.json and assetlist.json files + * Example: + * "https://raw.githubusercontent.com/initia-labs/initia-registry/main/initia/images/INIT.png" + * will be replaced with + * "https://registry.initia.xyz/initia/images/INIT.png" + * + * Step 2: Aggregate all chains into a single chains.json file + * Only specific properties are included in the aggregation. + */ + +import * as path from "path" +import { copyDirectory, deleteDirectory, getFilePathsInDirectory } from "./utils" +import { updateUrlsInDirectory, createUrlReplacer } from "./replaceUrls" +import { aggregateChainData } from "./aggregateChains" + +const rootDir = process.env.ROOT_DIR || "" +const srcDir = path.resolve(__dirname, "../..", rootDir) +const distDir = path.resolve(__dirname, "dist") + +deleteDirectory(distDir) +copyDirectory(srcDir, distDir, { excludes: ["testnets", "devnets", new RegExp("\\."), new RegExp("^_")] }) + +const dirs = getFilePathsInDirectory(distDir) + +/* Step 1 */ updateUrlsInDirectory(dirs, createUrlReplacer(rootDir)) +/* Step 2 */ aggregateChainData(dirs, path.join(distDir, "chains.json")) diff --git a/_api/src/replaceUrls.ts b/_api/src/replaceUrls.ts new file mode 100644 index 0000000..4b9409c --- /dev/null +++ b/_api/src/replaceUrls.ts @@ -0,0 +1,49 @@ +import { mapObjIndexed, replace } from "ramda" +import { getFilePathsInDirectory, isDirectory, readJsonFile, writeJsonFile } from "./utils" + +// Creates a URL replacer function based on the given network type +// Note: For mainnets, rootDir should be an empty string. +export function createUrlReplacer(rootDir: string) { + const baseUrls: Record = { + mainnets: "https://registry.initia.xyz", + testnets: "https://registry.testnet.initia.xyz", + devnets: "https://registry.devnet.initia.xyz", + } + + const regex = new RegExp(`https:\/\/raw\.githubusercontent\.com\/initia-labs\/initia-registry\/main\/${rootDir}`) + const baseUrl = baseUrls[rootDir || "mainnets"] + return replace(regex, baseUrl) +} + +// Recursively updates URLs in the given object using the replacer function +function updateUrlsInObject(obj: any, replacer: (url: string) => string): any { + if (Array.isArray(obj)) { + return obj.map((item) => updateUrlsInObject(item, replacer)) + } + + if (typeof obj === "object" && obj !== null) { + return mapObjIndexed((value) => { + if ( + typeof value === "string" && + value.startsWith("https://raw.githubusercontent.com/initia-labs/initia-registry") + ) + return replacer(value) + + return updateUrlsInObject(value, replacer) + }, obj) + } + + return obj +} + +// Recursively processes all JSON files in the given directory, updating their URLs +export function updateUrlsInDirectory(dirs: string[], replacer: (url: string) => string) { + for (const dir of dirs) { + for (const filePath of getFilePathsInDirectory(dir)) { + if (isDirectory(filePath)) continue // Skip `images` directory + const jsonContent = readJsonFile(filePath) + const modifiedContent = updateUrlsInObject(jsonContent, replacer) + writeJsonFile(filePath, modifiedContent) + } + } +} diff --git a/_api/src/utils.ts b/_api/src/utils.ts new file mode 100644 index 0000000..1815e76 --- /dev/null +++ b/_api/src/utils.ts @@ -0,0 +1,59 @@ +import * as fs from "fs" +import * as path from "path" + +// Create a folder +export function createDirectory(dir: string) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } +} + +// Delete a folder +export function deleteDirectory(dir: string) { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true }) + } +} + +// Checks if the given path is a directory +export function isDirectory(filePath: string): boolean { + return fs.statSync(filePath).isDirectory() +} + +// Copies the source directory to the destination directory +export function copyDirectory(srcDir: string, distDir: string, { excludes }: { excludes: (string | RegExp)[] }) { + const srcFolders = fs.readdirSync(srcDir).filter((folder) => { + return !excludes.some((exclude) => { + if (typeof exclude === "string") return folder === exclude + return exclude.test(folder) + }) + }) + + for (const srcFolder of srcFolders) { + const srcPath = path.join(srcDir, srcFolder) + const destPath = path.join(distDir, srcFolder) + + if (isDirectory(srcPath)) { + createDirectory(destPath) + copyDirectory(srcPath, destPath, { excludes: [new RegExp("^_")] }) + } else { + fs.copyFileSync(srcPath, destPath) + } + } +} + +// Returns an array of full paths for all files in the given directory +export function getFilePathsInDirectory(dir: string): string[] { + return fs.readdirSync(dir).map((file) => path.join(dir, file)) +} + +// Reads a JSON file and returns its content as an object +export function readJsonFile(filePath: string): any { + const content = fs.readFileSync(filePath, "utf-8") + return JSON.parse(content) +} + +// Writes the given data object to a JSON file +export function writeJsonFile(filePath: string, data: any) { + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)) +} diff --git a/_api/tsconfig.json b/_api/tsconfig.json new file mode 100644 index 0000000..97cb33b --- /dev/null +++ b/_api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/recommended/tsconfig.json" +}