@beforesemicolon/builder 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -0
 - package/.eslintrc.cjs +13 -0
 - package/.prettierignore +2 -0
 - package/.prettierrc +6 -0
 - package/package.json +65 -0
 - package/src/build-browser.ts +35 -0
 - package/src/build-modules.ts +53 -0
 - package/src/docs/renderer/code.ts +15 -0
 - package/src/docs/renderer/heading.ts +13 -0
 - package/src/docs/renderer/index.ts +10 -0
 - package/src/docs/renderer/link.ts +7 -0
 - package/src/docs/run.ts +212 -0
 - package/src/docs/templates/default.ts +19 -0
 - package/src/docs/types.ts +16 -0
 - package/tsconfig.json +24 -0
 
    
        package/.eslintignore
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            src/**/*.spec.ts
         
     | 
    
        package/.eslintrc.cjs
    ADDED
    
    | 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module.exports = {
         
     | 
| 
      
 2 
     | 
    
         
            +
                extends: [
         
     | 
| 
      
 3 
     | 
    
         
            +
                    'eslint:recommended',
         
     | 
| 
      
 4 
     | 
    
         
            +
                    'plugin:@typescript-eslint/recommended',
         
     | 
| 
      
 5 
     | 
    
         
            +
                    'prettier',
         
     | 
| 
      
 6 
     | 
    
         
            +
                ],
         
     | 
| 
      
 7 
     | 
    
         
            +
                parser: '@typescript-eslint/parser',
         
     | 
| 
      
 8 
     | 
    
         
            +
                plugins: ['@typescript-eslint'],
         
     | 
| 
      
 9 
     | 
    
         
            +
                root: true,
         
     | 
| 
      
 10 
     | 
    
         
            +
                rules: {
         
     | 
| 
      
 11 
     | 
    
         
            +
                    'no-prototype-builtins': ['off'],
         
     | 
| 
      
 12 
     | 
    
         
            +
                },
         
     | 
| 
      
 13 
     | 
    
         
            +
            }
         
     | 
    
        package/.prettierignore
    ADDED
    
    
    
        package/.prettierrc
    ADDED
    
    
    
        package/package.json
    ADDED
    
    | 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
                "name": "@beforesemicolon/builder",
         
     | 
| 
      
 3 
     | 
    
         
            +
                "version": "1.0.0",
         
     | 
| 
      
 4 
     | 
    
         
            +
                "description": "Utilities to build npm packages and documentation website",
         
     | 
| 
      
 5 
     | 
    
         
            +
                "engines": {
         
     | 
| 
      
 6 
     | 
    
         
            +
                    "node": ">=18.16.0"
         
     | 
| 
      
 7 
     | 
    
         
            +
                },
         
     | 
| 
      
 8 
     | 
    
         
            +
                "type": "module",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "scripts": {
         
     | 
| 
      
 10 
     | 
    
         
            +
                    "build": "rm -rf dist && npm-run-all lint && tsc",
         
     | 
| 
      
 11 
     | 
    
         
            +
                    "lint": "eslint ./src && prettier --check .",
         
     | 
| 
      
 12 
     | 
    
         
            +
                    "format": "eslint ./src --fix && prettier --write ."
         
     | 
| 
      
 13 
     | 
    
         
            +
                },
         
     | 
| 
      
 14 
     | 
    
         
            +
                "author": "Elson Correia",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "license": "BSD-3-Clause",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "repository": {
         
     | 
| 
      
 17 
     | 
    
         
            +
                    "url": "https://github.com/beforesemicolon/builder",
         
     | 
| 
      
 18 
     | 
    
         
            +
                    "type": "git"
         
     | 
| 
      
 19 
     | 
    
         
            +
                },
         
     | 
| 
      
 20 
     | 
    
         
            +
                "keywords": [
         
     | 
| 
      
 21 
     | 
    
         
            +
                    "builder",
         
     | 
| 
      
 22 
     | 
    
         
            +
                    "build npm",
         
     | 
| 
      
 23 
     | 
    
         
            +
                    "docs"
         
     | 
| 
      
 24 
     | 
    
         
            +
                ],
         
     | 
| 
      
 25 
     | 
    
         
            +
                "funding": {
         
     | 
| 
      
 26 
     | 
    
         
            +
                    "url": "https://github.com/sponsors/beforesemicolon",
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "type": "github"
         
     | 
| 
      
 28 
     | 
    
         
            +
                },
         
     | 
