npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gitbook-plugin-tags-info

v1.0.12

Published

Gitbook tags-info for markhsu

Readme

如何製作文章標籤與分類

簡介

可以在文章最後呈現分類&標籤

專案架構

20250106_gitbook-plugin-tags-info
├─ .npmignore
├─ assets
│  └─ plugin.css
├─ CHANGELOG.md
├─ custom-release-notes-generator.js
├─ index.js
├─ LICENSE
├─ package-lock.json
├─ package.json
└─ README.md

目錄

一、事前準備

請先到github Fork這個專案到自己的本機目錄修改

https://github.com/billryan/gitbook-plugin-tags

二、操作步驟

STEP1:請先撰寫.gitlab-ci.yml

stages:
  - semantic-release
  - npm-deploy

variables:
  NPM_REGISTRY_URL: "https://registry.npmjs.org/"

before_script:
  - npm config set registry $NPM_REGISTRY_URL

semantic-release:
  stage: semantic-release
  image: node:20.18.0
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
    - npm install semver
    - npm install -g --save-dev conventional-changelog-conventionalcommits semantic-release @semantic-release/changelog @semantic-release/gitlab @semantic-release/git @semantic-release/npm @semantic-release/release-notes-generator @semantic-release/gitlab-config
    - export GL_TOKEN=${GL_TOKEN}
    - GL_TOKEN=${GL_TOKEN} npx semantic-release
  tags:
    - docker

npm-deploy:
  stage: npm-deploy
  image: node:20.18.0
  script:
    - rm -rf node_modules/
    - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
    - npm install
    - npm publish
  needs:
    - semantic-release

STEP2:請先撰寫index.js

var tags_map = {};
var categories_map = {};
var slug = require('github-slugid');
var eol = require('os').EOL;

