@remotex-labs/xmap
Version:
A library with a sourcemap parser and TypeScript code formatter for the CLI
428 lines (426 loc) • 16.8 kB
JavaScript
"use strict";
var S = Object.create;
var l = Object.defineProperty;
var u = Object.getOwnPropertyDescriptor;
var y = Object.getOwnPropertyNames;
var x = Object.getPrototypeOf, C = Object.prototype.hasOwnProperty;
var K = (i, e) => {
for (var t in e)
l(i, t, { get: e[t], enumerable: !0 });
}, m = (i, e, t, n) => {
if (e && typeof e == "object" || typeof e == "function")
for (let o of y(e))
!C.call(i, o) && o !== t && l(i, o, { get: () => e[o], enumerable: !(n = u(e, o)) || n.enumerable });
return i;
};
var f = (i, e, t) => (t = i != null ? S(x(i)) : {}, m(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
e || !i || !i.__esModule ? l(t, "default", { value: i, enumerable: !0 }) : t,
i
)), v = (i) => m(l({}, "__esModule", { value: !0 }), i);
// src/components/highlighter.component.ts
var T = {};
K(T, {
CodeHighlighter: () => d,
highlightCode: () => w
});
module.exports = v(T);
var r = f(require("typescript"), 1), h = require("typescript"), a = require("@remotex-labs/xansi/xterm.component"), E = {
enumColor: a.xterm.burntOrange,
typeColor: a.xterm.lightGoldenrodYellow,
classColor: a.xterm.lightOrange,
stringColor: a.xterm.oliveGreen,
keywordColor: a.xterm.lightCoral,
commentColor: a.xterm.darkGray,
functionColor: a.xterm.lightOrange,
variableColor: a.xterm.burntOrange,
interfaceColor: a.xterm.lightGoldenrodYellow,
parameterColor: a.xterm.deepOrange,
getAccessorColor: a.xterm.lightYellow,
numericLiteralColor: a.xterm.lightGray,
methodSignatureColor: a.xterm.burntOrange,
regularExpressionColor: a.xterm.oliveGreen,
propertyAssignmentColor: a.xterm.canaryYellow,
propertyAccessExpressionColor: a.xterm.lightYellow,
expressionWithTypeArgumentsColor: a.xterm.lightOrange
}, d = class {
/**
* Creates an instance of the CodeHighlighter class.
*
* @param sourceFile - The TypeScript AST node representing the source file
* @param code - The source code string to be highlighted
* @param schema - The color scheme used for highlighting different elements in the code
*
* @since 1.0.0
*/
constructor(e, t, n) {
this.sourceFile = e;
this.code = t;
this.schema = n;
}
/**
* A Map of segments where the key is a combination of start and end positions.
*
* @remarks
* This structure ensures unique segments and allows for fast lookups and updates.
*
* @see HighlightNodeSegmentInterface
* @since 1.0.0
*/
segments = /* @__PURE__ */ new Map();
/**
* Parses a TypeScript AST node and processes its comments to identify segments that need highlighting.
*
* @param node - The TypeScript AST node to be parsed
*
* @since 1.0.0
*/
parseNode(e) {
this.processComments(e), this.processKeywords(e), this.processNode(e);
}
/**
* Generates a string with highlighted code segments based on the provided color scheme.
*
* @returns The highlighted code as a string, with ANSI color codes applied to the segments
*
* @remarks
* This method processes the stored segments, applies the appropriate colors to each segment,
* and returns the resulting highlighted code as a single string.
*
* @since 1.0.0
*/
highlight() {
let e = 0, t, n = [];
return Array.from(
this.segments.values()
).sort((s, c) => s.start - c.start || s.end - c.end).forEach((s) => {
if (t && s.start < t.end) {
let c = n.pop();
if (!c) return;
let g = this.getSegmentSource(s.start, s.end), p = `${s.color(g)}`;
n.push(c.replace(g, p));
return;
}
n.push(this.getSegmentSource(e, s.start)), n.push(s.color(this.getSegmentSource(s.start, s.end))), e = s.end, t = s;
}), n.join("") + this.getSegmentSource(e);
}
/**
* Extracts a text segment from the source code using position indices.
*
* @param start - The starting index position in the source text
* @param end - The ending index position in the source text (optional)
* @returns The substring of a source text between the start and end positions
*
* @remarks
* This utility method provides access to specific portions of the source code
* based on character positions. When the end parameter is omitted, the extraction
* will continue to the end of the source text.
*
* This method is typically used during the highlighting process to access the
* actual text content that corresponds to syntax nodes or other text ranges
* before applying formatting.
*
* @example
* ```ts
* // Extract a variable name
* const variableName = this.getSegmentSource(10, 15);
*
* // Extract from a position to the end of a source
* const remaining = this.getSegmentSource(100);
* ```
*
* @see addSegment
* @see highlight
*
* @since 1.0.0
*/
getSegmentSource(e, t) {
return this.code.slice(e, t);
}
/**
* Registers a text segment for syntax highlighting with specified style information.
*
* @param start - The starting position of the segment in the source text
* @param end - The ending position of the segment in the source text
* @param color - The {@link ColorFunctionType} to apply to this segment
*
* @remarks
* This method creates a unique key for each segment based on its position and stores the segment information in a map.
* Each segment contains its position information, styling code,
* and reset code which will later be used during the highlighting process.
*
* If multiple segments are added with the same positions, the later additions will
* overwrite earlier ones due to the map's key-based storage.
*
* @example
* ```ts
* // Highlight a variable name in red
* this.addSegment(10, 15, xterm.red);
*
* // Highlight a keyword with custom color
* this.addSegment(20, 26, xterm.blue);
* ```
*
* @see Colors
* @see processNode
*
* @since 1.0.0
*/
addSegment(e, t, n) {
let o = `${e}-${t}`;
this.segments.set(o, { start: e, end: t, color: n });
}
/**
* Processes and highlights comments associated with a TypeScript AST node.
*
* @param node - The TypeScript AST node whose comments are to be processed
*
* @remarks
* This method identifies both leading and trailing comments associated with the given node
* and adds them to the highlighting segments.
* The comments are extracted from the full source text using TypeScript's utility functions
* and are highlighted using the color specified
* in the schema's commentColor property.
*
* Leading comments appear before the node, while trailing comments appear after it.
* Both types are processed with the same highlighting style.
*
* @example
* ```ts
* // For a node that might have comments like:
* // This is a leading comment
* const x = 5; // This is a trailing comment
*
* this.processComments(someNode);
* // Both comments will be added to segments with the comment color
* ```
*
* @see addSegment
* @see ts.getLeadingCommentRanges
* @see ts.getTrailingCommentRanges
*
* @since 1.0.0
*/
processComments(e) {
[
...r.default.getTrailingCommentRanges(this.sourceFile.getFullText(), e.getFullStart()) || [],
...r.default.getLeadingCommentRanges(this.sourceFile.getFullText(), e.getFullStart()) || []
].forEach((n) => this.addSegment(n.pos, n.end, this.schema.commentColor));
}
/**
* Processes TypeScript keywords and primitive type references in an AST node for syntax highlighting.
*
* @param node - The TypeScript AST node to be processed for keywords
*
* @remarks
* This method handles two categories of tokens that require special highlighting:
*
* 1. Primitive type references: Highlights references to built-in types like `null`,
* `void`, `string`, `number`, `boolean`, and `undefined` using the type color.
*
* 2. TypeScript keywords: Identifies any node whose kind falls within the TypeScript
* keyword range (between FirstKeyword and LastKeyword) and highlights it using
* the keyword color.
*
* Each identified token is added to the segment collection with the appropriate position
* and color information.
*
* @example
* ```ts
* // Inside syntax highlighting process
* this.processKeywords(someNode);
* // If the node represents a keyword like 'const' or a primitive type like 'string',
* // it will be added to the segments with the appropriate color
* ```
*
* @see addSegment
* @see ts.SyntaxKind
*
* @since 1.0.0
*/
processKeywords(e) {
if ([
h.SyntaxKind.NullKeyword,
h.SyntaxKind.VoidKeyword,
h.SyntaxKind.StringKeyword,
h.SyntaxKind.NumberKeyword,
h.SyntaxKind.BooleanKeyword,
h.SyntaxKind.UndefinedKeyword
].includes(e.kind))
return this.addSegment(e.getStart(), e.getEnd(), this.schema.typeColor);
e && e.kind >= r.default.SyntaxKind.FirstKeyword && e.kind <= r.default.SyntaxKind.LastKeyword && this.addSegment(e.getStart(), e.getEnd(), this.schema.keywordColor);
}
/**
* Processes identifier nodes and applies appropriate syntax highlighting based on their context.
*
* @param node - The TypeScript AST node representing the identifier to be processed
*
* @remarks
* This method determines the appropriate color for an identifier by examining its parent node's kind.
* Different colors are applied based on the identifier's role in the code:
* - Enum members use enumColor
* - Interface names use interfaceColor
* - Class names use classColor
* - Function and method names use functionColor
* - Parameters use parameterColor
* - Variables and properties use variableColor
* - Types use typeColor
* - And more specialized cases for other syntax kinds
*
* Special handling is applied to property access expressions to differentiate between
* the object being accessed and the property being accessed.
*
* @example
* ```ts
* // Inside the CodeHighlighter class
* const identifierNode = getIdentifierNode(); // Get some identifier node
* this.processIdentifier(identifierNode);
* // The identifier is now added to segments with appropriate color based on its context
* ```
*
* @see addSegment
* @see HighlightSchemeInterface
*
* @since 1.0.0
*/
processIdentifier(e) {
let t = e.getEnd(), n = e.getStart();
switch (e.parent.kind) {
case r.default.SyntaxKind.EnumMember:
return this.addSegment(n, t, this.schema.enumColor);
case r.default.SyntaxKind.CallExpression:
case r.default.SyntaxKind.EnumDeclaration:
case r.default.SyntaxKind.PropertySignature:
case r.default.SyntaxKind.ModuleDeclaration:
return this.addSegment(n, t, this.schema.variableColor);
case r.default.SyntaxKind.InterfaceDeclaration:
return this.addSegment(n, t, this.schema.interfaceColor);
case r.default.SyntaxKind.GetAccessor:
return this.addSegment(n, t, this.schema.getAccessorColor);
case r.default.SyntaxKind.PropertyAssignment:
return this.addSegment(n, t, this.schema.propertyAssignmentColor);
case r.default.SyntaxKind.MethodSignature:
return this.addSegment(n, t, this.schema.methodSignatureColor);
case r.default.SyntaxKind.MethodDeclaration:
case r.default.SyntaxKind.FunctionDeclaration:
return this.addSegment(n, t, this.schema.functionColor);
case r.default.SyntaxKind.ClassDeclaration:
return this.addSegment(n, t, this.schema.classColor);
case r.default.SyntaxKind.Parameter:
return this.addSegment(n, t, this.schema.parameterColor);
case r.default.SyntaxKind.VariableDeclaration:
return this.addSegment(n, t, this.schema.variableColor);
case r.default.SyntaxKind.PropertyDeclaration:
return this.addSegment(n, t, this.schema.variableColor);
case r.default.SyntaxKind.PropertyAccessExpression:
return e.parent.getChildAt(0).getText() === e.getText() ? this.addSegment(n, t, this.schema.variableColor) : this.addSegment(n, t, this.schema.propertyAccessExpressionColor);
case r.default.SyntaxKind.ExpressionWithTypeArguments:
return this.addSegment(n, t, this.schema.expressionWithTypeArgumentsColor);
case r.default.SyntaxKind.BreakStatement:
case r.default.SyntaxKind.ShorthandPropertyAssignment:
case r.default.SyntaxKind.BindingElement:
return this.addSegment(n, t, this.schema.variableColor);
case r.default.SyntaxKind.BinaryExpression:
case r.default.SyntaxKind.SwitchStatement:
case r.default.SyntaxKind.TemplateSpan:
return this.addSegment(n, t, this.schema.variableColor);
case r.default.SyntaxKind.TypeReference:
case r.default.SyntaxKind.TypeAliasDeclaration:
return this.addSegment(n, t, this.schema.typeColor);
case r.default.SyntaxKind.NewExpression:
return this.addSegment(n, t, this.schema.variableColor);
}
}
/**
* Processes a TypeScript template expression and adds highlighting segments for its literal parts.
*
* @param templateExpression - The TypeScript template expression to be processed
*
* @remarks
* This method adds color segments for both the template head and each template span's literal part.
* All template string components are highlighted using the color defined in the schema's stringColor.
*
* @example
* ```ts
* // Given a template expression like: `Hello ${name}`
* this.processTemplateExpression(templateNode);
* // Both "Hello " and the closing part after the expression will be highlighted
* ```
*
* @see addSegment
*
* @since 1.0.0
*/
processTemplateExpression(e) {
let t = e.head.getStart(), n = e.head.getEnd();
this.addSegment(t, n, this.schema.stringColor), e.templateSpans.forEach((o) => {
let s = o.literal.getStart(), c = o.literal.getEnd();
this.addSegment(s, c, this.schema.stringColor);
});
}
/**
* Processes a TypeScript AST node and adds highlighting segments based on the node's kind.
*
* @param node - The TypeScript AST node to be processed
*
* @remarks
* This method identifies the node's kind and applies the appropriate color for highlighting.
* It handles various syntax kinds, including literals (string, numeric, regular expressions),
* template expressions, identifiers, and type references.
* For complex node types like template expressions and identifiers, it delegates to specialized processing methods.
*
* @throws Error - When casting to TypeParameterDeclaration fails for non-compatible node kinds
*
* @example
* ```ts
* // Inside the CodeHighlighter class
* const node = sourceFile.getChildAt(0);
* this.processNode(node);
* // Node is now added to the segment map with appropriate colors
* ```
*
* @see processTemplateExpression
* @see processIdentifier
*
* @since 1.0.0
*/
processNode(e) {
let t = e.getStart(), n = e.getEnd();
switch (e.kind) {
case r.default.SyntaxKind.TypeParameter:
return this.addSegment(t, t + e.name.text.length, this.schema.typeColor);
case r.default.SyntaxKind.TypeReference:
return this.addSegment(t, n, this.schema.typeColor);
case r.default.SyntaxKind.StringLiteral:
case r.default.SyntaxKind.NoSubstitutionTemplateLiteral:
return this.addSegment(t, n, this.schema.stringColor);
case r.default.SyntaxKind.RegularExpressionLiteral:
return this.addSegment(t, n, this.schema.regularExpressionColor);
case r.default.SyntaxKind.TemplateExpression:
return this.processTemplateExpression(e);
case r.default.SyntaxKind.Identifier:
return this.processIdentifier(e);
case r.default.SyntaxKind.BigIntLiteral:
case r.default.SyntaxKind.NumericLiteral:
return this.addSegment(t, n, this.schema.numericLiteralColor);
}
}
};
function w(i, e = {}) {
let t = r.default.createSourceFile("temp.ts", i, r.default.ScriptTarget.Latest, !0, r.default.ScriptKind.TS), n = new d(t, i, Object.assign(E, e));
function o(s) {
n.parseNode(s);
for (let c = 0; c < s.getChildCount(); c++)
o(s.getChildAt(c));
}
return r.default.forEachChild(t, o), n.highlight();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CodeHighlighter,
highlightCode
});
//# sourceMappingURL=highlighter.component.js.map