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
47 changes: 47 additions & 0 deletions .changeset/cli-check-known-types-derived-5115.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
'@object-ui/cli': patch
---

`objectui check`: the known-type list is now derived from the component registry instead of being a hand-written copy, which had drifted in both directions at once.

The command judged a schema's `type` against a seventeen-entry array typed by
hand into `packages/cli/src/commands/check.ts`. Nothing held that array against
the registry, and measured on `origin/main` @ `8378e9954` it was wrong in both
directions simultaneously:

- **Two phantoms.** `crud` and `gallery` were on the list and are registered by
nothing. `objectui check` passed `{ "type": "crud" }` in silence while
`SchemaRenderer` painted the OBJUI-001 "Unknown component type" panel for the
very same file — measured, both halves. `CRUDSchema` still has its interface,
zod mirror, validator branch and builder; what it has never had is a
registration. For `gallery`, the registered spelling is `object-gallery`.
- **221 bare keys missing, plus every namespaced spelling.** `object-grid`,
`object-form`, `card`, `div` and `view:grid` were all reported as
`⚠️ Unknown schema type`. False warnings at that volume are not a cosmetic
problem: they train authors to skip the output, which costs the phantom
direction its only reader.

The list now lives in `packages/cli/src/utils/known-schema-types.ts`, generated
by `node scripts/regenerate-known-schema-types.mjs` from the same
`deriveRegistryKeys` derivation that judges documentation snippets, and held to
it by a bidirectional pin in
`scripts/__tests__/known-schema-types-derivation-5115.test.ts` — a key the
registry has and the list lacks fails, and so does a key the list has and the
registry lacks. Bare and namespaced spellings are both carried, because
`register('grid', C, { namespace: 'view' })` really does store both.

A runtime lookup through `ComponentRegistry` was measured and rejected: eleven
of the fifteen genuinely-registered entries come from plugin packages the CLI
does not depend on, and a published CLI runs against a user project whose plugin
set this repository cannot know either way.

**Behaviour change, in both directions.** `{ "type": "crud" }` and
`{ "type": "gallery" }` now produce the `Unknown schema type` warning they
always should have, and a large number of real component types stop producing
one. The warning remains advisory — it never changes the command's exit code,
which is still driven only by files that fail to parse — so no run that passed
before fails now.

`check()` additionally takes the directory to scan as an optional argument
(defaulting, as before, to `process.cwd()`), so the behaviour can be tested
against a fixture tree.
104 changes: 104 additions & 0 deletions packages/cli/src/__tests__/check-known-types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* ObjectUI
* Copyright (c) 2024-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* `objectui check`'s unknown-type warning (objectui#5115).
*
* `scripts/__tests__/known-schema-types-derivation-5115.test.ts` pins that the
* shipped key set equals the registry derivation. This file pins the other
* half — that the COMMAND actually consults that set, and reports what it
* finds — over real files in a temporary directory.
*
* Fixtures live under `os.tmpdir()`, never in the repo tree: `check()` globs
* every JSON file under the directory it is handed, so a fixture committed
* inside this workspace would be scanned by every other run of the command as
* well — including the repo's own `pnpm check`.
*/

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';

import { check } from '../commands/check.js';

let cwd: string;
let lines: string[];
let restoreLog: () => void;

function writeSchema(name: string, body: unknown): void {
writeFileSync(join(cwd, name), JSON.stringify(body));
}

