Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/css/table-styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* Markdown Table Styles */
.markdown-table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
overflow: hidden;
}

.markdown-table th {
background: linear-gradient(135deg, #4a90e2 0%, #357abd 100%);
color: white;
font-weight: bold;
text-align: center;
padding: 12px 8px;
border: none;
}

.markdown-table td {
padding: 12px 8px;
text-align: center;
border: 1px solid #e0e0e0;
}

.markdown-table tr:nth-child(even) {
background-color: #f8f9fa;
}

.markdown-table tr:nth-child(odd) {
background-color: #ffffff;
}

.markdown-table tr:hover {
background-color: #e3f2fd;
transition: background-color 0.2s ease;
}

.markdown-table tbody tr:last-child td {
border-bottom: none;
}

/* Responsive Design */
@media (max-width: 768px) {
.markdown-table {
font-size: 12px;
}

.markdown-table th,
.markdown-table td {
padding: 8px 4px;
}
}

@media (max-width: 480px) {
.markdown-table {
font-size: 11px;
}

.markdown-table th,
.markdown-table td {
padding: 6px 2px;
}
}
20 changes: 2 additions & 18 deletions docs/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
<title>Marked Demo</title>
<link rel="stylesheet" href="./demo.css" type="text/css" />
<link rel="stylesheet" href="../css/shared.css" type="text/css" />
<link rel="stylesheet" href="../css/style.css" type="text/css" />
<link rel="stylesheet" href="../css/hljs-github-dark.css" type="text/css" />
<link rel="stylesheet" href="../css/table-styles.css" type="text/css" />
</head>

<body>
<a href="https://github.com/markedjs/marked" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#202020; color:#fff;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
Expand All @@ -28,20 +27,6 @@ <h1>Marked Demo</h1>
<br />
<a href="https://daringfireball.net/projects/markdown/dingus">Daring Fireball (pedantic) Demo</a>
</div>
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme">
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="4"></circle>
<path d="m12 2 0 2"></path>
<path d="m12 20 0 2"></path>
<path d="m4.93 4.93 1.41 1.41"></path>
<path d="m17.66 17.66 1.41 1.41"></path>
<path d="M2 12h2"></path>
<path d="M20 12h2"></path>
<path d="m6.34 17.66-1.41 1.41"></path>
<path d="m19.07 4.93-1.41 1.41"></path>
</svg>
<span class="text">Dark</span>
</button>
</header>

<div id="loading">Loading...</div>
Expand Down Expand Up @@ -92,7 +77,6 @@ <h2>You'll need to enable Javascript to use this tool.</h2>
</div>
</div>
</div>
<script src="../js/index.js"></script>
<script src="./demo.js" type="module"></script>
</body>

Expand Down
50 changes: 50 additions & 0 deletions docs/demo/worker.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
const tableExtension = {
renderer: {
table(token) {
let header = '';

// header
let cell = '';
for (let j = 0; j < token.header.length; j++) {
cell += this.tablecell(token.header[j]);
}
header += this.tablerow({ text: cell });

let body = '';
for (let j = 0; j < token.rows.length; j++) {
const row = token.rows[j];

cell = '';
for (let k = 0; k < row.length; k++) {
cell += this.tablecell(row[k]);
}

body += this.tablerow({ text: cell });
}
if (body) body = `<tbody>${body}</tbody>`;

return '<table class="markdown-table">\n'
+ '<thead>\n'
+ header
+ '</thead>\n'
+ body
+ '</table>\n';
},
tablerow({ text }) {
return `<tr class="markdown-table-row">\n${text}</tr>\n`;
},
tablecell(token) {
const content = this.parser.parseInline(token.tokens);
const type = token.header ? 'th' : 'td';
const tag = token.align
? `<${type} align="${token.align}">`
: `<${type}>`;
return tag + content + `</${type}>\n`;
},
},
};
Comment on lines +1 to +45

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The tableExtension object is duplicated here and in src/tableExtension.ts. This creates a maintenance burden, as changes would need to be applied in two places. It would be better to have a single source of truth. Consider compiling src/tableExtension.ts to JavaScript and importing it here to avoid code duplication.


const versionCache = {};
let currentVersion;

Expand Down Expand Up @@ -62,6 +108,10 @@ function parse(e) {
}
case 'parse': {
const marked = versionCache[currentVersion];
// Use table extension for local version
if (currentVersion === '../') {
marked.use(tableExtension);
}
// marked 0.0.1 had tokens array as the second parameter of lexer and no options
const options = currentVersion.endsWith('@0.0.1') ? [] : mergeOptions(e.data.options);
const startTime = new Date();
Expand Down
14 changes: 7 additions & 7 deletions src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ export const other = {
spaceLine: /^ +$/gm,
notSpaceStart: /^\S*/,
endingNewline: /\n$/,
listItemRegex: (bull: string) => new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`),
nextBulletRegex: (indent: number) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),
hrRegex: (indent: number) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),
fencesBeginRegex: (indent: number) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`),
headingBeginRegex: (indent: number) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`),
htmlBeginRegex: (indent: number) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}<(?:[a-z].*>|!--)`, 'i'),
listItemRegex: (bull: string) => new RegExp(`^( {0,4}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`),
nextBulletRegex: (indent: number) => /^ {0,4}(?:[*+-]|\d{1,9}[.)])((?:[ \t][^\n]*)?(?:\n|$))/,
hrRegex: (indent: number) => /^ {0,4}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
fencesBeginRegex: (indent: number) => /^ {0,4}(?:```|~~~)/,
headingBeginRegex: (indent: number) => /^ {0,4}#/,
htmlBeginRegex: (indent: number) => /^ {0,4}<(?:[a-z].*>|!--)/i,
Comment on lines +71 to +75

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The indent parameter is no longer used in nextBulletRegex, hrRegex, fencesBeginRegex, headingBeginRegex, and htmlBeginRegex after your changes. To improve code clarity and maintainability, you should mark it as unused by prefixing it with an underscore (_). This signals to other developers that the parameter is intentionally not used.

Suggested change
nextBulletRegex: (indent: number) => /^ {0,4}(?:[*+-]|\d{1,9}[.)])((?:[ \t][^\n]*)?(?:\n|$))/,
hrRegex: (indent: number) => /^ {0,4}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
fencesBeginRegex: (indent: number) => /^ {0,4}(?:```|~~~)/,
headingBeginRegex: (indent: number) => /^ {0,4}#/,
htmlBeginRegex: (indent: number) => /^ {0,4}<(?:[a-z].*>|!--)/i,
nextBulletRegex: (_indent: number) => /^ {0,4}(?:[*+-]|\d{1,9}[.)])((?:[ \t][^\n]*)?(?:\n|$))/,
hrRegex: (_indent: number) => /^ {0,4}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
fencesBeginRegex: (_indent: number) => /^ {0,4}(?:```|~~~)/,
headingBeginRegex: (_indent: number) => /^ {0,4}#/,
htmlBeginRegex: (_indent: number) => /^ {0,4}<(?:[a-z].*>|!--)/i,

};

/**
Expand Down Expand Up @@ -112,7 +112,7 @@ const def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +
.replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
.getRegex();

const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
const list = edit(/^( {0,4}bull)([ \t][^\n]+?)?(?:\n|$)/)
.replace(/bull/g, bullet)
.getRegex();

Expand Down
48 changes: 48 additions & 0 deletions src/tableExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { MarkedExtension } from './MarkedOptions.ts';
import type { Tokens } from './Tokens.ts';

export const tableExtension: MarkedExtension = {
renderer: {
table(token: Tokens.Table): string {
let header = '';

// header
let cell = '';
for (let j = 0; j < token.header.length; j++) {
cell += this.tablecell(token.header[j]);
}
header += this.tablerow({ text: cell });

let body = '';
for (let j = 0; j < token.rows.length; j++) {
const row = token.rows[j];

cell = '';
for (let k = 0; k < row.length; k++) {
cell += this.tablecell(row[k]);
}

body += this.tablerow({ text: cell });
}
if (body) body = `<tbody>${body}</tbody>`;

return '<table class="markdown-table">\n'
+ '<thead>\n'
+ header
+ '</thead>\n'
+ body
+ '</table>\n';
},
tablerow({ text }: Tokens.TableRow): string {
return `<tr class="markdown-table-row">\n${text}</tr>\n`;
},
tablecell(token: Tokens.TableCell): string {
const content = this.parser.parseInline(token.tokens);
const type = token.header ? 'th' : 'td';
const tag = token.align
? `<${type} align="${token.align}">`
: `<${type}>`;
return tag + content + `</${type}>\n`;
},
},
};
30 changes: 30 additions & 0 deletions test-bullet-issue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Test case for issue #2832 - bullet rendering with 4-space indentation
import { marked } from './lib/marked.esm.js';

const testCases = [
{
name: 'Empty nested item with 4-space indentation',
markdown: `- title
- desc
-`,
},
{
name: 'Empty nested item with 2-space indentation',
markdown: `- title
- desc
-`,
},
];

testCases.forEach((testCase) => {
console.log(`\n=== ${testCase.name} ===`);
console.log('Input:');
console.log(JSON.stringify(testCase.markdown));
console.log('\nOutput:');
try {
const result = marked.parse(testCase.markdown);
console.log(result);
} catch(error) {
console.error('Error:', error.message);
}
});
Comment on lines +1 to +30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test file is a great addition to verify the fix. However, it currently only logs the output to the console, requiring manual verification. To make it a robust, automated test, you should convert it to use a test framework like node:test (which seems to be used elsewhere in the project) and add assertions to check that the output HTML matches the expected result. This will help prevent future regressions automatically. For example, you can add assert.strictEqual(result, expectedHtml). Also, consider moving this file to the test/ directory and naming it with a .test.js suffix so it's picked up by the test runner.

Loading