ایک زبردست اشتہارات کو روکنے والا سافٹویئر. کم میموری اور cpu استعمال کرتا ہے مگر کام بہترین کرتا ہے.

اس کا بہترین اور پراثر کام کرنے کی تصاویر:
https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared

ہدایات: بڑا آن/ آف کا بٹن دبا کر آپ موجودہ ویب سائٹ پر uBlock کو فعال یا غیر فعال کر سکتے ہیں. یہ بٹن صرف موجودہ ویب سائٹ کے لئے ہے، باقی ویب سائٹس کو اس سے کوئی فرق نہیں پڑے گا.

***

Flexible, it's more than an "ad blocker": it can also read and create filters from hosts files.

یہ والے فلٹر پہلے سے لاگو ہوں گے:

- EasyList
- Peter Lowe’s Ad server list
- EasyPrivacy
- Malware domains

More lists are available for you to select if you wish:

- Fanboy’s Enhanced Tracking List
- Dan Pollock’s hosts file
- hpHosts’s Ad and tracking servers
- MVPS HOSTS
- Spam404
- And many others

جتنے زیادہ فلٹر لگائیں گے اتنی زیادہ میموری لے گا. Yet, even after adding Fanboy's two extra lists, hpHosts’s Ad and tracking servers, uBlock still has a lower memory footprint than other very popular blockers out there.

Also, be aware that selecting some of these extra lists may lead to higher likelihood of web site breakage -- especially those lists which are normally used as hosts file.

***

Without the preset lists of filters, this extension is nothing. So if ever you really do want to contribute something, think about the people working hard to maintain the filter lists you are using, which were made available to use by all for free.

***

مفت.
اوپن سورس عوامی لائسنس(جی.پی.ایل ورژن ٣) کے ساتھ
عوام کے لیے، عوام کا بنایا ہوا.

معاونین کی فہرست Github پر دیکھیں:
https://github.com/gorhill/uBlock/graphs/contributors
مترجمین کی فہرست Crowdin پر دیکھیں:
https://crowdin.net/project/ublock

***

It's quite an early version, keep this in mind when you review.

پراجیکٹ میں ترقیاتی کام کا ریکارڈ:
https://github.com/gorhill/uBlock/releases


keep keep keep keep keep replace keep keep keep keep

ಒಂದು ದಕ್ಷ ನಿರ್ಬಂಧಕ: ಮಿತವಾದ ಮೆಮೊರಿ ಹಾಗೂ ಸಿಪಿಯೂ ಹೆಜ್ಜೆಗುರುತು ಇದ್ದರೂ, ಇತರ ಜನಪ್ರಿಯ ನಿರ್ಬಂಧಕಗಳಿಗಿಂತ ಸಾವಿರಾರು ಹೆಚ್ಚು ಶೋಧಕಗಳನ್ನು ಹಾಕಿ ಮತ್ತು ಜಾರಿಗೆ ತರಬಲ್ಲದು.

ಇದರ ದಕ್ಷತೆಯ ಸಚಿತ್ರ ಪಕ್ಷಿನೋಟ: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared

ಬಳಕೆ: ಪಾಪ್ಅಪ್ ದೊಡ್ಡ ವಿದ್ಯುತ್ ಬಟನ್ ಶಾಶ್ವತವಾಗಿ / ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಬಹುದು ಪ್ರಸ್ತುತ ವೆಬ್ ಸೈಟ್ uBlock ಸಕ್ರಿಯಗೊಳಿಸಲು ಹೊಂದಿದೆ. ಇದು ಜಾಗತಿಕ ವಿದ್ಯುತ್ ಬಟನ್ ಅಲ್ಲ, ಪ್ರಸ್ತುತ ಜಾಲತಾಣಕ್ಕೆ ಮಾತ್ರ ಅನ್ವಯಿಸುತ್ತದೆ.

***

ಹೊಂದಿಕೊಳ್ಳುವಿಕೆ, ಇದು ಒಂದು "ಜಾಹೀರಾತು ನಿರ್ಬಂಧಕಕ್ಕಿಂತ" ಹೆಚ್ಚು: ಇದು ಅತಿಥೇಯಗಳ ಕಡತಗಳನ್ನು ಓದಬಲ್ಲದು ಹಾಗೂ ಶೋಧಕಗಳು ರಚಿಸಬಲ್ಲದು.

ಫಿಲ್ಟರ್ಗಳ ಪಟ್ಟಿಗಳನ್ನು ಲೋಡ್ ಮಾಡಿ ಸಂಪೂರ್ಣವಾಗಿ ವಿಧಿಸಲಾಗಿದೆ:

ಸರಳವಾದ ಪಟ್ಟಿ
ಪೀಟರ್ ಲೋವೆ ಜಾಹೀರಾತು ಸರ್ವರ್ ಪಟ್ಟಿ
ಸರಳ ಗೌಪ್ಯತೆ
ಮಾಲ್ವೇರ್ ಡೊಮೇನ್ಗಳ

ನೀವು ಬಯಸಿದರೆ, ಆಯ್ಕೆ ಮಾಡಲು ಹೆಚ್ಚು ಪಟ್ಟಿಗಳನ್ನು ಲಭ್ಯವಿದೆ:

ಫ್ಯಾನ್ಬಾಯ್ ಅವರ ಉತ್ತಮ ಸುಧಾರಿತ ಪಟ್ಟಿ
ಡಾನ್ ಪೊಲಾಕ್ ಅತಿಥೇಯಗಳ ಕಡತ
- ಹೆಚ್ಪಿಹೋಸ್ಟ್ನ ಜಾಹೀರಾತು ಮತ್ತು ಜಾಡುಹಿಡಿಯುವಿಕೆಯ ಸರ್ವರ್ಗಳು
-ಎಮ್ ವಿ ಪಿ ಎಸ್ ಹೋಸ್ಟ್ಗಳು
ಸ್ಪಾಮ್ ೪೦೪
- ಹಾಗೂ ಹಲವಾರು

ಸಹಜವಾಗಿ, ಹೆಚ್ಚು ಶೋಧಕಗಳು ಸಕ್ರಿಯವಾಗಿದ್ದಲ್ಲಿ, ಹೆಚ್ಚಿನ ಸ್ಮೃತಿ ಹೆಜ್ಜೆಗುರುತು. ಹೆಚ್ಪಿಹೋಸ್ಟ್ನ ಜಾಹೀರಾತು ಮತ್ತು ಜಾಡುಹಿಡಿಯುವಿಕೆಯ ಸರ್ವರ್ಗಳು ಫ್ಯಾನ್ಬಾಯ್ ಎರಡು ಹೆಚ್ಚುವರಿ ಪಟ್ಟಿಗಳನ್ನು ಸೇರಿಸಿದ ನಂತರವೂ, uBlock ಇತರ ಜನಪ್ರಿಯ ನಿರ್ಬಂಧಕಗಳಿಗಿಂತ ಕಡಿಮೆ ಸ್ಮೃತಿ ಹೆಜ್ಜೆಗುರುತನ್ನು ಹೊಂದಿದೆ.

ಅಲ್ಲದೆ, ಸಾಮಾನ್ಯವಾಗಿ ಈ ಹೆಚ್ಚುವರಿ ಪಟ್ಟಿಗಳಲ್ಲಿ ಕೆಲವನ್ನು ಆಯ್ಕೆ ಮಾಡುವುದು, ಜಾಲತಾಣ ಒಡೆಯುವಿಕೆಯ ಹೆಚ್ಚಿನ ಸಂಭವನೀಯತೆಗೆ ಕಾರಣವಾಗಬಹುದು, ವಿಶೇಷವಾಗಿ ಅತಿಥೇಯ ಕಡತವಾಗಿ ಬಳಸಲ್ಪಡುವ ಪಟ್ಟಿಗಳು - ತಿಳಿದಿರಲಿ.

***

ಪೂರ್ವನಿಯೋಜಿತ ಶೋಧಕಗಳ ಪಟ್ಟಿಗಳಿಲ್ಲದೆ, ಈ ಎಕ್ಸಟೆನ್ಶನ್ ಏನೂ ಅಲ್ಲ. ನೀವು ಎಂದಾದರೂ, ನಿಜವಾಗಿಯೂ ಏನಾದರೂ ಕೊಡುಗೆ ನೀಡಬೇಕು ಎಂದೆನಿಸಿದರೆ,
ಎಲ್ಲರೂ ಉಚಿತವಾಗಿ ಬಳಸಲು ದೊರೆಯುವಂತಾಗಿರುವಾ, ನೀವು ಬಳಸುವ ಶೋಧಕಪಟ್ಟಿಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಕಷ್ಟಪಟ್ಟು ಕೆಲಸ ಮಾಡುತಿರುವವರ ಬಗ್ಗೆ ಯೋಚಿಸಿ.

***

ಉಚಿತ.
ಸಾರ್ವಜನಿಕ ಪರವಾನಗಿ ಮುಕ್ತ ಆಕರ (ಜಿ. ಪಿ. ಎಲ್. ವಿ೩)
ಬಳಕೆದಾರರಿಂದ ಬಳಕೆದಾರರಿಗಾಗಿ.

ನೀಡುಗರು @ ಗಿಟ್ಹಬ್: https://github.com/gorhill/uBlock/graphs/contributors
ನೀಡುಗರು @ ಕ್ರೌಡ್ಇನ್: https://crowdin.net/project/ublock

***

ಇದು ಸಾಕಷ್ಟು ಆರಂಭಿಕ ಆವೃತ್ತಿ, ನೀವು ಪರಿಶೀಲಿಸುವಾಗ ಇದನ್ನು ಮನಸ್ಸಿನಲ್ಲಿ ಇರಿಸಿಕೊಳ್ಳಿ.

ಪ್ರಾಜೆಕ್ಟ್ ಬದಲಾವಣೆಗಳ ಲಾಗ್ :
https://github.com/gorhill/uBlock/releases


uBlock Origin

privacy storage tabs webNavigation webRequest webRequestBlocking <all_urls>


import md5 from '@parcel/utils/lib/md5';