/** Warnings only, with the ANSI colouring chalk may add stripped off. */
function unknownTypeWarnings(): string[] {
// eslint-disable-next-line no-control-regex -- matching the CSI sequences chalk emits
const ansi = /\u001b\[[0-9;]*m/g;
return lines.map((l) => l.replace(ansi, '')).filter((l) => l.includes('Unknown schema type'));
}

beforeEach(() => {
cwd = mkdtempSync(join(tmpdir(), 'objectui-check-'));
lines = [];
const original = console.log;
console.log = (...args: unknown[]) => {
lines.push(args.map(String).join(' '));
};
restoreLog = () => {
console.log = original;
};
});

afterEach(() => {
restoreLog();
rmSync(cwd, { recursive: true, force: true });
});

describe('objectui check — unknown schema types', () => {
it('warns about `crud`, which four declaration faces describe and no renderer registers', async () => {
// The defect objectui#5115 was filed for: this file passed in silence, and
// then rendered the OBJUI-001 "Unknown component type" panel in the browser.
writeSchema('crud-page.json', { type: 'crud', resource: '/api/accounts' });
await check(cwd);
expect(unknownTypeWarnings()).toEqual([
expect.stringContaining('Unknown schema type "crud" in crud-page.json'),
]);
});

it('warns about `gallery`, whose registered spelling is `object-gallery`', async () => {
writeSchema('gallery.json', { type: 'gallery' });
await check(cwd);
expect(unknownTypeWarnings()).toHaveLength(1);
expect(unknownTypeWarnings()[0]).toContain('"gallery"');
});

it('is silent for registered types the old hand-written list did not carry', async () => {
// `object-grid` (plugin-grid) and `view:grid` (the namespaced spelling of
// the same registration) were both reported as unknown before objectui#5115.
writeSchema('grid.json', { type: 'object-grid', objectApiName: 'account' });
writeSchema('ns-grid.json', { type: 'view:grid', objectApiName: 'account' });
writeSchema('gallery-ok.json', { type: 'object-gallery' });
await check(cwd);
expect(unknownTypeWarnings()).toEqual([]);
});

it('still warns for a type nothing registers', async () => {
writeSchema('bogus.json', { type: 'totally-made-up-xyz' });
await check(cwd);
expect(unknownTypeWarnings()).toHaveLength(1);
});

it('reports an unknown type as a warning, never as a failure', async () => {
// The check cannot know what a user project registers on its own, so an
// unrecognised type must not fail the run. Pinned because tightening the
// list would otherwise be free to become a breaking change by accident.
writeSchema('bogus.json', { type: 'totally-made-up-xyz' });
await check(cwd);
expect(lines.some((l) => l.includes('All checks passed'))).toBe(true);
});
});
24 changes: 15 additions & 9 deletions packages/cli/src/commands/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ import { globSync } from 'glob';
import { readFileSync } from 'fs';
import { join } from 'path';

export async function check() {
import { isKnownSchemaType } from '../utils/known-schema-types.js';

/**
* @param cwd Directory to scan. Defaults to the process working directory,
* which is what the `objectui check` command passes. Taking it as an
* argument is what lets the tests scan a fixture tree without `chdir`, which
* Vitest's worker threads do not support.
*/
export async function check(cwd: string = process.cwd()) {
console.log(chalk.bold('Object UI Schema Check'));
const cwd = process.cwd();


// 1. Find all JSON/YAML files
const files = globSync('**/*.{json,yaml,yml}', {
cwd,
Expand All @@ -32,12 +39,11 @@ export async function check() {
const content = JSON.parse(readFileSync(join(cwd, file), 'utf-8'));
// Schema validation: check for ObjectUI schema patterns
if (content && typeof content === 'object' && content.type) {
const knownTypes = [
'page', 'form', 'grid', 'crud', 'kanban', 'calendar', 'dashboard',
'chart', 'detail', 'list', 'timeline', 'gantt', 'map', 'gallery',
'object-view', 'detail-view', 'object-chart',
];
if (typeof content.type === 'string' && !knownTypes.includes(content.type)) {
// The known-type universe is DERIVED from the repository's
// registration calls (see `packages/cli/src/utils/known-schema-types.ts`
// and the script that writes it), not typed by hand. The array that
// used to sit here had drifted both ways at once — objectui#5115.
if (typeof content.type === 'string' && !isKnownSchemaType(content.type)) {
console.log(chalk.yellow(`⚠️ Unknown schema type "${content.type}" in ${file}`));
}
}
Expand Down
Loading
Loading