Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ jobs:
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
--component sourcemeta_core_dev
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
--component sourcemeta_blaze
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
--component sourcemeta_blaze_dev
- run: >
cmake --install ./build --prefix ./build/dist --config Release --verbose
--component sourcemeta_codegen
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ if(CODEGEN_INSTALL)
endif()

find_package(Core REQUIRED)
find_package(Blaze REQUIRED)

# Don't force downstream consumers on it
if(PROJECT_IS_TOP_LEVEL)
Expand Down
3 changes: 2 additions & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02
core https://github.com/sourcemeta/core b6640a840bf149edca223c9129a71d64474e12fc
core https://github.com/sourcemeta/core c43332629d71475f44d212f140effbf0a46c1492
blaze https://github.com/sourcemeta/blaze b97dc7b6dca47917f40ff465efa5e068e16e8534
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ compile: .always
--component sourcemeta_core
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
--component sourcemeta_core_dev
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
--component sourcemeta_blaze
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
--component sourcemeta_blaze_dev
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
--component sourcemeta_codegen
$(CMAKE) --install ./build --prefix ./build/dist --config $(PRESET) --verbose \
Expand Down
11 changes: 11 additions & 0 deletions cmake/FindBlaze.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
if(NOT Blaze_FOUND)
if(CODEGEN_INSTALL)
set(SOURCEMETA_BLAZE_INSTALL ON CACHE BOOL "enable installation")
Copy link
Copy Markdown

@augmentcode augmentcode bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FindBlaze.cmake sets SOURCEMETA_BLAZE_INSTALL, but the vendored Blaze CMake uses BLAZE_INSTALL to gate install rules/components; this looks like CODEGEN_INSTALL may not actually control Blaze’s install behavior as intended.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

else()
set(SOURCEMETA_BLAZE_INSTALL OFF CACHE BOOL "disable installation")
endif()

add_subdirectory("${PROJECT_SOURCE_DIR}/vendor/blaze")
include(Sourcemeta)
set(Blaze_FOUND ON)
endif()
3 changes: 2 additions & 1 deletion config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ if(NOT CODEGEN_COMPONENTS)
endif()

include(CMakeFindDependencyMacro)
find_dependency(Core COMPONENTS regex json jsonschema alterschema)
find_dependency(Core COMPONENTS regex json jsonschema)
find_dependency(Blaze COMPONENTS alterschema)

foreach(component ${CODEGEN_COMPONENTS})
if(component STREQUAL "ir")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class SOURCEMETA_CODEGEN_GENERATOR_EXPORT TypeScript {
auto operator()(const IRReference &entry) -> void;
auto operator()(const IRTuple &entry) -> void;
auto operator()(const IRUnion &entry) -> void;
auto operator()(const IRIntersection &entry) -> void;

private:
// Exporting symbols that depends on the standard C++ library is considered
Expand Down
16 changes: 16 additions & 0 deletions src/generator/typescript.cc
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,20 @@ auto TypeScript::operator()(const IRUnion &entry) -> void {
this->output << ";\n";
}

auto TypeScript::operator()(const IRIntersection &entry) -> void {
this->output << "export type "
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
<< " =\n";

const char *separator{""};
for (const auto &value : entry.values) {
this->output << separator << " "
<< mangle(this->prefix, value.pointer, value.symbol,
this->cache);
separator = " &\n";
}

this->output << ";\n";
}

} // namespace sourcemeta::codegen
2 changes: 1 addition & 1 deletion src/ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ target_link_libraries(sourcemeta_codegen_ir PUBLIC
target_link_libraries(sourcemeta_codegen_ir PUBLIC
sourcemeta::core::jsonschema)
target_link_libraries(sourcemeta_codegen_ir PRIVATE
sourcemeta::core::alterschema)
sourcemeta::blaze::alterschema)
9 changes: 7 additions & 2 deletions src/ir/include/sourcemeta/codegen/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ struct IRUnion : IRType {
std::vector<IRType> values;
};

/// @ingroup ir
struct IRIntersection : IRType {
std::vector<IRType> values;
};

