@shaclmate/compiler 2.0.14 → 2.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/_ShapesGraphToAstTransformer/transformNodeShapeToAstType.js +22 -7
  2. package/_ShapesGraphToAstTransformer/transformPropertyShapeToAstCompositeType.js +5 -3
  3. package/ast/ListType.d.ts +2 -2
  4. package/ast/ObjectType.d.ts +18 -12
  5. package/enums/IdentifierMintingStrategy.d.ts +5 -0
  6. package/enums/IdentifierMintingStrategy.js +2 -0
  7. package/enums/index.d.ts +1 -1
  8. package/enums/index.js +1 -1
  9. package/generators/json/AstJsonGenerator.js +5 -1
  10. package/generators/ts/DateTimeType.d.ts +3 -1
  11. package/generators/ts/DateTimeType.js +9 -1
  12. package/generators/ts/IdentifierType.d.ts +2 -0
  13. package/generators/ts/IdentifierType.js +11 -0
  14. package/generators/ts/Import.d.ts +0 -1
  15. package/generators/ts/Import.js +0 -5
  16. package/generators/ts/ListType.d.ts +4 -3
  17. package/generators/ts/ListType.js +27 -16
  18. package/generators/ts/ObjectType.d.ts +5 -4
  19. package/generators/ts/ObjectType.js +21 -9
  20. package/generators/ts/ObjectUnionType.d.ts +1 -1
  21. package/generators/ts/ObjectUnionType.js +22 -52
  22. package/generators/ts/OptionType.d.ts +3 -1
  23. package/generators/ts/OptionType.js +12 -11
  24. package/generators/ts/PrimitiveType.d.ts +2 -2
  25. package/generators/ts/PrimitiveType.js +9 -4
  26. package/generators/ts/SetType.d.ts +3 -1
  27. package/generators/ts/SetType.js +12 -8
  28. package/generators/ts/SnippetDeclarations.d.ts +11 -0
  29. package/generators/ts/SnippetDeclarations.js +215 -0
  30. package/generators/ts/TermType.d.ts +3 -1
  31. package/generators/ts/TermType.js +16 -8
  32. package/generators/ts/TsGenerator.js +15 -1
  33. package/generators/ts/Type.d.ts +15 -5
  34. package/generators/ts/Type.js +17 -0
  35. package/generators/ts/TypeFactory.js +18 -8
  36. package/generators/ts/UnionType.d.ts +2 -1
  37. package/generators/ts/UnionType.js +3 -3
  38. package/generators/ts/_ObjectType/IdentifierProperty.d.ts +7 -8
  39. package/generators/ts/_ObjectType/IdentifierProperty.js +99 -25
  40. package/generators/ts/_ObjectType/Property.d.ts +17 -3
  41. package/generators/ts/_ObjectType/Property.js +2 -1
  42. package/generators/ts/_ObjectType/ShaclProperty.d.ts +1 -0
  43. package/generators/ts/_ObjectType/ShaclProperty.js +4 -1
  44. package/generators/ts/_ObjectType/TypeDiscriminatorProperty.d.ts +3 -5
  45. package/generators/ts/_ObjectType/TypeDiscriminatorProperty.js +11 -4
  46. package/generators/ts/_ObjectType/equalsFunctionOrMethodDeclaration.js +1 -1
  47. package/generators/ts/_ObjectType/fromJsonFunctionDeclarations.js +1 -1
  48. package/generators/ts/_ObjectType/fromRdfFunctionDeclarations.js +1 -1
  49. package/generators/ts/_ObjectType/fromRdfTypeVariableStatement.d.ts +5 -0
  50. package/generators/ts/_ObjectType/fromRdfTypeVariableStatement.js +25 -0
  51. package/generators/ts/_ObjectType/index.d.ts +1 -0
  52. package/generators/ts/_ObjectType/index.js +1 -0
  53. package/generators/ts/_ObjectType/sparqlConstructQueryFunctionDeclaration.d.ts +5 -0
  54. package/generators/ts/_ObjectType/sparqlConstructQueryFunctionDeclaration.js +21 -0
  55. package/generators/ts/_ObjectType/sparqlConstructQueryStringFunctionDeclaration.d.ts +5 -0
  56. package/generators/ts/_ObjectType/sparqlConstructQueryStringFunctionDeclaration.js +20 -0
  57. package/generators/ts/_ObjectType/sparqlFunctionDeclarations.js +35 -58
  58. package/generators/ts/_ObjectType/toJsonFunctionOrMethodDeclaration.js +1 -1
  59. package/generators/ts/_ObjectType/toRdfFunctionOrMethodDeclaration.js +3 -3
  60. package/input/NodeShape.d.ts +2 -2
  61. package/input/generated.d.ts +24 -16
  62. package/input/generated.js +53 -8
  63. package/input/tsFeatures.d.ts +1 -1
  64. package/input/tsFeatures.js +21 -13
  65. package/package.json +4 -4
  66. package/enums/MintingStrategy.d.ts +0 -5
  67. package/enums/MintingStrategy.js +0 -2
@@ -9,6 +9,7 @@ import { Maybe } from "purify-ts";
9
9
  import { invariant } from "ts-invariant";
