0.8.1 • Published 2 months ago

zhlint v0.8.1

Weekly downloads
14
License
MIT
Repository
github
Last release
2 months ago

zhlint

一个中文内容的格式化工具。

如何安装

您可以通过 npmyarn 安装 zhlint

# 通过 npm 安装
npm install zhlint -g

# 或通过 yarn
yarn global add zhlint

# 或通过 pnpm
pnpm add zhlint -g

用法

作为 CLI

# Glob 文件,执行格式化命令,并打印错误报告,
# 如果有任何错误被发现,则会以错误码 `1` 退出。
zhlint <file-pattern>

# Glob 文件,并修复所有可能发现的错误。
zhlint <file-pattern> --fix

# 格式化文件并将修复后的内容输出到另一个文件。
zhlint <input-file-path> --output=<output-file-path>

# 打印用法信息
zhlint --help

错误报告看起来像这样:

npm.io

高阶用法

zhlint 还支持 rc 和 ignore 配置文件:

# 默认为 .zhlintrc
zhlint --config <filepath>

# 默认为 .zhlintignore
zhlint --ignore <filepath>
zhlint --file-ignore <filepath>

# 默认为 .zhlintcaseignore
zhlint --case-ignore <filepath>

# 默认为 current directory
zhlint --dir <path>

在 rc 配置文件中,您可以写一个 JSON,例如:

{
  "preset": "default",
  "rules": {
    "adjustedFullwidthPunctuation": ""
  }
}

关于更多细节,请参见支持的规则

在 file-ignore 文件中,您可以写多行内容来忽略相应的文件,其语法遵循 .gitignore 语法

在 case-ignore 文件中,您可以写多行内容来忽略一些特例,例如:

( , )

关于更多细节:请参见设置被忽略的特例

作为 Node.js 包

const { run, report } = require('zhlint')

const value = '自动在中文和English之间加入空格'
const options = { rules: { preset: 'default' }}
const output = run(value, options)

// 打印 '自动在中文和 English 之间加入空格'
console.log(output.result)

// 打印错误报告
report([output])

错误报告的格式像这样:

1:6 - 此处中英文内容之间需要一个空格

自动在中文和English之间加入空格
      ^

1:13 - 此处中英文内容之间需要一个空格

自动在中文和English之间加入空格
             ^
Invalid files:
- foo.md

Found 2 errors.

高阶用法

zhlint 还支持 rc 和 ignore 配置文件:

const { readRc, runWithConfig } = require('zhlint')

const value = '自动在中文和English之间加入空格'

const dir = '...' // 目标文件所在的目录
const configPath = '...' // rc 配置文件路径
const fileIgnorePath = '...' // file-ignore 文件路径
const caseIgnorePath = '...' // case-ignore 文件路径

const config = readRc(dir, configPath, fileIgnorePath, caseIgnorePath)
const output = runWithConfig(value, config)

// ... 后续操作

作为一个单独的包

您可以找到一个 JavaScript 文件 dist/zhlint.js 作为独立版本。 例如,要使用它,您可以直接将它添加到您的浏览器中作为 <script> 标签。 即可访问全局变量 zhlint

npm.io

API

  • run(str: string, options?: Options): Result:格式化某个文件内容。
    • 参数:
      • str:需要格式化的文本内容。
      • options:一些配置选项。
    • 返回值:
      • 针对输入的单个字符串的处理结果。其包好了修复格式之后的文本内容 value 以及所有 validation 的格式化信息。
  • report(results: Result[], logger?: Console): void:为每个文件打印格式化报告。
    • 参数:
      • results:所有格式化结果的数组。
      • logger:日志处理器实例,默认是 Node.js/浏览器中的 console
  • readRc: (dir: string, config: string, fileIgnore: string, caseIgnore: string, logger?: Console) => Config:读取配置文件信息。
  • runWithConfig(str: string, config: Config): Result:通过配置信息格式化特定内容。

选项

自定义你的格式化规则和其它高阶选项。

type Options = {
  rules?: RuleOptions
  hyperParse?: string[]
  ignoredCases?: IgnoredCase[]
  logger?: Console
}
  • rules:自定义格式化规则,可以是 undefined 意味着不做任何格式化,也可以是 { preset: 'default' } 以使用默认配置。关于 RuleOptions 的更多细节参见支持的规则
  • hyperParse:根据解析器名自定义超文本解析器列表。可以是 undefined 以使用默认的忽略特例的解析器Markdown 解析器以及Hexo tag 解析器
  • ignoredCases:提供想要忽略的特例。
  • logger:和 report(...) 中的参数相同。

RC Config

  • preset: string (可选项)
  • rules: RuleOptions 但不包括 preset 字段。(可选项)
  • hyperParsers: string[] (可选项)
  • caseIgnores: string[] 该优先级低于 .zhlintcaseignore。(可选项)

