mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 12:54:08 +01:00 
			
		
		
		
	�� Major ANTLR System Enhancements: ## New Features: - ✅ Generic ANTLR generation system (scripts/antlr-generate.mts) - ✅ Dedicated watch command for grammar development (scripts/antlr-watch.mts) - ✅ Build pipeline integration with postinstall hooks - ✅ Development server watch integration for .g4 files - ✅ Sequence diagram ANTLR parser implementation ## Build Integration: - 🏗️ Added ANTLR generation to build process (.esbuild/build.ts) - 📦 Added postinstall hooks to package.json files - 🔄 Integrated ANTLR generation with dev server (.esbuild/server-antlr.ts) - 🎯 Smart path detection for root vs package directory execution ## New Commands: - `pnpm antlr:generate` - Generic generation for all diagrams - `pnpm antlr:watch` - Grammar development with file watching - Auto-generation during `pnpm install` and `pnpm build` ## Documentation: - 📖 Consolidated all ANTLR docs into ANTLR_SETUP.md - 🗑️ Removed duplicate ANTLR_GENERATION.md - 📋 Added comprehensive troubleshooting and usage guides - 🎯 Updated with build integration and watch functionality ## Parser Implementations: - 🔄 Enhanced sequence diagram ANTLR parser with dual-pattern support - 🛠️ Added SequenceListener, SequenceVisitor, SequenceParserCore - ⚡ Improved flowchart parser integration and error handling ## Benefits: - 🔄 Zero manual steps - ANTLR files always generated automatically - ⚡ Fast grammar development with watch mode - 🎯 Unified workflow for all diagram types - 🛡️ CI/CD ready with build integration - 📊 Clear feedback and comprehensive logging This establishes a complete, production-ready ANTLR development workflow!
		
			
				
	
	
		
			177 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/* eslint-disable no-console */
 | 
						|
import chokidar from 'chokidar';
 | 
						|
import cors from 'cors';
 | 
						|
import { context } from 'esbuild';
 | 
						|
import type { Request, Response } from 'express';
 | 
						|
import express from 'express';
 | 
						|
import { execSync } from 'child_process';
 | 
						|
import { packageOptions } from '../.build/common.js';
 | 
						|
import { generateLangium } from '../.build/generateLangium.js';
 | 
						|
import { defaultOptions, getBuildConfig } from './util.js';
 | 
						|
 | 
						|
// Set environment variable to use ANTLR parser
 | 
						|
process.env.USE_ANTLR_PARSER = 'true';
 | 
						|
 | 
						|
const configs = Object.values(packageOptions).map(({ packageName }) =>
 | 
						|
  getBuildConfig({
 | 
						|
    ...defaultOptions,
 | 
						|
    minify: false,
 | 
						|
    core: false,
 | 
						|
    options: packageOptions[packageName],
 | 
						|
  })
 | 
						|
);
 | 
						|
const mermaidIIFEConfig = getBuildConfig({
 | 
						|
  ...defaultOptions,
 | 
						|
  minify: false,
 | 
						|
  core: false,
 | 
						|
  options: packageOptions.mermaid,
 | 
						|
  format: 'iife',
 | 
						|
});
 | 
						|
configs.push(mermaidIIFEConfig);
 | 
						|
 | 
						|
const contexts = await Promise.all(
 | 
						|
  configs.map(async (config) => ({ config, context: await context(config) }))
 | 
						|
);
 | 
						|
 | 
						|
let rebuildCounter = 1;
 | 
						|
const rebuildAll = async () => {
 | 
						|
  const buildNumber = rebuildCounter++;
 | 
						|
  const timeLabel = `Rebuild ${buildNumber} Time (total)`;
 | 
						|
  console.time(timeLabel);
 | 
						|
  await Promise.all(
 | 
						|
    contexts.map(async ({ config, context }) => {
 | 
						|
      const buildVariant = `Rebuild ${buildNumber} Time (${Object.keys(config.entryPoints!)[0]} ${config.format})`;
 | 
						|
      console.time(buildVariant);
 | 
						|
      await context.rebuild();
 | 
						|
      console.timeEnd(buildVariant);
 | 
						|
    })
 | 
						|
  ).catch((e) => console.error(e));
 | 
						|
  console.timeEnd(timeLabel);
 | 
						|
};
 | 
						|
 | 
						|
let clients: { id: number; response: Response }[] = [];
 | 
						|
