0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-25 09:43:03 +00:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Víctor Losada Hernández
efe67d2092 update build folder end 2026-01-24 01:24:31 +01:00
Víctor Losada Hernández
4e6c3add5b change imports 2026-01-24 01:10:51 +01:00
Víctor Losada Hernández
852510c93a more imports 2026-01-24 00:49:37 +01:00
Víctor Losada Hernández
3c3d29739a Merge branch 'master' of https://github.com/naturalcrit/homebrewery into vitreum-to-vite 2026-01-24 00:39:57 +01:00
Víctor Losada Hernández
eda1fb29c6 start changing imports 2026-01-24 00:04:32 +01:00
Víctor Losada Hernández
44b8ff8cd2 Merge branch 'master' of https://github.com/naturalcrit/homebrewery into vitreum-to-vite 2026-01-23 20:43:23 +01:00
Víctor Losada Hernández
0b5f039337 path changes 2026-01-23 19:07:24 +01:00
Víctor Losada Hernández
54b0dd46f8 vitreum headtags (temporary change) 2026-01-23 18:51:01 +01:00
Víctor Losada Hernández
d6b763e62d update import paths 2026-01-23 18:44:51 +01:00
Víctor Losada Hernández
dbeff56ffa change legacy snippets to use named exports and move from dedent-tabs to dedent 2026-01-23 17:38:16 +01:00
Víctor Losada Hernández
b72e5b9172 fix file imports 2026-01-23 17:18:03 +01:00
Víctor Losada Hernández
eb9fc4487c fix exports of admin and homebrew.jsx 2026-01-22 23:46:50 +01:00
Víctor Losada Hernández
c4c2d8cabd fix core import 2026-01-22 23:46:10 +01:00
Víctor Losada Hernández
14a2e3f8fd Merge branch 'master' of https://github.com/naturalcrit/homebrewery into vitreum-to-vite 2026-01-22 22:38:18 +01:00
Víctor Losada Hernández
9b93be7e23 first stage, unfinished 2026-01-20 19:40:52 +01:00
Víctor Losada Hernández
9fe918e2f0 vite config 2026-01-20 19:39:28 +01:00
52 changed files with 1339 additions and 3292 deletions

View File

@@ -49,4 +49,4 @@ const Admin = ()=>{
);
};
module.exports = Admin;
export default Admin;

View File

@@ -1,12 +1,10 @@
@import 'naturalcrit/styles/reset.less';
@import 'naturalcrit/styles/elements.less';
@import 'naturalcrit/styles/animations.less';
@import 'naturalcrit/styles/colors.less';
@import 'naturalcrit/styles/tooltip.less';
@import './shared/naturalcrit/styles/reset.less';
@import './shared/naturalcrit/styles/elements.less';
@import './shared/naturalcrit/styles/animations.less';
@import './shared/naturalcrit/styles/colors.less';
@import './shared/naturalcrit/styles/tooltip.less';
@import './themes/fonts/iconFonts/fontAwesome.less';
@import 'font-awesome/css/font-awesome.css';
html,body, #reactContainer, .naturalCrit { min-height : 100%; }
@sidebarWidth : 250px;

View File

@@ -1,3 +1,5 @@
@import '../../../shared/naturalcrit/styles/colors.less';
.brewUtil {
.result {
margin-top : 20px;

View File

@@ -1,7 +1,7 @@
import diceFont from 'themes/fonts/iconFonts/diceFont.js';
import elderberryInn from 'themes/fonts/iconFonts/elderberryInn.js';
import fontAwesome from 'themes/fonts/iconFonts/fontAwesome.js';
import gameIcons from 'themes/fonts/iconFonts/gameIcons.js';
import diceFont from '../../../themes/fonts/iconFonts/diceFont.js';
import elderberryInn from '../../../themes/fonts/iconFonts/elderberryInn.js';
import fontAwesome from '../../../themes/fonts/iconFonts/fontAwesome.js';
import gameIcons from '../../../themes/fonts/iconFonts/gameIcons.js';
const emojis = {
...diceFont,

View File

@@ -1,3 +1,5 @@
@import './shared/naturalcrit/styles/colors.less';
.renderWarnings {
position : relative;
float : right;

View File

@@ -1,3 +1,4 @@
import './splitPane.less';
import React, { useEffect, useState } from 'react';

View File

@@ -1,3 +1,4 @@
@import './shared/naturalcrit/styles/core.less';
.splitPane {
position : relative;

View File

@@ -16,7 +16,7 @@ import dedent from 'dedent';
import { printCurrentBrew } from '../../../shared/helpers.js';
import HeaderNav from './headerNav/headerNav.jsx';
import { safeHTML } from './safeHTML.js';
import safeHTML from './safeHTML.js';
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
const PAGEBREAK_REGEX_LEGACY = /\\page(?:break)?/m;

View File

@@ -1,3 +1,4 @@
@import './shared/naturalcrit/styles/colors.less';
.errorBar {
position : absolute;

View File

@@ -1,7 +1,7 @@
import './notificationPopup.less';
import React, { useEffect, useState } from 'react';
import request from '../../utils/request-middleware.js';
import Markdown from 'markdown.js';
import Markdown from '../../../../shared/markdown.js';
import Dialog from '../../../components/dialog.jsx';

View File

@@ -1,3 +1,5 @@
@import './client/homebrew/navbar/navbar.less';
.popups {
position : fixed;
top : calc(@navbarHeight + @viewerToolsHeight);

View File

@@ -43,4 +43,4 @@ function safeHTML(htmlString) {
return div.innerHTML;
};
module.exports.safeHTML = safeHTML;
export default safeHTML;

View File

@@ -1,4 +1,6 @@
@import 'themes/codeMirror/customEditorStyles.less';
@import './shared/naturalcrit/styles/core.less';
@import './themes/codeMirror/customEditorStyles.less';
.editor {
position : relative;
width : 100%;

View File

@@ -7,7 +7,8 @@ import request from '../../utils/request-middleware.js';
import Combobox from '../../../components/combobox.jsx';
import TagInput from '../tagInput/tagInput.jsx';
import Themes from 'themes/themes.json';
import Themes from '../../../../themes/themes.json';
import validations from './validations.js';
const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder'];

View File

@@ -1,4 +1,4 @@
@import 'naturalcrit/styles/colors.less';
@import './shared/naturalcrit/styles/core.less';
.userThemeName {
padding-right : 10px;

View File

@@ -9,11 +9,11 @@ import cx from 'classnames';
import { loadHistory } from '../../utils/versionHistory.js';
import { brewSnippetsToJSON } from '../../../../shared/helpers.js';
import Legacy5ePHB from 'themes/Legacy/5ePHB/snippets.js';
import V3_5ePHB from 'themes/V3/5ePHB/snippets.js';
import V3_5eDMG from 'themes/V3/5eDMG/snippets.js';
import V3_Journal from 'themes/V3/Journal/snippets.js';
import V3_Blank from 'themes/V3/Blank/snippets.js';
import Legacy5ePHB from '../../../../themes/Legacy/5ePHB/snippets.js';
import V3_5ePHB from '../../../../themes/V3/5ePHB/snippets.js';
import V3_5eDMG from '../../../../themes/V3/5eDMG/snippets.js';
import V3_Journal from '../../../../themes/V3/Journal/snippets.js';
import V3_Blank from '../../../../themes/V3/Blank/snippets.js';
const ThemeSnippets = {
Legacy_5ePHB : Legacy5ePHB,
@@ -23,7 +23,7 @@ const ThemeSnippets = {
V3_Blank : V3_Blank,
};
import EditorThemes from 'build/homebrew/codeMirror/editorThemes.json';
import EditorThemes from '../../../../build/homebrew/codeMirror/editorThemes.json';
const execute = function(val, props){
if(_.isFunction(val)) return val(props);

View File

@@ -1,3 +1,4 @@
@import './shared/naturalcrit/styles/core.less';
@import (less) './client/icons/customIcons.less';
@import (less) '././././themes/fonts/5e/fonts.less';

View File

@@ -46,8 +46,8 @@ const Homebrew = (props)=>{
global.config = config;
const backgroundObject = ()=>{
if(global.config.deployment || (config.local && config.development)){
const bgText = global.config.deployment || 'Local';
if(global.config?.deployment || (config?.local && config?.development)){
const bgText = global.config?.deployment || 'Local';
return {
backgroundImage : `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='100px' width='200px'><text x='0' y='15' fill='%23fff7' font-size='20'>${bgText}</text></svg>")`
};
@@ -58,7 +58,7 @@ const Homebrew = (props)=>{
return (
<Router location={url}>
<div className={`homebrew${(config.deployment || config.local) ? ' deployment' : ''}`} style={backgroundObject()}>
<div className={`homebrew${(config?.deployment || config?.local) ? ' deployment' : ''}`} style={backgroundObject()}>
<Routes>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={brew} userThemes={userThemes}/>} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={brew} />} />
@@ -80,4 +80,4 @@ const Homebrew = (props)=>{
);
};
module.exports = Homebrew;
export default Homebrew;

View File

@@ -1,4 +1,4 @@
@import 'naturalcrit/styles/core.less';
@import './shared/naturalcrit/styles/core.less';
.homebrew {
height : 100%;
background-color:@steel;

View File

@@ -97,7 +97,7 @@ const Account = createReactClass({
// Logged out
// LOCAL ONLY
if(global.config.local) {
if(global.config?.local) {
return <Nav.item color='teal' icon='fas fa-sign-in-alt' onClick={this.localLogin}>
login
</Nav.item>;

View File

@@ -1,3 +1,5 @@
@import './shared/naturalcrit/styles/core.less';
.navItem.error {
position : relative;
background-color : @red;

View File

@@ -7,17 +7,11 @@ import PatreonNavItem from './patreon.navitem.jsx';
const Navbar = createReactClass({
displayName : 'Navbar',
getInitialState : function() {
return {
//showNonChromeWarning : false,
ver : '0.0.0'
};
},
getInitialState : function() {
return {
ver : global.version
};
getInitialState: function() {
return {
// showNonChromeWarning: false, // uncomment if needed
ver: global.version || '0.0.0'
};
},
/*

View File

@@ -1,4 +1,4 @@
@import 'naturalcrit/styles/colors.less';
@import './shared/naturalcrit/styles/core.less';
@navbarHeight : 28px;
@viewerToolsHeight : 32px;

View File

@@ -1,3 +1,4 @@
@import './shared/naturalcrit/styles/core.less';
.brewItem {
position : relative;

View File

@@ -26,7 +26,7 @@ import RecentNavItems from '../../navbar/recent.navitem.jsx';
const { both: RecentNavItem } = RecentNavItems;
// Page specific imports
import { Meta } from 'vitreum/headtags';
import Meta from '../../../../vitreum/headtags.js';
import { md5 } from 'hash-wasm';
import { gzipSync, strToU8 } from 'fflate';
import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';

View File

@@ -27,7 +27,7 @@ const { both: RecentNavItem } = RecentNavItems;
// Page specific imports
import { Meta } from 'vitreum/headtags';
import Meta from '../../../../vitreum/headtags.js';
const BREWKEY = 'homebrewery-new';
const STYLEKEY = 'homebrewery-new-style';

View File

@@ -1,3 +1,5 @@
@import './shared/naturalcrit/styles/core.less';
.homePage {
position : relative;
a.floatingNewButton {

View File

@@ -26,7 +26,7 @@ import RecentNavItems from '../../navbar/recent.navitem.jsx';
const { both: RecentNavItem } = RecentNavItems;
// Page specific imports
import { Meta } from 'vitreum/headtags';
import { Meta } from '../../../../vitreum/headtags.js';
const BREWKEY = 'HB_newPage_content';
const STYLEKEY = 'HB_newPage_style';

View File

@@ -1,3 +1,5 @@
@import './shared/naturalcrit/styles/colors.less';
.newPage {
.navItem.save {
background-color : @orange;

View File

@@ -1,6 +1,6 @@
import './sharePage.less';
import React, { useState, useEffect, useCallback } from 'react';
import { Meta } from 'vitreum/headtags';
import Meta from '../../../../vitreum/headtags.js';
import Nav from '../../navbar/nav.jsx';
import Navbar from '../../navbar/navbar.jsx';

View File

@@ -1,3 +1,5 @@
@import './shared/naturalcrit/styles/core.less';
.vaultPage {
height : 100%;
overflow-y : hidden;

4274
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,10 @@
"url": "git://github.com/naturalcrit/homebrewery.git"
},
"scripts": {
"viteDev": "node scripts/dev.js",
"viteDevAdmin": "vite --config vite.config.js --ssr client/admin/admin.jsx",
"viteBuild": "vite build",
"viteStart": "vite preview --outDir build",
"dev": "node --experimental-require-module scripts/dev.js",
"quick": "node --experimental-require-module scripts/quick.js",
"build": "node --experimental-require-module scripts/buildHomebrew.js && node --experimental-require-module scripts/buildAdmin.js",
@@ -96,6 +100,7 @@
"@dmsnell/diff-match-patch": "^1.1.0",
"@googleapis/drive": "^19.2.0",
"@sanity/diff-match-patch": "^3.2.0",
"@vitejs/plugin-react": "^5.1.2",
"body-parser": "^2.2.0",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -114,7 +119,7 @@
"idb-keyval": "^6.2.2",
"js-yaml": "^4.1.1",
"jwt-simple": "^0.5.6",
"less": "^3.13.1",
"less": "^4.5.1",
"lodash": "^4.17.21",
"marked": "15.0.12",
"marked-alignment-paragraphs": "^1.0.0",
@@ -138,7 +143,7 @@
"romans": "^3.1.0",
"sanitize-filename": "1.6.3",
"superagent": "^10.2.1",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git",
"vite": "^7.3.1",
"written-number": "^0.11.1"
},
"devDependencies": {

View File

@@ -1,22 +1,44 @@
const label = 'dev';
console.time(label);
import express from "express";
import { createServer as createViteServer } from "vite";
import path from "path";
import url from "url";
import template from "../client/template.js";
const jsx = require('vitreum/steps/jsx.watch.js');
const less = require('vitreum/steps/less.watch.js');
const assets = require('vitreum/steps/assets.watch.js');
const server = require('vitreum/steps/server.watch.js');
const livereload = require('vitreum/steps/livereload.js');
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const app = express();
const Proj = require('./project.json');
async function start() {
const vite = await createViteServer({
server: { middlewareMode: true },
root: __dirname,
appType: "custom",
});
Promise.resolve()
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', { libs: Proj.libs, shared: ['./shared'] }))
.then((deps)=>less('homebrew', { shared: ['./shared'] }, deps))
.then(()=>jsx('admin', './client/admin/admin.jsx', { libs: Proj.libs, shared: ['./shared'] }))
.then((deps)=>less('admin', { shared: ['./shared'] }, deps))
app.use(vite.middlewares);
app.use("/assets", express.static(path.resolve(__dirname, "/client/assets")));
.then(()=>assets(Proj.assets, ['./shared', './client']))
.then(()=>livereload())
.then(()=>server('./server.js', ['server']))
.then(console.timeEnd.bind(console, label))
.catch(console.error);
app.use(/(.*)/, async (req, res, next) => {
try {
const parsed = url.parse(req.url);
const pathname = parsed.pathname || "/";
// Ignore vite HMR or ping requests
if (pathname.startsWith("/__vite")) return next();
const entry = pathname.startsWith("/admin") ? "admin" : "homebrew";
const ssrModule = await vite.ssrLoadModule(`client/${entry}/${entry}.jsx`);
const html = await template(entry, "", { path: pathname, ssrModule });
res.status(200).set({ "Content-Type": "text/html" }).end(html);
} catch (e) {
vite.ssrFixStacktrace(e);
console.error(e);
res.status(500).end(e.message);
}
});
app.listen(8000, () => console.log("Dev server running on http://localhost:8000"));
}
start();

View File

@@ -110,9 +110,9 @@ app.use(homebrewApi);
app.use(adminApi);
app.use(vaultApi);
const welcomeText = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const welcomeTextLegacy = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
const migrateText = fs.readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8');
const welcomeText = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const welcomeTextLegacy = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
const migrateText = fs.readFileSync('./client/homebrew/pages/homePage/migrate.md', 'utf8');
const changelogText = fs.readFileSync('changelog.md', 'utf8');
const faqText = fs.readFileSync('faq.md', 'utf8');

View File

@@ -1,9 +1,9 @@
@import 'naturalcrit/styles/reset.less';
//@import 'naturalcrit/styles/elements.less';
@import 'naturalcrit/styles/animations.less';
@import 'naturalcrit/styles/colors.less';
@import 'naturalcrit/styles/tooltip.less';
@import './reset.less';
//@import './elements.less';
@import './animations.less';
@import './colors.less';
@import './tooltip.less';
@font-face {
font-family : 'CodeLight';
src : data-uri('naturalcrit/styles/CODE Light.otf') format('opentype');

View File

@@ -1,6 +1,6 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
test('Processes the markdown within an HTML block if its just a class wrapper', function() {
const source = '<div>*Bold text*</div>';

View File

@@ -1,6 +1,6 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
describe('Inline Definition Lists', ()=>{
test('No Term 1 Definition', function() {

View File

@@ -1,4 +1,4 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
import dedent from 'dedent';
// Marked.js adds line returns after closing tags on some default tokens.

View File

@@ -1,6 +1,6 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
describe('Hard Breaks', ()=>{
test('Single Break', function() {

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
import dedent from 'dedent';
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
// Marked.js adds line returns after closing tags on some default tokens.
// This removes those line returns for comparison sake.

View File

@@ -1,6 +1,6 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
describe('Non-Breaking Spaces Interactions', ()=>{
test('I am actually a single-line definition list!', function() {

View File

@@ -1,6 +1,6 @@
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
describe('Justification', ()=>{
test('Left Justify', function() {

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
import dedent from 'dedent';
import Markdown from 'markdown.js';
import Markdown from './shared/markdown.js';
// Marked.js adds line returns after closing tags on some default tokens.
// This removes those line returns for comparison sake.

View File

@@ -1,12 +1,12 @@
/* eslint-disable max-lines */
import MagicGen from './snippets/magic.gen.js';
import MagicGen from './snippets/magic.gen.js';
import ClassTableGen from './snippets/classtable.gen.js';
import MonsterBlockGen from './snippets/monsterblock.gen.js';
import ClassFeatureGen from './snippets/classfeature.gen.js';
import CoverPageGen from './snippets/coverpage.gen.js';
import TableOfContentsGen from './snippets/tableOfContents.gen.js';
import dedent from 'dedent';
import dedent from 'dedent';
export default [

View File

@@ -1,6 +1,6 @@
import _ from 'lodash';
export default function(classname){
function classFeatureGen(classname) {
classname = _.sample(['archivist', 'fancyman', 'linguist', 'fletcher',
'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge']);
@@ -49,4 +49,6 @@ export default function(classname){
`- ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])}`,
'\n\n\n'
].join('\n');
};
}
export default classFeatureGen;

View File

@@ -98,8 +98,8 @@ const subtitles = [
];
export default ()=>{
return `<style>
function coverPageGen() {
return `<style>
.phb#p1{ text-align:center; }
.phb#p1:after{ display:none; }
</style>
@@ -114,4 +114,6 @@ export default ()=>{
</div>
\\page`;
};
}
export default coverPageGen;

View File

@@ -4,7 +4,7 @@ import ClassFeatureGen from './classfeature.gen.js';
import ClassTableGen from './classtable.gen.js';
export default function(){
function fullClassGen(){
const classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']);
@@ -40,4 +40,6 @@ export default function(){
].join('\n')}\n\n\n`;
};
}
export default fullClassGen;

View File

@@ -47,7 +47,8 @@ const getTOC = (pages)=>{
return res;
};
export default function(props){
function tableOfContentsGen(props){
const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
@@ -69,4 +70,6 @@ export default function(props){
##### Table Of Contents
${markdown}
</div>\n`;
};
}
export default tableOfContentsGen;

29
vite.config.js Normal file
View File

@@ -0,0 +1,29 @@
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
build: {
outDir: "build",
emptyOutDir: true,
ssrManifest: true,
rollupOptions: {
input: {
admin: path.resolve(__dirname, "client/admin/admin.jsx"),
homebrew: path.resolve(__dirname, "client/homebrew/homebrew.jsx"),
},
output: {
entryFileNames: "[name]/bundle.js",
chunkFileNames: "[name]/[name]-[hash].js",
assetFileNames: "[name]/[name].[ext]",
},
},
},
server: {
fs: {
allow: ["."],
},
},
});

84
vitreum/headtags.js Normal file
View File

@@ -0,0 +1,84 @@
import React, { useEffect } from "react";
import injectTag from "./injectTag.js";
const obj2props = (obj) =>
Object.entries(obj)
.map(([k, v]) => `${k}="${v}"`)
.join(" ");
const toStr = (chld) => (Array.isArray(chld) ? chld.join("") : chld);
const onServer = typeof window === "undefined";
let NamedTags = {};
let UnnamedTags = [];
export const HeadComponents = {
Title({ children }) {
if (onServer) NamedTags.title = `<title>${toStr(children)}</title>`;
useEffect(() => {
document.title = toStr(children);
}, [children]);
return null;
},
Favicon({ type = "image/png", href = "", rel = "icon", id = "favicon" }) {
if (onServer) NamedTags.favicon = `<link rel='shortcut icon' type="${type}" id="${id}" href="${href}" />`;
useEffect(() => {
document.getElementById(id).href = href;
}, [id, href]);
return null;
},
Description({ children }) {
if (onServer) NamedTags.description = `<meta name='description' content='${toStr(children)}' />`;
return null;
},
Noscript({ children }) {
if (onServer) UnnamedTags.push(`<noscript>${toStr(children)}</noscript>`);
return null;
},
Script({ children = [], ...props }) {
if (onServer) {
UnnamedTags.push(
children.length
? `<script ${obj2props(props)}>${toStr(children)}</script>`
: `<script ${obj2props(props)} />`,
);
}
return null;
},
Meta(props) {
let tag = `<meta ${obj2props(props)} />`;
props.property || props.name ? (NamedTags[props.property || props.name] = tag) : UnnamedTags.push(tag);
useEffect(() => {
document
.getElementsByTagName("head")[0]
.insertAdjacentHTML("beforeend", Object.values(NamedTags).join("\n"));
}, [NamedTags]);
return null;
},
Style({ children, type = "text/css" }) {
if (onServer) UnnamedTags.push(`<style type="${type}">${toStr(children)}</style>`);
return null;
},
};
export const Inject = ({ tag, children, ...props }) => {
useEffect(() => {
injectTag(tag, props, children);
}, []);
return null;
};
export const generate = () => Object.values(NamedTags).concat(UnnamedTags).join("\n");
export const flush = () => {
NamedTags = {};
UnnamedTags = [];
};
export const Meta = HeadComponents.Meta;
export default {
Inject,
...HeadComponents,
generate,
flush,
};

8
vitreum/injectTag.js Normal file
View File

@@ -0,0 +1,8 @@
const injectTag = (tag, props, children) => {
const injectNode = document.createElement(tag);
Object.entries(props).forEach(([key, val]) => injectNode[key] = val);
if (children) injectNode.appendChild(document.createTextNode(children));
document.getElementsByTagName('head')[0].appendChild(injectNode);
};
export default injectTag;