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

feat: document non top level partials #1354

107 changes: 107 additions & 0 deletions packages/mason/lib/src/brick_ignore.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'dart:collection';
import 'dart:io';

import 'package:glob/glob.dart';
import 'package:mason/mason.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;

/// A file that defines what brick files or directories should be ignored during
/// bundling.
///
/// Only those directories and files under `__brick__` can be ignored.
///
/// For example, given the following brick directory:
///
/// ```txt
/// __brick__/
/// ├─ HELLO.md
/// └─ goodbye.dart
/// brick.yaml
/// .brickignore
/// README.md
/// ```
///
/// And the following `.brickignore` file content:
///
/// ```txt
/// # Ignore all markdown files
/// **.md
/// ```
///
/// The `HELLO.md` file will be ignored during bundling, but `goodbye.dart` will
/// not be ignored. Those other files not under `__brick__` are not bundled,
/// hence they can not be ignored.
///
/// Note that the ignore file supports comments, which are lines that start
/// with a `#` character.
///
/// See also:
///
/// * [createBundle], which creates a [MasonBundle] from a brick directory.
@internal
class BrickIgnore {
BrickIgnore._({
required Iterable<Glob> globs,
required String path,
required String brickDirectoryPath,
}) : _globs = UnmodifiableListView(globs),
_path = path,
_brickDirectoryPath = brickDirectoryPath;

/// Creates a [BrickIgnore] from a [File].
factory BrickIgnore.fromFile(File file) {
final lines = file.readAsLinesSync();
final globs = lines
.where((line) => !line.startsWith(_commentCharacter))
.map((line) => Glob(line.trim()));

final brickDirectoryPath = path.join(file.parent.path, BrickYaml.dir);

return BrickIgnore._(
globs: globs,
path: file.path,
brickDirectoryPath: brickDirectoryPath,
);
}

/// The name of the file to be used as the ignore file.
static const file = '.brickignore';

/// The character that indicates a comment in the ignore file.
///
/// If the line starts with this character, the line is considered a comment
/// and is ignored.
///
/// For example:
/// ```txt
/// # This is a comment
/// ```
static const _commentCharacter = '#';

final UnmodifiableListView<Glob> _globs;

/// The absolute path to the ignore file.
final String _path;

/// The absolute path to the directory where all brick templated files are
/// located.
///
/// See also:
///
/// * [BrickYaml.dir], which is the name of the directory where the templated
/// brick files are located.
final String _brickDirectoryPath;

/// Whether or not the [filePath] is ignored.
///
/// Immediately returns false if the [filePath] is not within the brick
/// directory (`__brick__`). Otherwise, checks if the [filePath] matches
/// any of the globs in the ignore file.
bool isIgnored(String filePath) {
if (!path.isWithin(_brickDirectoryPath, filePath)) return false;

final relativePath = path.relative(filePath, from: path.dirname(_path));
return _globs.any((glob) => glob.matches(relativePath));
}
}
112 changes: 112 additions & 0 deletions packages/mason/test/src/brick_ignore_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'dart:io';

import 'package:mason/src/brick_ignore.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';

void main() {
group('$BrickIgnore', () {
late File brickIgnoreFile;

setUp(() {
final temporaryDirectory = Directory.systemTemp.createTempSync();
addTearDown(() => temporaryDirectory.deleteSync(recursive: true));

brickIgnoreFile =
File(path.join(temporaryDirectory.path, BrickIgnore.file))
..createSync();
});

test('fromFile returns normally', () {
expect(
() => BrickIgnore.fromFile(brickIgnoreFile),
returnsNormally,
);
});

group('isIgnored', () {
test('returns true when the file is ignored', () {
brickIgnoreFile.writeAsStringSync('**.md');
final brickIgnore = BrickIgnore.fromFile(brickIgnoreFile);

final ignoredFilePath = path.join(
brickIgnoreFile.parent.path,
'__brick__',
'HELLO.md',
);

expect(
brickIgnore.isIgnored(ignoredFilePath),
isTrue,
reason: '`HELLO.md` is under `__brick__` and is ignored by `**.md`',
);
});

test(
'returns false when the file to be ignored is not under __brick__',
() {
brickIgnoreFile.writeAsStringSync('**.md');
final brickIgnore = BrickIgnore.fromFile(brickIgnoreFile);

final ignoredFilePath =
path.join(brickIgnoreFile.parent.path, 'HELLO.md');

expect(
brickIgnore.isIgnored(ignoredFilePath),
isFalse,
reason: '`HELLO.md` is not under `__brick__`',
);
},
);

test('returns false when the file is not ignored', () {
brickIgnoreFile.writeAsStringSync('');
final brickIgnore = BrickIgnore.fromFile(brickIgnoreFile);

final ignoredFilePath = path.join(
brickIgnoreFile.parent.path,
'__brick__',
'HELLO.md',
);

expect(
brickIgnore.isIgnored(ignoredFilePath),
isFalse,
reason: '`HELLO.md` is under `__brick__` and there are no ignores',
);
});

test('returns as expected when the file has comments', () {
brickIgnoreFile.writeAsStringSync('''
# Some comment
**.md
# **.dart
''');
final brickIgnore = BrickIgnore.fromFile(brickIgnoreFile);

final ignoredFilePath = path.join(
brickIgnoreFile.parent.path,
'__brick__',
'HELLO.md',
);
expect(
brickIgnore.isIgnored(ignoredFilePath),
isTrue,
reason: '`HELLO.md` is under `__brick__` and is ignored by `**.md`',
);

final notIgnoredFilePath = path.join(
brickIgnoreFile.parent.path,
'__brick__',
'goodbye.dart',
);
expect(
brickIgnore.isIgnored(notIgnoredFilePath),
isFalse,
reason:
'`goodbye.dart` is under `__brick__` and there are no ignores',
);
});
});
});
}
3 changes: 3 additions & 0 deletions packages/mason_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ It is possible to have templates nested within other templates. For example, giv

The `{{~ header.md }}` and `{{~ footer.md }}` are partials (partial brick templates). Partials will not be generated but can be included as part of an existing template.

❗ **Note: Partials must always be directly under the `__brick__` directory. Non-top-level partials are [yet to be
supported](https://github.com/felangel/mason/issues/378).**

For example given the contents of `{{~ header.md }}` and `{{~ footer.md }}` respectively

```md
Expand Down
Loading