[
  {
    "path": ".cargo/config.toml",
    "content": "[target.'cfg(target_env = \"gnu\")']\nrustflags = [\"-C\", \"link-args=-Wl,-z,nodelete\"]\n\n[target.arm-unknown-linux-gnueabihf]\nlinker = \"arm-linux-gnueabihf-gcc\"\n\n[target.armv7-unknown-linux-gnueabihf]\nlinker = \"arm-linux-gnueabihf-gcc\"\n\n[target.aarch64-unknown-linux-gnu]\nlinker = \"aarch64-linux-gnu-gcc\"\n\n[target.aarch64-unknown-linux-musl]\nlinker = \"aarch64-linux-musl-gcc\"\n"
  },
  {
    "path": ".github/workflows/tag-release.yml",
    "content": "name: tag-release\non:\n  release:\n    types: [published]\n  workflow_dispatch:\njobs:\n  build-windows:\n    name: windows\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Build native packages\n        run: yarn build-release\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bindings-windows\n          path: ./*.node\n      - name: Smoke test\n        run: node -e \"require('./')\"\n\n  build-linux-gnu-x64:\n    name: linux-gnu-x64\n    runs-on: ubuntu-latest\n    container:\n      image: node:20\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Build native packages\n        run: yarn build-release\n        env:\n          CFLAGS: -std=c99\n      - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034\n        run: strip ./*.node\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bindings-linux-gnu-x64\n          path: ./*.node\n      - name: debug\n        run: ls -l ./*.node\n      - name: Smoke test\n        run: node -e 'require(\"./\")'\n\n  build-linux-gnu-arm:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - target: arm-unknown-linux-gnueabihf\n            arch: armhf\n            strip: arm-linux-gnueabihf-strip\n          - target: aarch64-unknown-linux-gnu\n            arch: arm64\n            strip: aarch64-linux-gnu-strip\n    name: ${{ matrix.target }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n          target: ${{ matrix.target }}\n      - name: Install cross compile toolchains\n        run: |\n          sudo apt-get update\n          sudo apt-get install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Build native packages\n        run: yarn build-release --target ${{ matrix.target }}\n      - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034\n        run: ${{ matrix.strip }} ./*.node\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bindings-${{ matrix.target }}\n          path: ./*.node\n      - name: debug\n        run: ls -l ./*.node\n      - name: Configure binfmt-support\n        run: docker run --rm --privileged multiarch/qemu-user-static:register --reset\n\n  build-linux-musl:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - target: x86_64-unknown-linux-musl\n            strip: strip\n          - target: aarch64-unknown-linux-musl\n            strip: aarch64-linux-musl-strip\n    name: ${{ matrix.target }}\n    runs-on: ubuntu-latest\n    container:\n      image: node:20-alpine\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install build tools\n        run: apk add --no-cache python3 make gcc g++ musl-dev curl tar\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n          target: ${{ matrix.target }}\n      - name: Install cross compile toolchains\n        if: ${{ matrix.target == 'aarch64-unknown-linux-musl' }}\n        run: |\n          curl -L -O https://github.com/devongovett/linux-musl-cross/releases/download/v1/aarch64-linux-musl-cross.tgz\n          tar -xzf aarch64-linux-musl-cross.tgz\n          cp -R aarch64-linux-musl-cross/* /usr\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Build native packages\n        run: yarn build-release --target ${{ matrix.target }}\n      - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034\n        run: ${{ matrix.strip }} ./*.node\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bindings-${{ matrix.target }}\n          path: ./*.node\n      - name: debug\n        run: ls -l ./*.node\n      - name: Smoke test\n        if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }}\n        run: node -e 'require(\"./\")'\n\n  build-macos:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - name: aarch64-apple-darwin\n            target: aarch64-apple-darwin\n          - name: x86_64-apple-darwin\n            target: x86_64-apple-darwin\n    name: ${{ matrix.name }}\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n          target: ${{ matrix.target }}\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Build native packages\n        run: |\n          sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*;\n          export CC=$(xcrun -f clang);\n          export CXX=$(xcrun -f clang++);\n          SYSROOT=$(xcrun --sdk macosx --show-sdk-path);\n          export CFLAGS=\"-isysroot $SYSROOT -isystem $SYSROOT\";\n          export MACOSX_DEPLOYMENT_TARGET=\"10.9\";\n          yarn build-release --target ${{ matrix.target }}\n      - name: Strip debug symbols\n        run: strip -x ./*.node\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bindings-${{ matrix.target }}\n          path: ./*.node\n      - name: debug\n        run: ls -l ./*.node\n\n  build-and-release:\n    runs-on: ubuntu-latest\n    name: Build and release the tagged version\n    needs:\n      - build-windows\n      - build-linux-musl\n      - build-linux-gnu-arm\n      - build-macos\n    steps:\n      - uses: actions/checkout@v1\n      - uses: bahmutov/npm-install@v1.1.0\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n        with:\n          path: artifacts\n      - name: Move artifacts\n        run: mv artifacts/*/*.node .\n      - name: Debug\n        run: ls -l ./*.node\n      - run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc\n        env:\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n      - run: npm publish\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\nnode_modules\nCargo.lock\n*.node\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"node-tree-sitter-highlight\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n\n[dependencies]\nnapi = \"2\"\nnapi-derive = \"2\"\nlazy_static = \"1.4\"\ntree-sitter-highlight = \"0.26.3\"\ntree-sitter-javascript = \"0.25.0\"\ntree-sitter-typescript = \"0.23.2\"\ntree-sitter-jsdoc = \"0.25.0\"\ntree-sitter-json = \"0.24.8\"\ntree-sitter-css = \"0.25.0\"\ntree-sitter-regex = \"0.25.0\"\ntree-sitter-yaml = \"0.7.2\"\ntree-sitter-html = \"0.23.2\"\ntree-sitter-c = \"0.24.1\"\ntree-sitter-bash = \"0.25.1\"\ntree-sitter-rust = \"0.24.0\"\n\n[build-dependencies]\nnapi-build = { version = \"1\" }\ntree-sitter = \"0.26.3\"\ntree-sitter-highlight = \"0.26.3\"\ntree-sitter-javascript = \"0.25.0\"\ntree-sitter-typescript = \"0.23.2\"\ntree-sitter-jsdoc = \"0.25.0\"\ntree-sitter-json = \"0.24.8\"\ntree-sitter-css = \"0.25.0\"\ntree-sitter-regex = \"0.25.0\"\ntree-sitter-yaml = \"0.7.2\"\ntree-sitter-html = \"0.23.2\"\ntree-sitter-c = \"0.24.1\"\ntree-sitter-bash = \"0.25.1\"\ntree-sitter-rust = \"0.24.0\"\n\n[profile.release]\nopt-level = 3\nlto = true\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Devon Govett\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject 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,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# tree-sitter-highlight\n\nA syntax highlighter for Node.js powered by [Tree Sitter](https://github.com/tree-sitter/tree-sitter). Written in Rust.\n\n## Usage\n\nThe following will output HTML:\n\n```js\nconst treeSitter = require('tree-sitter-highlight');\n\ntreeSitter.highlight('const foo = \"hi\";', treeSitter.Language.JS);\n// => '<span class=\"source\">...</span>'\n```\n\nYou can also output a [HAST](https://github.com/syntax-tree/hast) AST, which is useful for integrating with Markdown or MDX processors (e.g. Remark).\n\n```js\ntreeSitter.highlightHast('const foo = \"hi\";', treeSitter.Language.JS);\n// => {type: 'element', children: [...]}\n```\n\n## Themes\n\nThe output HTML will contain CSS class names for various tokens. These will depend on the language, but there are several common names used across languages. Here is a basic example theme:\n\n```css\n.keyword {\n  color: purple;\n}\n\n.function {\n  color: blue;\n}\n\n.type {\n  color: pink;\n}\n\n.string {\n  color: green;\n}\n\n.number {\n  color: brown;\n}\n\n.operator {\n  color: gray;\n}\n\n.comment {\n  color: lightgray;\n}\n```\n\nInspect the generated output HTML and design your CSS accordingly.\n\n## License\n\nMIT\n"
  },
  {
    "path": "bench.js",
    "content": "const {Benchmark} = require(\"tiny-benchy\");\nconst ts = require('./');\n\nlet input = `\nfunction Example() {\n  let alertDismiss = (close) => {\n    close();\n    alert('Dialog dismissed.');\n  };\n  return (\n    <DialogTrigger isDismissable>\n      <ActionButton>Info</ActionButton>\n      {(close) => (\n        <Dialog onDismiss={() => alertDismiss(close)}>\n          <Heading>Version Info</Heading>\n          <Divider />\n          <Content>\n            <Text>Version 1.0.0, Copyright 2020</Text>\n          </Content>\n        </Dialog>\n      )}\n    </DialogTrigger>\n  );\n}\n`;\n\nlet suite = new Benchmark({iterations: 50});\n\nsuite.add('html', () => {\n  ts.highlight(input, ts.Language.JSX);\n});\n\nsuite.add('hast', () => {\n  ts.highlightHast(input, ts.Language.JSX);\n});\n\nsuite.run();\n"
  },
  {
    "path": "build.rs",
    "content": "use tree_sitter::Language;\n\nextern crate napi_build;\n\nfn main() {\n    let mut queries = String::new();\n    queries.push_str(tree_sitter_javascript::HIGHLIGHT_QUERY);\n    queries.push_str(tree_sitter_javascript::JSX_HIGHLIGHT_QUERY);\n\n    let mut highlight_names = Vec::new();\n    add_highlight_names(\n        tree_sitter_javascript::LANGUAGE.into(),\n        &queries,\n        &mut highlight_names,\n    );\n\n    add_highlight_names(\n        tree_sitter_typescript::LANGUAGE_TSX.into(),\n        tree_sitter_typescript::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_css::LANGUAGE.into(),\n        tree_sitter_css::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_regex::LANGUAGE.into(),\n        tree_sitter_regex::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_jsdoc::LANGUAGE.into(),\n        tree_sitter_jsdoc::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_json::LANGUAGE.into(),\n        tree_sitter_json::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_yaml::LANGUAGE.into(),\n        tree_sitter_yaml::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_html::LANGUAGE.into(),\n        tree_sitter_html::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_c::LANGUAGE.into(),\n        tree_sitter_c::HIGHLIGHT_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_bash::LANGUAGE.into(),\n        tree_sitter_bash::HIGHLIGHT_QUERY,\n        &mut highlight_names,\n    );\n    add_highlight_names(\n        tree_sitter_rust::LANGUAGE.into(),\n        tree_sitter_rust::HIGHLIGHTS_QUERY,\n        &mut highlight_names,\n    );\n\n    highlight_names.sort();\n\n    let html_attrs: Vec<String> = highlight_names\n        .iter()\n        .map(|s| format!(\"class=\\\"{}\\\"\", s.replace('.', \" \")))\n        .collect();\n\n    let class_names: Vec<String> = highlight_names\n        .iter()\n        .map(|s| s.replace('.', \" \"))\n        .collect();\n\n    std::fs::write(\n        \"src/highlight_names.rs\",\n        format!(\n            \"pub const HIGHLIGHT_NAMES: &[&str] = &{:#?};\\n\\npub const HTML_ATTRS: &[&str] = &{:#?};\\n\\npub const CLASS_NAMES: &[&str] = &{:#?};\\n\",\n            highlight_names,\n            html_attrs,\n            class_names\n        ),\n    )\n    .expect(\"write error\");\n\n    napi_build::setup();\n}\n\nfn add_highlight_names(lang: Language, source: &str, highlights: &mut Vec<String>) {\n    let query = tree_sitter::Query::new(&lang, source).unwrap();\n    for capture in query.capture_names() {\n        if !highlights.iter().any(|h| h == capture) {\n            highlights.push(capture.to_string());\n        }\n    }\n}\n"
  },
  {
    "path": "index.d.ts",
    "content": "/* tslint:disable */\n/* eslint-disable */\n\n/* auto-generated by NAPI-RS */\n\nexport const enum Language {\n  JS = 0,\n  JSX = 1,\n  TS = 2,\n  TSX = 3,\n  JSON = 4,\n  YAML = 5,\n  CSS = 6,\n  HTML = 7,\n  Regex = 8,\n  JsDoc = 9,\n  C = 10,\n  Bash = 11,\n  Rust = 12\n}\nexport declare function highlight(code: string, language: Language): string\nexport interface HastProperties {\n  className: string\n}\nexport interface HastNode {\n  type: string\n  tagName: string\n  properties: HastProperties\n  children: Array<HastNode | HastTextNode>\n}\nexport interface HastTextNode {\n  type: string\n  value: string\n}\nexport declare function highlightHast(code: string, language: Language): HastNode\n"
  },
  {
    "path": "index.js",
    "content": "/* tslint:disable */\n/* eslint-disable */\n/* prettier-ignore */\n\n/* auto-generated by NAPI-RS */\n\nconst { existsSync, readFileSync } = require('fs')\nconst { join } = require('path')\n\nconst { platform, arch } = process\n\nlet nativeBinding = null\nlet localFileExisted = false\nlet loadError = null\n\nfunction isMusl() {\n  // For Node 10\n  if (!process.report || typeof process.report.getReport !== 'function') {\n    try {\n      const lddPath = require('child_process').execSync('which ldd').toString().trim()\n      return readFileSync(lddPath, 'utf8').includes('musl')\n    } catch (e) {\n      return true\n    }\n  } else {\n    const { glibcVersionRuntime } = process.report.getReport().header\n    return !glibcVersionRuntime\n  }\n}\n\nswitch (platform) {\n  case 'android':\n    switch (arch) {\n      case 'arm64':\n        localFileExisted = existsSync(join(__dirname, 'tree-sitter-highlight.android-arm64.node'))\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.android-arm64.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-android-arm64')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      case 'arm':\n        localFileExisted = existsSync(join(__dirname, 'tree-sitter-highlight.android-arm-eabi.node'))\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.android-arm-eabi.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-android-arm-eabi')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      default:\n        throw new Error(`Unsupported architecture on Android ${arch}`)\n    }\n    break\n  case 'win32':\n    switch (arch) {\n      case 'x64':\n        localFileExisted = existsSync(\n          join(__dirname, 'tree-sitter-highlight.win32-x64-msvc.node')\n        )\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.win32-x64-msvc.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-win32-x64-msvc')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      case 'ia32':\n        localFileExisted = existsSync(\n          join(__dirname, 'tree-sitter-highlight.win32-ia32-msvc.node')\n        )\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.win32-ia32-msvc.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-win32-ia32-msvc')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      case 'arm64':\n        localFileExisted = existsSync(\n          join(__dirname, 'tree-sitter-highlight.win32-arm64-msvc.node')\n        )\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.win32-arm64-msvc.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-win32-arm64-msvc')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      default:\n        throw new Error(`Unsupported architecture on Windows: ${arch}`)\n    }\n    break\n  case 'darwin':\n    localFileExisted = existsSync(join(__dirname, 'tree-sitter-highlight.darwin-universal.node'))\n    try {\n      if (localFileExisted) {\n        nativeBinding = require('./tree-sitter-highlight.darwin-universal.node')\n      } else {\n        nativeBinding = require('tree-sitter-highlight-darwin-universal')\n      }\n      break\n    } catch {}\n    switch (arch) {\n      case 'x64':\n        localFileExisted = existsSync(join(__dirname, 'tree-sitter-highlight.darwin-x64.node'))\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.darwin-x64.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-darwin-x64')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      case 'arm64':\n        localFileExisted = existsSync(\n          join(__dirname, 'tree-sitter-highlight.darwin-arm64.node')\n        )\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.darwin-arm64.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-darwin-arm64')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      default:\n        throw new Error(`Unsupported architecture on macOS: ${arch}`)\n    }\n    break\n  case 'freebsd':\n    if (arch !== 'x64') {\n      throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)\n    }\n    localFileExisted = existsSync(join(__dirname, 'tree-sitter-highlight.freebsd-x64.node'))\n    try {\n      if (localFileExisted) {\n        nativeBinding = require('./tree-sitter-highlight.freebsd-x64.node')\n      } else {\n        nativeBinding = require('tree-sitter-highlight-freebsd-x64')\n      }\n    } catch (e) {\n      loadError = e\n    }\n    break\n  case 'linux':\n    switch (arch) {\n      case 'x64':\n        if (isMusl()) {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-x64-musl.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-x64-musl.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-x64-musl')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        } else {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-x64-gnu.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-x64-gnu.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-x64-gnu')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        }\n        break\n      case 'arm64':\n        if (isMusl()) {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-arm64-musl.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-arm64-musl.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-arm64-musl')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        } else {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-arm64-gnu.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-arm64-gnu.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-arm64-gnu')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        }\n        break\n      case 'arm':\n        if (isMusl()) {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-arm-musleabihf.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-arm-musleabihf.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-arm-musleabihf')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        } else {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-arm-gnueabihf.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-arm-gnueabihf.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-arm-gnueabihf')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        }\n        break\n      case 'riscv64':\n        if (isMusl()) {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-riscv64-musl.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-riscv64-musl.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-riscv64-musl')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        } else {\n          localFileExisted = existsSync(\n            join(__dirname, 'tree-sitter-highlight.linux-riscv64-gnu.node')\n          )\n          try {\n            if (localFileExisted) {\n              nativeBinding = require('./tree-sitter-highlight.linux-riscv64-gnu.node')\n            } else {\n              nativeBinding = require('tree-sitter-highlight-linux-riscv64-gnu')\n            }\n          } catch (e) {\n            loadError = e\n          }\n        }\n        break\n      case 's390x':\n        localFileExisted = existsSync(\n          join(__dirname, 'tree-sitter-highlight.linux-s390x-gnu.node')\n        )\n        try {\n          if (localFileExisted) {\n            nativeBinding = require('./tree-sitter-highlight.linux-s390x-gnu.node')\n          } else {\n            nativeBinding = require('tree-sitter-highlight-linux-s390x-gnu')\n          }\n        } catch (e) {\n          loadError = e\n        }\n        break\n      default:\n        throw new Error(`Unsupported architecture on Linux: ${arch}`)\n    }\n    break\n  default:\n    throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)\n}\n\nif (!nativeBinding) {\n  if (loadError) {\n    throw loadError\n  }\n  throw new Error(`Failed to load native binding`)\n}\n\nconst { Language, highlight, highlightHast } = nativeBinding\n\nmodule.exports.Language = Language\nmodule.exports.highlight = highlight\nmodule.exports.highlightHast = highlightHast\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"tree-sitter-highlight\",\n  \"version\": \"1.1.2\",\n  \"description\": \"A syntax highlighter for Node powered by Tree Sitter. Written in Rust.\",\n  \"repository\": \"https://github.com/devongovett/tree-sitter-highlight\",\n  \"author\": \"Devon Govett <devongovett@gmail.com>\",\n  \"license\": \"MIT\",\n  \"napi\": {\n    \"name\": \"tree-sitter-highlight\"\n  },\n  \"files\": [\n    \"*.node\",\n    \"index.js\",\n    \"index.d.ts\"\n  ],\n  \"scripts\": {\n    \"build\": \"napi build --platform\",\n    \"build-release\": \"napi build --platform --release\"\n  },\n  \"devDependencies\": {\n    \"@napi-rs/cli\": \"^2.0.0\",\n    \"tiny-benchy\": \"^2.1.0\"\n  }\n}\n"
  },
  {
    "path": "src/highlight_names.rs",
    "content": "pub const HIGHLIGHT_NAMES: &[&str] = &[\n    \"attribute\",\n    \"boolean\",\n    \"character.special\",\n    \"comment\",\n    \"comment.documentation\",\n    \"constant\",\n    \"constant.builtin\",\n    \"constant.character\",\n    \"constructor\",\n    \"delimiter\",\n    \"embedded\",\n    \"escape\",\n    \"function\",\n    \"function.builtin\",\n    \"function.macro\",\n    \"function.method\",\n    \"function.special\",\n    \"keyword\",\n    \"label\",\n    \"number\",\n    \"operator\",\n    \"property\",\n    \"punctuation.bracket\",\n    \"punctuation.delimiter\",\n    \"punctuation.special\",\n    \"string\",\n    \"string.special\",\n    \"string.special.key\",\n    \"tag\",\n    \"tag.error\",\n    \"type\",\n    \"type.builtin\",\n    \"variable\",\n    \"variable.builtin\",\n    \"variable.parameter\",\n];\n\npub const HTML_ATTRS: &[&str] = &[\n    \"class=\\\"attribute\\\"\",\n    \"class=\\\"boolean\\\"\",\n    \"class=\\\"character special\\\"\",\n    \"class=\\\"comment\\\"\",\n    \"class=\\\"comment documentation\\\"\",\n    \"class=\\\"constant\\\"\",\n    \"class=\\\"constant builtin\\\"\",\n    \"class=\\\"constant character\\\"\",\n    \"class=\\\"constructor\\\"\",\n    \"class=\\\"delimiter\\\"\",\n    \"class=\\\"embedded\\\"\",\n    \"class=\\\"escape\\\"\",\n    \"class=\\\"function\\\"\",\n    \"class=\\\"function builtin\\\"\",\n    \"class=\\\"function macro\\\"\",\n    \"class=\\\"function method\\\"\",\n    \"class=\\\"function special\\\"\",\n    \"class=\\\"keyword\\\"\",\n    \"class=\\\"label\\\"\",\n    \"class=\\\"number\\\"\",\n    \"class=\\\"operator\\\"\",\n    \"class=\\\"property\\\"\",\n    \"class=\\\"punctuation bracket\\\"\",\n    \"class=\\\"punctuation delimiter\\\"\",\n    \"class=\\\"punctuation special\\\"\",\n    \"class=\\\"string\\\"\",\n    \"class=\\\"string special\\\"\",\n    \"class=\\\"string special key\\\"\",\n    \"class=\\\"tag\\\"\",\n    \"class=\\\"tag error\\\"\",\n    \"class=\\\"type\\\"\",\n    \"class=\\\"type builtin\\\"\",\n    \"class=\\\"variable\\\"\",\n    \"class=\\\"variable builtin\\\"\",\n    \"class=\\\"variable parameter\\\"\",\n];\n\npub const CLASS_NAMES: &[&str] = &[\n    \"attribute\",\n    \"boolean\",\n    \"character special\",\n    \"comment\",\n    \"comment documentation\",\n    \"constant\",\n    \"constant builtin\",\n    \"constant character\",\n    \"constructor\",\n    \"delimiter\",\n    \"embedded\",\n    \"escape\",\n    \"function\",\n    \"function builtin\",\n    \"function macro\",\n    \"function method\",\n    \"function special\",\n    \"keyword\",\n    \"label\",\n    \"number\",\n    \"operator\",\n    \"property\",\n    \"punctuation bracket\",\n    \"punctuation delimiter\",\n    \"punctuation special\",\n    \"string\",\n    \"string special\",\n    \"string special key\",\n    \"tag\",\n    \"tag error\",\n    \"type\",\n    \"type builtin\",\n    \"variable\",\n    \"variable builtin\",\n    \"variable parameter\",\n];\n"
  },
  {
    "path": "src/lib.rs",
    "content": "mod highlight_names;\n\nuse highlight_names::{CLASS_NAMES, HIGHLIGHT_NAMES, HTML_ATTRS};\nuse lazy_static::lazy_static;\nuse napi::bindgen_prelude::*;\nuse napi_derive::napi;\nuse tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter, HtmlRenderer};\n\n#[napi]\npub enum Language {\n    JS,\n    JSX,\n    TS,\n    TSX,\n    JSON,\n    YAML,\n    CSS,\n    HTML,\n    Regex,\n    JsDoc,\n    C,\n    Bash,\n    Rust,\n}\n\nmacro_rules! language {\n    ($mod: ident, $name: literal, $highlights: ident) => {{\n        let mut config =\n            HighlightConfiguration::new($mod::LANGUAGE.into(), $name, $mod::$highlights, \"\", \"\")\n                .unwrap();\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    }};\n    ($mod: ident, $name: literal, $highlights: ident, $injections: ident) => {{\n        let mut config = HighlightConfiguration::new(\n            $mod::LANGUAGE.into(),\n            $name,\n            $mod::$highlights,\n            $mod::$injections,\n            \"\",\n        )\n        .unwrap();\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    }};\n}\n\nlazy_static! {\n    static ref JS_CONFIG: HighlightConfiguration = {\n        let mut config = HighlightConfiguration::new(\n            tree_sitter_javascript::LANGUAGE.into(),\n            \"javascript\",\n            tree_sitter_javascript::HIGHLIGHT_QUERY,\n            tree_sitter_javascript::INJECTIONS_QUERY,\n            tree_sitter_javascript::LOCALS_QUERY,\n        )\n        .unwrap();\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    };\n    static ref JSX_CONFIG: HighlightConfiguration = {\n        let mut highlights = tree_sitter_javascript::JSX_HIGHLIGHT_QUERY.to_owned();\n        highlights.push_str(tree_sitter_javascript::HIGHLIGHT_QUERY);\n\n        let mut config = HighlightConfiguration::new(\n            tree_sitter_javascript::LANGUAGE.into(),\n            \"jsx\",\n            &highlights,\n            tree_sitter_javascript::INJECTIONS_QUERY,\n            tree_sitter_javascript::LOCALS_QUERY,\n        )\n        .unwrap();\n\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    };\n    static ref TS_CONFIG: HighlightConfiguration = {\n        let mut highlights = tree_sitter_typescript::HIGHLIGHTS_QUERY.to_owned();\n        highlights.push_str(tree_sitter_javascript::HIGHLIGHT_QUERY);\n\n        let mut locals = tree_sitter_typescript::LOCALS_QUERY.to_owned();\n        locals.push_str(tree_sitter_javascript::LOCALS_QUERY);\n\n        let mut config = HighlightConfiguration::new(\n            tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),\n            \"typescript\",\n            &highlights,\n            tree_sitter_javascript::INJECTIONS_QUERY,\n            &locals,\n        )\n        .unwrap();\n\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    };\n    static ref TSX_CONFIG: HighlightConfiguration = {\n        let mut highlights = tree_sitter_javascript::JSX_HIGHLIGHT_QUERY.to_owned();\n        highlights.push_str(tree_sitter_typescript::HIGHLIGHTS_QUERY);\n        highlights.push_str(tree_sitter_javascript::HIGHLIGHT_QUERY);\n\n        let mut locals = tree_sitter_typescript::LOCALS_QUERY.to_owned();\n        locals.push_str(tree_sitter_javascript::LOCALS_QUERY);\n\n        let mut config = HighlightConfiguration::new(\n            tree_sitter_typescript::LANGUAGE_TSX.into(),\n            \"tsx\",\n            &highlights,\n            tree_sitter_javascript::INJECTIONS_QUERY,\n            &locals,\n        )\n        .unwrap();\n\n        config.configure(HIGHLIGHT_NAMES);\n        config\n    };\n    static ref JSDOC_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_jsdoc, \"jsdoc\", HIGHLIGHTS_QUERY);\n    static ref JSON_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_json, \"json\", HIGHLIGHTS_QUERY);\n    static ref YAML_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_yaml, \"yaml\", HIGHLIGHTS_QUERY);\n    static ref CSS_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_css, \"css\", HIGHLIGHTS_QUERY);\n    static ref HTML_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_html, \"html\", INJECTIONS_QUERY);\n    static ref REGEX_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_regex, \"regex\", HIGHLIGHTS_QUERY);\n    static ref C_CONFIG: HighlightConfiguration = language!(tree_sitter_c, \"c\", HIGHLIGHT_QUERY);\n    static ref BASH_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_bash, \"bash\", HIGHLIGHT_QUERY);\n    static ref RUST_CONFIG: HighlightConfiguration =\n        language!(tree_sitter_rust, \"rust\", HIGHLIGHTS_QUERY);\n}\n\nimpl Language {\n    fn highlight_config(&self) -> &'static HighlightConfiguration {\n        match self {\n            Language::JS => &*JS_CONFIG,\n            Language::JSX => &*JSX_CONFIG,\n            Language::TS => &*TS_CONFIG,\n            Language::TSX => &*TSX_CONFIG,\n            Language::JSON => &*JSON_CONFIG,\n            Language::YAML => &*YAML_CONFIG,\n            Language::CSS => &*CSS_CONFIG,\n            Language::HTML => &*HTML_CONFIG,\n            Language::Regex => &*REGEX_CONFIG,\n            Language::JsDoc => &*JSDOC_CONFIG,\n            Language::C => &*C_CONFIG,\n            Language::Bash => &*BASH_CONFIG,\n            Language::Rust => &*RUST_CONFIG,\n        }\n    }\n\n    fn from_name(name: &str) -> Option<Language> {\n        Some(match name {\n            \"js\" | \"javascript\" => Language::JS,\n            \"jsx\" => Language::JSX,\n            \"ts\" | \"typescript\" => Language::TS,\n            \"tsx\" => Language::TSX,\n            \"json\" => Language::JSON,\n            \"yaml\" => Language::YAML,\n            \"css\" => Language::CSS,\n            \"html\" => Language::HTML,\n            \"regex\" => Language::Regex,\n            \"jsdoc\" => Language::JsDoc,\n            \"c\" => Language::C,\n            \"bash\" => Language::Bash,\n            \"sh\" => Language::Bash,\n            \"rust\" => Language::Rust,\n            \"rs\" => Language::Rust,\n            _ => return None,\n        })\n    }\n}\n\n#[napi]\npub fn highlight(code: String, language: Language) -> String {\n    let config = language.highlight_config();\n    let mut highlighter = Highlighter::new();\n    let highlights = highlighter\n        .highlight(&config, code.as_bytes(), None, |lang| {\n            Language::from_name(lang).map(|l| l.highlight_config())\n        })\n        .unwrap();\n\n    let mut renderer = HtmlRenderer::new();\n    renderer\n        .render(highlights, code.as_bytes(), &|highlight, res| {\n            res.extend_from_slice(HTML_ATTRS[highlight.0].as_bytes())\n        })\n        .unwrap();\n    unsafe { String::from_utf8_unchecked(renderer.html) }\n}\n\n#[derive(Debug)]\n#[napi(object)]\npub struct HastProperties {\n    pub class_name: String,\n}\n\n#[derive(Debug)]\n#[napi(object)]\npub struct HastNode {\n    #[napi(js_name = \"type\")]\n    pub kind: String,\n    pub tag_name: String,\n    pub properties: HastProperties,\n    pub children: Vec<Either<HastNode, HastTextNode>>,\n}\n\n#[derive(Debug)]\n#[napi(object)]\npub struct HastTextNode {\n    #[napi(js_name = \"type\")]\n    pub kind: String,\n    pub value: String,\n}\n\n#[napi]\npub fn highlight_hast(code: String, language: Language) -> HastNode {\n    let config = language.highlight_config();\n    let mut highlighter = Highlighter::new();\n    let highlights = highlighter\n        .highlight(&config, code.as_bytes(), None, |lang| {\n            Language::from_name(lang).map(|l| l.highlight_config())\n        })\n        .unwrap();\n\n    let mut stack = Vec::new();\n    stack.push(HastNode {\n        kind: \"element\".into(),\n        tag_name: \"span\".into(),\n        properties: HastProperties {\n            class_name: \"source\".into(),\n        },\n        children: Vec::new(),\n    });\n\n    for event in highlights {\n        match event.unwrap() {\n            HighlightEvent::HighlightStart(highlight) => {\n                let node = HastNode {\n                    kind: \"element\".into(),\n                    tag_name: \"span\".into(),\n                    properties: HastProperties {\n                        class_name: CLASS_NAMES[highlight.0].to_owned(),\n                    },\n                    children: Vec::new(),\n                };\n                stack.push(node);\n            }\n            HighlightEvent::Source { start, end } => {\n                let slice = &code[start..end];\n                let parent = stack.last_mut().unwrap();\n                if let Some(Either::B(text_node)) = parent.children.last_mut() {\n                    text_node.value.push_str(slice);\n                } else {\n                    let text_node = HastTextNode {\n                        kind: \"text\".into(),\n                        value: slice.into(),\n                    };\n                    parent.children.push(Either::B(text_node));\n                }\n            }\n            HighlightEvent::HighlightEnd => {\n                let node = stack.pop().unwrap();\n                let parent = stack.last_mut().unwrap();\n                parent.children.push(Either::A(node));\n            }\n        }\n    }\n\n    stack.pop().unwrap()\n}\n"
  }
]