\n${trimmed}\n
\n`; 58 | }; 59 | 60 | // const ulList = (_: string, __: string, item = '') => 61 | // `${item.trim()}`; 100 | 101 | // Process footnote references in the text [^1] 102 | const footnoteReferenceReplacer = (_match: string, id: string) => { 103 | // Create a link inside a superscript tag with proper references 104 | return `[${id}]`; 105 | }; 106 | 107 | // Process footnote definitions [^1]: Footnote text 108 | const footnoteDefinitionReplacer = ( 109 | _match: string, 110 | id: string, 111 | text: string, 112 | ) => { 113 | footnotes.push([id, text.trim()]); 114 | return ''; // Remove the definition from the main text 115 | }; 116 | 117 | // Generate the footnotes section 118 | const generateFootnotesSection = () => { 119 | if (footnotes.length === 0) return ''; 120 | 121 | const footnotesHtml = footnotes 122 | .map( 123 | ([id, text]) => ` 124 |
{{CODEBLOCKPH${codeBlocks.length - 1}}}\n`; 194 | }, 195 | ); 196 | }; 197 | 198 | // Function to extract and store inline code 199 | const extractInlineCode = (markdown: string): string => { 200 | return markdown.replace(/`([^`]+)`/g, (_match, code) => { 201 | inlineCode.push(code); 202 | return `{{INLINECODEPH${inlineCode.length - 1}}}`; 203 | }); 204 | }; 205 | 206 | // Function to restore code blocks with proper HTML escaping 207 | const restoreCodeBlocks = (markdown: string): string => { 208 | return markdown.replace( 209 | /
{{CODEBLOCKPH(\d+)}}<\/pre>/g, 210 | (_match, index) => { 211 | const code = codeBlocks[parseInt(index)]; 212 | return `${esc(code)}`; 213 | }, 214 | ); 215 | }; 216 | 217 | // Function to restore inline code with proper HTML escaping 218 | const restoreInlineCode = (markdown: string): string => { 219 | return markdown.replace(/{{INLINECODEPH(\d+)}}/g, (_match, index) => { 220 | const code = inlineCode[parseInt(index)]; 221 | return `${esc(code)}
`; 222 | }); 223 | }; 224 | 225 | /** Rules consist of tuples: RegExp, replacer function, repeat */ 226 | const rules = [ 227 | [/\r\n/g, '\n'], // Remove \r 228 | [/\n(#+)(.*)/g, header], // headers 229 | [/!\[([^\[]+)\]\((?:javascript:)?([^\)]+)\)/g, ''], // images, invoked before links 230 | [/\[([^\[]+)\]\((?:javascript:)?([^\)]+)\)/g, '$1'], // links 231 | [/([^\\])(\*\*|__)(.*?(_|\*)?)\2/g, '$1$3'], // bold 232 | [/([^\\])(\*|_)(.*?)\2/g, '$1$3'], // emphasis 233 | [/\\_/g, '_'], // underscores part 1 234 | [/\~\~(.*?)\~\~/g, '
$1'], // del 235 | [/\:\"(.*?)\"\:/g, '$1'], // quote 236 | // [/\n\s*```\n([^]*?)\n\s*```\s*\n/g, '\n$1'], // codeblock 237 | // [/`(.*?)`/g, (_: string, code: string) => `${esc(code)}
`], // inline code 238 | [/\n( *)(\*|-|\+)(.*)/g, ulList], // ul lists using +, - or * to denote an entry 239 | [/\n( *)([0-9]+\.)(.*)/g, olList], // ul lists 240 | // [/\n[0-9]+\.(.*)/g, olList], // ol lists 241 | [/\n(>|\>)(.*)/g, blockquote], // blockquotes 242 | [/(\^)(.*?)\1/g, '$2'], // superscript 243 | [/(\~)(.*?)\1/g, '$2'], // subscript 244 | [/\n-{5,}/g, '\n
'], // horizontal rule 245 | [ 246 | /( *\|[^\n]+\|\r?\n)((?: *\|:?[ -]+:?)+ *\|)(\n(?: *\|[^\n]+\|\r?\n?)*)?/g, 247 | table, 248 | ], 249 | [/\[\^([^\]]+)\](?!:)/g, footnoteReferenceReplacer], // footnote references 250 | [/\[\^([^\]]+)\]:\s*((?:[^\n]*\n?)*)/g, footnoteDefinitionReplacer], // footnote definitions 251 | [/\n([^\n]+)\n/g, para], // add paragraphs 252 | [/\s?<\/[ou]l>\s?<[ou]l>/g, '', 3], // fix extra ol and ul 253 | [/<\/blockquote>\n/g, '
\n'], // fix extra blockquote 254 | [/https?:\/\/[^"']*/g, cleanUpUrl], // fix em in links 255 | [/_/g, '_'], // underscores part 2 256 | ] as Array<[RegExp, RegexReplacer | string]>; 257 | 258 | /** 259 | * Render Markdown text into HTML. 260 | * 261 | * @param markdown Markdown text 262 | * @param removeParagraphs If true (default false), remove the \...\
around paragraphs 263 | * @param externalLinks If true (default false), replace \ with \ 264 | * to open them in a new page 265 | * @returns 266 | */ 267 | export const render = ( 268 | markdown: string, 269 | removeParagraphs = false, 270 | externalLinks = false, 271 | ) => { 272 | // Reset the storage arrays 273 | codeBlocks.length = 0; 274 | inlineCode.length = 0; 275 | footnotes.length = 0; 276 | 277 | // Extract code blocks and inline code before processing 278 | markdown = extractCodeBlocks(`\n${markdown}\n`); 279 | markdown = extractInlineCode(markdown); 280 | 281 | // Apply markdown rules 282 | rules.forEach(([regex, subst, repeat = 1]) => { 283 | for (let i = 0; i < repeat; i++) { 284 | markdown = markdown.replace(regex, subst as any); 285 | } 286 | }); 287 | 288 | // Restore code blocks and inline code with proper escaping 289 | markdown = restoreCodeBlocks(markdown); 290 | markdown = restoreInlineCode(markdown); 291 | 292 | // Add footnotes section if there are any footnotes 293 | markdown = markdown.trim() + generateFootnotesSection(); 294 | 295 | if (removeParagraphs) { 296 | markdown = markdown.replace(/^(.*)<\/p>$/s, '$1'); 297 | } 298 | if (externalLinks) { 299 | markdown = markdown.replace(/[1]. With some additional text after it. 51 |
52 |53 |`; 62 | const html = render( 63 | `Here is a simple footnote[^1]. With some additional text after it. 64 | 65 | [^1]: My reference.`, 66 | ); 67 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 68 | }); 69 | 70 | test('code', (t) => { 71 | const expected = 72 | '
54 |55 | 56 |
61 |- 57 | My reference. 58 | ↩ 59 |
60 |This is
'; 73 | const html = render('This is `italics` and this is `_italics_` too.'); 74 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 75 | }); 76 | 77 | test('multiline codeblock', (t) => { 78 | const expected = `italics
and this is_italics_
too.## Table example 79 | 80 | | Tables | Are | Cool | 81 | |---------------|:-------------:|------:| 82 | | col 3 is | right-aligned | $1600 | 83 | | col 2 is | centered | $12 | 84 | | zebra stripes | are neat | $1 |`; 85 | const html = render(` 86 | \`\`\`md 87 | 88 | ## Table example 89 | 90 | | Tables | Are | Cool | 91 | |---------------|:-------------:|------:| 92 | | col 3 is | right-aligned | $1600 | 93 | | col 2 is | centered | $12 | 94 | | zebra stripes | are neat | $1 | 95 | 96 | \`\`\` 97 | `); 98 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 99 | }); 100 | 101 | test('ul', (t) => { 102 | const expected = ''; 103 | const html = render(`- Item 1\n- Item 2\n- Item 3`); 104 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 105 | }); 106 | 107 | test('ul using +', (t) => { 108 | const expected = '
- Item 1
- Item 2
- Item 3
'; 109 | const html = render(`+ Item 1\n+ Item 2\n+ Item 3`); 110 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 111 | }); 112 | 113 | test('ul using *', (t) => { 114 | const expected = '
- Item 1
- Item 2
- Item 3
'; 115 | const html = render(`* Item 1\n* Item 2\n* Item 3`); 116 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 117 | }); 118 | 119 | test('nested ul + ul', (t) => { 120 | const expected = 121 | '
- Item 1
- Item 2
- Item 3
'; 122 | const html = render( 123 | `- Item 1\n - Item 1.1\n - Item 1.2\n - Item 1.3\n- Item 2\n- Item 3`, 124 | ); 125 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 126 | }); 127 | 128 | test('nested ul + ol', (t) => { 129 | const expected = 130 | '
- Item 1
- Item 1.1
- Item 1.2
- Item 1.3
- Item 2
- Item 3
'; 131 | const html = render( 132 | `- Item 1\n 1. Item 1.1\n 2. Item 1.2\n 3. Item 1.3\n- Item 2\n- Item 3`, 133 | ); 134 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 135 | }); 136 | 137 | test('ol', (t) => { 138 | const expected = '
- Item 1
- Item 1.1
- Item 1.2
- Item 1.3
- Item 2
- Item 3
'; 139 | const html = render(`1. Item 1\n2. Item 2\n3. Item 3`); 140 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 141 | }); 142 | 143 | test('nested ol + ol', (t) => { 144 | const expected = 145 | '
- Item 1
- Item 2
- Item 3
'; 146 | const html = render( 147 | `1. Item 1\n 1. Item 1.1\n 2. Item 1.2\n 3. Item 1.3\n2. Item 2\n3. Item 3`, 148 | ); 149 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 150 | }); 151 | 152 | test('nested ol + ul', (t) => { 153 | const expected = 154 | '
- Item 1
- Item 1.1
- Item 1.2
- Item 1.3
- Item 2
- Item 3
'; 155 | const html = render( 156 | `1. Item 1\n - Item 1.1\n - Item 1.2\n - Item 1.3\n2. Item 2\n3. Item 3`, 157 | ); 158 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 159 | }); 160 | 161 | test('table 1', (t) => { 162 | const table = ` 163 | | Threat \\ Context | rainy | sunny | 164 | |------------------|------------|------------| 165 | | terrorist | scenario 1 | scenario 2 | 166 | | criminal | scenario 3 | scenario 4 | 167 | `; 168 | const expected = `
- Item 1
- Item 1.1
- Item 1.2
- Item 1.3
- Item 2
- Item 3
169 | 170 |
`; 175 | const html = render(table); 176 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 177 | }); 178 | 179 | test('table 2', (t) => { 180 | const table = ` 181 | | Threat \\ Context | rainy | sunny | 182 | | ---------------- | ---------- | ---------- | 183 | | terrorist | scenario 1 | scenario 2 | 184 | | criminal | scenario 3 | scenario 4 | 185 | `; 186 | const expected = `171 | Threat \\ Context rainy sunny 172 | terrorist scenario 1 scenario 2 173 | 174 | criminal scenario 3 scenario 4 187 | 188 |
`; 193 | const html = render(table); 194 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 195 | }); 196 | 197 | test('parsing strong in own paragraph', (t) => { 198 | const md = `An **indie electronica music** bundle. 199 | 200 | **Featuring** songs by ... 201 | 202 | Pay what you want for Music`; 203 | const generated = render(md); 204 | const expected = `189 | Threat \\ Context rainy sunny 190 | terrorist scenario 1 scenario 2 191 | 192 | criminal scenario 3 scenario 4 205 | An indie electronica music bundle. 206 |
207 | 208 |209 | Featuring songs by ... 210 |
211 | 212 |213 | Pay what you want for Music 214 |
`; 215 | t.is(generated, expected); 216 | }); 217 | 218 | test('parsing longer text', (t) => { 219 | const md = `# Title 220 | 221 | To use **Slimdown**, grap it from [npm](https://www.npmjs.com/package/slimdown-js) or *fork* the project on [GitHub](https://github.com/erikvullings/slimdown-js). 222 | 223 | * One 224 | * Two 225 | * Three 226 | 227 | ## Underscores 228 | 229 | my\\_var\\_is 230 | 231 | ## Subhead 232 | 233 | One **two** three **four** five. 234 | 235 | One __two__ three _four_ five __six__ seven _eight_. 236 | 237 | 1. One 238 | 2. Two 239 | 3. Three 240 | 241 | More text with \`inline($code);\` sample. 242 | 243 | > A block quote 244 | > across two lines. 245 | 246 | More text...`; 247 | const expected = `Title
248 | 249 |250 | To use Slimdown, grap it from npm or fork the project on GitHub. 251 |
252 |253 |
257 | 258 |- One
254 |- Two
255 |- Three
256 |Underscores
259 | 260 |my_var_is
261 | 262 |Subhead
263 | 264 |265 | One two three four five. 266 |
267 | 268 |269 | One two three four five six seven eight. 270 |
271 |272 |
276 | 277 |- One
273 |- Two
274 |- Three
275 |278 | More text with
280 | 281 |inline($code);
sample. 279 |A block quote283 | 284 |
282 | across two lines.285 | More text... 286 |
`; 287 | const html = render(md); 288 | 289 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 290 | }); 291 | 292 | test('parsing links with underscores', (t) => { 293 | const md = `# Links fail with underscores 294 | 295 | [Test Link](http://www.google.com/?some_param=another_value)`; 296 | const expected = `Links fail with underscores
297 | `; 298 | const html = render(md); 299 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 300 | }); 301 | 302 | test('parsing images', (t) => { 303 | const md = `NS logo image: `; 304 | const expected = `NS logo image:
`; 305 | const html = render(md); 306 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 307 | }); 308 | 309 | test('parsing code blocks', (t) => { 310 | const md = `# Code example 311 | \`\`\` 312 | Tab indented 313 | codeblock 314 | \`\`\` 315 | `; 316 | const expected = `Code example
Tab indented codeblock`; 317 | const html = render(md); 318 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 319 | }); 320 | 321 | test('parsing inline code', (t) => { 322 | const md = `This is \`inline A & B\` code.`; 323 | const expected = `This isinline A & B
code.`; 324 | const html = render(md, true); 325 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 326 | }); 327 | 328 | test('parsing inline HTML code', (t) => { 329 | const md = `This is \`An HTML paragrahp
\` code.`; 330 | const expected = `This is<p>An HTML paragrahp</p>
code.`; 331 | const html = render(md, true); 332 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 333 | }); 334 | 335 | test('bypassing HTML code', (t) => { 336 | const md = `An HTML paragrahp`; 337 | const expected = `An HTML paragrahp`; 338 | const html = render(md, true); 339 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 340 | }); 341 | 342 | test('parsing tables', (t) => { 343 | const md = `# Table example 344 | 345 | | Tables | Are | Cool | 346 | |---------------|:-------------:|------:| 347 | | col 3 is | right-aligned | $1600 | 348 | | col 2 is | centered | $12 | 349 | | zebra stripes | are neat | $1 | 350 | `; 351 | const expected = `Table example
352 |353 | 354 |
`; 376 | const html = render(md); 377 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 378 | }); 379 | 380 | test('removing paragraphs', (t) => { 381 | const expected = 'Hello world'; 382 | const html = render('Hello world', true); 383 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 384 | }); 385 | 386 | test('do not remove paragraphs for longer text', (t) => { 387 | const expected = `355 | 359 |Tables 356 |Are 357 |Cool 358 |360 | 364 |col 3 is 361 |right-aligned 362 |$1600 363 |365 | 369 |col 2 is 366 |centered 367 |$12 368 |370 | 374 | 375 |zebra stripes 371 |are neat 372 |$1 373 |Hello world
How are you?
`; 388 | const html = render( 389 | `# Hello world 390 | 391 | How are you?`, 392 | true, 393 | ); 394 | t.is(removeWhitespaces(html), removeWhitespaces(expected)); 395 | }); 396 | 397 | test('creating links', (t) => { 398 | const md = 'This is a [link](https://www.google.com).'; 399 | const expected = 'This is a link.'; 400 | const html = render(md, true); 401 | t.is(html.trim(), expected); 402 | }); 403 | 404 | test('creating external links', (t) => { 405 | const md = 'This is a [link](https://www.google.com).'; 406 | const expected = 407 | 'This is a link.'; 408 | const html = render(md, true, true); 409 | t.is(html.trim(), expected); 410 | }); 411 | 412 | test('creating links with underscores', (t) => { 413 | const md = 'This is a [link](https://my_test_page.com).'; 414 | const expected = 'This is a link.'; 415 | const html = render(md, true); 416 | t.is(html.trim(), expected); 417 | }); 418 | 419 | test('creating emphasized text', (t) => { 420 | const md = 'This is _emphasized_ text.'; 421 | const expected = 'This is emphasized text.'; 422 | const html = render(md, true); 423 | t.is(html.trim(), expected); 424 | }); 425 | 426 | test('creating emphasized text 2', (t) => { 427 | const md = 'This is *emphasized* text.'; 428 | const expected = 'This is emphasized text.'; 429 | const html = render(md, true); 430 | t.is(html.trim(), expected); 431 | }); 432 | 433 | test('creating strong text', (t) => { 434 | const md = 'This is **strong** text.'; 435 | const expected = 'This is strong text.'; 436 | const html = render(md, true); 437 | t.is(html.trim(), expected); 438 | }); 439 | 440 | test('creating strong text 2', (t) => { 441 | const md = 'This is __strong__ text.'; 442 | const expected = 'This is strong text.'; 443 | const html = render(md, true); 444 | t.is(html.trim(), expected); 445 | }); 446 | 447 | test('creating strong and empasized text', (t) => { 448 | const md = 'This is ***strong and emphasized*** text.'; 449 | const expected = 450 | 'This is strong and emphasized text.'; 451 | const html = render(md, true); 452 | t.is(html.trim(), expected); 453 | }); 454 | 455 | test('creating strong and empasized text 2', (t) => { 456 | const md = 'This is ___strong and emphasized___ text.'; 457 | const expected = 458 | 'This is strong and emphasized text.'; 459 | const html = render(md, true); 460 | t.is(html.trim(), expected); 461 | }); 462 | 463 | test('creating deleted text', (t) => { 464 | const md = 'This is ~~deleted~~ text.'; 465 | const expected = 'This isdeletedtext.'; 466 | const html = render(md, true); 467 | t.is(html.trim(), expected); 468 | }); 469 | 470 | test('creating quotes', (t) => { 471 | const md = 'This is a quote: :"quoted": text.'; 472 | const expected = 'This is a quote:quotedtext.'; 473 | const html = render(md, true); 474 | t.is(html.trim(), expected); 475 | }); 476 | 477 | test('creating block quotes', (t) => { 478 | const md = '> This is a blockquoted text.'; 479 | const expected = 'This is a blockquoted text.'; 480 | const html = render(md, true); 481 | t.is(html.trim(), expected); 482 | }); 483 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */, 5 | "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 6 | // "lib": [ 7 | // "dom", 8 | // "es5", 9 | // "es2015.promise", 10 | // "es2017" 11 | // ] /* Specify library files to be included in the compilation. */, 12 | // "allowJs": true, /* Allow javascript files to be compiled. */ 13 | // "checkJs": true, /* Report errors in .js files. */ 14 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 15 | "declaration": true /* Generates corresponding '.d.ts' file. */, 16 | "skipLibCheck": true, 17 | "sourceMap": true /* Generates corresponding '.map' file. */, 18 | // "outFile": "./", /* Concatenate and emit output to single file. */ 19 | "outDir": "./dist" /* Redirect output structure to the directory. */, 20 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 21 | "removeComments": false /* Do not emit comments to output. */, 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | "importHelpers": true /* Import emit helpers from 'tslib'. */, 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | /* Strict Type-Checking Options */ 27 | "strict": true /* Enable all strict type-checking options. */, 28 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 29 | "strictNullChecks": true /* Enable strict null checks. */, 30 | "strictFunctionTypes": true /* Enable strict checking of function types. */, 31 | "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, 32 | "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, 33 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 34 | /* Additional Checks */ 35 | "noUnusedLocals": true /* Report errors on unused locals. */, 36 | "noUnusedParameters": true /* Report errors on unused parameters. */, 37 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, 38 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 39 | /* Module Resolution Options */ 40 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, 41 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 42 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 43 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 44 | // "typeRoots": [], /* List of folders to include type definitions from. */ 45 | // "types": [], /* Type declaration files to be included in compilation. */ 46 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, 47 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 48 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 49 | /* Source Map Options */ 50 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 51 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 54 | /* Experimental Options */ 55 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 56 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 57 | } 58 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": {}, 5 | "rules": { 6 | "no-console": false, 7 | "no-debugger": false, 8 | "quotemark": [true, "single"], 9 | "trailing-comma": [ 10 | true, 11 | { 12 | "multiline": { 13 | "objects": "always", 14 | "arrays": "always", 15 | "functions": "never", 16 | "typeLiterals": "ignore" 17 | }, 18 | "esSpecCompliant": true 19 | } 20 | ], 21 | "object-literal-sort-keys": false, 22 | "ordered-imports": false, 23 | "arrow-parens": [false, "ban-single-arg-parens"] 24 | }, 25 | "rulesDirectory": [] 26 | } 27 | --------------------------------------------------------------------------------