/// @ingroup ir
struct IRObjectValue : IRType {
bool required;
Expand Down Expand Up @@ -99,8 +104,8 @@ struct IRReference : IRType {

/// @ingroup ir
using IREntity =
std::variant<IRObject, IRScalar, IREnumeration, IRUnion, IRArray, IRTuple,
IRImpossible, IRAny, IRReference>;
std::variant<IRObject, IRScalar, IREnumeration, IRUnion, IRIntersection,
IRArray, IRTuple, IRImpossible, IRAny, IRReference>;

/// @ingroup ir
using IRResult = std::vector<IREntity>;
Expand Down
55 changes: 51 additions & 4 deletions src/ir/ir.cc
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@
#include <sourcemeta/blaze/alterschema.h>
#include <sourcemeta/codegen/ir.h>
#include <sourcemeta/core/alterschema.h>

#include <algorithm> // std::ranges::sort
#include <cassert> // assert
#include <unordered_set> // std::unordered_set

#include "ir_default_compiler.h"

namespace {

auto is_validation_subschema(
const sourcemeta::core::SchemaFrame &frame,
const sourcemeta::core::SchemaFrame::Location &location,
const sourcemeta::core::SchemaWalker &walker,
const sourcemeta::core::SchemaResolver &resolver) -> bool {
if (!location.parent.has_value()) {
return false;
}

const auto &parent{location.parent.value()};
if (parent.size() >= location.pointer.size()) {
return false;
}

const auto &keyword_token{location.pointer.at(parent.size())};
if (!keyword_token.is_property()) {
return false;
}

const auto parent_location{frame.traverse(parent)};
if (!parent_location.has_value()) {
return false;
}

const auto vocabularies{
frame.vocabularies(parent_location.value().get(), resolver)};
const auto &walker_result{walker(keyword_token.to_property(), vocabularies)};
using Type = sourcemeta::core::SchemaKeywordType;
if (walker_result.type == Type::ApplicatorValueTraverseAnyPropertyKey ||
walker_result.type == Type::ApplicatorValueTraverseAnyItem) {
return true;
}

return is_validation_subschema(frame, parent_location.value().get(), walker,
resolver);
}

} // anonymous namespace

namespace sourcemeta::codegen {

auto compile(const sourcemeta::core::JSON &input,
Expand All @@ -25,9 +66,9 @@ auto compile(const sourcemeta::core::JSON &input,
// (2) Canonicalize the schema for easier analysis
// --------------------------------------------------------------------------

sourcemeta::core::SchemaTransformer canonicalizer;
sourcemeta::core::add(canonicalizer,
sourcemeta::core::AlterSchemaMode::Canonicalizer);
sourcemeta::blaze::SchemaTransformer canonicalizer;
sourcemeta::blaze::add(canonicalizer,
sourcemeta::blaze::AlterSchemaMode::Canonicalizer);
[[maybe_unused]] const auto canonicalized{canonicalizer.apply(
schema, walker, resolver,
[](const auto &, const auto, const auto, const auto &,
Expand Down Expand Up @@ -67,6 +108,12 @@ auto compile(const sourcemeta::core::JSON &input,
continue;
}

// Skip subschemas under validation-only keywords that do not contribute
// to the type structure (like `contains`)
if (is_validation_subschema(frame, location, walker, resolver)) {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Skipping all descendants under contains/propertyNames can drop schemas that are still referenced via $ref, leading to generated aliases that reference non-emitted types.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/ir/ir.cc, line 113:

<comment>Skipping all descendants under `contains`/`propertyNames` can drop schemas that are still referenced via `$ref`, leading to generated aliases that reference non-emitted types.</comment>

<file context>
@@ -67,6 +108,12 @@ auto compile(const sourcemeta::core::JSON &input,
 
+    // Skip subschemas under validation-only keywords that do not contribute
+    // to the type structure (like `contains`)
+    if (is_validation_subschema(frame, location, walker, resolver)) {
+      continue;
+    }
</file context>
Fix with Cubic

continue;
}

const auto &subschema{sourcemeta::core::get(schema, location.pointer)};
result.push_back(compiler(schema, frame, location, resolver, subschema));
}
Expand Down
Loading
Loading