@huckleberry-inc/address 4.3.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.
Files changed (56) hide show
  1. package/README.md +151 -0
  2. package/build/cjs/AddressFormatter.js +98 -0
  3. package/build/cjs/format.js +53 -0
  4. package/build/cjs/graphqlQuery.js +76 -0
  5. package/build/cjs/index.js +23 -0
  6. package/build/cjs/loader.js +70 -0
  7. package/build/cjs/node_modules/@shopify/address-consts/build/esm/index.mjs.js +32 -0
  8. package/build/cjs/packages/address/src/AddressFormatter.js +98 -0
  9. package/build/cjs/packages/address/src/format.js +53 -0
  10. package/build/cjs/packages/address/src/graphqlQuery.js +76 -0
  11. package/build/cjs/packages/address/src/index.js +27 -0
  12. package/build/cjs/packages/address/src/loader.js +70 -0
  13. package/build/cjs/packages/address/src/utilities.js +65 -0
  14. package/build/cjs/utilities.js +65 -0
  15. package/build/esm/AddressFormatter.mjs +94 -0
  16. package/build/esm/format.mjs +48 -0
  17. package/build/esm/graphqlQuery.mjs +72 -0
  18. package/build/esm/index.mjs +4 -0
  19. package/build/esm/loader.mjs +64 -0
  20. package/build/esm/node_modules/@shopify/address-consts/build/esm/index.mjs.mjs +27 -0
  21. package/build/esm/packages/address/src/AddressFormatter.mjs +94 -0
  22. package/build/esm/packages/address/src/format.mjs +48 -0
  23. package/build/esm/packages/address/src/graphqlQuery.mjs +72 -0
  24. package/build/esm/packages/address/src/index.mjs +4 -0
  25. package/build/esm/packages/address/src/loader.mjs +64 -0
  26. package/build/esm/packages/address/src/utilities.mjs +59 -0
  27. package/build/esm/utilities.mjs +59 -0
  28. package/build/esnext/AddressFormatter.esnext +94 -0
  29. package/build/esnext/format.esnext +48 -0
  30. package/build/esnext/graphqlQuery.esnext +72 -0
  31. package/build/esnext/index.esnext +4 -0
  32. package/build/esnext/loader.esnext +64 -0
  33. package/build/esnext/node_modules/@shopify/address-consts/build/esm/index.mjs.esnext +27 -0
  34. package/build/esnext/packages/address/src/AddressFormatter.esnext +94 -0
  35. package/build/esnext/packages/address/src/format.esnext +48 -0
  36. package/build/esnext/packages/address/src/graphqlQuery.esnext +72 -0
  37. package/build/esnext/packages/address/src/index.esnext +4 -0
  38. package/build/esnext/packages/address/src/loader.esnext +64 -0
  39. package/build/esnext/packages/address/src/utilities.esnext +59 -0
  40. package/build/esnext/utilities.esnext +59 -0
  41. package/build/ts/AddressFormatter.d.ts +23 -0
  42. package/build/ts/AddressFormatter.d.ts.map +1 -0
  43. package/build/ts/format.d.ts +29 -0
  44. package/build/ts/format.d.ts.map +1 -0
  45. package/build/ts/graphqlQuery.d.ts +3 -0
  46. package/build/ts/graphqlQuery.d.ts.map +1 -0
  47. package/build/ts/index.d.ts +5 -0
  48. package/build/ts/index.d.ts.map +1 -0
  49. package/build/ts/loader.d.ts +11 -0
  50. package/build/ts/loader.d.ts.map +1 -0
  51. package/build/ts/utilities.d.ts +8 -0
  52. package/build/ts/utilities.d.ts.map +1 -0
  53. package/index.esnext +2 -0
  54. package/index.js +1 -0
  55. package/index.mjs +2 -0
  56. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # `@shopify/address`
