✨ Introduce AlertBlockSyntax (dart-lang/markdown#570)

* ✨ Introduce `CalloutBlockSyntax`

* ⚑️ More flex patterns

* πŸš€ Produce correct structure

* ⚑️ Case-insensitive types

* ⚑️ Improve `canParse`

* βœ… Fill test cases

* Update AUTHORS

* 🚚 Rename

* πŸ“ Add CHANGELOG

* πŸ”₯ Remove `zh`

* Update CHANGELOG.md

* Update pubspec.yaml

* πŸ”– Fix version

* πŸ§ͺ Add escape brackets case

* ⚑️ const type text map
diff --git a/pkgs/markdown/AUTHORS b/pkgs/markdown/AUTHORS
index 7f87fc4..ca5b46b 100644
--- a/pkgs/markdown/AUTHORS
+++ b/pkgs/markdown/AUTHORS
@@ -10,4 +10,4 @@
 Jirka DanΔ›k <dnk@mail.muni.cz>
 Seth Westphal <westy92@gmail.com>
 Tim Maffett <timmaffett@gmail.com>
-
+Alex Li <alexv.525.li@gmail.com>
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index 5fcfd57..fe71414 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -1,8 +1,9 @@
-## 7.1.2-wip
+## 7.2.0-wip
 
 * Require Dart `^3.1.0`.
 * Update all CommonMark specification links to 0.30.
-* Fix beginning of line detection in `AutolinkExtensionSyntax`. 
+* Fix beginning of line detection in `AutolinkExtensionSyntax`.
+* Add a new syntax `AlertBlockSyntax` to parse GitHub Alerts
 
 ## 7.1.1
 
diff --git a/pkgs/markdown/lib/markdown.dart b/pkgs/markdown/lib/markdown.dart
index 409baaf..a09e763 100644
--- a/pkgs/markdown/lib/markdown.dart
+++ b/pkgs/markdown/lib/markdown.dart
@@ -42,6 +42,7 @@
 
 export 'src/ast.dart';
 export 'src/block_parser.dart';
+export 'src/block_syntaxes/alert_block_syntax.dart';
 export 'src/block_syntaxes/block_syntax.dart';
 export 'src/block_syntaxes/blockquote_syntax.dart';
 export 'src/block_syntaxes/code_block_syntax.dart';
diff --git a/pkgs/markdown/lib/src/block_syntaxes/alert_block_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/alert_block_syntax.dart
new file mode 100644
index 0000000..be28ca9
--- /dev/null
+++ b/pkgs/markdown/lib/src/block_syntaxes/alert_block_syntax.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../ast.dart';
+import '../block_parser.dart';
+import '../line.dart';
+import '../patterns.dart';
+import 'block_syntax.dart';
+import 'code_block_syntax.dart';
+import 'paragraph_syntax.dart';
+
+/// Parses GitHub Alerts blocks.
+///
+/// See also: https://docs.github.com/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
+class AlertBlockSyntax extends BlockSyntax {
+  const AlertBlockSyntax();
+
+  @override
+  RegExp get pattern => alertPattern;
+
+  @override
+  bool canParse(BlockParser parser) {
+    return pattern.hasMatch(parser.current.content) &&
+        parser.lines.any((line) => _contentLineRegExp.hasMatch(line.content));
+  }
+
+  /// Whether this alert ends with a lazy continuation line.
+  // The definition of lazy continuation lines:
+  // https://spec.commonmark.org/0.30/#lazy-continuation-line
+  static bool _lazyContinuation = false;
+  static final _contentLineRegExp = RegExp(r'>?\s?(.*)*');
+
+  @override
+  List<Line> parseChildLines(BlockParser parser) {
+    // Grab all of the lines that form the alert, stripping off the ">".
+    final childLines = <Line>[];
+    _lazyContinuation = false;
+
+    while (!parser.isDone) {
+      final strippedContent =
+          parser.current.content.replaceFirst(RegExp(r'^\s*>?\s*'), '');
+      final match = _contentLineRegExp.firstMatch(strippedContent);
+      if (match != null) {
+        childLines.add(Line(strippedContent));
+        parser.advance();
+        _lazyContinuation = false;
+        continue;
+      }
+
+      final lastLine = childLines.last;
+
+      // A paragraph continuation is OK. This is content that cannot be parsed
+      // as any other syntax except Paragraph, and it doesn't match the bar in
+      // a Setext header.
+      // Because indented code blocks cannot interrupt paragraphs, a line
+      // matched CodeBlockSyntax is also paragraph continuation text.
+      final otherMatched =
+          parser.blockSyntaxes.firstWhere((s) => s.canParse(parser));
+      if ((otherMatched is ParagraphSyntax &&
+              !lastLine.isBlankLine &&
+              !codeFencePattern.hasMatch(lastLine.content)) ||
+          (otherMatched is CodeBlockSyntax &&
+              !indentPattern.hasMatch(lastLine.content))) {
+        childLines.add(parser.current);
+        _lazyContinuation = true;
+        parser.advance();
+      } else {
+        break;
+      }
+    }
+
+    return childLines;
+  }
+
+  @override
+  Node parse(BlockParser parser) {
+    // Parse the alert type from the first line.
+    final type =
+        pattern.firstMatch(parser.current.content)!.group(1)!.toLowerCase();
+    parser.advance();
+    final childLines = parseChildLines(parser);
+    // Recursively parse the contents of the alert.
+    final children = BlockParser(childLines, parser.document).parseLines(
+      // The setext heading underline cannot be a lazy continuation line in a
+      // block quote.
+      // https://spec.commonmark.org/0.30/#example-93
+      disabledSetextHeading: _lazyContinuation,
+      parentSyntax: this,
+    );
+
+    // Mapping the alert title text.
+    const typeTextMap = {
+      'note': 'Note',
+      'tip': 'Tip',
+      'important': 'Important',
+      'caution': 'Caution',
+      'warning': 'Warning',
+    };
+    final titleText = typeTextMap[type]!;
+    final titleElement = Element('p', [Text(titleText)])
+      ..attributes['class'] = 'markdown-alert-title';
+    final elementClass = 'markdown-alert markdown-alert-${type.toLowerCase()}';
+    return Element('div', [titleElement, ...children])
+      ..attributes['class'] = elementClass;
+  }
+}
diff --git a/pkgs/markdown/lib/src/extension_set.dart b/pkgs/markdown/lib/src/extension_set.dart
index 660759e..58a25d8 100644
--- a/pkgs/markdown/lib/src/extension_set.dart
+++ b/pkgs/markdown/lib/src/extension_set.dart
@@ -1,3 +1,4 @@
+import 'block_syntaxes/alert_block_syntax.dart';
 import 'block_syntaxes/block_syntax.dart';
 import 'block_syntaxes/fenced_code_block_syntax.dart';
 import 'block_syntaxes/footnote_def_syntax.dart';
@@ -60,6 +61,7 @@
         const UnorderedListWithCheckboxSyntax(),
         const OrderedListWithCheckboxSyntax(),
         const FootnoteDefSyntax(),
+        const AlertBlockSyntax(),
       ],
     ),
     List<InlineSyntax>.unmodifiable(
diff --git a/pkgs/markdown/lib/src/patterns.dart b/pkgs/markdown/lib/src/patterns.dart
index c75838f..4899ea1 100644
--- a/pkgs/markdown/lib/src/patterns.dart
+++ b/pkgs/markdown/lib/src/patterns.dart
@@ -151,3 +151,12 @@
 
 /// A line starts with `[`.
 final linkReferenceDefinitionPattern = RegExp(r'^[ ]{0,3}\[');
+
+/// Alert type patterns.
+/// A alert block is similar to a blockquote,
+/// starts with `> [!TYPE]`, and only 5 types are supported
+/// with case-insensitive.
+final alertPattern = RegExp(
+  r'^\s{0,3}>\s{0,3}\\?\[!(note|tip|important|caution|warning)\\?\]\s*$',
+  caseSensitive: false,
+);
diff --git a/pkgs/markdown/lib/src/version.dart b/pkgs/markdown/lib/src/version.dart
index c8a2b00..a70cac5 100644
--- a/pkgs/markdown/lib/src/version.dart
+++ b/pkgs/markdown/lib/src/version.dart
@@ -1,2 +1,2 @@
 // Generated code. Do not modify.
-const packageVersion = '7.1.2-wip';
+const packageVersion = '7.2.0-wip';
diff --git a/pkgs/markdown/pubspec.yaml b/pkgs/markdown/pubspec.yaml
index 6ddea8f..2ef835f 100644
--- a/pkgs/markdown/pubspec.yaml
+++ b/pkgs/markdown/pubspec.yaml
@@ -1,5 +1,5 @@
 name: markdown
-version: 7.1.2-wip
+version: 7.2.0-wip
 
 description: >-
   A portable Markdown library written in Dart that can parse Markdown into HTML.
diff --git a/pkgs/markdown/test/extensions/alert_extension.unit b/pkgs/markdown/test/extensions/alert_extension.unit
new file mode 100644
index 0000000..c2a3da1
--- /dev/null
+++ b/pkgs/markdown/test/extensions/alert_extension.unit
@@ -0,0 +1,94 @@
+>>> type note
+> [!NoTe]
+> Test note alert.
+<<<
+<div class="markdown-alert markdown-alert-note">
+<p class="markdown-alert-title">Note</p>
+<p>Test note alert.</p>
+</div>
+>>> type tip
+> [!TiP]
+> Test tip alert.
+<<<
+<div class="markdown-alert markdown-alert-tip">
+<p class="markdown-alert-title">Tip</p>
+<p>Test tip alert.</p>
+</div>
+>>> type important
+> [!ImpoRtanT]
+> Test important alert.
+<<<
+<div class="markdown-alert markdown-alert-important">
+<p class="markdown-alert-title">Important</p>
+<p>Test important alert.</p>
+</div>
+>>> type warning
+> [!WarNinG]
+> Test warning alert.
+<<<
+<div class="markdown-alert markdown-alert-warning">
+<p class="markdown-alert-title">Warning</p>
+<p>Test warning alert.</p>
+</div>
+>>> type caution
+> [!CauTioN]
+> Test caution alert.
+<<<
+<div class="markdown-alert markdown-alert-caution">
+<p class="markdown-alert-title">Caution</p>
+<p>Test caution alert.</p>
+</div>
+>>> invalid type
+> [!foo]
+> Test foo alert.
+<<<
+<blockquote>
+<p>[!foo]
+Test foo alert.</p>
+</blockquote>
+>>> contents can both contain/not contain starting quote
+> [!NOTE]
+Test note alert.
+>Test note alert x2.
+<<<
+<div class="markdown-alert markdown-alert-note">
+<p class="markdown-alert-title">Note</p>
+<p>Test note alert.
+Test note alert x2.</p>
+</div>
+>>> spaces everywhere
+ >  [!NOTE]
+>   Test note alert.
+  > Test note alert x2.
+<<<
+<div class="markdown-alert markdown-alert-note">
+<p class="markdown-alert-title">Note</p>
+<p>Test note alert.
+Test note alert x2.</p>
+</div>
+>>> title has 3 more spaces then fallback to blockquote
+>    [!NOTE]
+>   Test blockquote.
+<<<
+<blockquote>
+<p>[!NOTE]
+Test blockquote.</p>
+</blockquote>
+>>>nested blockquote
+> [!NOTE]
+>> Test nested blockquote.
+<<<
+<div class="markdown-alert markdown-alert-note">
+<p class="markdown-alert-title">Note</p>
+<blockquote>
+<p>Test nested blockquote.</p>
+</blockquote>
+</div>
+>>>escape brackets
+> \[!note\]
+> Test escape brackets.
+<<<
+<div class="markdown-alert markdown-alert-note">
+<p class="markdown-alert-title">Note</p>
+<p>Test escape brackets.</p>
+</div>
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 4d21fe8..1fea683 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -43,11 +43,16 @@
     blockSyntaxes: [const UnorderedListWithCheckboxSyntax()],
   );
   testFile(
+    'extensions/alert_extension.unit',
+    blockSyntaxes: [const AlertBlockSyntax()],
+  );
+
+  // Inline syntax extensions
+  testFile(
     'extensions/autolink_extension.unit',
     inlineSyntaxes: [AutolinkExtensionSyntax()],
   );
 
-  // Inline syntax extensions
   testFile(
     'extensions/emojis.unit',
     inlineSyntaxes: [EmojiSyntax()],