From 8caadbfa115113a5b190587319e43d5d23c09dfb Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Fri, 5 Aug 2022 12:46:00 +0300 Subject: [PATCH] reworked base rendering... Signed-off-by: Alex A. Naanou --- lib/path.js | 12 ++-- page.js | 158 +++++++++++++++++++++++++++++-------------------- parser.js | 2 +- pwiki2-test.js | 18 ++++-- pwiki2.js | 12 +--- store/base.js | 4 +- 6 files changed, 119 insertions(+), 87 deletions(-) diff --git a/lib/path.js b/lib/path.js index 25047a1..f5451f2 100755 --- a/lib/path.js +++ b/lib/path.js @@ -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 == ''){ diff --git a/page.js b/page.js index b272820..59e3017 100755 --- a/page.js +++ b/page.js @@ -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 + // -> 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([, ]) // .get(, [, ]) @@ -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() // @@ -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: '@source(.)' }, - text: '@include(..)' }, - _list: { - text: '@source(.)' }, + //text: '@source(.)' }, + text: '@include(.)' }, + _raw: { + text: '@quote(.)' }, RecursionError: { text: 'RECURSION ERROR: @quote(./path)' }, diff --git a/parser.js b/parser.js index 9523712..f934d6c 100755 --- a/parser.js +++ b/parser.js @@ -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){ diff --git a/pwiki2-test.js b/pwiki2-test.js index bfbdd4d..ed141db 100755 --- a/pwiki2-test.js +++ b/pwiki2-test.js @@ -81,11 +81,12 @@ pwiki.pwiki }) .update({ location: '/page', - text: 'PAGE\n' + text: '>>> PAGE\n' +'\n' +'@include(/test recursive="Recursion type 2 ()")\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... +'...unfiltered test text\n' +'\n' //+'locally filtered test text\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 ()")\n' +'\n' +'Including /page: @include(/page recursive="...")\n' +'\n' - +'Including /: \\@include(/)\n' + +'Sourceing /: \\@include(/)\n' + +'\n' + // XXX slots do not seem to work... + +'filled slot a\n' +'\n' +'@filter(test)', }) diff --git a/pwiki2.js b/pwiki2.js index 8b43f43..581b9aa 100755 --- a/pwiki2.js +++ b/pwiki2.js @@ -1,15 +1,9 @@ /********************************************************************** * * -* XXX the following are not the same: -* pwiki.get(/test).parse('@source('./path')') -* and -* pwiki.get('/test/*').parse('@source('./path')') -* 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... diff --git a/store/base.js b/store/base.js index 2307b07..1c6cd69 100755 --- a/store/base.js +++ b/store/base.js @@ -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 } } },