keep keep keep keep keep replace keep keep keep keep

import { test, test2 } from './macro' with { type: "macro" };

// @flow strict-local
import assert from 'assert';
import invariant from 'assert';
import path from 'path';
import {
  bundle,
  bundler,
  run,
  overlayFS,
  fsFixture,
  getNextBuild,
} from '@parcel/test-utils';

describe('macros', function () {
  let count = 0;
  let dir;
  beforeEach(async () => {
    dir = path.join(__dirname, 'macros', '' + ++count);
    await overlayFS.mkdirp(dir);
  });

  after(async () => {
    await overlayFS.rimraf(path.join(__dirname, 'macros'));
  });

  it('should support named imports', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { hash } from "./macro" with { type: "macro" };
        output = hash('hi');

      macro.js:
        import {hashString} from '@parcel/rust';
        export function hash(s) {
          return hashString(s);
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output="2a2300bbd7ea6e9a"'));
  });

  it('should support renamed imports', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { hashString as foo } from "@parcel/rust" with { type: "macro" };
        output = foo('hi');
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output="2a2300bbd7ea6e9a"'));
  });

  it('should support default imports', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import test from "./macro.js" with { type: "macro" };
        output = test('hi');

      macro.js:
        import {hashString} from '@parcel/rust';
        export default function test(s) {
          return hashString(s);
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output="2a2300bbd7ea6e9a"'));
  });

  it('should support namespace imports', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import * as ns from "@parcel/rust" with { type: "macro" };
        output = ns.hashString('hi');
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output="2a2300bbd7ea6e9a"'));
  });

  it('should support various JS value types', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(undefined, null, true, false, 1, 0, -2, 'hi', /yo/i, [1, {test: 8}]);

      macro.js:
        import {inspect} from 'util';
        export function test(...args) {
          return inspect(args);
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await run(b);
    assert.equal(
      res,
      require('util').inspect([
        undefined,
        null,
        true,
        false,
        1,
        0,
        -2,
        'hi',
        /yo/i,
        [1, {test: 8}],
      ]),
    );
  });

  it('should support returning various JS value types', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test();

      macro.js:
        export function test() {
          return [undefined, null, true, false, 1, 0, -2, 'hi', /yo/i, [1, {test: 8}]];
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await run(b);
    assert.deepEqual(res, [
      undefined,
      null,
      true,
      false,
      1,
      0,
      -2,
      'hi',
      /yo/i,
      [1, {test: 8}],
    ]);
  });

  it('should support evaluating expressions', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1 + 2, 'foo ' + 'bar', 3 + 'em', 'test'.length, 'test'['length'], 'test'[1], !true, [1, ...[2, 3]], {x: 2, ...{y: 3}}, true ? 1 : 0, typeof false, null ?? 2);

      macro.js:
        export function test(...args) {
          return args;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await run(b);
    assert.deepEqual(res, [
      3,
      'foo bar',
      '3em',
      4,
      4,
      'e',
      false,
      [1, 2, 3],
      {x: 2, y: 3},
      1,
      'boolean',
      2,
    ]);
  });

  it('should dead code eliminate falsy branches', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };

        if (test()) {
          console.log('bad');
        } else {
          console.log('good');
        }

      macro.js:
        export function test() {
          return false;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('console.log("good")'));
    assert(!res.includes('console.log("bad")'));
  });

  it('should support async macros', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test();

      macro.js:
        export function test() {
          return Promise.resolve(2);
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=2'));
  });

  it('should ignore macros in node_modules', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import test from "foo";
        output = test;

      node_modules/foo/index.js:
        import { test } from "./macro.js" with { type: "macro" };
        export default test();

      node_modules/foo/macro.js:
        export function test() {
          return 2;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('function test'));
  });

  it('should throw a diagnostic when an argument cannot be converted', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1, foo);

      macro.js:
        export function test() {
          return 2;
        }
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert.deepEqual(err.diagnostics, [
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 2,
                    column: 18,
                  },
                  end: {
                    line: 2,
                    column: 20,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
      ]);
    }
  });

  it('should throw a diagnostic when a macro errors', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1);

      macro.js:
        exports.test = function test() {
          throw new Error('test');
        }
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert(
        err.diagnostics[0].message.startsWith('Error evaluating macro: test'),
      );
      assert.deepEqual(err.diagnostics[0].codeFrames, [
        {
          filePath: path.join(dir, 'index.js'),
          codeHighlights: [
            {
              message: undefined,
              start: {
                line: 2,
                column: 10,
              },
              end: {
                line: 2,
                column: 16,
              },
            },
          ],
        },
      ]);
    }
  });

  it('should throw a diagnostic when a macro cannot be resolved', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1, 2);
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert.deepEqual(err.diagnostics, [
        {
          message: `Error loading macro: Could not resolve module "./macro.js" from "${path.join(
            dir,
            'index.js',
          )}"`,
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 1,
                    column: 1,
                  },
                  end: {
                    line: 1,
                    column: 57,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
      ]);
    }
  });

  it('should support returning functions', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1, 2)(3);

      macro.js:
        export function test(a, b) {
          return new Function('c', \`return \${a} + \${b} + c\`);
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=6'));
  });

  it('should support macros written in typescript', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.ts" with { type: "macro" };
        output = test(1, 2);

      macro.ts:
        export function test(a: number, b: number) {
          return a + b;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=3'));
  });

  it('should support macros written in typescript without extension', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro" with { type: "macro" };
        output = test(1, 2);

      macro.ts:
        export function test(a: number, b: number) {
          return a + b;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=3'));
  });

  it('should allow emitting additional assets', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { css } from "./macro.ts" with { type: "macro" };
        output = css('background: red') + css('color: pink');

      macro.ts:
        export function css(v) {
          this.addAsset({
            type: 'css',
            content: '.foo {\\n' + v + '\\n}'
          });
          return 'foo';
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output="foofoo"'));

    res = await overlayFS.readFile(b.getBundles()[1].filePath, 'utf8');
    assert(res.includes('.foo{color:pink;background:red}'));
  });

  it('should invalidate the cache when changing a macro', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test();

      macro.js:
        export function test() {
          return 2;
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
      shouldDisableCache: false,
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=2'));

    await fsFixture(overlayFS, dir)`
      macro.js:
        export function test() {
          return 3;
        }
    `;

    b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
      shouldDisableCache: false,
    });

    res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    assert(res.includes('output=3'));
  });

  it('should invalidate the cache on build', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test('test.txt');

      macro.js:
        export function test() {
          this.invalidateOnBuild();
          return Date.now();
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
      shouldDisableCache: false,
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    let match = res.match(/output=(\d+)/);
    assert(match);

    b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
      shouldDisableCache: false,
    });

    res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    let match2 = res.match(/output=(\d+)/);
    assert(match2);
    assert.notEqual(match[1], match2[1]);
  });

  it('should only error once if a macro errors during loading', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.js" with { type: "macro" };
        output = test(1, 2);
        output2 = test(1, 3);

      macro.js:
        export function test() {
          return Date.now(
        }
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert.equal(err.diagnostics.length, 1);
    }
  });

  it('should rebuild in watch mode after fixing an error', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { test } from "./macro.ts" with { type: "macro" };
        output = test('test.txt');

      macro.ts:
        export function test() {
          return Date.now(
        }
    `;

    let b = await bundler(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
      shouldDisableCache: false,
    });

    let subscription;
    try {
      subscription = await b.watch();
      let buildEvent = await getNextBuild(b);
      assert.equal(buildEvent.type, 'buildFailure');

      await fsFixture(overlayFS, dir)`
        macro.ts:
          export function test() {
            return Date.now();
          }
      `;

      buildEvent = await getNextBuild(b);
      assert.equal(buildEvent.type, 'buildSuccess');
      invariant(buildEvent.type === 'buildSuccess'); // flow

      let res = await overlayFS.readFile(
        buildEvent.bundleGraph.getBundles()[0].filePath,
        'utf8',
      );
      let match = res.match(/output=(\d+)/);
      assert(match);
    } finally {
      await subscription?.unsubscribe();
    }
  });

  it('should support evaluating constants', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { hashString } from "@parcel/rust" with { type: "macro" };
        import { test } from './macro' with { type: "macro" };
        const hi = "hi";
        const ref = hi;
        const arr = [hi];
        const obj = {a: {b: hi}};
        const [a, [b], ...c] = [hi, [hi], 2, 3, hi];
        const [x, y = hi] = [1];
        const {hi: d, e, ...f} = {hi, e: hi, x: 2, y: hi};
        const res = test();
        output1 = hashString(hi);
        output2 = hashString(ref);
        output3 = hashString(arr[0]);
        output4 = hashString(obj.a.b);
        output5 = hashString(a);
        output6 = hashString(b);
        output7 = hashString(c[2]);
        output8 = hashString(y);
        output9 = hashString(d);
        output10 = hashString(e);
        output11 = hashString(f.y);
        output12 = hashString(f?.y);
        output13 = hashString(res);

      macro.js:
        export function test() {
          return "hi";
        }
    `;

    let b = await bundle(path.join(dir, '/index.js'), {
      inputFS: overlayFS,
      mode: 'production',
    });

    let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
    for (let i = 1; i <= 13; i++) {
      assert(res.includes(`output${i}="2a2300bbd7ea6e9a"`));
    }
  });

  it('should throw a diagnostic when a constant is mutated', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { hashString } from "@parcel/rust" with { type: "macro" };
        const object = {foo: 'bar'};
        object.foo = 'test';
        output = hashString(object.foo);

        const arr = ['foo'];
        arr[0] = 'bar';
        output = hashString(arr[0]);
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert.deepEqual(err.diagnostics, [
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 3,
                    column: 1,
                  },
                  end: {
                    line: 3,
                    column: 19,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 7,
                    column: 1,
                  },
                  end: {
                    line: 7,
                    column: 14,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
      ]);
    }
  });

  it('should throw a diagnostic when a constant object is passed to a function', async function () {
    await fsFixture(overlayFS, dir)`
      index.js:
        import { hashString } from "@parcel/rust" with { type: "macro" };
        const bar = 'bar';
        const object = {foo: bar};
        doSomething(bar); // ok (string)
        doSomething(object.foo); // ok (evaluates to a string)
        doSomething(object); // error (object could be mutated)
        output = hashString(object.foo);

        const object2 = {foo: bar, obj: {}};
        doSomething(object2.obj); // error (object could be mutated)
        output2 = hashString(object2);

        const arr = ['foo'];
        doSomething(arr);
        output3 = hashString(arr[0]);

        const object3 = {foo: bar};
        doSomething(object3[unknown]);
        output4 = hashString(object3);
    `;

    try {
      await bundle(path.join(dir, '/index.js'), {
        inputFS: overlayFS,
        mode: 'production',
      });
    } catch (err) {
      assert.deepEqual(err.diagnostics, [
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 6,
                    column: 13,
                  },
                  end: {
                    line: 6,
                    column: 18,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 10,
                    column: 13,
                  },
                  end: {
                    line: 10,
                    column: 19,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 14,
                    column: 13,
                  },
                  end: {
                    line: 14,
                    column: 15,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
        {
          message: 'Could not statically evaluate macro argument',
          origin: '@parcel/transformer-js',
          codeFrames: [
            {
              filePath: path.join(dir, 'index.js'),
              codeHighlights: [
                {
                  message: undefined,
                  start: {
                    line: 18,
                    column: 13,
                  },
                  end: {
                    line: 18,
                    column: 19,
                  },
                },
              ],
            },
          ],
          hints: null,
        },
      ]);
    }
  });
});


.foo {
  color: red;

  &.bar {
    color: blue;
  }
}


import * as assert from 'assert';

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';

suite('Extension Test Suite', () => {
	vscode.window.showInformationMessage('Start all tests.');

	test('Sample test', () => {
		assert.strictEqual(-1, [1, 2, 3].indexOf(5));
		assert.strictEqual(-1, [1, 2, 3].indexOf(0));
	});
});


output11 = hashString(f.y);
output12 = hashString(f?.y);
output13 = hashString(res);
macro.js:


module.exports = {
    plugins: [
        require('posthtml-extend')({
            root: __dirname
        })
    ]
}


macro.js:
export function test() {
return "hi";
}

import { hashString } from "@parcel/rust";

use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

use swc_core::common::errors::Handler;
use swc_core::common::util::take::Take;
use swc_core::common::{SourceMap, Span, DUMMY_SP};
use swc_core::ecma::ast::*;
use swc_core::ecma::atoms::{js_word, JsWord};
use swc_core::ecma::parser::lexer::Lexer;
use swc_core::ecma::parser::{Parser, StringInput};
use swc_core::ecma::visit::{Fold, FoldWith};

use crate::utils::{
  error_buffer_to_diagnostics, match_export_name, match_property_name, CodeHighlight, Diagnostic,
  ErrorBuffer, SourceLocation,
};

pub enum MacroError {
  /// An error occurred loading a macro (e.g. resolution or syntax error).
  LoadError(String),
  /// An error was thrown when executing a macro.
  ExecutionError(String),
}

pub type MacroCallback = Arc<
  dyn Fn(String, String, Vec<JsValue>, SourceLocation) -> Result<JsValue, MacroError> + Send + Sync,
>;

pub struct Macros<'a> {
  /// Mapping of imported identifiers to import metadata.
  macros: HashMap<Id, MacroImport>,
  constants: HashMap<Id, Result<JsValue, Span>>,
  callback: MacroCallback,
  source_map: &'a SourceMap,
  diagnostics: &'a mut Vec<Diagnostic>,
  load_errors: HashSet<String>,
  assignment_span: Option<Span>,
  in_call: bool,
}

struct MacroImport {
  /// The import specifier.
  src: JsWord,
  /// The imported identifier. None if this is a namespace import.
  imported: Option<JsWord>,
  /// The location of the import specifier.
  span: Span,
}

impl<'a> Macros<'a> {
  pub fn new(
    callback: MacroCallback,
    source_map: &'a SourceMap,
    diagnostics: &'a mut Vec<Diagnostic>,
  ) -> Self {
    Macros {
      macros: HashMap::new(),
      constants: HashMap::new(),
      load_errors: HashSet::new(),
      callback,
      source_map,
      diagnostics,
      assignment_span: None,
      in_call: false,
    }
  }

  fn add_macro(&mut self, import: &ImportDecl) {
    for specifier in &import.specifiers {
      match specifier {
        ImportSpecifier::Named(named) => {
          let imported = match &named.imported {
            Some(imported) => match_export_name(imported).0.clone(),
            None => named.local.sym.clone(),
          };
          self.macros.insert(
            named.local.to_id(),
            MacroImport {
              src: import.src.value.clone(),
              imported: Some(imported),
              span: import.span,
            },
          );
        }
        ImportSpecifier::Default(default) => {
          self.macros.insert(
            default.local.to_id(),
            MacroImport {
              src: import.src.value.clone(),
              imported: Some(js_word!("default")),
              span: import.span,
            },
          );
        }
        ImportSpecifier::Namespace(namespace) => {
          self.macros.insert(
            namespace.local.to_id(),
            MacroImport {
              src: import.src.value.clone(),
              imported: None,
              span: import.span,
            },
          );
        }
      }
    }
  }

  fn call_macro(
    &mut self,
    src: String,
    export: String,
    call: CallExpr,
    import_span: Span,
  ) -> Result<Expr, Diagnostic> {
    // If a macro already errorered during loading, don't try callinbg it again.
    if self.load_errors.contains(&src) {
      return Ok(Expr::Lit(Lit::Null(Null::dummy())));
    }

    // Try to statically evaluate all of the function arguments.
    let mut args = Vec::with_capacity(call.args.len());
    for arg in &call.args {
      match self.eval(&*arg.expr) {
        Ok(val) => {
          if arg.spread.is_none() {
            args.push(val);
          } else if let JsValue::Array(val) = val {
            args.extend(val);
          } else {
            return Err(self.create_diagnostic(call.span));
          }
        }
        Err(span) => {
          return Err(self.create_diagnostic(span));
        }
      }
    }

    // If that was successful, call the function callback (on the JS thread).
    let loc = SourceLocation::from(self.source_map, call.span);
    match (self.callback)(src.clone(), export, args, loc.clone()) {
      Ok(val) => Ok(self.value_to_expr(val)?),
      Err(err) => match err {
        MacroError::LoadError(err) => {
          self.load_errors.insert(src);
          Err(Diagnostic {
            message: format!("Error loading macro: {}", err),
            code_highlights: Some(vec![CodeHighlight {
              message: None,
              loc: SourceLocation::from(self.source_map, import_span),
            }]),
            hints: None,
            show_environment: false,
            severity: crate::utils::DiagnosticSeverity::Error,
            documentation_url: None,
          })
        }
        MacroError::ExecutionError(err) => Err(Diagnostic {
          message: format!("Error evaluating macro: {}", err),
          code_highlights: Some(vec![CodeHighlight { message: None, loc }]),
          hints: None,
          show_environment: false,
          severity: crate::utils::DiagnosticSeverity::Error,
          documentation_url: None,
        }),
      },
    }
  }

  fn create_diagnostic(&self, span: Span) -> Diagnostic {
    Diagnostic {
      message: "Could not statically evaluate macro argument".into(),
      code_highlights: Some(vec![CodeHighlight {
        message: None,
        loc: SourceLocation::from(self.source_map, span),
      }]),
      hints: None,
      show_environment: false,
      severity: crate::utils::DiagnosticSeverity::Error,
      documentation_url: None,
    }
  }
}

impl<'a> Fold for Macros<'a> {
  fn fold_module(&mut self, mut node: Module) -> Module {
    // Pre-pass to find all macro imports.
    node.body.retain(|item| {
      if let ModuleItem::ModuleDecl(decl) = &item {
        if let ModuleDecl::Import(import) = &decl {
          if matches!(&import.with, Some(with) if is_macro(with)) {
            self.add_macro(import);
            return false;
          }
        }
      }

      true
    });

    // Only process the rest of the AST if we found any macro imports.
    if !self.macros.is_empty() {
      node = node.fold_children_with(self);
    }

    node
  }

  fn fold_expr(&mut self, node: Expr) -> Expr {
    if let Expr::Call(call) = node {
      if let Callee::Expr(expr) = &call.callee {
        match &**expr {
          Expr::Ident(ident) => {
            if let Some(specifier) = self.macros.get(&ident.to_id()) {
              if let Some(imported) = &specifier.imported {
                let src = specifier.src.to_string();
                let imported = imported.to_string();
                let span = specifier.span;
                let call = call.fold_with(self);
                return handle_error(
                  self.call_macro(src, imported, call, span),
                  &mut self.diagnostics,
                );
              }
            }
          }
          Expr::Member(member) => {
            // e.g. ns.macro()
            if let Expr::Ident(ident) = &*member.obj {
              if let (Some(specifier), Some(prop)) = (
                self.macros.get(&ident.to_id()),
                match_property_name(&member),
              ) {
                // Check that this is a namespace import.
                if specifier.imported.is_none() {
                  let src = specifier.src.to_string();
                  let imported = prop.0.to_string();
                  let span = specifier.span;
                  let call = call.fold_with(self);
                  return handle_error(
                    self.call_macro(src, imported, call, span),
                    &mut self.diagnostics,
                  );
                }
              }
            }
          }
          _ => {}
        }
      }

      // Not a macro. Track if we're in a call so we can error if constant
      // objects are referenced that might be mutated.
      self.in_call = true;
      let call = call.fold_with(self);
      self.in_call = false;
      return Expr::Call(call);
    }

    node.fold_children_with(self)
  }

  fn fold_var_decl(&mut self, mut node: VarDecl) -> VarDecl {
    node = node.fold_children_with(self);

    if node.kind == VarDeclKind::Const {
      for decl in &node.decls {
        if let Some(expr) = &decl.init {
          let val = self.eval(&*expr);
          self.eval_pat(val, &decl.name);
        }
      }
    }

    node
  }

  fn fold_assign_expr(&mut self, mut node: AssignExpr) -> AssignExpr {
    self.assignment_span = Some(node.span.clone());
    node.left = node.left.fold_with(self);
    self.assignment_span = None;

    node.right = node.right.fold_with(self);
    node
  }

  fn fold_member_expr(&mut self, node: MemberExpr) -> MemberExpr {
    if let Some(assignment_span) = self.assignment_span {
      // Error when re-assigning a property of a constant that's used in a macro.
      let node = node.fold_children_with(self);
      if let Expr::Ident(id) = &*node.obj {
        if let Some(constant) = self.constants.get_mut(&id.to_id()) {
          if constant.is_ok() {
            *constant = Err(assignment_span.clone());
          }
        }
      }

      return node;
    } else if self.in_call {
      // We need to error when passing a constant object into a non-macro call, since it might be mutated.
      // If the member expression evaluates to an object, continue traversing so we error in fold_ident.
      // Otherwise, return early to allow other properties to be accessed without error.
      let value = self
        .eval(&*node.obj)
        .and_then(|obj| self.eval_member_prop(obj, &node));
      if !matches!(
        value,
        Err(..) | Ok(JsValue::Object(..) | JsValue::Array(..))
      ) {
        return node;
      }
    }

    node.fold_children_with(self)
  }

  fn fold_ident(&mut self, node: Ident) -> Ident {
    if self.in_call {
      if let Some(constant) = self.constants.get_mut(&node.to_id()) {
        if matches!(constant, Ok(JsValue::Object(..) | JsValue::Array(..))) {
          // Mark access to constant object inside a call as an error since it could potentially be mutated.
          *constant = Err(node.span.clone());
        }
      }
    }

    node
  }
}

/// Checks if an object literal (from import attributes) has type: 'macro'.
fn is_macro(with: &ObjectLit) -> bool {
  for prop in &with.props {
    if let PropOrSpread::Prop(prop) = &prop {
      if let Prop::KeyValue(kv) = &**prop {
        let k = match &kv.key {
          PropName::Ident(Ident { sym, .. }) | PropName::Str(Str { value: sym, .. }) => sym.clone(),
          _ => continue,
        };
        if &k == "type"
          && matches!(&*kv.value, Expr::Lit(Lit::Str(Str { value, .. })) if value == "macro")
        {
          return true;
        }
      }
    }
  }

  false
}

fn handle_error(result: Result<Expr, Diagnostic>, diagnostics: &mut Vec<Diagnostic>) -> Expr {
  match result {
    Ok(expr) => expr,
    Err(err) => {
      if !diagnostics.iter().any(|d| *d == err) {
        diagnostics.push(err);
      }
      Expr::Lit(Lit::Null(Null::dummy()))
    }
  }
}

// A type that represents a basic JS value.
#[derive(Clone, Debug)]
pub enum JsValue {
  Undefined,
  Null,
  Bool(bool),
  Number(f64),
  String(String),
  Regex { source: String, flags: String },
  Array(Vec<JsValue>),
  Object(IndexMap<String, JsValue>),
  Function(String),
}

impl<'a> Macros<'a> {
  /// Statically evaluate a JS expression to a value, if possible.
  fn eval(&self, expr: &Expr) -> Result<JsValue, Span> {
    match expr.unwrap_parens() {
      Expr::Lit(lit) => match lit {
        Lit::Null(_) => Ok(JsValue::Null),
        Lit::Bool(v) => Ok(JsValue::Bool(v.value)),
        Lit::Num(v) => Ok(JsValue::Number(v.value)),
        Lit::Str(v) => Ok(JsValue::String(v.value.to_string())),
        Lit::JSXText(v) => Ok(JsValue::String(v.value.to_string())),
        Lit::Regex(v) => Ok(JsValue::Regex {
          source: v.exp.to_string(),
          flags: v.flags.to_string(),
        }),
        Lit::BigInt(v) => Err(v.span),
      },
      Expr::Tpl(tpl) => {
        let exprs: Vec<_> = tpl
          .exprs
          .iter()
          .filter_map(|expr| self.eval(&*expr).ok())
          .collect();
        if exprs.len() == tpl.exprs.len() {
          let mut res = String::new();
          let mut expr_iter = exprs.iter();
          for quasi in &tpl.quasis {
            res.push_str(&quasi.raw);
            match expr_iter.next() {
              None => {}
              Some(JsValue::String(s)) => res.push_str(s),
              Some(JsValue::Number(n)) => res.push_str(&n.to_string()),
              Some(JsValue::Bool(b)) => res.push_str(&b.to_string()),
              _ => return Err(tpl.span),
            }
          }

          Ok(JsValue::String(res))
        } else {
          Err(tpl.span)
        }
      }
      Expr::Array(arr) => {
        let mut res = Vec::with_capacity(arr.elems.len());
        for elem in &arr.elems {
          if let Some(elem) = elem {
            let val = self.eval(&*elem.expr)?;
            if elem.spread.is_some() {
              match val {
                JsValue::Array(arr) => {
                  res.extend(arr);
                }
                _ => return Err(arr.span),
              }
            } else {
              res.push(val);
            }
          } else {
            res.push(JsValue::Undefined);
          }
        }
        Ok(JsValue::Array(res))
      }
      Expr::Object(obj) => {
        let mut res = IndexMap::with_capacity(obj.props.len());
        for prop in &obj.props {
          match prop {
            PropOrSpread::Prop(prop) => match &**prop {
              Prop::KeyValue(kv) => {
                let v = self.eval(&*kv.value)?;
                let k = match &kv.key {
                  PropName::Ident(Ident { sym, .. }) | PropName::Str(Str { value: sym, .. }) => {
                    sym.to_string()
                  }
                  PropName::Num(n) => n.value.to_string(),
                  PropName::Computed(c) => match self.eval(&*c.expr) {
                    Err(e) => return Err(e),
                    Ok(JsValue::String(s)) => s,
                    Ok(JsValue::Number(n)) => n.to_string(),
                    Ok(JsValue::Bool(b)) => b.to_string(),
                    _ => return Err(c.span),
                  },
                  PropName::BigInt(v) => return Err(v.span),
                };

                res.insert(k.to_string(), v);
              }
              Prop::Shorthand(s) => {
                if let Some(val) = self.constants.get(&s.to_id()) {
                  res.insert(s.sym.to_string(), val.clone()?);
                } else {
                  return Err(s.span);
                }
              }
              _ => return Err(obj.span),
            },
            PropOrSpread::Spread(spread) => {
              let v = self.eval(&*spread.expr)?;
              match v {
                JsValue::Object(o) => res.extend(o),
                _ => return Err(obj.span),
              }
            }
          }
        }
        Ok(JsValue::Object(res))
      }
      Expr::Bin(bin) => match (bin.op, self.eval(&*bin.left), self.eval(&*bin.right)) {
        (BinaryOp::Add, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::String(format!("{}{}", a, b)))
        }
        (BinaryOp::Add, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a + b))
        }
        (BinaryOp::Add, Ok(JsValue::String(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::String(format!("{}{}", a, b)))
        }
        (BinaryOp::Add, Ok(JsValue::Number(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::String(format!("{}{}", a, b)))
        }
        (BinaryOp::BitAnd, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) & (b as i32)) as f64))
        }
        (BinaryOp::BitOr, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) | (b as i32)) as f64))
        }
        (BinaryOp::BitXor, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) ^ (b as i32)) as f64))
        }
        (BinaryOp::LShift, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) << (b as i32)) as f64))
        }
        (BinaryOp::RShift, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) >> (b as i32)) as f64))
        }
        (BinaryOp::ZeroFillRShift, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(((a as i32) >> (b as u32)) as f64))
        }
        (BinaryOp::Sub, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a - b))
        }
        (BinaryOp::Div, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a / b))
        }
        (BinaryOp::Mul, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a * b))
        }
        (BinaryOp::Mod, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a % b))
        }
        (BinaryOp::Exp, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Number(a.powf(b)))
        }
        (BinaryOp::EqEq, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => Ok(JsValue::Bool(a == b)),
        (BinaryOp::EqEqEq, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => Ok(JsValue::Bool(a == b)),
        (BinaryOp::NotEq, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => Ok(JsValue::Bool(a != b)),
        (BinaryOp::NotEqEq, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => {
          Ok(JsValue::Bool(a != b))
        }
        (BinaryOp::EqEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a == b))
        }
        (BinaryOp::EqEqEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a == b))
        }
        (BinaryOp::NotEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a != b))
        }
        (BinaryOp::NotEqEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a != b))
        }
        (BinaryOp::EqEq, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::Bool(a == b))
        }
        (BinaryOp::EqEqEq, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::Bool(a == b))
        }
        (BinaryOp::NotEq, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::Bool(a != b))
        }
        (BinaryOp::NotEqEq, Ok(JsValue::String(a)), Ok(JsValue::String(b))) => {
          Ok(JsValue::Bool(a != b))
        }
        (BinaryOp::Gt, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => Ok(JsValue::Bool(a > b)),
        (BinaryOp::GtEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a >= b))
        }
        (BinaryOp::Lt, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => Ok(JsValue::Bool(a < b)),
        (BinaryOp::LtEq, Ok(JsValue::Number(a)), Ok(JsValue::Number(b))) => {
          Ok(JsValue::Bool(a <= b))
        }
        (BinaryOp::LogicalAnd, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => {
          Ok(JsValue::Bool(a && b))
        }
        (BinaryOp::LogicalOr, Ok(JsValue::Bool(a)), Ok(JsValue::Bool(b))) => {
          Ok(JsValue::Bool(a || b))
        }
        (BinaryOp::NullishCoalescing, Ok(JsValue::Null | JsValue::Undefined), Ok(b)) => Ok(b),
        (BinaryOp::NullishCoalescing, Ok(a), Ok(_)) => Ok(a),
        _ => Err(bin.span),
      },
      Expr::Unary(unary) => match (unary.op, self.eval(&*unary.arg)) {
        (UnaryOp::Bang, Ok(JsValue::Bool(v))) => Ok(JsValue::Bool(!v)),
        (UnaryOp::Minus, Ok(JsValue::Number(v))) => Ok(JsValue::Number(-v)),
        (UnaryOp::Plus, Ok(JsValue::Number(v))) => Ok(JsValue::Number(v)),
        (UnaryOp::Plus, Ok(JsValue::String(v))) => {
          if let Ok(v) = v.parse() {
            Ok(JsValue::Number(v))
          } else {
            Err(unary.span)
          }
        }
        (UnaryOp::Tilde, Ok(JsValue::Number(v))) => Ok(JsValue::Number((!(v as i32)) as f64)),
        (UnaryOp::Void, Ok(_)) => Ok(JsValue::Undefined),
        (UnaryOp::TypeOf, Ok(JsValue::Bool(_))) => Ok(JsValue::String("boolean".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Number(_))) => Ok(JsValue::String("number".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::String(_))) => Ok(JsValue::String("string".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Object(_))) => Ok(JsValue::String("object".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Array(_))) => Ok(JsValue::String("object".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Regex { .. })) => Ok(JsValue::String("object".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Null)) => Ok(JsValue::String("object".to_string())),
        (UnaryOp::TypeOf, Ok(JsValue::Undefined)) => Ok(JsValue::String("undefined".to_string())),
        _ => Err(unary.span),
      },
      Expr::Cond(cond) => match self.eval(&*&cond.test) {
        Ok(JsValue::Bool(v)) => {
          if v {
            self.eval(&*&cond.cons)
          } else {
            self.eval(&*cond.alt)
          }
        }
        Ok(JsValue::Null) | Ok(JsValue::Undefined) => self.eval(&*cond.alt),
        Ok(JsValue::Object(_))
        | Ok(JsValue::Array(_))
        | Ok(JsValue::Function(_))
        | Ok(JsValue::Regex { .. }) => self.eval(&*cond.cons),
        Ok(JsValue::String(s)) => {
          if s.is_empty() {
            self.eval(&*cond.alt)
          } else {
            self.eval(&*cond.cons)
          }
        }
        Ok(JsValue::Number(n)) => {
          if n == 0.0 {
            self.eval(&*cond.alt)
          } else {
            self.eval(&*cond.cons)
          }
        }
        Err(e) => Err(e),
      },
      Expr::Ident(id) if &id.sym == "undefined" => Ok(JsValue::Undefined),
      Expr::Ident(id) => {
        if let Some(val) = self.constants.get(&id.to_id()) {
          val.clone()
        } else {
          Err(id.span)
        }
      }
      Expr::Member(member) => {
        let obj = self.eval(&*member.obj)?;
        self.eval_member_prop(obj, &member)
      }
      Expr::OptChain(opt) => {
        if let OptChainBase::Member(member) = &*opt.base {
          let obj = self.eval(&*member.obj)?;
          match obj {
            JsValue::Undefined | JsValue::Null => Ok(JsValue::Undefined),
            _ => self.eval_member_prop(obj, &member),
          }
        } else {
          Err(opt.span)
        }
      }
      Expr::Fn(FnExpr { function, .. }) => Err(function.span),
      Expr::Class(ClassExpr { class, .. }) => Err(class.span),
      Expr::JSXElement(el) => Err(el.span),
      Expr::This(ThisExpr { span, .. })
      | Expr::Update(UpdateExpr { span, .. })
      | Expr::Assign(AssignExpr { span, .. })
      | Expr::Call(CallExpr { span, .. })
      | Expr::New(NewExpr { span, .. })
      | Expr::Seq(SeqExpr { span, .. })
      | Expr::TaggedTpl(TaggedTpl { span, .. })
      | Expr::Arrow(ArrowExpr { span, .. })
      | Expr::Yield(YieldExpr { span, .. })
      | Expr::Await(AwaitExpr { span, .. })
      | Expr::JSXFragment(JSXFragment { span, .. })
      | Expr::PrivateName(PrivateName { span, .. }) => Err(*span),
      _ => Err(DUMMY_SP),
    }
  }

  fn eval_member_prop(&self, obj: JsValue, member: &MemberExpr) -> Result<JsValue, Span> {
    match &member.prop {
      MemberProp::Ident(id) => obj.get_id(id.as_ref()).ok_or(member.span),
      MemberProp::Computed(prop) => {
        let k = self.eval(&*prop.expr)?;
        obj.get(&k).ok_or(prop.span)
      }
      _ => Err(member.span),
    }
  }

  /// Convert JS value to AST.
  fn value_to_expr(&self, value: JsValue) -> Result<Expr, Diagnostic> {
    Ok(match value {
      JsValue::Null => Expr::Lit(Lit::Null(Null::dummy())),
      JsValue::Undefined => Expr::Ident(Ident::new(js_word!("undefined"), DUMMY_SP)),
      JsValue::Bool(b) => Expr::Lit(Lit::Bool(Bool {
        value: b,
        span: DUMMY_SP,
      })),
      JsValue::Number(n) => Expr::Lit(Lit::Num(Number {
        value: n,
        span: DUMMY_SP,
        raw: None,
      })),
      JsValue::String(s) => Expr::Lit(Lit::Str(Str {
        span: DUMMY_SP,
        value: s.into(),
        raw: None,
      })),
      JsValue::Regex { source, flags } => Expr::Lit(Lit::Regex(Regex {
        span: DUMMY_SP,
        exp: source.into(),
        flags: flags.into(),
      })),
      JsValue::Array(arr) => Expr::Array(ArrayLit {
        span: DUMMY_SP,
        elems: arr
          .into_iter()
          .map(|elem| -> Result<_, Diagnostic> {
            Ok(Some(ExprOrSpread {
              spread: None,
              expr: Box::new(self.value_to_expr(elem)?),
            }))
          })
          .collect::<Result<Vec<_>, Diagnostic>>()?,
      }),
      JsValue::Object(obj) => Expr::Object(ObjectLit {
        span: DUMMY_SP,
        props: obj
          .into_iter()
          .map(|(k, v)| -> Result<_, Diagnostic> {
            Ok(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
              key: if Ident::verify_symbol(&k).is_ok() {
                PropName::Ident(Ident::new(k.into(), DUMMY_SP))
              } else {
                PropName::Str(Str {
                  value: k.into(),
                  span: DUMMY_SP,
                  raw: None,
                })
              },
              value: Box::new(self.value_to_expr(v)?),
            }))))
          })
          .collect::<Result<Vec<_>, Diagnostic>>()?,
      }),
      JsValue::Function(source) => {
        let source_file = self
          .source_map
          .new_source_file(swc_core::common::FileName::MacroExpansion, source.into());
        let lexer = Lexer::new(
          Default::default(),
          Default::default(),
          StringInput::from(&*source_file),
          None,
        );

        let mut parser = Parser::new_from(lexer);
        match parser.parse_expr() {
          Ok(expr) => *expr,
          Err(err) => {
            let error_buffer = ErrorBuffer::default();
            let handler = Handler::with_emitter(true, false, Box::new(error_buffer.clone()));
            err.into_diagnostic(&handler).emit();
            let mut diagnostics = error_buffer_to_diagnostics(&error_buffer, &self.source_map);
            return Err(diagnostics.pop().unwrap());
          }
        }
      }
    })
  }

  fn eval_pat(&mut self, value: Result<JsValue, Span>, pat: &Pat) {
    match pat {
      Pat::Ident(name) => {
        self.constants.insert(name.to_id(), value);
      }
      Pat::Array(arr) => {
        for (index, elem) in arr.elems.iter().enumerate() {
          if let Some(elem) = elem {
            match elem {
              Pat::Array(ArrayPat { span, .. })
              | Pat::Object(ObjectPat { span, .. })
              | Pat::Ident(BindingIdent {
                id: Ident { span, .. },
                ..
              }) => self.eval_pat(
                value
                  .as_ref()
                  .and_then(|v| v.get_index(index).ok_or(span))
                  .map_err(|s| *s),
                elem,
              ),
              Pat::Rest(rest) => self.eval_pat(
                value
                  .as_ref()
                  .and_then(|v| v.rest(index).ok_or(&rest.span))
                  .map_err(|s| *s),
                &*rest.arg,
              ),
              Pat::Assign(assign) => self.eval_pat(
                value.as_ref().map_err(|e| *e).and_then(|v| {
                  v.get_index(index)
                    .ok_or(assign.span)
                    .or_else(|_| self.eval(&*assign.right))
                }),
                &*assign.left,
              ),
              _ => {}
            }
          }
        }
      }
      Pat::Object(obj) => {
        let mut consumed = HashSet::new();
        for prop in &obj.props {
          match prop {
            ObjectPatProp::KeyValue(kv) => {
              let val = value
                .as_ref()
                .map_err(|e| *e)
                .and_then(|value| match &kv.key {
                  PropName::Ident(id) => {
                    consumed.insert(id.sym.clone());
                    value.get_id(id.sym.as_str()).ok_or(id.span)
                  }
                  PropName::Str(s) => {
                    consumed.insert(s.value.clone());
                    value.get_id(s.value.as_str()).ok_or(s.span)
                  }
                  PropName::Num(n) => {
                    consumed.insert(n.value.to_string().into());
                    value.get_index(n.value as usize).ok_or(n.span)
                  }
                  PropName::Computed(c) => {
                    let k = &self.eval(&*c.expr)?;
                    match k {
                      JsValue::String(s) => {
                        consumed.insert(s.clone().into());
                      }
                      JsValue::Number(n) => {
                        consumed.insert(n.to_string().into());
                      }
                      _ => {}
                    }
                    value.get(&k).ok_or(c.span)
                  }
                  PropName::BigInt(v) => Err(v.span),
                });
              self.eval_pat(val, &*kv.value)
            }
            ObjectPatProp::Assign(assign) => {
              let val = value.as_ref().map_err(|e| *e).and_then(|value| {
                value
                  .get_id(assign.key.sym.as_str())
                  .ok_or(assign.span)
                  .or_else(|_| {
                    assign
                      .value
                      .as_ref()
                      .map_or(Err(assign.span), |v| self.eval(&*v))
                  })
              });
              self.constants.insert(assign.key.to_id(), val);
              consumed.insert(assign.key.sym.clone());
            }
            ObjectPatProp::Rest(rest) => {
              let val = value.as_ref().map_err(|e| *e).and_then(|value| {
                if let JsValue::Object(obj) = value {
                  let filtered = obj
                    .iter()
                    .filter(|(k, _)| !consumed.contains(&k.as_str().into()))
                    .map(|(k, v)| (k.clone(), v.clone()))
                    .collect();
                  Ok(JsValue::Object(filtered))
                } else {
                  Err(rest.span)
                }
              });
              self.eval_pat(val, &*rest.arg);
            }
          }
        }
      }
      _ => {}
    }
  }
}

impl JsValue {
  fn get(&self, prop: &JsValue) -> Option<JsValue> {
    match self {
      JsValue::Array(arr) => {
        if let JsValue::Number(n) = prop {
          arr.get(*n as usize).cloned()
        } else {
          None
        }
      }
      JsValue::Object(_) => match prop {
        JsValue::Number(n) => {
          let index = n.to_string();
          self.get_id(&index)
        }
        JsValue::String(s) => self.get_id(s),
        _ => None,
      },
      JsValue::String(s) => match prop {
        JsValue::String(prop) => self.get_id(prop),
        JsValue::Number(n) => s
          .get(*n as usize..=*n as usize)
          .map(|c| JsValue::String(c.to_owned())),
        _ => None,
      },
      _ => None,
    }
  }

  fn get_index(&self, index: usize) -> Option<JsValue> {
    if let JsValue::Array(arr) = self {
      arr.get(index).cloned()
    } else {
      None
    }
  }

  fn get_id(&self, prop: &str) -> Option<JsValue> {
    match self {
      JsValue::Object(obj) => obj.get(prop).cloned(),
      JsValue::String(s) => match prop {
        "length" => Some(JsValue::Number(s.len() as f64)),
        _ => None,
      },
      _ => None,
    }
  }

  fn rest(&self, index: usize) -> Option<JsValue> {
    if let JsValue::Array(arr) = self {
      arr.get(index..).map(|s| JsValue::Array(s.to_vec()))
    } else {
      None
    }
  }
}


let b = await bundle(path.join(dir, '/index.js'), {

return new Function('return "' + hashString(obj.a.b) + '"');


export default {
  data () {
    return {
      msg: 'Hello World'
    }
  }
}


let x = (sideEffect('before'), require('./require'), sideEffect('after'));


let res = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');

keep keep keep replace keep keep keep keep


if let Some(specifier) = self.macros.get(&ident.to_id()) {

if let Some(imported) = &specifier.imported {

let in_call = std::mem::take(&mut self.in_call);


// @flow strict-local

import type {Readable} from 'stream';
import type {FileSystem} from '@parcel/fs';

import {objectSortedEntriesDeep} from './collection';
import {hashString, Hash} from '@parcel/rust';

export function hashStream(stream: Readable): Promise<string> {
  let hash = new Hash();
  return new Promise((resolve, reject) => {
    stream.on('error', err => {
      reject(err);
    });
    stream
      .on('data', chunk => {
        hash.writeBuffer(chunk);
      })
      .on('end', function () {
        resolve(hash.finish());
      })
      .on('error', err => {
        reject(err);
      });
  });
}

export function hashObject(obj: {+[string]: mixed, ...}): string {
  return hashString(JSON.stringify(objectSortedEntriesDeep(obj)));
}

let testCache: {|[string]: Promise<string>|} = {
  /*:: ...null */
};
export function hashFile(fs: FileSystem, filePath: string): Promise<string> {
  if (process.env.PARCEL_BUILD_ENV === 'test') {
    // Development builds of these native modules are especially big and slow to hash.
    if (
      /parcel-swc\.[^\\/]+\.node$|lightningcss.[^\\/]+.node$/.test(filePath)
    ) {
      let cacheEntry = testCache[filePath];
      if (cacheEntry) return cacheEntry;
      let v = hashStream(fs.createReadStream(filePath));
      testCache[filePath] = v;
      return v;
    }
  }
  return hashStream(fs.createReadStream(filePath));
}


export default function bar() {
  return 2;
}


self.call_macro(src, imported, call, span),


export let v = 1;
export function update() {
	v++;
}


// Check that this is a namespace import.


<html>
  <head>
    <script src="index.js"></script>
  </head>
</html>


import { logger, run } from "./b.js";

output = [logger, run()];


module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:flowtype/recommended',
    'plugin:monorepo/recommended',
    'plugin:react/recommended',
    'prettier',
  ],
  parser: '@babel/eslint-parser',
  plugins: ['@parcel', 'flowtype', 'import', 'monorepo', 'react', 'mocha'],
  parserOptions: {
    ecmaVersion: 2018,
    ecmaFeatures: {
      jsx: true,
    },
    sourceType: 'module',
  },
  env: {
    es2020: true,
    node: true,
  },
  globals: {
    parcelRequire: true,
    define: true,
    SharedArrayBuffer: true,
  },
  // https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns
  overrides: [
    {
      files: ['**/test/**', '*.test.js', 'packages/core/integration-tests/**'],
      env: {
        mocha: true,
      },
      rules: {
        'import/no-extraneous-dependencies': 'off',
        'monorepo/no-internal-import': 'off',
        'monorepo/no-relative-import': 'off',
        'mocha/no-exclusive-tests': 'error',
      },
    },
  ],
  rules: {
    '@parcel/no-self-package-imports': 'error',
    'import/first': 'error',
    'import/newline-after-import': 'error',
    'import/no-extraneous-dependencies': 'error',
    'import/no-self-import': 'error',
    'no-prototype-builtins': 'off',
    'no-console': 'error',
    'no-return-await': 'error',
    'require-atomic-updates': 'off',
    'require-await': 'error',
  },
  settings: {
    flowtype: {
      onlyFilesWithFlowAnnotation: true,
    },
    react: {
      version: 'detect',
    },
  },
};


<!doctype html>
<script type="module" src="e.js"></script>


const {Transformer} = require('@parcel/plugin');

exports.default = new Transformer({
  async transform({asset}) {
    return [{
      type: 'js',
      content: `export default \`${await asset.getCode()}\``
    }];
  }
});


