Skip to content

feat(code-connect-urls) #11696

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion packages/react-core/.gitignore
Original file line number Diff line number Diff line change
@@ -2,4 +2,5 @@
/deprecated
/components
/layouts
/helpers
/helpers
yarn.lock
18 changes: 18 additions & 0 deletions packages/react-core/codeConnect/components/AboutModal.figma.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// import React from 'react';
// import { AboutModal } from './AboutModal';
// import figma from '@figma/code-connect';

// /**
// * -- This file was auto-generated by Code Connect --
// * `props` includes a mapping from Figma properties and variants to
// * suggested values. You should update this to match the props of your
// * code component, and update the `example` function to return the
// * code example you'd like to see in Figma
// */

// figma.connect(AboutModal, '[object Object]2879-13973', {
// props: {
// productName: figma.string('Product name')
// },
// example: (props) => <AboutModal />
// });
4 changes: 4 additions & 0 deletions packages/react-core/codeConnect/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"figmaBaseUrl": "https://www.figma.com/design/VMEX8Xg2nzhBX8rfBx53jp/branch/H3LonYnwH26v9zNEa2SXFk/PatternFly-6%3A-Components",
"defaultNodeId": "1-196"
}
116 changes: 116 additions & 0 deletions packages/react-core/codeConnect/scripts/figma-url-fixer.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable no-console */
import fs from 'fs/promises';
import { glob } from 'glob';
import figmaBaseUrl from '../config.json' assert { type: 'json' };

