Files
pnpm/text/comments-parser/src/insertComments.ts

84 lines
3.1 KiB
TypeScript

import { type CommentSpecifier } from './CommentSpecifier'
export function insertComments (json: string, comments: CommentSpecifier[]): string {
// We need to reintroduce the comments. So create an index of
// the lines of the manifest so we can try to match them up.
// We eliminate whitespace and quotes in the index entries,
// because pnpm may have changed them.
const jsonLines = json.split('\n')
const index: Record<string, number> = {}
const canonicalizer = /[\s'"]/g
for (let i = 0; i < jsonLines.length; ++i) {
const key = jsonLines[i].replace(canonicalizer, '')
if (key in index) {
index[key] = -1 // Mark this line as occurring twice
} else {
index[key] = i
}
}
// A place to put comments that come _before_ the lines they are
// anchored to:
const jsonPrefix: Record<string, string> = {}
for (const comment of comments) {
// First if we can find the line the comment was on, that is
// the most reliable locator:
let key = comment.on.replace(canonicalizer, '')
if (key && index[key] !== undefined && index[key] >= 0) {
jsonLines[index[key]] += ' ' + comment.content
continue
}
// Next, if it's not before anything, it must have been at the very end:
if (comment.before === undefined) {
jsonLines[jsonLines.length - 1] += comment.whitespace + comment.content
continue
}
// Next, try to put it before something; note the comment extractor
// used the convention that position 0 is before the first line:
let location = (comment.lineNumber === 0) ? 0 : -1
if (location < 0) {
key = comment.before.replace(canonicalizer, '')
if (key && index[key] !== undefined) {
location = index[key]
}
}
if (location >= 0) {
if (jsonPrefix[location]) {
jsonPrefix[location] += ' ' + comment.content
} else {
const inlineWhitespace = comment.whitespace.startsWith('\n')
? comment.whitespace.slice(1)
: comment.whitespace
jsonPrefix[location] = inlineWhitespace + comment.content
}
continue
}
// The last definite indicator we can use is that it is after something:
if (comment.after) {
key = comment.after.replace(canonicalizer, '')
if (key && index[key] !== undefined && index[key] >= 0) {
jsonLines[index[key]] += comment.whitespace + comment.content
continue
}
}
// Finally, try to get it in the right general location by using the
// line number, but warn the user the comment may have been relocated:
location = comment.lineNumber - 1 // 0 was handled above
let separator = ' '
if (location >= jsonLines.length) {
location = jsonLines.length - 1
separator = '\n'
}
jsonLines[location] += separator + comment.content +
' /* [comment possibly relocated by pnpm] */'
}
// Insert the accumulated prefixes:
for (let i = 0; i < jsonLines.length; ++i) {
if (jsonPrefix[i]) {
jsonLines[i] = jsonPrefix[i] + '\n' + jsonLines[i]
}
}
// And reassemble the manifest:
return jsonLines.join('\n')
}