Require Dart 3.3 (#88)

update lints, test on wasm
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index 4bd3568..9546b36 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -49,7 +49,7 @@
       matrix:
         # Add macos-latest and/or windows-latest if relevant for this package.
         os: [ubuntu-latest]
-        sdk: [2.18.0, dev]
+        sdk: [3.3, dev]
     steps:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
       - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3
@@ -67,3 +67,6 @@
       - name: Run Node tests
         run: dart test --platform node
         if: always() && steps.install.outcome == 'success'
+      - name: Run Chrome tests - wasm
+        run: dart test --platform chrome --compiler dart2wasm
+        if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 422e078..7263fc9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.1.3-wip
+
+- Require Dart 3.3.
+
 ## 2.1.2
 
 - Allow `file` version `7.x`.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index d802d72..90ba07b 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -9,5 +9,3 @@
     - avoid_unused_constructor_parameters
     - cancel_subscriptions
     - package_api_docs
-    - test_types_in_equals
-    - use_super_parameters
diff --git a/lib/glob.dart b/lib/glob.dart
index a7b92f6..f5e4bb4 100644
--- a/lib/glob.dart
+++ b/lib/glob.dart
@@ -48,7 +48,8 @@
   /// The parsed AST of the glob.
   final AstNode _ast;
 
-  /// The underlying object used to implement [list] and [listSync].
+  /// The underlying object used to implement [listFileSystem] and
+  /// [listFileSystemSync].
   ///
   /// This should not be read directly outside of [_listTreeForFileSystem].
   ListTree? _listTree;
diff --git a/lib/list_local_fs.dart b/lib/list_local_fs.dart
index 946596a..6ccbde0 100644
--- a/lib/list_local_fs.dart
+++ b/lib/list_local_fs.dart
@@ -4,7 +4,8 @@
 
 import 'package:file/file.dart';
 import 'package:file/local.dart';
-import 'package:glob/glob.dart';
+
+import 'glob.dart';
 
 /// Platform specific extensions for where `dart:io` exists, which use the
 /// local file system.
diff --git a/lib/src/ast.dart b/lib/src/ast.dart
index ac283d5..179b081 100644
--- a/lib/src/ast.dart
+++ b/lib/src/ast.dart
@@ -34,7 +34,7 @@
   /// In particular, this returns a glob AST with two guarantees:
   ///
   /// 1. There are no [OptionsNode]s other than the one at the top level.
-  /// 2. It matches the same set of paths as [this].
+  /// 2. It matches the same set of paths as `this`.
   ///
   /// For example, given the glob `{foo,bar}/{click/clack}`, this would return
   /// `{foo/click,foo/clack,bar/click,bar/clack}`.
@@ -189,10 +189,10 @@
   @override
   bool operator ==(Object other) =>
       other is SequenceNode &&
-      const IterableEquality().equals(nodes, other.nodes);
+      const IterableEquality<AstNode>().equals(nodes, other.nodes);
 
   @override
-  int get hashCode => const IterableEquality().hash(nodes);
+  int get hashCode => const IterableEquality<AstNode>().hash(nodes);
 
   @override
   String toString() => nodes.join();
@@ -343,10 +343,11 @@
   bool operator ==(Object other) =>
       other is RangeNode &&
       other.negated == negated &&
-      SetEquality().equals(ranges, other.ranges);
+      const SetEquality<Range>().equals(ranges, other.ranges);
 
   @override
-  int get hashCode => (negated ? 1 : 3) * const SetEquality().hash(ranges);
+  int get hashCode =>
+      (negated ? 1 : 3) * const SetEquality<Range>().hash(ranges);
 
   @override
   String toString() {
@@ -389,10 +390,12 @@
   @override
   bool operator ==(Object other) =>
       other is OptionsNode &&
-      const UnorderedIterableEquality().equals(options, other.options);
+      const UnorderedIterableEquality<SequenceNode>()
+          .equals(options, other.options);
 
   @override
-  int get hashCode => const UnorderedIterableEquality().hash(options);
+  int get hashCode =>
+      const UnorderedIterableEquality<SequenceNode>().hash(options);
 
   @override
   String toString() => '{${options.join(',')}}';
@@ -412,7 +415,7 @@
   bool get canMatchAbsolute {
     var nativeText =
         _context!.style == p.Style.windows ? text.replaceAll('/', '\\') : text;
-    return _context!.isAbsolute(nativeText);
+    return _context.isAbsolute(nativeText);
   }
 
   @override
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index c02c513..64bc974 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -20,7 +20,7 @@
   /// Returns a range that covers only [value].
   Range.singleton(int value) : this(value, value);
 
-  /// Whether [this] contains [value].
+  /// Whether `this` contains [value].
   bool contains(int value) => value >= min && value <= max;
 
   @override
@@ -31,7 +31,7 @@
   int get hashCode => 3 * min + 7 * max;
 }
 
-/// An implementation of [Match] constructed by [Glob]s.
+/// An implementation of [Match] constructed by `Glob`s.
 class GlobMatch implements Match {
   @override
   final String input;
diff --git a/pubspec.yaml b/pubspec.yaml
index 9768d9c..61794c0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,10 +1,10 @@
 name: glob
-version: 2.1.2
+version: 2.1.3-wip
 description: A library to perform Bash-style file and directory globbing.
 repository: https://github.com/dart-lang/glob
 
 environment:
-  sdk: '>=2.19.0 <4.0.0'
+  sdk: ^3.3.0
 
 dependencies:
   async: ^2.5.0
@@ -14,6 +14,6 @@
   string_scanner: ^1.1.0
 
 dev_dependencies:
-  dart_flutter_team_lints: ^1.0.0
+  dart_flutter_team_lints: ^2.0.0
   test: ^1.17.0
   test_descriptor: ^2.0.0
diff --git a/test/list_test.dart b/test/list_test.dart
index 912cade..12a3688 100644
--- a/test/list_test.dart
+++ b/test/list_test.dart
@@ -31,14 +31,14 @@
     test('returns empty list for non-existent case-sensitive directories',
         () async {
       expect(await Glob('non/existent/**', caseSensitive: true).list().toList(),
-          []);
+          <Never>[]);
     });
 
     test('returns empty list for non-existent case-insensitive directories',
         () async {
       expect(
           await Glob('non/existent/**', caseSensitive: false).list().toList(),
-          []);
+          <Never>[]);
     });
   });
 
