@@ -1,15 +0,0 @@ | |||||
import {FC, ReactNode} from 'react'; | |||||
export interface WrapperProps { | |||||
children: ReactNode; | |||||
} | |||||
export const Wrapper: FC<WrapperProps> = ({ | |||||
children, | |||||
}) => { | |||||
return ( | |||||
<div className="amanuensis-wrapper"> | |||||
{children} | |||||
</div> | |||||
) | |||||
}; |
@@ -1,8 +1,18 @@ | |||||
import { readFile, stat, writeFile } from 'fs/promises'; | |||||
import { stat, writeFile } from 'fs/promises'; | |||||
import { resolve } from 'path'; | import { resolve } from 'path'; | ||||
import { Argv } from 'yargs'; | import { Argv } from 'yargs'; | ||||
import { Stats } from 'fs'; | import { Stats } from 'fs'; | ||||
import { getPackages, TypedocData } from '../utils/data'; | |||||
import { getPackages } from '../utils/data'; | |||||
import { useBasePath } from '../mixins/base-path'; | |||||
import { CommandError } from '../utils/error'; | |||||
import { useInternalPath } from '../mixins/internal-path'; | |||||
export enum GenerateReturnCode { | |||||
SUCCESS = 0, | |||||
NO_TYPEDOC_JSON = -2, | |||||
COULD_NOT_GENERATE_TYPEDOC_DATA = -3, | |||||
COULD_NOT_GENERATE_PACKAGE_DATA = -4, | |||||
} | |||||
const ensureTypedocJson = async (typedocPath: string) => { | const ensureTypedocJson = async (typedocPath: string) => { | ||||
const trueTypedocPath = resolve(typedocPath); | const trueTypedocPath = resolve(typedocPath); | ||||
@@ -15,62 +25,59 @@ const ensureTypedocJson = async (typedocPath: string) => { | |||||
const err = errRaw as NodeJS.ErrnoException; | const err = errRaw as NodeJS.ErrnoException; | ||||
if (err.code === 'ENOENT') { | if (err.code === 'ENOENT') { | ||||
process.stdout.write('no\n'); | process.stdout.write('no\n'); | ||||
process.stderr.write('Could not find typedoc.json\n'); | |||||
throw new Error('Could not find typedoc.json'); | |||||
throw new CommandError(GenerateReturnCode.NO_TYPEDOC_JSON, 'Could not find typedoc.json'); | |||||
} | } | ||||
process.stdout.write('maybe?\n'); | process.stdout.write('maybe?\n'); | ||||
process.stderr.write('Could not ensure typedoc.json\n'); | |||||
throw err; | |||||
throw new CommandError(GenerateReturnCode.NO_TYPEDOC_JSON, 'Could not ensure typedoc.json', err); | |||||
} | } | ||||
if (statResult.isDirectory()) { | if (statResult.isDirectory()) { | ||||
process.stdout.write('no\n'); | process.stdout.write('no\n'); | ||||
process.stderr.write('typedoc.json is a directory\n'); | |||||
throw new Error('typedoc.json is a directory'); | |||||
throw new CommandError(GenerateReturnCode.NO_TYPEDOC_JSON, 'typedoc.json is a directory'); | |||||
} | } | ||||
process.stdout.write('yes\n'); | process.stdout.write('yes\n'); | ||||
}; | }; | ||||
const generateTypedocData = async () => { | |||||
const generateTypedocData = async (outPath: string, basePath: string) => { | |||||
process.stdout.write('Generating typedoc data...\n'); | process.stdout.write('Generating typedoc data...\n'); | ||||
const outPath = resolve(__dirname, '..', '..', '..', '.amanuensis', 'data.json'); | |||||
const typedocBinPath = resolve(__dirname, '..', '..', '..', 'node_modules', '.bin', 'typedoc'); | |||||
const typedocBinPath = await useInternalPath('node_modules', '.bin', 'typedoc'); | |||||
const { execa } = await import('execa'); | const { execa } = await import('execa'); | ||||
await execa(typedocBinPath, ['--json', outPath], { | |||||
const result = await execa(typedocBinPath, ['--json', outPath, '--pretty', 'false'], { | |||||
stdout: 'inherit', | stdout: 'inherit', | ||||
stderr: 'inherit', | stderr: 'inherit', | ||||
cwd: basePath, | |||||
}); | }); | ||||
if (result.exitCode !== 0) { | |||||
process.stdout.write('failed\n'); | |||||
throw new CommandError(GenerateReturnCode.COULD_NOT_GENERATE_TYPEDOC_DATA, 'Could not generate typedoc data'); | |||||
} | |||||
process.stdout.write('done\n'); | process.stdout.write('done\n'); | ||||
}; | }; | ||||
const produceGroupings = async () => { | |||||
const generatePackageData = async ( | |||||
typedocDataJsonPath: string, | |||||
configPath: string, | |||||
basePath: string, | |||||
) => { | |||||
process.stdout.write('Grouping typedoc data...\n'); | process.stdout.write('Grouping typedoc data...\n'); | ||||
const typedocDataJsonPath = resolve(__dirname, '..', '..', '..', '.amanuensis', 'data.json'); | |||||
const typedocDataJson = await readFile(typedocDataJsonPath, 'utf-8'); | |||||
const typedocData = JSON.parse(typedocDataJson) as TypedocData; | |||||
const packages = await getPackages(process.cwd()); | |||||
const groupings = { | |||||
packages, | |||||
typedocData, | |||||
}; | |||||
await writeFile(typedocDataJsonPath, JSON.stringify(groupings, null, 2)); | |||||
const packages = await getPackages(configPath, basePath); | |||||
process.stdout.write(`File written to ${typedocDataJsonPath}\n`); | |||||
try { | |||||
await writeFile(typedocDataJsonPath, JSON.stringify(packages)); | |||||
process.stdout.write(`File written to ${typedocDataJsonPath}\n`); | |||||
} catch (errRaw) { | |||||
const err = errRaw as NodeJS.ErrnoException; | |||||
process.stderr.write(`Could not write to ${typedocDataJsonPath}: ${err.message}\n`); | |||||
throw new CommandError(GenerateReturnCode.COULD_NOT_GENERATE_PACKAGE_DATA, 'Could not generate package data', err); | |||||
} | |||||
}; | }; | ||||
export const description = 'Generate documentation from typedoc.json' as const; | |||||
export enum GenerateReturnCode { | |||||
SUCCESS = 0, | |||||
NO_TYPEDOC_JSON = -1, | |||||
COULD_NOT_GENERATE_TYPEDOC_DATA = -2, | |||||
COULD_NOT_PRODUCE_GROUPINGS = -3, | |||||
} | |||||
export const description = 'Analyze project for fetching documentation data' as const; | |||||
export interface AnalyzeArgs { | export interface AnalyzeArgs { | ||||
typedocJsonPath?: string; | typedocJsonPath?: string; | ||||
@@ -84,26 +91,20 @@ export const builder = (yargs: Argv) => yargs | |||||
}); | }); | ||||
const analyze = async (args: AnalyzeArgs) => { | const analyze = async (args: AnalyzeArgs) => { | ||||
const { | |||||
typedocJsonPath = resolve(process.cwd(), 'typedoc.json'), | |||||
} = args; | |||||
const basePath = await useBasePath(); | |||||
const configPath = await useBasePath('amanuensis.config.json'); | |||||
const typedocJsonPath = args.typedocJsonPath ?? await useBasePath('typedoc.json'); | |||||
const typedocOutPath = await useInternalPath('.amanuensis', 'types.json'); | |||||
const packagesPath = await useInternalPath('.amanuensis', 'packages.json'); | |||||
try { | try { | ||||
await ensureTypedocJson(typedocJsonPath); | await ensureTypedocJson(typedocJsonPath); | ||||
} catch { | |||||
return GenerateReturnCode.NO_TYPEDOC_JSON; | |||||
} | |||||
try { | |||||
await generateTypedocData(); | |||||
} catch { | |||||
return GenerateReturnCode.COULD_NOT_GENERATE_TYPEDOC_DATA; | |||||
} | |||||
try { | |||||
await produceGroupings(); | |||||
} catch { | |||||
return GenerateReturnCode.COULD_NOT_PRODUCE_GROUPINGS; | |||||
await generateTypedocData(typedocOutPath, basePath); | |||||
await generatePackageData(packagesPath, configPath, basePath); | |||||
} catch (errRaw) { | |||||
const err = errRaw as CommandError; | |||||
process.stderr.write(`${err.message}\n`); | |||||
return err.exitCode; | |||||
} | } | ||||
return GenerateReturnCode.SUCCESS; | return GenerateReturnCode.SUCCESS; | ||||
@@ -1,11 +1,26 @@ | |||||
import { cp } from 'fs/promises'; | import { cp } from 'fs/promises'; | ||||
import { resolve } from 'path'; | |||||
import { Argv } from 'yargs'; | import { Argv } from 'yargs'; | ||||
import { useInternalPath } from '../mixins/internal-path'; | |||||
import { CommandError } from '../utils/error'; | |||||
export enum InitReturnCode { | |||||
SUCCESS = 0, | |||||
COULD_NOT_COPY_FILES = -2, | |||||
COULD_NOT_INSTALL_DEPENDENCIES = -3, | |||||
} | |||||
const copyFiles = async () => { | const copyFiles = async () => { | ||||
const srcPath = resolve(__dirname, '..', '..', '..', 'src', 'next'); | |||||
const destPath = resolve(__dirname, '..', '..', '..', '.amanuensis', 'next'); | |||||
await cp(srcPath, destPath, { recursive: true }); | |||||
try { | |||||
const srcPath = await useInternalPath('src', 'next'); | |||||
const destPath = await useInternalPath('.amanuensis', 'next'); | |||||
await cp(srcPath, destPath, { recursive: true }); | |||||
} catch (errRaw) { | |||||
const err = errRaw as NodeJS.ErrnoException; | |||||
throw new CommandError( | |||||
InitReturnCode.COULD_NOT_COPY_FILES, | |||||
`Could not copy files: ${err.message}`, | |||||
); | |||||
} | |||||
}; | }; | ||||
interface PackageManager { | interface PackageManager { | ||||
@@ -54,14 +69,18 @@ const installDependencies = async () => { | |||||
} | } | ||||
const { [selectedPackageManagerIndex]: selectedPackageManager } = packageManagers; | const { [selectedPackageManagerIndex]: selectedPackageManager } = packageManagers; | ||||
const cwd = resolve(__dirname, '..', '..', '..', '.amanuensis', 'next'); | |||||
const cwd = await useInternalPath('.amanuensis', 'next'); | |||||
process.stdout.write(`In path: ${cwd}\n`); | process.stdout.write(`In path: ${cwd}\n`); | ||||
process.stdout.write(`Installing dependencies with ${selectedPackageManager.name}\n`); | process.stdout.write(`Installing dependencies with ${selectedPackageManager.name}\n`); | ||||
await execa( | |||||
const result = await execa( | |||||
selectedPackageManager.installCmd[0], | selectedPackageManager.installCmd[0], | ||||
selectedPackageManager.installCmd[1], | selectedPackageManager.installCmd[1], | ||||
{ cwd }, | { cwd }, | ||||
); | ); | ||||
if (result.exitCode !== 0) { | |||||
throw new CommandError(InitReturnCode.COULD_NOT_INSTALL_DEPENDENCIES, 'Could not install dependencies'); | |||||
} | |||||
}; | }; | ||||
export const description = 'Initialize a new Amanuensis project' as const; | export const description = 'Initialize a new Amanuensis project' as const; | ||||
@@ -69,9 +88,15 @@ export const description = 'Initialize a new Amanuensis project' as const; | |||||
export const builder = (yargsBuilder: Argv) => yargsBuilder; | export const builder = (yargsBuilder: Argv) => yargsBuilder; | ||||
const init = async () => { | const init = async () => { | ||||
await copyFiles(); | |||||
await installDependencies(); | |||||
return 0; | |||||
try { | |||||
await copyFiles(); | |||||
await installDependencies(); | |||||
} catch (errRaw) { | |||||
const err = errRaw as CommandError; | |||||
process.stderr.write(`${err.message}\n`); | |||||
return err.exitCode; | |||||
} | |||||
return InitReturnCode.SUCCESS; | |||||
}; | }; | ||||
export default init; | export default init; |
@@ -1,17 +1,27 @@ | |||||
import { | import { | ||||
cp, readFile, rm, stat, writeFile, | cp, readFile, rm, stat, writeFile, | ||||
} from 'fs/promises'; | } from 'fs/promises'; | ||||
import { dirname, resolve, basename, extname, join } from 'path'; | |||||
import { | |||||
dirname, resolve, basename, extname, join, | |||||
} from 'path'; | |||||
import { Argv } from 'yargs'; | import { Argv } from 'yargs'; | ||||
import { mkdirp } from 'mkdirp'; | import { mkdirp } from 'mkdirp'; | ||||
import { TypedocData } from '../utils/data'; | |||||
import { PackageData } from '../utils/data'; | |||||
import { CommandError } from '../utils/error'; | |||||
import { useBasePath } from '../mixins/base-path'; | |||||
import { useInternalPath } from '../mixins/internal-path'; | |||||
export enum GenerateReturnCode { | |||||
SUCCESS = 0, | |||||
COULD_NOT_GENERATE_PAGES = -2, | |||||
} | |||||
const linkComponents = async () => { | |||||
const linkComponents = async (cwd: string) => { | |||||
process.stdout.write('Linking components...\n'); | process.stdout.write('Linking components...\n'); | ||||
const projectCwd = resolve(process.cwd(), '.amanuensis'); | |||||
const defaultCwd = resolve(__dirname, '..', '..', '..', 'src', 'next'); | |||||
const destCwd = resolve(__dirname, '..', '..', '..', '.amanuensis', 'next'); | |||||
const projectCwd = resolve(cwd, '.amanuensis'); | |||||
const defaultCwd = await useInternalPath('src', 'next'); | |||||
const destCwd = await useInternalPath('.amanuensis', 'next'); | |||||
const componentsList = [ | const componentsList = [ | ||||
'components/Wrapper.tsx', | 'components/Wrapper.tsx', | ||||
]; | ]; | ||||
@@ -21,6 +31,7 @@ const linkComponents = async () => { | |||||
} catch { | } catch { | ||||
// noop | // noop | ||||
} | } | ||||
await Promise.all(componentsList.map(async (componentPath) => { | await Promise.all(componentsList.map(async (componentPath) => { | ||||
const destPath = resolve(destCwd, componentPath); | const destPath = resolve(destCwd, componentPath); | ||||
let baseCwd = projectCwd; | let baseCwd = projectCwd; | ||||
@@ -42,23 +53,24 @@ const linkComponents = async () => { | |||||
process.stdout.write(`Linked ${componentPath}\n`); | process.stdout.write(`Linked ${componentPath}\n`); | ||||
})); | })); | ||||
const typedocDataJsonPath = resolve(__dirname, '..', '..', '..', '.amanuensis', 'data.json'); | |||||
const typedocDataJson = await readFile(typedocDataJsonPath, 'utf-8'); | |||||
const typedocData = JSON.parse(typedocDataJson) as TypedocData; | |||||
await Promise.all( | |||||
typedocData.packages.map(async (pkg: any) => { | |||||
await mkdirp(resolve(destCwd, 'content', pkg.basePath)); | |||||
await mkdirp(resolve(destCwd, 'pages', pkg.basePath)); | |||||
await Promise.all( | |||||
pkg.markdown.map(async (m: any) => { | |||||
const srcPath = resolve(process.cwd(), pkg.basePath, m.filePath); | |||||
const destPath = resolve(destCwd, 'content', pkg.basePath, m.name); | |||||
const pageDestPath = resolve(destCwd, 'pages', pkg.basePath, `${basename(m.name, extname(m.name))}.tsx`); | |||||
await cp(srcPath, destPath); | |||||
await writeFile( | |||||
pageDestPath, | |||||
`import {NextPage} from 'next'; | |||||
const packagesPath = await useInternalPath('.amanuensis', 'packages.json'); | |||||
const typedocDataJson = await readFile(packagesPath, 'utf-8'); | |||||
const typedocData = JSON.parse(typedocDataJson) as PackageData[]; | |||||
try { | |||||
await Promise.all( | |||||
typedocData.map(async (pkg) => { | |||||
await mkdirp(resolve(destCwd, 'content', pkg.basePath)); | |||||
await mkdirp(resolve(destCwd, 'pages', pkg.basePath)); | |||||
await Promise.all( | |||||
pkg.markdown.map(async (m) => { | |||||
const srcPath = resolve(cwd, pkg.basePath, m.filePath); | |||||
const destPath = resolve(destCwd, 'content', pkg.basePath, m.name); | |||||
const pageDestPath = resolve(destCwd, 'pages', pkg.basePath, `${basename(m.name, extname(m.name))}.tsx`); | |||||
await cp(srcPath, destPath); | |||||
await writeFile( | |||||
pageDestPath, | |||||
`import {NextPage} from 'next'; | |||||
import {Wrapper} from '@/components/Wrapper'; | import {Wrapper} from '@/components/Wrapper'; | ||||
import Content from '@/${join('content', pkg.basePath, m.name)}'; | import Content from '@/${join('content', pkg.basePath, m.name)}'; | ||||
@@ -72,29 +84,32 @@ const IndexPage: NextPage = () => { | |||||
export default IndexPage; | export default IndexPage; | ||||
`, | `, | ||||
// todo fix problem when building with import aliases | |||||
// todo find a way to build with tailwind | |||||
// todo link components to next project | |||||
); | |||||
}), | |||||
); | |||||
}), | |||||
); | |||||
const srcPath = resolve(process.cwd(), 'README.md'); | |||||
const destPath = resolve(destCwd, 'content', 'index.md'); | |||||
await cp(srcPath, destPath); | |||||
// todo fix problem when building with import aliases | |||||
// todo find a way to build with tailwind | |||||
// todo link components to next project | |||||
); | |||||
}), | |||||
); | |||||
}), | |||||
); | |||||
} catch (errRaw) { | |||||
console.log(errRaw); | |||||
throw new CommandError(GenerateReturnCode.COULD_NOT_GENERATE_PAGES, 'Could not write inner page file', errRaw as Error); | |||||
} | |||||
try { | |||||
const srcPath = resolve(cwd, 'README.md'); | |||||
const destPath = resolve(destCwd, 'content', 'index.md'); | |||||
await cp(srcPath, destPath); | |||||
} catch (errRaw) { | |||||
throw new CommandError(GenerateReturnCode.COULD_NOT_GENERATE_PAGES, 'Could not write index file', errRaw as Error); | |||||
} | |||||
process.stdout.write('done\n'); | process.stdout.write('done\n'); | ||||
}; | }; | ||||
export const description = 'Generate documentation from typedoc.json' as const; | export const description = 'Generate documentation from typedoc.json' as const; | ||||
export enum GenerateReturnCode { | |||||
SUCCESS = 0, | |||||
COULD_NOT_GENERATE_PAGES = -1, | |||||
} | |||||
export interface RefreshArgs { | export interface RefreshArgs { | ||||
subcommands?: string[]; | subcommands?: string[]; | ||||
} | } | ||||
@@ -107,9 +122,12 @@ export const builder = (yargs: Argv) => yargs | |||||
const refresh = async (args: RefreshArgs) => { | const refresh = async (args: RefreshArgs) => { | ||||
try { | try { | ||||
await linkComponents(); | |||||
} catch { | |||||
return GenerateReturnCode.COULD_NOT_GENERATE_PAGES; | |||||
const basePath = await useBasePath(); | |||||
await linkComponents(basePath); | |||||
} catch (errRaw) { | |||||
const err = errRaw as CommandError; | |||||
process.stderr.write(`${err.message}\n`); | |||||
return err.exitCode; | |||||
} | } | ||||
return GenerateReturnCode.SUCCESS; | return GenerateReturnCode.SUCCESS; | ||||
@@ -1,5 +1,6 @@ | |||||
import { Argv } from 'yargs'; | import { Argv } from 'yargs'; | ||||
import { resolve } from 'path'; | import { resolve } from 'path'; | ||||
import {CommandError} from '../utils/error'; | |||||
const DEFAULT_PORT = 3000 as const; | const DEFAULT_PORT = 3000 as const; | ||||
@@ -53,12 +54,17 @@ export const builder = (yargs: Argv) => yargs | |||||
}); | }); | ||||
const serve = async (args: ServeArgs) => { | const serve = async (args: ServeArgs) => { | ||||
const { | |||||
port = DEFAULT_PORT, | |||||
} = args; | |||||
const { port = DEFAULT_PORT } = args; | |||||
try { | |||||
await buildApp(); | |||||
await serveApp(port); | |||||
} catch (errRaw) { | |||||
const err = errRaw as CommandError; | |||||
process.stderr.write(`${err.message}\n`); | |||||
return err.exitCode; | |||||
} | |||||
await buildApp(); | |||||
await serveApp(port); | |||||
return ServeReturnCode.SUCCESS; | return ServeReturnCode.SUCCESS; | ||||
}; | }; | ||||
@@ -0,0 +1,22 @@ | |||||
import { resolve } from 'path'; | |||||
import { searchForConfigFile } from '../utils/paths'; | |||||
import { CommandError } from '../utils/error'; | |||||
export enum UseBasePathReturnCode { | |||||
COULD_NOT_FIND_CONFIG_FILE = -1, | |||||
} | |||||
export const useBasePath = async (...args: string[]) => { | |||||
const basePathPrefix = await searchForConfigFile(); | |||||
if (typeof basePathPrefix !== 'string') { | |||||
throw new CommandError( | |||||
UseBasePathReturnCode.COULD_NOT_FIND_CONFIG_FILE, | |||||
'Could not find config file', | |||||
); | |||||
} | |||||
const basePath = resolve(basePathPrefix, ...args); | |||||
process.stdout.write(`Using base path: ${basePath}\n`); | |||||
return basePath; | |||||
}; |
@@ -0,0 +1,7 @@ | |||||
import { resolve } from 'path'; | |||||
export const useInternalPath = (...args: string[]) => { | |||||
return Promise.resolve( | |||||
resolve(__dirname, '..', '..', '..', ...args), | |||||
); | |||||
}; |
@@ -31,15 +31,19 @@ interface SymbolIdMapEntry { | |||||
type TypedocDataNode = TypedocDataTextNode | TypedocDataInlineTagNode; | type TypedocDataNode = TypedocDataTextNode | TypedocDataInlineTagNode; | ||||
export interface TypedocData { | export interface TypedocData { | ||||
packages: any[]; | |||||
typedocData: { | |||||
readme: TypedocDataNode[]; | |||||
symbolIdMap: Record<string, SymbolIdMapEntry>; | |||||
}; | |||||
readme: TypedocDataNode[]; | |||||
symbolIdMap: Record<string, SymbolIdMapEntry>; | |||||
} | } | ||||
export const getPackages = async (cwd = process.cwd()) => { | |||||
const configPath = resolve(cwd, '.amanuensis', 'config.json'); | |||||
export interface PackageData { | |||||
name: string; | |||||
packageJson: Record<string, unknown>; | |||||
basePath: string; | |||||
markdown: { name: string; filePath: string; content: string }[]; | |||||
classifications: Record<string, string | undefined>; | |||||
} | |||||
export const getPackages = async (configPath: string, cwd: string): Promise<PackageData[]> => { | |||||
const configString = await readFile(configPath, 'utf-8'); | const configString = await readFile(configPath, 'utf-8'); | ||||
const config = JSON.parse(configString) as AmanuensisConfig; | const config = JSON.parse(configString) as AmanuensisConfig; | ||||
const searchPatternsRaw = config.package.searchPatterns; | const searchPatternsRaw = config.package.searchPatterns; | ||||
@@ -50,21 +54,26 @@ export const getPackages = async (cwd = process.cwd()) => { | |||||
? searchPattern | ? searchPattern | ||||
: `${searchPattern}/package.json`, | : `${searchPattern}/package.json`, | ||||
{ | { | ||||
cwd, | |||||
ignore: ['**/node_modules/**'], | ignore: ['**/node_modules/**'], | ||||
}, | }, | ||||
)), | )), | ||||
); | ); | ||||
const packagePaths = patternPackagePaths.flat(); | const packagePaths = patternPackagePaths.flat(); | ||||
const markdownFilePaths = await glob( | |||||
const markdownFilePathsRaw = await glob( | |||||
'**/*.{md,mdx}', | '**/*.{md,mdx}', | ||||
{ | { | ||||
cwd, | |||||
ignore: ['**/node_modules/**'], | ignore: ['**/node_modules/**'], | ||||
}, | }, | ||||
); | ); | ||||
return Promise.all( | |||||
packagePaths.map(async (packagePath) => { | |||||
const packageString = await readFile(packagePath, 'utf-8'); | |||||
const basePath = dirname(packagePath); | |||||
const markdownFilePaths = markdownFilePathsRaw.map((p) => resolve(cwd, p)); | |||||
const readPackages = await Promise.all( | |||||
packagePaths.map(async (packagePathRaw) => { | |||||
const absolutePackagePath = resolve(cwd, packagePathRaw); | |||||
const packageString = await readFile(absolutePackagePath, 'utf-8'); | |||||
const basePath = dirname(absolutePackagePath); | |||||
const packageJson = JSON.parse(packageString) as { name: string }; | const packageJson = JSON.parse(packageString) as { name: string }; | ||||
const classifications = Object.fromEntries( | const classifications = Object.fromEntries( | ||||
Object.entries(config.package.classifications) | Object.entries(config.package.classifications) | ||||
@@ -100,23 +109,24 @@ export const getPackages = async (cwd = process.cwd()) => { | |||||
return { | return { | ||||
name: packageJson.name, | name: packageJson.name, | ||||
packageJson, | packageJson, | ||||
basePath, | |||||
basePath: basePath.slice(cwd.length + 1), | |||||
markdown, | markdown, | ||||
classifications, | classifications, | ||||
}; | }; | ||||
}), | }), | ||||
); | ); | ||||
return readPackages.sort((a, b) => a.name.localeCompare(b.name)); | |||||
}; | }; | ||||
export const getFileSources = async (cwd = process.cwd()) => { | export const getFileSources = async (cwd = process.cwd()) => { | ||||
const typedocDataJsonPath = resolve(cwd, '.amanuensis', 'data.json'); | const typedocDataJsonPath = resolve(cwd, '.amanuensis', 'data.json'); | ||||
const typedocDataJson = await readFile(typedocDataJsonPath, 'utf-8'); | const typedocDataJson = await readFile(typedocDataJsonPath, 'utf-8'); | ||||
const typedocData = JSON.parse(typedocDataJson) as TypedocData; | const typedocData = JSON.parse(typedocDataJson) as TypedocData; | ||||
const symbolIdMapEntries = Object.values(typedocData.typedocData.symbolIdMap); | |||||
const symbolIdMapEntries = Object.values(typedocData.symbolIdMap); | |||||
const firstPartySources = symbolIdMapEntries.filter( | const firstPartySources = symbolIdMapEntries.filter( | ||||
({ sourceFileName }) => !sourceFileName.startsWith('node_modules'), | ({ sourceFileName }) => !sourceFileName.startsWith('node_modules'), | ||||
); | ); | ||||
const firstPartySourceFiles = firstPartySources.map(({ sourceFileName }) => sourceFileName); | const firstPartySourceFiles = firstPartySources.map(({ sourceFileName }) => sourceFileName); | ||||
const uniqueFirstPartySourceFiles = Array.from(new Set(firstPartySourceFiles)); | |||||
return uniqueFirstPartySourceFiles; | |||||
return Array.from(new Set(firstPartySourceFiles)); | |||||
}; | }; |
@@ -0,0 +1,7 @@ | |||||
export class CommandError extends Error { | |||||
constructor(readonly exitCode: number, message: string, cause?: Error) { | |||||
super(message); | |||||
this.name = 'CommandError'; | |||||
this.cause = cause; | |||||
} | |||||
} |
@@ -0,0 +1,23 @@ | |||||
import * as fs from 'fs/promises'; | |||||
import * as path from 'path'; | |||||
export const searchForConfigFile = async () => { | |||||
const filePath: string = path.resolve(process.cwd(), 'amanuensis.config.json'); | |||||
async function searchInDir(dirPath: string): Promise<string | null> { | |||||
const configFile: string = path.join(dirPath, 'amanuensis.config.json'); | |||||
try { | |||||
await fs.access(configFile, fs.constants.F_OK); | |||||
return dirPath; | |||||
} catch (err) { | |||||
if (dirPath === path.dirname(dirPath)) { | |||||
// Reached the root directory, config file not found | |||||
return null; | |||||
} | |||||
const parentDir: string = path.dirname(dirPath); | |||||
return searchInDir(parentDir); | |||||
} | |||||
} | |||||
return searchInDir(filePath); | |||||
}; |