export const nodeFromRootDir = (rootDir: string) => ({

keep keep keep keep keep replace keep keep keep keep keep

import Module from 'module';
import process from 'process';
import path from 'path';
import {addHook} from 'pirates';
import Parcel, {Dependency, Environment} from '@parcel/core';
import syncPromise from '@parcel/utils/lib/syncPromise';

const originalRequire = Module.prototype.require;
const DEFAULT_CLI_OPTS = {
  watch: false
};

let hooks = {};

export default function register(opts = DEFAULT_CLI_OPTS) {
  // Replace old hook, as this one likely contains options.
  if (hooks) {
    for (let extension in hooks) {
      hooks[extension]();
    }
  }

  let parcel = new Parcel({
    entries: [path.join(process.cwd(), 'index.js')],
    cliOpts: opts
  });

  let environment = new Environment({
    context: 'node',
    engines: {
      node: process.versions.node
    }
  });

  syncPromise(parcel.init());

  let isProcessing = false;

  // As Parcel is pretty much fully asynchronous, create an async function and wrap it in a syncPromise later...
  async function fileProcessor(code, filename) {
    if (isProcessing) {
      return code;
    }

    try {
      isProcessing = true;
      let result = await parcel.runTransform({
        filePath: filename,
        env: environment
      });

      if (result.assets && result.assets.length >= 1) {
        let output = '';
        let asset = result.assets.find(a => a.type === 'js');
        if (asset) {
          output = (await asset.getOutput()).code;
        }
        return output;
      }
    } catch (e) {
      console.error('@parcel/register failed to process: ', filename);
      console.error(e);
    } finally {
      isProcessing = false;
    }

    return '';
  }

  let hookFunction = (...args) => syncPromise(fileProcessor(...args));

  function resolveFile(currFile, targetFile) {
    try {
      isProcessing = true;
      let dep = new Dependency({
        moduleSpecifier: targetFile,
        sourcePath: currFile,
        env: environment
      });

      targetFile = syncPromise(parcel.resolverRunner.resolve(dep));

      let targetFileExtension = path.extname(targetFile);
      if (!hooks[targetFileExtension]) {
        hooks[targetFileExtension] = addHook(hookFunction, {
          exts: [targetFileExtension],
          ignoreNodeModules: false
        });
      }

      return targetFile;
    } finally {
      isProcessing = false;
    }
  }

  Module.prototype.require = function(filePath, ...args) {
    let resolved = filePath;
    if (!isProcessing) {
      resolved = resolveFile(this.filename, filePath);
    }

    return originalRequire.call(this, resolved, ...args);
  };
}

// Hook into require, this will be overwritten whenever it is called again or explicitly called with opts
register();


<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<img src="100x100.png" srcset="200x200.png 250w, 300x300.png 500w">
</body>
</html>


var local = require('./local');

function run() {
  output(local.a + local.b);
}

run();

// eslint-disable-next-line no-undef
reportModuleId(module.id);

module.hot.dispose(function () {
  output('dispose-' + module.id);
});

module.hot.accept(function () {
  output('accept-' + module.id);
});


require('@parcel/register');

const count = require('./count.js');
const number = require('./number.js');
const something = require('something');

const numberOne = number();
const numberTwo = number();

console.log(`${numberOne} + ${numberTwo} =`, count(numberOne, numberTwo));
console.log(something());


var {foo, bar} = require('./b');
output = foo + bar;


parcel-bundler

Blazing fast, zero configuration web application bundler

rm -rf coverage/* && nyc yarn test --reporter mocha-multi-reporters --reporter-options configFile=./test/mochareporters.json

nyc report --reporter=lcov --reporter=cobertura --reporter=html && codecov

yarn test-coverage && yarn report-coverage

yarn minify && babel src -d lib && ncp src/builtins lib/builtins

terser -c -m -o src/builtins/prelude.min.js src/builtins/prelude.js && terser -c -m -o src/builtins/prelude2.min.js src/builtins/prelude2.js

eslint . && prettier "./{src,bin,test}/**/*.{js,json,md}" --list-different

node -e "console.log('\u001b[35m\u001b[1mLove Parcel? You can now donate to our open collective:\u001b[22m\u001b[39m\n > \u001b[34mhttps://opencollective.com/parcel/donate\u001b[0m')"


const Asset = require('../Asset');
const localRequire = require('../utils/localRequire');
const {promisify} = require('@parcel/utils');
const Resolver = require('../Resolver');
const fs = require('@parcel/fs');
const path = require('path');
const parseCSSImport = require('../utils/parseCSSImport');

class LESSAsset extends Asset {
  constructor(name, options) {
    super(name, options);
    this.type = 'css';
  }

  async parse(code) {
    // less should be installed locally in the module that's being required
    let less = await localRequire('less', this.name);
    let render = promisify(less.render.bind(less));

    let opts =
      (await this.getConfig(['.lessrc', '.lessrc.js'], {packageKey: 'less'})) ||
      {};
    opts.filename = this.name;
    opts.plugins = (opts.plugins || []).concat(urlPlugin(this));

    return await render(code, opts);
  }

  collectDependencies() {
    for (let dep of this.ast.imports) {
      this.addDependency(dep, {includedInParent: true});
    }
  }

  generate() {
    return [
      {
        type: 'css',
        value: this.ast ? this.ast.css : '',
        hasDependencies: false
      }
    ];
  }
}

function urlPlugin(asset) {
  return {
    install: (less, pluginManager) => {
      let visitor = new less.visitors.Visitor({
        visitUrl: node => {
          node.value.value = asset.addURLDependency(
            node.value.value,
            node.currentFileInfo.filename
          );
          return node;
        }
      });

      visitor.run = visitor.visit;
      pluginManager.addVisitor(visitor);

      let LessFileManager = getFileManager(less, asset.options);
      pluginManager.addFileManager(new LessFileManager());
    }
  };
}

function getFileManager(less, options) {
  const resolver = new Resolver({
    extensions: ['.css', '.less'],
    rootDir: options.rootDir
  });

  class LessFileManager extends less.FileManager {
    supports() {
      return true;
    }

    supportsSync() {
      return false;
    }

    async loadFile(filename, currentDirectory) {
      filename = parseCSSImport(filename);
      let resolved = await resolver.resolve(
        filename,
        path.join(currentDirectory, 'index')
      );
      return {
        contents: await fs.readFile(resolved.path, 'utf8'),
        filename: resolved.path
      };
    }
  }

  return LessFileManager;
}

module.exports = LESSAsset;


module.exports = {
  extends: 'eslint:recommended',
  parser: 'babel-eslint',
  plugins: ['flowtype'],
  parserOptions: {
    ecmaVersion: 8,
    ecmaFeatures: {
      jsx: true
    }
  },
  env: {
    node: true,
    es6: true
  },
  globals: {
    parcelRequire: true,
    define: true
  }
};


const {add} = require('./add.wasm');

self.addEventListener('message', () => {
  self.postMessage(add(2, 3));
});


const URL = require('url');
const path = require('path');

/**
 * Joins a path onto a URL, and normalizes Windows paths
 * e.g. from \path\to\res.js to /path/to/res.js.
 */
module.exports = function(publicURL, assetPath) {
  const url = URL.parse(publicURL, false, true);
  const assetUrl = URL.parse(assetPath);
  url.pathname = path.posix.join(url.pathname, assetUrl.pathname);
  url.search = assetUrl.search;
  url.hash = assetUrl.hash;
  return URL.format(url);
};


console.error('@parcel/register failed to process: ', filename);


export default function () {
  return 3;
}


module.exports = function() {
  return process.pid;
};


export function test(arg) {
    return arg(exports)
}


module.exports = {
extends: 'eslint:recommended',
parser: 'babel-eslint',
plugins: ['flowtype'],
parserOptions: {
ecmaVersion: 8,


import {foo} from './c';

module.exports = import('./b').then(function (b) {
  return foo + b.foo;
});


const types = require('@babel/types');
const template = require('@babel/template').default;
const traverse = require('@babel/traverse').default;
const urlJoin = require('../utils/urlJoin');
const isURL = require('../utils/is-url');
const nodeBuiltins = require('node-libs-browser');

const requireTemplate = template('require("_bundle_loader")');
const argTemplate = template('require.resolve(MODULE)');
const serviceWorkerPattern = ['navigator', 'serviceWorker', 'register'];

module.exports = {
  ImportDeclaration(node, asset) {
    asset.isES6Module = true;
    addDependency(asset, node.source);
  },

  ExportNamedDeclaration(node, asset) {
    asset.isES6Module = true;
    if (node.source) {
      addDependency(asset, node.source);
    }
  },

  ExportAllDeclaration(node, asset) {
    asset.isES6Module = true;
    addDependency(asset, node.source);
  },

  ExportDefaultDeclaration(node, asset) {
    asset.isES6Module = true;
  },

  CallExpression(node, asset, ancestors) {
    let {callee, arguments: args} = node;

    let isRequire =
      types.isIdentifier(callee) &&
      callee.name === 'require' &&
      args.length === 1 &&
      types.isStringLiteral(args[0]) &&
      !hasBinding(ancestors, 'require') &&
      !isInFalsyBranch(ancestors);

    if (isRequire) {
      let optional = ancestors.some(a => types.isTryStatement(a)) || undefined;
      addDependency(asset, args[0], {optional});
      return;
    }

    let isDynamicImport =
      callee.type === 'Import' &&
      args.length === 1 &&
      types.isStringLiteral(args[0]);

    if (isDynamicImport) {
      if (isURL(args[0].value)) return;

      asset.addDependency('_bundle_loader');
      addDependency(asset, args[0], {dynamic: true});

      node.callee = requireTemplate().expression;
      node.arguments[0] = argTemplate({MODULE: args[0]}).expression;
      asset.isAstDirty = true;
      return;
    }

    const isRegisterServiceWorker =
      types.isStringLiteral(args[0]) &&
      types.matchesPattern(callee, serviceWorkerPattern);

    if (isRegisterServiceWorker) {
      // Treat service workers as an entry point so filenames remain consistent across builds.
      // https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#avoid_changing_the_url_of_your_service_worker_script
      addURLDependency(asset, args[0], {entry: true, isolated: true});
      return;
    }
  },

  NewExpression(node, asset) {
    const {callee, arguments: args} = node;

    const isWebWorker =
      callee.type === 'Identifier' &&
      (callee.name === 'Worker' || callee.name === 'SharedWorker') &&
      args.length === 1 &&
      types.isStringLiteral(args[0]);

    if (isWebWorker) {
      addURLDependency(asset, args[0], {isolated: true});
      return;
    }
  }
};

function hasBinding(node, name) {
  if (Array.isArray(node)) {
    return node.some(ancestor => hasBinding(ancestor, name));
  } else if (
    types.isProgram(node) ||
    types.isBlockStatement(node) ||
    types.isBlock(node)
  ) {
    return node.body.some(statement => hasBinding(statement, name));
  } else if (
    types.isFunctionDeclaration(node) ||
    types.isFunctionExpression(node) ||
    types.isArrowFunctionExpression(node)
  ) {
    return (
      (node.id && node.id.name === name) ||
      node.params.some(
        param => types.isIdentifier(param) && param.name === name
      )
    );
  } else if (types.isVariableDeclaration(node)) {
    return node.declarations.some(declaration => declaration.id.name === name);
  }

  return false;
}

function isInFalsyBranch(ancestors) {
  // Check if any ancestors are if statements
  return ancestors.some((node, index) => {
    if (types.isIfStatement(node)) {
      let res = evaluateExpression(node.test);
      if (res && res.confident) {
        // If the test is truthy, exclude the dep if it is in the alternate branch.
        // If the test if falsy, exclude the dep if it is in the consequent branch.
        let child = ancestors[index + 1];
        return res.value ? child === node.alternate : child === node.consequent;
      }
    }
  });
}

function evaluateExpression(node) {
  // Wrap the node in a standalone program so we can traverse it
  node = types.file(types.program([types.expressionStatement(node)]));

  // Find the first expression and evaluate it.
  let res = null;
  traverse(node, {
    Expression(path) {
      res = path.evaluate();
      path.stop();
    }
  });

  return res;
}

function addDependency(asset, node, opts = {}) {
  // Don't bundle node builtins
  if (asset.options.target === 'node' && node.value in nodeBuiltins) {
    return;
  }

  // If this came from an inline <script> tag, throw an error.
  // TODO: run JSPackager on inline script tags.
  let inlineHTML =
    asset.options.rendition && asset.options.rendition.inlineHTML;
  if (inlineHTML) {
    let err = new Error(
      'Imports and requires are not supported inside inline <script> tags yet.'
    );
    err.loc = node.loc && node.loc.start;
    throw err;
  }

  if (!asset.options.bundleNodeModules) {
    const isRelativeImport = /^[/~.]/.test(node.value);
    if (!isRelativeImport) return;
  }

  opts.loc = node.loc && node.loc.start;
  asset.addDependency(node.value, opts);
}

function addURLDependency(asset, node, opts = {}) {
  opts.loc = node.loc && node.loc.start;

  let assetPath = asset.addURLDependency(node.value, opts);
  if (!isURL(assetPath)) {
    assetPath = urlJoin(asset.options.publicURL, assetPath);
  }
  node.value = assetPath;
  asset.isAstDirty = true;
}


- fix: crash on first launch due missing directory (#78, #90, #93)

import fs from 'fs'
import path from 'path'
import { ipcMain, BrowserWindow } from 'electron'
import { windows } from './createWindow'
import { getPath, log } from './utils'

const FILE_NAME = 'preference.md'
const staticPath = path.join(__static, FILE_NAME)
const userDataPath = path.join(getPath('userData'), FILE_NAME)

class Preference {
  constructor (staticPath, userDataPath) {
    this.cache = null
    this.staticPath = staticPath
    this.userDataPath = userDataPath

    if (!fs.existsSync(userDataPath)) {
      const content = fs.readFileSync(staticPath, 'utf-8')
      fs.writeFileSync(userDataPath, content, 'utf-8')
    }
  }

  getAll () {
    const JSON_REG = /```json(.+)```/g
    const { userDataPath, cache } = this
    if (cache) {
      return cache
    } else {
      const content = fs.readFileSync(userDataPath, 'utf-8')
      try {
        const userSetting = JSON_REG.exec(content.replace(/\n/g, ''))[1]
        this.cache = JSON.parse(userSetting)
        return this.cache
      } catch (err) {
        log(err)
      }
    }
  }

  setItem (key, value) {
    const { userDataPath } = this
    const preUserSetting = this.getAll()
    const newUserSetting = this.cache = Object.assign({}, preUserSetting, { [key]: value })

    return new Promise((resolve, reject) => {
      const content = fs.readFileSync(userDataPath, 'utf-8')
      const tokens = content.split('```')
      const newContent = tokens[0] +
        '```json\n' +
        JSON.stringify(newUserSetting, null, 2) +
        '\n```' +
        tokens[2]
      fs.writeFile(userDataPath, newContent, 'utf-8', err => {
        if (err) reject(err)
        else resolve(newUserSetting)
      })
    })
  }
}

const preference = new Preference(staticPath, userDataPath)

ipcMain.on('AGANI::ask-for-user-preference', e => {
  const win = BrowserWindow.fromWebContents(e.sender)
  win.webContents.send('AGANI::user-preference', preference.getAll())
})

ipcMain.on('AGANI::set-user-preference', (e, pre) => {
  Object.keys(pre).map(key => {
    preference.setItem(key, pre[key])
      .then(() => {
        for (const win of windows.values()) {
          win.webContents.send('AGANI::user-preference', { [key]: pre[key] })
        }
      })
      .catch(log)
  })
})

export default preference


// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Utility function that allows modes to be combined. The mode given
// as the base argument takes care of most of the normal mode
// functionality, but a second (typically simple) mode is used, which
// can override the style of text. Both modes get to parse all of the
// text, but when both assign a non-null style to a piece of code, the
// overlay wins, unless the combine argument was true and not overridden,
// or state.overlay.combineTokens was true, in which case the styles are
// combined.

const overlayMode = CodeMirror => {
  CodeMirror.overlayMode = function (base, overlay, combine) {
    return {
      startState () {
        return {
          base: CodeMirror.startState(base),
          overlay: CodeMirror.startState(overlay),
          basePos: 0,
          baseCur: null,
          overlayPos: 0,
          overlayCur: null,
          streamSeen: null
        }
      },

      copyState (state) {
        return {
          base: CodeMirror.copyState(base, state.base),
          overlay: CodeMirror.copyState(overlay, state.overlay),
          basePos: state.basePos,
          baseCur: null,
          overlayPos: state.overlayPos,
          overlayCur: null
        }
      },

      token (stream, state) {
        if (stream !== state.streamSeen ||
          Math.min(state.basePos, state.overlayPos) < stream.start) {
          state.streamSeen = stream
          state.basePos = state.overlayPos = stream.start
        }

        if (stream.start === state.basePos) {
          state.baseCur = base.token(stream, state.base)
          state.basePos = stream.pos
        }

        if (stream.start === state.overlayPos) {
          stream.pos = stream.start
          state.overlayCur = overlay.token(stream, state.overlay)
          state.overlayPos = stream.pos
        }

        stream.pos = Math.min(state.basePos, state.overlayPos)

        // state.overlay.combineTokens always takes precedence over combine,
        // unless set to null
        if (state.overlayCur === null) {
          return state.baseCur
        } else if (
          (state.baseCur !== null &&
          state.overlay.combineTokens) ||
          (combine && state.overlay.combineTokens === null)
        ) {
          return state.baseCur + ' ' + state.overlayCur
        } else return state.overlayCur
      },

      indent: base.indent && function (state, textAfter) {
        return base.indent(state.base, textAfter)
      },

      electricChars: base.electricChars,

      innerMode (state) {
        return {
          state: state.base,
          mode: base
        }
      },

      blankLine (state) {
        var baseToken, overlayToken
        if (base.blankLine) baseToken = base.blankLine(state.base)
        if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay)

        return overlayToken == null
          ? baseToken
          : (combine && baseToken != null ? baseToken + ' ' + overlayToken : overlayToken)
      }
    }
  }
}

export default overlayMode


// Because the sidebar also use the file icons, So I put this file out of floatBox directory.
import 'file-icons-js/css/style.css'
import fileIcons from 'file-icons-js'

export default fileIcons


/**
 * Set `__static` path to static files in production
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
 */

if (process.env.NODE_ENV !== 'development') {
  global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}


{
"name": "marktext",
"version": "0.7.17",
"lockfileVersion": 1,
"requires": true,
"dependencies": {


import Vue from 'vue'
import LandingPage from '@/components/LandingPage'

describe('LandingPage.vue', () => {
  it('should render correct contents', () => {
    const vm = new Vue({
      el: document.createElement('div'),
      render: h => h(LandingPage)
    }).$mount()

    expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!')
  })
})


import Vue from 'vue'
Vue.config.devtools = false
Vue.config.productionTip = false

// require all test files (files that ends with .spec.js)
const testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)

// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)


import mousetrap from 'mousetrap'
import {
  getUniqueId
} from './utils'

class EventCenter {
  constructor () {
    this.events = []
    this.listeners = {}
    this.eventIds = new Set() // use to store eventId
    // bind mousetrap methods to event instance.
    ;['bind', 'unbind', 'trigger', 'stopCallback', 'reset', 'handleKey', 'addKeycodes'].forEach(method => {
      this[method] = mousetrap[method]
    })
  }
  /**
   * [attachDOMEvent] bind event listener to target, and return a unique ID,
   * this ID
   */
  attachDOMEvent (target, event, listener, capture) {
    if (this.checkHasBind(target, event, listener, capture)) return false
    const eventId = getUniqueId(this.eventIds)
    target.addEventListener(event, listener, capture)
    this.events.push({
      eventId,
      target,
      event,
      listener,
      capture
    })
    return eventId
  }
  /**
   * [detachDOMEvent removeEventListener]
   * @param  {[type]} eventId [unique eventId]
   */
  detachDOMEvent (eventId) {
    if (!eventId) return false
    const removeEvent = this.events.filter(e => e.eventId === eventId)[0]
    if (removeEvent) {
      const { target, event, listener, capture } = removeEvent
      target.removeEventListener(event, listener, capture)
    }
  }
  /**
   * [detachAllDomEvents remove all the DOM events handler]
   */
  detachAllDomEvents () {
    this.events.forEach(event => this.detachDOMEvent(event.eventId))
    this.reset()
  }
  /**
   * [subscribe] subscribe custom event
   */
  subscribe (event, listener) {
    const eventListener = this.listeners[event]
    if (eventListener && Array.isArray(eventListener)) {
      eventListener.push(listener)
    } else {
      this.listeners[event] = [listener]
    }
  }
  /**
   * dispatch custom event
   */
  dispatch (event, ...data) {
    const eventListener = this.listeners[event]
    if (eventListener && Array.isArray(eventListener)) {
      eventListener.forEach(listener => listener(...data))
    }
  }
  // Determine whether the event has been bind
  checkHasBind (cTarget, cEvent, cListener, cCapture) {
    let i
    let len = this.events.length
    for (i = 0; i < len; i++) {
      const { target, event, listener, capture } = this.events[i]
      if (target === cTarget && event === cEvent && listener === cListener && capture === cCapture) {
        return true
      }
    }
    return false
  }
}

export default EventCenter


keep keep keep keep replace replace replace replace keep keep


import { autoUpdater } from 'electron-updater'
import { ipcMain } from 'electron'
import createWindow from '../createWindow'
import userPreference from '../preference'

let updater
let win

autoUpdater.autoDownload = false

autoUpdater.on('error', error => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_ERROR', error === null ? 'Error: unknown' : (error.stack || error).toString())
  }
})

ipcMain.on('AGANI::NEED_UPDATE', (e, { needUpdate }) => {
  if (needUpdate) {
    autoUpdater.downloadUpdate()
  } else {
    updater.enabled = true
    updater = null
  }
})

autoUpdater.on('update-available', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_AVAILABLE', 'Found updates, do you want update now?')
  }
  updater.enabled = true
  updater = null
})

autoUpdater.on('update-not-available', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_NOT_AVAILABLE', 'Current version is up-to-date.')
  }
  updater.enabled = true
  updater = null
})

