Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 `<String>[...]` 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 <name>` 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 <name>` shows one package command. Outside an app, or when no package has commands, it does not start Dart.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/tools/create_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class $className extends ${className}Generator {
List<String> get hidden => <String>[];

/// Columns that create(), insert() and update() may take from a map,
/// like req.body. Add your columns here, eg. <String>['title', 'body'].
/// like req.body. Add your columns here, eg. `<String>['title', 'body']`.
/// While it is empty, create(req.body) throws MassAssignmentException.
@override
List<String> get fillable => <String>[];
Expand Down
13 changes: 12 additions & 1 deletion lib/src/tools/package_commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,17 @@ class PackageCommands {
(runner.existsSync() || (cache['libraries'] as Map).isEmpty)) {
_files = Map<String, String>.from(cache['files'] as Map);
_cacheKey = key;
return Map<String, String>.from(cache['libraries'] as Map);
Map<String, String> libs = Map<String, String>.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<String, String> files = <String, String>{};
Map<String, String> libs = discover(files: files);
Expand Down Expand Up @@ -339,6 +349,7 @@ Future<void> main(List<String> args) async {
}
if (matches == 0) {
print($notFound.replaceAll('{name}', name));
exitCode = 1;
return;
}
if (matches > 1) {
Expand Down
31 changes: 31 additions & 0 deletions test/generators_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,37 @@ void main() {
});
});

group('generators | doc comments', () {
/// `<String>` 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(<String>['create_posts_table']);
RegExp doc = RegExp(r'^\s*///(.*)$');
RegExp code = RegExp(r'`[^`]*`');
List<String> bad = <String>[];
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';
Expand Down
1 change: 1 addition & 0 deletions test/package_commands_e2e_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ final List<Late> commands = <Late>[Late()];
ProcessResult r = await dox(<String>['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 {
Expand Down
17 changes: 17 additions & 0 deletions test/package_commands_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ void main() {
expect(PackageCommands(root: app).libraries().keys, <String>['app']);
});

test('an old runner from an older CLI is written again', () {
package(
'pkg',
withKey,
lib: <String, String>{'commands.dart': 'var commands = [];'},
);
package('app', '', dir: app);
packageConfig(<String>['pkg']);
PackageCommands commands = PackageCommands(root: app);
Map<String, String> 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);
Expand Down
Loading