Skip to content

Commit

Permalink
Merge branch 'main' into docs/table-best-practices
Browse files Browse the repository at this point in the history
  • Loading branch information
marionnegp authored Nov 21, 2024
2 parents bc42f7b + 42aeb3c commit b3e0dee
Show file tree
Hide file tree
Showing 49 changed files with 1,465 additions and 422 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
run: npm ci --prefer-offline

- name: Build
run: npm run build
run: npm run docs

- name: Deploy
uses: peaceiris/actions-gh-pages@v3
Expand Down
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,21 @@ docs/_data/*.js
docs/assets/javascript/elements/playground-elements.js
docs/assets/javascript/elements/assets/playground-typescript-worker-*.js
docs/assets/javascript/environment.js
!docs/11ty-types.d.ts
!docs/_plugins/lit-ssr/worker/*.js

# Build artifacts
elements.js
elements/*/*.js
elements/*/test/*.js
!elements/**/demo/*.css
uxdot/*.js
react
lib/**/*.js
!elements/**/demo/*.css
*.tsbuildinfo
*.map
*.d.ts
!declaration.d.ts
!docs/11ty-types.d.ts
!./docs/11ty-types.d.ts
!*/11ty-types.d.ts
custom-elements.json
test-results.xml
rhds.min.js
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v22.7.0
v23.2.0
72 changes: 63 additions & 9 deletions docs/11ty-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ declare module '@11ty/eleventy-plugin-syntaxhighlight/src/getAttributes.js' {

declare module '@11ty/eleventy/src/UserConfig.js' {
import type MarkdownIt from 'markdown-it';
import type { URLPattern } from 'urlpattern-polyfill';

interface EleventyPage {
url: string;
fileSlug: string;
Expand Down Expand Up @@ -40,6 +42,7 @@ declare module '@11ty/eleventy/src/UserConfig.js' {
}

interface FilterContext extends Context { }

interface TransformContext extends Context {
inputPath: string;
outputPath: string;
Expand Down Expand Up @@ -76,24 +79,34 @@ declare module '@11ty/eleventy/src/UserConfig.js' {
compile(inputContent: string): (this: CompileContext, data: unknown) => string | Promise<string>;
}

interface EleventyBeforeEventHandlerOptions {
interface BeforeEvent {
directories: EleventyData['directories'],
/** @deprecated */
dir: {input: string; output: string; includes: string, data: string; layouts: string;};
outputMode: 'js'|'json'|'ndjson';
runMode: 'build'|'serve'|'watch';
}

interface EleventyAfterEventHandlerOptions extends EleventyBeforeEventHandlerOptions {
interface AfterEvent extends BeforeEvent {
results?: {inputPath:string;outputPath:string; url:string;content:string}[];
}

interface ContentMapEvent {
inputPathToUrl: Record<string, string>;
urlToInputPath: Record<string, string>;
}

type EleventyEvent =
| BeforeEvent
| AfterEvent
| ContentMapEvent
| string[]
| UserConfig;

type TransformCallback = (this: TransformContext, content: string) => string | Promise<string>;

type AddCollectionCallback = (api: CollectionApi) => CollectionItem[] | Promise<CollectionItem[]>;

type OnCallback<O = EleventyBeforeEventHandlerOptions> = (opts: O) => void | Promise<void>;

export type PluginFunction<Opts = unknown> = (config: UserConfig, opts?: Opts) => void | Promise<void>

type FilterFunction<T = string, R = string> = (this: FilterContext, data: T) => R | Promise<R>;
Expand All @@ -107,6 +120,45 @@ declare module '@11ty/eleventy/src/UserConfig.js' {
| (() => unknown)
| (() => Promise<unknown>);

interface EleventyDevServerResponse {
body: string;
status: number;
headers?: {
[key: string]: string | undefined,
};
}

interface ServerOptions {
liveReload: boolean;
domDiff: boolean;
port: number;
watch: string[];
showAllHosts: boolean
https: { key: string; cert: string; }
encoding: string;
showVersion: boolean;
indexFileName: string;
injectedScriptsFolder: string;
portReassignmentRetryCount: number;
folder: string;
/** @deprecated */
enabled: boolean;
/** @deprecated use domDiff */
domdiff: boolean;
onRequest: Record<string, (opts: {
url: URL;
pattern: URLPattern;
patternGroups: Record<string|number, string>;
}) =>
| undefined
| string
| EleventyDevServerResponse
| Promise<
| undefined
| string
| EleventyDevServerResponse>>;
}

export default class UserConfig {
addCollection(name: string, callback: AddCollectionCallback): void;
addDataExtension(names: string, processor: (content: string) => unknown | Promise<unknown>): void;
Expand All @@ -127,12 +179,14 @@ declare module '@11ty/eleventy/src/UserConfig.js' {
getFilter(name: string): FilterFunction;
getFilter(name: string): FilterFunctionWithArgs;
globalData: { [key: string]: DataEntry };
on(event: 'eleventy.before', callback: OnCallback): void;
on(event: 'eleventy.after', callback: OnCallback<EleventyAfterEventHandlerOptions>): void;
on(event: 'eleventy.beforeWatch', callback: (changedFiles: string[]) => void | Promise<void>): void;
on(event: 'eleventy.contentMap', callback: (opts: ({ inputPathToUrl: Record<string, string>, urlToInputPath: Record<string, string> })) => void | Promise<void>): void;
on(event: 'eleventy.beforeConfig', callback: (config: UserConfig) => void | Promise<void>): void;
on(event: 'eleventy.before', callback: (event: BeforeEvent) => void | Promise<void>): void;
on(event: 'eleventy.after', callback: (event: AfterEvent) => void | Promise<void>): void;
on(event: 'eleventy.contentMap', callback: (event: ContentMapEvent) => void | Promise<void>): void;
on(event: 'eleventy.beforeWatch', callback: (changedFiles: string[]) => void | Promise<void>): void;
on(event: 'eleventy.beforeConfig', callback: (config: UserConfig) => void | Promise<void>): void;
on(event: string, callback: (event: any) => void | Promise<void>): void;
setQuietMode(quiet: boolean): void;
setServerOptions(options: Partial<ServerOptions>): void
javascriptFunctions: Record<string, (...args: unknown[]) => any>;
watchIgnores: Set<string>;
}
Expand Down
27 changes: 27 additions & 0 deletions docs/_plugins/lit-ssr/lit-css-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { transform } from '@pwrs/lit-css';
import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';

interface HookContext {
source: string;
format: 'module' | 'commonjs' | 'wasm' | 'json';
}

type LoadFunction = (url: string, context: HookContext) => Promise<HookContext>;

const cache = new Map<string, string>();

export async function load(url: string, context: HookContext, nextLoad: LoadFunction) {
if (url.endsWith('.css')) {
if (!cache.has(url)) {
const filePath = fileURLToPath(new URL(url));
const css = await readFile(filePath, 'utf8');
cache.set(url, await transform({ css, filePath }));
}
const format = 'module';
const source = cache.get(url);
return { source, shortCircuit: true, format };
} else {
return nextLoad(url, context);
}
}
176 changes: 83 additions & 93 deletions docs/_plugins/lit-ssr/lit.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,103 @@
/**
* @license based on code from eleventy-plugin-lit
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

import type { EleventyPage } from '@11ty/eleventy/src/UserConfig.js';
import type { UserConfig } from '@11ty/eleventy';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { Worker } from 'node:worker_threads';

const __dirname = dirname(fileURLToPath(import.meta.url));
import { readFile, writeFile } from 'node:fs/promises';

interface Options {
componentModules?: string[];
}
import { Piscina } from 'piscina';
import tsBlankSpace from 'ts-blank-space';
import chalk from 'chalk';

// Lit SSR includes comment markers to track the outer template from
// the template we've generated here, but it's not possible for this
// outer template to be hydrated, so they serve no purpose.
function trimOuterMarkers(renderedContent: string) {
return renderedContent
.replace(/^((<!--[^<>]*-->)|(<\?>)|\s)+/, '')
.replace(/((<!--[^<>]*-->)|(<\?>)|\s)+$/, '');
}
import { register } from 'node:module';

export default function(eleventyConfig: UserConfig, opts?: Options) {
const { componentModules } = opts ?? {};
if (componentModules === undefined || componentModules.length === 0) {
// If there are no component modules, we could never have anything to
// render.
return;
}
export interface RenderRequestMessage {
content: string;
page: Pick<EleventyPage, 'inputPath' | 'outputPath'>;
}

const resolvedComponentModules = componentModules.map(module =>
pathToFileURL(resolve(process.cwd(), module)).href);
export interface RenderResponseMessage {
page: Pick<EleventyPage, 'inputPath' | 'outputPath'>;
rendered?: string;
durationMs: number;
}

let worker: Worker;
interface Options {
componentModules?: string[];
/** path from project root to tsconfig */
tsconfig?: string;
}

const requestIdResolveMap = new Map();
let requestId = 0;
async function redactTSFileInPlace(path: string) {
const inURL = new URL(path, import.meta.url);
const outURL = new URL(path.replace('.ts', '.js'), import.meta.url);
await writeFile(outURL, tsBlankSpace(await readFile(inURL, 'utf8')), 'utf8');
}

eleventyConfig.on('eleventy.before', async function() {
worker = new Worker(resolve(__dirname, './worker/worker.js'));
register('./lit-css-node.ts', import.meta.url);

worker.on('error', err => {
// eslint-disable-next-line no-console
console.error('Unexpected error while rendering lit component in worker thread', err);
throw err;
/**
* Eleventy plugin to server-render lit elements directly from typescript source
* @param eleventyConfig
* @param opts
*/
export default async function(eleventyConfig: UserConfig, opts?: Options) {
const imports = opts?.componentModules ?? [];
const tsconfig = opts?.tsconfig ?? './tsconfig.json';

let pool: Piscina;

// If there are no component modules, we could never have anything to
// render.
if (imports?.length) {
eleventyConfig.on('eleventy.before', async function() {
await redactTSFileInPlace('./worker.ts');
const filename = new URL('worker.js', import.meta.url).pathname;
pool = new Piscina({
filename,
workerData: {
imports,
tsconfig,
},
});
});

let requestResolve: (v?: unknown) => void;
const requestPromise = new Promise(_resolve => {
requestResolve = _resolve;
eleventyConfig.on('eleventy.after', async function() {
return pool.close();
});

worker.on('message', message => {
switch (message.type) {
case 'initialize-response': {
requestResolve();
break;
}
eleventyConfig.addTransform('render-lit', async function(this, content) {
const { outputPath, inputPath } = this.page;

case 'render-response': {
const { id, rendered } = message;
const _resolve = requestIdResolveMap.get(id);
if (_resolve === undefined) {
throw new Error(
'@lit-labs/eleventy-plugin-lit received invalid render-response message'
);
}
_resolve(rendered);
requestIdResolveMap.delete(id);
break;
}
if (!outputPath.endsWith('.html')) {
return content;
}
});

const message = {
type: 'initialize-request',
imports: resolvedComponentModules,
};

worker.postMessage(message);
await requestPromise;
});

eleventyConfig.on('eleventy.after', async () => {
await worker.terminate();
});

eleventyConfig.addTransform('render-lit', async function(this, content) {
const { outputPath, inputPath, fileSlug } = this.page;
if (!outputPath.endsWith('.html')) {
return content;
}

const renderedContent: string = await new Promise(_resolve => {
requestIdResolveMap.set(requestId, _resolve);
const message = {
type: 'render-request',
id: requestId++,
content,
page: JSON.parse(JSON.stringify({ outputPath, inputPath, fileSlug })),
};
worker.postMessage(message);
const page = { outputPath, inputPath };
const message = await pool.run({ page, content });
if (message.rendered) {
const { durationMs, rendered, page } = message;
if (durationMs > 1000) {
const color =
durationMs > 5000 ? chalk.red
: durationMs > 1000 ? chalk.yellow
: durationMs > 100 ? chalk.blue
: chalk.green;
// eslint-disable-next-line no-console
console.log(`${color(durationMs.toFixed(2).padEnd(8))} Rendered ${page.outputPath} in`);
}
return trimOuterMarkers(rendered);
} else {
return content;
}
});
}
}

const outerMarkersTrimmed = trimOuterMarkers(renderedContent);
return outerMarkersTrimmed;
});
};
// Lit SSR includes comment markers to track the outer template from
// the template we've generated here, but it's not possible for this
// outer template to be hydrated, so they serve no purpose.
function trimOuterMarkers(renderedContent: string) {
return renderedContent
.replace(/^((<!--[^<>]*-->)|(<\?>)|\s)+/, '')
.replace(/((<!--[^<>]*-->)|(<\?>)|\s)+$/, '');
}

Loading

0 comments on commit b3e0dee

Please sign in to comment.