Skip to content
Development
Command

/portaljs-add-dcat

Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.

From plugin
portaljs
2.3k25 skills25 commands
Install
> /plugin marketplace add datopian/portaljs
> /plugin install portaljs@datopian-portaljs

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/portaljs-add-dcat

Context preview

What this command does when you run it.

Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.

Command definition

portaljs-add-dcat.md
description: Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.
allowed-tools: Read, Write, Edit, Bash, WebFetch

/portaljs-add-dcat

Turn an existing PortalJS (`portaljs-catalog`) portal into a **harvestable** data catalog: emit standards-compliant **DCAT** metadata feeds so external catalogs and government data portals (data.europa.eu, data.gov, national portals) can harvest its datasets automatically.

PortalJS is Frictionless-native (a dataset is a Data Package — see `/portaljs-define-schema`); **DCAT is the serialization + harvest layer on top** (`lib/metadata/dcat.ts` + `lib/metadata/dcat-profiles.ts`). This skill selects one or more DCAT **application profiles**, maps every dataset's metadata to them, and writes static feed files at build so they harvest on **any** host (static Cloudflare Pages, a CDN, a Worker) — no runtime.

What it produces

Wired into `predev`/`prebuild` via `scripts/generate-dcat.ts`, so the feeds are always fresh:

| File | What | |------|------| | `public/catalog.jsonld` · `catalog.ttl` · `catalog.rdf` | **Canonical** feed (first profile) in JSON-LD, Turtle, RDF/XML — the stable autodiscovery targets | | `public/catalog.<profile>.{jsonld,ttl,rdf}` | One feed per configured profile × serialization (e.g. `catalog.dcat-ap.ttl`) | | `public/catalog-feeds.json` | Machine-readable index of every emitted feed | | `<link rel="alternate" type="application/ld+json" href="/catalog.jsonld">` in `_document.tsx` | Autodiscovery — how harvesters find the feed |

Config lives in **`dcat.config.json`** at the portal root (created/updated by this skill). All feeds regenerate from `datasets.json` + this config.

Supported profiles

Profiles are a **pluggable registry** (`lib/metadata/dcat-profiles.ts`) — national profiles are config/data, not hardcoded, and multiple can be emitted at once.

| id | Profile | Notes | |----|---------|-------| | `dcat-3` | DCAT 3 (W3C) | Default. `conformsTo` DCAT-3. | | `dcat-2` | DCAT 2 (W3C) | Same core subset stamped as DCAT-2. | | `dcat-ap` | DCAT-AP (data.europa.eu) | EU profile: adds foaf/vcard, publisher, contactPoint, `dcat:theme` (EU data-theme vocab). | | `dcat-us` | DCAT-US 3.0 (data.gov) | US federal / Project Open Data: adds publisher, contactPoint, `dct:accessLevel`. | | `geodcat-ap` | GeoDCAT-AP (spatial / INSPIRE) | DCAT-AP + spatial coverage: `dct:spatial` (bbox/geometry as `gsp:wktLiteral`) + `dcat:spatialResolutionInMeters` from each dataset's `dcat.spatial` (or the catalog-wide `spatial` config). | | `croissant` | Croissant (MLCommons / schema.org) | ML-dataset metadata: a schema.org `DataCatalog` of Croissant `Dataset`s with `cr:FileObject` distributions + `cr:RecordSet`/`cr:Field` mapped from the Frictionless Table Schema. **JSON-LD only.** | | `dcat-ap-se` · `dcat-ap-ch` · `dcat-ap-de` | National (Sweden / Switzerland / Germany) | DCAT-AP + national `conformsTo`; examples of the plug mechanism. |

**Add another national profile** without code: `registerDcatProfile(makeNationalProfile({ id, label, conformsTo, context }))` in a small module the app loads, then list its id in `dcat.config.json`. See `lib/metadata/README.md`.

**GeoDCAT-AP spatial input.** Per dataset, add `"dcat": { "spatial": { "bbox": "POLYGON((…))" } }` (or `"geometry"`, `"uri"`, `"spatialResolutionInMeters"`) to `datasets.json`; or set a catalog-wide `"spatial"` in `dcat.config.json`. WKT strings are serialized as `gsp:wktLiteral`. A dataset with no spatial coverage is still GeoDCAT-AP-conformant.

**Croissant** describes datasets for ML tooling (schema.org JSON-LD). It reads the Frictionless Table Schema to emit `cr:RecordSet`/`cr:Field`, and stamps a `sha256` on each `cr:FileObject` from the local `public/data/<file>` bytes (mlcroissant requires a checksum on a hosted file; remote-only files are emitted without one). Because a Croissant document describes a single dataset, the feed is a `DataCatalog` whose `dataset[]` entries are each a standalone Croissant `Dataset`.

Required input — ask, don't error

  • **Portal directory** — path to the portal project (defaults to current directory).
  • **Profiles** — which to emit (default: `dcat-3`). For national-portal harvesting the

user wants `dcat-ap` (EU) or `dcat-us` (US) + optionally a national profile.

  • **Publisher + contact** — DCAT-AP and DCAT-US **require** `dct:publisher` and

`dcat:contactPoint`. If the user picks one of those profiles, ask for the publishing organization (name + homepage) and a contact (name + email). Without them the feed is emitted but **fails conformance** — the generator will say what's missing.

  • **Site URL** — the portal's public origin (e.g. `https://data.example.org`), so feed

links are absolute. Without it links are root-relative (fine only for same-origin harvest). Reuse `SITE_URL` if already set for `/portaljs-deploy`.

If profiles beyond `dcat-3` are chosen and publisher/contact are missing, ask (one focused prompt) and wait:

DCAT-AP / DCAT-US require a publisher and a contact point. I need:
1. Publishing organization: name + homepage URL
2. Contact: name + email
3. Public site URL (Enter to use root-relative links)
You can say "skip" to emit the feed anyway — it will be flagged as non-conformant.

Steps

1. Gather input from `$ARGUMENTS` (interview if thin)

Extract: `PORTAL_DIR` (default `.`), `PROFILES` (default `["dcat-3"]`), `SITE_URL`, `PUBLISHER` (name/homepage), `CONTACT` (fn/email), `LICENSE` (default dataset license URI), `THEMES` (EU data-theme URIs), `LANGUAGES`, `ACCESS_LEVEL` (DCAT-US, default `public`).

2. Validate the portal directory

Confirm `PORTAL_DIR/datasets.json`, `PORTAL_DIR/package.json`, and `PORTAL_DIR/lib/metadata/` exist. If `lib/metadata/` is missing, the portal predates the metadata c

Read more
Ships withportaljs

🌀 AI-native framework for building data portals. Scaffold a full portal from a brief and load datasets in minutes with agentic skills — any backend (CKAN, GitHub, Frictionless).

Get the whole plugin