10
10
  import { Memoize } from "typescript-memoize";
11
11
  import { Import } from "./Import.js";
12
+ import { SnippetDeclarations } from "./SnippetDeclarations.js";
12
13
  import { Type } from "./Type.js";
13
14
  import { objectInitializer } from "./objectInitializer.js";
14
15
  /**
@@ -17,7 +18,7 @@ import { objectInitializer } from "./objectInitializer.js";
17
18
  export class TermType extends Type {
18
19
  constructor({ defaultValue, hasValues, in_, nodeKinds, ...superParameters }) {
19
20
  super(superParameters);
20
- this.equalsFunction = "purifyHelpers.Equatable.booleanEquals";
21
+ this.equalsFunction = "booleanEquals";
21
22
  this.mutable = false;
22
23
  this.defaultValue = defaultValue;
23
24
  this.hasValues = hasValues;
@@ -80,13 +81,6 @@ export class TermType extends Type {
80
81
  .map((nodeKind) => `rdfjs.${nodeKind}`)
81
82
  .join(" | ")})`;
82
83
  }
83
- get useImports() {
84
- const imports = [Import.RDFJS_TYPES];
85
- if (this.nodeKinds.has("Literal")) {
86
- imports.push(Import.RDF_LITERAL);
87
- }
88
- return imports;
89
- }
90
84
  fromJsonExpression({ variables, }) {
91
85
  invariant(this.nodeKinds.has("Literal") &&
92
86
  (this.nodeKinds.has("BlankNode") || this.nodeKinds.has("NamedNode")), "IdentifierType and LiteralType should override");
@@ -161,6 +155,13 @@ export class TermType extends Type {
161
155
  })
162
156
  .join(", ")}])`;
163
157
  }
158
+ snippetDeclarations(features) {
159
+ const snippetDeclarations = [];
160
+ if (features.has("equals")) {
161
+ snippetDeclarations.push(SnippetDeclarations.booleanEquals);
162
+ }
163
+ return snippetDeclarations;
164
+ }
164
165
  sparqlWherePatterns(parameters) {
165
166
  switch (parameters.context) {
166
167
  case "property":
@@ -201,6 +202,13 @@ export class TermType extends Type {
201
202
  .map((defaultValue) => `!${variables.value}.equals(${this.rdfjsTermExpression(defaultValue)}) ? ${variables.value} : undefined`)
202
203
  .orDefault(variables.value);
203
204
  }
205
+ useImports() {
206
+ const imports = [Import.RDFJS_TYPES];
207
+ if (this.nodeKinds.has("Literal")) {
208
+ imports.push(Import.RDF_LITERAL);
209
+ }
210
+ return imports;
211
+ }
204
212
  /**
205
213
  * Filter the rdfjsResource.Resource.Values to those that are relevant to the type.
206
214
  *
@@ -30,9 +30,13 @@ export class TsGenerator {
30
30
  addDeclarations({ objectTypes, objectUnionTypes, sourceFile, }) {
31
31
  // sourceFile.addStatements(this.configuration.dataFactoryImport);
32
32
  sourceFile.addStatements('import { DataFactory as dataFactory } from "n3"');
33
+ const declaredTypes = [
34
+ ...objectTypes,
35
+ ...objectUnionTypes,
36
+ ];
33
37
  // Gather imports
34
38
  const imports = [];
35
- for (const declaredType of [...objectTypes, ...objectUnionTypes]) {
39
+ for (const declaredType of declaredTypes) {
36
40
  imports.push(...declaredType.declarationImports);
37
41
  }
38
42
  // Deduplicate and add imports
@@ -51,6 +55,16 @@ export class TsGenerator {
51
55
  addedStructureImports.push(import_);
52
56
  }
53
57
  }
58
+ // Deduplicate and add snippet declarations
59
+ const addedSnippetDeclarations = new Set();
60
+ for (const declaredType of declaredTypes) {
61
+ for (const snippetDeclaration of declaredType.snippetDeclarations(declaredType.features)) {
62
+ if (!addedSnippetDeclarations.has(snippetDeclaration)) {
63
+ sourceFile.addStatements([snippetDeclaration]);
64
+ addedSnippetDeclarations.add(snippetDeclaration);
65
+ }
66
+ }
67
+ }
54
68
  // Add type declarations
55
69
  for (const objectType of objectTypes) {
56
70
  sourceFile.addStatements(objectType.declarations);
@@ -1,5 +1,6 @@
1
1
  import type { BlankNode, Literal, NamedNode, Variable } from "@rdfjs/types";
2
2
  import { Maybe } from "purify-ts";
3
+ import type { TsFeature } from "../../enums/index.js";
3
4
  import type { Import } from "./Import.js";
4
5
  /**
5
6
  * Abstract base class for generating TypeScript expressions and statemenst in the TypeScript generator.
@@ -13,7 +14,7 @@ export declare abstract class Type {
13
14
  abstract readonly conversions: readonly Type.Conversion[];
14
15
  /**
15
16
  * A function (reference or declaration) that compares two property values of this type, returning a
16
- * purifyHelpers.Equatable.EqualsResult.
17
+ * EqualsResult.
17
18
  */