输出格式

type Result = {
  // 基本信息和文件的可用性
  file?: string
  disabled: boolean

  // 原始的文本内容
  origin: string

  // 所有错误信息
  validations: Validation[]
}

type Validation = {
  message: string
  index: number
  length: number
}
  • Result
    • file:文件名。这是一个可选的字段,只在 CLI 中适用。
    • origin:原始的文本内容。
    • result:最终修复格式的文本内容。
    • validations:所有格式化信息。
  • Validation
    • index:输入的字符串中目标片段所在的索引值。
    • length:输入的字符串中目标片段的长度。
    • message:对该格式化信息的自然语言描述。

特性

Markdown 语法支持

我们默认支持格式化 Markdown 语法的文本内容。例如:

run('自动在_中文_和**English**之间加入空格')

这将首先分析 Markdown 语法并提取纯文本内容,然后执行格式化操作。修复后的纯文本内容可以复原为 Markdown 格式字符串,并作为结果中的输出 value 返回。

Hexo tag 语法支持

特别地,我们支持 Hexo tag 语法。这是因为当我们使用 Hexo 构建 Vue.js 网站时,Markdown 源文件中可能包含一些特殊的标签从而得到不可预测的格式化结果。

因此,我们会默认跳过 Hexo 风格的标签。例如:

run('现在过滤器只能用在插入文本中 (`{% raw %}{{ }}{% endraw %}` tags)。')

设置被忽略的特例

在一些特殊的情况下,我们可能会有一些特殊的文本内容不遵循规则。因此,我们可以使用 ignoredCases 选项来配置。例如,我们想要保留括号内的空格,这在默认情况下是格式不正确的。不过我们可以在文件的任何地方写一行 HTML 注释:

<!-- the good case -->

text before (text inside) text after

<!-- the bad case -->

vm.$on( event, callback )

<!-- 我们可以在这里写下要忽略的特例 -->
<!-- zhlint ignore: ( , ) -->

或传入高阶选项:

run(str, { ignoredCases: { textStart: '( ', textEnd: ' )' } })

如果你想要忽略整个文件,你也可以添加这个 HTML 注释:

<!-- zhlint disabled -->

支持的预处理器 (超文本解析器)

  • ignore:通过 HTML 注释 <!-- zhlint ignore: ... --> 匹配所有被忽略的特例
  • hexo:匹配所有 Hexo tag 以避免它们被解析。
  • markdown:用 Markdown 解析器找到所有的块级文本和内联级的标记。

支持的规则

大多数规则都提炼自过往 W3C 中文排版需求W3C HTML 中文兴趣组Vue.js 中文文档的翻译经验。

……这些规则也许存在争议。所以如果你对某些规则不够满意,我们非常希望得到大家的反馈和改进建议。我们也一直欢迎大家来创建 issue,以讨论出可能更好的规则。

