323 lines
8.8 KiB
JavaScript
Raw Normal View History

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _commentParser = require("comment-parser");
/**
* Transform based on https://github.com/syavorsky/comment-parser/blob/master/src/transforms/align.ts
*
* It contains some customizations to align based on the tags, and some custom options.
*/
/**
* @typedef {{
* hasNoTypes: boolean,
* maxNamedTagLength: import('./iterateJsdoc.js').Integer,
* maxUnnamedTagLength: import('./iterateJsdoc.js').Integer
* }} TypelessInfo
*/
const {
rewireSource
} = _commentParser.util;
/**
* @typedef {{
* name: import('./iterateJsdoc.js').Integer,
* start: import('./iterateJsdoc.js').Integer,
* tag: import('./iterateJsdoc.js').Integer,
* type: import('./iterateJsdoc.js').Integer
* }} Width
*/
/** @type {Width} */
const zeroWidth = {
name: 0,
start: 0,
tag: 0,
type: 0
};
/**
* @param {string[]} tags
* @param {import('./iterateJsdoc.js').Integer} index
* @param {import('comment-parser').Line[]} source
* @returns {boolean}
*/
const shouldAlign = (tags, index, source) => {
const tag = source[index].tokens.tag.replace('@', '');
const includesTag = tags.includes(tag);
if (includesTag) {
return true;
}
if (tag !== '') {
return false;
}
for (let iterator = index; iterator >= 0; iterator--) {
const previousTag = source[iterator].tokens.tag.replace('@', '');
if (previousTag !== '') {
if (tags.includes(previousTag)) {
return true;
}
return false;
}
}
return true;
};
/**
* @param {string[]} tags
* @returns {(
* width: Width,
* line: {
* tokens: import('comment-parser').Tokens
* },
* index: import('./iterateJsdoc.js').Integer,
* source: import('comment-parser').Line[]
* ) => Width}
*/
const getWidth = tags => {
return (width, {
tokens
}, index, source) => {
if (!shouldAlign(tags, index, source)) {
return width;
}
return {
name: Math.max(width.name, tokens.name.length),
start: tokens.delimiter === '/**' ? tokens.start.length : width.start,
tag: Math.max(width.tag, tokens.tag.length),
type: Math.max(width.type, tokens.type.length)
};
};
};
/**
* @param {{
* description: string;
* tags: import('comment-parser').Spec[];
* problems: import('comment-parser').Problem[];
* }} fields
* @returns {TypelessInfo}
*/
const getTypelessInfo = fields => {
const hasNoTypes = fields.tags.every(({
type
}) => {
return !type;
});
const maxNamedTagLength = Math.max(...fields.tags.map(({
tag,
name
}) => {
return name.length === 0 ? -1 : tag.length;
}).filter(length => {
return length !== -1;
})) + 1;
const maxUnnamedTagLength = Math.max(...fields.tags.map(({
tag,
name
}) => {
return name.length === 0 ? tag.length : -1;
}).filter(length => {
return length !== -1;
})) + 1;
return {
hasNoTypes,
maxNamedTagLength,
maxUnnamedTagLength
};
};
/**
* @param {import('./iterateJsdoc.js').Integer} len
* @returns {string}
*/
const space = len => {
return ''.padStart(len, ' ');
};
/**
* @param {{
* customSpacings: import('../src/rules/checkLineAlignment.js').CustomSpacings,
* tags: string[],
* indent: string,
* preserveMainDescriptionPostDelimiter: boolean,
* wrapIndent: string,
* disableWrapIndent: boolean,
* }} cfg
* @returns {(
* block: import('comment-parser').Block
* ) => import('comment-parser').Block}
*/
const alignTransform = ({
customSpacings,
tags,
indent,
preserveMainDescriptionPostDelimiter,
wrapIndent,
disableWrapIndent
}) => {
let intoTags = false;
/** @type {Width} */
let width;
/**
* @param {import('comment-parser').Tokens} tokens
* @param {TypelessInfo} typelessInfo
* @returns {import('comment-parser').Tokens}
*/
const alignTokens = (tokens, typelessInfo) => {
const nothingAfter = {
delim: false,
name: false,
tag: false,
type: false
};
if (tokens.description === '') {
nothingAfter.name = true;
tokens.postName = '';
if (tokens.name === '') {
nothingAfter.type = true;
tokens.postType = '';
if (tokens.type === '') {
nothingAfter.tag = true;
tokens.postTag = '';
/* c8 ignore next: Never happens because the !intoTags return. But it's here for consistency with the original align transform */
if (tokens.tag === '') {
nothingAfter.delim = true;
}
}
}
}
let untypedNameAdjustment = 0;
let untypedTypeAdjustment = 0;
if (typelessInfo.hasNoTypes) {
nothingAfter.tag = true;
tokens.postTag = '';
if (tokens.name === '') {
untypedNameAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
} else {
untypedNameAdjustment = typelessInfo.maxNamedTagLength > typelessInfo.maxUnnamedTagLength ? 0 : Math.max(0, typelessInfo.maxUnnamedTagLength - (tokens.tag.length + tokens.name.length + 1));
untypedTypeAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length;
}
}
// Todo: Avoid fixing alignment of blocks with multiline wrapping of type
if (tokens.tag === '' && tokens.type) {
return tokens;
}
const spacings = {
postDelimiter: (customSpacings === null || customSpacings === void 0 ? void 0 : customSpacings.postDelimiter) || 1,
postName: (customSpacings === null || customSpacings === void 0 ? void 0 : customSpacings.postName) || 1,
postTag: (customSpacings === null || customSpacings === void 0 ? void 0 : customSpacings.postTag) || 1,
postType: (customSpacings === null || customSpacings === void 0 ? void 0 : customSpacings.postType) || 1
};
tokens.postDelimiter = nothingAfter.delim ? '' : space(spacings.postDelimiter);
if (!nothingAfter.tag) {
tokens.postTag = space(width.tag - tokens.tag.length + spacings.postTag);
}
if (!nothingAfter.type) {
tokens.postType = space(width.type - tokens.type.length + spacings.postType + untypedTypeAdjustment);
}
if (!nothingAfter.name) {
// If post name is empty for all lines (name width 0), don't add post name spacing.
tokens.postName = width.name === 0 ? '' : space(width.name - tokens.name.length + spacings.postName + untypedNameAdjustment);
}
return tokens;
};
/**
* @param {import('comment-parser').Line} line
* @param {import('./iterateJsdoc.js').Integer} index
* @param {import('comment-parser').Line[]} source
* @param {TypelessInfo} typelessInfo
* @param {string|false} indentTag
* @returns {import('comment-parser').Line}
*/
const update = (line, index, source, typelessInfo, indentTag) => {
/** @type {import('comment-parser').Tokens} */
const tokens = {
...line.tokens
};
if (tokens.tag !== '') {
intoTags = true;
}
const isEmpty = tokens.tag === '' && tokens.name === '' && tokens.type === '' && tokens.description === '';
// dangling '*/'
if (tokens.end === '*/' && isEmpty) {
tokens.start = indent + ' ';
return {
...line,
tokens
};
}
switch (tokens.delimiter) {
case '/**':
tokens.start = indent;
break;
case '*':
tokens.start = indent + ' ';
break;
default:
tokens.delimiter = '';
// compensate delimiter
tokens.start = indent + ' ';
}
if (!intoTags) {
if (tokens.description === '') {
tokens.postDelimiter = '';
} else if (!preserveMainDescriptionPostDelimiter) {
tokens.postDelimiter = ' ';
}
return {
...line,
tokens
};
}
const postHyphenSpacing = (customSpacings === null || customSpacings === void 0 ? void 0 : customSpacings.postHyphen) ?? 1;
const hyphenSpacing = /^\s*-\s+/u;
tokens.description = tokens.description.replace(hyphenSpacing, '-' + ''.padStart(postHyphenSpacing, ' '));
// Not align.
if (shouldAlign(tags, index, source)) {
alignTokens(tokens, typelessInfo);
if (!disableWrapIndent && indentTag) {
tokens.postDelimiter += wrapIndent;
}
}
return {
...line,
tokens
};
};
return ({
source,
...fields
}) => {
width = source.reduce(getWidth(tags), {
...zeroWidth
});
const typelessInfo = getTypelessInfo(fields);
let tagIndentMode = false;
return rewireSource({
...fields,
source: source.map((line, index) => {
const indentTag = !disableWrapIndent && tagIndentMode && !line.tokens.tag && line.tokens.description;
const ret = update(line, index, source, typelessInfo, indentTag);
if (!disableWrapIndent && line.tokens.tag) {
tagIndentMode = true;
}
return ret;
})
});
};
};
var _default = exports.default = alignTransform;
module.exports = exports.default;
//# sourceMappingURL=alignTransform.cjs.map