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.
Files changed (63) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +1 -0
  3. package/package.json +4 -3
  4. package/src/api/__tests__/__snapshots__/index.test.js.snap +3 -3
  5. package/src/cli/__tests__/getAnswersDefaultValues.js +15 -0
  6. package/src/cli/__tests__/getAttributesFromIndex.test.js +18 -24
  7. package/src/cli/__tests__/getConfiguration.test.js +1 -22
  8. package/src/cli/__tests__/getFacetsFromIndex.test.js +39 -0
  9. package/src/cli/__tests__/getInformationFromIndex.js +68 -0
  10. package/src/cli/__tests__/isQuestionAsked.test.js +32 -10
  11. package/src/cli/__tests__/postProcessAnswers.js +111 -0
  12. package/src/cli/getAnswersDefaultValues.js +13 -0
  13. package/src/cli/getAttributesFromIndex.js +6 -4
  14. package/src/cli/getConfiguration.js +1 -38
  15. package/src/cli/getFacetsFromIndex.js +18 -0
  16. package/src/cli/getInformationFromIndex.js +40 -0
  17. package/src/cli/index.js +191 -113
  18. package/src/cli/isQuestionAsked.js +9 -16
  19. package/src/cli/postProcessAnswers.js +77 -0
  20. package/src/templates/Angular InstantSearch/angular.json +1 -0
  21. package/src/templates/Angular InstantSearch/package.json +25 -25
  22. package/src/templates/Angular InstantSearch/src/index.html +1 -0
  23. package/src/templates/Angular InstantSearch/src/styles.css +10 -2
  24. package/src/templates/Angular InstantSearch/tsconfig.json +1 -0
  25. package/src/templates/Autocomplete/.editorconfig +9 -0
  26. package/src/templates/Autocomplete/.eslintignore +3 -0
  27. package/src/templates/Autocomplete/.eslintrc.js +20 -0
  28. package/src/templates/Autocomplete/.gitignore.template +22 -0
  29. package/src/templates/Autocomplete/.prettierrc +5 -0
  30. package/src/templates/Autocomplete/.template.js +14 -0
  31. package/src/templates/Autocomplete/README.md +21 -0
  32. package/src/templates/Autocomplete/app.js.hbs +53 -0
  33. package/src/templates/Autocomplete/favicon.png +0 -0
  34. package/src/templates/Autocomplete/index.html.hbs +19 -0
  35. package/src/templates/Autocomplete/manifest.webmanifest +15 -0
  36. package/src/templates/Autocomplete/package.json +36 -0
  37. package/src/templates/Autocomplete/style.css +20 -0
  38. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.editorconfig +0 -0
  39. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.eslintignore +0 -0
  40. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.eslintrc.js +0 -0
  41. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.gitignore.template +0 -0
  42. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/.prettierrc +0 -0
  43. package/src/templates/{Autocomplete.js/.template.js → Autocomplete.js 0/.template.js } +1 -1
  44. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/README.md +0 -0
  45. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/favicon.png +0 -0
  46. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/index.html.hbs +0 -0
  47. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/manifest.webmanifest +0 -0
  48. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/package.json +0 -0
  49. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/app.css +0 -0
  50. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/app.js.hbs +0 -0
  51. package/src/templates/{Autocomplete.js → Autocomplete.js 0}/src/index.css +0 -0
  52. package/src/templates/InstantSearch.js/.template.js +3 -0
  53. package/src/templates/InstantSearch.js/index.html.hbs +4 -2
  54. package/src/templates/InstantSearch.js/src/app.js.hbs +24 -0
  55. package/src/templates/React InstantSearch/.template.js +4 -0
  56. package/src/templates/React InstantSearch/src/App.js.hbs +13 -2
  57. package/src/templates/Vue InstantSearch/.template.js +4 -1
  58. package/src/templates/Vue InstantSearch/src/App.vue +9 -2
  59. package/src/utils/__tests__/__snapshots__/index.test.js.snap +0 -4
  60. package/src/utils/__tests__/index.test.js +31 -3
  61. package/src/utils/index.js +14 -1
  62. package/src/cli/__tests__/getOptionsFromArguments.test.js +0 -72
  63. 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": "4.10.3",
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
- "algoliasearch": "3.35.1",
39
+ "@algolia/cache-in-memory": "4.11.0",
40
+ "algoliasearch": "4.11.0",
40
41
  "chalk": "3.0.0",
41
- "commander": "4.0.1",
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('algoliasearch');
4
+ jest.mock('../getInformationFromIndex');
5
5
 
6
6
  test('with search success should fetch attributes', async () => {
7
- algoliasearch.mockImplementationOnce(() => ({
8
- initIndex: () => ({
9
- search: () => ({
10
- hits: [
11
- {
12
- _highlightResult: {
13
- brand: 'brand',
14
- description: 'description',
15
- name: 'name',
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
- algoliasearch.mockImplementationOnce(() => ({
35
- initIndex: () => ({
36
- search: () => {
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 utils = require('../../utils');
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(true);
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(false);
18
+ ).toBe(true);
19
19
  });
20
20
 
21
- test('with unvalid template should ask', () => {
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(true);
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(false);
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 algoliasearch = require('algoliasearch');
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 index.search({ hitsPerPage: 1 });
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;