Giter Club home page Giter Club logo

meilisearch-js's Introduction

Meilisearch-JavaScript

Meilisearch JavaScript

npm version Tests Codecov Prettier License Bors enabled

⚑ The Meilisearch API client written for JavaScript

Meilisearch JavaScript is the Meilisearch API client for JavaScript developers.

Meilisearch is an open-source search engine. Learn more about Meilisearch.

Table of Contents

πŸ“– Documentation

This readme contains all the documentation you need to start using this Meilisearch SDK.

For general information on how to use Meilisearchβ€”such as our API reference, tutorials, guides, and in-depth articlesβ€”refer to our main documentation website.

⚑ Supercharge your Meilisearch experience

Say goodbye to server deployment and manual updates with Meilisearch Cloud. Get started with a 14-day free trial! No credit card required.

πŸ”§ Installation

We recommend installing meilisearch-js in your project with your package manager of choice.

npm install meilisearch

meilisearch-js officially supports node versions >= 14 and <= 18.

Instead of using a package manager, you may also import the library directly into your HTML via a CDN.

Run Meilisearch

To use one of our SDKs, you must first have a running Meilisearch instance. Consult our documentation for instructions on how to download and launch Meilisearch.

Import

After installing meilisearch-js, you must import it into your application. There are many ways of doing that depending on your development environment.

import syntax

Usage in an ES module environment:

import { MeiliSearch } from 'meilisearch'

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

<script> tag

Usage in an HTML (or alike) file:

<script src='https://cdn.jsdelivr.net/npm/meilisearch@latest/dist/bundles/meilisearch.umd.js'></script>
<script>
  const client = new MeiliSearch({
    host: 'http://127.0.0.1:7700',
    apiKey: 'masterKey',
  })
</script>

require syntax

Usage in a back-end node.js or another environment supporting CommonJS modules:

const { MeiliSearch } = require('meilisearch')

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

React Native

To use meilisearch-js with React Native, you must also install react-native-url-polyfill.

Deno

Usage in a Deno environment:

import { MeiliSearch } from "https://esm.sh/meilisearch"

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

🎬 Getting started

Add documents

const { MeiliSearch } = require('meilisearch')
// Or if you are in a ES environment
import { MeiliSearch } from 'meilisearch'

;(async () => {
  const client = new MeiliSearch({
    host: 'http://127.0.0.1:7700',
    apiKey: 'masterKey',
  })

  // An index is where the documents are stored.
  const index = client.index('movies')

  const documents = [
      { id: 1, title: 'Carol', genres: ['Romance', 'Drama'] },
      { id: 2, title: 'Wonder Woman', genres: ['Action', 'Adventure'] },
      { id: 3, title: 'Life of Pi', genres: ['Adventure', 'Drama'] },
      { id: 4, title: 'Mad Max: Fury Road', genres: ['Adventure', 'Science Fiction'] },
      { id: 5, title: 'Moana', genres: ['Fantasy', 'Action']},
      { id: 6, title: 'Philadelphia', genres: ['Drama'] },
  ]

  // If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
  let response = await index.addDocuments(documents)

  console.log(response) // => { "uid": 0 }
})()

Tasks such as document addition always return a unique identifier. You can use this identifier taskUid to check the status (enqueued, canceled, processing, succeeded or failed) of a task.

Basic search

// Meilisearch is typo-tolerant:
const search = await index.search('philoudelphia')
console.log(search)

Output:

