├── docs.ts ├── docs ├── index.md ├── modules │ ├── index.md │ ├── bin.ts.md │ ├── index.ts.md │ ├── Production.ts.md │ ├── Spawn.ts.md │ ├── Config.ts.md │ ├── Core.ts.md │ ├── FileSystem.ts.md │ ├── Logger.ts.md │ ├── Parser.ts.md │ ├── Module.ts.md │ └── Markdown.ts.md └── _config.yml ├── .gitignore ├── tsconfig.build.json ├── .prettierrc ├── src ├── bin.ts ├── Production.ts ├── Spawn.ts ├── index.ts ├── Config.ts ├── FileSystem.ts ├── Logger.ts ├── Module.ts ├── Markdown.ts ├── Core.ts └── Parser.ts ├── .vscode └── settings.json ├── tsconfig.eslint.json ├── vitest.config.ts ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── workflows │ └── main.yml ├── tsconfig.json ├── test ├── util.ts ├── Config.ts ├── Logger.ts ├── Module.ts ├── Markdown.ts └── Parser.ts ├── LICENSE ├── .eslintrc.json ├── package.json ├── CHANGELOG.md └── README.md /docs.ts: -------------------------------------------------------------------------------- 1 | import './src/bin' 2 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | nav_order: 1 4 | --- 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | lib 4 | dev 5 | coverage 6 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src"] 4 | } 5 | -------------------------------------------------------------------------------- /docs/modules/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Modules 3 | has_children: true 4 | permalink: /docs/modules 5 | nav_order: 2 6 | --- -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /docs/modules/bin.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: bin.ts 3 | nav_order: 1 4 | parent: Modules 5 | --- 6 | 7 | ## bin overview 8 | 9 | CLI 10 | 11 | Added in v0.2.0 12 | 13 | --- 14 | 15 |
['"])${projectName}(?:/lib)?(?:/(?.*))?\\k `, 'g') 316 | 317 | return source.replace(importRegex(config.projectName), (...args) => { 318 | const groups: { path?: string } = args[args.length - 1] 319 | return `from '../../src${groups.path ? `/${groups.path}` : ''}'` 320 | }) 321 | }) 322 | ) 323 | 324 | const handleImports: (files: ReadonlyArray) => ProgramWithConfig > = ReadonlyArray.traverse( 325 | RTE.ApplicativePar 326 | )((file) => 327 | pipe( 328 | replaceProjectName(file.content), 329 | RTE.map(addAssertImport), 330 | RTE.map((content) => File(file.path, content, file.overwrite)) 331 | ) 332 | ) 333 | 334 | const getExampleIndex = (examples: ReadonlyArray ): ProgramWithConfig => { 335 | const content = pipe( 336 | examples, 337 | ReadonlyArray.foldMap(S.Monoid)((example) => `import './${path.basename(example.path, '.ts')}'\n`) 338 | ) 339 | return pipe( 340 | RTE.ask (), 341 | RTE.map((env) => File(path.join(env.config.outDir, 'examples', 'index.ts'), `${content}\n`, true)) 342 | ) 343 | } 344 | 345 | const cleanExamples: ProgramWithConfig = pipe( 346 | RTE.ask (), 347 | RTE.chainTaskEitherK(({ fileSystem, config }) => fileSystem.remove(path.join(config.outDir, 'examples'))) 348 | ) 349 | 350 | const spawnTsNode: ProgramWithConfig = pipe( 351 | RTE.ask (), 352 | RTE.tap(({ logger }) => RTE.fromTaskEither(logger.debug('Type checking examples...'))), 353 | RTE.chainTaskEitherK(({ spawn, config }) => { 354 | const command = process.platform === 'win32' ? 'ts-node.cmd' : 'ts-node' 355 | const executable = path.join(process.cwd(), config.outDir, 'examples', 'index.ts') 356 | return spawn(command, executable) 357 | }) 358 | ) 359 | 360 | const writeExamples = (examples: ReadonlyArray ): ProgramWithConfig => 361 | pipe( 362 | RTE.ask (), 363 | RTE.tap(({ logger }) => RTE.fromTaskEither(logger.debug('Writing examples...'))), 364 | RTE.flatMap((C) => 365 | pipe( 366 | getExampleIndex(examples), 367 | RTE.map((index) => pipe(examples, ReadonlyArray.prepend(index))), 368 | RTE.chainTaskEitherK((files) => pipe(C, writeFiles(files))) 369 | ) 370 | ) 371 | ) 372 | 373 | const writeTsConfigJson: ProgramWithConfig = pipe( 374 | RTE.ask (), 375 | RTE.tap(({ logger }) => RTE.fromTaskEither(logger.debug('Writing examples tsconfig...'))), 376 | RTE.flatMap((env) => 377 | writeFile( 378 | File( 379 | path.join(process.cwd(), env.config.outDir, 'examples', 'tsconfig.json'), 380 | JSON.stringify( 381 | { 382 | compilerOptions: env.config.examplesCompilerOptions 383 | }, 384 | null, 385 | 2 386 | ), 387 | true 388 | ) 389 | ) 390 | ) 391 | ) 392 | 393 | const typeCheckExamples = (modules: ReadonlyArray ): ProgramWithConfig => 394 | pipe( 395 | getExampleFiles(modules), 396 | RTE.flatMap(handleImports), 397 | RTE.flatMap((examples) => 398 | examples.length === 0 399 | ? cleanExamples 400 | : pipe( 401 | writeExamples(examples), 402 | RTE.flatMap(() => writeTsConfigJson), 403 | RTE.flatMap(() => spawnTsNode), 404 | RTE.flatMap(() => cleanExamples) 405 | ) 406 | ) 407 | ) 408 | 409 | // ------------------------------------------------------------------------------------- 410 | // markdown 411 | // ------------------------------------------------------------------------------------- 412 | 413 | const getHome: ProgramWithConfig = pipe( 414 | RTE.ask (), 415 | RTE.map(({ config }) => 416 | File( 417 | path.join(process.cwd(), config.outDir, 'index.md'), 418 | `--- 419 | title: Home 420 | nav_order: 1 421 | --- 422 | `, 423 | false 424 | ) 425 | ) 426 | ) 427 | 428 | const getModulesIndex: ProgramWithConfig = pipe( 429 | RTE.ask (), 430 | RTE.map(({ config }) => 431 | File( 432 | path.join(process.cwd(), config.outDir, 'modules', 'index.md'), 433 | `--- 434 | title: Modules 435 | has_children: true 436 | permalink: /docs/modules 437 | nav_order: 2 438 | ---`, 439 | false 440 | ) 441 | ) 442 | ) 443 | 444 | const replace = 445 | (searchValue: string | RegExp, replaceValue: string): ((s: string) => string) => 446 | (s) => 447 | s.replace(searchValue, replaceValue) 448 | 449 | const resolveConfigYML = (previousContent: string, config: Config.Config): string => 450 | pipe( 451 | previousContent, 452 | replace(/^remote_theme:.*$/m, `remote_theme: ${config.theme}`), 453 | replace(/^search_enabled:.*$/m, `search_enabled: ${config.enableSearch}`), 454 | replace( 455 | /^ {2}'\S* on GitHub':\n {4}- '.*'/m, 456 | ` '${config.projectName} on GitHub':\n - '${config.projectHomepage}'` 457 | ) 458 | ) 459 | 460 | const getHomepageNavigationHeader = (config: Config.Config): string => { 461 | const isGitHub = config.projectHomepage.toLowerCase().includes('github') 462 | return isGitHub ? config.projectName + ' on GitHub' : 'Homepage' 463 | } 464 | 465 | const getConfigYML: ProgramWithConfig = pipe( 466 | RTE.ask (), 467 | RTE.chainTaskEitherK(({ fileSystem, config }) => { 468 | const filePath = path.join(process.cwd(), config.outDir, '_config.yml') 469 | return pipe( 470 | fileSystem.exists(filePath), 471 | TaskEither.flatMap((exists) => 472 | exists 473 | ? pipe( 474 | fileSystem.readFile(filePath), 475 | TaskEither.map((content) => File(filePath, resolveConfigYML(content, config), true)) 476 | ) 477 | : TaskEither.of( 478 | File( 479 | filePath, 480 | `remote_theme: ${config.theme} 481 | 482 | # Enable or disable the site search 483 | search_enabled: ${config.enableSearch} 484 | 485 | # Aux links for the upper right navigation 486 | aux_links: 487 | '${getHomepageNavigationHeader(config)}': 488 | - '${config.projectHomepage}'`, 489 | false 490 | ) 491 | ) 492 | ) 493 | ) 494 | }) 495 | ) 496 | 497 | const getMarkdownOutputPath = (module: Module): ProgramWithConfig => 498 | pipe( 499 | RTE.ask (), 500 | RTE.map(({ config }) => path.join(config.outDir, 'modules', `${module.path.slice(1).join(path.sep)}.md`)) 501 | ) 502 | 503 | const getModuleMarkdownFiles = (modules: ReadonlyArray ): ProgramWithConfig > => 504 | pipe( 505 | modules, 506 | RTE.traverseArrayWithIndex((order, module) => 507 | pipe( 508 | getMarkdownOutputPath(module), 509 | RTE.bindTo('outputPath'), 510 | RTE.bind('content', () => RTE.right(printModule(module, order + 1))), 511 | RTE.map(({ content, outputPath }) => File(outputPath, content, true)) 512 | ) 513 | ) 514 | ) 515 | 516 | const getMarkdownFiles = (modules: ReadonlyArray ): ProgramWithConfig > => 517 | pipe( 518 | RTE.sequenceArray([getHome, getModulesIndex, getConfigYML]), 519 | RTE.flatMap((meta) => 520 | pipe( 521 | getModuleMarkdownFiles(modules), 522 | RTE.map((files) => ReadonlyArray.getMonoid ().concat(meta, files)) 523 | ) 524 | ) 525 | ) 526 | 527 | const writeMarkdownFiles = (files: ReadonlyArray ): ProgramWithConfig