2
+
3
+ > [!CAUTION]
4
+ >
5
+ > `@shopify/address` is deprecated.
6
+ >
7
+ > Shopifolk, see
8
+ > [Shopify/quilt-internal](https://github.com/shopify/quilt-internal) for
9
+ > information on the latest packages available for use internally.
10
+
11
+ [![Build Status](https://github.com/Shopify/quilt/workflows/Node-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ANode-CI)
12
+ [![Build Status](https://github.com/Shopify/quilt/workflows/Ruby-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ARuby-CI)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) [![npm version](https://badge.fury.io/js/%40shopify%2Faddress.svg)](https://badge.fury.io/js/%40shopify%2Faddress)
14
+ ![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/%40shopify%2Faddress.svg)
15
+
16
+ Address utilities for formatting addresses.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ yarn add @shopify/address
22
+ ```
23
+
24
+ ## API Reference
25
+
26
+ - `country` field in Address is expected to be of format ISO 3166-1 alpha-2, eg. CA / FR / JP
27
+
28
+ ### `AddressFormatter` class
29
+
30
+ Show an address:
31
+
32
+ ```ts
33
+ import AddressFormatter from '@shopify/address';
34
+
35
+ const address = {
36
+ company: 'Shopify',
37
+ firstName: '恵子',
38
+ lastName: '田中',
39
+ address1: '八重洲1-5-3',
40
+ address2: '',
41
+ city: '目黒区',
42
+ province: 'JP-13',
43
+ zip: '100-8994',
44
+ country: 'JP',
45
+ phone: '',
46
+ };
47
+
48
+ const addressFormatter = new AddressFormatter('ja');
49
+ await addressFormatter.format(address);
50
+ /* =>
51
+ 日本
52
+ 〒100-8994東京都目黒区八重洲1-5-3
53
+ Shopify
54
+ 田中恵子様
55
+ */
56
+
57
+ await addressFormatter.getOrderedFields('CA');
58
+ /* =>
59
+ [
60
+ ['firstName', 'lastName'],
61
+ ['company'],
62
+ ['address1'],
63
+ ['address2'],
64
+ ['city'],
65
+ ['country', 'province', 'zip'],
66
+ ['phone']
67
+ ]
68
+ */
69
+ ```
70
+
71
+ #### `constructor(private locale: string)`
72
+
73
+ Instantiate the AddressFormatter by passing it a locale.
74
+
75
+ #### `updateLocale(locale: string)`
76
+
77
+ Update the current locale of the formatter. Following requests will be in the given locale.
78
+
79
+ #### `async .getCountry(countryCode: string): Promise<Country>`
80
+
81
+ Loads and returns data about a given country in the current locale. Country and province names are localized. Province names are sorted based on the locale.
82
+
83
+ #### `async .getCountries(): Promise<Country[]>`
84
+
85
+ Loads and returns data for all countries in the current locale. Countries are sorted based on the locale. Zones are also ordered based on the locale.
86
+
87
+ #### `async .getZoneName(countryCode: string, zoneCode: string): Promise<string>`
88
+
89
+ This returns the names of the provinces or regions for the specified country, displayed in the language that is currently set.
90
+
91
+ #### `async .getOrderedFields(countryCode): Promise<FieldName[][]>`
92
+
93
+ Returns how to order address fields for a country code. Fetches the country if not already cached.
94
+
95
+ #### `async .format(address: Address): Promise<string[]>`
96
+
97
+ Given an address, returns the address ordered for multiline rendering. Uses the `formatAddress` sync API in the background.
98
+
99
+ #### `AddressFormatter.resetCache(): void`
100
+
101
+ Resets the internal cache. Useful to avoid side-effects in test suite.
102
+
103
+ ### Sync API
104
+
105
+ If you already have the input data ready, like a `Country` object, you can use the sync API to get the result right away.
106
+
107
+ The following functions can be imported as stand-alone utilities.
108
+
109
+ #### `formatAddress(address: Address, country: Country): string[]`
110
+
111
+ Given an address and a country, returns the address ordered for multiline rendering. e.g.:
112
+
113
+ ```typescript
114
+ ['Shopify', 'Lindenstraße 9-14', '10969 Berlin', 'Germany'];
115
+ ```
116
+
117
+ #### `buildOrderedFields(country: Country): FieldName[][]`
118
+
119
+ Returns how to order address fields for a specific country.
120
+
121
+ Eg.:
122
+
123
+ ```typescript
124
+ [
125
+ ['firstName', 'lastName'],
126
+ ['company'],
127
+ ['address1'],
128
+ ['address2'],
129
+ ['city'],
130
+ ['country', 'province', 'zip'],
131
+ ['phone'],
132
+ ];
133
+ ```
134
+
135
+ ## Testing
136
+
137
+ If your component uses this package and you want to test it with mock API calls you can use the following:
138
+
139
+ ```ts
140
+ import {fetch} from '@shopify/jest-dom-mocks';
141
+ import {mockCountryRequests} from '@shopify/address/tests';
142
+ import AddressFormatter from '@shopify/address';
143
+
144
+ beforeEach(() => {
145
+ AddressFormatter.resetCache(); // to avoid side-effects.
146
+ mockCountryRequests();
147
+ });
148
+ afterEach(fetch.restore);
149
+ ```
150
+
151
+ Note: Only FR / JA and EN are mocked.
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var format = require('./format.js');
6
+ var loader = require('./loader.js');
7
+
8
+ const ORDERED_COUNTRIES_CACHE = new Map();
9
+ class AddressFormatter {
10
+ /**
11
+ * Useful in tests or any situation where the cache has undesirable
12
+ * side-effects.
13
+ */
14
+ static resetCache() {
15
+ ORDERED_COUNTRIES_CACHE.clear();
16
+ }
17
+ constructor(locale) {
18
+ this.locale = locale;
19
+ this.locale = locale;
20
+ }
21
+ updateLocale(locale) {
22
+ this.locale = locale;
23
+ }
24
+ async getCountry(countryCode, {
25
+ includeHiddenZones = false
26
+ } = {}) {
27
+ const country = this.loadCountryFromCache(countryCode, includeHiddenZones);
28
+ if (country) return country;
29
+ return loader.loadCountry(this.locale, countryCode, {
30
+ includeHiddenZones
31
+ });
32
+ }
33
+ async getCountries({
34
+ includeHiddenZones = false
35
+ } = {}) {
36
+ const cacheKey = this.cacheKey(this.locale, includeHiddenZones);
37
+ const cachedCountries = ORDERED_COUNTRIES_CACHE.get(cacheKey);
38
+ if (cachedCountries) return cachedCountries;
39
+ const countries = await loader.loadCountries(this.locale, {
40
+ includeHiddenZones
41
+ });
42
+ ORDERED_COUNTRIES_CACHE.set(cacheKey, countries);
43
+ return countries;
44
+ }
45
+ async getZoneName(countryCode, zoneCode) {
46
+ const country = await this.getCountry(countryCode);
47
+ const countryZone = country.zones.find(item => item.code === zoneCode);
48
+ if (!(countryZone !== null && countryZone !== void 0 && countryZone.name)) return undefined;
49
+ return countryZone.name;
50
+ }
51
+
52
+ /* Returns the address ordered in an array based based on the country code
53
+ * Eg.:
54
+ * [
55
+ * 'Shopify',
56
+ * 'First Name Last Name',
57
+ * 'Address 1',
58
+ * 'address2',
59
+ * 'Montréal',
60
+ * 'Canada Quebec H2J 4B7',
61
+ * '514 444 3333'
62
+ * ]
63
+ */
64
+ async format(address) {
65
+ const country = await this.getCountry(address.country);
66
+ return format.formatAddress(address, country);
67
+ }
68
+
69
+ /* Returns an array that shows how to order fields based on the country code
70
+ * Eg.:
71
+ * [
72
+ * ['company'],
73
+ * ['firstName', 'lastName'],
74
+ * ['address1'],
75
+ * ['address2'],
76
+ * ['city'],
77
+ * ['country', 'province', 'zip'],
78
+ * ['phone']
79
+ * ]
80
+ */
81
+ async getOrderedFields(countryCode) {
82
+ const country = await this.getCountry(countryCode);
83
+ return format.buildOrderedFields(country);
84
+ }
85
+ cacheKey(locale, includeHiddenZones) {
86
+ /* Cache list of countries per locale, both with and without hidden zones included */
87
+ return `${locale}-${includeHiddenZones}`;
88
+ }
89
+ loadCountryFromCache(countryCode, includeHiddenZones) {
90
+ const cachedCountries = ORDERED_COUNTRIES_CACHE.get(this.cacheKey(this.locale, includeHiddenZones));
91
+ if (!cachedCountries) return null;
92
+ return cachedCountries.find(({
93
+ code
94
+ }) => code === countryCode);
95
+ }
96
+ }
97
+
98
+ exports["default"] = AddressFormatter;
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var utilities = require('./utilities.js');
6
+
7
+ const LINE_DELIMITER = '_';
8
+ const DEFAULT_FORM_LAYOUT = '{firstName}{lastName}_{company}_{address1}_{address2}_{city}_{country}{province}{zip}_{phone}';
9
+ const DEFAULT_SHOW_LAYOUT = '{lastName} {firstName}_{company}_{address1} {address2}_{city} {province} {zip}_{country}_{phone}';
10
+
11
+ /**
12
+ * When it's time to render any address, use this function so that it's properly
13
+ * formatted for the country's locale.
14
+ *
15
+ * ```typescript
16
+ * ['Shopify', 'Lindenstraße 9-14', '10969 Berlin', 'Germany'];
17
+ * ```
18
+ * @returns all lines of a formatted address as an array of strings.
19
+ */
20
+ function formatAddress(address, country) {
21
+ const layout = country.formatting.show || DEFAULT_SHOW_LAYOUT;
22
+ return layout.split(LINE_DELIMITER).map(lineTemplate => utilities.renderLineTemplate(country, lineTemplate, address).trim());
23
+ }
24
+
25
+ /**
26
+ * In an edit form, this function can be used to properly order all the input
27
+ * fields.
28
+ *
29
+ * ```typescript
30
+ * [
31
+ * ['firstName', 'lastName'],
32
+ * ['company'],
33
+ * ['address1'],
34
+ * ['address2'],
35
+ * ['city'],
36
+ * ['country', 'province', 'zip'],
37
+ * ['phone'],
38
+ * ];
39
+ * ```
40
+ */
41
+ function buildOrderedFields(country) {
42
+ const format = country ? country.formatting.edit : DEFAULT_FORM_LAYOUT;
43
+ return format.split(LINE_DELIMITER).map(lineTemplate => {
44
+ const result = lineTemplate.match(utilities.FIELD_REGEXP);
45
+ if (!result) {
46
+ return [];
47
+ }
48
+ return result.map(field => utilities.FIELDS_MAPPING[field]);
49
+ });
50
+ }
51
+
52
+ exports.buildOrderedFields = buildOrderedFields;
53
+ exports.formatAddress = formatAddress;
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const query = `
6
+ query countries($locale: SupportedLocale!) {
7
+ getCountries(locale: $locale) {
8
+ name
9
+ code
10
+ continent
11
+ phoneNumberPrefix
12
+ autocompletionField
13
+ provinceKey
14
+ labels {
15
+ address1
16
+ address2
17
+ city
18
+ company
19
+ country
20
+ firstName
21
+ lastName
22
+ phone
23
+ postalCode
24
+ zone
25
+ }
26
+ optionalLabels {
27
+ address2
28
+ }
29
+ formatting {
30
+ edit
31
+ show
32
+ }
33
+ zones {
34
+ name
35
+ code
36
+ }
37
+ }
38
+ }
39
+
40
+ query country($countryCode: SupportedCountry!, $locale: SupportedLocale!) {
41
+ getCountry(countryCode: $countryCode, locale: $locale) {
42
+ name
43
+ code
44
+ continent
45
+ phoneNumberPrefix
46
+ autocompletionField
47
+ provinceKey
48
+ labels {
49
+ address1
50
+ address2
51
+ city
52
+ company
53
+ country
54
+ firstName
55
+ lastName
56
+ phone
57
+ postalCode
58
+ zone
59
+ }
60
+ optionalLabels {
61
+ address2
62
+ }
63
+ formatting {
64
+ edit
65
+ show
66
+ }
67
+ zones {
68
+ name
69
+ code
70
+ }
71
+ }
72
+ }
73
+ `;
74
+ var query$1 = query;
75
+
76
+ exports["default"] = query$1;
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var addressConsts = require('@huckleberry-inc/address-consts');
6
+ var loader = require('./loader.js');
7
+ var format = require('./format.js');
8
+ var AddressFormatter = require('./AddressFormatter.js');
9
+
10
+
11
+
12
+ exports.CountryLoaderError = loader.CountryLoaderError;
13
+ exports.loadCountries = loader.loadCountries;
14
+ exports.loadCountry = loader.loadCountry;
15
+ exports.buildOrderedFields = format.buildOrderedFields;
16
+ exports.formatAddress = format.formatAddress;
17
+ exports["default"] = AddressFormatter["default"];
18
+ Object.keys(addressConsts).forEach(function (k) {
19
+ if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
20
+ enumerable: true,
21
+ get: function () { return addressConsts[k]; }
22
+ });
23
+ });
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var addressConsts = require('@huckleberry-inc/address-consts');
6
+ var graphqlQuery = require('./graphqlQuery.js');
7
+
8
+ const loadCountries = memoizeAsync(async (locale, {
9
+ includeHiddenZones = false
10
+ } = {}) => {
11
+ const response = await fetch(addressConsts.GRAPHQL_ENDPOINT, {
12
+ method: 'POST',
13
+ headers: addressConsts.HEADERS,
14
+ body: JSON.stringify({
15
+ query: graphqlQuery["default"],
16
+ operationName: addressConsts.GraphqlOperationName.Countries,
17
+ variables: {
18
+ locale: locale.replace(/-/, '_').toUpperCase(),
19
+ includeHiddenZones
20
+ }
21
+ })
22
+ });
23
+ const countries = await response.json();
24
+ if (!('data' in countries) && 'errors' in countries) {
25
+ throw new CountryLoaderError(countries);
26
+ }
27
+ return countries.data.countries;
28
+ });
29
+ const loadCountry = memoizeAsync(async (locale, countryCode, {
30
+ includeHiddenZones = false
31
+ } = {}) => {
32
+ const response = await fetch(addressConsts.GRAPHQL_ENDPOINT, {
33
+ method: 'POST',
34
+ headers: addressConsts.HEADERS,
35
+ body: JSON.stringify({
36
+ query: graphqlQuery["default"],
37
+ operationName: addressConsts.GraphqlOperationName.Country,
38
+ variables: {
39
+ countryCode,
40
+ locale: locale.replace(/-/, '_').toUpperCase(),
41
+ includeHiddenZones
42
+ }
43
+ })
44
+ });
45
+ const country = await response.json();
46
+ if (!('data' in country) && 'errors' in country) {
47
+ throw new CountryLoaderError(country);
48
+ }
49
+ return country.data.country;
50
+ });
51
+ class CountryLoaderError extends Error {
52
+ constructor(errors) {
53
+ const errorMessage = errors.errors.map(error => error.message).join('; ');
54
+ super(errorMessage);
55
+ }
56
+ }
57
+ function memoizeAsync(asyncFunction) {
58
+ const cache = {};
59
+ return (...args) => {
60
+ const stringifiedArgs = JSON.stringify(args);
61
+ if (!cache[stringifiedArgs]) {
62
+ cache[stringifiedArgs] = asyncFunction.apply(this, args);
63
+ }
64
+ return cache[stringifiedArgs];
65
+ };
66
+ }
67
+
68
+ exports.CountryLoaderError = CountryLoaderError;
69
+ exports.loadCountries = loadCountries;
70
+ exports.loadCountry = loadCountry;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ exports.FieldName = void 0;
6
+ (function (FieldName) {
7
+ FieldName["FirstName"] = "firstName";
8
+ FieldName["LastName"] = "lastName";
9
+ FieldName["Country"] = "country";
10
+ FieldName["City"] = "city";
11
+ FieldName["PostalCode"] = "zip";
12
+ FieldName["Zone"] = "province";
13
+ FieldName["Address1"] = "address1";
14
+ FieldName["Address2"] = "address2";
15
+ FieldName["Phone"] = "phone";
16
+ FieldName["Company"] = "company";
17
+ })(exports.FieldName || (exports.FieldName = {}));
18
+ const GRAPHQL_ENDPOINT = 'https://atlas.shopifysvc.com/graphql';
19
+ exports.GraphqlOperationName = void 0;
20
+
21
+ /* eslint-disable @typescript-eslint/naming-convention */
22
+ (function (GraphqlOperationName) {
23
+ GraphqlOperationName["Countries"] = "countries";
24
+ GraphqlOperationName["Country"] = "country";
25
+ })(exports.GraphqlOperationName || (exports.GraphqlOperationName = {}));
26
+ const HEADERS = {
27
+ 'Content-Type': 'application/json',
28
+ 'Access-Control-Allow-Origin': '*'
29
+ };
30
+
31
+ exports.GRAPHQL_ENDPOINT = GRAPHQL_ENDPOINT;
32
+ exports.HEADERS = HEADERS;
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var format = require('./format.js');
6
+ var loader = require('./loader.js');
7
+
8
+ const ORDERED_COUNTRIES_CACHE = new Map();
9
+ class AddressFormatter {
10
+ /**
11
+ * Useful in tests or any situation where the cache has undesirable
12
+ * side-effects.
13
+ */
14
+ static resetCache() {
15
+ ORDERED_COUNTRIES_CACHE.clear();
16
+ }
17
+ constructor(locale) {
18
+ this.locale = locale;
19
+ this.locale = locale;
20
+ }
21
+ updateLocale(locale) {
22
+ this.locale = locale;
23
+ }
24
+ async getCountry(countryCode, {
25
+ includeHiddenZones = false
26
+ } = {}) {
27
+ const country = this.loadCountryFromCache(countryCode, includeHiddenZones);
28
+ if (country) return country;
29
+ return loader.loadCountry(this.locale, countryCode, {
30
+ includeHiddenZones
31
+ });
32
+ }
33
+ async getCountries({
34
+ includeHiddenZones = false
35
+ } = {}) {
36
+ const cacheKey = this.cacheKey(this.locale, includeHiddenZones);
37
+ const cachedCountries = ORDERED_COUNTRIES_CACHE.get(cacheKey);
38
+ if (cachedCountries) return cachedCountries;
39
+ const countries = await loader.loadCountries(this.locale, {
40
+ includeHiddenZones
41
+ });
42
+ ORDERED_COUNTRIES_CACHE.set(cacheKey, countries);
43
+ return countries;
44
+ }
45
+ async getZoneName(countryCode, zoneCode) {
46
+ const country = await this.getCountry(countryCode);
47
+ const countryZone = country.zones.find(item => item.code === zoneCode);
48
+ if (!(countryZone !== null && countryZone !== void 0 && countryZone.name)) return undefined;
49
+ return countryZone.name;
50
+ }
51
+
52
+ /* Returns the address ordered in an array based based on the country code
53
+ * Eg.:
54
+ * [
55
+ * 'Shopify',
56
+ * 'First Name Last Name',
57
+ * 'Address 1',
58
+ * 'address2',
59
+ * 'Montréal',
60
+ * 'Canada Quebec H2J 4B7',
61
+ * '514 444 3333'
62
+ * ]
63
+ */
64
+ async format(address) {
65
+ const country = await this.getCountry(address.country);
66
+ return format.formatAddress(address, country);
67
+ }
68
+
69
+ /* Returns an array that shows how to order fields based on the country code
70
+ * Eg.:
71
+ * [
72
+ * ['company'],
73
+ * ['firstName', 'lastName'],
74
+ * ['address1'],
75
+ * ['address2'],
76
+ * ['city'],
77
+ * ['country', 'province', 'zip'],
78
+ * ['phone']
79
+ * ]
80
+ */
81
+ async getOrderedFields(countryCode) {
82
+ const country = await this.getCountry(countryCode);
83
+ return format.buildOrderedFields(country);
84
+ }
85
+ cacheKey(locale, includeHiddenZones) {
86
+ /* Cache list of countries per locale, both with and without hidden zones included */
87
+ return `${locale}-${includeHiddenZones}`;
88
+ }
89
+ loadCountryFromCache(countryCode, includeHiddenZones) {
90
+ const cachedCountries = ORDERED_COUNTRIES_CACHE.get(this.cacheKey(this.locale, includeHiddenZones));
91
+ if (!cachedCountries) return null;
92
+ return cachedCountries.find(({
93
+ code
94
+ }) => code === countryCode);
95
+ }
96
+ }
97
+
98
+ exports["default"] = AddressFormatter;
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var utilities = require('./utilities.js');
6
+
7
+ const LINE_DELIMITER = '_';
8
+ const DEFAULT_FORM_LAYOUT = '{firstName}{lastName}_{company}_{address1}_{address2}_{city}_{country}{province}{zip}_{phone}';
9
+ const DEFAULT_SHOW_LAYOUT = '{lastName} {firstName}_{company}_{address1} {address2}_{city} {province} {zip}_{country}_{phone}';
10
+
11
+ /**
12
+ * When it's time to render any address, use this function so that it's properly
13
+ * formatted for the country's locale.
14
+ *
15
+ * ```typescript
16
+ * ['Shopify', 'Lindenstraße 9-14', '10969 Berlin', 'Germany'];
17
+ * ```
18
+ * @returns all lines of a formatted address as an array of strings.
19
+ */
20
+ function formatAddress(address, country) {
21
+ const layout = country.formatting.show || DEFAULT_SHOW_LAYOUT;
22
+ return layout.split(LINE_DELIMITER).map(lineTemplate => utilities.renderLineTemplate(country, lineTemplate, address).trim());
23
+ }
24
+
25
+ /**
26
+ * In an edit form, this function can be used to properly order all the input
27
+ * fields.
28
+ *
29
+ * ```typescript
30
+ * [
31
+ * ['firstName', 'lastName'],
32
+ * ['company'],
33
+ * ['address1'],
34
+ * ['address2'],
35
+ * ['city'],
36
+ * ['country', 'province', 'zip'],
37
+ * ['phone'],
38
+ * ];
39
+ * ```
40
+ */
41
+ function buildOrderedFields(country) {
42
+ const format = country ? country.formatting.edit : DEFAULT_FORM_LAYOUT;
43
+ return format.split(LINE_DELIMITER).map(lineTemplate => {
44
+ const result = lineTemplate.match(utilities.FIELD_REGEXP);
45
+ if (!result) {
46
+ return [];
47
+ }
48
+ return result.map(field => utilities.FIELDS_MAPPING[field]);
49
+ });
50
+ }
51
+
52
+ exports.buildOrderedFields = buildOrderedFields;
53
+ exports.formatAddress = formatAddress;