diff --git a/.babelrc.js b/.babelrc.js index 32c0360eac..ef46ad4e72 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -15,5 +15,41 @@ module.exports = { mjs: { presets: [['@babel/preset-env', { modules: false }]], }, + esm: { + presets: [['@babel/preset-env', { modules: false }]], + plugins: [ + function({ types }) { + return { + visitor: { + ImportDeclaration: function(path, state) { + if (!path.node.source) return; + var source = path.node.source.value; + if (source.match(/^\.{0,2}\//) && !source.endsWith('.es.js')) { + path.replaceWith( + types.importDeclaration( + path.node.specifiers, + types.stringLiteral(source + '.es.js'), + ), + ); + } + }, + ExportNamedDeclaration: function(path, state) { + if (!path.node.source) return; + const source = path.node.source.value; + if (source.match(/^\.{0,2}\//) && !source.endsWith('.es.js')) { + path.replaceWith( + types.exportNamedDeclaration( + path.node.declaration, + path.node.specifiers, + types.stringLiteral(source + '.es.js'), + ), + ); + } + }, + }, + }; + }, + ], + }, }, }; diff --git a/resources/build.js b/resources/build.js index c1cc17a8de..bcbe830396 100644 --- a/resources/build.js +++ b/resources/build.js @@ -101,6 +101,7 @@ function buildJSFile(filepath) { copyFile(srcPath, destPath + '.flow'); writeFile(destPath, babelBuild(srcPath, 'cjs')); writeFile(destPath.replace(/\.js$/, '.mjs'), babelBuild(srcPath, 'mjs')); + writeFile(destPath.replace(/\.js$/, '.es.js'), babelBuild(srcPath, 'esm')); } function buildPackageJSON() { diff --git a/src/execution/execute.js b/src/execution/execute.js index 8059719508..f6d5e18cfb 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -1,6 +1,6 @@ // @flow strict -import { forEach, isCollection } from 'iterall'; +import { forEach, isCollection } from '../jsutils/iterall'; import inspect from '../jsutils/inspect'; import memoize3 from '../jsutils/memoize3'; diff --git a/src/index.js b/src/index.js index 4c1945ec56..c266250114 100644 --- a/src/index.js +++ b/src/index.js @@ -125,7 +125,7 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, -} from './type'; +} from './type/index'; export type { GraphQLType, @@ -168,7 +168,7 @@ export type { GraphQLScalarSerializer, GraphQLScalarValueParser, GraphQLScalarLiteralParser, -} from './type'; +} from './type/index'; // Parse and operate on GraphQL language source files. export { @@ -204,7 +204,7 @@ export { isTypeDefinitionNode, isTypeSystemExtensionNode, isTypeExtensionNode, -} from './language'; +} from './language/index'; export type { ParseOptions, @@ -276,7 +276,7 @@ export type { UnionTypeExtensionNode, EnumTypeExtensionNode, InputObjectTypeExtensionNode, -} from './language'; +} from './language/index'; // Execute GraphQL queries. export { @@ -285,12 +285,12 @@ export { defaultTypeResolver, responsePathAsArray, getDirectiveValues, -} from './execution'; +} from './execution/index'; -export type { ExecutionArgs, ExecutionResult } from './execution'; +export type { ExecutionArgs, ExecutionResult } from './execution/index'; -export { subscribe, createSourceEventStream } from './subscription'; -export type { SubscriptionArgs } from './subscription'; +export { subscribe, createSourceEventStream } from './subscription/index'; +export type { SubscriptionArgs } from './subscription/index'; // Validate GraphQL documents. export { @@ -324,9 +324,9 @@ export { ValuesOfCorrectTypeRule, VariablesAreInputTypesRule, VariablesInAllowedPositionRule, -} from './validation'; +} from './validation/index'; -export type { ValidationRule } from './validation'; +export type { ValidationRule } from './validation/index'; // Create, format, and print GraphQL errors. export { @@ -335,9 +335,9 @@ export { locatedError, printError, formatError, -} from './error'; +} from './error/index'; -export type { GraphQLFormattedError } from './error'; +export type { GraphQLFormattedError } from './error/index'; // Utilities for operating on GraphQL type schema and parsed sources. export { @@ -406,7 +406,7 @@ export { findDangerousChanges, // Report all deprecated usage within a GraphQL document. findDeprecatedUsages, -} from './utilities'; +} from './utilities/index'; export type { IntrospectionOptions, @@ -434,4 +434,4 @@ export type { BuildSchemaOptions, BreakingChange, DangerousChange, -} from './utilities'; +} from './utilities/index'; diff --git a/src/jsutils/instanceOf.js b/src/jsutils/instanceOf.js index 967969ab21..aac7b9d93e 100644 --- a/src/jsutils/instanceOf.js +++ b/src/jsutils/instanceOf.js @@ -11,7 +11,8 @@ declare function instanceOf( // See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production // See: https://webpack.js.org/guides/production/ -export default process.env.NODE_ENV === 'production' +export default typeof process !== 'undefined' && +process.env.NODE_ENV === 'production' ? // eslint-disable-next-line no-shadow function instanceOf(value: mixed, constructor: mixed) { return value instanceof constructor; diff --git a/src/jsutils/iterall.js b/src/jsutils/iterall.js new file mode 100644 index 0000000000..736792731c --- /dev/null +++ b/src/jsutils/iterall.js @@ -0,0 +1,168 @@ +// @flow strict + +const SYMBOL = typeof Symbol === 'function' ? Symbol : undefined; +const SYMBOL_ITERATOR = SYMBOL && SYMBOL.iterator; +const SYMBOL_ASYNC_ITERATOR = SYMBOL && SYMBOL.asyncIterator; + +function AsyncFromSyncIterator(iterator) { + this._i = iterator; +} + +AsyncFromSyncIterator.prototype[$$asyncIterator] = function() { + return this; +}; + +AsyncFromSyncIterator.prototype.next = function() { + const step = this._i.next(); + return Promise.resolve(step.value).then(value => ({ + value, + done: step.done, + })); +}; + +function ArrayLikeIterator(obj) { + this._o = obj; + this._i = 0; +} + +ArrayLikeIterator.prototype[$$iterator] = function() { + return this; +}; + +ArrayLikeIterator.prototype.next = function() { + if (this._o === undefined || this._i >= this._o.length) { + this._o = undefined; + return { value: undefined, done: true }; + } + return { value: this._o[this._i++], done: false }; +}; + +export const $$iterator = SYMBOL_ITERATOR || '@@iterator'; + +export function isIterable(obj) { + return Boolean(getIteratorMethod(obj)); +} + +export function isArrayLike(obj) { + const length = obj != null && obj.length; + return typeof length === 'number' && length >= 0 && length % 1 === 0; +} + +export function isCollection(obj) { + return Object(obj) === obj && (isArrayLike(obj) || isIterable(obj)); +} + +export function getIterator(iterable) { + const method = getIteratorMethod(iterable); + if (method) { + return method.call(iterable); + } +} + +export function getIteratorMethod(iterable) { + if (iterable != null) { + const method = + (SYMBOL_ITERATOR && iterable[SYMBOL_ITERATOR]) || iterable['@@iterator']; + if (typeof method === 'function') { + return method; + } + } +} + +export function createIterator(collection) { + if (collection != null) { + const iterator = getIterator(collection); + if (iterator) { + return iterator; + } + if (isArrayLike(collection)) { + return new ArrayLikeIterator(collection); + } + } +} + +export function forEach(collection, callback, thisArg) { + if (collection != null) { + if (typeof collection.forEach === 'function') { + return collection.forEach(callback, thisArg); + } + let i = 0; + const iterator = getIterator(collection); + if (iterator) { + let step; + while (!(step = iterator.next()).done) { + callback.call(thisArg, step.value, i++, collection); + if (i > 9999999) { + throw new TypeError('Near-infinite iteration.'); + } + } + } else if (isArrayLike(collection)) { + for (; i < collection.length; i++) { + if (Object.prototype.hasOwnProperty.call(collection, i)) { + callback.call(thisArg, collection[i], i, collection); + } + } + } + } +} + +export const $$asyncIterator = SYMBOL_ASYNC_ITERATOR || '@@asyncIterator'; + +export const isAsyncIterable = obj => Boolean(getAsyncIteratorMethod(obj)); + +export const getAsyncIterator = asyncIterable => { + const method = getAsyncIteratorMethod(asyncIterable); + if (method) { + return method.call(asyncIterable); + } +}; + +export const getAsyncIteratorMethod = asyncIterable => { + if (asyncIterable != null) { + const method = + (SYMBOL_ASYNC_ITERATOR && asyncIterable[SYMBOL_ASYNC_ITERATOR]) || + asyncIterable['@@asyncIterator']; + if (typeof method === 'function') { + return method; + } + } +}; + +export const createAsyncIterator = source => { + if (source != null) { + const asyncIterator = getAsyncIterator(source); + if (asyncIterator) { + return asyncIterator; + } + const iterator = createIterator(source); + if (iterator) { + return new AsyncFromSyncIterator(iterator); + } + } +}; + +export const forAwaitEach = (source, callback, thisArg) => { + const asyncIterator = createAsyncIterator(source); + if (asyncIterator) { + let i = 0; + return new Promise((resolve, reject) => { + function next() { + asyncIterator + .next() + .then(step => { + if (!step.done) { + Promise.resolve(callback.call(thisArg, step.value, i++, source)) + .then(next) + .catch(reject); + } else { + resolve(); + } + return null; + }) + .catch(reject); + return null; + } + next(); + }); + } +}; diff --git a/src/subscription/__tests__/eventEmitterAsyncIterator.js b/src/subscription/__tests__/eventEmitterAsyncIterator.js index ea0d0d30ca..8d7dcc36f6 100644 --- a/src/subscription/__tests__/eventEmitterAsyncIterator.js +++ b/src/subscription/__tests__/eventEmitterAsyncIterator.js @@ -1,7 +1,7 @@ // @flow strict import type EventEmitter from 'events'; -import { $$asyncIterator } from 'iterall'; +import { $$asyncIterator } from '../../jsutils/iterall'; /** * Create an AsyncIterator from an EventEmitter. Useful for mocking a diff --git a/src/subscription/asyncIteratorReject.js b/src/subscription/asyncIteratorReject.js index 4dad88da40..3a43495d6a 100644 --- a/src/subscription/asyncIteratorReject.js +++ b/src/subscription/asyncIteratorReject.js @@ -1,6 +1,6 @@ // @flow strict -import { $$asyncIterator } from 'iterall'; +import { $$asyncIterator } from '../jsutils/iterall'; /** * Given an error, returns an AsyncIterable which will fail with that error. diff --git a/src/subscription/mapAsyncIterator.js b/src/subscription/mapAsyncIterator.js index e8cb4363c7..6fd59c0abc 100644 --- a/src/subscription/mapAsyncIterator.js +++ b/src/subscription/mapAsyncIterator.js @@ -1,6 +1,6 @@ // @flow strict -import { $$asyncIterator, getAsyncIterator } from 'iterall'; +import { $$asyncIterator, getAsyncIterator } from '../jsutils/iterall'; import { type PromiseOrValue } from '../jsutils/PromiseOrValue'; diff --git a/src/subscription/subscribe.js b/src/subscription/subscribe.js index 8958e74952..0460f028eb 100644 --- a/src/subscription/subscribe.js +++ b/src/subscription/subscribe.js @@ -1,6 +1,6 @@ // @flow strict -import { isAsyncIterable } from 'iterall'; +import { isAsyncIterable } from '../jsutils/iterall'; import inspect from '../jsutils/inspect'; import { addPath, pathToArray } from '../jsutils/Path'; diff --git a/src/utilities/astFromValue.js b/src/utilities/astFromValue.js index e4972960a4..f4dc087b4c 100644 --- a/src/utilities/astFromValue.js +++ b/src/utilities/astFromValue.js @@ -1,6 +1,6 @@ // @flow strict -import { forEach, isCollection } from 'iterall'; +import { forEach, isCollection } from '../jsutils/iterall'; import objectValues from '../polyfills/objectValues'; diff --git a/src/utilities/coerceInputValue.js b/src/utilities/coerceInputValue.js index d7f6ca747a..19901acf32 100644 --- a/src/utilities/coerceInputValue.js +++ b/src/utilities/coerceInputValue.js @@ -1,6 +1,6 @@ // @flow strict -import { forEach, isCollection } from 'iterall'; +import { forEach, isCollection } from '../jsutils/iterall'; import objectValues from '../polyfills/objectValues';