expo-linking 6.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 6.2.0 — 2023-11-14
14
+
15
+ ### 💡 Others
16
+
17
+ - Migrate to new standard `URL` support on native. ([#24941](https://github.com/expo/expo/pull/24941) by [@EvanBacon](https://github.com/EvanBacon))
18
+
19
+ ## 6.1.1 — 2023-10-17
20
+
21
+ _This version does not introduce any user-facing changes._
22
+
13
23
  ## 6.1.0 — 2023-09-15
14
24
 
15
25
  ### 🛠 Breaking changes
@@ -1,5 +1,4 @@
1
- import type { ParsedQs } from 'qs';
2
- export type QueryParams = ParsedQs;
1
+ export type QueryParams = Record<string, undefined | string | string[]>;
3
2
  export type ParsedURL = {
4
3
  scheme: string | null;
5
4
  hostname: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"Linking.types.d.ts","sourceRoot":"","sources":["../src/Linking.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAGnC,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAGnC,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;OAEG;IACH,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B,CAAC;AAGF,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAGrD,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,EAAE,YAAY,KAAK,IAAI,CAAC;AAGpE,MAAM,MAAM,gBAAgB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"Linking.types.d.ts","sourceRoot":"","sources":["../src/Linking.types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAGxE,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;OAEG;IACH,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B,CAAC;AAGF,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAGrD,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,EAAE,YAAY,KAAK,IAAI,CAAC;AAGpE,MAAM,MAAM,gBAAgB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Linking.types.js","sourceRoot":"","sources":["../src/Linking.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ParsedQs } from 'qs';\n\n// @docsMissing\nexport type QueryParams = ParsedQs;\n\n// @needsAudit @docsMissing\nexport type ParsedURL = {\n scheme: string | null;\n hostname: string | null;\n /**\n * The path into the app specified by the URL.\n */\n path: string | null;\n /**\n * The set of query parameters specified by the query string of the url used to open the app.\n */\n queryParams: QueryParams | null;\n};\n\n// @needsAudit\nexport type CreateURLOptions = {\n /**\n * URI protocol `<scheme>://` that must be built into your native app.\n */\n scheme?: string;\n /**\n * An object of parameters that will be converted into a query string.\n */\n queryParams?: QueryParams;\n /**\n * Should the URI be triple slashed `scheme:///path` or double slashed `scheme://path`.\n */\n isTripleSlashed?: boolean;\n};\n\n// @docsMissing\nexport type EventType = {\n url: string;\n nativeEvent?: MessageEvent;\n};\n\n// @docsMissing\nexport type URLListener = (event: EventType) => void;\n\n// @docsMissing\nexport type NativeURLListener = (nativeEvent: MessageEvent) => void;\n\n// @docsMissing\nexport type SendIntentExtras = { key: string; value: string | number | boolean };\n"]}
1
+ {"version":3,"file":"Linking.types.js","sourceRoot":"","sources":["../src/Linking.types.ts"],"names":[],"mappings":"","sourcesContent":["// @docsMissing\nexport type QueryParams = Record<string, undefined | string | string[]>;\n\n// @needsAudit @docsMissing\nexport type ParsedURL = {\n scheme: string | null;\n hostname: string | null;\n /**\n * The path into the app specified by the URL.\n */\n path: string | null;\n /**\n * The set of query parameters specified by the query string of the url used to open the app.\n */\n queryParams: QueryParams | null;\n};\n\n// @needsAudit\nexport type CreateURLOptions = {\n /**\n * URI protocol `<scheme>://` that must be built into your native app.\n */\n scheme?: string;\n /**\n * An object of parameters that will be converted into a query string.\n */\n queryParams?: QueryParams;\n /**\n * Should the URI be triple slashed `scheme:///path` or double slashed `scheme://path`.\n */\n isTripleSlashed?: boolean;\n};\n\n// @docsMissing\nexport type EventType = {\n url: string;\n nativeEvent?: MessageEvent;\n};\n\n// @docsMissing\nexport type URLListener = (event: EventType) => void;\n\n// @docsMissing\nexport type NativeURLListener = (nativeEvent: MessageEvent) => void;\n\n// @docsMissing\nexport type SendIntentExtras = { key: string; value: string | number | boolean };\n"]}
@@ -3,16 +3,20 @@ import { CreateURLOptions, ParsedURL } from './Linking.types';
3
3
  * Helper method for constructing a deep link into your app, given an optional path and set of query
4
4
  * parameters. Creates a URI scheme with two slashes by default.
5
5
  *
6
- * The scheme in bare and standalone must be defined in the Expo config (`app.config.js` or `app.json`)
7
- * under `expo.scheme`.
6
+ * The scheme must be defined in the Expo config (`app.config.js` or `app.json`) under `expo.scheme`
7
+ * or `expo.{android,ios}.scheme`. Platform-specific schemes defined under `expo.{android,ios}.scheme`
8
+ * take precedence over universal schemes defined under `expo.scheme`.
8
9
  *
9
10
  * # Examples
10
- * - Bare: `<scheme>://path` - uses provided scheme or scheme from Expo config `scheme`.
11
- * - Standalone, Custom: `yourscheme://path`
11
+ * - Development and production builds: `<scheme>://path` - uses the optional `scheme` property if provided, and otherwise uses the first scheme defined by your Expo config
12
12
  * - Web (dev): `https://localhost:19006/path`
13
13
  * - Web (prod): `https://myapp.com/path`
14
- * - Expo Client (dev): `exp://128.0.0.1:8081/--/path`
15
- * - Expo Client (prod): `exp://exp.host/@yourname/your-app/--/path`
14
+ * - Expo Go (dev): `exp://128.0.0.1:8081/--/path`
15
+ *
16
+ * The behavior of this method in Expo Go for published updates is undefined and should not be relied upon.
17
+ * The created URL in this case is neither stable nor predictable during the lifetime of the app.
18
+ * If a stable URL is needed, for example in authorization callbacks, a build (or development build)
19
+ * of your application should be used and the scheme provided.
16
20
  *
17
21
  * @param path Addition path components to append to the base URL.
18
22
  * @param namedParameters Additional options object.
@@ -1 +1 @@
1
- {"version":3,"file":"createURL.d.ts","sourceRoot":"","sources":["../src/createURL.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAoD9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,EAAE,MAAM,EAAE,WAAgB,EAAE,eAAuB,EAAE,GAAE,gBAAqB,GAC3E,MAAM,CAiDR;AAGD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CA6C5C"}
1
+ {"version":3,"file":"createURL.d.ts","sourceRoot":"","sources":["../src/createURL.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAoD9D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,EAAE,MAAM,EAAE,WAAgB,EAAE,eAAuB,EAAE,GAAE,gBAAqB,GAC3E,MAAM,CAsDR;AAGD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAoD5C"}
@@ -1,6 +1,4 @@
1
1
  import Constants from 'expo-constants';
2
- import qs from 'qs';
3
- import URL from 'url-parse';
4
2
  import { hasCustomScheme, resolveScheme } from './Schemes';
5
3
  import { validateURL } from './validateURL';
6
4
  function getHostUri() {
@@ -49,16 +47,20 @@ function ensureLeadingSlash(input, shouldAppend) {
49
47
  * Helper method for constructing a deep link into your app, given an optional path and set of query
50
48
  * parameters. Creates a URI scheme with two slashes by default.
51
49
  *
52
- * The scheme in bare and standalone must be defined in the Expo config (`app.config.js` or `app.json`)
53
- * under `expo.scheme`.
50
+ * The scheme must be defined in the Expo config (`app.config.js` or `app.json`) under `expo.scheme`
51
+ * or `expo.{android,ios}.scheme`. Platform-specific schemes defined under `expo.{android,ios}.scheme`
52
+ * take precedence over universal schemes defined under `expo.scheme`.
54
53
  *
55
54
  * # Examples
56
- * - Bare: `<scheme>://path` - uses provided scheme or scheme from Expo config `scheme`.
57
- * - Standalone, Custom: `yourscheme://path`
55
+ * - Development and production builds: `<scheme>://path` - uses the optional `scheme` property if provided, and otherwise uses the first scheme defined by your Expo config
58
56
  * - Web (dev): `https://localhost:19006/path`
59
57
  * - Web (prod): `https://myapp.com/path`
60
- * - Expo Client (dev): `exp://128.0.0.1:8081/--/path`
61
- * - Expo Client (prod): `exp://exp.host/@yourname/your-app/--/path`
58
+ * - Expo Go (dev): `exp://128.0.0.1:8081/--/path`
59
+ *
60
+ * The behavior of this method in Expo Go for published updates is undefined and should not be relied upon.
61
+ * The created URL in this case is neither stable nor predictable during the lifetime of the app.
62
+ * If a stable URL is needed, for example in authorization callbacks, a build (or development build)
63
+ * of your application should be used and the scheme provided.
62
64
  *
63
65
  * @param path Addition path components to append to the base URL.
64
66
  * @param namedParameters Additional options object.
@@ -90,10 +92,9 @@ export function createURL(path, { scheme, queryParams = {}, isTripleSlashed = fa
90
92
  queryString = queryStringMatchResult[2];
91
93
  let paramsFromHostUri = {};
92
94
  try {
93
- const parsedParams = qs.parse(queryString);
94
- if (typeof parsedParams === 'object') {
95
- paramsFromHostUri = parsedParams;
96
- }
95
+ paramsFromHostUri = Object.fromEntries(
96
+ // @ts-ignore: [Symbol.iterator] is indeed, available on every platform.
97
+ new URLSearchParams(queryString));
97
98
  }
98
99
  catch { }
99
100
  queryParams = {
@@ -101,7 +102,9 @@ export function createURL(path, { scheme, queryParams = {}, isTripleSlashed = fa
101
102
  ...paramsFromHostUri,
102
103
  };
103
104
  }
104
- queryString = qs.stringify(queryParams);
105
+ queryString = new URLSearchParams(
106
+ // For legacy purposes, we'll strip out the nullish values before creating the URL.
107
+ Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value != null))).toString();
105
108
  if (queryString) {
106
109
  queryString = `?${queryString}`;
107
110
  }
@@ -116,16 +119,24 @@ export function createURL(path, { scheme, queryParams = {}, isTripleSlashed = fa
116
119
  */
117
120
  export function parse(url) {
118
121
  validateURL(url);
119
- const parsed = URL(url, /* parseQueryString */ true);
120
- for (const param in parsed.query) {
121
- parsed.query[param] = decodeURIComponent(parsed.query[param]);
122
+ const queryParams = {};
123
+ let path = null;
124
+ let hostname = null;
125
+ let scheme = null;
126
+ try {
127
+ const parsed = new URL(url);
128
+ parsed.searchParams.forEach((value, key) => {
129
+ queryParams[key] = decodeURIComponent(value);
130
+ });
131
+ path = parsed.pathname || null;
132
+ hostname = parsed.hostname || null;
133
+ scheme = parsed.protocol || null;
134
+ }
135
+ catch {
136
+ path = url;
122
137
  }
123
- const queryParams = parsed.query;
124
138
  const hostUri = getHostUri() || '';
125
139
  const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri));
126
- let path = parsed.pathname || null;
127
- let hostname = parsed.hostname || null;
128
- let scheme = parsed.protocol || null;
129
140
  if (scheme) {
130
141
  // Remove colon at end
131
142
  scheme = scheme.substring(0, scheme.length - 1);
@@ -1 +1 @@
1
- {"version":3,"file":"createURL.js","sourceRoot":"","sources":["../src/createURL.ts"],"names":[],"mappings":"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","sourcesContent":["import Constants from 'expo-constants';\nimport qs from 'qs';\nimport URL from 'url-parse';\n\nimport { CreateURLOptions, ParsedURL } from './Linking.types';\nimport { hasCustomScheme, resolveScheme } from './Schemes';\nimport { validateURL } from './validateURL';\n\nfunction getHostUri(): string | null {\n if (Constants.expoConfig?.hostUri) {\n return Constants.expoConfig.hostUri;\n } else if (!hasCustomScheme()) {\n // we're probably not using up-to-date xdl, so just fake it for now\n // we have to remove the /--/ on the end since this will be inserted again later\n return removeScheme(Constants.linkingUri).replace(/\\/--($|\\/.*$)/, '');\n } else {\n return null;\n }\n}\n\nfunction isExpoHosted(): boolean {\n const hostUri = getHostUri();\n return !!(\n hostUri &&\n (/^(.*\\.)?(expo\\.io|exp\\.host|exp\\.direct|expo\\.test|expo\\.dev)(:.*)?(\\/.*)?$/.test(hostUri) ||\n Constants.expoGoConfig?.developer)\n );\n}\n\nfunction removeScheme(url: string): string {\n return url.replace(/^[a-zA-Z0-9+.-]+:\\/\\//, '');\n}\n\nfunction removePort(url: string): string {\n return url.replace(/(?=([a-zA-Z0-9+.-]+:\\/\\/)?[^/]):\\d+/, '');\n}\n\nfunction removeLeadingSlash(url: string): string {\n return url.replace(/^\\//, '');\n}\n\nfunction removeTrailingSlashAndQueryString(url: string): string {\n return url.replace(/\\/?\\?.*$/, '');\n}\n\nfunction ensureLeadingSlash(input: string, shouldAppend: boolean): string {\n const hasSlash = input.startsWith('/');\n if (hasSlash && !shouldAppend) {\n return input.substring(1);\n } else if (!hasSlash && shouldAppend) {\n return `/${input}`;\n }\n return input;\n}\n\n// @needsAudit\n/**\n * Helper method for constructing a deep link into your app, given an optional path and set of query\n * parameters. Creates a URI scheme with two slashes by default.\n *\n * The scheme in bare and standalone must be defined in the Expo config (`app.config.js` or `app.json`)\n * under `expo.scheme`.\n *\n * # Examples\n * - Bare: `<scheme>://path` - uses provided scheme or scheme from Expo config `scheme`.\n * - Standalone, Custom: `yourscheme://path`\n * - Web (dev): `https://localhost:19006/path`\n * - Web (prod): `https://myapp.com/path`\n * - Expo Client (dev): `exp://128.0.0.1:8081/--/path`\n * - Expo Client (prod): `exp://exp.host/@yourname/your-app/--/path`\n *\n * @param path Addition path components to append to the base URL.\n * @param namedParameters Additional options object.\n * @return A URL string which points to your app with the given deep link information.\n */\nexport function createURL(\n path: string,\n { scheme, queryParams = {}, isTripleSlashed = false }: CreateURLOptions = {}\n): string {\n const resolvedScheme = resolveScheme({ scheme });\n\n let hostUri = getHostUri() || '';\n\n if (hasCustomScheme() && isExpoHosted()) {\n hostUri = '';\n }\n\n if (path) {\n if (isExpoHosted() && hostUri) {\n path = `/--/${removeLeadingSlash(path)}`;\n }\n if (isTripleSlashed && !path.startsWith('/')) {\n path = `/${path}`;\n }\n } else {\n path = '';\n }\n\n // merge user-provided query params with any that were already in the hostUri\n // e.g. release-channel\n let queryString = '';\n const queryStringMatchResult = hostUri.match(/(.*)\\?(.+)/);\n if (queryStringMatchResult) {\n hostUri = queryStringMatchResult[1];\n queryString = queryStringMatchResult[2];\n let paramsFromHostUri = {};\n try {\n const parsedParams = qs.parse(queryString);\n if (typeof parsedParams === 'object') {\n paramsFromHostUri = parsedParams;\n }\n } catch {}\n queryParams = {\n ...queryParams,\n ...paramsFromHostUri,\n };\n }\n queryString = qs.stringify(queryParams);\n if (queryString) {\n queryString = `?${queryString}`;\n }\n\n hostUri = ensureLeadingSlash(hostUri, !isTripleSlashed);\n\n return encodeURI(\n `${resolvedScheme}:${isTripleSlashed ? '/' : ''}/${hostUri}${path}${queryString}`\n );\n}\n\n// @needsAudit\n/**\n * Helper method for parsing out deep link information from a URL.\n * @param url A URL that points to the currently running experience (e.g. an output of `Linking.createURL()`).\n * @return A `ParsedURL` object.\n */\nexport function parse(url: string): ParsedURL {\n validateURL(url);\n\n const parsed = URL(url, /* parseQueryString */ true);\n\n for (const param in parsed.query) {\n parsed.query[param] = decodeURIComponent(parsed.query[param]!);\n }\n const queryParams = parsed.query;\n\n const hostUri = getHostUri() || '';\n const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri));\n\n let path = parsed.pathname || null;\n let hostname = parsed.hostname || null;\n let scheme = parsed.protocol || null;\n\n if (scheme) {\n // Remove colon at end\n scheme = scheme.substring(0, scheme.length - 1);\n }\n\n if (path) {\n path = removeLeadingSlash(path);\n\n let expoPrefix: string | null = null;\n if (hostUriStripped) {\n const parts = hostUriStripped.split('/');\n expoPrefix = parts.slice(1).concat(['--/']).join('/');\n }\n\n if (isExpoHosted() && !hasCustomScheme() && expoPrefix && path.startsWith(expoPrefix)) {\n path = path.substring(expoPrefix.length);\n hostname = null;\n } else if (path.indexOf('+') > -1) {\n path = path.substring(path.indexOf('+') + 1);\n }\n }\n\n return {\n hostname,\n path,\n queryParams,\n scheme,\n };\n}\n"]}
1
+ {"version":3,"file":"createURL.js","sourceRoot":"","sources":["../src/createURL.ts"],"names":[],"mappings":"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","sourcesContent":["import Constants from 'expo-constants';\n\nimport { CreateURLOptions, ParsedURL } from './Linking.types';\nimport { hasCustomScheme, resolveScheme } from './Schemes';\nimport { validateURL } from './validateURL';\n\nfunction getHostUri(): string | null {\n if (Constants.expoConfig?.hostUri) {\n return Constants.expoConfig.hostUri;\n } else if (!hasCustomScheme()) {\n // we're probably not using up-to-date xdl, so just fake it for now\n // we have to remove the /--/ on the end since this will be inserted again later\n return removeScheme(Constants.linkingUri).replace(/\\/--($|\\/.*$)/, '');\n } else {\n return null;\n }\n}\n\nfunction isExpoHosted(): boolean {\n const hostUri = getHostUri();\n return !!(\n hostUri &&\n (/^(.*\\.)?(expo\\.io|exp\\.host|exp\\.direct|expo\\.test|expo\\.dev)(:.*)?(\\/.*)?$/.test(hostUri) ||\n Constants.expoGoConfig?.developer)\n );\n}\n\nfunction removeScheme(url: string): string {\n return url.replace(/^[a-zA-Z0-9+.-]+:\\/\\//, '');\n}\n\nfunction removePort(url: string): string {\n return url.replace(/(?=([a-zA-Z0-9+.-]+:\\/\\/)?[^/]):\\d+/, '');\n}\n\nfunction removeLeadingSlash(url: string): string {\n return url.replace(/^\\//, '');\n}\n\nfunction removeTrailingSlashAndQueryString(url: string): string {\n return url.replace(/\\/?\\?.*$/, '');\n}\n\nfunction ensureLeadingSlash(input: string, shouldAppend: boolean): string {\n const hasSlash = input.startsWith('/');\n if (hasSlash && !shouldAppend) {\n return input.substring(1);\n } else if (!hasSlash && shouldAppend) {\n return `/${input}`;\n }\n return input;\n}\n\n// @needsAudit\n/**\n * Helper method for constructing a deep link into your app, given an optional path and set of query\n * parameters. Creates a URI scheme with two slashes by default.\n *\n * The scheme must be defined in the Expo config (`app.config.js` or `app.json`) under `expo.scheme`\n * or `expo.{android,ios}.scheme`. Platform-specific schemes defined under `expo.{android,ios}.scheme`\n * take precedence over universal schemes defined under `expo.scheme`.\n *\n * # Examples\n * - Development and production builds: `<scheme>://path` - uses the optional `scheme` property if provided, and otherwise uses the first scheme defined by your Expo config\n * - Web (dev): `https://localhost:19006/path`\n * - Web (prod): `https://myapp.com/path`\n * - Expo Go (dev): `exp://128.0.0.1:8081/--/path`\n *\n * The behavior of this method in Expo Go for published updates is undefined and should not be relied upon.\n * The created URL in this case is neither stable nor predictable during the lifetime of the app.\n * If a stable URL is needed, for example in authorization callbacks, a build (or development build)\n * of your application should be used and the scheme provided.\n *\n * @param path Addition path components to append to the base URL.\n * @param namedParameters Additional options object.\n * @return A URL string which points to your app with the given deep link information.\n */\nexport function createURL(\n path: string,\n { scheme, queryParams = {}, isTripleSlashed = false }: CreateURLOptions = {}\n): string {\n const resolvedScheme = resolveScheme({ scheme });\n\n let hostUri = getHostUri() || '';\n\n if (hasCustomScheme() && isExpoHosted()) {\n hostUri = '';\n }\n\n if (path) {\n if (isExpoHosted() && hostUri) {\n path = `/--/${removeLeadingSlash(path)}`;\n }\n if (isTripleSlashed && !path.startsWith('/')) {\n path = `/${path}`;\n }\n } else {\n path = '';\n }\n\n // merge user-provided query params with any that were already in the hostUri\n // e.g. release-channel\n let queryString = '';\n const queryStringMatchResult = hostUri.match(/(.*)\\?(.+)/);\n if (queryStringMatchResult) {\n hostUri = queryStringMatchResult[1];\n queryString = queryStringMatchResult[2];\n let paramsFromHostUri = {};\n try {\n paramsFromHostUri = Object.fromEntries(\n // @ts-ignore: [Symbol.iterator] is indeed, available on every platform.\n new URLSearchParams(queryString)\n );\n } catch {}\n queryParams = {\n ...queryParams,\n ...paramsFromHostUri,\n };\n }\n queryString = new URLSearchParams(\n // For legacy purposes, we'll strip out the nullish values before creating the URL.\n Object.fromEntries(\n Object.entries(queryParams).filter(([, value]) => value != null) as [string, string][]\n )\n ).toString();\n if (queryString) {\n queryString = `?${queryString}`;\n }\n\n hostUri = ensureLeadingSlash(hostUri, !isTripleSlashed);\n\n return encodeURI(\n `${resolvedScheme}:${isTripleSlashed ? '/' : ''}/${hostUri}${path}${queryString}`\n );\n}\n\n// @needsAudit\n/**\n * Helper method for parsing out deep link information from a URL.\n * @param url A URL that points to the currently running experience (e.g. an output of `Linking.createURL()`).\n * @return A `ParsedURL` object.\n */\nexport function parse(url: string): ParsedURL {\n validateURL(url);\n\n const queryParams: Record<string, string> = {};\n let path: string | null = null;\n let hostname: string | null = null;\n let scheme: string | null = null;\n\n try {\n const parsed = new URL(url);\n\n parsed.searchParams.forEach((value, key) => {\n queryParams[key] = decodeURIComponent(value);\n });\n path = parsed.pathname || null;\n hostname = parsed.hostname || null;\n scheme = parsed.protocol || null;\n } catch {\n path = url;\n }\n\n const hostUri = getHostUri() || '';\n const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri));\n\n if (scheme) {\n // Remove colon at end\n scheme = scheme.substring(0, scheme.length - 1);\n }\n\n if (path) {\n path = removeLeadingSlash(path);\n\n let expoPrefix: string | null = null;\n if (hostUriStripped) {\n const parts = hostUriStripped.split('/');\n expoPrefix = parts.slice(1).concat(['--/']).join('/');\n }\n\n if (isExpoHosted() && !hasCustomScheme() && expoPrefix && path.startsWith(expoPrefix)) {\n path = path.substring(expoPrefix.length);\n hostname = null;\n } else if (path.indexOf('+') > -1) {\n path = path.substring(path.indexOf('+') + 1);\n }\n }\n\n return {\n hostname,\n path,\n queryParams,\n scheme,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"createURL.web.d.ts","sourceRoot":"","sources":["../src/createURL.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE9D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAgB,EAAE,GAAE,gBAAqB,GAAG,MAAM,CAY3F;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAoC5C"}
1
+ {"version":3,"file":"createURL.web.d.ts","sourceRoot":"","sources":["../src/createURL.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE9D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAgB,EAAE,GAAE,gBAAqB,GAAG,MAAM,CAe3F;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAoC5C"}
@@ -7,8 +7,9 @@ export function createURL(path, { queryParams = {} } = {}) {
7
7
  url.searchParams.set(key, encodeURIComponent(value));
8
8
  }
9
9
  else if (value != null) {
10
- // @ts-expect-error
11
- url.searchParams.set(key, value);
10
+ url.searchParams.set(key,
11
+ // @ts-expect-error: browser supports using array
12
+ value);
12
13
  }
13
14
  });
14
15
  return url.toString().replace(/\/$/, '');
@@ -1 +1 @@
1
- {"version":3,"file":"createURL.web.js","sourceRoot":"","sources":["../src/createURL.web.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,EAAE,WAAW,GAAG,EAAE,KAAuB,EAAE;IACjF,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;SACtD;aAAM,IAAI,KAAK,IAAI,IAAI,EAAE;YACxB,mBAAmB;YACnB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAClC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,MAAW,CAAC;IAChB,IAAI;QACF,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;KACvB;IAAC,MAAM;QACN,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,OAAO;gBACL,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,GAAG;gBACT,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,IAAI;aACb,CAAC;SACH;QACD,OAAO;YACL,QAAQ,EAAE,WAAW;YACrB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,EAAE;YACf,MAAM,EAAE,MAAM;SACf,CAAC;KACH;IACD,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,WAAW,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IACH,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;QACjC,oFAAoF;QACpF,IAAI,EACF,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,EAAE;gBACxB,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACxC,WAAW;QACX,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC","sourcesContent":["import { CreateURLOptions, ParsedURL } from './Linking.types';\n\nexport function createURL(path: string, { queryParams = {} }: CreateURLOptions = {}): string {\n if (typeof window === 'undefined') return '';\n const url = new URL(path, window.location.origin);\n Object.entries(queryParams).forEach(([key, value]) => {\n if (typeof value === 'string') {\n url.searchParams.set(key, encodeURIComponent(value));\n } else if (value != null) {\n // @ts-expect-error\n url.searchParams.set(key, value);\n }\n });\n return url.toString().replace(/\\/$/, '');\n}\n\nexport function parse(url: string): ParsedURL {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n if (typeof window === 'undefined') {\n return {\n hostname: null,\n path: url,\n queryParams: {},\n scheme: null,\n };\n }\n return {\n hostname: 'localhost',\n path: url,\n queryParams: {},\n scheme: 'http',\n };\n }\n const queryParams: Record<string, string> = {};\n parsed.searchParams.forEach((value, key) => {\n queryParams[key] = decodeURIComponent(value);\n });\n return {\n hostname: parsed.hostname || null,\n // TODO: We should probably update native to follow the default URL behavior closer.\n path:\n !parsed.hostname && !parsed.pathname\n ? null\n : parsed.pathname === ''\n ? null\n : parsed.pathname.replace(/^\\//, ''),\n queryParams,\n scheme: parsed.protocol.replace(/:$/, ''),\n };\n}\n"]}
1
+ {"version":3,"file":"createURL.web.js","sourceRoot":"","sources":["../src/createURL.web.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,EAAE,WAAW,GAAG,EAAE,KAAuB,EAAE;IACjF,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;SACtD;aAAM,IAAI,KAAK,IAAI,IAAI,EAAE;YACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAClB,GAAG;YACH,iDAAiD;YACjD,KAAK,CACN,CAAC;SACH;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,MAAW,CAAC;IAChB,IAAI;QACF,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;KACvB;IAAC,MAAM;QACN,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,OAAO;gBACL,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,GAAG;gBACT,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,IAAI;aACb,CAAC;SACH;QACD,OAAO;YACL,QAAQ,EAAE,WAAW;YACrB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,EAAE;YACf,MAAM,EAAE,MAAM;SACf,CAAC;KACH;IACD,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,WAAW,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IACH,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;QACjC,oFAAoF;QACpF,IAAI,EACF,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,EAAE;gBACxB,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACxC,WAAW;QACX,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC","sourcesContent":["import { CreateURLOptions, ParsedURL } from './Linking.types';\n\nexport function createURL(path: string, { queryParams = {} }: CreateURLOptions = {}): string {\n if (typeof window === 'undefined') return '';\n const url = new URL(path, window.location.origin);\n Object.entries(queryParams).forEach(([key, value]) => {\n if (typeof value === 'string') {\n url.searchParams.set(key, encodeURIComponent(value));\n } else if (value != null) {\n url.searchParams.set(\n key,\n // @ts-expect-error: browser supports using array\n value\n );\n }\n });\n return url.toString().replace(/\\/$/, '');\n}\n\nexport function parse(url: string): ParsedURL {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n if (typeof window === 'undefined') {\n return {\n hostname: null,\n path: url,\n queryParams: {},\n scheme: null,\n };\n }\n return {\n hostname: 'localhost',\n path: url,\n queryParams: {},\n scheme: 'http',\n };\n }\n const queryParams: Record<string, string> = {};\n parsed.searchParams.forEach((value, key) => {\n queryParams[key] = decodeURIComponent(value);\n });\n return {\n hostname: parsed.hostname || null,\n // TODO: We should probably update native to follow the default URL behavior closer.\n path:\n !parsed.hostname && !parsed.pathname\n ? null\n : parsed.pathname === ''\n ? null\n : parsed.pathname.replace(/^\\//, ''),\n queryParams,\n scheme: parsed.protocol.replace(/:$/, ''),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-linking",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "Create and open deep links universally",
5
5
  "main": "build/Linking.js",
6
6
  "types": "build/Linking.d.ts",
@@ -30,11 +30,8 @@
30
30
  "license": "MIT",
31
31
  "homepage": "https://docs.expo.dev/versions/latest/sdk/linking",
32
32
  "dependencies": {
33
- "@types/qs": "^6.9.7",
34
- "expo-constants": "~15.1.0",
35
- "invariant": "^2.2.4",
36
- "qs": "^6.11.0",
37
- "url-parse": "^1.5.9"
33
+ "expo-constants": "~15.3.0",
34
+ "invariant": "^2.2.4"
38
35
  },
39
36
  "devDependencies": {
40
37
  "expo-module-scripts": "^3.0.0"
@@ -42,5 +39,5 @@
42
39
  "jest": {
43
40
  "preset": "expo-module-scripts"
44
41
  },
45
- "gitHead": "ee2c866ba3c7fbc35ff2a3e896041cf15d3bd7c5"
42
+ "gitHead": "3142a086578deffd8704a8f1b6f0f661527d836c"
46
43
  }
@@ -1,7 +1,5 @@
1
- import type { ParsedQs } from 'qs';
2
-
3
1
  // @docsMissing
4
- export type QueryParams = ParsedQs;
2
+ export type QueryParams = Record<string, undefined | string | string[]>;
5
3
 
6
4
  // @needsAudit @docsMissing
7
5
  export type ParsedURL = {
package/src/createURL.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import Constants from 'expo-constants';
2
- import qs from 'qs';
3
- import URL from 'url-parse';
4
2
 
5
3
  import { CreateURLOptions, ParsedURL } from './Linking.types';
6
4
  import { hasCustomScheme, resolveScheme } from './Schemes';
@@ -58,16 +56,20 @@ function ensureLeadingSlash(input: string, shouldAppend: boolean): string {
58
56
  * Helper method for constructing a deep link into your app, given an optional path and set of query
59
57
  * parameters. Creates a URI scheme with two slashes by default.
60
58
  *
61
- * The scheme in bare and standalone must be defined in the Expo config (`app.config.js` or `app.json`)
62
- * under `expo.scheme`.
59
+ * The scheme must be defined in the Expo config (`app.config.js` or `app.json`) under `expo.scheme`
60
+ * or `expo.{android,ios}.scheme`. Platform-specific schemes defined under `expo.{android,ios}.scheme`
61
+ * take precedence over universal schemes defined under `expo.scheme`.
63
62
  *
64
63
  * # Examples
65
- * - Bare: `<scheme>://path` - uses provided scheme or scheme from Expo config `scheme`.
66
- * - Standalone, Custom: `yourscheme://path`
64
+ * - Development and production builds: `<scheme>://path` - uses the optional `scheme` property if provided, and otherwise uses the first scheme defined by your Expo config
67
65
  * - Web (dev): `https://localhost:19006/path`
68
66
  * - Web (prod): `https://myapp.com/path`
69
- * - Expo Client (dev): `exp://128.0.0.1:8081/--/path`
70
- * - Expo Client (prod): `exp://exp.host/@yourname/your-app/--/path`
67
+ * - Expo Go (dev): `exp://128.0.0.1:8081/--/path`
68
+ *
69
+ * The behavior of this method in Expo Go for published updates is undefined and should not be relied upon.
70
+ * The created URL in this case is neither stable nor predictable during the lifetime of the app.
71
+ * If a stable URL is needed, for example in authorization callbacks, a build (or development build)
72
+ * of your application should be used and the scheme provided.
71
73
  *
72
74
  * @param path Addition path components to append to the base URL.
73
75
  * @param namedParameters Additional options object.
@@ -105,17 +107,22 @@ export function createURL(
105
107
  queryString = queryStringMatchResult[2];
106
108
  let paramsFromHostUri = {};
107
109
  try {
108
- const parsedParams = qs.parse(queryString);
109
- if (typeof parsedParams === 'object') {
110
- paramsFromHostUri = parsedParams;
111
- }
110
+ paramsFromHostUri = Object.fromEntries(
111
+ // @ts-ignore: [Symbol.iterator] is indeed, available on every platform.
112
+ new URLSearchParams(queryString)
113
+ );
112
114
  } catch {}
113
115
  queryParams = {
114
116
  ...queryParams,
115
117
  ...paramsFromHostUri,
116
118
  };
117
119
  }
118
- queryString = qs.stringify(queryParams);
120
+ queryString = new URLSearchParams(
121
+ // For legacy purposes, we'll strip out the nullish values before creating the URL.
122
+ Object.fromEntries(
123
+ Object.entries(queryParams).filter(([, value]) => value != null) as [string, string][]
124
+ )
125
+ ).toString();
119
126
  if (queryString) {
120
127
  queryString = `?${queryString}`;
121
128
  }
@@ -136,20 +143,27 @@ export function createURL(
136
143
  export function parse(url: string): ParsedURL {
137
144
  validateURL(url);
138
145
 
139
- const parsed = URL(url, /* parseQueryString */ true);
140
-
141
- for (const param in parsed.query) {
142
- parsed.query[param] = decodeURIComponent(parsed.query[param]!);
146
+ const queryParams: Record<string, string> = {};
147
+ let path: string | null = null;
148
+ let hostname: string | null = null;
149
+ let scheme: string | null = null;
150
+
151
+ try {
152
+ const parsed = new URL(url);
153
+
154
+ parsed.searchParams.forEach((value, key) => {
155
+ queryParams[key] = decodeURIComponent(value);
156
+ });
157
+ path = parsed.pathname || null;
158
+ hostname = parsed.hostname || null;
159
+ scheme = parsed.protocol || null;
160
+ } catch {
161
+ path = url;
143
162
  }
144
- const queryParams = parsed.query;
145
163
 
146
164
  const hostUri = getHostUri() || '';
147
165
  const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri));
148
166
 
149
- let path = parsed.pathname || null;
150
- let hostname = parsed.hostname || null;
151
- let scheme = parsed.protocol || null;
152
-
153
167
  if (scheme) {
154
168
  // Remove colon at end
155
169
  scheme = scheme.substring(0, scheme.length - 1);
@@ -7,8 +7,11 @@ export function createURL(path: string, { queryParams = {} }: CreateURLOptions =
7
7
  if (typeof value === 'string') {
8
8
  url.searchParams.set(key, encodeURIComponent(value));
9
9
  } else if (value != null) {
10
- // @ts-expect-error
11
- url.searchParams.set(key, value);
10
+ url.searchParams.set(
11
+ key,
12
+ // @ts-expect-error: browser supports using array
13
+ value
14
+ );
12
15
  }
13
16
  });
14
17
  return url.toString().replace(/\/$/, '');