Compare commits

..

2 Commits

Author SHA1 Message Date
e4dc2d0471 rewriting @include(..)...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-06-29 16:21:10 +03:00
a4453e18a4 preparing for deeper testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-06-28 20:38:28 +03:00
4 changed files with 138 additions and 38 deletions

7
v3/package-lock.json generated
View File

@ -13,6 +13,7 @@
"ig-actions": "*", "ig-actions": "*",
"ig-features": "*", "ig-features": "*",
"ig-object": "*", "ig-object": "*",
"ig-serialize": "*",
"ig-types": "*", "ig-types": "*",
"requirejs": "*", "requirejs": "*",
"uuid": "*" "uuid": "*"
@ -141,6 +142,12 @@
"ig-stoppable": "^2.0.0" "ig-stoppable": "^2.0.0"
} }
}, },
"node_modules/ig-serialize": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ig-serialize/-/ig-serialize-1.3.4.tgz",
"integrity": "sha512-aIrZ7ljyXVRUsyvcWfF2JSz/4hWtDrJcTpHJ2y4QD48YXF/0eIwwzDbB6dCmoK2EO5fHfxa82WrVBncb+kDqvg==",
"license": "BSD-3-Clause"
},
"node_modules/ig-stoppable": { "node_modules/ig-stoppable": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.4.tgz", "resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.4.tgz",

View File

@ -11,7 +11,8 @@
"ig-actions": "*", "ig-actions": "*",
"ig-features": "*", "ig-features": "*",
"ig-object": "*", "ig-object": "*",
"ig-types": "*" "ig-types": "*",
"ig-serialize": "*"
}, },
"devDependencies": { "devDependencies": {
"ig-test": "*" "ig-test": "*"

View File

@ -1280,14 +1280,13 @@ module.parser = {
// all other slots with <name> will replace its content, unless // all other slots with <name> will replace its content, unless
// explicit shown/hidden arguments are given. // explicit shown/hidden arguments are given.
// NOTE: hidden has precedence over shown if both are given. // NOTE: hidden has precedence over shown if both are given.
// NOTE: slots are handled in order of occurrence of opening tags // XXX revise...
// in text and not by hierarchy, i.e. the later slot overrides
// the former and the most nested overrides the parent.
// This also works for cases where slots override slots they
// are contained in, this will not lead to recursion.
// //
// XXX revise the use of hidden/shown use mechanic and if it's // XXX revise the use of hidden/shown use mechanic and if it's
// needed... // needed...
// XXX do we need to guard from recursion???
// ...do not think it's possible -- everything is resolved
// once and just inseerted as-is (revise)
slot: Macro( slot: Macro(
['name', 'text', ['shown', 'hidden']], ['name', 'text', ['shown', 'hidden']],
function(parser, args, body, state){ function(parser, args, body, state){
@ -1352,13 +1351,79 @@ module.parser = {
// At the moment nested recursion is checked in a fast but // At the moment nested recursion is checked in a fast but
// not 100% correct manner focusing on path depth and ignoring // not 100% correct manner focusing on path depth and ignoring
// the context, this potentially can lead to false positives. // the context, this potentially can lead to false positives.
//
// XXX need a way to make encode option transparent... // XXX need a way to make encode option transparent...
// XXX need a way to wrap the included page...
// - template page...
// - prefix/sufix...
// XXX store a page cache in state... // XXX store a page cache in state...
// XXX UPDATE... // XXX UPDATE...
include2: Macro(
['src', 'recursive', 'join',
['s', 'strict', 'isolated']],
// XXX thinking that reimplementing this is a bit less boring than
// refactoring, and should be cleaner...
// XXX if the src is empty return nothing...
// XXX this needs to be reusable...
// XXX need a wrapper protocol -- is this the level for it???
// XXX page API used:
// .basepath
// .resolvePathVars(path)
// .get(path)
// is this a promise/value, iterable promise a generator
// an async generator, ... or a combination/stack of the above???
function(parser, args, body, state, key='included', handler){
var base = this.basepath
// XXX
//var src = parser.parse(this.resolvePathVars(args.src))
var src = args.src
return Promise.awaitOrRun(
parser.parse(this, src, state),
function(src){
var cache = state.cache ??= {}
if(cache[src]){
return cache[src] }
// XXX should this be a tree??
// ...need to at least split direct and
// indirect dependencies...
var depends = ((state.depends ??= {})[base] ??= {})
handler ??=
function(parser, page, state){
// XXX get page text
// XXX setup state
return parser.parse(this, page, state) }
var pageHandler =
function(page){
// XXX cache...
return handler.call(this, parser, page, state) }
// XXX can we do:
// return this.get(...)
// .iter()
// .map(...)
// .sync()
return Promise.awaitOrRun(
this.get(src),
function(pages){
return Promise.awaitOrRun(
// handle pages...
Promise
.iter(
pages
.map(pageHandler))
.sync(),
// cache the final result...
function(pages){
return (cache[src] = pages) }) }) }) }),
include: Macro( include: Macro(
['src', 'recursive', 'join', ['src', 'recursive', 'join',
['s', 'strict', 'isolated']], ['s', 'strict', 'isolated']],
async function*(args, body, state, key='included', handler){ async function*(parser, args, body, state, key='included', handler){
var macro = 'include' var macro = 'include'
if(typeof(args) == 'string'){ if(typeof(args) == 'string'){
var [macro, args, body, state, key, handler] = arguments var [macro, args, body, state, key, handler] = arguments

View File

@ -2,6 +2,7 @@
//--------------------------------------------------------------------- //---------------------------------------------------------------------
var test = require('ig-test') var test = require('ig-test')
var serialize = require('ig-serialize')
var parser = require('../parser').parser var parser = require('../parser').parser
@ -10,10 +11,11 @@ var parser = require('../parser').parser
test.Setups({ test.Setups({
empty: function(assert){ empty: function(assert){
return [ '', '' ] }, return { code: [ '', '' ] }},
// slot...
slot_empty: function(assert){ slot_empty: function(assert){
return [ return {code: [
'@slot(slot)', '@slot(slot)',
'@slot("slot")', '@slot("slot")',
'@slot(\'slot\')', '@slot(\'slot\')',
@ -26,95 +28,120 @@ test.Setups({
'<slot name=slot/>', '<slot name=slot/>',
'<slot name="slot"/>', '<slot name="slot"/>',
'<slot name=\'slot\'/>', '<slot name=\'slot\'/>',
'' ] }, '' ]} },
slot_value: function(assert){ slot_value: function(assert){
return [ return {code: [
'<slot slot value/>', '<slot slot value/>',
'<slot slot text=value/>', '<slot slot text=value/>',
'@slot(slot value)', '@slot(slot value)',
'@slot(slot text=value)', '@slot(slot text=value)',
'value' ] }, 'value' ]} },
slot_fill: function(assert){ slot_fill: function(assert){
var ins = this.slot_value(assert).slice(0, -1) var ins = this.slot_value(assert).code.slice(0, -1)
return [ return {code: [
...ins.map(function(e){ ...ins.map(function(e){
return e + '@slot(slot other)' }), return e + '@slot(slot other)' }),
...ins.map(function(e){ ...ins.map(function(e){
return e + '<slot slot other/>' }), return e + '<slot slot other/>' }),
...ins.map(function(e){ ...ins.map(function(e){
return e + '<slot slot>other</slot>' }), return e + '<slot slot>other</slot>' }),
'other' ] }, 'other' ]} },
slot_fill_fill: function(assert){ slot_fill_fill: function(assert){
var ins = this.slot_fill(assert).slice(0, -1) var ins = this.slot_fill(assert).code.slice(0, -1)
return [ return {code: [
...ins.map(function(e){ ...ins.map(function(e){
return e + '@slot(slot third)' }), return e + '@slot(slot third)' }),
...ins.map(function(e){ ...ins.map(function(e){
return e + '<slot slot third/>' }), return e + '<slot slot third/>' }),
...ins.map(function(e){ ...ins.map(function(e){
return e + '<slot slot>third</slot>' }), return e + '<slot slot>third</slot>' }),
'third' ] }, 'third' ]} },
/* XXX not sure how...
slot_recursion: function(assert){
return {code: [
'<slot slot> <slot slot.inner/> </slot> <slot slot.inner "@slot"/>',
'', ]} },
//*/
// XXX these are an alternative to <content/>... // XXX these are an alternative to <content/>...
slot_nested: function(assert){ slot_nested: function(assert){
return [ return {code: [
'<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot.content value)', '<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot.content value)',
'[[ value ]]' ] }, '[[ value ]]' ]} },
slot_nested_overwrite: function(assert){ slot_nested_overwrite: function(assert){
return [ return {code: [
'<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot value)', '<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot value)',
'value' ] }, 'value' ]} },
/* XXX SHOWN_HIDDEN /* XXX SHOWN_HIDDEN
// XXX these need to be revised... // XXX these need to be revised...
// ...do we actually need hidden/shown??? // ...do we actually need hidden/shown???
// //
slot_shown: function(assert){ slot_shown: function(assert){
var ins = this.slot_value(assert) var ins = this.slot_value(assert).code
var expect = ins.pop() var expect = ins.pop()
return [ return {code: [
...ins.map(function(i){ ...ins.map(function(i){
return i +' @slot(slot that shown)' }), return i +' @slot(slot that shown)' }),
'that that' ] }, 'that that' ]} },
slot_hidden: function(assert){ slot_hidden: function(assert){
return [ return {code: [
'<slot slot value hidden/>', '<slot slot value hidden/>',
'@slot(slot value hidden)', '@slot(slot value hidden)',
'' ] }, '' ]} },
slot_hidden_value: function(assert){ slot_hidden_value: function(assert){
var ins = this.slot_hidden(assert) var ins = this.slot_hidden(assert).code
var expect = ins.pop() var expect = ins.pop()
return [ return {code: [
...ins.map(function(i){ ...ins.map(function(i){
return i +'@slot(slot other)' }), return i +'@slot(slot other)' }),
'' ] }, '' ]} },
slot_hidden_shown: function(assert){ slot_hidden_shown: function(assert){
var ins = this.slot_hidden(assert) var ins = this.slot_hidden(assert).code
var expect = ins.pop() var expect = ins.pop()
return [ return {code: [
...ins.map(function(i){ ...ins.map(function(i){
return i +'@slot(slot shown)' }), return i +'@slot(slot shown)' }),
'' ] }, '' ]} },
//*/ //*/
// XXX var...
// XXX
// XXX arg...
// XXX
}) })
test.Modifiers({ test.Modifiers({
args: function(assert, state){
;(state.state = (state.state ?? {})).args = {
number: 1,
string: 'string',
}
return state },
}) })
test.Tests({ test.Tests({
parse: function(assert, state){ parse: function(assert, state){
var {page, code, st} = state
page ??= {}
st ??= {}
var res var res
var inputs = state.slice(0, -1) var inputs = code.slice(0, -1)
var expect = state.at(-1) var expect = code.at(-1)
var i = 0 var i = 0
for(var input of inputs){ for(var input of inputs){
var p = serialize.partialDeepCopy(page)
var s = serialize.partialDeepCopy(st)
assert( assert(
(res = parser.parse( (res = parser.parse(
{}, p,
input, input,
{})) s))
=== expect, === expect,
'Parsing:', 'Parsing:',
'\n\t in: "'+ input +'"', '\n\t in: "'+ input +'"',