@@ -48,26 +48,30 @@
     });
 
     test('returns empty list for non-existent case-sensitive directories', () {
-      expect(Glob('non/existent/**', caseSensitive: true).listSync(), []);
+      expect(
+          Glob('non/existent/**', caseSensitive: true).listSync(), <Never>[]);
     });
 
     test('returns empty list for non-existent case-insensitive directories',
         () {
-      expect(Glob('non/existent/**', caseSensitive: false).listSync(), []);
+      expect(
+          Glob('non/existent/**', caseSensitive: false).listSync(), <Never>[]);
     });
   });
 
   group('when case-sensitive', () {
     test('lists literals case-sensitively', () {
-      expect(Glob('foo/BAZ/qux', caseSensitive: true).listSync(), []);
+      expect(Glob('foo/BAZ/qux', caseSensitive: true).listSync(), <Never>[]);
     });
 
     test('lists ranges case-sensitively', () {
-      expect(Glob('foo/[BX][A-Z]z/qux', caseSensitive: true).listSync(), []);
+      expect(Glob('foo/[BX][A-Z]z/qux', caseSensitive: true).listSync(),
+          <Never>[]);
     });
 
     test('options preserve case-sensitivity', () {
-      expect(Glob('foo/{BAZ,ZAP}/qux', caseSensitive: true).listSync(), []);
+      expect(
+          Glob('foo/{BAZ,ZAP}/qux', caseSensitive: true).listSync(), <Never>[]);
     });
   });
 
@@ -317,7 +321,7 @@
     {bool recursive, bool followLinks, bool? caseSensitive});
 
 /// Runs [callback] in two groups with two values of [listFn]: one that uses
-/// [Glob.list], one that uses [Glob.listSync].
+/// `Glob.list`, one that uses `Glob.listSync`.
 void syncAndAsync(FutureOr Function(ListFn) callback) {
   group('async', () {
     callback((pattern, {recursive = false, followLinks = true, caseSensitive}) {