autoUpdater.on('update-downloaded', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_DOWNLOADED', 'Updates downloaded, application will be quit for update...')
  }
  setImmediate(() => autoUpdater.quitAndInstall())
})

export const userSetting = (menuItem, browserWindow) => {
  createWindow(userPreference.userDataPath)
}

export const checkUpdates = (menuItem, browserWindow) => {
  updater = menuItem
  win = browserWindow
  updater.enabled = false
  autoUpdater.checkForUpdates()
}


import * as actions from '../actions/format'

export default {
  label: 'Format',
  submenu: [{
    label: 'Strong',
    type: 'checkbox',
    accelerator: 'Shift+CmdOrCtrl+B',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'strong')
    }
  }, {
    label: 'Emphasis',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+E',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'em')
    }
  }, {
    label: 'Inline Code',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+`',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'inline_code')
    }
  }, {
    type: 'separator'
  }, {
    label: 'Strike',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+D',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'del')
    }
  }, {
    label: 'Hyperlink',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+L',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'link')
    }
  }, {
    label: 'Image',
    type: 'checkbox',
    accelerator: 'Shift+CmdOrCtrl+I',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'image')
    }
  }, {
    type: 'separator'
  }, {
    label: 'Clear Format',
    accelerator: 'Shift+CmdOrCtrl+R',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'clear')
    }
  }]
}


"jsbn": {
"version": "0.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",

keep keep keep replace replace keep keep keep

### 0.9.18

**:cactus:Feature**

**:butterfly:Optimization**

- adjust lineHeight and fontSize in typewriter mode
- optimization of output unstylish html @fxha

**:beetle:Bug fix**

- fix: #81
- fix: #55
- fix: #63

### 0.8.12

**:cactus:Feature**

- Add user preferences in `Mark Text menu`, the shoutcut is `CmdorCtrl + ,`, you can set the default `theme` and `autoSave`.
- Add `autoSave` to `file menu`, the default value is in `preferences.md` which you can open in `Mark Text menu`. #45
- Add drag and drop to open Markdown file with Mark Text @fxha
- User setting: fontSize, lineHeight, color in realtime mode.
- Move your file to other folder @DXXL
- Rename filename

**:butterfly:Optimization**

- Theme can be saved in user preferences now #16
- Custom About dialog @fxha

**:beetle:Bug fix**

- fix: prevent open image or file directly when drag and drop over Mark Text #42
- fix: set theme to all the open window not just the active one.
- fix: set correct application menu offset on windows #44
- fix: Missing preferences menu in Linux and Windows. @fxha

### 0.7.17

**Features**

1. Check for updates..., and auto update when update available.(Still need signature...:cry:)

2. Insert Image: ( In edit menu )

   - absolute path

   - relative path

   - Upload Image to cloud

3. Add file icons to languages when create code block or change language in code block.

**Bug fix**

1. It's hard to focus the input in code fence.

2. When input the language in code block, click the language item will not cause hide the float box.

3. other bugs in code block.

4. Windows user can not use open with feature.

5. The menu disapear in Linux sysyem.

6. Fix the bug that the language highlight disapear when open markdown file with code block

7. remove the symbol in output styled html. #41

8. escape the raw Markdown when open the markdown file. #37

**Optimization**

1. allow user to change install directory on windows.

2. Show notification when output HTML and PDF successfully.

3. update css-tree to latest version.

4. Add lineWrapping is true to codeMirror config

### 0.6.14

**Features**

- Add **dark** theme and **light** theme in both realtime preview mode and source code mode.

- Insert `doutu` into the document, use CMD + / to open the panel.

**Optimization**

- Customize the scroll bar background color and thumb color.

- Add collection of doutu.

- Add History search word of doutu.

**Bug fix**

- Fix bug when search key in code block will cause the search input lose focus.

- Fix the bug the editor will lose cursor after input Chinese.

### 0.5.2

**Features**

- Add Typewriter Mode, The current line will always in the center of the document. If you change the current line, it will be auto scroll to the new line.

- Add Focus Mode, the current paragraph's will be focused.

- Add Dark theme, Light theme.

**Optimization**

- Optimize the display of path name and file name in title bar.

- Eidtor will auto scroll to the highlight word when click Find Prev or Find Next.

**Bug fix**

- Set back the cursor when mode change between source code mode and normal mode

### 0.4.0

**Feature**

- Search value in document, Use **FIND PREV** and **FIND NEXT** to selection previous one or next one.

  Add animation of highlight word.

  Auto focus the search input when open search panel.

  close the search panel will auto selection the last highlight word by ESC button.

- Replace value

  Replace All

  Replace one and auto highlight the next word.

**Bug fix**

- fix the bug that click at the edge of code block will caused the code block does not be focused.

**Optimization**

- Optimize the display of word count in title bar. we also delete the background color of title bar to make it more concise.

- Customize the style of checkbox in Task List Item.

- Change the display of Insert Table dialog.

### 0.3.0

**Features**

- Export PDF

**Bug fix**

- fix the bug that editor can only print the first page.


'use strict'

import './globalSetting'
import { app, Menu } from 'electron'
import configureMenu, { dockMenu } from './menus'
import createWindow, { windows } from './createWindow'
import { isMarkdownFile } from './utils'

let openFilesCache = []

const onReady = () => {
  if (process.platform !== 'darwin' && process.argv.length >= 2) {
    for (const arg of process.argv) {
      if (isMarkdownFile(arg)) {
        openFilesCache = [arg]
        break
      }
    }
  }
  if (openFilesCache.length) {
    openFilesCache.forEach(path => createWindow(path))
    openFilesCache.length = 0 // empty the open file path cache
  } else {
    createWindow()
  }
  const menu = Menu.buildFromTemplate(configureMenu)
  Menu.setApplicationMenu(menu)
  if (process.platform === 'darwin') {
    // app.dock is only for macosx
    app.dock.setMenu(dockMenu)
  }
}

const openFile = (event, path) => {
  event.preventDefault()
  if (app.isReady()) {
    createWindow(path)
  } else {
    openFilesCache.push(path)
  }
}

app.on('open-file', openFile)

app.on('ready', onReady)

app.on('window-all-closed', () => {
  app.removeListener('open-file', openFile)
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (windows.size === 0) {
    onReady()
  }
})


    <input type="text" class="row-input">
    ×
    <input type="text" class="column-input">
    <button>OK</button>


.DS_Store
dist/electron/*
dist/web/*
build/*
!build/icons
coverage
.vscode
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep


keep keep keep keep replace replace keep keep keep keep keep


'use strict'

process.env.NODE_ENV = 'production'

const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')

const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
const webConfig = require('./webpack.web.config')

const doneLog = chalk.bgGreen.white(' DONE ') + ' '
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
const isCI = process.env.CI || false

if (process.env.BUILD_TARGET === 'clean') clean()
else if (process.env.BUILD_TARGET === 'web') web()
else build()

function clean () {
  del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
  console.log(`\n${doneLog}\n`)
  process.exit()
}

function build () {
  greeting()

  del.sync(['dist/electron/*', '!.gitkeep'])

  const tasks = ['main', 'renderer']
  const m = new Multispinner(tasks, {
    preText: 'building',
    postText: 'process'
  })

  let results = ''

  m.on('success', () => {
    process.stdout.write('\x1B[2J\x1B[0f')
    console.log(`\n\n${results}`)
    console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
    process.exit()
  })

  pack(mainConfig).then(result => {
    results += result + '\n\n'
    m.success('main')
  }).catch(err => {
    m.error('main')
    console.log(`\n  ${errorLog}failed to build main process`)
    console.error(`\n${err}\n`)
    process.exit(1)
  })

  pack(rendererConfig).then(result => {
    results += result + '\n\n'
    m.success('renderer')
  }).catch(err => {
    m.error('renderer')
    console.log(`\n  ${errorLog}failed to build renderer process`)
    console.error(`\n${err}\n`)
    process.exit(1)
  })
}

function pack (config) {
  return new Promise((resolve, reject) => {
    webpack(config, (err, stats) => {
      if (err) reject(err.stack || err)
      else if (stats.hasErrors()) {
        let err = ''

        stats.toString({
          chunks: false,
          colors: true
        })
        .split(/\r?\n/)
        .forEach(line => {
          err += `    ${line}\n`
        })

        reject(err)
      } else {
        resolve(stats.toString({
          chunks: false,
          colors: true
        }))
      }
    })
  })
}

function web () {
  del.sync(['dist/web/*', '!.gitkeep'])
  webpack(webConfig, (err, stats) => {
    if (err || stats.hasErrors()) console.log(err)

    console.log(stats.toString({
      chunks: false,
      colors: true
    }))

    process.exit()
  })
}

function greeting () {
  const cols = process.stdout.columns
  let text = ''

  if (cols > 85) text = 'lets-build'
  else if (cols > 60) text = 'lets-|build'
  else text = false

  if (text && !isCI) {
    say(text, {
      colors: ['yellow'],
      font: 'simple3d',
      space: false
    })
  } else console.log(chalk.yellow.bold('\n  lets-build'))
  console.log()
}


import { ipcMain, BrowserWindow } from 'electron'

import { windows } from './createWindow'

const staticPath = path.join(__static, FILE_NAME)

const userDataPath = path.join(getPath('userData'), FILE_NAME)

keep keep keep replace keep keep keep keep

import { getPath, log, mkdir } from './utils'


export const EXTENSIONS = [
  'markdown',
  'mdown',
  'mkdn',
  'md',
  'mkd',
  'mdwn',
  'mdtxt',
  'mdtext',
  'text',
  'txt'
]

export const IMAGE_EXTENSIONS = [
  'jpeg',
  'jpg',
  'png',
  'gif',
  'svg',
  'webp'
]

export const EXTENSION_HASN = {
  styledHtml: '.html',
  html: '.html',
  pdf: '.pdf'
}

export const VIEW_MENU_ITEM = {
  'Source Code Mode': false,
  'Typewriter Mode': false,
  'Focus Mode': false
}


@keyframes highlight {
  from {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  } /* ignored */
  to {
    transform: scale(1);
  }
}

h1.ag-active::before,
h2.ag-active::before,
h3.ag-active::before,
h4.ag-active::before,
h5.ag-active::before,
h6.ag-active::before {
  content: attr(data-head);
  width: 20px;
  height: 20px;
  text-align: center;
  line-height: 22px;
  display: block;
  position: absolute;
  top: 0;
  left: -25px;
  border: 1px solid #C0C4CC;
  border-radius: 3px;
  font-size: 12px;
  color: #C0C4CC;
  transform: scale(.7);
  font-weight: 100;
}

*::selection, .ag-selection {
  background: #E4E7ED;
  color: #303133;
}

.ag-highlight {
  animation-name: highlight;
  animation-duration: .25s;
  display: inline-block;
  background: rgb(249, 226, 153);
  color: #303133;
}

figure {
  padding: 0;
  margin: 0;
  margin-top: 25px;
  position: relative;
}
.ag-table-tool-bar {
  width: 100%;
  user-select: none;
  position: absolute;
  top: -20px;
  left: 0;
  display: none;
}
.ag-table-tool-bar ul {
  height: 18px;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  width: 100%;
}
.ag-table-tool-bar ul li {
  box-sizing: border-box;
  display: flex;
  width: 18px;
  height: 18px;
  padding: 2px;
  margin-right: 3px;
  cursor: pointer;
  border-radius: 3px;
  color: #606266;
}

.ag-table-tool-bar ul li[data-label=delete] {
  color: #E6A23C;
  position: absolute;
  right: 0;
  top: 0;
}

.ag-table-tool-bar ul li.active {
  color: #409eff;
}

.ag-table-tool-bar ul li svg {
  transition: transform .2s ease-in-out;
}

.ag-table-tool-bar ul li:hover svg {
  transform: scale(1.1);
}

figure.ag-active .ag-table-tool-bar {
  display: block;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0;
}

a {
  pointer-events: none;
}

li > h1,
li > h2,
li > h3,
li > h4,
li > h5,
li > h6 {
  display: initial;
}

li.ag-task-list-item {
  list-style-type: none;
  position: relative;
}

li.ag-task-list-item > input[type=checkbox] {
  position: absolute;
  cursor: pointer;
  width: inherit;
  height: inherit;
  margin: 4px 0px 0px;
  top: 1px;
  left: -20px;
}

li.ag-task-list-item > input.ag-checkbox-checked ~ p {
  color: #909399;
}

li.ag-task-list-item > input[type=checkbox]::before {
  content: '';
  width: 14px;
  height: 14px;
  box-sizing: border-box;
  display: inline-block;
  border: 2px solid #606266;
  border-radius: 2px;
  background-color: #FFFFFF;
  position: absolute;
  top: 0;
  left: 0;
}

li.ag-task-list-item > input.ag-checkbox-checked::before {
  border: none;
  background-color: #606266;
}

li.ag-task-list-item > input.ag-checkbox-checked::after {
  content: '';
  transform: rotate(-45deg);
  width: 7px;
  height: 3px;
  border: 2px solid #fff;
  border-top: none;
  border-right: none;
  position: absolute;
  display: inline-block;
  top: 4px;
  left: 3px;
}

li p .ag-hide:first-child {
  display: none;
}

p:not(.ag-active)[data-role="hr"] {
  position: relative;
  color: transparent;
}

p:not(.ag-active)[data-role="hr"]::before {
  content: '';
  width: 100%;
  height: 5px;
  background: gainsboro;
  position: absolute;
  bottom: -2px;
}

p:not(.ag-active)[data-role="hr"] * {
  color: transparent;
}
#ag-editor-id figure,
#ag-editor-id pre.ag-code-block {
  position: relative;
  display: inline-flex;
  box-sizing: border-box;
  width: 100%;
}

#ag-editor-id pre .CodeMirror {
  width: 100%;
}

img {
  max-width: 100%;
}

hr {
  cursor: default;
}

span.ag-emoji-marked-text {
  position: relative;
  color: aqua;
  text-decoration: none;
}

.ag-emoji-marked-text[data-emoji] {
  margin-left: 1.3em;
}

.ag-hide.ag-emoji-marked-text[data-emoji] {
  overflow: visible;
  white-space: nowrap;
  color: transparent;
}

.ag-emoji-marked-text::before {
  position: absolute;
  content: attr(data-emoji);
  color: #000;
  top: 0;
  left: -1.15em;
}

.ag-language-input {
  display: none;
  min-width: 80px;
  position: absolute;
  font-size: .85em;
  top: 5px;
  right: 5px;
  color: #606266;
  background: transparent;
  border: none;
  text-align: center;
  z-index: 10;
}

.ag-language-input::placeholder {
  color: #C0C4CC;
}

pre.ag-active .ag-language-input {
  display: block;
}

span.ag-image-marked-text, span.ag-link-in-bracket, span.ag-link-in-bracket .ag-backlash {
  color: rgb(125, 125, 125);
  font-size: 16px;
  text-decoration: none;
  font-family: monospace;
}

.ag-language {
  color: #708;
  text-decoration: none;
}
.ag-backlash {
  text-decoration: none;
  color: rgb(51, 51, 51);
}
.ag-warn {
  color: #000;
  text-decoration: none;
}

.ag-hide, .ag-hide .ag-highlight, .ag-hide .ag-selection {
  display: inline-block;
  width: 0;
  height: 0;
  overflow: hidden;
}

.ag-image-marked-text ~ img {
  display: block;
  margin: 0 auto;
}

.ag-image-marked-text::before {
  content: '';
  width: 1.1em;
  height: 1.1em;
  display: inline-block;
  vertical-align: middle;
  background: url(./assets/icons/image.svg);
  background-size: contain;
}

.ag-image-marked-text.ag-image-fail::before {
  background-image: url(./assets/icons/imagefail.svg);
}

span[data-role="link"], a[data-role="link"], span[data-role="link"] .ag-backlash {
  color: #0366d6;
  text-decoration: none;
}

.ag-focus-mode p.ag-paragraph,
.ag-focus-mode h1.ag-paragraph,
.ag-focus-mode h2.ag-paragraph,
.ag-focus-mode h3.ag-paragraph,
.ag-focus-mode h4.ag-paragraph,
.ag-focus-mode h5.ag-paragraph,
.ag-focus-mode h6.ag-paragraph,
.ag-focus-mode th.ag-paragraph,
.ag-focus-mode td.ag-paragraph,
.ag-focus-mode pre.ag-paragraph {
  opacity: .25;
}

.ag-focus-mode p.ag-active,
.ag-focus-mode h1.ag-active,
.ag-focus-mode h2.ag-active,
.ag-focus-mode h3.ag-active,
.ag-focus-mode h4.ag-active,
.ag-focus-mode h5.ag-active,
.ag-focus-mode h6.ag-active,
.ag-focus-mode th.ag-active,
.ag-focus-mode td.ag-active,
.ag-focus-mode pre.ag-active {
  opacity: 1;
}


keep keep keep keep replace keep keep keep

on: pull_request

name: Continuous Integration

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          cache: yarn
      - uses: actions-rs/toolchain@v1
      - uses: Swatinem/rust-cache@v1
      # use `--frozen-lockfile` to fail immediately if the committed yarn.lock needs updates
      # https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-frozen-lockfile
      - run: yarn --frozen-lockfile
      - run: yarn lint

  flow:
    name: Flow
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          cache: yarn
      - run: yarn --frozen-lockfile
      - run: yarn flow check

  benchmarks:
    name: Benchmarks
    runs-on: ubuntu-latest
    steps:
      - name: PR Benchmarks
        uses: parcel-bundler/parcel-benchmark-action@master
        env:
          PARCEL_BENCHMARK_APIKEY: ${{ secrets.PARCEL_BENCHMARK_APIKEY }}

  unit_tests:
    name: Unit tests (${{matrix.os}}, Node ${{matrix.node}})
    strategy:
      matrix:
        node: [12, 14]
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{matrix.os}}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          cache: yarn
          node-version: ${{matrix.node}}
      - uses: actions-rs/toolchain@v1
      - uses: Swatinem/rust-cache@v1
      - name: Bump max inotify watches (Linux only)
        run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p;
        if: ${{matrix.os == 'ubuntu-latest'}}
      - run: yarn --frozen-lockfile
      - run: yarn build-native-release
      - run: yarn test:unit

  integration_tests:
    name: Integration tests (${{matrix.os}}, Node ${{matrix.node}})
    strategy:
      matrix:
        node: [12, 14]
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{matrix.os}}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          cache: yarn
          node-version: ${{matrix.node}}
      - uses: actions-rs/toolchain@v1
      - uses: Swatinem/rust-cache@v1
      - name: Bump max inotify watches (Linux only)
        run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p;
        if: ${{matrix.os == 'ubuntu-latest'}}
      - run: yarn --frozen-lockfile
      - run: yarn build-native-release
      - run: yarn test:integration-ci
      # Similar to
      # https://github.com/marketplace/actions/publish-unit-test-results#use-with-matrix-strategy
      - name: Upload JUnit results
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: Integration tests (${{matrix.os}}, node ${{matrix.node}})
          path: '**/junit-*.xml'

  test_report:
    name: Test report
    runs-on: ubuntu-latest
    needs: [unit_tests, integration_tests]
    if: always()
    steps:
      - name: Create test report
        uses: mikepenz/action-junit-report@v2
        with:
          report_paths: artifacts/**/*.xml


const bar = require('./shared_sub');
const exp = { foo: bar }

export default exp;


var local = require('./local');
// eslint-disable-next-line no-unused-vars
var url = require('url');

module.exports = function () {
  return local.a + local.b;
};


name: Integration tests (${{matrix.os}}, Node ${{matrix.node}})

keep keep keep keep keep replace keep keep keep

// @flow strict-local

import type {
  Asset,
  BuildEvent,
  BuildSuccessEvent,
  BundleGraph,
  Dependency,
  FilePath,
  InitialParcelOptions,
  PackagedBundle,
} from '@parcel/types';
import type {FileSystem} from '@parcel/fs';
import type WorkerFarm from '@parcel/workers';

import invariant from 'assert';
import util from 'util';
import Parcel, {createWorkerFarm} from '@parcel/core';
import assert from 'assert';
import vm from 'vm';
import v8 from 'v8';
import {NodeFS, MemoryFS, OverlayFS, ncp as _ncp} from '@parcel/fs';
import path from 'path';
import url from 'url';
import WebSocket from 'ws';
import nullthrows from 'nullthrows';
import {parser as postHtmlParse} from 'posthtml-parser';
import postHtml from 'posthtml';
import EventEmitter from 'events';

import {makeDeferredWithPromise, normalizeSeparators} from '@parcel/utils';
import _chalk from 'chalk';
import resolve from 'resolve';

export const workerFarm = (createWorkerFarm(): WorkerFarm);
export const inputFS: NodeFS = new NodeFS();
export let outputFS: MemoryFS = new MemoryFS(workerFarm);
export let overlayFS: OverlayFS = new OverlayFS(outputFS, inputFS);

beforeEach(() => {
  outputFS = new MemoryFS(workerFarm);
  overlayFS = new OverlayFS(outputFS, inputFS);
});

// Recursively copies a directory from the inputFS to the outputFS
export async function ncp(source: FilePath, destination: FilePath) {
  await _ncp(inputFS, source, outputFS, destination);
}

// Mocha is currently run with exit: true because of this issue preventing us
// from properly ending the workerfarm after the test run:
// https://github.com/nodejs/node/pull/28788
//
// TODO: Remove exit: true in .mocharc.json and instead add the following in this file:
//   // Spin down the worker farm to stop it from preventing the main process from exiting
//   await workerFarm.end();
// when https://github.com/nodejs/node/pull/28788 is resolved.

const chalk = new _chalk.Instance();
const warning = chalk.keyword('orange');

/* eslint-disable no-console */
// $FlowFixMe[cannot-write]
console.warn = (...args) => {
  // eslint-disable-next-line no-console
  console.error(warning(...args));
};
/* eslint-enable no-console */

type ExternalModules = {|
  [name: string]: (vm$Context) => {[string]: mixed},
|};

export function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export function normalizeFilePath(filePath: string): FilePath {
  return normalizeSeparators(filePath);
}

export const distDir: string = path.resolve(
  __dirname,
  '..',
  '..',
  'integration-tests',
  'dist',
);

export async function removeDistDirectory() {
  await outputFS.rimraf(distDir);
}

export function symlinkPrivilegeWarning() {
  // eslint-disable-next-line no-console
  console.warn(
    `-----------------------------------
Skipping symbolic link test(s) because you don't have the privilege.
Run tests with Administrator privilege.
If you don't know how, check here: https://bit.ly/2UmWsbD
-----------------------------------`,
  );
}

export function getParcelOptions(
  entries: FilePath | Array<FilePath>,
  opts?: $Shape<InitialParcelOptions>,
): InitialParcelOptions {
  return mergeParcelOptions(
    {
      entries,
      shouldDisableCache: true,
      logLevel: 'none',
      defaultConfig: path.join(
        __dirname,
        process.env.PARCEL_TEST_EXPERIMENTAL_BUNDLER == null
          ? '.parcelrc-no-reporters'
          : '.parcelrc-experimental-bundler',
      ),
      inputFS,
      outputFS,
      workerFarm,
      shouldContentHash: true,
      defaultTargetOptions: {
        distDir,
        engines: {
          browsers: ['last 1 Chrome version'],
          node: '8',
        },
      },
    },
    opts,
  );
}

export function bundler(
  entries: FilePath | Array<FilePath>,
  opts?: $Shape<InitialParcelOptions>,
): Parcel {
  return new Parcel(getParcelOptions(entries, opts));
}

export function findAsset(
  bundleGraph: BundleGraph<PackagedBundle>,
  assetFileName: string,
): ?Asset {
  return bundleGraph.traverseBundles((bundle, context, actions) => {
    let asset = bundle.traverseAssets((asset, context, actions) => {
      if (path.basename(asset.filePath) === assetFileName) {
        actions.stop();
        return asset;
      }
    });
    if (asset) {
      actions.stop();
      return asset;
    }
  });
}

export function findDependency(
  bundleGraph: BundleGraph<PackagedBundle>,
  assetFileName: string,
  specifier: string,
): Dependency {
  let asset = nullthrows(
    findAsset(bundleGraph, assetFileName),
    `Couldn't find asset ${assetFileName}`,
  );

  let dependency = bundleGraph
    .getDependencies(asset)
    .find(d => d.specifier === specifier);
  invariant(
    dependency != null,
    `Couldn't find dependency ${assetFileName} -> ${specifier}`,
  );
  return dependency;
}

