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 🙏

© 2024 – Pkg Stats / Ryan Hefner

never-write

v1.2.3

Published

simple static site generator

Downloads

14

Readme

never-write

🇨🇳中文 | ENGLISH

never-write 是一个基于 markdown 的静态站点生成工具。

安装

npm i never-write -g

使用

# 初始化,必须在空目录
neverw --init

# 生成
neverw build

NEVERW --INIT

neverw --init 命令会生成一个基础模板。

# 所有文章、发布内容都写在 posts 目录下
- posts/
# 静态资源目录
- public/
# 配置文件
- never-write.yaml

所有文章都写在 posts 目录下,目前只会处理 .md 后缀的文件。

该目录可以在配置文件中重新制定(build.postsRootPath)。

never-write.yaml

never-write.yaml 标记了当前目录为一个处理站点,所以所有配置的路径都要当对于配置文件所在的目录。

该配置可以自己创建,也可以用 neverw --init 生成。

site:
  # 默认 空
  name: 站点名称
  # 默认 空
  desc: 站点描述

build:
  # 默认 ./dist
  outDir: 生成输出目录
  # 默认 /
  publicPath: 资源路径前缀
  # 默认 ./posts
  postsRootPath: markdown文件的查找根目录
  # 默认 ./public
  publicResourcePath: 静态资源,会直接拷贝到 outDir 下
  # 默认 无
  hook: 一些钩子,在文章、索引生成前后阶段调用
  # 默认 true
  htmlMinify: 是否压缩输出的html

render:
  # 可以传递字符串,为内部预设的主题
  # 如果传递对象,则会根据路径对应去找文件
  # 默认 cactus-dark,目前可选 cactus-dark|plain|plain-dark
  template:
    index: 索引页面模板
    post: 文章模板
    tagIndexes: 根据标签聚合的索引页面

  # 默认 15
  pageSize: 索引页一页的索引数量

  # 默认 YYYY/MM/DD hh:mm:ss
  dateFormatter: 会提前处理好一部分时间,方便模板里使用,处理的格式会按这个配置来

  # 默认 100
  summaryLength: 摘要的长度

  # 默认 空
  footerText: 页脚文案,可以是html

默认配置

site:
  name:
  desc:

build:
  outDir: ./dist
  publicPath: /
  postsRootPath: ./posts
  publicResourcePath: ./public
  htmlMinify: true
  hook:

render:
  template: cactus-dark
  pageSize: 15
  dateFormatter: YYYY/MM/DD hh:mm:ss
  summaryLength: 100
  footerText:

注意点

  • 配置文件里的所有路径需要是相对配置文件根目录的路径,参考默认配置

自定义模板、样式、主题

模板引擎为ejs,在创建阶段会提供一些变量使用,所以自定义模板只要提供符合ejs语法的文件传递给配置项即可。

参考:https://github.com/hiNISAL/never-write/blob/main/templates/theme/plain-dark/index.html

传递给EJS的选项

interface PostOptions {
  // markdown文件头部的yaml信息
  meta: Record<string, any>.
  // 拼接 publicPath后的路径
  staticPath: string;
  // 在站点中的相对路径
  relativePath: string;
  // markdown文件的文件信息
  sourceFileStat: fs.Stats;
  // 文件保存的名称
  filename: string;
  // 不包含后缀的文件名
  filenameWithoutExt: string;
  // never-write.yaml 中的 site 配置项
  site: { name: string; desc: string };
  // markdown 转换后的 html
  html: string;
  // md转换后剔除标签的文本内容
  text: string;
  // 摘要 默认取text的前100个字符
  summary: string;
  // 根据配置中 render.dateFormatter 生成的时间
  // mtime 是文件的修改时间
  dateFormat: { mtime: string };
  // 本地保存目录
  postSavedDir: string;
  // 保存在本地的路径
  postSavedFullPath: string;
  namespace: 'post';
  // 首页的路径
  home: string;
  // render config
  render: Record<string, any>;
}

索引页面传递的内容

template.indextemplate.tagIndexes 获得的属性是一样的。

interface IndexesOptions {
  // 由 post 组成的数组,长度为配置的pageSize
  posts: PostOptions[];
  // 当前第几页
  page: number;
  // 共有几页
  pageTotal: number;
  // 共由几篇内容
  totalCount: number;
  // 当前自己的站点相对路径
  selfPath: string;
  // 上一页的站点相对路径
  prevPagePath: string;
  // 下一页的站点相对路径
  nextPagePath: string;
  // 当前自己的站点拼接publicPath后的相对路径
  selfStaticPath: string;
  // 上一页的站点拼接publicPath后的相对路径
  prevPageStaticPath: string;
  // 下一页的站点拼接publicPath后的相对路径
  nextPageStaticPath: string;
  // 是否第一页
  isFirstPage: boolean;
  // 是否最后一页
  isEndPage: boolean;
  // never-write.yaml 中的 site 配置项
  site: { name: string; desc: string };
  namespace: 'indexes'|'tag';
  // 聚合好的tag
  tag: Record<string, {
    posts: PostOptions[];
    staticPath: string;
  }>;
  // 当前页面的tag,如果namespace是indexes,则永远为空
  tag: '';
  // 首页路径
  home: string;
  // render config
  render: Record<string, any>;
}

hook

在配置中加入:

build:
  hook: ./hook/index.js

build过程中就会读取该文件,并且执行对应的钩子。

// ./hook/index.js

module.exports = {
  // 生成每一篇前执行
  eachBeforeRenderPost(opt) {
    console.log(opt);
  },
  // 生成每一篇后执行
  eachAfterRenderPost(opt) {
    console.log(opt);
  },
  // 生成每一页索引前执行
  eachBeforeRenderIndexes(opt): {
    console.log(opt);
  },
  // 生成每一页索引后执行
  eachAfterRenderIndexes(opt): {
    console.log(opt);
  },
  // 生成每一页tag索引前执行
  eachBeforeRenderTagsIndexes(opt) {
    console.log(opt);
  },
  // 生成每一页tag索引后执行
  eachAfterRenderTagsIndexes(opt) {
    console.log(opt);
  },
  // 在索引页中的排序规则
  sortBy(a, b) {
    // ...
  },
};

怎么配置tag

markdown 文件的头部可以用yaml描述一些信息:

---
title: page title
tag: tag1, tag2, tag3
---

## 正文内容

嘻嘻哈哈哈

其中tag会通过,切割后解析成数组(可以直接传递yaml语法中的数组)。

在处理所有markdown文件时,会根据tag聚合。

保留目录

这些目录有特殊用途,在posts目录下不能使用。

  • page 用于存放索引分页
  • tags 用于存放tag索引页

其他

默认主题的样式照搬了hexo的cactus主题,感谢(自己想真的太麻烦了。