Pre-processor needs work
This commit is contained in:
parent
291563f93f
commit
2056d1eacc
11 changed files with 402 additions and 4 deletions
21
angel_jael/LICENSE
Normal file
21
angel_jael/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
6
jael.iml
6
jael.iml
|
@ -7,8 +7,12 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/jael/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael/build" />
|
||||
</content>
|
||||
<content url="file://$MODULE_DIR$/jael_preprocessor" />
|
||||
<content url="file://$MODULE_DIR$/jael_preprocessor">
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael_preprocessor/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/jael_preprocessor/build" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
21
jael/LICENSE
Normal file
21
jael/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -17,6 +17,7 @@ class TextNode extends ElementChild {
|
|||
|
||||
abstract class Element extends ElementChild {
|
||||
static const List<String> selfClosing = const [
|
||||
'include',
|
||||
'base',
|
||||
'basefont',
|
||||
'frame',
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
name: jael
|
||||
version: 1.0.0-alpha
|
||||
version: 1.0.0-alpha+1
|
||||
description: A simple server-side HTML templating engine for Dart.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage:
|
||||
homepage: https://github.com/angel-dart/jael/tree/master/jael
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
|
|
21
jael_preprocessor/LICENSE
Normal file
21
jael_preprocessor/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
201
jael_preprocessor/lib/jael_preprocessor.dart
Normal file
201
jael_preprocessor/lib/jael_preprocessor.dart
Normal file
|
@ -0,0 +1,201 @@
|
|||
import 'dart:async';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:jael/jael.dart';
|
||||
|
||||
/// Expands all `block[name]` tags within the template, replacing them with the correct content.
|
||||
Future<Document> resolve(Document document, Directory currentDirectory,
|
||||
{void onError(JaelError error)}) async {
|
||||
onError ?? (e) => throw e;
|
||||
|
||||
// Resolve all includes...
|
||||
var includesResolved =
|
||||
await resolveIncludes(document, currentDirectory, onError);
|
||||
|
||||
if (includesResolved.root.tagName.name != 'extend') return includesResolved;
|
||||
|
||||
var element = includesResolved.root;
|
||||
var attr = element.attributes
|
||||
.firstWhere((a) => a.name.name == 'src', orElse: () => null);
|
||||
if (attr == null) {
|
||||
onError(new JaelError(JaelErrorSeverity.warning,
|
||||
'Missing "src" attribute in "extend" tag.', element.tagName.span));
|
||||
return null;
|
||||
} else if (attr.value is! StringLiteral) {
|
||||
onError(new JaelError(
|
||||
JaelErrorSeverity.warning,
|
||||
'The "src" attribute in an "extend" tag must be a string literal.',
|
||||
element.tagName.span));
|
||||
return null;
|
||||
} else {
|
||||
var src = (attr.value as StringLiteral).value;
|
||||
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(
|
||||
doc, currentDirectory.fileSystem.directory(file.dirname),
|
||||
onError: onError);
|
||||
|
||||
Map<String, Element> blocks = {};
|
||||
var blockElements = element.children
|
||||
.where((e) => e is Element && e.tagName.name == 'block');
|
||||
|
||||
for (Element blockElement in blockElements) {
|
||||
var nameAttr = blockElement.attributes
|
||||
.firstWhere((a) => a.name.name == 'name', orElse: () => null);
|
||||
if (nameAttr == null) {
|
||||
onError(new JaelError(JaelErrorSeverity.warning,
|
||||
'Missing "name" attribute in "block" tag.', blockElement.span));
|
||||
} else if (nameAttr.value is! StringLiteral) {
|
||||
onError(new JaelError(
|
||||
JaelErrorSeverity.warning,
|
||||
'The "name" attribute in an "block" tag must be a string literal.',
|
||||
nameAttr.span));
|
||||
} else {
|
||||
var name = (nameAttr.value as StringLiteral).value;
|
||||
blocks[name] = blockElement;
|
||||
}
|
||||
}
|
||||
|
||||
var blocksExpanded =
|
||||
await _expandBlocks(processed.root, blocks, currentDirectory, onError);
|
||||
return new Document(document.doctype ?? processed.doctype, blocksExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Element> _expandBlocks(Element element, Map<String, Element> blocks,
|
||||
Directory currentDirectory, void onError(JaelError error)) async {
|
||||
if (element is SelfClosingElement)
|
||||
return element;
|
||||
else if (element is RegularElement) {
|
||||
if (element.children.isEmpty) return element;
|
||||
|
||||
List<ElementChild> expanded = [];
|
||||
|
||||
for (var child in element.children) {
|
||||
if (child is Element) {
|
||||
if (child is SelfClosingElement)
|
||||
expanded.add(child);
|
||||
else if (child is RegularElement) {
|
||||
if (child.tagName.name != 'block') {
|
||||
expanded.add(child);
|
||||
} else {
|
||||
var nameAttr =
|
||||
child.attributes.firstWhere((a) => a.name.name == 'name', orElse: () => null);
|
||||
if (nameAttr == null) {
|
||||
onError(new JaelError(
|
||||
JaelErrorSeverity.warning,
|
||||
'Missing "name" attribute in "block" tag.',
|
||||
child.span));
|
||||
} else if (nameAttr.value is! StringLiteral) {
|
||||
onError(new JaelError(
|
||||
JaelErrorSeverity.warning,
|
||||
'The "name" attribute in an "block" tag must be a string literal.',
|
||||
nameAttr.span));
|
||||
}
|
||||
|
||||
var name = (nameAttr.value as StringLiteral).value;
|
||||
Iterable<ElementChild> children;
|
||||
|
||||
if (!blocks.containsKey(name)) {
|
||||
children = child.children;
|
||||
} else {
|
||||
children = blocks[name].children;
|
||||
}
|
||||
|
||||
expanded.addAll(children);
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedError(
|
||||
'Unsupported element type: ${element.runtimeType}');
|
||||
}
|
||||
} else {
|
||||
expanded.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve all includes...
|
||||
expanded = await Future.wait(expanded.map((c) {
|
||||
if (c is! Element) return new Future.value(c);
|
||||
return _expandIncludes(c, currentDirectory, onError);
|
||||
}));
|
||||
|
||||
return new RegularElement(
|
||||
element.lt,
|
||||
element.tagName,
|
||||
element.attributes,
|
||||
element.gt,
|
||||
expanded,
|
||||
element.lt2,
|
||||
element.slash,
|
||||
element.tagName2,
|
||||
element.gt2);
|
||||
} else {
|
||||
throw new UnsupportedError(
|
||||
'Unsupported element type: ${element.runtimeType}');
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Element> _expandIncludes(Element element, Directory currentDirectory,
|
||||
void onError(JaelError error)) async {
|
||||
if (element.tagName.name != 'include') {
|
||||
if (element is SelfClosingElement)
|
||||
return element;
|
||||
else if (element is RegularElement) {
|
||||
List<ElementChild> expanded = [];
|
||||
|
||||
for (var child in element.children) {
|
||||
if (child is Element) {
|
||||
expanded.add(await _expandIncludes(child, currentDirectory, onError));
|
||||
} else {
|
||||
expanded.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
return new RegularElement(
|
||||
element.lt,
|
||||
element.tagName,
|
||||
element.attributes,
|
||||
element.gt,
|
||||
expanded,
|
||||
element.lt2,
|
||||
element.slash,
|
||||
element.tagName2,
|
||||
element.gt2);
|
||||
} else {
|
||||
throw new UnsupportedError(
|
||||
'Unsupported element type: ${element.runtimeType}');
|
||||
}
|
||||
}
|
||||
|
||||
var attr = element.attributes
|
||||
.firstWhere((a) => a.name.name == 'src', orElse: () => null);
|
||||
if (attr == null) {
|
||||
onError(new JaelError(JaelErrorSeverity.warning,
|
||||
'Missing "src" attribute in "include" tag.', element.tagName.span));
|
||||
return null;
|
||||
} else if (attr.value is! StringLiteral) {
|
||||
onError(new JaelError(
|
||||
JaelErrorSeverity.warning,
|
||||
'The "src" attribute in an "include" tag must be a string literal.',
|
||||
element.tagName.span));
|
||||
return null;
|
||||
} else {
|
||||
var src = (attr.value as StringLiteral).value;
|
||||
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(
|
||||
doc, currentDirectory.fileSystem.directory(file.dirname),
|
||||
onError: onError);
|
||||
return processed.root;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,10 @@
|
|||
name: jael_preprocessor
|
||||
version: 1.0.0-alpha
|
||||
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_processor
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
file: ^2.0.0
|
||||
jael: ^1.0.0-alpha
|
||||
|
|
74
jael_preprocessor/test/block_test.dart
Normal file
74
jael_preprocessor/test/block_test.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
import 'package:code_buffer/code_buffer.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:jael/jael.dart' as jael;
|
||||
import 'package:jael_preprocessor/jael_preprocessor.dart' as jael;
|
||||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
FileSystem fileSystem;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = new MemoryFileSystem();
|
||||
|
||||
// a.jl
|
||||
fileSystem.file('a.jl').writeAsStringSync('<b>a.jl</b>');
|
||||
|
||||
// b.jl
|
||||
fileSystem.file('b.jl').writeAsStringSync(
|
||||
'<i><include src="a.jl"><block name="greeting"><p>Hello</p></block></i>');
|
||||
|
||||
// c.jl
|
||||
fileSystem.file('c.jl').writeAsStringSync(
|
||||
'<extend src="b.jl"><block name="greeting">Goodbye</block></extend>');
|
||||
|
||||
// d.jl
|
||||
fileSystem.file('d.jl').writeAsStringSync(
|
||||
'<extend src="c.jl"><block name="greeting">Saluton!</block></extend>');
|
||||
});
|
||||
|
||||
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(
|
||||
original, fileSystem.directory(fileSystem.currentDirectory),
|
||||
onError: (e) => throw e);
|
||||
var buf = new CodeBuffer();
|
||||
var scope = new SymbolTable();
|
||||
const jael.Renderer().render(processed, buf, scope);
|
||||
print(buf);
|
||||
|
||||
expect(buf.toString(), '''
|
||||
<i>
|
||||
<b>
|
||||
a.jl
|
||||
</b>
|
||||
Goodbye
|
||||
</i>
|
||||
'''.trim());
|
||||
});
|
||||
|
||||
test('blocks can be overwritten', () 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(
|
||||
original, fileSystem.directory(fileSystem.currentDirectory),
|
||||
onError: (e) => throw e);
|
||||
var buf = new CodeBuffer();
|
||||
var scope = new SymbolTable();
|
||||
const jael.Renderer().render(processed, buf, scope);
|
||||
print(buf);
|
||||
|
||||
expect(buf.toString(), '''
|
||||
<i>
|
||||
<b>
|
||||
a.jl
|
||||
</b>
|
||||
Saluton!
|
||||
</i>
|
||||
'''.trim());
|
||||
});
|
||||
}
|
49
jael_preprocessor/test/include_test.dart
Normal file
49
jael_preprocessor/test/include_test.dart
Normal file
|
@ -0,0 +1,49 @@
|
|||
import 'package:code_buffer/code_buffer.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:jael/jael.dart' as jael;
|
||||
import 'package:jael_preprocessor/jael_preprocessor.dart' as jael;
|
||||
import 'package:symbol_table/symbol_table.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
FileSystem fileSystem;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = new MemoryFileSystem();
|
||||
|
||||
// a.jl
|
||||
fileSystem.file('a.jl').writeAsStringSync('<b>a.jl</b>');
|
||||
|
||||
// b.jl
|
||||
fileSystem.file('b.jl').writeAsStringSync('<i><include src="a.jl"></i>');
|
||||
|
||||
// c.jl
|
||||
fileSystem.file('c.jl').writeAsStringSync('<u><include src="b.jl"></u>');
|
||||
});
|
||||
|
||||
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);
|
||||
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);
|
||||
print(buf);
|
||||
|
||||
expect(
|
||||
buf.toString(),
|
||||
'''
|
||||
<u>
|
||||
<i>
|
||||
<b>
|
||||
a.jl
|
||||
</b>
|
||||
</i>
|
||||
</u>
|
||||
'''
|
||||
.trim());
|
||||
});
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
cd jael && pub get && pub run test
|
||||
cd ../angel_jael && pub get && pub run test
|
||||
cd ../jael_preprocessor/ && pub get && pub run test
|
||||
|
|
Loading…
Reference in a new issue