igniteui-theming
Version:
A set of Sass variables, mixins, and functions for generating palettes, typography, and elevations used by Ignite UI components.
276 lines (275 loc) • 9.97 kB
JavaScript
import "./blazor.js";
import "./react.js";
import "./webcomponents.js";
import "./angular.js";
import { resolve } from "node:path";
import { existsSync, readFileSync, readdirSync } from "node:fs";
//#region src/knowledge/platforms/index.ts
/**
* Platform-specific knowledge for theme generation
*
* This module exports platform-specific configurations and generators
* for all supported Ignite UI platforms:
* - Ignite UI for Angular
* - Ignite UI for Web Components
* - Ignite UI for React
* - Ignite UI for Blazor
*
* It also provides platform detection functionality with multi-signal analysis
* for automatic platform identification from project files.
*/
/**
* Ignite UI package patterns for each platform.
* These are HIGH confidence indicators (100).
*/
var IGNITE_PACKAGE_PATTERNS = {
angular: ["igniteui-angular", "@infragistics/igniteui-angular"],
webcomponents: ["igniteui-webcomponents", "@infragistics/igniteui-webcomponents"],
react: ["igniteui-react", "@infragistics/igniteui-react"],
blazor: [],
generic: []
};
/**
* Framework package patterns for fallback detection.
* These are LOW confidence indicators (40) - only used when no Ignite UI package found.
*/
var FRAMEWORK_PACKAGE_PATTERNS = {
"@angular/core": "angular",
lit: "webcomponents",
react: "react",
"react-dom": "react"
};
/**
* Detect platform from config files in the project root.
* Fast detection that only checks root directory, no deep scanning.
*
* @param projectRoot - Path to the project root directory
* @returns Array of detected config file signals
*/
function detectConfigFiles(projectRoot = ".") {
const signals = [];
const root = resolve(projectRoot);
if (existsSync(resolve(root, "angular.json"))) signals.push({
platform: "angular",
file: "angular.json",
confidence: 80
});
for (const viteConfig of [
"vite.config.ts",
"vite.config.js",
"vite.config.mts",
"vite.config.mjs"
]) {
const configPath = resolve(root, viteConfig);
if (existsSync(configPath)) try {
const content = readFileSync(configPath, "utf-8");
if (content.includes("@vitejs/plugin-react") || content.includes("plugin-react")) {
signals.push({
platform: "react",
file: viteConfig,
confidence: 80
});
break;
}
} catch {}
}
for (const nextConfig of [
"next.config.js",
"next.config.mjs",
"next.config.ts"
]) if (existsSync(resolve(root, nextConfig))) {
signals.push({
platform: "react",
file: nextConfig,
confidence: 80
});
break;
}
try {
const csprojFiles = readdirSync(root).filter((f) => f.endsWith(".csproj")).slice(0, 5);
for (const csproj of csprojFiles) try {
const content = readFileSync(resolve(root, csproj), "utf-8");
if (content.includes("IgniteUI.Blazor")) {
signals.push({
platform: "blazor",
file: csproj,
confidence: 100
});
break;
}
if (content.includes("Microsoft.NET.Sdk.BlazorWebAssembly") || content.includes("Microsoft.NET.Sdk.Razor")) {
signals.push({
platform: "blazor",
file: csproj,
confidence: 40
});
break;
}
} catch {}
} catch {}
return signals;
}
/**
* Detect platform from package.json dependencies and project config files.
*
* Uses a multi-signal approach with confidence scoring:
* 1. Ignite UI packages (HIGH - 100): Definitive platform match
* 2. Config files (MEDIUM-HIGH - 80): Strong platform indicator
* 3. Framework packages (LOW - 40): Fallback when no Ignite UI found
*
* When multiple platforms are detected with significant confidence (≥60),
* returns an ambiguous result asking the user to specify explicitly.
*
* @param dependencies - package.json dependencies
* @param devDependencies - package.json devDependencies
* @param projectRoot - Project root directory for config file detection
* @returns Platform detection result with signals and confidence
*/
function detectPlatformFromDependencies(dependencies = {}, devDependencies = {}, projectRoot = ".") {
const allDeps = {
...dependencies,
...devDependencies
};
const signals = [];
const platformScores = /* @__PURE__ */ new Map();
for (const [platform, patterns] of Object.entries(IGNITE_PACKAGE_PATTERNS)) for (const pattern of patterns) if (pattern in allDeps) {
signals.push({
type: "ignite_package",
package: pattern,
confidence: 100
});
const current = platformScores.get(platform) || 0;
platformScores.set(platform, Math.max(current, 100));
}
const configSignals = detectConfigFiles(projectRoot);
for (const signal of configSignals) {
signals.push({
type: "config_file",
file: signal.file,
confidence: signal.confidence
});
const current = platformScores.get(signal.platform) || 0;
platformScores.set(signal.platform, Math.max(current, signal.confidence));
}
for (const [pkg, platform] of Object.entries(FRAMEWORK_PACKAGE_PATTERNS)) if (pkg in allDeps) {
const currentScore = platformScores.get(platform) || 0;
if (currentScore < 60) {
signals.push({
type: "framework_package",
package: pkg,
confidence: 40
});
platformScores.set(platform, Math.max(currentScore, 40));
}
}
const hasIgniteProduct = signals.some((s) => s.type === "ignite_package") || signals.some((s) => s.type === "config_file" && s.confidence === 100);
if (platformScores.size === 0) return {
platform: "generic",
confidence: "none",
signals: [],
reason: "No Ignite UI packages, framework packages, or config files detected. Using generic (standalone) mode."
};
if (!hasIgniteProduct) return {
platform: "generic",
confidence: "low",
signals,
reason: "No Ignite UI product package detected. Framework or config signals found but no Ignite UI product in use. Using generic (standalone) mode."
};
const sorted = Array.from(platformScores.entries()).sort((a, b) => b[1] - a[1]);
const [topPlatform, topScore] = sorted[0];
const ambiguousThreshold = 60;
const significantPlatforms = sorted.filter(([, score]) => score >= ambiguousThreshold);
if (significantPlatforms.length > 1) {
const alternatives = significantPlatforms.map(([platform, score]) => {
return {
platform,
confidence: score,
signals: signals.filter((s) => {
if (s.type === "ignite_package") return IGNITE_PACKAGE_PATTERNS[platform]?.includes(s.package);
if (s.type === "config_file") return configSignals.some((cs) => cs.platform === platform && cs.file === s.file);
if (s.type === "framework_package") return FRAMEWORK_PACKAGE_PATTERNS[s.package] === platform;
return false;
})
};
});
return {
platform: null,
confidence: "none",
ambiguous: true,
alternatives,
signals,
reason: `Multiple platforms detected: ${alternatives.map((a) => a.platform).join(", ")}. Please specify platform explicitly.`
};
}
let confidenceLevel;
if (topScore >= 80) confidenceLevel = "high";
else if (topScore >= 60) confidenceLevel = "medium";
else confidenceLevel = "low";
const detectedPackageSignal = signals.find((s) => s.type === "ignite_package" && IGNITE_PACKAGE_PATTERNS[topPlatform]?.includes(s.package));
return {
platform: topPlatform,
confidence: confidenceLevel,
signals,
detectedPackage: detectedPackageSignal?.package,
reason: `Detected ${topPlatform} with ${confidenceLevel} confidence (score: ${topScore})`
};
}
/**
* Determine if a detected package is a licensed @infragistics package.
* Only applies to Angular - other platforms always use the free igniteui-theming package.
*
* @param detectedPackage - The package name detected from package.json
* @returns True if the package is a licensed @infragistics package
*/
function isLicensedPackage(detectedPackage) {
return detectedPackage?.startsWith("@infragistics/") ?? false;
}
/**
* Platform metadata for display purposes
*/
var PLATFORM_METADATA = {
angular: {
id: "angular",
name: "Ignite UI for Angular",
shortName: "Angular",
packageName: "igniteui-angular",
licensedPackageName: "@infragistics/igniteui-angular",
themingModule: "igniteui-angular/theming",
licensedThemingModule: "@infragistics/igniteui-angular/theming",
description: "Uses core() and theme() mixins from igniteui-angular/theming module. Requires ig-typography CSS class on root element. Available as OSS (igniteui-angular) or licensed (@infragistics/igniteui-angular) package."
},
webcomponents: {
id: "webcomponents",
name: "Ignite UI for Web Components",
shortName: "Web Components",
packageName: "igniteui-webcomponents",
themingModule: "igniteui-theming",
description: "Uses igniteui-theming directly with palette(), typography(), and elevations() mixins. Supports runtime theme switching via configureTheme(). The igniteui-theming package is always free/OSS."
},
react: {
id: "react",
name: "Ignite UI for React",
shortName: "React",
packageName: "igniteui-react",
themingModule: "igniteui-theming",
description: "Uses igniteui-theming directly with palette(), typography(), and elevations() mixins. Common with Vite or Next.js projects. The igniteui-theming package is always free/OSS."
},
blazor: {
id: "blazor",
name: "Ignite UI for Blazor",
shortName: "Blazor",
packageName: "IgniteUI.Blazor",
themingModule: "igniteui-theming",
description: "Uses igniteui-theming for Sass compilation in .NET Blazor projects. Theme styles are compiled to CSS and referenced in Blazor components. The igniteui-theming package is always free/OSS."
},
generic: {
id: "generic",
name: "Ignite UI Theming (Standalone)",
shortName: "Generic",
packageName: "igniteui-theming",
themingModule: "igniteui-theming",
description: "Platform-agnostic output using igniteui-theming directly. For projects that do not use a specific Ignite UI product framework (Angular, Web Components, React, or Blazor). Supports palette, typography, elevations, and theme generation. Component theming is not available in generic mode."
}
};
//#endregion
export { PLATFORM_METADATA, detectConfigFiles, detectPlatformFromDependencies, isLicensedPackage };