Start adding markdown conversion to site generator.
This commit is contained in:
parent
f0091f9e04
commit
f44c79dc1f
31 changed files with 692 additions and 461 deletions
|
@ -2,19 +2,24 @@
|
|||
list(APPEND website_generator_LIB_INCLUDES
|
||||
ContentFile.h
|
||||
ContentFile.cpp
|
||||
ContentPage.h
|
||||
ContentPage.cpp
|
||||
MarkdownContentParser.h
|
||||
MarkdownContentParser.cpp
|
||||
SiteGeneratorConfig.h
|
||||
SiteGeneratorConfig.cpp
|
||||
WebsiteGenerator.h
|
||||
WebsiteGenerator.cpp)
|
||||
WebsiteGenerator.cpp
|
||||
)
|
||||
|
||||
|
||||
add_executable(website_generator main.cpp ${website_generator_LIB_INCLUDES})
|
||||
|
||||
target_include_directories(website_generator PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(website_generator PUBLIC core web)
|
||||
target_link_libraries(website_generator PUBLIC core web compiler)
|
||||
|
||||
set_property(TARGET website_generator PROPERTY FOLDER apps/website-generator)
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#include "ContentFile.h"
|
||||
|
||||
#include "PathUtils.h"
|
||||
#include "MarkdownDocument.h"
|
||||
#include "MarkdownElement.h"
|
||||
|
||||
ContentFile::ContentFile(const Path& filename)
|
||||
: mFilename(filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ContentFile::~ContentFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Path ContentFile::getFilename() const
|
||||
{
|
||||
return mFilename;
|
||||
}
|
||||
|
||||
std::string ContentFile::getOutputLocation() const
|
||||
{
|
||||
const auto metadata_item = getMetadataItem("save_as");
|
||||
return metadata_item.empty() ? PathUtils::getBaseFilename(mFilename) : metadata_item;
|
||||
}
|
||||
|
||||
void ContentFile::load()
|
||||
{
|
||||
MarkdownContentParser parser;
|
||||
auto result = parser.run(mFilename);
|
||||
|
||||
mMetadata = result.first;
|
||||
mContentBody = std::move(result.second);
|
||||
}
|
||||
|
||||
std::string ContentFile::getMetadataItem(const std::string& key) const
|
||||
{
|
||||
const auto check = mMetadata.find(key);
|
||||
if (check == mMetadata.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return check->second;
|
||||
}
|
||||
}
|
||||
|
||||
void ContentFile::write(const Path& path)
|
||||
{
|
||||
|
||||
}
|
|
@ -7,73 +7,37 @@
|
|||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
class MarkdownDocument;
|
||||
|
||||
class ContentFile
|
||||
{
|
||||
public:
|
||||
using FileMetadata = std::unordered_map<std::string, std::string>;
|
||||
using FileContentBody = std::vector<std::string>;
|
||||
|
||||
ContentFile(const Path& filename)
|
||||
: mFilename(filename)
|
||||
{
|
||||
ContentFile(const Path& filename);
|
||||
|
||||
virtual ~ContentFile();
|
||||
|
||||
Path getFilename() const;
|
||||
|
||||
virtual void load();
|
||||
|
||||
std::string getMetadataItem(const std::string& key) const;
|
||||
|
||||
virtual std::string getOutputLocation() const;
|
||||
|
||||
MarkdownDocument* getContentBody() const
|
||||
{
|
||||
return mContentBody.get();
|
||||
}
|
||||
|
||||
virtual ~ContentFile() = default;
|
||||
|
||||
Path getFilename() const
|
||||
{
|
||||
return mFilename;
|
||||
}
|
||||
|
||||
virtual void load()
|
||||
{
|
||||
MarkdownContentParser parser;
|
||||
parser.run(mFilename);
|
||||
|
||||
mMetadata = parser.getFileMetadata();
|
||||
mContentBody = parser.getContentBody();
|
||||
}
|
||||
|
||||
std::string getMetadataItem(const std::string& key) const
|
||||
{
|
||||
const auto check = mMetadata.find(key);
|
||||
if (check == mMetadata.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return check->second;
|
||||
}
|
||||
}
|
||||
|
||||
void write(const Path& path);
|
||||
protected:
|
||||
Path mFilename;
|
||||
FileMetadata mMetadata;
|
||||
FileContentBody mContentBody;
|
||||
};
|
||||
|
||||
class ContentPage : public ContentFile
|
||||
{
|
||||
public:
|
||||
|
||||
ContentPage(const Path& filename)
|
||||
: ContentFile(filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void load() override
|
||||
{
|
||||
ContentFile::load();
|
||||
}
|
||||
|
||||
std::string getOutputLocation() const
|
||||
{
|
||||
const auto metadata_item = getMetadataItem("save_as");
|
||||
return metadata_item.empty() ? File::getBaseFilename(mFilename) : metadata_item;
|
||||
}
|
||||
std::unique_ptr<MarkdownDocument> mContentBody;
|
||||
std::string mProcessedOutput;
|
||||
};
|
||||
|
||||
class ContentArticle : public ContentFile
|
||||
|
|
14
apps/website-generator/ContentPage.cpp
Normal file
14
apps/website-generator/ContentPage.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "ContentPage.h"
|
||||
|
||||
ContentPage::ContentPage(const Path& filename)
|
||||
: ContentFile(filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ContentPage::load()
|
||||
{
|
||||
ContentFile::load();
|
||||
}
|
||||
|
||||
|
11
apps/website-generator/ContentPage.h
Normal file
11
apps/website-generator/ContentPage.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "ContentFile.h"
|
||||
|
||||
class ContentPage : public ContentFile
|
||||
{
|
||||
public:
|
||||
ContentPage(const Path& filename);
|
||||
|
||||
void load() override;
|
||||
};
|
|
@ -1,11 +1,20 @@
|
|||
#include "MarkdownContentParser.h"
|
||||
|
||||
void MarkdownContentParser::run(const Path& path)
|
||||
#include "MarkdownParser.h"
|
||||
#include "MarkdownDocument.h"
|
||||
#include "MarkdownElement.h"
|
||||
|
||||
#include "File.h"
|
||||
|
||||
std::pair<MarkdownContentParser::FileMetadata, std::unique_ptr<MarkdownDocument>> MarkdownContentParser::run(const Path& path)
|
||||
{
|
||||
FileMetadata metadata;
|
||||
FileMetadata output_metadata;
|
||||
|
||||
const auto lines = File(path).readLines();
|
||||
bool metadata_finished = false;
|
||||
|
||||
std::string content_body;
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (!metadata_finished)
|
||||
|
@ -14,28 +23,23 @@ void MarkdownContentParser::run(const Path& path)
|
|||
if (!metadata)
|
||||
{
|
||||
metadata_finished = true;
|
||||
mContentBody.push_back(line);
|
||||
content_body += line + '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
mMetadata[metadata->first] = metadata->second;
|
||||
output_metadata[metadata->first] = metadata->second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mContentBody.push_back(line);
|
||||
content_body += line + '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MarkdownContentParser::FileContentBody MarkdownContentParser::getContentBody() const
|
||||
{
|
||||
return mContentBody;
|
||||
}
|
||||
MarkdownParser md_parser;
|
||||
auto content = md_parser.run(content_body);
|
||||
|
||||
MarkdownContentParser::FileMetadata MarkdownContentParser::getFileMetadata() const
|
||||
{
|
||||
return mMetadata;
|
||||
return {output_metadata, std::move(content)};
|
||||
}
|
||||
|
||||
std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::checkForMetadataItem(const std::string& line) const
|
||||
|
@ -69,4 +73,4 @@ std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::ch
|
|||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class MarkdownDocument;
|
||||
|
||||
class MarkdownContentParser
|
||||
{
|
||||
public:
|
||||
using FileMetadataItem = std::pair<std::string, std::string>;
|
||||
using FileMetadata = std::unordered_map<std::string, std::string>;
|
||||
using FileContentBody = std::vector<std::string>;
|
||||
|
||||
void run(const Path& path);
|
||||
|
||||
FileContentBody getContentBody() const;
|
||||
|
||||
FileMetadata getFileMetadata() const;
|
||||
|
||||
std::pair<FileMetadata, std::unique_ptr<MarkdownDocument>> run(const Path& path);
|
||||
private:
|
||||
std::optional<FileMetadataItem> checkForMetadataItem(const std::string& line) const;
|
||||
|
||||
FileMetadata mMetadata;
|
||||
FileContentBody mContentBody;
|
||||
};
|
||||
|
|
21
apps/website-generator/SiteGeneratorConfig.cpp
Normal file
21
apps/website-generator/SiteGeneratorConfig.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "SiteGeneratorConfig.h"
|
||||
|
||||
Path SiteGeneratorConfig::getThemePath() const
|
||||
{
|
||||
return mThemesPath;
|
||||
}
|
||||
|
||||
std::string SiteGeneratorConfig::getActiveTheme() const
|
||||
{
|
||||
return mActiveTheme;
|
||||
}
|
||||
|
||||
void SiteGeneratorConfig::setThemePath(const Path& path)
|
||||
{
|
||||
mThemesPath = path;
|
||||
}
|
||||
|
||||
void SiteGeneratorConfig::setActiveTheme(const std::string& theme)
|
||||
{
|
||||
mActiveTheme = theme;
|
||||
}
|
22
apps/website-generator/SiteGeneratorConfig.h
Normal file
22
apps/website-generator/SiteGeneratorConfig.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class SiteGeneratorConfig
|
||||
{
|
||||
public:
|
||||
Path getThemePath() const;
|
||||
|
||||
std::string getActiveTheme() const;
|
||||
|
||||
void setThemePath(const Path& path);
|
||||
|
||||
void setActiveTheme(const std::string& theme);
|
||||
|
||||
private:
|
||||
Path mThemesPath;
|
||||
std::string mActiveTheme;
|
||||
};
|
18
apps/website-generator/SiteTemplateFile.h
Normal file
18
apps/website-generator/SiteTemplateFile.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class SiteTemplateFile
|
||||
{
|
||||
public:
|
||||
SiteTemplateFile(const Path& path)
|
||||
: mPath(path)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
Path mPath;
|
||||
};
|
|
@ -1,6 +1,27 @@
|
|||
#include "WebsiteGenerator.h"
|
||||
|
||||
#include "TomlReader.h"
|
||||
#include "Directory.h"
|
||||
#include "ContentFile.h"
|
||||
#include "ContentPage.h"
|
||||
#include "TemplateFile.h"
|
||||
#include "TemplatingEngine.h"
|
||||
#include "SiteGeneratorConfig.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
#include "FileLogger.h"
|
||||
|
||||
WebsiteGenerator::WebsiteGenerator()
|
||||
: mConfig(std::make_unique<SiteGeneratorConfig>()),
|
||||
mTemplateEngine()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WebsiteGenerator::~WebsiteGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WebsiteGenerator::findProject(const std::string& searchPath)
|
||||
{
|
||||
|
@ -8,10 +29,42 @@ void WebsiteGenerator::findProject(const std::string& searchPath)
|
|||
if (std::filesystem::exists(config_path))
|
||||
{
|
||||
mProjectPath = searchPath;
|
||||
std::cout << "Found config.toml in " << searchPath << std::endl;
|
||||
MLOG_INFO("Found project config in: " << mProjectPath);
|
||||
}
|
||||
}
|
||||
|
||||
void WebsiteGenerator::findContentFiles()
|
||||
{
|
||||
const bool is_recursive{true};
|
||||
const auto pages_files = Directory::getFilesWithExtension(getPagesPath(), ".md", is_recursive);
|
||||
for (const auto& path : pages_files)
|
||||
{
|
||||
mPages.push_back(std::make_unique<ContentPage>(path));
|
||||
}
|
||||
|
||||
const auto article_files = Directory::getFilesWithExtension(getArticlesPath(), ".md", is_recursive);
|
||||
for (const auto& path : article_files)
|
||||
{
|
||||
mArticles.push_back(std::make_unique<ContentArticle>(path));
|
||||
}
|
||||
MLOG_INFO("Found: " << mArticles.size() << " articles");
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getContentPath() const
|
||||
{
|
||||
return mProjectPath / "content";
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getPagesPath() const
|
||||
{
|
||||
return getContentPath() / "pages";
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getArticlesPath() const
|
||||
{
|
||||
return getContentPath() / "articles";
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getConfigPath() const
|
||||
{
|
||||
return mProjectPath / "config.toml";
|
||||
|
@ -28,72 +81,54 @@ void WebsiteGenerator::readConfig()
|
|||
auto location = items["location"];
|
||||
auto active_theme = items["active_theme"];
|
||||
|
||||
mConfig.setThemePath(location);
|
||||
mConfig.setActiveTheme(active_theme);
|
||||
mConfig->setThemePath(location);
|
||||
mConfig->setActiveTheme(active_theme);
|
||||
}
|
||||
}
|
||||
|
||||
void WebsiteGenerator::parseContentFiles()
|
||||
{
|
||||
findContentFiles();
|
||||
|
||||
for (auto& page : mPages)
|
||||
{
|
||||
page.load();
|
||||
page->load();
|
||||
}
|
||||
|
||||
for (auto& article : mArticles)
|
||||
{
|
||||
article->load();
|
||||
}
|
||||
}
|
||||
|
||||
void WebsiteGenerator::parseTemplateFiles()
|
||||
{
|
||||
const auto template_path = mProjectPath / mConfig.getThemePath() / mConfig.getActiveTheme();
|
||||
|
||||
const auto template_files = Directory::getFilesWithExtension(template_path, ".html");
|
||||
for (const auto& path : template_files)
|
||||
{
|
||||
mTemplateFiles[path.stem().string()] = std::make_unique<TemplateFile>(path);
|
||||
}
|
||||
const auto template_path = mProjectPath / mConfig->getThemePath() / mConfig->getActiveTheme();
|
||||
mTemplateEngine = std::make_unique<TemplatingEngine>(template_path);
|
||||
mTemplateEngine->loadTemplateFiles();
|
||||
}
|
||||
|
||||
void WebsiteGenerator::doSubstitutions()
|
||||
{
|
||||
auto article_template = mTemplateEngine->processTemplate("article");
|
||||
for (auto& article : mArticles)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void WebsiteGenerator::write()
|
||||
{
|
||||
// Setup output dir
|
||||
const auto output_dir = mProjectPath / "output_custom";
|
||||
const auto output_dir = mProjectPath / "output";
|
||||
std::filesystem::create_directory(output_dir);
|
||||
|
||||
// Write each page
|
||||
}
|
||||
|
||||
void WebsiteGenerator::findContentFiles()
|
||||
{
|
||||
const auto pages_files = Directory::getFilesWithExtension(getPagesPath(), ".md");
|
||||
for (const auto& path : pages_files)
|
||||
for (auto& article : mArticles)
|
||||
{
|
||||
mPages.push_back(ContentPage(path));
|
||||
}
|
||||
|
||||
const auto article_files = Directory::getFilesWithExtension(getArticlesPath(), ".md");
|
||||
for (const auto& path : article_files)
|
||||
{
|
||||
mArticles.push_back(ContentArticle(path));
|
||||
auto article_path = article->getFilename();
|
||||
auto relative_path = PathUtils::getRelativePath(article_path, getArticlesPath());
|
||||
auto updated_filename = PathUtils::getPathDelimited(relative_path);
|
||||
auto final_path = output_dir / Path(updated_filename).replace_extension(".html");
|
||||
}
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getContentPath() const
|
||||
{
|
||||
return mProjectPath / "content";
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getPagesPath() const
|
||||
{
|
||||
return getContentPath() / "pages";
|
||||
}
|
||||
|
||||
Path WebsiteGenerator::getArticlesPath() const
|
||||
{
|
||||
return getContentPath() / "articles";
|
||||
}
|
||||
|
|
|
@ -1,72 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "Directory.h"
|
||||
#include "ContentFile.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class GeneratorConfig
|
||||
{
|
||||
public:
|
||||
void setThemePath(const Path& path)
|
||||
{
|
||||
mThemesPath = path;
|
||||
}
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
void setActiveTheme(const std::string& theme)
|
||||
{
|
||||
mActiveTheme = theme;
|
||||
}
|
||||
|
||||
Path getThemePath() const
|
||||
{
|
||||
return mThemesPath;
|
||||
}
|
||||
|
||||
std::string getActiveTheme() const
|
||||
{
|
||||
return mActiveTheme;
|
||||
}
|
||||
|
||||
private:
|
||||
Path mThemesPath;
|
||||
std::string mActiveTheme;
|
||||
};
|
||||
|
||||
class TemplateFile
|
||||
{
|
||||
public:
|
||||
TemplateFile(const Path& path)
|
||||
: mPath(path)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
Path mPath;
|
||||
};
|
||||
class SiteGeneratorConfig;
|
||||
class ContentPage;
|
||||
class ContentArticle;
|
||||
class TemplatingEngine;
|
||||
|
||||
class WebsiteGenerator
|
||||
{
|
||||
public:
|
||||
WebsiteGenerator();
|
||||
|
||||
~WebsiteGenerator();
|
||||
|
||||
void doSubstitutions();
|
||||
|
||||
void findProject(const std::string& searchPath);
|
||||
|
||||
void readConfig();
|
||||
|
||||
void parseContentFiles();
|
||||
|
||||
void parseTemplateFiles();
|
||||
|
||||
void doSubstitutions();
|
||||
void readConfig();
|
||||
|
||||
void write();
|
||||
|
||||
private:
|
||||
|
||||
void findContentFiles();
|
||||
|
||||
Path getContentPath() const;
|
||||
|
@ -78,8 +44,10 @@ private:
|
|||
Path getConfigPath() const;
|
||||
|
||||
std::filesystem::path mProjectPath;
|
||||
GeneratorConfig mConfig;
|
||||
std::vector<ContentPage> mPages;
|
||||
std::vector<ContentArticle> mArticles;
|
||||
std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
|
||||
|
||||
std::unique_ptr<SiteGeneratorConfig> mConfig;
|
||||
std::unique_ptr<TemplatingEngine> mTemplateEngine;
|
||||
|
||||
std::vector<std::unique_ptr<ContentPage> > mPages;
|
||||
std::vector<std::unique_ptr<ContentArticle> > mArticles;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "WebsiteGenerator.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
auto args = CommandLineArgs::Create();
|
||||
|
@ -21,10 +20,10 @@ int main(int argc, char *argv[])
|
|||
generator.parseTemplateFiles();
|
||||
|
||||
// Substitute template files
|
||||
generator.doSubstitutions();
|
||||
|
||||
// Write output
|
||||
generator.write();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue