diff --git a/config/plugins/withImageGenerator/index.js b/config/plugins/withGenerator/index.js similarity index 93% rename from config/plugins/withImageGenerator/index.js rename to config/plugins/withGenerator/index.js index 584fd64..968befc 100644 --- a/config/plugins/withImageGenerator/index.js +++ b/config/plugins/withGenerator/index.js @@ -10,9 +10,9 @@ const withLatex = (nextConfig = {}) => { outputPath = "../../"; } config.module.rules.push({ - test: /\.png.yml$/, + test: /\.gen.yml$/, use: [{ - loader: 'file-loader', + loader: require.resolve('./webpack.js'), options: { publicPath: `${nextConfig.assetPrefix || nextConfig.basePath || ''}/_next/static/images/`, @@ -27,8 +27,6 @@ const withLatex = (nextConfig = {}) => { }, esModule: nextConfig.esModule || false, }, - }, { - loader: require.resolve('./webpack.js'), }], }); if (typeof nextConfig.webpack === "function") { diff --git a/config/plugins/withGenerator/webpack.js b/config/plugins/withGenerator/webpack.js new file mode 100644 index 0000000..2607e9b --- /dev/null +++ b/config/plugins/withGenerator/webpack.js @@ -0,0 +1,31 @@ +require('reflect-metadata'); +require('@babel/register')({ + extensions: [".es6", ".es", ".jsx", ".js", ".mjs", ".ts"], +}); +const loaderUtils = require('loader-utils'); +const path = require('path'); +const yaml = require('yaml'); +const { generate } = require('../../../src/generators'); + +module.exports = function (source) { + var callback = this.async(); + const location = this.resourcePath; + const definition = yaml.parse(source); + const options = this.getOptions(); + generate(definition, location) + .then((output) => { + const files = Object.entries(output).reduce((output, [key, value]) => { + const { name, content } = value; + const targetName = loaderUtils.interpolateName(this, `[contenthash]/${name}`, {content: content}); + const location = path.join(options.outputPath, targetName); + const publicPath = path.join(options.publicPath, targetName); + this.emitFile(location, content); + return { + ...output, + [key]: publicPath, + } + }, {}); + callback(null, `module.exports = ${JSON.stringify(files)}`); + }) + .catch(callback); +} diff --git a/config/plugins/withImageGenerator/webpack.js b/config/plugins/withImageGenerator/webpack.js deleted file mode 100644 index 7c726cf..0000000 --- a/config/plugins/withImageGenerator/webpack.js +++ /dev/null @@ -1,16 +0,0 @@ -require('reflect-metadata'); -require('@babel/register')({ - extensions: [".es6", ".es", ".jsx", ".js", ".mjs", ".ts"], -}); -const { generateImage } = require('../../../src/images'); - -module.exports = function (source) { - var callback = this.async(); - const location = this.resourcePath; - generateImage(source, location) - .then((canvas) => { - const buffer = canvas.toBuffer('image/png', {}) - callback(null, buffer); - }) - .catch(callback); -} diff --git a/config/plugins/withLatex/index.js b/config/plugins/withLatex/index.js deleted file mode 100644 index a8b7f0f..0000000 --- a/config/plugins/withLatex/index.js +++ /dev/null @@ -1,34 +0,0 @@ -const withLatex = (nextConfig = {}) => { - return Object.assign({}, nextConfig, { - webpack(config, options) { - const {isServer, dev} = options; - let outputPath = ''; - if (isServer && dev) { - outputPath = "../"; - } else if (isServer) { - outputPath = "../../"; - } - config.module.rules.push({ - test: /\.tex.yml$/, - use: [{ - loader: 'file-loader', - options: { - publicPath: `${nextConfig.assetPrefix || nextConfig.basePath || ''}/_next/static/images/`, - outputPath: `${outputPath}static/images/`, - name: "[name]-[hash].pdf", - esModule: nextConfig.esModule || false, - }, - }, { - loader: require.resolve('./webpack.js'), - }], - }); - if (typeof nextConfig.webpack === "function") { - return nextConfig.webpack(config, options); - } - - return config; - }, - }); -} - -module.exports = withLatex; diff --git a/config/plugins/withLatex/webpack.js b/config/plugins/withLatex/webpack.js deleted file mode 100644 index 9774adf..0000000 --- a/config/plugins/withLatex/webpack.js +++ /dev/null @@ -1,31 +0,0 @@ -require('reflect-metadata'); -require('@babel/register')({ - extensions: [".es6", ".es", ".jsx", ".js", ".mjs", ".ts"], -}); -const latex = require("node-latex") -var Readable = require('stream').Readable -const { generateLatex } = require('../../../src/latex'); - -module.exports = function (source) { - var callback = this.async(); - const location = this.resourcePath; - generateLatex(source, location) - .then((result) => { - const chunks = []; - const input = new Readable(); - input.push(result); - input.push(null); - const latexStream = latex(input); - latexStream.on('data', (chunk) => { - chunks.push(Buffer.from(chunk)); - }) - latexStream.on('finish', () => { - const result = Buffer.concat(chunks); - callback(null, result); - }) - latexStream.on('error', (err) => { - callback(err); - }) - }) - .catch(callback); -} diff --git a/content/articles/hiring/a4.gen.yml b/content/articles/hiring/a4.gen.yml new file mode 100644 index 0000000..25ecbfd --- /dev/null +++ b/content/articles/hiring/a4.gen.yml @@ -0,0 +1,4 @@ +data: + type: article + structure: ./index.yml +generator: latex diff --git a/content/articles/hiring/a4.tex.yml b/content/articles/hiring/a4.tex.yml deleted file mode 100644 index 02b0302..0000000 --- a/content/articles/hiring/a4.tex.yml +++ /dev/null @@ -1,3 +0,0 @@ -data: - structure: ./index.yml -generator: article diff --git a/content/articles/hiring/index.yml b/content/articles/hiring/index.yml index 53332e8..a82db6f 100644 --- a/content/articles/hiring/index.yml +++ b/content/articles/hiring/index.yml @@ -1,6 +1,6 @@ title: How to hire engineers, by an engineer cover: cover.png published: 2022-03-16 -shareImage: ./share.png.yml +shareImage: ./share.gen.yml parts: - main.md diff --git a/content/articles/hiring/share.gen.yml b/content/articles/hiring/share.gen.yml new file mode 100644 index 0000000..bc147f2 --- /dev/null +++ b/content/articles/hiring/share.gen.yml @@ -0,0 +1,5 @@ +generator: image +data: + type: share + width: 450 + height: 300 diff --git a/content/articles/hiring/share.png.yml b/content/articles/hiring/share.png.yml deleted file mode 100644 index 35f1bdb..0000000 --- a/content/articles/hiring/share.png.yml +++ /dev/null @@ -1,3 +0,0 @@ -generator: share -width: 450 -height: 300 diff --git a/content/articles/hyperconnect/a4.gen.yml b/content/articles/hyperconnect/a4.gen.yml new file mode 100644 index 0000000..d65ee0b --- /dev/null +++ b/content/articles/hyperconnect/a4.gen.yml @@ -0,0 +1,4 @@ +data: + structure: ./index.yml + type: article +generator: latex diff --git a/content/articles/hyperconnect/a4.tex.yml b/content/articles/hyperconnect/a4.tex.yml deleted file mode 100644 index 02b0302..0000000 --- a/content/articles/hyperconnect/a4.tex.yml +++ /dev/null @@ -1,3 +0,0 @@ -data: - structure: ./index.yml -generator: article diff --git a/content/articles/my-home-runs-redux/a4.gen.yml b/content/articles/my-home-runs-redux/a4.gen.yml new file mode 100644 index 0000000..e48e07b --- /dev/null +++ b/content/articles/my-home-runs-redux/a4.gen.yml @@ -0,0 +1,4 @@ +data: + type: article + structure: ./index.yml +generator: latex diff --git a/content/articles/my-home-runs-redux/a4.tex.yml b/content/articles/my-home-runs-redux/a4.tex.yml deleted file mode 100644 index 02b0302..0000000 --- a/content/articles/my-home-runs-redux/a4.tex.yml +++ /dev/null @@ -1,3 +0,0 @@ -data: - structure: ./index.yml -generator: article diff --git a/content/articles/my-home-runs-redux/index.yml b/content/articles/my-home-runs-redux/index.yml index 65627b3..cb9084b 100644 --- a/content/articles/my-home-runs-redux/index.yml +++ b/content/articles/my-home-runs-redux/index.yml @@ -1,6 +1,5 @@ title: My home runs Redux cover: cover.png published: 2022-03-15 -shareImage: ./share.png.yml parts: - main.md diff --git a/content/articles/my-home-runs-redux/share.png.yml b/content/articles/my-home-runs-redux/share.png.yml deleted file mode 100644 index 35f1bdb..0000000 --- a/content/articles/my-home-runs-redux/share.png.yml +++ /dev/null @@ -1,3 +0,0 @@ -generator: share -width: 450 -height: 300 diff --git a/content/profile/a4.gen.yml b/content/profile/a4.gen.yml new file mode 100644 index 0000000..1bea3e4 --- /dev/null +++ b/content/profile/a4.gen.yml @@ -0,0 +1,3 @@ +generator: latex +data: + type: resume diff --git a/content/profile/a4.tex.yml b/content/profile/a4.tex.yml deleted file mode 100644 index e2933ee..0000000 --- a/content/profile/a4.tex.yml +++ /dev/null @@ -1 +0,0 @@ -generator: resume diff --git a/content/profile/image.gen.yml b/content/profile/image.gen.yml new file mode 100644 index 0000000..ae3cd21 --- /dev/null +++ b/content/profile/image.gen.yml @@ -0,0 +1,7 @@ +generator: image +data: + type: resize + width: 780 + height: 780 + src: ./me.jpg + output: image/jpeg diff --git a/content/profile/index.yml b/content/profile/index.yml index 6462bd9..d130470 100644 --- a/content/profile/index.yml +++ b/content/profile/index.yml @@ -1,6 +1,7 @@ name: Morten Olsen tagline: “...One part genius, one part crazy” -avatar: me.jpg +avatar: ./image.gen.yml +resumeImage: ./me.jpg email: hello@buy-me.coffee github: morten-olsen location: Copenhagen, DK diff --git a/next.config.js b/next.config.js index 31a2c00..e9f1e39 100644 --- a/next.config.js +++ b/next.config.js @@ -1,8 +1,7 @@ const withPlugins = require("next-compose-plugins"); const withImages = require("./config/plugins/withImages.js"); -const withLatex = require('./config/plugins/withLatex'); -const withImageGenerator = require('./config/plugins/withImageGenerator'); +const withGenerator = require('./config/plugins/withGenerator'); const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true", }); @@ -11,12 +10,14 @@ const nextConfig = { poweredByHeader: false, images: { disableStaticImages: true - } + }, + experimental: { + concurrentFeatures: true, + }, }; module.exports = withPlugins([ - withLatex, - withImageGenerator, + withGenerator, [withImages,{ esModule: true, // using ES modules is beneficial in the case of module concatenation and tree shaking. inlineImageLimit: 0, // disable image inlining to data:base64 diff --git a/package.json b/package.json index 8c598f9..8c0a7cb 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ "@react-three/drei": "^8.18.6", "@react-three/fiber": "^7.0.26", "date-fns": "^2.28.0", - "next": "^12.1.0", + "next": "^12.1.2", "node-latex": "^3.1.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^17", + "react-dom": "^17", "react-markdown": "^8.0.1", "reflect-metadata": "^0.1.13", "rehype-img-size": "^1.0.1", diff --git a/src/components/hero/background.tsx b/src/components/hero/background.tsx index 2851a41..21cbc1f 100644 --- a/src/components/hero/background.tsx +++ b/src/components/hero/background.tsx @@ -36,19 +36,12 @@ const Cloud = ({ texture }) => { const ref = useRef(); const width = window.innerWidth / 2; const height = window.innerHeight / 2; - const [ready, setReady] = useState(false); useFrame(() => { - if (!ref.current || !ready) return; + if (!ref.current) return; ref.current.rotation.z -= 0.003; }); - useEffect(() => { - setTimeout(() => { - setReady(true); - }, 500) - }, []); - return ( { } export { HeroBackground }; +export default HeroBackground; diff --git a/src/components/hero/index.tsx b/src/components/hero/index.tsx index 4ff7ceb..35f9a9f 100644 --- a/src/components/hero/index.tsx +++ b/src/components/hero/index.tsx @@ -1,7 +1,6 @@ -import React from 'react'; +import React, { Suspense, useEffect, useState } from 'react'; import styled from 'styled-components'; import { Profile } from '../../data/repos/profile'; -import { HeroBackground } from './background'; import { SlideIn } from '../animations/slide-in'; type Props = { @@ -91,31 +90,44 @@ const SocialLogo = styled.div<{ src: string }>` background-size: cover; ` -const Hero: React.FC = ({ profile }) => ( - - - - - - {profile.name} - - {profile.tagline} - - - - Resumé - - - {profile.social.map((social) => ( - - - - {social.name} - +const Hero: React.FC = ({ profile }) => { + const [background, setBackground] = useState(); + useEffect( + () => { + import('./background').then(({ default: HeroBackground }) => { + console.log('goo', typeof HeroBackground); + setBackground(); + }) + }, + [], + ) + return ( + + + + + + {profile.name} + + {profile.tagline} + + + + Resumé + - ))} - - -); + {profile.social.map((social) => ( + + + + {social.name} + + + ))} + + {background} + + ); +} export { Hero }; diff --git a/src/data/assets/WebpackAssets.ts b/src/data/assets/WebpackAssets.ts index 5e79f95..b496238 100644 --- a/src/data/assets/WebpackAssets.ts +++ b/src/data/assets/WebpackAssets.ts @@ -4,7 +4,7 @@ import { AssetResolver } from './'; const assetModules = (require as any).context( '../../../content', true, - /\.(png|jpe?g|svg|gif|tex\.yml|png\.yml)$/, + /\.(png|jpe?g|svg|gif|gen\.yml)$/, ) const assets = assetModules.keys().reduce((output, key: string) => ({ ...output, diff --git a/src/data/assets/index.ts b/src/data/assets/index.ts index 258578a..c69d992 100644 --- a/src/data/assets/index.ts +++ b/src/data/assets/index.ts @@ -2,7 +2,7 @@ import { Service } from "typedi"; @Service() abstract class AssetResolver { - public abstract getPath(...loc: string[]): string; + public abstract getPath(...loc: string[]): any; } export { AssetResolver }; diff --git a/src/data/repos/articles.ts b/src/data/repos/articles.ts index add11c4..3020355 100644 --- a/src/data/repos/articles.ts +++ b/src/data/repos/articles.ts @@ -72,8 +72,8 @@ export class ArticleDB { stats, pdfs: { a4: this.#assets.getPath( - path.resolve('/', 'articles', id, 'a4.tex.yml'), - ) || null, + path.resolve('/', 'articles', id, 'a4.gen.yml'), + )?.url || null, } }; diff --git a/src/data/repos/profile.ts b/src/data/repos/profile.ts index 26e7fed..6ae6b44 100644 --- a/src/data/repos/profile.ts +++ b/src/data/repos/profile.ts @@ -18,7 +18,8 @@ export type Profile = { value: string; link: string; logo: string; - }[] + }[]; + resumeImage?: string; platforms: { name: string; level: number; @@ -50,9 +51,16 @@ export class ProfileDB { const image = this.#assets.getPath( 'profile', structure.avatar, - ) + )?.url structure.avatar = image || null; } + if (structure.resumeImage) { + const image = this.#assets.getPath( + 'profile', + structure.resumeImage, + ) + structure.resumeImage = image || null; + } structure.social = structure.social.map((social) => { const image = this.#assets.getPath('profile', social.logo); return { @@ -62,8 +70,8 @@ export class ProfileDB { }) structure.resume = this.#assets.getPath( 'profile', - 'a4.tex.yml', - ); + 'a4.gen.yml', + )?.url || null; return structure; } diff --git a/src/generators/images/index.ts b/src/generators/images/index.ts new file mode 100644 index 0000000..164fec1 --- /dev/null +++ b/src/generators/images/index.ts @@ -0,0 +1,21 @@ +import { createCanvas } from 'canvas'; +import { Generator } from '../types'; +import { generators } from './types'; + +const mimeToExt = { + 'image/jpeg': 'jpg', +} + +export const generateImage: Generator = async (data, location) => { + const { type, output, ...rest } = data; + const canvas = createCanvas(data.width, data.height) + const generator = generators[type]; + await generator(rest, location, canvas); + const ext = mimeToExt[output] || 'png'; + return { + url: { + name: `img.${ext}`, + content: canvas.toBuffer(output), + } + }; +} diff --git a/src/images/generator/index.ts b/src/generators/images/types/index.ts similarity index 57% rename from src/images/generator/index.ts rename to src/generators/images/types/index.ts index 5c01f08..0ba87b8 100644 --- a/src/images/generator/index.ts +++ b/src/generators/images/types/index.ts @@ -1,8 +1,10 @@ -import { ImageGenerator } from "./Generator"; +import { ImageGenerator } from "./types"; import { shareGenerator } from "./share"; +import { resizeGenerator } from "./resize"; const generators: {[name: string]: ImageGenerator} = { share: shareGenerator, + resize: resizeGenerator, }; export { generators }; diff --git a/src/generators/images/types/resize.ts b/src/generators/images/types/resize.ts new file mode 100644 index 0000000..4da5c1c --- /dev/null +++ b/src/generators/images/types/resize.ts @@ -0,0 +1,86 @@ + +import { loadImage, CanvasRenderingContext2D, Image, createCanvas } from 'canvas'; +import path from 'path'; +import Container from "typedi"; +import { AssetResolver } from "../../../data/assets"; +import { ArticleDB } from "../../../data/repos/articles"; +import { ImageGenerator } from "./types"; +import { ProfileDB } from '../../../data/repos/profile'; + +const assets = { + getPath: (...source: string[]) => { + return path.join(process.cwd(), 'content', ...source); + }, +} + +function drawImageProp( + ctx: CanvasRenderingContext2D, + img: Image, + x?: number, + y?: number, + w?: number, + h?: number, + offsetX?: number, + offsetY?: number, +) { + + if (arguments.length === 2) { + x = y = 0; + w = ctx.canvas.width; + h = ctx.canvas.height; + } + + // default offset is center + offsetX = typeof offsetX === "number" ? offsetX : 0.5; + offsetY = typeof offsetY === "number" ? offsetY : 0.5; + + // keep bounds [0.0, 1.0] + if (offsetX < 0) offsetX = 0; + if (offsetY < 0) offsetY = 0; + if (offsetX > 1) offsetX = 1; + if (offsetY > 1) offsetY = 1; + + var iw = img.width, + ih = img.height, + r = Math.min(w / iw, h / ih), + nw = iw * r, // new prop. width + nh = ih * r, // new prop. height + cx, cy, cw, ch, ar = 1; + + // decide which gap to fill + if (nw < w) ar = w / nw; + if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated + nw *= ar; + nh *= ar; + + // calc source rectangle + cw = iw / (nw / w); + ch = ih / (nh / h); + + cx = (iw - cw) * offsetX; + cy = (ih - ch) * offsetY; + + // make sure source rectangle is valid + if (cx < 0) cx = 0; + if (cy < 0) cy = 0; + if (cw > iw) cw = iw; + if (ch > ih) ch = ih; + + // fill image in dest. rectangle + ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h); +} + +const resizeGenerator: ImageGenerator = async (data, location, canvas) => { + const dir = path.dirname(location); + const src = path.join(dir, data.src); + Container.set(AssetResolver, assets) + const ctx = canvas.getContext('2d'); + ctx.antialias = 'subpixel'; + const cover = await loadImage(src); + drawImageProp( + ctx, + cover, + ) +}; + +export { resizeGenerator }; diff --git a/src/images/generator/share.ts b/src/generators/images/types/share.ts similarity index 92% rename from src/images/generator/share.ts rename to src/generators/images/types/share.ts index 9b2fc67..9122135 100644 --- a/src/images/generator/share.ts +++ b/src/generators/images/types/share.ts @@ -1,11 +1,10 @@ import { loadImage, CanvasRenderingContext2D, Image, createCanvas } from 'canvas'; import path from 'path'; import Container from "typedi"; -import { AssetResolver } from "../../data/assets"; -import { ArticleDB } from "../../data/repos/articles"; -import { ImageGenerator } from "./Generator"; -import drawMultiLine from 'canvas-multiline-text'; -import { ProfileDB } from '../../data/repos/profile'; +import { AssetResolver } from "../../../data/assets"; +import { ArticleDB } from "../../../data/repos/articles"; +import { ImageGenerator } from "./types"; +import { ProfileDB } from '../../../data/repos/profile'; const assets = { getPath: (...source: string[]) => { diff --git a/src/images/generator/Generator.ts b/src/generators/images/types/types.ts similarity index 100% rename from src/images/generator/Generator.ts rename to src/generators/images/types/types.ts diff --git a/src/generators/index.ts b/src/generators/index.ts new file mode 100644 index 0000000..767ccc7 --- /dev/null +++ b/src/generators/index.ts @@ -0,0 +1,16 @@ +import { Generator } from "./types"; +import { generateImage } from "./images"; +import { generateLatex } from "./latex"; + +const generators: {[name: string]: Generator} = { + image: generateImage, + latex: generateLatex, +} +const generate = async (definition: any, location: string) => { + const { data, generator } = definition; + const generatorFn = generators[generator]; + const result = await generatorFn(data, location); + return result; +}; + +export { generate }; diff --git a/src/latex/generators/Generator.ts b/src/generators/latex/generators/Generator.ts similarity index 100% rename from src/latex/generators/Generator.ts rename to src/generators/latex/generators/Generator.ts diff --git a/src/latex/generators/article/index.ts b/src/generators/latex/generators/article/index.ts similarity index 89% rename from src/latex/generators/article/index.ts rename to src/generators/latex/generators/article/index.ts index 34f76e8..816d920 100644 --- a/src/latex/generators/article/index.ts +++ b/src/generators/latex/generators/article/index.ts @@ -1,7 +1,7 @@ -import type { Article } from "../../../data/repos/articles"; +import type { Article } from "../../../../data/repos/articles"; import fs from 'fs-extra'; import { LatexGenerator } from "../Generator"; -import { generate } from '../../../data/helpers/markdown'; +import { generate } from '../../../../data/helpers/markdown'; import path from 'path'; import yaml from 'yaml'; import { fromMarkdown } from "../../helpers/convert"; diff --git a/src/latex/generators/index.ts b/src/generators/latex/generators/index.ts similarity index 100% rename from src/latex/generators/index.ts rename to src/generators/latex/generators/index.ts diff --git a/src/latex/generators/resume/index.ts b/src/generators/latex/generators/resume/index.ts similarity index 93% rename from src/latex/generators/resume/index.ts rename to src/generators/latex/generators/resume/index.ts index 462749b..5499710 100644 --- a/src/latex/generators/resume/index.ts +++ b/src/generators/latex/generators/resume/index.ts @@ -1,9 +1,9 @@ import path from 'path'; import { LatexGenerator } from "../Generator"; import Container from 'typedi'; -import { AssetResolver } from '../../../data/assets'; -import { ProfileDB } from '../../../data/repos/profile'; -import { ExperienceDB } from '../../../data/repos/experiences'; +import { AssetResolver } from '../../../../data/assets'; +import { ProfileDB } from '../../../../data/repos/profile'; +import { ExperienceDB } from '../../../../data/repos/experiences'; import { fromMarkdown } from '../../helpers/convert'; const assets = { @@ -20,9 +20,8 @@ const resume: LatexGenerator = async (data, location) => { const experienceDB = Container.get(ExperienceDB); const profile = await profileDB.get(); - const avatar = profile.avatar; + const avatar = profile.resumeImage; const experiences = await experienceDB.list(); - console.log('a', avatar); return ` \\documentclass[10pt, a4paper]{article} diff --git a/src/latex/helpers/convert.ts b/src/generators/latex/helpers/convert.ts similarity index 98% rename from src/latex/helpers/convert.ts rename to src/generators/latex/helpers/convert.ts index 8168b4f..aef62b6 100644 --- a/src/latex/helpers/convert.ts +++ b/src/generators/latex/helpers/convert.ts @@ -63,7 +63,6 @@ const renderer = (outerDepth: number) => ({ return `\\texttt{${sanitize(code)}}` }, image: (link: string) => { - console.log('link', link); if (!existsSync(link)) { return 'Online image not supported'; } diff --git a/src/generators/latex/index.ts b/src/generators/latex/index.ts new file mode 100644 index 0000000..31fbd5c --- /dev/null +++ b/src/generators/latex/index.ts @@ -0,0 +1,37 @@ +import latex from 'node-latex'; +import { Readable } from 'stream'; +import { Generator } from '../types'; +import { generators } from "./generators"; + +const latexToPdf = (doc: string) => new Promise((resolve, reject) => { + const chunks = []; + const input = new Readable(); + input.push(doc); + input.push(null); + const latexStream = latex(input); + latexStream.on('data', (chunk) => { + chunks.push(Buffer.from(chunk)); + }) + latexStream.on('finish', () => { + const result = Buffer.concat(chunks); + resolve(result); + }) + latexStream.on('error', (err) => { + reject(err); + }) +}); + +const generateLatex: Generator = async (data: any, location: string) => { + const { type, ...rest } = data; + const generator = generators[type]; + const doc = await generator(rest, location); + const content = await latexToPdf(doc); + return { + url: { + name: 'doc.pdf', + content, + } + }; +} + +export { generateLatex }; diff --git a/src/generators/types.ts b/src/generators/types.ts new file mode 100644 index 0000000..bd6af80 --- /dev/null +++ b/src/generators/types.ts @@ -0,0 +1,7 @@ +export type Generator = (data: TData, location: string) => Promise<{ + [key: string]: { + name: string; + content: string | Buffer; + } +}>; + diff --git a/src/images/index.ts b/src/images/index.ts deleted file mode 100644 index 83b8ee8..0000000 --- a/src/images/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createCanvas } from 'canvas'; -import yaml from 'yaml'; -import { generators } from './generator'; - -export const generateImage = async (source: string, location: string) => { - const definition = yaml.parse(source); - const canvas = createCanvas(definition.width * 2, definition.height * 2) - const generator = generators[definition.generator]; - await generator(definition.data, location, canvas); - return canvas; -} diff --git a/src/latex/index.ts b/src/latex/index.ts deleted file mode 100644 index 98d7e3f..0000000 --- a/src/latex/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { generators } from "./generators"; -import yaml from 'yaml'; -import path from 'path'; - -const generateLatex = async (source: string, location: string) => { - const definition = yaml.parse(source); - const generator = generators[definition.generator]; - const latex = await generator(definition.data, location); - return latex; -} - -export { generateLatex }; diff --git a/yarn.lock b/yarn.lock index 06d4813..a5e3b08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -458,65 +458,70 @@ dependencies: webpack-bundle-analyzer "4.3.0" -"@next/env@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" - integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== +"@next/env@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.2.tgz#4b0f5fd448ac60b821d2486d2987948e3a099f03" + integrity sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q== -"@next/swc-android-arm64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39" - integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA== +"@next/swc-android-arm-eabi@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz#675e952d9032ac7bec02f3f413c17d33bbd90857" + integrity sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ== -"@next/swc-darwin-arm64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135" - integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg== +"@next/swc-android-arm64@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz#d9710c50853235f258726b19a649df9c29a49682" + integrity sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ== -"@next/swc-darwin-x64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd" - integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug== +"@next/swc-darwin-arm64@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz#aadd21b711c82b3efa9b4ecf7665841259e1fa7e" + integrity sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ== -"@next/swc-linux-arm-gnueabihf@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7" - integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog== +"@next/swc-darwin-x64@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz#3b1a389828f5c88ecb828a6394692fdeaf175081" + integrity sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw== -"@next/swc-linux-arm64-gnu@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093" - integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q== +"@next/swc-linux-arm-gnueabihf@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz#db4371ca716bf94c94d4f6b001ac3c9d08d97d79" + integrity sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw== -"@next/swc-linux-arm64-musl@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566" - integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA== +"@next/swc-linux-arm64-gnu@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz#0e71db03b8b12ed315c8be7d15392ecefe562b7c" + integrity sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w== -"@next/swc-linux-x64-gnu@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e" - integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A== +"@next/swc-linux-arm64-musl@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz#f1b055793da1c12167ed3b6e32aef8289721a1fb" + integrity sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA== -"@next/swc-linux-x64-musl@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31" - integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw== +"@next/swc-linux-x64-gnu@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz#69764ffaacb3b9b373897fff15d7dd871455efe2" + integrity sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw== -"@next/swc-win32-arm64-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283" - integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw== +"@next/swc-linux-x64-musl@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz#0ddaedb5ec578c01771f83be2046dafb2f70df91" + integrity sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA== -"@next/swc-win32-ia32-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1" - integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q== +"@next/swc-win32-arm64-msvc@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz#9e17ed56d5621f8c6961193da3a0b155cea511c9" + integrity sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw== -"@next/swc-win32-x64-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064" - integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg== +"@next/swc-win32-ia32-msvc@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz#ddd260cbe8bc4002fb54415b80baccf37f8db783" + integrity sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg== + +"@next/swc-win32-x64-msvc@12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz#37412a314bcf4c6006a74e1ef9764048344f3848" + integrity sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw== "@polka/url@^1.0.0-next.20": version "1.0.0-next.21" @@ -2622,28 +2627,29 @@ next-mdx-remote@^4.0.0: vfile "^5.3.0" vfile-matter "^3.0.1" -next@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d" - integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q== +next@^12.1.2: + version "12.1.2" + resolved "https://registry.yarnpkg.com/next/-/next-12.1.2.tgz#c5376a8ae17d3e404a2b691c01f94c8943306f29" + integrity sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ== dependencies: - "@next/env" "12.1.0" + "@next/env" "12.1.2" caniuse-lite "^1.0.30001283" postcss "8.4.5" - styled-jsx "5.0.0" + styled-jsx "5.0.1" use-subscription "1.5.1" optionalDependencies: - "@next/swc-android-arm64" "12.1.0" - "@next/swc-darwin-arm64" "12.1.0" - "@next/swc-darwin-x64" "12.1.0" - "@next/swc-linux-arm-gnueabihf" "12.1.0" - "@next/swc-linux-arm64-gnu" "12.1.0" - "@next/swc-linux-arm64-musl" "12.1.0" - "@next/swc-linux-x64-gnu" "12.1.0" - "@next/swc-linux-x64-musl" "12.1.0" - "@next/swc-win32-arm64-msvc" "12.1.0" - "@next/swc-win32-ia32-msvc" "12.1.0" - "@next/swc-win32-x64-msvc" "12.1.0" + "@next/swc-android-arm-eabi" "12.1.2" + "@next/swc-android-arm64" "12.1.2" + "@next/swc-darwin-arm64" "12.1.2" + "@next/swc-darwin-x64" "12.1.2" + "@next/swc-linux-arm-gnueabihf" "12.1.2" + "@next/swc-linux-arm64-gnu" "12.1.2" + "@next/swc-linux-arm64-musl" "12.1.2" + "@next/swc-linux-x64-gnu" "12.1.2" + "@next/swc-linux-x64-musl" "12.1.2" + "@next/swc-win32-arm64-msvc" "12.1.2" + "@next/swc-win32-ia32-msvc" "12.1.2" + "@next/swc-win32-x64-msvc" "12.1.2" node-abi@^2.21.0: version "2.30.1" @@ -2965,7 +2971,7 @@ react-composer@^5.0.2: dependencies: prop-types "^15.6.0" -react-dom@^17.0.2: +react-dom@^17: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -3031,7 +3037,7 @@ react-use-measure@^2.1.1: dependencies: debounce "^1.2.1" -react@^17.0.2: +react@^17: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -3483,10 +3489,10 @@ styled-components@^5.3.3: shallowequal "^1.1.0" supports-color "^5.5.0" -styled-jsx@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77" - integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA== +styled-jsx@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80" + integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw== supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0"