dart-lang / web

Lightweight browser API bindings built around JS static interop.
https://pub.dev/packages/web
BSD 3-Clause "New" or "Revised" License
134 stars 23 forks source link

How to fix compilation errors when not building for web? #317

Open gdurandrexel opened 4 weeks ago

gdurandrexel commented 4 weeks ago

I have a flutter app that should support mobile and web, but compilation fails when building for mobile with errors such as "Dart library 'dart:js_interop' is not available on this platform.".

In my code I import dart:io and web/web and use kIsWeb to call IO methods or web methods.

When compiling for web, importing dart:io does not cause any problem.

I've read about creating a stub file and importing io or web implementations, but my code is sprinkled with kIsWeb checks with tiny bits of code. Is it really the only way to make this work? How come importing dart:io is fine but web/web is not? Could this package provide a stub so we can write something like this:

import 'package:web/web_stub.dart' as web
  if (dart.library.js_interop) 'package:web/web.dart';
srujzs commented 3 weeks ago

kIsWeb separates code logic so that you only call web APIs when compiling to web and vice versa. In this case, a stub could work, but checking for kIsWeb everywhere is quite clunky. Instead, I'd stub out the platform-specific logic and then call those stubs.

When compiling for web, importing dart:io does not cause any problem.

Can you elaborate? Are you using a stub there to avoid the unsupported error?

gdurandrexel commented 3 weeks ago

kIsWeb is convenient when only a tiny portion of your code is dependent of the platform. Without this I have to create 3 files (stub, io, web) and create a public function for just 3 lines of code in each platform, instead of writing if(kIsWeb) doWebStuff else doIOStuff. It's cumbersome to implement and maintain. So I wanted to know if there was another way.

If I import dart:io, to use Platform.isAndroid for instance, this compiles fine for web and I can call the io code conditionally at runtime. If I import web/web is does not compile for io, which prevents to use kIsWeb to branch platform-specific code as described above. Actually is seems to be more dart:js_interop that complains, but since web/web imports it, it is basically the same. So if I ever need to import web/web, I have to go the stub way, which is overkill in my opinion in several instances in my code.

I'm not very familiar with all this: I've just stumbled upon this kind of problem recently. So if you tell me there's no way to import web/web without going the stub way, that's fine.

tiprecpago commented 2 weeks ago

@srujzs I am having the exact same issue.

Even though my code logic is wrapped around if (kIsWeb), just by having the import

import 'package:web/web.dart';

it causes my project to stop compiling to native platform targets.

Just like @gdurandrexel suggested, the solution in this case was to create a stub file and add a conditional import.

import 'package:web_stub.dart'
  if (dart.library.js_interop) 'package:web/web.dart';

I would be really nice to have this stub file created inside the package so it doesn't affect native platform targets or maybe just add this info to the package's home page, just in case someone else has the same problem.

srujzs commented 2 weeks ago

Without this I have to create 3 files (stub, io, web) and create a public function for just 3 lines of code in each platform, instead of writing if(kIsWeb) doWebStuff else doIOStuff.

I may be wrong, but I think you only need two stubs - one for the VM and one for web and then just do a conditional like you were thinking of for package:web:

import 'vm_impl.dart' as impl
  if (dart.library.js_interop) 'web_impl.dart';

So if you tell me there's no way to import web/web without going the stub way, that's fine.

Ultimately if you want to avoid creating your own more general stubs and want to still use kIsWeb, a stub of all of web is the only remaining solution. The VM will indeed complain you're importing a platform-specific library, because that's what package:web is.

I would be really nice to have this stub file created inside the package so it doesn't affect native platform targets or maybe just add this info to the package's home page, just in case someone else has the same problem.

It's a good idea to put this in the README or an FAQ. I'm not completely opposed to a stub of web - it just feels like the wrong solution when you're comparing with the package ecosystem. I also recognize that that opinion adds some onus on users to implement their specific stubs I'm proposing above.

Similar issue: https://github.com/dart-lang/web/issues/55

gdurandrexel commented 2 weeks ago

Yes, you're right, I need only 2 files as you explained. I had come across several examples with another stub file and at that time I was still in my copy/paste phase regarding this new kind of problem.

Yes, a full stub of web/web may work, and I proposed this solution because the code is generated, so it might not be too much work or need too much maintenance (though it may, I have no idea). There would still be the problem of dart:js_interop that surfaces in the API. If we cannot apply the same technique for this package also, then there is no point in creating a stub for web/web I suppose.

srujzs commented 1 week ago

If we cannot apply the same technique for this package also, then there is no point in creating a stub for web/web I suppose.

Agreed. We may be able to get away with typedefs, but I'm not sure. We definitely do not want to either do a huge breaking change to web or stub dart:js_interop on the VM.

ykmnkmi commented 1 week ago

Is there a way to override dart:* package paths? Similar to passing a path to an overridden package_config.json.

kevmoo commented 1 week ago

Is there a way to override dart:* package paths? Similar to passing a path to an overridden package_config.json.

Nope. dart: libraries are hard-wired. What do you want to override? (Also, this is a bit off-topic)

ykmnkmi commented 1 week ago

@kevmoo I mean that we could use it to provide paths to the patched web and js_interop packages, if that’s possible.

kevmoo commented 1 week ago

you can override web, but js_interop is baked in.