18
19
  abstract readonly equalsFunction: string;
19
20
  /**
@@ -28,10 +29,6 @@ export declare abstract class Type {
28
29
  * Name of the type.
29
30
  */
30
31
  abstract readonly name: string;
31
- /**
32
- * Imports necessary to use this type.
33
- */
34
- abstract readonly useImports: readonly Import[];
35
32
  protected readonly dataFactoryVariable: string;
36
33
  constructor({ dataFactoryVariable, }: {
37
34
  dataFactoryVariable: string;
@@ -87,6 +84,15 @@ export declare abstract class Type {
87
84
  zod: string;
88
85
  };
89
86
  }): string;
87
+ /**
88
+ * Reusable function, type, and other declarations that are not particular to this type but that type-specific code
89
+ * relies on. For example, the equals function/method of ObjectType has a custom return type that's the same across all
90
+ * ObjectType's. Instead of re-declaring the return type anonymously on every equals function, declare a named type
91
+ * as a snippet and reference it.
92
+ *
93
+ * The generator deduplicates snippet declarations across all types before adding them to the source.
94
+ */
95
+ snippetDeclarations(_features: Set<TsFeature>): readonly string[];
90
96
  /**
91
97
  * An array of SPARQL.js CONSTRUCT template triples for a value of this type, as strings (so they can incorporate runtime calls).
92
98
  *
@@ -153,6 +159,10 @@ export declare abstract class Type {
153
159
  value: string;
154
160
  };
155
161
  }): string;
162
+ /**
163
+ * Imports necessary to use this type.
164
+ */
165
+ useImports(_features: Set<TsFeature>): readonly Import[];
156
166
  protected rdfjsTermExpression(rdfjsTerm: Omit<BlankNode, "equals"> | Omit<Literal, "equals"> | Omit<NamedNode, "equals"> | Omit<Variable, "equals">): string;
157
167
  }
