-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
225 lines (186 loc) · 6.32 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import deglob from 'deglob'
import find from 'find-file-up'
import Githost from 'find-githost'
import { engine } from 'unified-engine'
import supportsColor from 'supports-color'
import { fromCallback } from 'catering'
import defaultReporter from 'vfile-reporter-shiny'
import { remark as processor } from 'remark'
import remarkGfm from 'remark-gfm'
import remarkCommonChangelog from 'remark-common-changelog'
import remarkGithub from 'remark-github'
import remarkAutolinkReferences from 'remark-autolink-references'
import path from 'node:path'
import fs from 'node:fs'
import linter from './lint.js'
const kPromise = Symbol('promise')
function hallmark (options, callback) {
callback = fromCallback(callback, kPromise)
const fix = !!options.fix
const cwd = path.resolve(options.cwd || '.')
const pkg = read('package.json', cwd) || {}
const rc = Object.assign({}, read('.hallmarkrc', cwd), pkg.hallmark)
const files = first('files', options, rc) || ['*.md']
const repository = repo(cwd, options, rc, pkg)
const ignore = concat('ignore', rc, options)
deglob(files, { usePackageJson: false, cwd, ignore }, function (err, files) {
if (err) return callback(err)
if (!files.length) {
callback(null, { code: 0, files: [] })
return
}
let reporter
let reporterOptions
if (options.report === false) {
reporter = function noop () {}
} else if (options.report) {
// Only take one --report option
reporter = [].concat(options.report)[0]
if (typeof reporter !== 'string') {
// Reporter was specified with subarg syntax
reporterOptions = reporter
reporter = reporter._[0]
}
} else {
reporter = defaultReporter
}
const paddedTable = rc.paddedTable !== false
const validateLinks = rc.validateLinks !== false
const changelog = Object.assign({}, rc.changelog, options.changelog)
const plugins = { plugins: concat('plugins', rc, options) }
const fixers = { plugins: concat('fixers', rc, options) }
if ('contributors' in rc) {
return callback(new Error("The 'contributors' option has been removed"))
}
if ('community' in rc) {
return callback(new Error("The 'community' option has been removed"))
}
engine({
processor,
extensions: ['md', 'markdown'],
color: supportsColor.stdout,
files,
cwd,
reporter,
reporterOptions,
plugins: [
[remarkGfm, {
tableCellPadding: true,
// Allow disabling padding because on big tables it creates noise.
tablePipeAlign: paddedTable,
// In addition, use fixed width columns. TODO: use string-width package
stringLength: paddedTable ? (s) => String(s).length : () => 3
}],
[remarkCommonChangelog, { cwd, fix, pkg, repository, ...changelog }],
[remarkGithub, { repository }],
// Does nothing unless configured
rc.autolinkReferences
? [remarkAutolinkReferences, {
...rc.autolinkReferences,
fix
}]
: null,
fix ? fixers : null,
linter({ fix, repository, paddedTable, validateLinks }),
plugins
].filter(Boolean),
settings: {
// One style for code blocks, whether they have a language or not.
fences: true,
// Whether to indent the content of list items with the size of the bullet plus one space
listItemIndent: 'one',
// Marker to use for bullets of items in unordered lists
bullet: '-',
// Marker to use for thematic breaks
rule: '-',
// Marker to use to serialize emphasis
emphasis: '_',
// Marker to use to serialize strong
strong: '*'
},
pluginPrefix: 'remark',
// "Whether to write successfully processed files"
output: fix,
// "Whether to write the processed file to streamOut"
out: false,
// "Call back with an unsuccessful (1) code on warnings as well as errors"
frail: true,
// "Do not report successful files"
quiet: true
}, function (err, code, context) {
if (err) return callback(err)
callback(null, { code, files: context.files })
})
})
return callback[kPromise]
}
export function lint (options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
return hallmark({ ...options, fix: false }, callback)
}
export function fix (options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
return hallmark({ ...options, fix: true }, callback)
}
export const cc = {
add: function (target, options, callback) {
if (!target) {
throw new TypeError('First argument "target" is required')
} else if (Array.isArray(target)) {
if (!target.every(t => typeof t === 'string' && t.trim() !== '')) {
throw new TypeError('First argument "target" must be a string or array of strings')
}
} else if (typeof target === 'object' && target !== null) {
// Range
} else if (typeof target !== 'string') {
throw new TypeError('First argument "target" must be a string or array of strings')
}
if (typeof options === 'function') {
callback = options
options = {}
} else if (options == null) {
options = {}
}
const files = ['CHANGELOG.md']
const fp = path.join(options.cwd || '.', files[0])
if (!fs.existsSync(fp)) {
fs.writeFileSync(fp, '# Changelog\n')
}
const changelog = {
commits: options.commits !== false,
...options.changelog,
add: target
}
return hallmark({ ...options, files, changelog, fix: true }, callback)
}
}
function read (file, cwd) {
const fp = find.sync(file, cwd, 100)
return fp ? JSON.parse(fs.readFileSync(fp, 'utf8')) : null
}
function first (key, ...sources) {
for (const src of sources) {
if (src[key]) return src[key]
}
}
function repo (cwd, options, rc, pkg) {
const override = options.repository || rc.repository
const committish = false
if (override) {
return Githost.fromUrl(override, { committish }).https()
}
const host = (
Githost.fromPkg(pkg, { committish, optional: true }) ||
Githost.fromGit(cwd, { committish })
)
return host.https()
}
function concat (key, rc, options) {
return [].concat(rc[key] || []).concat(options[key] || [])
}