// @flow strict-local
import {
  bundler,
  getNextBuildSuccess,
  inputFS,
  overlayFS,
  run,
} from '@parcel/test-utils';
import assert from 'assert';
import path from 'path';
import sinon from 'sinon';
import {NodePackageManager} from '@parcel/package-manager';

import {type Asset} from '@parcel/types';

const CONFIG = Symbol.for('parcel-plugin-config');
let packageManager = new NodePackageManager(inputFS, '/');

describe('incremental bundling', function () {
  let defaultBundlerSpy, customBundlerSpy;
  let assertChangedAssets = (actual: number, expected: number) => {
    assert.equal(
      actual,
      expected,
      `the number of changed assets should be ${expected}, not ${actual}`,
    );
  };

  let assertTimesBundled = (actual: number, expected: number) => {
    assert.equal(
      actual,
      expected,
      `the bundler should have bundled ${expected} time(s), not ${actual}`,
    );
  };

  let getChangedAssetsBeforeRuntimes = (changedAssets: Array<Asset>) => {
    return changedAssets.filter(a => !a.filePath.includes('runtime'));
  };
  beforeEach(async () => {
    let Bundler = (
      await packageManager.require('@parcel/bundler-default', __filename)
    ).default;
    let CustomBundler = await packageManager.require(
      './integration/incremental-bundling/node_modules/parcel-bundler-test',
      __filename,
    );

    defaultBundlerSpy = sinon.spy(Bundler[CONFIG], 'bundle'); // $FlowFixMe[prop-missing]

    customBundlerSpy = sinon.spy(CustomBundler[CONFIG], 'bundle');
  });

  afterEach(() => {
    defaultBundlerSpy.restore();
    customBundlerSpy.restore();
  });

  describe('non-dependency based changes', () => {
    describe('javascript', () => {
      it('add a console log should not bundle by default', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index.js'),
            `import {a} from './a';
console.log('index.js');
console.log(a);
console.log('adding a new console');`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 1);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );
          assert(contents.includes(`console.log("adding a new console")`));
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });

      it('disable by setting option to false', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
            shouldBundleIncrementally: false,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index.js'),
            `import {a} from './a';
console.log('index.js');
console.log(a);
console.log('adding a new console');`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 1);
          assertTimesBundled(defaultBundlerSpy.callCount, 2);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );
          assert(contents.includes(`console.log("adding a new console")`));
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });

      it('add a console log should not bundle', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
            shouldBundleIncrementally: true,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index.js'),
            `import {a} from './a';
console.log('index.js');
console.log(a);
console.log('adding a new console');`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 1);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );
          assert(contents.includes(`console.log("adding a new console")`));
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });

      it('updating a string value should not bundle', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
            shouldBundleIncrementally: true,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index.js'),
            `import {a} from './a';
console.log('index.js - updated string');
console.log(a);
`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 1);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );
          assert(
            contents.includes(`console.log("index.js - updated string");`),
          );
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });

      it('adding a comment', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
            shouldBundleIncrementally: true,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index.js'),
            `import {a} from './a';
// test comment
console.log('index.js');
console.log(a);`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 1);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );
          assert(contents.includes(`// test comment`));
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });

      // this case is similar to applying a patch or restarting parcel with changes
      it('adds multiple non-dependency related changes', async () => {
        let subscription;
        let fixture = path.join(__dirname, '/integration/incremental-bundling');
        try {
          let b = bundler(path.join(fixture, 'index-export.js'), {
            inputFS: overlayFS,
            shouldDisableCache: false,
            shouldBundleIncrementally: true,
          });

          await overlayFS.mkdirp(fixture);
          subscription = await b.watch();

          let event = await getNextBuildSuccess(b);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          await overlayFS.writeFile(
            path.join(fixture, 'index-export.js'),
            `import {a} from './a';
console.log('adding a new console');
module.exports = a;`,
          );

          await overlayFS.writeFile(
            path.join(fixture, 'a.js'),
            `export const a = 'a updated';`,
          );

          event = await getNextBuildSuccess(b);
          assertChangedAssets(event.changedAssets.size, 2);
          assertTimesBundled(defaultBundlerSpy.callCount, 1);

          let result = await b.run();
          let contents = await overlayFS.readFile(
            result.bundleGraph.getBundles()[0].filePath,
            'utf8',
          );

          assert(contents.includes(`console.log("adding a new console")`));

          let bundleOutput = await run(result.bundleGraph);
          assert.equal(bundleOutput, 'a updated');
        } finally {
          if (subscription) {
            await subscription.unsubscribe();
            subscription = null;
          }
        }
      });
    });

    it('update an imported css file', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index-with-css.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'a.css'),
          `html {
  color: red;
}
`,
        );

        event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 1);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        let result = await b.run();
        let bundleCSS = result.bundleGraph.getBundles()[1];
        assert.equal(bundleCSS.type, 'css');

        let cssContent = await overlayFS.readFile(bundleCSS.filePath, 'utf8');
        assert(cssContent.includes(`color: red;`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('update both the js and imported css file', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index-with-css.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index-with-css.js'),
          `import {a} from './a';
import './a.css';
console.log('index.js');
console.log(a, 'updated');`,
        );

        await overlayFS.writeFile(
          path.join(fixture, 'a.css'),
          `html {
  color: red;
}`,
        );

        event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 2);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        let result = await b.run();
        let contents = await overlayFS.readFile(
          result.bundleGraph.getBundles()[0].filePath,
          'utf8',
        );

        assert(contents.includes(`console.log((0, _a.a), "updated");`));

        let bundleCSS = result.bundleGraph.getBundles()[1];
        assert.equal(bundleCSS.type, 'css');

        let cssContent = await overlayFS.readFile(bundleCSS.filePath, 'utf8');
        assert(cssContent.includes(`color: red;`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('update the bundles if entry is html and js asset is modified', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.html'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index.js'),
          `import {a} from './a';
// test comment
console.log('index.js');
console.log(a);`,
        );

        event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 1);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        let result = await b.run();

        let bundleHTML = result.bundleGraph.getBundles()[0];
        assert.equal(bundleHTML.type, 'html');
        let htmlContent = await overlayFS.readFile(bundleHTML.filePath, 'utf8');

        assert(htmlContent.includes(`<html>`));

        let bundleJS = result.bundleGraph.getBundles()[1];
        assert.equal(bundleJS.type, 'js');

        let jsContent = await overlayFS.readFile(bundleJS.filePath, 'utf8');
        assert(jsContent.includes(`// test comment`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });
  });

  describe('dependency based changes should run the bundler', () => {
    it('adding a new dependency', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index.js'),
          `import {a} from './a';
import {b} from './b';
console.log('index.js', b);
console.log(a);
`,
        );

        let event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 2);
        assertTimesBundled(defaultBundlerSpy.callCount, 2);

        let contents = await overlayFS.readFile(
          event.bundleGraph.getBundles()[0].filePath,
          'utf8',
        );

        assert(contents.includes(`console.log("index.js", (0, _b.b));`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('adding a new dependency of a different type', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index.js'),
          `import {a} from './a';
import './a.css';
console.log(a);
`,
        );

        let event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 2);
        assertTimesBundled(defaultBundlerSpy.callCount, 2);

        // one CSS and one JS bundle
        assert.equal(event.bundleGraph.getBundles().length, 2);

        let contents = await overlayFS.readFile(
          event.bundleGraph.getBundles()[0].filePath,
          'utf8',
        );

        assert(contents.includes(`console.log((0, _a.a));`));

        let bundleCSS = event.bundleGraph.getBundles()[1];
        assert.equal(bundleCSS.type, 'css');

        let cssContent = await overlayFS.readFile(bundleCSS.filePath, 'utf8');
        assert(cssContent.includes(`color: #00f;`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('adding a new dynamic import', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index.js'),
          `import {a} from './a';
const b = import('./b');
console.log(a, b);
`,
        );

        let event = await getNextBuildSuccess(b);
        let assets = Array.from(event.changedAssets.values());
        assertChangedAssets(getChangedAssetsBeforeRuntimes(assets).length, 2);
        assertTimesBundled(defaultBundlerSpy.callCount, 2);

        // original bundle and new dynamic import bundle JS bundle
        assert.equal(event.bundleGraph.getBundles().length, 2);

        let contents = await overlayFS.readFile(
          event.bundleGraph.getBundles()[0].filePath,
          'utf8',
        );

        assert(contents.includes(`console.log((0, _a.a), b);`));

        let dynamicBundle = event.bundleGraph.getBundles()[1];
        assert.equal(dynamicBundle.type, 'js');

        let dynamicContent = await overlayFS.readFile(
          dynamicBundle.filePath,
          'utf8',
        );
        assert(
          dynamicContent.includes(`parcelHelpers.export(exports, "b", ()=>b);
const b = "b";`),
        );
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('removing a dependency', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        await overlayFS.writeFile(
          path.join(fixture, 'index.js'),
          `// import {a} from './a';
console.log('index.js');`,
        );

        event = await getNextBuildSuccess(b);
        assertChangedAssets(event.changedAssets.size, 1);
        assertTimesBundled(defaultBundlerSpy.callCount, 2);

        let output = await overlayFS.readFile(
          path.join(fixture, 'index.js'),
          'utf8',
        );
        assert(output.includes(`// import {a} from './a'`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });
  });

  describe('other changes that would for a re-bundle', () => {
    it('changing the bundler in parcel configs', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);
        assertTimesBundled(customBundlerSpy.callCount, 0);

        await overlayFS.writeFile(
          path.join(fixture, '.parcelrc'),
          JSON.stringify({
            extends: '@parcel/config-default',
            bundler: 'parcel-bundler-test',
          }),
        );

        event = await getNextBuildSuccess(b);
        let assets = Array.from(event.changedAssets.values());
        // should contain all the assets
        assertChangedAssets(getChangedAssetsBeforeRuntimes(assets).length, 3);
        // the default bundler was only called once
        assertTimesBundled(defaultBundlerSpy.callCount, 1);
        // calls the new bundler to rebundle
        assertTimesBundled(customBundlerSpy.callCount, 1);

        let output = await overlayFS.readFile(
          path.join(fixture, 'index.js'),
          'utf8',
        );
        assert(output.includes(`import {a} from './a'`));
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });

    it('changing bundler options', async () => {
      let subscription;
      let fixture = path.join(__dirname, '/integration/incremental-bundling');
      try {
        let b = bundler(path.join(fixture, 'index.js'), {
          inputFS: overlayFS,
          shouldDisableCache: false,
          shouldBundleIncrementally: true,
        });

        await overlayFS.mkdirp(fixture);
        subscription = await b.watch();

        let event = await getNextBuildSuccess(b);
        assertTimesBundled(defaultBundlerSpy.callCount, 1);

        let pkgFile = path.join(fixture, 'package.json');
        let pkg = JSON.parse(await overlayFS.readFile(pkgFile));
        await overlayFS.writeFile(
          pkgFile,
          JSON.stringify({
            ...pkg,
            '@parcel/bundler-default': {
              http: 1,
            },
          }),
        );

        event = await getNextBuildSuccess(b);

        // should contain all the assets
        assertChangedAssets(event.changedAssets.size, 3);
        assertTimesBundled(defaultBundlerSpy.callCount, 2);
      } finally {
        if (subscription) {
          await subscription.unsubscribe();
          subscription = null;
        }
      }
    });
  });

  it('changing the namer', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, 'index.js'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);

      await overlayFS.writeFile(
        path.join(fixture, '.parcelrc'),
        JSON.stringify({
          extends: '@parcel/config-default',
          namers: ['parcel-namer-test'],
        }),
      );

      event = await getNextBuildSuccess(b);

      // should contain all the assets
      assertChangedAssets(event.changedAssets.size, 3);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);

      let result = await b.run();
      let bundles = result.bundleGraph.getBundles();
      assert.deepEqual(
        bundles.map(b => b.name),
        bundles.map(b => `${b.id}.${b.type}`),
      );
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });

  it('changing the runtimes', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, 'index.js'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);

      await overlayFS.writeFile(
        path.join(fixture, '.parcelrc'),
        JSON.stringify({
          extends: '@parcel/config-default',
          runtimes: ['parcel-runtime-test'],
        }),
      );

      event = await getNextBuildSuccess(b);

      // should contain all the assets
      let assets = Array.from(event.changedAssets.values());
      assertChangedAssets(getChangedAssetsBeforeRuntimes(assets).length, 3);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);

      let result = await b.run();
      let res = await run(result.bundleGraph, null, {require: false});
      assert.equal(res.runtime_test, true);
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });

  it('changing target options', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, 'index.js'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);
      assertTimesBundled(customBundlerSpy.callCount, 0);

      let pkgFile = path.join(fixture, 'package.json');
      let pkg = JSON.parse(await overlayFS.readFile(pkgFile));
      await overlayFS.writeFile(
        pkgFile,
        JSON.stringify({
          ...pkg,
          targets: {
            esmodule: {
              outputFormat: 'esmodule',
            },
          },
        }),
      );
      event = await getNextBuildSuccess(b);

      assertChangedAssets(event.changedAssets.size, 3);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);

      let output = await overlayFS.readFile(
        path.join(fixture, 'index.js'),
        'utf8',
      );
      assert(output.includes(`import {a} from './a'`));
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });

  it('adding a new the entry', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, '*.html'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);
      assertTimesBundled(customBundlerSpy.callCount, 0);

      await overlayFS.writeFile(
        path.join(fixture, 'index-new-entry.html'),
        '<html />',
      );

      event = await getNextBuildSuccess(b);

      // should contain all the assets
      assertChangedAssets(event.changedAssets.size, 1);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });
  it('changing symbols (adding a new dependency via one symbol)', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, 'index-multi-symbol.js'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
        defaultTargetOptions: {
          shouldScopeHoist: true,
        },
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);

      await overlayFS.writeFile(
        path.join(fixture, 'index-multi-symbol.js'),
        `import {a,b,c} from './multi-symbol-util.js';

      console.log('index.js');
      console.log(a,b,c);
      module.exports = {a, b, c};
      `,
      );

      event = await getNextBuildSuccess(b);
      assertChangedAssets(event.changedAssets.size, 1);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);

      let result = await b.run();
      let contents = await overlayFS.readFile(
        result.bundleGraph.getBundles()[0].filePath,
        'utf8',
      );
      assert(
        /console\.log\(\(0, [^)]+\), \(0, [^)]+\), \(0, [^)]+\)\);/.test(
          contents,
        ),
      );
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });
  it('changing symbols (removing a dependency via one symbol)', async () => {
    let subscription;
    let fixture = path.join(__dirname, '/integration/incremental-bundling');
    try {
      let b = bundler(path.join(fixture, 'index-multi-symbol.js'), {
        inputFS: overlayFS,
        shouldDisableCache: false,
        shouldBundleIncrementally: true,
        defaultTargetOptions: {
          shouldScopeHoist: true,
        },
      });

      await overlayFS.mkdirp(fixture);
      subscription = await b.watch();

      let event = await getNextBuildSuccess(b);
      assertTimesBundled(defaultBundlerSpy.callCount, 1);

      await overlayFS.writeFile(
        path.join(fixture, 'index-multi-symbol.js'),
        `import {a } from './multi-symbol-util.js';

console.log('index.js');
console.log(a);
module.exports = {a};
`,
      );

      event = await getNextBuildSuccess(b);
      assertChangedAssets(event.changedAssets.size, 1);
      assertTimesBundled(defaultBundlerSpy.callCount, 2);

      let result = await b.run();
      let contents = await overlayFS.readFile(
        result.bundleGraph.getBundles()[0].filePath,
        'utf8',
      );
      assert(/console\.log\(\(0, [^)]+\)\);/.test(contents));

      result.bundleGraph.getBundles()[0].traverseAssets(a => {
        assert(!a.filePath.endsWith('b.js'));
      });
    } finally {
      if (subscription) {
        await subscription.unsubscribe();
        subscription = null;
      }
    }
  });
});