158
168
  export declare namespace Type {
@@ -23,6 +23,17 @@ export class Type {
23
23
  jsonUiSchemaElement(_parameters) {
24
24
  return Maybe.empty();
25
25
  }
26
+ /**
27
+ * Reusable function, type, and other declarations that are not particular to this type but that type-specific code
28
+ * relies on. For example, the equals function/method of ObjectType has a custom return type that's the same across all
29
+ * ObjectType's. Instead of re-declaring the return type anonymously on every equals function, declare a named type
30
+ * as a snippet and reference it.
31
+ *
32
+ * The generator deduplicates snippet declarations across all types before adding them to the source.
33
+ */
34
+ snippetDeclarations(_features) {
35
+ return [];
36
+ }
26
37
  /**
27
38
  * An array of SPARQL.js CONSTRUCT template triples for a value of this type, as strings (so they can incorporate runtime calls).
28
39
  *
@@ -90,6 +101,12 @@ export class Type {
90
101
  return [];
91
102
  }
92
103
  }
104
+ /**
105
+ * Imports necessary to use this type.
106
+ */
107
+ useImports(_features) {
108
+ return [];
109
+ }
93
110
  rdfjsTermExpression(rdfjsTerm) {
94
111
  return rdfjsTermExpression({
95
112
  dataFactoryVariable: this.dataFactoryVariable,
@@ -214,8 +214,8 @@ export class TypeFactory {
214
214
  dataFactoryVariable: this.dataFactoryVariable,
215
215
  defaultValue: Maybe.empty(),
216
216
  hasValues: [],
217
- in_: [],
218
- nodeKinds: astType.nodeKinds,
217
+ in_: astType.identifierIn,
218
+ nodeKinds: astType.identifierKinds,
219
219
  });
220
220
  const objectType = new ObjectType({
221
221
  abstract: astType.abstract,
@@ -232,7 +232,7 @@ export class TypeFactory {
232
232
  lazyDescendantObjectTypes: () => astType.descendantObjectTypes.map((astType) => this.createObjectTypeFromAstType(astType)),
233
233
  lazyParentObjectTypes: () => astType.parentObjectTypes.map((astType) => this.createObjectTypeFromAstType(astType)),
234
234
  lazyProperties: () => {
235
- const properties = astType.properties.map((astProperty) => this.createObjectTypePropertyFromAstProperty(astProperty));
235
+ const properties = astType.properties.map((astProperty) => this.createObjectTypePropertyFromAstProperty(astType, astProperty));
236
236
  let identifierPropertyClassDeclarationVisibility;
237
237
  if (astType.abstract) {
238
238
  // If the type is abstract, don't declare a property.
@@ -253,10 +253,13 @@ export class TypeFactory {
253
253
  abstract: astType.abstract,
254
254
  classDeclarationVisibility: identifierPropertyClassDeclarationVisibility,
255
255
  dataFactoryVariable: this.dataFactoryVariable,
256
- mintingStrategy: astType.mintingStrategy,
256
+ identifierMintingStrategy: astType.identifierMintingStrategy,
257
257
  name: astType.tsIdentifierPropertyName,
258
258
  lazyObjectTypeMutable: () => properties.some((property) => property.mutable || property.type.mutable),
259
- objectTypeDeclarationType: astType.tsObjectDeclarationType,
259
+ objectType: {
260
+ declarationType: astType.tsObjectDeclarationType,
261
+ features: astType.tsFeatures,
262
+ },
260
263
  override: astType.parentObjectTypes.length > 0,
261
264
  type: identifierType,
262
265
  visibility: "public",
@@ -277,7 +280,10 @@ export class TypeFactory {
277
280
  abstract: astType.abstract,
278
281
  dataFactoryVariable: this.dataFactoryVariable,
279
282
  name: astType.tsTypeDiscriminatorPropertyName,
280
- objectTypeDeclarationType: objectType.declarationType,
283
+ objectType: {
284
+ declarationType: astType.tsObjectDeclarationType,
285
+ features: astType.tsFeatures,
286
+ },
281
287
  override: objectType.parentObjectTypes.length > 0,
282
288
  type: new ObjectType.TypeDiscriminatorProperty.Type({
283
289
  mutable: false,
@@ -289,14 +295,14 @@ export class TypeFactory {
289
295
  }
290
296
  return properties.sort((left, right) => left.name.localeCompare(right.name));
291
297
  },
292
- mintingStrategy: astType.mintingStrategy,
298
+ mintingStrategy: astType.identifierMintingStrategy,
293
299
  name: tsName(astType.name),
294
300
  toRdfTypes: astType.toRdfTypes,
295
301
  });
296
302
  this.cachedObjectTypesByIdentifier.set(astType.name.identifier, objectType);
297
303
  return objectType;
298
304
  }
299
- createObjectTypePropertyFromAstProperty(astObjectTypeProperty) {
305
+ createObjectTypePropertyFromAstProperty(astObjectType, astObjectTypeProperty) {
300
306
  {
301
307
  const cachedProperty = this.cachedObjectTypePropertiesByIdentifier.get(astObjectTypeProperty.name.identifier);
302
308
  if (cachedProperty) {
@@ -309,6 +315,10 @@ export class TypeFactory {
309
315
  description: astObjectTypeProperty.description,
310
316
  label: astObjectTypeProperty.label,
311
317
  mutable: astObjectTypeProperty.mutable.orDefault(false),
318
+ objectType: {
319
+ declarationType: astObjectType.tsObjectDeclarationType,
320
+ features: astObjectType.tsFeatures,
321
+ },
312
322
  name: tsName(astObjectTypeProperty.name),
313
323
  path: astObjectTypeProperty.path.iri,
314
324
  type: this.createTypeFromAstType(astObjectTypeProperty.type),
@@ -1,4 +1,5 @@
1
1
  import { Maybe } from "purify-ts";
2
+ import type { TsFeature } from "../../enums/index.js";
2
3
  import type { Import } from "./Import.js";
3
4
  import { Type } from "./Type.js";
4
5
  export declare class UnionType extends Type {
@@ -15,7 +16,6 @@ export declare class UnionType extends Type {
15
16
  get equalsFunction(): string;
16
17
  get jsonName(): string;
17
18
  get mutable(): boolean;
18
- get useImports(): readonly Import[];
19
19
  private get _discriminatorProperty();
20
20
  private get memberTypeTraits();
21
21
  fromJsonExpression({ variables, }: Parameters<Type["fromJsonExpression"]>[0]): string;
@@ -26,6 +26,7 @@ export declare class UnionType extends Type {
26
26
  sparqlWherePatterns(parameters: Parameters<Type["sparqlWherePatterns"]>[0]): readonly string[];
27
27
  toJsonExpression({ variables, }: Parameters<Type["toJsonExpression"]>[0]): string;
28
28
  toRdfExpression({ variables, }: Parameters<Type["toRdfExpression"]>[0]): string;
29
+ useImports(features: Set<TsFeature>): readonly Import[];
29
30
  private ternaryExpression;
30
31
  }
31
32
  //# sourceMappingURL=UnionType.d.ts.map
@@ -71,9 +71,6 @@ ${this.memberTypeTraits
71
71
  get mutable() {
72
72
  return this.memberTypes.some((memberType) => memberType.mutable);
73
73
  }
74
- get useImports() {
75
- return this.memberTypes.flatMap((memberType) => memberType.useImports);
76
- }
77
74
  get _discriminatorProperty() {
78
75
  let sharedDiscriminatorProperty;
79
76
  for (const memberType of this.memberTypes) {
@@ -252,6 +249,9 @@ ${this.memberTypeTraits
252
249
  variables,
253
250
  });
254
251
  }
252
+ useImports(features) {
253
+ return this.memberTypes.flatMap((memberType) => memberType.useImports(features));
254
+ }
255
255
  ternaryExpression({ memberTypeExpression, variables, }) {
256
256
  return this.memberTypeTraits.reduce((expression, memberTypeTraits) => {
257
257
  if (expression.length === 0) {
@@ -1,24 +1,22 @@
1
1
  import { Maybe } from "purify-ts";
2
2
  import type { GetAccessorDeclarationStructure, OptionalKind, PropertyDeclarationStructure, PropertySignatureStructure } from "ts-morph";
3
- import type { MintingStrategy, PropertyVisibility, TsObjectDeclarationType } from "../../../enums/index.js";
3
+ import type { IdentifierMintingStrategy, PropertyVisibility } from "../../../enums/index.js";
4
4
  import type { IdentifierType } from "../IdentifierType.js";
5
5
  import { Import } from "../Import.js";
6
6
  import { Property } from "./Property.js";
7
7
  export declare class IdentifierProperty extends Property<IdentifierType> {
8
8
  readonly abstract: boolean;
9
- readonly equalsFunction = "purifyHelpers.Equatable.booleanEquals";
9
+ readonly equalsFunction = "booleanEquals";
10
10
  readonly mutable = false;
11
11
  private readonly classDeclarationVisibility;
12
+ private readonly identifierMintingStrategy;
12
13
  private readonly lazyObjectTypeMutable;
13
- private readonly mintingStrategy;
14
- private readonly objectTypeDeclarationType;
15
14
  private readonly override;
16
- constructor({ abstract, classDeclarationVisibility, lazyObjectTypeMutable, mintingStrategy, objectTypeDeclarationType, override, ...superParameters }: {
15
+ constructor({ abstract, classDeclarationVisibility, lazyObjectTypeMutable, identifierMintingStrategy, override, ...superParameters }: {
17
16
  abstract: boolean;
18
17
  classDeclarationVisibility: Maybe<PropertyVisibility>;
19
18
  lazyObjectTypeMutable: () => boolean;
20
- mintingStrategy: Maybe<MintingStrategy>;
21
- objectTypeDeclarationType: TsObjectDeclarationType;
19
+ identifierMintingStrategy: Maybe<IdentifierMintingStrategy>;
22
20
  override: boolean;
23
21
  type: IdentifierType;
24
22
  } & ConstructorParameters<typeof Property>[0]);
@@ -28,10 +26,11 @@ export declare class IdentifierProperty extends Property<IdentifierType> {
28
26
  get declarationImports(): readonly Import[];
29
27
  get interfacePropertySignature(): OptionalKind<PropertySignatureStructure>;
30
28
  get jsonPropertySignature(): OptionalKind<PropertySignatureStructure>;
29
+ get snippetDeclarations(): readonly string[];
31
30
  classConstructorStatements({ variables, }: Parameters<Property<IdentifierType>["classConstructorStatements"]>[0]): readonly string[];
32
31
  fromJsonStatements({ variables, }: Parameters<Property<IdentifierType>["fromJsonStatements"]>[0]): readonly string[];
33
32
  fromRdfStatements({ variables, }: Parameters<Property<IdentifierType>["fromRdfStatements"]>[0]): readonly string[];
34
- hashStatements(): readonly string[];
33
+ hashStatements({ variables, }: Parameters<Property<IdentifierType>["hashStatements"]>[0]): readonly string[];
35
34
  interfaceConstructorStatements({ variables, }: Parameters<Property<IdentifierType>["interfaceConstructorStatements"]>[0]): readonly string[];
36
35
  jsonUiSchemaElement({ variables, }: Parameters<Property<IdentifierType>["jsonUiSchemaElement"]>[0]): Maybe<string>;
37
36
  jsonZodSchema({ variables, }: Parameters<Property<IdentifierType>["jsonZodSchema"]>[0]): ReturnType<Property<IdentifierType>["jsonZodSchema"]>;
@@ -1,25 +1,26 @@
1
+ import { rdf } from "@tpluscode/rdf-ns-builders";
1
2
  import { Maybe } from "purify-ts";
2
3
  import { invariant } from "ts-invariant";
3
4
  import { Import } from "../Import.js";
5
+ import { SnippetDeclarations } from "../SnippetDeclarations.js";
4
6
  import { Property } from "./Property.js";
5
7
  export class IdentifierProperty extends Property {
6
- constructor({ abstract, classDeclarationVisibility, lazyObjectTypeMutable, mintingStrategy, objectTypeDeclarationType, override, ...superParameters }) {
8
+ constructor({ abstract, classDeclarationVisibility, lazyObjectTypeMutable, identifierMintingStrategy, override, ...superParameters }) {
7
9
  super(superParameters);
8
- this.equalsFunction = "purifyHelpers.Equatable.booleanEquals";
10
+ this.equalsFunction = "booleanEquals";
9
11
  this.mutable = false;
10
12
  invariant(this.visibility === "public");
11
13
  this.abstract = abstract;
12
14
  this.classDeclarationVisibility = classDeclarationVisibility;
13
- if (mintingStrategy.isJust()) {
14
- this.mintingStrategy = mintingStrategy.unsafeCoerce();
15
+ if (identifierMintingStrategy.isJust()) {
16
+ this.identifierMintingStrategy = identifierMintingStrategy.unsafeCoerce();
15
17
  }
16
18
  else if (this.type.nodeKinds.has("BlankNode")) {
17
- this.mintingStrategy = "blankNode";
19
+ this.identifierMintingStrategy = "blankNode";
18
20
  }
19
21
  else {
20
- this.mintingStrategy = "none";
22
+ this.identifierMintingStrategy = "none";
21
23
  }
22
- this.objectTypeDeclarationType = objectTypeDeclarationType;
23
24
  this.lazyObjectTypeMutable = lazyObjectTypeMutable;
24
25
  this.override = override;
25
26
  }
@@ -28,7 +29,7 @@ export class IdentifierProperty extends Property {
28
29
  return Maybe.empty();
29
30
  }
30
31
  let mintIdentifier;
31
- switch (this.mintingStrategy) {
32
+ switch (this.identifierMintingStrategy) {
32
33
  case "blankNode":
33
34
  mintIdentifier = "dataFactory.blankNode()";
34
35
  break;
@@ -72,10 +73,11 @@ export class IdentifierProperty extends Property {
72
73
  if (!this.classDeclarationVisibility.isJust()) {
73
74
  return Maybe.empty();
74
75
  }
75
- switch (this.mintingStrategy) {
76
+ switch (this.identifierMintingStrategy) {
76
77
  case "none":
77
78
  // Immutable, public identifier property, no getter
78
79
  return Maybe.of({
80
+ isReadonly: true,
79
81
  name: this.name,
80
82
  type: this.type.name,
81
83
  });
@@ -91,21 +93,28 @@ export class IdentifierProperty extends Property {
91
93
  }
92
94
  }
93
95
  get constructorParametersPropertySignature() {
94
- if (this.objectTypeDeclarationType === "class" && this.abstract) {
96
+ if (this.objectType.declarationType === "class" && this.abstract) {
95
97
  return Maybe.empty();
96
98
  }
99
+ const typeNames = new Set(); // Remove duplicates with a set
100
+ for (const conversion of this.type.conversions) {
101
+ if (conversion.sourceTypeName !== "undefined") {
102
+ typeNames.add(conversion.sourceTypeName);
103
+ }
104
+ }
97
105
  return Maybe.of({
98
- hasQuestionToken: this.objectTypeDeclarationType === "class" &&
99
- this.mintingStrategy !== "none",
106
+ hasQuestionToken: this.objectType.declarationType === "class" &&
107
+ this.identifierMintingStrategy !== "none",
100
108
  isReadonly: true,
101
109
  name: this.name,
102
- type: this.type.name,
110
+ type: [...typeNames].sort().join(" | "),
103
111
  });
104
112
  }
105
113
  get declarationImports() {
106
- const imports = this.type.useImports.concat();
107
- if (this.objectTypeDeclarationType === "class") {
108
- switch (this.mintingStrategy) {
114
+ const imports = this.type.useImports().concat();
115
+ if (this.objectType.features.has("hash") &&
116
+ this.objectType.declarationType === "class") {
117
+ switch (this.identifierMintingStrategy) {
109
118
  case "sha256":
110
119
  imports.push(Import.SHA256);
111
120
  break;
@@ -130,15 +139,38 @@ export class IdentifierProperty extends Property {
130
139
  type: "string",
131
140
  };
132
141
  }
142
+ get snippetDeclarations() {
143
+ const snippetDeclarations = [];
144
+ if (this.objectType.features.has("equals")) {
145
+ snippetDeclarations.push(SnippetDeclarations.booleanEquals);
146
+ }
147
+ return snippetDeclarations;
148
+ }
133
149
  classConstructorStatements({ variables, }) {
134
150
  if (this.abstract) {
135
151
  return [];
136
152
  }
137
- return this.classPropertyDeclaration
138
- .map((classPropertyDeclaration) => [
139
- `this.${classPropertyDeclaration.name} = ${variables.parameter};`,
140
- ])
141
- .orDefault([]);
153
+ if (this.classPropertyDeclaration.isNothing()) {
154
+ return [];
155
+ }
156
+ const classPropertyDeclaration = this.classPropertyDeclaration.unsafeCoerce();
157
+ const typeConversions = this.type.conversions;
158
+ if (typeConversions.length === 1) {
159
+ return [
160
+ `this.${classPropertyDeclaration.name} = ${variables.parameter};`,
161
+ ];
162
+ }
163
+ const statements = [];
164
+ for (const conversion of this.type.conversions) {
165
+ if (conversion.sourceTypeName !== "undefined") {
166
+ statements.push(`if (${conversion.sourceTypeCheckExpression(variables.parameter)}) { this.${classPropertyDeclaration.name} = ${conversion.conversionExpression(variables.parameter)}; }`);
167
+ }
168
+ }
169
+ if (!classPropertyDeclaration.hasQuestionToken) {
170
+ // We shouldn't need this else, since the parameter now has the never type, but have to add it to appease the TypeScript compiler
171
+ statements.push(`{ this.${classPropertyDeclaration.name} =( ${variables.parameter}) as never;\n }`);
172
+ }
173
+ return [statements.join(" else ")];
142
174
  }
143
175
  fromJsonStatements({ variables, }) {
144
176
  return [
@@ -146,21 +178,63 @@ export class IdentifierProperty extends Property {
146
178
  ];
147
179
  }
148
180
  fromRdfStatements({ variables, }) {
181
+ if (this.type.in_.length > 0 && this.type.isNamedNodeKind) {
182
+ // Treat sh:in as a union of the IRIs
183
+ // rdfjs.NamedNode<"http://example.com/1" | "http://example.com/2">
184
+ return [
185
+ `let ${this.name}: ${this.type.name};`,
186
+ `switch (${variables.resource}.identifier.value) { ${this.type.in_.map((iri) => `case "${iri.value}": ${this.name} = ${this.rdfjsTermExpression(iri)}; break;`).join(" ")} default: return purify.Left(new rdfjsResource.Resource.MistypedValueError({ actualValue: ${variables.resource}.identifier, expectedValueType: ${JSON.stringify(this.type.name)}, focusResource: ${variables.resource}, predicate: ${this.rdfjsTermExpression(rdf.subject)} })); }`,
187
+ ];
188
+ }
149
189
  return [`const ${this.name} = ${variables.resource}.identifier`];
150
190
  }
151
- hashStatements() {
152
- return [];
191
+ hashStatements({ variables, }) {
192
+ if (this.abstract) {
193
+ // Identifier will only be hashed by a concrete class.
194
+ return [];
195
+ }
196
+ switch (this.identifierMintingStrategy) {
197
+ case "blankNode":
198
+ case "none":
199
+ case "uuidv4":
200
+ // The identifier minting won't call hash, so we should hash the identifier.
201
+ return [`${variables.hasher}.update(${variables.value}.value);`];
202
+ case "sha256":
203
+ // The identifier minting will call hash, so we can't hash the identifier.
204
+ return [];
205
+ }
153
206
  }
154
207
  interfaceConstructorStatements({ variables, }) {
155
- return [`const ${this.name} = ${variables.parameter}`];
208
+ const typeConversions = this.type.conversions;
209
+ if (typeConversions.length === 1) {
210
+ return [`const ${this.name} = ${variables.parameter};`];
211
+ }
212
+ const statements = [`let ${this.name}: ${this.type.name};`];
213
+ const conversionBranches = [];
214
+ for (const conversion of this.type.conversions) {
215
+ conversionBranches.push(`if (${conversion.sourceTypeCheckExpression(variables.parameter)}) { ${this.name} = ${conversion.conversionExpression(variables.parameter)}; }`);
216
+ }
217
+ // We shouldn't need this else, since the parameter now has the never type, but have to add it to appease the TypeScript compiler
218
+ conversionBranches.push(`{ ${this.name} =( ${variables.parameter}) as never;\n }`);
219
+ statements.push(conversionBranches.join(" else "));
220
+ return statements;
156
221
  }
157
222
  jsonUiSchemaElement({ variables, }) {
158
223
  return Maybe.of(`{ label: "Identifier", scope: \`\${${variables.scopePrefix}}/properties/${this.jsonPropertySignature.name}\`, type: "Control" }`);
159
224
  }
160
225
  jsonZodSchema({ variables, }) {
226
+ let schema;
227
+ if (this.type.in_.length > 0 && this.type.isNamedNodeKind) {
228
+ // Treat sh:in as a union of the IRIs
229
+ // rdfjs.NamedNode<"http://example.com/1" | "http://example.com/2">
230
+ schema = `${variables.zod}.enum(${JSON.stringify(this.type.in_.map((iri) => iri.value))})`;
231
+ }
232
+ else {
233
+ schema = `${variables.zod}.string().min(1)`;
234
+ }
161
235
  return {
162
236
  key: this.jsonPropertySignature.name,
163
- schema: `${variables.zod}.string().min(1)`,
237
+ schema,
164
238
  };
165
239
  }
166
240
  sparqlConstructTemplateTriples() {
@@ -1,7 +1,7 @@
1
1
  import type { BlankNode, Literal, NamedNode, Variable } from "@rdfjs/types";
2
2
  import type { Maybe } from "purify-ts";
3
3
  import { type GetAccessorDeclarationStructure, type OptionalKind, type PropertyDeclarationStructure, type PropertySignatureStructure, Scope } from "ts-morph";
4
- import type { PropertyVisibility } from "../../../enums/index.js";
4
+ import type { PropertyVisibility, TsFeature, TsObjectDeclarationType } from "../../../enums/index.js";
5
5
  import type { Import } from "../Import.js";
6
6
  import type { Type } from "../Type.js";
7
7
  export declare abstract class Property<TypeT extends {
@@ -21,7 +21,7 @@ export declare abstract class Property<TypeT extends {
21
21
  */
22
22
  abstract readonly constructorParametersPropertySignature: Maybe<OptionalKind<PropertySignatureStructure>>;
23
23
  /**
24
- * Function declaration that takes two values of the property and compares them, returning and purifyHelpers.Equatable.EqualsResult.
24
+ * Function declaration that takes two values of the property and compares them, returning and EqualsResult.
25
25
  */
26
26
  abstract readonly equalsFunction: string;
27
27
  /**
@@ -40,6 +40,15 @@ export declare abstract class Property<TypeT extends {
40
40
  * TypeScript identifier-safe name of the property.
41
41
  */
42
42
  readonly name: string;
43
+ /**
44
+ * Reusable function, type, and other declarations that are not particular to this property but that property-specific code
45
+ * relies on. For example, the equals function/method of ObjectType has a custom return type that's the same across all
46
+ * ObjectType's. Instead of re-declaring the return type anonymously on every equals function, declare a named type
47
+ * as a snippet and reference it.
48
+ *
49
+ * The generator deduplicates snippet declarations across all types before adding them to the source.
50
+ */
51
+ abstract readonly snippetDeclarations: readonly string[];
43
52
  /**
44
53
  * Property type
45
54
  . */
@@ -49,9 +58,14 @@ export declare abstract class Property<TypeT extends {
49
58
  */
50
59
  readonly visibility: PropertyVisibility;
51
60
  protected readonly dataFactoryVariable: string;
52
- constructor({ dataFactoryVariable, name, type, visibility, }: {
61
+ protected readonly objectType: {
62
+ readonly declarationType: TsObjectDeclarationType;
63
+ readonly features: Set<TsFeature>;
64
+ };
65
+ constructor({ dataFactoryVariable, name, objectType, type, visibility, }: {
53
66
  dataFactoryVariable: string;
54
67
  name: string;
68
+ objectType: Property<TypeT>["objectType"];
55
69
  type: TypeT;
56
70
  visibility: PropertyVisibility;
57
71
  });
@@ -1,9 +1,10 @@
1
1
  import { Scope, } from "ts-morph";
2
2
  import { rdfjsTermExpression } from "./rdfjsTermExpression.js";
3
3
  export class Property {
4
- constructor({ dataFactoryVariable, name, type, visibility, }) {
4
+ constructor({ dataFactoryVariable, name, objectType, type, visibility, }) {
5
5
  this.dataFactoryVariable = dataFactoryVariable;
6
6
  this.name = name;
7
+ this.objectType = objectType;
7
8
  this.type = type;
8
9
  this.visibility = visibility;
9
10
  }
@@ -26,6 +26,7 @@ export declare class ShaclProperty extends Property<Type> {
26
26
  get equalsFunction(): string;
27
27
  get interfacePropertySignature(): OptionalKind<PropertySignatureStructure>;
28
28
  get jsonPropertySignature(): OptionalKind<PropertySignatureStructure>;
29
+ get snippetDeclarations(): readonly string[];
29
30
  private get declarationComment();
30
31
  private get pathExpression();
31
32
  classConstructorStatements({ variables, }: Parameters<Property<Type>["classConstructorStatements"]>[0]): readonly string[];
@@ -50,7 +50,7 @@ export class ShaclProperty extends Property {
50
50
  });
51
51
  }
52
52
  get declarationImports() {
53
- return this.type.useImports;
53
+ return this.type.useImports(this.objectType.features);
54
54
  }
55
55
  get equalsFunction() {
56
56
  return this.type.equalsFunction;
@@ -70,6 +70,9 @@ export class ShaclProperty extends Property {
70
70
  type: this.type.jsonName,
71
71
  };
72
72
  }
73
+ get snippetDeclarations() {
74
+ return this.type.snippetDeclarations(this.objectType.features);
75
+ }
73
76
  get declarationComment() {
74
77
  return this.comment
75
78
  .alt(this.description)
@@ -1,17 +1,14 @@
1
1
  import { Maybe } from "purify-ts";
2
2
  import type { GetAccessorDeclarationStructure, OptionalKind, PropertyDeclarationStructure, PropertySignatureStructure } from "ts-morph";
3
- import type { TsObjectDeclarationType } from "../../../enums/index.js";
4
3
  import { Property } from "./Property.js";
5
4
  export declare class TypeDiscriminatorProperty extends Property<TypeDiscriminatorProperty.Type> {
6
- readonly equalsFunction = "purifyHelpers.Equatable.strictEquals";
5
+ readonly equalsFunction = "strictEquals";
7
6
  readonly mutable = false;
8
7
  readonly value: string;
9
8
  private readonly abstract;
10
- private readonly objectTypeDeclarationType;
11
9
  private readonly override;
12
- constructor({ abstract, objectTypeDeclarationType, override, type, value, ...superParameters }: {
10
+ constructor({ abstract, override, type, value, ...superParameters }: {
13
11
  abstract: boolean;
14
- objectTypeDeclarationType: TsObjectDeclarationType;
15
12
  override: boolean;
16
13
  type: TypeDiscriminatorProperty.Type;
17
14
  value: string;
@@ -21,6 +18,7 @@ export declare class TypeDiscriminatorProperty extends Property<TypeDiscriminato
21
18
  get constructorParametersPropertySignature(): Maybe<OptionalKind<PropertySignatureStructure>>;
22
19
  get interfacePropertySignature(): OptionalKind<PropertySignatureStructure>;
23
20
  get jsonPropertySignature(): OptionalKind<PropertySignatureStructure>;
21
+ get snippetDeclarations(): readonly string[];
24
22
  classConstructorStatements(): readonly string[];
25
23
  fromJsonStatements(): readonly string[];
26
24
  fromRdfStatements(): readonly string[];