Skip to content
Twilight React Playground
ثيم رائدaren

REACT_THEME_TYPE & manifest helpers

functionAdvanced

Node helpers that check twilight.json declares "type": "react", the line that tells the Salla CLI this is a React theme.

import { REACT_THEME_TYPE, getThemeType, isReactThemeManifest, describeThemeTypeIssue, TwilightManifest } from '@salla.sa/twilight-theme-engine/vite';

In plain words

Every theme has a twilight.json file describing it. Older themes built with Twig templates and React themes share that format, so a React theme marks itself with one line: "type": "react". The Salla CLI reads it to decide how to run and publish your theme; without it the folder is treated as a Twig theme.

These helpers check that line from your own scripts, for example a CI step that fails when the manifest is wrong. They take the manifest already parsed with JSON.parse, and they run in Node: import them in build scripts, never in a page.

Signature

const REACT_THEME_TYPE = 'react';

function getThemeType(raw: unknown): string | undefined
function isReactThemeManifest(raw: unknown): boolean
function describeThemeTypeIssue(raw: unknown, fileLabel?: string): string | null  // fileLabel: 'twilight.json'

interface TwilightManifest {
  type?: string;
  name?: Record<string, string>;
  description?: Record<string, string>;
  repository?: string;
  author_email?: string;
  support_url?: string;
  features?: string[];
  settings?: unknown[];
  components?: unknown[];
}

// Input               getThemeType   isReactThemeManifest   describeThemeTypeIssue
// { type: 'react' }   'react'        true                   null
// {}  null  { type: 42 }  { type: '  ' }
//                     undefined      false                  'twilight.json has no "type": "react" — …'
// { type: ' react ' } ' react '      false                  '… compares this value exactly …'
// { type: 'React' }   'React'        false                  '… declares "type": "React", but …'

Example

scripts/check-manifest.mjs
// Run from the theme folder in CI: node scripts/check-manifest.mjs
import { readFileSync } from 'node:fs';
import { describeThemeTypeIssue } from '@salla.sa/twilight-theme-engine/vite';

const manifest = JSON.parse(readFileSync('twilight.json', 'utf8'));
const issue = describeThemeTypeIssue(manifest);

if (issue) {
  console.error(issue);
  process.exit(1);
}
console.log('twilight.json declares a React theme.');

How it behaves

  • getThemeType returns type exactly as written, or undefined when the input is not an object or type is missing, not a string, or only whitespace. It never trims, because the CLI compares with ===.

  • isReactThemeManifest(raw) is getThemeType(raw) === REACT_THEME_TYPE, so 'React' and ' react ' are false.

  • describeThemeTypeIssue returns null for a correct manifest, otherwise one sentence naming the problem and the fix: no type, a react wrapped in whitespace (quoted with JSON.stringify so the spaces show), or another value. fileLabel is the file name used in the sentence.

  • The engine runs the same check in vite dev: the twilight:schema plugin prints [twilight-react:schema] <message> when the dev server starts and whenever twilight.json changes. A build does not check.

  • Absence means Twig: a "twig" value is never written. TwilightManifest types only the top level; settings and components are unknown[].

Gotchas

  • Import them only in Node. @salla.sa/twilight-theme-engine/vite loads fs, path, crypto, module, esbuild and the native oxc-parser at the top of the module, so importing it from a component breaks the browser bundle and the server build. That is also why this page has no live demo.

  • "type": "React" or " react " looks right and still sends the theme down the Twig path in the CLI. The dev server warns about both; a build does not.

  • A missing twilight.json is silent in vite dev: only a present, wrong one warns. Invalid JSON gets its own warning (… is not valid JSON — theme settings and the dev widget will be empty.), because these helpers receive parsed input.

Related

Source and docs