mimic-tree
Version:
mimic-tree is a Node.js library for creating dynamic data trees that mimic the structure of source data. It reproduces the original folder paths and file structures like JSON or YAML with different values, maintaining the integrity of the original structu
1 lines • 18.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/lib/type.ts","../src/lib/loader.ts","../src/lib/clone.ts","../src/lib/export.ts","../src/index.ts"],"names":["convert","key","promises","resolve"],"mappings":"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","sourcesContent":["export type Extension = 'json' | 'yaml'\n\nexport type Primitive = string | number | boolean | null | undefined\n\nexport function isPrimitive(value: unknown): value is Primitive {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean' ||\n value === null ||\n value === undefined\n )\n}\n\nexport function isPureObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\ntype Pattern = string\n\n/**\n *\n * @params value\n * @params context\n * @returns Primitive | Promise<Primitive>\n */\nexport type Converter = (\n value: Primitive,\n context: ConvertContext\n) => Primitive | Promise<Primitive>\n\n/**\n *\n */\nexport interface Config {\n /**\n *\n * @default process.cwd()\n */\n cwd?: string\n\n /**\n *\n */\n src: string\n\n /**\n *\n */\n dist: string\n\n /**\n *\n */\n ignore?: Pattern[]\n\n /**\n *\n * @param value\n * @param context\n */\n convert: Converter\n\n /**\n *\n * @default ['json']\n */\n extensions?: Extension[]\n\n /**\n *\n * @default true\n */\n cache?: boolean\n\n /**\n * @default false\n */\n parallel?: number | false\n}\n\nexport const defaultConfig = {\n cwd: process.cwd(),\n extensions: ['json'],\n cache: true,\n parallel: false\n} satisfies Omit<Config, 'src' | 'dist' | 'convert'>\n\nexport type FixedConfig = Config & typeof defaultConfig\n\nexport interface ConvertContext {\n /**\n *\n */\n key: string\n\n /**\n *\n */\n value: Primitive\n\n /**\n *\n */\n file: FileContext\n}\n\nexport interface FileContext {\n /**\n *\n */\n filePath: string\n\n /**\n *\n */\n fileName: string\n\n /**\n *\n */\n extension: Extension\n\n /**\n *\n */\n outputFile: string\n\n /**\n *\n */\n json: unknown\n}\n\nexport interface MimicTreeContxt {\n /**\n *\n */\n files: FileContext[]\n\n /**\n *\n */\n config: FixedConfig\n}\n","import type { MimicTreeContxt, FixedConfig, Extension } from './type'\nimport { resolve } from 'path'\nimport { promises } from 'fs'\nimport globby from 'globby'\nimport { FileContext } from './type'\n\nconst { readFile } = promises\n\nexport async function loadContext(config: FixedConfig): Promise<MimicTreeContxt> {\n const baseDir = resolve(config.cwd ?? process.cwd(), config.src)\n\n const [founds, ignores] = await Promise.all([\n globFiles(baseDir, config),\n ignoreFiles(baseDir, config)\n ])\n\n const files = founds.filter((file) => !ignores.includes(file))\n\n return {\n files: await loadAllFiles(baseDir, files, config),\n config\n }\n}\n\nconst extensionAliases = {\n yaml: ['yml', 'yaml'],\n json: ['json']\n} satisfies Record<Extension, string[]>\nObject.freeze(extensionAliases)\n\nconst extensions = Object.keys(extensionAliases) as Extension[]\nObject.freeze(extensions)\n\nfunction globFiles(baseDir: string, config: FixedConfig): Promise<string[]> {\n return globby(baseDir, {\n expandDirectories: {\n extensions: (config.extensions ?? ['json']).flatMap((ext) => extensionAliases[ext])\n },\n gitignore: false,\n cwd: config.cwd\n })\n}\n\nasync function ignoreFiles(baseDir: string, config: FixedConfig): Promise<string[]> {\n if ((config.ignore ?? []).length <= 0) {\n return []\n }\n\n return globby(config.ignore!, {\n gitignore: true,\n cwd: config.cwd\n })\n}\n\nasync function loadAllFiles(\n baseDir: string,\n files: string[],\n config: FixedConfig\n): Promise<FileContext[]> {\n const loader = new JsonLoader(baseDir, config)\n await loader.init()\n\n const load = async (filePath: string) => loader.load(filePath)\n\n const results = await Promise.all(files.map(load))\n\n if (results.some((result) => result.error)) {\n throw new Error()\n }\n\n const outputBaseDir = resolve(config.cwd ?? process.cwd(), config.dist)\n console.log(outputBaseDir)\n\n return results.map((result) => ({\n ...(result as Omit<FileContext, 'outputFile'>),\n outputFile: resolve(outputBaseDir, result.filePath, result.fileName).replace(outputBaseDir, '.')\n }))\n}\n\ntype LoadJsonResult = Omit<FileContext, 'outputFile' | 'json'> & { json?: unknown; error?: string }\n\ntype Parser = (raw: string) => unknown\n\nclass JsonLoader {\n constructor(\n private readonly baseDir: string,\n private readonly config: FixedConfig\n ) {}\n\n #parsers: Partial<Record<Extension, Parser>> = {\n json: (raw) => JSON.parse(raw)\n }\n\n async init() {\n if (this.config.extensions?.includes('yaml')) {\n this.#parsers['yaml'] = await this.#createYamlParser()\n }\n }\n\n async #createYamlParser(): Promise<Parser> {\n const { parse } = await import('yaml')\n return (raw) => parse(raw, { prettyErrors: true })\n }\n\n #assertSupportedExtension(ext: string): asserts ext is Extension {\n if (!(this.config.extensions ?? ['json']).includes(ext as Extension)) {\n throw new Error(`Unsupported file extension: ${ext}`)\n }\n }\n\n #getPath(fullpath: string): string {\n return (\n './' +\n fullpath\n .replace(/^\\.\\//, '')\n .replace(new RegExp(`^${this.baseDir.replace(/^\\.\\//, '')}/`), '')\n .replace(getFileName(fullpath), '')\n )\n }\n\n #parse(raw: string, ext: Extension): unknown {\n const parser = this.#parsers[ext]\n if (!parser) {\n throw new Error(`Unsupported file extension: ${ext}`)\n }\n return parser(raw)\n }\n\n #errorToString(e: unknown): string {\n if (e instanceof Error) {\n return `${e.name}: ${e.message}`\n }\n return String(e)\n }\n\n async load(filePath: string): Promise<LoadJsonResult> {\n const importPath = resolve(this.baseDir, filePath)\n const fileName = getFileName(filePath)\n const extension = getExtension(filePath)\n this.#assertSupportedExtension(extension)\n const fixedFilePath = this.#getPath(filePath)\n\n const raw = await readFile(importPath, 'utf-8')\n\n try {\n return {\n filePath: fixedFilePath,\n fileName,\n extension: extension,\n json: this.#parse(raw, extension)\n }\n } catch (e) {\n return {\n filePath: fixedFilePath,\n fileName,\n extension,\n error: this.#errorToString(e)\n }\n }\n }\n}\n\nfunction getExtension(filePath: string): Extension {\n const ext = filePath.split('.').pop() ?? ''\n const found = Object.entries(extensionAliases).find(([, aliases]) =>\n (aliases as readonly string[]).includes(ext)\n )\n return (found?.[0] as Extension) ?? ext\n}\n\nfunction getFileName(filePath: string): string {\n return filePath.split('/').pop() ?? ''\n}\n","import {\n FixedConfig,\n ConvertContext,\n FileContext,\n isPrimitive,\n isPureObject,\n Primitive\n} from './type'\nimport { PromisePool } from '@supercharge/promise-pool'\n\nconst ROOT_KEY = '[ROOT]'\n\nexport async function convert(fileContext: FileContext, config: FixedConfig): Promise<FileContext> {\n const convertTargets = convertFlattenKV('', fileContext.json)\n\n const { results, errors } = await createWorker(config)\n .for(convertTargets)\n .process(async ({ key, value }) => {\n const { convert } = config\n const context: ConvertContext = { key, value, file: fileContext }\n const result = await convert(value, context)\n\n return { key, value: result }\n })\n\n if (errors.length > 0) {\n throw errors[0]\n }\n\n return {\n ...fileContext,\n json: toObject(results)\n }\n}\n\n/**\n *\n * @param keyPrefix\n * @param value\n */\nfunction convertFlattenKV(\n keyPrefix: string,\n value: unknown\n): Array<{ key: string; value: Primitive }> {\n if (isPrimitive(value)) {\n // root の場合 keyPrefix は空文字列\n return [{ key: keyPrefix || ROOT_KEY, value }]\n }\n\n if (Array.isArray(value)) {\n return value.flatMap((v, i) => convertFlattenKV(`${keyPrefix}[${i}]`, v))\n }\n\n if (isPureObject(value)) {\n const requiredDot = keyPrefix !== '' && !keyPrefix.endsWith(']')\n\n return Object.entries(value).flatMap(([k, v]) =>\n convertFlattenKV(`${keyPrefix}${requiredDot ? '.' : ''}${k}`, v)\n )\n }\n\n return []\n}\n\n/**\n *\n * @param flattenKVs\n */\nfunction toObject(flattenKVs: Array<{ key: string; value: Primitive }>): unknown {\n if (flattenKVs.length <= 1) {\n return undefined\n }\n\n if (flattenKVs.length === 1 && flattenKVs[0]!.key === ROOT_KEY) {\n return flattenKVs[0]!.value\n }\n\n if (flattenKVs[0]!.key.startsWith('[')) {\n // 先に sort しているので、index 順になっている\n const indexes = flattenKVs\n .map(({ key }) => key.match(/^\\[(\\d+)\\]/)![1]!)\n .map(Number)\n .filter((v, i, self) => self.indexOf(v) === i)\n\n const pickByIndex = (index: number) =>\n flattenKVs.filter(({ key }) => key.startsWith(`[${index}]`))\n\n return indexes.reduce((acc, index) => {\n const picked = pickByIndex(index).map(({ key, value }) => ({\n key: key.replace(/^\\[\\d+\\]/, ''),\n value\n }))\n return [...acc, toObject(picked)]\n }, [] as unknown[])\n }\n\n const keys = flattenKVs\n .map(({ key }) => (key.includes('.') ? key.split('.')[0]! : key))\n .filter((v, i, self) => self.indexOf(v) === i)\n\n return keys.reduce((acc, key) => {\n const exactMatch = flattenKVs.find(({ key: k }) => k === key)\n if (exactMatch) {\n return { ...acc, [key]: exactMatch.value }\n }\n\n const picked = flattenKVs\n .filter(({ key: k }) => k.startsWith(`${key}.`))\n .map(({ key, value }) => ({ key: key.replace(/^.+\\./, ''), value }))\n return { ...acc, [key]: toObject(picked) }\n }, {})\n}\n\n/**\n *\n * @param param0\n */\nfunction createWorker({ parallel }: FixedConfig): typeof PromisePool {\n let pool = PromisePool\n\n if (typeof parallel === 'number') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pool = pool.withConcurrency(parallel) as any\n }\n\n return pool\n}\n","import { promises } from 'fs'\nimport { resolve } from 'path'\nimport { Extension, FileContext, FixedConfig } from './type'\n\nconst { writeFile, mkdir } = promises\n\nexport async function exportFile(fileContext: FileContext, config: FixedConfig): Promise<void> {\n const outputData = await stringifyStrategy[fileContext.extension](fileContext)\n\n await write(fileContext, outputData, config)\n}\n\ntype Stringify = (fileContext: FileContext) => string | Promise<string>\n\nasync function write(fileContext: FileContext, data: string, config: FixedConfig): Promise<void> {\n const outputBaseDir = resolve(config.cwd ?? process.cwd(), config.dist)\n const filePath = fileContext.outputFile.split('/').slice(0, -1).join('/')\n\n await mkdir(resolve(outputBaseDir, filePath), { recursive: true })\n\n await writeFile(resolve(outputBaseDir, fileContext.outputFile), data)\n}\nconst stringifyStrategy = {\n json: (fileContext) => JSON.stringify(fileContext.json, null, 2),\n yaml: async (fileContext) => {\n const yaml = await import('yaml')\n return yaml.stringify(fileContext.json)\n }\n} as Record<Extension, Stringify>\n","import { defaultConfig, type Config, FixedConfig } from './lib/type'\nimport { loadContext } from './lib/loader'\nimport { convert } from './lib/clone'\nimport { exportFile } from './lib/export'\n\nexport type { Config as Option }\n\nexport async function mimicTree(config: Config): Promise<void> {\n const fixedConfig = {\n ...defaultConfig,\n ...config\n } as FixedConfig\n\n const context = await loadContext(fixedConfig)\n\n for (const f of context.files) {\n const result = await convert(f, context.config)\n await exportFile(result, context.config)\n }\n}\n"]}