feat: move to marmite

This commit is contained in:
Andrea 2024-12-27 12:27:06 +01:00
parent d7952ad640
commit 4e3576d4a7
Signed by: nullndr
GPG Key ID: 8DA8996EF89F33BB
34 changed files with 49 additions and 12953 deletions

View File

@ -1,3 +1,4 @@
node_modules .gitignore
build site
public/build LICENSE
README.md

View File

@ -1,18 +0,0 @@
name: Code quality
on:
push:
pull_request:
jobs:
code_quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: latest
- name: Run Biome
run: biome ci .

6
.gitignore vendored
View File

@ -1,6 +1,2 @@
node_modules
/.cache
/build
/public/build
.env .env
site

View File

@ -1,19 +1,9 @@
FROM node:22-alpine AS base FROM ghcr.io/rochacbruno/marmite
RUN apk add --no-cache libc6-compat WORKDIR /input
WORKDIR /app COPY . ./
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm cache clean --force
EXPOSE 3000 EXPOSE 3000
ENV PORT 3000 CMD ["--serve", "--bind", "0.0.0.0:3000"]
CMD ["npm", "run", "start"]

View File

@ -1,3 +1,3 @@
# yaaaw.it # nullndr.com
This is my personal website made with [remix.run](remix.run/docs) 🚀 This is my personal website made with [marmite](https://rochacbruno.github.io/marmite/) 🚀

View File

@ -1,7 +0,0 @@
export function Body({ children }: React.PropsWithChildren) {
return (
<body className="bg-[#222447] text-[#d6d6d6] font-['monospace']">
{children}
</body>
);
}

View File

@ -1,3 +0,0 @@
export function LinkWrapper({ children }: React.PropsWithChildren) {
return <div className="mt-5 hover:text-[#e6c2bf] font-bold">{children}</div>;
}

View File

@ -1,12 +0,0 @@
export function Notbyai() {
return (
<div className="flex flex-col items-center">
<a href="https://notbyai.fyi/" target="_blank" rel="noreferrer">
<img
alt="nobyai logo"
src="https://user-images.githubusercontent.com/62137266/225653923-a69103f5-b318-4e52-9ea1-95b61d388366.svg"
/>
</a>
</div>
);
}

View File

@ -1,24 +0,0 @@
import type { SerializeFrom } from "@remix-run/node";
import { Link } from "@remix-run/react";
import { useFormattedDate } from "~/hooks";
import type { Post } from "~/utils/posts.server";
export function PostPreview({
title,
description,
filename,
published,
}: SerializeFrom<Post>) {
const formattedDate = useFormattedDate(published);
return (
<div className="">
<Link to={filename}>
<div className="py-3 font-bold">
<div className="text-[#ffff00]">{title}</div>
<div>{description}</div>
<time className="text-sm">{formattedDate}</time>
</div>
</Link>
</div>
);
}

View File

@ -1,7 +0,0 @@
export function Title({ children }: React.PropsWithChildren) {
return (
<div className="mt-5">
<h1 className="text-[#ffff00] text-2xl font-bold">{children}</h1>
</div>
);
}

View File

@ -1 +0,0 @@
export * from "./useFormattedDate";

View File

@ -1,6 +0,0 @@
export function useFormattedDate(_date: Date) {
const date = new Date(_date);
const month = date.getUTCMonth().toString().padStart(2, "0");
const monthDate = date.getUTCDate().toString().padStart(2, "0");
return `${date.getUTCFullYear()}-${month}-${monthDate}`;
}

View File

@ -1,101 +0,0 @@
import codeHikeStyle from "@code-hike/mdx/dist/index.css?url";
import type { LinksFunction, MetaFunction } from "@remix-run/node";
import {
Link,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
isRouteErrorResponse,
useRouteError,
} from "@remix-run/react";
import stylesheet from "~/tailwind.css?url";
import { Body } from "./components/Body";
import { LinkWrapper } from "./components/LinkWrapper";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesheet },
{ rel: "stylesheet", href: codeHikeStyle },
];
export const meta: MetaFunction = () => [
{
charSet: "utf-8",
},
{
title: "Nullndr",
},
{
name: "viewport",
content: "width=device-width,initial-scale=1.0",
},
];
export function Layout({ children }: React.PropsWithChildren) {
return (
<html lang="en">
<head>
<Meta />
<Links />
<script
defer
data-domain="nullndr.com"
src="https://plausible.nullndr.com/js/script.file-downloads.hash.outbound-links.pageview-props.tagged-events.js"
/>
<script
dangerouslySetInnerHTML={{
__html:
"window.plausible = window.plausible || function(){(window.plausible.q = window.plausible.q || []).push(arguments)}",
}}
/>
</head>
<Body>
{children}
<ScrollRestoration />
<Scripts />
</Body>
</html>
);
}
export default function App() {
return <Outlet />;
}
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<div className="flex flex-col items-center justify-around h-[100vh]">
<div>
<div className="text-center text-[#ffff00] text-[10vw] font-bold">
404
</div>
<div className="font-bold">Where do you think you are going?</div>
<div className="text-center">
<LinkWrapper>
<Link to="/">Home</Link>
</LinkWrapper>
</div>
</div>
</div>
);
}
return (
<div className="flex flex-col items-center justify-around h-[100vh]">
<div>
<div className="text-center text-[#ffff00] text-[6vw] font-bold">
Something bad happened
</div>
<div className="text-center">
<LinkWrapper>
<Link to="/">Home</Link>
</LinkWrapper>
</div>
</div>
</div>
);
}

