Skip to content

feat: SHACL → schema CLI converters (closes #140)#172

Merged
karelklima merged 1 commit into
karelklima:mainfrom
renanpontez:feat/shacl-to-schema
May 17, 2026
Merged

feat: SHACL → schema CLI converters (closes #140)#172
karelklima merged 1 commit into
karelklima:mainfrom
renanpontez:feat/shacl-to-schema

Conversation

@renanpontez
Copy link
Copy Markdown
Contributor

Closes #140.

Adds two CLI subcommands deriving LDkit schemas from SHACL, parallel to the existing shexc-to-schema / context-to-schema:

  • shacl-to-schema <method> <input> — single-file output
  • shacl-to-package <method> <input> <outDir> — multi-file (one .ts per namespace + barrel index.ts, with cross-file imports for relations). Enables tree-shaking for large vocabularies.

Supported SHACL: sh:targetClass, sh:property / sh:path (incl. sh:inversePath), sh:datatype, sh:nodeKind sh:IRI, sh:node, sh:class, cardinality (sh:minCount / sh:maxCount), sh:and / sh:or / sh:in, sh:uniqueLang. Self-refs and duplicate-path merging handled.

Namespace-aware naming: identifiers prefixed by the declared @prefix (e.g. meta:CampaignMetaCampaignSchema) to disambiguate cross-vocabulary local-part collisions. --prefix-alias prefix=Alias renames short prefixes.

Default-safe cross-refs: sh:node X emits "@type": ldkit.IRI, not "@schema": XSchema — avoids accidental recursive graph walks via findByIri(). Consumers opt into nested decoding at call sites.

31 unit tests added (shacl_to_schema.test.ts 28, shacl_to_package.test.ts 3). No breaking changes.

Adds two CLI subcommands deriving LDkit schemas from SHACL shapes:

- shacl-to-schema <method> <input>           — single-file output, parallels
  the existing shexc-to-schema / context-to-schema converters.
- shacl-to-package <method> <input> <outDir> — multi-file output (one .ts
  per namespace + a barrel index.ts), with cross-file imports for relations.
  Enables tree-shaking for large vocabularies.

Highlights:

- Idiomatic SHACL support: sh:targetClass, sh:property, sh:path (incl.
  sh:inversePath), sh:datatype, sh:nodeKind sh:IRI, sh:node, sh:class,
  sh:minCount / sh:maxCount, sh:uniqueLang, sh:and / sh:or / sh:in.
  Self-references and merge of duplicate sh:property paths handled.

- Namespace-aware naming: schema names are prefixed by their declared
  @Prefix (meta:Campaign → MetaCampaignSchema) to disambiguate
  cross-vocabulary local-part collisions. --prefix-alias prefix=Alias
  renames short prefixes (e.g. m: → Marketer*).

- Default-safe cross-refs: sh:node X emits "@type": ldkit.IRI by default
  (not "@Schema": XSchema) so findByIri() doesn't recursively walk a whole
  graph. Consumers opt into nested decoding at call sites via spread.

- Multi-file output (shacl-to-package): per-namespace files import their
  namespace consts from a single namespaces.ts to break ESM cycles.
  Drop-in compatible: import { XSchema } from "./.ldkit" still works via
  the barrel.

Coverage:
- 28 unit tests in tests/scripts/shacl_to_schema.test.ts
- 3 unit tests in tests/scripts/shacl_to_package.test.ts
- All existing tests still pass

Breaking changes: none — purely additive. Existing CLI commands
(shexc-to-schema, shexj-to-schema, context-to-schema) untouched.
@karelklima
Copy link
Copy Markdown
Owner

Thanks for the PR, I'll review it in the next few days. After it's merged I'll prepare a new release.

@karelklima karelklima merged commit b09bfde into karelklima:main May 17, 2026
2 of 3 checks passed
@karelklima
Copy link
Copy Markdown
Owner

Thanks again for the PR, I completed the code review, ran some tests and verified that the new CLI converters work as expected. Released in https://www.npmjs.com/package/ldkit/v/2.7.0

FTR the test pipeline failed because of a Deno issue, unrelated to the code that you contributed.

karelklima pushed a commit that referenced this pull request May 20, 2026
…mports (#174)

## Summary

Two small follow-ups to #172 (SHACL → schema CLI converters):

1. **Deterministic property order.** 
a. `n3` Store quad iteration order isn't guaranteed stable across
parses, so re-running `shacl-to-schema` against the same input could
produce noisy diffs in generated files.
b. **Properties are now sorted alphabetically before emit. Adds a
regression test covering this**.

2. **Workerd-friendly `createNamespace` import path.** 
a. The generated code (and a couple of internal scripts) imported
`createNamespace` from the root `ldkit` entrypoint. That entrypoint
transitively pulls in modules that don't load in Cloudflare Workers /
workerd.
b. Switched all 4 call sites (`scripts/extract_namespace.ts`,
`scripts/schema_to_script.ts`, `scripts/schema_to_package.ts`, plus the
generator) to import from `ldkit/namespaces`, which is the narrow,
side-effect-free entrypoint.
c. Tests updated to match.

Both issues were surfaced while consuming the converter in a
workerd-deployed project after #172 landed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add SHACL to LDkit schema converter

2 participants