Example repo for 11ty/eleventy#2248.
This gist was created by @pspeter3!
Example repo for 11ty/eleventy#2248.
This gist was created by @pspeter3!
module.exports = function(eleventyConfig) { | |
// v2.0.0-canary.19 or newer | |
eleventyConfig.addExtension([ "11ty.jsx", "11ty.ts", "11ty.tsx" ], { | |
key: "11ty.js", | |
}); | |
}; |
import h from "vhtml"; | |
export const data = { | |
title: "Eleventy 11ty.js Extensions", | |
}; | |
interface Context { | |
log(message: string): void; | |
} | |
interface Data { | |
readonly title: string; | |
} | |
export function render(this: Context, { title }: Data) { | |
this.log(title); | |
return ( | |
<html lang="en"> | |
<head> | |
<title>{title}</title> | |
<link | |
rel="stylesheet" | |
href="https://cdn.jsdelivr.net/npm/@picocss/[email protected]/css/pico.min.css" | |
integrity="sha256-TMMCiSUqLaqob0cQkqOwl6oJLd2X5WKYJ4ML+BQRQOA=" | |
crossorigin="anonymous" | |
></link> | |
</head> | |
<body> | |
<main className="container"> | |
<h1>{title}</h1> | |
<p> | |
Example repo to show the value of{" "} | |
<a href="https://github.com/11ty/eleventy/issues/2248"> | |
11ty/eleventy#2248 | |
</a> | |
. | |
</p> | |
</main> | |
</body> | |
</html> | |
); | |
} |
{ | |
"name": "11ty-extensions", | |
"description": "Example repo to show the value of https://github.com/11ty/eleventy/issues/2248", | |
"scripts": { | |
"build": "node --require esbuild-register node_modules/.bin/eleventy", | |
}, | |
"repository": { | |
"type": "git", | |
"url": "git+ssh://[email protected]/56931a837cb854c55bb06024287ead95.git" | |
}, | |
"devDependencies": { | |
"@11ty/eleventy": "^2.0.0-canary.19", | |
"@types/vhtml": "^2.2.4", | |
"esbuild-register": "^3.3.2", | |
"patch-package": "^6.4.7", | |
"typescript": "^4.6.3", | |
"vhtml": "^2.2.0" | |
} | |
} |
{ | |
"compilerOptions": { | |
"target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | |
"jsx": "react", /* Specify what JSX code is generated. */ | |
"jsxFactory": "h", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ | |
"module": "commonjs", /* Specify what module code is generated. */ | |
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ | |
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ | |
"strict": true, /* Enable all strict type-checking options. */ | |
"skipLibCheck": true /* Skip type checking all .d.ts files. */ | |
} | |
} |
@pauleveritt it's a TypeScript feature which lets you declare the type of this
in a function. See https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function. The annotation is nice for frameworks like Eleventy which pass data through the thisArg
. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call for more details.
@pspeter3 My apologies, you are exactly right. I just switched back and it works. I guess my previous issue was a typing error not related to the this
part.
FWIW, I spent around 20h in front of this last week, migrating a very big Gatsby site. It's been a joy. I wonder if you're interested in two things:
I’m assuming those questions are directed at me and the answer to both is yes.
Hey, I suggest adding the following to the script section in the package.json example just to demonstrate how to use the eleventy flags
"serve":"node --require esbuild-register node_modules/.bin/eleventy --serve"
As an update: @khalidabuhakmeh and I have gotten very, very far into this. One place we stumbled: vhtml doesn't support async. When we got to a component that needed to render an image, we were in trouble.
We investigated Nano JSX, dropped it in. Was quite interesting. The vhtml
got its last activity 3 years ago, whereas Nano JSX is quite active. Unfortunately the Suspense component didn't really pan out for the renderSSR
approach.
We found a clever workaround on the issue involving, of all things, collections.
Why not put the config file in ts too?
"dev": "node --require esbuild-register node_modules/.bin/eleventy --config=.eleventy.ts --serve"
Well @khalidabuhakmeh put our Gatsby->11ty 2.0 plus TS/TSX/Vite rewrite into production last week. It's been a blast. We did some neat content and component things.
Thanks all for this gist which I stumbled across in December and completely changed directions.
I'm curious how you're using Vite and 11ty together
@pspeter3 We plan soon to write a bunch of stuff about what we're doing. I'll confess, I approached this as writing a larger system, Gatsby-ish.
I have a wild idea for a next step. To avoid prop drilling, I'd like child components to have access to the this: Context
above. It's hard: vhtml
is the caller. I have an idea for that. But I worry the work @zachleat is doing on dependency tracking for incremental will get broken.
@pspeter3 You up for doing a Meet sometime, I can show you what we did? You after all were the trigger. 😀
Sure! Definitely curious to learn more. I also think you may be able to use Preact instead of vhtml so you can pass the data via context. I also think that may be better for memory since vhtml does some caching internally. I'll try playing with that idea today and let you know how it goes.
@pspeter3 Here is a fairly-recent setup: https://github.com/pauleveritt/g3-tsx ... just before we got out of spike solution mode. Interesting bits:
@pspeter3 Did you ever get a chance to play with preact to pass data via context?
I'd like to clean up the types better but I got a working prototype with the following:
import { createContext } from "preact";
import { useContext } from "preact/hooks";
import type { FunctionComponent, VNode } from "preact";
export interface EleventyContext {}
const Context = createContext<EleventyContext | null>(null);
export type EleventyComponent<T> = (
this: EleventyContext,
data: T
) => VNode<{}>;
export function createEleventContextProvider<T>(
Component: FunctionComponent<{ data: T }>
): EleventyComponent<T> {
return function (this: EleventyContext, data: T): VNode<{}> {
return (
<Context.Provider value={this}>
<Component data={data} />
</Context.Provider>
);
};
}
export function useEleventyContext(): EleventyContext {
const value = useContext(Context);
if (value === null) {
throw new Error("Missing EleventyContext");
}
return value;
}
This is the way I set up the component
import { Name } from "./_components/name";
import { createEleventContextProvider } from "./_utilities/eleventy-context";
export const data = {
layout: "html",
title: "Preact Test",
};
export const render = createEleventContextProvider(
({ data: title }: { data: typeof data }) => (
<>
<h1>{title}</h1>
<Name />
</>
)
);
The Name
component is using the useEleventyContext
hook as a test but it's not actually important here.
Nicer than what I came up with, thanks @pspeter3
@pspeter3 Can I see your component? Curious if you've eliminated import of h
and what are your return types.
I did manage to eliminate h
but I think there is an issue with esbuild-register
not respecting the jsx-import-source flag.
@pspeter3 I think I have h
replaced, but have some typing things happening. Any chance you're up for a demo repo or a Google Meet?
@pspeter3 Here's a recreation of what I'm struggling with. I can make a full repo if it would help https://gist.github.com/pauleveritt/bef52a4f1f7dd9d7d77ed5d53d8d08a3
My guess is that .bind
isn't doing what I think it's doing. But inside MyView
, the this
isn't what it's supposed to be. I tried adding it to the MyView
args list but couldn't get the typing right.
@pspeter3 I made a repo with the minimal example but the render function is returning an object (from JSX processing) when 11ty expects a string. Obviously I don't have the h/rendering installed correctly in tsconfig.
As a note to others: esbuild-register
appears to have landed jsxImportSource
last year. Need to investigate how to make it work.
That's for esbuild unfortunately. You need egoist/esbuild-register#84
Both the above esbuild-register
and new tsx
approaches are now on the official docs, thanks everyone!
https://www.11ty.dev/docs/languages/typescript/
https://www.11ty.dev/docs/languages/jsx/
@pauleveritt ah, I must be missing something here. In a standard
11ty.js
template with arender
method (or export) you’re absolutely right, render only passes in one argument: https://www.11ty.dev/docs/languages/javascript/#classesBut above, in a
tsx
Typescript template, you can declarethis
separately, no? Perhaps @pspeter3 might be more helpful here, since they wrote the original code 😅