Skip to content

Commit

Permalink
Merge pull request #82 from NuiCpp/inline_js
Browse files Browse the repository at this point in the history
Inline js
  • Loading branch information
5cript authored Oct 19, 2023
2 parents 34518df + 650cc4b commit c205237
Show file tree
Hide file tree
Showing 24 changed files with 1,684 additions and 33 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(NUI_SOURCE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "NUI source di
include(${CMAKE_CURRENT_LIST_DIR}/cmake/common.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/options.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/static_analyzers.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/inline_extractor.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/dependencies/boostpp.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/dependencies/mplex.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/dependencies/interval_tree.cmake)
Expand All @@ -30,6 +31,8 @@ else()
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools/patch_acorn)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools/parcel_adapter)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools/patch_emscripten_config)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools/inline_parser)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools/inline_injector)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/backend/common.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/backend/emscripten.cmake)
endif()
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ Tested on:
- Ubuntu 20 (using clang15 and libstdc++10) ![example workflow](https://github.com/NuiCpp/Nui/actions/workflows/ubuntu_20.yml/badge.svg)
- Ubuntu 22 (using clang14 and libstdc++12) ![example workflow](https://github.com/NuiCpp/Nui/actions/workflows/ubuntu_22.yml/badge.svg)
- Windows (using clang16 and current msys libstdc++) ![example workflow](https://github.com/NuiCpp/Nui/actions/workflows/windows.yml/badge.svg)
- MacOS: Currently In Progress: https://github.com/NuiCpp/Nui/tree/macos

Not automatically tested but should work:
- Arch Linux
- Other Linux Distributions with new enough packages for C++20

Planed:
- Mac OS
- Android if this gets more traction here: https://github.com/webview/webview/issues/871

## Examples
Expand All @@ -44,4 +44,4 @@ Nui uses the following dependencies:
- 5cript/mplex https://github.com/5cript/mplex - [MIT LICENSE](https://github.com/5cript/mplex/blob/master/LICENSE)
- nlohmann/json https://github.com/nlohmann/json - [MIT LICENSE](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT)
- portable-file-dialogs https://github.com/samhocevar/portable-file-dialogs - [WTFPL LICENSE](https://github.com/samhocevar/portable-file-dialogs/blob/main/README.md)
- 5cript/roar https://github.com/5cript/roar - [BSL-1.0 LICENSE](https://github.com/5cript/roar/blob/master/LICENSE)
- 5cript/roar https://github.com/5cript/roar - [BSL-1.0 LICENSE](https://github.com/5cript/roar/blob/master/LICENSE)
17 changes: 11 additions & 6 deletions cmake/backend/emscripten.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ else()
COMMAND "${CMAKE_BINARY_DIR}/_deps/emscripten-src/emsdk" install latest --build=Release
COMMAND "${CMAKE_BINARY_DIR}/_deps/emscripten-src/upstream/emscripten/emcc" --generate-config
COMMAND $<TARGET_FILE:patch-acorn> "${CMAKE_BINARY_DIR}/_deps/emscripten-src/upstream/emscripten/tools/acorn-optimizer.js"
COMMAND $<TARGET_FILE:patch-emscripten-config>
"${CMAKE_BINARY_DIR}/_deps/emscripten-src/upstream/emscripten/.emscripten"
"${CMAKE_BINARY_DIR}/_deps/binaryen_release-src"
COMMAND $<TARGET_FILE:patch-emscripten-config>
"${CMAKE_BINARY_DIR}/_deps/emscripten-src/upstream/emscripten/.emscripten"
"${CMAKE_BINARY_DIR}/_deps/binaryen_release-src"
"${CMAKE_BINARY_DIR}/_deps/emscripten-src/java/bin/java.exe"
# not setting node, because global installed node might be preferred
# "${CMAKE_BINARY_DIR}/_deps/emscripten-src/node/bin/node.exe"
Expand Down Expand Up @@ -106,9 +106,11 @@ function(nui_add_emscripten_target)
"${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten"
SOURCE_DIR "${SOURCE_DIR}"
# emscripten cmake with passed down Release/Debug build type
CONFIGURE_COMMAND
CONFIGURE_COMMAND
${EMCMAKE} cmake
${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_CMAKE_OPTIONS}
-DNUI_INLINE_EXTRACTOR_TARGET_FILE=$<TARGET_FILE:inline-parser>
-DNUI_INLINE_INJECTOR_TARGET_FILE=$<TARGET_FILE:inline-injector>
"${SOURCE_DIR}"
# copy over package.json and fill parcel options that do not exist on it
${BUILD_COMMAND}
Expand All @@ -120,6 +122,7 @@ function(nui_add_emscripten_target)
BUILD_ALWAYS 1
BUILD_BYPRODUCTS "${CMAKE_BINARY_DIR}/module_${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}/bin/index.html"
INSTALL_COMMAND ""
DEPENDS inline-parser inline-injector parcel-adapter
)
add_dependencies(${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET} ${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten)
add_dependencies(${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten bin2hpp)
Expand All @@ -128,8 +131,10 @@ function(nui_add_emscripten_target)
DEPENDS ${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_PREJS}
)
add_custom_target(
${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-parcel-dep
DEPENDS ${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten
${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-parcel-dep
DEPENDS
${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten
"${CMAKE_BINARY_DIR}/module_${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}/bin/index.html"
)
add_dependencies(
${NUI_ADD_EMSCRIPTEN_TARGET_ARGS_TARGET}-emscripten
Expand Down
23 changes: 16 additions & 7 deletions cmake/frontend/emscripten.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function(nui_prepare_emscripten_target)
cmake_parse_arguments(
NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS
""
"NO_INLINE;NO_INLINE_INJECT"
"TARGET;PREJS;STATIC;UNPACKED_MODE"
"EMSCRIPTEN_LINK_OPTIONS;EMSCRIPTEN_COMPILE_OPTIONS;PARCEL_ARGS"
${ARGN}
Expand All @@ -14,26 +14,35 @@ function(nui_prepare_emscripten_target)

nui_set_target_output_directories(${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET})

if (NOT NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_UNPACKED_MODE)
set(NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_UNPACKED_MODE off)
endif()

set(INLINER_COMMAND "")
if (NOT NO_INLINE)
nui_enable_inline(TARGET ${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET})
if (NOT NO_INLINE_INJECT)
set(INLINER_COMMAND COMMAND ${NUI_INLINE_INJECTOR_TARGET_FILE} "${CMAKE_BINARY_DIR}/static/index.html" "${CMAKE_BINARY_DIR}/nui-inline/inline_imports.js" "${CMAKE_BINARY_DIR}/nui-inline/inline_imports.css")
endif()
endif()

add_custom_target(
${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET}-npm-install
${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET}-npm-install
COMMAND npm install
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

add_custom_target(
${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET}-parcel
${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_TARGET}-parcel
COMMAND ${CMAKE_COMMAND} -E copy_directory "${NUI_SOURCE_DIRECTORY}/nui/js" "${CMAKE_BINARY_DIR}/nui-js"
COMMAND ${CMAKE_COMMAND} -E copy_directory ${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_STATIC} "${CMAKE_BINARY_DIR}/static"
${INLINER_COMMAND}
COMMAND "${CMAKE_BINARY_DIR}/node_modules/.bin/parcel" build --dist-dir "${CMAKE_BINARY_DIR}/bin" ${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_PARCEL_ARGS}
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
BYPRODUCTS "${CMAKE_BINARY_DIR}/bin/index.html"
DEPENDS "${CMAKE_BINARY_DIR}/bin/index.js"
)

if (NOT NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_UNPACKED_MODE)
set(NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_UNPACKED_MODE off)
endif()

if (${NUI_PREPARE_EMSCRIPTEN_TARGET_ARGS_UNPACKED_MODE})
set(SINGLE_FILE_STRING "")
add_custom_target(
Expand Down
96 changes: 96 additions & 0 deletions cmake/inline_extractor.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
function(nui_preprocess_inline_js)
set(one_value_args TARGET SOURCE OUTPUT DIRECTORY INLINE_CACHE DEPENDS IS_FIRST)
set(multi_value_args EXTRA_CXX_FLAGS)
cmake_parse_arguments(CPP "" "${one_value_args}" "${multi_value_args}" ${ARGN})

string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)
string(REPLACE " " ";" c_flags "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${build_type}}")

add_custom_command(
OUTPUT ${CPP_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CPP_DIRECTORY}
COMMAND ${CMAKE_CXX_COMPILER}
#"-D$<JOIN:$<TARGET_PROPERTY:${CPP_TARGET},COMPILE_DEFINITIONS>,;-D>"
"-I$<JOIN:$<TARGET_PROPERTY:${CPP_TARGET},INCLUDE_DIRECTORIES>,;-I>"
${c_flags}
$<TARGET_PROPERTY:${CPP_TARGET},COMPILE_OPTIONS>
${CPP_EXTRA_CXX_FLAGS}
-E ${CPP_SOURCE} -o ${CPP_OUTPUT}
# COMMAND "${NUI_INLINE_EXTRACTOR_TARGET_FILE}" ${CPP_INLINE_CACHE} ${CPP_OUTPUT} ${CPP_IS_FIRST}
COMMAND_EXPAND_LISTS VERBATIM
IMPLICIT_DEPENDS C ${CPP_SOURCE}
DEPENDS ${CPP_SOURCE} ${CPP_DEPENDS})
endfunction()

function(nui_enable_inline)
set(one_value_args TARGET UNPACKED_MODE)
set(multi_value_args)
cmake_parse_arguments(nui_enable_inline_ARGS "" "${one_value_args}" "${multi_value_args}" ${ARGN})

get_target_property(INLINE_JS_SOURCES ${nui_enable_inline_ARGS_TARGET} SOURCES)

set(INLINE_DIRECTORY_SUBDIR "nui-inline")
set(INLINE_DIRECTORY "${CMAKE_BINARY_DIR}/${INLINE_DIRECTORY_SUBDIR}")
set(INLINE_CACHE "${INLINE_DIRECTORY}/inline.cache")
set(INLINE_IMPORTS_SCRIPTS "${INLINE_DIRECTORY}/inline_imports.js")
set(INLINE_IMPORTS_STYLES "${INLINE_DIRECTORY}/inline_imports.css")

# for each source file preprocess it:
set(IS_FIRST TRUE)
foreach(source_file ${INLINE_JS_SOURCES})
get_filename_component(source_file_name "${source_file}" NAME)
set(preprocessed_source_file "${INLINE_DIRECTORY}/${source_file_name}.i")
nui_preprocess_inline_js(
TARGET ${nui_enable_inline_ARGS_TARGET}
DIRECTORY "${INLINE_DIRECTORY}"
INLINE_CACHE "${INLINE_CACHE}"
SOURCE "${source_file}"
OUTPUT "${preprocessed_source_file}"
EXTRA_CXX_FLAGS -P -CC -DNUI_INLINE -DNUI_MODULE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DNUI_MODULE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
IS_FIRST ${IS_FIRST}
)
set(IS_FIRST FALSE)
message(STATUS "Preprocessing ${source_file} to ${preprocessed_source_file}")
list(APPEND preprocessed_sources "${preprocessed_source_file}")
endforeach()

add_custom_command(
OUTPUT
${INLINE_IMPORTS_SCRIPTS}
${INLINE_IMPORTS_STYLES}
COMMAND ${CMAKE_COMMAND} -E make_directory ${INLINE_DIRECTORY}
COMMAND "${NUI_INLINE_EXTRACTOR_TARGET_FILE}" ${INLINE_CACHE} ${CMAKE_BINARY_DIR} ${INLINE_DIRECTORY_SUBDIR} ${preprocessed_sources}
COMMAND_EXPAND_LISTS VERBATIM
DEPENDS ${preprocessed_sources}
)

add_custom_target(
nui-inline-${nui_enable_inline_ARGS_TARGET}
ALL
DEPENDS ${INLINE_IMPORTS_SCRIPTS}
DEPENDS ${INLINE_IMPORTS_STYLES}
)
add_dependencies(${nui_enable_inline_ARGS_TARGET} nui-inline-${nui_enable_inline_ARGS_TARGET})

if (NOT nui_enable_inline_ARGS_UNPACKED_MODE)
set(nui_enable_inline_ARGS_UNPACKED_MODE off)
endif()

if (NOT ${nui_enable_inline_ARGS_UNPACKED_MODE})
add_custom_command(
OUTPUT
"${CMAKE_BINARY_DIR}/index_inserts.html"
COMMAND ${NUI_INLINE_INJECTOR_TARGET_FILE} "${CMAKE_BINARY_DIR}/module_${nui_enable_inline_ARGS_TARGET}/bin/index.html" ${INLINE_IMPORTS_SCRIPTS} ${INLINE_IMPORTS_STYLES}
DEPENDS
${INLINE_IMPORTS_SCRIPTS}
${INLINE_IMPORTS_STYLES}
"${CMAKE_BINARY_DIR}/module_${nui_enable_inline_ARGS_TARGET}/bin/index.html"
)

add_custom_target(
nui-inline-inject-${nui_enable_inline_ARGS_TARGET}
ALL
DEPENDS "${CMAKE_BINARY_DIR}/index_inserts.html"
)
endif()
endfunction()
35 changes: 20 additions & 15 deletions nui/include/nui/frontend/elements/impl/html_element.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,29 @@ namespace Nui
}
}

#define NUI_MAKE_HTML_ELEMENT_RENAME(NAME, HTML_ACTUAL) \
struct NAME : ::Nui::HtmlElement \
{ \
NAME(NAME const&) = default; \
NAME(NAME&&) = default; \
NAME(std::vector<::Nui::Attribute> const& attributes) \
: ::Nui::HtmlElement{HTML_ACTUAL, &::Nui::RegularHtmlElementBridge, attributes} \
{} \
NAME(std::vector<::Nui::Attribute>&& attributes) \
: ::Nui::HtmlElement{HTML_ACTUAL, &::Nui::RegularHtmlElementBridge, std::move(attributes)} \
{} \
template <typename... T> \
NAME(T&&... attributes) \
: ::Nui::HtmlElement{HTML_ACTUAL, &::Nui::RegularHtmlElementBridge, std::forward<T>(attributes)...} \
{} \
};

#define NUI_MAKE_HTML_ELEMENT(NAME) NUI_MAKE_HTML_ELEMENT_RENAME(NAME, #NAME)

#define NUI_DECLARE_HTML_ELEMENT_RENAME(NAME, HTML_ACTUAL) \
namespace Nui::Elements \
{ \
struct NAME : HtmlElement \
{ \
NAME(NAME const&) = default; \
NAME(NAME&&) = default; \
NAME(std::vector<Attribute> const& attributes) \
: HtmlElement{HTML_ACTUAL, &RegularHtmlElementBridge, attributes} \
{} \
NAME(std::vector<Attribute>&& attributes) \
: HtmlElement{HTML_ACTUAL, &RegularHtmlElementBridge, std::move(attributes)} \
{} \
template <typename... T> \
NAME(T&&... attributes) \
: HtmlElement{HTML_ACTUAL, &RegularHtmlElementBridge, std::forward<T>(attributes)...} \
{} \
}; \
NUI_MAKE_HTML_ELEMENT_RENAME(NAME, HTML_ACTUAL) \
}

#define NUI_DECLARE_HTML_ELEMENT(NAME) NUI_DECLARE_HTML_ELEMENT_RENAME(NAME, #NAME)
6 changes: 3 additions & 3 deletions nui/test/nui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ add_executable(nui-tests
engine/object.cpp
engine/array.cpp
)
target_link_libraries(nui-tests PRIVATE
nui-frontend-mocked
gtest
target_link_libraries(nui-tests PRIVATE
nui-frontend-mocked
gtest
)
gtest_discover_tests(nui-tests)

Expand Down
11 changes: 11 additions & 0 deletions tools/inline_injector/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.16)

project(inline-injector VERSION 0.1.0)

add_executable(inline-injector main.cpp)
target_compile_features(inline-injector PRIVATE cxx_std_20)

set_target_properties(inline-injector
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools_bin"
)
86 changes: 86 additions & 0 deletions tools/inline_injector/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <iostream>
#include <fstream>
#include <string>
#include <filesystem>

std::string readFile(const std::filesystem::path& path)
{
std::ifstream file{path, std::ios_base::binary};
if (!file)
{
throw std::runtime_error{"Could not open " + path.string()};
}

file.seekg(0, std::ios::end);
std::string content;
content.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&content[0], content.size());
return content;
}

int main(int argc, char** argv)
{
if (argc != 4)
{
std::cout << "Expected 3 argument: <index.html> <import_scripts> <import_styles>, but got " << argc - 1 << "\n";
return 1;
}

const auto index = std::filesystem::path{argv[1]};
const auto importScripts = std::filesystem::path{argv[2]};
const auto importStyles = std::filesystem::path{argv[3]};

std::string indexHtml;
try
{
indexHtml = readFile(index);
}
catch (const std::exception& e)
{
std::cout << "Error reading file: " << e.what() << "\n";
return 1;
}

// make relative path of import scripts to index file:
const auto relativeImportScriptsFile = std::filesystem::relative(importScripts, index.parent_path());
const auto relativeImportStylesFile = std::filesystem::relative(importStyles, index.parent_path());
const auto binIndex =
std::filesystem::relative(index.parent_path() / ".." / "bin" / "index.js", index.parent_path());

const std::string importScriptsHtml = "\t<script type=\"module\" defer>\n\t\timport \"" +
relativeImportScriptsFile.generic_string() + "\";\n</script>\n";
const std::string importStylesHtml =
"\t<style>\n\t\t@import \"" + relativeImportStylesFile.generic_string() + "\";\n</style>\n";
const std::string importBinIndexHtml =
"\t<script type=\"module\" defer>\n\t\timport \"" + binIndex.generic_string() + "\";\n</script>\n";

// find end of header </head> from behind in indexHtml:
const auto headEnd = indexHtml.rfind("</head>");
if (headEnd == std::string::npos)
{
std::cout << "Could not find </head> in " << index << "\n";
return 1;
}

// insert importScriptsHtml before headEnd:
indexHtml.insert(headEnd, importScriptsHtml);

// insert importStylesHtml before headEnd:
indexHtml.insert(headEnd, importStylesHtml);

// insert importBinIndexHtml before headEnd:
if (indexHtml.find(binIndex.generic_string()) == std::string::npos)
indexHtml.insert(headEnd, importBinIndexHtml);

// write indexHtml back to index file:
std::ofstream file{index, std::ios_base::binary};
if (!file)
{
std::cout << "Could not open " << index << " for writing\n";
return 1;
}

file.write(indexHtml.data(), indexHtml.size());
return 0;
}
Loading

0 comments on commit c205237

Please sign in to comment.