{
  "hits": [
    {
      "id": "6",
      "title": "Philadelphia",
      "genres": ["Drama"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 1,
  "query": "philoudelphia"
}

Using search parameters

meilisearch-js supports all search parameters described in our main documentation website.

await index.search(
  'wonder',
  {
    attributesToHighlight: ['*']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action", "Adventure"],
      "_formatted": {
        "id": "2",
        "title": "<em>Wonder</em> Woman",
        "genres": ["Action", "Adventure"]
      }
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 0,
  "query": "wonder"
}

Custom search with filters

To enable filtering, you must first add your attributes to the filterableAttributes index setting.

await index.updateFilterableAttributes([
    'id',
    'genres'
  ])

You only need to perform this operation once per index.

Note that Meilisearch rebuilds your index whenever you update filterableAttributes. Depending on the size of your dataset, this might take considerable time. You can track the process using the tasks API).

After you configured filterableAttributes, you can use the filter search parameter to refine your search:

await index.search(
  'wonder',
  {
    filter: ['id > 1 AND genres = Action']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action","Adventure"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 0,
  "query": "wonder"
}

Placeholder search

Placeholder search makes it possible to receive hits based on your parameters without having any query (q). For example, in a movies database you can run an empty query to receive all results filtered by genre.

await index.search(
  '',
  {
    filter: ['genres = fantasy'],
    facets: ['genres']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action","Adventure"]
    },
    {
      "id": 5,
      "title": "Moana",
      "genres": ["Fantasy","Action"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 2,
  "processingTimeMs": 0,
  "query": "",
  "facetDistribution": {
    "genres": {
      "Action": 2,
      "Fantasy": 1,
      "Adventure": 1
    }
  }
}

Note that to enable faceted search on your dataset you need to add genres to the filterableAttributes index setting. For more information on filtering and faceting, consult our documentation settings.

Abortable search

You can abort a pending search request by providing an AbortSignal to the request.

const controller = new AbortController()

index
  .search('wonder', {}, {
    signal: controller.signal,
  })
  .then((response) => {
    /** ... */
  })
  .catch((e) => {
    /** Catch AbortError here. */
  })

controller.abort()

Using Meilisearch behind a proxy

Custom request config

You can provide a custom request configuration. for example, with custom headers.

const client: MeiliSearch = new MeiliSearch({
  host: 'http://localhost:3000/api/meilisearch/proxy',
  requestConfig: {
    headers: {
      Authorization: AUTH_TOKEN
    },
    // OR
    credentials: 'include'
  }
})

Custom http client

You can use your own HTTP client, for example, with axios.

const client: MeiliSearch = new MeiliSearch({
  host: 'http://localhost:3000/api/meilisearch/proxy',
  httpClient: async (url, opts) => {
    const response = await $axios.request({
      url,
      data: opts?.body,
      headers: opts?.headers,
      method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
    })

    return response.data
  }
})

πŸ€– Compatibility with Meilisearch

This package guarantees compatibility with version v1.x of Meilisearch, but some features may not be present. Please check the issues for more info.

πŸ’‘ Learn more

The following sections in our main documentation website may interest you:

This repository also contains more examples.

βš™οΈ Contributing

We welcome all contributions, big and small! If you want to know more about this SDK's development workflow or want to contribute to the repo, please visit our contributing guidelines for detailed instructions.

πŸ“œ API resources

Search

client.index<T>('xxx').search(query: string, options: SearchParams = {}, config?: Partial<Request>): Promise<SearchResponse<T>>
client.index<T>('xxx').searchGet(query: string, options: SearchParams = {}, config?: Partial<Request>): Promise<SearchResponse<T>>

Multi Search

client.multiSearch(queries?: MultiSearchParams, config?: Partial<Request>): Promise<Promise<MultiSearchResponse<T>>>

multiSearch uses the POST method when performing its request to Meilisearch.

Search For Facet Values

client.index<T>('myIndex').searchForFacetValues(params: SearchForFacetValuesParams, config?: Partial<Request>): Promise<SearchForFacetValuesResponse>

Documents

client.index('myIndex').addDocuments(documents: Document<T>[]): Promise<EnqueuedTask>
client.index('myIndex').addDocumentsFromString(documents: string, contentType: ContentType, queryParams: RawDocumentAdditionOptions): Promise<EnqueuedTask>
client.index('myIndex').addDocumentsInBatches(documents: Document<T>[], batchSize = 1000): Promise<EnqueuedTask[]>
client.index('myIndex').updateDocuments(documents: Array<Document<Partial<T>>>): Promise<EnqueuedTask>
client.index('myIndex').updateDocumentsFromString(documents: string, contentType: ContentType, queryParams: RawDocumentAdditionOptions): Promise<EnqueuedTask>
client.index('myIndex').updateDocumentsInBatches(documents: Array<Document<Partial<T>>>, batchSize = 1000): Promise<EnqueuedTask[]>
client.index.getDocuments(parameters: DocumentsQuery = {}): Promise<DocumentsResults<T>>>
client.index('myIndex').getDocument(documentId: string): Promise<Document<T>>
client.index('myIndex').deleteDocument(documentId: string | number): Promise<EnqueuedTask>
client.index('myIndex').deleteDocuments(params: DocumentsDeletionQuery | DocumentsIds): Promise<EnqueuedTask>
client.index('myIndex').deleteAllDocuments(): Promise<Types.EnqueuedTask>

Tasks

client.getTasks(parameters: TasksQuery): Promise<TasksResults>
client.getTask(uid: number): Promise<Task>
client.deleteTasks(parameters: DeleteTasksQuery = {}): Promise<EnqueuedTask>
client.cancelTasks(parameters: CancelTasksQuery = {}): Promise<EnqueuedTask>
client.index('myIndex').getTasks(parameters: TasksQuery): Promise<TasksResults>
client.index('myIndex').getTask(uid: number): Promise<Task>

Wait for one task

Using the client
client.waitForTask(uid: number, { timeOutMs?: number, intervalMs?: number }): Promise<Task>
Using the index
client.index('myIndex').waitForTask(uid: number, { timeOutMs?: number, intervalMs?: number }): Promise<Task>

Wait for multiple tasks

Using the client
client.waitForTasks(uids: number[], { timeOutMs?: number, intervalMs?: number }): Promise<Task[]>
Using the index
client.index('myIndex').waitForTasks(uids: number[], { timeOutMs?: number, intervalMs?: number }): Promise<Task[]>

Indexes

client.getIndexes(parameters: IndexesQuery): Promise<IndexesResults<Index[]>>
client.getRawIndexes(parameters: IndexesQuery): Promise<IndexesResults<IndexObject[]>>
client.createIndex<T>(uid: string, options?: IndexOptions): Promise<EnqueuedTask>

Create a local reference to an index

client.index<T>(uid: string): Index<T>
client.getIndex<T>(uid: string): Promise<Index<T>>
client.getRawIndex(uid: string): Promise<IndexObject>
client.index('myIndex').getRawInfo(): Promise<IndexObject>
Using the client
client.updateIndex(uid: string, options: IndexOptions): Promise<EnqueuedTask>
Using the index object
client.index('myIndex').update(data: IndexOptions): Promise<EnqueuedTask>
Using the client
client.deleteIndex(uid): Promise<void>
Using the index object
client.index('myIndex').delete(): Promise<void>
client.index('myIndex').getStats(): Promise<IndexStats>
Return Index instance with updated information
client.index('myIndex').fetchInfo(): Promise<Index>
Get Primary Key of an Index
client.index('myIndex').fetchPrimaryKey(): Promise<string | undefined>
Swap two indexes
client.swapIndexes(params: SwapIndexesParams): Promise<EnqueuedTask>

Settings

client.index('myIndex').getSettings(): Promise<Settings>
client.index('myIndex').updateSettings(settings: Settings): Promise<EnqueuedTask>
client.index('myIndex').resetSettings(): Promise<EnqueuedTask>

Pagination Settings

client.index('myIndex').getPagination(): Promise<PaginationSettings>
client.index('myIndex').updatePagination(pagination: PaginationSettings): Promise<EnqueuedTask>
client.index('myIndex').resetPagination(): Promise<EnqueuedTask>

Synonyms

client.index('myIndex').getSynonyms(): Promise<Synonyms>
client.index('myIndex').updateSynonyms(synonyms: Synonyms): Promise<EnqueuedTask>
client.index('myIndex').resetSynonyms(): Promise<EnqueuedTask>

Stop words

client.index('myIndex').getStopWords(): Promise<string[]>
client.index('myIndex').updateStopWords(stopWords: string[] | null ): Promise<EnqueuedTask>
client.index('myIndex').resetStopWords(): Promise<EnqueuedTask>

Ranking rules

client.index('myIndex').getRankingRules(): Promise<string[]>
client.index('myIndex').updateRankingRules(rankingRules: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetRankingRules(): Promise<EnqueuedTask>

Distinct Attribute

client.index('myIndex').getDistinctAttribute(): Promise<string | void>
client.index('myIndex').updateDistinctAttribute(distinctAttribute: string | null): Promise<EnqueuedTask>
client.index('myIndex').resetDistinctAttribute(): Promise<EnqueuedTask>

Searchable attributes

client.index('myIndex').getSearchableAttributes(): Promise<string[]>
client.index('myIndex').updateSearchableAttributes(searchableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetSearchableAttributes(): Promise<EnqueuedTask>

Displayed attributes

client.index('myIndex').getDisplayedAttributes(): Promise<string[]>
client.index('myIndex').updateDisplayedAttributes(displayedAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetDisplayedAttributes(): Promise<EnqueuedTask>

Filterable attributes

client.index('myIndex').getFilterableAttributes(): Promise<string[]>
client.index('myIndex').updateFilterableAttributes(filterableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetFilterableAttributes(): Promise<EnqueuedTask>

Sortable attributes

client.index('myIndex').getSortableAttributes(): Promise<string[]>
client.index('myIndex').updateSortableAttributes(sortableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetSortableAttributes(): Promise<EnqueuedTask>

Faceting

client.index('myIndex').getFaceting(): Promise<Faceting>
client.index('myIndex').updateFaceting(faceting: Faceting): Promise<EnqueuedTask>
client.index('myIndex').resetFaceting(): Promise<EnqueuedTask>

Typo tolerance

client.index('myIndex').getTypoTolerance(): Promise<TypoTolerance>
client.index('myIndex').updateTypoTolerance(typoTolerance: TypoTolerance | null): Promise<EnqueuedTask>
client.index('myIndex').resetTypoTolerance(): Promise<EnqueuedTask>

Separator tokens

client.index('myIndex').getSeparatorTokens(): Promise<SeparatorTokens>
client.index('myIndex').updateSeparatorTokens(separatorTokens: SeparatorTokens | null): Promise<EnqueuedTask>
client.index('myIndex').resetSeparatorTokens(): Promise<EnqueuedTask>

Non Separator tokens

client.index('myIndex').getNonSeparatorTokens(): Promise<NonSeparatorTokens>
client.index('myIndex').updateNonSeparatorTokens(nonSeparatorTokens: NonSeparatorTokens | null): Promise<EnqueuedTask>
client.index('myIndex').resetNonSeparatorTokens(): Promise<EnqueuedTask>

Dictionary

client.index('myIndex').getDictionary(): Promise<Dictionary>
client.index('myIndex').updateDictionary(dictionary: Dictionary | null): Promise<EnqueuedTask>
client.index('myIndex').resetDictionary(): Promise<EnqueuedTask>

Proximity Precision

client.index('myIndex').getProximityPrecision(): Promise<ProximityPrecision>
client.index('myIndex').updateProximityPrecision(proximityPrecision: ProximityPrecision): Promise<EnqueuedTask>
client.index('myIndex').resetProximityPrecision(): Promise<EnqueuedTask>

Embedders

⚠️ This feature is experimental. Activate the vectorStore experimental feature to use it

client.index('myIndex').getEmbedders(): Promise<Embedders>
client.index('myIndex').updateEmbedders(embedders: Embedders): Promise<EnqueuedTask>
client.index('myIndex').resetEmbedders(): Promise<EnqueuedTask>

Keys

client.getKeys(parameters: KeysQuery): Promise<KeysResults>
client.getKey(keyOrUid: string): Promise<Key>
client.createKey(options: KeyCreation): Promise<Key>
client.updateKey(keyOrUid: string, options: KeyUpdate): Promise<Key>
client.deleteKey(keyOrUid: string): Promise<void>

isHealthy

client.isHealthy(): Promise<boolean>

Health

client.health(): Promise<Health>

Stats

client.getStats(): Promise<Stats>

Version

client.getVersion(): Promise<Version>

Dumps

client.createDump(): Promise<EnqueuedTask>

Snapshots

client.createSnapshot(): Promise<EnqueuedTask>

Meilisearch provides and maintains many SDKs and integration tools like this one. We want to provide everyone with an amazing search experience for any kind of project. For a full overview of everything we create and maintain, take a look at the integration-guides repository.

meilisearch-js's People

Contributors

alallema avatar amit-ksh avatar artfuldev avatar bb avatar bidoubiwa avatar blenderskool avatar bors[bot] avatar brunoocasali avatar curquiza avatar dependabot-preview[bot] avatar dependabot[bot] avatar dichotommy avatar emyann avatar eskombro avatar icyjoseph avatar k-kumar-01 avatar mdubus avatar meili-bors[bot] avatar meili-bot avatar neupauer avatar nicolasvienot avatar qdequele avatar roy9495 avatar sushrut111 avatar thelearneer avatar tpayet avatar trim21 avatar vishalsodani avatar vishnugt avatar viveknshah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

meilisearch-js's Issues

Example Request using locally hosted instance of meilisearch

I am making a Zola(static site generator built with rust) startpage theme and I would like to add some awesome functionality that would allow the user to type rs and then be able to search crates & rustdoc similiar to this rust search extension. I think it would be awesome to have a startpage that can help them with any rust problems they may have.

I've been reading the documentation and the example and I'm not sure where to start with meilisearch-js.

Make the createIndex method to return an Index object

Currently, if we create an index, we have to get it just after to use the associated methods:

await client.createIndex({ uid: 'books' })
client.getIndex('books').addDocument(...)

It would be better to return an Index object in the createIndex method, so that we can write:

client.createIndex({ uid: 'books' }).addDocuments(...)

This suggestion is breaking and should wait for the v0.10.0 of MeiliSearch to be implemented πŸ˜‰

Getting started does not work

I tried:

$ yarn init
$ yarn add meilisearch

In test.js

const MeiliSearch = require('meilisearch')

const meili = new MeiliSearch({ host: 'http://127.0.0.1:7700' })
const index = await meili.createIndex({ uid: 'indexUID' })

Then:

$ node test.js

I get:

const index = await meili.createIndex({ uid: 'indexUID' })

SyntaxError: await is only valid in async function
    at Module._compile (internal/modules/cjs/loader.js:723:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)

(Same error if I try with getIndex)

Can we do a getting started directly working for the JS beginners like me? πŸ˜‡

Version

meilisearch: v0.9.0
node: v10.18.1

Make eslint work on es, node and typescrypt files

For the moment only the .ts files are correctly linted.

Some javascript import/export files are also linted correctly but recieve typescript warning as the following :

$ /meilisearch-js/rollup.config.js
  9:1  warning  Missing return type on function  @typescript-eslint/explicit-function-return-type

The whole scripts directory written in node require/exports are conflicting with the linter rules:

$ meilisearch-js/scripts/build.js
   5:32  error    Require statement not part of import statement    @typescript-eslint/no-var-requires
   6:41  error    Require statement not part of import statement    @typescript-eslint/no-var-requires
   7:13  error    Require statement not part of import statement    @typescript-eslint/no-var-requires
  12:1   error    'main' was used before it was defined             @typescript-eslint/no-use-before-define
  14:1   warning  Missing return type on function                   @typescript-eslint/explicit-function-return-type
  15:3   error    'writeDtsHeader' was used before it was defined   @typescript-eslint/no-use-before-define
  18:1   warning  Missing return type on function                   @typescript-eslint/explicit-function-return-type
  19:21  error    'getDtsHeader' was used before it was defined     @typescript-eslint/no-use-before-define
  27:3   error    'prependFileSync' was used before it was defined  @typescript-eslint/no-use-before-define
  38:1   warning  Missing return type on function                   @typescript-eslint/explicit-function-return-type
  39:27  error    Use the `RegExp#exec()` method instead            @typescript-eslint/prefer-regexp-exec
  56:1   warning  Missing return type on function                   @typescript-eslint/explicit-function-return-type

It would be nice if the eslintrc files could make rules based on the file types.

When I add the linting of the examples directory it becomes really slow. This is why I have added the directory in .eslintignore

TODO

  • Linting works on every JS files in the repository without enforcing typescript rules
  • Linting works on the examples directory without improving too much the linting time

Facet Search Not Working Correctly and Docs Incorrect

JS snippet in docs indicates no need for quotes "

client.getIndex('movies')
  .search('thriller', {
    facetFilters: [['genres:Horror', 'genres:Mystery'], 'director:Jordan Peele']
  })

Additionally Facet Filter is not working for me on Front-End


Example

      attributesForFaceting: ['category'],
  const searchOverIndex = async (term: string) => {
    console.log(`category:"${activeCategory}"`)
    const search = await usersIndex.search(
      term || 'search',
      activeCategory ? { facetFilters: [`category:"${activeCategory}"`] } : {}
    );
    setUsers(search.hits as any);
  };

Console Output

category:"Computing & ICT"

Result

Hits stay the same despite faceting

Version 0.11

Cleanup tsconfig file

Since we use rollup-plugin-typescript2 some configuration from our tsconfig.json are ignored.
Still, it is used by the linter as a parser.

It would be great if someone with good typescript knowledge could cleanup the file or even improve it if needed.

Make package compatible with node <v12.0

Because of the function fromEntries used in this repo the package is not compatible in environments with a node version lower than v12.

This is an unecessary restriction as there are many alternatives to this function.

attributesForFaceting type in settings

I'm looking to add attributesForFaceting to my initial settings for an index like so:

    await searchIndex.getIndex('users').updateSettings({
      searchableAttributes,
      displayedAttributes,
      rankingRules,
      attrbutesForFaceting: [...]
    });

The code completion/types don't seem to be there. Is this yet to be implemented?
The docs express that you can explicitly call:

client.getIndex('movies')
  .updateAttributesForFaceting([
    'genre',
    'director'
  ])

Should this be used instead, after the initial updateSettings call?

Version 0.11

CI does not pass on some occasion

Somes it's on node 10, sometimes on node 12 or 14.

Screenshot 2020-10-12 at 11 29 22

It is always the same test that is failing. I guess it is because we should give the server more time somewhere to complete its task.

The failing test is in /tests/env/express.

Here is the error output:

Jest dev-server output:
[Jest Dev server] /home/runner/work/meilisearch-js/meilisearch-js/tests/env/express
[Jest Dev server] Running at Port 3000
(node:3291) UnhandledPromiseRejectionWarning: Error: Caught error after test environment was torn down

Navigation failed because browser has disconnected!
    at LifecycleWatcher._eventListeners.helper_js_1.helper.addEventListener (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/puppeteer/common/LifecycleWatcher.js:51:147)
    at slice.map (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/vendor/mitt/src/index.js:47:62)
    at Array.map (<anonymous>)
    at Object.emit (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/vendor/mitt/src/index.js:47:43)
    at CDPSession.emit (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/puppeteer/common/EventEmitter.js:72:22)
    at CDPSession._onClosed (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:247:14)
    at Connection._onMessage (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:94:25)
    at WebSocketTransport._ws.addEventListener (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/puppeteer/lib/cjs/puppeteer/common/WebSocketTransport.js:13:32)
    at WebSocket.onMessage (/home/runner/work/meilisearch-js/meilisearch-js/tests/env/express/node_modules/ws/lib/event-target.js:125:16)
    at WebSocket.emit (events.js:198:13)
(node:3291) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:3291) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
FAIL tests/client.test.js (5.832 s)
  MeiliSearch JS Browser test
    βœ• Should have generated a meilisearch client and displayed (155 ms)

  ● MeiliSearch JS Browser test β€Ί Should have generated a meilisearch client and displayed

    Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:29:45)

  ● MeiliSearch JS Browser test β€Ί Should have generated a meilisearch client and displayed

    Execution context was destroyed, most likely because of a navigation.



Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        5.887 s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Error: Process completed with exit code 1.

Improve JSdoc commentaries

For the moment, there is a basic jsdoc implementation above each function.

This should be extended by adding:

  • @params to each function commentary
  • @returnsto each function commentary

https://compodoc.app/guides/jsdoc-tags.html
Other propositions are welcome. I'm considering tsDoc but there is a lack in documentation.

Create tests on multiple version of node (12, 14, ..)

For the moment we only make our tests run on node version Ubuntu is using (which is 12).

We should test that our package work on the most popular node versions (i.e: at least 12 and 14)

How

Using dockers containers

your_job:
  runs-on: ubuntu-latest
  container: node:10.9

or

name: Use Node.js ${{ node-version }}
uses: actions/setup-node@v1
  with:
    node-version: ${{ node-version }}

But beware, it happened multiple times that download from node became really slow ([1][2]).

Multiple issues when using umd file

Disclaimer

πŸ™‹β€β™€οΈ I'm so sorry if I missed something really obvious in JS to remove these errors, but I'm stuck...

With v0.11.0

In my index.html

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js"></script>

I get in the console:

ReferenceError: require is not defined
    <anonyme> https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js:5

Capture d’écran 2020-06-19 aΜ€ 22 46 30

NB

No error message with:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js"></script>

With v0.10.1 (I use it because the v0.11.0 does not work)

I know this version is outdated but maybe there is the same issue in the v0.11.0 too.

In my index.html file:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js"></script>
<script src="./src/app.js"></script>

In .src/app.js

const meiliClient = new Meilisearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey'
})

I get this error in the console:

TypeError: instance is undefinedmeilisearch.umd.js:151:17
    MeiliAxiosWrapper https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js:151
    Meilisearch https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.umd.js:1098
    <anonyme> file:///Users/curquiza/.../src/app.js:69

Capture d’écran 2020-06-19 aΜ€ 23 09 33

Plus, you can notice I had to write new Meilisearch to get it worked (and not new MeiliSearch) which is really confusing comparing with the Getting Started.

Conclusion

These issues have to be fixed:

  • The UMD file should be importable in an HTML (see the first point)
  • The UMD file should be usable in a vanilla JS file passed as a script in an HTML file (see the second point)
  • Meilisearch has to be renamed into MeiliSearch in the UMD file
  • Add tests to check both of the first points if this is possible πŸ˜‡

Federated search

For a better customer experience, users should be able to implement a federated search -> search into multiple index from the same input.

Results should be displayed as sonn as they are received, we shouldn't wait for three indices to answer

Client not working when importing CJS in ESM

In some circumstances the MeiliSearch packages was not importable.
It threw an error when trying to use it.

When does the problem occurs

When trying to import MeiliSearch in an es6 environments that imports the main package present in the package.json and not the module package, it imports the cjs version and not the esm.

This throws an error.

Exemple

Using NestJS :

import meilisearch from 'meilisearch'

const client = new meilisearch({
  host: '123'
})
const client = new meilisearch_1.default({
               ^

TypeError: meilisearch_1.default is not a constructor
...

What causes the problem

As explained in this comment.

your issue is due to the mismatch between how commonjs export stuff module.exports = something and how ES6 export stuff export default something.

To fix this it you just have to enable esModuleInterop in your tsconfig compile option, also enable allowSyntheticDefaultImports for typesystem compatibility

esModuleInterop was set as true in the tsconfig.json of the MeiliSearch packages but it was overide by the default typescript configuration of the rollup-typescript plugin.

As explained in this issue: ezolenko/rollup-plugin-typescript2#226

The default tsconfig option "include" is overridden by the index from the original tsconfig.json

Which in our case was not the incude option but the esModuleInterop option.

How to fix it

Overide the rollup default configuration using the tsconfigOverride in the plugin's option.

typescript({
    useTsconfigDeclarationDir: true,
    tsconfigOverride: {
      esModuleInterop: true,
    },
  }),

How to prevent it

We should make basic end-to-end tests on different environments (ESM, Node, Typescript, UMD,..) if the plugin is importable and usable.

MeiliSearch Integration with Nuxt.js

Hi, I am trying to use MeiliSearch in my app which is written in Nuxt.js.
I have downloaded the meilisearch project from github and running it on 127.0.0.1:7700 as per the document. I have also imported the javascript sdk 'meilisearch' as per the document.

Can anyone guide me use it properly in my app.
I have written below code in my vue compoent.
import MeiliSearch from 'meilisearch'; // For importing the sdk

// I have written Below function for getting the search results
// NOTE: I created index and then commented it so that I can use the index
async meiliSearch() {
var client = new MeiliSearch({host: 'http://127.0.0.1:7700', apiKey: 'master-key'});
// const index = await client.createIndex({ uid: 'books' });
const index = client.getIndex('books');
const documents = [
{ book_id: 123, title: 'Pride and Prejudice' },
{ book_id: 456, title: 'Le Petit Prince' },
{ book_id: 1, title: 'Alice In Wonderland' },
{ book_id: 1344, title: 'The Hobbit' },
{ book_id: 4, title: 'Harry Potter and the Half-Blood Prince' },
{ book_id: 42, title: "The Hitchhiker's Guide to the Galaxy" },
]

     let response = await index.addDocuments(documents)
     console.log("My Response"+response) // => { "updateId": 0 }

      const search = await index.search('harry pottre')
      console.log("Here is Meili Search" + search)
},

When i am calling this function I get [Object, Object] in my console.log

remove 1000ms timeout

I would want to be able to get search results that take more than 1000ms to come back.

Type alias 'FacetFilter' circularly references itself.

I'm getting a type error when building a typescript codebase with meilisearch-js:

node_modules/meilisearch/dist/types/types.d.ts(37,21): error TS2456: Type alias 'FacetFilter' circularly references itself.

Currently using "skipLibCheck": true in tsconfig.json as a workaround.

Update on code-sample file, add attributes for faceting

Those 3 cURL examples samples should be added to the javascript
.code-samples.meilisearch.yaml

get_attributes_for_faceting_1: |-
  $ curl \
    -X GET 'http://localhost:7700/indexes/movies/settings/attributes-for-faceting'
update_attributes_for_faceting_1: |-
  $ curl \
    -X GET 'http://localhost:7700/indexes/movies/settings/attributes-for-faceting' \
    --data '[
        "genre",
        "director"
    ]'
reset_attributes_for_faceting_1: |-
  $ curl \
    -X DELETE 'http://localhost:7700/indexes/movies/settings/attributes-for-faceting'

Create tests with NPM PACK

Before any publish we should test the packages with npm pack during CI testing.

How to use npm pack

npm pack generated a tgz file that will the same that you would download from npm.

In the meilisearch-js SDK:

npm link

This will create : meilisearch-0.10.0.tgz (the number being the actual version of the package)

In another project:

 npm install ~/meilisearch-js/meilisearch-0.10.0.tgz

The path to the generated tgz file.

Why should we test it

This is to prevent that if the package work locally, it may not have the right configuration to work when it is published on NPM.

How should we test it

This is an example with circleCi : https://github.com/then/is-promise/pull/25/files (line 125)

test:
    steps:
      - run:
          name: Test
          command: npm test
      - run:
          name: Pack
          command: npm pack
      - run:
          name: Setup Import Test
          command: echo $PWD && cd .. && mkdir test-import && cp -a project/test-import/ test-import/test-import/ && cd test-import && npm init -y && npm i ../project/is-promise-2.2.2.tgz
      - run:
          name: Test Import
          command: cd ../test-import && node test-import/test.js

I have no further idea on how to do it properly

Create error handler

For the moment the throws are native or given from axios to the client. They should be more clear.

Tests shouldn't rely on error message but on error code

In several tests there is an assertion to check if an error is thrown. Currently, tests like this one check that the corresponding error message corresponds to what's expected.

Error messages are subject to change anytime by MeiliSearch core and there is no guarante of stability or reliability. Checking if the expected error is raise should use errorCodes instead of message (here the list), as the error codes are supposed to be stable and change only in very rare ocassions

Join on string parameter

Bug report

  • @meili/meili-api version: 0.8.0
  • Affected browsers (and versions): Chrome 78.0

Current behaviour

When sending a query parameter already formatted to fit in the url

 meili
      .Index('test_uid')
      .search({
        q: event.target.value,
        attributesToHighlight: 'overview,title'
      })
Uncaught TypeError: options.attributesToHighlight.join is not a function

Expected behaviour

Either it accepts both form, in array of string format, either it returns a clear error of the problem.

Type Document seem to be not combitable with other interfaces

This is the Document type.

type Document<T> = DocumentLike & {
    [key in keyof T]: T[key]
}

It is supposed to accept any objects { } with appropriate values. See the example below

interface IName {  name: string }
let testObj: IName = { name: 'Aora' }
idx.addAdocuments([ testObj ]) // error!

This isn't making any sense. Because if I remove the interface (IName) of testObj, this suddenly works just fine. That means it works as it is supposed to. So what's up with the type error?

Type 'IName' is not assignable to type 'Document<any>'.
  Type 'IName' is not assignable to type 'DocumentLike'.
    Index signature is missing in type 'IName'.ts(2322)

Another great example would the README.md file

const documents = [
    { book_id: 123, title: 'Pride and Prejudice' },
    ...
  ]

Here each object can be given an interface like

interface IBook {book_id: number,  title: string}
const documents = [
    { book_id: 123, title: 'Prejudice' } as IBook,
    ...
  ]

And as you expect, using this in the addDocuments function produces the same error. Pardon me if I'm wrong somewhere.

Replace sleep in test with setInterval

For the moment to test if the requests to MeiliSearch worked, sleeps are putted randomly to let the server finish all its tasks.

This is not a consistent way of testing. It should be a loop that checks every other second if the status of the task is processed.

CORS issue when used in browser

Potentially, the use of the head "X-MEILI-API-KEY" Could result in a CORS problem where the header is forbidden.

This should be tested and should have a fix. Maybe this package could recognize the environment in which the package is used (server or client) and act on the headers permission accordingly?

Use the powerful typing from typescript for SearchResponse

Hey! In the Go library there are no generics so I am a bit blocked to make a suitable return for the response to the hit.

TypeScript has a strong typing system and you should use it.

An exemple with the SearchResponse :

export interface SearchResponse<T> {
  hits: Hit<T>[]
  offset: number
  limit: number
  processingTimeMs: number
  query: string
}

export type Hit<T> = T & { _formatted?: T }

This to ensure that there may have a _formatted field in each object in the hits property.

Should replace applicationId with projectId

Bug report

  • @meili/meili-api version: 0.0.9 ()

Current behaviour

Project is now used as naming for a collection of indexes and not application

config = {
 projectId: 'xxx',
 apiKey: 'xxx'
}

Placeholder search

I believe v0.13 introduces placeholder (no query string) search. It's not clear how to use it with the Javascript client though. I tried using an empty string + facet filters and that didn't return any hits. If it is already implemented, an example in the docs would be cool.

Yarn build failed with exit code 1

If you encounter this error when yarn build.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

the yarn format:fix is not executed automatically when building. It has to be done manually before building. If there is a style error the build is not completed without more explanations than exit code 1. If you have this error it means you need to yarn format:fix.

Potentially it could also be the tsc version. Check if your are at the latest which is Version 3.7.4

Search route badly formatted

The search route should have this as a prototype :

index.search(query: string, [options: object])

Options being optional and query mandatory.

Re-write all the tests with TypeScript

Since our library works for TypeScript, could we re-write all the tests in TypeScript? The goal is to avoid this kind of mistake in #491 according to one of our contributors.

What do you think @bidoubiwa? Would this issue prevent other mistakes like this?
How huge would be this work? πŸ™‚

Use iife for browser and cjs for node

UMD bundle

In this issue I want to show you why the UMD is not working in its current state (466), why we will first go through a step where iife is used for browser and cjs for node, and later, a step where UMD works.

In the first step, we create a simple ES code that will be transpiled into UMD javascript. We follow the steps given here.

Base

First step, creation of ES script.
code

//index.js
export default 'Hello, world!';

Then, we create a rollup configuration to transpile the script in UMD.
rollup

    "build": "rollup index.js --file dist/bundle.js --format umd --name 'MyModuleName'"

UMD output:

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global = global || self, global.MyModuleName = factory());
}(this, (function () { 'use strict';

	var index = 'Hello, world!';

	return index;

})));

[explanation on how UMD works](https://riptutorial.com/javascript/example/16339/universal-module-definition--umd-

Explaination on global and factory:

global could be global in Node or in the browser it could be window. It is passed by providing this. factory is the function that is after this. That is where the application code ("business logic" or "meat") is.
UMD should work in any JavaScript environment, it just adapts the logic for what ever module loading system is present.

Tried in the following applications:

build: βœ…

node: βœ…

node dist/bundle.js

browser : βœ…

<!-- index.html -->
<script src="dist/bundle.js"></script>
<script>
  console.log(window.MyModuleName);
</script>

AMD: βœ…

<!-- amd.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
<script>
  window.requirejs(['dist/bundle'], function(MyModuleName) {
    console.log(MyModuleName);
  });
</script>

Adding Axios

Now, we try to add axios to our ES script.

code

import axios from 'axios'

axios.get('https://google.com').then(res => {
    console.log(Object.keys(res));
    
})
export default 'Hello, world!';

This requires a more complete rollup configuration.
rollup:

// rollup.config.js
module.exports = [
    // browser-friendly UMD build
    {
      input: './index.js',
      output: {
        name: "MyModuleName", // module name
        file: "./dist/bundle.js", // output file
        format: 'umd', // format
        globals: {
          axios: 'axios', // accessible globals
        },
      },
    }
]

UMD output:
)

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('axios')) :
    typeof define === 'function' && define.amd ? define(['axios'], factory) :
    (global = global || self, global.MyModuleName = factory(global.axios));
}(this, (function (axios) { 'use strict';

    axios = axios && Object.prototype.hasOwnProperty.call(axios, 'default') ? axios['default'] : axios;

    axios.get('https://google.com').then(res => {
        console.log(Object.keys(res));
        
    });
    var index = 'Hello, world!';

    return index;

})));

build: βœ…

node: βœ…

node dist/bundle.js

browser : ❌

bundle.js:9 Uncaught TypeError: Cannot read property 'get' of undefined
    at bundle.js:9
    at bundle.js:4
    at bundle.js:5

used in axios.get. Meaning get is undefined.

AMD: ❌

require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/axios.js net::ERR_FILE_NOT_FOUND
require.min.js:1 Uncaught Error: Script error for "axios", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)

Cannot fetch axios. Try to import file that does not exist.

Solutions 1

Step 1 : node resolve plugin

Add @rollup/plugin-node-resolve: A Rollup plugin which locates modules using the Node resolution algorithm, for using third party modules in node_modules.
Rollup config:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'

module.exports = [
    // browser-friendly UMD build
    {
      input: './index.js', // directory to transpilation of typescript
      output: {
        name: "MyModuleName",
        file: "./dist/bundle.js",
        format: 'umd',
        globals: {
          axios: 'axios',
        },
      },
      plugins: [
          resolve(),
      ]
    }
]

Build error ❌

[!] Error: 'default' is not exported by node_modules/axios/index.js, imported by index.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module

Explaination for the above link:

Import declarations must have corresponding export declarations in the imported module. For example, if you have import a from './a.js' in a module, and a.js doesn't have an export default declaration, or import {foo} from './b.js', and b.js doesn't export foo, Rollup cannot bundle the code.

This error frequently occurs with CommonJS modules converted by @rollup/plugin-commonjs, which makes a reasonable attempt to generate named exports from the CommonJS code but won't always succeed, because the freewheeling nature of CommonJS is at odds with the rigorous approach we benefit from in JavaScript modules. It can be solved by using the namedExports option, which allows you to manually fill in the information gaps.

Apparently namedExports is not necessary anymore.

The namedExports option has been removed, now requires rollup >= 2.3.4. Instead of manually defining named exports, rollup now handles this automatically for you.

Step 2 : commonjs plugin

Add @rollup/plugin-commonjs: 🍣 A Rollup plugin to convert CommonJS modules to ES6, so they can be included in a Rollup bundle.

import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';


module.exports = [
    // browser-friendly UMD build
    {
      input: './index.js', // directory to transpilation of typescript
      output: {
        name: "MyModuleName",
        file: "./dist/bundle.js",
        format: 'umd',
        globals: {
          axios: 'axios',
        },
      },
      plugins: [
          resolve(),
          commonjs()
      ]
    }
]

Build error ❌

./index.js β†’ ./dist/bundle.js...
[!] Error: Unexpected token (Note that you need @rollup/plugin-json to import JSON files)
node_modules/axios/package.json (2:8)

Issues mentionned:

To make rollup understand json files, just use rollup-plugin-json. I've just tried and it's working fine.

Step 3 : json plugin

Add @rollup/plugin-json: A Rollup plugin which Converts .json files to ES6 modules.

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json'


module.exports = [
    // browser-friendly UMD build
    {
      input: './index.js', // directory to transpilation of typescript
      output: {
        name: "MyModuleName",
        file: "./dist/bundle.js",
        format: 'umd',
        globals: {
          axios: 'axios',
        },
      },
      plugins: [
          json(),
          resolve(),
          commonjs()
      ]
    }
]

build: βœ…
Warning:

(!) Missing shims for Node.js built-ins
Creating a browser bundle that depends on 'http', 'https', 'url', 'assert', 'stream', 'tty', 'util' and 'zlib'. You might need to include https://github.com/ionic-team/rollup-plugin-node-polyfills
(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
http (imported by node_modules/axios/lib/adapters/http.js, node_modules/follow-redirects/index.js, http?commonjs-external)
https (imported by node_modules/axios/lib/adapters/http.js, node_modules/follow-redirects/index.js, https?commonjs-external)
url (imported by node_modules/axios/lib/adapters/http.js, node_modules/follow-redirects/index.js, url?commonjs-external)
zlib (imported by node_modules/axios/lib/adapters/http.js, zlib?commonjs-external)
stream (imported by node_modules/follow-redirects/index.js, stream?commonjs-external)
assert (imported by node_modules/follow-redirects/index.js, assert?commonjs-external)
supports-color (imported by node_modules/debug/src/node.js, supports-color?commonjs-external)
tty (imported by node_modules/debug/src/node.js, tty?commonjs-external)
util (imported by node_modules/debug/src/node.js, util?commonjs-external)
(!) Missing global variable names
Use output.globals to specify browser global variable names corresponding to external modules
http (guessing 'http')
https (guessing 'https')
url (guessing 'url')
assert (guessing 'assert')
stream (guessing 'stream')
tty (guessing 'tty')
util (guessing 'util')
supports-color (guessing 'supportsColor')
zlib (guessing 'zlib')

output: huge file

node: ❌

internal/modules/cjs/loader.js:1017
  throw err;
  ^

Error: Cannot find module 'supports-color'
Require stack:
- /Users/charlottevermandel/rollup-umd/dist/bundle.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1014:15)
    at Function.Module._load (internal/modules/cjs/loader.js:884:27)
    at Module.require (internal/modules/cjs/loader.js:1074:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at /Users/charlottevermandel/rollup-umd/dist/bundle.js:2:213
    at Object.<anonymous> (/Users/charlottevermandel/rollup-umd/dist/bundle.js:5:2)
    at Module._compile (internal/modules/cjs/loader.js:1185:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1205:10)
    at Module.load (internal/modules/cjs/loader.js:1034:32)
    at Function.Module._load (internal/modules/cjs/loader.js:923:14) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/Users/charlottevermandel/rollup-umd/dist/bundle.js' ]
}

browser: ❌

Uncaught ReferenceError: process is not defined
    at bundle.js:1616
    at createCommonjsModule (bundle.js:978)
    at bundle.js:1567
    at bundle.js:4
    at bundle.js:5

amd: ❌

require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/http.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/https.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/url.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/assert.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/stream.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/tty.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/util.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/supports-color.js net::ERR_FILE_NOT_FOUND
require.min.js:1 GET file:///Users/charlottevermandel/rollup-umd/zlib.js net::ERR_FILE_NOT_FOUND
require.min.js:1 Uncaught Error: Script error for "http", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "https", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "url", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "assert", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "stream", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "tty", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "util", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "supports-color", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)
require.min.js:1 Uncaught Error: Script error for "zlib", needed by: dist/bundle
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.min.js:1)
    at HTMLScriptElement.onScriptError (require.min.js:1)

