Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding an option to redirect all not found to a specific asset #3361

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions build_runner/CHANGELOG.md
@@ -1,3 +1,8 @@
## 2.2.1-1-dev
jodinathan marked this conversation as resolved.
Show resolved Hide resolved

- Added a flag to redirect all not found to a specific
asset: not-found-defaults-to

## 2.2.1-dev

- Stop ignoring the no_leading_underscores_for_library_prefixes lint in
Expand Down
76 changes: 41 additions & 35 deletions build_runner/lib/src/entrypoint/options.dart
Expand Up @@ -26,6 +26,7 @@ const liveReloadOption = 'live-reload';
const logPerformanceOption = 'log-performance';
const logRequestsOption = 'log-requests';
const lowResourcesModeOption = 'low-resources-mode';
const notFoundDefaultsToOption = 'not-found-defaults-to';
const outputOption = 'output';
const releaseOption = 'release';
const trackPerformanceOption = 'track-performance';
Expand Down Expand Up @@ -276,41 +277,45 @@ class ServeOptions extends WatchOptions {
final bool logRequests;
final List<ServeTarget> serveTargets;

ServeOptions._({
required this.hostName,
required this.buildUpdates,
required this.logRequests,
required this.serveTargets,
required Set<BuildFilter> buildFilters,
required bool deleteFilesByDefault,
required bool enableLowResourcesMode,
required String? configKey,
required Set<BuildDirectory> buildDirs,
required bool outputSymlinksOnly,
required bool trackPerformance,
required bool skipBuildScriptCheck,
required bool verbose,
required Map<String, Map<String, dynamic>> builderConfigOverrides,
required bool isReleaseBuild,
required String? logPerformanceDir,
required bool usePollingWatcher,
required List<String> enableExperiments,
}) : super._(
buildFilters: buildFilters,
deleteFilesByDefault: deleteFilesByDefault,
enableLowResourcesMode: enableLowResourcesMode,
configKey: configKey,
buildDirs: buildDirs,
outputSymlinksOnly: outputSymlinksOnly,
trackPerformance: trackPerformance,
skipBuildScriptCheck: skipBuildScriptCheck,
verbose: verbose,
builderConfigOverrides: builderConfigOverrides,
isReleaseBuild: isReleaseBuild,
logPerformanceDir: logPerformanceDir,
usePollingWatcher: usePollingWatcher,
enableExperiments: enableExperiments,
);
// An [AssetId] path to redirect when the file is not found by
// the web server
final String? notFoundDefaultsTo;

ServeOptions._(
{required this.hostName,
required this.buildUpdates,
required this.logRequests,
required this.serveTargets,
required Set<BuildFilter> buildFilters,
required bool deleteFilesByDefault,
required bool enableLowResourcesMode,
required String? configKey,
required Set<BuildDirectory> buildDirs,
required bool outputSymlinksOnly,
required bool trackPerformance,
required bool skipBuildScriptCheck,
required bool verbose,
required Map<String, Map<String, dynamic>> builderConfigOverrides,
required bool isReleaseBuild,
required String? logPerformanceDir,
required bool usePollingWatcher,
required List<String> enableExperiments,
this.notFoundDefaultsTo})
jodinathan marked this conversation as resolved.
Show resolved Hide resolved
: super._(
buildFilters: buildFilters,
deleteFilesByDefault: deleteFilesByDefault,
enableLowResourcesMode: enableLowResourcesMode,
configKey: configKey,
buildDirs: buildDirs,
outputSymlinksOnly: outputSymlinksOnly,
trackPerformance: trackPerformance,
skipBuildScriptCheck: skipBuildScriptCheck,
verbose: verbose,
builderConfigOverrides: builderConfigOverrides,
isReleaseBuild: isReleaseBuild,
logPerformanceDir: logPerformanceDir,
usePollingWatcher: usePollingWatcher,
enableExperiments: enableExperiments);

factory ServeOptions.fromParsedArgs(ArgResults argResults,
Iterable<String> positionalArgs, String rootPackage, Command command) {
Expand Down Expand Up @@ -381,6 +386,7 @@ class ServeOptions extends WatchOptions {
logPerformanceDir: argResults[logPerformanceOption] as String?,
usePollingWatcher: argResults[usePollingWatcherOption] as bool,
enableExperiments: argResults[enableExperimentOption] as List<String>,
notFoundDefaultsTo: argResults[notFoundDefaultsToOption] as String?,
);
}
}
Expand Down
5 changes: 4 additions & 1 deletion build_runner/lib/src/entrypoint/serve.dart
Expand Up @@ -31,7 +31,9 @@ class ServeCommand extends WatchCommand {
..addFlag(liveReloadOption,
defaultsTo: false,
negatable: false,
help: 'Enables automatic page reloading on rebuilds. ');
help: 'Enables automatic page reloading on rebuilds. ')
..addOption(notFoundDefaultsToOption,
help: 'Redirect not found to a specific asset.');
}

