@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.
- package/README.md +151 -0
- package/build/cjs/AddressFormatter.js +98 -0
- package/build/cjs/format.js +53 -0
- package/build/cjs/graphqlQuery.js +76 -0
- package/build/cjs/index.js +23 -0
- package/build/cjs/loader.js +70 -0
- package/build/cjs/node_modules/@shopify/address-consts/build/esm/index.mjs.js +32 -0
- package/build/cjs/packages/address/src/AddressFormatter.js +98 -0
- package/build/cjs/packages/address/src/format.js +53 -0
- package/build/cjs/packages/address/src/graphqlQuery.js +76 -0
- package/build/cjs/packages/address/src/index.js +27 -0
- package/build/cjs/packages/address/src/loader.js +70 -0
- package/build/cjs/packages/address/src/utilities.js +65 -0
- package/build/cjs/utilities.js +65 -0
- package/build/esm/AddressFormatter.mjs +94 -0
- package/build/esm/format.mjs +48 -0
- package/build/esm/graphqlQuery.mjs +72 -0
- package/build/esm/index.mjs +4 -0
- package/build/esm/loader.mjs +64 -0
- package/build/esm/node_modules/@shopify/address-consts/build/esm/index.mjs.mjs +27 -0
- package/build/esm/packages/address/src/AddressFormatter.mjs +94 -0
- package/build/esm/packages/address/src/format.mjs +48 -0
- package/build/esm/packages/address/src/graphqlQuery.mjs +72 -0
- package/build/esm/packages/address/src/index.mjs +4 -0
- package/build/esm/packages/address/src/loader.mjs +64 -0
- package/build/esm/packages/address/src/utilities.mjs +59 -0
- package/build/esm/utilities.mjs +59 -0
- package/build/esnext/AddressFormatter.esnext +94 -0
- package/build/esnext/format.esnext +48 -0
- package/build/esnext/graphqlQuery.esnext +72 -0
- package/build/esnext/index.esnext +4 -0
- package/build/esnext/loader.esnext +64 -0
- package/build/esnext/node_modules/@shopify/address-consts/build/esm/index.mjs.esnext +27 -0
- package/build/esnext/packages/address/src/AddressFormatter.esnext +94 -0
- package/build/esnext/packages/address/src/format.esnext +48 -0
- package/build/esnext/packages/address/src/graphqlQuery.esnext +72 -0
- package/build/esnext/packages/address/src/index.esnext +4 -0
- package/build/esnext/packages/address/src/loader.esnext +64 -0
- package/build/esnext/packages/address/src/utilities.esnext +59 -0
- package/build/esnext/utilities.esnext +59 -0
- package/build/ts/AddressFormatter.d.ts +23 -0
- package/build/ts/AddressFormatter.d.ts.map +1 -0
- package/build/ts/format.d.ts +29 -0
- package/build/ts/format.d.ts.map +1 -0
- package/build/ts/graphqlQuery.d.ts +3 -0
- package/build/ts/graphqlQuery.d.ts.map +1 -0
- package/build/ts/index.d.ts +5 -0
- package/build/ts/index.d.ts.map +1 -0
- package/build/ts/loader.d.ts +11 -0
- package/build/ts/loader.d.ts.map +1 -0
- package/build/ts/utilities.d.ts +8 -0
- package/build/ts/utilities.d.ts.map +1 -0
- package/index.esnext +2 -0
- package/index.js +1 -0
- package/index.mjs +2 -0
- 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
|
+
[](https://github.com/Shopify/quilt/actions?query=workflow%3ANode-CI)
|
|
12
|
+
[](https://github.com/Shopify/quilt/actions?query=workflow%3ARuby-CI)
|
|
13
|
+
[](LICENSE.md) [](https://badge.fury.io/js/%40shopify%2Faddress)
|
|
14
|
+

|
|
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;
|