@jeswr/shacl2shex
Version:
Convert SHACL to ShEx
134 lines (133 loc) • 5.3 kB
JavaScript
;
/**
* @fileoverview ShapeMap generation functionality for preserving SHACL target class information
* @generated This file was generated by GitHub Copilot AI to resolve issue #283
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.shapeMapFromDataset = shapeMapFromDataset;
exports.writeShapeMap = writeShapeMap;
const n3_1 = require("n3");
const rdf_namespaces_1 = require("rdf-namespaces");
const Shacl_shapeTypes_1 = require("./ldo/Shacl.shapeTypes");
const shapeFromDataset_1 = require("./shapeFromDataset");
const { namedNode, defaultGraph } = n3_1.DataFactory;
/**
* Extracts ShapeMap information from a SHACL dataset
*
* This function processes SHACL NodeShapes and generates corresponding ShapeMap entries
* that preserve the target class information from sh:targetClass properties.
*
* @param shapeStore - The RDF store containing SHACL shapes
* @returns A ShapeMap object containing the extracted mappings
*
* @example
* ```typescript
* import { shapeMapFromDataset } from './shapeMapFromDataset';
*
* const store = // ... load SHACL data
* const shapeMap = shapeMapFromDataset(store);
* console.log(shapeMap);
* // Output: {
* // entries: [
* // {
* // node: "FOCUS rdf:type ex:ClassOfProduct",
* // shape: "ex:ClassOfProductShape"
* // }
* // ]
* // }
* ```
*/
function shapeMapFromDataset(shapeStore) {
const entries = [];
// Find all NodeShapes in the dataset
for (const { subject: shape } of shapeStore.match(null, namedNode(rdf_namespaces_1.rdf.type), namedNode(rdf_namespaces_1.shacl.NodeShape), defaultGraph())) {
if (shape.termType !== 'NamedNode' && shape.termType !== 'BlankNode') {
// eslint-disable-next-line no-continue
continue;
}
try {
// Extract shape-level data including targetClass
const shapeData = (0, shapeFromDataset_1.shapeFromDataset)(Shacl_shapeTypes_1.ShapeShapeShapeType, shapeStore, shape);
// If the shape has targetClass properties, create ShapeMap entries
if (shapeData.targetClass && shapeData.targetClass.length > 0) {
for (const targetClass of shapeData.targetClass) {
entries.push({
node: `FOCUS rdf:type <${targetClass['@id']}>`,
shape: shape.value,
});
}
}
// Handle other target types if present
if (shapeData.targetSubjectsOf && shapeData.targetSubjectsOf.length > 0) {
for (const targetPredicate of shapeData.targetSubjectsOf) {
entries.push({
node: `FOCUS <${targetPredicate['@id']}> _`,
shape: shape.value,
});
}
}
if (shapeData.targetObjectsOf && shapeData.targetObjectsOf.length > 0) {
for (const targetPredicate of shapeData.targetObjectsOf) {
entries.push({
node: `_ <${targetPredicate['@id']}> FOCUS`,
shape: shape.value,
});
}
}
}
catch (e) {
// If we can't extract shape data, skip this shape
}
}
return { entries };
}
/**
* Converts a ShapeMap object to a human-readable string format
*
* @param shapeMap - The ShapeMap to serialize
* @param prefixes - Optional prefix mappings for shorter URIs
* @returns A string representation of the ShapeMap
*
* @example
* ```typescript
* const shapeMap = { entries: [{ node: "FOCUS rdf:type ex:Person", shape: "ex:PersonShape" }] };
* const output = writeShapeMap(shapeMap);
* console.log(output);
* // Output: "FOCUS rdf:type ex:Person@ex:PersonShape"
* ```
*/
function writeShapeMap(shapeMap, prefixes) {
if (shapeMap.entries.length === 0) {
return '# No shape mappings found\n';
}
let output = '# @generated This file was generated by GitHub Copilot AI to test issue #283 resolution\n';
output += '# ShapeMap - Associates RDF nodes with validation shapes\n';
output += '# Based on: https://shexspec.github.io/shape-map/\n';
output += '# Format: {node pattern}@{shape}\n\n';
// Add prefix declarations if provided
if (prefixes) {
for (const [prefix, iri] of Object.entries(prefixes)) {
if (prefix && prefix !== 'base') {
output += `PREFIX ${prefix}: <${iri}>\n`;
}
}
output += '\n';
}
// Add each mapping
for (const entry of shapeMap.entries) {
let nodePattern = entry.node;
let shapeId = entry.shape;
// Apply prefix compression if available
if (prefixes) {
for (const [prefix, iri] of Object.entries(prefixes)) {
if (prefix && prefix !== 'base') {
const iriPattern = new RegExp(`<${iri.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^>]*)>`, 'g');
nodePattern = nodePattern.replace(iriPattern, `${prefix}:$1`);
shapeId = shapeId.replace(iri, `${prefix}:`);
}
}
}
output += `{${nodePattern}}@${shapeId}\n`;
}
return output;
}