mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
update
This commit is contained in:
committed by
Morten Olsen
parent
b654ba7e74
commit
e6e938bd42
@@ -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") {
|
||||
31
config/plugins/withGenerator/webpack.js
Normal file
31
config/plugins/withGenerator/webpack.js
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
4
content/articles/hiring/a4.gen.yml
Normal file
4
content/articles/hiring/a4.gen.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
data:
|
||||
type: article
|
||||
structure: ./index.yml
|
||||
generator: latex
|
||||
@@ -1,3 +0,0 @@
|
||||
data:
|
||||
structure: ./index.yml
|
||||
generator: article
|
||||
@@ -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
|
||||
|
||||
5
content/articles/hiring/share.gen.yml
Normal file
5
content/articles/hiring/share.gen.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
generator: image
|
||||
data:
|
||||
type: share
|
||||
width: 450
|
||||
height: 300
|
||||
@@ -1,3 +0,0 @@
|
||||
generator: share
|
||||
width: 450
|
||||
height: 300
|
||||
4
content/articles/hyperconnect/a4.gen.yml
Normal file
4
content/articles/hyperconnect/a4.gen.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
data:
|
||||
structure: ./index.yml
|
||||
type: article
|
||||
generator: latex
|
||||
@@ -1,3 +0,0 @@
|
||||
data:
|
||||
structure: ./index.yml
|
||||
generator: article
|
||||
4
content/articles/my-home-runs-redux/a4.gen.yml
Normal file
4
content/articles/my-home-runs-redux/a4.gen.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
data:
|
||||
type: article
|
||||
structure: ./index.yml
|
||||
generator: latex
|
||||
@@ -1,3 +0,0 @@
|
||||
data:
|
||||
structure: ./index.yml
|
||||
generator: article
|
||||
@@ -1,6 +1,5 @@
|
||||
title: My home runs Redux
|
||||
cover: cover.png
|
||||
published: 2022-03-15
|
||||
shareImage: ./share.png.yml
|
||||
parts:
|
||||
- main.md
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
generator: share
|
||||
width: 450
|
||||
height: 300
|
||||
3
content/profile/a4.gen.yml
Normal file
3
content/profile/a4.gen.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
generator: latex
|
||||
data:
|
||||
type: resume
|
||||
@@ -1 +0,0 @@
|
||||
generator: resume
|
||||
7
content/profile/image.gen.yml
Normal file
7
content/profile/image.gen.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
generator: image
|
||||
data:
|
||||
type: resize
|
||||
width: 780
|
||||
height: 780
|
||||
src: ./me.jpg
|
||||
output: image/jpeg
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -36,19 +36,12 @@ const Cloud = ({ texture }) => {
|
||||
const ref = useRef<any>();
|
||||
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 (
|
||||
<mesh
|
||||
ref={ref}
|
||||
@@ -138,3 +131,4 @@ const HeroBackground = () => {
|
||||
}
|
||||
|
||||
export { HeroBackground };
|
||||
export default HeroBackground;
|
||||
|
||||
@@ -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,7 +90,18 @@ const SocialLogo = styled.div<{ src: string }>`
|
||||
background-size: cover;
|
||||
`
|
||||
|
||||
const Hero: React.FC<Props> = ({ profile }) => (
|
||||
const Hero: React.FC<Props> = ({ profile }) => {
|
||||
const [background, setBackground] = useState<any>();
|
||||
useEffect(
|
||||
() => {
|
||||
import('./background').then(({ default: HeroBackground }) => {
|
||||
console.log('goo', typeof HeroBackground);
|
||||
setBackground(<HeroBackground />);
|
||||
})
|
||||
},
|
||||
[],
|
||||
)
|
||||
return (
|
||||
<Wrapper>
|
||||
<Avatar src={profile.avatar}>
|
||||
<AvatarSpacer />
|
||||
@@ -115,7 +125,9 @@ const Hero: React.FC<Props> = ({ profile }) => (
|
||||
</SlideIn>
|
||||
))}
|
||||
</Social>
|
||||
{background}
|
||||
</Wrapper>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export { Hero };
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
21
src/generators/images/index.ts
Normal file
21
src/generators/images/index.ts
Normal file
@@ -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),
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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 };
|
||||
86
src/generators/images/types/resize.ts
Normal file
86
src/generators/images/types/resize.ts
Normal file
@@ -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 };
|
||||
@@ -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[]) => {
|
||||
16
src/generators/index.ts
Normal file
16
src/generators/index.ts
Normal file
@@ -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 };
|
||||
@@ -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";
|
||||
@@ -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<Data> = 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}
|
||||
@@ -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';
|
||||
}
|
||||
37
src/generators/latex/index.ts
Normal file
37
src/generators/latex/index.ts
Normal file
@@ -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<Buffer>((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 };
|
||||
7
src/generators/types.ts
Normal file
7
src/generators/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type Generator<TData = any> = (data: TData, location: string) => Promise<{
|
||||
[key: string]: {
|
||||
name: string;
|
||||
content: string | Buffer;
|
||||
}
|
||||
}>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 };
|
||||
148
yarn.lock
148
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"
|
||||
|
||||
Reference in New Issue
Block a user