diff --git a/pkgs/dart_pad/lib/sharing/editor_ui.dart b/pkgs/dart_pad/lib/sharing/editor_ui.dart index 04111c2eb..74d3d8304 100644 --- a/pkgs/dart_pad/lib/sharing/editor_ui.dart +++ b/pkgs/dart_pad/lib/sharing/editor_ui.dart @@ -182,11 +182,10 @@ abstract class EditorUi { return !hasErrors && !hasWarnings; } catch (e) { if (e is! TimeoutException) { - final message = e is ApiRequestError ? e.message : '$e'; displayIssues([ AnalysisIssue( kind: 'error', - message: message, + message: '$e', // set invalid line number, so NO line # will be displayed location: Location(line: -1), ) @@ -250,10 +249,9 @@ abstract class EditorUi { return true; } catch (e) { ga.sendException('${e.runtimeType}'); - final message = e is ApiRequestError ? e.message : '$e'; showSnackbar('Error compiling to JavaScript'); clearOutput(); - showOutput('Error compiling to JavaScript:\n$message', error: true); + showOutput('Error compiling to JavaScript:\n$e', error: true); return false; } finally { runButton.disabled = false; diff --git a/pkgs/dart_services/lib/server.dart b/pkgs/dart_services/lib/server.dart index 205c967e6..fc76b811d 100644 --- a/pkgs/dart_services/lib/server.dart +++ b/pkgs/dart_services/lib/server.dart @@ -148,8 +148,14 @@ Middleware exceptionResponse() { return (Request request) async { try { return await handler(request); - } catch (e) { - return Response.badRequest(body: e is BadRequest ? e.message : '$e'); + } catch (e, st) { + if (e is BadRequest) { + return Response.badRequest(body: e.message); + } + + _logger.severe('${request.requestedUri.path} $e', null, st); + + return Response.badRequest(body: '$e'); } }; }; diff --git a/pkgs/dart_services/lib/src/analysis.dart b/pkgs/dart_services/lib/src/analysis.dart index 0c7e6c67a..445aa3917 100644 --- a/pkgs/dart_services/lib/src/analysis.dart +++ b/pkgs/dart_services/lib/src/analysis.dart @@ -50,28 +50,26 @@ class Analyzer { return analysisServer.analyze(source, imports: imports); } - Future completeV3(String source, int offset) async { + Future complete(String source, int offset) async { await _checkPackageReferences(getAllImportsFor(source)); - return analysisServer.completeV3(source, offset); + return analysisServer.complete(source, offset); } - Future fixesV3(String source, int offset) async { + Future fixes(String source, int offset) async { await _checkPackageReferences(getAllImportsFor(source)); - return analysisServer.fixesV3(source, offset); + return analysisServer.fixes(source, offset); } Future format(String source, int? offset) async { - await _checkPackageReferences(getAllImportsFor(source)); - return analysisServer.format(source, offset); } - Future dartdocV3(String source, int offset) async { + Future dartdoc(String source, int offset) async { await _checkPackageReferences(getAllImportsFor(source)); - return analysisServer.dartdocV3(source, offset); + return analysisServer.dartdoc(source, offset); } /// Check that the set of packages referenced is valid. @@ -83,8 +81,9 @@ class Analyzer { if (unsupportedImports.isNotEmpty) { final unsupportedUris = - unsupportedImports.map((import) => import.uri.stringValue); - throw BadRequest('Unsupported import(s): $unsupportedUris'); + unsupportedImports.map((import) => import.uri.stringValue).toList(); + final plural = unsupportedUris.length == 1 ? 'import' : 'imports'; + throw BadRequest('unsupported $plural ${unsupportedUris.join(', ')}'); } } @@ -149,7 +148,7 @@ class AnalysisServerWrapper { }); } - Future completeV3(String source, int offset) async { + Future complete(String source, int offset) async { final results = await _completeImpl( {kMainDart: source}, kMainDart, @@ -208,7 +207,7 @@ class AnalysisServerWrapper { ); } - Future fixesV3(String src, int offset) async { + Future fixes(String src, int offset) async { final mainFile = _getPathFromName(kMainDart); await _loadSources({mainFile: src}); @@ -260,7 +259,7 @@ class AnalysisServerWrapper { }); } - Future dartdocV3(String src, int offset) async { + Future dartdoc(String src, int offset) async { final sourcepath = _getPathFromName(kMainDart); await _loadSources(_getOverlayMapWithPaths({kMainDart: src})); diff --git a/pkgs/dart_services/lib/src/common_server.dart b/pkgs/dart_services/lib/src/common_server.dart index 929ea0699..6c462cdd5 100644 --- a/pkgs/dart_services/lib/src/common_server.dart +++ b/pkgs/dart_services/lib/src/common_server.dart @@ -140,7 +140,7 @@ class CommonServerApi { api.SourceRequest.fromJson(await request.readAsJson()); final result = await serialize(() => - impl.analyzer.completeV3(sourceRequest.source, sourceRequest.offset!)); + impl.analyzer.complete(sourceRequest.source, sourceRequest.offset!)); return ok(result.toJson()); } @@ -152,8 +152,8 @@ class CommonServerApi { final sourceRequest = api.SourceRequest.fromJson(await request.readAsJson()); - final result = await serialize(() => - impl.analyzer.fixesV3(sourceRequest.source, sourceRequest.offset!)); + final result = await serialize( + () => impl.analyzer.fixes(sourceRequest.source, sourceRequest.offset!)); return ok(result.toJson()); } @@ -183,7 +183,7 @@ class CommonServerApi { api.SourceRequest.fromJson(await request.readAsJson()); final result = await serialize(() { - return impl.analyzer.dartdocV3( + return impl.analyzer.dartdoc( sourceRequest.source, sourceRequest.offset!, ); diff --git a/pkgs/dart_services/lib/src/compiling.dart b/pkgs/dart_services/lib/src/compiling.dart index 92c80acce..49038a9f3 100644 --- a/pkgs/dart_services/lib/src/compiling.dart +++ b/pkgs/dart_services/lib/src/compiling.dart @@ -50,16 +50,6 @@ class Compiler { String source, { bool returnSourceMap = false, }) async { - final imports = getAllImportsFor(source); - final unsupportedImports = - getUnsupportedImports(imports, sourceFiles: {kMainDart}); - if (unsupportedImports.isNotEmpty) { - return CompilationResults(problems: [ - for (final import in unsupportedImports) - CompilationProblem._('unsupported import: ${import.uri.stringValue}'), - ]); - } - final temp = Directory.systemTemp.createTempSync('dartpad'); _logger.fine('Temp directory created: ${temp.path}'); @@ -120,14 +110,6 @@ class Compiler { /// Compile the given string and return the resulting [DDCCompilationResults]. Future compileDDC(String source) async { final imports = getAllImportsFor(source); - final unsupportedImports = - getUnsupportedImports(imports, sourceFiles: {kMainDart}); - if (unsupportedImports.isNotEmpty) { - return DDCCompilationResults.failed([ - for (final import in unsupportedImports) - CompilationProblem._('unsupported import: ${import.uri.stringValue}'), - ]); - } final temp = Directory.systemTemp.createTempSync('dartpad'); _logger.fine('Temp directory created: ${temp.path}'); @@ -183,8 +165,9 @@ class Compiler { await _ddcDriver.doWork(WorkRequest()..arguments.addAll(arguments)); if (response.exitCode != 0) { - return DDCCompilationResults.failed( - [CompilationProblem._(response.output)]); + return DDCCompilationResults.failed([ + CompilationProblem._(_rewritePaths(response.output)), + ]); } else { // The `--single-out-file` option for dartdevc was removed in v2.7.0. As // a result, the JS code produced above does *not* provide a name for @@ -313,3 +296,25 @@ bool _doNothing(String from, String to) { } return false; } + +/// Remove any references to 'bootstrap.dart' and replace with referenced to +/// 'main.dart'. +String _rewritePaths(String output) { + final lines = output.split('\n'); + + return lines.map((line) { + final token1 = 'lib/bootstrap.dart:'; + var index = line.indexOf(token1); + if (index != -1) { + return 'main.dart:${line.substring(index + token1.length)}'; + } + + final token2 = 'lib/main.dart:'; + index = line.indexOf(token2); + if (index != -1) { + return 'main.dart:${line.substring(index + token2.length)}'; + } + + return line; + }).join('\n'); +} diff --git a/pkgs/dart_services/lib/src/logging.dart b/pkgs/dart_services/lib/src/logging.dart index 641b02f3f..95f3b1690 100644 --- a/pkgs/dart_services/lib/src/logging.dart +++ b/pkgs/dart_services/lib/src/logging.dart @@ -6,13 +6,23 @@ import 'package:logging/logging.dart'; const bool verboseLogging = false; +final _wsRegex = RegExp(r' \s+'); + void emitLogsToStdout() { Logger.root.onRecord.listen((LogRecord record) { if (verboseLogging || record.level >= Level.INFO) { - print('[${record.level.name.toLowerCase()}] ${record.message}'); + var stackTrace = ''; if (record.stackTrace != null) { - print(record.stackTrace); + var lines = record.stackTrace!.toString().split('\n').take(5).join(' '); + lines = lines.replaceAll(_wsRegex, ' '); + stackTrace = ' $lines'; } + + print( + '[${record.level.name.toLowerCase()}] ' + '${record.message}' + '$stackTrace', + ); } }); } diff --git a/pkgs/dart_services/lib/src/project_templates.dart b/pkgs/dart_services/lib/src/project_templates.dart index 4a0914405..787d4cf58 100644 --- a/pkgs/dart_services/lib/src/project_templates.dart +++ b/pkgs/dart_services/lib/src/project_templates.dart @@ -213,7 +213,7 @@ String? _packageNameFromPackageUri(String uriString) { /// /// Note: The filenames in [sourceFiles] were sanitized of any /// 'package:'/etc syntax as the file set arrives from the endpoint, and -/// before being passed to [getUnsupportedImports].This is done so +/// before being passed to [getUnsupportedImports]. This is done so /// the list can't be used to bypass unsupported imports. List getUnsupportedImports( List imports, { diff --git a/pkgs/dart_services/lib/src/shared/services.dart b/pkgs/dart_services/lib/src/shared/services.dart index ee49d2941..3016f7e0a 100644 --- a/pkgs/dart_services/lib/src/shared/services.dart +++ b/pkgs/dart_services/lib/src/shared/services.dart @@ -49,10 +49,7 @@ class ServicesClient { final response = await client.get(Uri.parse('${rootUrl}api/v3/$action')); if (response.statusCode != 200) { - throw ApiRequestError( - '$action: ${response.statusCode}: ${response.reasonPhrase}', - response.body, - ); + throw ApiRequestError(action, response.body); } else { try { return responseFactory( @@ -68,13 +65,13 @@ class ServicesClient { Map request, T Function(Map json) responseFactory, ) async { - final response = await client.post(Uri.parse('${rootUrl}api/v3/$action'), - encoding: utf8, body: json.encode(request)); + final response = await client.post( + Uri.parse('${rootUrl}api/v3/$action'), + encoding: utf8, + body: json.encode(request), + ); if (response.statusCode != 200) { - throw ApiRequestError( - '$action: ${response.statusCode}: ${response.reasonPhrase}', - response.body, - ); + throw ApiRequestError(action, response.body); } else { try { return responseFactory( @@ -93,5 +90,5 @@ class ApiRequestError implements Exception { final String body; @override - String toString() => '$message\n$body'; + String toString() => '$message: $body'; } diff --git a/pkgs/dart_services/test/analysis_test.dart b/pkgs/dart_services/test/analysis_test.dart index 14985f950..393799640 100644 --- a/pkgs/dart_services/test/analysis_test.dart +++ b/pkgs/dart_services/test/analysis_test.dart @@ -25,7 +25,7 @@ void defineTests() { test('simple_completion', () async { // Just after `i.` on line 3 of [completionCode] - final results = await analysisServer.completeV3(completionCode, 32); + final results = await analysisServer.complete(completionCode, 32); expect(results.replacementLength, 0); expect(results.replacementOffset, 32); final completions = results.suggestions.map((c) => c.completion).toList(); @@ -48,11 +48,9 @@ void defineTests() { test('completions polluted on second request (repro #126)', () async { // https://github.com/dart-lang/dart-services/issues/126 - return analysisServer - .completeV3(completionFilterCode, 17) - .then((results) { + return analysisServer.complete(completionFilterCode, 17).then((results) { return analysisServer - .completeV3(completionFilterCode, 17) + .complete(completionFilterCode, 17) .then((results) { expect(results.replacementLength, 2); expect(results.replacementOffset, 16); @@ -66,7 +64,7 @@ void defineTests() { // We're testing here that we don't have any path imports - we don't want // to enable browsing the file system. final testCode = "import '/'; main() { int a = 0; a. }"; - final results = await analysisServer.completeV3(testCode, 9); + final results = await analysisServer.complete(testCode, 9); final completions = results.suggestions; if (completions.isNotEmpty) { @@ -81,7 +79,7 @@ void defineTests() { // Ensure we can import dart: imports. final testCode = "import 'dart:c'; main() { int a = 0; a. }"; - final results = await analysisServer.completeV3(testCode, 14); + final results = await analysisServer.complete(testCode, 14); final completions = results.suggestions; expect( @@ -98,13 +96,13 @@ void defineTests() { test('import_and_other_test', () async { final testCode = "import '/'; main() { int a = 0; a. }"; - final results = await analysisServer.completeV3(testCode, 34); + final results = await analysisServer.complete(testCode, 34); expect(completionsContains(results, 'abs'), true); }); test('quickFix simple', () async { - final results = await analysisServer.fixesV3(quickFixesCode, 25); + final results = await analysisServer.fixes(quickFixesCode, 25); final changes = results.fixes; expect(changes, isNotEmpty); @@ -149,7 +147,7 @@ void defineTests() { final idx = 61; expect(completionLargeNamespaces.substring(idx - 1, idx), 'A'); final results = - await analysisServer.completeV3(completionLargeNamespaces, 61); + await analysisServer.complete(completionLargeNamespaces, 61); expect(completionsContains(results, 'A'), true); expect(completionsContains(results, 'AB'), true); expect(completionsContains(results, 'ABC'), true); diff --git a/pkgs/dart_services/test/compiling_test.dart b/pkgs/dart_services/test/compiling_test.dart index 8f51bbb2c..323b1b9ac 100644 --- a/pkgs/dart_services/test/compiling_test.dart +++ b/pkgs/dart_services/test/compiling_test.dart @@ -92,6 +92,15 @@ void defineTests() { contains('Error: Expected \';\' after this.')); }); + test('compileDDC with no main', () async { + final result = await compiler.compileDDC(sampleCodeNoMain); + expect(result.success, false); + expect(result.problems.length, 1); + expect(result.problems.first.message, + contains("Error: Method not found: 'main'")); + expect(result.problems.first.message, startsWith('main.dart:')); + }); + test('compileDDC with multiple errors', () async { final result = await compiler.compileDDC(sampleCodeErrors); expect(result.success, false); @@ -172,7 +181,7 @@ void main() { missingMethod ('foo'); } final result = await compiler.compile(code); expect(result.problems, hasLength(1)); expect(result.problems.single.message, - equals('unsupported import: foo.dart')); + contains("Error when reading 'lib/foo.dart'")); }); test('bad import - http', () async { @@ -183,7 +192,7 @@ void main() { missingMethod ('foo'); } final result = await compiler.compile(code); expect(result.problems, hasLength(1)); expect(result.problems.single.message, - equals('unsupported import: http://example.com')); + contains("Error when reading 'http://example.com'")); }); test('multiple bad imports', () async { @@ -192,11 +201,11 @@ import 'package:foo'; import 'package:bar'; '''; final result = await compiler.compile(code); - expect(result.problems, hasLength(2)); - expect(result.problems[0].message, - equals('unsupported import: package:foo')); - expect(result.problems[1].message, - equals('unsupported import: package:bar')); + expect(result.problems, hasLength(1)); + expect(result.problems.single.message, + contains("Invalid package URI 'package:foo'")); + expect(result.problems.single.message, + contains("Invalid package URI 'package:bar'")); }); test('disallow compiler warnings', () async { diff --git a/pkgs/dart_services/test/src/sample_code.dart b/pkgs/dart_services/test/src/sample_code.dart index 3aa5c462a..b2299c8ea 100644 --- a/pkgs/dart_services/test/src/sample_code.dart +++ b/pkgs/dart_services/test/src/sample_code.dart @@ -518,6 +518,12 @@ void main() { } '''; +const sampleCodeNoMain = ''' +void missing_main() { + print("hello"); +} +'''; + const sampleCodeErrors = ''' void main() { print1("hello"); diff --git a/pkgs/dart_services/tool/grind.dart b/pkgs/dart_services/tool/grind.dart index 2c0c1aba3..b8c1158e2 100644 --- a/pkgs/dart_services/tool/grind.dart +++ b/pkgs/dart_services/tool/grind.dart @@ -22,16 +22,6 @@ Future main(List args) async { return grind(args); } -@Task() -@Depends(buildStorageArtifacts) -Future serve() async { - await _run(Platform.executable, arguments: [ - path.join('bin', 'server.dart'), - '--port', - '8080', - ]); -} - final List compilationArtifacts = [ 'dart_sdk.js', 'flutter_web.js', diff --git a/pkgs/dartpad_shared/lib/services.dart b/pkgs/dartpad_shared/lib/services.dart index ee49d2941..3016f7e0a 100644 --- a/pkgs/dartpad_shared/lib/services.dart +++ b/pkgs/dartpad_shared/lib/services.dart @@ -49,10 +49,7 @@ class ServicesClient { final response = await client.get(Uri.parse('${rootUrl}api/v3/$action')); if (response.statusCode != 200) { - throw ApiRequestError( - '$action: ${response.statusCode}: ${response.reasonPhrase}', - response.body, - ); + throw ApiRequestError(action, response.body); } else { try { return responseFactory( @@ -68,13 +65,13 @@ class ServicesClient { Map request, T Function(Map json) responseFactory, ) async { - final response = await client.post(Uri.parse('${rootUrl}api/v3/$action'), - encoding: utf8, body: json.encode(request)); + final response = await client.post( + Uri.parse('${rootUrl}api/v3/$action'), + encoding: utf8, + body: json.encode(request), + ); if (response.statusCode != 200) { - throw ApiRequestError( - '$action: ${response.statusCode}: ${response.reasonPhrase}', - response.body, - ); + throw ApiRequestError(action, response.body); } else { try { return responseFactory( @@ -93,5 +90,5 @@ class ApiRequestError implements Exception { final String body; @override - String toString() => '$message\n$body'; + String toString() => '$message: $body'; } diff --git a/pkgs/sketch_pad/lib/model.dart b/pkgs/sketch_pad/lib/model.dart index f7bca2ce2..822845a41 100644 --- a/pkgs/sketch_pad/lib/model.dart +++ b/pkgs/sketch_pad/lib/model.dart @@ -319,9 +319,12 @@ class AppServices { appModel.analysisIssues.value = results.issues; appModel.packageImports.value = results.packageImports; } catch (error) { - final message = error is ApiRequestError ? error.message : '$error'; appModel.analysisIssues.value = [ - AnalysisIssue(kind: 'error', message: message, location: Location()), + AnalysisIssue( + kind: 'error', + message: '$error', + location: Location(line: 0, column: 0), + ), ]; appModel.packageImports.value = []; }