From b97cc78584dbb7633a8bef62e6e1dec5f69b5043 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 30 Jul 2018 16:33:44 +0100 Subject: [PATCH 1/4] Use a unique querystring package instead of three different ones --- core-blocks/embed/index.js | 4 ++-- core-blocks/latest-posts/edit.js | 6 +++--- package-lock.json | 11 +++-------- package.json | 1 - packages/editor/package.json | 3 +-- .../editor/src/components/page-attributes/parent.js | 4 ++-- .../components/post-taxonomies/flat-term-selector.js | 4 ++-- .../post-taxonomies/hierarchical-term-selector.js | 6 +++--- packages/editor/src/components/url-input/index.js | 4 ++-- 9 files changed, 18 insertions(+), 25 deletions(-) diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index 9d34bb91bc40cc..0d98464636903b 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -3,7 +3,7 @@ */ import { parse } from 'url'; import { includes, kebabCase, toLower } from 'lodash'; -import { stringify } from 'querystring'; +import httpBuildQuery from 'http-build-query'; import memoize from 'memize'; import classnames from 'classnames'; @@ -27,7 +27,7 @@ import './editor.scss'; const HOSTS_NO_PREVIEWS = [ 'facebook.com' ]; // Caches the embed API calls, so if blocks get transformed, or deleted and added again, we don't spam the API. -const wpEmbedAPI = memoize( ( url ) => apiFetch( { path: `/oembed/1.0/proxy?${ stringify( { url } ) }` } ) ); +const wpEmbedAPI = memoize( ( url ) => apiFetch( { path: `/oembed/1.0/proxy?${ httpBuildQuery( { url } ) }` } ) ); const matchesPatterns = ( url, patterns = [] ) => { return patterns.some( ( pattern ) => { diff --git a/core-blocks/latest-posts/edit.js b/core-blocks/latest-posts/edit.js index 3cc908799fd5ee..2570bf49175dd3 100644 --- a/core-blocks/latest-posts/edit.js +++ b/core-blocks/latest-posts/edit.js @@ -4,7 +4,7 @@ import { get, isUndefined, pickBy } from 'lodash'; import moment from 'moment'; import classnames from 'classnames'; -import { stringify } from 'querystringify'; +import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -160,14 +160,14 @@ class LatestPostsEdit extends Component { export default withAPIData( ( props ) => { const { postsToShow, order, orderBy, categories } = props.attributes; - const latestPostsQuery = stringify( pickBy( { + const latestPostsQuery = httpBuildQuery( pickBy( { categories, order, orderby: orderBy, per_page: postsToShow, _fields: [ 'date_gmt', 'link', 'title' ], }, ( value ) => ! isUndefined( value ) ) ); - const categoriesListQuery = stringify( { + const categoriesListQuery = httpBuildQuery( { per_page: 100, _fields: [ 'id', 'name', 'parent' ], } ); diff --git a/package-lock.json b/package-lock.json index 38f56b2fc9a3ba..46bceb6e3b458b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3469,10 +3469,9 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", + "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", - "querystring": "^0.2.0", - "querystringify": "^1.0.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", @@ -16908,7 +16907,8 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true }, "querystring-es3": { "version": "0.2.1", @@ -16916,11 +16916,6 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, - "querystringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", - "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" - }, "quick-lru": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", diff --git a/package.json b/package.json index 41db25ed2c0d27..95d4cd92356b39 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "moment": "2.22.1", "moment-timezone": "0.5.16", "prop-types": "15.5.10", - "querystringify": "1.0.0", "re-resizable": "4.7.1", "react": "16.4.1", "react-dom": "16.4.1", diff --git a/packages/editor/package.json b/packages/editor/package.json index 5c49033920d202..7ca7fb6061f7fc 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -46,10 +46,9 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", + "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", - "querystring": "^0.2.0", - "querystringify": "^1.0.0", "react-autosize-textarea": "^3.0.2", "redux-multi": "^0.1.12", "redux-optimist": "^1.0.0", diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index bcea83a6e91bdc..53077c642aca35 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -2,7 +2,7 @@ * External dependencies */ import { get } from 'lodash'; -import { stringify } from 'querystringify'; +import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -65,7 +65,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => { const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { const isHierarchical = get( postType, [ 'hierarchical' ], false ); const restBase = get( postType, [ 'rest_base' ], false ); - const queryString = stringify( { + const queryString = httpBuildQuery( { context: 'edit', per_page: -1, exclude: postId, diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index a7e685b6e32630..6ca6a0cf624b13 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -2,7 +2,7 @@ * External dependencies */ import { isEmpty, get, unescape as unescapeString, find, throttle, uniqBy, invoke } from 'lodash'; -import { stringify } from 'querystring'; +import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -77,7 +77,7 @@ class FlatTermSelector extends Component { fetchTerms( params = {} ) { const { taxonomy } = this.props; const query = { ...DEFAULT_QUERY, ...params }; - const request = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ stringify( query ) }` } ); + const request = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( query ) }` } ); request.then( ( terms ) => { this.setState( ( state ) => ( { availableTerms: state.availableTerms.concat( diff --git a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js index f8a6a15a10aeae..07f1fd62e875bc 100644 --- a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js @@ -2,7 +2,7 @@ * External dependencies */ import { get, unescape as unescapeString, without, find, some, invoke } from 'lodash'; -import { stringify } from 'querystring'; +import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -121,7 +121,7 @@ class HierarchicalTermSelector extends Component { if ( errorCode === 'term_exists' ) { // search the new category created since last fetch this.addRequest = apiFetch( { - path: `/wp/v2/${ taxonomy.rest_base }?${ stringify( { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } ) }`, + path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } ) }`, } ); return this.addRequest .then( ( searchResult ) => { @@ -183,7 +183,7 @@ class HierarchicalTermSelector extends Component { if ( ! taxonomy ) { return; } - this.fetchRequest = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ stringify( DEFAULT_QUERY ) }` } ); + this.fetchRequest = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( DEFAULT_QUERY ) }` } ); this.fetchRequest.then( ( terms ) => { // resolve const availableTermsTree = buildTermsTree( terms ); diff --git a/packages/editor/src/components/url-input/index.js b/packages/editor/src/components/url-input/index.js index 77757d4dbcf103..0de3fc210b90fb 100644 --- a/packages/editor/src/components/url-input/index.js +++ b/packages/editor/src/components/url-input/index.js @@ -4,7 +4,7 @@ import { throttle } from 'lodash'; import classnames from 'classnames'; import scrollIntoView from 'dom-scroll-into-view'; -import { stringify } from 'querystringify'; +import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -91,7 +91,7 @@ class URLInput extends Component { } ); const request = apiFetch( { - path: `/gutenberg/v1/search?${ stringify( { + path: `/gutenberg/v1/search?${ httpBuildQuery( { search: value, per_page: 20, type: 'post', From e8d3fdb8b96dfc806bfb027cc3df9e5b791f09ca Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 31 Jul 2018 09:33:26 +0100 Subject: [PATCH 2/4] Update the URL package to be PHP compliant --- core-blocks/embed/index.js | 6 ++++-- core-blocks/latest-posts/edit.js | 14 +++++++------- package-lock.json | 14 ++++---------- packages/components/package.json | 2 +- .../components/src/server-side-render/index.js | 8 +++++--- packages/editor/package.json | 1 - .../src/components/page-attributes/parent.js | 8 ++++---- .../post-taxonomies/flat-term-selector.js | 8 +++++--- .../post-taxonomies/hierarchical-term-selector.js | 11 ++++++++--- packages/editor/src/components/url-input/index.js | 6 +++--- packages/url/package.json | 3 ++- packages/url/src/index.js | 10 +++++----- packages/url/src/test/index.test.js | 7 +++++++ 13 files changed, 55 insertions(+), 43 deletions(-) diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index 0d98464636903b..e30a87125121a0 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -3,7 +3,6 @@ */ import { parse } from 'url'; import { includes, kebabCase, toLower } from 'lodash'; -import httpBuildQuery from 'http-build-query'; import memoize from 'memize'; import classnames from 'classnames'; @@ -16,6 +15,7 @@ import { Button, Placeholder, Spinner, SandBox, IconButton, Toolbar } from '@wor import { createBlock } from '@wordpress/blocks'; import { RichText, BlockControls } from '@wordpress/editor'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -27,7 +27,9 @@ import './editor.scss'; const HOSTS_NO_PREVIEWS = [ 'facebook.com' ]; // Caches the embed API calls, so if blocks get transformed, or deleted and added again, we don't spam the API. -const wpEmbedAPI = memoize( ( url ) => apiFetch( { path: `/oembed/1.0/proxy?${ httpBuildQuery( { url } ) }` } ) ); +const wpEmbedAPI = memoize( ( url ) => + apiFetch( { path: addQueryArgs( '/oembed/1.0/proxy', { url } ) } ) +); const matchesPatterns = ( url, patterns = [] ) => { return patterns.some( ( pattern ) => { diff --git a/core-blocks/latest-posts/edit.js b/core-blocks/latest-posts/edit.js index 2570bf49175dd3..f269ce2014c1d7 100644 --- a/core-blocks/latest-posts/edit.js +++ b/core-blocks/latest-posts/edit.js @@ -4,7 +4,6 @@ import { get, isUndefined, pickBy } from 'lodash'; import moment from 'moment'; import classnames from 'classnames'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -27,6 +26,7 @@ import { BlockAlignmentToolbar, BlockControls, } from '@wordpress/editor'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -160,19 +160,19 @@ class LatestPostsEdit extends Component { export default withAPIData( ( props ) => { const { postsToShow, order, orderBy, categories } = props.attributes; - const latestPostsQuery = httpBuildQuery( pickBy( { + const latestPostsQuery = pickBy( { categories, order, orderby: orderBy, per_page: postsToShow, _fields: [ 'date_gmt', 'link', 'title' ], - }, ( value ) => ! isUndefined( value ) ) ); - const categoriesListQuery = httpBuildQuery( { + }, ( value ) => ! isUndefined( value ) ); + const categoriesListQuery = { per_page: 100, _fields: [ 'id', 'name', 'parent' ], - } ); + }; return { - latestPosts: `/wp/v2/posts?${ latestPostsQuery }`, - categoriesList: `/wp/v2/categories?${ categoriesListQuery }`, + latestPosts: addQueryArgs( '/wp/v2/posts', latestPostsQuery ), + categoriesList: addQueryArgs( '/wp/v2/categories', categoriesListQuery ), }; } )( LatestPostsEdit ); diff --git a/package-lock.json b/package-lock.json index 46bceb6e3b458b..8c6b67e061f6b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3086,11 +3086,11 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", + "@wordpress/url": "file:packages/url", "classnames": "^2.2.5", "clipboard": "^1.7.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "moment": "^2.22.1", @@ -3469,7 +3469,6 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", @@ -3715,7 +3714,8 @@ "@wordpress/url": { "version": "file:packages/url", "requires": { - "@babel/runtime": "^7.0.0-beta.52" + "@babel/runtime": "^7.0.0-beta.52", + "qs": "^6.5.2s" } }, "@wordpress/viewport": { @@ -10548,11 +10548,6 @@ "readable-stream": "^2.0.2" } }, - "http-build-query": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/http-build-query/-/http-build-query-0.7.0.tgz", - "integrity": "sha512-r5jnQ/PcKzFg2dLJAj9xU8cBHuLubxLli6NiFtziNrDqVwPcjIgioamZszzGOPJq6ekUu+WfIcgsuPqe9MX4Ag==" - }, "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", @@ -16890,8 +16885,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "5.1.1", diff --git a/packages/components/package.json b/packages/components/package.json index d66d8b7abae377..35287e33190c00 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,11 +30,11 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/url": "file:../url", "classnames": "^2.2.5", "clipboard": "^1.7.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "moment": "^2.22.1", diff --git a/packages/components/src/server-side-render/index.js b/packages/components/src/server-side-render/index.js index e6031903e65097..29814b08ed2b34 100644 --- a/packages/components/src/server-side-render/index.js +++ b/packages/components/src/server-side-render/index.js @@ -12,7 +12,7 @@ import { } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; -import httpBuildQuery from 'http-build-query'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies. @@ -21,8 +21,10 @@ import Placeholder from '../placeholder'; import Spinner from '../spinner'; export function rendererPathWithAttributes( block, attributes = null ) { - return `/gutenberg/v1/block-renderer/${ block }?context=edit` + - ( null !== attributes ? '&' + httpBuildQuery( { attributes } ) : '' ); + return addQueryArgs( `/gutenberg/v1/block-renderer/${ block }`, { + context: 'edit', + ...( null !== attributes ? { attributes } : {} ), + } ); } export class ServerSideRender extends Component { diff --git a/packages/editor/package.json b/packages/editor/package.json index 7ca7fb6061f7fc..54de007a90091e 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -46,7 +46,6 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 53077c642aca35..e07ed64e2c79a1 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -2,7 +2,6 @@ * External dependencies */ import { get } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -11,6 +10,7 @@ import { __ } from '@wordpress/i18n'; import { TreeSelect, withAPIData } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -65,7 +65,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => { const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { const isHierarchical = get( postType, [ 'hierarchical' ], false ); const restBase = get( postType, [ 'rest_base' ], false ); - const queryString = httpBuildQuery( { + const queryString = { context: 'edit', per_page: -1, exclude: postId, @@ -73,9 +73,9 @@ const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { _fields: [ 'id', 'parent', 'title' ], orderby: 'menu_order', order: 'asc', - } ); + }; return isHierarchical && restBase ? - { items: `/wp/v2/${ restBase }?${ queryString }` } : + { items: addQueryArgs( `/wp/v2/${ restBase }`, queryString ) } : {}; } ); diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 6ca6a0cf624b13..d2be11cb3804d7 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -2,7 +2,6 @@ * External dependencies */ import { isEmpty, get, unescape as unescapeString, find, throttle, uniqBy, invoke } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -13,6 +12,7 @@ import { FormTokenField } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Module constants @@ -77,7 +77,9 @@ class FlatTermSelector extends Component { fetchTerms( params = {} ) { const { taxonomy } = this.props; const query = { ...DEFAULT_QUERY, ...params }; - const request = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( query ) }` } ); + const request = apiFetch( { + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, query ), + } ); request.then( ( terms ) => { this.setState( ( state ) => ( { availableTerms: state.availableTerms.concat( @@ -116,7 +118,7 @@ class FlatTermSelector extends Component { if ( errorCode === 'term_exists' ) { // If the terms exist, fetch it instead of creating a new one. this.addRequest = apiFetch( { - path: `/wp/v2/${ taxonomy.rest_base }?${ stringify( { ...DEFAULT_QUERY, search: termName } ) }`, + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, { ...DEFAULT_QUERY, search: termName } ), } ); return this.addRequest.then( ( searchResult ) => { return find( searchResult, ( result ) => isSameTermName( result.name, termName ) ); diff --git a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js index 07f1fd62e875bc..72c3c599a66ed6 100644 --- a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js @@ -2,7 +2,6 @@ * External dependencies */ import { get, unescape as unescapeString, without, find, some, invoke } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -13,6 +12,7 @@ import { TreeSelect, withSpokenMessages, Button } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -121,7 +121,10 @@ class HierarchicalTermSelector extends Component { if ( errorCode === 'term_exists' ) { // search the new category created since last fetch this.addRequest = apiFetch( { - path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } ) }`, + path: addQueryArgs( + `/wp/v2/${ taxonomy.rest_base }`, + { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } + ), } ); return this.addRequest .then( ( searchResult ) => { @@ -183,7 +186,9 @@ class HierarchicalTermSelector extends Component { if ( ! taxonomy ) { return; } - this.fetchRequest = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( DEFAULT_QUERY ) }` } ); + this.fetchRequest = apiFetch( { + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, DEFAULT_QUERY ), + } ); this.fetchRequest.then( ( terms ) => { // resolve const availableTermsTree = buildTermsTree( terms ); diff --git a/packages/editor/src/components/url-input/index.js b/packages/editor/src/components/url-input/index.js index 0de3fc210b90fb..1dc8de94f04820 100644 --- a/packages/editor/src/components/url-input/index.js +++ b/packages/editor/src/components/url-input/index.js @@ -4,7 +4,6 @@ import { throttle } from 'lodash'; import classnames from 'classnames'; import scrollIntoView from 'dom-scroll-into-view'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -17,6 +16,7 @@ import { Spinner, withSpokenMessages, Popover } from '@wordpress/components'; import { withInstanceId } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; import deprecated from '@wordpress/deprecated'; +import { addQueryArgs } from '@wordpress/url'; // Since URLInput is rendered in the context of other inputs, but should be // considered a separate modal node, prevent keyboard events from propagating @@ -91,11 +91,11 @@ class URLInput extends Component { } ); const request = apiFetch( { - path: `/gutenberg/v1/search?${ httpBuildQuery( { + path: addQueryArgs( '/gutenberg/v1/search', { search: value, per_page: 20, type: 'post', - } ) }`, + } ), } ); request.then( ( posts ) => { diff --git a/packages/url/package.json b/packages/url/package.json index 4657460af92a22..8fe57dac8f85bf 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -19,7 +19,8 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.0.0-beta.52" + "@babel/runtime": "^7.0.0-beta.52", + "qs": "^6.5.2s" }, "publishConfig": { "access": "public" diff --git a/packages/url/src/index.js b/packages/url/src/index.js index 0463a306f0dcac..7b8204c369def8 100644 --- a/packages/url/src/index.js +++ b/packages/url/src/index.js @@ -1,7 +1,8 @@ /** * External dependencies */ -import { parse, format } from 'url'; +import { parse as parseURL, format as stringifyURL } from 'url'; +import { parse, stringify } from 'qs'; const EMAIL_REGEXP = /^(mailto:)?[a-z0-9._%+-]+@[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$/i; const USABLE_HREF_REGEXP = /^(?:[a-z]+:|#|\?|\.|\/)/i; @@ -15,11 +16,10 @@ const USABLE_HREF_REGEXP = /^(?:[a-z]+:|#|\?|\.|\/)/i; * @return {string} Updated URL */ export function addQueryArgs( url, args ) { - const parsedURL = parse( url, true ); - const query = { ...parsedURL.query, ...args }; - delete parsedURL.search; + const parsedURL = parseURL( url ); + const query = { ...parse( parsedURL.query ), ...args }; - return format( { ...parsedURL, query } ); + return stringifyURL( { ...parsedURL, search: stringify( query ) } ); } /** diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js index 63a7a587db77e0..0bd8b81abe4461 100644 --- a/packages/url/src/test/index.test.js +++ b/packages/url/src/test/index.test.js @@ -24,6 +24,13 @@ describe( 'addQueryArgs', () => { expect( addQueryArgs( url, args ) ).toBe( 'https://andalouses.example/beach?night=false&sun=true&sand=false' ); } ); + + test( 'should update args to an URL with array parameters', () => { + const url = 'https://andalouses.example/beach?time[]=10&time[]=11'; + const args = { beach: [ 'sand', 'rock' ] }; + + expect( decodeURI( addQueryArgs( url, args ) ) ).toBe( 'https://andalouses.example/beach?time[0]=10&time[1]=11&beach[0]=sand&beach[1]=rock' ); + } ); } ); describe( 'prependHTTP', () => { From 03c149280fa32a55af439e5129ddb2b46f4f564b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 31 Jul 2018 16:15:13 +0100 Subject: [PATCH 3/4] Drop the url package dependency from '@wordpress/url' --- packages/url/src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/url/src/index.js b/packages/url/src/index.js index 7b8204c369def8..a002ccdeca0ca9 100644 --- a/packages/url/src/index.js +++ b/packages/url/src/index.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { parse as parseURL, format as stringifyURL } from 'url'; import { parse, stringify } from 'qs'; const EMAIL_REGEXP = /^(mailto:)?[a-z0-9._%+-]+@[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$/i; @@ -16,10 +15,11 @@ const USABLE_HREF_REGEXP = /^(?:[a-z]+:|#|\?|\.|\/)/i; * @return {string} Updated URL */ export function addQueryArgs( url, args ) { - const parsedURL = parseURL( url ); - const query = { ...parse( parsedURL.query ), ...args }; + const queryStringIndex = url.indexOf( '?' ); + const query = queryStringIndex !== -1 ? parse( url.substr( queryStringIndex + 1 ) ) : {}; + const baseUrl = queryStringIndex !== -1 ? url.substr( 0, queryStringIndex ) : url; - return stringifyURL( { ...parsedURL, search: stringify( query ) } ); + return baseUrl + '?' + stringify( { ...query, ...args } ); } /** From 9f914ab62ca9c0724aaa957b0a1df1105ba75baf Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 31 Jul 2018 16:16:40 +0100 Subject: [PATCH 4/4] Small naming convetion tweaks --- .../src/components/page-attributes/parent.js | 4 +-- packages/url/src/test/index.test.js | 26 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index e07ed64e2c79a1..ed9ab45218089f 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -65,7 +65,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => { const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { const isHierarchical = get( postType, [ 'hierarchical' ], false ); const restBase = get( postType, [ 'rest_base' ], false ); - const queryString = { + const query = { context: 'edit', per_page: -1, exclude: postId, @@ -75,7 +75,7 @@ const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { order: 'asc', }; return isHierarchical && restBase ? - { items: addQueryArgs( `/wp/v2/${ restBase }`, queryString ) } : + { items: addQueryArgs( `/wp/v2/${ restBase }`, query ) } : {}; } ); diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js index 0bd8b81abe4461..40fbc4fe20d53a 100644 --- a/packages/url/src/test/index.test.js +++ b/packages/url/src/test/index.test.js @@ -4,28 +4,28 @@ import { addQueryArgs, prependHTTP } from '../'; describe( 'addQueryArgs', () => { - test( 'should append args to an URL without query string', () => { + it( 'should append args to an URL without query string', () => { const url = 'https://andalouses.example/beach'; const args = { sun: 'true', sand: 'false' }; expect( addQueryArgs( url, args ) ).toBe( 'https://andalouses.example/beach?sun=true&sand=false' ); } ); - test( 'should append args to an URL with query string', () => { + it( 'should append args to an URL with query string', () => { const url = 'https://andalouses.example/beach?night=false'; const args = { sun: 'true', sand: 'false' }; expect( addQueryArgs( url, args ) ).toBe( 'https://andalouses.example/beach?night=false&sun=true&sand=false' ); } ); - test( 'should update args to an URL with conflicting query string', () => { + it( 'should update args to an URL with conflicting query string', () => { const url = 'https://andalouses.example/beach?night=false&sun=false&sand=true'; const args = { sun: 'true', sand: 'false' }; expect( addQueryArgs( url, args ) ).toBe( 'https://andalouses.example/beach?night=false&sun=true&sand=false' ); } ); - test( 'should update args to an URL with array parameters', () => { + it( 'should update args to an URL with array parameters', () => { const url = 'https://andalouses.example/beach?time[]=10&time[]=11'; const args = { beach: [ 'sand', 'rock' ] }; @@ -34,55 +34,55 @@ describe( 'addQueryArgs', () => { } ); describe( 'prependHTTP', () => { - test( 'should prepend http to a domain', () => { + it( 'should prepend http to a domain', () => { const url = 'wordpress.org'; expect( prependHTTP( url ) ).toBe( 'http://' + url ); } ); - test( 'shouldn’t prepend http to an email', () => { + it( 'shouldn’t prepend http to an email', () => { const url = 'foo@wordpress.org'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to an absolute URL', () => { + it( 'shouldn’t prepend http to an absolute URL', () => { const url = '/wordpress'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to a relative URL', () => { + it( 'shouldn’t prepend http to a relative URL', () => { const url = './wordpress'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to an anchor URL', () => { + it( 'shouldn’t prepend http to an anchor URL', () => { const url = '#wordpress'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to a URL that already has http', () => { + it( 'shouldn’t prepend http to a URL that already has http', () => { const url = 'http://wordpress.org'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to a URL that already has https', () => { + it( 'shouldn’t prepend http to a URL that already has https', () => { const url = 'https://wordpress.org'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to a URL that already has ftp', () => { + it( 'shouldn’t prepend http to a URL that already has ftp', () => { const url = 'ftp://wordpress.org'; expect( prependHTTP( url ) ).toBe( url ); } ); - test( 'shouldn’t prepend http to a URL that already has mailto', () => { + it( 'shouldn’t prepend http to a URL that already has mailto', () => { const url = 'mailto:foo@wordpress.org'; expect( prependHTTP( url ) ).toBe( url );