module.exports = {
  book: {
    assets: './assets',
    css: [
      "plugin.css"
    ]
  },
  hooks: {
    "page:before": function(page) {
      if (this.output.name != 'website') return page;

      if (page.path === 'tags.md') {
        for (var key in tags_map) {
          if (tags_map.hasOwnProperty(key)) {
            var tag_header = eol.concat('## ', key, eol);
            page.content = page.content.concat(tag_header);
            tags_map[key].forEach(function(e) {
              var tag_body = eol.concat('- ', '[', e.title, ']', '(', e.url, ')');
              page.content = page.content.concat(tag_body);
            })
            page.content = page.content.concat(eol);
          }
        }
        
        for (var key in categories_map) {
          if (categories_map.hasOwnProperty(key)) {
            var cat_header = eol.concat('## ', key, eol);
            page.content = page.content.concat(cat_header);
            categories_map[key].forEach(function(e) {
              var cat_body = eol.concat('- ', '[', e.title, ']', '(', e.url, ')');
              page.content = page.content.concat(cat_body);
            })
            page.content = page.content.concat(eol);
          }
        }
        return page;
      }

      var rawtags = '';
      if (page.tags) {
        rawtags = page.tags;
      } else {
        page.content = page.content.concat(eol);
        var _tag_exist = page.content.match(/^\s*tags:\s*\[*(.*?)\]*$/im);
        if (_tag_exist) rawtags = _tag_exist[1];
      }

      var rawcategories = '';
      if (page.categories) {
        rawcategories = page.categories;
      } else {
        page.content = page.content.concat(eol);
        var _cat_exist = page.content.match(/^\s*categories:\s*\[*(.*?)\]*$/im);
        if (_cat_exist) rawcategories = _cat_exist[1];
      }

      var created = '無';
      var updated = '無';
      
      var _created_exist = page.content.match(/^\s*created:\s*(.*)$/im);
      if (_created_exist) created = _created_exist[1].trim();
      
      var _updated_exist = page.content.match(/^\s*updated:\s*(.*)$/im);
      if (_updated_exist) updated = _updated_exist[1].trim();

      var tags = [];
      if (rawtags) {
        rawtags = ('' + rawtags).split(',');
        rawtags.forEach(function(e) {
          var tags_ = e.match(/^\s*['"]*\s*(.*?)\s*['"]*\s*$/)[1];
          if (tags_) tags.push(tags_);
        });
      }

      var categories = [];
      if (rawcategories) {
        rawcategories = ('' + rawcategories).split(',');
        rawcategories.forEach(function(e) {
          var cats_ = e.match(/^\s*['"]*\s*(.*?)\s*['"]*\s*$/)[1];
          if (cats_) categories.push(cats_);
        });
      }

      tags.forEach(function(e) {
        if (!tags_map[e]) tags_map[e] = [];
        tags_map[e].push({
          url: page.path,
          title: page.title
        });
      });

      categories.forEach(function(e) {
        if (!categories_map[e]) categories_map[e] = [];
        categories_map[e].push({
          url: page.path,
          title: page.title
        });
      });

      var tags_before_ = [];
      var cats_before_ = [];

      tags.forEach(function(e) {
        if (page.type === 'markdown') {
          tags_before_.push('<a href="/tags.html#' + slug(e) + '" class="tag-button"><i class="fa fa-tag" aria-hidden="true"></i> ' + e + '</a>');
        } else {
          tags_before_.push('link:/tags.html#' + slug(e) + '[' + e + ']');
        }
      });

      categories.forEach(function(e) {
        if (page.type === 'markdown') {
          cats_before_.push('<a href="/tags.html#' + slug(e) + '" class="category-button"><i class="fa fa-folder" aria-hidden="true"></i> ' + e + '</a>');
        } else {
          cats_before_.push('link:/tags.html#' + slug(e) + '[' + e + ']');
        }
      });

      var articleInfo = eol + '<div class="article-info">' +
        '<div class="info-item"><span class="label">標題:</span> ' + page.title + '</div>' +
        '<div class="info-item"><span class="label">作者:</span> MarkHSU</div>' +
        '<div class="info-item"><span class="label">創建時間:</span> ' + created + '</div>' +
        '<div class="info-item"><span class="label">修改時間:</span> ' + updated + '</div>' +
        '<div class="info-item"><span class="label">文章連結:</span> <a href="' + page.path + '">' + page.path + '</a></div>' +
        '<div class="info-item"><span class="label">版權聲明:</span> © 2025 MarkHSU. All rights reserved' +
        '</div>' + eol;

      var meta_format = eol.concat(eol, 'metastart', eol, articleInfo);
      
      if (tags_before_.length > 0 || cats_before_.length > 0) {
        meta_format += '<div class="meta-wrapper">';
        if (tags_before_.length > 0) {
          meta_format += '<div class="meta-section tags-section">' + 
            tags_before_.join(' ') + 
            '</div>';
        }
        if (cats_before_.length > 0) {
          meta_format += '<div class="meta-section categories-section">' + 
            cats_before_.join(' ') + 
            '</div>';
        }
      }
      meta_format += eol + 'metastop' + eol;
      page.content = page.content.replace(/^\s?tags:\s?\[?(.*?)\]?$/im, eol);
      page.content = page.content.replace(/^\s?categories:\s?\[?(.*?)\]?$/im, eol);

      var placement = this.config.get('pluginsConfig.tags.placement') || 'top';
      
      if (placement === 'bottom') {
        page.content = page.content.concat(meta_format);
      } else {
        if (page.type === 'markdown') {
          page.content = page.content.replace(/^#\s*(.*?)$/m, '#$1' + meta_format);
        } else {
          page.content = page.content.replace(/^=\s*(.*?)$/m, '=$1' + meta_format);
        }
      }
      return page;
    },

    "page": function(page) {
      page.content = page.content.replace(/<\/div>\s*metastop/g, '');
      page.content = page.content.replace(/<\/div>\s*$/g, '');
      
      page.content = page.content.replace(
        /(<div class="paragraph">)?\s*<p>metastart<\/p>\s*(<\/div>)?/g, 
        '<div class="article-meta">'
      );
      page.content = page.content.replace(
        /(<div class="paragraph">)?\s*<p>metastop<\/p>\s*(<\/div>)?/g, 
        '</div>'
      );
      
      page.content = page.content.replace('<strong>ADOCTAGS</strong>', '<i class="fa fa-tags" aria-hidden="true"></i> ');
      page.content = page.content.replace('<strong>ADOCCATS</strong>', '<i class="fa fa-folder" aria-hidden="true"></i> ');
      
      return page;
    }
  }
};

STEP3:請先撰寫assets\plugin.css

.article-info {
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  border-left: 3px solid #000;
  border-radius: 5px;
  padding: 15px;
  margin-bottom: 20px;
  text-align: left;
}

.info-item {
  margin: 5px 0;
  color: #000;
  font-size: 14px;
}

.info-item .label {
  font-weight: bold;
  font-size: 14px;
  margin-right: 5px;
  color: #000;
}

.meta-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 20px;
  margin-top: 15px;
}

.meta-section {
  display: flex;
  align-items: center;
}

.tag-icon,
.category-icon {
  margin-right: 10px;
  color: #000;
}

.tag-item,
.category-item {
  display: inline-block;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 15px;
  padding: 4px 12px;
  margin: 0 4px;
  font-size: 0.9em;
}

.tag-item a,
.category-item a {
  color: #000;
  text-decoration: none;
}

.tag-item:hover,
.category-item:hover {
  background: #f0f0f0;
}

.article-meta .fa {
  margin-right: 5px;
}

.article-meta a {
  color: #000;
  text-decoration: none;
}

.article-meta a:hover {
  text-decoration: underline;
}

.tag-button, .category-button {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px;
    margin: 2px;
    border: none;
    border-radius: 15px;
    cursor: pointer;
    font-size: 13px;
    transition: all 0.2s ease;
}

.tag-button {
    background-color: #e8f5e9 !important;
    color: #2e7d32 !important;
}

.tag-button:hover {
    background: transparent;
    cursor: pointer;
    border: 1px solid #e8f5e9;
    color: #2e7d32;
    transition: all 0.3ms;
    text-decoration: none !important;
}

.category-button {
    background-color: #e3f2fd !important;
    color: #1976d2 !important;
}

.category-button:hover {
    background: transparent;
    cursor: pointer;
    border: 1px solid #e3f2fd;
    color: #1976d2;
    transition: all 0.3ms;
    text-decoration: none !important;
}

.tag-button i, .category-button i {
    margin-right: 5px;
}

.meta-wrapper {
    margin: 10px 0;
}

.meta-section {
    margin: 5px 0;
}

.article-meta {
    padding: 15px;
    border-radius: 5px;
    margin: 15px 0;
}

.article-info {
    margin-bottom: 10px;
}

.info-item {
    margin: 5px 0;
}

.info-item .label {
    font-weight: bold;
    margin-right: 5px;
}

STEP4:請先撰寫package.json

{
  "name": "gitbook-plugin-tags-info",
  "version": "1.0.9",
  "description": "Gitbook tags-info for markhsu",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "semantic-release": "semantic-release"
  },
  "repository": {
    "type": "git",
    "url": "git+https://markweb.idv.tw:10443/gitbooknpmproject/gitbook-plugin-tags-info.git"
  },
  "author": "小彥",
  "license": "ISC",
  "bugs": {
    "url": "https://markweb.idv.tw:10443/gitbooknpmproject/gitbook-plugin-tags-info/-/issues"
  },
  "homepage": "https://markweb.idv.tw:10443/gitbooknpmproject/gitbook-plugin-tags-info/-/blob/master/README.md",
  "dependencies": {
    "semver": "^7.6.3"
  },
  "engines": {
    "gitbook": ">=3.0.0"
  },
  "release": {
    "extends": "@semantic-release/gitlab-config",
    "analyzeCommits": {
      "preset": "angular",
      "releaseRules": [
        {
          "type": "feat",
          "release": "minor"
        },
        {
          "type": "fix",
          "release": "patch"
        },
        {
          "type": "docs",
          "release": "patch"
        },
        {
          "type": "style",
          "release": "patch"
        },
        {
          "type": "chore",
          "release": "patch"
        },
        {
          "type": "refactor",
          "release": "patch"
        },
        {
          "type": "test",
          "release": "patch"
        },
        {
          "type": "build",
          "release": "patch"
        },
        {
          "type": "ci",
          "release": "patch"
        }
      ]
    },
    "plugins": [
      [
        "@semantic-release/commit-analyzer",
        {
          "path": "./custom-release-notes-generator.js"
        }
      ],
      [
        "@semantic-release/release-notes-generator",
        {
          "preset": "conventionalcommits",
          "presetConfig": {
            "types": [
              {
                "type": "feat",
                "section": "✨ 新增功能"
              },
              {
                "type": "fix",
                "section": "🐞 錯誤修正"
              },
              {
                "type": "perf",
                "section": "🚀 效能調整"
              },
              {
                "type": "revert",
                "section": "⏪ 退版"
              },
              {
                "type": "docs",
                "section": "📃 文件調整",
                "hidden": false
              },
              {
                "type": "style",
                "section": "🌈 樣式調整",
                "hidden": false
              },
              {
                "type": "chore",
                "section": "🛠️ 重大更新",
                "hidden": true
              },
              {
                "type": "refactor",
                "section": "🔨 程式碼重構",
                "hidden": true
              },
              {
                "type": "test",
                "section": "🔬 單元測試",
                "hidden": false
              },
              {
                "type": "build",
                "section": "🔧 程式重構",
                "hidden": false
              },
              {
                "type": "ci",
                "section": "🐎 持續整合",
                "hidden": false
              },
              {
                "type": "other",
                "section": "🔄 其他",
                "hidden": false
              }
            ]
          },
          "parserOpts": {
            "noteKeywords": [
              "BREAKING CHANGE",
              "BREAKING CHANGES",
              "BREAKING"
            ]
          }
        }
      ],
      "@semantic-release/gitlab",
      [
        "@semantic-release/git",
        {
          "assets": [
            "package.json",
            "package-lock.json",
            "CHANGELOG.md"
          ],
          "message": "${nextRelease.type === 'major' ? '🛠️chore(release):這次是重大版更!!!' : '🐞feat/fix(release):這只是小版更!!!'}\n\n v${nextRelease.version} 新的專案版本已釋出!!! [skip ci]"
        }
      ]
    ],
    "prepare": [
      "@semantic-release/changelog",
      "@semantic-release/npm",
      {
        "path": "@semantic-release/git",
        "assets": [
          "package.json",
          "package-lock.json",
          "CHANGELOG.md"
        ],
        "npmPublish": true,
        "message": "${nextRelease.type === 'major' ? '🛠️chore(release):這次是重大版更!!!' : '🐞feat/fix(release):這只是小版更!!!'}\n\n v${nextRelease.version} 新的專案版本已釋出!!! [skip ci]"
      }
    ],
    "generateNotes": {
      "path": "./custom-release-notes-generator.js"
    }
  }
}

三、完成結果