UNPKG

@webqit/quantum-js

Version:

Runtime extension to JavaScript that let's us do Imperative Reactive Programming (IRP) in the very language.

81 lines (75 loc) 4.96 kB
/** * @imports */ import { _await } from '../util.js'; import { resolveParams } from '../params.js'; import Runtime from './Runtime.js'; import Scope from './Scope.js'; import State from './State.js'; export { State, Runtime } export function $eval( sourceType, parseCompileCallback, source, params ) { // params could have: env, functionParams, parserParams, compilerParams, runtimeParams const { env, functionParams = [], exportNamespace, fileName } = params; const { parserParams, compilerParams, runtimeParams, } = resolveParams( params ); const inBrowser = Object.getOwnPropertyDescriptor( globalThis, 'window' )?.get?.toString().includes( '[native code]' ) ?? false; const asyncEval = [ 'async-script', 'module' ].includes( sourceType ); // Format source? Mode can be: function, async-function, script, async-script, module if ( sourceType === 'module' ) { parserParams.sourceType = sourceType; parserParams.allowAwaitOutsideFunction = true; } else if ( [ 'function', 'async-function' ].includes( sourceType ) ) { // Design the actual stateful function const body = ` ` + source.split( `\n` ).join( `\n ` ); source = `return ${ sourceType === 'async-function' ? 'async ' : '' }function**(${ functionParams.join( ', ' ) }) {\n${ body }\n}`; // The top-level program is a simple return statement as above. This return shouldn't be treated as reactive nor return a state object, but the plain value parserParams.executionMode = 'RegularProgram'; } else if ( ![ 'script', 'async-script' ].includes( sourceType ) ) { throw new Error( `Unrecognized sourceType specified: "${ sourceType }".` ); } // Proceed to parse-compile compilerParams.sourceType = sourceType; parserParams.inBrowser = inBrowser; compilerParams.base64 = asyncEval && inBrowser && `export default async function(%0) {%1}`; const compilation = parseCompileCallback( source, { parserParams, compilerParams } ); if ( compilation instanceof Promise && ![ 'async-function', 'async-script', 'module' ].includes( sourceType ) ) { throw new Error( `Parse-compile can only return a Promise for sourceTypes: async-function, async-script, module.` ); } // Proceed to eval runtimeParams.sourceType = sourceType; runtimeParams.inBrowser = inBrowser; runtimeParams.exportNamespace = exportNamespace; runtimeParams.fileName = fileName; return _await( compilation, compilation => { const isFunction = [ 'function', 'async-function' ].includes( sourceType ); // Below, "async-function" would already has async in the returned function // And no need to ask compilation.topLevelAwait const $eval = ( $qIdentifier, source ) => { if ( runtimeParams.compileFunction ) return runtimeParams.compileFunction( source.toString(), [ $qIdentifier ] ); if ( compilerParams.base64 ) { /* @experimental */ // Save to a variable to fool bundlephobia about the import(); const dataUrl = `data:text/javascript;base64,${ source.toString( 'base64' ) }`; const impt = () => import( dataUrl ).then( m => m.default ); //if ( window.webqit?.realdom?.schedule ) return window.webqit?.realdom?.schedule( 'write', impt, true ); return impt(); } return new ( asyncEval ? ( async function() {} ).constructor : Function )( $qIdentifier, source.toString() ); }; return _await( $eval( compilation.identifier + '', compilation ), main => { const createRuntime = ( thisContext, $env = env ) => { let $main = main; if ( thisContext ) { $main = $main.bind( thisContext ); } // There's always a global scope let contextType = 'global', scope = new Scope( undefined, contextType, globalThis ); // Then this, for script scope, which may also directly reflect/mutate any provided "env" if ( sourceType.endsWith( 'script' ) || $env ) { contextType = 'env'; scope = new Scope( scope, contextType, $env ); } // Or this for module scope. And where "env" was provided, the "env" scope above too if ( sourceType === 'module' ) { contextType = 'module'; scope = new Scope( scope, contextType ); } if ( typeof thisContext !== 'undefined' ) { scope = new Scope( scope, 'this', { [ 'this' ]: thisContext } ); } return new Runtime( undefined, contextType, { ...runtimeParams, originalSource: compilation.originalSource, executionMode: compilation.isQuantumProgram && 'QuantumProgram' || 'RegularProgram' }, scope, $main ); }; return isFunction ? createRuntime().execute() // Produces the actual stateful function designed above : { createRuntime, compilation }; } ); } ); }