Step 4: add resolve options

Following the first solution that was suggested here.
We add the options to the node-resolve plugin.

mainFields: ['jsnext', 'main']

Specifies the properties to scan within a package.json, used to determine the bundle entry point. The order of property names is significant, as the first-found property is used as the resolved entry point. If the array contains 'browser', key/values specified in the package.json browser property will be used.

browser

If true, instructs the plugin to use the "browser" property in package.json files to specify alternative files to load for bundling. This is useful when bundling for a browser environment. Alternatively, a value of 'browser' can be added to the mainFields option. If false, any "browser" properties in package files will be ignored. This option takes precedence over mainFields.

config:

import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json'


module.exports = [
    // browser-friendly UMD build
    {
      input: './index.js', // directory to transpilation of typescript
      output: {
        name: "MyModuleName",
        file: "./dist/bundle.js",
        format: 'umd',
        globals: {
          axios: 'axios',
        },
      },
      plugins: [
          json(),
          resolve({
            browser: true,
            mainFields: ['jsnext', 'main']
          }),
          commonjs()
      ]
    }
]

build: βœ…
output UMD: huge file

node: ❌

(node:40087) UnhandledPromiseRejectionWarning: ReferenceError: XMLHttpRequest is not defined
    at dispatchXhrRequest (/Users/charlottevermandel/rollup-umd/dist/bundle.js:799:21)
    at new Promise (<anonymous>)
    at xhrAdapter (/Users/charlottevermandel/rollup-umd/dist/bundle.js:791:12)
    at dispatchRequest (/Users/charlottevermandel/rollup-umd/dist/bundle.js:1098:12)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:40087) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:40087) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Browser: βœ…

