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
18 changes: 8 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ jobs:
- name: Check documentation links
run: sh scripts/check-docs-links.sh

- name: Check documentation style
run: sh scripts/check-docs-style.sh

- name: Check for removed source syntax in docs
run: sh scripts/check-removed-syntax.sh

Expand All @@ -148,20 +151,15 @@ jobs:
go-version: '1.26.4'
cache: true

- name: Compile docs site against in-tree GOWDK
- name: Build and smoke docs site against in-tree GOWDK
working-directory: docs-site
run: |
set -euxo pipefail
# The site embeds dist/site; a placeholder lets the main package
# compile without the full asset build (Render compiles the real site
# at deploy). This job only catches Go/generator drift against HEAD.
mkdir -p dist/site
printf '<!doctype html>\n' > dist/site/index.html
go build ./...
scripts/install-tailwind-linux.sh
scripts/build-production.sh
scripts/smoke-production.sh
go test ./...
go vet ./...
# The docs generator must run against the repo's own docs/ tree.
go run ./cmd/syncdocs
test -f src/components/docs-sidebar.cmp.gwdk

example-reports:
name: Example reports
Expand Down
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ Replace `github.com/acme/hello-gowdk/ui` with your app module path.
## CLI at a Glance

"Inspectable" is not just a slogan — the CLI exposes every stage of the
pipeline. Run `gowdk` with no arguments for full flags.
pipeline. Run `gowdk` with no arguments for the registered command list, and use
[the CLI reference](docs/reference/cli.md) for the complete flag contract. The
command registry is covered by `cmd/gowdk/main_commands_test.go`; this section
is a short overview, not a second source of truth.

### Build and run

Expand All @@ -157,6 +160,7 @@ pipeline. Run `gowdk` with no arguments for full flags.
| `gowdk dev` | Build, serve, rebuild on change, live-reload browsers, show an error overlay |
| `gowdk preview` | Build and serve a local deploy preview |
| `gowdk serve` | Serve already-generated build output |
| `gowdk clean` | Remove configured generated outputs, with `--dry-run` and `--json` |

### Inspect and debug

Expand All @@ -168,7 +172,7 @@ pipeline. Run `gowdk` with no arguments for full flags.
| `gowdk doctor` | Check local environment and project health |
| `gowdk audit` | Derive security posture, evaluate baseline/declared policies, and optionally emit/run audit tests (`--json` for CI) |
| `gowdk inspect ir` / `tree` / `endpoint-graph` / `asset-graph` / `go-bindings` | Print validated compiler IR, source-linked node tree, endpoint dispatch graph, asset graph, or Go binding report JSON |
| `gowdk manifest` / `routes` / `sitemap` | Print validated manifest, route/endpoint metadata, or editor site-map JSON |
| `gowdk manifest` / `routes` / `endpoints` / `sitemap` | Print validated manifest, route metadata, backend endpoint metadata, or editor site-map JSON |
| `gowdk tokens` | Print raw language tokens for a file |
| `gowdk fmt` | Format `.gwdk` sources (`--write`) |

Expand All @@ -179,6 +183,7 @@ pipeline. Run `gowdk` with no arguments for full flags.
| `gowdk generate stubs` | Write conservative missing action/API Go handler stubs next to their owning source package |
| `gowdk contracts` / `graph` / `trace` / `list` | Print contract registration metadata, the command/event graph, a single contract trace, or filtered lists of commands/queries/events/jobs |
| `gowdk add <addon>` | Wire an optional addon into `gowdk.config.go` (`add --list` for addable built-ins, `add --list --registry` for metadata; `add seo` requires `--base-url`) |
| `gowdk playground policy` / `export` / `run` | Print sandbox policy, archive a source project, or run a project through the explicit hosted-execution sandbox |
| `gowdk lsp` | Start the language server over stdio |

## Design
Expand Down Expand Up @@ -219,7 +224,12 @@ How responsibility is split, and the opinions behind it:

## What Works Today

This table describes the current demoable 0.x slice. Status levels:
This table describes the current demoable 0.x slice. "Current Limit" separates
GOWDK backlog from app-owned work by design; app-owned limits mean the compiler
intentionally leaves domain policy, persistence, credentials, deployment, or
business validation in ordinary Go/application infrastructure.

Status levels:

