mirror of
https://github.com/LukeHagar/jsdoc-cheatsheet.git
synced 2025-12-06 04:20:07 +00:00
fix: resolve syntax highlighting error in Next.js
Create custom syntax highlighter for reliable code examples. Co-authored-by: Luke Hagar <5702154+LukeHagar@users.noreply.github.com>
This commit is contained in:
319
app/page.tsx
319
app/page.tsx
@@ -9,43 +9,51 @@ import { Separator } from "@/components/ui/separator"
|
|||||||
import { BookOpen, Code, FileText, Hash, Info, Settings, Tag, Users, ChevronRight } from "lucide-react"
|
import { BookOpen, Code, FileText, Hash, Info, Settings, Tag, Users, ChevronRight } from "lucide-react"
|
||||||
|
|
||||||
const SyntaxHighlighter = ({ code, language = "javascript" }: { code: string; language?: string }) => {
|
const SyntaxHighlighter = ({ code, language = "javascript" }: { code: string; language?: string }) => {
|
||||||
const [highlightedCode, setHighlightedCode] = useState(code)
|
const highlightCode = (text: string) => {
|
||||||
|
// Escape HTML first
|
||||||
|
let highlighted = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """)
|
||||||
|
|
||||||
useEffect(() => {
|
// Apply syntax highlighting with proper HTML structure
|
||||||
// Simple syntax highlighting for JavaScript/JSDoc
|
highlighted = highlighted
|
||||||
const highlight = (text: string) => {
|
// JSDoc comments and tags
|
||||||
return (
|
.replace(/(\/\*\*[\s\S]*?\*\/)/g, '<span class="text-green-400">$1</span>')
|
||||||
text
|
.replace(/(@\w+)/g, '<span class="text-blue-400 font-semibold">$1</span>')
|
||||||
// JSDoc tags
|
|
||||||
.replace(/(@\w+)/g, '<span class="text-blue-400 font-semibold">$1</span>')
|
// JavaScript keywords
|
||||||
// Comments
|
.replace(
|
||||||
.replace(/(\/\*\*[\s\S]*?\*\/)/g, '<span class="text-green-400">$1</span>')
|
/\b(function|class|const|let|var|if|else|for|while|return|async|await|new|this|super|extends|implements|static|public|private|protected)\b/g,
|
||||||
.replace(/(\/\/.*$)/gm, '<span class="text-green-400">$1</span>')
|
'<span class="text-purple-400 font-semibold">$1</span>',
|
||||||
// Strings
|
|
||||||
.replace(/(['"`])((?:(?!\1)[^\\]|\\.)*)(\1)/g, '<span class="text-yellow-300">$1$2$3</span>')
|
|
||||||
// Keywords
|
|
||||||
.replace(
|
|
||||||
/\b(function|class|const|let|var|if|else|for|while|return|new|this|typeof|instanceof)\b/g,
|
|
||||||
'<span class="text-purple-400 font-medium">$1</span>',
|
|
||||||
)
|
|
||||||
// Types in curly braces
|
|
||||||
.replace(/\{([^}]+)\}/g, '<span class="text-cyan-300">{$1}</span>')
|
|
||||||
// Numbers
|
|
||||||
.replace(/\b(\d+\.?\d*)\b/g, '<span class="text-orange-400">$1</span>')
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
setHighlightedCode(highlight(code))
|
// Strings
|
||||||
}, [code])
|
.replace(/(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g, '<span class="text-green-300">$1$2$1</span>')
|
||||||
|
|
||||||
|
// Numbers
|
||||||
|
.replace(/\b(\d+\.?\d*)\b/g, '<span class="text-orange-400">$1</span>')
|
||||||
|
|
||||||
|
// Booleans and null/undefined
|
||||||
|
.replace(/\b(true|false|null|undefined)\b/g, '<span class="text-red-400">$1</span>')
|
||||||
|
|
||||||
|
// Types in curly braces
|
||||||
|
.replace(/\{([^}]+)\}/g, '<span class="text-cyan-300">{<span class="text-cyan-200">$1</span>}</span>')
|
||||||
|
|
||||||
|
return highlighted
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<pre className="bg-slate-900 text-slate-100 p-4 rounded-lg text-sm font-mono overflow-x-auto border border-slate-700 leading-relaxed">
|
<pre className="bg-slate-900 text-slate-100 p-4 rounded-lg text-sm font-mono overflow-x-auto border border-slate-700 leading-relaxed">
|
||||||
<code dangerouslySetInnerHTML={{ __html: highlightedCode }} />
|
<code dangerouslySetInnerHTML={{ __html: highlightCode(code) }} />
|
||||||
</pre>
|
</pre>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = [
|
const sections = [
|
||||||
|
{
|
||||||
|
id: "kitchen-sink",
|
||||||
|
title: "Kitchen Sink Example",
|
||||||
|
icon: BookOpen,
|
||||||
|
subsections: ["Complete Example"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "basic",
|
id: "basic",
|
||||||
title: "Basic Tags",
|
title: "Basic Tags",
|
||||||
@@ -109,6 +117,206 @@ const sections = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const jsdocData = {
|
const jsdocData = {
|
||||||
|
kitchenSink: {
|
||||||
|
title: "Kitchen Sink Example",
|
||||||
|
description: "A comprehensive example showcasing multiple JSDoc features in one code block",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
tag: "Complete Example",
|
||||||
|
syntax: "Complete function documentation",
|
||||||
|
example: `/**
|
||||||
|
* Advanced user management service with comprehensive JSDoc documentation
|
||||||
|
* @module UserService
|
||||||
|
* @description Handles user creation, authentication, and profile management
|
||||||
|
* @author Jane Smith <jane@example.com>
|
||||||
|
* @version 2.1.0
|
||||||
|
* @since 1.0.0
|
||||||
|
* @requires Database
|
||||||
|
* @requires EmailService
|
||||||
|
* @exports UserService
|
||||||
|
* @example
|
||||||
|
* // Initialize the service
|
||||||
|
* const userService = new UserService(database, emailService);
|
||||||
|
*
|
||||||
|
* // Create a new user
|
||||||
|
* const user = await userService.createUser({
|
||||||
|
* name: 'John Doe',
|
||||||
|
* email: 'john@example.com',
|
||||||
|
* role: 'admin'
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} UserData
|
||||||
|
* @property {string} name - Full name of the user
|
||||||
|
* @property {string} email - Email address (must be unique)
|
||||||
|
* @property {('admin'|'user'|'guest')} [role='user'] - User role in the system
|
||||||
|
* @property {number} [age] - User age (optional)
|
||||||
|
* @property {UserPreferences} [preferences] - User preferences object
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} UserPreferences
|
||||||
|
* @property {boolean} emailNotifications - Whether to send email notifications
|
||||||
|
* @property {string} theme - UI theme preference
|
||||||
|
* @property {string} language - Preferred language code
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback ValidationCallback
|
||||||
|
* @param {Error|null} error - Validation error if any
|
||||||
|
* @param {boolean} isValid - Whether the data is valid
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User management service class
|
||||||
|
* @class UserService
|
||||||
|
* @implements {EventEmitter}
|
||||||
|
* @fires UserService#userCreated
|
||||||
|
* @fires UserService#userDeleted
|
||||||
|
* @example
|
||||||
|
* const service = new UserService(db, emailService);
|
||||||
|
* service.on('userCreated', (user) => {
|
||||||
|
* console.log('New user created:', user.name);
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
class UserService extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Creates a new UserService instance
|
||||||
|
* @constructor
|
||||||
|
* @param {Database} database - Database connection instance
|
||||||
|
* @param {EmailService} emailService - Email service for notifications
|
||||||
|
* @throws {TypeError} When database or emailService is not provided
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
constructor(database, emailService) {
|
||||||
|
super();
|
||||||
|
if (!database || !emailService) {
|
||||||
|
throw new TypeError('Database and EmailService are required');
|
||||||
|
}
|
||||||
|
this.db = database;
|
||||||
|
this.emailService = emailService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new user in the system
|
||||||
|
* @async
|
||||||
|
* @method createUser
|
||||||
|
* @memberof UserService
|
||||||
|
* @param {UserData} userData - User information object
|
||||||
|
* @param {Object} [options={}] - Additional options
|
||||||
|
* @param {boolean} [options.sendWelcomeEmail=true] - Send welcome email
|
||||||
|
* @param {boolean} [options.validateEmail=true] - Validate email format
|
||||||
|
* @returns {Promise<User>} Promise resolving to created user object
|
||||||
|
* @throws {ValidationError} When user data is invalid
|
||||||
|
* @throws {DuplicateEmailError} When email already exists
|
||||||
|
* @throws {DatabaseError} When database operation fails
|
||||||
|
* @fires UserService#userCreated
|
||||||
|
* @example
|
||||||
|
* // Basic user creation
|
||||||
|
* const user = await userService.createUser({
|
||||||
|
* name: 'Alice Johnson',
|
||||||
|
* email: 'alice@example.com'
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Advanced user creation with options
|
||||||
|
* const adminUser = await userService.createUser({
|
||||||
|
* name: 'Bob Admin',
|
||||||
|
* email: 'bob@example.com',
|
||||||
|
* role: 'admin',
|
||||||
|
* preferences: {
|
||||||
|
* emailNotifications: false,
|
||||||
|
* theme: 'dark',
|
||||||
|
* language: 'en'
|
||||||
|
* }
|
||||||
|
* }, {
|
||||||
|
* sendWelcomeEmail: false,
|
||||||
|
* validateEmail: true
|
||||||
|
* });
|
||||||
|
* @since 1.0.0
|
||||||
|
* @todo Add support for bulk user creation
|
||||||
|
* @todo Implement user avatar upload
|
||||||
|
*/
|
||||||
|
async createUser(userData, options = {}) {
|
||||||
|
// Implementation here...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User created event
|
||||||
|
* @event UserService#userCreated
|
||||||
|
* @type {Object}
|
||||||
|
* @property {User} user - The created user object
|
||||||
|
* @property {Date} timestamp - When the user was created
|
||||||
|
*/
|
||||||
|
this.emit('userCreated', { user: newUser, timestamp: new Date() });
|
||||||
|
|
||||||
|
return newUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates user email address
|
||||||
|
* @static
|
||||||
|
* @method validateEmail
|
||||||
|
* @param {string} email - Email address to validate
|
||||||
|
* @param {ValidationCallback} callback - Callback function for validation result
|
||||||
|
* @returns {boolean} True if email format is valid
|
||||||
|
* @example
|
||||||
|
* // Synchronous validation
|
||||||
|
* const isValid = UserService.validateEmail('test@example.com');
|
||||||
|
*
|
||||||
|
* // Asynchronous validation with callback
|
||||||
|
* UserService.validateEmail('test@example.com', (error, isValid) => {
|
||||||
|
* if (error) {
|
||||||
|
* console.error('Validation error:', error);
|
||||||
|
* } else {
|
||||||
|
* console.log('Email is valid:', isValid);
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
static validateEmail(email, callback) {
|
||||||
|
// Implementation here...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since version 2.0.0. Use {@link UserService#createUser} instead.
|
||||||
|
* @method addUser
|
||||||
|
* @param {UserData} userData - User data
|
||||||
|
* @returns {Promise<User>} Created user
|
||||||
|
* @see {@link UserService#createUser}
|
||||||
|
*/
|
||||||
|
async addUser(userData) {
|
||||||
|
console.warn('addUser is deprecated. Use createUser instead.');
|
||||||
|
return this.createUser(userData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace UserService.Utils
|
||||||
|
* @description Utility functions for user management
|
||||||
|
*/
|
||||||
|
UserService.Utils = {
|
||||||
|
/**
|
||||||
|
* Formats user display name
|
||||||
|
* @function formatDisplayName
|
||||||
|
* @memberof UserService.Utils
|
||||||
|
* @param {string} firstName - User's first name
|
||||||
|
* @param {string} lastName - User's last name
|
||||||
|
* @returns {string} Formatted display name
|
||||||
|
* @example
|
||||||
|
* const displayName = UserService.Utils.formatDisplayName('John', 'Doe');
|
||||||
|
* console.log(displayName); // "John D."
|
||||||
|
*/
|
||||||
|
formatDisplayName(firstName, lastName) {
|
||||||
|
return \`\${firstName} \${lastName.charAt(0)}.\`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = UserService;`,
|
||||||
|
description: "Complete function documentation with all common tags",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
basic: {
|
basic: {
|
||||||
title: "Basic Tags",
|
title: "Basic Tags",
|
||||||
description: "Essential JSDoc tags for documenting your code",
|
description: "Essential JSDoc tags for documenting your code",
|
||||||
@@ -116,32 +324,34 @@ const jsdocData = {
|
|||||||
{
|
{
|
||||||
tag: "@description",
|
tag: "@description",
|
||||||
syntax: "@description {string} Description text",
|
syntax: "@description {string} Description text",
|
||||||
example: "/**\n * @description Calculates the sum of two numbers\n */",
|
example:
|
||||||
|
"/**\n * @description Calculates the sum of two numbers\n * @param {number} a - First number\n * @param {number} b - Second number\n * @returns {number} The sum\n */\nfunction add(a, b) {\n return a + b;\n}",
|
||||||
description: "Provides a description of the function, class, or variable",
|
description: "Provides a description of the function, class, or variable",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@author",
|
tag: "@author",
|
||||||
syntax: "@author {string} Author name <email>",
|
syntax: "@author {string} Author name <email>",
|
||||||
example: "/**\n * @author John Doe <john@example.com>\n */",
|
example: "/**\n * @author John Doe <john@example.com>\n * @author Jane Smith <jane@example.com>\n */",
|
||||||
description: "Specifies the author of the code",
|
description: "Specifies the author(s) of the code",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@version",
|
tag: "@version",
|
||||||
syntax: "@version {string} Version number",
|
syntax: "@version {string} Version number",
|
||||||
example: "/**\n * @version 1.2.0\n */",
|
example: "/**\n * @version 1.2.0\n * @since 1.0.0\n */",
|
||||||
description: "Indicates the version of the code",
|
description: "Indicates the current version of the code",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@since",
|
tag: "@since",
|
||||||
syntax: "@since {string} Version when added",
|
syntax: "@since {string} Version when added",
|
||||||
example: "/**\n * @since 1.0.0\n */",
|
example: "/**\n * @since 1.0.0\n * @description Added in the initial release\n */",
|
||||||
description: "Specifies when the feature was added",
|
description: "Specifies when the feature was first added",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@deprecated",
|
tag: "@deprecated",
|
||||||
syntax: "@deprecated {string} Deprecation message",
|
syntax: "@deprecated {string} Deprecation message",
|
||||||
example: "/**\n * @deprecated Use newFunction() instead\n */",
|
example:
|
||||||
description: "Marks code as deprecated",
|
"/**\n * @deprecated Since version 2.0.0. Use newFunction() instead.\n * @see {@link newFunction}\n */\nfunction oldFunction() {\n // deprecated implementation\n}",
|
||||||
|
description: "Marks code as deprecated with migration guidance",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -152,25 +362,29 @@ const jsdocData = {
|
|||||||
{
|
{
|
||||||
tag: "@param",
|
tag: "@param",
|
||||||
syntax: "@param {type} name Description",
|
syntax: "@param {type} name Description",
|
||||||
example: "/**\n * @param {number} x - The first number\n * @param {number} y - The second number\n */",
|
example:
|
||||||
description: "Documents function parameters",
|
"/**\n * @param {number} x - The first number\n * @param {number} y - The second number\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.strict=false] - Use strict mode\n */\nfunction calculate(x, y, options = {}) {\n // implementation\n}",
|
||||||
|
description: "Documents function parameters with types and descriptions",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@returns",
|
tag: "@returns",
|
||||||
syntax: "@returns {type} Description",
|
syntax: "@returns {type} Description",
|
||||||
example: "/**\n * @returns {number} The sum of x and y\n */",
|
example:
|
||||||
description: "Documents the return value",
|
"/**\n * @param {number[]} numbers - Array of numbers\n * @returns {number} The sum of all numbers\n * @returns {null} Returns null if array is empty\n */\nfunction sum(numbers) {\n if (numbers.length === 0) return null;\n return numbers.reduce((a, b) => a + b, 0);\n}",
|
||||||
|
description: "Documents the return value and its type",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@throws",
|
tag: "@throws",
|
||||||
syntax: "@throws {ErrorType} Description",
|
syntax: "@throws {ErrorType} Description",
|
||||||
example: "/**\n * @throws {TypeError} When input is not a number\n */",
|
example:
|
||||||
|
"/**\n * @param {string} email - User email address\n * @throws {TypeError} When email is not a string\n * @throws {Error} When email format is invalid\n */\nfunction validateEmail(email) {\n if (typeof email !== 'string') {\n throw new TypeError('Email must be a string');\n }\n if (!email.includes('@')) {\n throw new Error('Invalid email format');\n }\n}",
|
||||||
description: "Documents exceptions that may be thrown",
|
description: "Documents exceptions that may be thrown",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "@yields",
|
tag: "@yields",
|
||||||
syntax: "@yields {type} Description",
|
syntax: "@yields {type} Description",
|
||||||
example: "/**\n * @yields {number} The next number in sequence\n */",
|
example:
|
||||||
|
"/**\n * @generator\n * @yields {number} The next number in the Fibonacci sequence\n * @returns {Generator<number, void, unknown>}\n */\nfunction* fibonacci() {\n let a = 0, b = 1;\n while (true) {\n yield a;\n [a, b] = [b, a + b];\n }\n}",
|
||||||
description: "Documents what a generator function yields",
|
description: "Documents what a generator function yields",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -397,6 +611,7 @@ class User {
|
|||||||
* @module UserManagement
|
* @module UserManagement
|
||||||
* @description Handles user creation and deletion
|
* @description Handles user creation and deletion
|
||||||
* @requires Database
|
* @requires Database
|
||||||
|
* @requires EmailService
|
||||||
* @exports createUser
|
* @exports createUser
|
||||||
* @exports deleteUser
|
* @exports deleteUser
|
||||||
* @example
|
* @example
|
||||||
@@ -408,6 +623,7 @@ class User {
|
|||||||
* deleteUser(user.id);
|
* deleteUser(user.id);
|
||||||
*/
|
*/
|
||||||
const Database = require('./Database');
|
const Database = require('./Database');
|
||||||
|
const EmailService = require('./EmailService');
|
||||||
|
|
||||||
function createUser(name, email) {
|
function createUser(name, email) {
|
||||||
// Implementation here
|
// Implementation here
|
||||||
@@ -534,8 +750,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function JSDocCheatsheet() {
|
export default function JSDocCheatsheet() {
|
||||||
const [activeSection, setActiveSection] = useState("basic")
|
const [activeSection, setActiveSection] = useState("kitchen-sink")
|
||||||
const [expandedSections, setExpandedSections] = useState<string[]>(["basic"])
|
const [expandedSections, setExpandedSections] = useState<string[]>(["kitchen-sink"])
|
||||||
const [isScrolling, setIsScrolling] = useState(false)
|
const [isScrolling, setIsScrolling] = useState(false)
|
||||||
|
|
||||||
const toggleSection = (sectionId: string) => {
|
const toggleSection = (sectionId: string) => {
|
||||||
@@ -670,6 +886,23 @@ export default function JSDocCheatsheet() {
|
|||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="flex-1 min-w-0">
|
<main className="flex-1 min-w-0">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{/* Kitchen Sink Example */}
|
||||||
|
<Card className="border-primary/20 bg-gradient-to-br from-primary/5 to-primary/10 shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-2xl text-foreground flex items-center gap-2">
|
||||||
|
<Badge variant="default" className="bg-primary text-primary-foreground">
|
||||||
|
Kitchen Sink Example
|
||||||
|
</Badge>
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="text-base">
|
||||||
|
A comprehensive example showcasing multiple JSDoc features in one code block
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<SyntaxHighlighter code={jsdocData.kitchenSink.items[0].example} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Section Header */}
|
{/* Section Header */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<h2 className="text-3xl font-bold text-foreground mb-2">
|
<h2 className="text-3xl font-bold text-foreground mb-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user