create-instantsearch-app 4.10.2 → 5.0.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 (63) hide show
  1. package/CHANGELOG.md +94 -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 -0
  54. package/src/templates/InstantSearch.js/src/app.js.hbs +25 -1
  55. package/src/templates/React InstantSearch/.template.js +4 -0
  56. package/src/templates/React InstantSearch/src/App.js.hbs +13 -0
  57. package/src/templates/Vue InstantSearch/.template.js +4 -1
  58. package/src/templates/Vue InstantSearch/src/App.vue +9 -0
  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,97 @@
1
+ # [5.0.0](https://github.com/algolia/create-instantsearch-app/compare/4.11.1...5.0.0) (2021-12-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **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))
7
+
8
+
9
+ ### Code Refactoring
10
+
11
+ * **index:** rewrite answer parsing ([#541](https://github.com/algolia/create-instantsearch-app/issues/541)) ([efd2799](https://github.com/algolia/create-instantsearch-app/commit/efd279943e23545c3c0806f8e9632ae32c83c0d6))
12
+
13
+
14
+ ### BREAKING CHANGES
15
+
16
+ * **index:** 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
17
+
18
+ * postprocess answers
19
+
20
+ * e2e installs should not have any asked questions
21
+
22
+ * make e2e build test pass
23
+
24
+ * more info
25
+
26
+ * update to newer commander to fix the "name" issue
27
+
28
+ * refactor: validate appPath like appName
29
+
30
+ * error for empty string path
31
+
32
+ * clean lockfile
33
+
34
+ * refactor initial questions
35
+
36
+ * silently check for existing answers on appName
37
+
38
+ * don't ask initial questions if config is set
39
+
40
+ * introduce --no-interactive, mostly for test
41
+
42
+ * write tests
43
+
44
+ * fix argument
45
+
46
+ * undo change
47
+
48
+ * make .default apply always
49
+
50
+ * refactor templates to be part of initialQuestions
51
+
52
+ * cover initialQuestions in tests
53
+
54
+ * fix error
55
+
56
+
57
+
58
+ ## [4.11.1](https://github.com/algolia/create-instantsearch-app/compare/4.11.0...4.11.1) (2021-11-18)
59
+
60
+
61
+ ### Bug Fixes
62
+
63
+ * **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))
64
+
65
+
66
+
67
+ # [4.11.0](https://github.com/algolia/create-instantsearch-app/compare/4.10.3...4.11.0) (2021-11-16)
68
+
69
+
70
+ ### Bug Fixes
71
+
72
+ * **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))
73
+ * **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))
74
+ * **Angular IS:** enable esModuleInterop ([#527](https://github.com/algolia/create-instantsearch-app/issues/527)) ([502e776](https://github.com/algolia/create-instantsearch-app/commit/502e776cf9ca9eca489d6acbde5f8c603de56bd0))
75
+ * **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))
76
+ * **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))
77
+
78
+
79
+ ### Features
80
+
81
+ * **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))
82
+ * **facets:** use dynamicWidgets ([#534](https://github.com/algolia/create-instantsearch-app/issues/534)) ([7df3225](https://github.com/algolia/create-instantsearch-app/commit/7df32252a0558f0e09288735cfddd8062af5dc09))
83
+
84
+
85
+
86
+ ## [4.10.3](https://github.com/algolia/create-instantsearch-app/compare/4.10.2...4.10.3) (2021-09-01)
87
+
88
+
89
+ ### Bug Fixes
90
+
91
+ * **js:** enable typescript completion using window ([1fd684b](https://github.com/algolia/create-instantsearch-app/commit/1fd684bf4c5c15b0f7c5d20a6bcf25037ddf9684))
92
+
93
+
94
+
1
95
  ## [4.10.2](https://github.com/algolia/create-instantsearch-app/compare/4.10.1...4.10.2) (2021-09-01)
2
96
 
3
97
 
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.2",
3
+ "version": "5.0.0",
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
+ };