diff --git a/.eslintignore b/.eslintignore index ca1cfe1eb05..5053ed53c34 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,3 +14,4 @@ dist coverage node_modules +tmp diff --git a/.gitignore b/.gitignore index 23af83c4273..830d7a4cac7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* +tmp diff --git a/.prettierignore b/.prettierignore index 6aac09c05e5..bc04c907c1f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,3 +2,4 @@ packages/core/parcel-bundler/src/builtins packages/core/parcel-bundler/test/integration packages/core/parcel-bundler/test/input packages/core/parcel-bundler/test/dist +tmp diff --git a/packages/core/fs/.gitignore b/packages/core/fs/.gitignore new file mode 100644 index 00000000000..a65b41774ad --- /dev/null +++ b/packages/core/fs/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/core/fs/index.js b/packages/core/fs/index.js new file mode 100644 index 00000000000..d5bd6981bc3 --- /dev/null +++ b/packages/core/fs/index.js @@ -0,0 +1,5 @@ +// Node 8 supports native async functions - no need to use compiled code! +module.exports = + parseInt(process.versions.node, 10) < 8 + ? require('./lib/fs') + : require('./src/fs'); diff --git a/packages/core/fs/package.json b/packages/core/fs/package.json new file mode 100644 index 00000000000..a457e82e649 --- /dev/null +++ b/packages/core/fs/package.json @@ -0,0 +1,26 @@ +{ + "name": "@parcel/fs", + "version": "1.10.3", + "description": "Blazing fast, zero configuration web application bundler", + "main": "index.js", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/parcel-bundler/parcel.git" + }, + "engines": { + "node": ">= 6.0.0" + }, + "scripts": { + "test": "echo this package has no tests yet", + "test-ci": "yarn build && yarn test", + "format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"", + "lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different", + "build": "babel src -d lib", + "prepublish": "yarn build" + }, + "dependencies": { + "mkdirp": "^0.5.1", + "@parcel/utils": "^1.10.3" + } +} diff --git a/packages/core/fs/src/.babelrc b/packages/core/fs/src/.babelrc new file mode 100644 index 00000000000..1968e060259 --- /dev/null +++ b/packages/core/fs/src/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [["@babel/preset-env", { + "targets": { + "node": "6" + } + }]], + "plugins": ["@babel/plugin-transform-runtime"] +} diff --git a/packages/core/fs/src/.eslintrc.json b/packages/core/fs/src/.eslintrc.json new file mode 100644 index 00000000000..387cf114abe --- /dev/null +++ b/packages/core/fs/src/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../../.eslintrc.json" +} diff --git a/packages/core/parcel-bundler/src/utils/fs.js b/packages/core/fs/src/fs.js similarity index 93% rename from packages/core/parcel-bundler/src/utils/fs.js rename to packages/core/fs/src/fs.js index 830efa410aa..2677726507a 100644 --- a/packages/core/parcel-bundler/src/utils/fs.js +++ b/packages/core/fs/src/fs.js @@ -1,4 +1,4 @@ -const promisify = require('./promisify'); +const {promisify} = require('@parcel/utils'); const fs = require('fs'); const mkdirp = require('mkdirp'); diff --git a/packages/core/logger/package.json b/packages/core/logger/package.json index dd781655fba..d7146e6111f 100644 --- a/packages/core/logger/package.json +++ b/packages/core/logger/package.json @@ -21,7 +21,8 @@ }, "devDependencies": { "mocha": "^5.2.0", - "sinon": "^5.0.1" + "sinon": "^5.0.1", + "@parcel/babel-register": "^1.10.3" }, "dependencies": { "chalk": "^2.1.0", diff --git a/packages/core/parcel-bundler/.gitignore b/packages/core/parcel-bundler/.gitignore index 98e260578c0..bc45e23288c 100644 --- a/packages/core/parcel-bundler/.gitignore +++ b/packages/core/parcel-bundler/.gitignore @@ -8,3 +8,4 @@ test/integration/**/Cargo.lock test/**/node_modules test/**/yarn.lock test/**/package-lock.json +test/integration/babel-plugin-autoinstall/package.json \ No newline at end of file diff --git a/packages/core/parcel-bundler/package.json b/packages/core/parcel-bundler/package.json index ec5c7ba84c3..20f9140b887 100644 --- a/packages/core/parcel-bundler/package.json +++ b/packages/core/parcel-bundler/package.json @@ -70,7 +70,9 @@ "ws": "^5.1.1", "@parcel/watcher": "1.10.3", "@parcel/workers": "^1.10.3", - "@parcel/logger": "^1.10.3" + "@parcel/logger": "^1.10.3", + "@parcel/utils": "^1.10.3", + "@parcel/fs": "^1.10.3" }, "devDependencies": { "@babel/cli": "^7.0.0", @@ -114,7 +116,8 @@ "typescript": "^3.0.0", "vue": "^2.5.16", "vue-template-compiler": "^2.5.16", - "@parcel/babel-register": "^1.10.3" + "@parcel/babel-register": "^1.10.3", + "@parcel/test-utils": "^1.10.3" }, "scripts": { "test": "cross-env NODE_ENV=test mocha", diff --git a/packages/core/parcel-bundler/src/Asset.js b/packages/core/parcel-bundler/src/Asset.js index 1a68aa377f1..3b3c11a7668 100644 --- a/packages/core/parcel-bundler/src/Asset.js +++ b/packages/core/parcel-bundler/src/Asset.js @@ -1,7 +1,7 @@ const URL = require('url'); const path = require('path'); const clone = require('clone'); -const fs = require('./utils/fs'); +const fs = require('@parcel/fs'); const md5 = require('./utils/md5'); const isURL = require('./utils/is-url'); const config = require('./utils/config'); diff --git a/packages/core/parcel-bundler/src/Bundler.js b/packages/core/parcel-bundler/src/Bundler.js index d577f4654fe..f82222bf88f 100644 --- a/packages/core/parcel-bundler/src/Bundler.js +++ b/packages/core/parcel-bundler/src/Bundler.js @@ -1,4 +1,4 @@ -const fs = require('./utils/fs'); +const fs = require('@parcel/fs'); const Resolver = require('./Resolver'); const Parser = require('./Parser'); const WorkerFarm = require('@parcel/workers'); diff --git a/packages/core/parcel-bundler/src/FSCache.js b/packages/core/parcel-bundler/src/FSCache.js index ff683ef4fd4..dedee45b2ea 100644 --- a/packages/core/parcel-bundler/src/FSCache.js +++ b/packages/core/parcel-bundler/src/FSCache.js @@ -1,4 +1,4 @@ -const fs = require('./utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const md5 = require('./utils/md5'); const objectHash = require('./utils/objectHash'); diff --git a/packages/core/parcel-bundler/src/Resolver.js b/packages/core/parcel-bundler/src/Resolver.js index d6de2c32bbe..d5037782eaa 100755 --- a/packages/core/parcel-bundler/src/Resolver.js +++ b/packages/core/parcel-bundler/src/Resolver.js @@ -2,7 +2,7 @@ const builtins = require('./builtins'); const nodeBuiltins = require('node-libs-browser'); const path = require('path'); const {isGlob} = require('./utils/glob'); -const fs = require('./utils/fs'); +const fs = require('@parcel/fs'); const micromatch = require('micromatch'); const EMPTY_SHIM = require.resolve('./builtins/_empty'); diff --git a/packages/core/parcel-bundler/src/assets/GLSLAsset.js b/packages/core/parcel-bundler/src/assets/GLSLAsset.js index dee790d8191..fc69a96cf88 100644 --- a/packages/core/parcel-bundler/src/assets/GLSLAsset.js +++ b/packages/core/parcel-bundler/src/assets/GLSLAsset.js @@ -1,7 +1,7 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); const path = require('path'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const Resolver = require('../Resolver'); class GLSLAsset extends Asset { diff --git a/packages/core/parcel-bundler/src/assets/GraphqlAsset.js b/packages/core/parcel-bundler/src/assets/GraphqlAsset.js index 8517e2574f3..c03bea63f42 100644 --- a/packages/core/parcel-bundler/src/assets/GraphqlAsset.js +++ b/packages/core/parcel-bundler/src/assets/GraphqlAsset.js @@ -1,7 +1,7 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); const Resolver = require('../Resolver'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const os = require('os'); const IMPORT_RE = /^# *import +['"](.*)['"] *;? *$/; diff --git a/packages/core/parcel-bundler/src/assets/JSAsset.js b/packages/core/parcel-bundler/src/assets/JSAsset.js index ef758511b6e..5e108895203 100644 --- a/packages/core/parcel-bundler/src/assets/JSAsset.js +++ b/packages/core/parcel-bundler/src/assets/JSAsset.js @@ -14,7 +14,7 @@ const terser = require('../transforms/terser'); const SourceMap = require('../SourceMap'); const hoist = require('../scope-hoisting/hoist'); const path = require('path'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const logger = require('@parcel/logger'); const IMPORT_RE = /\b(?:import\b|export\b|require\s*\()/; diff --git a/packages/core/parcel-bundler/src/assets/LESSAsset.js b/packages/core/parcel-bundler/src/assets/LESSAsset.js index aa4241edf24..f9784899809 100644 --- a/packages/core/parcel-bundler/src/assets/LESSAsset.js +++ b/packages/core/parcel-bundler/src/assets/LESSAsset.js @@ -1,8 +1,8 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const Resolver = require('../Resolver'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const parseCSSImport = require('../utils/parseCSSImport'); diff --git a/packages/core/parcel-bundler/src/assets/ReasonAsset.js b/packages/core/parcel-bundler/src/assets/ReasonAsset.js index 5d5509fa6bd..1c195457c3b 100644 --- a/packages/core/parcel-bundler/src/assets/ReasonAsset.js +++ b/packages/core/parcel-bundler/src/assets/ReasonAsset.js @@ -1,5 +1,5 @@ const Asset = require('../Asset'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const localRequire = require('../utils/localRequire'); class ReasonAsset extends Asset { diff --git a/packages/core/parcel-bundler/src/assets/RustAsset.js b/packages/core/parcel-bundler/src/assets/RustAsset.js index 2c6cfb690f3..cb73ac93e1c 100644 --- a/packages/core/parcel-bundler/src/assets/RustAsset.js +++ b/packages/core/parcel-bundler/src/assets/RustAsset.js @@ -1,10 +1,10 @@ const path = require('path'); const commandExists = require('command-exists'); const childProcess = require('child_process'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const exec = promisify(childProcess.execFile); const tomlify = require('tomlify-j0.4'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const Asset = require('../Asset'); const config = require('../utils/config'); const pipeSpawn = require('../utils/pipeSpawn'); diff --git a/packages/core/parcel-bundler/src/assets/SASSAsset.js b/packages/core/parcel-bundler/src/assets/SASSAsset.js index b62524bcd96..3760328e650 100644 --- a/packages/core/parcel-bundler/src/assets/SASSAsset.js +++ b/packages/core/parcel-bundler/src/assets/SASSAsset.js @@ -1,6 +1,6 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const path = require('path'); const os = require('os'); const Resolver = require('../Resolver'); diff --git a/packages/core/parcel-bundler/src/assets/StylusAsset.js b/packages/core/parcel-bundler/src/assets/StylusAsset.js index 114fc0e293f..b32946380c7 100644 --- a/packages/core/parcel-bundler/src/assets/StylusAsset.js +++ b/packages/core/parcel-bundler/src/assets/StylusAsset.js @@ -2,7 +2,7 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); const Resolver = require('../Resolver'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); const {dirname, resolve, relative} = require('path'); const {isGlob, glob} = require('../utils/glob'); diff --git a/packages/core/parcel-bundler/src/packagers/Packager.js b/packages/core/parcel-bundler/src/packagers/Packager.js index c60dfb7fd4a..ab5f9fbfb09 100644 --- a/packages/core/parcel-bundler/src/packagers/Packager.js +++ b/packages/core/parcel-bundler/src/packagers/Packager.js @@ -1,7 +1,7 @@ const fs = require('fs'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const path = require('path'); -const {mkdirp} = require('../utils/fs'); +const {mkdirp} = require('@parcel/fs'); class Packager { constructor(bundle, bundler) { diff --git a/packages/core/parcel-bundler/src/packagers/RawPackager.js b/packages/core/parcel-bundler/src/packagers/RawPackager.js index 06677f38b04..90f1701c6b7 100644 --- a/packages/core/parcel-bundler/src/packagers/RawPackager.js +++ b/packages/core/parcel-bundler/src/packagers/RawPackager.js @@ -1,6 +1,6 @@ const Packager = require('./Packager'); const path = require('path'); -const fs = require('../utils/fs'); +const fs = require('@parcel/fs'); class RawPackager extends Packager { static shouldAddAsset() { diff --git a/packages/core/parcel-bundler/src/transforms/babel/babelrc.js b/packages/core/parcel-bundler/src/transforms/babel/babelrc.js index a7e44ebec20..f5a67aac31c 100644 --- a/packages/core/parcel-bundler/src/transforms/babel/babelrc.js +++ b/packages/core/parcel-bundler/src/transforms/babel/babelrc.js @@ -3,7 +3,7 @@ const logger = require('@parcel/logger'); const path = require('path'); const localRequire = require('../../utils/localRequire'); const installPackage = require('../../utils/installPackage'); -const fs = require('../../utils/fs'); +const fs = require('@parcel/fs'); const micromatch = require('micromatch'); async function getBabelConfig(asset, isSource) { diff --git a/packages/core/parcel-bundler/src/transforms/babel/config.js b/packages/core/parcel-bundler/src/transforms/babel/config.js index abffc73bc80..cc48de65fdb 100644 --- a/packages/core/parcel-bundler/src/transforms/babel/config.js +++ b/packages/core/parcel-bundler/src/transforms/babel/config.js @@ -3,7 +3,7 @@ const getEnvConfig = require('./env'); const getJSXConfig = require('./jsx'); const getFlowConfig = require('./flow'); const path = require('path'); -const fs = require('../../utils/fs'); +const fs = require('@parcel/fs'); const NODE_MODULES = `${path.sep}node_modules${path.sep}`; const ENV_PLUGINS = require('@babel/preset-env/data/plugins'); diff --git a/packages/core/parcel-bundler/src/utils/config.js b/packages/core/parcel-bundler/src/utils/config.js index 7fd06af210d..56633d3cb6f 100644 --- a/packages/core/parcel-bundler/src/utils/config.js +++ b/packages/core/parcel-bundler/src/utils/config.js @@ -1,4 +1,4 @@ -const fs = require('./fs'); +const fs = require('@parcel/fs'); const path = require('path'); const clone = require('clone'); diff --git a/packages/core/parcel-bundler/src/utils/getCertificate.js b/packages/core/parcel-bundler/src/utils/getCertificate.js index 43d3afec6b2..91552aacc56 100644 --- a/packages/core/parcel-bundler/src/utils/getCertificate.js +++ b/packages/core/parcel-bundler/src/utils/getCertificate.js @@ -1,4 +1,4 @@ -const fs = require('./fs'); +const fs = require('@parcel/fs'); async function getCertificate(options) { try { diff --git a/packages/core/parcel-bundler/src/utils/installPackage.js b/packages/core/parcel-bundler/src/utils/installPackage.js index 4748ccd784e..fee03bcb207 100644 --- a/packages/core/parcel-bundler/src/utils/installPackage.js +++ b/packages/core/parcel-bundler/src/utils/installPackage.js @@ -1,12 +1,12 @@ const config = require('./config'); -const promisify = require('./promisify'); +const {promisify} = require('@parcel/utils'); const resolve = promisify(require('resolve')); const commandExists = require('command-exists'); const logger = require('@parcel/logger'); const pipeSpawn = require('./pipeSpawn'); const PromiseQueue = require('./PromiseQueue'); const path = require('path'); -const fs = require('./fs'); +const fs = require('@parcel/fs'); const WorkerFarm = require('@parcel/workers'); const YARN_LOCK = 'yarn.lock'; diff --git a/packages/core/parcel-bundler/src/utils/localRequire.js b/packages/core/parcel-bundler/src/utils/localRequire.js index a34e6e84c57..24f20da9345 100644 --- a/packages/core/parcel-bundler/src/utils/localRequire.js +++ b/packages/core/parcel-bundler/src/utils/localRequire.js @@ -1,5 +1,5 @@ const {dirname} = require('path'); -const promisify = require('../utils/promisify'); +const {promisify} = require('@parcel/utils'); const resolve = promisify(require('resolve')); const installPackage = require('./installPackage'); diff --git a/packages/core/parcel-bundler/test/asset.js b/packages/core/parcel-bundler/test/asset.js index 5992bf3b740..11f83118cba 100644 --- a/packages/core/parcel-bundler/test/asset.js +++ b/packages/core/parcel-bundler/test/asset.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const Asset = require('../src/Asset'); const {bundle} = require('./utils'); diff --git a/packages/core/parcel-bundler/test/autoinstall.js b/packages/core/parcel-bundler/test/autoinstall.js index 86cb8e90a54..0aa5b8fb098 100644 --- a/packages/core/parcel-bundler/test/autoinstall.js +++ b/packages/core/parcel-bundler/test/autoinstall.js @@ -1,6 +1,6 @@ const assert = require('assert'); const install = require('../src/utils/installPackage'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {ncp, rimraf} = require('./utils'); const path = require('path'); const inputDirPath = path.join(__dirname, '/input'); diff --git a/packages/core/parcel-bundler/test/babel.js b/packages/core/parcel-bundler/test/babel.js index 8ebedb1d75a..5383d61392e 100644 --- a/packages/core/parcel-bundler/test/babel.js +++ b/packages/core/parcel-bundler/test/babel.js @@ -1,5 +1,5 @@ const babelCore = require('@babel/core'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const { babel6toBabel7, babel7toBabel6 diff --git a/packages/core/parcel-bundler/test/contentHashing.js b/packages/core/parcel-bundler/test/contentHashing.js index b230e44556b..9dc279db394 100644 --- a/packages/core/parcel-bundler/test/contentHashing.js +++ b/packages/core/parcel-bundler/test/contentHashing.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, rimraf, ncp} = require('./utils'); describe('content hashing', function() { diff --git a/packages/core/parcel-bundler/test/css.js b/packages/core/parcel-bundler/test/css.js index feeba213645..44be2354224 100644 --- a/packages/core/parcel-bundler/test/css.js +++ b/packages/core/parcel-bundler/test/css.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree, rimraf, ncp} = require('./utils'); describe('css', function() { diff --git a/packages/core/parcel-bundler/test/elm.js b/packages/core/parcel-bundler/test/elm.js index 245ac4ca46c..31174c29615 100644 --- a/packages/core/parcel-bundler/test/elm.js +++ b/packages/core/parcel-bundler/test/elm.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, assertBundleTree, run} = require('./utils'); describe('elm', function() { diff --git a/packages/core/parcel-bundler/test/encodedURI.js b/packages/core/parcel-bundler/test/encodedURI.js index bcaee2be22e..8450f167962 100644 --- a/packages/core/parcel-bundler/test/encodedURI.js +++ b/packages/core/parcel-bundler/test/encodedURI.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, assertBundleTree} = require('./utils'); describe('encodedURI', function() { diff --git a/packages/core/parcel-bundler/test/fs-cache.js b/packages/core/parcel-bundler/test/fs-cache.js index feb1734eb32..f868ce894fa 100644 --- a/packages/core/parcel-bundler/test/fs-cache.js +++ b/packages/core/parcel-bundler/test/fs-cache.js @@ -1,7 +1,8 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); -const {sleep, rimraf, ncp} = require('./utils'); +const fs = require('@parcel/fs'); +const {rimraf, ncp} = require('./utils'); +const {sleep} = require('@parcel/test-utils'); const FSCache = require('../src/FSCache'); const cachePath = path.join(__dirname, '.cache'); diff --git a/packages/core/parcel-bundler/test/fs.js b/packages/core/parcel-bundler/test/fs.js index c3640d988e1..1bfa96c2721 100644 --- a/packages/core/parcel-bundler/test/fs.js +++ b/packages/core/parcel-bundler/test/fs.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const {bundle, run, assertBundleTree} = require('./utils'); diff --git a/packages/core/parcel-bundler/test/generateCertificate.js b/packages/core/parcel-bundler/test/generateCertificate.js index ede0d1d1179..844156eb7a0 100644 --- a/packages/core/parcel-bundler/test/generateCertificate.js +++ b/packages/core/parcel-bundler/test/generateCertificate.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const generateCertificate = require('../src/utils/generateCertificate'); const {rimraf, ncp} = require('./utils'); diff --git a/packages/core/parcel-bundler/test/getCertificate.js b/packages/core/parcel-bundler/test/getCertificate.js index 841e4be378c..67a1b99f7f2 100644 --- a/packages/core/parcel-bundler/test/getCertificate.js +++ b/packages/core/parcel-bundler/test/getCertificate.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const getCertificate = require('../src/utils/getCertificate'); const https = { diff --git a/packages/core/parcel-bundler/test/glob.js b/packages/core/parcel-bundler/test/glob.js index f63268010bd..9663d7be4b6 100644 --- a/packages/core/parcel-bundler/test/glob.js +++ b/packages/core/parcel-bundler/test/glob.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const {bundle, run, assertBundleTree} = require('./utils'); diff --git a/packages/core/parcel-bundler/test/glsl.js b/packages/core/parcel-bundler/test/glsl.js index f28f67e5cfb..b8b1db9e3db 100644 --- a/packages/core/parcel-bundler/test/glsl.js +++ b/packages/core/parcel-bundler/test/glsl.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree, normaliseNewlines} = require('./utils'); describe('glsl', function() { diff --git a/packages/core/parcel-bundler/test/hmr.js b/packages/core/parcel-bundler/test/hmr.js index a7b0fee1f68..e06276c6e30 100644 --- a/packages/core/parcel-bundler/test/hmr.js +++ b/packages/core/parcel-bundler/test/hmr.js @@ -1,7 +1,8 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); -const {bundler, run, sleep, rimraf, ncp} = require('./utils'); +const {bundler, run, rimraf, ncp} = require('./utils'); +const {sleep} = require('@parcel/test-utils'); const WebSocket = require('ws'); const json5 = require('json5'); const sinon = require('sinon'); diff --git a/packages/core/parcel-bundler/test/html.js b/packages/core/parcel-bundler/test/html.js index aba09e19e87..b5574abf088 100644 --- a/packages/core/parcel-bundler/test/html.js +++ b/packages/core/parcel-bundler/test/html.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, assertBundleTree} = require('./utils'); const path = require('path'); diff --git a/packages/core/parcel-bundler/test/javascript.js b/packages/core/parcel-bundler/test/javascript.js index a19a6a31f13..afb7605b934 100644 --- a/packages/core/parcel-bundler/test/javascript.js +++ b/packages/core/parcel-bundler/test/javascript.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const { bundle, @@ -9,7 +9,7 @@ const { deferred, ncp } = require('./utils'); -const {mkdirp} = require('../src/utils/fs'); +const {mkdirp} = require('@parcel/fs'); const {symlinkSync} = require('fs'); describe('javascript', function() { diff --git a/packages/core/parcel-bundler/test/less.js b/packages/core/parcel-bundler/test/less.js index 01d9c880504..da522dd66d0 100644 --- a/packages/core/parcel-bundler/test/less.js +++ b/packages/core/parcel-bundler/test/less.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree} = require('./utils'); describe('less', function() { diff --git a/packages/core/parcel-bundler/test/lineCounter.js b/packages/core/parcel-bundler/test/lineCounter.js index 1fb8a37be0c..e74c65ee15d 100644 --- a/packages/core/parcel-bundler/test/lineCounter.js +++ b/packages/core/parcel-bundler/test/lineCounter.js @@ -1,4 +1,4 @@ -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const assert = require('assert'); const lineCounter = require('../src/utils/lineCounter'); diff --git a/packages/core/parcel-bundler/test/parser.js b/packages/core/parcel-bundler/test/parser.js index 0b82acfa26b..af10583255a 100644 --- a/packages/core/parcel-bundler/test/parser.js +++ b/packages/core/parcel-bundler/test/parser.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, assertBundleTree} = require('./utils'); describe('parser', function() { diff --git a/packages/core/parcel-bundler/test/pug.js b/packages/core/parcel-bundler/test/pug.js index c35ebcddd7e..79bc8f0f778 100644 --- a/packages/core/parcel-bundler/test/pug.js +++ b/packages/core/parcel-bundler/test/pug.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, assertBundleTree, normaliseNewlines} = require('./utils'); describe('pug', function() { diff --git a/packages/core/parcel-bundler/test/resolver.js b/packages/core/parcel-bundler/test/resolver.js index 9c8259d0a2a..f645f23dd8d 100644 --- a/packages/core/parcel-bundler/test/resolver.js +++ b/packages/core/parcel-bundler/test/resolver.js @@ -2,7 +2,7 @@ const Resolver = require('../src/Resolver'); const path = require('path'); const assert = require('assert'); const {rimraf, ncp} = require('./utils'); -const {mkdirp} = require('../src/utils/fs'); +const {mkdirp} = require('@parcel/fs'); const {symlinkSync} = require('fs'); const rootDir = path.join(__dirname, 'input/resolver'); diff --git a/packages/core/parcel-bundler/test/rust.js b/packages/core/parcel-bundler/test/rust.js index a6935b04c54..90e4bd11b60 100644 --- a/packages/core/parcel-bundler/test/rust.js +++ b/packages/core/parcel-bundler/test/rust.js @@ -1,7 +1,7 @@ const assert = require('assert'); const path = require('path'); const {bundle, bundler, run, assertBundleTree} = require('./utils'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const commandExists = require('command-exists'); describe('rust', function() { diff --git a/packages/core/parcel-bundler/test/sass.js b/packages/core/parcel-bundler/test/sass.js index fdcafa2c7fd..5febcbd0e64 100644 --- a/packages/core/parcel-bundler/test/sass.js +++ b/packages/core/parcel-bundler/test/sass.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree} = require('./utils'); describe('sass', function() { diff --git a/packages/core/parcel-bundler/test/scope-hoisting.js b/packages/core/parcel-bundler/test/scope-hoisting.js index 65dd8a71f55..f54d6949763 100644 --- a/packages/core/parcel-bundler/test/scope-hoisting.js +++ b/packages/core/parcel-bundler/test/scope-hoisting.js @@ -1,7 +1,7 @@ const assert = require('assert'); const path = require('path'); const {bundle: _bundle, run} = require('./utils'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const bundle = (name, opts = {}) => _bundle(name, Object.assign({scopeHoist: true}, opts)); diff --git a/packages/core/parcel-bundler/test/server.js b/packages/core/parcel-bundler/test/server.js index cff34af0164..a00cd6c4816 100644 --- a/packages/core/parcel-bundler/test/server.js +++ b/packages/core/parcel-bundler/test/server.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundler} = require('./utils'); const http = require('http'); const https = require('https'); diff --git a/packages/core/parcel-bundler/test/sourcemaps.js b/packages/core/parcel-bundler/test/sourcemaps.js index 598343a52ce..4eafe6b6e05 100644 --- a/packages/core/parcel-bundler/test/sourcemaps.js +++ b/packages/core/parcel-bundler/test/sourcemaps.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); const mapValidator = require('sourcemap-validator'); const {bundler, bundle, run, assertBundleTree} = require('./utils'); diff --git a/packages/core/parcel-bundler/test/stylus.js b/packages/core/parcel-bundler/test/stylus.js index 3704be344dc..40458ed46ed 100644 --- a/packages/core/parcel-bundler/test/stylus.js +++ b/packages/core/parcel-bundler/test/stylus.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree} = require('./utils'); describe('stylus', function() { diff --git a/packages/core/parcel-bundler/test/sugarss.js b/packages/core/parcel-bundler/test/sugarss.js index bf83487f41d..6f0e18994df 100644 --- a/packages/core/parcel-bundler/test/sugarss.js +++ b/packages/core/parcel-bundler/test/sugarss.js @@ -1,6 +1,6 @@ const assert = require('assert'); const {bundle, assertBundleTree} = require('./utils'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const path = require('path'); describe('sugarss', function() { diff --git a/packages/core/parcel-bundler/test/typescript.js b/packages/core/parcel-bundler/test/typescript.js index 2d07dd607fb..7aaada0a37b 100644 --- a/packages/core/parcel-bundler/test/typescript.js +++ b/packages/core/parcel-bundler/test/typescript.js @@ -1,6 +1,6 @@ const assert = require('assert'); const path = require('path'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const {bundle, run, assertBundleTree} = require('./utils'); describe('typescript', function() { diff --git a/packages/core/parcel-bundler/test/utils.js b/packages/core/parcel-bundler/test/utils.js index c7681c4ec5a..f844538007c 100644 --- a/packages/core/parcel-bundler/test/utils.js +++ b/packages/core/parcel-bundler/test/utils.js @@ -1,13 +1,14 @@ const Bundler = require('../src/Bundler'); const assert = require('assert'); const vm = require('vm'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const nodeFS = require('fs'); const path = require('path'); const WebSocket = require('ws'); const Module = require('module'); -const promisify = require('../src/utils/promisify'); +const {promisify} = require('@parcel/utils'); +const {sleep} = require('@parcel/test-utils'); const rimraf = promisify(require('rimraf')); const ncp = promisify(require('ncp')); @@ -38,10 +39,6 @@ beforeEach(async function() { await removeDistDirectory(); }); -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - function bundler(file, opts) { return new Bundler( file, @@ -275,7 +272,6 @@ function normaliseNewlines(text) { return text.replace(/(\r\n|\n|\r)/g, '\n'); } -exports.sleep = sleep; exports.bundler = bundler; exports.bundle = bundle; exports.run = run; diff --git a/packages/core/parcel-bundler/test/vue.js b/packages/core/parcel-bundler/test/vue.js index 8e19930f8e5..c53dbb5220f 100644 --- a/packages/core/parcel-bundler/test/vue.js +++ b/packages/core/parcel-bundler/test/vue.js @@ -1,7 +1,7 @@ const assert = require('assert'); const path = require('path'); const {bundle, assertBundleTree, run} = require('./utils'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); describe('vue', function() { it('should produce a basic vue bundle', async function() { diff --git a/packages/core/parcel-bundler/test/watcher.js b/packages/core/parcel-bundler/test/watcher.js index 87607512784..58bee8fb362 100644 --- a/packages/core/parcel-bundler/test/watcher.js +++ b/packages/core/parcel-bundler/test/watcher.js @@ -1,16 +1,16 @@ const assert = require('assert'); -const fs = require('../src/utils/fs'); +const fs = require('@parcel/fs'); const nodeFS = require('fs'); const path = require('path'); const { bundler, run, assertBundleTree, - sleep, nextBundle, rimraf, ncp } = require('./utils'); +const {sleep} = require('@parcel/test-utils'); const {symlinkSync} = require('fs'); const inputDir = path.join(__dirname, '/input'); diff --git a/packages/core/test-utils/.gitignore b/packages/core/test-utils/.gitignore new file mode 100644 index 00000000000..a65b41774ad --- /dev/null +++ b/packages/core/test-utils/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/core/test-utils/index.js b/packages/core/test-utils/index.js new file mode 100644 index 00000000000..e879acea0a2 --- /dev/null +++ b/packages/core/test-utils/index.js @@ -0,0 +1,5 @@ +// Node 8 supports native async functions - no need to use compiled code! +module.exports = + parseInt(process.versions.node, 10) < 8 + ? require('./lib/utils') + : require('./src/utils'); diff --git a/packages/core/test-utils/package.json b/packages/core/test-utils/package.json new file mode 100644 index 00000000000..023b4b997c0 --- /dev/null +++ b/packages/core/test-utils/package.json @@ -0,0 +1,22 @@ +{ + "name": "@parcel/test-utils", + "version": "1.10.3", + "description": "Blazing fast, zero configuration web application bundler", + "main": "index.js", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/parcel-bundler/parcel.git" + }, + "engines": { + "node": ">= 6.0.0" + }, + "scripts": { + "test": "echo this package has no tests yet", + "test-ci": "yarn build && yarn test", + "format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"", + "lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different", + "build": "babel src -d lib", + "prepublish": "yarn build" + } +} diff --git a/packages/core/test-utils/src/.babelrc b/packages/core/test-utils/src/.babelrc new file mode 100644 index 00000000000..1968e060259 --- /dev/null +++ b/packages/core/test-utils/src/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [["@babel/preset-env", { + "targets": { + "node": "6" + } + }]], + "plugins": ["@babel/plugin-transform-runtime"] +} diff --git a/packages/core/test-utils/src/.eslintrc.json b/packages/core/test-utils/src/.eslintrc.json new file mode 100644 index 00000000000..387cf114abe --- /dev/null +++ b/packages/core/test-utils/src/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../../.eslintrc.json" +} diff --git a/packages/core/test-utils/src/utils.js b/packages/core/test-utils/src/utils.js new file mode 100644 index 00000000000..4cdf552655a --- /dev/null +++ b/packages/core/test-utils/src/utils.js @@ -0,0 +1,5 @@ +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +exports.sleep = sleep; diff --git a/packages/core/utils/.gitignore b/packages/core/utils/.gitignore new file mode 100644 index 00000000000..a65b41774ad --- /dev/null +++ b/packages/core/utils/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/core/utils/index.js b/packages/core/utils/index.js new file mode 100644 index 00000000000..86e3b5c897a --- /dev/null +++ b/packages/core/utils/index.js @@ -0,0 +1,10 @@ +// Node 8 supports native async functions - no need to use compiled code! +exports.promisify = + parseInt(process.versions.node, 10) < 8 + ? require('./lib/promisify') + : require('./src/promisify'); + +exports.errorUtils = + parseInt(process.versions.node, 10) < 8 + ? require('./lib/errorUtils') + : require('./src/errorUtils'); diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json new file mode 100644 index 00000000000..988a8f50732 --- /dev/null +++ b/packages/core/utils/package.json @@ -0,0 +1,22 @@ +{ + "name": "@parcel/utils", + "version": "1.10.3", + "description": "Blazing fast, zero configuration web application bundler", + "main": "index.js", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/parcel-bundler/parcel.git" + }, + "engines": { + "node": ">= 6.0.0" + }, + "scripts": { + "test": "echo this package has no tests yet", + "test-ci": "yarn build && yarn test", + "format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"", + "lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different", + "build": "babel src -d lib", + "prepublish": "yarn build" + } +} diff --git a/packages/core/utils/src/.babelrc b/packages/core/utils/src/.babelrc new file mode 100644 index 00000000000..1968e060259 --- /dev/null +++ b/packages/core/utils/src/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [["@babel/preset-env", { + "targets": { + "node": "6" + } + }]], + "plugins": ["@babel/plugin-transform-runtime"] +} diff --git a/packages/core/utils/src/.eslintrc.json b/packages/core/utils/src/.eslintrc.json new file mode 100644 index 00000000000..387cf114abe --- /dev/null +++ b/packages/core/utils/src/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../../.eslintrc.json" +} diff --git a/packages/core/workers/src/errorUtils.js b/packages/core/utils/src/errorUtils.js similarity index 100% rename from packages/core/workers/src/errorUtils.js rename to packages/core/utils/src/errorUtils.js diff --git a/packages/core/parcel-bundler/src/utils/promisify.js b/packages/core/utils/src/promisify.js similarity index 100% rename from packages/core/parcel-bundler/src/utils/promisify.js rename to packages/core/utils/src/promisify.js diff --git a/packages/core/watcher/package.json b/packages/core/watcher/package.json index 1646fc08799..19656404b63 100644 --- a/packages/core/watcher/package.json +++ b/packages/core/watcher/package.json @@ -9,10 +9,21 @@ "url": "https://github.com/parcel-bundler/parcel.git" }, "scripts": { + "test": "cross-env NODE_ENV=test mocha", + "test-ci": "yarn build && yarn test", + "format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"", + "lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different", "build": "babel src -d lib", "prepublish": "yarn build" }, "dependencies": { "fswatcher-child": "^1.0.5" + }, + "devDependencies": { + "mocha": "^5.2.0", + "@parcel/babel-register": "^1.10.3", + "@parcel/fs": "^1.10.3", + "@parcel/utils": "^1.10.3", + "@parcel/test-utils": "^1.10.3" } } diff --git a/packages/core/watcher/src/Watcher.js b/packages/core/watcher/src/Watcher.js index a7b7fc67ce9..dccd5cf3292 100644 --- a/packages/core/watcher/src/Watcher.js +++ b/packages/core/watcher/src/Watcher.js @@ -1,5 +1,8 @@ -const FSWatcher = require('fswatcher-child'); +const fork = require('child_process').fork; +const optionsTransfer = require('./options'); const Path = require('path'); +const {EventEmitter} = require('events'); +const {errorUtils} = require('@parcel/utils'); /** * This watcher wraps chokidar so that we watch directories rather than individual files on macOS. @@ -7,21 +10,152 @@ const Path = require('path'); * Chokidar does not have support for watching directories on non-macOS platforms, so we disable * this behavior in order to prevent watching more individual files than necessary (e.g. node_modules). */ -class Watcher { - constructor() { - // FS events on macOS are flakey in the tests, which write lots of files very quickly - // See https://github.com/paulmillr/chokidar/issues/612 - this.shouldWatchDirs = - process.platform === 'darwin' && process.env.NODE_ENV !== 'test'; - this.watcher = new FSWatcher({ - useFsEvents: this.shouldWatchDirs, +class Watcher extends EventEmitter { + constructor( + options = { + // FS events on macOS are flakey in the tests, which write lots of files very quickly + // See https://github.com/paulmillr/chokidar/issues/612 + useFsEvents: + process.platform === 'darwin' && process.env.NODE_ENV !== 'test', ignoreInitial: true, ignorePermissionErrors: true, ignored: /\.cache|\.git/ - }); - + } + ) { + super(); + this.options = optionsTransfer.encode(options); + this.watchedPaths = new Set(); + this.child = null; + this.ready = false; + this.readyQueue = []; this.watchedDirectories = new Map(); this.stopped = false; + + this.on('ready', () => { + this.ready = true; + for (let func of this.readyQueue) { + func(); + } + this.readyQueue = []; + }); + + this.startchild(); + } + + startchild() { + if (this.child) return; + + let filteredArgs = process.execArgv.filter( + v => !/^--(debug|inspect)/.test(v) + ); + + let options = { + execArgv: filteredArgs, + env: process.env, + cwd: process.cwd() + }; + + this.child = fork(Path.join(__dirname, 'child'), process.argv, options); + + if (this.watchedPaths.size > 0) { + this.sendCommand('add', [Array.from(this.watchedPaths)]); + } + + this.child.send({ + type: 'init', + options: this.options + }); + + this.child.on('message', msg => this.handleEmit(msg.event, msg.path)); + this.child.on('error', () => {}); + this.child.on('exit', () => this.handleClosed()); + // this.child.on('close', () => this.handleClosed()); + } + + handleClosed() { + if (!this.stopped) { + // Restart the child + this.child = null; + this.ready = false; + this.startchild(); + } + + this.emit('childDead'); + } + + handleEmit(event, data) { + if (event === 'watcherError') { + data = errorUtils.jsonToError(data); + } + + this.emit(event, data); + } + + sendCommand(func, args) { + if (!this.ready) { + return this.readyQueue.push(() => this.sendCommand(func, args)); + } + + this.child.send({ + type: 'function', + name: func, + args: args + }); + } + + _addPath(path) { + if (!this.watchedPaths.has(path)) { + this.watchedPaths.add(path); + return true; + } + } + + add(paths) { + let added = false; + if (Array.isArray(paths)) { + for (let path of paths) { + added = !added ? this._addPath(path) : true; + } + } else { + added = this._addPath(paths); + } + if (added) this.sendCommand('add', [paths]); + } + + _closePath(path) { + if (this.watchedPaths.has(path)) { + this.watchedPaths.delete(path); + } + this.sendCommand('_closePath', [path]); + } + + _emulateChildDead() { + if (!this.child) { + return; + } + + this.child.send({ + type: 'die' + }); + } + + _emulateChildError() { + if (!this.child) { + return; + } + + this.child.send({ + type: 'emulate_error' + }); + } + + getWatched() { + let watchList = {}; + for (let path of this.watchedPaths) { + let key = this.options.cwd ? Path.relative(this.options.cwd, path) : path; + watchList[key || '.'] = []; + } + return watchList; } /** @@ -72,12 +206,12 @@ class Watcher { for (let dir of children) { count += this.watchedDirectories.get(dir); - this.watcher._closePath(dir); + this._closePath(dir); this.watchedDirectories.delete(dir); } let dir = Path.dirname(path); - this.watcher.add(dir); + this.add(dir); this.watchedDirectories.set(dir, count); } else { // Otherwise, increment the reference count of the parent watcher. @@ -87,10 +221,22 @@ class Watcher { ); } } else { - this.watcher.add(path); + this.add(path); } } + _unwatch(paths) { + let removed = false; + if (Array.isArray(paths)) { + for (let p of paths) { + removed = !removed ? this.watchedPaths.delete(p) : true; + } + } else { + removed = this.watchedPaths.delete(paths); + } + if (removed) this.sendCommand('unwatch', [paths]); + } + /** * Remove a path from the watcher */ @@ -102,36 +248,27 @@ class Watcher { let count = this.watchedDirectories.get(dir) - 1; if (count === 0) { this.watchedDirectories.delete(dir); - this.watcher.unwatch(dir); + this._unwatch(dir); } else { this.watchedDirectories.set(dir, count); } } } else { - this.watcher.unwatch(path); + this._unwatch(path); } } - /** - * Add an event handler - */ - on(event, callback) { - this.watcher.on(event, callback); - } - - /** - * Add an event handler - */ - once(event, callback) { - this.watcher.once(event, callback); - } - /** * Stop watching all paths */ async stop() { this.stopped = true; - await this.watcher.close(); + + if (this.child) { + this.child.kill(); + + return new Promise(resolve => this.once('childDead', resolve)); + } } } diff --git a/packages/core/watcher/src/child.js b/packages/core/watcher/src/child.js new file mode 100644 index 00000000000..abf3bcd7dfe --- /dev/null +++ b/packages/core/watcher/src/child.js @@ -0,0 +1,52 @@ +const {FSWatcher} = require('chokidar'); +const {errorUtils} = require('@parcel/utils'); +const optionsTransfer = require('./options'); + +let watcher; +function sendEvent(event, path) { + process.send({ + event: event, + path: path + }); +} + +function handleError(e) { + sendEvent('watcherError', errorUtils.errorToJson(e)); +} + +function init(options) { + options = optionsTransfer.decode(options); + watcher = new FSWatcher(options); + watcher.on('all', sendEvent); + sendEvent('ready'); +} + +function executeFunction(functionName, args) { + try { + watcher[functionName](...args); + } catch (e) { + handleError(e); + } +} + +process.on('message', msg => { + switch (msg.type) { + case 'init': + init(msg.options); + break; + case 'function': + executeFunction(msg.name, msg.args); + break; + case 'die': + process.exit(); + break; + case 'emulate_error': + throw new Error('this is an emulated error'); + } +}); + +process.on('error', handleError); +process.on('uncaughtException', handleError); +process.on('disconnect', () => { + process.exit(); +}); diff --git a/packages/core/watcher/src/options.js b/packages/core/watcher/src/options.js new file mode 100644 index 00000000000..ba3637511f5 --- /dev/null +++ b/packages/core/watcher/src/options.js @@ -0,0 +1,40 @@ +function type(options) { + return Object.prototype.toString.call(options).slice(8, -1); +} + +function encode(options) { + if (options && options.ignored) { + const ignoredType = type(options.ignored); + if (ignoredType !== 'Array') { + options.ignored = [options.ignored]; + } + + options.ignored.forEach((value, index) => { + const valueType = type(value); + if (valueType === 'RegExp') { + options.ignored[index] = value.source; + if (!options._regIndexs) { + options._regIndexs = []; + } + options._regIndexs.push(index); + } + }); + } + + return options; +} + +function decode(options) { + if (options && options.ignored && options._regIndexs) { + for (let index of options._regIndexs) { + options.ignored[index] = new RegExp(options.ignored[index]); + } + } + + delete options._regIndexs; + + return options; +} + +exports.encode = encode; +exports.decode = decode; diff --git a/packages/core/watcher/test/.babelrc b/packages/core/watcher/test/.babelrc new file mode 100644 index 00000000000..eb1af60481b --- /dev/null +++ b/packages/core/watcher/test/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [["@babel/preset-env", { + "targets": { + "node": "current" + } + }]], + "plugins": ["@babel/plugin-transform-runtime"] +} diff --git a/packages/core/watcher/test/.eslintrc.json b/packages/core/watcher/test/.eslintrc.json new file mode 100644 index 00000000000..87769487177 --- /dev/null +++ b/packages/core/watcher/test/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../../.eslintrc.json", + "env": { + "mocha": true + } +} diff --git a/packages/core/watcher/test/changeEvent.js b/packages/core/watcher/test/changeEvent.js new file mode 100644 index 00000000000..2b7739344d1 --- /dev/null +++ b/packages/core/watcher/test/changeEvent.js @@ -0,0 +1,104 @@ +const Watcher = require('../index'); +const fs = require('@parcel/fs'); +const path = require('path'); +const assert = require('assert'); +const {sleep} = require('@parcel/test-utils'); + +describe('change event', function() { + let tmpFolder = path.join(__dirname, './tmp/'); + + before(() => { + fs.mkdirp(tmpFolder); + }); + + it('Should emit event on filechange', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(changed, 'File should be flagged as changed.'); + + await watcher.stop(); + }); + + it('Should emit event on filechange using arrays', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add([filepath]); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(changed, 'File should be flagged as changed.'); + + await watcher.stop(); + }); + + it('Should not emit event if file has been added and removed', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + + await fs.writeFile(filepath, 'this is a text document'); + + await sleep(250); + + watcher.add(filepath); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + watcher.unwatch(filepath); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(!changed, 'Should not have emitted a change event.'); + + await watcher.stop(); + }); +}); diff --git a/packages/core/watcher/test/errorHandling.js b/packages/core/watcher/test/errorHandling.js new file mode 100644 index 00000000000..6dd59f05ca3 --- /dev/null +++ b/packages/core/watcher/test/errorHandling.js @@ -0,0 +1,84 @@ +const Watcher = require('../index'); +const fs = require('@parcel/fs'); +const path = require('path'); +const assert = require('assert'); +const {sleep} = require('@parcel/test-utils'); + +describe('error handling', function() { + let tmpFolder = path.join(__dirname, './tmp/'); + + before(() => { + fs.mkdirp(tmpFolder); + }); + + it('Should restart child process if it dies', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + watcher._emulateChildDead(); + + await sleep(1000); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(changed, 'Should have emitted a change event.'); + + await watcher.stop(); + }); + + it('Should restart child process on errors', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + + let hasThrown = false; + watcher.on('watcherError', () => (hasThrown = true)); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + watcher._emulateChildError(); + + await sleep(1000); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(changed, 'Should have emitted a change event.'); + + await watcher.stop(); + + assert(hasThrown, 'Should have emitted an error event.'); + }); +}); diff --git a/packages/core/watcher/test/fswatcher.js b/packages/core/watcher/test/fswatcher.js new file mode 100644 index 00000000000..013e19ba04e --- /dev/null +++ b/packages/core/watcher/test/fswatcher.js @@ -0,0 +1,32 @@ +const Watcher = require('../index'); +const {sleep} = require('@parcel/test-utils'); +const assert = require('assert'); + +describe('Watcher', function() { + it('Should be able to create a new watcher', async () => { + let watcher = new Watcher(); + + assert(!!watcher.child); + assert(!watcher.ready); + + await sleep(1000); + + assert(!!watcher.child); + assert(watcher.ready); + + await watcher.stop(); + }); + + it('Should be able to properly destroy the watcher', async () => { + let watcher = new Watcher(); + + await sleep(1000); + + assert(!!watcher.child); + assert(watcher.ready); + + let time = Date.now(); + await watcher.stop(); + assert.notEqual(time, Date.now()); + }); +}); diff --git a/packages/core/watcher/test/mocha.opts b/packages/core/watcher/test/mocha.opts new file mode 100644 index 00000000000..0d00e6a708a --- /dev/null +++ b/packages/core/watcher/test/mocha.opts @@ -0,0 +1,3 @@ +--require @parcel/babel-register +--timeout 10000 +--exit \ No newline at end of file diff --git a/packages/core/watcher/test/options.js b/packages/core/watcher/test/options.js new file mode 100644 index 00000000000..6f23d797724 --- /dev/null +++ b/packages/core/watcher/test/options.js @@ -0,0 +1,124 @@ +const Watcher = require('../index'); +const fs = require('@parcel/fs'); +const path = require('path'); +const assert = require('assert'); +const {sleep} = require('@parcel/test-utils'); + +describe('options', function() { + let tmpFolder = path.join(__dirname, './tmp/'); + + before(() => { + fs.mkdirp(tmpFolder); + }); + + it('Should pass init options with correct ignored regex', async () => { + let watcher = new Watcher({ + ignored: /file/ + }); + + let filepath = path.join(tmpFolder, 'file1.txt'); + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + await fs.writeFile(filepath, 'this is not a text document'); + + await sleep(500); + + assert(!changed, 'File should not be flagged as changed.'); + + await watcher.stop(); + }); + + it('Should pass init options with a more complex ignored regex', async () => { + let watcher = new Watcher({ + ignored: /file|config/ + }); + + let filepaths = [ + path.join(tmpFolder, 'file1.txt'), + path.join(tmpFolder, 'config.json') + ]; + + for (let filepath of filepaths) { + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + } + + let changed = false; + watcher.once('change', () => { + changed = true; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + for (let filepath of filepaths) { + await fs.writeFile(filepath, 'this is not a text document'); + + watcher.add(filepath); + } + + await sleep(500); + + assert(!changed, 'File should not be flagged as changed.'); + + await watcher.stop(); + }); + + it('Should not ignore any files outside of the regex', async () => { + let watcher = new Watcher({ + ignored: /file|config/ + }); + + let filepaths = [ + path.join(tmpFolder, 'file1.txt'), + path.join(tmpFolder, 'config.json'), + path.join(tmpFolder, 'something') + ]; + + for (let filepath of filepaths) { + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + } + + let changed = 0; + watcher.once('change', () => { + changed++; + }); + + if (!watcher.ready) { + await new Promise(resolve => watcher.once('ready', resolve)); + } + + await sleep(250); + + for (let filepath of filepaths) { + await fs.writeFile(filepath, 'this is not a text document'); + + watcher.add(filepath); + } + + await sleep(500); + + assert.equal(changed, 1, 'One file should have changed once.'); + + await watcher.stop(); + }); +}); diff --git a/packages/core/watcher/test/watched.js b/packages/core/watcher/test/watched.js new file mode 100644 index 00000000000..2ed3e6aac8e --- /dev/null +++ b/packages/core/watcher/test/watched.js @@ -0,0 +1,28 @@ +const Watcher = require('../index'); +const fs = require('@parcel/fs'); +const path = require('path'); +const assert = require('assert'); + +describe('watched paths', function() { + let tmpFolder = path.join(__dirname, './tmp/'); + + before(() => { + fs.mkdirp(tmpFolder); + }); + + it('Should return watched paths', async () => { + let watcher = new Watcher({}); + + let filepath = path.join(tmpFolder, 'file1.txt'); + await fs.writeFile(filepath, 'this is a text document'); + + watcher.add(filepath); + + assert( + Object.keys(watcher.getWatched())[0] === filepath, + 'getWatched should return all the watched paths.' + ); + + await watcher.stop(); + }); +}); diff --git a/packages/core/workers/package.json b/packages/core/workers/package.json index 5715db344b7..2f2e9827fd7 100644 --- a/packages/core/workers/package.json +++ b/packages/core/workers/package.json @@ -23,6 +23,7 @@ "mocha": "^5.2.0" }, "dependencies": { - "physical-cpu-count": "^2.0.0" + "physical-cpu-count": "^2.0.0", + "@parcel/utils": "^1.10.3" } } diff --git a/packages/core/workers/src/Worker.js b/packages/core/workers/src/Worker.js index 9dd266a82c5..851d3f415dc 100644 --- a/packages/core/workers/src/Worker.js +++ b/packages/core/workers/src/Worker.js @@ -1,6 +1,6 @@ const childProcess = require('child_process'); const {EventEmitter} = require('events'); -const errorUtils = require('./errorUtils'); +const {errorUtils} = require('@parcel/utils'); const childModule = require.resolve('./child'); diff --git a/packages/core/workers/src/WorkerFarm.js b/packages/core/workers/src/WorkerFarm.js index b7af519d350..5cfcff22cab 100644 --- a/packages/core/workers/src/WorkerFarm.js +++ b/packages/core/workers/src/WorkerFarm.js @@ -1,5 +1,5 @@ const {EventEmitter} = require('events'); -const errorUtils = require('./errorUtils'); +const {errorUtils} = require('@parcel/utils'); const Worker = require('./Worker'); const cpuCount = require('./cpuCount'); diff --git a/packages/core/workers/src/child.js b/packages/core/workers/src/child.js index 9483779ac95..a6b5a4e7bc0 100644 --- a/packages/core/workers/src/child.js +++ b/packages/core/workers/src/child.js @@ -1,4 +1,4 @@ -const errorUtils = require('./errorUtils'); +const {errorUtils} = require('@parcel/utils'); class Child { constructor() {