diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5e0e87b..9217469 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,7 +49,7 @@ jobs: cd example fvm config --cache-path ../.fvm/cache fvm flutter pub get - fvm flutter build web --release --web-renderer auto + fvm flutter build web --wasm --release - name: deploy to netlify uses: nwtgck/actions-netlify@v3.0 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cef855..f958fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.9 + +* Add support for Wasm. + ## 0.0.8 * Updated rxdart to the latest version. diff --git a/README.md b/README.md index cc82dfa..c49f81f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Flutter package for enabling SEO (meta, body tag) support on Web. The package li To use this plugin, add `seo` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). ```yaml dependencies: - seo: ^0.0.8 + seo: ^0.0.9 ``` Use `usePathUrlStrategy()` to ensure that Google recognizes each URL as a distinct page. Failure to do so may result in Google perceiving all URLs as the same page. For additional details, refer to [this video](https://www.youtube.com/watch?v=vow-m6R-YHo). diff --git a/example/.metadata b/example/.metadata index 784ce12..8dda3be 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - platform: web - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 # User provided section diff --git a/example/web/_headers b/example/web/_headers new file mode 100644 index 0000000..2a351fb --- /dev/null +++ b/example/web/_headers @@ -0,0 +1,3 @@ +/* + Cross-Origin-Embedder-Policy: credentialless + Cross-Origin-Opener-Policy: same-origin diff --git a/lib/html/node_list_extension.dart b/lib/html/node_list_extension.dart new file mode 100644 index 0000000..077fca5 --- /dev/null +++ b/lib/html/node_list_extension.dart @@ -0,0 +1,7 @@ +import 'package:web/web.dart'; + +extension NodeListExtension on NodeList { + List toList() { + return List.generate(length, (index) => item(index)); + } +} diff --git a/lib/html/seo_controller.dart b/lib/html/seo_controller.dart index 0fdba1f..041f7d2 100644 --- a/lib/html/seo_controller.dart +++ b/lib/html/seo_controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; -// ignore: avoid_web_libraries_in_flutter -import 'dart:html'; +import 'dart:js_interop'; +import 'package:seo/html/node_list_extension.dart'; +import 'package:web/web.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -43,38 +44,6 @@ class SeoController extends StatefulWidget { } class _SeoControllerState extends State { - final _headValidator = NodeValidatorBuilder() - ..allowHtml5(uriPolicy: _AllowAllUriPolicy()) - ..allowCustomElement( - 'meta', - attributes: ['name', 'http-equiv', 'content', 'flt-seo'], - ) - ..allowCustomElement( - 'link', - attributes: [ - 'title', - 'rel', - 'type', - 'hreflang', - 'href', - 'media', - 'flt-seo' - ], - ); - - final _bodyValidator = NodeValidatorBuilder() - ..allowHtml5(uriPolicy: _AllowAllUriPolicy()) - ..allowCustomElement('div', attributes: ['flt-seo']) - ..allowCustomElement('noscript') - ..allowCustomElement('h1', attributes: ['style']) - ..allowCustomElement('h2', attributes: ['style']) - ..allowCustomElement('h3', attributes: ['style']) - ..allowCustomElement('h4', attributes: ['style']) - ..allowCustomElement('h5', attributes: ['style']) - ..allowCustomElement('h6', attributes: ['style']) - ..allowCustomElement('p', attributes: ['style']) - ..allowCustomElement('a', attributes: ['rel']); - StreamSubscription? _subscription; int? _headHash; int? _bodyHash; @@ -129,13 +98,15 @@ class _SeoControllerState extends State { if (_headHash == hash) return; _headHash = hash; - head.children - .removeWhere((element) => element.attributes.containsKey('flt-seo')); + head + .querySelectorAll('[flt-seo]') + .toList() + .nonNulls + .forEach((node) => head.removeChild(node)); - head.insertAdjacentHtml( + head.insertAdjacentHTML( 'beforeEnd', - html.head, - validator: _headValidator, + html.head.toJS, ); } @@ -147,13 +118,15 @@ class _SeoControllerState extends State { if (_bodyHash == hash) return; _bodyHash = hash; - body.children - .removeWhere((element) => element.attributes.containsKey('flt-seo')); + body + .querySelectorAll('[flt-seo]') + .toList() + .nonNulls + .forEach((node) => body.removeChild(node)); - body.insertAdjacentHtml( + body.insertAdjacentHTML( 'afterBegin', - '
${html.body}
', - validator: _bodyValidator, + '
${html.body}
'.toJS, ); } @@ -173,11 +146,6 @@ class _SeoControllerState extends State { } } -class _AllowAllUriPolicy implements UriPolicy { - @override - bool allowsUri(String uri) => true; -} - class _InheritedSeoTreeWidget extends InheritedWidget { final SeoTree tree; diff --git a/pubspec.yaml b/pubspec.yaml index 8107d98..af3c8e7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: seo description: Flutter package for enabling SEO (meta, body tag) support on Web. -version: 0.0.8 +version: 0.0.9 homepage: https://github.com/krokyze/flutter_seo environment: @@ -11,6 +11,7 @@ dependencies: flutter: sdk: flutter rxdart: ">=0.27.5 <0.29.0" + web: ^1.1.0 dev_dependencies: flutter_lints: ^5.0.0 diff --git a/test/base.dart b/test/base.dart index 9e8a512..3ed6958 100644 --- a/test/base.dart +++ b/test/base.dart @@ -1,21 +1,24 @@ -// ignore: avoid_web_libraries_in_flutter -import 'dart:html'; +import 'package:seo/html/node_list_extension.dart'; +import 'package:web/web.dart'; import 'dart:ui'; -import 'package:collection/collection.dart'; - const largeScreenSize = Size(1024, 2048 * 128); const debounceTime = Duration(milliseconds: 250); -String? get headHtml => document.head?.children - .where((element) => element.attributes.containsKey('flt-seo')) - .map((element) => element.outerHtml) +String? get headHtml => document.head + ?.querySelectorAll('[flt-seo]') + .toList() + .whereType() + .map((node) => node.outerHTML.toString()) .join('\n'); -String? get bodyHtml => document.body?.children - .firstWhereOrNull((element) => element.attributes.containsKey('flt-seo')) - ?.innerHtml; +String? get bodyHtml => document.body + ?.querySelectorAll('[flt-seo]') + .toList() + .whereType() + .map((node) => node.innerHTML.toString()) + .firstOrNull; Duration measure(T Function() measure) { final stopwatch = Stopwatch()..start(); diff --git a/test/semantics_tree_test.dart b/test/semantics_tree_test.dart index 2f15dfc..50ddc04 100644 --- a/test/semantics_tree_test.dart +++ b/test/semantics_tree_test.dart @@ -53,7 +53,7 @@ void main() { expect( bodyHtml, - '
', + '
', ); }); @@ -86,7 +86,7 @@ void main() { expect( bodyHtml, - '

$anchor

$text

', + '

$anchor

$text

', ); }); diff --git a/test/widget_tree_test.dart b/test/widget_tree_test.dart index a70d21a..0173f16 100644 --- a/test/widget_tree_test.dart +++ b/test/widget_tree_test.dart @@ -54,7 +54,7 @@ void main() { expect( bodyHtml, - '
', + '
', ); }); @@ -101,7 +101,7 @@ void main() { expect( bodyHtml, - '

$anchor

$text

', + '

$anchor

$text

', ); });