function eventsHandler(request: Request, response: Response) {
 | 
						|
  const headers = {
 | 
						|
    'Content-Type': 'text/event-stream',
 | 
						|
    Connection: 'keep-alive',
 | 
						|
    'Cache-Control': 'no-cache',
 | 
						|
  };
 | 
						|
  response.writeHead(200, headers);
 | 
						|
  const clientId = Date.now();
 | 
						|
  clients.push({
 | 
						|
    id: clientId,
 | 
						|
    response,
 | 
						|
  });
 | 
						|
  request.on('close', () => {
 | 
						|
    clients = clients.filter((client) => client.id !== clientId);
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
let timeoutID: NodeJS.Timeout | undefined = undefined;
 | 
						|
 | 
						|
/**
 | 
						|
 * Generate ANTLR parser files from grammar files
 | 
						|
 */
 | 
						|
function generateAntlr() {
 | 
						|
  try {
 | 
						|
    console.log('🎯 ANTLR: Generating parser files...');
 | 
						|
    execSync('tsx scripts/antlr-generate.mts', { stdio: 'inherit' });
 | 
						|
    console.log('✅ ANTLR: Parser files generated successfully');
 | 
						|
  } catch (error) {
 | 
						|
    console.error('❌ ANTLR: Failed to generate parser files:', error);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Debounce file change events to avoid rebuilding multiple times.
 | 
						|
 */
 | 
						|
function handleFileChange() {
 | 
						|
  if (timeoutID !== undefined) {
 | 
						|
    clearTimeout(timeoutID);
 | 
						|
  }
 | 
						|
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
						|
  timeoutID = setTimeout(async () => {
 | 
						|
    await rebuildAll();
 | 
						|
    sendEventsToAll();
 | 
						|
    timeoutID = undefined;
 | 
						|
  }, 100);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Handle ANTLR grammar file changes with debouncing
 | 
						|
 */
 | 
						|
function handleAntlrFileChange() {
 | 
						|
  if (timeoutID !== undefined) {
 | 
						|
    clearTimeout(timeoutID);
 | 
						|
  }
 | 
						|
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
						|
  timeoutID = setTimeout(async () => {
 | 
						|
    generateAntlr();
 | 
						|
    await rebuildAll();
 | 
						|
    sendEventsToAll();
 | 
						|
    timeoutID = undefined;
 | 
						|
  }, 100);
 | 
						|
}
 | 
						|
 | 
						|
function sendEventsToAll() {
 | 
						|
  clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
 | 
						|
}
 | 
						|
 | 
						|
async function createServer() {
 | 
						|
  await generateLangium();
 | 
						|
  generateAntlr();
 | 
						|
  handleFileChange();
 | 
						|
  const app = express();
 | 
						|
 | 
						|
  // Watch for regular source file changes
 | 
						|
  chokidar
 | 
						|
    .watch('**/src/**/*.{js,ts,langium,yaml,json}', {
 | 
						|
      ignoreInitial: true,
 | 
						|
      ignored: [/node_modules/, /dist/, /docs/, /coverage/],
 | 
						|
    })
 | 
						|
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
						|
    .on('all', async (event, path) => {
 | 
						|
      // Ignore other events.
 | 
						|
      if (!['add', 'change'].includes(event)) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      console.log(`${path} changed. Rebuilding...`);
 | 
						|
      if (path.endsWith('.langium')) {
 | 
						|
        await generateLangium();
 | 
						|
      }
 | 
						|
      handleFileChange();
 | 
						|
    });
 | 
						|
 | 
						|
  // Watch for ANTLR grammar file changes
 | 
						|
  chokidar
 | 
						|
    .watch('**/src/**/parser/antlr/*.g4', {
 | 
						|
      ignoreInitial: true,
 | 
						|
      ignored: [/node_modules/, /dist/, /docs/, /coverage/],
 | 
						|
    })
 | 
						|
    .on('all', (event, path) => {
 | 
						|
      // Ignore other events.
 | 
						|
      if (!['add', 'change'].includes(event)) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      console.log(`🎯 ANTLR grammar file ${path} changed. Regenerating parsers...`);
 | 
						|
      handleAntlrFileChange();
 | 
						|
    });
 | 
						|
 | 
						|
  app.use(cors());
 | 
						|
  app.get('/events', eventsHandler);
 | 
						|
  for (const { packageName } of Object.values(packageOptions)) {
 | 
						|
    app.use(express.static(`./packages/${packageName}/dist`));
 | 
						|
  }
 | 
						|
  app.use(express.static('demos'));
 | 
						|
  app.use(express.static('cypress/platform'));
 | 
						|
 | 
						|
  app.listen(9000, () => {
 | 
						|
    console.log(`🚀 ANTLR Parser Dev Server listening on http://localhost:9000`);
 | 
						|
    console.log(`🎯 Environment: USE_ANTLR_PARSER=${process.env.USE_ANTLR_PARSER}`);
 | 
						|
    console.log(`🔍 Watching: .g4 grammar files for auto-regeneration`);
 | 
						|
    console.log(`📁 Generated: ANTLR parser files ready`);
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
void createServer();
 |