keep keep keep keep replace keep keep keep keep

// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/api/app/app.h"

#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/browser/net_disk_cache_remover.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_shell.h"
#include "content/nw/src/shell_browser_context.h"
#include "content/common/view_messages.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/render_process_host.h"

using base::MessageLoop;
using content::Shell;
using content::ShellBrowserContext;
using content::RenderProcessHost;

namespace api {

namespace {

// Get render process host.
RenderProcessHost* GetRenderProcessHost() {
  RenderProcessHost* render_process_host = NULL;
  std::vector<Shell*> windows = Shell::windows();
  for (size_t i = 0; i < windows.size(); ++i) {
    if (!windows[i]->is_devtools()) {
      render_process_host = windows[i]->web_contents()->GetRenderProcessHost();
      break;
    }
  }

  return render_process_host;
}

void GetRenderProcessHosts(std::set<RenderProcessHost*>& rphs) {
  RenderProcessHost* render_process_host = NULL;
  std::vector<Shell*> windows = Shell::windows();
  for (size_t i = 0; i < windows.size(); ++i) {
    if (!windows[i]->is_devtools()) {
      render_process_host = windows[i]->web_contents()->GetRenderProcessHost();
      rphs.insert(render_process_host);
    }
  }
}

}  // namespace

// static
void App::Call(const std::string& method,
               const base::ListValue& arguments) {
  if (method == "Quit") {
    Quit();
    return;
  } else if (method == "CloseAllWindows") {
    CloseAllWindows();
    return;
  }
  NOTREACHED() << "Calling unknown method " << method << " of App";
}

// static
void App::Call(Shell* shell,
               const std::string& method,
               const base::ListValue& arguments,
               base::ListValue* result) {
  if (method == "GetDataPath") {
    ShellBrowserContext* browser_context =
      static_cast<ShellBrowserContext*>(shell->web_contents()->GetBrowserContext());
    result->AppendString(browser_context->GetPath().value());
    return;
  }else if (method == "GetArgv") {
    nw::Package* package = shell->GetPackage();
    CommandLine* command_line = CommandLine::ForCurrentProcess();
    CommandLine::StringVector args = command_line->GetArgs();
    CommandLine::StringVector argv = command_line->original_argv();

    // Ignore first non-switch arg if it's not a standalone package.
    bool ignore_arg = !package->self_extract();
    for (unsigned i = 1; i < argv.size(); ++i) {
      if (ignore_arg && argv[i] == args[0]) {
        ignore_arg = false;
        continue;
      }

      result->AppendString(argv[i]);
    }

    return;
  } else if (method == "ClearCache") {
    ClearCache(GetRenderProcessHost());
  } else if (method == "GetPackage") {
    result->AppendString(shell->GetPackage()->package_string());
    return;
  }

  NOTREACHED() << "Calling unknown sync method " << method << " of App";
}

// static
void App::CloseAllWindows() {
  std::vector<Shell*> windows = Shell::windows();

  for (size_t i = 0; i < windows.size(); ++i) {
    // Only send close event to browser windows, since devtools windows will
    // be automatically closed.
    if (!windows[i]->is_devtools()) {
      // If there is no js object bound to the window, then just close.
      if (windows[i]->ShouldCloseWindow())
        delete windows[i];
    }
  }
}

// static
void App::Quit(RenderProcessHost* render_process_host) {
  // Send the quit message.
  int no_use;
  if (render_process_host) {
    render_process_host->Send(new ViewMsg_WillQuit(&no_use));
  }else{
    std::set<RenderProcessHost*> rphs;
    std::set<RenderProcessHost*>::iterator it;

    GetRenderProcessHosts(rphs);
    for (it = rphs.begin(); it != rphs.end(); it++) {
      RenderProcessHost* rph = *it;
      DCHECK(rph != NULL);

      rph->Send(new ViewMsg_WillQuit(&no_use));
    }
  }
  // Then quit.
  MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
}

// static
void App::EmitOpenEvent(const std::string& path) {
  std::set<RenderProcessHost*> rphs;
  std::set<RenderProcessHost*>::iterator it;

  GetRenderProcessHosts(rphs);
  for (it = rphs.begin(); it != rphs.end(); it++) {
    RenderProcessHost* rph = *it;
    DCHECK(rph != NULL);

    rph->Send(new ShellViewMsg_Open(path));
  }
}

void App::ClearCache(content::RenderProcessHost* render_process_host) {
  render_process_host->Send(new ShellViewMsg_ClearCache());
  nw::RemoveHttpDiskCache(render_process_host->GetBrowserContext(),
                          render_process_host->GetID());
}

}  // namespace api


mkdir -p tmp
tmpdir=$PWD/tmp
pushd third_party
find node -iname '*.h' -or -iname '*.gypi' -or -iname gyp -or -iname gyp_addon | xargs tar cf - | (cd $tmpdir; tar xf - )
popd
rm -rf $tmpdir/node/deps/v8
find v8 -iname '*.h' -or -iname '*.gypi' -or -iname gyp -or -iname gyp_addon | xargs tar cf - | (cd $tmpdir/node/deps; tar xf - )
rm -rf $tmpdir/node/deps/v8/build
rm -rf $tmpdir/node/deps/v8/test
rm -rf $tmpdir/node/deps/v8/out
rm -rf $tmpdir/node/deps/npm/node_modules

cat $tmpdir/node/src/node.h | sed -e 's|third_party/node/deps/uv/include/uv.h|uv.h|' > tmp_node.h && mv tmp_node.h $tmpdir/node/src/node.h

pushd tmp
tar czf ../nw-headers-v0.6.1.tar.gz node
popd