package.json
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "w5han",
"license": "ISC",
"dependencies": {
"highlight.js": "^11.8.0",
"markdown-it": "^13.0.1"
}
}
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://xn--7ct.top/favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="https://xn--7ct.top/favicon.ico" type="image/x-icon">
<style>
#search {
padding: 10px;
font-size: 18px;
}
body > h3 {
text-align: center;
font-weight: normal;
}
body > p {
text-align: center;
}
body > footer {
width: fit-content;
margin: 0 auto;
}
body {
margin: 0;
padding: 0;
min-height: 100vh;
background: linear-gradient(to bottom, #e0e8da, #8187a1);
}
</style>
<title>砸砸灰</title>
</head>
<body>
<div style="margin-top: 0px; height: 1px;"></div>
<p>
<input id="search" type="text" placeholder="自用搜索框">
</p>
<h3>若遇加载问题, 多刷新几次</h3>
<p><a href="/html/黑神话悟空影神图/ystHeiShenHua.html">黑神话悟空-影神图</a></p>
<p><a href="/html/俄罗斯方块/game.html">俄罗斯方块</a></p>
<p><a href="/html/轮播图/lunbotu2.html">bilibili轮播图还原</a></p>
<p><a href="/html/抽拉效果/抽拉效果.html">抽拉效果</a></p>
<p><a href="/html/显卡天梯图/显卡天梯图react.html">显卡天梯图</a></p>
<p><a href="/html/立方体/立方体.html">css立方体</a></p>
<p><a href="/html/webgl/webgl07.html">webgl绘制多条闪烁的折线</a></p>
<p><a href="/html/webgl/webgl22.html">webgl跳动的盒子</a></p>
<p><a href="/html/webgl/webgl23.html">webgl一池春水</a></p>
<p><a href="/html/webgl/webgl30.html">webgl多点异色</a></p>
<p><a href="/html/webgl/webgl32.html">webgl一片春色</a></p>
<p><a href="/html/todo/todo.html">我的TODO(练习键盘事件和拖拽事件)</a></p>
<p><a href="/markdown/笔记0.md">建站笔记</a></p>
<p><a href="/html/代码结构.html">代码高亮</a></p>
<p><a href="/html/兰亭序.html">兰亭序(打字特效)</a></p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web">MDN</a></p>
<p><a href="https://csdiy.wiki/">CS自学指南</a></p>
<p><a href="https://browser.engineering/">浏览器</a></p>
<p>欢迎交流</p>
<p>QQ: 30435566</p>
<footer><a href="https://beian.miit.gov.cn/" target="_blank">湘ICP备2024094932号-1</a></footer>
</body>
</html>
<script>
const search = document.querySelector('#search')
search.addEventListener("keyup", function(e){
if (e.key === 'Enter') {
// console.log(e.srcElement.value, '-----e.srcElement.value')
const searchKey = e.srcElement.value
window.location.href = "https://www.bing.com/search?q=" + searchKey
}
})
</script>
代码结构.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style></style>
<title>代码结构</title>
</head>
<body>
<br>
<img src="../image/代码结构截图.png">
[package.json]
[index.html]
[代码结构.html]
[copyEvent.js]
[app.js]
[utils.js]
[代码结构.js]
</body>
</html>
<script src="./copyEvent.js"></script>
copyEvent.js
const btns = document.querySelectorAll(".copy-btn")
for (const btn of btns) {
btn.onclick = function (e) {
// console.log(e.target.dataset.file, '-----e.target.dataset.file')
let key = e.target.dataset.key
key = '.' + key // 当前路径位于根路径的下一级, 加个点去根目录
let option
if (/.html$/.test(key)) option = {headers : { "Content-Type": "text/plain", }}
fetch(key, option)
.then(response => response.text())
.then((data) => copyText(data))
}
}
function copyText(str) {
// console.log(str, '-----str')
// 创建一个文本输入框
const textArea = document.createElement("textarea");
textArea.value = str;
// 将文本输入框添加到DOM中
document.body.appendChild(textArea);
// 选择文本
textArea.select();
// 尝试复制文本到剪切板
document.execCommand('copy');
// 从DOM中移除文本输入框
document.body.removeChild(textArea);
}
app.js
const { 代码结构 } = require('./server/htmljs/代码结构.js')
const { MarkDown, fileRead, returnHtml, returnJavaScript, returnCss, returnText, returnImage } = require('./server/utils.js')
const http = require('http');
const fs = require('fs');
const hostname = "127.0.0.1";
const port = 3000;
const markDown = new MarkDown()
const server = http.createServer((req, res) => {
// 修复路由中文字符的问题
req.url = decodeURIComponent(req.url)
if (req.url === '/text') {
returnHtml(res, fileRead('./输入效果.html'))
} else if (/.md$/.test(req.url)) {
let str = fileRead('.' + req.url)
str = markDown.get(str)
returnHtml(res, str)
} else if (/.html$/.test(req.url)) {
// ↓↓↓↓↓↓↓↓↓ 代码结构.html
if (req.headers["content-type"] == 'text/plain') {
// 复制功能, 读取html文件的文本内容, 不按html解析
returnText(res, fileRead('.' + req.url))
return
}
if (req.url === '/html/代码结构.html') {
let str = fileRead('.' + req.url)
str = 代码结构(markDown, str)
returnHtml(res, str)
return
}
// ↑↑↑↑↑↑↑↑↑ 代码结构.html
// 未特殊处理得一般html
returnHtml(res, fileRead('.' + req.url))
} else if (/\.(js|mjs)$/.test(req.url)) {
returnJavaScript(res, fileRead('.' + req.url))
} else if (/\.(css)$/.test(req.url)) {
returnCss(res, fileRead('.' + req.url))
} else if (/\.(css|json)$/.test(req.url)) {
returnText(res, fileRead('.' + req.url))
} else if (req.method === 'GET' && req.url === '/favicon.ico') {
res.setHeader('Content-Type', 'image/x-icon');
fs.createReadStream('./favicon.ico').pipe(res);
} else if (/\.(jpg|png|ico|svg|gif|webp|bmp|tiff)$/.test(req.url)) {
// 读取图片文件
console.log('+++++', req.url)
returnImage(res, fs.readFileSync(`./${req.url}`), req.url)
} else if (req.url === '/') {
returnHtml(res, fileRead('./index.html'))
} else {
returnText(res, `页面没找到 ${req.url}`)
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
utils.js
const fs = require('fs')
const { EOL } = require('os')
class MarkDown {
constructor() {
const hljs = this._initHljs()
this.md = this._getMarkDown(hljs)
this.hljsCss = this._getHljsCss()
}
get(str) {
// 修复markdown-it插件问题, 换行没生效
// 使用markdown语法 +两个尾空格表示换行
const reg = new RegExp('(\\S'+EOL+')', 'g')
str = str.replace(reg, '$1 '+EOL)
const {hljsCss, md} = this
return `${hljsCss}<div class="markdown">${md.render(str)}</div>`
}
_initHljs() {
const hljs = require('highlight.js'); // https://highlightjs.org/
hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript'));
return hljs
}
_getMarkDown(hljs) {
return require('markdown-it')({
html: true, // markdown中启用html标签
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value;
}
return ''; // 使用额外的默认转义
}
})
}
_getHljsCss() {
let hljsCss = fs.readFileSync('node_modules/highlight.js/styles/github.css')
hljsCss = `<head><style>
${hljsCss.toString()}
body { text-align: center; background: #f7f8fc; margin: 0; }
pre { text-align: start; width:fit-content; margin: auto; }
.markdown {
width: 70vw; margin:auto; padding: 8px;
background: #ffffff;
position: relative;
overflow: hidden;
margin-bottom: 10px;
}
.markdown .absolute-a {
position: absolute;
top: 0;
right: 0;
}
</style></head>`
return hljsCss
}
}
function fileRead(filePath, callback) {
try {
return fs.readFileSync(filePath).toString()
} catch (error) {
throw error
}
}
function returnHtml(res, str) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(str)
}
function returnJavaScript(res, str) {
res.statusCode = 200
res.setHeader('Content-Type', 'application/javascript; charset=utf-8')
res.end(str)
}
function returnCss(res, str) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/css; charset=utf-8')
res.end(str)
}
function returnText(res, str) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end(str)
}
function returnImage(res, imageContent, url) {
const extension = url.split('.').pop(); // 取出图片后缀(jpg|png|ico)
res.statusCode = 200
res.writeHead(200, { 'Content-Type': `image/${extension}` });
res.end(imageContent)
}
module.exports = {
MarkDown,
fileRead,
returnHtml,
returnJavaScript,
returnCss,
returnText,
returnImage,
}
代码结构.js
const fs = require('fs')
function 代码结构(markDown, content) {
const { hljsCss, get: _mdget } = markDown
const mdget = _mdget.bind(markDown)
content = content.replace(
'<style></style>',
hljsCss,
)
const dict = [
['[package.json]', './package.json'],
['[index.html]', './index.html'],
['[代码结构.html]', './html/代码结构.html'],
['[copyEvent.js]', './html/copyEvent.js'],
['[app.js]', './app.js'],
['[utils.js]', './server/utils.js'],
['[代码结构.js]', './server/htmljs/代码结构.js'],
]
for (const item of dict) {
const [tag, filePath] = item
// tag == '[package.json]'
// filePath == './package.json'
const fileName = tag.slice(1, -1) // 去掉中括号 package.json
const newTag = `{${fileName}}`
const lang = filePath.split('.').slice(-1)[0] // lang == json | js | 等等
const fileContent = fs.readFileSync(filePath).toString()
let str = "```" + lang + "\r\n" + fileContent + "\r\n ```"
str = mdget(str)
str = `
<p>${fileName}</p>
<div class="markdown">
${str}
<button class="absolute-a copy-btn" data-key="${filePath}">复制</button>
</div>
`
// ** 由于str中包含tag, 为了避免转换str中的tag, 将原始的tag转换为新标记newTag, 转换完所有的tag再替换为str
content = content.replace(tag, newTag)
item.push(str)
}
for (const item of dict) {
const [tag, filePath, str] = item
const fileName = tag.slice(1, -1) // 去掉中括号 package.json
const newTag = `{${fileName}}`
// ** 将临时标记newTag替换为str
content = content.replace(newTag, str)
}
return content
}
module.exports = {
代码结构
}