export function mergeParcelOptions(
  optsOne: InitialParcelOptions,
  optsTwo?: InitialParcelOptions | null,
): InitialParcelOptions {
  if (!optsTwo) {
    return optsOne;
  }

  return {
    ...optsOne,
    ...optsTwo,
    // $FlowFixMe
    defaultTargetOptions: {
      ...optsOne?.defaultTargetOptions,
      // $FlowFixMe
      ...optsTwo?.defaultTargetOptions,
    },
  };
}

export function assertDependencyWasExcluded(
  bundleGraph: BundleGraph<PackagedBundle>,
  assetFileName: string,
  specifier: string,
): void {
  let dep = findDependency(bundleGraph, assetFileName, specifier);
  invariant(
    bundleGraph.isDependencySkipped(dep),
    util.inspect(dep) + " wasn't deferred",
  );
}

export async function bundle(
  entries: FilePath | Array<FilePath>,
  opts?: InitialParcelOptions,
): Promise<BundleGraph<PackagedBundle>> {
  return (await bundler(entries, opts).run()).bundleGraph;
}

export function getNextBuild(b: Parcel): Promise<BuildEvent> {
  return new Promise((resolve, reject) => {
    let subscriptionPromise = b
      .watch((err, buildEvent) => {
        if (err) {
          reject(err);
          return;
        }

        subscriptionPromise
          .then(subscription => {
            // If the watch callback was reached, subscription must have been successful
            invariant(subscription != null);
            return subscription.unsubscribe();
          })
          .then(() => {
            // If the build promise hasn't been rejected, buildEvent must exist
            invariant(buildEvent != null);
            resolve(buildEvent);
          })
          .catch(reject);
      })
      .catch(reject);
  });
}

