reworked base rendering...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-08-05 12:46:00 +03:00
parent 8c24ba36bd
commit 8caadbfa11
6 changed files with 119 additions and 87 deletions

View File

@ -126,10 +126,14 @@ module = {
// search for .ALTERNATIVE_PAGES...
// XXX should we search for each path element or just the last one (current)???
// XXX should we keep the trailing '/'???
paths: function*(path='/', seen){
var alt_pages = !seen
seen = seen
?? new Set()
paths: function*(path='/', strict=false){
if(path === true || path === false){
strict = path
path = '/' }
var alt_pages = !strict
var seen = strict instanceof Set ?
strict
: new Set()
path = this.normalize(path, 'string')
// special case: root...
if(path == '/' || path == ''){

158
page.js
View File

@ -255,13 +255,39 @@ object.Constructor('BasePage', {
// relative proxies to store...
exists: relProxy('exists'),
find: relProxy('find'),
match: relMatchProxy('match'),
resolve: relMatchProxy('resolve'),
delete: function(path='.'){
this.__delete__()
return this },
//
// Find current path (non-strict)
// .find()
// .find(false)
// .find('.')
// .find('.', false)
// -> path
// -> undefined
//
// Find current path in strict/non-strict mode...
// .find(true)
// .find(false)
// -> path
// -> undefined
//
// Find path relative to current page (strict/non-strict)
// .find(<path>[, <strict>])
// -> path
// -> undefined
//
find: function(path='.', strict=false){
if(path === true || path === false){
strict = path
path = '.' }
return this.store.find(
pwpath.relative(this.location, path), strict) },
//
// .get(<path>[, <data>])
// .get(<path>, <strict>[, <data>])
@ -280,31 +306,30 @@ object.Constructor('BasePage', {
}) },
// XXX should this be an iterator???
each: function(path){
each: async function*(path){
var that = this
// NOTE: we are trying to avoid resolving non-pattern paths unless
// we really have to...
path = path ?
pwpath.relative(this.path, path)
: this.path
//var paths = this.match(path)
var paths = path.includes('*') ?
this.resolve(path)
: path
paths = paths instanceof Array ?
paths
: paths instanceof Promise ?
paths.iter()
await paths
: [paths]
return paths
.map(function(path){
return that.get('/'+ path) }) },
map: function(func){
for(var path of paths){
yield that.get('/'+ path) } },
map: async function(func){
return this.each().map(func) },
filter: function(func){
filter: async function(func){
return this.each().filter(func) },
reduce: function(func, dfl){
reduce: async function(func, dfl){
return this.each().reduce(func, dfl) },
// sorting...
@ -316,10 +341,11 @@ object.Constructor('BasePage', {
if(this.length <= 1){
return this }
// sort...
this.metadata = { order: await this.each()
.sort(...arguments)
.map(function(p){
return p.path }) }
this.metadata =
{ order: await this.each()
.sort(...arguments)
.map(function(p){
return p.path }) }
return this },
reverse: async function(){
// not sorting single pages...
@ -645,36 +671,46 @@ object.Constructor('Page', BasePage, {
return }
// parse arg values...
src = await this.parse(src, state)
var full = this.get(src).path
var base = this.get(src).path
handler = handler
?? function(src){
?? async function(src){
return this.get(src)
.parse(
isolated ?
{seen: (state.seen ?? []).slice()}
: state) }
// handle recursion...
var parent_seen = 'seen' in state
var seen = state.seen =
(state.seen
?? []).slice()
// recursion detected...
if(seen.includes(full) || full == this.path){
if(!recursive){
return this.parse(this.get('./'+this.RECURSIVE_TPL).raw) }
// have the 'recursive' arg...
return this.parse(recursive, state) }
seen.push(full)
// XXX this is really odd -- works OK for multiple pages
// and res turns into a string '[object Promise]'
// for a non-pattern page...
return this.get(src)
.each()
.map(async function(page){
var full = page.path
// load the included page...
var res = await handler.call(this, src)
// handle recursion...
var parent_seen = 'seen' in state
var seen = state.seen =
(state.seen
?? []).slice()
// recursion detected...
//if(seen.includes(full) || full == base){
if(seen.includes(full)){
if(!recursive){
return page.parse(page.get('./'+page.RECURSION_ERROR).raw) }
// have the 'recursive' arg...
return page.parse(recursive, state) }
seen.push(full)
if(!parent_seen){
delete state.seen }
// load the included page...
var res = await handler.call(page, full)
return res }),
if(!parent_seen){
delete state.seen }
return res })
.join('\n') }),
// NOTE: the main difference between this and @include is that
// this renders the src in the context of current page while
// include is rendered in the context of its page but with
@ -686,7 +722,7 @@ object.Constructor('Page', BasePage, {
'source',
args, body, state, 'sources',
async function(src){
return await this.parse(await this.get(src).raw +'', state) }) }),
return this.parse(await this.get(src).raw, state) }) }),
//
// @quote(<src>)
//
@ -902,9 +938,6 @@ object.Constructor('Page', BasePage, {
if(src){
src = await this.parse(src, state)
/* XXX ARRAY page...
var pages = this.get(src, strict).each()
/*/
var pages = this.get(src, strict)
pages = await pages.isArray ?
// XXX should we wrap this in pages...
@ -912,7 +945,6 @@ object.Constructor('Page', BasePage, {
.map(function(data){
return that.virtual({text: data}) })
: await pages.each()
//*/
// no matching pages -> get the else block...
if(pages.length == 0
&& (text || args['else'])){
@ -971,15 +1003,7 @@ object.Constructor('Page', BasePage, {
state = text
text = null }
state = state ?? {}
text = text
?? await this.each()
return text instanceof Array ?
Promise.iter(text)
.map(function(text){
return text instanceof Page ?
that.__parser__.parse(text, null, state)
: that.__parser__.parse(that, text, state) })
: this.__parser__.parse(this, text, state) },
return this.__parser__.parse(this, text, state) },
// true if page has an array value but is not a pattern page...
//
@ -1031,24 +1055,31 @@ object.Constructor('Page', BasePage, {
//
// NOTE: this uses .PAGE_TPL to render the page.
// NOTE: writing to .raw is the same as writing to .text...
//* XXX not sure if this is a good strategy...
get text(){ return (async function(){
var path = pwpath.split(this.path)
path.at(-1)[0] == '_'
|| path.push(this.PAGE_TPL)
var tpl = pwpath.join(path)
var tpl_name = path.pop()
path = pwpath.join(path)
// get the template relative to the top most pattern...
tpl = await this.get(tpl).find(true)
if(!tpl){
throw new Error('UNKNOWN RENDER TEMPLATE: '+ tpl_name) }
// render template in context of page...
return this.get(path)
.parse(await this.get(tpl).raw) }).call(this) },
/*/
var path = pwpath.split(this.path)
return [path.at(-1)[0] == '_' ?
await this.parse()
: await this.get('./'+ this.PAGE_TPL).parse()]
.flat()
.join('\n') }).call(this) },
/*/
get text(){ return (async function(){
var tpl = '/'+ await this.find('./'+ this.PAGE_TPL)
return [await this.parse(
tpl.endsWith(this.PAGE_TPL.split(/[\\\/]/).pop()) ?
[await this.get(tpl).raw]
: [] )]
.flat()
.join('\n') }).call(this) },
//*/
//*/
set text(value){
this.__update__({text: value}) },
//this.onTextUpdate(value) },
@ -1064,13 +1095,10 @@ module.System = {
// base templates...
//
_text: {
// XXX join does not seem to do anything...
// ...this might be applied to each page rather than to a
// set of pages...
//text: '<macro src=".." join="\n">@source(.)</macro>' },
text: '@include(..)' },
_list: {
text: '<macro src=".." join="\n---\n">@source(.)</macro>' },
//text: '<macro src="." join="\n">@source(.)</macro>' },
text: '@include(.)' },
_raw: {
text: '@quote(.)' },
RecursionError: {
text: 'RECURSION ERROR: @quote(./path)' },

View File

@ -444,13 +444,13 @@ module.BaseParser = {
// XXX add a special filter to clear pending filters... (???)
parse: async function(page, ast, state={}){
var that = this
// XXX should we handle strings as input???
ast = ast
?? this.expand(page, null, state)
ast = typeof(ast) == 'string' ?
this.expand(page, ast, state)
: ast
return ast
// post handlers...
.map(function(section){

View File

@ -81,11 +81,12 @@ pwiki.pwiki
})
.update({
location: '/page',
text: 'PAGE\n'
text: '>>> PAGE\n'
+'\n'
+'@include(/test recursive="Recursion type 2 (<now/>)")\n'
+'\n'
+'@slot(name=b text="filled slot")\n',
+'@slot(name=b text="filled slot")\n'
+ '<<< PAGE\n',
})
.update({
location: '/other',
@ -97,22 +98,27 @@ pwiki.pwiki
+'\n'
+'globally filtered test text...\n'
+'\n'
// XXX this does not seem to work...
+'<filter -test>...unfiltered test text</filter>\n'
+'\n'
//+'<filter test>locally filtered test text</filter>\n'
+'\n'
+'@slot(name=a text="non-filled slot")\n'
// XXX slots do not seem to work...
+'@slot(name=a text="non-filled slot a")\n'
+'\n'
+'@slot(name=b text="non-filled slot")\n'
+'@slot(name=b text="non-filled slot b")\n'
+'\n'
+'Including /other #1: @include(/other)\n'
//+'Including /other #2: @include(/other)\n'
+'Including /other #2: @include(/other)\n'
+'\n'
+'Including /test: @include(/test recursive="Recursion type 1 (<now/>)")\n'
+'\n'
+'Including /page: @include(/page recursive="...")\n'
+'\n'
+'Including /: \\@include(/)\n'
+'Sourceing /: \\@include(/)\n'
+'\n'
// XXX slots do not seem to work...
+'<slot name="a">filled slot a</slot>\n'
+'\n'
+'@filter(test)',
})

View File

@ -1,15 +1,9 @@
/**********************************************************************
*
*
* XXX the following are not the same:
* pwiki.get(/test).parse('<macro src="*" join="\n">@source('./path')</macro>')
* and
* pwiki.get('/test/*').parse('<macro src="." join="\n">@source('./path')</macro>')
* the former is called once and iterates while the later is called
* per-page...
* ...should be the same -- i.e. the macro should decide...
* page range -> include -> joun
* page range -> macro -> iterate
* XXX revise macros...
* - filters behave in an odd way...
* - slots do not seem to work...
* XXX wikiword filter seems to hang on /
* XXX do filters affect the whole page or only what comes after???
* XXX need to be able to affect the default render wrpaper...

View File

@ -107,8 +107,8 @@ module.BaseStore = {
// normalize the output...
|| false },
// find the closest existing alternative path...
find: async function(path){
for(var p of await pwpath.paths(path)){
find: async function(path, strict=false){
for(var p of await pwpath.paths(path, !!strict)){
p = await this.exists(p)
if(p){
return p } } },