Skip to content

Commit 2f64070

Browse files
committed
Breaking: use ESM and drop Node.js < 14
CommonJS modules can no longer `require('hallmark')`. They must use ESM themselves, or a dynamic `import()`. For further guidance please see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c As for the `hallmark` CLI: in Node.js versions older than 14 it is now a noop. Needed for #80. Depends on tape-testing/tape#571
1 parent 0082679 commit 2f64070

9 files changed

Lines changed: 144 additions & 99 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
strategy:
88
matrix:
99
os: [ubuntu-latest, macos-latest, windows-latest]
10-
node: [10, 12, 14, 16]
10+
node: [14, 16]
1111
runs-on: ${{ matrix.os }}
1212
name: ${{ matrix.os }} / Node ${{ matrix.node }}
1313
steps:

cli.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
#!/usr/bin/env node
2-
'use strict'
32

4-
if (process.version.match(/^v(\d+)\./)[1] < 10) {
5-
console.error('hallmark: Node 10 or greater is required. `hallmark` did not run.')
6-
process.exit(0)
7-
}
3+
import subarg from 'subarg'
4+
import fs from 'node:fs'
5+
import * as hallmark from './index.js'
86

9-
const argv = require('subarg')(process.argv.slice(2), {
7+
const argv = subarg(process.argv.slice(2), {
108
boolean: ['fix', 'help', 'version', 'commits'],
119
string: ['report'],
1210
default: {
@@ -25,16 +23,17 @@ const argv = require('subarg')(process.argv.slice(2), {
2523
if (argv.help) {
2624
usage(0)
2725
} else if (argv.version) {
28-
console.log(require('./package.json').version)
26+
const fp = new URL('./package.json', import.meta.url)
27+
console.log(JSON.parse(fs.readFileSync(fp, 'utf8')).version)
2928
} else {
3029
const { commits, _: rest, ...options } = argv
3130

3231
if (rest[0] === 'lint') {
3332
options.files = files(rest.slice(1))
34-
require('./index.js').lint(options, done)
33+
hallmark.lint(options, done)
3534
} else if (rest[0] === 'fix') {
3635
options.files = files(rest.slice(1))
37-
require('./index.js').fix(options, done)
36+
hallmark.fix(options, done)
3837
} else if (rest[0] === 'bump') {
3938
console.error("Error: the 'bump' command has been renamed to 'cc add'.\n")
4039
usage(1)
@@ -43,7 +42,7 @@ if (argv.help) {
4342
const target = rest[2]
4443
if (!target) usage(1)
4544
options.files = files(rest.slice(3))
46-
require('./index.js').cc.add(target, { ...options, commits }, done)
45+
hallmark.cc.add(target, { ...options, commits }, done)
4746
} else {
4847
console.error('Error: unknown command.')
4948
usage(1)
@@ -52,7 +51,7 @@ if (argv.help) {
5251
// Old usage (no commands)
5352
// TODO: deprecate?
5453
options.files = files(rest)
55-
require('./index.js')[options.fix ? 'fix' : 'lint'](options, done)
54+
hallmark[options.fix ? 'fix' : 'lint'](options, done)
5655
}
5756
}
5857

@@ -66,9 +65,8 @@ function done (err, result) {
6665
}
6766

6867
function usage (exitCode) {
69-
const fs = require('fs')
70-
const path = require('path')
71-
const usage = fs.readFileSync(path.join(__dirname, 'USAGE'), 'utf8').trim()
68+
const fp = new URL('./USAGE', import.meta.url)
69+
const usage = fs.readFileSync(fp, 'utf8').trim()
7270

7371
if (exitCode) {
7472
console.error(usage)

compat/cli.cjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env node
2+
'use strict'
3+
4+
// Must be CommonJS to support Node.js < 12.20
5+
if (process.version.match(/^v(\d+)\./)[1] < 14) {
6+
// Return silently to support hallmark in 'npm test'
7+
console.error('Skipping hallmark: Node 14 or greater is required.')
8+
process.exit(0)
9+
}
10+
11+
// Wrapped again to avoid 'Unexpected token import'
12+
require('./import-cli.cjs')

compat/import-cli.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict'
2+
3+
import('../cli.js').catch(function (err) {
4+
console.error(err)
5+
process.exit(1)
6+
})

index.js

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
'use strict'
2-
3-
const deglob = require('deglob')
4-
const find = require('find-file-up')
5-
const Githost = require('find-githost')
6-
const engine = require('unified-engine')
7-
const color = require('supports-color').stdout
8-
const fromCallback = require('catering').fromCallback
9-
const processor = require('remark')
10-
const path = require('path')
11-
const fs = require('fs')
1+
import deglob from 'deglob'
2+
import find from 'find-file-up'
3+
import Githost from 'find-githost'
4+
import engine from 'unified-engine'
5+
import { stdout as color } from 'supports-color'
6+
import { fromCallback } from 'catering'
7+
import defaultReporter from 'vfile-reporter-shiny'
8+
import processor from 'remark'
9+
import remarkCommonChangelog from 'remark-common-changelog'
10+
import remarkGithub from 'remark-github'
11+
import remarkAutolinkReferences from 'remark-autolink-references'
12+
import remarkToc from 'remark-toc'
13+
import remarkCollapse from 'remark-collapse'
14+
import path from 'node:path'
15+
import fs from 'node:fs'
16+
import linter from './lint.js'
1217

1318
const kPromise = Symbol('promise')
1419

@@ -46,7 +51,7 @@ function hallmark (options, callback) {
4651
reporter = reporter._[0]
4752
}
4853
} else {
49-
reporter = require('vfile-reporter-shiny')
54+
reporter = defaultReporter
5055
}
5156

5257
const paddedTable = rc.paddedTable !== false
@@ -73,23 +78,23 @@ function hallmark (options, callback) {
7378
reporter,
7479
reporterOptions,
7580
plugins: [
76-
[require('remark-common-changelog'), { cwd, fix, pkg, repository, ...changelog }],
77-
[require('remark-github'), { repository }],
81+
[remarkCommonChangelog, { cwd, fix, pkg, repository, ...changelog }],
82+
[remarkGithub, { repository }],
7883

7984
// Does nothing unless configured
8085
rc.autolinkReferences
81-
? [require('remark-autolink-references'), {
86+
? [remarkAutolinkReferences, {
8287
...rc.autolinkReferences,
8388
fix
8489
}]
8590
: null,
8691

8792
// TODO: https://github.com/vweevers/hallmark/issues/36
88-
toc ? [require('remark-toc'), { tight: true }] : null,
89-
toc ? [require('remark-collapse'), collapseToc()] : null,
93+
toc ? [remarkToc, { tight: true }] : null,
94+
toc ? [remarkCollapse, collapseToc()] : null,
9095

9196
fix ? fixers : null,
92-
require('./lint.js')({ fix, repository, paddedTable, validateLinks }),
97+
linter({ fix, repository, paddedTable, validateLinks }),
9398
plugins
9499
].filter(Boolean),
95100
settings: {
@@ -121,7 +126,7 @@ function hallmark (options, callback) {
121126
return callback[kPromise]
122127
}
123128

124-
exports.lint = function (options, callback) {
129+
export function lint (options, callback) {
125130
if (typeof options === 'function') {
126131
callback = options
127132
options = {}
@@ -130,7 +135,7 @@ exports.lint = function (options, callback) {
130135
return hallmark({ ...options, fix: false }, callback)
131136
}
132137

133-
exports.fix = function (options, callback) {
138+
export function fix (options, callback) {
134139
if (typeof options === 'function') {
135140
callback = options
136141
options = {}
@@ -139,28 +144,29 @@ exports.fix = function (options, callback) {
139144
return hallmark({ ...options, fix: true }, callback)
140145
}
141146

142-
exports.cc = {}
143-
exports.cc.add = function (target, options, callback) {
144-
if (!target) {
145-
throw new TypeError('First argument "target" is required')
146-
} else if (typeof target !== 'string') {
147-
throw new TypeError('First argument "target" must be a string')
148-
}
147+
export const cc = {
148+
add: function (target, options, callback) {
149+
if (!target) {
150+
throw new TypeError('First argument "target" is required')
151+
} else if (typeof target !== 'string') {
152+
throw new TypeError('First argument "target" must be a string')
153+
}
149154

150-
if (typeof options === 'function') {
151-
callback = options
152-
options = {}
153-
} else if (options == null) {
154-
options = {}
155-
}
155+
if (typeof options === 'function') {
156+
callback = options
157+
options = {}
158+
} else if (options == null) {
159+
options = {}
160+
}
156161

157-
const changelog = {
158-
commits: options.commits !== false,
159-
...options.changelog,
160-
add: target
161-
}
162+
const changelog = {
163+
commits: options.commits !== false,
164+
...options.changelog,
165+
add: target
166+
}
162167

163-
return hallmark({ ...options, changelog, fix: true }, callback)
168+
return hallmark({ ...options, changelog, fix: true }, callback)
169+
}
164170
}
165171

166172
function read (file, cwd) {

lint.js

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
1-
module.exports = function ({ fix, repository, paddedTable, validateLinks }) {
1+
import remarkLint from 'remark-lint'
2+
import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references'
3+
import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions'
4+
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions'
5+
import remarkLintNoInlinePadding from 'remark-lint-no-inline-padding'
6+
import remarkLintBlockquoteIndentation from 'remark-lint-blockquote-indentation'
7+
import remarkLintCheckboxContentIndent from 'remark-lint-checkbox-content-indent'
8+
import remarkLintFinalNewline from 'remark-lint-final-newline'
9+
import remarkLintListItemBulletIndent from 'remark-lint-list-item-bullet-indent'
10+
import remarkLintListItemIndent from 'remark-lint-list-item-indent'
11+
import remarkLintNoAutoLinkWithoutProtocol from 'remark-lint-no-auto-link-without-protocol'
12+
import remarkLintNoBlockquoteWithoutMarker from 'remark-lint-no-blockquote-without-marker'
13+
import remarkLintNoLiteralUrls from 'remark-lint-no-literal-urls'
14+
import remarkLintNoHeadingContentIndent from 'remark-lint-no-heading-content-indent'
15+
import remarkLintHardBreakSpaces from 'remark-lint-hard-break-spaces'
16+
import remarkLintCodeBlockStyle from 'remark-lint-code-block-style'
17+
import remarkLintTableCellPadding from 'remark-lint-table-cell-padding'
18+
import remarkLintTablePipes from 'remark-lint-table-pipes'
19+
import remarkLintCheckboxCharacterStyle from 'remark-lint-checkbox-character-style'
20+
import remarkLintDefinitionCase from 'remark-lint-definition-case'
21+
import remarkValidateLinks from 'remark-validate-links'
22+
23+
export default function ({ fix, repository, paddedTable, validateLinks }) {
224
const preset = {
325
plugins: [
4-
require('remark-lint'),
26+
remarkLint,
527

628
// These are not automatically fixed by remark-stringify
7-
require('remark-lint-no-undefined-references'),
8-
require('remark-lint-no-unused-definitions'),
9-
require('remark-lint-no-duplicate-definitions'),
10-
require('remark-lint-no-inline-padding'),
11-
[require('remark-lint-blockquote-indentation'), 2], // Means 1 space.
12-
require('remark-lint-checkbox-content-indent')
29+
remarkLintNoUndefinedReferences,
30+
remarkLintNoUnusedDefinitions,
31+
remarkLintNoDuplicateDefinitions,
32+
remarkLintNoInlinePadding,
33+
[remarkLintBlockquoteIndentation, 2], // Means 1 space.
34+
remarkLintCheckboxContentIndent
1335

1436
// TBD
1537
// require('remark-lint-no-shortcut-reference-image'),
@@ -19,32 +41,32 @@ module.exports = function ({ fix, repository, paddedTable, validateLinks }) {
1941

2042
if (!fix) {
2143
preset.plugins.push(
22-
require('remark-lint-final-newline'),
23-
require('remark-lint-list-item-bullet-indent'),
24-
[require('remark-lint-list-item-indent'), 'space'],
25-
require('remark-lint-no-auto-link-without-protocol'),
26-
require('remark-lint-no-blockquote-without-marker'),
27-
require('remark-lint-no-literal-urls'),
28-
require('remark-lint-no-heading-content-indent'),
29-
require('remark-lint-hard-break-spaces'),
30-
[require('remark-lint-code-block-style'), 'fenced'],
44+
remarkLintFinalNewline,
45+
remarkLintListItemBulletIndent,
46+
[remarkLintListItemIndent, 'space'],
47+
remarkLintNoAutoLinkWithoutProtocol,
48+
remarkLintNoBlockquoteWithoutMarker,
49+
remarkLintNoLiteralUrls,
50+
remarkLintNoHeadingContentIndent,
51+
remarkLintHardBreakSpaces,
52+
[remarkLintCodeBlockStyle, 'fenced'],
3153

3254
// TODO: support fixed-width columns (https://github.com/remarkjs/remark-lint/issues/217)
33-
paddedTable ? [require('remark-lint-table-cell-padding'), 'padded'] : null,
55+
paddedTable ? [remarkLintTableCellPadding, 'padded'] : null,
3456

35-
require('remark-lint-table-pipes'),
36-
[require('remark-lint-checkbox-character-style'), {
57+
remarkLintTablePipes,
58+
[remarkLintCheckboxCharacterStyle, {
3759
checked: 'x', unchecked: ' '
3860
}],
39-
require('remark-lint-definition-case')
61+
remarkLintDefinitionCase
4062
)
4163
}
4264

4365
// Temporarily allow skipping link validation, because remark does not parse
4466
// HTML anchors - as used in various Level readme's. Those readme's should be
4567
// updated to use markdown only.
4668
if (validateLinks) {
47-
preset.plugins.push([require('remark-validate-links'), {
69+
preset.plugins.push([remarkValidateLinks, {
4870
// If we don't pass this, remark-validate-links tries to get the repo url
4971
// from `git remote -v` which is not desirable for forks.
5072
repository: repository || false

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44
"description": "Markdown Style Guide, with linter and automatic fixer",
55
"author": "Vincent Weevers",
66
"license": "GPL-3.0",
7-
"bin": "cli.js",
8-
"main": "index.js",
7+
"bin": "compat/cli.cjs",
8+
"type": "module",
9+
"exports": "./index.js",
910
"scripts": {
10-
"test": "standard && depcheck && node cli.js && tape test/*.js",
11+
"test": "standard && depcheck --ignores subarg && node cli.js && tape test/*.js",
1112
"hallmark": "node cli.js --fix",
1213
"check-licenses": "npm-consider install --test --production"
1314
},
1415
"files": [
1516
"CHANGELOG.md",
1617
"cli.js",
18+
"compat",
1719
"index.js",
1820
"lint.js",
1921
"USAGE"
@@ -56,12 +58,12 @@
5658
"vfile-reporter-shiny": "^0.1.2"
5759
},
5860
"devDependencies": {
59-
"depcheck": "^1.2.0",
61+
"depcheck": "^1.4.2",
6062
"git-pull-or-clone": "^2.0.1",
6163
"npm-consider": "^1.7.0",
6264
"standard": "^16.0.4",
63-
"tape": "^5.0.1",
64-
"tempy": "0.2.1"
65+
"tape": "github:substack/tape#d487add",
66+
"tempy": "^2.0.0"
6567
},
6668
"repository": {
6769
"type": "git",
@@ -84,7 +86,7 @@
8486
"unified"
8587
],
8688
"engines": {
87-
"node": ">=6"
89+
"node": ">=14"
8890
},
8991
"config": {
9092
"allowedLicenseTypes": [

0 commit comments

Comments
 (0)