export async function getNextBuildSuccess(
  b: Parcel,
): Promise<BuildSuccessEvent> {
  let evt = await getNextBuild(b);
  invariant(evt.type === 'buildSuccess');
  return evt;
}

export function shallowEqual(
  a: $Shape<{|+[string]: mixed|}>,
  b: $Shape<{|+[string]: mixed|}>,
): boolean {
  if (Object.keys(a).length !== Object.keys(b).length) {
    return false;
  }

  for (let [key, value] of Object.entries(a)) {
    if (!b.hasOwnProperty(key) || b[key] !== value) {
      return false;
    }
  }

  return true;
}

type RunOpts = {require?: boolean, strict?: boolean, ...};

export async function runBundles(
  bundleGraph: BundleGraph<PackagedBundle>,
  parent: PackagedBundle,
  bundles: Array<[string, PackagedBundle]>,
  globals: mixed,
  opts: RunOpts = {},
  externalModules?: ExternalModules,
): Promise<mixed> {
  let entryAsset = nullthrows(
    bundles
      .map(([, b]) => b.getMainEntry() || b.getEntryAssets()[0])
      .filter(Boolean)[0],
  );
  let env = entryAsset.env;
  let target = env.context;
  let outputFormat = env.outputFormat;

  let ctx, promises;
  switch (target) {
    case 'browser': {
      let prepared = prepareBrowserContext(parent, globals);
      ctx = prepared.ctx;
      promises = prepared.promises;
      break;
    }
    case 'node':
    case 'electron-main':
      nodeCache.clear();
      ctx = prepareNodeContext(
        outputFormat === 'commonjs' && parent.filePath,
        globals,
      );
      break;
    case 'electron-renderer': {
      nodeCache.clear();
      let prepared = prepareBrowserContext(parent, globals);
      prepareNodeContext(
        outputFormat === 'commonjs' && parent.filePath,
        globals,
        prepared.ctx,
      );
      ctx = prepared.ctx;
      promises = prepared.promises;
      break;
    }
    case 'web-worker':
    case 'service-worker': {
      let prepared = prepareWorkerContext(parent.filePath, globals);
      ctx = prepared.ctx;
      promises = prepared.promises;
      break;
    }
    case 'worklet': {
      ctx = Object.assign({}, globals);
      break;
    }
    default:
      throw new Error('Unknown target ' + target);
  }

  // A utility to prevent optimizers from removing side-effect-free code needed for testing
  // $FlowFixMe[prop-missing]
  ctx.sideEffectNoop = () => {};

  vm.createContext(ctx);
  let esmOutput;
  if (outputFormat === 'esmodule') {
    let res = await runESM(
      bundles[0][1].target.distDir,
      bundles.map(([code, bundle]) => [code, bundle.filePath]),
      ctx,
      overlayFS,
      externalModules,
      true,
    );

    esmOutput = bundles.length === 1 ? res[0] : res;
  } else {
    invariant(
      externalModules == null,
      'externalModules are only supported with ESM',
    );
    for (let [code, b] of bundles) {
      // require, parcelRequire was set up in prepare*Context
      new vm.Script((opts.strict ? '"use strict";\n' : '') + code, {
        filename:
          b.bundleBehavior === 'inline'
            ? b.name
            : normalizeSeparators(path.relative(b.target.distDir, b.filePath)),
        async importModuleDynamically(specifier) {
          let filePath = path.resolve(path.dirname(parent.filePath), specifier);
          let code = await overlayFS.readFile(filePath, 'utf8');
          let modules = await runESM(
            b.target.distDir,
            [[code, filePath]],
            ctx,
            overlayFS,
            externalModules,
            true,
          );
          return modules[0];
        },
      }).runInContext(ctx);
    }
  }
  if (promises) {
    // await any ongoing dynamic imports during the run
    await Promise.all(promises);
  }

  if (opts.require !== false) {
    switch (outputFormat) {
      case 'global':
        if (env.shouldScopeHoist) {
          return typeof ctx.output !== 'undefined' ? ctx.output : undefined;
        } else {
          for (let key in ctx) {
            if (key.startsWith('parcelRequire')) {
              // $FlowFixMe[incompatible-use]
              return ctx[key](bundleGraph.getAssetPublicId(entryAsset));
            }
          }
        }
        return;
      case 'commonjs':
        invariant(typeof ctx.module === 'object' && ctx.module != null);
        return ctx.module.exports;
      case 'esmodule':
        return esmOutput;
      default:
        throw new Error(
          'Unable to run bundle with outputFormat ' + env.outputFormat,
        );
    }
  }

  return ctx;
}