| 
      
 29 
     | 
    
         
            +
                "devDependencies": {
         
     | 
| 
      
 30 
     | 
    
         
            +
                    "@gjsify/esbuild-plugin-transform-ext": "0.0.4",
         
     | 
| 
      
 31 
     | 
    
         
            +
                    "@types/jest": "^29.5.11",
         
     | 
| 
      
 32 
     | 
    
         
            +
                    "@types/jsdom": "^21.1.6",
         
     | 
| 
      
 33 
     | 
    
         
            +
                    "@types/jsdom-global": "^3.0.7",
         
     | 
| 
      
 34 
     | 
    
         
            +
                    "@types/node": "^20.10.5",
         
     | 
| 
      
 35 
     | 
    
         
            +
                    "@types/node-sass": "^4.11.7",
         
     | 
| 
      
 36 
     | 
    
         
            +
                    "@typescript-eslint/eslint-plugin": "^6.15.0",
         
     | 
| 
      
 37 
     | 
    
         
            +
                    "@typescript-eslint/parser": "^6.15.0",
         
     | 
| 
      
 38 
     | 
    
         
            +
                    "core-js": "^3.34.0",
         
     | 
| 
      
 39 
     | 
    
         
            +
                    "esbuild": "^0.19.10",
         
     | 
| 
      
 40 
     | 
    
         
            +
                    "esbuild-plugin-text-replace": "^1.3.0",
         
     | 
| 
      
 41 
     | 
    
         
            +
                    "eslint": "^8.56.0",
         
     | 
| 
      
 42 
     | 
    
         
            +
                    "eslint-config-prettier": "^9.1.0",
         
     | 
| 
      
 43 
     | 
    
         
            +
                    "eslint-config-standard": "^17.1.0",
         
     | 
| 
      
 44 
     | 
    
         
            +
                    "eslint-plugin-import": "^2.29.1",
         
     | 
| 
      
 45 
     | 
    
         
            +
                    "eslint-plugin-n": "^16.5.0",
         
     | 
| 
      
 46 
     | 
    
         
            +
                    "eslint-plugin-prettier": "^5.1.2",
         
     | 
| 
      
 47 
     | 
    
         
            +
                    "eslint-plugin-promise": "^6.1.1",
         
     | 
| 
      
 48 
     | 
    
         
            +
                    "front-matter": "^4.0.2",
         
     | 
| 
      
 49 
     | 
    
         
            +
                    "global-jsdom": "^9.2.0",
         
     | 
| 
      
 50 
     | 
    
         
            +
                    "highlight.js": "^11.10.0",
         
     | 
| 
      
 51 
     | 
    
         
            +
                    "install": "^0.13.0",
         
     | 
| 
      
 52 
     | 
    
         
            +
                    "isomorphic-dompurify": "^2.16.0",
         
     | 
| 
      
 53 
     | 
    
         
            +
                    "jsdom": "^23.0.1",
         
     | 
| 
      
 54 
     | 
    
         
            +
                    "marked": "^14.1.2",
         
     | 
| 
      
 55 
     | 
    
         
            +
                    "marked-highlight": "^2.1.4",
         
     | 
| 
      
 56 
     | 
    
         
            +
                    "nodemon": "^3.1.7",
         
     | 
| 
      
 57 
     | 
    
         
            +
                    "npm": "^10.8.3",
         
     | 
| 
      
 58 
     | 
    
         
            +
                    "npm-run-all": "^4.1.5",
         
     | 
| 
      
 59 
     | 
    
         
            +
                    "prettier": "3.1.1",
         
     | 
| 
      
 60 
     | 
    
         
            +
                    "tinybench": "^2.8.0",
         
     | 
| 
      
 61 
     | 
    
         
            +
                    "ts-jest": "^29.1.1",
         
     | 
| 
      
 62 
     | 
    
         
            +
                    "tsx": "^4.7.0",
         
     | 
| 
      
 63 
     | 
    
         
            +
                    "typescript": "^5.3.3"
         
     | 
| 
      
 64 
     | 
    
         
            +
                }
         
     | 
| 
      
 65 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import esbuild, { Plugin } from 'esbuild'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            // the Doc exported with @beforesemicolon/html-parser is not used so no need
         
     | 
| 
      
 4 
     | 
    
         
            +
            // to include it in the final package
         
     | 
