Fixed jael_preprocessor tests

This commit is contained in:
thomashii@dukefirehawk.com 2021-04-30 15:08:54 +08:00
parent e83d56c9af
commit 750dd63309
7 changed files with 149 additions and 117 deletions

View file

@ -17,10 +17,10 @@
* Migrated json_god to 4.0.0 (13/13 tests passed)
* Migrated angel_client to 4.0.0 (6/13 tests passed)
* Migrated angel_websocket to 4.0.0 (2/3 tests passed)
* Updated test to 4.0.0 (1/1 test passed)
* Migrated test to 4.0.0 (1/1 test passed)
* Added symbol_table and migrated to 2.0.0 (16/16 tests passed)
* Migrated jael to 4.0.0 (20/20 tests passed)
* Updated jael_preprocessor to 3.0.0 (in progress)
* Migrated jael_preprocessor to 3.0.0 (5/5 tests passed)
* Updated angel_jael to 3.0.0 (in progress)
* Updated pub_sub to 3.0.0 (in progress)
* Updated production to 2.0.0 (in progress)

View file

@ -4,11 +4,11 @@ import 'package:file/file.dart';
import 'package:jael/jael.dart' as jael;
import 'package:jael_preprocessor/jael_preprocessor.dart' as jael;
Future<jael.Document> process(
Future<jael.Document?> process(
jael.Document doc, Directory dir, errorHandler(jael.JaelError e)) {
return jael.resolve(doc, dir, onError: errorHandler, patch: [
(doc, dir, onError) {
print(doc.root.children.length);
print(doc!.root.children.length);
return doc;
},
]);

View file

@ -1,19 +1,20 @@
import 'dart:async';
import 'dart:collection';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:file/file.dart';
import 'package:jael/jael.dart';
import 'package:symbol_table/symbol_table.dart';
/// Modifies a Jael document.
typedef FutureOr<Document> Patcher(Document document,
Directory currentDirectory, void onError(JaelError error));
typedef FutureOr<Document>? Patcher(Document? document,
Directory currentDirectory, void onError(JaelError error)?);
/// Expands all `block[name]` tags within the template, replacing them with the correct content.
///
/// To apply additional patches to resolved documents, provide a set of [patch]
/// functions.
Future<Document> resolve(Document document, Directory currentDirectory,
{void onError(JaelError error), Iterable<Patcher> patch}) async {
Future<Document?> resolve(Document document, Directory currentDirectory,
{void onError(JaelError error)?, Iterable<Patcher>? patch}) async {
onError ?? (e) => throw e;
// Resolve all includes...
@ -25,7 +26,7 @@ Future<Document> resolve(Document document, Directory currentDirectory,
if (patch?.isNotEmpty != true) return patched;
for (var p in patch) {
for (var p in patch!) {
patched = await p(patched, currentDirectory, onError);
}
@ -33,24 +34,29 @@ Future<Document> resolve(Document document, Directory currentDirectory,
}
/// Folds any `extend` declarations.
Future<Document> applyInheritance(Document document, Directory currentDirectory,
void onError(JaelError error), Iterable<Patcher> patch) async {
Future<Document?> applyInheritance(
Document? document,
Directory currentDirectory,
void onError(JaelError error)?,
Iterable<Patcher>? patch) async {
if (document == null) {
return null;
}
if (document.root.tagName.name != 'extend') {
// This is not an inherited template, so just fill in the existing blocks.
var root =
replaceChildrenOfElement(document.root, {}, onError, true, false);
return new Document(document.doctype, root);
return Document(document.doctype, root);
}
var element = document.root;
var attr =
element.attributes.firstWhere((a) => a.name == 'src', orElse: () => null);
var attr = element.attributes.firstWhereOrNull((a) => a.name == 'src');
if (attr == null) {
onError(new JaelError(JaelErrorSeverity.warning,
onError!(JaelError(JaelErrorSeverity.warning,
'Missing "src" attribute in "extend" tag.', element.tagName.span));
return null;
} else if (attr.value is! StringLiteral) {
onError(new JaelError(
onError!(JaelError(
JaelErrorSeverity.warning,
'The "src" attribute in an "extend" tag must be a string literal.',
element.tagName.span));
@ -70,10 +76,10 @@ Future<Document> applyInheritance(Document document, Directory currentDirectory,
var out = hierarchy?.root;
if (out is! RegularElement) {
return hierarchy.rootDocument;
return hierarchy!.rootDocument;
}
Element setOut(Element out, Map<String, RegularElement> definedOverrides,
Element setOut(Element out, Map<String?, RegularElement> definedOverrides,
bool anyTemplatesRemain) {
var children = <ElementChild>[];
@ -87,40 +93,40 @@ Future<Document> applyInheritance(Document document, Directory currentDirectory,
}
}
var root = hierarchy.root as RegularElement;
return new RegularElement(root.lt, root.tagName, root.attributes, root.gt,
var root = hierarchy!.root as RegularElement;
return RegularElement(root.lt, root.tagName, root.attributes, root.gt,
children, root.lt2, root.slash, root.tagName2, root.gt2);
}
// Loop through all extends, filling in blocks.
while (hierarchy.extendsTemplates.isNotEmpty) {
while (hierarchy!.extendsTemplates.isNotEmpty) {
var tmpl = hierarchy.extendsTemplates.removeFirst();
var definedOverrides = findBlockOverrides(tmpl, onError);
if (definedOverrides == null) break;
out =
setOut(out, definedOverrides, hierarchy.extendsTemplates.isNotEmpty);
setOut(out!, definedOverrides, hierarchy.extendsTemplates.isNotEmpty);
}
// Lastly, just default-fill any remaining blocks.
var definedOverrides = findBlockOverrides(out, onError);
if (definedOverrides != null) out = setOut(out, definedOverrides, false);
var definedOverrides = findBlockOverrides(out!, onError);
out = setOut(out, definedOverrides, false);
// Return our processed document.
return new Document(document.doctype, out);
return Document(document.doctype, out);
}
}
Map<String, RegularElement> findBlockOverrides(
Element tmpl, void onError(JaelError e)) {
Element tmpl, void onError(JaelError e)?) {
var out = <String, RegularElement>{};
for (var child in tmpl.children) {
if (child is RegularElement && child.tagName?.name == 'block') {
if (child is RegularElement && child.tagName.name == 'block') {
var name = child.attributes
.firstWhere((a) => a.name == 'name', orElse: () => null)
.firstWhereOrNull((a) => a.name == 'name')
?.value
?.compute(new SymbolTable()) as String;
if (name?.trim()?.isNotEmpty == true) {
?.compute(SymbolTable()) as String?;
if (name != null && name.trim().isNotEmpty == true) {
out[name] = child;
}
}
@ -130,27 +136,31 @@ Map<String, RegularElement> findBlockOverrides(
}
/// Resolves the document hierarchy at a given node in the tree.
Future<DocumentHierarchy> resolveHierarchy(Document document,
Directory currentDirectory, void onError(JaelError e)) async {
var extendsTemplates = new Queue<Element>();
String parent;
Future<DocumentHierarchy?> resolveHierarchy(Document document,
Directory currentDirectory, void onError(JaelError e)?) async {
var extendsTemplates = Queue<Element>();
String? parent;
while (document != null && (parent = getParent(document, onError)) != null) {
Document? doc = document;
while (doc != null && (parent = getParent(doc, onError)) != null) {
try {
extendsTemplates.addFirst(document.root);
var file = currentDirectory.childFile(parent);
extendsTemplates.addFirst(doc.root);
var file = currentDirectory.childFile(parent!);
var parsed = parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: onError);
document = await resolveIncludes(parsed, currentDirectory, onError);
sourceUrl: file.uri, onError: onError)!;
doc = await resolveIncludes(parsed, currentDirectory, onError);
} on FileSystemException catch (e) {
onError(new JaelError(
JaelErrorSeverity.error, e.message, document.root.span));
onError!(
JaelError(JaelErrorSeverity.error, e.message, document.root.span));
return null;
}
}
if (document == null) return null;
return new DocumentHierarchy(document, extendsTemplates);
if (doc == null) {
return null;
}
return DocumentHierarchy(doc, extendsTemplates);
}
class DocumentHierarchy {
@ -164,17 +174,16 @@ class DocumentHierarchy {
Iterable<ElementChild> replaceBlocks(
Element element,
Map<String, RegularElement> definedOverrides,
void onError(JaelError e),
Map<String?, RegularElement> definedOverrides,
void onError(JaelError e)?,
bool replaceWithDefault,
bool anyTemplatesRemain) {
if (element.tagName.name == 'block') {
var nameAttr = element.attributes
.firstWhere((a) => a.name == 'name', orElse: () => null);
var name = nameAttr?.value?.compute(new SymbolTable());
var nameAttr = element.attributes.firstWhereOrNull((a) => a.name == 'name');
var name = nameAttr?.value?.compute(SymbolTable());
if (name?.trim()?.isNotEmpty != true) {
onError(new JaelError(
onError!(JaelError(
JaelErrorSeverity.warning,
'This <block> has no `name` attribute, and will be outputted as-is.',
element.span));
@ -190,7 +199,7 @@ Iterable<ElementChild> replaceBlocks(
onError, replaceWithDefault, anyTemplatesRemain);
return [
new RegularElement(
RegularElement(
element.lt,
element.tagName,
element.attributes,
@ -209,7 +218,7 @@ Iterable<ElementChild> replaceBlocks(
return [element];
}
} else {
return allChildrenOfRegularElement(definedOverrides[name],
return allChildrenOfRegularElement(definedOverrides[name]!,
definedOverrides, onError, replaceWithDefault, anyTemplatesRemain);
}
} else if (element is SelfClosingElement) {
@ -225,7 +234,7 @@ Iterable<ElementChild> replaceBlocks(
Element replaceChildrenOfElement(
Element el,
Map<String, RegularElement> definedOverrides,
void onError(JaelError e),
void onError(JaelError e)?,
bool replaceWithDefault,
bool anyTemplatesRemain) {
if (el is RegularElement) {
@ -238,20 +247,20 @@ Element replaceChildrenOfElement(
RegularElement replaceChildrenOfRegularElement(
RegularElement el,
Map<String, RegularElement> definedOverrides,
void onError(JaelError e),
Map<String?, RegularElement> definedOverrides,
void onError(JaelError e)?,
bool replaceWithDefault,
bool anyTemplatesRemain) {
var children = allChildrenOfRegularElement(
el, definedOverrides, onError, replaceWithDefault, anyTemplatesRemain);
return new RegularElement(el.lt, el.tagName, el.attributes, el.gt, children,
return RegularElement(el.lt, el.tagName, el.attributes, el.gt, children,
el.lt2, el.slash, el.tagName2, el.gt2);
}
List<ElementChild> allChildrenOfRegularElement(
RegularElement el,
Map<String, RegularElement> definedOverrides,
void onError(JaelError e),
Map<String?, RegularElement> definedOverrides,
void onError(JaelError e)?,
bool replaceWithDefault,
bool anyTemplatesRemain) {
var children = <ElementChild>[];
@ -269,18 +278,17 @@ List<ElementChild> allChildrenOfRegularElement(
}
/// Finds the name of the parent template.
String getParent(Document document, void onError(JaelError error)) {
String? getParent(Document document, void onError(JaelError error)?) {
var element = document.root;
if (element?.tagName?.name != 'extend') return null;
if (element.tagName.name != 'extend') return null;
var attr =
element.attributes.firstWhere((a) => a.name == 'src', orElse: () => null);
var attr = element.attributes.firstWhereOrNull((a) => a.name == 'src');
if (attr == null) {
onError(new JaelError(JaelErrorSeverity.warning,
onError!(JaelError(JaelErrorSeverity.warning,
'Missing "src" attribute in "extend" tag.', element.tagName.span));
return null;
} else if (attr.value is! StringLiteral) {
onError(new JaelError(
onError!(JaelError(
JaelErrorSeverity.warning,
'The "src" attribute in an "extend" tag must be a string literal.',
element.tagName.span));
@ -291,14 +299,22 @@ String getParent(Document document, void onError(JaelError error)) {
}
/// Expands all `include[src]` tags within the template, and fills in the content of referenced files.
Future<Document> resolveIncludes(Document document, Directory currentDirectory,
void onError(JaelError error)) async {
return new Document(document.doctype,
await _expandIncludes(document.root, currentDirectory, onError));
Future<Document?> resolveIncludes(Document? document,
Directory currentDirectory, void onError(JaelError error)?) async {
if (document == null) {
return null;
}
Element? rootElement =
await _expandIncludes(document.root, currentDirectory, onError);
if (rootElement != null) {
return Document(document.doctype, rootElement);
} else {
return null;
}
}
Future<Element> _expandIncludes(Element element, Directory currentDirectory,
void onError(JaelError error)) async {
Future<Element?> _expandIncludes(Element element, Directory currentDirectory,
void onError(JaelError error)?) async {
if (element.tagName.name != 'include') {
if (element is SelfClosingElement)
return element;
@ -307,13 +323,17 @@ Future<Element> _expandIncludes(Element element, Directory currentDirectory,
for (var child in element.children) {
if (child is Element) {
expanded.add(await _expandIncludes(child, currentDirectory, onError));
Element? includeElement =
await _expandIncludes(child, currentDirectory, onError);
if (includeElement != null) {
expanded.add(includeElement);
}
} else {
expanded.add(child);
}
}
return new RegularElement(
return RegularElement(
element.lt,
element.tagName,
element.attributes,
@ -324,19 +344,18 @@ Future<Element> _expandIncludes(Element element, Directory currentDirectory,
element.tagName2,
element.gt2);
} else {
throw new UnsupportedError(
throw UnsupportedError(
'Unsupported element type: ${element.runtimeType}');
}
}
var attr =
element.attributes.firstWhere((a) => a.name == 'src', orElse: () => null);
var attr = element.attributes.firstWhereOrNull((a) => a.name == 'src');
if (attr == null) {
onError(new JaelError(JaelErrorSeverity.warning,
onError!(JaelError(JaelErrorSeverity.warning,
'Missing "src" attribute in "include" tag.', element.tagName.span));
return null;
} else if (attr.value is! StringLiteral) {
onError(new JaelError(
onError!(JaelError(
JaelErrorSeverity.warning,
'The "src" attribute in an "include" tag must be a string literal.',
element.tagName.span));
@ -346,10 +365,13 @@ Future<Element> _expandIncludes(Element element, Directory currentDirectory,
var file =
currentDirectory.fileSystem.file(currentDirectory.uri.resolve(src));
var contents = await file.readAsString();
var doc = parseDocument(contents, sourceUrl: file.uri, onError: onError);
var processed = await resolve(
var doc = parseDocument(contents, sourceUrl: file.uri, onError: onError)!;
var processed = await (resolve(
doc, currentDirectory.fileSystem.directory(file.dirname),
onError: onError);
onError: onError));
if (processed == null) {
return null;
}
return processed.root;
}
}

View file

@ -1,19 +1,29 @@
name: jael_preprocessor
version: 3.0.0
version: 4.0.0
description: A pre-processor for resolving blocks and includes within Jael templates.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/jael/tree/master/jael_preprocessor
publish_to: none
environment:
sdk: '>=2.10.0 <3.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
file: ^6.1.0
jael:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/jael/jael
symbol_table: ^2.0.0
symbol_table:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/symbol_table
collection: ^1.15.0-nullsafety.4
dev_dependencies:
code_buffer:
test: ^1.15.7
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/code_buffer
test: ^1.17.3

View file

@ -7,10 +7,10 @@ import 'package:symbol_table/symbol_table.dart';
import 'package:test/test.dart';
main() {
FileSystem fileSystem;
late FileSystem fileSystem;
setUp(() {
fileSystem = new MemoryFileSystem();
fileSystem = MemoryFileSystem();
// a.jl
fileSystem.file('a.jl').writeAsStringSync('<b>a.jl</b>');
@ -51,13 +51,13 @@ main() {
test('blocks are replaced or kept', () async {
var file = fileSystem.file('c.jl');
var original = jael.parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: (e) => throw e);
var processed = await jael.resolve(
sourceUrl: file.uri, onError: (e) => throw e)!;
var processed = await (jael.resolve(
original, fileSystem.directory(fileSystem.currentDirectory),
onError: (e) => throw e);
var buf = new CodeBuffer();
var scope = new SymbolTable();
const jael.Renderer().render(processed, buf, scope);
onError: (e) => throw e));
var buf = CodeBuffer();
var scope = SymbolTable();
const jael.Renderer().render(processed!, buf, scope);
print(buf);
expect(
@ -76,13 +76,13 @@ main() {
test('block defaults are emitted', () async {
var file = fileSystem.file('b.jl');
var original = jael.parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: (e) => throw e);
var processed = await jael.resolve(
sourceUrl: file.uri, onError: (e) => throw e)!;
var processed = await (jael.resolve(
original, fileSystem.directory(fileSystem.currentDirectory),
onError: (e) => throw e);
var buf = new CodeBuffer();
var scope = new SymbolTable();
const jael.Renderer().render(processed, buf, scope);
onError: (e) => throw e));
var buf = CodeBuffer();
var scope = SymbolTable();
const jael.Renderer().render(processed!, buf, scope);
print(buf);
expect(
@ -104,13 +104,13 @@ main() {
() async {
var file = fileSystem.file('d.jl');
var original = jael.parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: (e) => throw e);
var processed = await jael.resolve(
sourceUrl: file.uri, onError: (e) => throw e)!;
var processed = await (jael.resolve(
original, fileSystem.directory(fileSystem.currentDirectory),
onError: (e) => throw e);
var buf = new CodeBuffer();
var scope = new SymbolTable();
const jael.Renderer().render(processed, buf, scope);
onError: (e) => throw e));
var buf = CodeBuffer();
var scope = SymbolTable();
const jael.Renderer().render(processed!, buf, scope);
print(buf);
expect(
@ -129,13 +129,13 @@ main() {
test('blocks within blocks', () async {
var file = fileSystem.file('foxtrot.jl');
var original = jael.parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: (e) => throw e);
var processed = await jael.resolve(
sourceUrl: file.uri, onError: (e) => throw e)!;
var processed = await (jael.resolve(
original, fileSystem.directory(fileSystem.currentDirectory),
onError: (e) => throw e);
var buf = new CodeBuffer();
var scope = new SymbolTable();
const jael.Renderer().render(processed, buf, scope);
onError: (e) => throw e));
var buf = CodeBuffer();
var scope = SymbolTable();
const jael.Renderer().render(processed!, buf, scope);
print(buf);
expect(

View file

@ -7,10 +7,10 @@ import 'package:symbol_table/symbol_table.dart';
import 'package:test/test.dart';
main() {
FileSystem fileSystem;
late FileSystem fileSystem;
setUp(() {
fileSystem = new MemoryFileSystem();
fileSystem = MemoryFileSystem();
// a.jl
fileSystem.file('a.jl').writeAsStringSync('<b>a.jl</b>');
@ -25,12 +25,12 @@ main() {
test('includes are expanded', () async {
var file = fileSystem.file('c.jl');
var original = jael.parseDocument(await file.readAsString(),
sourceUrl: file.uri, onError: (e) => throw e);
sourceUrl: file.uri, onError: (e) => throw e)!;
var processed = await jael.resolveIncludes(original,
fileSystem.directory(fileSystem.currentDirectory), (e) => throw e);
var buf = new CodeBuffer();
var scope = new SymbolTable();
const jael.Renderer().render(processed, buf, scope);
var buf = CodeBuffer();
var scope = SymbolTable();
const jael.Renderer().render(processed!, buf, scope);
print(buf);
expect(

View file

@ -41,5 +41,5 @@ dependencies:
path: packages/mock_request
web_socket_channel: ^2.0.0
dev_dependencies:
test: ^1.17.1
test: ^1.17.3