@webqit/oohtml 4.1.6 → 4.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-binding.js +11 -6
- package/dist/data-binding.js.map +3 -3
- package/dist/html-imports.js +1 -1
- package/dist/html-imports.js.map +2 -2
- package/dist/main.js +22 -17
- package/dist/main.js.map +3 -3
- package/dist/main.lite.js +26 -21
- package/dist/main.lite.js.map +3 -3
- package/dist/scoped-js.js +1 -1
- package/dist/scoped-js.js.map +2 -2
- package/package.json +1 -1
- package/src/data-binding/index.js +36 -17
- package/src/html-imports/HTMLModule.js +12 -6
- package/src/html-imports/_HTMLImportElement.js +1 -1
- package/src/init.js +5 -5
- package/src/scoped-js/index.js +32 -18
- package/test/index.js +1 -1
- package/test/scoped-js.test.js +3 -2
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
+
import { xpathQuery } from '@webqit/realdom/src/realtime/Util.js';
|
|
5
6
|
import { _, _init, _splitOuter } from '../util.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -38,12 +39,12 @@ function realtime( config ) {
|
|
|
38
39
|
const window = this, { webqit: { realdom } } = window;
|
|
39
40
|
// ----------------
|
|
40
41
|
realdom.realtime( window.document ).query( `(${ config.discreteBindingsSelector })`, record => {
|
|
41
|
-
cleanup.call( this, ...record.exits );
|
|
42
|
-
mountDiscreteBindings.call(
|
|
42
|
+
cleanup.call( this, ...record.exits );
|
|
43
|
+
mountDiscreteBindings.call( window, config, ...record.entrants );
|
|
43
44
|
}, { live: true, subtree: 'cross-roots', timing: 'sync' } );
|
|
44
45
|
realdom.realtime( window.document ).query( config.attrSelector, record => {
|
|
45
|
-
cleanup.call( this, ...record.exits );
|
|
46
|
-
mountInlineBindings.call(
|
|
46
|
+
cleanup.call( this, ...record.exits );
|
|
47
|
+
mountInlineBindings.call( window, config, ...record.entrants );
|
|
47
48
|
}, { live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true } );
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -97,21 +98,21 @@ function cleanup( ...entries ) {
|
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
function patternMatch( config, str ) {
|
|
102
|
+
const tagStart = config.tokens.tagStart.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
103
|
+
const tagEnd = config.tokens.tagEnd.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
104
|
+
const stateStart = config.tokens.stateStart.split( '' ).map( x => x === ' ' ? `(?:\\s+)?` : `\\${ x }` ).join( '' );
|
|
105
|
+
const stateEnd = config.tokens.stateEnd.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
106
|
+
const pattern = `^${ tagStart }(.*?)(?:${ stateStart }(\\d+)${ stateEnd }(?:\\s+)?)?${ tagEnd }$`;
|
|
107
|
+
const [ /*raw*/, expr, span ] = str.match( new RegExp( pattern ) );
|
|
108
|
+
return { raw: str, expr, span: parseInt( span ?? 0 ) };
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
async function mountDiscreteBindings( config, ...entries ) {
|
|
101
112
|
const window = this;
|
|
102
|
-
const patternMatch = str => {
|
|
103
|
-
const tagStart = config.tokens.tagStart.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
104
|
-
const tagEnd = config.tokens.tagEnd.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
105
|
-
const stateStart = config.tokens.stateStart.split( '' ).map( x => x === ' ' ? `(?:\\s+)?` : `\\${ x }` ).join( '' );
|
|
106
|
-
const stateEnd = config.tokens.stateEnd.split( '' ).map( x => `\\${ x }` ).join( '' );
|
|
107
|
-
const pattern = `^${ tagStart }(.*?)(?:${ stateStart }(\\d+)${ stateEnd }(?:\\s+)?)?${ tagEnd }$`;
|
|
108
|
-
const [ /*raw*/, expr, span ] = str.match( new RegExp( pattern ) );
|
|
109
|
-
return { raw: str, expr, span: parseInt( span ?? 0 ) };
|
|
110
|
-
};
|
|
111
|
-
|
|
112
113
|
const instances = entries.reduce( ( instances, node ) => {
|
|
113
114
|
if ( node.isBound ) return instances;
|
|
114
|
-
const template = patternMatch( node.nodeValue );
|
|
115
|
+
const template = patternMatch( config, node.nodeValue );
|
|
115
116
|
let textNode = node;
|
|
116
117
|
if ( template.span ) {
|
|
117
118
|
textNode = node.nextSibling;
|
|
@@ -131,7 +132,7 @@ async function mountDiscreteBindings( config, ...entries ) {
|
|
|
131
132
|
}, [] );
|
|
132
133
|
|
|
133
134
|
for ( const { textNode, template, anchorNode } of instances ) {
|
|
134
|
-
const compiled = compileDiscreteBindings( config, template.expr );
|
|
135
|
+
const compiled = compileDiscreteBindings.call( window, config, template.expr );
|
|
135
136
|
const { scope, bindings } = createDynamicScope.call( this, config, textNode.parentNode );
|
|
136
137
|
Object.defineProperty( textNode, '$oohtml_internal_databinding_anchorNode', { value: anchorNode, configurable: true } );
|
|
137
138
|
try {
|
|
@@ -156,7 +157,7 @@ function compileDiscreteBindings( config, str ) {
|
|
|
156
157
|
|
|
157
158
|
async function mountInlineBindings( config, ...entries ) {
|
|
158
159
|
for ( const node of entries ) {
|
|
159
|
-
const compiled = compileInlineBindings( config, node.getAttribute( config.attr.render ) );
|
|
160
|
+
const compiled = compileInlineBindings.call( this, config, node.getAttribute( config.attr.render ) );
|
|
160
161
|
const { scope, bindings } = createDynamicScope.call( this, config, node );
|
|
161
162
|
const signals = [];
|
|
162
163
|
Object.defineProperty( node, '$oohtml_internal_databinding_signals', { value: signals, configurable: true } );
|
|
@@ -234,6 +235,13 @@ function compileInlineBindings( config, str ) {
|
|
|
234
235
|
$existing__.clear();
|
|
235
236
|
}`;
|
|
236
237
|
}
|
|
238
|
+
// Treat other "@" directives as events
|
|
239
|
+
return `
|
|
240
|
+
const handler = () => ${ arg };
|
|
241
|
+
this.addEventListener( '${ param }', handler );
|
|
242
|
+
const abort = () => this.removeEventListener( '${ param }', handler );
|
|
243
|
+
this.$oohtml_internal_databinding_signals?.push( { abort } );
|
|
244
|
+
`;
|
|
237
245
|
}
|
|
238
246
|
if ( str.trim() ) throw new Error( `Invalid binding: ${ str }.` );
|
|
239
247
|
} ).join( `\n` );
|
|
@@ -244,3 +252,14 @@ function compileInlineBindings( config, str ) {
|
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
const escDouble = str => str.replace(/"/g, '\\"');
|
|
255
|
+
|
|
256
|
+
export function idleCompiler( node ) {
|
|
257
|
+
const window = this, { webqit: { oohtml: { configs: { DATA_BINDING: config } } } } = window;
|
|
258
|
+
xpathQuery( window, node, `(${ config.discreteBindingsSelector })` ).forEach( node => {
|
|
259
|
+
const template = patternMatch( config, node.nodeValue );
|
|
260
|
+
compileDiscreteBindings.call( window, config, template.expr );
|
|
261
|
+
} );
|
|
262
|
+
( node?.matches( config.attrSelector ) ? [ node ] : [] ).concat([ ...( node?.querySelectorAll( config.attrSelector ) || [] ) ]).forEach( node => {
|
|
263
|
+
compileInlineBindings.call( window, config, node.getAttribute( config.attr.render ) );
|
|
264
|
+
} );
|
|
265
|
+
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import { _isNumeric } from '@webqit/util/js/index.js';
|
|
6
5
|
import { getDefs } from './index.js';
|
|
7
6
|
import { _, env } from '../util.js';
|
|
8
7
|
|
|
@@ -22,6 +21,7 @@ export default class HTMLModule {
|
|
|
22
21
|
const { window } = env, { webqit: { realdom, oohtml: { configs } } } = window;
|
|
23
22
|
_( host ).get( `defsmanager::instance` )?.dispose();
|
|
24
23
|
_( host ).set( `defsmanager::instance`, this );
|
|
24
|
+
this.window = window;
|
|
25
25
|
this.host = host;
|
|
26
26
|
this.config = configs.HTML_IMPORTS;
|
|
27
27
|
this.parent = parent;
|
|
@@ -31,8 +31,8 @@ export default class HTMLModule {
|
|
|
31
31
|
this.validateDefId( this.defId );
|
|
32
32
|
// ----------
|
|
33
33
|
this.realtimeA = realdom.realtime( this.host.content ).children( record => {
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
34
|
+
this.expose( record.entrants, true );
|
|
35
|
+
this.expose( record.exits, false );
|
|
36
36
|
}, { live: true, timing: 'sync' } );
|
|
37
37
|
// ----------
|
|
38
38
|
this.realtimeB = realdom.realtime( this.host ).attr( [ 'src', 'loading' ], ( ...args ) => this.evaluateLoading( ...args ), {
|
|
@@ -67,7 +67,7 @@ export default class HTMLModule {
|
|
|
67
67
|
*
|
|
68
68
|
* @returns Void
|
|
69
69
|
*/
|
|
70
|
-
|
|
70
|
+
expose( entries, isConnected ) {
|
|
71
71
|
const { window } = env, { webqit: { Observer } } = window;
|
|
72
72
|
let dirty, allFragments = this.defs[ '#' ] || [];
|
|
73
73
|
Observer.batch( this.defs, () => {
|
|
@@ -76,10 +76,16 @@ export default class HTMLModule {
|
|
|
76
76
|
const isTemplate = entry.matches( this.config.templateSelector );
|
|
77
77
|
const defId = ( entry.getAttribute( isTemplate ? this.config.attr.def : this.config.attr.fragmentdef ) || '' ).trim();
|
|
78
78
|
if ( isConnected ) {
|
|
79
|
-
if ( isTemplate && defId ) {
|
|
80
|
-
|
|
79
|
+
if ( isTemplate && defId ) {
|
|
80
|
+
new HTMLModule( entry, this.host, this.level + 1 );
|
|
81
|
+
} else {
|
|
81
82
|
allFragments.push( entry );
|
|
82
83
|
dirty = true;
|
|
84
|
+
if ( typeof requestIdleCallback === 'function' ) {
|
|
85
|
+
requestIdleCallback( () => {
|
|
86
|
+
this.config.idleCompilers?.forEach( callback => callback.call( this.window, entry ) );
|
|
87
|
+
} );
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
if ( defId ) {
|
|
85
91
|
this.validateDefId( defId );
|
|
@@ -126,7 +126,7 @@ export default function() {
|
|
|
126
126
|
priv.disconnectedCallback = () => {
|
|
127
127
|
priv.hydrationImportRequest?.abort();
|
|
128
128
|
priv.hydrationImportRequest = null;
|
|
129
|
-
if ( priv.anchorNode
|
|
129
|
+
if ( priv.anchorNode?.isConnected ) return;
|
|
130
130
|
priv.moduleRefRealtime?.disconnect();
|
|
131
131
|
priv.moduleRefRealtime = null;
|
|
132
132
|
};
|
package/src/init.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import NamespacedHTML from './namespaced-html/index.js';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import ContextAPI from './context-api/index.js';
|
|
6
|
+
import ScopedJS, { idleCompiler as idleCompiler1 } from './scoped-js/index.js';
|
|
7
|
+
import DataBinding, { idleCompiler as idleCompiler2 } from './data-binding/index.js';
|
|
9
8
|
import BindingsAPI from './bindings-api/index.js';
|
|
10
9
|
import HTMLImports from './html-imports/index.js';
|
|
11
|
-
import
|
|
10
|
+
import ContextAPI from './context-api/index.js';
|
|
11
|
+
import ScopedCSS from './scoped-css/index.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @init
|
|
@@ -20,7 +20,7 @@ export default function init( QuantumJS, configs = {} ) {
|
|
|
20
20
|
ContextAPI.call( this, ( configs.CONTEXT_API || {} ) );
|
|
21
21
|
BindingsAPI.call( this, ( configs.BINDINGS_API || {} ) ); // Depends on ContextAPI
|
|
22
22
|
NamespacedHTML.call( this, ( configs.NAMESPACED_HTML || {} ) ); // Depends on ContextAPI
|
|
23
|
-
HTMLImports.call( this, ( configs.HTML_IMPORTS || {} ) ); // Depends on ContextAPI
|
|
23
|
+
HTMLImports.call( this, { ...( configs.HTML_IMPORTS || {} ), idleCompilers: [ idleCompiler1, idleCompiler2 ] } ); // Depends on ContextAPI
|
|
24
24
|
DataBinding.call( this, ( configs.DATA_BINDING || {} ) ); // Depends on ContextAPI, BindingsAPI, HTMLImports
|
|
25
25
|
ScopedCSS.call( this, ( configs.SCOPED_CSS || {} ) ); // Depends on NamespacedHTML
|
|
26
26
|
ScopedJS.call( this, ( configs.SCOPED_JS || {} ) );
|
package/src/scoped-js/index.js
CHANGED
|
@@ -91,30 +91,16 @@ async function execute( config, execHash ) {
|
|
|
91
91
|
* @return Void
|
|
92
92
|
*/
|
|
93
93
|
function realtime( config ) {
|
|
94
|
-
const window = this, { webqit: { oohtml, realdom
|
|
94
|
+
const window = this, { webqit: { oohtml, realdom } } = window;
|
|
95
95
|
if ( !window.HTMLScriptElement.supports ) { window.HTMLScriptElement.supports = type => [ 'text/javascript', 'application/javascript' ].includes( type ); }
|
|
96
96
|
const handled = new WeakSet;
|
|
97
97
|
realdom.realtime( window.document ).query( config.scriptSelector, record => {
|
|
98
98
|
record.entrants.forEach( script => {
|
|
99
99
|
if ( handled.has( script ) ) return;
|
|
100
|
-
const textContent = ( script._ = script.textContent.trim() ) && script._.startsWith( '/*@oohtml*/if(false){' ) && script._.endsWith( '}/*@oohtml*/' ) ? script._.slice( 21, -12 ) : script.textContent;
|
|
101
|
-
if ( !script.scoped && !script.quantum && !textContent.includes( 'quantum' ) ) return;
|
|
102
|
-
handled.add( script );
|
|
103
100
|
// Do compilation
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if ( !( compiledScript = compileCache.get( sourceHash ) ) ) {
|
|
108
|
-
const { parserParams, compilerParams, runtimeParams } = config.advanced;
|
|
109
|
-
compiledScript = new ( script.type === 'module' ? QuantumModule : ( QuantumScript || AsyncQuantumScript ) )( textContent, {
|
|
110
|
-
exportNamespace: `#${ script.id }`,
|
|
111
|
-
fileName: `${ window.document.url?.split( '#' )?.[ 0 ] || '' }#${ script.id }`,
|
|
112
|
-
parserParams: { ...parserParams, quantumMode: script.quantum },
|
|
113
|
-
compilerParams,
|
|
114
|
-
runtimeParams,
|
|
115
|
-
} );
|
|
116
|
-
compileCache.set( sourceHash, compiledScript );
|
|
117
|
-
}
|
|
101
|
+
const compiledScript = compileScript.call( window, config, script );
|
|
102
|
+
if ( !compiledScript ) return;
|
|
103
|
+
handled.add( script );
|
|
118
104
|
// Run now!!!
|
|
119
105
|
const thisContext = script.scoped ? script.parentNode || record.target : ( script.type === 'module' ? undefined : window );
|
|
120
106
|
if ( script.scoped ) { thisContext[ config.api.scripts ].push( script ); }
|
|
@@ -126,4 +112,32 @@ function realtime( config ) {
|
|
|
126
112
|
} );
|
|
127
113
|
}, { live: true, subtree: 'cross-roots', timing: 'intercept', generation: 'entrants', eventDetails: true } );
|
|
128
114
|
// ---
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function compileScript( config, script ) {
|
|
118
|
+
const window = this, { webqit: { oohtml, QuantumScript, AsyncQuantumScript, QuantumModule } } = window;
|
|
119
|
+
const textContent = ( script._ = script.textContent.trim() ) && script._.startsWith( '/*@oohtml*/if(false){' ) && script._.endsWith( '}/*@oohtml*/' ) ? script._.slice( 21, -12 ) : script.textContent;
|
|
120
|
+
if ( !script.scoped && !script.quantum && !textContent.includes( 'quantum' ) ) return;
|
|
121
|
+
const sourceHash = _toHash( textContent );
|
|
122
|
+
const compileCache = oohtml.Script.compileCache[ script.quantum ? 0 : 1 ];
|
|
123
|
+
let compiledScript;
|
|
124
|
+
if ( !( compiledScript = compileCache.get( sourceHash ) ) ) {
|
|
125
|
+
const { parserParams, compilerParams, runtimeParams } = config.advanced;
|
|
126
|
+
compiledScript = new ( script.type === 'module' ? QuantumModule : ( QuantumScript || AsyncQuantumScript ) )( textContent, {
|
|
127
|
+
exportNamespace: `#${ script.id }`,
|
|
128
|
+
fileName: `${ window.document.url?.split( '#' )?.[ 0 ] || '' }#${ script.id }`,
|
|
129
|
+
parserParams: { ...parserParams, quantumMode: script.quantum },
|
|
130
|
+
compilerParams,
|
|
131
|
+
runtimeParams,
|
|
132
|
+
} );
|
|
133
|
+
compileCache.set( sourceHash, compiledScript );
|
|
134
|
+
}
|
|
135
|
+
return compiledScript;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function idleCompiler( node ) {
|
|
139
|
+
const window = this, { webqit: { oohtml: { configs: { SCOPED_JS: config } } } } = window;
|
|
140
|
+
[ ...( node?.querySelectorAll( config.scriptSelector ) || [] ) ].forEach( script => {
|
|
141
|
+
compileScript.call( window, config, script );
|
|
142
|
+
} );
|
|
129
143
|
}
|
package/test/index.js
CHANGED
|
@@ -27,8 +27,8 @@ export function createDocumentPrefixed( prefix, head = '', body = '', callback =
|
|
|
27
27
|
<head>
|
|
28
28
|
<meta name="$q-compiler-url" content="../quantum-js/dist/compiler.js">
|
|
29
29
|
${ prefix ? `<meta name="webqit" content="prefix=${ prefix };">` : `` }
|
|
30
|
-
<script ssr src="/dist/main.lite.js"></script>
|
|
31
30
|
${ head }
|
|
31
|
+
<script ssr src="/dist/main.lite.js"></script>
|
|
32
32
|
</head>
|
|
33
33
|
<body>${ body }</body>
|
|
34
34
|
</html>`;
|
package/test/scoped-js.test.js
CHANGED
|
@@ -10,7 +10,8 @@ describe(`Test: Scoped JS`, function() {
|
|
|
10
10
|
describe(`Scripts`, function() {
|
|
11
11
|
|
|
12
12
|
it(`Should do basic observe`, async function() {
|
|
13
|
-
const head =
|
|
13
|
+
const head = ``;
|
|
14
|
+
const body = `
|
|
14
15
|
<h1>Hello World!</h1>
|
|
15
16
|
<script scoped quantum>
|
|
16
17
|
testRecords.push( this );
|
|
@@ -19,7 +20,7 @@ describe(`Test: Scoped JS`, function() {
|
|
|
19
20
|
|
|
20
21
|
const window = createDocument( head, body );
|
|
21
22
|
window.testRecords = [];
|
|
22
|
-
await delay(
|
|
23
|
+
await delay( 500 ); // Takes time to dynamically load Reflex compiler
|
|
23
24
|
|
|
24
25
|
expect( window.testRecords ).to.have.length( 1 );
|
|
25
26
|
expect( window.testRecords[ 0 ] ).to.eql( window.document.body );
|