- **Works** — the listed path works end-to-end today.
- **Works, contract unstable** — the listed path works end-to-end, but the
Expand All @@ -241,6 +251,7 @@ This table describes the current demoable 0.x slice. Status levels:
| Components | Works, contract unstable | Components support imported contracts, slots, scoped CSS/assets, first local client behavior, and generated island assets. Page stores can opt into localStorage/sessionStorage persistence with `persist "local"`/`persist "session"`, including WASM island read/write/sync through the host loader. | Non-string props, richer slots/events, real `g:if`/`g:for`, lifecycle cleanup, and dependency diagnostics are planned. | [Components](docs/language/components.md) | [Components](examples/components/base/base-components.page.gwdk) |
| WASM islands | Early | Component-level `wasm` and page-level `go client {}` emit Go `js/wasm` browser assets for supported fixtures; build-time validation checks browser-safe imports and ABI exports, browser tests cover mount/event/patch/emit/destroy/store participation, and `runtime/wasm` exposes the Go payload/result helper. | User-code runtime validation beyond the current patch/store contract remains planned. | [Components](docs/language/components.md) | [WASM example](examples/components/wasm/README.md) |
| CSS/assets | Works, contract unstable | CSS processors, page CSS, scoped component CSS, component assets, asset manifests, content-hashed filenames, optional production obfuscation for compiler-owned JS, and optional Tailwind wrapper exist. | CSS processor contracts and optional dependency boundaries need hardening. | [CSS](docs/reference/css.md) | [CSS](examples/css/styled.page.gwdk) |
| SEO | Works, contract unstable | The SEO addon emits `sitemap.xml` and `robots.txt`, validates supported `jsonld` page metadata, injects deterministic JSON-LD into generated HTML, and generated apps can serve `/sitemap.xml` by merging public build-time URLs with an optional dynamic sitemap provider. | Search console ownership, private/auth content policy, source-of-truth inventory queries, and request-time-only URL discovery stay app-owned; unsupported schema kinds remain GOWDK backlog. | [SEO](docs/reference/seo.md) | [SEO](examples/seo) |
| One-binary output | Works, contract unstable | `gowdk build --app --bin` can generate and compile an embedded Go server for supported SPA/backend/SSR slices, `--docker` emits a minimal non-root Docker context beside the binary, and CI starts the embed example binary to verify health plus embedded page serving. | Runtime operations, richer Docker target config, and split/backend-only deploys are still expanding. | [Deployment](docs/reference/deployment.md) | [Embed](examples/embed/site.page.gwdk) |
| Generated app WASM | Early | `gowdk build --app --wasm` compiles the generated app into a Go `js/wasm` deploy artifact, and CI verifies the emitted module header. | Host runtime/loader integration is deploy-platform owned; this is separate from component-level WASM islands. | [Deployment](docs/reference/deployment.md) | [Embed](examples/embed/site.page.gwdk) |
| Contracts | Works, contract unstable | Runtime contracts support typed queries, commands, events, jobs, role filtering, local dispatch, file outbox, broker/fanout adapters, worker replay with dedup/backoff hooks, contract graph/trace/list commands, generated `g:command`/`g:query` web adapters, and explicit domain-event to query invalidation metadata. | Separate worker/cron binary generators and editor-first contract visualization remain planned platform tooling. | [Contracts](docs/reference/contracts.md) | [Runtime contracts](runtime/contracts) |
Expand Down
3 changes: 3 additions & 0 deletions addons/seo/seo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type Options = gowdk.SEOOptions
// URL describes one additional sitemap URL.
type URL = gowdk.SEOURL

// DynamicSitemap describes an optional request-time sitemap provider.
type DynamicSitemap = gowdk.SEODynamicSitemap

// Addon enables build-time SEO output. BaseURL is required when building.
func Addon(options ...Options) gowdk.Addon {
var selected Options
Expand Down
25 changes: 25 additions & 0 deletions cmd/gowdk/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,31 @@ func TestTestCommandRunsInitializedProjectAppStageWithJSON(t *testing.T) {
})
}