export async function runBundle(
  bundleGraph: BundleGraph<PackagedBundle>,
  bundle: PackagedBundle,
  globals: mixed,
  opts: RunOpts = {},
  externalModules?: ExternalModules,
): Promise<mixed> {
  if (bundle.type === 'html') {
    let code = await overlayFS.readFile(nullthrows(bundle.filePath), 'utf8');
    let ast = postHtmlParse(code, {
      lowerCaseAttributeNames: true,
    });

    let bundles = bundleGraph.getBundles({includeInline: true});
    let scripts = [];
    postHtml().walk.call(ast, node => {
      if (node.attrs?.nomodule != null) {
        return node;
      }
      if (node.tag === 'script' && node.attrs?.src) {
        let src = url.parse(nullthrows(node.attrs).src);
        if (src.hostname == null) {
          let p = path.join(distDir, nullthrows(src.pathname));
          let b = nullthrows(bundles.find(b => b.filePath === p));
          scripts.push([overlayFS.readFileSync(b.filePath, 'utf8'), b]);
        }
      } else if (node.tag === 'script' && node.content && !node.attrs?.src) {
        let content = node.content.join('');
        let inline = bundles.filter(
          b => b.bundleBehavior === 'inline' && b.type === 'js',
        );
        scripts.push([content, inline[0]]);
      }
      return node;
    });

    return runBundles(
      bundleGraph,
      bundle,
      scripts,
      globals,
      opts,
      externalModules,
    );
  } else {
    return runBundles(
      bundleGraph,
      bundle,
      [[overlayFS.readFileSync(bundle.filePath, 'utf8'), bundle]],
      globals,
      opts,
      externalModules,
    );
  }
}

