├── .circleci
└── config.yml
├── .env.example
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.js
├── .yarn
└── releases
│ └── yarn-3.2.0.cjs
├── .yarnrc.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── README.md
├── docs
└── screenshots
│ └── frontend
│ ├── dark-theme.png
│ └── normal-theme.png
├── frontend
├── CONTRIBUTING.md
├── README.md
├── package.json
├── src
│ ├── AlgoliasearchNetlify.ts
│ ├── AutocompleteWrapper.ts
│ ├── index.scss
│ ├── index.ts
│ ├── templates.tsx
│ └── types
│ │ ├── global.d.ts
│ │ ├── index.ts
│ │ ├── options.ts
│ │ └── record.ts
├── tsconfig.json
└── webpack.config.js
├── jest.config.js
├── logo.png
├── netlify.toml
├── package.json
├── plugin
├── .nodemon.json
├── CONTRIBUTING.md
├── README.md
├── manifest.yml
├── package.json
├── src
│ ├── dev.ts
│ ├── escapeRegExp.ts
│ ├── index.ts
│ ├── starMatch.test.ts
│ ├── starMatch.ts
│ └── types.ts
└── tsconfig.json
├── public
├── 1.html
├── 2.html
├── README.md
├── favicon.ico
└── index.html
├── renovate.json
├── scripts
├── dev_plugin.sh
├── generate_netlify_toml.sh
└── release.sh
├── tsconfig.base.json
└── yarn.lock
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | aliases:
3 | # Forward the current folder when using wokflows
4 | # persist-to-workspace & attach-workspace
5 | - &persist-work-dir
6 | root: .
7 | paths: .
8 |
9 | - &attach-work-dir
10 | at: ~/app/
11 |
12 | # Cache Management
13 | - &restore_node_modules
14 | keys:
15 | - v2.8-node-modules-{{ arch }}-{{ checksum "yarn.lock" }}
16 |
17 | - &save_node_modules
18 | key: v2.8-node-modules-{{ arch }}-{{ checksum "yarn.lock" }}
19 | paths:
20 | - .yarn/cache
21 | - node_modules
22 | - src/plugin/node_modules
23 | - src/frontend/node_modules
24 |
25 | # Dependencies
26 | - &yarn
27 | name: Run Yarn
28 | command: |
29 | yarn install --immutable
30 |
31 | defaults: &defaults
32 | working_directory: ~/app
33 | docker:
34 | - image: cimg/node:16.14.0
35 | environment:
36 | NODE_ENV: 'test'
37 |
38 | version: 2
39 |
40 | jobs:
41 | checkout:
42 | <<: *defaults
43 |
44 | steps:
45 | - checkout
46 |
47 | - restore_cache: *restore_node_modules
48 | - run: *yarn
49 |
50 | - save_cache: *save_node_modules
51 | - persist-to-workspace: *persist-work-dir
52 |
53 | lint:
54 | <<: *defaults
55 |
56 | steps:
57 | - attach-workspace: *attach-work-dir
58 |
59 | - run: yarn lint
60 |
61 | test:
62 | <<: *defaults
63 |
64 | steps:
65 | - attach-workspace: *attach-work-dir
66 |
67 | - run: yarn test
68 |
69 | workflows:
70 | version: 2
71 | suite:
72 | jobs:
73 | - checkout
74 | - lint:
75 | requires:
76 | - checkout
77 | - test:
78 | requires:
79 | - checkout
80 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | ALGOLIA_API_KEY=
2 | ALGOLIA_BASE_URL=https://crawler.algolia.com
3 |
4 | # Netlify spec
5 | SITE_NAME="algoliasearch-netlify"
6 | DEPLOY_PRIME_URL="https://master--algoliasearch-netlify.netlify.app"
7 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-commonjs
2 | module.exports = {
3 | env: {
4 | browser: true, // For frontend only
5 | es2020: true,
6 | jest: true,
7 | },
8 | extends: ['algolia', 'algolia/jest', 'algolia/react', 'algolia/typescript'],
9 | parser: '@typescript-eslint/parser',
10 | parserOptions: {
11 | ecmaVersion: 11,
12 | sourceType: 'module',
13 | },
14 | plugins: ['prettier', '@typescript-eslint', 'import', 'algolia'],
15 | rules: {
16 | 'algolia/func-style-toplevel': 'error',
17 |
18 | 'arrow-body-style': 'off',
19 | 'no-console': 'off',
20 | 'no-continue': 'off',
21 | 'no-loop-func': 'off',
22 | 'consistent-return': 'off',
23 | '@typescript-eslint/no-unused-vars': 'warn',
24 |
25 | '@typescript-eslint/explicit-member-accessibility': [
26 | 'error',
27 | { accessibility: 'no-public' },
28 | ],
29 | 'react/react-in-jsx-scope': 'off',
30 |
31 | // TMP
32 | 'jsdoc/check-examples': ['off'],
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 |
84 | # Gatsby files
85 | .cache/
86 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
87 | # https://nextjs.org/blog/next-9-1#public-directory-support
88 | # public
89 |
90 | # vuepress build output
91 | .vuepress/dist
92 |
93 | # Serverless directories
94 | .serverless/
95 |
96 | # FuseBox cache
97 | .fusebox/
98 |
99 | # DynamoDB Local files
100 | .dynamodb/
101 |
102 | # TernJS port file
103 | .tern-port
104 |
105 | # Local Netlify folder
106 | .netlify
107 |
108 | # Built files
109 | dist/
110 |
111 | # IDEs
112 | .vscode
113 | .idea
114 |
115 | # npm package lock
116 | package-lock.json
117 |
118 | # .env
119 | .env
120 |
121 | .DS_Store
122 |
123 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
124 | .yarn/*
125 | !.yarn/releases
126 | !.yarn/plugins
127 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.14.2
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | frontend/README.md
2 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true,
6 | printWidth: 80,
7 | }
8 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-3.2.0.cjs
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.15](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.14...v1.0.15) (2022-04-25)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * ci with yarn3 ([f4475b6](https://github.com/algolia/algoliasearch-netlify/commit/f4475b643d8b89ea9faaa063432be028bb3e8a04))
7 | * upgrade yarn ([db83c9c](https://github.com/algolia/algoliasearch-netlify/commit/db83c9cd3a48e0c83095933f2a37fc9eb9abee02))
8 |
9 |
10 | ### Reverts
11 |
12 | * use TS ([967e96d](https://github.com/algolia/algoliasearch-netlify/commit/967e96d345ceb9e27959c28985d976b6b89acce7))
13 |
14 |
15 |
16 | ## [1.0.14](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.13...v1.0.14) (2022-02-11)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * type error + unecessary build step ([f6c3a8b](https://github.com/algolia/algoliasearch-netlify/commit/f6c3a8bd371d5025a7bce1338ec311cacc8ec7c9))
22 | * yarn build leftover ([e81979a](https://github.com/algolia/algoliasearch-netlify/commit/e81979a9db581cdc2e1d56939375b6546730960f))
23 |
24 |
25 | ### Reverts
26 |
27 | * Revert "fix(deps): update dependency node-fetch to v3 (#448)" ([73f3704](https://github.com/algolia/algoliasearch-netlify/commit/73f3704ffea8a7c0487ffacce3863d27d08aa862)), closes [#448](https://github.com/algolia/algoliasearch-netlify/issues/448)
28 |
29 |
30 |
31 | ## [1.0.13](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.12...v1.0.13) (2022-01-26)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * **deps:** update dependency node-fetch to v3 ([#448](https://github.com/algolia/algoliasearch-netlify/issues/448)) ([ec8da2d](https://github.com/algolia/algoliasearch-netlify/commit/ec8da2dce1c86b56ee2aca46a3a5a0417bdde00b))
37 | * netlify use TS directly ([bcaeb71](https://github.com/algolia/algoliasearch-netlify/commit/bcaeb713decd6ec29c723c5ca1aa5c1ddfe4c89e))
38 | * **deps:** update dependency node-fetch to v2.6.6 ([#545](https://github.com/algolia/algoliasearch-netlify/issues/545)) ([1970c75](https://github.com/algolia/algoliasearch-netlify/commit/1970c75d15caa16ccee2a8b746c074dae2acd217))
39 | * **deps:** update dependency node-fetch to v2.6.7 ([#672](https://github.com/algolia/algoliasearch-netlify/issues/672)) ([125a4d5](https://github.com/algolia/algoliasearch-netlify/commit/125a4d54496934a0d4ef7dbf2205e3bd2a2079c5))
40 |
41 |
42 |
43 | ## [1.0.12](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.11...v1.0.12) (2021-11-03)
44 |
45 |
46 | ### Bug Fixes
47 |
48 | * upgrade autocomplete.js ([1e321a1](https://github.com/algolia/algoliasearch-netlify/commit/1e321a1eb6f5ae5d97f961928345be14eb43574e))
49 |
50 |
51 |
52 | ## [1.0.11](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.10...v1.0.11) (2021-11-03)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * hierarchy to breadcrumbs wrong attribute displayed ([4d01afe](https://github.com/algolia/algoliasearch-netlify/commit/4d01afe976795e2094906a85f45ea93bf10cd750))
58 |
59 |
60 |
61 | ## [1.0.10](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.9...v1.0.10) (2021-10-11)
62 |
63 |
64 | ### Bug Fixes
65 |
66 | * bump version ([c6ac356](https://github.com/algolia/algoliasearch-netlify/commit/c6ac35660e740363b9215059ac345adedf9ff94e))
67 | * **deps:** update dependency node-fetch to v2.6.2 ([#451](https://github.com/algolia/algoliasearch-netlify/issues/451)) ([b170103](https://github.com/algolia/algoliasearch-netlify/commit/b1701031f3536baa635a3ffeb08b55127ac2cd6d))
68 | * **deps:** update dependency node-fetch to v2.6.5 ([#482](https://github.com/algolia/algoliasearch-netlify/issues/482)) ([df17f0b](https://github.com/algolia/algoliasearch-netlify/commit/df17f0bb057df0ebdde80f35b88fc054635b6596))
69 |
70 |
71 |
72 | ## [1.0.9](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.8...v1.0.9) (2021-08-30)
73 |
74 |
75 | ### Bug Fixes
76 |
77 | * update renovate ([83facfe](https://github.com/algolia/algoliasearch-netlify/commit/83facfeb94954fb71aa5b109f8cd9ef473f62b1a))
78 | * upgrade webpack to V5 ([#436](https://github.com/algolia/algoliasearch-netlify/issues/436)) ([4ec3d1c](https://github.com/algolia/algoliasearch-netlify/commit/4ec3d1cddf1754cc2695e5313a548ea9c2bbc00e))
79 |
80 |
81 |
82 | ## [1.0.8](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.7...v1.0.8) (2021-07-20)
83 |
84 |
85 | ### Bug Fixes
86 |
87 | * **autocomplete:** use the new Requester API ([#314](https://github.com/algolia/algoliasearch-netlify/issues/314)) ([67ca346](https://github.com/algolia/algoliasearch-netlify/commit/67ca3462357624d1f6f7bde6045726d7653afc1d))
88 |
89 |
90 | ### Features
91 |
92 | * **doc:** move everything to algolia.com ([78f8a4e](https://github.com/algolia/algoliasearch-netlify/commit/78f8a4eb04591993ebd17737e93b6ea5b783d6f6))
93 |
94 |
95 |
96 | ## [1.0.7](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.6...v1.0.7) (2021-04-22)
97 |
98 |
99 | ### Bug Fixes
100 |
101 | * move docs to algolia.com ([#277](https://github.com/algolia/algoliasearch-netlify/issues/277)) ([6495afc](https://github.com/algolia/algoliasearch-netlify/commit/6495afc79a03cad0e8b54f9683bea873ecf26ed3))
102 | * undocument powered by ([#278](https://github.com/algolia/algoliasearch-netlify/issues/278)) ([ee0cfdd](https://github.com/algolia/algoliasearch-netlify/commit/ee0cfddd9f46982d93138ffd8a42bded5b854112))
103 |
104 |
105 |
106 | ## [1.0.6](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.5...v1.0.6) (2021-03-22)
107 |
108 |
109 |
110 | ## [1.0.5](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.4...v1.0.5) (2021-03-04)
111 |
112 |
113 | ### Bug Fixes
114 |
115 | * autocomplete to alpha.44 ([#227](https://github.com/algolia/algoliasearch-netlify/issues/227)) ([9a467eb](https://github.com/algolia/algoliasearch-netlify/commit/9a467ebf6f6fba28baaad9844404493954c26d42))
116 |
117 |
118 |
119 | ## [1.0.4](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.3...v1.0.4) (2021-02-09)
120 |
121 |
122 | ### Bug Fixes
123 |
124 | * Update autocomplete to alpha.41 ([#198](https://github.com/algolia/algoliasearch-netlify/issues/198)) ([19dc101](https://github.com/algolia/algoliasearch-netlify/commit/19dc1012801f7f24194e54c74a8d53ac3f00f43c))
125 |
126 |
127 |
128 | ## [1.0.3](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.2...v1.0.3) (2020-12-16)
129 |
130 |
131 | ### Bug Fixes
132 |
133 | * **frontend:** suggestionSnippet correct logic ([#155](https://github.com/algolia/algoliasearch-netlify/issues/155)) ([0076ecb](https://github.com/algolia/algoliasearch-netlify/commit/0076ecb7dc87d3527b47befdbbc59d5737bb015b))
134 |
135 |
136 |
137 | ## [1.0.2](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.1...v1.0.2) (2020-12-14)
138 |
139 |
140 | ### Bug Fixes
141 |
142 | * **frontend:** css edge cases ([5705615](https://github.com/algolia/algoliasearch-netlify/commit/5705615db67acc701f79cc5f161ea4faf40bb6bf))
143 | * **frontend:** inherit border-radius ([a4c2fb8](https://github.com/algolia/algoliasearch-netlify/commit/a4c2fb8dc5560ba4d2c1af9cb4ec78075286ba39))
144 |
145 |
146 |
147 | ## [1.0.1](https://github.com/algolia/algoliasearch-netlify/compare/v1.0.0...v1.0.1) (2020-12-14)
148 |
149 |
150 | ### Bug Fixes
151 |
152 | * **frontend:** upgrade autocomplete ([#146](https://github.com/algolia/algoliasearch-netlify/issues/146)) ([bd6753f](https://github.com/algolia/algoliasearch-netlify/commit/bd6753f129454cf8ffc09ef3ab91aa379a40e09b))
153 |
154 |
155 |
156 | # [1.0.0](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.21...v1.0.0) (2020-12-09)
157 |
158 |
159 | ### Features
160 |
161 | * Extraction templates ([#142](https://github.com/algolia/algoliasearch-netlify/issues/142)) ([912386f](https://github.com/algolia/algoliasearch-netlify/commit/912386f1e9397cc0f85ba89ffe9de0429b768ad6))
162 | * **UI:** Hierarchical records support ([#141](https://github.com/algolia/algoliasearch-netlify/issues/141)) ([1c84083](https://github.com/algolia/algoliasearch-netlify/commit/1c840832cf130070d80bf42e24f6e0579a3f3226))
163 | * migrate to autocomplete.js v1 [BREAKING CHANGES] ([#135](https://github.com/algolia/algoliasearch-netlify/issues/135)) ([1f93199](https://github.com/algolia/algoliasearch-netlify/commit/1f9319924f27cd5ddce03b6578f63096b26c6600))
164 |
165 |
166 |
167 | ## [0.0.21](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.20...v0.0.21) (2020-11-23)
168 |
169 |
170 | ### Bug Fixes
171 |
172 | * synchronise branch naming ([9381082](https://github.com/algolia/algoliasearch-netlify/commit/93810825a31dda990762f5ecfad0d9b694f8d146))
173 |
174 |
175 | ### Features
176 |
177 | * add renderJavaScript option ([#134](https://github.com/algolia/algoliasearch-netlify/issues/134)) ([dd91d7e](https://github.com/algolia/algoliasearch-netlify/commit/dd91d7ebfe88917253fc335df76e12d54b37200c))
178 |
179 |
180 |
181 | ## [0.0.20](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.19...v0.0.20) (2020-11-19)
182 |
183 |
184 | ### Features
185 |
186 | * new 'mainBranch' parameter ([#123](https://github.com/algolia/algoliasearch-netlify/issues/123)) ([0ca01b6](https://github.com/algolia/algoliasearch-netlify/commit/0ca01b642eb28c56c96b21be3664b02610d97047))
187 |
188 |
189 |
190 | ## [0.0.19](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.18...v0.0.19) (2020-11-12)
191 |
192 |
193 | ### Features
194 |
195 | * new params pathPrefix, customDomain ([#122](https://github.com/algolia/algoliasearch-netlify/issues/122)) ([f06ae0d](https://github.com/algolia/algoliasearch-netlify/commit/f06ae0d0793b039ef7d281d232081e906c9d08a9))
196 |
197 |
198 |
199 | ## [0.0.18](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.17...v0.0.18) (2020-10-29)
200 |
201 |
202 | ### Bug Fixes
203 |
204 | * **frontend:** add a default color for results title ([#103](https://github.com/algolia/algoliasearch-netlify/issues/103)) ([f22bf92](https://github.com/algolia/algoliasearch-netlify/commit/f22bf92ac236786e2c0c808de458c78c6e113bef))
205 |
206 |
207 |
208 | ## [0.0.17](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.16...v0.0.17) (2020-10-27)
209 |
210 |
211 | ### Features
212 |
213 | * use algoliasearch-lite to avoid preflight requests ([#102](https://github.com/algolia/algoliasearch-netlify/issues/102)) ([2ec2ede](https://github.com/algolia/algoliasearch-netlify/commit/2ec2ede8ddb05ef71262f88b3f31a7ca66efbd8d))
214 |
215 |
216 |
217 | ## [0.0.16](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.15...v0.0.16) (2020-10-26)
218 |
219 |
220 | ### Features
221 |
222 | * **frontend:** add warning when no search input is found ([#101](https://github.com/algolia/algoliasearch-netlify/issues/101)) ([07a9669](https://github.com/algolia/algoliasearch-netlify/commit/07a96698437555ccd2e5e88697f02eb1144de780))
223 |
224 |
225 |
226 | ## [0.0.15](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.14...v0.0.15) (2020-10-20)
227 |
228 |
229 | ### Bug Fixes
230 |
231 | * **autocomplete:** handle null description or content ([#96](https://github.com/algolia/algoliasearch-netlify/issues/96)) ([32afff6](https://github.com/algolia/algoliasearch-netlify/commit/32afff6c60265fcdf5d51da5a7f96a637a0da613))
232 | * **webpack:** fix dev server command ([#95](https://github.com/algolia/algoliasearch-netlify/issues/95)) ([aa353ea](https://github.com/algolia/algoliasearch-netlify/commit/aa353ea919b35320bcda591e4728e6717bf0f752))
233 | * use debug mode env to log more info ([#84](https://github.com/algolia/algoliasearch-netlify/issues/84)) ([dc60780](https://github.com/algolia/algoliasearch-netlify/commit/dc60780a3163f39916e3ffc8af1b7eac5b3cf087))
234 |
235 |
236 |
237 | ## [0.0.14](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.13...v0.0.14) (2020-10-07)
238 |
239 |
240 | ### Bug Fixes
241 |
242 | * missing baseUrl when no env var ([#71](https://github.com/algolia/algoliasearch-netlify/issues/71)) ([9c46e36](https://github.com/algolia/algoliasearch-netlify/commit/9c46e36faa24a4192cb21ea1ba1a28d5ba2a1c2e))
243 | * typo in frontend readme ([475314e](https://github.com/algolia/algoliasearch-netlify/commit/475314e035176eb50334d03a09236a4e5d1112ea))
244 | * use failPlugin instead of failBuild ([#74](https://github.com/algolia/algoliasearch-netlify/issues/74)) ([fc21018](https://github.com/algolia/algoliasearch-netlify/commit/fc210183a756845e365118429ac54b4a22c239d7))
245 |
246 |
247 |
248 | ## [0.0.13](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.12...v0.0.13) (2020-10-06)
249 |
250 |
251 | ### Bug Fixes
252 |
253 | * add more logs + default branches + readme ([#68](https://github.com/algolia/algoliasearch-netlify/issues/68)) ([8d0e0b5](https://github.com/algolia/algoliasearch-netlify/commit/8d0e0b5a1d69ca98afc6221ab7b765b99cef670f))
254 |
255 |
256 |
257 | ## [0.0.12](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.11...v0.0.12) (2020-10-02)
258 |
259 |
260 | ### Features
261 |
262 | * **plugin:** add siteId and branch params support ([#60](https://github.com/algolia/algoliasearch-netlify/issues/60)) ([3a2c587](https://github.com/algolia/algoliasearch-netlify/commit/3a2c58781c7f99f60b4583d07a611bbe78c58eeb))
263 | * **public:** add favicon ([#61](https://github.com/algolia/algoliasearch-netlify/issues/61)) ([432b67a](https://github.com/algolia/algoliasearch-netlify/commit/432b67a212bb5d0ea713cf8180770ad9d88c662d))
264 |
265 |
266 |
267 | ## [0.0.11](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.10...v0.0.11) (2020-10-02)
268 |
269 |
270 | ### Bug Fixes
271 |
272 | * add missing input ([b7f0781](https://github.com/algolia/algoliasearch-netlify/commit/b7f078161c3ce61ca8ac1ed94238e7b13514c204))
273 |
274 |
275 | ### Features
276 |
277 | * **branches:** support star patterns ([#59](https://github.com/algolia/algoliasearch-netlify/issues/59)) ([34f04d9](https://github.com/algolia/algoliasearch-netlify/commit/34f04d9411d760791cbddb81332a368f5e6284c2))
278 |
279 |
280 |
281 | ## [0.0.10](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.9...v0.0.10) (2020-10-02)
282 |
283 |
284 |
285 | ## [0.0.9](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.8...v0.0.9) (2020-10-02)
286 |
287 |
288 | ### Bug Fixes
289 |
290 | * Update record schema ([#56](https://github.com/algolia/algoliasearch-netlify/issues/56)) ([f1a7ace](https://github.com/algolia/algoliasearch-netlify/commit/f1a7ace123766a72a173ac0f4880e598a51b6d1c))
291 | * **dev:** fix fork-ts-checker config options ([#55](https://github.com/algolia/algoliasearch-netlify/issues/55)) ([7abc0d0](https://github.com/algolia/algoliasearch-netlify/commit/7abc0d06127fcf796b8e9e3f108521661e584b40))
292 |
293 |
294 | ### Features
295 |
296 | * **plugin:** add "branches" input ([#58](https://github.com/algolia/algoliasearch-netlify/issues/58)) ([cf397e2](https://github.com/algolia/algoliasearch-netlify/commit/cf397e2725878e0aeed60d972f3fcc9a091e9454))
297 |
298 |
299 |
300 | ## [0.0.8](https://github.com/algolia/algoliasearch-netlify/compare/v0.0.7...v0.0.8) (2020-10-01)
301 |
302 |
303 | ### Bug Fixes
304 |
305 | * try to get correct branch name ([#53](https://github.com/algolia/algoliasearch-netlify/issues/53)) ([dbcda60](https://github.com/algolia/algoliasearch-netlify/commit/dbcda60ceda4eeaaaeb202ddba411b8047e2b596))
306 | * **README:** fix npm version badge ([#47](https://github.com/algolia/algoliasearch-netlify/issues/47)) ([717e6e1](https://github.com/algolia/algoliasearch-netlify/commit/717e6e1c16e2fb75d16e4e5ec4e632d7eb301e1b))
307 | * tutorial ([#43](https://github.com/algolia/algoliasearch-netlify/issues/43)) ([fce5c2e](https://github.com/algolia/algoliasearch-netlify/commit/fce5c2e3f2c15a90ac55a4e4162b046283c104ea))
308 | * **doc:** correct library links ([#44](https://github.com/algolia/algoliasearch-netlify/issues/44)) ([1f8533e](https://github.com/algolia/algoliasearch-netlify/commit/1f8533e0558c48c953f3ce0ecb87e723cca6c598))
309 |
310 |
311 | ### Features
312 |
313 | * **dev:** improve dev env ([#54](https://github.com/algolia/algoliasearch-netlify/issues/54)) ([ef53726](https://github.com/algolia/algoliasearch-netlify/commit/ef5372677f9e34cafeeedce0da7d0bf4223e9512))
314 | * **frontend:** handle content snippeting ([#51](https://github.com/algolia/algoliasearch-netlify/issues/51)) ([61a0d4e](https://github.com/algolia/algoliasearch-netlify/commit/61a0d4e35c1ac33081ef3ecbef1a60f02e2074a4))
315 | * add env var to disable the plugin ([#45](https://github.com/algolia/algoliasearch-netlify/issues/45)) ([60c7c44](https://github.com/algolia/algoliasearch-netlify/commit/60c7c44a0c843665795d3591aa29cab3c6b60433))
316 | * **plugin:** send version in crawl call ([#42](https://github.com/algolia/algoliasearch-netlify/issues/42)) ([f52622b](https://github.com/algolia/algoliasearch-netlify/commit/f52622b1f684ff083e6e59e1d2c9e17d4597cd7c))
317 |
318 |
319 |
320 | ## [0.0.7](https://github.com/algolia/algoliasearch-netlify/compare/8493dd5559af4ec9bd75a9be1c6392d611029ea4...v0.0.7) (2020-09-28)
321 |
322 |
323 | ### Bug Fixes
324 |
325 | * **release:** add missing --new-version flag ([ee56219](https://github.com/algolia/algoliasearch-netlify/commit/ee56219fe980cf746245f2d846df0f7933936f19))
326 | * disable in prod ([#21](https://github.com/algolia/algoliasearch-netlify/issues/21)) ([78fd020](https://github.com/algolia/algoliasearch-netlify/commit/78fd02055f986825a4a3fb2a0ffc3905e56493d1))
327 | * review from Netlify ([#34](https://github.com/algolia/algoliasearch-netlify/issues/34)) ([c559cb0](https://github.com/algolia/algoliasearch-netlify/commit/c559cb0fef844355b86540e61a7367b87d9a03c8))
328 | * title ([f3d3a74](https://github.com/algolia/algoliasearch-netlify/commit/f3d3a744278b7bcc0e8771d4229373b74f4ccc93))
329 | * title ([5429fc5](https://github.com/algolia/algoliasearch-netlify/commit/5429fc52ca671ddcbd436ac51b7f2630a0a81c5e))
330 | * title ([ba6aafc](https://github.com/algolia/algoliasearch-netlify/commit/ba6aafcb1a8121d0bcb80b67eacdf6350168d412))
331 | * title ([8493dd5](https://github.com/algolia/algoliasearch-netlify/commit/8493dd5559af4ec9bd75a9be1c6392d611029ea4))
332 | * update dependencies ([#22](https://github.com/algolia/algoliasearch-netlify/issues/22)) ([d545cc0](https://github.com/algolia/algoliasearch-netlify/commit/d545cc01244036007063abfe9f4efb7943f97af7))
333 |
334 |
335 | ### Features
336 |
337 | * add circleci ([#20](https://github.com/algolia/algoliasearch-netlify/issues/20)) ([5feafa9](https://github.com/algolia/algoliasearch-netlify/commit/5feafa9533ab9a096dde4d54a34c7ac5d734b7e2))
338 | * add front-end bundle ([#35](https://github.com/algolia/algoliasearch-netlify/issues/35)) ([9e42155](https://github.com/algolia/algoliasearch-netlify/commit/9e42155f0b9e6a86b13e459d5d2ecdbed28bdb5a))
339 | * add siteName & deployPrimeUrl ([8de476a](https://github.com/algolia/algoliasearch-netlify/commit/8de476a21c0653f0c70aa1b2092682b60add55f5))
340 | * new version ([86a1378](https://github.com/algolia/algoliasearch-netlify/commit/86a1378e1e87c6c20a6819eccf9124581ec2ce6e))
341 | * prepare rename to algoliasearch-netlify ([#19](https://github.com/algolia/algoliasearch-netlify/issues/19)) ([d3ccb96](https://github.com/algolia/algoliasearch-netlify/commit/d3ccb9687e8274398ac044e2f5b9c114121a2576))
342 | * release script ([#40](https://github.com/algolia/algoliasearch-netlify/issues/40)) ([e670563](https://github.com/algolia/algoliasearch-netlify/commit/e67056337da0a1393f202ded32efa04f5261eccb))
343 |
344 |
345 |
346 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Development
4 |
5 | ```sh
6 | yarn dev
7 | ```
8 |
9 | Launches all 3 dev tools:
10 |
11 | - [plugin](./plugin)
12 | - [frontend library](./frontend)
13 | - and [test website](./public)
14 |
15 | See each tool's respective README.
16 |
17 | ## Releasing
18 |
19 | ```sh
20 | yarn release
21 | ```
22 |
23 | This releases both `plugin` & `frontend` to be sure our versions are aligned even if there were changes in only one of both projects.
24 | Push to your website
25 |
26 | ## Architecture
27 |
28 | - [`frontend/`](./frontend/): Front-end library
29 | - [`plugin/`](./plugin/): Netlify plugin sources
30 | - [`public/`](./public/): Test website
31 |
32 | This repository is itself a netlify site, which allows us to test the whole setup.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Algolia Netlify plugin
19 |
20 | Automatically index your website to Algolia when deploying your project to Netlify with the Algolia Crawler.
21 |
22 | - [What is Algolia?](https://www.algolia.com/doc/guides/getting-started/what-is-algolia/)
23 | - [What is Algolia's Crawler?](https://www.algolia.com/doc/tools/crawler/getting-started/overview/)
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ## Documentation
32 |
33 | - [Getting Started](https://www.algolia.com/doc/tools/crawler/netlify-plugin/quick-start/)
34 | - [Plugin configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/plugin/)
35 | - [Front-end configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/front-end/)
36 | - [FAQ](https://www.algolia.com/doc/tools/crawler/netlify-plugin/netlify-faq/)
37 | - [Data Extraction](https://www.algolia.com/doc/tools/crawler/netlify-plugin/extraction-strategy/)
38 | - [Uninstall](https://www.algolia.com/doc/tools/crawler/netlify-plugin/uninstalling/)
39 |
40 | ## Troubleshooting
41 |
42 | - Need help? We have you covered in our [Discourse forum](https://discourse.algolia.com/c/netlify/28)
43 | - Found a bug in the plugin? Please read our [contributing guide](/CONTRIBUTING.md) and either open an [issue](https://github.com/algolia/algoliasearch-netlify/issues) or a [pull request](https://github.com/algolia/algoliasearch-netlify/pulls)
44 | - Can't find the answer to your issue? Please reach out to the [Algolia Support team](https://support.algolia.com/hc/en-us/requests/new)
45 |
46 | ## Development & Release
47 |
48 | See [CONTRIBUTING.md](./CONTRIBUTING.md).
49 |
--------------------------------------------------------------------------------
/docs/screenshots/frontend/dark-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/algoliasearch-netlify/97ec146098930e60b51ea973ebdff06c32dfef8c/docs/screenshots/frontend/dark-theme.png
--------------------------------------------------------------------------------
/docs/screenshots/frontend/normal-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/algoliasearch-netlify/97ec146098930e60b51ea973ebdff06c32dfef8c/docs/screenshots/frontend/normal-theme.png
--------------------------------------------------------------------------------
/frontend/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Scripts
4 |
5 | - `yarn dev`: run dev environment
6 | - `yarn release`: build & publish the library
7 |
8 | ## Development
9 |
10 | From this folder:
11 |
12 | ```sh
13 | yarn dev
14 | ```
15 |
16 | Or from the root of the repository:
17 |
18 | ```sh
19 | yarn dev:frontend
20 | ```
21 |
22 | This runs a `webpack-dev-server` on port 9100.
23 | Meant to be used in conjunction with the [test website](../public/).
24 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # algoliasearch-netlify-frontend
2 |
3 | `algoliasearch-netlify-frontend` is the front-end bundle we recommend to use with our Netlify plugin.
4 |
5 | - [Install the plugin](https://www.algolia.com/doc/tools/crawler/netlify-plugin/quick-start/)
6 | - [Plugin configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/plugin/)
7 | - [Front-end configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/front-end/)
8 |
9 | ## Development & Release
10 |
11 | See [CONTRIBUTING.md](./CONTRIBUTING.md).
12 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@algolia/algoliasearch-netlify-frontend",
3 | "version": "1.0.15",
4 | "author": "Algolia Team ",
5 | "license": "MIT",
6 | "repository": "https://github.com/algolia/algoliasearch-netlify.git",
7 | "bugs": {
8 | "url": "https://github.com/algolia/algoliasearch-netlify/issues"
9 | },
10 | "files": [
11 | "README.md",
12 | "dist/"
13 | ],
14 | "scripts": {
15 | "build": "webpack --mode production",
16 | "dev": "PORT=9100 webpack serve --mode development",
17 | "postinstall": "[ -d dist/ ] || npm run build"
18 | },
19 | "devDependencies": {
20 | "@algolia/autocomplete-js": "1.5.6",
21 | "@algolia/autocomplete-preset-algolia": "1.5.6",
22 | "@algolia/autocomplete-theme-classic": "1.5.6",
23 | "@algolia/transporter": "4.13.0",
24 | "@babel/core": "7.17.9",
25 | "@babel/preset-env": "7.16.11",
26 | "@types/react": "17.0.44",
27 | "algoliasearch": "4.13.0",
28 | "babel-loader": "8.2.4",
29 | "clean-webpack-plugin": "4.0.0",
30 | "core-js": "3.21.1",
31 | "css-loader": "6.7.1",
32 | "fork-ts-checker-webpack-plugin": "6.5.1",
33 | "mini-css-extract-plugin": "2.6.0",
34 | "mustache": "4.2.0",
35 | "node-sass": "7.0.1",
36 | "postcss": "8.4.12",
37 | "postcss-loader": "6.2.1",
38 | "postcss-preset-env": "7.4.3",
39 | "preact": "10.7.1",
40 | "sass-loader": "12.6.0",
41 | "terser-webpack-plugin": "4.2.3",
42 | "ts-loader": "9.2.8",
43 | "webpack": "5.72.0",
44 | "webpack-cli": "4.9.2",
45 | "webpack-dev-server": "4.8.1"
46 | },
47 | "keywords": [
48 | "algolia",
49 | "algoliasearch",
50 | "crawl",
51 | "crawler",
52 | "indexing",
53 | "jamstack",
54 | "netlify-plugin",
55 | "netlify-search",
56 | "netlify",
57 | "plugin",
58 | "robots",
59 | "search",
60 | "ui"
61 | ]
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/src/AlgoliasearchNetlify.ts:
--------------------------------------------------------------------------------
1 | import { AutocompleteWrapper } from './AutocompleteWrapper';
2 | import type { Options } from './types';
3 |
4 | const defaultOptions: Omit<
5 | Options,
6 | 'apiKey' | 'appId' | 'branch' | 'selector' | 'siteId'
7 | > = {
8 | analytics: true,
9 | hitsPerPage: 5,
10 | debug: false,
11 | poweredBy: true,
12 | placeholder: 'Search...',
13 | openOnFocus: false,
14 | };
15 |
16 | const mandatory: Array = [
17 | 'appId',
18 | 'apiKey',
19 | 'selector',
20 | 'siteId',
21 | 'branch',
22 | ];
23 |
24 | const instances: AutocompleteWrapper[] = [];
25 |
26 | function algoliasearchNetlify(_options: Options): void {
27 | const options = {
28 | ...defaultOptions,
29 | ..._options,
30 | };
31 | for (const key of mandatory) {
32 | if (options[key]) continue;
33 |
34 | throw new Error(`[algoliasearch-netlify] Missing mandatory key: ${key}`);
35 | }
36 |
37 | const autocomplete = new AutocompleteWrapper(options);
38 | instances.push(autocomplete);
39 |
40 | // Wait for DOM initialization, then render
41 | const render = (): void => {
42 | autocomplete.render();
43 | };
44 | if (['complete', 'interactive'].includes(document.readyState)) {
45 | render();
46 | } else {
47 | document.addEventListener('DOMContentLoaded', render);
48 | }
49 | }
50 |
51 | export { algoliasearchNetlify };
52 |
--------------------------------------------------------------------------------
/frontend/src/AutocompleteWrapper.ts:
--------------------------------------------------------------------------------
1 | import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
2 | import type {
3 | AutocompleteApi,
4 | AutocompleteSource,
5 | SourceTemplates,
6 | } from '@algolia/autocomplete-js';
7 | import type { HighlightedHit } from '@algolia/autocomplete-preset-algolia';
8 | import algoliasearch from 'algoliasearch/lite';
9 | import type { SearchClient } from 'algoliasearch/lite';
10 |
11 | // @ts-expect-error
12 | import { version } from '../package.json';
13 |
14 | import { templates } from './templates';
15 | import type { Options, AlgoliaRecord } from './types';
16 |
17 | class AutocompleteWrapper {
18 | private options;
19 | private indexName;
20 | private client;
21 | private $themeNode: HTMLStyleElement | null = null;
22 | private autocomplete: AutocompleteApi | undefined;
23 |
24 | constructor(options: Options) {
25 | this.options = options;
26 | this.client = this.createClient();
27 | this.indexName = this.computeIndexName();
28 | }
29 |
30 | render(): void {
31 | const $input = document.querySelector(this.options.selector) as HTMLElement;
32 | if (!$input) {
33 | console.error(
34 | `[algoliasearch-netlify] no element ${this.options.selector} found`
35 | );
36 | return;
37 | }
38 |
39 | let detachedMediaQuery = undefined;
40 | if (this.options.detached !== undefined) {
41 | if (this.options.detached === true) {
42 | detachedMediaQuery = '';
43 | } else if (this.options.detached === false) {
44 | detachedMediaQuery = 'none';
45 | } else {
46 | detachedMediaQuery = this.options.detached.mediaQuery;
47 | }
48 | }
49 | const instance = autocomplete({
50 | container: $input,
51 | autoFocus: false,
52 | placeholder: this.options.placeholder,
53 | debug: this.options.debug,
54 | openOnFocus: this.options.openOnFocus,
55 | panelPlacement: 'input-wrapper-width',
56 | detachedMediaQuery,
57 | getSources: () => {
58 | return [this.getSources()];
59 | },
60 | });
61 | this.applyTheme($input.firstElementChild as HTMLElement);
62 |
63 | this.autocomplete = instance;
64 | }
65 |
66 | private computeIndexName(): string {
67 | const { siteId, branch } = this.options;
68 |
69 | // Keep in sync with crawler code in /netlify/crawl
70 | const cleanBranch = branch
71 | .trim()
72 | .replace(/[^\p{L}\p{N}_.-]+/gu, '-')
73 | .replace(/-{2,}/g, '-')
74 | .toLocaleLowerCase();
75 | return `netlify_${siteId}_${cleanBranch}_all`;
76 | }
77 |
78 | private createClient(): SearchClient {
79 | const client = algoliasearch(this.options.appId, this.options.apiKey);
80 | client.addAlgoliaAgent(`Netlify integration ${version}`);
81 | return client;
82 | }
83 |
84 | private getSources(): AutocompleteSource> {
85 | const poweredBy = this.options.poweredBy;
86 | const tpls: SourceTemplates> = {
87 | header() {
88 | return '';
89 | },
90 | item({ item, components }) {
91 | return templates.item(item, components);
92 | },
93 | footer() {
94 | if (poweredBy) {
95 | return templates.poweredBy({
96 | hostname: window.location.host,
97 | });
98 | }
99 | return '';
100 | },
101 | };
102 | const res: AutocompleteSource> = {
103 | sourceId: 'algoliaHits',
104 | getItems: ({ query }) => {
105 | return getAlgoliaResults({
106 | searchClient: this.client,
107 | queries: [
108 | {
109 | indexName: this.indexName,
110 | query,
111 | params: {
112 | analytics: this.options.analytics,
113 | hitsPerPage: this.options.hitsPerPage,
114 | },
115 | },
116 | ],
117 | });
118 | },
119 | getItemUrl({ item }) {
120 | return item.url;
121 | },
122 | templates: tpls,
123 | };
124 | return res;
125 | }
126 |
127 | private applyTheme(el: HTMLElement | null): void {
128 | if (!el || !this.options.theme) {
129 | return;
130 | }
131 |
132 | const theme = this.options.theme;
133 | this.$themeNode = addCss(
134 | `.aa-Autocomplete, .aa-Panel, .aa-DetachedContainer {
135 | ${theme.mark && `--color-mark: ${theme.mark};`}
136 | ${theme.mark && `--color-background: ${theme.background};`}
137 | ${theme.mark && `--color-selected: ${theme.selected};`}
138 | ${theme.mark && `--color-text: ${theme.text};`}
139 | ${theme.mark && `--color-source-icon: ${theme.colorSourceIcon};`}
140 | }`,
141 | this.$themeNode
142 | );
143 | }
144 | }
145 |
146 | function addCss(
147 | css: string,
148 | $mainStyle: HTMLElement | null = null
149 | ): HTMLStyleElement {
150 | const $usedSibling =
151 | $mainStyle ??
152 | document.querySelector(
153 | 'link[rel=stylesheet][href*="algoliasearchNetlify"]'
154 | ) ??
155 | document.getElementsByTagName('head')[0].lastChild!;
156 | const $styleTag = document.createElement('style');
157 | $styleTag.setAttribute('type', 'text/css');
158 | $styleTag.appendChild(document.createTextNode(css));
159 | return $usedSibling.parentNode!.insertBefore(
160 | $styleTag,
161 | $usedSibling.nextSibling
162 | );
163 | }
164 |
165 | export { AutocompleteWrapper };
166 |
--------------------------------------------------------------------------------
/frontend/src/index.scss:
--------------------------------------------------------------------------------
1 | @import '~@algolia/autocomplete-theme-classic';
2 |
3 | $color-bg: #fff;
4 | $color-muted: #969faf;
5 | $color-light: #797979;
6 | $color-text: #23263b;
7 | $color-mark: rgb(84, 104, 255);
8 | $color-bg-selected: #f5f5fa;
9 | $color-input-icon: #777;
10 | $color-source-icon: rgba(80, 80, 80, 0.32);
11 |
12 | $font-size-xs: 12px;
13 | $font-size-s: 14px;
14 | $font-size-m: 16px;
15 |
16 | $size-xs: 2px;
17 | $size-s: 4px;
18 | $size-m: 8px;
19 | $size-l: 16px;
20 | $size-xl: 32px;
21 |
22 | $height: $size-xl;
23 | $height-icon: $size-l;
24 | $font-size: $font-size-m;
25 |
26 | .aa-Autocomplete, .aa-Panel, .aa-DetachedContainer {
27 | --color-mark: #{$color-mark};
28 | --color-background: #{$color-bg};
29 | --color-selected: #{$color-bg-selected};
30 | --color-text: #{$color-text};
31 | --color-input-icon: #{$color-input-icon};
32 | --color-source-icon: #{$color-source-icon};
33 | --height: #{$height};
34 | --height-icon: #{$height-icon};
35 | --font-size: #{$font-size};
36 | }
37 |
38 | //// ---- Overridden definitions of classic theme
39 |
40 | .aa-Panel {
41 | min-width: 350px;
42 | z-index: 1100;
43 | margin-top: $size-xs;
44 |
45 | .aa-PanelLayout {
46 | padding-top: 0;
47 | padding-bottom: 0;
48 | background-color: var(--color-background);
49 |
50 | .aa-PanelLayoutPreview {
51 | border-left: solid 1px var(--color-selected);
52 | }
53 | }
54 |
55 | .aa-GradientBottom {
56 | background-image: none;
57 | }
58 | }
59 |
60 | .aa-Autocomplete, .aa-DetachedFormContainer {
61 | .aa-Form {
62 | height: var(--height);
63 | font-size: var(--font-size);
64 | padding: 0;
65 | background-color: var(--color-background);
66 |
67 | &:focus-within {
68 | box-shadow: none;
69 | }
70 | .aa-InputWrapperPrefix {
71 | padding: 0;
72 |
73 | .aa-Label {
74 | padding: 0;
75 |
76 | svg {
77 | left: 0;
78 | vertical-align: middle;
79 | color: var(--color-input-icon);
80 | width: var(--height-icon);
81 | }
82 | }
83 | }
84 | .aa-InputWrapper {
85 | .aa-Input {
86 | height: var(--height);
87 | color: var(--color-text);
88 | }
89 | }
90 | .aa-InputWrapperSuffix {
91 | height: var(--height);
92 |
93 | .aa-ClearButton {
94 | padding: 0;
95 |
96 | &:hover,
97 | &:focus {
98 | color: var(--color-text);
99 | }
100 | }
101 | }
102 | }
103 | }
104 |
105 | .aa-Item {
106 | color: var(--color-text);
107 | padding: $size-xs 0;
108 |
109 | a {
110 | color: inherit;
111 | text-decoration: none;
112 | }
113 |
114 | &[aria-selected='true'] {
115 | background-color: var(--color-selected);
116 | }
117 | .aa-ItemContent {
118 | display: flex;
119 | color: var(--color-text);
120 |
121 | mark {
122 | color: var(--color-mark);
123 | background-color: transparent;
124 | }
125 | }
126 | .aa-ItemIcon {
127 | align-items: baseline;
128 | color: var(--color-source-icon);
129 |
130 | background: none;
131 | box-shadow: none;
132 | margin: 0 var(--aa-spacing-half) 0 2px;
133 | }
134 | .aa-ItemTitle {
135 | font-size: $font-size-s;
136 | font-weight: bold;
137 | line-height: 18px;
138 | }
139 | .aa-ItemHierarchy {
140 | font-size: $font-size-xs;
141 | font-style: italic;
142 | line-height: 18px;
143 | opacity: 0.8;
144 | padding: 1px 0;
145 | }
146 | .aa-ItemDescription {
147 | font-size: $font-size-xs;
148 | line-height: 16px;
149 | color: $color-light;
150 | }
151 | }
152 |
153 | .aa-DetachedContainer {
154 | background: var(--color-background);
155 |
156 | .aa-DetachedFormContainer {
157 | .aa-DetachedCancelButton {
158 | color: var(--color-text);
159 | }
160 | }
161 | }
162 |
163 | .aa-DetachedOverlay {
164 | z-index: 10;
165 | }
166 |
167 | .aa-DetachedSearchButton {
168 | background-color: var(--color-background);
169 |
170 | .aa-DetachedSearchButtonIcon {
171 | color: var(--color-input-icon);
172 | }
173 | }
174 |
175 |
176 | /* Search by */
177 | .aa-powered-by-link {
178 | display: inline-block;
179 | width: 64px;
180 | height: 18px;
181 | margin-left: $size-s;
182 | text-indent: 101%;
183 | overflow: hidden;
184 | white-space: nowrap;
185 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODUgMTIwIj48ZyBmaWxsPSJub25lIj48cGF0aCBmaWxsPSIjNTQ2OEZGIiBkPSJNMTYuOC0xLjAwMWg4OC40YzguNyAwIDE1LjggNy4wNjUgMTUuOCAxNS44djg4LjQwNWMwIDguNy03LjA2NSAxNS43OTUtMTUuOCAxNS43OTVIMTYuOGMtOC43IDAtMTUuOC03LjA2LTE1LjgtMTUuNzk1VjE0Ljc1OWMwLTguNjk1IDcuMDYtMTUuNzYgMTUuOC0xNS43NiIvPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik03My41MDUgMjUuNzg4di00LjExNWE1LjIwOSA1LjIwOSAwIDAwLTUuMjEtNS4yMDVINTYuMTVhNS4yMDkgNS4yMDkgMCAwMC01LjIxIDUuMjA1djQuMjI1YzAgLjQ3LjQzNS44LjkxLjY5YTM3Ljk2NiAzNy45NjYgMCAwMTEwLjU3LTEuNDljMy40NjUgMCA2Ljg5NS40NyAxMC4yMSAxLjM4LjQ0LjExLjg3NS0uMjE1Ljg3NS0uNjlNNDAuMjIgMzEuMTczbC0yLjA3NS0yLjA3NWE1LjIwNiA1LjIwNiAwIDAwLTcuMzY1IDBsLTIuNDggMi40NzVhNS4xODUgNS4xODUgMCAwMDAgNy4zNTVsMi4wNCAyLjA0Yy4zMy4zMjUuODA1LjI1IDEuMDk1LS4wNzVhMzkuODc2IDM5Ljg3NiAwIDAxMy45NzUtNC42NiAzNy42OCAzNy42OCAwIDAxNC43LTRjLjM2NC0uMjIuNC0uNzMuMTEtMS4wNm0yMi4xNjQgMTMuMDY1djE3LjhjMCAuNTEuNTUuODc1IDEuMDIuNjJsMTUuODI1LTguMTljLjM2LS4xOC40Ny0uNjIuMjktLjk4LTMuMjgtNS43NTUtOS4zNy05LjY4NS0xNi40MDUtOS45NC0uMzY1IDAtLjczLjI5LS43My42OW0wIDQyLjg4Yy0xMy4xOTUgMC0yMy45MTUtMTAuNzA1LTIzLjkxNS0yMy44OCAwLTEzLjE3NSAxMC43Mi0yMy44NzUgMjMuOTE1LTIzLjg3NSAxMy4yIDAgMjMuOTE2IDEwLjcgMjMuOTE2IDIzLjg3NXMtMTAuNjggMjMuODgtMjMuOTE2IDIzLjg4bTAtNTcuOGMtMTguNzQgMC0zMy45NCAxNS4xOC0zMy45NCAzMy45MiAwIDE4Ljc0NSAxNS4yIDMzLjg5IDMzLjk0IDMzLjg5czMzLjk0LTE1LjE4IDMzLjk0LTMzLjkyNWMwLTE4Ljc0NS0xNS4xNjUtMzMuODg1LTMzLjk0LTMzLjg4NSIvPjxwYXRoIGZpbGw9IiM1NDY4RkYiIGQ9Ik0yNDAuMjE0IDk0LjE3N2MtMjMuMzY1LjExLTIzLjM2NS0xOC44NTUtMjMuMzY1LTIxLjg3NWwtLjA0LTcxLjA0NSAxNC4yNTQtMi4yNnY3MC42MWMwIDEuNzE1IDAgMTIuNTYgOS4xNSAxMi41OTV2MTEuOTc1aC4wMDF6bS01Ny43OC0xMS42MWM0LjM3NCAwIDcuNjItLjI1NSA5Ljg4LS42OVY2Ny4zOTJhMjkuMTk2IDI5LjE5NiAwIDAwLTMuNDMtLjY5NSAzMy43NDIgMzMuNzQyIDAgMDAtNC45NTYtLjM2NWMtMS41NyAwLTMuMTc1LjExLTQuNzc1LjM2NS0xLjYwNS4yMi0zLjA2NS42NTUtNC4zNCAxLjI3NS0xLjI3NS42Mi0yLjMzNSAxLjQ5NS0zLjEgMi42Mi0uOCAxLjEzLTEuMTY1IDEuNzg1LTEuMTY1IDMuNDk1IDAgMy4zNDUgMS4xNjUgNS4yOCAzLjI4IDYuNTUgMi4xMTUgMS4yNzUgNC45OTUgMS45MyA4LjYwNiAxLjkzem0tMS4yNC01MS42ODVjNC43IDAgOC42NzQuNTg1IDExLjg4NCAxLjc1IDMuMjA2IDEuMTY1IDUuNzk2IDIuOCA3LjY5IDQuODc1IDEuOTM1IDIuMTEgMy4yNDUgNC45MTUgNC4wNDYgNy45Ljg0IDIuOTg1IDEuMjQgNi4yNiAxLjI0IDkuODZ2MzYuNjJjLTIuMTg1LjQ3LTUuNTA2IDEuMDE1LTkuOTUgMS42Ny00LjQ0Ni42NTUtOS40NC45ODUtMTQuOTg2Ljk4NS0zLjY4IDAtNy4wNy0uMzY1LTEwLjA5NS0xLjA1NS0zLjA2NS0uNjktNS42NS0xLjgyLTcuODQtMy4zODUtMi4xNS0xLjU2NS0zLjgyNS0zLjU3LTUuMDY1LTYuMDQtMS4yMDUtMi40OC0xLjgyNS01Ljk3LTEuODI1LTkuNjEgMC0zLjQ5NS42OS01LjcxNSAyLjA0NS04LjEyIDEuMzgtMi40IDMuMjQtNC4zNjUgNS41NzUtNS44OTUgMi4zNy0xLjUzIDUuMDY1LTIuNjIgOC4xNjUtMy4yNzUgMy4xLS42NTUgNi4zNDUtLjk4NSA5LjY5NS0uOTg1IDEuNTcgMCAzLjIxLjExIDQuOTYuMjkgMS43MTUuMTg1IDMuNTc1LjUxNSA1LjU0NS45ODV2LTIuMzNjMC0xLjYzNS0uMTg1LTMuMi0uNTg1LTQuNjU1YTEwLjAxMiAxMC4wMTIgMCAwMC0yLjA0NS0zLjg5NWMtLjk4NS0xLjEzLTIuMjU1LTIuMDA1LTMuODYtMi42Mi0xLjYwNS0uNjItMy42NS0xLjA5NS02LjA5LTEuMDk1LTMuMjggMC02LjI3LjQtOS4wMDUuODc1LTIuNzM1LjQ3LTQuOTk1IDEuMDItNi43MSAxLjYzNWwtMS43MS0xMS42OGMxLjc4NS0uNjIgNC40NDUtMS4yNCA3Ljg3NS0xLjg1NSAzLjQyNS0uNjYgNy4xMS0uOTUgMTEuMDQ1LS45NWguMDAxem0yODEuNTEgNTEuMjg1YzQuMzc1IDAgNy42MTUtLjI1NSA5Ljg3NS0uNjk1di0xNC40OGMtLjgtLjIyLTEuOTMtLjQ3NS0zLjQyNS0uNjk1YTMzLjgxMyAzMy44MTMgMCAwMC00Ljk2LS4zNjVjLTEuNTY1IDAtMy4xNy4xMS00Ljc3NS4zNjUtMS42LjIyLTMuMDYuNjU1LTQuMzM1IDEuMjc1LTEuMjguNjItMi4zMzUgMS40OTUtMy4xIDIuNjItLjgwNSAxLjEzLTEuMTY1IDEuNzg1LTEuMTY1IDMuNDk1IDAgMy4zNDUgMS4xNjUgNS4yOCAzLjI4IDYuNTUgMi4xNSAxLjMxIDQuOTk1IDEuOTMgOC42MDUgMS45M3ptLTEuMjA1LTUxLjY0NWM0LjcgMCA4LjY3NC41OCAxMS44ODQgMS43NDUgMy4yMDUgMS4xNjUgNS43OTUgMi44IDcuNjkgNC44NzUgMS44OTUgMi4wNzUgMy4yNDUgNC45MTUgNC4wNDUgNy45Ljg0IDIuOTg1IDEuMjQgNi4yNiAxLjI0IDkuODY1djM2LjYxNWMtMi4xODUuNDctNS41MDUgMS4wMTUtOS45NSAxLjY3NS00LjQ0NS42NTUtOS40NC45OC0xNC45ODUuOTgtMy42OCAwLTcuMDctLjM2NS0xMC4wOTQtMS4wNTUtMy4wNjUtLjY5LTUuNjUtMS44Mi03Ljg0LTMuMzg1LTIuMTUtMS41NjUtMy44MjUtMy41Ny01LjA2NS02LjA0LTEuMjA1LTIuNDc1LTEuODI1LTUuOTctMS44MjUtOS42MSAwLTMuNDk1LjY5NS01LjcxNSAyLjA0NS04LjEyIDEuMzgtMi40IDMuMjQtNC4zNjUgNS41NzUtNS44OTUgMi4zNy0xLjUyNSA1LjA2NS0yLjYyIDguMTY1LTMuMjc1IDMuMS0uNjU1IDYuMzQ1LS45OCA5LjctLjk4IDEuNTY1IDAgMy4yMDUuMTEgNC45NTUuMjlzMy41NzUuNTEgNS41NC45ODV2LTIuMzNjMC0xLjY0LS4xOC0zLjIwNS0uNTgtNC42NmE5Ljk3NyA5Ljk3NyAwIDAwLTIuMDQ1LTMuODk1Yy0uOTg1LTEuMTMtMi4yNTUtMi4wMDUtMy44Ni0yLjYyLTEuNjA2LS42Mi0zLjY1LTEuMDktNi4wOS0xLjA5LTMuMjggMC02LjI3LjQtOS4wMDUuODctMi43MzUuNDc1LTQuOTk1IDEuMDItNi43MSAxLjY0bC0xLjcxLTExLjY4NWMxLjc4NS0uNjIgNC40NDUtMS4yMzUgNy44NzUtMS44NTUgMy40MjUtLjYyIDcuMTA1LS45NDUgMTEuMDQ1LS45NDV6bS00Mi44LTYuNzdjNC43NzQgMCA4LjY4LTMuODYgOC42OC04LjYzIDAtNC43NjUtMy44NjYtOC42MjUtOC42OC04LjYyNS00LjgxIDAtOC42NzUgMy44Ni04LjY3NSA4LjYyNSAwIDQuNzcgMy45IDguNjMgOC42NzUgOC42M3ptNy4xOCA3MC40MjVoLTE0LjMyNnYtNjEuNDRsMTQuMzI1LTIuMjU1djYzLjY5NWguMDAxem0tMjUuMTE2IDBjLTIzLjM2NS4xMS0yMy4zNjUtMTguODU1LTIzLjM2NS0yMS44NzVsLS4wNC03MS4wNDUgMTQuMjU1LTIuMjZ2NzAuNjFjMCAxLjcxNSAwIDEyLjU2IDkuMTUgMTIuNTk1djExLjk3NXptLTQ2LjMzNS0zMS40NDVjMC02LjE1NS0xLjM1LTExLjI4NS0zLjk3NC0xNC44NS0yLjYyNS0zLjYwNS02LjMwNS01LjM4NS0xMS4wMS01LjM4NS00LjcgMC04LjM4NiAxLjc4LTExLjAwNiA1LjM4NS0yLjYyNSAzLjYtMy45MDQgOC42OTUtMy45MDQgMTQuODUgMCA2LjIyNSAxLjMxNSAxMC40MDUgMy45NCAxNC4wMSAyLjYyNSAzLjY0IDYuMzA1IDUuNDI1IDExLjAxIDUuNDI1IDQuNyAwIDguMzg1LTEuODIgMTEuMDEtNS40MjUgMi42MjQtMy42NCAzLjkzNC03Ljc4NSAzLjkzNC0xNC4wMXptMTQuNTgtLjAzNWMwIDQuODA1LS42OSA4LjQ0LTIuMTE0IDEyLjQxLTEuNDIgMy45NjUtMy40MjUgNy4zNS02LjAxIDEwLjE1NS0yLjU5IDIuOC01LjY5IDQuOTg1LTkuMzM2IDYuNTE1LTMuNjQ0IDEuNTI1LTkuMjYgMi40LTEyLjA2NSAyLjQtMi44MS0uMDM1LTguMzg1LS44MzUtMTEuOTk1LTIuNC0zLjYxLTEuNTY1LTYuNzEtMy43MTUtOS4yOTUtNi41MTUtMi41OS0yLjgwNS00LjU5NC02LjE5LTYuMDU0LTEwLjE1NS0xLjQ1Ni0zLjk3LTIuMTg1LTcuNjA1LTIuMTg1LTEyLjQxcy42NTQtOS40MyAyLjExNC0xMy4zNmMxLjQ2LTMuOTMgMy41LTcuMjggNi4xMjUtMTAuMDggMi42MjUtMi44MDUgNS43Ni00Ljk1NSA5LjMzLTYuNDggMy42MS0xLjUzIDcuNTg1LTIuMjU1IDExLjg4NS0yLjI1NSA0LjMwNSAwIDguMjc1Ljc2IDExLjkyIDIuMjU1IDMuNjUgMS41MjUgNi43ODYgMy42NzUgOS4zMzYgNi40OCAyLjU4NCAyLjggNC41OSA2LjE1IDYuMDUgMTAuMDggMS41MyAzLjkzIDIuMjk1IDguNTU1IDIuMjk1IDEzLjM2aC0uMDAxem0tMTA3LjI4NCAwYzAgNS45NjUgMS4zMSAxMi41OSAzLjkzNSAxNS4zNTUgMi42MjUgMi43NyA2LjAxNCA0LjE1IDEwLjE3NSA0LjE1IDIuMjYgMCA0LjQxLS4zMjUgNi40MTQtLjk0NSAyLjAwNS0uNjIgMy42MDYtMS4zNSA0Ljg4Ni0yLjIydi0zNS4zNGMtMS4wMi0uMjItNS4yODYtMS4wOTUtOS40MS0xLjItNS4xNzUtLjE1LTkuMTEgMS45NjUtMTEuODggNS4zNDUtMi43MzYgMy4zOS00LjEyIDkuMzItNC4xMiAxNC44NTV6bTM5LjYyNSAyOC4wOTVjMCA5LjcyLTIuNDggMTYuODE1LTcuNDc2IDIxLjMzLTQuOTkgNC41MS0xMi42MSA2Ljc3LTIyLjg5IDYuNzctMy43NTUgMC0xMS41NTUtLjczLTE3Ljc5LTIuMTFsMi4yOTUtMTEuMjg1YzUuMjE1IDEuMDkgMTIuMTA1IDEuMzg1IDE1LjcxNSAxLjM4NSA1LjcyIDAgOS44MDUtMS4xNjUgMTIuMjQ1LTMuNDk1IDIuNDQ1LTIuMzMgMy42NDUtNS43ODUgMy42NDUtMTAuMzc1di0yLjMzYy0xLjQyLjY5LTMuMjggMS4zODUtNS41NzUgMi4xMTUtMi4yOTUuNjktNC45NTUgMS4wNTUtNy45NSAxLjA1NS0zLjkzNSAwLTcuNTEtLjYyLTEwLjc1LTEuODYtMy4yNDUtMS4yMzUtNi4wNTUtMy4wNTUtOC4zNS01LjQ2LTIuMjk1LTIuNC00LjEyLTUuNDItNS4zOTUtOS4wMjUtMS4yNzUtMy42MDUtMS45MzUtMTAuMDQ1LTEuOTM1LTE0Ljc3NSAwLTQuNDQuNjk1LTEwLjAxIDIuMDQ2LTEzLjcyNSAxLjM4NC0zLjcxIDMuMzUtNi45MTUgNi4wMTQtOS41NyAyLjYyNi0yLjY1NSA1LjgzNS00LjY5NSA5LjU5LTYuMTkgMy43NTUtMS40OSA4LjE2LTIuNDM1IDEyLjkzNS0yLjQzNSA0LjYzNSAwIDguOS41OCAxMy4wNTUgMS4yNzUgNC4xNTUuNjkgNy42OSAxLjQxNSAxMC41NyAyLjIxNXY1Ni40OWguMDAxeiIvPjwvZz48L3N2Zz4=);
186 | background-repeat: no-repeat;
187 | background-size: contain;
188 | vertical-align: middle;
189 | }
190 | .aa-powered-by {
191 | text-align: right;
192 | font-size: $font-size-xs;
193 | color: $color-muted;
194 | padding: $size-m $size-m $size-s 0;
195 | font-weight: normal;
196 | }
197 |
--------------------------------------------------------------------------------
/frontend/src/index.ts:
--------------------------------------------------------------------------------
1 | import { algoliasearchNetlify } from './AlgoliasearchNetlify';
2 |
3 | // eslint-disable-next-line import/no-commonjs
4 | module.exports = algoliasearchNetlify;
5 |
--------------------------------------------------------------------------------
/frontend/src/templates.tsx:
--------------------------------------------------------------------------------
1 | import type { AutocompleteComponents, VNode } from '@algolia/autocomplete-js';
2 | import type { Hit } from '@algolia/client-search';
3 |
4 | import type { AlgoliaRecord } from './types';
5 |
6 | export const templates = {
7 | poweredBy: ({ hostname }: { hostname: string }): VNode => {
8 | const escapedHostname = encodeURIComponent(hostname);
9 | return (
10 |
19 | );
20 | },
21 |
22 | item: (
23 | hit: AlgoliaRecord,
24 | components: AutocompleteComponents
25 | ): JSX.Element => {
26 | return (
27 |
28 |
29 |
40 |
41 |
42 | {hit.hierarchy?.lvl0 ?? (
43 |
44 | )}
45 |
46 | {hit.hierarchy && (
47 |
48 | {hierarchyToBreadcrumbs(hit, components)}
49 |
50 | )}
51 |
52 | {getSuggestionSnippet(hit, components)}
53 |
54 |
55 |
56 |
57 | );
58 | },
59 | };
60 |
61 | /**
62 | * Transform a highlighted hierarchy object into an array of Highlighted elements.
63 | * 3 levels max are returned.
64 | *
65 | * @param hit - A record with a hierarchy field ( { lvl0: string, lvl1: string, lvl2: string, ... } ).
66 | * @param components - Autocomplete components.
67 | * @returns An array of JSX.Elements | string, representing of the highlighted hierarchy starting from lvl1.
68 | * Between each element, we insert a ' > ' character to render them as breadcrumbs eventually.
69 | */
70 | function hierarchyToBreadcrumbs(
71 | hit: Hit,
72 | components: AutocompleteComponents
73 | ): Array {
74 | const breadcrumbArray: Array = [];
75 | let addedLevels = 0;
76 | if (!hit.hierarchy) {
77 | return breadcrumbArray;
78 | }
79 | for (let i = 1; i < 7 && addedLevels < 3; ++i) {
80 | const lvl = `lvl${i}`;
81 | if (hit.hierarchy[lvl] && hit.hierarchy[lvl].length > 0) {
82 | if (addedLevels > 0) {
83 | breadcrumbArray.push(' > ');
84 | }
85 | breadcrumbArray.push(
86 |
87 | );
88 | ++addedLevels;
89 | }
90 | }
91 | return breadcrumbArray;
92 | }
93 |
94 | function getSuggestionSnippet(
95 | hit: Hit,
96 | components: AutocompleteComponents
97 | ): JSX.Element | string {
98 | // If they are defined as `searchableAttributes`, 'description' and 'content' are always
99 | // present in the `_snippetResult`, even if they don't match.
100 | // So we need to have 1 check on the presence and 1 check on the match
101 | const description = hit._snippetResult?.description;
102 | const content = hit._snippetResult?.content;
103 |
104 | // Take in priority props that matches the search
105 | if (description && description.matchLevel === 'full') {
106 | return ;
107 | }
108 | if (content && content.matchLevel === 'full') {
109 | return ;
110 | }
111 |
112 | // Otherwise take the prop that was at least correctly returned
113 | if (description && !content) {
114 | return ;
115 | }
116 | if (content) {
117 | return ;
118 | }
119 |
120 | // Otherwise raw value or empty
121 | return hit.description || hit.content || '';
122 | }
123 |
--------------------------------------------------------------------------------
/frontend/src/types/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'autocomplete.js' {
2 | declare function autocomplete(..._args: any[]): any;
3 | export default autocomplete;
4 | }
5 | declare module 'autocomplete.js/src/common/utils' {
6 | declare function isMsie(): boolean;
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './options';
2 | export * from './record';
3 |
--------------------------------------------------------------------------------
/frontend/src/types/options.ts:
--------------------------------------------------------------------------------
1 | export interface Options {
2 | // Mandatory
3 | appId: string;
4 | apiKey: string;
5 | selector: string;
6 | siteId: string;
7 | branch: string;
8 |
9 | // Optional
10 | analytics?: boolean;
11 | hitsPerPage?: number;
12 | theme?: {
13 | mark?: string;
14 | background?: string;
15 | selected?: string;
16 | text?: string;
17 | colorSourceIcon?: string;
18 | };
19 | debug?: boolean;
20 | detached?: boolean | { mediaQuery: string };
21 | placeholder?: string;
22 | openOnFocus?: boolean;
23 | poweredBy?: boolean;
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/src/types/record.ts:
--------------------------------------------------------------------------------
1 | export type AlgoliaRecord = {
2 | objectID: string;
3 |
4 | url: string;
5 | origin: string;
6 | title: string;
7 | content: string;
8 |
9 | lang?: string;
10 | description?: string;
11 | keywords?: string[];
12 | image?: string;
13 | authors?: string[];
14 | datePublished?: number;
15 | dateModified?: number;
16 | category?: string;
17 |
18 | hierarchy?: Hierarchy;
19 | hierarchicalCategories?: Hierarchy;
20 |
21 | urlDepth?: number;
22 | position?: number;
23 | };
24 |
25 | export type Hierarchy = { [lvl: string]: string };
26 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "lib": ["es2020", "DOM"],
5 | "rootDir": "src",
6 | /* Use a fake outDir */
7 | "outDir": "dist-webpack-never-used",
8 | "jsx": "react-jsx",
9 | "jsxImportSource": "preact"
10 | },
11 | "include": ["src/**/*"]
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | /* eslint import/no-commonjs: 'off' */
3 | const path = require('path');
4 |
5 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
6 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
8 | const TerserPlugin = require('terser-webpack-plugin');
9 | const { HotModuleReplacementPlugin } = require('webpack');
10 |
11 | function plugins({ production }) {
12 | if (production === undefined) {
13 | throw new Error('plugins: Missing parameter');
14 | }
15 |
16 | const defaultPlugins = [
17 | new CleanWebpackPlugin(),
18 | new MiniCssExtractPlugin({
19 | filename: '[name].css',
20 | chunkFilename: '[id].css',
21 | }),
22 | ];
23 |
24 | let additionalPlugins = [];
25 | if (production) {
26 | console.log('No additionalPlugin yet');
27 | } else {
28 | additionalPlugins = [
29 | new HotModuleReplacementPlugin(),
30 | new ForkTsCheckerWebpackPlugin({
31 | typescript: {
32 | configFile: 'tsconfig.json',
33 | },
34 | eslint: {
35 | files: 'src/**/*.{js,ts}',
36 | },
37 | }),
38 | ];
39 | }
40 |
41 | return [...defaultPlugins, ...additionalPlugins];
42 | }
43 |
44 | function styleLoaders({ production, sourceMap }) {
45 | if (production === undefined || sourceMap === undefined) {
46 | throw new Error('styleLoaders: Missing parameter');
47 | }
48 |
49 | const loaders = [];
50 |
51 | loaders.push({
52 | loader: MiniCssExtractPlugin.loader,
53 | });
54 |
55 | loaders.push({
56 | loader: 'css-loader',
57 | options: {
58 | sourceMap,
59 | },
60 | });
61 |
62 | loaders.push({
63 | loader: 'postcss-loader',
64 | options: {
65 | postcssOptions: {
66 | plugins: [
67 | [
68 | 'postcss-preset-env',
69 | {
70 | stage: 4, // Only stable polyfills
71 | autoprefixer: {},
72 | },
73 | ],
74 | ],
75 | sourceMap,
76 | },
77 | },
78 | });
79 |
80 | loaders.push({
81 | loader: 'sass-loader',
82 | options: {
83 | sourceMap,
84 | sassOptions: {
85 | includePaths: [path.resolve(__dirname, 'src')],
86 | },
87 | },
88 | });
89 |
90 | return loaders;
91 | }
92 |
93 | module.exports = function (env, options) {
94 | const mode = options.mode || 'development';
95 | const production = mode === 'production';
96 | const sourceMap = true;
97 |
98 | console.log(`Webpack running in "${mode}" mode`);
99 |
100 | const resolvedExtensions = ['.ts', '.tsx', '.js'];
101 |
102 | const buildFolder = 'dist';
103 |
104 | // See what changes what here: https://webpack.js.org/configuration/devtool/#devtool
105 | let devtool = 'eval-cheap-module-source-map';
106 | if (production) {
107 | devtool = 'cheap-source-map';
108 | } else if (process.env.CI) {
109 | devtool = undefined;
110 | }
111 |
112 | let devServer;
113 | if (!production) {
114 | devServer = {
115 | static: path.resolve(__dirname, buildFolder),
116 | port: process.env.PORT || '1234',
117 | historyApiFallback: true,
118 | hot: true,
119 | client: {
120 | overlay: {
121 | warnings: false,
122 | errors: true,
123 | },
124 | },
125 | headers: {
126 | 'Access-Control-Allow-Origin': 'http://localhost:9000',
127 | 'Access-Control-Allow-Methods':
128 | 'HEAD, GET, POST, PUT, DELETE, PATCH, OPTIONS',
129 | },
130 | };
131 | }
132 |
133 | return {
134 | target: 'web',
135 | mode,
136 | devServer,
137 | entry: {
138 | algoliasearchNetlify: [
139 | path.resolve(__dirname, 'src', 'index.scss'),
140 | path.resolve(__dirname, 'src', 'index.ts'),
141 | ],
142 | },
143 | devtool,
144 | module: {
145 | rules: [
146 | {
147 | test: /\.tsx?$/,
148 | exclude: /node_modules/,
149 | use: [
150 | {
151 | loader: 'babel-loader',
152 | options: {
153 | compact: production,
154 | presets: [
155 | ['@babel/preset-env', { useBuiltIns: 'entry', corejs: '3' }],
156 | ],
157 | },
158 | },
159 | {
160 | loader: 'ts-loader',
161 | options: {
162 | configFile: 'tsconfig.json',
163 | transpileOnly: !production,
164 | experimentalWatchApi: !production,
165 | },
166 | },
167 | ],
168 | },
169 | {
170 | test: /\.scss$/,
171 | exclude: /node_modules/,
172 | use: styleLoaders({ production, sourceMap }),
173 | },
174 | ],
175 | },
176 | resolve: {
177 | extensions: resolvedExtensions,
178 | },
179 | optimization: {
180 | minimize: production,
181 | minimizer: [
182 | new TerserPlugin({
183 | extractComments: false,
184 | parallel: true,
185 | terserOptions: {
186 | parse: {},
187 | compress: {},
188 | mangle: true,
189 | output: null,
190 | toplevel: true,
191 | ie8: false,
192 | },
193 | }),
194 | ],
195 | usedExports: true,
196 | sideEffects: true,
197 | },
198 | plugins: plugins({ production }),
199 | output: {
200 | path: path.resolve(__dirname, buildFolder),
201 | filename: '[name].js',
202 | libraryTarget: 'umd',
203 | library: '[name]',
204 | publicPath: '/',
205 | chunkLoadTimeout: 30000,
206 | },
207 | stats: {
208 | assets: true,
209 | assetsSort: 'size',
210 | builtAt: false,
211 | cached: false,
212 | cachedAssets: false,
213 | children: false,
214 | chunks: false,
215 | colors: true,
216 | entrypoints: false,
217 | hash: false,
218 | loggingTrace: true,
219 | modules: false,
220 | version: false,
221 | },
222 | };
223 | };
224 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-commonjs
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | testPathIgnorePatterns: ['/node_modules/', '/dist/'],
6 | };
7 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/algoliasearch-netlify/97ec146098930e60b51ea973ebdff06c32dfef8c/logo.png
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 |
2 | # This file is generated by scripts/generate_netlify_toml.sh
3 | # DO NOT MODIFY, MODIFY THE GENERATING SCRIPT
4 |
5 |
6 | [[plugins]]
7 | package = "@algolia/netlify-plugin-crawler"
8 | [plugins.inputs]
9 | branches = ["*"]
10 |
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@algolia/netlify",
3 | "version": "1.0.15",
4 | "private": true,
5 | "author": "Algolia Team ",
6 | "license": "MIT",
7 | "repository": "https://github.com/algolia/algoliasearch-netlify.git",
8 | "workspaces": [
9 | "frontend",
10 | "plugin"
11 | ],
12 | "scripts": {
13 | "build": "echo 'Website already built in public'",
14 | "build:plugin": "cd plugin && yarn build",
15 | "dev": "yarn && concurrently --success first --kill-others --names 'plugin,frontend,website' --prefix-colors 'magenta,cyan,yellow' 'yarn dev:plugin' 'yarn dev:frontend' 'yarn dev:website'",
16 | "dev:frontend": "cd frontend && yarn dev",
17 | "dev:plugin": "cd plugin && yarn dev",
18 | "dev:website": "http-server --port 9000 -c -1 public",
19 | "changelog:generate": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
20 | "lint": "eslint --ext=jsx,ts,tsx,js .",
21 | "release": "./scripts/release.sh",
22 | "test": "jest"
23 | },
24 | "devDependencies": {
25 | "@algolia/algoliasearch-netlify-frontend": "*",
26 | "@algolia/netlify-plugin-crawler": "*",
27 | "@netlify/build": "26.5.2",
28 | "@types/jest": "27.4.1",
29 | "@types/node": "16.11.26",
30 | "@typescript-eslint/eslint-plugin": "5.18.0",
31 | "@typescript-eslint/parser": "5.18.0",
32 | "concurrently": "7.1.0",
33 | "conventional-changelog": "3.1.25",
34 | "conventional-changelog-cli": "2.2.2",
35 | "eslint": "8.12.0",
36 | "eslint-config-algolia": "20.0.0",
37 | "eslint-config-prettier": "8.5.0",
38 | "eslint-config-standard": "16.0.3",
39 | "eslint-plugin-algolia": "2.0.0",
40 | "eslint-plugin-eslint-comments": "3.2.0",
41 | "eslint-plugin-import": "2.26.0",
42 | "eslint-plugin-jest": "25.7.0",
43 | "eslint-plugin-jsdoc": "37.9.7",
44 | "eslint-plugin-jsx-a11y": "6.6.0",
45 | "eslint-plugin-node": "11.1.0",
46 | "eslint-plugin-prettier": "4.0.0",
47 | "eslint-plugin-promise": "6.0.0",
48 | "eslint-plugin-react": "7.29.4",
49 | "eslint-plugin-react-hooks": "4.4.0",
50 | "eslint-plugin-standard": "4.1.0",
51 | "http-server": "14.1.0",
52 | "jest": "27.5.1",
53 | "json": "11.0.0",
54 | "netlify-cli": "8.19.3",
55 | "nodemon": "2.0.15",
56 | "prettier": "2.7.1",
57 | "renovate-config-algolia": "2.1.9",
58 | "ts-jest": "27.1.4",
59 | "typescript": "4.6.3"
60 | },
61 | "engines": {
62 | "yarn": "1.*",
63 | "node": "16.*"
64 | },
65 | "packageManager": "yarn@3.2.0"
66 | }
67 |
--------------------------------------------------------------------------------
/plugin/.nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "ext": "js,json,ts",
3 | "watch": [
4 | "../.env",
5 | "src"
6 | ],
7 | "delay": "500",
8 | "verbose": false,
9 | "ignore": []
10 | }
11 |
--------------------------------------------------------------------------------
/plugin/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Scripts
4 |
5 | - `yarn dev`: run dev environment
6 |
7 | ## Development
8 |
9 | ### Pre-requisites
10 |
11 | **Only accessible to Algolia employees.**
12 |
13 | 1. Access to the Algolia team on Netlify (only granted to Algolia employees).
14 | 2. Access to the test website in this org:
15 | 3. Clone the repo and link it to the test website on Netlify:
16 |
17 | ```sh
18 | git clone git@github.com:algolia/algoliasearch-netlify.git
19 | cd algoliasearch-netlify
20 | yarn
21 | yarn netlify link
22 | # Accept linking it with the current git remote, it'll detect the correct site automatically
23 | ```
24 |
25 | 4. Setup `.env` by copying the example file:
26 |
27 | ```sh
28 | cp .env.example .env
29 | ```
30 |
31 | Make sure the values in this file are good.
32 |
33 | ### Running the dev env
34 |
35 | From this folder:
36 |
37 | ```sh
38 | yarn dev
39 | ```
40 |
41 | Or from the root of the repository:
42 |
43 | ```sh
44 | yarn dev:plugin
45 | ```
46 |
47 | It builds the site locally, running the local version of the plugin.
48 |
49 | To change the crawler target from the prod one to a local instance, simply change in your `.env` `ALGOLIA_BASE_URL`.
50 |
--------------------------------------------------------------------------------
/plugin/README.md:
--------------------------------------------------------------------------------
1 | # netlify-plugin-crawler
2 |
3 | This plugin links your Netlify site with Algolia's Crawler.
4 | It will trigger a crawl on each successful build.
5 |
6 | - [Install the plugin](https://www.algolia.com/doc/tools/crawler/netlify-plugin/quick-start/)
7 | - [Plugin configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/plugin/)
8 | - [Front-end configuration](https://www.algolia.com/doc/tools/crawler/netlify-plugin/front-end/)
9 |
10 | ## Development & Release
11 |
12 | See [CONTRIBUTING.md](./CONTRIBUTING.md).
13 |
--------------------------------------------------------------------------------
/plugin/manifest.yml:
--------------------------------------------------------------------------------
1 | name: '@algolia/netlify-plugin-crawler'
2 | inputs:
3 | - name: disabled
4 | description: Disable the plugin
5 | default: false
6 | - name: branches
7 | description: Branches to index. Each branch in this array will have its content crawled in a dedicated index.
8 | default: ['master', 'main']
9 | - name: mainBranch
10 | description: Main working branch. Useful when you tweak Algolia search settings. If set, Algolia indices created for other branches will inherit the settings of the mainBranch's index.
11 | - name: pathPrefix
12 | description: The prefix of your website if it's not at the root level, e.g /blog
13 | - name: customDomain
14 | description: The custom domain that you use, if it's not possible to define it on your Netlify's settings.
15 | - name: renderJavaScript
16 | description: If true, we will use JavaScript to render your website.
17 | - name: template
18 | description: Change the way we extract records and their schema.
19 |
--------------------------------------------------------------------------------
/plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@algolia/netlify-plugin-crawler",
3 | "version": "1.0.15",
4 | "author": "Algolia Team ",
5 | "license": "MIT",
6 | "repository": "https://github.com/algolia/algoliasearch-netlify.git",
7 | "bugs": {
8 | "url": "https://github.com/algolia/algoliasearch-netlify/issues"
9 | },
10 | "main": "dist/index.js",
11 | "files": [
12 | "README.md",
13 | "manifest.yml",
14 | "dist"
15 | ],
16 | "scripts": {
17 | "build": "npx tsc -b",
18 | "dev": "nodemon --config .nodemon.json --exec '../scripts/dev_plugin.sh'"
19 | },
20 | "dependencies": {
21 | "node-fetch": "2.6.7"
22 | },
23 | "devDependencies": {
24 | "@types/node-fetch": "2.6.1",
25 | "dotenv": "16.0.0"
26 | },
27 | "keywords": [
28 | "netlify",
29 | "netlify-plugin",
30 | "search",
31 | "crawl",
32 | "crawler",
33 | "robots",
34 | "indexing",
35 | "netlify-search",
36 | "algolia",
37 | "algoliasearch"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/plugin/src/dev.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | function forceEnvVar(env: Record, key: string): void {
4 | if (env[key] === undefined) {
5 | throw new Error(`Missing ${key} in .env`);
6 | }
7 | process.env[key] = env[key];
8 | }
9 |
10 | // In dev env, yarn netlify build inherits of the env vars set in Netlify's UI.
11 | // We need to manually override them.
12 | export function loadDevEnvVariables(): void {
13 | // eslint-disable-next-line @typescript-eslint/no-var-requires
14 | const dotenv = require('dotenv');
15 |
16 | const filePath = path.join(__dirname, '..', '..', '.env');
17 | const env = dotenv.config({ path: filePath }).parsed;
18 |
19 | forceEnvVar(env, 'ALGOLIA_API_KEY');
20 | forceEnvVar(env, 'ALGOLIA_BASE_URL');
21 | }
22 |
--------------------------------------------------------------------------------
/plugin/src/escapeRegExp.ts:
--------------------------------------------------------------------------------
1 | // Taken from https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
2 | export function escapeRegExp(string: string): string {
3 | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
4 | }
5 |
--------------------------------------------------------------------------------
/plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { NetlifyPluginOptions, NetlifyPluginUtils } from '@netlify/build';
2 | import type { Response } from 'node-fetch';
3 | import fetch from 'node-fetch';
4 |
5 | // @ts-expect-error
6 | import { version } from '../package.json';
7 |
8 | import { loadDevEnvVariables } from './dev';
9 | import { starMatch } from './starMatch';
10 | import type { PluginInputs } from './types';
11 |
12 | function createSummaryLogger(
13 | show: NetlifyPluginUtils['status']['show']
14 | ): (message: string) => void {
15 | return (message): void => {
16 | show({ title: 'Algolia Crawler', summary: message });
17 | console.log(message);
18 | };
19 | }
20 |
21 | export async function onSuccess(
22 | params: NetlifyPluginOptions
23 | ): Promise {
24 | console.log('Algolia Netlify plugin started');
25 |
26 | // Debug
27 | // console.log(JSON.stringify(params, null, 2));
28 | // console.log(JSON.stringify(process.env, null, 2));
29 |
30 | const { utils, inputs, constants } = params;
31 |
32 | const isDev = process.env.ALGOLIA_DEV_ENV === 'true';
33 | const isDebugMode = process.env.NETLIFY_BUILD_DEBUG === 'true';
34 |
35 | if (isDev) loadDevEnvVariables();
36 |
37 | const summary = createSummaryLogger(utils.status.show);
38 |
39 | const siteId = constants.SITE_ID;
40 | const isLocal = constants.IS_LOCAL;
41 |
42 | // HEAD is incorrect locally if you try to run `yarn netlify build`
43 | // before having pushed your first commit on this branch, it says `master`
44 | const branch = process.env.HEAD!;
45 | const siteName = process.env.SITE_NAME;
46 | const deployPrimeUrl = process.env.DEPLOY_PRIME_URL;
47 |
48 | const isEnvDisabled = process.env.ALGOLIA_DISABLED === 'true';
49 | const isInputDisabled = inputs.disabled;
50 |
51 | const algoliaBaseUrl =
52 | process.env.ALGOLIA_BASE_URL || 'https://crawler.algolia.com';
53 | const algoliaApiKey = process.env.ALGOLIA_API_KEY;
54 |
55 | const branches = inputs.branches;
56 | const mainBranch = inputs.mainBranch;
57 | const pathPrefix = inputs.pathPrefix;
58 | const customDomain = inputs.customDomain;
59 | const renderJavaScript = inputs.renderJavaScript;
60 | const template = inputs.template;
61 |
62 | if (isEnvDisabled) {
63 | summary(`Disabled by the "ALGOLIA_DISABLED" environment variable`);
64 | return;
65 | }
66 |
67 | if (isInputDisabled) {
68 | summary(`Disabled by the "disabled" input in "netlify.toml"`);
69 | return;
70 | }
71 |
72 | if (isLocal && !isDev) {
73 | return utils.build.failPlugin(
74 | 'This plugin does not work locally, please deploy to a branch to test it.'
75 | );
76 | }
77 |
78 | // Check internal constants
79 | if (!siteName) {
80 | return utils.build.failPlugin('Missing or invalid SITE_NAME');
81 | }
82 | if (!deployPrimeUrl) {
83 | return utils.build.failPlugin('Missing DEPLOY_PRIME_URL');
84 | }
85 |
86 | // Check required env vars
87 | const missingEnvMessage = (key: string): string =>
88 | `Missing ${key}, please go to ${algoliaBaseUrl}/admin/netlify to complete your installation.`;
89 | if (!algoliaBaseUrl) {
90 | return utils.build.failPlugin(missingEnvMessage('ALGOLIA_BASE_URL'));
91 | }
92 | if (!isDev && !algoliaApiKey) {
93 | return utils.build.failPlugin(missingEnvMessage('ALGOLIA_API_KEY'));
94 | }
95 |
96 | // Check branch is whitelisted
97 | if (!branches.some((pattern) => starMatch(pattern, branch))) {
98 | summary(`"${branch}" is not part of configuration's "branches", skipping`);
99 | return;
100 | }
101 |
102 | const endpoint = `${algoliaBaseUrl}/api/1/netlify/crawl`;
103 | const apiKey =
104 | isDev && !algoliaApiKey ? 'not-necessary-in-dev' : algoliaApiKey;
105 | const creds = `${siteId}:${apiKey}`;
106 |
107 | let response: Response;
108 | try {
109 | const body = JSON.stringify({
110 | branch,
111 | mainBranch,
112 | siteName,
113 | deployPrimeUrl,
114 | version,
115 | pathPrefix,
116 | customDomain,
117 | renderJavaScript,
118 | template,
119 | });
120 | console.log('Sending request to crawl', endpoint);
121 | if (isDebugMode) {
122 | console.log(body);
123 | }
124 |
125 | response = await fetch(endpoint, {
126 | method: 'POST',
127 | headers: {
128 | Authorization: `Basic ${Buffer.from(creds).toString('base64')}`,
129 | 'Content-Type': 'application/json',
130 | },
131 | body,
132 | });
133 |
134 | if (!response.ok) {
135 | console.warn(response);
136 | throw new Error(
137 | `${response.statusText} ${JSON.stringify(response.json())}`
138 | );
139 | }
140 | } catch (error) {
141 | console.error('Could not reach algolia', error);
142 | utils.build.failPlugin(
143 | `Could not reach Algolia's Crawler, got: ${(error as Error).message}`
144 | );
145 | return;
146 | }
147 | const json = (await response.json()) as { crawlerId: string };
148 |
149 | console.log(`API answered: ${response.status}`);
150 |
151 | const crawlerUrl = `${algoliaBaseUrl}/admin/user_configs/${json.crawlerId}`;
152 | summary(`Your crawler is running at: ${crawlerUrl}`);
153 | console.log('Done.');
154 | }
155 |
--------------------------------------------------------------------------------
/plugin/src/starMatch.test.ts:
--------------------------------------------------------------------------------
1 | import { starMatch } from './starMatch';
2 |
3 | describe('starMatch', () => {
4 | it.each([
5 | ['foo', 'foo'],
6 | ['fo*', 'foo'],
7 | ['*oo', 'foo'],
8 | ['*o*', 'foo'],
9 | ['foo*', 'foo'],
10 | ['*foo', 'foo'],
11 | ['*foo*', 'foo'],
12 | ['foo*bar', 'foobar'],
13 | ['fo*ar', 'foobar'],
14 | ])('"%s" should match "%s"', (pattern: string, value: string) => {
15 | expect(starMatch(pattern, value)).toBe(true);
16 | });
17 |
18 | it.each([
19 | ['foo', 'bar'],
20 | ['foo', 'foa'],
21 | ['*foo', 'foobar'],
22 | ['foo*', 'barfoo'],
23 | ])('"%s" should not match "%s"', (pattern: string, value: string) => {
24 | expect(starMatch(pattern, value)).toBe(false);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/plugin/src/starMatch.ts:
--------------------------------------------------------------------------------
1 | import { escapeRegExp } from './escapeRegExp';
2 |
3 | export function starMatch(pattern: string, value: string): boolean {
4 | const regexp = pattern.split('*').map(escapeRegExp).join('.*');
5 | return new RegExp(`^${regexp}$`).test(value);
6 | }
7 |
--------------------------------------------------------------------------------
/plugin/src/types.ts:
--------------------------------------------------------------------------------
1 | export type PluginInputs = {
2 | disabled: boolean;
3 | branches: string[];
4 | mainBranch?: string;
5 | pathPrefix?: string;
6 | customDomain?: string;
7 | renderJavaScript?: boolean;
8 | template?: string;
9 | };
10 |
--------------------------------------------------------------------------------
/plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "./dist"
6 | },
7 | "include": [
8 | "src"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/public/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | First test page
5 |
6 |
7 |
8 | First test page
9 | This is the contents of the first test page.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/public/2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Second test page
5 |
6 |
7 |
8 | Second test page
9 | This is the contents of the second test page.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/public/README.md:
--------------------------------------------------------------------------------
1 | # algoliasearch-netlify test website
2 |
3 | This website is a testing website.
4 | It holds some crawlable content and has a testing page for our frontend library.
5 | It is run by running `yarn dev` or more specifically `yarn dev:website` at the root of the project.
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/algoliasearch-netlify/97ec146098930e60b51ea973ebdff06c32dfef8c/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Algoliasearch Netlify Test Website
6 |
7 |
8 |
9 |
10 |
11 |
106 |
107 |
108 |
109 | Algoliasearch Netlify Test Website
110 |
111 |
115 |
116 | Script
117 |
118 |
119 | Pending...
120 |
121 |
122 |
Base URL
123 |
124 |
CSS
125 |
126 |
JS
127 |
128 |
Code
129 |
130 |
131 |
Test before
132 |
Error
133 |
134 |
135 | Reset
136 | Test
137 |
138 |
139 |
140 | Test content
141 |
142 | Some content to index.
143 | Links to other pages:
144 |
152 |
153 |
154 |
156 |
357 |
358 |
359 |
360 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:js-lib",
4 | "algolia"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/scripts/dev_plugin.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | set -e
4 |
5 | cd "$(dirname "${BASH_SOURCE[0]}")"
6 | cd ..
7 |
8 | [ -f .env ] || (echo 'Missing .env' && exit 1)
9 | set -a
10 | source .env
11 | set +a
12 |
13 | restore_netlify_toml() {
14 | ./scripts/generate_netlify_toml.sh
15 | }
16 |
17 | ALGOLIA_DEV_ENV=true ./scripts/generate_netlify_toml.sh
18 | trap restore_netlify_toml EXIT
19 |
20 | ALGOLIA_DEV_ENV=true yarn netlify build
21 |
--------------------------------------------------------------------------------
/scripts/generate_netlify_toml.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | set -e
4 | cd "$(dirname "${BASH_SOURCE[0]}")"
5 |
6 | target="../netlify.toml"
7 |
8 | # Common content
9 | common='
10 | # This file is generated by scripts/generate_netlify_toml.sh
11 | # DO NOT MODIFY, MODIFY THE GENERATING SCRIPT
12 | '
13 |
14 | # Dev only
15 | dev_only='
16 | [[plugins]]
17 | package = "./plugin/dist/index.js"
18 | [plugins.inputs]
19 | branches = ["*"]
20 | mainBranch = "master"
21 |
22 | [[plugins]]
23 | package = "@algolia/netlify-plugin-crawler"
24 | [plugins.inputs]
25 | disabled = true
26 | '
27 |
28 | # Prod only
29 | prod_only='
30 | [[plugins]]
31 | package = "@algolia/netlify-plugin-crawler"
32 | [plugins.inputs]
33 | branches = ["*"]
34 | '
35 |
36 | echo "$common" >"$target"
37 | if [ "$ALGOLIA_DEV_ENV" = "true" ]; then
38 | echo "$dev_only" >>"$target"
39 | else
40 | echo "$prod_only" >>"$target"
41 | fi
42 |
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | set -e
4 |
5 | cd "$(dirname "${BASH_SOURCE[0]}")"
6 | cd ..
7 |
8 | # Check if the git directory is clean
9 | if [[ $(git diff --shortstat 2>/dev/null | tail -n1) != "" ]]; then
10 | echo "Your git directory is unclean"
11 | exit 1
12 | fi
13 |
14 | current=$(npx json -f package.json version)
15 | read -p "New version number (current is ${current}): " version
16 | export ALGOLIASEARCH_NETLIFY_VERSION=$version
17 |
18 | # Ask for confirmation
19 | echo
20 | read -p "[All] We'll release \"v$ALGOLIASEARCH_NETLIFY_VERSION\". Continue (yn)? " -n 1 -r
21 | echo
22 | [[ $REPLY =~ ^[Yy]$ ]] || exit -1
23 |
24 | # Preparing
25 |
26 | ## Building plugin
27 | echo
28 | echo 'Preparing plugin...'
29 | cd plugin/
30 | npm version -s --no-git-tag-version $ALGOLIASEARCH_NETLIFY_VERSION
31 | yarn build
32 | cd ..
33 |
34 | ## Building front-end
35 | echo
36 | echo 'Preparing frontend...'
37 | cd frontend/
38 | npm version -s --no-git-tag-version $ALGOLIASEARCH_NETLIFY_VERSION
39 | yarn build
40 | cd ..
41 |
42 | ## Git commit & tag
43 | echo
44 | echo 'Preparing changelog, creating commit & tag...'
45 | ### Initial (fake) commit & version tag
46 | npm version -s --no-git-tag-version $ALGOLIASEARCH_NETLIFY_VERSION
47 | git add package.json plugin/package.json frontend/package.json
48 | git commit -m "chore(release): $ALGOLIASEARCH_NETLIFY_VERSION"
49 | git tag -a "v$ALGOLIASEARCH_NETLIFY_VERSION" -m "$ALGOLIASEARCH_NETLIFY_VERSION"
50 | ### Changelog generation (we need the tag to exist)
51 | yarn changelog:generate
52 | git add CHANGELOG.md
53 | git commit --amend -m "chore(release): $ALGOLIASEARCH_NETLIFY_VERSION"
54 | git tag -a "v$ALGOLIASEARCH_NETLIFY_VERSION" -m "$ALGOLIASEARCH_NETLIFY_VERSION" -f
55 |
56 | # Releasing
57 |
58 | echo
59 | echo 'Everything built, ready for release.'
60 |
61 | ## 2FA is mandatory on npm for all Algolia employees
62 | echo
63 | echo "[npm] One time password: "
64 | read OTP
65 | [[ $OTP =~ [0-9]{6} ]] || exit -1
66 |
67 | ## Release plugin
68 | echo
69 | echo "Publishing plugin on npm..."
70 | cd plugin/
71 | npm publish --otp $OTP
72 | cd ..
73 |
74 | echo
75 | echo "Publishing frontend on npm..."
76 | ## Release frontend
77 | cd frontend/
78 | npm publish --otp $OTP
79 | cd ..
80 |
81 | ## Release git
82 | echo
83 | echo "Pushing on git remote..."
84 | git push
85 | git push --tags
86 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 | /* Basic Options */
5 | // "incremental": true, /* Enable incremental compilation */
6 | "target": "es2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
7 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
8 | "lib": [
9 | "es2020"
10 | ] /* Specify library files to be included in the compilation. */,
11 | // "allowJs": true, /* Allow javascript files to be compiled. */
12 | // "checkJs": true, /* Report errors in .js files. */
13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
14 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
15 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
16 | // "sourceMap": true, /* Generates corresponding '.map' file. */
17 | // "outFile": "./", /* Concatenate and emit output to single file. */
18 | // "outDir": "./dist", /* Redirect output structure to the directory. */
19 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
20 | // "composite": true, /* Enable project compilation */
21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
22 | // "removeComments": true, /* Do not emit comments to output. */
23 | // "noEmit": true, /* Do not emit outputs. */
24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
27 | /* Strict Type-Checking Options */
28 | "strict": true /* Enable all strict type-checking options. */,
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 | /* Additional Checks */
37 | // "noUnusedLocals": true, /* Report errors on unused locals. */
38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
41 | /* Module Resolution Options */
42 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
46 | // "typeRoots": [], /* List of folders to include type definitions from. */
47 | // "types": [], /* Type declaration files to be included in compilation. */
48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
52 | /* Source Map Options */
53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
57 | /* Experimental Options */
58 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
59 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
60 | /* Advanced Options */
61 | "skipLibCheck": true /* Skip type checking of declaration files. */,
62 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
63 | },
64 | "include": []
65 | }
66 |
--------------------------------------------------------------------------------