AMD: βœ…

Following THIS stackoverflow post.

To make your package as small as possible I'd recommend using the Fetch API. A UMD library has the three types of consumers where fetch comes in handy;
Node.js - has not implemented but can use node-fetch to polyfill common behaviour using only node libraries (no heavy dependencies like superagent, unirest and axios etc - these add security concerns as well as bloat!).
Browser - Fetch is a WHATWG standard and is available in most modern browsers but might require an npm package such as whatwg-fetch to polyfill older browsers
Isomorphic/Universal - the same javascript running in browser and node.js which you find in progressive web apps.They need to use a library called isomorphic-fetch to load either whatwg-fetch or the node.js version of fetch.
It should be handled by the projects consumer though so README should include instructions to each of the three types of users above.
Node.js and isomorphic instructions are basically as below.

Possible solutions to this problem:

  1. Create two distinct rollup output, one for cjs that will be main in the package.json and one that will be iife that will be in the key browser of the package.json.
    If we opt for this solution users will have to :
<!-- index.html -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/meilisearch.iife.js"></script>
<script>
  console.log(window.MyModuleName);
</script>

instead of

<!-- index.html -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>
  console.log(window.MyModuleName);
</script>

This file will be a little heavy as it is bundled with all its dependencies.

  1. Remove axios and use fetch as it seems that it makes the umd possible.

