From 99589f1058c028098c525ec3263d72daa21e1931 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 21 Sep 2017 16:09:04 -0400 Subject: [PATCH 1/7] Add the `ignore` package to the dev bundle. https://npmjs.org/package/ignore --- scripts/dev-bundle-tool-package.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index 022f9d1f67..9b63682bc5 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -44,6 +44,7 @@ var packageJson = { "moment": "2.8.4", "rimraf": "2.6.1", "glob": "7.0.6", + ignore: "3.3.5", // XXX: When we update this, see if it fixes this Github issue: // https://github.com/jgm/CommonMark/issues/276 . If it does, remove the // workaround from the tool. From 3e6ca236e0f4bb30cfcef84bd593b44884483a15 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 21 Sep 2017 20:13:36 -0400 Subject: [PATCH 2/7] Bump $BUNDLE_VERSION to 4.8.36 before rebuilding dev bundle. --- meteor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meteor b/meteor index 8661fb2933..af4236e800 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=4.8.35 +BUNDLE_VERSION=4.8.36 # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. From f4f4fd99941fbcbb576a56a887309e35fc75ebe0 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 21 Sep 2017 18:04:41 -0400 Subject: [PATCH 3/7] Optimistically depend on parent directory when file does not exist. With any luck, this will solve a host of problems related to undetected changes when new files are created. --- tools/fs/optimistic.js | 62 ++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/tools/fs/optimistic.js b/tools/fs/optimistic.js index f54b61f58f..386828dc2b 100644 --- a/tools/fs/optimistic.js +++ b/tools/fs/optimistic.js @@ -5,6 +5,7 @@ import { watch } from "./safe-watcher.js"; import { sha1 } from "./watch.js"; import { pathSep, + pathDirname, pathIsAbsolute, statOrNull, lstat, @@ -142,27 +143,17 @@ function maybeDependOnNodeModules(path) { } } -let npmDepCount = 0; - -// Called by any optimistic function that receives a */node_modules/* path -// as its first argument, so that we can later bulk-invalidate the results -// of those calls if the contents of the node_modules directory change. -// Note that this strategy will not detect changes within subdirectories -// of this node_modules directory, but that's ok because the use case we -// care about is adding or removing npm packages. -const dependOnNodeModules = wrap(nodeModulesDir => { - assert(pathIsAbsolute(nodeModulesDir)); - assert(nodeModulesDir.endsWith(pathSep + "node_modules")); +let dependOnDirectorySalt = 0; +const dependOnDirectory = wrap(dir => { // Always return something different to prevent optimism from // second-guessing the dirtiness of this function. - return ++npmDepCount; - + return ++dependOnDirectorySalt; }, { - subscribe(nodeModulesDir) { + subscribe(dir) { let watcher = watch( - nodeModulesDir, - () => dependOnNodeModules.dirty(nodeModulesDir), + dir, + () => dependOnDirectory.dirty(dir), ); return function () { @@ -174,19 +165,52 @@ const dependOnNodeModules = wrap(nodeModulesDir => { } }); +// Called when an optimistic function detects the given file does not +// exist, but needs to return null or false rather than throwing an +// exception. When/if the file is eventually created, we might only get a +// file change notification for the parent directory, so it's important to +// depend on the parent directory using this function, so that we don't +// cache the unsuccessful result forever. +function dependOnParentDirectory(path) { + const parentDir = pathDirname(path); + if (parentDir !== path) { + dependOnDirectory(parentDir); + } +} + +// Called by any optimistic function that receives a */node_modules/* path +// as its first argument, so that we can later bulk-invalidate the results +// of those calls if the contents of the node_modules directory change. +// Note that this strategy will not detect changes within subdirectories +// of this node_modules directory, but that's ok because the use case we +// care about is adding or removing npm packages. +const dependOnNodeModules = wrap(nodeModulesDir => { + assert(pathIsAbsolute(nodeModulesDir)); + assert(nodeModulesDir.endsWith(pathSep + "node_modules")); + return dependOnDirectory(nodeModulesDir); +}); + // Invalidate all optimistic results derived from paths involving the // given node_modules directory. export function dirtyNodeModulesDirectory(nodeModulesDir) { dependOnNodeModules.dirty(nodeModulesDir); } -export const optimisticStatOrNull = makeOptimistic("statOrNull", statOrNull); +export const optimisticStatOrNull = makeOptimistic("statOrNull", path => { + const result = statOrNull(path); + if (result === null) { + dependOnParentDirectory(path); + } + return result; +}); + export const optimisticLStat = makeOptimistic("lstat", lstat); export const optimisticLStatOrNull = makeOptimistic("lstatOrNull", path => { try { return optimisticLStat(path); } catch (e) { if (e.code !== "ENOENT") throw e; + dependOnParentDirectory(path); return null; } }); @@ -203,6 +227,8 @@ export const optimisticHashOrNull = makeOptimistic("hashOrNull", (...args) => { } } + dependOnParentDirectory(args[0]); + return null; }); @@ -213,6 +239,7 @@ makeOptimistic("readJsonOrNull", (path, options) => { } catch (e) { if (e.code === "ENOENT") { + dependOnParentDirectory(path); return null; } @@ -230,6 +257,7 @@ const optimisticIsSymbolicLink = wrap(path => { return lstat(path).isSymbolicLink(); } catch (e) { if (e.code !== "ENOENT") throw e; + dependOnParentDirectory(path); return false; } }, { From 7fd4fd559bbe31bdd97794815f70bc9e75798d99 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 21 Sep 2017 16:06:55 -0400 Subject: [PATCH 4/7] Support .meteorignore files. Closes https://github.com/meteor/meteor-feature-requests/issues/5. --- tools/fs/optimistic.js | 15 + tools/isobuild/build-plugin.js | 14 +- tools/isobuild/compiler.js | 24 +- tools/isobuild/package-source.js | 52 +- tools/tests/apps/meteor-ignore/.gitignore | 1 + .../meteor-ignore/.meteor/.finished-upgraders | 17 + .../apps/meteor-ignore/.meteor/.gitignore | 1 + tools/tests/apps/meteor-ignore/.meteor/.id | 7 + .../tests/apps/meteor-ignore/.meteor/packages | 17 + .../apps/meteor-ignore/.meteor/platforms | 2 + .../tests/apps/meteor-ignore/.meteor/release | 1 + .../tests/apps/meteor-ignore/.meteor/versions | 64 ++ tools/tests/apps/meteor-ignore/a.js | 1 + tools/tests/apps/meteor-ignore/b.js | 1 + .../apps/meteor-ignore/imports/registry.js | 11 + tools/tests/apps/meteor-ignore/lib/e.js | 1 + tools/tests/apps/meteor-ignore/lib/f.js | 1 + tools/tests/apps/meteor-ignore/main.js | 1 + .../apps/meteor-ignore/package-lock.json | 603 ++++++++++++++++++ tools/tests/apps/meteor-ignore/package.json | 11 + tools/tests/apps/meteor-ignore/server/c.js | 2 + tools/tests/apps/meteor-ignore/server/d.js | 1 + tools/tests/meteor-ignore.js | 75 +++ 23 files changed, 917 insertions(+), 6 deletions(-) create mode 100644 tools/tests/apps/meteor-ignore/.gitignore create mode 100644 tools/tests/apps/meteor-ignore/.meteor/.finished-upgraders create mode 100644 tools/tests/apps/meteor-ignore/.meteor/.gitignore create mode 100644 tools/tests/apps/meteor-ignore/.meteor/.id create mode 100644 tools/tests/apps/meteor-ignore/.meteor/packages create mode 100644 tools/tests/apps/meteor-ignore/.meteor/platforms create mode 100644 tools/tests/apps/meteor-ignore/.meteor/release create mode 100644 tools/tests/apps/meteor-ignore/.meteor/versions create mode 100644 tools/tests/apps/meteor-ignore/a.js create mode 100644 tools/tests/apps/meteor-ignore/b.js create mode 100644 tools/tests/apps/meteor-ignore/imports/registry.js create mode 100644 tools/tests/apps/meteor-ignore/lib/e.js create mode 100644 tools/tests/apps/meteor-ignore/lib/f.js create mode 100644 tools/tests/apps/meteor-ignore/main.js create mode 100644 tools/tests/apps/meteor-ignore/package-lock.json create mode 100644 tools/tests/apps/meteor-ignore/package.json create mode 100644 tools/tests/apps/meteor-ignore/server/c.js create mode 100644 tools/tests/apps/meteor-ignore/server/d.js create mode 100644 tools/tests/meteor-ignore.js diff --git a/tools/fs/optimistic.js b/tools/fs/optimistic.js index 386828dc2b..eb596a8fbf 100644 --- a/tools/fs/optimistic.js +++ b/tools/fs/optimistic.js @@ -7,6 +7,7 @@ import { pathSep, pathDirname, pathIsAbsolute, + pathJoin, statOrNull, lstat, readFile, @@ -252,6 +253,20 @@ makeOptimistic("readJsonOrNull", (path, options) => { } }); +export const optimisticReadMeteorIgnore = wrap(dir => { + const meteorIgnorePath = pathJoin(dir, ".meteorignore"); + const meteorIgnoreStat = optimisticStatOrNull(meteorIgnorePath); + + if (meteorIgnoreStat && + meteorIgnoreStat.isFile()) { + return require("ignore")().add( + optimisticReadFile(meteorIgnorePath, "utf8") + ); + } + + return null; +}); + const optimisticIsSymbolicLink = wrap(path => { try { return lstat(path).isSymbolicLink(); diff --git a/tools/isobuild/build-plugin.js b/tools/isobuild/build-plugin.js index ff305b3d55..cafd77c4f6 100644 --- a/tools/isobuild/build-plugin.js +++ b/tools/isobuild/build-plugin.js @@ -208,6 +208,10 @@ export class SourceProcessorSet { }); } + if (filename === ".meteorignore") { + return new SourceClassification("meteor-ignore"); + } + // Now check to see if a plugin registered for an extension. We prefer // longer extensions. const parts = filename.split('.'); @@ -303,8 +307,14 @@ class SourceClassification { legacyIsTemplate, arch, } = {}) { - const knownTypes = ['extension', 'filename', 'legacy-handler', 'wrong-arch', - 'unmatched']; + const knownTypes = [ + 'extension', + 'filename', + 'legacy-handler', + 'wrong-arch', + 'unmatched', + 'meteor-ignore', + ]; if (knownTypes.indexOf(type) === -1) { throw Error(`Unknown SourceClassification type ${ type }`); } diff --git a/tools/isobuild/compiler.js b/tools/isobuild/compiler.js index 08e693e102..ef51393802 100644 --- a/tools/isobuild/compiler.js +++ b/tools/isobuild/compiler.js @@ -562,6 +562,13 @@ api.addAssets('${relPath}', 'client').`); Console.nudge(true); + if (classification.type === "meteor-ignore") { + // Return after watching .meteorignore files but before adding them + // as resources to be processed by compiler plugins. To see how + // these files are handled, see PackageSource#_findSources. + return; + } + if (contents === null) { // It really sucks to put this check here, since this isn't publish // code... @@ -765,9 +772,12 @@ function runLinters({inputSourceArch, isopackCache, sources, classification.type === 'unmatched') { return; } - // We shouldn't ever add a legacy handler and we're not hardcoding JS for - // linters, so we should always have SourceProcessor if anything matches. - if (! classification.sourceProcessors) { + + // We shouldn't ever add a legacy handler and we're not hardcoding JS + // for linters, so we should always have SourceProcessor if anything + // matches, unless this is a .meteorignore file. + if (classification.type !== "meteor-ignore" && + ! classification.sourceProcessors) { throw Error( `Unexpected classification for ${ relPath }: ${ classification.type }`); } @@ -776,6 +786,14 @@ function runLinters({inputSourceArch, isopackCache, sources, const {hash, contents} = watch.readAndWatchFileWithHash( watchSet, files.pathResolve(inputSourceArch.sourceRoot, relPath)); + + if (classification.type === "meteor-ignore") { + // Return after watching .meteorignore files but before adding them + // as resources to be processed by compiler plugins. To see how + // these files are handled, see PackageSource#_findSources. + return; + } + const wrappedSource = { relPath, contents, hash, fileOptions, arch: inputSourceArch.arch, diff --git a/tools/isobuild/package-source.js b/tools/isobuild/package-source.js index cbe085d4a5..14d2a2b0d2 100644 --- a/tools/isobuild/package-source.js +++ b/tools/isobuild/package-source.js @@ -32,6 +32,7 @@ import { optimisticReadFile, optimisticHashOrNull, optimisticStatOrNull, + optimisticReadMeteorIgnore, } from "../fs/optimistic.js"; // XXX: This is a medium-term hack, to avoid having the user set a package name @@ -67,6 +68,7 @@ var loadOrderSort = function (sourceProcessorSet, arch) { case 'wrong-arch': case 'unmatched': + case 'meteor-ignore': return false; default: @@ -994,9 +996,14 @@ _.extend(PackageSource.prototype, { const sourceReadOptions = sourceProcessorSet.appReadDirectoryOptions(arch); + // Adding, removing, or modifying a .meteorignore file should trigger + // a rebuild with the new rules applied. + sourceReadOptions.names.push(".meteorignore"); + // Ignore files starting with dot (unless they are explicitly in - // 'names'). + // sourceReadOptions.names, e.g. .meteorignore, added above). sourceReadOptions.exclude.push(/^\./); + // Ignore the usual ignorable files. sourceReadOptions.exclude.push(...ignoreFiles); @@ -1059,6 +1066,8 @@ _.extend(PackageSource.prototype, { return baseCacheKey + "\0" + dir; } + const dotMeteorIgnoreFiles = Object.create(null); + function find(dir, depth, inNodeModules) { // Remove trailing slash. dir = dir.replace(/\/$/, ""); @@ -1076,6 +1085,14 @@ _.extend(PackageSource.prototype, { return []; } + const absDir = files.pathJoin(self.sourceRoot, dir); + if (! inNodeModules) { + const ignore = optimisticReadMeteorIgnore(absDir); + if (ignore) { + dotMeteorIgnoreFiles[dir] = ignore; + } + } + const readOptions = inNodeModules ? nodeModulesReadOptions : sourceReadOptions; @@ -1092,6 +1109,36 @@ _.extend(PackageSource.prototype, { : topLevelExcludes }); + Object.keys(dotMeteorIgnoreFiles).forEach(ignoreDir => { + const ignore = dotMeteorIgnoreFiles[ignoreDir]; + + function removeIgnoredFilesFrom(array) { + let target = 0; + + array.forEach(item => { + let relPath = files.pathRelative(ignoreDir, item); + + if (! relPath.startsWith("..")) { + if (item.endsWith("/")) { + // The trailing slash is discarded by files.pathRelative. + relPath += "/"; + } + + if (ignore.ignores(relPath)) { + return; + } + } + + array[target++] = item; + }); + + array.length = target; + } + + removeIgnoredFilesFrom(sources); + removeIgnoredFilesFrom(subdirectories); + }); + let nodeModulesDir; subdirectories.forEach(subdir => { @@ -1112,6 +1159,9 @@ _.extend(PackageSource.prototype, { } }); + // Don't apply any .meteorignore rules to files inside node_modules. + delete dotMeteorIgnoreFiles[dir]; + if (isApp && nodeModulesDir && (! inNodeModules || sources.length > 0)) { diff --git a/tools/tests/apps/meteor-ignore/.gitignore b/tools/tests/apps/meteor-ignore/.gitignore new file mode 100644 index 0000000000..40b878db5b --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/tools/tests/apps/meteor-ignore/.meteor/.finished-upgraders b/tools/tests/apps/meteor-ignore/.meteor/.finished-upgraders new file mode 100644 index 0000000000..910574ce2d --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/.finished-upgraders @@ -0,0 +1,17 @@ +# This file contains information which helps Meteor properly upgrade your +# app when you run 'meteor update'. You should check it into version control +# with your project. + +notices-for-0.9.0 +notices-for-0.9.1 +0.9.4-platform-file +notices-for-facebook-graph-api-2 +1.2.0-standard-minifiers-package +1.2.0-meteor-platform-split +1.2.0-cordova-changes +1.2.0-breaking-changes +1.3.0-split-minifiers-package +1.4.0-remove-old-dev-bundle-link +1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package diff --git a/tools/tests/apps/meteor-ignore/.meteor/.gitignore b/tools/tests/apps/meteor-ignore/.meteor/.gitignore new file mode 100644 index 0000000000..4083037423 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/tools/tests/apps/meteor-ignore/.meteor/.id b/tools/tests/apps/meteor-ignore/.meteor/.id new file mode 100644 index 0000000000..4b458953bb --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/.id @@ -0,0 +1,7 @@ +# This file contains a token that is unique to your project. +# Check it into your repository along with the rest of this directory. +# It can be used for purposes such as: +# - ensuring you don't accidentally deploy one app on top of another +# - providing package authors with aggregated statistics + +z1q9vs1ilju2.upauxu4iy59o diff --git a/tools/tests/apps/meteor-ignore/.meteor/packages b/tools/tests/apps/meteor-ignore/.meteor/packages new file mode 100644 index 0000000000..4e4f1624c6 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/packages @@ -0,0 +1,17 @@ +# Meteor packages used by this project, one per line. +# Check this file (and the other files in this directory) into your repository. +# +# 'meteor add' and 'meteor remove' will edit this file for you, +# but you can also edit it by hand. + +meteor-base # Packages every Meteor app needs to have +mobile-experience # Packages for a great mobile UX +static-html # Define static page content in .html files +reactive-var # Reactive variable for tracker +tracker # Meteor's client-side reactive programming library + +standard-minifier-css # CSS minifier run for production mode +standard-minifier-js # JS minifier run for production mode +es5-shim # ECMAScript 5 compatibility for older browsers +ecmascript # Enable ECMAScript2015+ syntax in app code +shell-server # Server-side component of the `meteor shell` command diff --git a/tools/tests/apps/meteor-ignore/.meteor/platforms b/tools/tests/apps/meteor-ignore/.meteor/platforms new file mode 100644 index 0000000000..efeba1b50c --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/platforms @@ -0,0 +1,2 @@ +server +browser diff --git a/tools/tests/apps/meteor-ignore/.meteor/release b/tools/tests/apps/meteor-ignore/.meteor/release new file mode 100644 index 0000000000..621e94f0ec --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/release @@ -0,0 +1 @@ +none diff --git a/tools/tests/apps/meteor-ignore/.meteor/versions b/tools/tests/apps/meteor-ignore/.meteor/versions new file mode 100644 index 0000000000..2a0c9c4a0e --- /dev/null +++ b/tools/tests/apps/meteor-ignore/.meteor/versions @@ -0,0 +1,64 @@ +allow-deny@1.0.9 +autoupdate@1.3.12 +babel-compiler@6.20.0 +babel-runtime@1.0.1 +base64@1.0.10 +binary-heap@1.0.10 +blaze-tools@1.0.10 +boilerplate-generator@1.2.0 +caching-compiler@1.1.9 +caching-html-compiler@1.1.2 +callback-hook@1.0.10 +check@1.2.5 +ddp@1.3.1 +ddp-client@2.1.3 +ddp-common@1.2.9 +ddp-server@2.0.2 +diff-sequence@1.0.7 +dynamic-import@0.1.3 +ecmascript@0.8.2 +ecmascript-runtime@0.4.1 +ecmascript-runtime-client@0.4.3 +ecmascript-runtime-server@0.4.1 +ejson@1.0.14 +es5-shim@4.6.15 +geojson-utils@1.0.10 +hot-code-push@1.0.4 +html-tools@1.0.11 +htmljs@1.0.11 +http@1.2.12 +id-map@1.0.9 +launch-screen@1.1.1 +livedata@1.0.18 +logging@1.1.17 +meteor@1.7.2 +meteor-base@1.1.0 +minifier-css@1.2.16 +minifier-js@2.1.3 +minimongo@1.3.1 +mobile-experience@1.0.5 +mobile-status-bar@1.0.14 +modules@0.10.0 +modules-runtime@0.8.0 +mongo@1.2.2 +mongo-dev-server@1.0.1 +mongo-id@1.0.6 +npm-mongo@2.2.30 +ordered-dict@1.0.9 +promise@0.9.0 +random@1.0.10 +reactive-var@1.0.11 +reload@1.1.11 +retry@1.0.9 +routepolicy@1.0.12 +shell-server@0.2.4 +spacebars-compiler@1.1.3 +standard-minifier-css@1.3.5 +standard-minifier-js@2.1.1 +static-html@1.2.2 +templating-tools@1.1.2 +tracker@1.1.3 +underscore@1.0.10 +url@1.1.0 +webapp@1.3.19 +webapp-hashing@1.0.9 diff --git a/tools/tests/apps/meteor-ignore/a.js b/tools/tests/apps/meteor-ignore/a.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/a.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/apps/meteor-ignore/b.js b/tools/tests/apps/meteor-ignore/b.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/b.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/apps/meteor-ignore/imports/registry.js b/tools/tests/apps/meteor-ignore/imports/registry.js new file mode 100644 index 0000000000..7dea8ddb80 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/imports/registry.js @@ -0,0 +1,11 @@ +const ids = []; + +export function add(id) { + ids.push(id); +} + +Meteor.startup(() => { + ids.sort().forEach(id => { + console.log(id); + }); +}); diff --git a/tools/tests/apps/meteor-ignore/lib/e.js b/tools/tests/apps/meteor-ignore/lib/e.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/lib/e.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/apps/meteor-ignore/lib/f.js b/tools/tests/apps/meteor-ignore/lib/f.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/lib/f.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/apps/meteor-ignore/main.js b/tools/tests/apps/meteor-ignore/main.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/main.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/apps/meteor-ignore/package-lock.json b/tools/tests/apps/meteor-ignore/package-lock.json new file mode 100644 index 0000000000..f8c2fe01f1 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/package-lock.json @@ -0,0 +1,603 @@ +{ + "name": "meteor-ignore", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "babel-runtime": { + "version": "7.0.0-beta.1", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-7.0.0-beta.1.tgz", + "integrity": "sha512-fFvXG9uqIwtkaLVymtS3oHnjzhdEC8Yb5Da9swQ+Ur9xCpkJlVOENyHMCVnLCO7XwDKtkUtvn/LeufKdwKd3uw==", + "requires": { + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" + } + }, + "core-js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" + }, + "meteor-node-stubs": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.2.11.tgz", + "integrity": "sha1-cV5Owc6IgkiylgThbQkiVrDLfjQ=", + "requires": { + "assert": "1.4.1", + "browserify-zlib": "0.1.4", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.11.0", + "domain-browser": "1.1.7", + "events": "1.1.1", + "http-browserify": "1.7.0", + "https-browserify": "0.0.1", + "os-browserify": "0.2.1", + "path-browserify": "0.0.0", + "process": "0.11.9", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "git+https://github.com/meteor/readable-stream.git#2e9112d7d31a2af6e0682db0e18679b1e5fd4694", + "stream-browserify": "2.0.1", + "string_decoder": "1.0.1", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "Base64": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz", + "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg=" + }, + "asn1.js": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.0.tgz", + "integrity": "sha1-9xoSQ/PnnUbXsH1/v0gk7nOvBUo=", + "requires": { + "bn.js": "4.11.6", + "inherits": "2.0.1", + "minimalistic-assert": "1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + }, + "base64-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", + "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=" + }, + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "brace-expansion": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.6.tgz", + "integrity": "sha1-QChwa5FfkfezSaLgvzw3YDnSFuU=" + }, + "browserify-aes": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", + "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.3", + "create-hash": "1.1.2", + "evp_bytestokey": "1.0.0", + "inherits": "2.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "requires": { + "browserify-aes": "1.0.6", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.0" + } + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "requires": { + "cipher-base": "1.0.3", + "des.js": "1.0.0", + "inherits": "2.0.1" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "4.11.6", + "randombytes": "2.0.3" + } + }, + "browserify-sign": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.0.tgz", + "integrity": "sha1-EHc5EMPCBtVCCkaq2GlPgguFlo8=", + "requires": { + "bn.js": "4.11.6", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.2", + "create-hmac": "1.1.4", + "elliptic": "6.3.2", + "inherits": "2.0.1", + "parse-asn1": "5.0.0" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "requires": { + "pako": "0.2.9" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "1.2.0", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "cipher-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", + "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=", + "requires": { + "inherits": "2.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "requires": { + "bn.js": "4.11.6", + "elliptic": "6.3.2" + } + }, + "create-hash": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz", + "integrity": "sha1-USEAYte7dHn2xlu0GpIgix1hq60=", + "requires": { + "cipher-base": "1.0.3", + "inherits": "2.0.1", + "ripemd160": "1.0.1", + "sha.js": "2.4.8" + } + }, + "create-hmac": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.4.tgz", + "integrity": "sha1-0/tLolPriz9W456i+8uK90e9MXA=", + "requires": { + "create-hash": "1.1.2", + "inherits": "2.0.1" + } + }, + "crypto-browserify": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", + "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=", + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.0", + "create-ecdh": "4.0.0", + "create-hash": "1.1.2", + "create-hmac": "1.1.4", + "diffie-hellman": "5.0.2", + "inherits": "2.0.1", + "pbkdf2": "3.0.9", + "public-encrypt": "4.0.0", + "randombytes": "2.0.3" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "2.0.1", + "minimalistic-assert": "1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "requires": { + "bn.js": "4.11.6", + "miller-rabin": "4.0.0", + "randombytes": "2.0.3" + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "elliptic": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.2.tgz", + "integrity": "sha1-5MgeCCnPCmWrcOmYuCMnI7XBvEg=", + "requires": { + "bn.js": "4.11.6", + "brorand": "1.0.6", + "hash.js": "1.0.3", + "inherits": "2.0.1" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "evp_bytestokey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", + "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=", + "requires": { + "create-hash": "1.1.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.1", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "hash.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", + "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", + "requires": { + "inherits": "2.0.1" + } + }, + "http-browserify": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz", + "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=", + "requires": { + "Base64": "0.2.1", + "inherits": "2.0.1" + } + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", + "requires": { + "bn.js": "4.11.6", + "brorand": "1.0.6" + } + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "parse-asn1": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.0.0.tgz", + "integrity": "sha1-NQYPbVAV03Yox3D04JGgtaJ4vCM=", + "requires": { + "asn1.js": "4.9.0", + "browserify-aes": "1.0.6", + "create-hash": "1.1.2", + "evp_bytestokey": "1.0.0", + "pbkdf2": "3.0.9" + } + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pbkdf2": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.9.tgz", + "integrity": "sha1-8sSyWmAAWLPDdzwIbDfbvuH/5pM=", + "requires": { + "create-hmac": "1.1.4" + } + }, + "process": { + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.9.tgz", + "integrity": "sha1-e9WtIapiU+fahoImTx4R0RwDGME=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "requires": { + "bn.js": "4.11.6", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.2", + "parse-asn1": "5.0.0", + "randombytes": "2.0.3" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew=" + }, + "readable-stream": { + "version": "git+https://github.com/meteor/readable-stream.git#2e9112d7d31a2af6e0682db0e18679b1e5fd4694", + "requires": { + "inherits": "2.0.1", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.0.1", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "requires": { + "glob": "7.1.2" + } + }, + "ripemd160": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz", + "integrity": "sha1-k6S71JQrxXS2mo+lfHHeEOzKfW4=" + }, + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", + "requires": { + "inherits": "2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "requires": { + "inherits": "2.0.1", + "readable-stream": "git+https://github.com/meteor/readable-stream.git#2e9112d7d31a2af6e0682db0e18679b1e5fd4694" + } + }, + "string_decoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", + "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "requires": { + "safe-buffer": "5.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "requires": { + "process": "0.11.9" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" + } + } +} diff --git a/tools/tests/apps/meteor-ignore/package.json b/tools/tests/apps/meteor-ignore/package.json new file mode 100644 index 0000000000..4797086153 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/package.json @@ -0,0 +1,11 @@ +{ + "name": "meteor-ignore", + "private": true, + "scripts": { + "start": "meteor run" + }, + "dependencies": { + "babel-runtime": "^7.0.0-beta.0", + "meteor-node-stubs": "~0.2.4" + } +} diff --git a/tools/tests/apps/meteor-ignore/server/c.js b/tools/tests/apps/meteor-ignore/server/c.js new file mode 100644 index 0000000000..0c5700298f --- /dev/null +++ b/tools/tests/apps/meteor-ignore/server/c.js @@ -0,0 +1,2 @@ +require("/imports/registry.js").add(module.id); +require("./d.js"); diff --git a/tools/tests/apps/meteor-ignore/server/d.js b/tools/tests/apps/meteor-ignore/server/d.js new file mode 100644 index 0000000000..8a1d00f709 --- /dev/null +++ b/tools/tests/apps/meteor-ignore/server/d.js @@ -0,0 +1 @@ +require("/imports/registry.js").add(module.id); diff --git a/tools/tests/meteor-ignore.js b/tools/tests/meteor-ignore.js new file mode 100644 index 0000000000..e779752bd8 --- /dev/null +++ b/tools/tests/meteor-ignore.js @@ -0,0 +1,75 @@ +const selftest = require("../tool-testing/selftest.js"); +const Sandbox = selftest.Sandbox; + +selftest.define(".meteorignore", function () { + const s = new Sandbox(); + + s.createApp("myapp", "meteor-ignore"); + s.cd("myapp"); + + let run = s.run(); + run.waitSecs(30); + run.match("/a.js"); + run.match("/b.js"); + run.match("/lib/e.js"); + run.match("/lib/f.js"); + run.match("/main.js"); + run.match("/server/c.js"); + run.match("/server/d.js"); + run.match("App running at"); + + s.write("server/.meteorignore", "c.*"); + run.waitSecs(10); + run.match("/a.js"); + run.match("/b.js"); + run.match("/lib/e.js"); + run.match("/lib/f.js"); + run.match("/main.js"); + run.match("/server/d.js"); + run.match("restarted"); + + s.write(".meteorignore", "server/d.js"); + run.waitSecs(10); + run.match("/a.js"); + run.match("/b.js"); + run.match("/lib/e.js"); + run.match("/lib/f.js"); + run.match("/main.js"); + run.match("restarted"); + + s.write("lib/.meteorignore", "*.js\n!e.*"); + run.waitSecs(10); + run.match("/a.js"); + run.match("/b.js"); + run.match("/lib/e.js"); + run.match("/main.js"); + run.match("restarted"); + + s.write(".meteorignore", "lib/**"); + run.waitSecs(10); + run.match("/a.js"); + run.match("/b.js"); + run.match("/main.js"); + run.match("/server/d.js"); + run.match("restarted"); + + s.write(".meteorignore", "/*.js\nlib"); + run.waitSecs(10); + run.match("/server/d.js"); + run.match("restarted"); + + s.unlink(".meteorignore"); + s.unlink("lib/.meteorignore"); + s.unlink("server/.meteorignore"); + run.waitSecs(10); + run.match("/a.js"); + run.match("/b.js"); + run.match("/lib/e.js"); + run.match("/lib/f.js"); + run.match("/main.js"); + run.match("/server/c.js"); + run.match("/server/d.js"); + run.match("restarted"); + + run.stop(); +}); From bce7129712a58b5efdb6c27e553a7e49cf65f978 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 22 Sep 2017 09:38:24 -0400 Subject: [PATCH 5/7] Stop setting METEOR_DISABLE_OPTIMISTIC_CACHING=1 in Circle CI. Since 8c70716954f8fcdd02fc0c673a39da2b7186ce2b, optimistic file watching uses far fewer file descriptors, so the original motivation of setting this environment variable no longer holds. --- .circleci/config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a2704fe2c..d0a4dabd5a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,11 +79,6 @@ build_machine_environment: &build_machine_environment # the mess of temp directories that Meteor makes. METEOR_SAVE_TMPDIRS: 1 - # Disable the optimistic caching of file watchers, which incurs a slight - # polling delay which is less than ideal in a CI environment where file - # watchers should be plentiful. - METEOR_DISABLE_OPTIMISTIC_CACHING: 1 - # Skip these tests on every test run. # For readability, this is a regex wrapped across multiple lines in quotes. SELF_TEST_EXCLUDE: "\ From eb8189c99fa226f90137b93a2c2fbeee6032a380 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 22 Sep 2017 09:49:51 -0400 Subject: [PATCH 6/7] Mention .meteorignore support in History.md. --- History.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/History.md b/History.md index 4f9e859f33..10a9ac8557 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,14 @@ ## v.NEXT +* Meteor now supports `.meteorignore` files, which cause the build system + to ignore certain files and directories using the same pattern syntax as + [`.gitignore` files](https://git-scm.com/docs/gitignore). These files + may appear in any directory of your app or package, specifying rules for + the directory tree below them. Of course, `.meteorignore` files are also + fully integrated with Meteor's file watching system, so they can be + added, removed, or modified during development. + [Feature request #5](https://github.com/meteor/meteor-feature-requests/issues/5) + * DDP's `connection.onReconnect = func` feature has been deprecated. This functionality was previously supported as a way to set a function to be called as the first step of reconnecting. This approach has proven to be From 022f87c5b17d71214eff68a563e2d4b1e6367f48 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 22 Sep 2017 14:06:17 -0400 Subject: [PATCH 7/7] Avoid stopping fs.watchFile pollers when rewatching. Calling unwatchFile may result in stopping the watcher before watchFile is called, which then restarts it. This temporary stoppage appears to cause change events to be missed sometimes. In particular, preventing this stop/start with the acrobatics in this commit seems to fix recent compiler-plugins.js test failures. --- tools/fs/safe-watcher.js | 74 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/tools/fs/safe-watcher.js b/tools/fs/safe-watcher.js index 9c2eedbd39..98865ef265 100644 --- a/tools/fs/safe-watcher.js +++ b/tools/fs/safe-watcher.js @@ -3,6 +3,7 @@ import { Profile } from "../tool-env/profile.js"; import { statOrNull, pathDirname, + pathResolve, convertToOSPath, convertToStandardPath, watchFile, @@ -160,18 +161,17 @@ function startNewWatcher(absPath) { safeUnwatch(); } - // This is a no-op if we're not watching the file. - unwatchFile(absPath, watchFileWrapper); + // Since we're about to restart the stat-based file watcher, we don't + // want to miss any of its events because of the lastWatcherEventTime + // throttling that it attempts to do. + lastWatcherEventTime = 0; // We use files.watchFile in addition to watcher.watch as a fail-safe // to detect file changes even on network file systems. However // (unless the user disabled watcher or this watcher call failed), we // use a relatively long default polling interval of 5000ms to save // CPU cycles. - watchFile(absPath, { - persistent: false, - interval: getPollingInterval(), - }, watchFileWrapper); + statWatch(absPath, getPollingInterval(), watchFileWrapper); } function watchFileWrapper(...args) { @@ -250,6 +250,68 @@ export function closeAllWatchers() { }); } +const statWatchers = Object.create(null); + +function statWatch(absPath, interval, callback) { + const oldWatcher = statWatchers[absPath]; + + while (oldWatcher) { + // Make sure this callback no longer appears among the listeners for + // this StatWatcher. + const countBefore = oldWatcher.stat.listenerCount("change"); + + // This removes at most one occurrence of the callback from the + // listeners list... + oldWatcher.stat.removeListener("change", callback); + + // ... so we have to keep calling it until the first time + // it removes nothing. + if (oldWatcher.stat.listenerCount("change") === countBefore) { + break; + } + } + + // This doesn't actually call newStat.start again if there's already a + // watcher for this file, so it won't change any interval previously + // specified. In the rare event that the interval needs to change, we + // manually stop and restart the StatWatcher below. + const newStat = watchFile(absPath, { + persistent: false, // never persistent + interval, + }, callback); + + if (! oldWatcher) { + const newWatcher = { + stat: newStat, + interval, + }; + + newStat.on("stop", () => { + if (statWatchers[absPath] === newWatcher) { + delete statWatchers[absPath]; + } + }); + + return statWatchers[absPath] = newWatcher; + } + + // These should be identical at this point, but just in case. + oldWatcher.stat = newStat; + + // If the interval needs to be changed, manually stop and restart the + // StatWatcher using lower-level methods than unwatchFile and watchFile. + if (oldWatcher.interval !== interval) { + oldWatcher.stat.stop(); + oldWatcher.stat.start( + convertToOSPath(pathResolve(absPath)), + false, // never persistent + oldWatcher.interval = interval, + ); + } + + return oldWatcher; +} + function watchLibraryWatch(absPath, callback) { if (WATCHER_ENABLED) { try {