From fcdbb8d5de0468eacbc0fe312c6c2e59f849a684 Mon Sep 17 00:00:00 2001 From: Brad Griffith Date: Mon, 6 Jan 2020 14:13:48 -0600 Subject: [PATCH] Revert "Remove node's url from lib/i18n-utils. (#38560)" This reverts commit 6d0daccc526eba34463cd32c560f11cf69f92f90. --- client/lib/i18n-utils/switch-locale.js | 41 ++++++++----------- client/lib/i18n-utils/utils.js | 54 +++++++++++--------------- client/lib/url/index.ts | 2 +- client/lib/url/url-parts.ts | 43 -------------------- 4 files changed, 41 insertions(+), 99 deletions(-) diff --git a/client/lib/i18n-utils/switch-locale.js b/client/lib/i18n-utils/switch-locale.js index 4937d462aeef5..24a7587baec29 100644 --- a/client/lib/i18n-utils/switch-locale.js +++ b/client/lib/i18n-utils/switch-locale.js @@ -4,12 +4,12 @@ import i18n from 'i18n-calypso'; import debugFactory from 'debug'; import { map, includes } from 'lodash'; +import { parse as parseUrl, format as formatUrl } from 'url'; /** * Internal dependencies */ import { isDefaultLocale, getLanguage } from './utils'; -import { getUrlFromParts } from 'lib/url/url-parts'; const debug = debugFactory( 'calypso:i18n' ); @@ -18,14 +18,13 @@ const getPromises = {}; /** * De-duplicates repeated GET fetches of the same URL while one is taking place. * Once it's finished, it'll allow for the same request to be done again. - * * @param {string} url The URL to fetch * * @returns {Promise} The fetch promise. */ function dedupedGet( url ) { if ( ! ( url in getPromises ) ) { - getPromises[ url ] = globalThis.fetch( url ).finally( () => delete getPromises[ url ] ); + getPromises[ url ] = fetch( url ).finally( () => delete getPromises[ url ] ); } return getPromises[ url ]; @@ -36,7 +35,7 @@ function dedupedGet( url ) { * Normally it should only serve as a helper function for `getLanguageFileUrl`, * but we export it here still in help with the test suite. * - * @returns {string} The path URL to the language files. + * @return {String} The path URL to the language files. */ export function getLanguageFilePathUrl() { const protocol = typeof window === 'undefined' ? 'https://' : '//'; // use a protocol-relative path in the browser @@ -48,11 +47,11 @@ export function getLanguageFilePathUrl() { * Get the language file URL for the given locale and file type, js or json. * A revision cache buster will be appended automatically if `setLangRevisions` has been called beforehand. * - * @param {string} localeSlug A locale slug. e.g. fr, jp, zh-tw - * @param {string} fileType The desired file type, js or json. Default to json. - * @param {object} languageRevisions An optional language revisions map. If it exists, the function will append the revision within as cache buster. + * @param {String} localeSlug A locale slug. e.g. fr, jp, zh-tw + * @param {String} fileType The desired file type, js or json. Default to json. + * @param {Object} languageRevisions An optional language revisions map. If it exists, the function will append the revision within as cache buster. * - * @returns {string} A language file URL. + * @return {String} A language file URL. */ export function getLanguageFileUrl( localeSlug, fileType = 'json', languageRevisions = {} ) { if ( ! includes( [ 'js', 'json' ], fileType ) ) { @@ -145,16 +144,11 @@ export default function switchLocale( localeSlug ) { } export function loadUserUndeployedTranslations( currentLocaleSlug ) { - if ( typeof window === 'undefined' || ! window.location || ! window.location.search ) { + if ( ! location || ! location.search ) { return; } - const search = new URLSearchParams( window.location.search ); - // TODO: replace with Object.fromEntries when available (core-js@3). - const params = {}; - for ( const [ key, value ] of search.entries() ) { - params[ key ] = value; - } + const parsedURL = parseUrl( location.search, true ); const { 'load-user-translations': username, @@ -162,7 +156,7 @@ export function loadUserUndeployedTranslations( currentLocaleSlug ) { translationSet = 'default', translationStatus = 'current', locale = currentLocaleSlug, - } = params; + } = parsedURL.query; if ( ! username ) { return; @@ -192,18 +186,17 @@ export function loadUserUndeployedTranslations( currentLocaleSlug ) { format: 'json', }; - const requestUrl = getUrlFromParts( { + const requestUrl = formatUrl( { protocol: 'https:', host: 'translate.wordpress.com', pathname, query, } ); - return window - .fetch( requestUrl.href, { - headers: { Accept: 'application/json' }, - credentials: 'include', - } ) + return fetch( requestUrl, { + headers: { Accept: 'application/json' }, + credentials: 'include', + } ) .then( res => res.json() ) .then( translations => i18n.addTranslations( translations ) ); } @@ -244,8 +237,8 @@ function switchWebpackCSS( isRTL ) { * Loads a CSS stylesheet into the page. * * @param {string} cssUrl URL of a CSS stylesheet to be loaded into the page - * @param {window.Element} currentLink an existing DOM element before which we want to insert the new one - * @returns {Promise} the new DOM element after the CSS has been loaded + * @param {Element} currentLink an existing DOM element before which we want to insert the new one + * @returns {Promise} the new DOM element after the CSS has been loaded */ function loadCSS( cssUrl, currentLink ) { return new Promise( resolve => { diff --git a/client/lib/i18n-utils/utils.js b/client/lib/i18n-utils/utils.js index ecd36bb16da59..9eef7a25600b6 100644 --- a/client/lib/i18n-utils/utils.js +++ b/client/lib/i18n-utils/utils.js @@ -2,6 +2,7 @@ * External dependencies */ import { find, isString, map, pickBy, includes, endsWith } from 'lodash'; +import url from 'url'; import { getLocaleSlug } from 'i18n-calypso'; /** @@ -9,7 +10,6 @@ import { getLocaleSlug } from 'i18n-calypso'; */ import config from 'config'; import { languages } from 'languages'; -import { getUrlParts, getUrlFromParts } from 'lib/url/url-parts'; /** * a locale can consist of three component @@ -30,7 +30,7 @@ export function getPathParts( path ) { * Checks if provided locale is a default one. * * @param {string} locale - locale slug (eg: 'fr') - * @returns {boolean} true when the default locale is provided + * @return {boolean} true when the default locale is provided */ export function isDefaultLocale( locale ) { return locale === config( 'i18n_default_locale_slug' ); @@ -40,7 +40,7 @@ export function isDefaultLocale( locale ) { * Checks if provided locale has a parentLangSlug and is therefore a locale variant * * @param {string} locale - locale slug (eg: 'fr') - * @returns {boolean} true when the locale has a parentLangSlug + * @return {boolean} true when the locale has a parentLangSlug */ export function isLocaleVariant( locale ) { if ( ! isString( locale ) ) { @@ -66,9 +66,8 @@ export function isLocaleRtl( locale ) { * Checks against a list of locales that don't have any GP translation sets * A 'translation set' refers to a collection of strings to be translated see: * https://glotpress.blog/the-manual/translation-sets/ - * * @param {string} locale - locale slug (eg: 'fr') - * @returns {boolean} true when the locale is NOT a member of the exception list + * @return {boolean} true when the locale is NOT a member of the exception list */ export function canBeTranslated( locale ) { return [ 'en', 'sr_latin' ].indexOf( locale ) === -1; @@ -77,7 +76,7 @@ export function canBeTranslated( locale ) { /** * Return a list of all supported language slugs * - * @returns {Array} A list of all supported language slugs + * @return {Array} A list of all supported language slugs */ export function getLanguageSlugs() { return map( languages, 'langSlug' ); @@ -85,9 +84,8 @@ export function getLanguageSlugs() { /** * Matches and returns language from config.languages based on the given localeSlug - * - * @param {string} langSlug locale slug of the language to match - * @returns {object|undefined} An object containing the locale data or undefined. + * @param {String} langSlug locale slug of the language to match + * @return {Object|undefined} An object containing the locale data or undefined. */ export function getLanguage( langSlug ) { if ( localeRegex.test( langSlug ) ) { @@ -104,12 +102,11 @@ export function getLanguage( langSlug ) { /** * Assuming that locale is adding at the end of path, retrieves the locale if present. - * * @param {string} path - original path - * @returns {string|undefined} The locale slug if present or undefined + * @return {string|undefined} The locale slug if present or undefined */ export function getLocaleFromPath( path ) { - const urlParts = getUrlParts( path ); + const urlParts = url.parse( path ); const locale = getPathParts( urlParts.pathname ).pop(); return 'undefined' === typeof getLanguage( locale ) ? undefined : locale; @@ -125,7 +122,7 @@ export function getLocaleFromPath( path ) { * @returns {string} original path with new locale slug */ export function addLocaleToPath( path, locale ) { - const urlParts = getUrlParts( path ); + const urlParts = url.parse( path ); const queryString = urlParts.search || ''; return removeLocaleFromPath( urlParts.pathname ) + `/${ locale }` + queryString; @@ -181,38 +178,34 @@ const urlLocalizationMapping = { export function localizeUrl( fullUrl, locale ) { const localeSlug = locale || ( typeof getLocaleSlug === 'function' ? getLocaleSlug() : 'en' ); - const urlParts = getUrlParts( String( fullUrl ) ); + const urlParts = url.parse( String( fullUrl ) ); - if ( ! urlParts.host ) { + if ( ! urlParts ) { return fullUrl; } // Let's unify the URL. - urlParts.protocol = 'https:'; - // Let's use `host` for everything. - delete urlParts.hostname; - + urlParts.protocol = 'https'; + if ( 'en.wordpress.com' === urlParts.hostname ) { + urlParts.host = 'wordpress.com'; + } if ( ! endsWith( urlParts.pathname, '.php' ) ) { urlParts.pathname = ( urlParts.pathname + '/' ).replace( /\/+$/, '/' ); } if ( ! localeSlug || 'en' === localeSlug ) { - if ( 'en.wordpress.com' === urlParts.host ) { + if ( 'en.wordpress.com' === urlParts.hostname ) { urlParts.host = 'wordpress.com'; - return getUrlFromParts( urlParts ).href; + return url.format( urlParts ); } return fullUrl; } - if ( 'en.wordpress.com' === urlParts.host ) { - urlParts.host = 'wordpress.com'; - } - - const lookup = [ urlParts.host, urlParts.host + urlParts.pathname ]; + const lookup = [ urlParts.hostname, urlParts.hostname + urlParts.pathname ]; for ( let i = lookup.length - 1; i >= 0; i-- ) { if ( lookup[ i ] in urlLocalizationMapping ) { - return getUrlFromParts( urlLocalizationMapping[ lookup[ i ] ]( urlParts, localeSlug ) ).href; + return url.format( urlLocalizationMapping[ lookup[ i ] ]( urlParts, localeSlug ) ); } } @@ -223,12 +216,11 @@ export function localizeUrl( fullUrl, locale ) { /** * Removes the trailing locale slug from the path, if it is present. * '/start/en' => '/start', '/start' => '/start', '/start/flow/fr' => '/start/flow', '/start/flow' => '/start/flow' - * * @param {string} path - original path * @returns {string} original path minus locale slug */ export function removeLocaleFromPath( path ) { - const urlParts = getUrlParts( path ); + const urlParts = url.parse( path ); const queryString = urlParts.search || ''; const parts = getPathParts( urlParts.pathname ); const locale = parts.pop(); @@ -243,9 +235,9 @@ export function removeLocaleFromPath( path ) { /** * Filter out unexpected values from the given language revisions object. * - * @param {object} languageRevisions A candidate language revisions object for filtering. + * @param {Object} languageRevisions A candidate language revisions object for filtering. * - * @returns {object} A valid language revisions object derived from the given one. + * @return {Object} A valid language revisions object derived from the given one. */ export function filterLanguageRevisions( languageRevisions ) { const langSlugs = getLanguageSlugs(); diff --git a/client/lib/url/index.ts b/client/lib/url/index.ts index 8ee89bb9987ce..0dacea23eb86b 100644 --- a/client/lib/url/index.ts +++ b/client/lib/url/index.ts @@ -12,4 +12,4 @@ export { default as isHttps } from './is-https'; export { addSchemeIfMissing, setUrlScheme } from './scheme-utils'; export { decodeURIIfValid, decodeURIComponentIfValid } from './decode-utils'; export { default as format } from './format'; -export { getUrlParts, getUrlFromParts } from './url-parts'; +export { getUrlParts } from './url-parts'; diff --git a/client/lib/url/url-parts.ts b/client/lib/url/url-parts.ts index 314931b6c0d32..efab3ada5b833 100644 --- a/client/lib/url/url-parts.ts +++ b/client/lib/url/url-parts.ts @@ -24,20 +24,6 @@ interface UrlParts { password: string; } -interface OptionalUrlParts { - protocol?: string; - host?: string; - hostname?: string; - port?: string; - origin?: string; - pathname?: string; - hash?: string; - search?: string; - searchParams?: URLSearchParams; - username?: string; - password?: string; -} - type UrlPartKey = keyof UrlParts; const EMPTY_URL: Readonly< UrlParts > = Object.freeze( { @@ -114,32 +100,3 @@ export function getUrlParts( url: URLString | URL ): UrlParts { return pathParts; } - -/** - * Returns a URL object built from the provided URL parts. - * - * @param parts the provided URL parts. - * - * @returns the generated URL object. - */ -export function getUrlFromParts( parts: OptionalUrlParts ): URL { - if ( ! parts?.protocol ) { - throw new Error( 'getUrlFromParts: protocol missing.' ); - } - - if ( ! parts.host && ! parts.hostname ) { - throw new Error( 'getUrlFromParts: host missing.' ); - } - - const result = new URL( BASE_URL ); - - // Apply 'host' first, since it includes both hostname and port. - result.host = parts.host ?? result.host; - - for ( const part of URL_PART_KEYS ) { - if ( part !== 'host' && part !== 'origin' && part !== 'searchParams' ) { - result[ part ] = parts[ part ] ?? result[ part ]; - } - } - return result; -}