First version done
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,json,yml}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/.yarn/** linguist-vendored
|
||||||
|
/.yarn/releases/* binary
|
||||||
|
/.yarn/plugins/**/* binary
|
||||||
|
/.pnp.* binary linguist-generated
|
||||||
5
.yarnrc.yml
Normal file
5
.yarnrc.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
compressionLevel: mixed
|
||||||
|
|
||||||
|
enableGlobalCache: false
|
||||||
|
|
||||||
|
nodeLinker: node-modules
|
||||||
19
package.json
Normal file
19
package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "markdown2png",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Convert Markdown files to PNG",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "ts-node src/index.ts"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"marked": "^4.3.0",
|
||||||
|
"puppeteer": "^21.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/marked": "^4.0.8",
|
||||||
|
"@types/node": "^18.15.11",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^5.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/index.ts
Normal file
109
src/index.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
interface PageDimensions {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function convertMarkdownToImage(markdownPath: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const markdownContent = await fs.readFile(markdownPath, 'utf-8');
|
||||||
|
|
||||||
|
const htmlContent = marked(markdownContent);
|
||||||
|
|
||||||
|
const fullHtml = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
color: #24292e;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${htmlContent}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
await fs.mkdir('output', { recursive: true });
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await page.setContent(fullHtml, { waitUntil: 'networkidle0' });
|
||||||
|
|
||||||
|
const dimensions = await page.evaluate((): PageDimensions => {
|
||||||
|
return {
|
||||||
|
width: document.documentElement.offsetWidth,
|
||||||
|
height: document.documentElement.offsetHeight
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.setViewport({
|
||||||
|
width: dimensions.width,
|
||||||
|
height: dimensions.height,
|
||||||
|
deviceScaleFactor: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
const outputPath = path.join(
|
||||||
|
'output',
|
||||||
|
`${path.basename(markdownPath, '.md')}.png`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.screenshot({
|
||||||
|
path: outputPath,
|
||||||
|
fullPage: true,
|
||||||
|
type: 'png'
|
||||||
|
});
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
console.log(`Successfully converted ${markdownPath} to ${outputPath}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Conversion error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const inputFile = process.argv[2];
|
||||||
|
|
||||||
|
if (!inputFile) {
|
||||||
|
console.log('Usage: npm start <markdown-file>');
|
||||||
|
console.log('Example: npm start input/document.md');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await convertMarkdownToImage(inputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["ES2020", "DOM"],
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user