Add stubs for Skwasm renderer (#35748)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 260ae32..41a98a9 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1283,6 +1283,10 @@
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codecs.dart
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/serialization.dart
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart
+FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
+FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart
+FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart
+FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/svg.dart
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/test_embedding.dart
 FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
diff --git a/lib/web_ui/dev/steps/compile_tests_step.dart b/lib/web_ui/dev/steps/compile_tests_step.dart
index 40656b5..e9931b7 100644
--- a/lib/web_ui/dev/steps/compile_tests_step.dart
+++ b/lib/web_ui/dev/steps/compile_tests_step.dart
@@ -186,11 +186,16 @@
   // different dart2js options.
   final List<FilePath> htmlTargets = <FilePath>[];
   final List<FilePath> canvasKitTargets = <FilePath>[];
+  final List<FilePath> skwasmTargets = <FilePath>[];
   final String canvasKitTestDirectory =
       pathlib.join(environment.webUiTestDir.path, 'canvaskit');
+  final String skwasmTestDirectory =
+      pathlib.join(environment.webUiTestDir.path, 'skwasm');
   for (final FilePath testFile in testFiles) {
     if (pathlib.isWithin(canvasKitTestDirectory, testFile.absolute)) {
       canvasKitTargets.add(testFile);
+    } else if (pathlib.isWithin(skwasmTestDirectory, testFile.absolute)) {
+      skwasmTargets.add(testFile);
     } else {
       htmlTargets.add(testFile);
     }
@@ -198,9 +203,11 @@
 
   await Future.wait(<Future<void>>[
     if (htmlTargets.isNotEmpty)
-      _compileTestsInParallel(targets: htmlTargets, forCanvasKit: false),
+      _compileTestsInParallel(targets: htmlTargets),
     if (canvasKitTargets.isNotEmpty)
       _compileTestsInParallel(targets: canvasKitTargets, forCanvasKit: true),
+    if (skwasmTargets.isNotEmpty)
+      _compileTestsInParallel(targets: skwasmTargets, forSkwasm: true),
   ]);
 
   stopwatch.stop();
@@ -220,11 +227,12 @@
 /// Spawns multiple dart2js processes to compile [targets] in parallel.
 Future<void> _compileTestsInParallel({
   required List<FilePath> targets,
-  required bool forCanvasKit,
+  bool forCanvasKit = false,
+  bool forSkwasm = false,
 }) async {
   final Stream<bool> results = _dart2jsPool.forEach(
     targets,
-    (FilePath file) => compileUnitTest(file, forCanvasKit: forCanvasKit),
+    (FilePath file) => compileUnitTest(file, forCanvasKit: forCanvasKit, forSkwasm: forSkwasm),
   );
   await for (final bool isSuccess in results) {
     if (!isSuccess) {
@@ -250,7 +258,7 @@
 /// directory before test are build. See [_copyFilesFromTestToBuild].
 ///
 /// Later the extra files will be deleted in [_cleanupExtraFilesUnderTestDir].
-Future<bool> compileUnitTest(FilePath input, {required bool forCanvasKit}) async {
+Future<bool> compileUnitTest(FilePath input, {required bool forCanvasKit, required bool forSkwasm}) async {
   final String targetFileName = pathlib.join(
     environment.webUiBuildDir.path,
     '${input.relativeToWebUi}.browser_test.dart.js',
@@ -277,6 +285,7 @@
     // renderer explicitly.
     '-DFLUTTER_WEB_AUTO_DETECT=false',
     '-DFLUTTER_WEB_USE_SKIA=$forCanvasKit',
+    '-DFLUTTER_WEB_USE_SKWASM=$forSkwasm',
 
     '-O2',
     '-o',
diff --git a/lib/web_ui/lib/src/engine/configuration.dart b/lib/web_ui/lib/src/engine/configuration.dart
index 948404a..395e3f8 100644
--- a/lib/web_ui/lib/src/engine/configuration.dart
+++ b/lib/web_ui/lib/src/engine/configuration.dart
@@ -68,6 +68,9 @@
   static const bool flutterWebAutoDetect =
       bool.fromEnvironment('FLUTTER_WEB_AUTO_DETECT', defaultValue: true);
 
+  static const bool flutterWebUseSkwasm =
+      bool.fromEnvironment('FLUTTER_WEB_USE_SKWASM');
+
   /// Enable the Skia-based rendering backend.
   ///
   /// Using flutter tools option "--web-render=canvaskit" would set the value to
diff --git a/lib/web_ui/lib/src/engine/renderer.dart b/lib/web_ui/lib/src/engine/renderer.dart
index e66d77f..41523b3 100644
--- a/lib/web_ui/lib/src/engine/renderer.dart
+++ b/lib/web_ui/lib/src/engine/renderer.dart
@@ -15,6 +15,7 @@
 import 'fonts.dart';
 import 'html/renderer.dart';
 import 'html_image_codec.dart';
+import 'skwasm/skwasm_stub/renderer.dart' if (dart.library.ffi) 'skwasm/skwasm_impl/renderer.dart';
 
 final Renderer _renderer = Renderer._internal();
 Renderer get renderer => _renderer;
@@ -26,6 +27,9 @@
 /// of functionality needed by the rest of the generic web engine code.
 abstract class Renderer {
   factory Renderer._internal() {
+    if (FlutterConfiguration.flutterWebUseSkwasm) {
+      return SkwasmRenderer();
+    }
     bool useCanvasKit;
     if (FlutterConfiguration.flutterWebAutoDetect) {
       if (requestedRendererType != null) {
@@ -154,27 +158,27 @@
   ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2);
 
   ui.TextStyle createTextStyle({
-    ui.Color? color,
-    ui.TextDecoration? decoration,
-    ui.Color? decorationColor,
-    ui.TextDecorationStyle? decorationStyle,
-    double? decorationThickness,
-    ui.FontWeight? fontWeight,
-    ui.FontStyle? fontStyle,
-    ui.TextBaseline? textBaseline,
-    String? fontFamily,
-    List<String>? fontFamilyFallback,
-    double? fontSize,
-    double? letterSpacing,
-    double? wordSpacing,
-    double? height,
-    ui.TextLeadingDistribution? leadingDistribution,
-    ui.Locale? locale,
-    ui.Paint? background,
-    ui.Paint? foreground,
-    List<ui.Shadow>? shadows,
-    List<ui.FontFeature>? fontFeatures,
-    List<ui.FontVariation>? fontVariations,
+    required ui.Color? color,
+    required ui.TextDecoration? decoration,
+    required ui.Color? decorationColor,
+    required ui.TextDecorationStyle? decorationStyle,
+    required double? decorationThickness,
+    required ui.FontWeight? fontWeight,
+    required ui.FontStyle? fontStyle,
+    required ui.TextBaseline? textBaseline,
+    required String? fontFamily,
+    required List<String>? fontFamilyFallback,
+    required double? fontSize,
+    required double? letterSpacing,
+    required double? wordSpacing,
+    required double? height,
+    required ui.TextLeadingDistribution? leadingDistribution,
+    required ui.Locale? locale,
+    required ui.Paint? background,
+    required ui.Paint? foreground,
+    required List<ui.Shadow>? shadows,
+    required List<ui.FontFeature>? fontFeatures,
+    required List<ui.FontVariation>? fontVariations,
   });
 
   ui.ParagraphStyle createParagraphStyle({
diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
new file mode 100644
index 0000000..554302e
--- /dev/null
+++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library skwasm_impl;
+
+export 'skwasm_impl/renderer.dart';
diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart
new file mode 100644
index 0000000..ff240be
--- /dev/null
+++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart
@@ -0,0 +1,168 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+import 'package:ui/ui.dart' as ui;
+
+import '../../embedder.dart';
+import '../../fonts.dart';
+import '../../html_image_codec.dart';
+import '../../renderer.dart';
+
+// TODO(jacksongardner): Actually implement skwasm renderer.
+class SkwasmRenderer implements Renderer {
+  @override
+  ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Path copyPath(ui.Path src) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageFilter createBlurImageFilter({double sigmaX = 0.0, double sigmaY = 0.0, ui.TileMode tileMode = ui.TileMode.clamp}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, ui.Offset center, double radius, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix]) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageShader createImageShader(ui.Image image, ui.TileMode tmx, ui.TileMode tmy, Float64List matrix4, ui.FilterQuality? filterQuality) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Gradient createLinearGradient(ui.Offset from, ui.Offset to, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ImageFilter createMatrixImageFilter(Float64List matrix4, {ui.FilterQuality filterQuality = ui.FilterQuality.low}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Paint createPaint() {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Path createPath() {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.PictureRecorder createPictureRecorder() {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Gradient createRadialGradient(ui.Offset center, double radius, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.SceneBuilder createSceneBuilder() {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.StrutStyle createStrutStyle({String? fontFamily, List<String>? fontFamilyFallback, double? fontSize, double? height, ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Gradient createSweepGradient(ui.Offset center, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, Float32List? matrix4]) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.TextStyle createTextStyle({ui.Color? color, ui.TextDecoration? decoration, ui.Color? decorationColor, ui.TextDecorationStyle? decorationStyle, double? decorationThickness, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.TextBaseline? textBaseline, String? fontFamily, List<String>? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, ui.TextLeadingDistribution? leadingDistribution, ui.Locale? locale, ui.Paint? background, ui.Paint? foreground, List<ui.Shadow>? shadows, List<ui.FontFeature>? fontFeatures, List<ui.FontVariation>? fontVariations}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Vertices createVertices(ui.VertexMode mode, List<ui.Offset> positions, {List<ui.Offset>? textureCoordinates, List<ui.Color>? colors, List<int>? indices}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  ui.Vertices createVerticesRaw(ui.VertexMode mode, Float32List positions, {Float32List? textureCoordinates, Int32List? colors, Uint16List? indices}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  void decodeImageFromPixels(Uint8List pixels, int width, int height, ui.PixelFormat format, ui.ImageDecoderCallback callback, {int? rowBytes, int? targetWidth, int? targetHeight, bool allowUpscaling = true}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  FontCollection get fontCollection => throw UnimplementedError('Not yet implemented');
+
+  @override
+  FutureOr<void> initialize() {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  Future<ui.Codec> instantiateImageCodec(Uint8List list, {int? targetWidth, int? targetHeight, bool allowUpscaling = true}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  Future<ui.Codec> instantiateImageCodecFromUrl(Uri uri, {WebOnlyImageCodecChunkCallback? chunkCallback}) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  void renderScene(ui.Scene scene) {
+    throw UnimplementedError('Not yet implemented');
+  }
+
+  @override
+  String get rendererTag => throw UnimplementedError('Not yet implemented');
+
+  @override
+  void reset(FlutterViewEmbedder embedder) {
+    throw UnimplementedError('Not yet implemented');
+  }
+}
diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart
new file mode 100644
index 0000000..32f7960
--- /dev/null
+++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library skwasm_stub;
+
+export 'skwasm_stub/renderer.dart';
diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart
new file mode 100644
index 0000000..6858335
--- /dev/null
+++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart
@@ -0,0 +1,167 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+import 'package:ui/ui.dart' as ui;
+
+import '../../embedder.dart';
+import '../../fonts.dart';
+import '../../html_image_codec.dart';
+import '../../renderer.dart';
+
+class SkwasmRenderer implements Renderer {
+  @override
+  ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Path copyPath(ui.Path src) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageFilter createBlurImageFilter({double sigmaX = 0.0, double sigmaY = 0.0, ui.TileMode tileMode = ui.TileMode.clamp}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, ui.Offset center, double radius, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix]) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageShader createImageShader(ui.Image image, ui.TileMode tmx, ui.TileMode tmy, Float64List matrix4, ui.FilterQuality? filterQuality) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Gradient createLinearGradient(ui.Offset from, ui.Offset to, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ImageFilter createMatrixImageFilter(Float64List matrix4, {ui.FilterQuality filterQuality = ui.FilterQuality.low}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Paint createPaint() {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Path createPath() {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.PictureRecorder createPictureRecorder() {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Gradient createRadialGradient(ui.Offset center, double radius, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.SceneBuilder createSceneBuilder() {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.StrutStyle createStrutStyle({String? fontFamily, List<String>? fontFamilyFallback, double? fontSize, double? height, ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Gradient createSweepGradient(ui.Offset center, List<ui.Color> colors, [List<double>? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, Float32List? matrix4]) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.TextStyle createTextStyle({ui.Color? color, ui.TextDecoration? decoration, ui.Color? decorationColor, ui.TextDecorationStyle? decorationStyle, double? decorationThickness, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.TextBaseline? textBaseline, String? fontFamily, List<String>? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, ui.TextLeadingDistribution? leadingDistribution, ui.Locale? locale, ui.Paint? background, ui.Paint? foreground, List<ui.Shadow>? shadows, List<ui.FontFeature>? fontFeatures, List<ui.FontVariation>? fontVariations}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Vertices createVertices(ui.VertexMode mode, List<ui.Offset> positions, {List<ui.Offset>? textureCoordinates, List<ui.Color>? colors, List<int>? indices}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  ui.Vertices createVerticesRaw(ui.VertexMode mode, Float32List positions, {Float32List? textureCoordinates, Int32List? colors, Uint16List? indices}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  void decodeImageFromPixels(Uint8List pixels, int width, int height, ui.PixelFormat format, ui.ImageDecoderCallback callback, {int? rowBytes, int? targetWidth, int? targetHeight, bool allowUpscaling = true}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  FontCollection get fontCollection => throw UnimplementedError('Skwasm not implemented on this platform.');
+
+  @override
+  FutureOr<void> initialize() {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  Future<ui.Codec> instantiateImageCodec(Uint8List list, {int? targetWidth, int? targetHeight, bool allowUpscaling = true}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  Future<ui.Codec> instantiateImageCodecFromUrl(Uri uri, {WebOnlyImageCodecChunkCallback? chunkCallback}) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  void renderScene(ui.Scene scene) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+
+  @override
+  String get rendererTag => throw UnimplementedError('Skwasm not implemented on this platform.');
+
+  @override
+  void reset(FlutterViewEmbedder embedder) {
+    throw UnimplementedError('Skwasm not implemented on this platform.');
+  }
+}
diff --git a/lib/web_ui/test/skwasm/smoke_test.dart b/lib/web_ui/test/skwasm/smoke_test.dart
new file mode 100644
index 0000000..74c5a30
--- /dev/null
+++ b/lib/web_ui/test/skwasm/smoke_test.dart
@@ -0,0 +1,27 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@TestOn('chrome || safari || firefox')
+
+import 'dart:async';
+
+import 'package:test/bootstrap/browser.dart';
+import 'package:test/test.dart';
+import 'package:ui/src/engine/renderer.dart';
+import 'package:ui/src/engine/skwasm/skwasm_stub/renderer.dart';
+
+void main() {
+  internalBootstrapBrowserTest(() => testMain);
+}
+
+Future<void> testMain() async {
+  group('Skwasm stub tests', () {
+    test('Skwasm stub renderer throws', () {
+      expect(renderer, isA<SkwasmRenderer>());
+      expect(() {
+        renderer.initialize();
+      }, throwsUnimplementedError);
+    });
+  });
+}
diff --git a/web_sdk/BUILD.gn b/web_sdk/BUILD.gn
index ba7d190..1c653d5 100644
--- a/web_sdk/BUILD.gn
+++ b/web_sdk/BUILD.gn
@@ -19,17 +19,6 @@
                              ],
                              "list lines")
 
-web_engine_sources =
-    exec_script("//third_party/dart/tools/list_dart_files.py",
-                [
-                  "absolute",
-                  rebase_path("//flutter/lib/web_ui/lib/src/engine"),
-                ],
-                "list lines")
-
-web_engine_sources +=
-    [ rebase_path("//flutter/lib/web_ui/lib/src/engine.dart") ]
-
 group("web_sdk") {
   deps = [
     ":flutter_dartdevc_canvaskit_html_kernel_sdk",
@@ -43,60 +32,100 @@
   ]
 }
 
-prebuilt_dart_action("web_ui_sources") {
-  inputs = web_ui_sources + [ "sdk_rewriter.dart" ]
+template("sdk_rewriter") {
+  ui = defined(invoker.ui) && invoker.ui
+  if (!ui) {
+    assert(defined(invoker.library_name), "Must pass 'library_name'")
+    assert(defined(invoker.api_file), "Must pass 'api_file'")
+  }
+  assert(defined(invoker.input_dir), "Must pass 'input_dir'")
+  assert(defined(invoker.output_dir), "Must pass 'output_dir'")
 
-  packages = dart_sdk_package_config
-  script = "sdk_rewriter.dart"
-  output_dir = rebase_path("$root_out_dir/flutter_web_sdk/lib/ui/")
-  input_dir = rebase_path("//flutter/lib/web_ui/lib/")
-  pool = "//flutter/build/dart:dart_pool"
+  source_dart_files = exec_script("//third_party/dart/tools/list_dart_files.py",
+                                  [
+                                    "absolute",
+                                    rebase_path(invoker.input_dir),
+                                  ],
+                                  "list lines")
 
-  outputs = [
-    "$target_gen_dir/$target_name.stamp",
-    "$root_out_dir/flutter_web_sdk/lib/ui/",
-  ]
+  if (defined(invoker.exclude_patterns)) {
+    filtered_files = filter_exclude(source_dart_files, invoker.exclude_patterns)
+    source_dart_files = []
+    source_dart_files = filtered_files
+  }
 
-  args = [
-    "--output-dir=$output_dir",
-    "--input-dir=$input_dir",
-    "--ui",
-    "--stamp",
-    rebase_path(outputs[0], root_build_dir),
-  ]
+  prebuilt_dart_action(target_name) {
+    packages = dart_sdk_package_config
+    pool = "//flutter/build/dart:dart_pool"
 
-  foreach(source_file, web_ui_sources) {
-    path = rebase_path(source_file)
-    args += [ "--input=$path" ]
+    script = "sdk_rewriter.dart"
+    inputs = source_dart_files + [ "sdk_rewriter.dart" ]
+
+    stamp_location = "$target_gen_dir/$target_name.stamp"
+    outputs = [
+      stamp_location,
+      invoker.output_dir,
+    ]
+
+    input_dir = rebase_path(invoker.input_dir)
+    output_dir = rebase_path(invoker.output_dir)
+
+    args = [
+      "--output-dir=$output_dir",
+      "--input-dir=$input_dir",
+      "--stamp",
+      rebase_path(stamp_location, root_build_dir),
+    ]
+    if (ui) {
+      args += [ "--ui" ]
+    } else {
+      library_name = invoker.library_name
+      api_file = rebase_path(invoker.api_file)
+      args += [
+        "--library-name=$library_name",
+        "--api-file=$api_file",
+      ]
+    }
+
+    foreach(source_file, source_dart_files) {
+      path = rebase_path(source_file)
+      args += [ "--source-file=$path" ]
+    }
   }
 }
 
-prebuilt_dart_action("web_engine_sources") {
-  inputs = web_engine_sources + [ "sdk_rewriter.dart" ]
+sdk_rewriter("web_ui_library_sources") {
+  ui = true
+  input_dir = "//flutter/lib/web_ui/lib/"
+  output_dir = "$root_out_dir/flutter_web_sdk/lib/ui/"
 
-  packages = dart_sdk_package_config
-  script = "sdk_rewriter.dart"
-  output_dir = rebase_path("$root_out_dir/flutter_web_sdk/lib/_engine/")
-  input_dir = rebase_path("//flutter/lib/web_ui/lib/src/")
-  pool = "//flutter/build/dart:dart_pool"
+  # exclude everything in the engine directory, it will be a separate internal library
+  exclude_patterns = [ rebase_path("//flutter/lib/web_ui/lib/src/*") ]
+}
 
-  outputs = [
-    "$target_gen_dir/$target_name.stamp",
-    "$root_out_dir/flutter_web_sdk/lib/_engine/",
-  ]
+sdk_rewriter("web_engine_library") {
+  library_name = "engine"
+  api_file = "//flutter/lib/web_ui/lib/src/engine.dart"
+  input_dir = "//flutter/lib/web_ui/lib/src/engine/"
+  output_dir = "$root_out_dir/flutter_web_sdk/lib/_engine/"
 
-  args = [
-    "--output-dir=$output_dir",
-    "--input-dir=$input_dir",
-    "--engine",
-    "--stamp",
-    rebase_path(outputs[0], root_build_dir),
-  ]
+  # exclude skwasm, it will be a separate internal library
+  exclude_patterns =
+      [ rebase_path("//flutter/lib/web_ui/lib/src/engine/skwasm/*") ]
+}
 
-  foreach(source_file, web_engine_sources) {
-    path = rebase_path(source_file)
-    args += [ "--input=$path" ]
-  }
+sdk_rewriter("skwasm_stub_library") {
+  library_name = "skwasm_stub"
+  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart"
+  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/"
+  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_stub/"
+}
+
+sdk_rewriter("skwasm_impl_library") {
+  library_name = "skwasm_impl"
+  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart"
+  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/"
+  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_impl/"
 }
 
 copy("web_ui_library") {
@@ -119,9 +148,11 @@
     action(target_name) {
       not_needed(invoker, [ "packages" ])
       deps = [
-        ":web_engine_sources",
+        ":skwasm_impl_library",
+        ":skwasm_stub_library",
+        ":web_engine_library",
         ":web_ui_library",
-        ":web_ui_sources",
+        ":web_ui_library_sources",
         "//flutter:dart_sdk",
       ]
       script = "//build/gn_run_binary.py"
@@ -148,9 +179,11 @@
       forward_variables_from(invoker, "*")
 
       deps = [
-        ":web_engine_sources",
+        ":skwasm_impl_library",
+        ":skwasm_stub_library",
+        ":web_engine_library",
         ":web_ui_library",
-        ":web_ui_sources",
+        ":web_ui_library_sources",
         "//flutter:dart_sdk",
         "//third_party/dart/pkg:pkg_files_stamp",
         "//third_party/dart/utils/dartdevc:dartdevc_files_stamp",
@@ -177,9 +210,11 @@
   if (flutter_prebuilt_dart_sdk) {
     action(target_name) {
       deps = [
-        ":web_engine_sources",
+        ":skwasm_impl_library",
+        ":skwasm_stub_library",
+        ":web_engine_library",
         ":web_ui_library",
-        ":web_ui_sources",
+        ":web_ui_library_sources",
         "//flutter:dart_sdk",
       ]
       script = "//build/gn_run_binary.py"
@@ -207,9 +242,11 @@
       forward_variables_from(invoker, "*")
 
       deps = [
-        ":web_engine_sources",
+        ":skwasm_impl_library",
+        ":skwasm_stub_library",
+        ":web_engine_library",
         ":web_ui_library",
-        ":web_ui_sources",
+        ":web_ui_library_sources",
         "//flutter:dart_sdk",
         "//third_party/dart/pkg:pkg_files_stamp",
         "//third_party/dart/utils/dartdevc:dartdevc_files_stamp",
@@ -225,7 +262,7 @@
 
 # Compile the unsound DDC SDK's summary.
 _kernel_worker("flutter_dartdevc_kernel_sdk_outline") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   outputs = [ sdk_dill ]
 
@@ -257,7 +294,7 @@
 
 # Compiles the unsound html only renderer.
 _dartdevc("flutter_dartdevc_kernel_sdk") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -303,7 +340,7 @@
 
 # Compiles the unsound canvaskit only renderer.
 _dartdevc("flutter_dartdevc_canvaskit_kernel_sdk") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -351,7 +388,7 @@
 
 # Compiles the unsound autodetect renderer.
 _dartdevc("flutter_dartdevc_canvaskit_html_kernel_sdk") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -399,7 +436,7 @@
 
 # Compiles the sound html only renderer.
 _dartdevc("flutter_dartdevc_kernel_sdk_sound") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -446,7 +483,7 @@
 
 # Compiles the sound canvaskit only renderer.
 _dartdevc("flutter_dartdevc_canvaskit_kernel_sdk_sound") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -494,7 +531,7 @@
 
 # Compiles the sound autodetect renderer.
 _dartdevc("flutter_dartdevc_canvaskit_html_kernel_sdk_sound") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   packages = dart_sdk_package_config
 
@@ -542,7 +579,7 @@
 
 # Compile the sound DDC SDK's summary.
 _kernel_worker("flutter_dartdevc_kernel_sdk_outline_sound") {
-  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources + web_engine_sources
+  inputs = [ "sdk_rewriter.dart" ] + web_ui_sources
 
   outputs = [ sdk_dill_sound ]
 
@@ -585,9 +622,11 @@
       ":flutter_dartdevc_kernel_sdk_outline",
       ":flutter_dartdevc_kernel_sdk_outline_sound",
       ":flutter_dartdevc_kernel_sdk_sound",
-      ":web_engine_sources",
+      ":skwasm_impl_library",
+      ":skwasm_stub_library",
+      ":web_engine_library",
       ":web_ui_library",
-      ":web_ui_sources",
+      ":web_ui_library_sources",
     ]
     sources = get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk")
     sources +=
@@ -600,8 +639,10 @@
     sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_sound")
     sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_sound")
     sources += get_target_outputs(":web_ui_library")
-    sources += get_target_outputs(":web_ui_sources")
-    sources += get_target_outputs(":web_engine_sources")
+    sources += get_target_outputs(":web_ui_library_sources")
+    sources += get_target_outputs(":skwasm_stub_library")
+    sources += get_target_outputs(":skwasm_impl_library")
+    sources += get_target_outputs(":web_engine_library")
     tmp_files = []
     foreach(source, sources) {
       tmp_files += [
diff --git a/web_sdk/libraries.json b/web_sdk/libraries.json
index 01a9fec..7f9c878 100644
--- a/web_sdk/libraries.json
+++ b/web_sdk/libraries.json
@@ -14,6 +14,9 @@
       },
       "_engine": {
         "uri": "lib/_engine/engine.dart"
+      },
+      "_skwasm_stub": {
+        "uri": "lib/_skwasm_stub/skwasm_stub.dart"
       }
     }
   },
@@ -30,6 +33,9 @@
       },
       "_engine": {
         "uri": "lib/_engine/engine.dart"
+      },
+      "_skwasm_stub": {
+        "uri": "lib/_skwasm_stub/skwasm_stub.dart"
       }
     }
   },
@@ -46,6 +52,9 @@
       },
       "_engine": {
         "uri": "lib/_engine/engine.dart"
+      },
+      "_skwasm_impl": {
+        "uri": "lib/_skwasm_impl/skwasm_impl.dart"
       }
     }
   }
diff --git a/web_sdk/libraries.yaml b/web_sdk/libraries.yaml
index 46bb23f..5a95df9 100644
--- a/web_sdk/libraries.yaml
+++ b/web_sdk/libraries.yaml
@@ -7,7 +7,7 @@
 # Note: if you edit this file, you must also edit libraries.json in this
 # directory:
 #
-#.    python third_party/dart/tools/yaml2json.py flutter/web_sdk/libraries.yaml flutter/web_sdk/libraries.json
+#.    dart third_party/dart/tools/yaml2json.dart flutter/web_sdk/libraries.yaml flutter/web_sdk/libraries.json
 #
 # We currently have several different files that needs to be updated when
 # changing libraries, sources, and patch files.  See
@@ -24,6 +24,9 @@
     _engine:
       uri: "lib/_engine/engine.dart"
 
+    _skwasm_stub:
+      uri: "lib/_skwasm_stub/skwasm_stub.dart"
+
 dart2js:
   include:
     - {path: "../dart-sdk/lib/libraries.json", target: dart2js}
@@ -35,6 +38,9 @@
     _engine:
       uri: "lib/_engine/engine.dart"
 
+    _skwasm_stub:
+      uri: "lib/_skwasm_stub/skwasm_stub.dart"
+
 wasm:
   include:
     - {path: "../dart-sdk/lib/libraries.json", target: wasm}
@@ -45,3 +51,6 @@
 
     _engine:
       uri: "lib/_engine/engine.dart"
+
+    _skwasm_impl:
+      uri: "lib/_skwasm_impl/skwasm_impl.dart"
diff --git a/web_sdk/sdk_rewriter.dart b/web_sdk/sdk_rewriter.dart
index bacc768..cb383b6 100644
--- a/web_sdk/sdk_rewriter.dart
+++ b/web_sdk/sdk_rewriter.dart
@@ -11,8 +11,9 @@
   ..addOption('output-dir')
   ..addOption('input-dir')
   ..addFlag('ui')
-  ..addFlag('engine')
-  ..addMultiOption('input')
+  ..addOption('library-name')
+  ..addOption('api-file')
+  ..addMultiOption('source-file')
   ..addOption('stamp');
 
 final List<Replacer> uiPatterns = <Replacer>[
@@ -33,10 +34,11 @@
   ),
 ];
 
-final List<Replacer> engineLibraryPatterns = <Replacer>[
-  AllReplacer(RegExp(r'library\s+engine;'), '''
+List<Replacer> generateApiFilePatterns(String libraryName, String extraImports) {
+  return <Replacer>[
+    AllReplacer(RegExp('library\\s+$libraryName;'), '''
 @JS()
-library dart._engine;
+library dart._$libraryName;
 
 import 'dart:async';
 import 'dart:collection';
@@ -47,27 +49,33 @@
 import 'dart:math' as math;
 import 'dart:typed_data';
 import 'dart:ui' as ui;
-'''),
-  // Replace exports of engine files with "part" directives.
-  MappedReplacer(RegExp(r'''
-export\s*'engine/(.*)';
+$extraImports
+'''
+    ),
+    // Replace exports of engine files with "part" directives.
+    MappedReplacer(RegExp('''
+export\\s*'$libraryName/(.*)';
 '''), (Match match) => '''
-part 'engine/${match.group(1)}';
-'''),
-];
+part '$libraryName/${match.group(1)}';
+'''
+    ),
+  ];
+}
 
-final List<Replacer> enginePartsPatterns = <Replacer>[
-  AllReplacer(RegExp(r'part\s+of\s+engine;'), 'part of dart._engine;'),
-  // Remove library-level JS annotations.
-  AllReplacer(RegExp(r'\n@JS(.*)\nlibrary .+;'), ''),
-  // Remove library directives.
-  AllReplacer(RegExp(r'\nlibrary .+;'), ''),
-  // Remove imports/exports from all engine parts.
-  AllReplacer(RegExp(r'\nimport\s*.*'), ''),
-  AllReplacer(RegExp(r'\nexport\s*.*'), ''),
-];
+List<Replacer> generatePartsPatterns(String libraryName) {
+  return <Replacer>[
+    AllReplacer(RegExp('part\\s+of\\s+$libraryName;'), 'part of dart._$libraryName;'),
+    // Remove library-level JS annotations.
+    AllReplacer(RegExp(r'\n@JS(.*)\nlibrary .+;'), ''),
+    // Remove library directives.
+    AllReplacer(RegExp(r'\nlibrary .+;'), ''),
+    // Remove imports/exports from all part files.
+    AllReplacer(RegExp(r'\nimport\s*.*'), ''),
+    AllReplacer(RegExp(r'\nexport\s*.*'), ''),
+  ];
+}
 
-final List<Replacer> sharedPatterns = <Replacer>[
+final List<Replacer> stripMetaPatterns = <Replacer>[
   AllReplacer(RegExp(r"import\s*'package:meta/meta.dart';"), ''),
   AllReplacer('@required', ''),
   AllReplacer('@protected', ''),
@@ -82,38 +90,75 @@
   final ArgResults results = argParser.parse(arguments);
   final Directory directory = Directory(results['output-dir'] as String);
   final String inputDirectoryPath = results['input-dir'] as String;
-  for (final String inputFilePath in results['input'] as Iterable<String>) {
-    final File inputFile = File(inputFilePath);
-    final File outputFile = File(path.join(
-        directory.path, inputFile.path.substring(inputDirectoryPath.length)))
-      ..createSync(recursive: true);
-    final String source = inputFile.readAsStringSync();
-    final String rewrittenContent = rewriteFile(
-      source,
-      filePath: inputFilePath,
-      isUi: results['ui'] as bool,
-      isEngine: results['engine'] as bool,
-    );
-    outputFile.writeAsStringSync(rewrittenContent);
-    if (results['stamp'] != null) {
-      File(results['stamp'] as String).writeAsStringSync('stamp');
+
+  String Function(String source)? preprocessor;
+  List<Replacer> replacementPatterns;
+  String? libraryName;
+  if (results['ui'] as bool) {
+    replacementPatterns = uiPatterns;
+  } else {
+    libraryName = results['library-name'] as String;
+    if (libraryName == null) {
+      throw Exception('library-name must be specified if not rewriting ui');
     }
+    preprocessor = (String source) => preprocessPartFile(source, libraryName!);
+    replacementPatterns = generatePartsPatterns(libraryName);
+  }
+  for (final String inputFilePath in results['source-file'] as Iterable<String>) {
+    String pathSuffix = inputFilePath.substring(inputDirectoryPath.length);
+    if (libraryName != null) {
+      pathSuffix = path.join(libraryName, pathSuffix);
+    }
+    final String outputFilePath = path.join(directory.path, pathSuffix);
+    processFile(inputFilePath, outputFilePath, preprocessor, replacementPatterns);
+  }
+
+  if (results['api-file'] != null) {
+    if (libraryName == null) {
+      throw Exception('library-name must be specified if api-file is specified');
+    }
+
+    final String inputFilePath = results['api-file'] as String;
+    final String outputFilePath = path.join(
+        directory.path, path.basename(inputFilePath));
+    String extraImports = '';
+    if (libraryName == 'engine') {
+      extraImports = "import 'dart:_skwasm_stub' if (dart.library.ffi) 'dart:_skwasm_impl';\n";
+    } else if (libraryName == 'skwasm_stub' || libraryName == 'skwasm_impl') {
+      extraImports = "import 'dart:_engine';\n";
+    }
+    replacementPatterns = generateApiFilePatterns(libraryName, extraImports);
+
+    processFile(
+      inputFilePath,
+      outputFilePath,
+      (String source) => validateApiFile(inputFilePath, source, libraryName!),
+      replacementPatterns
+    );
+  }
+
+
+  if (results['stamp'] != null) {
+    File(results['stamp'] as String).writeAsStringSync('stamp');
   }
 }
 
-String rewriteFile(String source, {required String filePath, required bool isUi, required bool isEngine}) {
-  final List<Replacer> replacementPatterns = <Replacer>[];
-  replacementPatterns.addAll(sharedPatterns);
-  if (isUi) {
-    replacementPatterns.addAll(uiPatterns);
-  } else if (isEngine) {
-    if (filePath.endsWith('lib/src/engine.dart')) {
-      _validateEngineSource(filePath, source);
-      replacementPatterns.addAll(engineLibraryPatterns);
-    } else {
-      source = _preprocessEnginePartFile(source);
-      replacementPatterns.addAll(enginePartsPatterns);
-    }
+void processFile(String inputFilePath, String outputFilePath, String Function(String source)? preprocessor, List<Replacer> replacementPatterns) {
+  final File inputFile = File(inputFilePath);
+  final File outputFile = File(outputFilePath)
+    ..createSync(recursive: true);
+  outputFile.writeAsStringSync(processSource(
+    inputFile.readAsStringSync(),
+    preprocessor,
+    replacementPatterns));
+}
+
+String processSource(String source, String Function(String source)? preprocessor, List<Replacer> replacementPatterns) {
+  if (preprocessor != null) {
+    source = preprocessor(source);
+  }
+  for (final Replacer replacer in stripMetaPatterns) {
+    source = replacer.perform(source);
   }
   for (final Replacer replacer in replacementPatterns) {
     source = replacer.perform(source);
@@ -121,17 +166,17 @@
   return source;
 }
 
-// Enforces a particular structure in engine.dart source code.
+// Enforces a particular structure in top level api files for sublibraries.
 //
-// Code in `engine.dart` must only be made of the library directive, exports,
+// Code in api files must only be made of the library directive, exports,
 // and code comments. Imports are disallowed. Instead, the required imports are
 // added by this script during the rewrite.
-void _validateEngineSource(String engineDartPath, String engineDartCode) {
-  const List<String> expectedLines = <String>[
-    'library engine;',
+String validateApiFile(String apiFilePath, String apiFileCode, String libraryName) {
+  final List<String> expectedLines = <String>[
+    'library $libraryName;',
   ];
 
-  final List<String> lines = engineDartCode.split('\n');
+  final List<String> lines = apiFileCode.split('\n');
   for (int i = 0; i < lines.length; i += 1) {
     final int lineNumber = i + 1;
     final String line = lines[i].trim();
@@ -157,20 +202,21 @@
     }
 
     throw Exception(
-      'on line $lineNumber: unexpected code in $engineDartPath. This file '
+      'on line $lineNumber: unexpected code in $apiFilePath. This file '
       'may only contain comments and exports. Found:\n'
       '$line'
     );
   }
+  return apiFileCode;
 }
 
-String _preprocessEnginePartFile(String source) {
-  if (source.startsWith('part of engine;') || source.contains('\npart of engine;')) {
+String preprocessPartFile(String source, String libraryName) {
+  if (source.startsWith('part of $libraryName;') || source.contains('\npart of $libraryName;')) {
     // The file hasn't been migrated yet.
     // Do nothing.
   } else {
     // Insert the part directive at the beginning of the file.
-    source = 'part of engine;\n$source';
+    source = 'part of $libraryName;\n$source';
   }
   return source;
 }
diff --git a/web_sdk/test/sdk_rewriter_test.dart b/web_sdk/test/sdk_rewriter_test.dart
index 4e17279..22a424a 100644
--- a/web_sdk/test/sdk_rewriter_test.dart
+++ b/web_sdk/test/sdk_rewriter_test.dart
@@ -35,6 +35,8 @@
 import 'dart:math' as math;
 import 'dart:typed_data';
 import 'dart:ui' as ui;
+import 'dart:extra';
+
 
 
 // Comment 2
@@ -44,11 +46,13 @@
 part 'engine/file3.dart';
 ''';
 
-    final String result = rewriteFile(
+    final String result = processSource(
       source,
-      filePath: '/path/to/lib/web_ui/lib/src/engine.dart',
-      isUi: false,
-      isEngine: true,
+      (String source) => validateApiFile(
+        '/path/to/lib/web_ui/lib/src/engine.dart',
+        source,
+        'engine'),
+      generateApiFilePatterns('engine', "import 'dart:extra';\n"),
     );
     expect(result, expected);
   });
@@ -64,11 +68,13 @@
 
     Object? caught;
     try {
-      rewriteFile(
+      processSource(
         source,
-        filePath: '/path/to/lib/web_ui/lib/src/engine.dart',
-        isUi: false,
-        isEngine: true,
+        (String source) => validateApiFile(
+          '/path/to/lib/web_ui/lib/src/engine.dart',
+          source,
+          'engine'),
+        generateApiFilePatterns('engine', ''),
       );
     } catch(error) {
       caught = error;
@@ -112,11 +118,10 @@
 }
 ''';
 
-    final String result = rewriteFile(
+    final String result = processSource(
       source,
-      filePath: '/path/to/lib/web_ui/lib/src/engine/my_file.dart',
-      isUi: false,
-      isEngine: true,
+      (String source) => preprocessPartFile(source, 'engine'),
+      generatePartsPatterns('engine'),
     );
     expect(result, expected);
   });
@@ -138,11 +143,10 @@
 }
 ''';
 
-    final String result = rewriteFile(
+    final String result = processSource(
       source,
-      filePath: '/path/to/lib/web_ui/lib/src/engine/my_file.dart',
-      isUi: false,
-      isEngine: true,
+      (String source) => preprocessPartFile(source, 'engine'),
+      generatePartsPatterns('engine'),
     );
     expect(result, expected);
   });