export function run(
  bundleGraph: BundleGraph<PackagedBundle>,
  globals: mixed,
  opts: RunOpts = {},
  externalModules?: ExternalModules,
  // $FlowFixMe[unclear-type]
): Promise<any> {
  let bundle = nullthrows(
    bundleGraph.getBundles().find(b => b.type === 'js' || b.type === 'html'),
  );
  return runBundle(bundleGraph, bundle, globals, opts, externalModules);
}

export function assertBundles(
  bundleGraph: BundleGraph<PackagedBundle>,
  expectedBundles: Array<{|
    name?: string | RegExp,
    type?: string,
    assets: Array<string>,
  |}>,
) {
  let actualBundles = [];
  const byAlphabet = (a, b) => (a.toLowerCase() < b.toLowerCase() ? -1 : 1);

  bundleGraph.traverseBundles(bundle => {
    let assets = [];

    bundle.traverseAssets(asset => {
      if (/@swc[/\\]helpers/.test(asset.filePath)) {
        // Skip all helpers for now, as they add friction and churn to assertions.
        // A longer term solution might have an explicit opt-in to this behavior, or
        // if we enable symbol propagation unconditionally, the set of helpers
        // should be more minimal.
        return;
      }

      if (/runtime-[a-z0-9]{16}\.js/.test(asset.filePath)) {
        // Skip runtime assets, which have hashed filenames for source maps.
        return;
      }

      const name = path.basename(asset.filePath);
      assets.push(name);
    });

    assets.sort(byAlphabet);
    actualBundles.push({
      name:
        bundle.bundleBehavior === 'inline'
          ? bundle.name
          : path.basename(bundle.filePath),
      type: bundle.type,
      assets,
    });
  });

  for (let bundle of expectedBundles) {
    if (!Array.isArray(bundle.assets)) {
      throw new Error(
        'Expected bundle must include an array of expected assets',
      );
    }
    bundle.assets.sort(byAlphabet);
  }

  const byName = (a, b) => {
    if (typeof a.name === 'string' && typeof b.name === 'string') {
      return a.name.localeCompare(b.name);
    }

    return 0;
  };

  const byAssets = (a, b) =>
    a.assets.join(',').localeCompare(b.assets.join(','));
  expectedBundles.sort(byName).sort(byAssets);
  actualBundles.sort(byName).sort(byAssets);
  assert.equal(
    actualBundles.length,
    expectedBundles.length,
    'expected number of bundles mismatched',
  );

  let i = 0;
  for (let bundle of expectedBundles) {
    let actualBundle = actualBundles[i++];
    let name = bundle.name;
    let actualName = actualBundle.name;
    if (name != null && actualName != null) {
      if (typeof name === 'string') {
        assert.equal(
          actualName,
          name,
          `Bundle name "${actualName}", does not match expected name "${name}"`,
        );
      } else if (name instanceof RegExp) {
        assert(
          actualName.match(name),
          `${actualName} does not match regexp ${name.toString()}`,
        );
      } else {
        // $FlowFixMe[incompatible-call]
        assert.fail('Expected bundle name has invalid type');
      }
    }

    if (bundle.type != null) {
      assert.equal(actualBundle.type, bundle.type);
    }

    if (bundle.assets) {
      assert.deepEqual(actualBundle.assets, bundle.assets);
    }
  }
}

export function normaliseNewlines(text: string): string {
  return text.replace(/(\r\n|\n|\r)/g, '\n');
}

function prepareBrowserContext(
  bundle: PackagedBundle,
  globals: mixed,
): {|
  ctx: vm$Context,
  promises: Array<Promise<mixed>>,
|} {
  // for testing dynamic imports
  const fakeElement = {
    remove() {},
  };

  const head = {
    children: [],
    appendChild(el) {
      head.children.push(el);

      if (el.tag === 'script') {
        let {deferred, promise} = makeDeferredWithPromise();
        promises.push(promise);
        setTimeout(function () {
          let pathname = url.parse(el.src).pathname;
          let file = path.join(bundle.target.distDir, pathname);

          new vm.Script(
            // '"use strict";\n' +
            overlayFS.readFileSync(file, 'utf8'),
            {
              filename: pathname.slice(1),
            },
          ).runInContext(ctx);

          el.onload();
          deferred.resolve();
        }, 0);
      } else if (typeof el.onload === 'function') {
        el.onload();
      }
    },
  };

  let promises = [];

  const fakeDocument = {
    head,
    createElement(tag) {
      return {tag};
    },

    getElementsByTagName() {
      return [head];
    },

    createEvent() {
      // For Vue
      return {timeStamp: Date.now()};
    },

    getElementById(id) {
      if (id !== '__parcel__error__overlay__') return fakeElement;
    },

    body: {
      appendChild() {
        return null;
      },
    },

    currentScript: null,
  };

  var exports = {};

  function PatchedError(message) {
    const patchedError = new Error(message);
    const stackStart = patchedError.stack.indexOf('at new Error');
    const stackEnd = patchedError.stack.includes('at Script.runInContext')
      ? patchedError.stack.indexOf('at Script.runInContext')
      : patchedError.stack.indexOf('at runNextTicks');
    const stack = patchedError.stack.slice(stackStart, stackEnd).split('\n');
    stack.shift();
    stack.pop();
    for (let [i, line] of stack.entries()) {
      stack[i] = line.replace(
        /( ?.* )\(?(.*)\)?$/,
        (_, prefix, path) =>
          prefix +
          (path.endsWith(')')
            ? `(http://localhost/${path.slice(0, path.length - 1)})`
            : `http://localhost/${path}`),
      );
    }
    patchedError.stack =
      patchedError.stack.slice(0, stackStart).replace(/ +$/, '') +
      stack.join('\n');

    return patchedError;
  }

  PatchedError.prototype = Object.create(Error.prototype);
  Object.defineProperty(PatchedError, 'name', {
    writable: true,
    value: 'Error',
  });
  PatchedError.prototype.constructor = PatchedError;

  var ctx = Object.assign(
    {
      Error: PatchedError,
      exports,
      module: {exports},
      document: fakeDocument,
      WebSocket,
      console: {...console, clear: () => {}},
      location: {
        hostname: 'localhost',
        origin: 'http://localhost',
        protocol: 'http',
      },
      navigator: {
        userAgent: '',
      },
      fetch(url) {
        return Promise.resolve({
          async arrayBuffer() {
            let readFilePromise = overlayFS.readFile(
              path.join(path.dirname(bundle.target.distDir), url),
            );
            promises.push(readFilePromise);
            return new Uint8Array(await readFilePromise).buffer;
          },
          text() {
            let readFilePromise = overlayFS.readFile(
              path.join(path.dirname(bundle.target.distDir), url),
              'utf8',
            );
            promises.push(readFilePromise);
            return readFilePromise;
          },
        });
      },
      atob(str) {
        return Buffer.from(str, 'base64').toString('binary');
      },
      btoa(str) {
        return Buffer.from(str, 'binary').toString('base64');
      },
      URL,
      Worker: createWorkerClass(bundle.filePath),
    },
    globals,
  );

  ctx.window = ctx.self = ctx;
  return {ctx, promises};
}

function createWorkerClass(filePath: FilePath) {
  return class Worker extends EventEmitter {
    constructor(url) {
      super();
      this._run(url);
    }

    async _run(url) {
      let u = new URL(url);
      let filename = path.join(path.dirname(filePath), u.pathname);
      let {ctx, promises} = prepareWorkerContext(filename, {
        postMessage: msg => {
          this.emit('message', msg);
        },
      });

      let code = await overlayFS.readFile(filename, 'utf8');
      vm.createContext(ctx);
      new vm.Script(code, {
        filename: 'http://localhost/' + path.basename(filename),
      }).runInContext(ctx);

      if (promises) {
        await Promise.all(promises);
      }
    }

    addEventListener(evt, callback) {
      super.on(evt, callback);
    }

    removeEventListener(evt, callback) {
      super.removeListener(evt, callback);
    }
  };
}

function prepareWorkerContext(
  filePath: FilePath,
  globals: mixed,
): {|
  ctx: vm$Context,
  promises: Array<Promise<mixed>>,
|} {
  let promises = [];

  var exports = {};
  var ctx = Object.assign(
    {
      exports,
      module: {exports},
      WebSocket,
      console,
      location: {hostname: 'localhost', origin: 'http://localhost'},
      importScripts(...urls) {
        for (let u of urls) {
          new vm.Script(
            overlayFS.readFileSync(
              path.join(path.dirname(filePath), url.parse(u).pathname),
              'utf8',
            ),
            {
              filename: path.basename(url.parse(u).pathname),
            },
          ).runInContext(ctx);
        }
      },
      fetch(url) {
        return Promise.resolve({
          async arrayBuffer() {
            let readFilePromise = overlayFS.readFile(
              path.join(path.dirname(filePath), url),
            );
            promises.push(readFilePromise);
            return new Uint8Array(await readFilePromise).buffer;
          },
          text() {
            let readFilePromise = overlayFS.readFile(
              path.join(path.dirname(filePath), url),
              'utf8',
            );
            promises.push(readFilePromise);
            return readFilePromise;
          },
        });
      },
      atob(str) {
        return Buffer.from(str, 'base64').toString('binary');
      },
      btoa(str) {
        return Buffer.from(str, 'binary').toString('base64');
      },
      URL,
      Worker: createWorkerClass(filePath),
    },
    globals,
  );

  ctx.window = ctx.self = ctx;
  return {ctx, promises};
}