@override
Expand Down Expand Up @@ -98,6 +100,7 @@ class ServeCommand extends WatchCommand {
logPerformanceDir: options.logPerformanceDir,
directoryWatcherFactory: options.directoryWatcherFactory,
buildFilters: options.buildFilters,
notFoundDefaultsTo: options.notFoundDefaultsTo
);

servers.forEach((target, server) {
Expand Down
4 changes: 3 additions & 1 deletion build_runner/lib/src/generate/build.dart
Expand Up @@ -165,7 +165,8 @@ Future<ServeHandler> watch(List<BuilderApplication> builders,
bool? isReleaseBuild,
Map<String, Map<String, dynamic>>? builderConfigOverrides,
String? logPerformanceDir,
Set<BuildFilter>? buildFilters}) =>
Set<BuildFilter>? buildFilters,
String? notFoundDefaultsTo}) =>
watch_impl.watch(
builders,
assumeTty: assumeTty,
Expand All @@ -190,4 +191,5 @@ Future<ServeHandler> watch(List<BuilderApplication> builders,
isReleaseBuild: isReleaseBuild,
logPerformanceDir: logPerformanceDir,
buildFilters: buildFilters,
notFoundDefaultsTo: notFoundDefaultsTo
);
15 changes: 11 additions & 4 deletions build_runner/lib/src/generate/watch_impl.dart
Expand Up @@ -55,6 +55,7 @@ Future<ServeHandler> watch(
bool? isReleaseBuild,
String? logPerformanceDir,
Set<BuildFilter>? buildFilters,
String? notFoundDefaultsTo
}) async {
builderConfigOverrides ??= const {};
buildDirs ??= <BuildDirectory>{};
Expand Down Expand Up @@ -105,7 +106,8 @@ Future<ServeHandler> watch(
.any((target) => target.outputLocation?.path.isNotEmpty ?? false),
buildDirs,
buildFilters,
isReleaseMode: isReleaseBuild ?? false);
isReleaseMode: isReleaseBuild ?? false,
notFoundDefaultsTo: notFoundDefaultsTo);

