diff --git a/History.md b/History.md index 63517aace8..6810b12625 100644 --- a/History.md +++ b/History.md @@ -131,6 +131,25 @@ * You can now run `meteor test --driver-package user:package` without first running `meteor add user:package`. +## v1.5.3, 2017-11-04 + +* Node has been upgraded to version 4.8.5, a recommended security + release: https://nodejs.org/en/blog/release/v4.8.5/. While it was + expected that Node 4.8.5 would also include our fix of a faulty + backport of garbage collection-related logic in V8, the timing + of this security release has caused that to be delayed until 4.8.6. + Therefore, this Node still includes our patch for this issue. + [Issue #8648](https://github.com/meteor/meteor/issues/8648) + +* Various backports from Meteor 1.6, as detailed in the + [PR for Meteor 1.5.3](https://github.com/meteor/meteor/pull/9266). + Briefly, these involve fixes for: + * Child imports of dynamically imported modules within packages. + [#9182](https://github.com/meteor/meteor/issues/9182) + * Unresolved circular dependencies. + [#9176](https://github.com/meteor/meteor/issues/9176) + * Windows temporary directory handling. + ## v1.5.2.2, 2017-10-02 * Fixes a regression in 1.5.2.1 which resulted in the macOS firewall diff --git a/packages/meteor/define-package.js b/packages/meteor/define-package.js new file mode 100644 index 0000000000..9acb89a04a --- /dev/null +++ b/packages/meteor/define-package.js @@ -0,0 +1,31 @@ +function PackageRegistry() {} + +var PRp = PackageRegistry.prototype; + +// Set global.Package[name] = pkg || {}. If additional arguments are +// supplied, their keys will be copied into pkg if not already present. +// This method is defined on the prototype of global.Package so that it +// will not be included in Object.keys(Package). +PRp._define = function definePackage(name, pkg) { + pkg = pkg || {}; + + var argc = arguments.length; + for (var i = 2; i < argc; ++i) { + var arg = arguments[i]; + for (var s in arg) { + if (! (s in pkg)) { + pkg[s] = arg[s]; + } + } + } + + return this[name] = pkg; +}; + +// Initialize the Package namespace used by all Meteor packages. +global.Package = new PackageRegistry(); + +if (typeof exports === "object") { + // This code is also used by meteor/tools/isobuild/bundler.js. + exports.PackageRegistry = PackageRegistry; +} diff --git a/packages/meteor/global.js b/packages/meteor/global.js index 98ef2571c2..7bf9d3d4f6 100644 --- a/packages/meteor/global.js +++ b/packages/meteor/global.js @@ -1 +1,2 @@ +// Export a reliable global object for all Meteor code. global = this; diff --git a/packages/meteor/package.js b/packages/meteor/package.js index f9c55c9ed7..fe758281ed 100644 --- a/packages/meteor/package.js +++ b/packages/meteor/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Core Meteor environment", - version: '1.8.0' + version: '1.8.1' }); Package.registerBuildPlugin({ @@ -28,6 +28,7 @@ Package.onUse(function (api) { api.export("meteorEnv"); api.addFiles('cordova_environment.js', 'web.cordova'); + api.addFiles('define-package.js', ['client', 'server']); api.addFiles('helpers.js', ['client', 'server']); api.addFiles('setimmediate.js', ['client', 'server']); api.addFiles('timers.js', ['client', 'server']); diff --git a/packages/non-core/coffeescript-compiler/.npm/package/npm-shrinkwrap.json b/packages/non-core/coffeescript-compiler/.npm/package/npm-shrinkwrap.json index cf2561e14e..d1d2f5a57f 100644 --- a/packages/non-core/coffeescript-compiler/.npm/package/npm-shrinkwrap.json +++ b/packages/non-core/coffeescript-compiler/.npm/package/npm-shrinkwrap.json @@ -2,9 +2,9 @@ "lockfileVersion": 1, "dependencies": { "coffeescript": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", - "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.0.2.tgz", + "integrity": "sha512-/bCMyzu7KSJPF2gRNYWpbEmfPkNL8AzXs78ktxhPTpSXlKetZRl7kIYGqU055UqUVnkKRJK4eUkUhRHQpvdilA==" }, "source-map": { "version": "0.5.7", diff --git a/packages/non-core/coffeescript-compiler/coffeescript-compiler.js b/packages/non-core/coffeescript-compiler/coffeescript-compiler.js index 283ccecce2..fa4b937181 100644 --- a/packages/non-core/coffeescript-compiler/coffeescript-compiler.js +++ b/packages/non-core/coffeescript-compiler/coffeescript-compiler.js @@ -22,7 +22,10 @@ export class CoffeeScriptCompiler { // prevents CoffeeScript projects from using the modules package and // putting require or import statements within backticks; it just // won't happen automatically because of Babel. - runtime: false + runtime: false, + // CoffeeScript 2 supports for JSX, which Meteor supports only for React, + // per packages/ecmascript/plugin.js. + react: true }); } diff --git a/packages/non-core/coffeescript-compiler/package.js b/packages/non-core/coffeescript-compiler/package.js index d1989c3b80..81028830fe 100644 --- a/packages/non-core/coffeescript-compiler/package.js +++ b/packages/non-core/coffeescript-compiler/package.js @@ -1,13 +1,23 @@ +// The NPM `coffeescript` module requires Node 6+; but instead of checking for +// a Node runtime version, detect support for async functions, which were +// added in Node 7.6. +try { + new Function('async () => {}')(); +} catch (exception) { + throw new Error('Your runtime does not support this version of CoffeeScript. Please upgrade to Meteor 1.6 or later, or use a 1.x version of CoffeeScript.'); +} + + Package.describe({ name: 'coffeescript-compiler', summary: 'Compiler for CoffeeScript code, supporting the coffeescript package', // This version of NPM `coffeescript` module, with _1, _2 etc. // If you change this, make sure to also update ../coffeescript/package.js to match. - version: '1.12.7_3' + version: '2.0.2_1' }); Npm.depends({ - 'coffeescript': '1.12.7', + 'coffeescript': '2.0.2', 'source-map': '0.5.7' }); diff --git a/packages/non-core/coffeescript-test-helper/exporting.coffee b/packages/non-core/coffeescript-test-helper/exporting.coffee index 93e4873187..5321e53ed3 100644 --- a/packages/non-core/coffeescript-test-helper/exporting.coffee +++ b/packages/non-core/coffeescript-test-helper/exporting.coffee @@ -1,15 +1,8 @@ COFFEESCRIPT_EXPORTED = 123 COFFEESCRIPT_EXPORTED_ONE_MORE = 234 -# Having backticks in the code is required to trigger Babel processing -# the file and re-wrapping the list of defined variables. `COFFEESCRIPT_EXPORTED_WITH_BACKTICKS = 345` -# Defining a class which extends a new class forces CoffeeScript -# to define an "extend" function, which then in turn forces Babel -# to re-wrap the list of defined variables so that each is defined -# in its own line. - class TestClass class ClassExtending extends TestClass diff --git a/packages/non-core/coffeescript-test-helper/package.js b/packages/non-core/coffeescript-test-helper/package.js index e77126f9ba..da318c53d2 100644 --- a/packages/non-core/coffeescript-test-helper/package.js +++ b/packages/non-core/coffeescript-test-helper/package.js @@ -1,10 +1,10 @@ Package.describe({ summary: "Used by the coffeescript package's tests", - version: "1.0.10-beta.30" + version: "1.1.0" }); Package.onUse(function (api) { - api.use('coffeescript@1.12.7-2-beta.30', ['client', 'server']); + api.use('coffeescript@2.0.2_1', ['client', 'server']); api.export('COFFEESCRIPT_EXPORTED'); api.export('COFFEESCRIPT_EXPORTED_ONE_MORE'); api.export('COFFEESCRIPT_EXPORTED_WITH_BACKTICKS'); diff --git a/packages/non-core/coffeescript/package.js b/packages/non-core/coffeescript/package.js index f414bb4aa4..fa5eabcd76 100644 --- a/packages/non-core/coffeescript/package.js +++ b/packages/non-core/coffeescript/package.js @@ -6,12 +6,12 @@ Package.describe({ // so bumping the version of this package will be how they get newer versions // of `coffeescript-compiler`. If you change this, make sure to also update // ../coffeescript-compiler/package.js to match. - version: '1.12.7_3' + version: '2.0.2_1' }); Package.registerBuildPlugin({ name: 'compile-coffeescript', - use: ['caching-compiler@1.1.9', 'ecmascript@0.8.3', 'coffeescript-compiler@=1.12.7_3'], + use: ['caching-compiler@1.1.9', 'ecmascript@0.8.3', 'coffeescript-compiler@=2.0.2_1'], sources: ['compile-coffeescript.js'] }); diff --git a/packages/non-core/coffeescript/tests/coffeescript_tests.coffee b/packages/non-core/coffeescript/tests/coffeescript_tests.coffee index ef584dc8cd..ff35349e06 100644 --- a/packages/non-core/coffeescript/tests/coffeescript_tests.coffee +++ b/packages/non-core/coffeescript/tests/coffeescript_tests.coffee @@ -28,3 +28,17 @@ Tinytest.add "coffeescript - import local module via native import statement", ( import { testingForNativeImportedModule123456789 } from "./coffeescript_module.coffee"; Tinytest.add "coffeescript - import local module exported by a CoffeeScript native export statement, via native import statement", (test) -> test.isTrue testingForNativeImportedModule123456789? + + +# CoffeeScript 2 is active, with its conforming-to-ES2015 breaking changes +Tinytest.add "coffeescript - ES2015 conformity", (test) -> + f = (a = 1) -> a + test.isTrue f(null) is null # `f(null)` would be 1 in CoffeeScript 1.x + +# JSX +Tinytest.add "coffeescript - JSX", (test) -> + # Mock React + React = + createElement: (tag, attributes, body) -> + "<#{tag}>#{body}" + test.isTrue
Hello from JSX!
is '
Hello from JSX!
' diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index 8323b8dea4..9f37416648 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -172,6 +172,7 @@ var release = require('../packaging/release.js'); import { loadIsopackage } from '../tool-env/isopackets.js'; import { CORDOVA_PLATFORM_VERSIONS } from '../cordova'; import { gzipSync } from "zlib"; +import { PackageRegistry } from "../../packages/meteor/define-package.js"; const SOURCE_URL_PREFIX = "meteor://\u{1f4bb}app"; @@ -1771,7 +1772,7 @@ class JsImage { // in a compartment of Package load(bindings) { var self = this; - var ret = {}; + var ret = new PackageRegistry(); // XXX This is mostly duplicated from // static-assets/server/boot.js, as is Npm.require below. diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index 110a3f125f..1b0abdbabf 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -8,6 +8,7 @@ var linker = require('./linker.js'); var util = require('util'); var _ = require('underscore'); var Profile = require('../tool-env/profile.js').Profile; +import assert from "assert"; import {sha1, readAndWatchFileWithHash} from '../fs/watch.js'; import LRU from 'lru-cache'; import {sourceMapLength} from '../utils/utils.js'; @@ -61,7 +62,7 @@ import { isTestFilePath } from './test-files.js'; // Cache the (slightly post-processed) results of linker.fullLink. const CACHE_SIZE = process.env.METEOR_LINKER_CACHE_SIZE || 1024*1024*100; const CACHE_DEBUG = !! process.env.METEOR_TEST_PRINT_LINKER_CACHE_DEBUG; -const LINKER_CACHE_SALT = 18; // Increment this number to force relinking. +const LINKER_CACHE_SALT = 19; // Increment this number to force relinking. const LINKER_CACHE = new LRU({ max: CACHE_SIZE, // Cache is measured in bytes. We don't care about servePath. @@ -1124,11 +1125,12 @@ export class PackageSourceBatch { const appFilesWithoutNodeModules = []; outputFiles.forEach(file => { - const parts = file.installPath.split("/"); + const parts = file.absModuleId.split("/"); + assert.strictEqual(parts[0], ""); const nodeModulesIndex = parts.indexOf("node_modules"); - if (nodeModulesIndex === -1 || (nodeModulesIndex === 0 && - parts[1] === "meteor")) { + if (nodeModulesIndex === -1 || (nodeModulesIndex === 1 && + parts[2] === "meteor")) { appFilesWithoutNodeModules.push(file); } else { // This file is going to be installed in a node_modules @@ -1308,10 +1310,11 @@ export class PackageSourceBatch { files: jsResources.map((inputFile) => { fileHashes.push(inputFile.hash); return { - installPath: inputFile.installPath, + absModuleId: inputFile.absModuleId, sourceMap: !! inputFile.sourceMap, mainModule: inputFile.mainModule, imported: inputFile.imported, + alias: inputFile.alias, lazy: inputFile.lazy, bare: inputFile.bare, }; diff --git a/tools/isobuild/import-scanner.js b/tools/isobuild/import-scanner.js index 65f3658652..623964ec52 100644 --- a/tools/isobuild/import-scanner.js +++ b/tools/isobuild/import-scanner.js @@ -2,7 +2,7 @@ import assert from "assert"; import {inspect} from "util"; import {Script} from "vm"; import { - isString, isEmpty, has, keys, each, map, omit, + isString, isObject, isEmpty, has, keys, each, map, omit, } from "underscore"; import {sha1} from "../fs/watch.js"; import {matches as archMatches} from "../utils/archinfo.js"; @@ -16,7 +16,6 @@ import { pathJoin, pathRelative, pathNormalize, - pathDirname, pathBasename, pathExtname, pathIsAbsolute, @@ -24,6 +23,12 @@ import { convertToPosixPath, } from "../fs/files.js"; +const { + relative: posixRelative, + dirname: posixDirname, + sep: posixSep, +} = require("path").posix; + import { optimisticReadFile, optimisticStatOrNull, @@ -90,6 +95,29 @@ function canBeParsedAsPlainJS(dataString, hash) { return result; } +function stripLeadingSlash(path) { + if (typeof path === "string" && + path.charAt(0) === "/") { + return path.slice(1); + } + + return path; +} + +function ensureLeadingSlash(path) { + if (typeof path !== "string") { + return path; + } + + const posix = convertToPosixPath(path); + + if (posix.charAt(0) !== "/") { + return "/" + posix; + } + + return posix; +} + // Map from SHA (which is already calculated, so free for us) // to the results of calling findImportedModuleIdentifiers. // Each entry is an array of strings, and this is a case where @@ -172,7 +200,9 @@ export default class ImportScanner { // If the old file is just an empty stub, let the new file take // precedence over it. if (old.implicit === true) { - return this.absPathToOutputIndex[absPath] = file; + return Object.assign(old, { + implicit: file.implicit || false + }, file); } // If the new file is just an empty stub, pretend the _addFile @@ -217,7 +247,7 @@ export default class ImportScanner { // indicates that the file has been imported, but only dynamically). file.imported = false; - file.installPath = file.installPath || this._getInstallPath(absPath); + file.absModuleId = file.absModuleId || this._getAbsModuleId(absPath); if (! this._addFile(absPath, file)) { // Collisions can happen if a compiler plugin calls addJavaScript @@ -244,6 +274,9 @@ export default class ImportScanner { const absSourcePath = pathJoin(this.sourceRoot, file.sourcePath); const absTargetPath = pathJoin(this.sourceRoot, file.targetPath); + const absSourceId = this._getAbsModuleId(absSourcePath); + const absTargetId = this._getAbsModuleId(absTargetPath); + // If file.targetPath differs from file.sourcePath, generate a new // file object with that .sourcePath that imports the original file. // This allows either the .sourcePath or the .targetPath to be used @@ -251,12 +284,11 @@ export default class ImportScanner { // to have the same .sourcePath but different .targetPaths. let sourceFile = this._getFile(absSourcePath); if (! sourceFile) { - const installPath = this._getInstallPath(absSourcePath); sourceFile = this._addFile(absSourcePath, { type: file.type, sourcePath: file.sourcePath, - servePath: installPath, - installPath, + servePath: stripLeadingSlash(absSourceId), + absModuleId: absSourceId, dataString: "", deps: {}, lazy: true, @@ -266,12 +298,12 @@ export default class ImportScanner { // Make sure the original file gets installed at the target path // instead of the source path. - file.installPath = this._getInstallPath(absTargetPath); + file.absModuleId = absTargetId; file.sourcePath = file.targetPath; const relativeId = this._getRelativeImportId( - absSourcePath, - absTargetPath, + absSourceId, + absTargetId, ); // Set the contents of the source module to import the target @@ -298,7 +330,7 @@ export default class ImportScanner { sourceFile.data = Buffer.from(sourceFile.dataString, "utf8"); sourceFile.hash = sha1(sourceFile.data); sourceFile.deps[relativeId] = { - installPath: file.installPath, + absModuleId: file.absModuleId, possiblySpurious: false, dynamic: false }; @@ -510,7 +542,7 @@ export default class ImportScanner { // Return all installable output files that are either eager or // imported (statically or dynamically). return this.outputFiles.filter(file => { - return file.installPath && + return file.absModuleId && ! file[fakeSymbol] && (! file.lazy || file.imported === true || @@ -590,7 +622,7 @@ export default class ImportScanner { const packageJsonFile = this._addPkgJsonToOutput(path, pkg, forDynamicImport); - if (! parentFile.installPath) { + if (! parentFile.absModuleId) { // If parentFile is not installable, then we won't return it // from getOutputFiles, so we don't need to worry about // recording any parentFile.deps[id].helpers. @@ -598,8 +630,8 @@ export default class ImportScanner { } const relativeId = this._getRelativeImportId( - parentFile.installPath, - packageJsonFile.installPath + parentFile.absModuleId, + packageJsonFile.absModuleId ); // Although not explicitly imported, any package.json modules @@ -607,16 +639,30 @@ export default class ImportScanner { // implicit "helpers." info.helpers[relativeId] = forDynamicImport; }); + + // Any relevant package.json files must have already been added via + // this._addPkgJsonToOutput before we check whether this file has an + // .alias. In other words, the Resolver is responsible for including + // relevant package.json files in resolved.packageJsonMap so that + // they can be handled by the loop above. + const file = this._getFile(resolved.path); + if (file && file.alias) { + file.imported = forDynamicImport + ? file.imported || "dynamic" + : true; + + return file.alias; + } } return resolved; } - _getRelativeImportId(parentPath, childPath) { - const relativeId = convertToPosixPath(pathRelative( - pathDirname(parentPath), - childPath - ), true); + _getRelativeImportId(absParentId, absChildId) { + const relativeId = posixRelative( + posixDirname(absParentId), + absChildId + ); // If the result of pathRelative does not already start with a "." or // a "/", prepend a "./" to make it a valid relative identifier @@ -695,7 +741,7 @@ export default class ImportScanner { // If the module is an implicit package.json stub, update to the // explicit version now. if (depFile.jsonData && - depFile.installPath.endsWith("/package.json") && + depFile.absModuleId.endsWith("/package.json") && depFile.implicit === true) { const file = this._readModule(absImportedPath); if (file) { @@ -712,13 +758,13 @@ export default class ImportScanner { return; } - const installPath = this._getInstallPath(absImportedPath); - if (! installPath) { + const absModuleId = this._getAbsModuleId(absImportedPath); + if (! absModuleId) { // The given path cannot be installed on this architecture. return; } - info.installPath = installPath; + info.absModuleId = absModuleId; // If the module is not readable, _readModule may return // null. Otherwise it will return an object with .data, .dataString, @@ -730,8 +776,8 @@ export default class ImportScanner { depFile.type = "js"; // TODO Is this correct? depFile.sourcePath = pathRelative(this.sourceRoot, absImportedPath); - depFile.installPath = installPath; - depFile.servePath = installPath; + depFile.absModuleId = absModuleId; + depFile.servePath = stripLeadingSlash(absModuleId); depFile.lazy = true; // Setting depFile.imported = false is necessary so that // this._scanFile(depFile, dynamic) doesn't think the file has been @@ -745,7 +791,7 @@ export default class ImportScanner { // handled natively by Node, so we don't need to build a // meteorInstall-style bundle beyond the entry-point module. if (! this.isWeb() && - depFile.installPath.startsWith("node_modules/") && + depFile.absModuleId.startsWith("/node_modules/") && // If optimistic functions care about this file, e.g. because it // resides in a linked npm package, then we should allow it to // be watched by including it in the server bundle by not @@ -840,13 +886,13 @@ export default class ImportScanner { return info; } - // Returns a relative path indicating where to install the given file - // via meteorInstall. May return undefined if the file should not be - // installed on the current architecture. - _getInstallPath(absPath) { + // Returns an absolute module identifier indicating where to install the + // given file via meteorInstall. May return undefined if the file should + // not be installed on the current architecture. + _getAbsModuleId(absPath) { let path = - this._getNodeModulesInstallPath(absPath) || - this._getSourceRootInstallPath(absPath); + this._getNodeModulesAbsModuleId(absPath) || + this._getSourceRootAbsModuleId(absPath); if (! path) { return; @@ -864,11 +910,11 @@ export default class ImportScanner { } // Install paths should always be delimited by /. - return convertToPosixPath(path); + return ensureLeadingSlash(path); } - _getNodeModulesInstallPath(absPath) { - let installPath; + _getNodeModulesAbsModuleId(absPath) { + let absModuleId; this.nodeModulesPaths.some(path => { const relPathWithinNodeModules = pathRelative(path, absPath); @@ -880,24 +926,29 @@ export default class ImportScanner { // Install the module into the local node_modules directory within // this app or package. - return installPath = pathJoin( + return absModuleId = pathJoin( "node_modules", relPathWithinNodeModules ); }); - return installPath; + return ensureLeadingSlash(absModuleId); } - _getSourceRootInstallPath(absPath) { - const installPath = pathRelative(this.sourceRoot, absPath); + _getSourceRootAbsModuleId(absPath) { + const relPath = pathRelative(this.sourceRoot, absPath); - if (installPath.startsWith("..")) { + if (relPath.startsWith("..")) { // absPath is not a subdirectory of this.sourceRoot. return; } - const dirs = this._splitPath(pathDirname(installPath)); + const dirs = relPath.split("/"); + dirs.pop(); // Discard the module's filename. + while (dirs[0] === "") { + dirs.shift(); + } + const isApp = ! this.name; const bundlingForWeb = this.isWeb(); @@ -934,19 +985,11 @@ export default class ImportScanner { if (dir === "node_modules") { // Accept any file within a node_modules directory. - return installPath; + return ensureLeadingSlash(relPath); } } - return installPath; - } - - _splitPath(path) { - const partsInReverse = []; - for (let dir; (dir = pathDirname(path)) !== path; path = dir) { - partsInReverse.push(pathBasename(path)); - } - return partsInReverse.reverse(); + return ensureLeadingSlash(relPath); } // Called by this.resolver when a module identifier cannot be resolved. @@ -1043,6 +1086,7 @@ export default class ImportScanner { }).join("")); const relPkgJsonPath = pathRelative(this.sourceRoot, pkgJsonPath); + const absModuleId = this._getAbsModuleId(pkgJsonPath); const pkgFile = { type: "js", // We represent the JSON module with JS. @@ -1050,8 +1094,8 @@ export default class ImportScanner { jsonData: pkg, deps: {}, // Avoid accidentally re-scanning this file. sourcePath: relPkgJsonPath, - installPath: this._getInstallPath(pkgJsonPath), - servePath: relPkgJsonPath, + absModuleId, + servePath: stripLeadingSlash(absModuleId), hash: sha1(data), lazy: true, imported: forDynamicImport ? "dynamic" : true, @@ -1071,12 +1115,122 @@ export default class ImportScanner { this.watchSet.addFile(pkgJsonPath, hash); } + this._resolvePkgJsonBrowserAliases(pkgFile, forDynamicImport); + return pkgFile; } + + _resolvePkgJsonBrowserAliases(pkgFile, forDynamicImport = false) { + if (! this.isWeb()) { + return; + } + + const browser = pkgFile.jsonData.browser; + if (! isObject(browser)) { + return; + } + + const deps = pkgFile.deps; + const absPkgJsonPath = pathJoin(this.sourceRoot, pkgFile.sourcePath); + + Object.keys(browser).forEach(sourceId => { + deps[sourceId] = deps[sourceId] || {}; + + // TODO What if sourceId is a top-level node_modules identifier? + const source = this.resolver.resolve(sourceId, absPkgJsonPath); + if (! source || source === "missing") { + return; + } + + const file = this._getFile(source.path); + if (file && file.alias) { + // If we previously set an .alias for this file, assume it is + // complete and return early. + return; + } + + const sourceAbsModuleId = this._getAbsModuleId(source.path); + const hasAuthorityToCreateAlias = + this._areAbsModuleIdsInSamePackage( + pkgFile.absModuleId, + sourceAbsModuleId + ); + + // A package.json file's "browser" field can only establish aliases + // for modules contained by the same package. + if (! hasAuthorityToCreateAlias) { + return; + } + + const targetId = browser[sourceId]; + const alias = {}; + + if (typeof targetId === "string") { + deps[targetId] = deps[targetId] || {}; + + const target = this.resolver.resolve(targetId, absPkgJsonPath); + if (! target || target === "missing") { + return; + } + + Object.assign(alias, target); + alias.absModuleId = this._getAbsModuleId(target.path); + + } else if (targetId === false) { + // This is supposed to indicate the alias refers to an empty stub. + alias.absModuleId = false; + + } else { + return; + } + + if (file) { + file.alias = alias; + } else { + const relSourcePath = pathRelative(this.sourceRoot, source.path); + + this._addFile(source.path, { + alias, + data: Buffer.from("", "utf8"), + dataString: "", + sourcePath: relSourcePath, + absModuleId: sourceAbsModuleId, + servePath: stripLeadingSlash(sourceAbsModuleId), + lazy: true, + imported: false, + implicit: true, + }); + } + }); + } + + _areAbsModuleIdsInSamePackage(path1, path2) { + if (! (isString(path1) && isString(path2))) { + return false; + } + + // Enforce that the input paths look like absolute module identifiers. + assert.strictEqual(path1.charAt(0), "/"); + assert.strictEqual(path2.charAt(0), "/"); + + function getPackageRoot(path) { + const parts = path.split("/"); + assert.strictEqual(parts[0], ""); + const nmi = parts.lastIndexOf("node_modules"); + return parts.slice(0, nmi + 2).join("/"); + } + + return getPackageRoot(path1) === getPackageRoot(path2); + } } -each(["_readFile", "_findImportedModuleIdentifiers", - "_getInstallPath"], funcName => { +each([ + "_readFile", + "_findImportedModuleIdentifiers", + "_getAbsModuleId", + "_addPkgJsonToOutput", + "_resolvePkgJsonBrowserAliases", +], funcName => { ImportScanner.prototype[funcName] = Profile( `ImportScanner#${funcName}`, ImportScanner.prototype[funcName]); }); diff --git a/tools/isobuild/isopack.js b/tools/isobuild/isopack.js index 7ce03e8c6a..8a0b0fb683 100644 --- a/tools/isobuild/isopack.js +++ b/tools/isobuild/isopack.js @@ -18,6 +18,7 @@ var buildPluginModule = require('./build-plugin.js'); var Console = require('../console/console.js').Console; var Profile = require('../tool-env/profile.js').Profile; import { requestGarbageCollection } from "../utils/gc.js"; +import { Unibuild } from "./unibuild.js"; var rejectBadPath = function (p) { if (p.match(/\.\./)) { @@ -25,93 +26,6 @@ var rejectBadPath = function (p) { } }; -/////////////////////////////////////////////////////////////////////////////// -// Unibuild -/////////////////////////////////////////////////////////////////////////////// - -// Options: -// - kind [required] (main/plugin/app) -// - arch [required] -// - uses -// - implies -// - watchSet -// - nodeModulesDirectories -// - declaredExports -// - resources - -var nextBuildId = 1; -var Unibuild = function (isopack, options) { - var self = this; - options = options || {}; - self.pkg = isopack; - - self.kind = options.kind; - self.arch = options.arch; - - self.uses = options.uses; - self.implies = options.implies || []; - - // This WatchSet will end up having the watch items from the - // SourceArch (such as package.js or .meteor/packages), plus all of - // the actual source files for the unibuild (including items that we - // looked at to find the source files, such as directories we - // scanned). - self.watchSet = options.watchSet || new watch.WatchSet(); - - // Each Unibuild is given a unique id when it's loaded (it is - // not saved to disk). This is just a convenience to make it easier - // to keep track of Unibuilds in a map; it's used by bundler - // and compiler. We put some human readable info in here too to make - // debugging easier. - self.id = self.pkg.name + "." + self.kind + "@" + self.arch + "#" + - (nextBuildId ++); - - // 'declaredExports' are the variables which are exported from this package. - // A list of objects with keys 'name' (required) and 'testOnly' (boolean, - // defaults to false). - self.declaredExports = options.declaredExports; - - // All of the data provided for eventual inclusion in the bundle, - // other than JavaScript that still needs to be fed through the - // final link stage. A list of objects with these keys: - // - // type: "source", "head", "body", "asset". (resources produced by - // legacy source handlers can also be "js" or "css". - // - // data: The contents of this resource, as a Buffer. For example, - // for "head", the data to insert in ; for "js", the - // JavaScript source code (which may be subject to further - // processing such as minification); for "asset", the contents of a - // static resource such as an image. - // - // servePath: The (absolute) path at which the resource would prefer - // to be served. Interpretation varies by type. For example, always - // honored for "asset", ignored for "head" and "body", sometimes - // honored for CSS but ignored if we are concatenating. - // - // sourceMap: Allowed only for "js". If present, a string. - // - // fileOptions: for "source", the options passed to `api.addFiles`. - // plugin-specific. - // - // extension: for "source", the file extension that this matched - // against at build time. null if matched against a specific filename. - self.resources = options.resources; - - // Map from absolute paths of node_modules directories to - // NodeModulesDirectory objects. - self.nodeModulesDirectories = options.nodeModulesDirectories; - - // Provided for backwards compatibility; please use - // unibuild.nodeModulesDirectories instead! - _.some(self.nodeModulesDirectories, (nmd, nodeModulesPath) => { - if (! nmd.local) { - self.nodeModulesPath = nodeModulesPath; - return true; - } - }); -}; - /////////////////////////////////////////////////////////////////////////////// // Isopack /////////////////////////////////////////////////////////////////////////////// @@ -979,141 +893,28 @@ _.extend(Isopack.prototype, { return; } - var unibuildJson = JSON.parse( - files.readFile(files.pathJoin(dir, unibuildMeta.path))); - - var unibuildBasePath = - files.pathDirname(files.pathJoin(dir, unibuildMeta.path)); - - if (unibuildJson.format !== "unipackage-unibuild-pre1" && - unibuildJson.format !== "isopack-2-unibuild") { - throw new Error("Unsupported isopack unibuild format: " + - JSON.stringify(unibuildJson.format)); - } - - // Is this unibuild the legacy pre-"compiler plugin" format which contains - // "prelink" resources of pre-processed JS files (as well as the - // "packageVariables" field) instead of individual "source" resources (and - // a "declaredExports" field)? - var unibuildHasPrelink = - unibuildJson.format === "unipackage-unibuild-pre1"; - - var resources = []; - - _.each(unibuildJson.resources, function (resource) { - rejectBadPath(resource.file); - var data = files.readBufferWithLengthAndOffset( - files.pathJoin(unibuildBasePath, resource.file), - resource.length, resource.offset); - - if (resource.type === "prelink") { - if (! unibuildHasPrelink) { - throw Error("Unexpected prelink resource in " + - unibuildJson.format + " at " + dir); - } - // We found a "prelink" resource, because we're processing a package - // published with an older version of Meteor which did not create - // isopack-2 isopacks and which always preprocessed and linked all JS - // files instead of leaving that until bundle time. Let's pretend it - // was just a single js source file, but leave a "legacyPrelink" field - // on it so we can not re-link that part (and not re-analyze for - // assigned variables). - var prelinkResource = { - type: "source", - extension: "js", - data: data, - path: resource.servePath, - // It's a shame to have to calculate the hash here instead of having - // it on disk, but this only runs for legacy packages anyway. - hash: watch.sha1(data), - // Legacy prelink files definitely don't have a source processor! - // They were created by an Isobuild that didn't even know about - // source processors! - usesDefaultSourceProcessor: true, - legacyPrelink: { - packageVariables: unibuildJson.packageVariables || [] - } - }; - if (resource.sourceMap) { - rejectBadPath(resource.sourceMap); - prelinkResource.legacyPrelink.sourceMap = files.readFile( - files.pathJoin(unibuildBasePath, resource.sourceMap), 'utf8'); - } - resources.push(prelinkResource); - } else if (resource.type === "source") { - resources.push({ - type: "source", - extension: resource.extension, - usesDefaultSourceProcessor: - !! resource.usesDefaultSourceProcessor, - data: data, - path: resource.path, - hash: resource.hash, - fileOptions: resource.fileOptions - }); - } else if (_.contains(["head", "body", "css", "js", "asset"], - resource.type)) { - resources.push({ - type: resource.type, - data: data, - servePath: resource.servePath || undefined, - path: resource.path || undefined - }); - } else { - throw new Error("bad resource type in isopack: " + - JSON.stringify(resource.type)); - } - }); - - var declaredExports; - if (unibuildHasPrelink) { - // Legacy unibuild; it stores packageVariables and says some of them - // are exports. - declaredExports = []; - _.each(unibuildJson.packageVariables, function (pv) { - if (pv.export) { - declaredExports.push({ - name: pv.name, - testOnly: pv.export === 'tests' - }); - } - }); - } else { - declaredExports = unibuildJson.declaredExports || []; - } - - unibuildJson.uses && unibuildJson.uses.forEach((use) => { - if (!use.weak && compiler.isIsobuildFeaturePackage(use.package) && - self.isobuildFeatures.indexOf(use.package) === -1) { - self.isobuildFeatures.push(use.package); - } - }); - - // Rebuild binary npm packages if unibuild arch matches host arch. - const rebuildBinaries = archinfo.matches( - archinfo.host(), - unibuildMeta.arch - ); - - const nodeModulesDirectories = bundler.NodeModulesDirectory - .readDirsFromJSON(unibuildJson.node_modules, { - packageName: self.name, - sourceRoot: unibuildBasePath, - rebuildBinaries, - }); - - self.unibuilds.push(new Unibuild(self, { - // At some point we stopped writing 'kind's to the metadata file, so - // default to main. - kind: unibuildMeta.kind || 'main', + const unibuild = Unibuild.fromJSON(JSON.parse( + files.readFile(files.pathJoin(dir, unibuildMeta.path)) + ), { + isopack: self, + kind: unibuildMeta.kind, arch: unibuildMeta.arch, - uses: unibuildJson.uses, - implies: unibuildJson.implies, + unibuildBasePath: files.pathDirname( + files.pathJoin(dir, unibuildMeta.path)), watchSet: unibuildWatchSets[unibuildMeta.path], - nodeModulesDirectories, - declaredExports: declaredExports, - resources: resources - })); + }); + + if (unibuild.uses) { + unibuild.uses.forEach(use => { + if (! use.weak && + compiler.isIsobuildFeaturePackage(use.package) && + self.isobuildFeatures.indexOf(use.package) === -1) { + self.isobuildFeatures.push(use.package); + } + }); + } + + self.unibuilds.push(unibuild); }); self.cordovaDependencies = mainJson.cordovaDependencies || null; @@ -1285,8 +1086,6 @@ _.extend(Isopack.prototype, { path: unibuildJsonFile }); - var jsResourcesForLegacyPrelink = []; - // Save unibuild dependencies. Keyed by the json path rather than thinking // too hard about how to encode pair (name, arch). if (isopackBuildInfoJson) { @@ -1294,154 +1093,29 @@ _.extend(Isopack.prototype, { unibuild.watchSet.toJSON(); } - // Figure out where the npm dependencies go. - let node_modules = {}; - _.each(unibuild.nodeModulesDirectories, nmd => { - const bundlePath = _.has(npmDirsToCopy, nmd.sourcePath) - // We already have this npm directory from another unibuild. - ? npmDirsToCopy[nmd.sourcePath] - : npmDirsToCopy[nmd.sourcePath] = builder.generateFilename( - nmd.getPreferredBundlePath("isopack"), - { directory: true } - ); - node_modules[bundlePath] = nmd.toJSON(); - }); - - const preferredPaths = Object.keys(node_modules); - if (preferredPaths.length === 1) { - // For backwards compatibility, if there's only one node_modules - // directory, store it as a single string. - node_modules = preferredPaths[0]; - } - - // Construct unibuild metadata - var unibuildJson = { - format: "isopack-2-unibuild", - declaredExports: unibuild.declaredExports, - uses: _.map(unibuild.uses, function (u) { - return { - 'package': u.package, - // For cosmetic value, leave false values for these options out of - // the JSON file. - constraint: u.constraint || undefined, - unordered: u.unordered || undefined, - weak: u.weak || undefined - }; - }), - implies: (_.isEmpty(unibuild.implies) ? undefined : unibuild.implies), - resources: [] - }; - - if (preferredPaths.length > 0) { - // If there are no node_modules directories, don't confuse older - // versions of Meteor by storing an empty object. - unibuildJson.node_modules = node_modules; - } - const usesModules = ! isopackCache || isopackCache.uses(self, "modules", unibuild.arch); - // Output 'head', 'body' resources nicely - var concat = { head: [], body: [] }; - var offset = { head: 0, body: 0 }; - _.each(unibuild.resources, function (resource) { - if (_.contains(["head", "body"], resource.type)) { - if (concat[resource.type].length) { - concat[resource.type].push(Buffer.from("\n", "utf8")); - offset[resource.type]++; - } - if (! (resource.data instanceof Buffer)) { - throw new Error("Resource data must be a Buffer"); - } - - if (! usesModules && - resource.fileOptions && - resource.fileOptions.lazy) { - // Omit lazy resources from the unibuild JSON file. - return; - } - - unibuildJson.resources.push({ - type: resource.type, - file: files.pathJoin(unibuildDir, resource.type), - length: resource.data.length, - offset: offset[resource.type] - }); - - concat[resource.type].push(resource.data); - offset[resource.type] += resource.data.length; - } + const unibuildJson = unibuild.toJSON({ + builder, + unibuildDir, + usesModules, + npmDirsToCopy, }); - _.each(concat, function (parts, type) { - if (parts.length) { - builder.write(files.pathJoin(unibuildDir, type), { - data: Buffer.concat(concat[type], offset[type]) - }); - } - }); - - // Output other resources each to their own file - _.each(unibuild.resources, function (resource) { - if (_.contains(["head", "body"], resource.type)) { - // already did this one - return; - } - - const generatedFilename = - builder.writeToGeneratedFilename( - files.pathJoin(unibuildDir, - resource.servePath || resource.path), - { data: resource.data }); - - if (! usesModules && - resource.fileOptions && - resource.fileOptions.lazy) { - // Omit lazy resources from the unibuild JSON file, but only - // after they are copied into the bundle (immediately above). - return; - } - - // If we're going to write a legacy prelink file later, track the - // original form of the resource object (with the source in a Buffer, - // etc) instead of the later version. #HardcodeJs - if (writeLegacyBuilds && - resource.type === "source" && - resource.extension == "js") { - jsResourcesForLegacyPrelink.push({ - data: resource.data, - hash: resource.hash, - servePath: unibuild.pkg._getServePath(resource.path), - bare: resource.fileOptions && resource.fileOptions.bare, - sourceMap: resource.sourceMap, - // If this file was actually read from a legacy isopack and is - // itself prelinked, this will be an object with some metadata - // about it, and we can skip re-running prelink later. - legacyPrelink: resource.legacyPrelink - }); - } - - unibuildJson.resources.push({ - type: resource.type, - extension: resource.extension, - file: generatedFilename, - length: resource.data.length, - offset: 0, - usesDefaultSourceProcessor: - resource.usesDefaultSourceProcessor || undefined, - servePath: resource.servePath || undefined, - path: resource.path || undefined, - hash: resource.hash || undefined, - fileOptions: resource.fileOptions || undefined - }); - }); + // If we're going to write a legacy prelink file later, track the + // original form of the resource object (with the source in a + // Buffer, etc) instead of the later version. #HardcodeJs + const jsResourcesForLegacyPrelink = + writeLegacyBuilds ? unibuild.getLegacyJsResources() : []; // Control file for unibuild builder.writeJson(unibuildJsonFile, unibuildJson); + unibuildInfos.push({ - unibuild: unibuild, - unibuildJson: unibuildJson, - jsResourcesForLegacyPrelink: jsResourcesForLegacyPrelink + unibuild, + unibuildJson, + jsResourcesForLegacyPrelink, }); }); @@ -1700,7 +1374,9 @@ _.extend(Isopack.prototype, { 'tools', 'examples', 'LICENSE.txt', 'LICENSES', 'meteor', 'meteor.bat', 'scripts/admin/launch-meteor', 'packages/package-version-parser/package-version-parser.js', - 'packages/meteor/flush-buffers-on-exit-in-windows.js'); + 'packages/meteor/define-package.js', + 'packages/meteor/flush-buffers-on-exit-in-windows.js', + ); // Trim blank line and unnecessary examples. pathsToCopy = _.filter(pathsToCopy.split('\n'), function (f) { diff --git a/tools/isobuild/linker.js b/tools/isobuild/linker.js index 03ec054c1c..0e74142be7 100644 --- a/tools/isobuild/linker.js +++ b/tools/isobuild/linker.js @@ -245,8 +245,13 @@ _.extend(Module.prototype, { return; } + if (file.aliasId) { + addToTree(file.aliasId, file.absModuleId, tree); + return; + } + if (file.isDynamic()) { - const servePath = "dynamic/" + file.installPath; + const servePath = files.pathJoin("dynamic", file.absModuleId); const { code: source, map } = file.getPrelinkedOutput({ sourceWidth: sourceWidth, @@ -264,13 +269,14 @@ _.extend(Module.prototype, { const stubArray = file.deps.slice(0); - if (file.installPath.endsWith("/package.json") && + if (file.absModuleId.endsWith("/package.json") && file.jsonData) { const stub = {}; function tryMain(name) { const value = file.jsonData[name]; - if (_.isString(value)) { + if (_.isString(value) || + _.isObject(value)) { stub[name] = value; } } @@ -281,12 +287,12 @@ _.extend(Module.prototype, { stubArray.push(stub); } - addToTree(stubArray, file.installPath, tree); + addToTree(stubArray, file.absModuleId, tree); } else { // If the file is not dynamic, then it should be included in the // initial bundle, so we add it to the static tree. - addToTree(file, file.installPath, tree); + addToTree(file, file.absModuleId, tree); } }); @@ -310,6 +316,25 @@ _.extend(Module.prototype, { ++moduleCount; chunks.push(JSON.stringify(t, null, 2)); + } else if (typeof t === "string") { + // This case can happen if a package.json file has an + // object-valued "browser" field that aliases this module to a + // different module identifier string. Note that the runtime + // module system resolves string aliases relative to the original + // module identifier, so it's probably a good idea to make sure + // these identifiers are absolute (start with a '/') to avoid + // ambiguity, since identifiers in package.json "browser" fields + // are meant to be resolved relative to the package.json file. + ++moduleCount; + chunks.push(JSON.stringify(t)); + + } else if (t === false) { + // This case can happen if a package.json file has an + // object-valued "browser" field that maps this module to `false`, + // indicating it should be replaced by an empty stub. + ++moduleCount; + chunks.push("function(){}"); + } else if (t instanceof File) { ++moduleCount; @@ -420,7 +445,7 @@ _.extend(Module.prototype, { chunks.push( file.mainModule ? "\nvar " + exportsName + " = " : "\n", "require(", - JSON.stringify("./" + file.installPath), + JSON.stringify(file.absModuleId), ");" ); }); @@ -437,6 +462,10 @@ export function addToTree(value, path, tree) { const parts = path.split("/"); const lastIndex = parts.length - 1; parts.forEach((part, i) => { + if (part === "") { + return; + } + tree = _.has(tree, part) ? tree[part] : tree[part] = i < lastIndex ? {} : value; @@ -515,13 +544,17 @@ var File = function (inputFile, module) { self.sourcePath = inputFile.sourcePath; // Absolute module identifier to use when installing this file via - // meteorInstall. If the inputFile has no .installPath, then this file + // meteorInstall. If the inputFile has no .absModuleId, then this file // cannot be installed as a module. - self.installPath = inputFile.installPath || null; + self.absModuleId = inputFile.absModuleId || null; // the path where this file would prefer to be served if possible self.servePath = inputFile.servePath; + if (inputFile.alias) { + self.aliasId = inputFile.alias.absModuleId; + } + // Module identifiers imported or required by this module, if any. // Excludes dynamically imported dependencies, and may exclude // dependencies already included in the non-dynamic initial bundle. @@ -575,8 +608,8 @@ _.extend(File.prototype, { computeAssignedVariables: Profile("linker File#computeAssignedVariables", function () { var self = this; - if (self.installPath) { - const parts = self.installPath.split("/"); + if (self.absModuleId) { + const parts = self.absModuleId.split("/"); const nmi = parts.indexOf("node_modules"); if (nmi >= 0 && parts[nmi + 1] !== "meteor") { // If this file is in a node_modules directory and is not part of @@ -995,22 +1028,25 @@ var getFooter = function ({ if (name && exported) { chunks.push("\n\n/* Exports */\n"); - chunks.push("if (typeof Package === 'undefined') Package = {};\n"); - const pkgInit = packageDot(name) + " = " + (exportsName || "{}"); - if (_.isEmpty(exported)) { - // Even if there are no exports, we need to define Package.foo, - // because the existence of Package.foo is how another package - // (e.g., one that weakly depends on foo) can tell if foo is loaded. - chunks.push(pkgInit, ";\n"); - } else { + + // Even if there are no exports, we need to define Package.foo, + // because the existence of Package.foo is how another package + // (e.g., one that weakly depends on foo) can tell if foo is loaded. + chunks.push("Package._define(" + JSON.stringify(name)); + + if (exportsName) { + // If we have an exports object, use it as Package[name]. + chunks.push(", ", exportsName); + } + + if (! _.isEmpty(exported)) { const scratch = {}; _.each(exported, symbol => scratch[symbol] = symbol); const symbolTree = writeSymbolTree(buildSymbolTree(scratch)); - chunks.push("(function (pkg, symbols) {\n", - " for (var s in symbols)\n", - " (s in pkg) || (pkg[s] = symbols[s]);\n", - "})(", pkgInit, ", ", symbolTree, ");\n"); + chunks.push(", ", symbolTree); } + + chunks.push(");\n"); } chunks.push("\n})();\n"); diff --git a/tools/isobuild/resolver.js b/tools/isobuild/resolver.js index c498265775..17c1659fd1 100644 --- a/tools/isobuild/resolver.js +++ b/tools/isobuild/resolver.js @@ -1,5 +1,6 @@ import { isString, + isObject, isFunction, each, has, @@ -12,6 +13,7 @@ import { pathRelative, pathNormalize, pathDirname, + pathBasename, convertToOSPath, convertToPosixPath, } from "../fs/files.js"; @@ -78,6 +80,16 @@ export default class Resolver { return JSON.stringify([id, pathDirname(absParentPath)]); } }); + + this._cacheMethod("_findPkgJsonSubsetForPath"); + this._cacheMethod("_getPkgJsonSubsetForDir"); + } + + _cacheMethod(name) { + const original = this[name]; + this[name] = wrap( + (...args) => original.apply(this, args) + ); } static isTopLevel(id) { @@ -152,6 +164,18 @@ export default class Resolver { resolved.packageJsonMap = packageJsonMap; } + // If the package.json file that governs resolved.path has a + // "browser" field, include it in resolved.packageJsonMap so that + // the ImportScanner can register the appropriate browser aliases. + const pkgJsonInfo = this._findPkgJsonSubsetForPath(resolved.path); + if (pkgJsonInfo && + isObject(pkgJsonInfo.pkg.browser)) { + if (! resolved.packageJsonMap) { + resolved.packageJsonMap = Object.create(null); + } + resolved.packageJsonMap[pkgJsonInfo.path] = pkgJsonInfo.pkg; + } + resolved.id = convertToPosixPath( convertToOSPath(resolved.path), true @@ -278,6 +302,34 @@ export default class Resolver { } _resolvePkgJsonMain(dirPath, _seenDirPaths) { + const found = this._getPkgJsonSubsetForDir(dirPath); + if (! found) { + return null; + } + + if (isString(found.main)) { + // The "main" field of package.json does not have to begin with ./ + // to be considered relative, so first we try simply appending it to + // the directory path before falling back to a full resolve, which + // might return a package from a node_modules directory. + const resolved = this._joinAndStat(dirPath, found.main) || + this._resolve(found.main, found.path, _seenDirPaths); + + if (resolved && typeof resolved === "object") { + if (! resolved.packageJsonMap) { + resolved.packageJsonMap = Object.create(null); + } + + resolved.packageJsonMap[found.path] = found.pkg; + + return resolved; + } + } + + return null; + } + + _getPkgJsonSubsetForDir(dirPath) { const pkgJsonPath = pathJoin(dirPath, "package.json"); const pkg = optimisticReadJsonOrNull(pkgJsonPath); if (! pkg) { @@ -299,8 +351,13 @@ export default class Resolver { let main; function tryMain(name) { const value = pkg[name]; + if (isString(value)) { main = main || value; + } + + if (isString(value) || + isObject(value)) { pkgSubset[name] = value; } } @@ -311,26 +368,38 @@ export default class Resolver { tryMain("main"); - if (isString(main)) { - // The "main" field of package.json does not have to begin with ./ - // to be considered relative, so first we try simply appending it to - // the directory path before falling back to a full resolve, which - // might return a package from a node_modules directory. - const resolved = this._joinAndStat(dirPath, main) || - this._resolve(main, pkgJsonPath, _seenDirPaths); + return { + path: pkgJsonPath, + pkg: pkgSubset, + main, + }; + } - if (resolved && typeof resolved === "object") { - if (! resolved.packageJsonMap) { - resolved.packageJsonMap = Object.create(null); - } + _findPkgJsonSubsetForPath(path) { + const stat = this.statOrNull(path); - resolved.packageJsonMap[pkgJsonPath] = pkgSubset; + if (stat && stat.isDirectory()) { + const found = this._getPkgJsonSubsetForDir(path); + if (found) { + return found; + } - return resolved; + if (path === this.sourceRoot) { + return null; } } - return null; + const parentDir = pathDirname(path); + + if (parentDir === path) { + return null; + } + + if (pathBasename(parentDir) === "node_modules") { + return null; + } + + return this._findPkgJsonSubsetForPath(parentDir); } }; diff --git a/tools/isobuild/unibuild.js b/tools/isobuild/unibuild.js new file mode 100644 index 0000000000..58361331ac --- /dev/null +++ b/tools/isobuild/unibuild.js @@ -0,0 +1,383 @@ +"use strict"; + +import _ from "underscore"; +import files from "../fs/files.js"; +import { WatchSet, sha1 } from "../fs/watch.js"; +import { NodeModulesDirectory } from "./bundler.js"; +import * as archinfo from "../utils/archinfo.js"; + +function rejectBadPath(p) { + if (p.indexOf("..") >= 0) { + throw new Error("bad path: " + p); + } +} + +let nextBuildId = 1; + +export class Unibuild { + constructor(isopack, { + kind, // required (main/plugin/app) + arch, // required + uses, + implies, + watchSet, + nodeModulesDirectories, + declaredExports, + resources, + }) { + this.pkg = isopack; + this.kind = kind; + this.arch = arch; + this.uses = uses; + this.implies = implies || []; + + // This WatchSet will end up having the watch items from the + // SourceArch (such as package.js or .meteor/packages), plus all of + // the actual source files for the unibuild (including items that we + // looked at to find the source files, such as directories we + // scanned). + this.watchSet = watchSet || new WatchSet(); + + // Each Unibuild is given a unique id when it's loaded (it is not + // saved to disk). This is just a convenience to make it easier to + // keep track of Unibuilds in a map; it's used by bundler and + // compiler. We put some human readable info in here too to make + // debugging easier. + this.id = this.pkg.name + "." + this.kind + "@" + this.arch + "#" + + (nextBuildId ++); + + // 'declaredExports' are the variables which are exported from this + // package. A list of objects with keys 'name' (required) and + // 'testOnly' (boolean, defaults to false). + this.declaredExports = declaredExports; + + // All of the data provided for eventual inclusion in the bundle, + // other than JavaScript that still needs to be fed through the final + // link stage. A list of objects with these keys: + // + // type: "source", "head", "body", "asset". (resources produced by + // legacy source handlers can also be "js" or "css". + // + // data: The contents of this resource, as a Buffer. For example, for + // "head", the data to insert in ; for "js", the JavaScript + // source code (which may be subject to further processing such as + // minification); for "asset", the contents of a static resource such + // as an image. + // + // servePath: The (absolute) path at which the resource would prefer + // to be served. Interpretation varies by type. For example, always + // honored for "asset", ignored for "head" and "body", sometimes + // honored for CSS but ignored if we are concatenating. + // + // sourceMap: Allowed only for "js". If present, a string. + // + // fileOptions: for "source", the options passed to `api.addFiles`. + // plugin-specific. + // + // extension: for "source", the file extension that this matched + // against at build time. null if matched against a specific filename. + this.resources = resources; + + // Map from absolute paths of node_modules directories to + // NodeModulesDirectory objects. + this.nodeModulesDirectories = nodeModulesDirectories; + + // Provided for backwards compatibility; please use + // unibuild.nodeModulesDirectories instead! + _.some(this.nodeModulesDirectories, (nmd, nodeModulesPath) => { + if (! nmd.local) { + this.nodeModulesPath = nodeModulesPath; + return true; + } + }); + } + + static fromJSON(unibuildJson, { + isopack, + // At some point we stopped writing 'kind's to the metadata file, so + // default to main. + kind = "main", + arch, + unibuildBasePath, + watchSet, + }) { + if (unibuildJson.format !== "unipackage-unibuild-pre1" && + unibuildJson.format !== "isopack-2-unibuild") { + throw new Error("Unsupported isopack unibuild format: " + + JSON.stringify(unibuildJson.format)); + } + + // Is this unibuild the legacy pre-"compiler plugin" format which contains + // "prelink" resources of pre-processed JS files (as well as the + // "packageVariables" field) instead of individual "source" resources (and + // a "declaredExports" field)? + const unibuildHasPrelink = + unibuildJson.format === "unipackage-unibuild-pre1"; + + const resources = []; + + _.each(unibuildJson.resources, function (resource) { + rejectBadPath(resource.file); + + const data = files.readBufferWithLengthAndOffset( + files.pathJoin(unibuildBasePath, resource.file), + resource.length, + resource.offset, + ); + + if (resource.type === "prelink") { + if (! unibuildHasPrelink) { + throw Error("Unexpected prelink resource in " + + unibuildJson.format + " at " + unibuildBasePath); + } + + // We found a "prelink" resource, because we're processing a package + // published with an older version of Meteor which did not create + // isopack-2 isopacks and which always preprocessed and linked all JS + // files instead of leaving that until bundle time. Let's pretend it + // was just a single js source file, but leave a "legacyPrelink" field + // on it so we can not re-link that part (and not re-analyze for + // assigned variables). + const prelinkResource = { + type: "source", + extension: "js", + data: data, + path: resource.servePath, + // It's a shame to have to calculate the hash here instead of having + // it on disk, but this only runs for legacy packages anyway. + hash: sha1(data), + // Legacy prelink files definitely don't have a source processor! + // They were created by an Isobuild that didn't even know about + // source processors! + usesDefaultSourceProcessor: true, + legacyPrelink: { + packageVariables: unibuildJson.packageVariables || [] + } + }; + + if (resource.sourceMap) { + rejectBadPath(resource.sourceMap); + prelinkResource.legacyPrelink.sourceMap = files.readFile( + files.pathJoin(unibuildBasePath, resource.sourceMap), 'utf8'); + } + + resources.push(prelinkResource); + + } else if (resource.type === "source") { + resources.push({ + type: "source", + extension: resource.extension, + usesDefaultSourceProcessor: + !! resource.usesDefaultSourceProcessor, + data: data, + path: resource.path, + hash: resource.hash, + fileOptions: resource.fileOptions + }); + + } else if (_.contains(["head", "body", "css", "js", "asset"], + resource.type)) { + resources.push({ + type: resource.type, + data: data, + servePath: resource.servePath || undefined, + path: resource.path || undefined + }); + + } else { + throw new Error("bad resource type in isopack: " + + JSON.stringify(resource.type)); + } + }); + + let declaredExports = unibuildJson.declaredExports || []; + + if (unibuildHasPrelink) { + // Legacy unibuild; it stores packageVariables and says some of them + // are exports. + declaredExports = []; + + _.each(unibuildJson.packageVariables, function (pv) { + if (pv.export) { + declaredExports.push({ + name: pv.name, + testOnly: pv.export === "tests", + }); + } + }); + } + + const nodeModulesDirectories = + NodeModulesDirectory.readDirsFromJSON(unibuildJson.node_modules, { + packageName: isopack.name, + sourceRoot: unibuildBasePath, + // Rebuild binary npm packages if unibuild arch matches host arch. + rebuildBinaries: archinfo.matches(archinfo.host(), arch) + }); + + return new this(isopack, { + kind, + arch, + uses: unibuildJson.uses, + implies: unibuildJson.implies, + watchSet, + nodeModulesDirectories, + declaredExports: declaredExports, + resources: resources, + }); + } + + toJSON({ + builder, + unibuildDir, + usesModules, + npmDirsToCopy, + }) { + const unibuild = this; + const unibuildJson = { + format: "isopack-2-unibuild", + declaredExports: unibuild.declaredExports, + uses: _.map(unibuild.uses, u => ({ + 'package': u.package, + // For cosmetic value, leave false values for these options out of + // the JSON file. + constraint: u.constraint || undefined, + unordered: u.unordered || undefined, + weak: u.weak || undefined, + })), + implies: (_.isEmpty(unibuild.implies) ? undefined : unibuild.implies), + resources: [], + }; + + // Figure out where the npm dependencies go. + let node_modules = {}; + _.each(unibuild.nodeModulesDirectories, nmd => { + const bundlePath = _.has(npmDirsToCopy, nmd.sourcePath) + // We already have this npm directory from another unibuild. + ? npmDirsToCopy[nmd.sourcePath] + : npmDirsToCopy[nmd.sourcePath] = builder.generateFilename( + nmd.getPreferredBundlePath("isopack"), + { directory: true } + ); + node_modules[bundlePath] = nmd.toJSON(); + }); + + const preferredPaths = Object.keys(node_modules); + if (preferredPaths.length === 1) { + // For backwards compatibility, if there's only one node_modules + // directory, store it as a single string. + node_modules = preferredPaths[0]; + } + + if (preferredPaths.length > 0) { + // If there are no node_modules directories, don't confuse older + // versions of Meteor by storing an empty object. + unibuildJson.node_modules = node_modules; + } + + // Output 'head', 'body' resources nicely + const concat = { head: [], body: [] }; + const offset = { head: 0, body: 0 }; + + _.each(unibuild.resources, function (resource) { + if (_.contains(["head", "body"], resource.type)) { + if (concat[resource.type].length) { + concat[resource.type].push(Buffer.from("\n", "utf8")); + offset[resource.type]++; + } + if (! (resource.data instanceof Buffer)) { + throw new Error("Resource data must be a Buffer"); + } + + if (! usesModules && + resource.fileOptions && + resource.fileOptions.lazy) { + // Omit lazy resources from the unibuild JSON file. + return; + } + + unibuildJson.resources.push({ + type: resource.type, + file: files.pathJoin(unibuildDir, resource.type), + length: resource.data.length, + offset: offset[resource.type] + }); + + concat[resource.type].push(resource.data); + offset[resource.type] += resource.data.length; + } + }); + + _.each(concat, function (parts, type) { + if (parts.length) { + builder.write(files.pathJoin(unibuildDir, type), { + data: Buffer.concat(concat[type], offset[type]) + }); + } + }); + + // Output other resources each to their own file + _.each(unibuild.resources, function (resource) { + if (_.contains(["head", "body"], resource.type)) { + // already did this one + return; + } + + const generatedFilename = + builder.writeToGeneratedFilename( + files.pathJoin( + unibuildDir, + resource.servePath || resource.path, + ), + { data: resource.data } + ); + + if (! usesModules && + resource.fileOptions && + resource.fileOptions.lazy) { + // Omit lazy resources from the unibuild JSON file, but only after + // they are copied into the bundle (immediately above). + return; + } + + unibuildJson.resources.push({ + type: resource.type, + extension: resource.extension, + file: generatedFilename, + length: resource.data.length, + offset: 0, + usesDefaultSourceProcessor: + resource.usesDefaultSourceProcessor || undefined, + servePath: resource.servePath || undefined, + path: resource.path || undefined, + hash: resource.hash || undefined, + fileOptions: resource.fileOptions || undefined + }); + }); + + return unibuildJson; + } + + getLegacyJsResources() { + const legacyJsResources = []; + + this.resources.forEach(resource => { + if (resource.type === "source" && + resource.extension === "js") { + legacyJsResources.push({ + data: resource.data, + hash: resource.hash, + servePath: this.pkg._getServePath(resource.path), + bare: resource.fileOptions && resource.fileOptions.bare, + sourceMap: resource.sourceMap, + // If this file was actually read from a legacy isopack and is + // itself prelinked, this will be an object with some metadata + // about it, and we can skip re-running prelink later. + legacyPrelink: resource.legacyPrelink + }); + } + }); + + return legacyJsResources; + } +} diff --git a/tools/static-assets/server/boot.js b/tools/static-assets/server/boot.js index 422e3e2818..fc4306a4a0 100644 --- a/tools/static-assets/server/boot.js +++ b/tools/static-assets/server/boot.js @@ -2,7 +2,6 @@ var Fiber = require("fibers"); var fs = require("fs"); var path = require("path"); var Future = require("fibers/future"); -var _ = require('underscore'); var sourcemap_support = require('source-map-support'); var bootUtils = require('./boot-utils.js'); @@ -103,7 +102,7 @@ function maybeWaitForDebuggerToAttach() { } // Read all the source maps into memory once. -_.each(serverJson.load, function (fileInfo) { +serverJson.load.forEach(function (fileInfo) { if (fileInfo.sourceMap) { var rawSourceMap = fs.readFileSync( path.resolve(serverDir, fileInfo.sourceMap), 'utf8'); @@ -123,11 +122,12 @@ _.each(serverJson.load, function (fileInfo) { } }); -var retrieveSourceMap = function (pathForSourceMap) { - if (_.has(parsedSourceMaps, pathForSourceMap)) +function retrieveSourceMap(pathForSourceMap) { + if (hasOwn.call(parsedSourceMaps, pathForSourceMap)) { return { map: parsedSourceMaps[pathForSourceMap] }; + } return null; -}; +} var origWrapper = sourcemap_support.wrapCallSite; var wrapCallSite = function (frame) { @@ -217,7 +217,7 @@ var specialArgPaths = { var loadServerBundles = Profile("Load server bundles", function () { var infos = []; - _.each(serverJson.load, function (fileInfo) { + serverJson.load.forEach(function (fileInfo) { var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path)); var nonLocalNodeModulesPaths = []; @@ -230,7 +230,8 @@ var loadServerBundles = Profile("Load server bundles", function () { if (typeof fileInfo.node_modules === "string") { addNodeModulesPath(fileInfo.node_modules); } else if (fileInfo.node_modules) { - _.each(fileInfo.node_modules, function (info, path) { + Object.keys(fileInfo.node_modules).forEach(function (path) { + const info = fileInfo.node_modules[path]; if (! info.local) { addNodeModulesPath(path); } @@ -324,7 +325,7 @@ var loadServerBundles = Profile("Load server bundles", function () { // using this string elsewhere. assetPath = files.unicodeNormalizePath(assetPath); - if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) { + if (! fileInfo.assets || ! hasOwn.call(fileInfo.assets, assetPath)) { _callback(new Error("Unknown asset: " + assetPath)); } else { var filePath = path.join(serverDir, fileInfo.assets[assetPath]); @@ -352,7 +353,7 @@ var loadServerBundles = Profile("Load server bundles", function () { // using this string elsewhere. assetPath = files.unicodeNormalizePath(assetPath); - if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) { + if (! fileInfo.assets || ! hasOwn.call(fileInfo.assets, assetPath)) { throw new Error("Unknown asset: " + assetPath); } @@ -435,11 +436,15 @@ var runMain = Profile("Run main()", function () { mains.push(main); globalMain = main; } - typeof Package !== 'undefined' && _.each(Package, function (p, n) { - if ('main' in p && p.main !== globalMain) { - mains.push(p.main); - } - }); + if (typeof Package !== "undefined") { + Object.keys(Package).forEach(function (name) { + const { main } = Package[name]; + if (typeof main === "function" && + main !== globalMain) { + mains.push(main); + } + }); + } if (! mains.length) { process.stderr.write("Program has no main() function.\n"); process.exit(1); diff --git a/tools/tests/apps/dynamic-import/.meteor/packages b/tools/tests/apps/dynamic-import/.meteor/packages index 551d7c0b4c..be3334ddb3 100644 --- a/tools/tests/apps/dynamic-import/.meteor/packages +++ b/tools/tests/apps/dynamic-import/.meteor/packages @@ -4,25 +4,23 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -meteor-base@1.1.0 # Packages every Meteor app needs to have +meteor-base@1.2.0 # Packages every Meteor app needs to have mobile-experience@1.0.5 # Packages for a great mobile UX -mongo@1.2.2 # The database Meteor supports right now +mongo@1.3.0 # The database Meteor supports right now blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views reactive-var@1.0.11 # Reactive variable for tracker jquery@1.11.10 # Helpful client-side library tracker@1.1.3 # Meteor's client-side reactive programming library standard-minifier-css@1.3.5 # CSS minifier run for production mode -standard-minifier-js@2.1.2 # JS minifier run for production mode +standard-minifier-js@2.2.0 # JS minifier run for production mode es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers. -ecmascript@0.8.2 # Enable ECMAScript2015+ syntax in app code -shell-server@0.2.4 # Server-side component of the `meteor shell` command +ecmascript@0.9.0 # Enable ECMAScript2015+ syntax in app code +shell-server@0.3.0 # Server-side component of the `meteor shell` command autopublish@1.0.7 # Publish all data to the clients (for prototyping) insecure@1.0.7 # Allow all DB writes from clients (for prototyping) -dynamic-import@0.1.3 -dispatch:mocha-phantomjs -dispatch:mocha-browser +dynamic-import@0.2.0 lazy-test-package helper-package user:colon-name diff --git a/tools/tests/apps/dynamic-import/.meteor/release b/tools/tests/apps/dynamic-import/.meteor/release index ca6b9a2768..0fa8d22dda 100644 --- a/tools/tests/apps/dynamic-import/.meteor/release +++ b/tools/tests/apps/dynamic-import/.meteor/release @@ -1 +1 @@ -METEOR@1.5.2.1 +METEOR@1.6 diff --git a/tools/tests/apps/dynamic-import/package-lock.json b/tools/tests/apps/dynamic-import/package-lock.json new file mode 100644 index 0000000000..21dd49639d --- /dev/null +++ b/tools/tests/apps/dynamic-import/package-lock.json @@ -0,0 +1,859 @@ +{ + "name": "dynamic-import", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "arson": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/arson/-/arson-0.2.5.tgz", + "integrity": "sha1-KlW7D62x4zHTM6veYnn2eCdie6M=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "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=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + } + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.3" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "meteor-node-stubs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.3.2.tgz", + "integrity": "sha512-l93SS/HutbqBRJODO2m7hup8cYI2acF5bB39+ZvP2BX8HMmCSCXeFH7v0sr4hD7zrVvHQA5UqS0pcDYKn0VM6g==", + "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.1", + "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.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "git+https://github.com/meteor/readable-stream.git#d64a64aa6061b9b6855feff4d09e58fb3b2e4502", + "stream-browserify": "2.0.1", + "string_decoder": "1.0.3", + "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.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "requires": { + "bn.js": "4.11.8", + "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": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.0.tgz", + "integrity": "sha512-W2bIMLYoZ9oow7TyePpMJk9l9LY7O3R61a/68bVCDOtnJynnwe3ZeW2IzzSkrQnPKNdJrxVDn3ALZNisSBwb7g==", + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.1", + "safe-buffer": "5.1.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.1.0", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.3" + } + }, + "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.4", + "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.8", + "randombytes": "2.0.5" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "elliptic": "6.4.0", + "inherits": "2.0.1", + "parse-asn1": "5.1.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.1", + "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.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.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.8", + "elliptic": "6.4.0" + } + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.1", + "ripemd160": "2.0.1", + "sha.js": "2.4.9" + } + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "inherits": "2.0.1", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.9" + } + }, + "crypto-browserify": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "diffie-hellman": "5.0.2", + "inherits": "2.0.1", + "pbkdf2": "3.0.14", + "public-encrypt": "4.0.0", + "randombytes": "2.0.5" + } + }, + "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.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.5" + } + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.1", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.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.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "1.3.4", + "safe-buffer": "5.1.1" + } + }, + "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-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "requires": { + "inherits": "2.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.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=" + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.1" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.1" + } + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "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.8" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "requires": { + "asn1.js": "4.9.1", + "browserify-aes": "1.1.0", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.14" + } + }, + "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.14", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "requires": { + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.9" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "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.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "parse-asn1": "5.1.0", + "randombytes": "2.0.5" + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "readable-stream": { + "version": "git+https://github.com/meteor/readable-stream.git#d64a64aa6061b9b6855feff4d09e58fb3b2e4502", + "requires": { + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "requires": { + "hash-base": "2.0.2", + "inherits": "2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sha.js": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", + "integrity": "sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A==", + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.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#d64a64aa6061b9b6855feff4d09e58fb3b2e4502" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.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.10" + } + }, + "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=" + } + } + }, + "moment": { + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz", + "integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc=" + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "optimism": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.3.3.tgz", + "integrity": "sha1-BjDrmir90bGLBS5MmOEplL6U3xI=", + "requires": { + "lru-cache": "4.0.2" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } + }, + "prop-types": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", + "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "requires": { + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "react": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.0.0.tgz", + "integrity": "sha1-zn348ZQbA28Cssyp29DLHw6FXi0=", + "requires": { + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.6.0" + } + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "ua-parser-js": { + "version": "0.7.17", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", + "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } +} diff --git a/tools/tests/apps/dynamic-import/package.json b/tools/tests/apps/dynamic-import/package.json index e27a40748c..8cb61383dd 100644 --- a/tools/tests/apps/dynamic-import/package.json +++ b/tools/tests/apps/dynamic-import/package.json @@ -13,6 +13,7 @@ "moment": "^2.17.1", "optimism": "^0.3.3", "private": "^0.1.7", - "react": "^15.4.2" + "react": "^16.0.0", + "uuid": "^3.1.0" } } diff --git a/tools/tests/apps/dynamic-import/tests.js b/tools/tests/apps/dynamic-import/tests.js index 75d4f51ecd..6eb32ecdea 100644 --- a/tools/tests/apps/dynamic-import/tests.js +++ b/tools/tests/apps/dynamic-import/tests.js @@ -71,7 +71,7 @@ describe("dynamic import(...)", function () { import("react") ]).then(([{ name }, React]) => { assert.strictEqual(name, "react"); - assert.strictEqual(typeof React.createClass, "function"); + assert.strictEqual(typeof React.createElement, "function"); assertDeepEqual(React, require("re" + "act")); }); }); @@ -229,6 +229,24 @@ describe("dynamic import(...)", function () { assert.strictEqual(typeof m.wrap, "function"); }); }); + + it('should support object-valued package.json "browser" fields', () => { + return import("uuid").then(({ default: uuid }) => { + const id = uuid(); + assert.strictEqual(typeof id, "string"); + assert.strictEqual(id.split("-").length, 5); + + if (Meteor.isClient) { + assert.strictEqual( + require.resolve("uuid/lib/rng.js"), + "/node_modules/uuid/lib/rng-browser.js" + ); + const uuidPkgJsonId = ["uuid", "package.json"].join("/"); + const { browser } = require(uuidPkgJsonId); + assert.strictEqual(typeof browser, "object"); + } + }); + }); }); function maybeClearDynamicImportCache() { diff --git a/tools/tests/apps/modules/.meteor/packages b/tools/tests/apps/modules/.meteor/packages index cd1cbf9f04..696f418ae6 100644 --- a/tools/tests/apps/modules/.meteor/packages +++ b/tools/tests/apps/modules/.meteor/packages @@ -4,24 +4,22 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -meteor-base@1.1.0 # Packages every Meteor app needs to have +meteor-base@1.2.0 # Packages every Meteor app needs to have mobile-experience@1.0.5 # Packages for a great mobile UX -mongo@1.2.2 # The database Meteor supports right now +mongo@1.3.0 # The database Meteor supports right now blaze-html-templates # Compile .html files into Meteor Blaze views session@1.1.7 # Client-side reactive dictionary for your app jquery@1.11.10 # Helpful client-side library tracker@1.1.3 # Meteor's client-side reactive programming library es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers. -ecmascript@0.8.3 # Enable ECMAScript2015+ syntax in app code +ecmascript@0.9.0 # Enable ECMAScript2015+ syntax in app code coffeescript modules-test-package -dispatch:mocha-phantomjs -dispatch:mocha-browser standard-minifier-css@1.3.5 -standard-minifier-js@2.1.2 +standard-minifier-js@2.2.0 client-only-ecmascript modules-test-plugin -shell-server@0.2.4 -dynamic-import@0.1.3 +shell-server@0.3.0 +dynamic-import@0.2.0 diff --git a/tools/tests/apps/modules/.meteor/release b/tools/tests/apps/modules/.meteor/release index 099d5b9c0e..0fa8d22dda 100644 --- a/tools/tests/apps/modules/.meteor/release +++ b/tools/tests/apps/modules/.meteor/release @@ -1 +1 @@ -METEOR@1.5.2.2 +METEOR@1.6 diff --git a/tools/tests/apps/modules/package-lock.json b/tools/tests/apps/modules/package-lock.json index 5747186120..aa61871fd8 100644 --- a/tools/tests/apps/modules/package-lock.json +++ b/tools/tests/apps/modules/package-lock.json @@ -14,9 +14,9 @@ "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" }, "aws-sdk": { - "version": "2.135.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.135.0.tgz", - "integrity": "sha1-gfSke5khLy8ja/WxGws6OgIIbbQ=", + "version": "2.145.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.145.0.tgz", + "integrity": "sha1-CFu0VTIx3v2TuWsNlQI3F8nDwJM=", "requires": { "buffer": "4.9.1", "crypto-browserify": "1.0.9", diff --git a/tools/tests/apps/modules/package.json b/tools/tests/apps/modules/package.json index e79595cbdc..66982c1023 100644 --- a/tools/tests/apps/modules/package.json +++ b/tools/tests/apps/modules/package.json @@ -14,6 +14,7 @@ "mssql": "^3.1.1", "regenerator-runtime": "^0.9.5", "stripe": "^4.4.0", + "uuid": "^3.1.0", "winston": "^2.3.1" }, "scripts": { diff --git a/tools/tests/apps/modules/tests.js b/tools/tests/apps/modules/tests.js index 462277b4df..cd13ed2787 100644 --- a/tools/tests/apps/modules/tests.js +++ b/tools/tests/apps/modules/tests.js @@ -286,6 +286,23 @@ describe("local node_modules", () => { const pkg = require("moment/package.json"); assert.strictEqual(pkg.version, "2.11.1"); }); + + it('should support object-valued package.json "browser" fields', () => { + const uuid = require("uuid"); + const id = uuid(); + assert.strictEqual(typeof id, "string"); + assert.strictEqual(id.split("-").length, 5); + + if (Meteor.isClient) { + assert.strictEqual( + require.resolve("uuid/lib/rng.js"), + "/node_modules/uuid/lib/rng-browser.js" + ); + + const { browser } = require(["uuid", "package.json"].join("/")); + assert.strictEqual(typeof browser, "object"); + } + }); }); describe("Meteor packages", () => { diff --git a/tools/tests/command-line.js b/tools/tests/command-line.js index f73a994e14..08fa71d5f3 100644 --- a/tools/tests/command-line.js +++ b/tools/tests/command-line.js @@ -367,8 +367,8 @@ selftest.define("argument parsing", function () { }); s.cd("app-with-extra-packages", function () { run = s.run("test", - "--extra-packages", "practicalmeteor:mocha, extra-package-1, extra-package-2@=0.0.2", - "--driver-package", "practicalmeteor:mocha"); + "--extra-packages", "tmeasday:acceptance-test-driver, extra-package-1, extra-package-2@=0.0.2", + "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(60); run.match("extra-package-1: foobar"); run.match("extra-package-2: barfoo"); diff --git a/tools/tests/test-modes.js b/tools/tests/test-modes.js index b7bfa213e7..9e7fd238f2 100644 --- a/tools/tests/test-modes.js +++ b/tools/tests/test-modes.js @@ -7,23 +7,24 @@ selftest.define("'meteor test --port' accepts/rejects proper values", function ( s.createApp("myapp", "standard-app"); s.cd("myapp"); + s.set("") - var runAddPackage = s.run("add", "practicalmeteor:mocha"); + var runAddPackage = s.run("add", "tmeasday:acceptance-test-driver"); runAddPackage.waitSecs(30); - runAddPackage.match(/practicalmeteor:mocha\b.*?added/) + runAddPackage.match(/tmeasday:acceptance-test-driver\b.*?added/) runAddPackage.expectExit(0); - run = s.run("test", "--port", "3700", "--driver-package", "practicalmeteor:mocha"); + run = s.run("test", "--port", "3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(120); run.match('App running at: http://localhost:3700/'); run.stop(); - run = s.run("test", "--port", "127.0.0.1:3700", "--driver-package", "practicalmeteor:mocha"); + run = s.run("test", "--port", "127.0.0.1:3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(120); run.match('App running at: http://127.0.0.1:3700/'); run.stop(); - - run = s.run("test", "--port", "[::]:3700", "--driver-package", "practicalmeteor:mocha"); + + run = s.run("test", "--port", "[::]:3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(120); run.match('App running at: http://[::]:3700/'); run.stop();