const nodeCache = new Map();
// no filepath = ESM
// $FlowFixMe
function prepareNodeContext(filePath, globals, ctx: any = {}) {
  let exports = {};
  let req =
    filePath &&
    (specifier => {
      // $FlowFixMe[prop-missing]
      let res = resolve.sync(specifier, {
        basedir: path.dirname(filePath),
        preserveSymlinks: true,
        extensions: ['.js', '.json'],
        readFileSync: (...args) => {
          return overlayFS.readFileSync(...args);
        },
        isFile: file => {
          try {
            var stat = overlayFS.statSync(file);
          } catch (err) {
            return false;
          }
          return stat.isFile();
        },
        isDirectory: file => {
          try {
            var stat = overlayFS.statSync(file);
          } catch (err) {
            return false;
          }
          return stat.isDirectory();
        },
      });

      // Shim FS module using overlayFS
      if (res === 'fs') {
        return {
          readFile: async (file, encoding, cb) => {
            let res = await overlayFS.readFile(file, encoding);
            cb(null, res);
          },
          readFileSync: (file, encoding) => {
            return overlayFS.readFileSync(file, encoding);
          },
        };
      }

      if (res === specifier) {
        // $FlowFixMe[unsupported-syntax]
        return require(specifier);
      }

      let cached = nodeCache.get(res);
      if (cached) {
        return cached.module.exports;
      }

      let g = {
        ...globals,
      };

      for (let key in ctx) {
        if (
          key !== 'module' &&
          key !== 'exports' &&
          key !== '__filename' &&
          key !== '__dirname' &&
          key !== 'require'
        ) {
          g[key] = ctx[key];
        }
      }

      let childCtx = prepareNodeContext(res, g);
      nodeCache.set(res, childCtx);

      vm.createContext(childCtx);
      new vm.Script(
        //'"use strict";\n' +
        overlayFS.readFileSync(res, 'utf8'),
        {
          filename: path.basename(res),
        },
      ).runInContext(childCtx);
      return childCtx.module.exports;
    });

  if (filePath) {
    ctx.module = {exports, require: req};
    ctx.exports = exports;
    ctx.__filename = filePath;
    ctx.__dirname = path.dirname(filePath);
    ctx.require = req;
  }

  ctx.console = console;
  ctx.process = process;
  ctx.setTimeout = setTimeout;
  ctx.setImmediate = setImmediate;
  ctx.global = ctx;
  ctx.URL = URL;
  Object.assign(ctx, globals);
  return ctx;
}

let instanceId = 0;
export async function runESM(
  baseDir: FilePath,
  entries: Array<[string, string]>,
  context: vm$Context,
  fs: FileSystem,
  externalModules: ExternalModules = {},
  requireExtensions: boolean = false,
): Promise<Array<{|[string]: mixed|}>> {
  let id = instanceId++;
  let cache = new Map();
  function load(specifier, referrer, code = null) {
    if (path.isAbsolute(specifier) || specifier.startsWith('.')) {
      let extname = path.extname(specifier);
      if (extname && extname !== '.js' && extname !== '.mjs') {
        throw new Error(
          'Unknown file extension in ' +
            specifier +
            ' from ' +
            referrer.identifier,
        );
      }
      let filename = path.resolve(
        baseDir,
        path.dirname(referrer.identifier),
        !extname && !requireExtensions ? specifier + '.js' : specifier,
      );

      let m = cache.get(filename);
      if (m) {
        return m;
      }

      let source = code ?? fs.readFileSync(filename, 'utf8');
      // $FlowFixMe Experimental
      m = new vm.SourceTextModule(source, {
        identifier: `${normalizeSeparators(
          path.relative(baseDir, filename),
        )}?id=${id}`,
        importModuleDynamically: entry,
        context,
        initializeImportMeta(meta) {
          meta.url = `http://localhost/${path.basename(filename)}`;
        },
      });
      cache.set(filename, m);
      return m;
    } else {
      if (!(specifier in externalModules)) {
        throw new Error(
          `Couldn't resolve ${specifier} from ${referrer.identifier}`,
        );
      }

      let m = cache.get(specifier);
      if (m) {
        return m;
      }

      let ns = externalModules[specifier](context);

      // $FlowFixMe Experimental
      m = new vm.SyntheticModule(
        Object.keys(ns),
        function () {
          for (let [k, v] of Object.entries(ns)) {
            this.setExport(k, v);
          }
        },
        {identifier: specifier, context},
      );
      cache.set(specifier, m);
      return m;
    }
  }

  async function _entry(m) {
    if (m.status === 'unlinked') {
      await m.link((specifier, referrer) => load(specifier, referrer));
    }
    if (m.status === 'linked') {
      await m.evaluate();
    }
    return m;
  }

  let entryPromises = new Map();
  function entry(specifier, referrer, code) {
    let m = load(specifier, referrer, code);
    let promise = entryPromises.get(m);
    if (!promise) {
      promise = _entry(m);
      entryPromises.set(m, promise);
    }
    return promise;
  }

  let modules = [];
  for (let [code, f] of entries) {
    modules.push(await entry(f, {identifier: ''}, code));
  }

  for (let m of modules) {
    if (m.status === 'errored') {
      throw m.error;
    }
  }

  return modules.map(m => m.namespace);
}

export async function assertESMExports(
  b: BundleGraph<PackagedBundle>,
  expected: mixed,
  externalModules?: ExternalModules,
  // $FlowFixMe[unclear-type]
  evaluate: ?({|[string]: any|}) => mixed,
) {
  let parcelResult = await run(b, undefined, undefined, externalModules);

  let entry = nullthrows(
    b
      .getBundles()
      .find(b => b.type === 'js')
      ?.getMainEntry(),
  );
  nodeCache.clear();
  let [nodeResult] = await runESM(
    b.getBundles()[0].target.distDir,
    [[await inputFS.readFile(entry.filePath, 'utf8'), entry.filePath]],
    vm.createContext(prepareNodeContext(false, {})),
    inputFS,
    externalModules,
  );

  if (evaluate) {
    parcelResult = await evaluate(parcelResult);
    nodeResult = await evaluate(nodeResult);
  }
  assert.deepEqual(
    parcelResult,
    nodeResult,
    "Bundle exports don't match Node's native behaviour",
  );

  if (!evaluate) {
    parcelResult = {...parcelResult};
  }
  assert.deepEqual(parcelResult, expected);
}

export async function assertNoFilePathInCache(
  fs: FileSystem,
  dir: string,
  projectRoot: string,
) {
  let entries = await fs.readdir(dir);
  for (let entry of entries) {
    // Skip watcher snapshots for linux/windows, which contain full file paths.
    if (path.extname(entry) === '.txt') {
      continue;
    }

    let fullPath = path.join(dir, entry);
    let stat = await fs.stat(fullPath);
    if (stat.isDirectory()) {
      await assertNoFilePathInCache(fs, fullPath, projectRoot);
    } else if (stat.isFile()) {
      let contents = await fs.readFile(fullPath);

      // For debugging purposes, log all instances of the projectRoot in the cache.
      // Otherwise, fail the test if one is found.
      if (process.env.PARCEL_DEBUG_CACHE_FILEPATH != null) {
        if (contents.includes(projectRoot)) {
          let deserialized;
          try {
            // $FlowFixMe
            deserialized = v8.deserialize(contents);
          } catch (err) {
            // rudimentary detection of binary files
            if (!contents.includes(0)) {
              deserialized = contents.toString();
            } else {
              deserialized = contents;
            }
          }

          if (deserialized != null) {
            // eslint-disable-next-line no-console
            console.log(
              `Found projectRoot ${projectRoot} in cache file ${fullPath}`,
            );
            // eslint-disable-next-line no-console
            console.log(
              require('util').inspect(deserialized, {depth: 50, colors: true}),
            );
          }
        }
      } else {
        assert(
          !contents.includes(projectRoot),
          `Found projectRoot ${projectRoot} in cache file ${fullPath}`,
        );
      }
    }
  }
}


// @flow strict-local

import type {PackageName, ConfigResult} from '@parcel/types';
import type {
  Config,
  Environment,
  InternalFileCreateInvalidation,
  InternalDevDepOptions,
} from './types';
import type {ProjectPath} from './projectPath';

import {fromProjectPathRelative} from './projectPath';
import {createEnvironment} from './Environment';
import {hashString} from '@parcel/hash';

type ConfigOpts = {|
  plugin: PackageName,
  searchPath: ProjectPath,
  isSource?: boolean,
  env?: Environment,
  result?: ConfigResult,
  invalidateOnFileChange?: Set<ProjectPath>,
  invalidateOnFileCreate?: Array<InternalFileCreateInvalidation>,
  invalidateOnEnvChange?: Set<string>,
  invalidateOnOptionChange?: Set<string>,
  devDeps?: Array<InternalDevDepOptions>,
  invalidateOnStartup?: boolean,
|};

export function createConfig({
  plugin,
  isSource,
  searchPath,
  env,
  result,
  invalidateOnFileChange,
  invalidateOnFileCreate,
  invalidateOnEnvChange,
  invalidateOnOptionChange,
  devDeps,
  invalidateOnStartup,
}: ConfigOpts): Config {
  let environment = env ?? createEnvironment();
  return {
    id: hashString(
      plugin +
        fromProjectPathRelative(searchPath) +
        environment.id +
        String(isSource),
    ),
    isSource: isSource ?? false,
    searchPath,
    env: environment,
    result: result ?? null,
    cacheKey: null,
    invalidateOnFileChange: invalidateOnFileChange ?? new Set(),
    invalidateOnFileCreate: invalidateOnFileCreate ?? [],
    invalidateOnEnvChange: invalidateOnEnvChange ?? new Set(),
    invalidateOnOptionChange: invalidateOnOptionChange ?? new Set(),
    devDeps: devDeps ?? [],
    invalidateOnStartup: invalidateOnStartup ?? false,
  };
}


output('a');

if (b) {
  require('./b');
}

output('d');


importModuleDynamically: (specifier, referrer) =>


import { add } from "lodash";

export default `async-${add(1, 2)}`;


const b = require('./b');

b.changeFoo(3);

output = b.default + b.foo + b.bar;