func TestResolveExplicitTestPathsUsesLaunchDirectory(t *testing.T) {
launchRoot := t.TempDir()
relative := filepath.Join("site", "pages", "home.page.gwdk")
absolute := filepath.Join(launchRoot, "site", "pages", "about.page.gwdk")

var paths []string
var launchCwd string
withWorkingDir(t, launchRoot, func() {
var err error
launchCwd, err = os.Getwd()
if err != nil {
t.Fatal(err)
}
paths, err = resolveExplicitTestPaths([]string{relative, absolute})
if err != nil {
t.Fatal(err)
}
})

wantRelative := filepath.Join(launchCwd, relative)
if len(paths) != 2 || paths[0] != wantRelative || paths[1] != absolute {
t.Fatalf("unexpected resolved test paths: %#v", paths)
}
}

func TestTestCommandRejectsUpdateFlag(t *testing.T) {
_, err := parseTestOptions([]string{"--update"})
if err == nil || !strings.Contains(err.Error(), `unknown test flag "--update"`) {
Expand Down
26 changes: 25 additions & 1 deletion cmd/gowdk/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,13 +328,18 @@ func buildTestWorkdir(cli cliOptions, options testOptions, modules []string) (*t
}
}

paths, err := resolveExplicitTestPaths(options.Paths)
if err != nil {
cleanup()
return nil, nil, err
}
fmt.Fprintf(os.Stderr, "gowdk test [build]: %s\n", work.Root)
request := buildRequest{
OutputDir: work.OutputDir,
AppDir: work.AppDir,
BinaryPath: work.BinaryPath,
Modules: modules,
Paths: append([]string(nil), options.Paths...),
Paths: paths,
}
if err := runInWorkingDir(cli.ProjectRoot, func() error {
return runTestBuildOnce(cli, request, options.JSON)
Expand All @@ -345,6 +350,25 @@ func buildTestWorkdir(cli cliOptions, options testOptions, modules []string) (*t
return work, cleanup, nil
}

func resolveExplicitTestPaths(paths []string) ([]string, error) {
if len(paths) == 0 {
return nil, nil
}
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
resolved := make([]string, 0, len(paths))
for _, path := range paths {
if filepath.IsAbs(path) {
resolved = append(resolved, filepath.Clean(path))
continue
}
resolved = append(resolved, filepath.Clean(filepath.Join(cwd, path)))
}
return resolved, nil
}

func runTestBuildOnce(cli cliOptions, request buildRequest, quietStdout bool) error {
if !quietStdout {
return buildOnce(cli, request, newBuildTimingRecorder(false))
Expand Down
6 changes: 2 additions & 4 deletions docs-site/.gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Generated site output. `dist/site` is committed so Render's default Go build
# can run the docs-site server without a custom build command.
# Generated site output. Production and CI rebuild `dist/site` from source.
dist/*
!dist/site/
!dist/site/**
.gowdk/
gowdk_cache/

Expand All @@ -18,3 +15,4 @@ app
/gowdk-page
/syncdocs
tools/tailwindcss
tools/gowdk
26 changes: 15 additions & 11 deletions docs-site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ sidebar, "on this page" TOC, breadcrumbs, prev/next, ⌘K search, copy buttons,
callouts) lives in the reusable `DocsPage`/`DocsSidebar`/`Callout` components, so
every generated page is modular and consistent.

`dist/site/` is also generated output. It is rebuilt by CI and Render from the
markdown, `.gwdk`, CSS, assets, and in-tree compiler; do not commit generated
HTML or hashed assets from that directory.

## Prerequisites

- Go 1.26.4+.
Expand Down Expand Up @@ -72,18 +76,13 @@ Watches the `.gwdk` sources and `app.css` and rebuilds on change. Open
## Build Site Output

```sh
go run ./cmd/syncdocs
rm -rf dist/site
(cd .. && go build -o docs-site/tools/gowdk ./cmd/gowdk)
./tools/gowdk build
mkdir -p dist/site/assets
cp -R assets/. dist/site/assets/
cp assets/favicon.ico dist/site/favicon.ico
scripts/build-production.sh
```

Always run `cmd/syncdocs` before the GOWDK build so the published docs match the
selected GOWDK source. `rm -rf dist/site` is required because the generated tree
mirrors the repo structure and stale routes must not linger.
Always run the production script before starting the site binary. It runs
`cmd/syncdocs`, clears `dist/site`, builds with the in-tree CLI, copies static
assets, and compiles the Go server. CI and Render use the same script.

`./tools/gowdk build` compiles the `.gwdk` sources
to static HTML, emits each page's `<head>` from its `title`, `description`, and
Expand Down Expand Up @@ -126,8 +125,7 @@ To preview website changes locally before opening a PR:
./tools/gowdk dev --addr 127.0.0.1:8091
# or a production-faithful preview that serves the exact built output through
# the site's own Go binary (the same one that ships to production):
go run ./cmd/syncdocs
rm -rf dist/site && ./tools/gowdk build
scripts/build-production.sh
GOWDK_ADDR=127.0.0.1:8091 go run .
```

Expand Down Expand Up @@ -156,6 +154,12 @@ project.
- `app.css`: Tailwind v4 input and the site's visual system.
- `cmd/syncdocs/`: generator that builds the docs pages and sidebar from the
main repo's `docs/` markdown (uses `goldmark`). See "Sync docs".
- `scripts/build-production.sh`: production-faithful build used by CI and
Render.
- `scripts/smoke-production.sh`: local smoke check for the generated site
served through the production binary.
- `scripts/install-tailwind-linux.sh`: pinned Tailwind standalone CLI install
for Linux CI/Render environments.
- `src/pages/index.page.gwdk`: the documentation home served at `/`.
- `src/pages/docs/**.page.gwdk`: the documentation pages — **generated**; do not
hand-edit.
Expand Down
50 changes: 40 additions & 10 deletions docs-site/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,13 @@ a {

.prose :not(pre) > code {
padding: 1px 5px;
border: 1px solid #c6dbe4;
border-radius: 5px;
background: var(--color-surface-soft);
color: #0a5f74;
background: #f7fbfd;
color: #174956;
font-family: var(--font-mono);
font-size: 0.85em;
font-weight: 500;
font-weight: 600;
word-break: break-word;
}

Expand All @@ -460,7 +461,7 @@ a {
figure.code {
margin: 22px 0 28px;
border: 1px solid rgba(8, 26, 33, 0.5);
border-radius: 12px;
border-radius: 8px;
background: var(--color-code);
overflow: hidden;
box-shadow: var(--shadow-card);
Expand All @@ -469,6 +470,7 @@ figure.code {
figure.code figcaption {
display: flex;
align-items: center;
justify-content: space-between;
padding: 9px 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
color: #8fb2bf;
Expand All @@ -487,11 +489,36 @@ figure.code pre {
line-height: 1.7;
}

.code-lang {
display: inline-flex;
min-height: 20px;
align-items: center;
}

.prose pre code {
font-family: var(--font-mono);
font-size: 13px;
}

.tok-keyword,
.tok-directive {
color: #7dd3fc;
font-weight: 650;
}

.tok-string {
color: #f7c873;
}

.tok-number {
color: #c4b5fd;
}

.tok-comment {
color: #8aa6b2;
font-style: italic;
}

/* tables emitted by the docs renderer */
.table-scroll {
margin: 22px 0 28px;
Expand Down Expand Up @@ -897,12 +924,12 @@ figure.code {
font-size: 11.5px;
font-weight: 600;
cursor: pointer;
opacity: 0;
opacity: 0.84;
transition: opacity 130ms ease, background 130ms ease;
}

figure.code:hover .code-copy,
.prose pre:hover .code-copy,
.prose > pre:hover .code-copy,
.code-copy:focus-visible {
opacity: 1;
}
Expand All @@ -912,13 +939,13 @@ figure.code:hover .code-copy,
color: #fff;
}

/* generated fenced code blocks (goldmark <pre><code class="language-*">) */
.prose pre {
/* fallback for any authored raw <pre>; generated fences use figure.code */
.prose > pre {
position: relative;
margin: 22px 0 28px;
padding: 16px 18px;
border: 1px solid rgba(8, 26, 33, 0.5);
border-radius: 12px;
border-radius: 8px;
background: var(--color-code);
color: var(--color-code-text);
overflow-x: auto;
Expand Down Expand Up @@ -1165,12 +1192,15 @@ body.docs-modal-open {

.prose pre,
figure.code pre {
font-size: 12.5px;
}

.prose > pre {
margin-right: -14px;
margin-left: -14px;
border-right: 0;
border-left: 0;
border-radius: 0;
font-size: 12.5px;
}

.doc-pager {
Expand Down
Loading
Loading