unawaited(watch.buildResults.drain().then((_) async {
await terminator.cancel();
Expand Down Expand Up @@ -134,7 +136,8 @@ WatchImpl _runWatch(
bool willCreateOutputDirs,
Set<BuildDirectory> buildDirs,
Set<BuildFilter> buildFilters,
{bool isReleaseMode = false}) =>
{bool isReleaseMode = false,
String? notFoundDefaultsTo}) =>
WatchImpl(
options,
environment,
Expand All @@ -146,7 +149,8 @@ WatchImpl _runWatch(
willCreateOutputDirs,
buildDirs,
buildFilters,
isReleaseMode: isReleaseMode);
isReleaseMode: isReleaseMode,
notFoundDefaultsTo: notFoundDefaultsTo);

class WatchImpl implements BuildState {
BuildImpl? _build;
Expand All @@ -172,6 +176,9 @@ class WatchImpl implements BuildState {
/// The [PackageGraph] for the current program.
final PackageGraph packageGraph;

// An asset path to redirect when the file is not found by the web server
final String? notFoundDefaultsTo;

/// The directories to build upon file changes and where to output them.
final Set<BuildDirectory> _buildDirs;

Expand Down Expand Up @@ -200,7 +207,7 @@ class WatchImpl implements BuildState {
this._willCreateOutputDirs,
this._buildDirs,
this._buildFilters,
{bool isReleaseMode = false})
{bool isReleaseMode = false, this.notFoundDefaultsTo})
: _debounceDelay = options.debounceDelay,
packageGraph = options.packageGraph {
buildResults = _run(
Expand Down
25 changes: 23 additions & 2 deletions build_runner/lib/src/server/server.dart
Expand Up @@ -47,8 +47,10 @@ ServeHandler createServeHandler(WatchImpl watch) {
var rootPackage = watch.packageGraph.root.name;
var assetGraphHanderCompleter = Completer<AssetGraphHandler>();
var assetHandlerCompleter = Completer<AssetHandler>();

watch.reader.then((reader) async {
assetHandlerCompleter.complete(AssetHandler(reader, rootPackage));
assetHandlerCompleter.complete(AssetHandler(reader, rootPackage,
notFoundDefaultsTo: watch.notFoundDefaultsTo));
assetGraphHanderCompleter
.complete(AssetGraphHandler(reader, rootPackage, watch.assetGraph!));
}).catchError((_) {}); // These errors are separately handled.
Expand Down Expand Up @@ -300,10 +302,19 @@ window.\$dartLoader.forceLoadModule('packages/build_runner/src/server/build_upda
class AssetHandler {
final FinalizedReader _reader;
final String _rootPackage;
late final AssetId _notFoundDefaultsTo;
late final bool _redirectNotFound;

final _typeResolver = MimeTypeResolver();

AssetHandler(this._reader, this._rootPackage);
AssetHandler(this._reader, this._rootPackage, {String? notFoundDefaultsTo}) {
jodinathan marked this conversation as resolved.
Show resolved Hide resolved
_redirectNotFound =
notFoundDefaultsTo != null && notFoundDefaultsTo.isNotEmpty;

if (_redirectNotFound) {
_notFoundDefaultsTo = AssetId(_rootPackage, notFoundDefaultsTo!);
}
}

Future<shelf.Response> handle(shelf.Request request, {String rootDir = ''}) =>
(request.url.path.endsWith('/') || request.url.path.isEmpty)
Expand Down Expand Up @@ -332,6 +343,16 @@ class AssetHandler {
return shelf.Response.notFound(
await _findDirectoryList(assetId));
}

if (_redirectNotFound) {
final pathSegments = assetId.pathSegments;

if (!pathSegments.contains('package') &&
!pathSegments.contains('lib')) {
return _handle(request, _notFoundDefaultsTo);
}
}

return shelf.Response.notFound('Not Found');
default:
return shelf.Response.notFound('Not Found');
Expand Down
2 changes: 1 addition & 1 deletion build_runner/pubspec.yaml
@@ -1,5 +1,5 @@
name: build_runner
version: 2.2.1-dev
version: 2.2.1-1-dev
description: A build system for Dart code generation and modular compilation.
repository: https://github.com/dart-lang/build/tree/master/build_runner

Expand Down
19 changes: 17 additions & 2 deletions build_runner/test/server/serve_handler_test.dart
Expand Up @@ -24,6 +24,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';

void main() {
late ServeHandler serveHandler;
late ServeHandler serveSpaHandler;
late InMemoryRunnerAssetReader reader;
late MockWatchImpl watchImpl;
late AssetGraph assetGraph;
Expand All @@ -38,6 +39,9 @@ void main() {
packageGraph,
assetGraph);
serveHandler = createServeHandler(watchImpl);

watchImpl.notFoundDefaultsTo = 'web/index.html';
serveSpaHandler = createServeHandler(watchImpl);
watchImpl
.addFutureResult(Future.value(BuildResult(BuildStatus.success, [])));
});
Expand All @@ -58,6 +62,13 @@ void main() {
expect(await response.readAsString(), 'content');
});

test('retrieve default asset when not found', () async {
_addSource('a|web/index.html', 'content');
var response = await serveSpaHandler.handlerFor('web')(
Request('GET', Uri.parse('http://server.com/foo.html')));
expect(await response.readAsString(), 'content');
});

test('caching with etags works', () async {
_addSource('a|web/index.html', 'content');
var handler = serveHandler.handlerFor('web');
Expand Down Expand Up @@ -442,14 +453,17 @@ class MockWatchImpl implements WatchImpl {

Future<BuildResult>? _currentBuild;

@override
String? notFoundDefaultsTo;

@override
Future<BuildResult>? get currentBuild => _currentBuild;
@override
set currentBuild(Future<BuildResult>? _) =>
throw UnsupportedError('unsupported!');

final _futureBuildResultsController = StreamController<Future<BuildResult>>();
final _buildResultsController = StreamController<BuildResult>();
final _buildResultsController = StreamController<BuildResult>.broadcast();

@override
Stream<BuildResult> get buildResults => _buildResultsController.stream;
Expand All @@ -467,7 +481,8 @@ class MockWatchImpl implements WatchImpl {
_futureBuildResultsController.add(result);
}

MockWatchImpl(this.reader, this.packageGraph, this.assetGraph) {
MockWatchImpl(this.reader, this.packageGraph, this.assetGraph,
{this.notFoundDefaultsTo}) {
var firstBuild = Completer<BuildResult>();
_currentBuild = firstBuild.future;
_futureBuildResultsController.stream.listen((futureBuildResult) {
Expand Down