Conclusion

Fast solution

Replace package.json main key with cjs bundle and change browser key with iife bundle.

"main": "./dist/bundles/meilisearch.cjs.js",
"module": "./dist/bundles/meilisearch.esm.js",
"browser": "./dist/bundles/meilisearch.iife.js",

Change umd rollup config with the following:

  {
    input: 'src/meilisearch.ts', // directory to transpilation of typescript
    output: {
      name: LIB_NAME,
      file: getOutputFileName(
        // will add .min. in filename if in production env
        resolve(ROOT, pkg.browser),
        env === 'production'
      ),
      format: 'iife',
      sourcemap: env === 'production', // create sourcemap for error reporting in production mode
      globals: {
        axios: 'axios',
      },
    },
    plugins: [
      ...PLUGINS,
      nodeResolve({
        mainFields: ['jsnext', 'main'],
        preferBuiltins: true,
        browser: true
      }),
      commonjs({
        include: 'node_modules/axios/**',
      }),
      json(),
      env === 'production' ? terser() : {}, // will minify the file in production mode
    ],
  }

Best solution:

Remove axios and replace with fetch-polyfill and rollup-plugin-node-polyfills
This will resolve the following issues:

  • create UMD that works
  • Remove the whole error stack loss

Other related issues and stackoverflow posts:

Problem 2: meilisearch naming

To change the name it is done here:

const LIB_NAME = pascalCase(normalizePackageName(pkg.name))

the pascalCase transforms meilisearch into Meilisearch. The pascalCase does not work as intented as the pkg.name is retrieved from package.json and is meilisearch. I suggest we change it like this:

const LIB_NAME = "MeiliSearch"

as this will impact this the umd or soon to be iife bundle.

See name key in output key.

module.exports = [
  // browser-friendly UMD build
  {
    input: 'src/meilisearch.ts', // directory to transpilation of typescript
    output: {
      name: LIB_NAME,
      format: 'iife',
      ...
    },
    plugins: [
    ...
    ],
  },

Wrong example for search_parameter_guide_crop_1

In the Attributes to Crop section of the documentation, a wrong example is given. This results in the following error when it's run:

Invalid JSON: invalid type: string "title", expected a sequence at line 1 column 39

The current example used is:

client.getIndex('movies').search('shifu', {
  attributesToCrop: 'overview',
  cropLength: 10
})

The correct example should be:

client.getIndex('movies').search('shifu', {
  attributesToCrop: ['overview'],
  cropLength: 10
})

Notice that attributesToCrop is an array. The correct example is also standardized with the other languages examples.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.