diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d6a0b..57e85e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +- Fix: an unknown command in an app with package commands (like an app with dox_queue) now exits with code 1. Before, it printed "Could not find a command" and exited with 0. An app that already has `.dart_tool/dox/commands.dart` gets the new file on the next run. +- Fix: the model from `dox make:model` has no `unintended_html_in_doc_comment` info any more. The `[...]` example in its doc comment is now in backticks. - New: package commands. A package (or the app) adds `dox` commands with the `dox: commands:` key in its pubspec.yaml, like `dox: {commands: package:dox_queue/commands.dart}`. `dox ` finds them when the name is not a built-in command. It reads `.dart_tool/package_config.json`, writes `.dart_tool/dox/commands.dart`, and runs it with `dart run`. The exit code of the command is the exit code of `dox`. The file is written again only when package_config.json or a pubspec.yaml change. - A built-in command always wins. Two packages with the same command name print an error (exit code 1). A bad `dox: commands:` key prints a warning, and the other packages still work. - `dox help` lists the package commands, one group per package. `dox help ` shows one package command. Outside an app, or when no package has commands, it does not start Dart. diff --git a/lib/src/tools/create_model.dart b/lib/src/tools/create_model.dart index 6edf88b..e3fccc0 100644 --- a/lib/src/tools/create_model.dart +++ b/lib/src/tools/create_model.dart @@ -16,7 +16,7 @@ class $className extends ${className}Generator { List get hidden => []; /// Columns that create(), insert() and update() may take from a map, - /// like req.body. Add your columns here, eg. ['title', 'body']. + /// like req.body. Add your columns here, eg. `['title', 'body']`. /// While it is empty, create(req.body) throws MassAssignmentException. @override List get fillable => []; diff --git a/lib/src/tools/package_commands.dart b/lib/src/tools/package_commands.dart index 6df32cc..eac5d67 100644 --- a/lib/src/tools/package_commands.dart +++ b/lib/src/tools/package_commands.dart @@ -118,7 +118,17 @@ class PackageCommands { (runner.existsSync() || (cache['libraries'] as Map).isEmpty)) { _files = Map.from(cache['files'] as Map); _cacheKey = key; - return Map.from(cache['libraries'] as Map); + Map libs = Map.from( + cache['libraries'] as Map, + ); + + /// a new dox CLI can make a different runner, so write it again + /// when its text is old + if (libs.isNotEmpty) { + String source = runnerSource(libs); + if (runner.readAsStringSync() != source) _writeAtomic(runner, source); + } + return libs; } Map files = {}; Map libs = discover(files: files); @@ -339,6 +349,7 @@ Future main(List args) async { } if (matches == 0) { print($notFound.replaceAll('{name}', name)); + exitCode = 1; return; } if (matches > 1) { diff --git a/test/generators_test.dart b/test/generators_test.dart index 4f4d9ac..7bfa0be 100644 --- a/test/generators_test.dart +++ b/test/generators_test.dart @@ -431,6 +431,37 @@ void main() { }); }); + group('generators | doc comments', () { + /// `` in a doc comment, outside backticks, is the analyzer + /// info unintended_html_in_doc_comment in the app + test('no <...> outside backticks', () async { + File('pubspec.yaml').writeAsStringSync('name: my_app\n'); + createController('PostController', true); + createModel('Post'); + createMiddleware('AuthMiddleware'); + createDto('PostDto'); + createSerializer('PostSerializer'); + createFactory('PostFactory'); + createSeeder('PostSeeder'); + createJob('SendMailJob'); + await CommandRegistry.findCommand('make:migration')! + .function(['create_posts_table']); + RegExp doc = RegExp(r'^\s*///(.*)$'); + RegExp code = RegExp(r'`[^`]*`'); + List bad = []; + for (FileSystemEntity f in Directory.current.listSync(recursive: true)) { + if (f is! File || !f.path.endsWith('.dart')) continue; + for (String line in f.readAsLinesSync()) { + Match? m = doc.firstMatch(line); + if (m != null && m.group(1)!.replaceAll(code, '').contains('<')) { + bad.add('${f.path}: $line'); + } + } + } + expect(bad, isEmpty); + }); + }); + group('dox binary', () { test('make files through the real cli in a temp dir', () async { String bin = '${originalCwd.path}/bin/dox.dart'; diff --git a/test/package_commands_e2e_test.dart b/test/package_commands_e2e_test.dart index 8df8827..c664f07 100644 --- a/test/package_commands_e2e_test.dart +++ b/test/package_commands_e2e_test.dart @@ -309,6 +309,7 @@ final List commands = [Late()]; ProcessResult r = await dox(['nope']); expect(r.stdout, contains('Could not find a command named "nope"')); expect(r.stdout, contains('`dox: commands:`')); + expect(r.exitCode, 1); }); test('help lists the package commands by package', () async { diff --git a/test/package_commands_test.dart b/test/package_commands_test.dart index f66b504..a663749 100644 --- a/test/package_commands_test.dart +++ b/test/package_commands_test.dart @@ -197,6 +197,23 @@ void main() { expect(PackageCommands(root: app).libraries().keys, ['app']); }); + test('an old runner from an older CLI is written again', () { + package( + 'pkg', + withKey, + lib: {'commands.dart': 'var commands = [];'}, + ); + package('app', '', dir: app); + packageConfig(['pkg']); + PackageCommands commands = PackageCommands(root: app); + Map libs = commands.libraries(); + commands.runner.writeAsStringSync('// old runner'); + + /// the cache key is the same, but the runner text is old + expect(PackageCommands(root: app).libraries(), libs); + expect(commands.runner.readAsStringSync(), commands.runnerSource(libs)); + }); + test('a path dependency that changes its key is read again', () { package('pkg', ''); package('app', '', dir: app);