Skip to content

Instantly share code, notes, and snippets.

View sergiodxa's full-sized avatar

Sergio Xalambrí sergiodxa

View GitHub Profile
@sergiodxa
sergiodxa / crud.ts
Last active March 15, 2025 21:49
A crud helper for RRv7 routes.ts file
import { index, prefix, route } from "@react-router/dev/routes";
import { camelize, pluralize, singularize } from "inflected";
function createCrud(base = "./views") {
/**
* Create a CRUD route configuration.
* @param name The name of the resource. It will be pluralized for the path.
* @param options The options for the crud.
* @param options.member Extra routes to add to each member.
* @param options.collection Extra routes to add to the collection.
import Auth0 from "@auth/core/providers/auth0";
import { RemixAuth } from "~/services/auth"
import { sessionStorage } from "~/services/session.server";
export let { loader, action } = RemixAuth({
sessionStorage: sessionStorage, // this does nothing yet
secret: process.env.AUTH_SECRET ?? "s3cr3t",
providers: [
Auth0({
@sergiodxa
sergiodxa / persist-inputs-remix-gh-copilot.md
Created October 20, 2022 21:47
An article completely generated by GitHub Copilot based on a title, with just a few minor errors

Persist user inputs after form submit in Remix

This tutorial will show you how to persist user inputs after form submit in Remix.

Prerequisites

Steps

@sergiodxa
sergiodxa / tailwind.config.js
Created June 22, 2022 18:07
Example Tailwind config for max and range screen sizes.
module.exports = {
theme: {
screens: {
"-2xl": { max: "1536px" },
"-xl": { max: "1280px" },
"-lg": { max: "1024px" },
"-md": { max: "768px" },
"-sm": { max: "640px" },
"sm~md": { min: "640px", max: "768px" },
"md~lg": { min: "768px", max: "1024px" },
@sergiodxa
sergiodxa / articles.test.ts
Last active May 15, 2022 22:34
An E2E helper for Vitest and Puppeteer to test Remix app
import { test, expect, describe, beforeAll, afterAll } from "vitest";
import "pptr-testing-library/extend";
import { type App, start } from "test/helpers/app";
import { loader } from "./articles";
import { logger } from "~/services/logger.server";
import type { PrismaClient } from "@prisma/client";
import { createDatabaseClient } from "test/helpers/db";
describe("E2E", () => {
let app: App;
@sergiodxa
sergiodxa / entry.server.tsx
Last active December 5, 2024 08:05
Dynamically generate a PDF with Remix
import { renderToStream } from "@react-pdf/renderer";
import ReactDOMServer from "react-dom/server";
import { EntryContext, Headers, RemixServer, Request, Response } from "remix";
import PDF, { loader } from "./pdfs/my-pdf.server";
async function handlePDFRequest(request: Request, headers: Headers) {
// get the data for the PDF
let response = await loader({ request, context: {}, params: {} });
// if it's a response return it, this means we redirected
if (response instanceof Response) return response;
@sergiodxa
sergiodxa / .eslintrc.js
Created July 30, 2021 22:45
My favorite ESLint configuration
/* eslint-disable unicorn/prefer-module */
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: [
"@typescript-eslint",
"unicorn",
"import",
"react",
"prettier",
@sergiodxa
sergiodxa / remix-auth.ts
Created July 21, 2021 23:25
Remix authentication library usage example
// create a session storage instance
let sessionStorage = createCookieSessionStorage();
// create authenticator instance
let authenticator = new Authenticator<User>(
sessionStorage
);
// configure the authenticator to use the Auth0 strategy for sign-in
authenticator.use(
@sergiodxa
sergiodxa / csrf.tsx
Last active September 9, 2022 18:03
A module with the implementation for CSRF protection in Remix
import { randomBytes } from 'crypto';
import * as React from 'react';
import { Request, Session } from 'remix';
import { parseBody, parseParams } from './parse-body';
/**
* An error that is thrown when a CSRF token is missing or invalid.
* @example
* throw new InvalidAuthenticityToken("Can't verify CSRF token authenticity.");
* @example
@sergiodxa
sergiodxa / serializer.ts
Last active May 26, 2021 01:50
Serializer in TS similar to ActiveModel::Serializer
abstract class Serializer {
abstract attributes: string[];
[customAttribute: string]: unknown; // this allow this[attribute] to work so we can use getters to add extra attributes
constructor(protected input: Record<string, unknown>) {}
private filterAttributes() {
return Object.fromEntries(
this.attributes.map((attribute) => {