comment-block-transformer 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/package.json +14 -12
- package/src/index.js +65 -11
- package/test/manual/_run.js +90 -0
- package/test/manual/_test.yml +2 -0
- package/test/manual/_yaml-example.js +85 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.2.1](https://github.com/DavidWells/markdown-magic/compare/comment-block-transformer@0.2.0...comment-block-transformer@0.2.1) (2025-11-03)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* replace the block by position instead of simple replace ([fd93c39](https://github.com/DavidWells/markdown-magic/commit/fd93c39fea3c4c03901babe6039243194624bdcd))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# 0.2.0 (2025-10-25)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* exclude manual test directory from test runner ([f2e9b1a](https://github.com/DavidWells/markdown-magic/commit/f2e9b1ad0754cfd7f797ed3b09a3df622675525f)), closes [#105](https://github.com/DavidWells/markdown-magic/issues/105)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* add comprehensive tests for block-transformer with multiple plugins and middleware ([1c7df35](https://github.com/DavidWells/markdown-magic/commit/1c7df357a41933e98d3da377277829b6b628c76b)), closes [#86](https://github.com/DavidWells/markdown-magic/issues/86)
|
|
28
|
+
* setup pnpm workspace with proper dependency linking ([0c8c2f9](https://github.com/DavidWells/markdown-magic/commit/0c8c2f9e10e11918f0e446e1793878825ec88c14)), closes [#88](https://github.com/DavidWells/markdown-magic/issues/88)
|
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comment-block-transformer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Transform markdown blocks based on configured transforms",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "uvu test '.test.([mc]js|[jt]sx?)$'",
|
|
9
|
+
"build": "pnpm run types",
|
|
10
|
+
"types": "tsc --emitDeclarationOnly --outDir types",
|
|
11
|
+
"clean": "rimraf types",
|
|
12
|
+
"publish": "git push origin && git push origin --tags",
|
|
13
|
+
"release:patch": "pnpm run build && pnpm version patch && pnpm publish",
|
|
14
|
+
"release:minor": "pnpm run build && pnpm version minor && pnpm publish",
|
|
15
|
+
"release:major": "pnpm run build && pnpm version major && pnpm publish"
|
|
16
|
+
},
|
|
7
17
|
"dependencies": {
|
|
8
|
-
"comment-block-parser": "1.0
|
|
18
|
+
"comment-block-parser": "1.1.0"
|
|
9
19
|
},
|
|
10
20
|
"devDependencies": {
|
|
11
21
|
"typescript": "^5.0.0",
|
|
@@ -14,13 +24,5 @@
|
|
|
14
24
|
"publishConfig": {
|
|
15
25
|
"access": "public"
|
|
16
26
|
},
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"build": "pnpm run types",
|
|
20
|
-
"types": "tsc --emitDeclarationOnly --outDir types",
|
|
21
|
-
"clean": "rimraf types",
|
|
22
|
-
"release:patch": "pnpm run build && pnpm version patch && pnpm publish",
|
|
23
|
-
"release:minor": "pnpm run build && pnpm version minor && pnpm publish",
|
|
24
|
-
"release:major": "pnpm run build && pnpm version major && pnpm publish"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
+
"gitHead": "ddedd0639200db28f1b1d0e3b805603ffaa234ee"
|
|
28
|
+
}
|
package/src/index.js
CHANGED
|
@@ -121,7 +121,7 @@ async function blockTransformer(inputText, config) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
const {
|
|
124
|
+
const { openPattern, closePattern } = foundBlocks
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
const blocksWithTransforms = foundBlocks.blocks
|
|
@@ -136,13 +136,16 @@ async function blockTransformer(inputText, config) {
|
|
|
136
136
|
|
|
137
137
|
const regexInfo = {
|
|
138
138
|
blocks: foundBlocks.pattern,
|
|
139
|
-
open:
|
|
140
|
-
close:
|
|
139
|
+
open: openPattern,
|
|
140
|
+
close: closePattern,
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const transformsToRun = sortTransforms(blocksWithTransforms, transforms)
|
|
144
144
|
|
|
145
145
|
let missingTransforms = []
|
|
146
|
+
// Track cumulative offset changes as we modify the text
|
|
147
|
+
let cumulativeOffset = 0
|
|
148
|
+
|
|
146
149
|
let updatedContents = await transformsToRun.reduce(async (contentPromise, originalMatch) => {
|
|
147
150
|
const updatedText = await contentPromise
|
|
148
151
|
/* Apply leading middleware */
|
|
@@ -211,6 +214,8 @@ async function blockTransformer(inputText, config) {
|
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
let newContent = afterContent.content.value
|
|
217
|
+
|
|
218
|
+
// console.log("afterContent.content.rawValue", afterContent.content.rawValue)
|
|
214
219
|
/* handle different cases of typeof newContent. @TODO: make this an option */
|
|
215
220
|
if (typeof newContent === 'number') {
|
|
216
221
|
newContent = String(newContent)
|
|
@@ -220,14 +225,28 @@ async function blockTransformer(inputText, config) {
|
|
|
220
225
|
newContent = JSON.stringify(newContent, null, 2)
|
|
221
226
|
}
|
|
222
227
|
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
// console.log('options', options)
|
|
229
|
+
// console.log('newContent', `"${newContent}"`)
|
|
230
|
+
|
|
231
|
+
const formattedNewContent = (options.noTrim) ? newContent : newContent
|
|
232
|
+
// const formattedNewContent = newContent//.trim()
|
|
233
|
+
// console.log('formattedNewContent', `"${formattedNewContent}"`)
|
|
234
|
+
const fix = removeConflictingComments(formattedNewContent, openPattern, closePattern)
|
|
235
|
+
|
|
236
|
+
// console.log('fix', `"${fix}"`)
|
|
225
237
|
|
|
226
238
|
let preserveIndent = 0
|
|
227
|
-
|
|
228
|
-
|
|
239
|
+
// console.log('match.content.indent', match.content.indent)
|
|
240
|
+
if (match.content.indent) {
|
|
241
|
+
preserveIndent = match.content.indent
|
|
242
|
+
// console.log('preserveIndent', preserveIndent)
|
|
229
243
|
} else if (preserveIndent === 0) {
|
|
230
|
-
preserveIndent = block.
|
|
244
|
+
preserveIndent = block.indent
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Don't apply indentation for single-line content when original was also single-line
|
|
248
|
+
if (!context.isMultiline && !block.match.includes('\n')) {
|
|
249
|
+
preserveIndent = 0
|
|
231
250
|
}
|
|
232
251
|
|
|
233
252
|
let addTrailingNewline = ''
|
|
@@ -245,9 +264,21 @@ async function blockTransformer(inputText, config) {
|
|
|
245
264
|
fixWrapper = '\n'
|
|
246
265
|
}
|
|
247
266
|
|
|
267
|
+
// console.log('updatedText', block.value)
|
|
268
|
+
|
|
248
269
|
const indent = addLeadingNewline + indentString(fix, preserveIndent) + addTrailingNewline
|
|
249
270
|
const newCont = `${openTag}${fixWrapper}${indent}${fixWrapper}${closeTag}`
|
|
250
|
-
|
|
271
|
+
|
|
272
|
+
// Use position-based replacement to handle duplicate block content
|
|
273
|
+
const adjustedStart = block.start + cumulativeOffset
|
|
274
|
+
const adjustedEnd = block.end + cumulativeOffset
|
|
275
|
+
const before = updatedText.substring(0, adjustedStart)
|
|
276
|
+
const after = updatedText.substring(adjustedEnd)
|
|
277
|
+
const newContents = before + newCont + after
|
|
278
|
+
|
|
279
|
+
// Update offset for next iteration
|
|
280
|
+
cumulativeOffset += (newCont.length - block.value.length)
|
|
281
|
+
|
|
251
282
|
return Promise.resolve(newContents)
|
|
252
283
|
}, Promise.resolve(inputText))
|
|
253
284
|
|
|
@@ -259,6 +290,9 @@ async function blockTransformer(inputText, config) {
|
|
|
259
290
|
|
|
260
291
|
const stripComments = isNewPath && removeComments
|
|
261
292
|
|
|
293
|
+
|
|
294
|
+
// console.log('inputText', inputText)
|
|
295
|
+
// console.log('updatedContents', updatedContents)
|
|
262
296
|
return {
|
|
263
297
|
isChanged: inputText !== updatedContents,
|
|
264
298
|
isNewPath,
|
|
@@ -300,7 +334,7 @@ function getDetails({
|
|
|
300
334
|
}
|
|
301
335
|
|
|
302
336
|
if (srcPath) {
|
|
303
|
-
const location = getCodeLocation(srcPath, foundBlock.
|
|
337
|
+
const location = getCodeLocation(srcPath, foundBlock.lines[0])
|
|
304
338
|
foundBlock.sourceLocation = location
|
|
305
339
|
}
|
|
306
340
|
return foundBlock
|
|
@@ -387,10 +421,30 @@ function trimString(str) {
|
|
|
387
421
|
return str.trim()
|
|
388
422
|
}
|
|
389
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Trim leading and trailing lines from a string
|
|
426
|
+
* @param {string} str - The string to trim
|
|
427
|
+
* @returns {string} The string with leading and trailing lines removed
|
|
428
|
+
*/
|
|
429
|
+
function trimLeadingAndTrailing(str) {
|
|
430
|
+
if (!str) return str
|
|
431
|
+
return str.replace(/^\s*\n+/, '').replace(/\n+\s*$/, '')
|
|
432
|
+
}
|
|
433
|
+
|
|
390
434
|
function getCodeLocation(srcPath, line, column = '0') {
|
|
391
435
|
return `${srcPath}:${line}:${column}`
|
|
392
436
|
}
|
|
393
437
|
|
|
438
|
+
if (require.main === module) {
|
|
439
|
+
const yaml = `
|
|
440
|
+
- name: Run tests two
|
|
441
|
+
run: npm test two
|
|
442
|
+
`
|
|
443
|
+
|
|
444
|
+
// console.log(indentString(yaml, 4))
|
|
445
|
+
}
|
|
446
|
+
|
|
394
447
|
module.exports = {
|
|
395
|
-
blockTransformer
|
|
448
|
+
blockTransformer,
|
|
449
|
+
indentString,
|
|
396
450
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const core = require('@actions/core')
|
|
2
|
+
const github = require('@actions/github')
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
const context = github.context
|
|
6
|
+
|
|
7
|
+
let issueNumber, prNumber, headRef, baseRef, headSha, isPR = false;
|
|
8
|
+
let triggerText = '';
|
|
9
|
+
|
|
10
|
+
if (context.eventName === 'pull_request_target') {
|
|
11
|
+
// When a PR is created or updated
|
|
12
|
+
isPR = true;
|
|
13
|
+
issueNumber = context.payload.pull_request.number;
|
|
14
|
+
prNumber = context.payload.pull_request.number;
|
|
15
|
+
headRef = context.payload.pull_request.head.ref;
|
|
16
|
+
baseRef = context.payload.pull_request.base.ref;
|
|
17
|
+
headSha = context.payload.pull_request.head.sha;
|
|
18
|
+
triggerText = context.payload.pull_request.body;
|
|
19
|
+
|
|
20
|
+
console.log(`PR #${prNumber}: ${baseRef} <- ${headRef} (${headSha})`);
|
|
21
|
+
|
|
22
|
+
} else if (context.eventName === 'issues') {
|
|
23
|
+
// When an Issue is created or assigned
|
|
24
|
+
isPR = false;
|
|
25
|
+
issueNumber = context.payload.issue.number;
|
|
26
|
+
triggerText = `${context.payload.issue.title} ${context.payload.issue.body}`;
|
|
27
|
+
|
|
28
|
+
console.log(`Issue #${issueNumber} created`);
|
|
29
|
+
|
|
30
|
+
} else if (context.eventName === 'issue_comment') {
|
|
31
|
+
// Issue/PR comment
|
|
32
|
+
issueNumber = context.payload.issue.number;
|
|
33
|
+
triggerText = context.payload.comment.body;
|
|
34
|
+
|
|
35
|
+
if (context.payload.issue.pull_request) {
|
|
36
|
+
// Comment on a PR
|
|
37
|
+
isPR = true;
|
|
38
|
+
try {
|
|
39
|
+
const pr = await github.rest.pulls.get({
|
|
40
|
+
owner: context.repo.owner,
|
|
41
|
+
repo: context.repo.repo,
|
|
42
|
+
pull_number: issueNumber
|
|
43
|
+
});
|
|
44
|
+
prNumber = issueNumber;
|
|
45
|
+
headRef = pr.data.head.ref;
|
|
46
|
+
baseRef = pr.data.base.ref;
|
|
47
|
+
headSha = pr.data.head.sha;
|
|
48
|
+
|
|
49
|
+
console.log(`PR Comment #${prNumber}: ${baseRef} <- ${headRef}`);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Error fetching PR info:', error);
|
|
52
|
+
// In case of error, treat as a regular Issue
|
|
53
|
+
isPR = false;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
// Regular Issue comment
|
|
57
|
+
isPR = false;
|
|
58
|
+
console.log(`Issue Comment #${issueNumber}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
} else if (context.eventName === 'pull_request_review_comment' || context.eventName === 'pull_request_review') {
|
|
62
|
+
// PR review related
|
|
63
|
+
isPR = true;
|
|
64
|
+
issueNumber = context.payload.pull_request.number;
|
|
65
|
+
prNumber = context.payload.pull_request.number;
|
|
66
|
+
headRef = context.payload.pull_request.head.ref;
|
|
67
|
+
baseRef = context.payload.pull_request.base.ref;
|
|
68
|
+
headSha = context.payload.pull_request.head.sha;
|
|
69
|
+
|
|
70
|
+
if (context.eventName === 'pull_request_review_comment') {
|
|
71
|
+
triggerText = context.payload.comment.body;
|
|
72
|
+
} else {
|
|
73
|
+
triggerText = context.payload.review.body;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`PR Review #${prNumber}: ${baseRef} <- ${headRef}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Set outputs
|
|
80
|
+
core.setOutput('issue-number', issueNumber);
|
|
81
|
+
core.setOutput('pr-number', prNumber || '');
|
|
82
|
+
core.setOutput('head-ref', headRef || '');
|
|
83
|
+
core.setOutput('base-ref', baseRef || '');
|
|
84
|
+
core.setOutput('head-sha', headSha || '');
|
|
85
|
+
core.setOutput('is-pr', isPR);
|
|
86
|
+
core.setOutput('trigger-text', triggerText);
|
|
87
|
+
|
|
88
|
+
console.log(`Final Context: Issue #${issueNumber}, isPR: ${isPR}, Event: ${context.eventName}`);
|
|
89
|
+
|
|
90
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const { blockTransformer, indentString } = require('../../src')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const util = require('util')
|
|
5
|
+
|
|
6
|
+
function logValue(value, isFirst, isLast) {
|
|
7
|
+
const prefix = `${isFirst ? '> ' : ''}`
|
|
8
|
+
if (typeof value === 'object') {
|
|
9
|
+
console.log(`${util.inspect(value, false, null, true)}\n`)
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
if (isFirst) {
|
|
13
|
+
console.log(`\n\x1b[33m${prefix}${value}\x1b[0m`)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
console.log((typeof value === 'string' && value.includes('\n')) ? `\`${value}\`` : value)
|
|
17
|
+
// isLast && console.log(`\x1b[37m\x1b[1m${'─'.repeat(94)}\x1b[0m\n`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function deepLog() {
|
|
21
|
+
for (let i = 0; i < arguments.length; i++) logValue(arguments[i], i === 0, i === arguments.length - 1)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const yaml = `
|
|
25
|
+
name: Test Workflow
|
|
26
|
+
description: A simple test workflow for testing purposes
|
|
27
|
+
version: 1.0.0
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
push:
|
|
31
|
+
branches: [main, develop]
|
|
32
|
+
pull_request:
|
|
33
|
+
branches: [main]
|
|
34
|
+
|
|
35
|
+
jobs:
|
|
36
|
+
test:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout code
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: npm test
|
|
44
|
+
|
|
45
|
+
## include file src="_test.yml" ##
|
|
46
|
+
- name: Run tests two
|
|
47
|
+
run: npm test two
|
|
48
|
+
## /include ##
|
|
49
|
+
|
|
50
|
+
## include js src="_run.js" ##
|
|
51
|
+
- name: Run tests two
|
|
52
|
+
run: npm test two
|
|
53
|
+
## /include ##
|
|
54
|
+
|
|
55
|
+
build:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
needs: test
|
|
58
|
+
steps:
|
|
59
|
+
- name: Build project
|
|
60
|
+
run: npm run build
|
|
61
|
+
`
|
|
62
|
+
|
|
63
|
+
blockTransformer(yaml, {
|
|
64
|
+
syntax: 'yaml',
|
|
65
|
+
open: 'include',
|
|
66
|
+
close: '/include',
|
|
67
|
+
transforms: {
|
|
68
|
+
file: ({ content, options }) => {
|
|
69
|
+
const { src } = options
|
|
70
|
+
const blockContent = fs.readFileSync(path.join(__dirname, src), 'utf8')
|
|
71
|
+
return blockContent
|
|
72
|
+
},
|
|
73
|
+
js: (api) => {
|
|
74
|
+
console.log('api', api)
|
|
75
|
+
const { content, options } = api
|
|
76
|
+
console.log('XYZ', content)
|
|
77
|
+
const jsContent = fs.readFileSync(path.join(__dirname, options.src), 'utf8')
|
|
78
|
+
return `
|
|
79
|
+
- name: Run tests two
|
|
80
|
+
run: |
|
|
81
|
+
${indentString(jsContent, 4)}
|
|
82
|
+
`
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}).then(deepLog)
|