Njs: export default function name() {...}

Created on 28 Mar 2019  路  6Comments  路  Source: nginx/njs

now is parsed as named function expression, so the code below does not work:

export default function tropical() {
}
tropical.prototype.sum = function(a, b) {
    return a < b ? a : b;
};
tropical.prototype.prod = function(a, b) {
    return a + b;
};
tropical.prototype.ZERO = Infinity;
tropical.prototype.ONE = 0;
ES6 enhancement help wanted

All 6 comments

@xeioex @drsm

I'm still not sure about vm->options.module.
Do you mean the script will behave like a normal module if running by -t module?

  1. If yes, can a module be executed by CLI?
console.log('start module');
function foo() {}
export default foo;
  1. export is required in module, so it's strange that if the main script behaves like a module, right?

  2. It seems we introduced an issue after supporting blocked scope function.

diff -r 31232e755143 njs/njs_parser.c
--- a/njs/njs_parser.c  Sun Apr 21 18:11:58 2019 +0800
+++ b/njs/njs_parser.c  Tue Apr 23 01:24:31 2019 +0800
@@ -203,7 +203,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs
     } else {
         if (type == NJS_SCOPE_GLOBAL) {
             type += NJS_INDEX_GLOBAL_OFFSET;
-            scope->module = vm->options.module;
+            scope->module = vm->options.module; // wrong???
         }

@hongzhidao

ES5 has one execution mode of a file.
ES6 has two:

  • script mode (like in ES5)
  • module mode (new).

The second one was introduced to support import/export stuff.
Imports and exports, by design (not in our simplified implementation) are early bound at compilation phase.
So actually:
export name means "here's some variable (as memory region) named name, it is accessible", like non-static var in C.
import name from mod means "create a lexical binding named name in the current module namespace to the memory region of the module mod named *default*"
and so on...
The mesh of links between module scopes is created.

@xeioex @drsm

I'm still not sure about vm->options.module.
Do you mean the script will behave like a normal module if running by -t module?

  1. If yes, can a module be executed by CLI?
console.log('start module');
function foo() {}
export default foo;
  1. export is required in module, so it'll be curious that if the main script behaves like a module, right?

no, export is not required.
in REPL it will do nothing, as nobody can import it, there is no file.
but files are executed OK:

root@node:~# head one.mjs two.mjs 
==> one.mjs <==
export const one = 1;
import { two } from './two.mjs'
// to leave TDZ
setImmediate(() => console.log('two from one', two));

==> two.mjs <==
export const two = 2;
import { one } from './one.mjs'
// to leave TDZ
setImmediate(() => console.log('one from two', one));
root@node:~# node --experimental-modules one.mjs 
(node:8490) ExperimentalWarning: The ESM module loader is experimental.
one from two 1
two from one 2
root@node:~# node --experimental-modules two.mjs 
(node:8501) ExperimentalWarning: The ESM module loader is experimental.
two from one 2
one from two 1

@drsm

no, export is not required.

  1. In the case of -t module, the script is not a module, as we set export statement is required in module file. right?

export name means "here's some variable (as memory region) named name, it is accessible", like non-static var in C.

  1. export statement can exist in both script and module files?

  2. We need one more condition in adding variable?

The patch is what I want to submit. @xeioex take a look.

diff -r 52983554e61f njs/njs_parser.c
--- a/njs/njs_parser.c  Mon Apr 22 19:53:41 2019 +0300
+++ b/njs/njs_parser.c  Tue Apr 23 02:46:18 2019 +0800
@@ -203,7 +203,6 @@ njs_parser_scope_begin(njs_vm_t *vm, njs
     } else {
         if (type == NJS_SCOPE_GLOBAL) {
             type += NJS_INDEX_GLOBAL_OFFSET;
-            scope->module = vm->options.module;
         }

         scope->next_index[0] = type;
diff -r 52983554e61f njs/njs_variable.c
--- a/njs/njs_variable.c    Mon Apr 22 19:53:41 2019 +0300
+++ b/njs/njs_variable.c    Tue Apr 23 02:46:18 2019 +0800
@@ -91,17 +91,17 @@ njs_variable_scope_add(njs_vm_t *vm, njs
     if (nxt_lvlhsh_find(&scope->variables, lhq) == NXT_OK) {
         var = lhq->value;

-        if (!scope->module && scope->type != NJS_SCOPE_BLOCK) {
-            return var;
-        }
-
-        if (type == NJS_VARIABLE_FUNCTION
-            || var->type == NJS_VARIABLE_FUNCTION)
+        if (scope->module || scope->type == NJS_SCOPE_BLOCK
+            || (scope->type == NJS_SCOPE_GLOBAL && vm->options.module))
         {
-            njs_parser_syntax_error(vm, vm->parser,
-                                    "\"%V\" has already been declared",
-                                    &lhq->key);
-            return NULL;
+            if (type == NJS_VARIABLE_FUNCTION
+                || var->type == NJS_VARIABLE_FUNCTION)
+            {
+                njs_parser_syntax_error(vm, vm->parser,
+                                        "\"%V\" has already been declared",
+                                        &lhq->key);
+                return NULL;
+            }
         }

         return var;

@hongzhidao

Didn't get it.

$ head main.js one.js two.js 
==> main.js <==
import one from 'one.js';
import two from 'two.js';
one();
two();

==> one.js <==
export default function() {
    console.log('one');
}

==> two.js <==
export default function() {
    console.log('two');
}
$ build/njs -t module main.js 
one
two
$ build/njs -t module one.js 
SyntaxError: Illegal export statement in one.js:1

Everything is ok.
We can't export from main file for now, as there no early binding.

-t script should be fixed to disallow import/export at all, i think.

Caused coredump now.

==> test.js <==
var x;
export default x;

$ build/njs -t module test.js

Continue it tomorrow :)

@hongzhidao @xeioex

Some thoughts:

  • import/export stuff should be disabled when -t script.
    Otherwise there is no chance to evolve it to support all the features.
    Not sure, but we'll have to do it at some point.

  • export from the main file/REPL (entry point) should be restricted for now.
    As it won't work properly anyway in the current implementation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xeioex picture xeioex  路  3Comments

porunov picture porunov  路  4Comments

xbb123 picture xbb123  路  4Comments

fishioon picture fishioon  路  3Comments

reyou picture reyou  路  5Comments