Module: Opencdd::Codegen::Ts

Defined in:
lib/opencdd/codegen/ts.rb

Overview

Serializes the Ruby registries (Opencdd::PropertyIds::REGISTRY and Opencdd::MetaClasses) to TypeScript files that the OpenCDD Editor imports at build time. The editor has no runtime dependency on Ruby.

Constant Summary collapse

PROPERTY_HELPERS =
<<~TS.freeze
  export function entry(id: string): PropertyEntry | undefined {
    return REGISTRY[id];
  }

  export function multilingual(id: string): boolean {
    return REGISTRY[id]?.multilingual ?? false;
  }

  export function appliesTo(id: string): AppliesTo | undefined {
    return REGISTRY[id]?.appliesTo;
  }

  export function valueKind(id: string): ValueKind | undefined {
    return REGISTRY[id]?.valueKind;
  }

  export const ALL_IDS: readonly string[] = Object.keys(REGISTRY);

  export function canonicalId(name: string): string | undefined {
    if (name in REGISTRY) return name;
    return ALIAS_MAP[name];
  }

  export function normalize(rawId: string | null | undefined): string | null {
    if (!rawId) return null;
    const s = rawId.trim();
    if (!s) return null;
    const match = s.match(/\\.([A-Za-z0-9-]+)$/);
    if (!match) return canonicalId(s) ?? s;
    const base = s.slice(0, s.length - match[0].length);
    const lang = match[1];
    const canonical = canonicalId(base) ?? base;
    return `${canonical}.${lang}`;
  }
TS
META_HELPERS =
<<~TS.freeze
  export function entry(irdi: string): MetaClassEntry | undefined {
    return REGISTRY[irdi];
  }

  export function entityTypeFor(irdi: string): EntityType | undefined {
    return REGISTRY[irdi]?.entityType;
  }

  export function codePropertyIdFor(irdi: string): string | undefined {
    return REGISTRY[irdi]?.codePropertyId;
  }

  export function metaClassForType(type: EntityType): string | undefined {
    for (const [irdi, e] of Object.entries(REGISTRY)) {
      if (e.entityType === type) return irdi;
    }
    return undefined;
  }

  export const ALL_CODES: readonly string[] = Object.keys(REGISTRY);
TS

Class Method Summary collapse

Class Method Details

.alias_map_literal(registry) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/opencdd/codegen/ts.rb', line 110

def alias_map_literal(registry)
  pairs = []
  registry.each_value do |e|
    e.aliases.each { |a| pairs << "  \"#{a}\": \"#{e.id}\"," }
  end
  "const ALIAS_MAP: Readonly<Record<string, string>> = {\n#{pairs.join("\n")}\n};"
end

.entry_literal(entry) ⇒ Object



104
105
106
107
108
# File 'lib/opencdd/codegen/ts.rb', line 104

def entry_literal(entry)
  aliases = entry.aliases.map { |a| "\"#{a}\"" }.join(", ")
  "{ id: \"#{entry.id}\", aliases: [#{aliases}], appliesTo: \"#{entry.applies_to}\", " \
    "multilingual: #{entry.multilingual}, valueKind: \"#{entry.value_kind}\" }"
end

.generate_all(editor_dir) ⇒ Object



13
14
15
16
# File 'lib/opencdd/codegen/ts.rb', line 13

def generate_all(editor_dir)
  generate_property_ids(File.join(editor_dir, "src", "models", "PropertyIds.generated.ts"))
  generate_meta_classes(File.join(editor_dir, "src", "models", "MetaClasses.generated.ts"))
end

.generate_meta_classes(path) ⇒ Object



54
55
56
# File 'lib/opencdd/codegen/ts.rb', line 54

def generate_meta_classes(path)
  write_file(path, render_meta_classes)
end

.generate_property_ids(path) ⇒ Object



18
19
20
# File 'lib/opencdd/codegen/ts.rb', line 18

def generate_property_ids(path)
  write_file(path, render_property_ids)
