text: import assert from 'assert';
  import nodeFS from 'fs';
  import path from 'path';
  import {
    assertBundles,
    bundler,
    getNextBuild,
    run,
    assertBundleTree,
    nextBundle,
    ncp,
    inputFS as fs,
    sleep,
    symlinkPrivilegeWarning,
    outputFS,
    overlayFS,
  } from '@parcel/test-utils';
  import {symlinkSync} from 'fs';
  
  const inputDir = path.join(__dirname, '/watcher');
  const distDir = path.join(inputDir, 'dist');
  
  describe('watcher', function() {
    let subscription;
    afterEach(async () => {
      if (subscription) {
        await subscription.unsubscribe();
      }
      subscription = null;
    });
  
    it('should rebuild on source file change', async function() {
      await outputFS.mkdirp(inputDir);
      await outputFS.writeFile(
        path.join(inputDir, '/index.js'),
        'module.exports = "hello"',
        'utf8',
      );
      let b = bundler(path.join(inputDir, '/index.js'), {inputFS: overlayFS});
      subscription = await b.watch();
      let buildEvent = await getNextBuild(b);
      let output = await run(buildEvent.bundleGraph);
      assert.equal(output, 'hello');
  
      await outputFS.writeFile(
        path.join(inputDir, '/index.js'),
        'module.exports = "something else"',
        'utf8',
      );
      buildEvent = await getNextBuild(b);
      output = await run(buildEvent.bundleGraph);
      assert.equal(output, 'something else');
    });
  
    it('should rebuild on a source file change after a failed transformation', async () => {
      await outputFS.mkdirp(inputDir);
      await outputFS.writeFile(
        path.join(inputDir, '/index.js'),
        'syntax\\error',
        'utf8',
      );
      let b = bundler(path.join(inputDir, '/index.js'), {inputFS: overlayFS});
      subscription = await b.watch();
      let buildEvent = await getNextBuild(b);
      assert.equal(buildEvent.type, 'buildFailure');
      await outputFS.writeFile(
        path.join(inputDir, '/index.js'),
        'module.exports = "hello"',
        'utf8',
      );
      buildEvent = await getNextBuild(b);
      let output = await run(buildEvent.bundleGraph);
  
      assert.equal(output, 'hello');
    });
  
    it('should rebuild on a config file change', async function() {
      let inDir = path.join(__dirname, 'integration/parcelrc-custom');
      let outDir = path.join(inDir, 'dist');
  
      await ncp(path.join(__dirname, 'integration/parcelrc-custom'), inDir);
      await ncp(
        path.dirname(require.resolve('@parcel/config-default')),
        path.join(inDir, 'node_modules', '@parcel', 'config-default'),
      );
      let copyPath = path.join(inDir, 'configCopy');
      let configPath = path.join(inDir, '.parcelrc');
      let b = bundler(path.join(inDir, 'index.js'), {
        inputFS: overlayFS,
        targets: {
          main: {
            distDir: outDir,
          },
        },
      });
      subscription = await b.watch();
      await getNextBuild(b);
      let distFile = await outputFS.readFile(
        path.join(outDir, 'index.js'),
        'utf8',
      );
      assert(distFile.includes('() => null'));
      await outputFS.copyFile(copyPath, configPath);
      await getNextBuild(b);
      distFile = await outputFS.readFile(path.join(outDir, 'index.js'), 'utf8');
      assert(distFile.includes('TRANSFORMED CODE'));
    });
  
    it('should rebuild properly when a dependency is removed', async function() {
      await ncp(path.join(__dirname, 'integration/babel-default'), inputDir);
  
      let b = bundler(path.join(inputDir, 'index.js'), {
        inputFS: overlayFS,
        targets: {
          main: {
            engines: {
              node: '^8.0.0',
            },
            distDir,
          },
        },
      });
  
      subscription = await b.watch();
      await getNextBuild(b);
      let distFile = await outputFS.readFile(
        path.join(distDir, 'index.js'),
        'utf8',
      );
      assert(distFile.includes('Foo'));
      await outputFS.writeFile(
        path.join(inputDir, 'index.js'),
        'console.log("no more dependencies")',
      );
      await getNextBuild(b);
      distFile = await outputFS.readFile(path.join(distDir, 'index.js'), 'utf8');
      assert(!distFile.includes('Foo'));
    });
  
    it.skip('should re-generate bundle tree when files change', async function() {
      await ncp(path.join(__dirname, '/integration/dynamic-hoist'), inputDir);
  
      let b = bundler(path.join(inputDir, '/index.js'), {watch: true});
      let bundle = await b.bundle();
  
      await assertBundleTree(bundle, {
        name: 'index.js',
        assets: [
          'index.js',
          'common.js',
          'common-dep.js',
          'bundle-loader.js',
          'bundle-url.js',
          'js-loader.js',
        ],
        childBundles: [
          {
            assets: ['a.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            assets: ['b.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            type: 'map',
          },
        ],
      });
  
      let output = await run(bundle);
      assert.equal(await output(), 7);
  
      // change b.js so that it no longer depends on common.js.
      // This should cause common.js and dependencies to no longer be hoisted to the root bundle.
      await sleep(100);
      fs.writeFile(path.join(inputDir, '/b.js'), 'module.exports = 5;');
  
      bundle = await nextBundle(b);
      await assertBundleTree(bundle, {
        name: 'index.js',
        assets: ['index.js', 'bundle-loader.js', 'bundle-url.js', 'js-loader.js'],
        childBundles: [
          {
            assets: ['a.js', 'common.js', 'common-dep.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            assets: ['b.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            type: 'map',
          },
        ],
      });
  
      output = await run(bundle);
      assert.equal(await output(), 8);
    });
  
    it.skip('should only re-package bundles that changed', async function() {
      await ncp(path.join(__dirname, '/integration/dynamic-hoist'), inputDir);
      let b = bundler(path.join(inputDir, '/index.js'), {watch: true});
  
      await b.bundle();
      let mtimes = (await fs.readdir(path.join(__dirname, '/dist'))).map(
        f =>
          (nodeFS.statSync(path.join(__dirname, '/dist/', f)).mtime.getTime() /
            1000) |
          0,
      );
  
      await sleep(1100); // mtime only has second level precision
      fs.writeFile(
        path.join(inputDir, '/b.js'),
        'module.exports = require("./common")',
      );
  
      await nextBundle(b);
      let newMtimes = (await fs.readdir(path.join(__dirname, '/dist'))).map(
        f =>
          (nodeFS.statSync(path.join(__dirname, '/dist/', f)).mtime.getTime() /
            1000) |
          0,
      );
      assert.deepEqual(mtimes.sort().slice(0, 2), newMtimes.sort().slice(0, 2));
      assert.notEqual(mtimes[mtimes.length - 1], newMtimes[newMtimes.length - 1]);
    });
  
    it.skip('should unload assets that are orphaned', async function() {
      await ncp(path.join(__dirname, '/integration/dynamic-hoist'), inputDir);
      let b = bundler(path.join(inputDir, '/index.js'), {watch: true});
  
      let bundle = await b.bundle();
      await assertBundleTree(bundle, {
        name: 'index.js',
        assets: [
          'index.js',
          'common.js',
          'common-dep.js',
          'bundle-loader.js',
          'bundle-url.js',
          'js-loader.js',
        ],
        childBundles: [
          {
            assets: ['a.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            assets: ['b.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            type: 'map',
          },
        ],
      });
  
      let output = await run(bundle);
      assert.equal(await output(), 7);
  
      assert(b.loadedAssets.has(path.join(inputDir, '/common-dep.js')));
  
      // Get rid of common-dep.js
      await sleep(100);
      fs.writeFile(path.join(inputDir, '/common.js'), 'module.exports = 5;');
  
      bundle = await nextBundle(b);
      await assertBundleTree(bundle, {
        name: 'index.js',
        assets: [
          'index.js',
          'common.js',
          'bundle-loader.js',
          'bundle-url.js',
          'js-loader.js',
        ],
        childBundles: [
          {
            assets: ['a.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            assets: ['b.js'],
            childBundles: [
              {
                type: 'map',
              },
            ],
          },
          {
            type: 'map',
          },
        ],
      });
  
      output = await run(bundle);
      assert.equal(await output(), 13);
  
      assert(!b.loadedAssets.has(path.join(inputDir, 'common-dep.js')));
    });
  
    it.skip('should recompile all assets when a config file changes', async function() {
      await ncp(path.join(__dirname, '/integration/babel'), inputDir);
      let b = bundler(path.join(inputDir, 'index.js'), {watch: true});
  
      await b.bundle();
      let file = await fs.readFile(
        path.join(__dirname, '/dist/index.js'),
        'utf8',
      );
      assert(!file.includes('function Foo'));
      assert(!file.includes('function Bar'));
  
      // Change babelrc, should recompile both files
      let babelrc = JSON.parse(
        await fs.readFile(path.join(inputDir, '/.babelrc'), 'utf8'),
      );
      babelrc.presets[0][1].targets.browsers.push('IE >= 11');
  
      await sleep(100);
      fs.writeFile(path.join(inputDir, '/.babelrc'), JSON.stringify(babelrc));
  
      await nextBundle(b);
      file = await fs.readFile(path.join(__dirname, '/dist/index.js'), 'utf8');
      assert(file.includes('function Foo'));
      assert(file.includes('function Bar'));
    });
  
    it.skip('should rebuild if the file behind a symlink changes', async function() {
      await ncp(
        path.join(__dirname, '/integration/commonjs-with-symlinks/'),
        inputDir,
      );
  
      try {
        // Create the symlink here to prevent cross platform and git issues
        symlinkSync(
          path.join(inputDir, 'local.js'),
          path.join(inputDir, 'src/symlinked_local.js'),
        );
  
        let b = bundler(path.join(inputDir, '/src/index.js'), {
          watch: true,
        });
  
        let bundle = await b.bundle();
        let output = await run(bundle);
  
        assert.equal(output(), 3);
  
        await sleep(100);
        fs.writeFile(
          path.join(inputDir, '/local.js'),
          'exports.a = 5; exports.b = 5;',
        );
  
        bundle = await nextBundle(b);
        output = await run(bundle);
        assert.equal(output(), 10);
      } catch (e) {
        if (e.code == 'EPERM') {
          symlinkPrivilegeWarning();
          this.skip();
        } else {
          throw e;
        }
      }
    });
  
    it('should add and remove necessary runtimes to bundles', async () => {
      await ncp(path.join(__dirname, 'integration/dynamic'), inputDir);
  
      let indexPath = path.join(inputDir, 'index.js');
  
      let b = bundler(indexPath, {inputFS: overlayFS});
      let bundleGraph;
      subscription = await b.watch((err, event) => {
        assert(event.type === 'buildSuccess');
        bundleGraph = event.bundleGraph;
      });
      await getNextBuild(b);
      assertBundles(bundleGraph, [
        {
          name: 'index.js',
          assets: [
            'index.js',
            'bundle-url.js',
            'cacheLoader.js',
            'js-loader.js',
            'JSRuntime.js',
          ],
        },
        {assets: ['local.js']},
      ]);
  
      await outputFS.writeFile(path.join(inputDir, 'other.js'), '');
      await outputFS.writeFile(
        indexPath,
        (await outputFS.readFile(indexPath, 'utf8')) +
          "\nimport('./other.js');\n",
      );
  
      await getNextBuild(b);
      assertBundles(bundleGraph, [
        {
          name: 'index.js',
          assets: [
            'index.js',
            'bundle-url.js',
            'cacheLoader.js',
            'js-loader.js',
            'JSRuntime.js',
            'JSRuntime.js',
          ],
        },
        {assets: ['local.js']},
        {assets: ['other.js']},
      ]);
  
      await outputFS.writeFile(indexPath, '');
  
      await getNextBuild(b);
      assertBundles(bundleGraph, [
        {
          name: 'index.js',
          assets: ['index.js'],
        },
      ]);
    });
  });
  