| 
      
 5 
     | 
    
         
            +
            const emptyParserDoc = {
         
     | 
| 
      
 6 
     | 
    
         
            +
                name: 'empty-parser-Doc-plugin',
         
     | 
| 
      
 7 
     | 
    
         
            +
                setup(build) {
         
     | 
| 
      
 8 
     | 
    
         
            +
                    build.onResolve({ filter: /Doc$/ }, (args) => {
         
     | 
| 
      
 9 
     | 
    
         
            +
                        if (args.importer.includes('@beforesemicolon/html-parser')) {
         
     | 
| 
      
 10 
     | 
    
         
            +
                            return {
         
     | 
| 
      
 11 
     | 
    
         
            +
                                path: args.path,
         
     | 
| 
      
 12 
     | 
    
         
            +
                                namespace: 'empty-Doc',
         
     | 
| 
      
 13 
     | 
    
         
            +
                            }
         
     | 
| 
      
 14 
     | 
    
         
            +
                        }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    })
         
     | 
| 
      
 16 
     | 
    
         
            +
                    build.onLoad({ filter: /.*/, namespace: 'empty-Doc' }, () => {
         
     | 
| 
      
 17 
     | 
    
         
            +
                        return {
         
     | 
| 
      
 18 
     | 
    
         
            +
                            contents: '',
         
     | 
| 
      
 19 
     | 
    
         
            +
                        }
         
     | 
| 
      
 20 
     | 
    
         
            +
                    })
         
     | 
| 
      
 21 
     | 
    
         
            +
                },
         
     | 
| 
      
 22 
     | 
    
         
            +
            } as Plugin
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            esbuild
         
     | 
| 
      
 25 
     | 
    
         
            +
                .build({
         
     | 
| 
      
 26 
     | 
    
         
            +
                    entryPoints: ['src/client.ts'],
         
     | 
| 
      
 27 
     | 
    
         
            +
                    outfile: 'dist/client.js',
         
     | 
| 
      
 28 
     | 
    
         
            +
                    bundle: true,
         
     | 
| 
      
 29 
     | 
    
         
            +
                    keepNames: true,
         
     | 
| 
      
 30 
     | 
    
         
            +
                    sourcemap: true,
         
     | 
| 
      
 31 
     | 
    
         
            +
                    target: 'esnext',
         
     | 
| 
      
 32 
     | 
    
         
            +
                    minify: true,
         
     | 
| 
      
 33 
     | 
    
         
            +
                    plugins: [emptyParserDoc],
         
     | 
| 
      
 34 
     | 
    
         
            +
                })
         
     | 
| 
      
 35 
     | 
    
         
            +
                .catch(console.error)
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import esbuild from 'esbuild'
         
     | 
| 
      
 2 
     | 
    
         
            +
            import path from 'path'
         
     | 
| 
      
 3 
     | 
    
         
            +
            import fs from 'fs'
         
     | 
