Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/components/studioBrand.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
type LogoConfig,
type LogoSize,
resolveTsSsgContext,
resolvePageContext,
} from '@purestack/ts-common'
import { defineComponent, html, type RefOrValue } from 'regor'

Expand All @@ -17,7 +17,7 @@ export function defineStudioBrandComponent() {
props: ['size'],
context: (head) => ({
...head.props,
config: resolveTsSsgContext(head).site.logo,
config: resolvePageContext(head).site.logo,
}),
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Panel :context="pageContext.pageInfo" tone="accent" variant="surface" bodyClass="p-3" class="mb-4">
<p class="text-eyebrow mt-0">From this page's frontmatter</p>
<strong>{{ frontmatter.title }}</strong>
<ul class="mb-0">
<li r-for="item in frontmatter.highlights">
{{ item }}
</li>
</ul>
</Panel>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<RegorApp id="launch-checklist" src="./regor-launch-checklist.ts"/>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ highlights:

# Regor

<a href="https://github.com/PureStackStudio/Regor" target="_blank" rel="noopener">Regor</a> is the component framework PureStack is built on. It runs in two places:
<a
href="https://github.com/PureStackStudio/Regor"
target="_blank"
rel="noopener"
>Regor</a
>
is the component framework PureStack is built on. It runs in two places:

<Grid columns="1" columnsSm="2" class="gap-3 mb-4">
<Panel tone="neutral" bodyClass="p-3">
Expand Down Expand Up @@ -153,7 +159,7 @@ Every page goes through the same four steps:
| Where it runs | Your build, once per page | The visitor's browser |
| What it produces | Static HTML | A live, interactive interface |
| Event handlers, `r-model`, timers | Not kept | Run normally |
| Page data (`tsSsgContext`) | Available | Not available |
| Page data (`pageContext`) | Available | Not available |
| Components | All built-in and registered components | The component families you register |

## Regor at build time
Expand Down Expand Up @@ -185,45 +191,22 @@ A plain attribute passes a string. An attribute that starts with a colon is a Re

### Expressions and page data

Text in double braces is a Regor expression, anywhere in a page. Inside markup, expressions can read `tsSsgContext`, which holds the page and site data that PureStack provides during the build:
Text in double braces is a Regor expression, anywhere in a page. Inside markup, expressions can read `pageContext`, which holds the page and site data that PureStack provides during the build:

| Field | Contents |
| --- | --- |
| `tsSsgContext.pageInfo.frontmatter` | The page's frontmatter, including any custom fields you add |
| `tsSsgContext.pageInfo.urlPath` | The page's URL, such as `/guides/extending/regor/` |
| `tsSsgContext.pageInfo.relPath` | The source file path inside the content folder |
| `tsSsgContext.site` | The resolved site configuration |
| `tsSsgContext.navigation`, `tsSsgContext.outline` | The page's navigation and heading outline |
| `tsSsgContext.locale`, `tsSsgContext.locales` | The page's language and the site's languages |

This page lists three highlights in its frontmatter. The panel below reads them during the build:

<Panel tone="accent" variant="surface" bodyClass="p-3" class="mb-4">
<p class="text-eyebrow mt-0">From this page's frontmatter</p>
<strong>{{ tsSsgContext.pageInfo.frontmatter.title }}</strong>
<ul class="mb-0">
<li r-for="item in tsSsgContext.pageInfo.frontmatter.highlights">
{{ item }}
</li>
</ul>
</Panel>
| `pageContext.pageInfo.frontmatter` | The page's frontmatter, including any custom fields you add |
| `pageContext.pageInfo.urlPath` | The page's URL, such as `/guides/extending/regor/` |
| `pageContext.pageInfo.relPath` | The source file path inside the content folder |
| `pageContext.site` | The resolved site configuration |
| `pageContext.navigation`, `pageContext.outline` | The page's navigation and heading outline |
| `pageContext.locale`, `pageContext.locales` | The page's language and the site's languages |

```mdx
---
title: Regor
highlights:
- One component model for pages and apps
- Plain TypeScript, no compiler plugin
- Built-in components and theme styles in both places
---
This page lists three highlights in its frontmatter. The panel below reads them during the build. `:context="pageContext.pageInfo"` makes `frontmatter` available directly inside the panel:

<Panel tone="accent" variant="surface" bodyClass="p-3">
<strong>{{ tsSsgContext.pageInfo.frontmatter.title }}</strong>
<ul>
<li r-for="item in tsSsgContext.pageInfo.frontmatter.highlights">{{ item }}</li>
</ul>
</Panel>
```
<import-content src="./_regor-frontmatter.mdx"/>

<import-codeblock src="./_regor-frontmatter.mdx"/>

`r-if`, `r-else-if`, and `r-else` choose what to render. `r-for` repeats an element for each item. Loop over data from frontmatter, the page context, or component state.

Expand Down Expand Up @@ -294,9 +277,10 @@ export default defineConfig({
>
Export the interface that types a component, as <code>ReleaseNote</code> is
exported here. The
<a href="/guides/getting-started/vscode-extension/">VS Code extension</a> finds components
through their exported interfaces. Without the export, it can't complete the
component's props or jump from a tag in your markup to the component's source.
<a href="/guides/getting-started/vscode-extension/">VS Code extension</a>
finds components through their exported interfaces. Without the export, it
can't complete the component's props or jump from a tag in your markup to the
component's source.
</AlertBox>

Then use it in any page. Content between the tags fills the `<slot>`:
Expand All @@ -307,19 +291,17 @@ Then use it in any page. Content between the tags fills the `<slot>`:
</ReleaseNote>
```

Tag names match registered keys regardless of case, so `<ReleaseNote>` finds `releaseNote`. The kebab-case form `<release-note>` works too. Components can also read page data. Pass the component head to `resolveTsSsgContext` from `@purestack/ts-common`, as the built-in navigation and page-link components do.
Tag names match registered keys regardless of case, so `<ReleaseNote>` finds `releaseNote`. The kebab-case form `<release-note>` works too. Components can also read page data. Pass the component head to `resolvePageContext` from `@purestack/ts-common`, as the built-in navigation and page-link components do.

## Regor in the browser

### Mount an app

Place a `RegorApp` in a page and point it at a TypeScript file next to the page. It renders an `app` element with the `id` you give it and loads the script. The script calls `createApp` with the matching selector:

<RegorApp id="launch-checklist" src="./regor-launch-checklist.ts"/>
<import-content src="./_regor-launch-checklist.mdx"/>

```mdx
<RegorApp id="launch-checklist" src="./regor-launch-checklist.ts"/>
```
<import-codeblock src="./_regor-launch-checklist.mdx"/>

<import-codeblock src="./regor-launch-checklist.ts"/>

Expand All @@ -332,8 +314,8 @@ Browser apps do not get the build's component list. Each app registers the compo
<AlertBox tone="info" icon="tabler:info-circle" title="Register icons too">
Register each icon your app shows, including the defaults that components draw
themselves, such as the arrow in a select field. The
<a href="/guides/styling/icons/#icons-in-browser-apps">Icons guide</a> lists those
defaults.
<a href="/guides/styling/icons/#icons-in-browser-apps">Icons guide</a> lists
those defaults.
</AlertBox>

### Template syntax
Expand Down Expand Up @@ -483,7 +465,7 @@ These patterns scale from a single widget to a multi-page admin tool with tables

## Use other libraries

A [PageScript](/components/runtime/page-script/) loads a browser entry point. Use it to enhance existing HTML or mount a browser-compatible library. PureStack bundles local TypeScript and its imports with esbuild; framework-specific source formats may need an additional compilation step. Start with a plain TypeScript module, as shown in [runtime scripts](../../components/runtime).
A [PageScript](/components/runtime/page-script/) loads a browser entry point. Use it to enhance existing HTML or mount a browser-compatible library. PureStack bundles local TypeScript and its imports with esbuild; framework-specific source formats may need an additional compilation step. Start with a plain TypeScript module, as shown in [runtime scripts](../../../components/runtime).

<AlertBox tone="warning" icon="tabler:alert-triangle" title="What you give up">
Other frameworks can't use the built-in components, and they don't inherit the
Expand Down
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{
"private": true,
"name": "purestack-repo",
"version": "1.1.8",
"version": "1.1.9",
"packageManager": "yarn@4.18.1",
"type": "module",
"sideEffects": false,
"workspaces": [
"packages/*"
],
"dependencyConsistency": {
"exceptions": {
"typescript": "The repository uses TypeScript 7 for tsc; the VS Code extension needs TypeScript 6's JavaScript compiler API."
}
},
"license": "MIT",
"repository": {
"type": "git",
Expand Down Expand Up @@ -43,6 +48,7 @@
"lint": "yarn workspaces foreach --no-private --all --verbose --topological -- run lint",
"lint:ci": "yarn workspaces foreach --no-private --all --verbose --topological -- exec biome check ./src --formatter-enabled=false",
"check-eol": "yarn tsx scripts/check-eol.ts",
"check-dependencies": "yarn tsx scripts/check-dependencies.ts",
"bump-version": "yarn tsx --no-warnings scripts/bump-version.ts",
"bundle": "yarn tsx --no-warnings scripts/bundle.ts",
"cleanup": "yarn tsx --no-warnings scripts/cleanup.ts",
Expand All @@ -53,6 +59,6 @@
"pack-vscode-ext": "yarn --cwd packages/ts-ssg-vscode build && yarn --cwd packages/ts-ssg-vscode package",
"longest": "yarn tsx ./scripts/longest-functions.ts",
"format": "biome format --write",
"pre-commit": "yarn check-eol && yarn regor-components && yarn embed && yarn format && yarn lint && yarn build && yarn test run"
"pre-commit": "yarn check-dependencies && yarn check-eol && yarn regor-components && yarn embed && yarn format && yarn lint && yarn build && yarn test run"
}
}
2 changes: 1 addition & 1 deletion packages/purestack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "purestack",
"version": "1.1.8",
"version": "1.1.9",
"packageManager": "yarn@4.9.2",
"module": "./dist/purestack.mjs",
"types": "./dist/purestack.d.mts",
Expand Down
2 changes: 1 addition & 1 deletion packages/purestack/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const version: string = '1.1.8'
export const version: string = '1.1.9'

export * from '@purestack/ts-ssg'
6 changes: 3 additions & 3 deletions packages/ts-common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ scripts. This package defines site configuration, page frontmatter, navigation,
logos, templates, and the rendering context so those packages agree on the same
shapes.

Import types such as `SiteConfig`, `PageFrontmatter`, and `TsSsgContext` when
building a custom integration. The runtime exports `resolveTsSsgContext` and
`tryResolveTsSsgContext` find the site context in a Regor component.
Import types such as `SiteConfig`, `PageFrontmatter`, and `PageContext` when
building a custom integration. The runtime exports `resolvePageContext` and
`tryResolvePageContext` find the site context in a Regor component.
2 changes: 1 addition & 1 deletion packages/ts-common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@purestack/ts-common",
"version": "1.1.8",
"version": "1.1.9",
"packageManager": "yarn@4.9.2",
"module": "./dist/ts-common.mjs",
"types": "./dist/ts-common.d.mts",
Expand Down
8 changes: 4 additions & 4 deletions packages/ts-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export type {
PageTranslationInfo,
} from './page-template-types'
export {
resolveTsSsgContext,
tryResolveTsSsgContext,
} from './resolveTsSsgContext'
resolvePageContext,
tryResolvePageContext,
} from './resolvePageContext'
export type {
AnalyticsConfig,
AuthConfig,
Expand All @@ -63,7 +63,7 @@ export type {
SiteStyleConfigInput,
} from './site-config-types'
export type {
PageContext,
PageOutlineItem,
TsSsgContext,
TsSsgNavigation,
} from './ts-ssg-context'
35 changes: 35 additions & 0 deletions packages/ts-common/src/resolvePageContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { PageContext } from './ts-ssg-context'

type ContextCarrier = {
pageContext?: PageContext
}

type HeadLike = {
ctx?: unknown[]
}

function hasPageContext(value: unknown): value is ContextCarrier {
return typeof value === 'object' && value !== null && 'pageContext' in value
}

function hasContextStack(value: unknown): value is HeadLike {
return typeof value === 'object' && value !== null && 'ctx' in value
}

export function resolvePageContext(head: unknown): PageContext {
const stack = hasContextStack(head) && Array.isArray(head.ctx) ? head.ctx : []
for (const ctx of stack) {
if (!hasPageContext(ctx)) continue
if (ctx.pageContext) return ctx.pageContext
}
throw new Error('pageContext is not available in the Regor context stack.')
}

export function tryResolvePageContext(head: unknown): PageContext | undefined {
const stack = hasContextStack(head) && Array.isArray(head.ctx) ? head.ctx : []
for (const ctx of stack) {
if (!hasPageContext(ctx)) continue
if (ctx.pageContext) return ctx.pageContext
}
return undefined
}
37 changes: 0 additions & 37 deletions packages/ts-common/src/resolveTsSsgContext.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/ts-common/src/ts-ssg-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface TsSsgNavigation {
pageLinks?: PageNavigationLinks
}

export interface TsSsgContext {
export interface PageContext {
site: SiteConfig
pageInfo: PageInfo
navigation?: TsSsgNavigation
Expand Down
4 changes: 2 additions & 2 deletions packages/ts-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@purestack/ts-components",
"version": "1.1.8",
"version": "1.1.9",
"packageManager": "yarn@4.9.2",
"module": "./dist/ts-components.mjs",
"types": "./dist/ts-components.d.mts",
Expand Down Expand Up @@ -55,7 +55,7 @@
"@purestack/ts-page-scripts": "workspace:^",
"@purestack/ts-style": "workspace:^",
"@purestack/ts-util": "workspace:^",
"regor": "^1.7.3"
"regor": "^1.7.4"
},
"regorComponents": [
"AlertBox",
Expand Down
8 changes: 4 additions & 4 deletions packages/ts-components/src/standard/barChart/barChart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('BarChart rendering', () => {
const app = createApp(
{
components: defineBarChartComponents(),
tsSsgContext: createTestContext(),
pageContext: createTestContext(),
items: values.map((value, index) => ({
label: `Day ${index + 1}`,
value,
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('BarChart rendering', () => {
const app = createApp(
{
components: defineBarChartComponents(),
tsSsgContext: createTestContext(),
pageContext: createTestContext(),
items: [
{ label: 'Core', value: 42, color: '#18a999' },
{ label: 'Edge', value: 28, color: '#f4a261' },
Expand Down Expand Up @@ -102,7 +102,7 @@ describe('BarChart rendering', () => {
const app = createApp(
{
components: defineBarChartComponents(),
tsSsgContext: createTestContext(),
pageContext: createTestContext(),
items: [{ label: 'Invalid', value: Number.NaN }],
},
{
Expand Down Expand Up @@ -131,7 +131,7 @@ describe('BarChart rendering', () => {
const app = createApp(
{
components: defineBarChartComponents(),
tsSsgContext: createTestContext(),
pageContext: createTestContext(),
items: [{ label: 'Core', value: 42 }],
},
{
Expand Down
Loading
Loading