create-instantsearch-app 4.10.3 → 5.0.1
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 +55 -0
- package/README.md +1 -0
- package/package.json +4 -3
- package/src/api/__tests__/__snapshots__/index.test.js.snap +3 -3
- package/src/cli/__tests__/getAnswersDefaultValues.js +15 -0
- package/src/cli/__tests__/getAttributesFromIndex.test.js +18 -24
- package/src/cli/__tests__/getConfiguration.test.js +1 -22
- package/src/cli/__tests__/getFacetsFromIndex.test.js +39 -0
- package/src/cli/__tests__/getInformationFromIndex.js +68 -0
- package/src/cli/__tests__/isQuestionAsked.test.js +32 -10
- package/src/cli/__tests__/postProcessAnswers.js +111 -0
- package/src/cli/getAnswersDefaultValues.js +13 -0
- package/src/cli/getAttributesFromIndex.js +6 -4
- package/src/cli/getConfiguration.js +1 -38
- package/src/cli/getFacetsFromIndex.js +18 -0
- package/src/cli/getInformationFromIndex.js +40 -0
- package/src/cli/index.js +191 -113
- package/src/cli/isQuestionAsked.js +9 -16
- package/src/cli/postProcessAnswers.js +77 -0
- package/src/templates/Angular InstantSearch/angular.json +1 -0
- package/src/templates/Angular InstantSearch/package.json +25 -25
- package/src/templates/Angular InstantSearch/src/index.html +1 -0
- package/src/templates/Angular InstantSearch/src/styles.css +10 -2
- package/src/templates/Angular InstantSearch/tsconfig.json +1 -0
- package/src/templates/Autocomplete/.editorconfig +9 -0
- package/src/templates/Autocomplete/.eslintignore +3 -0
- package/src/templates/Autocomplete/.eslintrc.js +20 -0
- package/src/templates/Autocomplete/.gitignore.template +22 -0
- package/src/templates/Autocomplete/.prettierrc +5 -0
- package/src/templates/Autocomplete/.template.js +14 -0
- package/src/templates/Autocomplete/README.md +21 -0
- package/src/templates/Autocomplete/app.js.hbs +53 -0
- package/src/templates/Autocomplete/favicon.png +0 -0
- package/src/templates/Autocomplete/index.html.hbs +19 -0
- package/src/templates/Autocomplete/manifest.webmanifest +15 -0
- package/src/templates/Autocomplete/package.json +36 -0
- package/src/templates/Autocomplete/style.css +20 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.editorconfig +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.eslintignore +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.eslintrc.js +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.gitignore.template +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.prettierrc +0 -0
- package/src/templates/{Autocomplete.js/.template.js → Autocomplete.js 0/.template.js } +1 -1
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/README.md +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/favicon.png +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/index.html.hbs +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/manifest.webmanifest +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/package.json +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/app.css +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/app.js.hbs +0 -0
- package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/index.css +0 -0
- package/src/templates/InstantSearch.js/.template.js +3 -0
- package/src/templates/InstantSearch.js/index.html.hbs +4 -2
- package/src/templates/InstantSearch.js/src/app.js.hbs +24 -0
- package/src/templates/React InstantSearch/.template.js +4 -0
- package/src/templates/React InstantSearch/src/App.js.hbs +13 -2
- package/src/templates/Vue InstantSearch/.template.js +4 -1
- package/src/templates/Vue InstantSearch/src/App.vue +9 -2
- package/src/utils/__tests__/__snapshots__/index.test.js.snap +0 -4
- package/src/utils/__tests__/index.test.js +31 -3
- package/src/utils/index.js +14 -1
- package/src/cli/__tests__/getOptionsFromArguments.test.js +0 -72
- package/src/cli/getOptionsFromArguments.js +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
## [5.0.1](https://github.com/algolia/create-instantsearch-app/compare/5.0.0...5.0.1) (2021-12-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **dynamicWidgets:** allow only dynamicWidgets as attribute ([3717de6](https://github.com/algolia/create-instantsearch-app/commit/3717de6fc5811c191629d366988dcc2e05773a9c))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# [5.0.0](https://github.com/algolia/create-instantsearch-app/compare/4.11.1...5.0.0) (2021-12-02)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **dynamicWidgets:** prevent "ais.dynamicWidgets" attribute showing up ([#542](https://github.com/algolia/create-instantsearch-app/issues/542)) ([559f705](https://github.com/algolia/create-instantsearch-app/commit/559f705ebcef48a3794efd7a588abd187d6642c9))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Code Refactoring
|
|
19
|
+
|
|
20
|
+
* **index:** rewrite answer parsing ([#541](https://github.com/algolia/create-instantsearch-app/issues/541)) ([efd2799](https://github.com/algolia/create-instantsearch-app/commit/efd279943e23545c3c0806f8e9632ae32c83c0d6))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
the program now asks questions if some of the parameters are sent via arguments. Before this, giving an argument would cause it not to ask any questions anymore, even if they still would be useful. You can avoid this behaviour by passing --config or --no-interactive
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## [4.11.1](https://github.com/algolia/create-instantsearch-app/compare/4.11.0...4.11.1) (2021-11-18)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* **react:** add missing configure widget import ([#540](https://github.com/algolia/create-instantsearch-app/issues/540)) ([626a568](https://github.com/algolia/create-instantsearch-app/commit/626a56859c48e756429da66195d7da1a6c79e39e))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# [4.11.0](https://github.com/algolia/create-instantsearch-app/compare/4.10.3...4.11.0) (2021-11-16)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Bug Fixes
|
|
41
|
+
|
|
42
|
+
* **angular:** move styles import to angular.json ([#532](https://github.com/algolia/create-instantsearch-app/issues/532)) ([d2f73b8](https://github.com/algolia/create-instantsearch-app/commit/d2f73b895c0ed7a2530920f770f67879cca0a9a5))
|
|
43
|
+
* **angular:** replace css imports with CDN <link>s ([#537](https://github.com/algolia/create-instantsearch-app/issues/537)) ([fb4c2a8](https://github.com/algolia/create-instantsearch-app/commit/fb4c2a87852fe2f4b3d2289a21234fdf04bddc98))
|
|
44
|
+
* **Angular IS:** enable esModuleInterop ([#527](https://github.com/algolia/create-instantsearch-app/issues/527)) ([502e776](https://github.com/algolia/create-instantsearch-app/commit/502e776cf9ca9eca489d6acbde5f8c603de56bd0))
|
|
45
|
+
* **ci:** replace legacy circleci node images ([#533](https://github.com/algolia/create-instantsearch-app/issues/533)) ([9ffb469](https://github.com/algolia/create-instantsearch-app/commit/9ffb46925db6e2a6e015be98b589f1b7cf22a9f3))
|
|
46
|
+
* **cli:** configure inquirer with correct initial values for attributesToDisplay ([#530](https://github.com/algolia/create-instantsearch-app/issues/530)) ([5cd6142](https://github.com/algolia/create-instantsearch-app/commit/5cd6142d12658dde616690ab1b045c613adf711a))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
* **autocomplete:** add Autocomplete v1 template ([#528](https://github.com/algolia/create-instantsearch-app/issues/528)) ([5c72d05](https://github.com/algolia/create-instantsearch-app/commit/5c72d05100d90900afbc848eaa7417c175c126cc))
|
|
52
|
+
* **facets:** use dynamicWidgets ([#534](https://github.com/algolia/create-instantsearch-app/issues/534)) ([7df3225](https://github.com/algolia/create-instantsearch-app/commit/7df32252a0558f0e09288735cfddd8062af5dc09))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
1
56
|
## [4.10.3](https://github.com/algolia/create-instantsearch-app/compare/4.10.2...4.10.3) (2021-09-01)
|
|
2
57
|
|
|
3
58
|
|
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ $ create-instantsearch-app --help
|
|
|
71
71
|
--library-version <libraryVersion> The version of the library
|
|
72
72
|
--config <config> The configuration file to get the options from
|
|
73
73
|
--no-installation Ignore dependency installation
|
|
74
|
+
--no-interactive Do not ask any interactive questions
|
|
74
75
|
-h, --help output usage information
|
|
75
76
|
```
|
|
76
77
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-instantsearch-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "⚡️ Build InstantSearch apps at the speed of thought",
|
|
6
6
|
"keywords": [
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
"node": ">= 10"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"
|
|
39
|
+
"@algolia/cache-in-memory": "4.11.0",
|
|
40
|
+
"algoliasearch": "4.11.0",
|
|
40
41
|
"chalk": "3.0.0",
|
|
41
|
-
"commander": "4.
|
|
42
|
+
"commander": "4.1.1",
|
|
42
43
|
"inquirer": "8.0.0",
|
|
43
44
|
"jstransformer-handlebars": "1.1.0",
|
|
44
45
|
"latest-semver": "2.0.0",
|
|
@@ -6,10 +6,10 @@ exports[`Options with invalid name throws 1`] = `
|
|
|
6
6
|
- name can only contain URL-friendly characters"
|
|
7
7
|
`;
|
|
8
8
|
|
|
9
|
-
exports[`Options with unknown template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete.js, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
9
|
+
exports[`Options with unknown template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete, Autocomplete.js 0, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
10
10
|
|
|
11
|
-
exports[`Options with wrong template path throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete.js, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
11
|
+
exports[`Options with wrong template path throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete, Autocomplete.js 0, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
12
12
|
|
|
13
13
|
exports[`Options without path throws 1`] = `"The option \`path\` is required."`;
|
|
14
14
|
|
|
15
|
-
exports[`Options without template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete.js, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
15
|
+
exports[`Options without template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, Autocomplete, Autocomplete.js 0, InstantSearch Android, InstantSearch iOS, InstantSearch.js, InstantSearch.js 2, InstantSearch.js widget, JavaScript Client, JavaScript Helper, JavaScript Helper 2, React InstantSearch, React InstantSearch Native, React InstantSearch widget, Vue InstantSearch, Vue InstantSearch 1, Vue InstantSearch 2"`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const getAnswersDefaultValues = require('../getAnswersDefaultValues');
|
|
2
|
+
|
|
3
|
+
test('without attributesToDisplay in configuration', () => {
|
|
4
|
+
const { attributesToDisplay } = getAnswersDefaultValues({}, {}, undefined);
|
|
5
|
+
expect(attributesToDisplay).toBeUndefined();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
test('with attributesToDisplay in configuration', () => {
|
|
9
|
+
const { attributesToDisplay } = getAnswersDefaultValues(
|
|
10
|
+
{},
|
|
11
|
+
{ attributesToDisplay: ['name'] },
|
|
12
|
+
undefined
|
|
13
|
+
);
|
|
14
|
+
expect(attributesToDisplay).toEqual(['name']);
|
|
15
|
+
});
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
const algoliasearch = require('algoliasearch');
|
|
2
1
|
const getAttributesFromIndex = require('../getAttributesFromIndex');
|
|
2
|
+
const getInformationFromIndex = require('../getInformationFromIndex');
|
|
3
3
|
|
|
4
|
-
jest.mock('
|
|
4
|
+
jest.mock('../getInformationFromIndex');
|
|
5
5
|
|
|
6
6
|
test('with search success should fetch attributes', async () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
title: 'title',
|
|
17
|
-
},
|
|
7
|
+
getInformationFromIndex.mockImplementationOnce(() =>
|
|
8
|
+
Promise.resolve({
|
|
9
|
+
hits: [
|
|
10
|
+
{
|
|
11
|
+
_highlightResult: {
|
|
12
|
+
brand: 'brand',
|
|
13
|
+
description: 'description',
|
|
14
|
+
name: 'name',
|
|
15
|
+
title: 'title',
|
|
18
16
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
})
|
|
22
|
-
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
);
|
|
23
21
|
|
|
24
22
|
const attributes = await getAttributesFromIndex({
|
|
25
23
|
appId: 'appId',
|
|
@@ -31,13 +29,9 @@ test('with search success should fetch attributes', async () => {
|
|
|
31
29
|
});
|
|
32
30
|
|
|
33
31
|
test('with search failure should return default attributes', async () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
throw new Error();
|
|
38
|
-
},
|
|
39
|
-
}),
|
|
40
|
-
}));
|
|
32
|
+
getInformationFromIndex.mockImplementationOnce(() =>
|
|
33
|
+
Promise.reject(new Error())
|
|
34
|
+
);
|
|
41
35
|
|
|
42
36
|
const attributes = await getAttributesFromIndex({
|
|
43
37
|
appId: 'appId',
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
const loadJsonFile = require('load-json-file');
|
|
2
|
-
const
|
|
3
|
-
const { getConfiguration, getLibraryVersion } = require('../getConfiguration');
|
|
2
|
+
const getConfiguration = require('../getConfiguration');
|
|
4
3
|
|
|
5
4
|
jest.mock('load-json-file');
|
|
6
5
|
|
|
7
|
-
jest.mock('../../utils', () => ({
|
|
8
|
-
...require.requireActual('../../utils'),
|
|
9
|
-
fetchLibraryVersions: jest.fn(() => Promise.resolve(['1.0.0'])),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
6
|
test('without template throws', async () => {
|
|
13
7
|
expect.assertions(1);
|
|
14
8
|
|
|
@@ -36,21 +30,6 @@ test('with options from arguments and prompt merge', async () => {
|
|
|
36
30
|
);
|
|
37
31
|
});
|
|
38
32
|
|
|
39
|
-
test('without stable version available', async () => {
|
|
40
|
-
utils.fetchLibraryVersions.mockImplementationOnce(() =>
|
|
41
|
-
Promise.resolve(['1.0.0-beta.0'])
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const libraryVersion = await getLibraryVersion(
|
|
45
|
-
{
|
|
46
|
-
template: 'InstantSearch.js',
|
|
47
|
-
},
|
|
48
|
-
utils.getAppTemplateConfig(utils.getTemplatePath('InstantSearch.js'))
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
expect(libraryVersion).toBe('1.0.0-beta.0');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
33
|
test('with config file overrides all options', async () => {
|
|
55
34
|
loadJsonFile.mockImplementationOnce(x => Promise.resolve(x));
|
|
56
35
|
const ignoredOptions = {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const getFacetsFromIndex = require('../getFacetsFromIndex');
|
|
2
|
+
const getInformationFromIndex = require('../getInformationFromIndex');
|
|
3
|
+
|
|
4
|
+
jest.mock('../getInformationFromIndex');
|
|
5
|
+
|
|
6
|
+
test('with search success should fetch attributes', async () => {
|
|
7
|
+
getInformationFromIndex.mockImplementationOnce(() =>
|
|
8
|
+
Promise.resolve({
|
|
9
|
+
facets: {
|
|
10
|
+
abc: {},
|
|
11
|
+
def: {},
|
|
12
|
+
something: {},
|
|
13
|
+
'something.nested': {},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const attributes = await getFacetsFromIndex({
|
|
19
|
+
appId: 'appId',
|
|
20
|
+
apiKey: 'apiKey',
|
|
21
|
+
indexName: 'indexName',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(attributes).toEqual(['abc', 'def', 'something', 'something.nested']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('with search failure should return default attributes', async () => {
|
|
28
|
+
getInformationFromIndex.mockImplementationOnce(() =>
|
|
29
|
+
Promise.reject(new Error())
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const attributes = await getFacetsFromIndex({
|
|
33
|
+
appId: 'appId',
|
|
34
|
+
apiKey: 'apiKey',
|
|
35
|
+
indexName: 'indexName',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(attributes).toEqual([]);
|
|
39
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const algoliasearch = require('algoliasearch');
|
|
2
|
+
const getInformationFromIndex = require('../getInformationFromIndex');
|
|
3
|
+
|
|
4
|
+
jest.mock('algoliasearch', () => {
|
|
5
|
+
const _algoliasearch = jest.fn(() => ({ search: _algoliasearch.__search }));
|
|
6
|
+
_algoliasearch.__search = jest.fn(() =>
|
|
7
|
+
Promise.resolve({
|
|
8
|
+
results: [
|
|
9
|
+
{
|
|
10
|
+
hits: [],
|
|
11
|
+
facets: {},
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return _algoliasearch;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('returns default information', async () => {
|
|
21
|
+
const info = await getInformationFromIndex({
|
|
22
|
+
appId: 'a',
|
|
23
|
+
apiKey: 'a',
|
|
24
|
+
indexName: 'a',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(info).toEqual({ hits: [], facets: {} });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('returns {} on error', async () => {
|
|
31
|
+
algoliasearch.__search.mockImplementationOnce(() =>
|
|
32
|
+
Promise.reject(new Error())
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const info = await getInformationFromIndex({
|
|
36
|
+
appId: 'a',
|
|
37
|
+
apiKey: 'a',
|
|
38
|
+
indexName: 'a',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(info).toEqual({});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('creates client once per credentials', async () => {
|
|
45
|
+
await getInformationFromIndex({
|
|
46
|
+
appId: 'a',
|
|
47
|
+
apiKey: 'a',
|
|
48
|
+
indexName: 'a',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(algoliasearch).toHaveBeenCalledTimes(1);
|
|
52
|
+
|
|
53
|
+
await getInformationFromIndex({
|
|
54
|
+
appId: 'b',
|
|
55
|
+
apiKey: 'b',
|
|
56
|
+
indexName: 'b',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(algoliasearch).toHaveBeenCalledTimes(2);
|
|
60
|
+
|
|
61
|
+
await getInformationFromIndex({
|
|
62
|
+
appId: 'b',
|
|
63
|
+
apiKey: 'b',
|
|
64
|
+
indexName: 'c',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(algoliasearch).toHaveBeenCalledTimes(2);
|
|
68
|
+
});
|
|
@@ -4,30 +4,30 @@ test('with appId undefined should ask', () => {
|
|
|
4
4
|
expect(
|
|
5
5
|
isQuestionAsked({
|
|
6
6
|
question: { name: 'appId', validate: input => Boolean(input) },
|
|
7
|
-
args: { appId: undefined },
|
|
7
|
+
args: { appId: undefined, interactive: true },
|
|
8
8
|
})
|
|
9
|
-
).toBe(
|
|
9
|
+
).toBe(false);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
test('with appId defined should not ask', () => {
|
|
13
13
|
expect(
|
|
14
14
|
isQuestionAsked({
|
|
15
15
|
question: { name: 'appId', validate: input => Boolean(input) },
|
|
16
|
-
args: { appId: 'APP_ID' },
|
|
16
|
+
args: { appId: 'APP_ID', interactive: true },
|
|
17
17
|
})
|
|
18
|
-
).toBe(
|
|
18
|
+
).toBe(true);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
test('with
|
|
21
|
+
test('with invalid template should ask', () => {
|
|
22
22
|
expect(
|
|
23
23
|
isQuestionAsked({
|
|
24
24
|
question: {
|
|
25
25
|
name: 'template',
|
|
26
26
|
validate: () => false,
|
|
27
27
|
},
|
|
28
|
-
args: { template: 'Unvalid' },
|
|
28
|
+
args: { template: 'Unvalid', interactive: true },
|
|
29
29
|
})
|
|
30
|
-
).toBe(
|
|
30
|
+
).toBe(false);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
test('with valid template should not ask', () => {
|
|
@@ -37,9 +37,9 @@ test('with valid template should not ask', () => {
|
|
|
37
37
|
name: 'template',
|
|
38
38
|
validate: () => true,
|
|
39
39
|
},
|
|
40
|
-
args: { template: 'InstantSearch.js' },
|
|
40
|
+
args: { template: 'InstantSearch.js', interactive: true },
|
|
41
41
|
})
|
|
42
|
-
).toBe(
|
|
42
|
+
).toBe(true);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
test('with indexName should ask attributesToDisplay', () => {
|
|
@@ -48,7 +48,29 @@ test('with indexName should ask attributesToDisplay', () => {
|
|
|
48
48
|
question: {
|
|
49
49
|
name: 'attributesToDisplay',
|
|
50
50
|
},
|
|
51
|
-
args: { indexName: 'INDEX_NAME' },
|
|
51
|
+
args: { indexName: 'INDEX_NAME', interactive: true },
|
|
52
52
|
})
|
|
53
53
|
).toBe(false);
|
|
54
54
|
});
|
|
55
|
+
|
|
56
|
+
test('with config it does not ask', () => {
|
|
57
|
+
expect(
|
|
58
|
+
isQuestionAsked({
|
|
59
|
+
question: {
|
|
60
|
+
name: 'attributesToDisplay',
|
|
61
|
+
},
|
|
62
|
+
args: { config: '' },
|
|
63
|
+
})
|
|
64
|
+
).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('with --no-interactive it does not ask', () => {
|
|
68
|
+
expect(
|
|
69
|
+
isQuestionAsked({
|
|
70
|
+
question: {
|
|
71
|
+
name: 'attributesToDisplay',
|
|
72
|
+
},
|
|
73
|
+
args: { interactive: false },
|
|
74
|
+
})
|
|
75
|
+
).toBe(true);
|
|
76
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const postProcessAnswers = require('../postProcessAnswers');
|
|
2
|
+
const utils = require('../../utils');
|
|
3
|
+
|
|
4
|
+
jest.mock('../../utils', () => ({
|
|
5
|
+
...require.requireActual('../../utils'),
|
|
6
|
+
fetchLibraryVersions: jest.fn(() => Promise.resolve(['1.0.0'])),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
test('merges configuration and answers', async () => {
|
|
10
|
+
expect(
|
|
11
|
+
await postProcessAnswers({
|
|
12
|
+
configuration: { attributesForFaceting: ['test'], b: 1 },
|
|
13
|
+
answers: { attributesForFaceting: [] },
|
|
14
|
+
templateConfig: {},
|
|
15
|
+
optionsFromArguments: {},
|
|
16
|
+
})
|
|
17
|
+
).toEqual(expect.objectContaining({ attributesForFaceting: [], b: 1 }));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('libraryVersion', () => {
|
|
21
|
+
test('library version from answers is used', async () => {
|
|
22
|
+
const { libraryVersion } = await postProcessAnswers({
|
|
23
|
+
configuration: {},
|
|
24
|
+
answers: {
|
|
25
|
+
template: 'InstantSearch.js',
|
|
26
|
+
attributesForFaceting: [],
|
|
27
|
+
libraryVersion: '0.1.2',
|
|
28
|
+
},
|
|
29
|
+
optionsFromArguments: {},
|
|
30
|
+
templateConfig: utils.getAppTemplateConfig(
|
|
31
|
+
utils.getTemplatePath('InstantSearch.js')
|
|
32
|
+
),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(libraryVersion).toBe('0.1.2');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('library version falls back to beta if it is the only available', async () => {
|
|
39
|
+
utils.fetchLibraryVersions.mockImplementationOnce(() =>
|
|
40
|
+
Promise.resolve(['1.0.0-beta.0'])
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const { libraryVersion } = await postProcessAnswers({
|
|
44
|
+
configuration: {},
|
|
45
|
+
answers: {
|
|
46
|
+
template: 'InstantSearch.js',
|
|
47
|
+
attributesForFaceting: [],
|
|
48
|
+
},
|
|
49
|
+
optionsFromArguments: {},
|
|
50
|
+
templateConfig: utils.getAppTemplateConfig(
|
|
51
|
+
utils.getTemplatePath('InstantSearch.js')
|
|
52
|
+
),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(libraryVersion).toBe('1.0.0-beta.0');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('creates alternative names', async () => {
|
|
60
|
+
expect(
|
|
61
|
+
await postProcessAnswers({
|
|
62
|
+
configuration: {},
|
|
63
|
+
answers: {
|
|
64
|
+
attributesForFaceting: [],
|
|
65
|
+
organization: 'algolia',
|
|
66
|
+
name: 'date-range',
|
|
67
|
+
},
|
|
68
|
+
templateConfig: {
|
|
69
|
+
packageNamePrefix: 'instantsearch-widget-',
|
|
70
|
+
},
|
|
71
|
+
optionsFromArguments: {},
|
|
72
|
+
})
|
|
73
|
+
).toEqual(
|
|
74
|
+
expect.objectContaining({
|
|
75
|
+
packageName: '@algolia/instantsearch-widget-date-range',
|
|
76
|
+
widgetType: 'algolia.date-range',
|
|
77
|
+
camelCaseName: 'dateRange',
|
|
78
|
+
pascalCaseName: 'DateRange',
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('detects dynamic widgets', async () => {
|
|
84
|
+
expect(
|
|
85
|
+
await postProcessAnswers({
|
|
86
|
+
configuration: {},
|
|
87
|
+
templateConfig: {},
|
|
88
|
+
optionsFromArguments: {},
|
|
89
|
+
answers: { attributesForFaceting: ['ais.dynamicWidgets', 'test'] },
|
|
90
|
+
})
|
|
91
|
+
).toEqual(
|
|
92
|
+
expect.objectContaining({
|
|
93
|
+
attributesForFaceting: ['test'],
|
|
94
|
+
flags: { dynamicWidgets: true },
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(
|
|
99
|
+
await postProcessAnswers({
|
|
100
|
+
configuration: {},
|
|
101
|
+
templateConfig: {},
|
|
102
|
+
optionsFromArguments: {},
|
|
103
|
+
answers: { attributesForFaceting: ['test'] },
|
|
104
|
+
})
|
|
105
|
+
).toEqual(
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
attributesForFaceting: ['test'],
|
|
108
|
+
flags: { dynamicWidgets: false },
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = function getAnswersDefaultValues(
|
|
2
|
+
optionsFromArguments,
|
|
3
|
+
configuration,
|
|
4
|
+
template
|
|
5
|
+
) {
|
|
6
|
+
return {
|
|
7
|
+
...configuration,
|
|
8
|
+
...optionsFromArguments,
|
|
9
|
+
template,
|
|
10
|
+
// name has a default of '', as it's a special case in Commander
|
|
11
|
+
name: optionsFromArguments.name || configuration.name,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
const
|
|
1
|
+
const getInformationFromIndex = require('./getInformationFromIndex');
|
|
2
2
|
|
|
3
3
|
module.exports = async function getAttributesFromIndex({
|
|
4
4
|
appId,
|
|
5
5
|
apiKey,
|
|
6
6
|
indexName,
|
|
7
7
|
} = {}) {
|
|
8
|
-
const client = algoliasearch(appId, apiKey);
|
|
9
|
-
const index = client.initIndex(indexName);
|
|
10
8
|
const defaultAttributes = ['title', 'name', 'description'];
|
|
11
9
|
let attributes = [];
|
|
12
10
|
|
|
13
11
|
try {
|
|
14
|
-
const { hits } = await
|
|
12
|
+
const { hits } = await getInformationFromIndex({
|
|
13
|
+
appId,
|
|
14
|
+
apiKey,
|
|
15
|
+
indexName,
|
|
16
|
+
});
|
|
15
17
|
const [firstHit] = hits;
|
|
16
18
|
const highlightedAttributes = Object.keys(firstHit._highlightResult);
|
|
17
19
|
attributes = [
|
|
@@ -1,37 +1,4 @@
|
|
|
1
|
-
const latestSemver = require('latest-semver');
|
|
2
1
|
const loadJsonFile = require('load-json-file');
|
|
3
|
-
const camelCase = require('lodash.camelcase');
|
|
4
|
-
|
|
5
|
-
const { fetchLibraryVersions } = require('../utils');
|
|
6
|
-
|
|
7
|
-
function capitalize(str) {
|
|
8
|
-
return str.substr(0, 1).toUpperCase() + str.substr(1);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function createNameAlternatives({ organization, name, templateConfig }) {
|
|
12
|
-
return {
|
|
13
|
-
packageName: `@${organization}/${templateConfig.packageNamePrefix ||
|
|
14
|
-
''}${name}`,
|
|
15
|
-
widgetType: `${organization}.${name}`,
|
|
16
|
-
camelCaseName: camelCase(name),
|
|
17
|
-
pascalCaseName: capitalize(camelCase(name)),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function getLibraryVersion(config, templateConfig) {
|
|
22
|
-
const { libraryName } = templateConfig;
|
|
23
|
-
const { libraryVersion } = config;
|
|
24
|
-
|
|
25
|
-
if (libraryName && !libraryVersion) {
|
|
26
|
-
const versions = await fetchLibraryVersions(libraryName);
|
|
27
|
-
|
|
28
|
-
// Return the latest available version when
|
|
29
|
-
// the stable version is not available
|
|
30
|
-
return latestSemver(versions) || versions[0];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return libraryVersion;
|
|
34
|
-
}
|
|
35
2
|
|
|
36
3
|
async function getConfiguration({ options = {}, answers = {} } = {}) {
|
|
37
4
|
const config = options.config
|
|
@@ -45,8 +12,4 @@ async function getConfiguration({ options = {}, answers = {} } = {}) {
|
|
|
45
12
|
return config;
|
|
46
13
|
}
|
|
47
14
|
|
|
48
|
-
module.exports =
|
|
49
|
-
getConfiguration,
|
|
50
|
-
getLibraryVersion,
|
|
51
|
-
createNameAlternatives,
|
|
52
|
-
};
|
|
15
|
+
module.exports = getConfiguration;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const getInformationFromIndex = require('./getInformationFromIndex');
|
|
2
|
+
|
|
3
|
+
module.exports = async function getFacetsFromIndex({
|
|
4
|
+
appId,
|
|
5
|
+
apiKey,
|
|
6
|
+
indexName,
|
|
7
|
+
} = {}) {
|
|
8
|
+
try {
|
|
9
|
+
const { facets } = await getInformationFromIndex({
|
|
10
|
+
appId,
|
|
11
|
+
apiKey,
|
|
12
|
+
indexName,
|
|
13
|
+
});
|
|
14
|
+
return Object.keys(facets);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const algoliasearch = require('algoliasearch');
|
|
2
|
+
const { createInMemoryCache } = require('@algolia/cache-in-memory');
|
|
3
|
+
|
|
4
|
+
const clients = new Map();
|
|
5
|
+
function getClient(appId, apiKey) {
|
|
6
|
+
const key = [appId, apiKey].join('__');
|
|
7
|
+
let client = clients.get(key);
|
|
8
|
+
if (!client) {
|
|
9
|
+
client = algoliasearch(appId, apiKey, {
|
|
10
|
+
responsesCache: createInMemoryCache(),
|
|
11
|
+
requestsCache: createInMemoryCache(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
clients.set(key, client);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return client;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function getInformationFromIndex({ appId, apiKey, indexName }) {
|
|
21
|
+
try {
|
|
22
|
+
const client = getClient(appId, apiKey);
|
|
23
|
+
return await client
|
|
24
|
+
.search([
|
|
25
|
+
{
|
|
26
|
+
indexName,
|
|
27
|
+
params: {
|
|
28
|
+
hitsPerPage: 1,
|
|
29
|
+
facets: '*',
|
|
30
|
+
maxValuesPerFacet: 1,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
])
|
|
34
|
+
.then(({ results: [result] }) => result);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = getInformationFromIndex;
|