"use strict";

const assert = require("assert");
const FastPath = require("./fast-path.js");
const getOption = require("./options.js").get;
const utils = require("./utils.js");

const IEV = require("./import-export-visitor.js");
const importExportVisitor = new IEV;

const AV = require("./assignment-visitor.js");
const assignmentVisitor = new AV;

const hasOwn = Object.prototype.hasOwnProperty;
const shebangRegExp = /^#!.*/;
const importExportRegExp = /\b(?:im|ex)port\b/;

exports.compile = function (code, options) {
  const parse = getOption(options, "parse");
  code = code.replace(shebangRegExp, "");

  const result = { ast: null, code };

  if (! getOption(options, "force") &&  ! importExportRegExp.test(code)) {

    if (getOption(options, "ast")) {
      // To take full advantage of identity checking, you should probably
      // pass the { ast: false } option to the compile function.
      result.ast = parse(code);
    }
    return result;
  }

  const ast = parse(code);
  const rootPath = FastPath.from(ast);

  importExportVisitor.visit(rootPath, code, options);

  const magicString = importExportVisitor.magicString;

  assignmentVisitor.visit(rootPath, {
    exportedLocalNames: importExportVisitor.exportedLocalNames,
    magicString: magicString,
    modifyAST: importExportVisitor.modifyAST
  });

  importExportVisitor.finalizeHoisting();

  if (importExportVisitor.modifyAST) {
    // If the importExportVisitor modified the AST because options.ast was
    // truthy, then include it in the result.
    result.ast = ast;
  }

  result.code = magicString.toString();

  return result;
};

exports.transform = function (ast, options) {
  const importExportOptions = Object.assign({}, options);
  const rootPath = FastPath.from(ast);

  // Essential so that the AST will be modified.
  importExportOptions.ast = true;

  importExportVisitor.visit(
    rootPath,
    null, // No code to modify.
    importExportOptions
  );

  assignmentVisitor.visit(rootPath, {
    exportedLocalNames: importExportVisitor.exportedLocalNames,
    modifyAST: true
  });

  importExportVisitor.finalizeHoisting();

  return ast;
};

@@ -680,16 +680,21 @@
 rce%22) &&
+%0A    
   ! impo
@@ -721,16 +721,116 @@
 code)) %7B
+%0A    // Let the caller know the result is no different from the input.%0A    options.identical = true;
 %0A%0A    if
@@ -1037,16 +1037,17 @@
 ;%0A    %7D%0A
+%0A
     retu
@@ -1240,16 +1240,59 @@
 tring;%0A%0A
+  if (importExportVisitor.madeChanges) %7B%0A  
   assign
@@ -1317,32 +1317,34 @@
 rootPath, %7B%0A    
+  
 exportedLocalNam
@@ -1391,16 +1391,18 @@
 es,%0A    
+  
 magicStr
@@ -1419,16 +1419,18 @@
 String,%0A
+  
     modi
@@ -1466,23 +1466,27 @@
 difyAST%0A
+  
   %7D);%0A%0A
+  
   import
@@ -1520,16 +1520,132 @@
 ing();%0A%0A
+  %7D else %7B%0A    // Let the caller know the result is no different from the input.%0A    options.identical = true;%0A  %7D%0A%0A
   if (im
@@ -2218,16 +2218,59 @@
 s%0A  );%0A%0A
+  if (importExportVisitor.madeChanges) %7B%0A  
   assign
@@ -2295,24 +2295,26 @@
 rootPath, %7B%0A
+  
     exported
@@ -2365,24 +2365,26 @@
 lNames,%0A    
+  
 modifyAST: t
@@ -2389,21 +2389,25 @@
  true%0A  
+ 
+ 
 %7D);%0A%0A
+  
   import
@@ -2433,32 +2433,148 @@
 izeHoisting();%0A%0A
+  %7D else %7B%0A    // Let the caller know the result is no different from the input.%0A    options.identical = true;%0A  %7D%0A%0A
   return ast;%0A%7D;

Use importExportVisitor.madeChanges to avoid later work.