| 
      
 4 
     | 
    
         
            +
            import { transformExtPlugin } from '@gjsify/esbuild-plugin-transform-ext'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            function readFilesRecursively(directoryPath: string) {
         
     | 
| 
      
 7 
     | 
    
         
            +
                const files: string[] = []
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                function readDirectory(currentPath: string) {
         
     | 
| 
      
 10 
     | 
    
         
            +
                    const entries = fs.readdirSync(currentPath, { withFileTypes: true })
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    for (const entry of entries) {
         
     | 
| 
      
 13 
     | 
    
         
            +
                        const fullPath = path.join(currentPath, entry.name)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                        if (entry.isDirectory()) {
         
     | 
| 
      
 16 
     | 
    
         
            +
                            readDirectory(fullPath)
         
     | 
| 
      
 17 
     | 
    
         
            +
                        } else {
         
     | 
| 
      
 18 
     | 
    
         
            +
                            files.push(fullPath)
         
     | 
| 
      
 19 
     | 
    
         
            +
                        }
         
     | 
| 
      
 20 
     | 
    
         
            +
                    }
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                readDirectory(directoryPath)
         
     | 
| 
      
 24 
     | 
    
         
            +
                return files
         
     | 
| 
      
 25 
     | 
    
         
            +
            }
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            const directoryPath = path.join(process.cwd(), 'src')
         
     | 
| 
      
 28 
     | 
    
         
            +
            const allFiles = readFilesRecursively(directoryPath).filter(
         
     | 
| 
      
 29 
     | 
    
         
            +
                (file) => !file.endsWith('.spec.ts') && !file.endsWith('/client.ts')
         
     | 
| 
      
 30 
     | 
    
         
            +
            )
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            Promise.all([
         
     | 
| 
      
 33 
     | 
    
         
            +
                esbuild.build({
         
     | 
| 
      
 34 
     | 
    
         
            +
                    entryPoints: allFiles,
         
     | 
| 
      
 35 
     | 
    
         
            +
                    outdir: 'dist/esm',
         
     | 
| 
      
 36 
     | 
    
         
            +
                    target: 'esnext',
         
     | 
| 
      
 37 
     | 
    
         
            +
                    minify: true,
         
     | 
| 
      
 38 
     | 
    
         
            +
                    format: 'esm',
         
     | 
| 
      
 39 
     | 
    
         
            +
                    platform: 'node',
         
     | 
| 
      
 40 
     | 
    
         
            +
                    keepNames: true,
         
     | 
| 
      
 41 
     | 
    
         
            +
                    plugins: [transformExtPlugin({ outExtension: { '.ts': '.js' } })],
         
     | 
| 
      
 42 
     | 
    
         
            +
                }),
         
     | 
| 
      
 43 
     | 
    
         
            +
                esbuild.build({
         
     | 
| 
      
 44 
     | 
    
         
            +
                    entryPoints: allFiles,
         
     | 
| 
      
 45 
     | 
    
         
            +
                    outdir: 'dist/cjs',
         
     | 
| 
      
 46 
     | 
    
         
            +
                    target: 'esnext',
         
     | 
| 
      
 47 
     | 
    
         
            +
                    minify: true,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    format: 'cjs',
         
     | 
| 
      
 49 
     | 
    
         
            +
                    platform: 'node',
         
     | 
| 
      
 50 
     | 
    
         
            +
                    keepNames: true,
         
     | 
| 
      
 51 
     | 
    
         
            +
                    plugins: [transformExtPlugin({ outExtension: { '.ts': '.js' } })],
         
     | 
| 
      
 52 
     | 
    
         
            +
                }),
         
     | 
| 
      
 53 
     | 
    
         
            +
            ]).catch(console.error)
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { Tokens } from 'marked'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export default function code({ lang, text }: Tokens.Code) {
         
     | 
| 
      
 4 
     | 
    
         
            +
                return `<div class="code-snippet">
         
     | 
| 
      
 5 
     | 
    
         
            +
                    ${lang ? `<div class="label ${lang.toLowerCase()}">${lang}</div>` : ''}
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <div class="content">
         
     | 
| 
      
 7 
     | 
    
         
            +
                        <pre>
         
     | 
| 
      
 8 
     | 
    
         
            +
                            <code class="hljs language-javascript">${text}</code>
         
     | 
| 
      
 9 
     | 
    
         
            +
                        </pre>
         
     | 
| 
      
 10 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <button type="button" class="code-copy-btn" style="visibility: hidden">
         
     | 
| 
      
 12 
     | 
    
         
            +
                        copy
         
     | 
| 
      
 13 
     | 
    
         
            +
                    </button>
         
     | 
| 
      
 14 
     | 
    
         
            +
                </div>`
         
     | 
| 
      
 15 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { Tokens } from 'marked'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export default function heading({ tokens, depth }: Tokens.Heading) {
         
     | 
| 
      
 4 
     | 
    
         
            +
                // @ts-expect-error this
         
     | 
| 
      
 5 
     | 
    
         
            +
                const text = this.parser.parseInline(tokens)
         
     | 
| 
      
 6 
     | 
    
         
            +
                const escapedText = text
         
     | 
| 
      
 7 
     | 
    
         
            +
                    .toLowerCase()
         
     | 
| 
      
 8 
     | 
    
         
            +
                    .replace(/[^\w]+/g, ' ')
         
     | 
| 
      
 9 
     | 
    
         
            +
                    .trim()
         
     | 
| 
      
 10 
     | 
    
         
            +
                    .replace(/\s/g, '-')
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                return `<h${depth} id="${escapedText}"><a href="#${escapedText}">${text}</a></h${depth}>`
         
     | 
| 
      
 13 
     | 
    
         
            +
            }
         
     | 
    
        package/src/docs/run.ts
    ADDED
    
    | 
         @@ -0,0 +1,212 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { Marked } from 'marked'
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { cp, mkdir, readdir, readFile, writeFile } from 'fs/promises'
         
     | 
| 
      
 3 
     | 
    
         
            +
            import path from 'path'
         
     | 
| 
      
 4 
     | 
    
         
            +
            import fm from 'front-matter'
         
     | 
| 
      
 5 
     | 
    
         
            +
            import DOMPurify from 'isomorphic-dompurify'
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { markedHighlight } from 'marked-highlight'
         
     | 
| 
      
 7 
     | 
    
         
            +
            import hljs from 'highlight.js'
         
     | 
| 
      
 8 
     | 
    
         
            +
            import defaultTemp from './templates/default.js'
         
     | 
| 
      
 9 
     | 
    
         
            +
            import { CustomOptions, PageProps, SiteMap } from './types.js'
         
     | 
| 
      
 10 
     | 
    
         
            +
            import renderer from './renderer/index.js'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            const layouts: Map<string, (props: PageProps) => string> = new Map()
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            layouts.set('default', defaultTemp)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            let currentFileMeta: {
         
     | 
| 
      
 17 
     | 
    
         
            +
                attributes: CustomOptions
         
     | 
| 
      
 18 
     | 
    
         
            +
                siteMap: SiteMap
         
     | 
| 
      
 19 
     | 
    
         
            +
            }
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            const marked = new Marked(
         
     | 
| 
      
 22 
     | 
    
         
            +
                markedHighlight({
         
     | 
| 
      
 23 
     | 
    
         
            +
                    langPrefix: 'hljs language-',
         
     | 
| 
      
 24 
     | 
    
         
            +
                    highlight(code, lang) {
         
     | 
| 
      
 25 
     | 
    
         
            +
                        const language = hljs.getLanguage(lang) ? lang : 'plaintext'
         
     | 
| 
      
 26 
     | 
    
         
            +
                        return hljs.highlight(code, { language }).value
         
     | 
| 
      
 27 
     | 
    
         
            +
                    },
         
     | 
| 
      
 28 
     | 
    
         
            +
                })
         
     | 
| 
      
 29 
     | 
    
         
            +
            )
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            marked.use({ renderer })
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            const docsDir = path.resolve(process.cwd(), 'docs')
         
     | 
| 
      
 34 
     | 
    
         
            +
            const docsSiteDir = path.resolve(process.cwd(), 'website')
         
     | 
| 
      
 35 
     | 
    
         
            +
            const docsLayoutsDir = path.resolve(process.cwd(), 'docs/_layouts')
         
     | 
| 
      
 36 
     | 
    
         
            +
            const docsAssetsDir = path.resolve(process.cwd(), 'docs/assets')
         
     | 
| 
      
 37 
     | 
    
         
            +
            const docsStylesheetsDir = path.resolve(process.cwd(), 'docs/stylesheets')
         
     | 
| 
      
 38 
     | 
    
         
            +
            const docsScriptsDir = path.resolve(process.cwd(), 'docs/scripts')
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            const traverseDirectory = async (dir: string) => {
         
     | 
| 
      
 41 
     | 
    
         
            +
                const items = await readdir(dir)
         
     | 
| 
      
 42 
     | 
    
         
            +
                const files: string[] = []
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                for (const item of items) {
         
     | 
| 
      
 45 
     | 
    
         
            +
                    const ext = path.extname(item)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    if (/^[._]/.test(item)) continue
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    if (ext) {
         
     | 
| 
      
 50 
     | 
    
         
            +
                        files.push(path.join(dir, item))
         
     | 
| 
      
 51 
     | 
    
         
            +
                    } else if (!/(stylesheets|assets|scripts)/.test(item)) {
         
     | 
| 
      
 52 
     | 
    
         
            +
                        files.push(...(await traverseDirectory(path.join(dir, item))))
         
     | 
| 
      
 53 
     | 
    
         
            +
                    }
         
     | 
| 
      
 54 
     | 
    
         
            +
                }
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                return files
         
     | 
| 
      
 57 
     | 
    
         
            +
            }
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            marked.use({
         
     | 
| 
      
 60 
     | 
    
         
            +
                hooks: {
         
     | 
| 
      
 61 
     | 
    
         
            +
                    postprocess(html: string) {
         
     | 
| 
      
 62 
     | 
    
         
            +
                        const { layout = 'default', ...options } =
         
     | 
| 
      
 63 
     | 
    
         
            +
                            currentFileMeta.attributes
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                        html = DOMPurify.sanitize(html)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                        const tableOfContent = [
         
     | 
| 
      
 68 
     | 
    
         
            +
                            ...html.matchAll(
         
     | 
| 
      
 69 
     | 
    
         
            +
                                /<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([^<]+)<\/a>/gm
         
     | 
| 
      
 70 
     | 
    
         
            +
                            ),
         
     | 
| 
      
 71 
     | 
    
         
            +
                        ].map((m) => ({ path: m[2], label: m[3], level: m[1] }))
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                        return (
         
     | 
| 
      
 74 
     | 
    
         
            +
                            layouts.get(layout)?.({
         
     | 
| 
      
 75 
     | 
    
         
            +
                                ...options,
         
     | 
| 
      
 76 
     | 
    
         
            +
                                content: html,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                siteMap: currentFileMeta.siteMap,
         
     | 
| 
      
 78 
     | 
    
         
            +
                                tableOfContent,
         
     | 
| 
      
 79 
     | 
    
         
            +
                            }) || html
         
     | 
| 
      
 80 
     | 
    
         
            +
                        )
         
     | 
| 
      
 81 
     | 
    
         
            +
                    },
         
     | 
| 
      
 82 
     | 
    
         
            +
                },
         
     | 
| 
      
 83 
     | 
    
         
            +
            })
         
     | 
| 
      
 84 
     | 
    
         
            +
            ;(async () => {
         
     | 
| 
      
 85 
     | 
    
         
            +
                // create website dir
         
     | 
| 
      
 86 
     | 
    
         
            +
                await mkdir(docsSiteDir, { recursive: true })
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                try {
         
     | 
| 
      
 89 
     | 
    
         
            +
                    await cp(docsAssetsDir, docsAssetsDir.replace(docsDir, docsSiteDir), {
         
     | 
| 
      
 90 
     | 
    
         
            +
                        recursive: true,
         
     | 
| 
      
 91 
     | 
    
         
            +
                    })
         
     | 
| 
      
 92 
     | 
    
         
            +
                } catch (e) {
         
     | 
| 
      
 93 
     | 
    
         
            +
                    // ignore
         
     | 
| 
      
 94 
     | 
    
         
            +
                }
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                try {
         
     | 
| 
      
 97 
     | 
    
         
            +
                    await cp(
         
     | 
| 
      
 98 
     | 
    
         
            +
                        docsStylesheetsDir,
         
     | 
| 
      
 99 
     | 
    
         
            +
                        docsStylesheetsDir.replace(docsDir, docsSiteDir),
         
     | 
| 
      
 100 
     | 
    
         
            +
                        { recursive: true }
         
     | 
| 
      
 101 
     | 
    
         
            +
                    )
         
     | 
| 
      
 102 
     | 
    
         
            +
                } catch (e) {
         
     | 
| 
      
 103 
     | 
    
         
            +
                    // ignore
         
     | 
| 
      
 104 
     | 
    
         
            +
                }
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                try {
         
     | 
| 
      
 107 
     | 
    
         
            +
                    await cp(docsScriptsDir, docsScriptsDir.replace(docsDir, docsSiteDir), {
         
     | 
| 
      
 108 
     | 
    
         
            +
                        recursive: true,
         
     | 
| 
      
 109 
     | 
    
         
            +
                    })
         
     | 
| 
      
 110 
     | 
    
         
            +
                } catch (e) {
         
     | 
| 
      
 111 
     | 
    
         
            +
                    // ignore
         
     | 
| 
      
 112 
     | 
    
         
            +
                }
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                try {
         
     | 
| 
      
 115 
     | 
    
         
            +
                    // import the layouts first
         
     | 
| 
      
 116 
     | 
    
         
            +
                    const layoutPaths = await traverseDirectory(docsLayoutsDir)
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                    for (const layoutPath of layoutPaths) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                        if (/\.(j|t)s$/.test(layoutPath)) {
         
     | 
| 
      
 120 
     | 
    
         
            +
                            const fileName = path
         
     | 
| 
      
 121 
     | 
    
         
            +
                                .basename(layoutPath)
         
     | 
| 
      
 122 
     | 
    
         
            +
                                .replace(path.extname(layoutPath), '')
         
     | 
| 
      
 123 
     | 
    
         
            +
                            const handler = await import(layoutPath)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                            layouts.set(fileName, handler.default)
         
     | 
| 
      
 126 
     | 
    
         
            +
                        }
         
     | 
| 
      
 127 
     | 
    
         
            +
                    }
         
     | 
| 
      
 128 
     | 
    
         
            +
                } catch (e) {
         
     | 
| 
      
 129 
     | 
    
         
            +
                    // ignore
         
     | 
| 
      
 130 
     | 
    
         
            +
                }
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                const filePaths = await traverseDirectory(docsDir)
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                const siteMap: SiteMap = new Map()
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                ;(
         
     | 
| 
      
 137 
     | 
    
         
            +
                    await Promise.all(
         
     | 
| 
      
 138 
     | 
    
         
            +
                        filePaths
         
     | 
| 
      
 139 
     | 
    
         
            +
                            .filter((filePath) => filePath.endsWith('.md'))
         
     | 
| 
      
 140 
     | 
    
         
            +
                            .map(async (filePath) => {
         
     | 
| 
      
 141 
     | 
    
         
            +
                                const content = await readFile(filePath, 'utf-8')
         
     | 
| 
      
 142 
     | 
    
         
            +
                                return {
         
     | 
| 
      
 143 
     | 
    
         
            +
                                    filePath,
         
     | 
| 
      
 144 
     | 
    
         
            +
                                    // @ts-expect-error call unknown
         
     | 
| 
      
 145 
     | 
    
         
            +
                                    ...(fm(content) as {
         
     | 
| 
      
 146 
     | 
    
         
            +
                                        attributes: CustomOptions
         
     | 
| 
      
 147 
     | 
    
         
            +
                                        body: string
         
     | 
| 
      
 148 
     | 
    
         
            +
                                    }),
         
     | 
| 
      
 149 
     | 
    
         
            +
                                }
         
     | 
| 
      
 150 
     | 
    
         
            +
                            })
         
     | 
| 
      
 151 
     | 
    
         
            +
                    )
         
     | 
| 
      
 152 
     | 
    
         
            +
                )
         
     | 
| 
      
 153 
     | 
    
         
            +
                    .sort((a, b) => a.attributes.order - b.attributes.order)
         
     | 
| 
      
 154 
     | 
    
         
            +
                    .map(({ attributes, body, filePath }) => {
         
     | 
| 
      
 155 
     | 
    
         
            +
                        const pathname = filePath
         
     | 
| 
      
 156 
     | 
    
         
            +
                            .replace(docsDir, '')
         
     | 
| 
      
 157 
     | 
    
         
            +
                            .replace(/\.md/, '.html')
         
     | 
| 
      
 158 
     | 
    
         
            +
                        const fileWebsitePath = filePath
         
     | 
| 
      
 159 
     | 
    
         
            +
                            .replace(docsDir, docsSiteDir)
         
     | 
| 
      
 160 
     | 
    
         
            +
                            .replace('.md', '.html')
         
     | 
| 
      
 161 
     | 
    
         
            +
                        const fileDirWebsitePath = fileWebsitePath.replace(
         
     | 
| 
      
 162 
     | 
    
         
            +
                            path.basename(fileWebsitePath),
         
     | 
| 
      
 163 
     | 
    
         
            +
                            ''
         
     | 
| 
      
 164 
     | 
    
         
            +
                        )
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                        const fileName = path.basename(pathname) || '/'
         
     | 
| 
      
 167 
     | 
    
         
            +
                        const dir = pathname.replace(fileName, '').replace(/\/$/, '')
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                        let currentDir = siteMap
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                        dir.split('/')
         
     | 
| 
      
 172 
     | 
    
         
            +
                            .filter(Boolean)
         
     | 
| 
      
 173 
     | 
    
         
            +
                            .forEach((d) => {
         
     | 
| 
      
 174 
     | 
    
         
            +
                                if (!currentDir.has(d)) {
         
     | 
| 
      
 175 
     | 
    
         
            +
                                    currentDir.set(d, new Map())
         
     | 
| 
      
 176 
     | 
    
         
            +
                                }
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                                currentDir = currentDir.get(d) as SiteMap
         
     | 
| 
      
 179 
     | 
    
         
            +
                            })
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                        const attrs = {
         
     | 
| 
      
 182 
     | 
    
         
            +
                            ...attributes,
         
     | 
| 
      
 183 
     | 
    
         
            +
                            path: pathname,
         
     | 
| 
      
 184 
     | 
    
         
            +
                        } as CustomOptions
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                        if (!currentDir.has(fileName)) {
         
     | 
| 
      
 187 
     | 
    
         
            +
                            currentDir.set(fileName, attrs)
         
     | 
| 
      
 188 
     | 
    
         
            +
                        }
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                        return {
         
     | 
| 
      
 191 
     | 
    
         
            +
                            attributes: attrs,
         
     | 
| 
      
 192 
     | 
    
         
            +
                            fileWebsitePath,
         
     | 
| 
      
 193 
     | 
    
         
            +
                            fileDirWebsitePath,
         
     | 
| 
      
 194 
     | 
    
         
            +
                            body,
         
     | 
| 
      
 195 
     | 
    
         
            +
                            siteMap,
         
     | 
| 
      
 196 
     | 
    
         
            +
                        }
         
     | 
| 
      
 197 
     | 
    
         
            +
                    })
         
     | 
| 
      
 198 
     | 
    
         
            +
                    .forEach(
         
     | 
| 
      
 199 
     | 
    
         
            +
                        async ({
         
     | 
| 
      
 200 
     | 
    
         
            +
                            attributes,
         
     | 
| 
      
 201 
     | 
    
         
            +
                            body,
         
     | 
| 
      
 202 
     | 
    
         
            +
                            fileDirWebsitePath,
         
     | 
| 
      
 203 
     | 
    
         
            +
                            fileWebsitePath,
         
     | 
| 
      
 204 
     | 
    
         
            +
                            siteMap,
         
     | 
| 
      
 205 
     | 
    
         
            +
                        }) => {
         
     | 
| 
      
 206 
     | 
    
         
            +
                            currentFileMeta = { attributes, siteMap }
         
     | 
| 
      
 207 
     | 
    
         
            +
                            const contentMd = await marked.parse(body)
         
     | 
| 
      
 208 
     | 
    
         
            +
                            await mkdir(fileDirWebsitePath, { recursive: true })
         
     | 
| 
      
 209 
     | 
    
         
            +
                            await writeFile(fileWebsitePath, contentMd)
         
     | 
| 
      
 210 
     | 
    
         
            +
                        }
         
     | 
| 
      
 211 
     | 
    
         
            +
                    )
         
     | 
| 
      
 212 
     | 
    
         
            +
            })()
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { PageProps } from '../types.js'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export default ({ title, description, content }: PageProps) => `
         
     | 
| 
      
 4 
     | 
    
         
            +
            <!doctype html>
         
     | 
| 
      
 5 
     | 
    
         
            +
            <html lang="en">
         
     | 
| 
      
 6 
     | 
    
         
            +
                <head>
         
     | 
| 
      
 7 
     | 
    
         
            +
                    <meta charset="UTF-8" />
         
     | 
| 
      
 8 
     | 
    
         
            +
                    <meta
         
     | 
| 
      
 9 
     | 
    
         
            +
                        name="viewport"
         
     | 
| 
      
 10 
     | 
    
         
            +
                        content="width=device-width, initial-scale=1.0"
         
     | 
| 
      
 11 
     | 
    
         
            +
                    />
         
     | 
| 
      
 12 
     | 
    
         
            +
                    <meta name="description" content="${description}" />
         
     | 
| 
      
 13 
     | 
    
         
            +
                    <title>${title}</title>
         
     | 
| 
      
 14 
     | 
    
         
            +
                </head>
         
     | 
| 
      
 15 
     | 
    
         
            +
                <body>
         
     | 
| 
      
 16 
     | 
    
         
            +
                    ${content}
         
     | 
| 
      
 17 
     | 
    
         
            +
                </body>
         
     | 
| 
      
 18 
     | 
    
         
            +
            </html>
         
     | 
| 
      
 19 
     | 
    
         
            +
            `
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            export interface PageProps {
         
     | 
| 
      
 2 
     | 
    
         
            +
                name: string
         
     | 
| 
      
 3 
     | 
    
         
            +
                path: string
         
     | 
| 
      
 4 
     | 
    
         
            +
                order: number
         
     | 
| 
      
 5 
     | 
    
         
            +
                title: string
         
     | 
| 
      
 6 
     | 
    
         
            +
                description: string
         
     | 
| 
      
 7 
     | 
    
         
            +
                content: string
         
     | 
| 
      
 8 
     | 
    
         
            +
                siteMap: SiteMap
         
     | 
| 
      
 9 
     | 
    
         
            +
                tableOfContent: { path: string; label: string }[]
         
     | 
| 
      
 10 
     | 
    
         
            +
            }
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            export interface CustomOptions extends Omit<PageProps, 'content'> {
         
     | 
| 
      
 13 
     | 
    
         
            +
                layout: string
         
     | 
| 
      
 14 
     | 
    
         
            +
            }
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            export type SiteMap = Map<string, CustomOptions | SiteMap>
         
     | 
    
        package/tsconfig.json
    ADDED
    
    | 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
                "compilerOptions": {
         
     | 
| 
      
 3 
     | 
    
         
            +
                    "target": "es2015",
         
     | 
| 
      
 4 
     | 
    
         
            +
                    "types": ["node"],
         
     | 
| 
      
 5 
     | 
    
         
            +
                    "moduleResolution": "nodenext",
         
     | 
| 
      
 6 
     | 
    
         
            +
                    "lib": ["decorators", "es5", "scripthost", "es2015.promise"],
         
     | 
| 
      
 7 
     | 
    
         
            +
                    "outDir": "./dist",
         
     | 
| 
      
 8 
     | 
    
         
            +
                    "module": "nodenext",
         
     | 
| 
      
 9 
     | 
    
         
            +
                    "declaration": true,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    "declarationMap": false,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    "sourceMap": true,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    "removeComments": true,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    "downlevelIteration": true,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    "strict": true,
         
     | 
| 
      
 15 
     | 
    
         
            +
                    "alwaysStrict": true,
         
     | 
| 
      
 16 
     | 
    
         
            +
                    "resolveJsonModule": true,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    "allowSyntheticDefaultImports": true,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    "esModuleInterop": true,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    "skipLibCheck": true,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    "forceConsistentCasingInFileNames": true,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    "allowJs": true
         
     | 
| 
      
 22 
     | 
    
         
            +
                },
         
     | 
| 
      
 23 
     | 
    
         
            +
                "include": ["./src/**/*"]
         
     | 
| 
      
 24 
     | 
    
         
            +
            }
         
     |