/**
* Process a single file to fix Figma URLs with detailed debugging
*/
export async function processFigmaFile(filePath) {
try {
console.log(`\nProcessing file: ${filePath}`);
const content = await fs.readFile(filePath, 'utf8');
let modified = false;

// Find all figma.connect calls with string literals as second parameter
// Updated regex to be more flexible with whitespace and formatting
const figmaConnectRegex = /figma\.connect\(\s*[^,]+,\s*(['"])([^'"]+)\1/g;
let match;
let matchCount = 0;

// Test the content for any figma.connect calls
const hasConnect = content.includes('figma.connect');
console.log(`Contains figma.connect calls: ${hasConnect}`);

if (!hasConnect) {
return false;
}

// Process all matches
while ((match = figmaConnectRegex.exec(content)) !== null) {
matchCount++;
const [fullMatch, quotes, url] = match;
console.log(`\nMatch #${matchCount} found: ${fullMatch}`);
console.log(`URL extracted: ${url}`);

// Only process if the URL doesn't already have the correct root
const needsUpdate = !url.startsWith(figmaBaseUrl);
console.log(`URL needs update: ${needsUpdate}`);

if (needsUpdate) {
// Extract node ID from current URL
let nodeId = null;

// Try to extract from node-id parameter
const nodeIdMatch = url.match(/node-id=([^&]+)/);
if (nodeIdMatch) {
nodeId = nodeIdMatch[1];
console.log(`Found node-id in URL parameter: ${nodeId}`);
} else {
// Try to extract from end of URL (format: digits-digits)
const pathParts = url.split('/');
const lastPart = pathParts[pathParts.length - 1];
if (/^\d+-\d+$/.test(lastPart)) {
nodeId = lastPart;
console.log(`Found node-id at end of URL: ${nodeId}`);
}
}

// Only update if we successfully extracted a node ID
if (nodeId) {
const newUrl = `${figmaBaseUrl}${nodeId}`;
console.log(`New URL will be: ${newUrl}`);

// Create new content by replacing the old URL with the new one
const updatedContent = content.replace(fullMatch, fullMatch.replace(url, newUrl));

// Check if replacement actually changed anything
if (updatedContent !== content) {
console.log(`Successfully updated URL in content`);
await fs.writeFile(filePath, updatedContent, 'utf8');
console.log(`Updated file: ${filePath}`);
modified = true;
} else {
console.log(`Warning: Replacement had no effect on content`);
}
} else {
console.log(`Could not extract node ID from URL: ${url}`);
}
}
}

console.log(`Total matches found: ${matchCount}`);
return modified;
} catch (error) {
console.error(`Error processing ${filePath}:`, error);
return false;
}
}

// Simple test function that processes one file
export async function testProcessFile(filePath) {
console.log('Running test on file:', filePath);
const result = await processFigmaFile(filePath);
console.log('Processing result:', result);
}

// If this file is run directly, execute the fix
if (import.meta.url === `file://${process.argv[1]}`) {
const testFile = process.argv[2];

if (testFile) {
// Test a specific file if provided
testProcessFile(testFile);
} else {
// Otherwise, find and process all files
console.log('Finding all .figma.tsx files...');
glob('**/*.figma.tsx', {
ignore: ['**/node_modules/**', '**/dist/**']
}).then((files) => {
console.log(`Found ${files.length} files to process`);
if (files.length > 0) {
testProcessFile(files[0]); // Test with the first file found
}
});
}
}
79 changes: 79 additions & 0 deletions packages/react-core/codeConnect/scripts/figma-url-watcher.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-disable no-console */
import { glob } from 'glob';
import chokidar from 'chokidar';
import { processFigmaFile } from './figma-url-fixer.mjs';
import figmaBaseUrl from '../config.json' assert { type: 'json' };

// Figma file watcher function
async function figmaFileWather() {
console.log('Starting Figma URL watcher...');
console.log('Current directory:', process.cwd());
console.log('Using root URL:', figmaBaseUrl);

// Find all .figma.tsx files directly using glob
const files = await glob('**/*.figma.tsx', {
ignore: ['**/node_modules/**', '**/dist/**', 'codeConnect/tests/**/*'],
absolute: true // Get absolute paths
});

console.log(`Found ${files.length} .figma.tsx files in the project:`);

if (files.length === 0) {
console.log('No .figma.tsx files found. Please check your project structure.');
return;
}

// Log found files
files.forEach((file) => console.log(` - ${file}`));

// Process all files first
let fixedCount = 0;
for (const file of files) {
try {
const wasFixed = await processFigmaFile(file);
if (wasFixed) {
fixedCount++;
}
} catch (error) {
console.error(`Error processing ${file}:`, error.message);
}
}

console.log(`Initial processing complete. Fixed ${fixedCount} files.`);

// Now set up watcher for these specific files
const watcher = chokidar.watch(files, {
persistent: true,
ignoreInitial: true, // We already processed them
awaitWriteFinish: {
stabilityThreshold: 300,
pollInterval: 100
}
});

// Simple file handler
const handleFile = async (filePath) => {
console.log(`File changed: ${filePath}`);
try {
await processFigmaFile(filePath);
} catch (error) {
console.error(`Error processing ${filePath}:`, error.message);
}
};

// Set up event handlers
watcher
.on('change', handleFile)
.on('ready', () => {
console.log('Watcher ready. Monitoring these files for changes:');
files.forEach((file) => console.log(` - ${file}`));
})
.on('error', (error) => console.error(`Watcher error:`, error));

console.log('Watcher started. Press Ctrl+C to stop.');
}

// Run the figmaFileWather function
figmaFileWather().catch((error) => {
console.error('Fatal error:', error);
});
33 changes: 33 additions & 0 deletions packages/react-core/figma.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"codeConnect": {
"parser": "react",
"label": "PF-React",
"include": [
"components/*.tsx",
"icons/generated/*.tsx",
"icons/**/*.tsx",
"icons/*.tsx",
"icons/IconWrapper.figma.tsx",
"icons/icons.figma.tsx"
],
"paths": {
"../generated": ["icons/generated"],
"../src/components/Icon": ["src/components/Icon"]
},
"aliases": {
"@patternfly/react-core": "."
},
"options": {
"instanceSwapper": {
"enabled": true
},
"development": {
"enabled": true,
"verbose": true
},
"production": {
"enabled": false
}
}
}
}
7 changes: 7 additions & 0 deletions packages/react-core/package.json
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
"version": "6.2.0-prerelease.34",
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
"main": "dist/js/index.js",
"type": "module",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"typesVersions": {
@@ -42,19 +43,25 @@
"build:umd": "rollup -c --environment IS_PRODUCTION",
"build:single:packages": "node ../../scripts/build-single-packages.mjs --config single-packages.config.json",
"clean": "rimraf dist components layouts helpers next deprecated",
"figma:watchUrls": "node codeConnect/scripts/figma-url-watcher.mjs",
"figma:fixUrls": "node codeConnect/scripts/figma-url-fixer.mjs",
"generate": "node scripts/copyStyles.mjs",
"subpaths": "node ../../scripts/exportSubpaths.mjs --config subpaths.config.json"
},
"dependencies": {
"@patternfly/react-icons": "workspace:^",
"@patternfly/react-styles": "workspace:^",
"@patternfly/react-tokens": "workspace:^",
"@types/glob": "^8.1.0",
"@types/node": "^22.13.5",
"focus-trap": "7.6.4",
"react": "^18.2.0",
"react-dropzone": "^14.3.5",
"tslib": "^2.8.1"
},
"devDependencies": {
"@patternfly/patternfly": "6.2.0-prerelease.25",
"@figma/code-connect": "^1.3.1",
"case-anything": "^3.1.2",
"css": "^3.0.0",
"fs-extra": "^11.3.0"