Use typescript for the project
This commit is contained in:
parent
0f1d41a991
commit
83fd79f674
24 changed files with 2882 additions and 115 deletions
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
We are using Node.js <span id="node-version"></span>,
|
||||
Chromium <span id="chrome-version"></span>,
|
||||
and Electron <span id="electron-version"></span>.
|
||||
</body>
|
||||
</html>
|
|
@ -1,34 +0,0 @@
|
|||
import { app, BrowserWindow } from 'electron'
|
||||
|
||||
// import pkg from 'electron';
|
||||
// console.log("package ", pkg)
|
||||
// const { app, BrowserWindow } = pkg;
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
//win.loadFile('index.html')
|
||||
win.loadFile('/Users/agurgul/projs/home/docs/artur.gurgul.pro/.build/index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
// REPL
|
||||
// { app } = await import('electron')
|
||||
|
||||
export function run() {
|
||||
console.log("running")
|
||||
}
|
30
src/index.ts
Normal file
30
src/index.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Command } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
//import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { newProject, buildProject, appProject } from './project.js'
|
||||
import { serve } from './serve.js'
|
||||
|
||||
const program = new Command()
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize project in the current directory with the default theme')
|
||||
.action(newProject)
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.description('Build the webpage')
|
||||
.action(buildProject)
|
||||
|
||||
program
|
||||
.command('serve')
|
||||
.description('Run the website locally')
|
||||
.action(serve)
|
||||
|
||||
program
|
||||
.command('app')
|
||||
.description('Run notes as the the app')
|
||||
.action(appProject)
|
||||
|
||||
program.parse(process.argv)
|
55
src/markdown.ts
Normal file
55
src/markdown.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import hljs from 'highlight.js'
|
||||
import { marked } from 'marked'
|
||||
import { markedHighlight } from 'marked-highlight'
|
||||
import matter from 'gray-matter'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
marked.use(markedHighlight({
|
||||
langPrefix: 'hljs language-',
|
||||
highlight: function(code, lang) {
|
||||
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||
return hljs.highlight(code, { language }).value;
|
||||
}
|
||||
}))
|
||||
|
||||
export function parseMD(file: string) {
|
||||
|
||||
const fileContents = fs.readFileSync(path.join("./", file), 'utf8')
|
||||
|
||||
const { data: metadata, content: markdownContent } = matter(fileContents)
|
||||
|
||||
const htmlContent = marked(markdownContent)
|
||||
|
||||
return {
|
||||
meta: metadata,
|
||||
content: htmlContent
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = new marked.Renderer()
|
||||
renderer.paragraph = (text) => {
|
||||
return text.text
|
||||
}
|
||||
|
||||
|
||||
export function parseMarkdown(obj: any) {
|
||||
for (let key in obj) {
|
||||
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||
if (Array.isArray(obj[key])) {
|
||||
for (let i = 0; i < obj[key].length; i++) {
|
||||
if (typeof obj[key][i] === 'object' && obj[key][i] !== null) {
|
||||
parseMarkdown(obj[key][i]);
|
||||
}
|
||||
else if (typeof obj[key][i] === 'string') {
|
||||
obj[key][i] = marked(obj[key][i], { renderer });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parseMarkdown(obj[key]);
|
||||
}
|
||||
} else if (typeof obj[key] === 'string') {
|
||||
obj[key] = marked(obj[key], { renderer });
|
||||
}
|
||||
}
|
||||
}
|
72
src/project.ts
Normal file
72
src/project.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { cp } from './utils.js'
|
||||
|
||||
// Get the directory of the current file
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
// Path relative to the script file's directory
|
||||
const DEFAULT_PROJECT_PATH = path.join(__dirname, 'empty')
|
||||
|
||||
export function newProject() {
|
||||
console.log("Initialize a new project")
|
||||
console.log(DEFAULT_PROJECT_PATH)
|
||||
cp(DEFAULT_PROJECT_PATH, ".")
|
||||
}
|
||||
|
||||
import { readConfig } from './site.js'
|
||||
import { build } from './site.js'
|
||||
|
||||
export function buildProject() {
|
||||
console.log("building")
|
||||
|
||||
let config = {
|
||||
... readConfig(),
|
||||
buildDir: './.build',
|
||||
ignore: [".build", ".sajt"]
|
||||
}
|
||||
|
||||
config.remote.port = 22
|
||||
config.remote.privateKey = fs.readFileSync(path.resolve(process.env.HOME ?? "", '.ssh/id_rsa'))
|
||||
|
||||
|
||||
build(config)
|
||||
|
||||
//loadTemplate()
|
||||
//parseMD()
|
||||
//buildProject(config)
|
||||
}
|
||||
|
||||
//import { run } from './src/desktop/main.js'
|
||||
import * as proc from 'child_process'
|
||||
|
||||
//import { app, BrowserWindow } from 'electron'
|
||||
|
||||
import * as electron from 'electron'
|
||||
|
||||
export function appProject() {
|
||||
//run()
|
||||
|
||||
//const child = proc.spawn(electron, ["."])
|
||||
// console.log(electron)
|
||||
// console.log(electron.default)
|
||||
|
||||
// const child = proc.spawn(electron.default, [".build"])
|
||||
|
||||
// https://www.matthewslipper.com/2019/09/22/everything-you-wanted-electron-child-process.html
|
||||
// exec('node start', (error, stdout, stderr) => {
|
||||
// if (error) {
|
||||
// console.error(`error: ${error.message}`)
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (stderr) {
|
||||
// console.error(`stderr: ${stderr}`);
|
||||
// return
|
||||
// }
|
||||
|
||||
// console.log(`stdout:\n${stdout}`)
|
||||
// })
|
||||
}
|
17
src/serve.ts
Normal file
17
src/serve.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import express from 'express'
|
||||
import { setWatcher } from './watch.js'
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
|
||||
export function serve() {
|
||||
app.use(express.static('./.build'))
|
||||
|
||||
setWatcher(url => {
|
||||
console.log(url)
|
||||
})
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on http://localhost:${PORT}`)
|
||||
})
|
||||
}
|
157
src/site.ts
Executable file
157
src/site.ts
Executable file
|
@ -0,0 +1,157 @@
|
|||
import fs from 'fs'
|
||||
import pug from 'pug'
|
||||
import path from 'path'
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
import { cp } from "./utils.js"
|
||||
import { parseMarkdown } from './markdown.js'
|
||||
import { getAllFilesWithExtension, pathToArray, parseYML } from './utils.js'
|
||||
import { parseMD } from './markdown.js'
|
||||
|
||||
function removeDirectorySync(directory: string) {
|
||||
try {
|
||||
fs.rmSync(directory, { recursive: true, force: true })
|
||||
console.log("Directory and its contents removed.")
|
||||
} catch (err: any) {
|
||||
console.error(`Error removing directory: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export function readConfig(): any {
|
||||
const __dirname = process.cwd()
|
||||
const configPath = path.join(__dirname, '.sajt/config.yaml')
|
||||
const fileContents = fs.readFileSync(configPath, 'utf8')
|
||||
return yaml.load(fileContents)
|
||||
}
|
||||
|
||||
function compile(template: string, content: any, output: string) {
|
||||
if (template == null) {
|
||||
console.error("Template is not defined")
|
||||
return
|
||||
}
|
||||
const compiledFunction = pug.compileFile(`.sajt/layouts/${template}.pug`);
|
||||
const data = {
|
||||
...content,
|
||||
site: {posts: []}
|
||||
}
|
||||
|
||||
const dirname = path.dirname(output)
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true })
|
||||
}
|
||||
|
||||
const html = compiledFunction(data)
|
||||
fs.writeFileSync(output, html)
|
||||
console.log(`HTML has been rendered and saved to ${output}`);
|
||||
}
|
||||
|
||||
function compileData(template: string, content: object, output: string) {
|
||||
const compiledFunction = pug.compileFile(`.sajt/layouts/${template}.pug`)
|
||||
|
||||
const dirname = path.dirname(output)
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true })
|
||||
}
|
||||
|
||||
|
||||
const html = compiledFunction(content)
|
||||
fs.writeFileSync(output, html)
|
||||
console.log(`HTML has been rendered and saved to ${output}`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function readMetadata(ignore: string[]) {
|
||||
let htmlExtension = "html"
|
||||
|
||||
let listMD = getAllFilesWithExtension('.',".md", ignore)
|
||||
.map(f => { return {
|
||||
pathMD: f,
|
||||
type: "md",
|
||||
data: {} as any,
|
||||
md: parseMD(f)
|
||||
} as any })
|
||||
// sites needs to include data from header
|
||||
|
||||
let listYML = getAllFilesWithExtension('.',".yml", ignore)
|
||||
.map(f => { return {
|
||||
pathMD: f,
|
||||
type: "yml",
|
||||
data: parseYML(f),
|
||||
md: {meta: {}}
|
||||
} as any })
|
||||
|
||||
let list = listMD.concat(listYML)
|
||||
|
||||
for(const site of list) {
|
||||
|
||||
//console.log(site.md.meta.path)
|
||||
// TODO: data can set default path
|
||||
if (site.md.meta?.path != null && site.md.meta?.path != undefined) {
|
||||
site.path = path.join("/", site.md.meta.path)
|
||||
} else {
|
||||
const parsedPath = path.parse(site.pathMD)
|
||||
const basePath = path.join("/", parsedPath.dir, parsedPath.name)
|
||||
site.path = basePath
|
||||
}
|
||||
|
||||
// add proper extension
|
||||
const parsedPath = path.parse(site.path)
|
||||
parsedPath.ext = htmlExtension.startsWith('.') ? htmlExtension : `.${htmlExtension}`
|
||||
parsedPath.base = `${parsedPath.name}${parsedPath.ext}`
|
||||
site.path = path.format(parsedPath)
|
||||
|
||||
// add dirs metadata
|
||||
const dirArray = pathToArray(site.path)
|
||||
site.fileName = dirArray.pop()
|
||||
dirArray.shift()
|
||||
site.dir = dirArray
|
||||
|
||||
site.meta = site.md.meta
|
||||
|
||||
site.hidden = site.data?.hidden || false
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function build(config: any) {
|
||||
removeDirectorySync(config.buildDir)
|
||||
cp("./.sajt/static", path.join(config.buildDir, "static"))
|
||||
|
||||
let data = readMetadata(config.ignore)
|
||||
let pages = data.map(site => {
|
||||
return {
|
||||
title: site.meta.title,
|
||||
url: site.path
|
||||
}
|
||||
})
|
||||
|
||||
for(const site of data) {
|
||||
if (site.type == "md") {
|
||||
compile(site.meta.layout,
|
||||
{
|
||||
content: site.md.content,
|
||||
title: site.meta.title,
|
||||
hidden: false,
|
||||
pages
|
||||
},
|
||||
path.join(config.buildDir, site.path))
|
||||
} else if (site.type == "yml") {
|
||||
let data = {...site.data}
|
||||
delete data.layout
|
||||
parseMarkdown(data)
|
||||
compileData(site.data.layout,
|
||||
{data, pages, hidden: data.hidden},
|
||||
path.join(config.buildDir, site.path))
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(readMetadata())
|
||||
// sajt
|
||||
|
||||
// Not to upload now
|
||||
//uploadDirectory(serverConfig, buildFolder)
|
||||
}
|
36
src/ssh.ts
Normal file
36
src/ssh.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*const Client = require('ssh2-sftp-client')
|
||||
|
||||
async function uploadDirectory(serverConfig, localDirPath) {
|
||||
const sftp = new Client()
|
||||
await sftp.connect(serverConfig)
|
||||
try {
|
||||
await upload(sftp, config, localDirPath, serverConfig.path)
|
||||
} catch (err) {
|
||||
console.error(`Error: ${err.message}`)
|
||||
} finally {
|
||||
await sftp.end()
|
||||
console.log('Connection closed')
|
||||
}
|
||||
}
|
||||
|
||||
async function upload(sftp, config, localPath, remotePath) {
|
||||
|
||||
console.log('Connected to the server')
|
||||
|
||||
const files = fs.readdirSync(localPath)
|
||||
|
||||
for (const file of files) {
|
||||
const localFilePath = path.join(localPath, file)
|
||||
const remoteFilePath = `${remotePath}/${file}`
|
||||
|
||||
if (fs.statSync(localFilePath).isDirectory()) {
|
||||
await sftp.mkdir(remoteFilePath, true)
|
||||
await upload(sftp, config, localFilePath, remoteFilePath)
|
||||
} else {
|
||||
const fileContent = fs.readFileSync(localFilePath)
|
||||
await sftp.put(Buffer.from(fileContent), remoteFilePath)
|
||||
console.log(`File transferred successfully: ${localFilePath}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
53
src/utils.ts
Normal file
53
src/utils.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import yaml from 'js-yaml'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export function parseYML(file: string) {
|
||||
const fileContents = fs.readFileSync(file, 'utf8')
|
||||
return yaml.load(fileContents)
|
||||
}
|
||||
|
||||
export function getAllFilesWithExtension(directory: string, extension: string, excludes: string[]): string[] {
|
||||
let results: string[] = []
|
||||
function readDirectory(directory: string) {
|
||||
const items = fs.readdirSync(directory)
|
||||
|
||||
items.forEach(item => {
|
||||
if(excludes.includes(item)) {
|
||||
return
|
||||
}
|
||||
const itemPath = path.join(directory, item)
|
||||
const stat = fs.statSync(itemPath)
|
||||
if (stat.isDirectory()) {
|
||||
readDirectory(itemPath)
|
||||
} else if (path.extname(item) === extension) {
|
||||
results.push(itemPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
readDirectory(directory)
|
||||
return results
|
||||
}
|
||||
|
||||
// copyDirectory
|
||||
export function cp(source: string, destination: string) {
|
||||
fs.mkdirSync(destination, { recursive: true })
|
||||
const items = fs.readdirSync(source)
|
||||
items.forEach(item => {
|
||||
const sourceItemPath = path.join(source, item)
|
||||
const destinationItemPath = path.join(destination, item)
|
||||
const stat = fs.statSync(sourceItemPath)
|
||||
if (stat.isDirectory()) {
|
||||
cp(sourceItemPath, destinationItemPath)
|
||||
} else {
|
||||
fs.copyFileSync(sourceItemPath, destinationItemPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function pathToArray(filePath: string) {
|
||||
// Normalize the file path to handle different OS path separators
|
||||
const normalizedPath = path.normalize(filePath)
|
||||
// Split the path into an array of directories
|
||||
return normalizedPath.split(path.sep)
|
||||
}
|
35
src/watch.ts
Normal file
35
src/watch.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export function setWatcher(callback: (url: string) => void) {
|
||||
// Directory to watch
|
||||
const directoryPath = './';
|
||||
// Ensure the directory exists
|
||||
if (!fs.existsSync(directoryPath)) {
|
||||
fs.mkdirSync(directoryPath, { recursive: true });
|
||||
}
|
||||
console.log(`Watching for changes in: ${directoryPath}`);
|
||||
// Watch the directory for changes
|
||||
fs.watch(directoryPath, (eventType, filename) => {
|
||||
if (filename) {
|
||||
const fullPath = path.join(directoryPath, filename);
|
||||
console.log(`File ${filename} has been ${eventType}`);
|
||||
// Check if the file was added, changed, or deleted
|
||||
fs.stat(fullPath, (err, stats) => {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log(`File ${filename} was deleted.`);
|
||||
} else {
|
||||
console.error(`Error checking file status: ${err.message}`);
|
||||
}
|
||||
} else {
|
||||
if (stats.isFile()) {
|
||||
console.log(`File ${filename} exists with size: ${stats.size} bytes`);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('Filename not provided, but change detected.');
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue