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 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.1.1",
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.7"
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
- "scripts": {
18
- "test": "uvu test",
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 { COMMENT_OPEN_REGEX, COMMENT_CLOSE_REGEX } = foundBlocks
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: COMMENT_OPEN_REGEX,
140
- close: COMMENT_CLOSE_REGEX,
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
- const formattedNewContent = (options.noTrim) ? newContent : trimString(newContent)
224
- const fix = removeConflictingComments(formattedNewContent, COMMENT_OPEN_REGEX, COMMENT_CLOSE_REGEX)
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
- if (match.content.indentation) {
228
- preserveIndent = match.content.indentation.length
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.indentation.length
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
- const newContents = updatedText.replace(block.value, () => newCont)
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.block.lines[0])
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,2 @@
1
+ - name: New thing
2
+ run: npm test four
@@ -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)