UNPKG

@ryusei/light

Version:

<div align="center"> <a href="https://light.ryuseijs.com"> <img alt="RyuseiLight" src="https://light.ryuseijs.com/images/svg/logo.svg" width="70"> </a>

377 lines (303 loc) 8.61 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>TypeScript</title> <link href="../../../../dist/css/themes/ryuseilight-ryusei.min.css" rel="stylesheet"> </head> <body> <h3>Practical Example</h3> <pre class="ryuseilight"> import { LINE_BREAK } from &#039;../../constants/characters&#039;; import { Options, LanguageInfo, Token, Component } from &#039;../../types&#039;; import { EventBus } from &#039;../../event/EventBus&#039;; import { PROJECT_CODE_SHORT } from &#039;../../constants/project&#039;; import { BODY, CODE, CONTAINER, LINE, ROOT, TOKEN } from &#039;../../constants/classes&#039;; import { forOwn, escapeHtml } from &#039;../../utils&#039;; /** * Stores all Component functions. */ const Components: Record&lt;string, Component&gt; = {}; /** * The class for highlighting code via provided tokens. * * @since 0.0.1 */ export class Renderer { /** * Adds components. * * @param components - An object literal with Component functions. */ static compose( components: Record&lt;string, Component&gt; ): void { forOwn( components, ( Component, name ) =&gt; { Components[ name ] = Component; } ); } /** * Holds lines with tokens. */ readonly lines = []; /** * Holds the language info. */ readonly info: LanguageInfo; /** * Holds the root element if provided. */ readonly root: HTMLElement | undefined; /** * Holds options. */ readonly options: Options; /** * Holds the EventBus instance. */ readonly event: EventBus = new EventBus(); /** * The Renderer constructor. * * @param lines - Lines with tokens to render. * @param info - The language info object. * @param root - Optional. A root element to highlight. * @param options - Options. */ constructor( lines: Token[][], info: LanguageInfo, root?: HTMLElement, options: Options = {} ) { this.lines = lines; this.info = info; this.root = root; this.options = options; this.init(); } /** * Initializes the instance. */ protected init(): void { const { lines } = this; if ( lines.length ) { const tokens = lines[ lines.length - 1 ]; if ( ! tokens.length || ( tokens.length === 1 &amp;&amp; ! tokens[ 0 ][ 1 ].trim() ) ) { // Removes the last empty line. lines.pop(); } } forOwn( Components, Component =&gt; { Component( this ); } ); this.event.emit( &#039;mounted&#039; ); } /** * Renders lines as HTML. * * @param append - A function to add fragments to the HTML string. * * @return A rendered HTML string. */ protected renderLines( append: ( fragment: string ) =&gt; void ): void { const event = this.event; const tag = this.options.span ? &#039;span&#039; : &#039;code&#039;; for ( let i = 0; i &lt; this.lines.length; i++ ) { const tokens = this.lines[ i ]; const classes = [ LINE ]; event.emit( &#039;line:open&#039;, append, classes, i ); append( `&lt;div class=&quot;${ classes.join( &#039; &#039; ) }&quot;&gt;` ); if ( tokens.length ) { for ( let j = 0; j &lt; tokens.length; j++ ) { const token = tokens[ j ]; const classes = [ `${ TOKEN } ${ PROJECT_CODE_SHORT }__${ token[ 0 ] }` ]; event.emit( &#039;token&#039;, token, classes ); append( `&lt;${ tag } class=&quot;${ classes.join( &#039; &#039; ) }&quot;&gt;${ escapeHtml( token[ 1 ] ) }&lt;/${ tag }&gt;` ); } } else { append( LINE_BREAK ); } append( &#039;&lt;/div&gt;&#039; ); event.emit( &#039;line:closed&#039;, append, i ); } } /** * Returns all lines and wrapper elements. * * @param pre - Whether to wrap elements by `pre` or not. * * @return An HTML string. */ html( pre: boolean ): string { const event = this.event; let html = &#039;&#039;; const append = ( fragment: string ) =&gt; { html += fragment }; if ( pre ) { html += `&lt;pre class=&quot;${ ROOT } ${ ROOT }--${ this.info.id }&quot;&gt;`; } const containerClasses = [ CONTAINER ]; event.emit( &#039;open&#039;, append, containerClasses ); html += `&lt;div class=&quot;${ containerClasses.join( &#039; &#039; ) }&quot;&gt;`; event.emit( &#039;opened&#039;, append ); const bodyClasses = [ `${ BODY }${ this.options.wrap ? ` ${ BODY }--wrap` : &#039;&#039; }` ]; event.emit( &#039;body:open&#039;, append, bodyClasses ); html += `&lt;div class=&quot;${ bodyClasses.join( &#039; &#039; ) }&quot;&gt;`; event.emit( &#039;body:opened&#039;, append ); html += `&lt;div class=&quot;${ CODE }&quot;&gt;`; this.renderLines( append ); html += `&lt;/div&gt;`; // code event.emit( &#039;body:close&#039;, append ); html += `&lt;/div&gt;`; // body event.emit( &#039;close&#039;, append ); html += `&lt;/div&gt;`; // container event.emit( &#039;closed&#039;, append ); if ( pre ) { html += `&lt;/pre&gt;`; } return html; } /** * Destroys the instance. */ destroy(): void { this.event.emit( &#039;destroy&#039; ); this.event.destroy(); } } </pre> <h3>Comments/Strings</h3> <pre> /** * Multiline comment * 'Should not be a string' * "Should not be a string" */ /* Multiline comment in a single line */ // Single line comment // 'Should not be a string' // "Should not be a string" 'Single quote' 'Single \'quote\' with escape' 'Single 'quote' with single quote' 'Single "quote" with double quote' 'Single `quote` with back quote' "Double quote" "Double \"quote\" with escape" "Double "quote" with double quote" "Double 'quote' with single quote" "Double `quote` with back quote" `Back quote` 'Back \`quote\` with escape' 'Back `quote` with back quote' 'Back 'quote' with single quote' 'Back "quote" with double quote' '/* Should not be a comment */' '// Should not be a comment' "/* Should not be a comment */" "// Should not be a comment" `/* Should not be a comment */` `// Should not be a comment` </pre> <h3>RegExp</h3> <pre> /^.*?[\n\s]/gmsi </pre> <h3>Template Literal</h3> <pre> `Multiline template literal` `The result will be ${ ( a + b ) * 3 }` // Nested template literal `container ${ isMobile() // ${ comment } // ` ? 'is-mobile' : `container--${ page.isFront() ? 'front' : 'page' }` }`; </pre> <h3>Functions/Generics</h3> <pre> // Function function apply&lt;T extends object>( value: T ) {} // Anonymous function const a = function &lt;T extends object>( value: T ) {} // Arrow function &lt;T extends object> ( value: T ) => {} // Method { apply&lt;T extends object>( value: T ) {} } type Assign&lt;T, U> = Omit&lt;T, keyof U> & U; export function assign&lt;T extends object, U extends object[]>( object: T, ...sources: U ): Assign&lt;T, U> { const keys: Array&lt;string | symbol> = getKeys( source ); if ( a < 1 && b > d ) { console.log( a ); } for ( let i = 0; i < keys.length; i++ ) { } } return object; } </pre> <h3>Typing</h3> <pre> declare var process: any; type Token = [ string, number, ...RegExp[] ]; interface CustomDivElement extends HTMLDivElement { selectionStart: number, selectionEnd: number, setSelection( number, number ): void; } namespace Lexer { export interface Grammar { main: Tokenizers[]; [ key: string ]: Tokenizers[]; } } function isArray&lt;T>( subject: T[] ): subject is T[] { return Array.isArray( subject ); } </pre> <h3>Keywords</h3> <pre> declare, keyof, namespace, readonly, type, string, number, boolean, bigint, symbol, any, never, unknown </pre> <h3>Classes</h3> <pre> Object.keys( object ); class Component { constructor() { } } const component = new Component(); </pre> <h3>Booleans</h3> <pre> true, false </pre> <h3>Numbers</h3> <pre> 0 1 1.23 .23 +1.23 -1.23 1e10 1e+10 1e-10 1E10 1E+10 1E-10 1.2e10 1.2e+10 1.2e-10 1.2E10 1.2E+10 1.2E-10 </pre> <h3>Operators</h3> <pre> + - ~ ! / * % ** < > <= >= == != === !== << >> >>> & | ^ && || ?? ? = *= **= /= %= += -= <<= >>= >>>= &= ^= |= &&= ||= ??= : </pre> <h3>Brackets</h3> <pre> {} () [] </pre> <h3>Delimiters</h3> <pre> ; . , ;;;; .... ,,,, </pre> <script src="../../../../dist/js/ryuseilight.min.js"></script> <script src="../../../../dist/js/languages/typescript.min.js"></script> <script> const ryuseilight = new RyuseiLight(); ryuseilight.apply( 'pre', { language: 'typescript' } ); </script> </body> </html>