diff --git a/AUTHORS b/AUTHORS index 9a8594865..5f540f102 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,3 +18,4 @@ Simon Binder (simolus3) - Contributor Mahdi Khodadadifard (xclud) - Contributor Jamiu Okanlawon (developerjamiu) - Contributor Harish Anbalagan (harishwarrior) - Contributor +Lewin Pauli (lewinpauli) - Contributor diff --git a/packages/jaspr/CHANGELOG.md b/packages/jaspr/CHANGELOG.md index 429527e2b..30dac4b7f 100644 --- a/packages/jaspr/CHANGELOG.md +++ b/packages/jaspr/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased patch + +- Fixed `jaspr serve` pinning a CPU core at 100% when the project has no `bin/` or `test/` + directory. The hot reload server used `debounceInterval: Duration.zero`, which is also used + as the polling delay for directory watchers that fall back to polling (e.g. for paths that + don't exist), turning it into a tight busy loop. (#816) + ## 0.23.1 - Fixed expression compilation when debugging a client-side application with an AOT installed CLI. diff --git a/packages/jaspr_cli/lib/src/commands/dev_command.dart b/packages/jaspr_cli/lib/src/commands/dev_command.dart index 8acc08d55..5d7d3aee7 100644 --- a/packages/jaspr_cli/lib/src/commands/dev_command.dart +++ b/packages/jaspr_cli/lib/src/commands/dev_command.dart @@ -187,7 +187,13 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper { if (useHotReload) { final import = entryPoint.replaceFirst('lib', 'package:${project.requirePubspecYaml['name']}'); - serverTarget.writeAsStringSync(serverEntrypoint(import)); + // hotreloader watches 'bin' and 'test' by default. If they don't exist in this + // project, exclude them so it doesn't fall back to a polling watcher for them. + final excludedPaths = [ + for (final dir in ['bin', 'test']) + if (!Directory(dir).existsSync()) dir, + ]; + serverTarget.writeAsStringSync(serverEntrypoint(import, excludedPaths)); args.add(serverTarget.path); } else { @@ -450,18 +456,22 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper { } } -String serverEntrypoint(String import) => +String serverEntrypoint(String import, List excludedPaths) => ''' import '$import' as m; import 'package:hotreloader/hotreloader.dart'; - + void main(List args) async { final mainFunc = m.main as dynamic; final mainCall = mainFunc is dynamic Function(List) ? () => mainFunc(args) : () => mainFunc(); try { await HotReloader.create( - debounceInterval: Duration.zero, + // A non-zero interval is required: hotreloader falls back to polling watchers + // for paths that don't exist (e.g. unused bin/ or test/ dirs), and a zero + // pollingDelay there causes a busy loop pinning a CPU core (see #816). + debounceInterval: Duration(milliseconds: 200), + excludedPaths: {${excludedPaths.map((e) => "'$e'").join(', ')}}, onAfterReload: (ctx) => mainCall(), ); print('[INFO] Server hot reload is enabled.');