All files / lib/internal/modules/esm module_job.js

100% Statements 143/143
100% Branches 33/33
100% Functions 7/7
100% Lines 143/143

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 142x 15x 142x 142x 142x 142x 142x 142x 142x 142x 142x 444x 444x 444x 444x 444x 444x 444x 444x 444x 444x 444x 443x 433x 433x 433x 433x 433x 433x 433x 433x 331x 331x 331x 324x 433x 433x 433x 433x 425x 425x 444x 444x 444x 444x 444x 444x 444x 444x 444x 444x 444x 142x 142x 160x 147x 147x 160x 160x 142x 142x 147x 147x 464x 25x 25x 439x 439x 425x 147x 147x 133x 133x 147x 1x 1x 1x 137x 132x 132x 147x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 8x 8x 8x 125x 134x 404x 404x 404x 404x 147x 142x 142x 160x 137x 137x 137x 134x 160x 142x 142x 142x  
'use strict';
 
const {
  ArrayPrototypeJoin,
  ObjectSetPrototypeOf,
  PromiseAll,
  SafeSet,
  SafePromise,
  StringPrototypeIncludes,
  StringPrototypeMatch,
  StringPrototypeSplit,
} = primordials;
 
const { ModuleWrap } = internalBinding('module_wrap');
 
const { decorateErrorStack } = require('internal/util');
const assert = require('internal/assert');
const resolvedPromise = SafePromise.resolve();
 
function noop() {}
 
let hasPausedEntry = false;
 
/* A ModuleJob tracks the loading of a single Module, and the ModuleJobs of
 * its dependencies, over time. */
class ModuleJob {
  // `loader` is the Loader instance used for loading dependencies.
  // `moduleProvider` is a function
  constructor(loader, url, moduleProvider, isMain, inspectBrk) {
    this.loader = loader;
    this.isMain = isMain;
    this.inspectBrk = inspectBrk;
 
    this.module = undefined;
    // Expose the promise to the ModuleWrap directly for linking below.
    // `this.module` is also filled in below.
    this.modulePromise = moduleProvider.call(loader, url, isMain);
 
    // Wait for the ModuleWrap instance being linked with all dependencies.
    const link = async () => {
      this.module = await this.modulePromise;
      assert(this.module instanceof ModuleWrap);
 
      // Explicitly keeping track of dependency jobs is needed in order
      // to flatten out the dependency graph below in `_instantiate()`,
      // so that circular dependencies can't cause a deadlock by two of
      // these `link` callbacks depending on each other.
      const dependencyJobs = [];
      const promises = this.module.link(async (specifier) => {
        const jobPromise = this.loader.getModuleJob(specifier, url);
        dependencyJobs.push(jobPromise);
        const job = await jobPromise;
        return job.modulePromise;
      });
 
      if (promises !== undefined)
        await SafePromise.all(promises);
 
      return SafePromise.all(dependencyJobs);
    };
    // Promise for the list of all dependencyJobs.
    this.linked = link();
    // This promise is awaited later anyway, so silence
    // 'unhandled rejection' warnings.
    this.linked.catch(noop);
 
    // instantiated == deep dependency jobs wrappers are instantiated,
    // and module wrapper is instantiated.
    this.instantiated = undefined;
  }
 
  instantiate() {
    if (this.instantiated === undefined) {
      this.instantiated = this._instantiate();
    }
    return this.instantiated;
  }
 
  async _instantiate() {
    const jobsInGraph = new SafeSet();
    const addJobsToDependencyGraph = async (moduleJob) => {
      if (jobsInGraph.has(moduleJob)) {
        return;
      }
      jobsInGraph.add(moduleJob);
      const dependencyJobs = await moduleJob.linked;
      return PromiseAll(dependencyJobs.map(addJobsToDependencyGraph));
    };
    await addJobsToDependencyGraph(this);
 
    try {
      if (!hasPausedEntry && this.inspectBrk) {
        hasPausedEntry = true;
        const initWrapper = internalBinding('inspector').callAndPauseOnStart;
        initWrapper(this.module.instantiate, this.module);
      } else {
        this.module.instantiate();
      }
    } catch (e) {
      decorateErrorStack(e);
      if (StringPrototypeIncludes(e.message,
                                  ' does not provide an export named')) {
        const splitStack = StringPrototypeSplit(e.stack, '\n');
        const parentFileUrl = splitStack[0];
        const childSpecifier = StringPrototypeMatch(e.message, /module '(.*)' does/)[1];
        const childFileURL =
              await this.loader.resolve(childSpecifier, parentFileUrl);
        const format = await this.loader.getFormat(childFileURL);
        if (format === 'commonjs') {
          const importStatement = splitStack[1];
          const namedImports = StringPrototypeMatch(importStatement, /{.*}/)[0];
          e.message = `The requested module '${childSpecifier}' is expected ` +
            'to be of type CommonJS, which does not support named exports. ' +
            'CommonJS modules can be imported by importing the default ' +
            'export.\n' +
            'For example:\n' +
            `import pkg from '${childSpecifier}';\n` +
            `const ${namedImports} = pkg;`;
          const newStack = StringPrototypeSplit(e.stack, '\n');
          newStack[3] = `SyntaxError: ${e.message}`;
          e.stack = ArrayPrototypeJoin(newStack, '\n');
        }
      }
      throw e;
    }
 
    for (const dependencyJob of jobsInGraph) {
      // Calling `this.module.instantiate()` instantiates not only the
      // ModuleWrap in this module, but all modules in the graph.
      dependencyJob.instantiated = resolvedPromise;
    }
  }
 
  async run() {
    await this.instantiate();
    const timeout = -1;
    const breakOnSigint = false;
    await this.module.evaluate(timeout, breakOnSigint);
    return { module: this.module };
  }
}
ObjectSetPrototypeOf(ModuleJob.prototype, null);
module.exports = ModuleJob;