[
  {
    "path": ".github/workflows/test.yml",
    "content": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake\n# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby\n\nname: CI Test\n\non:\n  push:\n    branches:\n      - '**'\n    tags:\n      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10\n  pull_request:\n    branches:\n      - '**'\n\nenv:\n  PRINCE_VER: 15.3\n  DOCKERHUB_SLUG: openbayes/docusaurus-prince-pdf\n  GHCR_SLUG: ghcr.io/signcl/docusaurus-prince-pdf\n\njobs:\n  build-n-deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Install Node.js\n      uses: actions/setup-node@v4\n      with:\n        node-version: 20\n\n    - name: Install bun\n      uses: oven-sh/setup-bun@v2\n\n    - name: Install dependencies\n      run: bun install\n\n    - name: Install Prince\n      run: |\n        curl https://www.princexml.com/download/prince-${{ env.PRINCE_VER }}-linux-generic-x86_64.tar.gz -O\n        tar zxf prince-${{ env.PRINCE_VER }}-linux-generic-x86_64.tar.gz\n        cd prince-${{ env.PRINCE_VER }}-linux-generic-x86_64\n        yes \"\" | sudo ./install.sh\n\n    - name: Build PDF\n      run: bun run test\n\n    - name: Upload results\n      uses: actions/upload-artifact@v4\n      with:\n        name: result\n        path: pdf/docusaurus.io-docs.pdf\n        if-no-files-found: error\n\n    # Start build Docker image\n    - name: Set up QEMU\n      uses: docker/setup-qemu-action@v3\n\n    - name: Set up Docker Buildx\n      id: buildx\n      uses: docker/setup-buildx-action@v3\n\n    - name: Available platforms\n      run: echo ${{ steps.buildx.outputs.platforms }}\n\n    - name: Log in to Docker Hub\n      uses: docker/login-action@v3\n      with:\n        username: ${{ secrets.DOCKER_USER }}\n        password: ${{ secrets.DOCKER_PASS }}\n\n    - name: Login to GitHub Container Registry\n      uses: docker/login-action@v3\n      with:\n        registry: ghcr.io\n        username: ${{ github.actor }}\n        password: ${{ secrets.GITHUB_TOKEN }}\n\n    - name: Docker meta\n      id: meta\n      uses: docker/metadata-action@v5\n      with:\n        images: |\n          ${{ env.DOCKERHUB_SLUG }}\n          ${{ env.GHCR_SLUG }}\n        tags: |\n          type=edge\n          type=schedule\n          type=ref,event=branch\n          type=ref,event=pr\n          type=semver,pattern={{version}}\n          type=semver,pattern={{major}}.{{minor}}\n          type=semver,pattern={{major}}\n          type=sha\n\n    - name: Build and push\n      uses: docker/bake-action@v6\n      with:\n        files: |\n          ./docker-bake.hcl\n          ${{ steps.meta.outputs.bake-file }}\n        source: .\n        targets: build-all\n        push: ${{ github.event_name != 'pull_request' }}\n        set: |\n          *.cache-from=type=gha\n          *.cache-to=type=gha,mode=max\n"
  },
  {
    "path": ".gitignore",
    "content": "pdf/\nnode_modules/\n.DS_Store\nbuild/\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true,\n  // https://biomejs.dev/reference/vscode/\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.biome\": \"explicit\",\n    \"source.organizeImports.biome\": \"explicit\"\n  }\n}\n"
  },
  {
    "path": "Dockerfile",
    "content": "# https://bun.sh/guides/ecosystem/docker\n# use the official Bun image\n# see all versions at https://hub.docker.com/r/oven/bun/tags\nFROM oven/bun:1-alpine as base\nWORKDIR /app\n\n# install dependencies into temp directory\n# this will cache them and speed up future builds\nFROM base AS install\nRUN mkdir -p /temp/dev\nCOPY package.json bun.lock /temp/dev/\nRUN cd /temp/dev && bun install --frozen-lockfile\n\n# install with --production (exclude devDependencies)\nRUN mkdir -p /temp/prod\nCOPY package.json bun.lock /temp/prod/\nRUN cd /temp/prod && bun install --frozen-lockfile --production\n\n# copy node_modules from temp directory\n# then copy all (non-ignored) project files into the image\nFROM base AS prerelease\nCOPY --from=install /temp/dev/node_modules node_modules\nCOPY . .\n\n# [optional] tests & build\nENV NODE_ENV=production\n# RUN bun test\n# RUN bun run build\n\n# copy production dependencies and source code into final image\nFROM base AS release\nCOPY --from=install /temp/prod/node_modules node_modules\nCOPY --from=prerelease /app/index.ts .\nCOPY --from=prerelease /app/print.css .\nCOPY --from=prerelease /app/package.json .\n\n# install prince\nVOLUME /app\nARG TARGETARCH\n# https://www.princexml.com/latest/\n# https://www.princexml.com/download/prince-14.2-alpine3.13-x86_64.tar.gz -o prince.tar.gz\nARG PRINCE_VER=15.1\nARG DISTRO=linux-generic\nRUN echo \"Building for $TARGETARCH\"\nRUN apk add --no-cache curl\nRUN prince_arch=$([ \"$TARGETARCH\" == \"arm64\" ] && echo \"aarch64-musl\" || echo \"x86_64\") \\\n    && curl https://www.princexml.com/download/prince-${PRINCE_VER}-${DISTRO}-${prince_arch}.tar.gz -o prince.tar.gz \\\n    && mkdir prince \\\n    && tar -zxvf prince.tar.gz -C prince --strip-components=1 \\\n    && rm prince.tar.gz \\\n    && cd prince \\\n    && yes \"\" | ./install.sh\nRUN apk add --no-cache \\\n    terminus-font \\\n    ttf-inconsolata \\\n    ttf-dejavu \\\n    font-croscore \\\n    font-noto \\\n    font-noto-extra \\\n    --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community/\n\n# Install fonts\nRUN apk add --no-cache msttcorefonts-installer fontconfig && \\\n    update-ms-fonts && \\\n    fc-cache -f && rm -rf /var/cache/*\n\n# run the app\nUSER bun\nEXPOSE 8080/tcp\nENTRYPOINT [ \"bun\", \"run\", \"index.ts\" ]\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2023 OpenBayes\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Docusaurus Prince PDF Generator\n\nExtract rendered data from Docusaurus/Fumadocs/etc and generate PDF, the hard way\n\n## Demo/Examples\n\n<img width=\"1008\" alt=\"Prince PDF for Docusaurus Documentation\" src=\"https://user-images.githubusercontent.com/96356/127639981-68aae100-9b96-4abc-920a-5fd8c6507a0d.png\">\n\nYou can download it in [GitHub Actions](https://github.com/signcl/docusaurus-prince-pdf/actions/workflows/test.yml) artifacts section to see the result.\n\nThis project is using the method 1 (see below) for generating PDF. You must have [Prince](https://www.princexml.com/) installed on your local machine.\n\n## Usage\n\nInstall [Prince](https://www.princexml.com/download/) first.\n\nRun the following commands to generate PDF:\n\n```bash\n# Genrate PDF from specific site under `docs` scope\nnpx docusaurus-prince-pdf -u https://docusaurus.io/docs\n\n# Change generating scope to `/docs/cli/`\nnpx docusaurus-prince-pdf -u https://docusaurus.io/docs/cli\n\n# Custom working (output) directory\nnpx docusaurus-prince-pdf -u https://openbayes.com/docs --dest ./pdf-output\n\n# Custom output file name\nnpx docusaurus-prince-pdf -u https://openbayes.com/docs --output docs.pdf\n```\n\nTo generate PDF from a local Docusaurus instance. You need to first build the site locally:\n\n```bash\n# Build the site\n(npm|bun|yarn|pnpm) build\n\n# Serve built site locally\n(npm|bun|yarn|pnpm) serve\n\n# Generate PDF from local Docusaurus instance\nnpx docusaurus-prince-pdf -u http://localhost:4000/docs # Change port to your serving port\n```\n\n## Docker\n\n- [Docker Hub](https://hub.docker.com/r/openbayes/docusaurus-prince-pdf)\n- [ghcr.io](https://github.com/orgs/signcl/packages/container/package/docusaurus-prince-pdf)\n\nYou can run this program with Docker image:\n\n```bash\ndocker run --rm -it --init \\\n  -v $(pwd)/pdf:/app/pdf \\\n  openbayes/docusaurus-prince-pdf \\\n  -u https://docusaurus.io/docs/\n```\n\nIf you need Asiatic languages support like Chinese and Japanese. You can mount your custom fonts directory to Docker image:\n\n```bash\ndocker run --rm -it --init \\\n  -v $(pwd)/pdf:/app/pdf \\\n  -v $(pwd)/fonts:/root/.fonts \\\n  openbayes/docusaurus-prince-pdf \\\n  -u https://docusaurus.io/docs/\n```\n\n## GitHub Actions\n\nYou can also run this program inside GitHub Actions:\n\n```yaml\njobs:\n  build:\n    # prerequisites...\n\n    - name: Install Prince\n      run: |\n        curl https://www.princexml.com/download/prince-14.2-linux-generic-x86_64.tar.gz -O\n        tar zxf prince-14.2-linux-generic-x86_64.tar.gz\n        cd prince-14.2-linux-generic-x86_64\n        yes \"\" | sudo ./install.sh\n\n    - name: Build PDF\n      run: npx docusaurus-prince-pdf -u https://docusaurus.io/docs/\n\n    - name: Upload results\n      uses: actions/upload-artifact@v3\n      with:\n        name: result\n        # The output filename can be specified with --output option\n        path: pdf/docusaurus.io-docs.pdf\n        if-no-files-found: error\n\n    # ...other steps\n```\n\nYou can also run `prince` with prebuilt Prince Docker image:\n\n```yaml\njobs:\n  build:\n    # prerequisites...\n\n    - name: Build PDF\n      run: docker run --rm -it -v $(pwd)/pdf:/app/pdf openbayes/docusaurus-prince-pdf -u https://docusaurus.io/docs/\n\n    # ...other steps\n```\n\n## Development\n\nYou need to have [Bun](https://bun.sh/) installed first. This can also let you run latest code on your local machine.\n\n```bash\nbun run index.ts -u http://localhost:4000/docs\n```\n\n## Options\n\n- `--url` (`-u`): Base URL, should be the `baseUrl` of the Docusaurus instance (e.g. https://docusaurus.io/docs/)\n- `--selector` (`-s`): CSS selector to find the link of the next page\n- `--dest` (`-d`): Working directory. Default to `./pdf`\n- `--file` (`-f`): Change default list output filename\n- `--output` (`-o`): Change PDF output filename\n- `--include-index`: Include passed URL in generated PDF\n- `--prepend`: Prepend additional pages, split with comma\n- `--append`: Append additional pages, split with comma\n- `--prince-args`: Additional options for Prince. ie. `--prince-args=\"--page-size='210mm 297mm'\"` or `--prince-args \"\\\\-\\\\-page\\\\-size='210mm 297mm'\"`\n- `--prince-docker`: Use external Prince docker image to generate PDF. See https://github.com/sparanoid/docker-prince for more info\n- `--list-only`: Fetch list without generating PDF\n- `--pdf-only`: Generate PDF without fetching list. Ensure list exists\n- `--cookie`: Specify the cookie with the domain part, e.g. `--cookie=\"token=123456; domain=example.com;\"`\n\n## How it works\n\nLike [mr-pdf](https://github.com/kohheepeace/mr-pdf), this package looks for the next pagination links on generated Docusaurus site. Collect them in a list and then pass the list to Prince to generate the PDF.\n\nYou can specify the CSS selector if you're using custom Docusaurus theme:\n\n```bash\nnpx docusaurus-prince-pdf -u https://openbayes.com/ --selector 'nav.custom-pagination-item--next > a'\n```\n\n## Does it work with Fumadocs or other static site/docs generators?\n\nIt should work with any static site/docs generators that have consistent pagination links.\n\nFor Fumadocs, you can use the same method to generate the PDF using custom selector like this:\n\n```bash\nnpx docusaurus-prince-pdf -u https://fumadocs.vercel.app/docs/ui --selector '#nd-page > article > div.grid.grid-cols-2.gap-4.pb-6 a:last-child'\n```\n\nThe Tailwind-styled selector is not elegant but it works.\n\n## Why this package?\n\nI made a comparison list for the two methods of generating PDF from Docusaurus.\n\n### Method 1: Prince\n\nThe good:\n\n- Best font subsetting support\n- Text can be selected and copy/paste correctly\n- Fancy Table of Contents\n\nThe bad:\n\n- Watermark on first page of generated PDF make it hard to handle in CI/CD environments\n- Doesn't work with some CSS syntax (e.g. `mask-image`)\n- Doesn't work with some HTML features (e.g. `srcset`)\n- Commercial license is expensive ([$3,800](https://www.princexml.com/purchase/))\n\nThe ugly:\n\n- None\n\n### Method 2: [mr-pdf](https://github.com/kohheepeace/mr-pdf) (not used in this project)\n\nThe good:\n\n- Free and open-source\n- Works with Docusaurus sites\n- CI/CD friendly\n- Based on Puppeteer make it works for most modern CSS syntax (e.g. `mask-image`)\n\nThe bad:\n\n- Doesn't work well with system Dark Mode. You will get a dark background in generated PDF when you have `respectPrefersColorScheme` enabled in your Docusaurus instance. But it's not an issue in Ci/CD environments\n- No Table of Contents\n\nThe ugly:\n\n- Based on Puppeteer make the text cannot be copied or searched correctly\n- Link anchors (links start with `#`) not well handled\n\nUsage:\n\n```bash\nnpx mr-pdf --initialDocURLs=\"https://openbayes.com/docs/\" --paginationSelector=\".pagination-nav__item--next > a\" --contentSelector=\"article\"\n```\n\n## License\n\nMIT\n"
  },
  {
    "path": "biome.jsonc",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.2.0/schema.json\",\n  \"vcs\": {\n    \"enabled\": true,\n    \"clientKind\": \"git\",\n    \"useIgnoreFile\": true\n  },\n  \"files\": {\n    \"ignoreUnknown\": true,\n    \"includes\": [\"**\", \"!node_modules\", \"!dist\", \"!build\", \"!output\", \"!out\", \"!references\"]\n  },\n  \"formatter\": {\n    \"enabled\": true,\n    \"indentStyle\": \"space\",\n    \"indentWidth\": 2,\n    \"lineWidth\": 120\n  },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": {\n      \"recommended\": true,\n      \"complexity\": {\n        \"noImportantStyles\": \"off\"\n      }\n    }\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\",\n      \"jsxQuoteStyle\": \"single\",\n      \"semicolons\": \"asNeeded\",\n      \"trailingCommas\": \"es5\",\n      \"arrowParentheses\": \"asNeeded\"\n    }\n  }\n}\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n\n# Build the project using bun\nbun build --compile --minify --sourcemap --target=bun-linux-x64 ./index.ts --outfile build/dpdf-linux-x64\nbun build --compile --minify --sourcemap --target=bun-linux-arm64 ./index.ts --outfile build/dpdf-linux-arm64\nbun build --compile --minify --sourcemap --target=bun-windows-x64 ./index.ts --outfile build/dpdf-windows-x64\nbun build --compile --minify --sourcemap --target=bun-darwin-arm64 ./index.ts --outfile build/dpdf-darwin-arm64\nbun build --compile --minify --sourcemap --target=bun-darwin-x64 ./index.ts --outfile build/dpdf-darwin-x64\n"
  },
  {
    "path": "docker-bake.hcl",
    "content": "variable \"DEFAULT_TAG\" {\n  default = [\"openbayes/docusaurus-prince-pdf:local\"]\n}\n\n# Special target: https://github.com/docker/metadata-action#bake-definition\ntarget \"docker-metadata-action\" {}\n\n# Default target if none specified\ngroup \"default\" {\n  targets = [\"build-local\"]\n}\n\ntarget \"build\" {\n  inherits = [\"docker-metadata-action\"]\n}\n\ntarget \"build-local\" {\n  inherits = [\"build\"]\n  tags = \"${DEFAULT_TAG}\"\n  output = [\"type=docker\"]\n}\n\ntarget \"build-all\" {\n  inherits = [\"build\"]\n  platforms = [\n    \"linux/amd64\",\n    \"linux/arm64\",\n  ]\n}\n"
  },
  {
    "path": "index.ts",
    "content": "import { exec } from 'node:child_process'\nimport { parseArgs } from 'node:util'\nimport jsdom from 'jsdom'\n\n// const browser = new Browser()\nconst { JSDOM } = jsdom\nconst buffer = new Set()\n\nconst baseDir = import.meta.dir\n\nconst { values } = parseArgs({\n  args: Bun.argv,\n  options: {\n    /** Base URL, should be the `baseUrl` of the Docusaurus instance (e.g. https://docusaurus.io/docs/) */\n    url: {\n      type: 'string',\n      short: 'u',\n    },\n    /** CSS selector to find the link of the next page */\n    selector: {\n      type: 'string',\n      short: 's',\n    },\n    /** Working directory. Default to `./pdf` */\n    dest: {\n      type: 'string',\n      short: 'd',\n      default: './pdf',\n    },\n    /** Change default list output filename */\n    file: {\n      type: 'string',\n      short: 'f',\n    },\n    /** Change PDF output filename */\n    output: {\n      type: 'string',\n      short: 'o',\n    },\n    /** Include passed URL in generated PDF */\n    'include-index': {\n      type: 'boolean',\n    },\n    /** Prepend additional pages, split with comma */\n    prepend: {\n      type: 'string',\n    },\n    /** Append additional pages, split with comma */\n    append: {\n      type: 'string',\n    },\n    /** Additional options for Prince. ie. `--prince-args=\"--page-size='210mm 297mm'\"` or `--prince-args \"\\\\-\\\\-page\\\\-size='210mm 297mm'\"` */\n    'prince-args': {\n      type: 'string',\n    },\n    /** Use external Prince docker image to generate PDF. See https://github.com/sparanoid/docker-prince for more info */\n    'prince-docker': {\n      type: 'boolean',\n    },\n    /** Fetch list without generating PDF */\n    'list-only': {\n      type: 'boolean',\n    },\n    /** Generate PDF without fetching list. Ensure list exists */\n    'pdf-only': {\n      type: 'boolean',\n    },\n    /** Specify the cookie with the domain part, e.g. `--cookie=\"token=123456; domain=example.com;\"` */\n    cookie: {\n      type: 'string',\n    },\n  },\n  strict: true,\n  allowPositionals: true,\n})\n\nconst url = values.url ? values.url.replace(/\\/$/, '') : 'https://dev.openbayes.com/docs'\nconst parsedUrl = new URL(url)\nconst baseUrl = parsedUrl.origin\nconst scope = parsedUrl.pathname\nconst scopeName = scope !== '/' ? `-${scope.replace(/\\/$/g, '').replace(/^\\//g, '').replace(/\\//g, '-')}` : ''\n\nconst dest = values.dest\nconst listFile = values.file || `${dest}/${parsedUrl.hostname}${scopeName}.txt`\nconst pdfFile = values.output || `${dest}/${parsedUrl.hostname}${scopeName}.pdf`\nconst fetchOptions = {}\n\nfunction execute(cmd: string): Promise<{\n  stdout: string\n  stderr: string\n}> {\n  const s = (b: string) => String(b).trim()\n\n  return new Promise((resolve, reject) => {\n    exec(cmd, (error, stdout, stderr) => {\n      if (error) return reject(error)\n      resolve({ stdout: s(stdout), stderr: s(stderr) })\n    })\n  })\n}\n\nasync function generatePdf(list: string, filename: string, cookie?: string) {\n  console.log(`Generating PDF ${filename}`)\n\n  const args = values['prince-args'] || ''\n  const cookieArg = cookie ? `--cookie \"${cookie}\"` : ''\n\n  const princeCmd = values['prince-args']\n    ? `docker run --rm -i -v ${baseDir}/:/config sparanoid/prince --no-warn-css --style=/config/print.css ${cookieArg} --input-list=/config/${list} -o /config/${filename} ${args}`\n    : `prince --no-warn-css --style=${baseDir}/print.css ${cookieArg} --input-list=${list} -o ${filename} ${args}`\n  console.log(`Executing command: ${princeCmd}`)\n\n  // TODO: https://github.com/oven-sh/bun/issues/9747\n  // await $`${princeCmd}`\n\n  await execute(princeCmd)\n    .then(resp => {\n      console.log(resp.stdout)\n      console.log(`Done`)\n    })\n    .catch(err => {\n      throw new Error(err)\n    })\n}\n\nasync function requestPage(url: string) {\n  try {\n    const resp = await fetch(url, fetchOptions)\n    const body = await resp.text()\n\n    const dom = new JSDOM(body).window\n    const nextLinkEl = dom.document.body.querySelector(values.selector || '.pagination-nav__link--next')\n\n    // TODO: jsdom does not have bultin DOM types.\n    const nextLink = nextLinkEl && 'href' in nextLinkEl && `${baseUrl}${nextLinkEl.href}`\n    const cycle = buffer.has(nextLink)\n\n    if (!cycle && nextLink) {\n      const nextLink = `${baseUrl}${nextLinkEl.href}`\n      console.log(`Got link: ${nextLink}`)\n\n      buffer.add(nextLink)\n      requestPage(nextLink)\n    } else {\n      if (cycle) {\n        console.log(`Pagination cycle detected on ${url}`)\n      } else {\n        console.log('No next link found!')\n      }\n\n      if (values.append) {\n        values.append.split(',').forEach(async item => {\n          const url = item.match(/^https?:\\/\\//) ? item : `${baseUrl}${scope}${item}`\n          buffer.add(url)\n          console.log(`Got link: ${url} [append]`)\n        })\n      }\n\n      if (buffer.size > 0) {\n        console.log(`Writing buffer (${buffer.size} links) to ${listFile}`)\n        await Bun.write(listFile, [...buffer].join('\\n'))\n\n        if (!values['list-only']) {\n          generatePdf(listFile, pdfFile, values.cookie)\n        }\n      } else {\n        console.log('No buffer to write!')\n      }\n    }\n  } catch (err) {\n    console.error('Error fetching page:', err)\n  }\n}\n\nif (values['pdf-only']) {\n  generatePdf(listFile, pdfFile, values.cookie)\n} else {\n  if (values.prepend) {\n    values.prepend.split(',').forEach(item => {\n      const url = item.match(/^https?:\\/\\//) ? item : `${baseUrl}${scope}${item}`\n      buffer.add(url)\n      console.log(`Got link: ${url} [prepend]`)\n    })\n  }\n\n  if (values['include-index']) {\n    console.log(`Got link: ${baseUrl}${scope} [index]`)\n    buffer.add(`${baseUrl}${scope}`)\n  }\n\n  requestPage(`${baseUrl}${scope}`)\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"docusaurus-prince-pdf\",\n  \"version\": \"1.2.2\",\n  \"description\": \"Extract rendered data from Docusaurus and generate PDF, the hard way\",\n  \"main\": \"index.js\",\n  \"bin\": \"index.js\",\n  \"type\": \"module\",\n  \"repository\": \"https://github.com/signcl/docusaurus-prince-pdf\",\n  \"author\": \"OpenBayes\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"test\": \"bun run index.ts -u https://docusaurus.io/docs/\",\n    \"build\": \"bash run build.sh\",\n    \"release\": \"bunx release-it\",\n    \"lint\": \"biome check\",\n    \"format\": \"biome format --write\"\n  },\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\"\n  },\n  \"release-it\": {\n    \"github\": {\n      \"release\": true\n    }\n  },\n  \"dependencies\": {\n    \"jsdom\": \"^26.1.0\"\n  },\n  \"packageManager\": \"bun@1.1.7\",\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"^2.2.0\",\n    \"@types/bun\": \"latest\",\n    \"@types/jsdom\": \"^21.1.7\"\n  },\n  \"peerDependencies\": {\n    \"typescript\": \"^5.9.2\"\n  }\n}\n"
  },
  {
    "path": "print.css",
    "content": "@media print {\n  /* Fix pages cut off bug by Prince */\n  /* Ref https://www.princexml.com/forum/topic/4608 */\n  .row {\n    display: block !important;\n  }\n\n  .markdown header h1 {\n    /* biome-ignore lint/correctness/noUnknownFunction: vendor-specific */\n    string-set: doctitle content();\n  }\n\n  @page {\n    /* biome-ignore lint/correctness/noUnknownProperty: vendor-specific */\n    prince-shrink-to-fit: auto;\n  }\n\n  /* Elements should be removed in PDF */\n  .navbar,\n  .pagination-nav,\n  .theme-doc-breadcrumbs,\n  a.hash-link,\n  div[class*=\"docItemContainer\"] article footer,\n  aside[class*=\"docSidebarContainer\"],\n  a[class*=\"skipToContent\"],\n  div[class*=\"lastUpdated\"],\n  div[class*=\"tocCollapsible\"],\n  div[class*=\"tableOfContents\"],\n  .footer {\n    display: none !important;\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    // Enable latest features\n    \"lib\": [\"ESNext\"],\n    \"types\": [\"@types/bun\"],\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"allowJs\": true,\n\n    // Bundler mode\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"verbatimModuleSyntax\": true,\n    \"noEmit\": true,\n\n    // Best practices\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"noFallthroughCasesInSwitch\": true,\n\n    // Some stricter flags (disabled by default)\n    \"noUnusedLocals\": false,\n    \"noUnusedParameters\": false,\n    \"noPropertyAccessFromIndexSignature\": false\n  }\n}\n"
  }
]