type RuleOptions = {
  /* PRESET */

  // Custom preset, currently only support:
  // - `'default'`
  preset?: string

  /* PUNCTUATIONS */

  // Convert these punctuations into halfwidth.
  // default preset: `()`
  // e.g. `(文字)` -> `(文字)`
  halfwidthPunctuation?: string

  // Convert these punctuations into fullwidth.
  // default preset: `,。:;?!“”‘’`
  // e.g. `文字,文字.` -> `文字,文字。`
  fullwidthPunctuation?: string

  // Treat these fullwidth punctuations as half-fullWidthPunctuation
  // when processing the spaces issues around them.
  // Since something like quotations in morder Chinese fonts are
  // only rendered in halfwidth.
  // default preset: `“”‘’`
  adjustedFullwidthPunctuation?: string

  // Convert traditional Chinese punctuations into simplified ones or vice versa.
  // default preset: `simplified`
  // e.g. `「文字」` -> `“文字”`
  //
  // besides the above, we also unify some common punctuations below:
  //
  // // U+2047 DOUBLE QUESTION MARK, U+203C DOUBLE EXCLAMATION MARK
  // // U+2048 QUESTION EXCLAMATION MARK, U+2049 EXCLAMATION QUESTION MARK
  // '??': ['⁇'],
  // '!!': ['‼'],
  // '?!': ['⁈'],
  // '!?': ['⁉'],
  //
  // // U+002F SOLIDUS, U+FF0F FULLWIDTH SOLIDUS
  // '/': ['/', '/'],
  //
  // // U+FF5E FULLWIDTH TILDE
  // '~': ['~', '~'],
  //
  // // U+2026 HORIZONTAL ELLIPSIS, U+22EF MIDLINE HORIZONTAL ELLIPSIS
  // '…': ['…', '⋯'],
  //
  // // U+25CF BLACK CIRCLE, U+2022 BULLET, U+00B7 MIDDLE DOT,
  // // U+2027 HYPHENATION POINT, U+30FB KATAKANA MIDDLE DOT
  // '·': ['●', '•', '·', '‧', '・'],
  //
  // advanced usage: you can also specify a more detailed map like:
  //
  // ```
  // {
  //   default: true, // follow all the default preset
  //   '「': ['“', '【'], // convert `“` or `【` into `「`
  //   '」': ['”', '】'], // convert `”` or `】` into `」`
  //  '…': true, // follow the default preset for this character
  //  '·': false, // not unify any of these characters
  // }
  // ```
  unifiedPunctuation?: 'traditional' | 'simplified' | Record<string, boolean | string[]> & { default: boolean }

  // Special case: skip `fullWidthPunctuation` for abbreviations.
  // default preset:
  // `['Mr.','Mrs.','Dr.','Jr.','Sr.','vs.','etc.','i.e.','e.g.','a.k.a']`
  skipAbbrs?: string[]

  /* SPACES AROUND LETTERS */

  // default preset: `true`
  // - `true`: one space
  // - `undefined`: do nothing
  // e.g. `foo  bar` -> `foo bar`
  spaceBetweenHalfwidthContent?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文 字` -> `文字`
  noSpaceBetweenFullwidthContent?: boolean

  // default preset: `true`
  // - `true`: one space
  // - `false`: zero space
  // - `undefined`: do nothing
  // e.g. `文字 foo文字` -> `文字 foo 文字` (`true`)
  // e.g. `文字foo 文字` -> `文字foo文字` (`false`)
  spaceBetweenMixedwidthContent?: boolean

  // Special case: skip `spaceBetweenMixedWidthContent`
  // for numbers x Chinese units.
  // default preset: `年月日天号时分秒`
  skipZhUnits?: string

  /* SPACES AROUND PUNCTUATIONS */

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文字 ,文字` -> `文字,文字`
  noSpaceBeforePauseOrStop?: boolean

  // default preset: `true`
  // - `true`: one space
  // - `false`: zero space
  // - `undefined`: do nothing
  // e.g. `文字,文字` -> `文字, 文字` (`true`)
  // e.g. `文字, 文字` -> `文字,文字` (`false`)
  spaceAfterHalfwidthPauseOrStop?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文字, 文字` -> `文字,文字`
  noSpaceAfterFullwidthPauseOrStop?: boolean

  /* SPACES AROUND QUOTATIONS */

  // default preset: `true`
  // - `true`: one space
  // - `false`: zero space
  // - `undefined`: do nothing
  // e.g. `文字 "文字"文字` -> `文字 "文字" 文字` (`true`)
  // e.g. `文字"文字" 文字` -> `文字"文字"文字` (`false`)
  spaceOutsideHalfwidthQuotation?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文字 “文字” 文字` -> `文字“文字”文字`
  noSpaceOutsideFullwidthQuotation?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文字“ 文字 ”文字` -> `文字“文字”文字`
  noSpaceInsideQuotation?: boolean

  /* SPACES AROUND BRACKETS */

  // default preset: `true`
  // - `true`: one space
  // - `false`: zero space
  // - `undefined`: do nothing
  spaceOutsideHalfwidthBracket?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  noSpaceOutsideFullwidthBracket?: boolean

  // default preset: `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  noSpaceInsideBracket?: boolean

  /* SPACES AROUND CODE */

  // default preset: `true`
  // - `true`: one space
  // - `false`: zero space
  // - `undefined`: do nothing
  // e.g. '文字 `code`文字' -> '文字 `code` 文字' ('true')
  // e.g. '文字`code` 文字' -> '文字`code`文字' ('false')
  spaceOutsideCode?: boolean

  /* SPACES AROUND MARKDOWN/HTML WRAPPERS */

  // default `true`
  // - `true`: zero space
  // - `undefined`: do nothing
  // e.g. `文字** foo **文字` -> `文字 **foo** 文字`
  noSpaceInsideHyperMark?: boolean

  /* SPACES AT THE BEGINNING/END */

  // default `true`
  // e.g. ` 文字 ` -> `文字`
  trimSpace?: boolean

  /* SKIP PURE WESTERN SENTENCES */

  // default `true`
  skipPureWestern?: boolean
}

更多信息

zhlint 是一个开源项目,目前源代码在 GitHub 上,也欢迎大家来提交 issue

0.8.1

2 months ago

0.8.0

3 months ago

0.7.4

3 months ago

0.7.2

4 months ago

0.7.3

4 months ago

0.7.1

1 year ago

0.7.0

1 year ago

0.6.2

2 years ago

0.6.1

2 years ago

0.6.0

2 years ago

0.5.3

2 years ago

0.5.0

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.5.2

2 years ago

0.5.1

2 years ago

0.3.0

4 years ago

0.2.2

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago