rusackas commented on code in PR #38253:
URL: https://github.com/apache/superset/pull/38253#discussion_r2854889818


##########
docs/scripts/generate-if-changed.mjs:
##########
@@ -0,0 +1,306 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Smart generator wrapper: only runs generators whose input files have 
changed.
+ *
+ * Computes a hash of each generator's input files (stories, engine specs,
+ * openapi.json, and the generator scripts themselves). Compares against a
+ * stored cache. Skips generators whose inputs and outputs are unchanged.
+ *
+ * Usage:
+ *   node scripts/generate-if-changed.mjs          # smart mode (default)
+ *   node scripts/generate-if-changed.mjs --force   # force regenerate all
+ */
+
+import { createHash } from 'crypto';
+import { execSync, spawn } from 'child_process';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const DOCS_DIR = path.resolve(__dirname, '..');
+const ROOT_DIR = path.resolve(DOCS_DIR, '..');
+const CACHE_FILE = path.join(DOCS_DIR, '.docusaurus', 'generator-hashes.json');
+
+const FORCE = process.argv.includes('--force');
+
+// Ensure local node_modules/.bin is on PATH (needed for docusaurus CLI)
+const localBin = path.join(DOCS_DIR, 'node_modules', '.bin');
+process.env.PATH = `${localBin}${path.delimiter}${process.env.PATH}`;
+
+// ---------------------------------------------------------------------------
+// Generator definitions
+// ---------------------------------------------------------------------------
+
+const GENERATORS = [
+  {
+    name: 'superset-components',
+    command: 'node scripts/generate-superset-components.mjs',
+    inputs: [
+      {
+        type: 'glob',
+        base: path.join(ROOT_DIR, 
'superset-frontend/packages/superset-ui-core/src/components'),
+        pattern: '**/*.stories.tsx',
+      },
+      {
+        type: 'glob',
+        base: path.join(ROOT_DIR, 
'superset-frontend/packages/superset-core/src'),
+        pattern: '**/*.stories.tsx',
+      },
+      { type: 'file', path: path.join(DOCS_DIR, 
'scripts/generate-superset-components.mjs') },
+      { type: 'file', path: path.join(DOCS_DIR, 
'src/components/StorybookWrapper.jsx') },
+    ],
+    outputs: [
+      path.join(DOCS_DIR, 'developer_portal/components/index.mdx'),
+      path.join(DOCS_DIR, 'static/data/components.json'),
+      path.join(DOCS_DIR, 'src/types/apache-superset-core/index.d.ts'),
+    ],
+  },
+  {
+    name: 'database-docs',
+    command: 'node scripts/generate-database-docs.mjs',
+    inputs: [
+      {
+        type: 'glob',
+        base: path.join(ROOT_DIR, 'superset/db_engine_specs'),
+        pattern: '**/*.py',
+      },
+      { type: 'file', path: path.join(DOCS_DIR, 
'scripts/generate-database-docs.mjs') },
+    ],
+    outputs: [
+      path.join(DOCS_DIR, 'src/data/databases.json'),
+      path.join(DOCS_DIR, 'docs/databases/supported'),
+    ],
+  },
+  {
+    name: 'api-docs',
+    command:
+      'python3 scripts/fix-openapi-spec.py && docusaurus gen-api-docs superset 
&& node scripts/convert-api-sidebar.mjs && node scripts/generate-api-index.mjs 
&& node scripts/generate-api-tag-pages.mjs',
+    inputs: [
+      { type: 'file', path: path.join(DOCS_DIR, 
'static/resources/openapi.json') },
+      { type: 'file', path: path.join(DOCS_DIR, 'scripts/fix-openapi-spec.py') 
},
+      { type: 'file', path: path.join(DOCS_DIR, 
'scripts/convert-api-sidebar.mjs') },
+      { type: 'file', path: path.join(DOCS_DIR, 
'scripts/generate-api-index.mjs') },
+      { type: 'file', path: path.join(DOCS_DIR, 
'scripts/generate-api-tag-pages.mjs') },
+    ],
+    outputs: [
+      path.join(DOCS_DIR, 'docs/api.mdx'),
+    ],
+  },
+];
+
+// ---------------------------------------------------------------------------
+// Hashing utilities
+// ---------------------------------------------------------------------------
+
+function walkDir(dir, pattern) {
+  const results = [];
+  if (!fs.existsSync(dir)) return results;
+
+  const regex = globToRegex(pattern);
+  function walk(currentDir) {
+    for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
+      const fullPath = path.join(currentDir, entry.name);
+      if (entry.isDirectory()) {
+        if (entry.name === 'node_modules' || entry.name === '__pycache__') 
continue;
+        walk(fullPath);
+      } else {
+        const relativePath = path.relative(dir, fullPath);
+        if (regex.test(relativePath)) {
+          results.push(fullPath);
+        }
+      }

Review Comment:
   Good catch\! Fixed by normalizing `path.relative()` output to forward 
slashes with `.split(path.sep).join("/")`, ensuring glob patterns match 
correctly on Windows. Applied in f9a6c4a.



##########
docs/scripts/generate-superset-components.mjs:
##########
@@ -1332,6 +1335,123 @@ ${sections}
 `;
 }
 
+/**
+ * Build metadata for a component (for JSON output)
+ */
+function buildComponentMetadata(component, storyContent) {
+  const { componentName, description, category, sourceConfig, 
resolvedImportPath, extensionCompatible } = component;
+  const { args, controls, gallery, liveExample } = 
extractArgsAndControls(storyContent, componentName);
+  const labels = CATEGORY_LABELS[category] || {
+    title: category.charAt(0).toUpperCase() + category.slice(1).replace(/-/g, 
' '),
+  };
+
+  return {
+    name: componentName,
+    category,
+    categoryLabel: labels.title || category,
+    description: description || '',
+    importPath: resolvedImportPath || sourceConfig.importPrefix,
+    package: sourceConfig.docImportPrefix,
+    extensionCompatible: Boolean(extensionCompatible),
+    propsCount: Object.keys(args).length,
+    controlsCount: controls.length,
+    hasGallery: Boolean(gallery && gallery.sizes && gallery.styles),
+    hasLiveExample: Boolean(liveExample),
+    docPath: 
`developer_portal/components/${category}/${componentName.toLowerCase()}`,
+    storyFile: component.relativePath,
+  };
+}
+
+/**
+ * Extract type and component export declarations from a component source file.
+ * Used to generate .d.ts type declarations for extension-compatible 
components.
+ */
+function extractComponentTypes(componentPath) {
+  if (!fs.existsSync(componentPath)) return null;
+  const content = fs.readFileSync(componentPath, 'utf-8');
+
+  const types = [];
+  for (const match of 
content.matchAll(/export\s+type\s+(\w+)\s*=\s*([^;]+);/g)) {
+    types.push({ name: match[1], definition: match[2].trim() });
+  }
+
+  const components = [];
+  for (const match of content.matchAll(/export\s+const\s+(\w+)\s*[=:]/g)) {
+    components.push(match[1]);
+  }
+
+  return { types, components };
+}
+
+/**
+ * Generate TypeScript type declarations for extension-compatible components.
+ * Produces a .d.ts file that downstream consumers can reference.
+ */
+function generateExtensionTypeDeclarations(extensionComponents) {
+  const imports = new Set();
+  const typeDeclarations = [];
+  const componentDeclarations = [];
+
+  for (const comp of extensionComponents) {
+    const componentDir = path.dirname(comp.filePath);
+    const componentFile = path.join(componentDir, 'index.tsx');
+    const extracted = extractComponentTypes(componentFile);
+    if (!extracted) continue;
+
+    for (const type of extracted.types) {
+      if (type.definition.includes('AntdAlertProps') || 
type.definition.includes('AlertProps')) {
+        imports.add("import type { AlertProps as AntdAlertProps } from 
'antd/es/alert';");
+      }
+      if (type.definition.includes('PropsWithChildren') || 
type.definition.includes('FC')) {
+        imports.add("import type { PropsWithChildren, FC } from 'react';");
+      }
+      typeDeclarations.push(`export type ${type.name} = ${type.definition};`);
+    }
+
+    for (const name of extracted.components) {
+      const propsType = `${name}Props`;
+      const hasPropsType = extracted.types.some(t => t.name === propsType);
+      componentDeclarations.push(
+        hasPropsType
+          ? `export const ${name}: FC<${propsType}>;`
+          : `export const ${name}: FC<Record<string, unknown>>;`
+      );
+    }
+  }
+
+  return `/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file

Review Comment:
   Good catch\! Added a guard that ensures the `FC` (and `PropsWithChildren`) 
import from React is always added whenever component declarations exist, 
regardless of whether the type definitions themselves reference those types. 
Fixed in f9a6c4a.



##########
docs/yarn.lock:
##########
@@ -2679,6 +2716,58 @@
   dependencies:
     langium "3.3.1"
 
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/error-codes/-/error-codes-0.22.0.tgz#31ccc990dc240d73912ba7bd001f7e35ac751992";
+  integrity 
sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==
+
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/runtime-core/-/runtime-core-0.22.0.tgz#7321ec792bb7d1d22bee6162ec43564b769d2a3c";
+  integrity 
sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==
+  dependencies:
+    "@module-federation/error-codes" "0.22.0"
+    "@module-federation/sdk" "0.22.0"
+
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/runtime-tools/-/runtime-tools-0.22.0.tgz#36f2a7cb267af208a9d1a237fe9a71b4bf31431e";
+  integrity 
sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==
+  dependencies:
+    "@module-federation/runtime" "0.22.0"
+    "@module-federation/webpack-bundler-runtime" "0.22.0"
+
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/runtime/-/runtime-0.22.0.tgz#f789c9ef40d846d110711c8221ecc0ad938d43d8";
+  integrity 
sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==
+  dependencies:
+    "@module-federation/error-codes" "0.22.0"
+    "@module-federation/runtime-core" "0.22.0"
+    "@module-federation/sdk" "0.22.0"
+
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/sdk/-/sdk-0.22.0.tgz#6ad4c1de85a900c3c80ff26cb87cce253e3a2770";
+  integrity 
sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==
+
+"@module-federation/[email protected]":
+  version "0.22.0"
+  resolved 
"https://registry.yarnpkg.com/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.22.0.tgz#dcbe8f972d722fe278e6a7c21988d4bee53d401d";
+  integrity 
sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==
+  dependencies:
+    "@module-federation/runtime" "0.22.0"
+    "@module-federation/sdk" "0.22.0"
+
+"@napi-rs/[email protected]":
+  version "1.0.7"
+  resolved 
"https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz#dcfea99a75f06209a235f3d941e3460a51e9b14c";
+  integrity 
sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==
+  dependencies:
+    "@emnapi/core" "^1.5.0"
+    "@emnapi/runtime" "^1.5.0"
+    "@tybys/wasm-util" "^0.10.1"
+

Review Comment:
   The yarn.lock was auto-generated by `yarn add @docusaurus/faster` — no 
manual edits were made. The new entries (module-federation, lightningcss, 
swc-loader) are transitive dependencies of `@docusaurus/faster`. The lockfile 
has been regenerated cleanly as part of the rebase in f9a6c4a.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to