Table of Contents

HopScript Modules

Hop.js supports Nodejs Modules. The import/exports mechanism, the file name resolution, the caching, the module object, and the variable scoping are compatible in Hop.js and Node.js. Hop.js adds several extensions to Nodejs Modules.

require( id [, language ] )

The arguments are as follows:

Modules are loaded differently depending on their source file suffix.

When idis a directory, the loader looks in the directory for a file named package.json to tell how to load the module.

When id is an http url, Hop.js assumes that the file is to be retrieved from a remote Hop.js server, and issues http requests to the given server to get the file contents. Modules required within the retrieved file are downloaded from the same location, except for system modules which are assumed to be available locally.

Example

The module htmlr.js requires the file htmlr.html. This second file is parsed, the Hop expressions it contains are evaluated, and the constructed HTML Dom tree is returned as the result of the require call.

htmlr/htmlr.js

service htmlr() {
   return require( "./htmlr.html" );
}
         
console.log( "Go to \"http://%s:%d/hop/htmlr\"", hop.hostname, hop.port );

htmlr/htmlr.html

<html>
  <head css=${module.filename.replace(".html",".hss")}/>
  <div class="div1" onclick=~{alert( "you have clicked 1" )}>1</div>
  <div class="div2" onclick=~{alert( "you have clicked 2" )}>2</div>
</html>

Languages

The optional argument language is a string denoting an implementation language for the module to be required. The builtin languages are:

Additional custom languages may also be defined. See chapter Language definition. The optional language defaults to require.lang.

require.lang

The default language used to require other modules.

Note: Language importation is recursive. That is, if a module mod is imported with a language lang, all the modules imported by mod will be considered implemented in the same language lang, unless a specific language is specified on the require calls.

This can be changed by modified the value of the require.lang attribute.

DSL

Modules implemented in Hop DSL (Domain Specific Languages) that extend the Hop syntax by implementing their parser with the builtin machinery of the Hop parser can auto-declare their implementation language. In that case, the require calls that load them do not need to specify any implementation language. The declaration is an extra use string to be included in the head of the program. For instance:

"use hiphop"
"use strict"

exports.prg = hiphop module( in A, in B, in R, out O ) {
   do {
      fork {
         await now( A );
      } par {
         await now( B );
      }
      emit O();
   } every( now( R ) )
}

The syntax of the use string declaration is:

<UseDeclaration>  use <Identifier>

The Identifier should be a module name resolvable using require.resolve( Identifier ).

Client Side modules

Modules can be imported from either server-side or client-side code. However, a module can be imported from a client-side. For that, it must be first mentionned in a script tag of the head of the web page, using the special attribute module. Then, it can be required using the same syntax as any regular server-side module. The src attribute of the script tag must exactly match the path mentioned in the require call. See API HTML for details.

Example

Modules can be required by either server-side source code and client-side source code. This example shows this latter possibility. The module mod1.js and mod2.js are used by document constructued by the service requirec but only the module mod1.js is explicitly required by the HTML document. The module mod1.js requires the module mod2.js. In this cases, both modules have to be mentionned in a script tag of the head element of the main document.

Note: Modules mentioned in the head's module attribute are loaded asynchronously when the page is created on the client. Thus they cannot be required in following sequential code. Two options are then possible to require modules: i) using the require form inside a window.onload callback or ii) using the require form inside a defer script. This is the option used in this example (note the defer attribute of the head tag).

requirec/requirec.js

const mod1 = require( "./mod1.js" );

service requirec() {
   return <html> 
     <head>
       <script src="./mod1.js" lang="hopscript"/>
       <script src="./mod2.js" lang="hopscript"/>
       <script src="./example.json" lang="hopscript"/>
       <script src="./mod.html" lang="hopscript"/>
       <script defer>
          var mod1 = require( "./mod1.js" );
          var ex = require( "./example.json" );
          var dhtml = require( "./mod.html" );
       </script>
     </head>
     <button onclick=~{ document.body.appendChild( mod1.hello() ) }>
       click me
     </button>
     <button onclick=~{ alert( "desc=" + ex.description ) }>
       json me
     </button>
     <button onclick=~{ alert( "desc=" + dhtml.outerHTML ) }>
       html me
     </button>
   </html>;
}

console.log( "Go to \"http://%s:%d/hop/requirec\"", hop.hostname, hop.port );

requirec/mod1.js

var mod2 = require( "./mod2.js" );

var s = "";

s += "hello";

exports.hello = function( x ) {
   return mod2.hello( s );
}

requirec/mod2.js

exports.hello = function( s ) {
   return <button onclick=~{ alert( "s=" + s ) }>${s}</button>;
}

requirec/example.json

{
   "tags": [ "module" ],
   "description": "Multitier require forms",
   "doc": "Shows how to use nodejs compatible require function and module objects.",
   "files": [ "requirec.js", "mod1.js", "mod2.js", "example.json" ],
}

requirec/mod.html

<div onclick=~{ alert( "clicked" )}>
  an <span>html</span> element
</div>