end

.render_meta_classesObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/opencdd/codegen/ts.rb', line 58

def render_meta_classes
  Opencdd::MetaClasses.reset!
  metas = Opencdd::MetaClasses.all
  type_map = Opencdd::MetaClasses::TYPE_BY_META_CLASS
  code_map = Opencdd::MetaClasses::CODE_PROPERTY_IDS

  entity_types = type_map.values.map(&:to_s).uniq.sort

  out = []
  out << "// AUTO-GENERATED by `rake generate_ts`. Do not edit by hand."
  out << "// Source: Opencdd::MetaClasses in lib/opencdd/meta_class.rb"
  out << ""
  out << union_type("EntityType", entity_types)
  out << ""
  out << "export interface MetaClassEntry {"
  out << "  readonly irdi: string;"
  out << "  readonly name: string;"
  out << "  readonly entityType: EntityType;"
  out << "  readonly codePropertyId: string;"
  out << "  readonly allowedPropertyIds: readonly string[];"
  out << "}"
  out << ""
  out << "export const REGISTRY: Readonly<Record<string, MetaClassEntry>> = {"
  metas.each do |mc|
    out << "  \"#{mc.irdi}\": {"
    out << "    irdi: \"#{mc.irdi}\","
    out << "    name: \"#{mc.name}\","
    out << "    entityType: \"#{type_map[mc.irdi]}\","
    out << "    codePropertyId: \"#{code_map[mc.irdi]}\","
    ids = mc.allowed_property_ids.map { |id| "\"#{id}\"" }.join(", ")
    out << "    allowedPropertyIds: [#{ids}],"
    out << "  },"
  end
  out << "};"
  out << ""
  metas.each { |mc| out << "export const #{mc.irdi} = \"#{mc.irdi}\";" }
  out << ""
  out << META_HELPERS
  out.join("\n") + "\n"
end

.render_property_idsObject

Returns the generated TS source as a string (no filesystem access). Testable in-memory. See TODO.impl/31.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/opencdd/codegen/ts.rb', line 24

def render_property_ids
  registry = Opencdd::PropertyIds::REGISTRY

  out = []
  out << "// AUTO-GENERATED by `rake generate_ts`. Do not edit by hand."
  out << "// Source: Opencdd::PropertyIds::REGISTRY in lib/opencdd/property_ids.rb"
  out << ""
  out << union_type("AppliesTo", registry.values.map { |e| e.applies_to.to_s }.uniq.sort)
  out << union_type("ValueKind", registry.values.map { |e| e.value_kind.to_s }.uniq.sort)
  out << ""
  out << "export interface PropertyEntry {"
  out << "  readonly id: string;"
  out << "  readonly aliases: readonly string[];"
  out << "  readonly appliesTo: AppliesTo;"
  out << "  readonly multilingual: boolean;"
  out << "  readonly valueKind: ValueKind;"
  out << "}"
  out << ""
  out << "export const REGISTRY: Readonly<Record<string, PropertyEntry>> = {"
  registry.each_value { |e| out << "  \"#{e.id}\": #{entry_literal(e)}," }
  out << "};"
  out << ""
  registry.each_key { |id| out << "export const #{id} = \"#{id}\";" }
  out << ""
  out << PROPERTY_HELPERS
  out << ""
  out << alias_map_literal(registry)
  out.join("\n") + "\n"
end

.union_type(name, values) ⇒ Object



99
100
101
102
# File 'lib/opencdd/codegen/ts.rb', line 99

def union_type(name, values)
  body = values.map { |v| "  | \"#{v}\"" }.join("\n")
  "export type #{name} =\n#{body};"
end

.write_file(path, content) ⇒ Object



118
119
120
121
122
# File 'lib/opencdd/codegen/ts.rb', line 118

def write_file(path, content)
  FileUtils.mkdir_p(File.dirname(path))
  File.write(path, content + "\n")
  puts "Generated #{path}"
end