View File

@ -1,65 +0,0 @@
import { Link } from "@remix-run/react";
import {
FaGithub,
FaGitlab,
FaLinkedin,
FaMastodon,
FaStackOverflow,
FaTelegramPlane,
FaTwitter,
} from "react-icons/fa";
import { IconContext } from "react-icons/lib";
import { MdEmail } from "react-icons/md";
import { LinkWrapper } from "~/components/LinkWrapper";
import { Notbyai } from "~/components/Notbyai";
export default function Index() {
return (
<div className="flex h-screen min-h-full flex-col justify-center">
<div className="flex flex-col flex-grow place-content-center">
<div>
<div className="text-center text-[6vw]">
<span>$ echo "Hello, world!"</span>
<span className="animate-blink">|</span>
</div>
<nav className="pt-5 flex justify-center flex-wrap">
<Notbyai />
<IconContext.Provider
value={{ color: "yellow", className: "p-2", size: "3em" }}
>
<a rel="me" href="https://mastodon.uno/@nullndr">
<FaMastodon />
</a>
<a href="https://t.me/nullndr">
<FaTelegramPlane />
</a>
<a href="https://gitlab.com/nullndr">
<FaGitlab />
</a>
<a href="https://github.com/nullndr">
<FaGithub />
</a>
<a href="https://twitter.com/nullndr">
<FaTwitter />
</a>
<a href="mailto: nullndr@duck.com">
<MdEmail />
</a>
<a href="https://linkedin.com/in/nullndr">
<FaLinkedin />
</a>
<a href="https://stackoverflow.com/users/10503039/nullndr">
<FaStackOverflow />
</a>
</IconContext.Provider>
</nav>
<div className="flex justify-center items-center space-x-6">
<LinkWrapper>
<Link to="/blog">Blog</Link>
</LinkWrapper>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,97 +0,0 @@
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import { getMDXComponent } from "mdx-bundler/client/index.js";
import React from "react";
import { FaEdit } from "react-icons/fa";
import { Notbyai } from "~/components/Notbyai";
import { Title } from "~/components/Title";
import { useFormattedDate } from "~/hooks";
import { getMdxFile } from "~/utils/posts.server";
export const handle = {
to: "/blog",
text: "Go Back",
};
export const loader = async ({ params }: LoaderFunctionArgs) => {
const file = params.file;
if (file == null) {
throw new Response(null, { status: 400 });
}
const data = await getMdxFile(file);
return { ...data, file };
};
export const meta: MetaFunction<typeof loader> = ({ data }) => {
if (data == null) {
return [];
}
const {
frontmatter: { title, description },
} = data;
return [
{
title,
},
{
property: "og:title",
content: title,
},
{
property: "description",
content: description,
},
];
};
export default function Post() {
const {
code,
frontmatter: { title, date },
file,
} = useLoaderData<typeof loader>();
const formattedDate = useFormattedDate(date);
const MdxComponent = React.useMemo(() => getMDXComponent(code), [code]);
return (
<div className="mx-2">
<header>
<Title>{title}</Title>
<span>
<time>{formattedDate}</time> ·{" "}
<Link to="/blog" className="hover:text-[#e6c2bf] font-bold">
Go Back
</Link>
</span>
</header>
<main>
<div className="space-y-5">
<div className=" prose-blockquote:bg-[#282c34] prose-blockquote:border-l-4 prose-blockquote:border-[#21252b] prose-blockquote:px-2 prose-blockquote:py-2 prose-blockquote:rounded-r">
<div className="prose-h2:text-[#ffff00] prose-h2:font-bold prose-h2:text-2xl">
<div className="prose-code:px-1 prose-code:py-0.5 prose-code:bg-[#282c34] prose-code:text-[#d6d6d6] prose-code:rounded prose-code:font-mono">
<div className="space-y-5 dark:prose-invert prose-a:font-bold hover:prose-a:text-[#ffff00] prose-p:text-[#d6d6d6]">
<MdxComponent />
</div>
</div>
</div>
</div>
<Notbyai />
<div>
<Link
to={`https://github.com/nullndr/website/edit/main/posts/${file}.mdx`}
className="hover:text-[#ffff00]"
>
<div className="flex justify-end items-center space-x-2 pb-5">
<FaEdit />
<div className="font-bold">Typo?</div>
</div>
</Link>
</div>
</div>
</main>
</div>
);
}

View File

@ -1,27 +0,0 @@
import { Link, useLoaderData } from "@remix-run/react";
import { LinkWrapper } from "~/components/LinkWrapper";
import { PostPreview } from "~/components/PostPreview";
import { Title } from "~/components/Title";
import { findPosts } from "~/utils/posts.server";
export const loader = () => {
return findPosts();
};
export default function Blog() {
const posts = useLoaderData<typeof loader>();
return (
<div className="mx-2">
<LinkWrapper>
<Link to="/">Home</Link>
</LinkWrapper>
<Title>Here I blog about whatever get my attention</Title>
<div className="mt-5">
{posts.map((post) => (
<PostPreview {...post} key={post.title} />
))}
</div>
</div>
);
}

View File

@ -1,26 +0,0 @@
import type { MetaFunction } from "@remix-run/node";
import { Outlet } from "react-router-dom";
export const meta: MetaFunction = () => {
return [
{
title: "Nullndr's blog",
},
{
property: "og:title",
content: "Nullndr's blog",
},
{
property: "description",
content: "Another blog by Nullndr.",
},
];
};
export default function BlogLayout() {
return (
<div className="space-y-6 max-w-3xl mx-auto">
<Outlet />
</div>
);
}

View File

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,81 +0,0 @@
import { remarkCodeHike } from "@code-hike/mdx";
import { bundleMDX } from "mdx-bundler";
import { readFile, readdir } from "node:fs/promises";
import path from "node:path";
type FrontMatter = {
title: string;
description: string;
date: Date;
isFeatured: boolean;
};
export const getMdxFile = async (file: string) => {
const filePath = path.join(process.cwd(), `posts/${file}.mdx`);
const postContent = (await readFile(filePath)).toString();
return bundleMDX<FrontMatter>({
source: postContent,
mdxOptions(options) {
return {
rehypePlugins: [...(options.rehypePlugins ?? [])],
remarkPlugins: [
...(options.remarkPlugins ?? []),
[
remarkCodeHike,
{
theme: "one-dark-pro",
lineNumbers: true,
showCopyButton: true,
autoImport: true,
},
],
],
};
},
});
};
export type Post = Omit<FrontMatter, "published"> & {
filename: string;
published: string;
};
export const findPosts = async () => {
const files = await readdir("posts");
const posts = await Promise.all(
files
.filter((file) => file.endsWith(".mdx"))
.map(async (file) => {
const filePath = path.join(process.cwd(), `posts/${file}`);
const postContent = (await readFile(filePath)).toString();
const { frontmatter } = await bundleMDX<FrontMatter>({
source: postContent,
mdxOptions() {
return {
remarkPlugins: [
[
remarkCodeHike,
{
theme: "one-dark-pro",
lineNumbers: true,
showCopyButton: true,
autoImport: true,
},
],
],
};
},
});
return {
...frontmatter,
filename: file.replace(".mdx", ""),
published: frontmatter.date.toISOString(),
};
}),
);
return posts.sort((a, b) =>
new Date(a.published) > new Date(b.published) ? -1 : 1,
);
};

View File

@ -1,54 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"files": {
"ignoreUnknown": false,
"ignore": ["node_modules", "build"]
},
"vcs": {
"clientKind": "git",
"defaultBranch": "main",
"enabled": false,
"root": ".",
"useIgnoreFile": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off",
"noConfusingVoidType": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf"
},
"organizeImports": {
"enabled": true
},
"javascript": {
"formatter": {
"enabled": true,
"quoteStyle": "double",
"jsxQuoteStyle": "double",
"trailingCommas": "all",
"semicolons": "always",
"arrowParentheses": "always"
}
},
"overrides": [
{
"include": ["**/*.{ts,tsx}"],
"javascript": {
"globals": []
}
}
]
}

8
content/_htmlhead.md Normal file
View File

@ -0,0 +1,8 @@
<script
defer
data-domain="nullndr.com"
src="https://plausible.nullndr.com/js/script.file-downloads.hash.outbound-links.pageview-props.tagged-events.js"
></script>
<script>
window.plausible = window.plausible || function(){(window.plausible.q = window.plausible.q || []).push(arguments)},
</script>

View File

@ -2,8 +2,11 @@
title: Hello, world title: Hello, world
date: 2023-03-15 date: 2023-03-15
description: The hello world post description: The hello world post
author: nullndr
--- ---
# Hello, world
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Libero id faucibus nisl tincidunt eget nullam non. Viverra suspendisse potenti nullam ac. Libero id faucibus nisl tincidunt eget nullam non. Viverra suspendisse potenti nullam ac.
Neque volutpat ac tincidunt vitae semper quis. Libero justo laoreet sit amet cursus sit amet dictum. Neque volutpat ac tincidunt vitae semper quis. Libero justo laoreet sit amet cursus sit amet dictum.

View File

@ -2,8 +2,11 @@
title: How this site is built title: How this site is built
date: 2024-09-29 date: 2024-09-29
description: A technical explanation of how this site works description: A technical explanation of how this site works
author: nullndr
--- ---
# How this site is built
This site is built with [remix.run](https://remix.run). There is no database for the posts, instead the posts are written directly in [MDX](https://mdxjs.com/). This site is built with [remix.run](https://remix.run). There is no database for the posts, instead the posts are written directly in [MDX](https://mdxjs.com/).
The transformation from MDX to the component is done with the following function: The transformation from MDX to the component is done with the following function:

View File

@ -2,8 +2,12 @@
title: Run Shopify App without the App Bridge title: Run Shopify App without the App Bridge
date: 2024-10-07 date: 2024-10-07
description: Let's get rid of Shopify's app bridge. description: Let's get rid of Shopify's app bridge.
tags: shopify
author: nullndr
--- ---
# Run Shopify App without the App Bridge
This post wants to explain how to run a Shopify'app outside the [cli](https://github.com/Shopify/cli) and the [app bridge](https://shopify.dev/docs/api/app-bridge). This post wants to explain how to run a Shopify'app outside the [cli](https://github.com/Shopify/cli) and the [app bridge](https://shopify.dev/docs/api/app-bridge).
> Please pay attention, you won't be able to use anything from the App Bridge API. The installation process is also affected, since you won't be able to > Please pay attention, you won't be able to use anything from the App Bridge API. The installation process is also affected, since you won't be able to

2
env.d.ts vendored
View File

@ -1,2 +0,0 @@
/// <reference types="vite/client" />
/// <reference types="@remix-run/node" />

19
marmite.yaml Normal file
View File

@ -0,0 +1,19 @@
name: "Nullndr's blog"
tagline: "Another blog by Nullndr"
url: "https://nullndr.com"
pagination: 10
enable_search: true
menu:
- ["Tags", "tags.html"]
- ["Archive", "archive.html"]
- ["GitHub", "https://github.com/nullndr"]
authors:
nullndr:
name: "Andrea Foletto"
avatar: "https://avatars.githubusercontent.com/u/62137266?v=4"
links:
- ["Telegram", "https://t.me/nullndr"]
- ["Linkedin", "https://linkedin.com/in/nullndr"]
- ["X", "https://x.com/nullndr"]
- ["StackOverflow", "https://stackoverflow.com/users/10503039/nullndr"]
- ["GitHub", "https://github.com/nullndr"]

12263
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
{
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix vite:build",
"dev": "remix vite:dev",
"start": "remix-serve ./build/server/index.js",
"typecheck": "tsc",
"lint": "biome lint",
"format": "biome format --write",
"routes": "remix routes"
},
"dependencies": {
"@code-hike/mdx": "0.9.0",
"@remix-run/node": "2.15.2",
"@remix-run/react": "2.15.2",
"@remix-run/serve": "2.15.2",
"codehike": "1.0.1",
"esbuild": "0.24.0",
"isbot": "5.1.17",
"mdx-bundler": "10.0.3",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-icons": "5.3.0",
"remark-frontmatter": "^5.0.0",
"remark-mdx-frontmatter": "^5.0.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
"@mdx-js/rollup": "3.0.1",
"@remix-run/dev": "2.15.2",
"@tailwindcss/typography": "0.5.15",
"@types/react": "18.3.8",
"@types/react-dom": "18.3.0",
"autoprefixer": "10.4.20",
"postcss": "8.4.47",
"tailwindcss": "3.4.12",
"typescript": "5.6.2",
"vite": "5.4.7",
"vite-tsconfig-paths": "5.0.1"
}
}

View File

@ -1,5 +0,0 @@
export default {
plugins: {
tailwindcss: {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@ -1,5 +0,0 @@
User-Agent: *
Allow: /
User-Agent: *
Allow: /blog

View File

@ -1,20 +0,0 @@
import type { Config } from "tailwindcss";
export default {
content: ["./app/**/*.{ts,tsx}"],
theme: {
extend: {
animation: {
blink: "blink 1.5s linear infinite alternate",
},
keyframes: {
blink: {
"from, 49.9%": { opacity: "0" },
"to, 50%": { opacity: "1" },
},
},
},
},
plugins: [require("@tailwindcss/typography")],
} satisfies Config;

View File

@ -1,25 +0,0 @@
{
"include": ["env.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"types": ["./node_modules/@remix-run/react/future/single-fetch.d.ts"],
// Remix takes care of building everything in `remix build`.
"noEmit": true
}
}

View File

@ -1,33 +0,0 @@
import { remarkCodeHike } from "@code-hike/mdx";
import mdx from "@mdx-js/rollup";
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
declare module "@remix-run/node" {
interface Future {
v3_singleFetch: true;
}
}
export default defineConfig({
server: {
port: 3000,
},
plugins: [
mdx({
remarkPlugins: [[remarkCodeHike]],
}),
remix({
ignoredRouteFiles: ["**/*.css"],
future: {
v3_singleFetch: true,
v3_lazyRouteDiscovery: true,
v3_throwAbortReason: true,
v3_relativeSplatPath: true,
v3_fetcherPersist: true,
},
}),
tsconfigPaths(),
],
});