Skip to content

Commit

Permalink
Add JSDoc based types & update unified
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Jul 15, 2021
1 parent 883c7da commit 6fe4cbb
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
coverage/
node_modules/
.DS_Store
*.d.ts
*.log
yarn.lock
132 changes: 80 additions & 52 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import path from 'path'
// @ts-expect-error: hush
import gitDiffTree from 'git-diff-tree'
import {findUpOne} from 'vfile-find-up'

const own = {}.hasOwnProperty

/** @type {string} */
let previousRange

/** @type {import('unified').Plugin<[]>} */
export default function diff() {
/** @type {Record<string, string>} */
let cache = {}

return transform

function transform(tree, file, next) {
return function (_, file, next) {
const base = file.dirname
/** @type {string|undefined} */
let commitRange
/** @type {string[]|undefined} */
let range

// Looks like Travis.
Expand All @@ -23,6 +27,7 @@ export default function diff() {
}
// Looks like GH Actions.
else if (process.env.GITHUB_SHA) {
// @ts-expect-error: fine.
range =
// This is a PR: check the whole PR.
// Refs take the form `refs/heads/main`.
Expand All @@ -32,10 +37,17 @@ export default function diff() {
process.env.GITHUB_HEAD_REF.split('/').pop()
]
: [process.env.GITHUB_SHA + '^1', process.env.GITHUB_SHA]
// @ts-expect-error: We definitely just defined this
commitRange = range.join('...')
}

if (!base || !commitRange || range.length !== 2) {
if (
!base ||
!commitRange ||
!range ||
!file.dirname ||
range.length !== 2
) {
return next()
}

Expand All @@ -48,71 +60,84 @@ export default function diff() {
if (own.call(cache, base)) {
tick(cache[base])
} else {
findUpOne('.git', file.dirname, ongit)
}
findUpOne('.git', file.dirname, (error, git) => {
// Never happens.
/* c8 ignore next */
if (error) return next(error)

function ongit(error, git) {
// Never happens.
/* c8 ignore next */
if (error) return next(error)
// Not testable in a Git repo…
/* c8 ignore next */
if (!git) return next(new Error('Not in a git repository'))

// Not testable in a Git repo…
/* c8 ignore next */
if (!git) return next(new Error('Not in a git repository'))

cache[base] = git.dirname
tick(git.dirname)
cache[base] = git.dirname
tick(git.dirname)
})
}

/**
* @param {string} root
*/
function tick(root) {
/** @type {Record<string, [number, number][]>} */
const diffs = {}

gitDiffTree(path.join(root, '.git'), {
// @ts-expect-error: fine.
originalRev: range[0],
// @ts-expect-error: fine.
rev: range[1]
})
.on('error', next)
.on('data', (type, data) => {
if (type !== 'patch') return

const lines = data.lines
const re = /^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@/
const match = lines[0].match(re)

// Should not happen, maybe if Git returns weird diffs?
/* c8 ignore next */
if (!match) return

const ranges = []
const start = Number.parseInt(match[3], 10) - 1
let index = 0
let position

while (++index < lines.length) {
const line = lines[index]

if (line.charAt(0) === '+') {
const no = start + index

if (position === undefined) {
position = ranges.length
ranges.push([no, no])
.on(
'data',
/**
* @param {string} type
* @param {{lines: string, aPath: string, bPath: string}} data
*/
(type, data) => {
if (type !== 'patch') return

const lines = data.lines
const re = /^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@/
const match = lines[0].match(re)

// Should not happen, maybe if Git returns weird diffs?
/* c8 ignore next */
if (!match) return

/** @type {[number, number][]} */
const ranges = []
const start = Number.parseInt(match[3], 10) - 1
let index = 0
/** @type {number|undefined} */
let position

while (++index < lines.length) {
const line = lines[index]

if (line.charAt(0) === '+') {
const no = start + index

if (position === undefined) {
position = ranges.length
ranges.push([no, no])
} else {
ranges[position][1] = no
}
} else {
ranges[position][1] = no
position = undefined
}
} else {
position = undefined
}
}

const fp = path.resolve(root, data.bPath)
const fp = path.resolve(root, data.bPath)

// Long diffs.
/* c8 ignore next */
if (!(fp in diffs)) diffs[fp] = []
// Long diffs.
/* c8 ignore next */
if (!(fp in diffs)) diffs[fp] = []

diffs[fp].push(...ranges)
})
diffs[fp].push(...ranges)
}
)
.on('end', () => {
const fp = path.resolve(file.cwd, file.path)
const ranges = diffs[fp]
Expand All @@ -125,7 +150,10 @@ export default function diff() {

file.messages = file.messages.filter((message) =>
ranges.some(
(range) => message.line >= range[0] && message.line <= range[1]
(range) =>
message.line &&
message.line >= range[0] &&
message.line <= range[1]
)
)

Expand Down
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,40 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"git-diff-tree": "^1.0.0",
"vfile-find-up": "^6.0.0"
},
"devDependencies": {
"@types/rimraf": "^3.0.0",
"@types/tape": "^4.0.0",
"c8": "^7.0.0",
"nlcst-to-string": "^3.0.0",
"prettier": "^2.0.0",
"remark-cli": "^9.0.0",
"remark-preset-wooorm": "^8.0.0",
"retext-english": "^3.0.0",
"retext-stringify": "^2.0.0",
"rimraf": "^3.0.0",
"tape": "^5.0.0",
"to-vfile": "^7.0.0",
"unified": "^10.0.0-beta.1",
"type-coverage": "^2.0.0",
"typescript": "^4.0.0",
"unified": "^10.0.0",
"unist-util-visit": "^3.0.0",
"xo": "^0.39.0"
},
"scripts": {
"build": "rimraf \"*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node --conditions development test/index.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --conditions development test/index.js",
"test": "npm run format && npm run test-coverage"
"test": "npm run build && npm run format && npm run test-coverage"
},
"prettier": {
"tabWidth": 2,
Expand All @@ -70,5 +78,11 @@
"plugins": [
"preset-wooorm"
]
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}
36 changes: 18 additions & 18 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import cp from 'child_process'
import {promises as fsPromises} from 'fs'
import fs from 'fs'
import path from 'path'
import {promisify} from 'util'
import test from 'tape'
import {toVFile} from 'to-vfile'
import {processor} from './processor.js'
import rimraf from 'rimraf'

const exec = promisify(cp.exec)

Expand All @@ -23,6 +24,16 @@ const current = process.cwd()

process.chdir(path.join(current, 'test'))

process.on('exit', async () => {
process.env.TRAVIS_COMMIT_RANGE = range
process.env.GITHUB_SHA = sha
process.env.GITHUB_BASE_REF = base
process.env.GITHUB_HEAD_REF = head
process.chdir(path.join(current))
fs.rmSync(path.join('test', '.git'), {recursive: true, force: true})
fs.rmSync(path.join('test', 'example.txt'))
})

test('diff() (travis)', async (t) => {
const stepOne = [
'Lorem ipsum dolor sit amet.',
Expand All @@ -37,6 +48,7 @@ test('diff() (travis)', async (t) => {
t.plan(7)

await exec('git init')

// Set up.
try {
await exec('git config --global user.email')
Expand Down Expand Up @@ -135,11 +147,9 @@ test('diff() (travis)', async (t) => {
t.pass('should pass')

delete process.env.TRAVIS_COMMIT_RANGE
await Promise.allSettled([
fsPromises.rm('.git', {recursive: true, force: true}),
fsPromises.rm('new.txt'),
fsPromises.rm('example.txt')
])
rimraf.sync('.git')
rimraf.sync('new.txt')
rimraf.sync('example.txt')
})

test('diff() (GitHub Actions)', async (t) => {
Expand Down Expand Up @@ -213,16 +223,6 @@ test('diff() (GitHub Actions)', async (t) => {
delete process.env.GITHUB_SHA
delete process.env.GITHUB_BASE_REF
delete process.env.GITHUB_HEAD_REF
await Promise.allSettled([
fsPromises.rm('.git', {recursive: true, force: true}),
fsPromises.rm('example.txt')
])
})

process.on('exit', () => {
process.env.TRAVIS_COMMIT_RANGE = range
process.env.GITHUB_SHA = sha
process.env.GITHUB_BASE_REF = base
process.env.GITHUB_HEAD_REF = head
process.chdir(path.join(current))
rimraf.sync('.git')
rimraf.sync('example.txt')
})
22 changes: 8 additions & 14 deletions test/processor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {unified} from 'unified'
// @ts-expect-error: next
import retextEnglish from 'retext-english'
// @ts-expect-error: next
import retextStringify from 'retext-stringify'
import {visit} from 'unist-util-visit'
import {toString} from 'nlcst-to-string'
Expand All @@ -8,20 +10,12 @@ import unifiedDiff from '../index.js'
export const processor = unified()
.use(retextEnglish)
.use(retextStringify)
.use(lorem)
.use(unifiedDiff)
.freeze()

function lorem() {
return transformer

function transformer(tree, file) {
visit(tree, 'WordNode', visitor)

function visitor(node) {
.use(() => (tree, file) => {
visit(tree, 'WordNode', (node) => {
if (/lorem/i.test(toString(node))) {
file.message('No lorem!', node)
}
}
}
}
})
})
.use(unifiedDiff)
.freeze()
16 changes: 16 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"include": ["*.js", "test/**/*.js"],
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020"],
"module": "ES2020",
"moduleResolution": "node",
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDeclarationOnly": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"strict": true
}
}

0 comments on commit 6fe4cbb

Please sign in to comment.