{"id":13725382,"url":"https://github.com/arialpew/bs-dynamic-import","last_synced_at":"2025-05-07T20:32:17.333Z","repository":{"id":91432035,"uuid":"124566019","full_name":"arialpew/bs-dynamic-import","owner":"arialpew","description":"📦🚀 BuckleScript dynamic import interop on JavaScript environment","archived":true,"fork":false,"pushed_at":"2020-05-24T03:11:16.000Z","size":165,"stargazers_count":31,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-14T10:50:57.984Z","etag":null,"topics":["bucklescript","code-splitting","dynamic","import","javascript","lazy-loading","module","ocaml","reasonml"],"latest_commit_sha":null,"homepage":"https://npmjs.com/bs-dynamic-import","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arialpew.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-09T16:32:09.000Z","updated_at":"2023-07-27T12:03:35.000Z","dependencies_parsed_at":"2023-04-28T01:16:23.661Z","dependency_job_id":null,"html_url":"https://github.com/arialpew/bs-dynamic-import","commit_stats":null,"previous_names":["kmeillet/bs-dynamic-import"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arialpew%2Fbs-dynamic-import","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arialpew%2Fbs-dynamic-import/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arialpew%2Fbs-dynamic-import/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arialpew%2Fbs-dynamic-import/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arialpew","download_url":"https://codeload.github.com/arialpew/bs-dynamic-import/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224645354,"owners_count":17346131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bucklescript","code-splitting","dynamic","import","javascript","lazy-loading","module","ocaml","reasonml"],"created_at":"2024-08-03T01:02:21.197Z","updated_at":"2024-11-14T15:31:16.118Z","avatar_url":"https://github.com/arialpew.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Summary\r\n\r\n[![Build Status](https://travis-ci.org/kMeillet/bs-dynamic-import.svg?branch=master)](https://travis-ci.org/kMeillet/bs-dynamic-import)\r\n![NPM license](https://img.shields.io/npm/l/bs-dynamic-import.svg?style=flat)\r\n\r\n**Warning :** This package is now in read-only mode, if you want to lazy load React components in a ReasonML application use \"[reason-loadable](https://github.com/kMeillet/reason-loadable)\".\r\n\r\nIf you want to import something else like a JSON document or JavaScript code that isn't a React component (example : Math library), take a look at \"[reason-loadable](https://github.com/kMeillet/reason-loadable)\" source code and tests, it can do that with some adaptation.\r\n\r\nThis package probably doesn't work anymore since BuckleScript changed object representation in JavaScript output in recent version (from array to object).\r\n\r\n-------------------------------------\r\n\r\n📦🚀 BuckleScript dynamic import interop on JavaScript environment.\r\n\r\nProvide a clear path for Reason/Ocaml module to become importable at runtime, preserve type-safety.\r\n\r\n**Note :** This project does not target native compilation but JavaScript compilation.\r\n\r\n* [Installation](#installation)\r\n* [Motivation](#motivation)\r\n  * [Common problems](#common-problems)\r\n  * [Support](#support)\r\n* [Example](#example)\r\n  * [Basic example](#basic-example)\r\n  * [Multiple module](#multiple-module)\r\n* [API](#api)\r\n  * [DynamicImport](#dynamicimport)\r\n  * [Infix](#infix)\r\n* [Common errors](#common-errors)\r\n\r\n# Installation\r\n\r\n```sh\r\nnpm install bs-dynamic-import --save\r\n```\r\n\r\nThen add it to \"bsconfig.json\" :\r\n\r\n```sh\r\n\"bs-dependencies\": [\r\n \"bs-dynamic-import\"\r\n]\r\n```\r\n\r\nYou can now use **\"DynamicImport\"** module.\r\n\r\n# Motivation\r\n\r\nI will try to explain my propose and the following pattern for dynamic import API in Reason/Ocaml.\r\n\r\nThe existing syntactic forms for JavaScript importing modules are static declarations. They accept a string literal as the module specifier, and introduce bindings into the local scope via a pre-runtime \"linking\" process. \r\n\r\nThis is a great design for the 90% case, and supports important use cases such as static analysis, bundling tools, and tree shaking.\r\n\r\nHowever, it's also desirable to be able to dynamically load parts of a JavaScript application at runtime. This could be because of factors only known at runtime (such as the user's language), for performance reasons (not loading code until it is likely to be used), or for robustness reasons (surviving failure to load a non-critical module). Such dynamic code-loading has a long history, especially on the web, but also in Node.js (to delay startup costs). The existing import syntax does not support such use cases ...\r\n\r\nTruly dynamic code loading also enables advanced scenarios, such as racing multiple modules against each other and choosing the first to successfully load.\r\n\r\nhttps://tc39.github.io/proposal-dynamic-import/\r\n\r\nIn Reason/Ocaml, every file is a module : file name map to module name and you can make module into module. With BuckleScript, we can compile Reason/Ocaml module to JavaScript module.\r\n\r\nBuckleScript doesn't provide dynamic import.\r\n \r\n🔥 **\"bs-dynamic-import\"** let you use dynamic import right now with BuckleScript.\r\n\r\n`When BuckleScript will release dynamic import support, you should drop \"bs-dynamic-import\" and switch to BuckleScript syntax ; no worries, this project offers a basic API and can be replaced very quickly if needed.`\r\n\r\n## Common problems\r\n\r\nSome of the most common problematic patterns that were covered include :\r\n\r\n* **Commonjs/ESM support**. ✔️\r\n* **Dynamic import Reason/Ocaml module**. ✔️ \r\n* **Dynamic import multiple module in parallel**. ✔️\r\n* **Race module against each other**. ❌\r\n\r\n## Support\r\n\r\n- Server-side (Node.js) : Node.js doesn't support dynamic import, you should use [Babel](https://babeljs.io/) with [\"babel-plugin-dynamic-import-node\"](https://github.com/airbnb/babel-plugin-dynamic-import-node). [#example](https://github.com/kMeillet/bs-dynamic-import)\r\n\r\n- Client-side (web) : you should use a bundler ([Webpack 4](https://webpack.js.org/) and [Parcel](https://github.com/parcel-bundler/parcel) support dynamic import with zero-configuration, Rollup require experimental flag). [#example](https://github.com/kMeillet/reason-loadable/tree/master/examples)\r\n\r\n# Example\r\n\r\n## Basic example\r\n\r\nConsider a Math module and Main module :\r\n\r\n```reason\r\n/* Math.re */\r\nlet addOne = x =\u003e x + 1;\r\n/* ... */\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\n3 |\u003e Math.addOne |\u003e Js.log; /* 4 */\r\n```\r\n\r\n**Note :** Pipe operator **\"|\u003e\"** help chaining function and avoid parenthesis ceremony.\r\n\r\nModule are static (know at compile time). If you want to import Math dynamically (on runtime), use **\"DynamicImport\"** module.\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  |\u003e Js.Promise.then_(((module AnonymouModule): (module MathType)) =\u003e\r\n      3 |\u003e AnonymouModule.addOne |\u003e Js.log |\u003e Js.Promise.resolve /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n**Note :** You must always import BuckleScript output (bs.js), don't try to import Reason/Ocaml file (that will not work).\r\n\r\n1) First, we declare a module type who refer to Math module type himself.\r\n\r\n2) We open **\"DynamicImport\"** module locally to avoid name collision. **\"DynamicImport\"** module come with multiple functions and infix operator but the most important part of the API are **\"import\"** and **\"resolve\"** function.\r\n\r\n3) **\"DynamicImport.import\"** share signature with dynamic import JavaScript API : take a module path and return a Promise of module. This Promise should be passed to **\"DynamicImport.resolve\"** when you want to resolve module.\r\n\r\n**Note :** when using **\"DynamicImport.import\"** you should provide \".bs.js\" extension (or configure your bundler to recognize \".bs.js\" extension as \".js\" extension).\r\n\r\n**Note :** if you import wrong module or a path who doesn't exist, compiler will not complain so be careful about this situation when you move/rename file, like with JavaScript module.\r\n\r\n4) After module is dynamically imported, you can use **\"Js.Promise.then_\"** and \"repack\" the anonymous module with correct module type. If you use wrong module type or forgot to provide it, you will face compiler error.\r\n\r\n**Note :** Using **\"Js.Promise.then_\"** is verbose (you have to wrap result with **\"Js.Promise.resolve\"** every time), we provide **\"\u003c$\u003e\"** (map) operator to traverse Promise and apply your function (return value will be wrapped into Promise).\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module AnonymouModule): (module MathType)) =\u003e\r\n      3 |\u003e AnonymouModule.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n🔥 Look much better !\r\n\r\nFinally, you can catch error with **\"Js.Promise.catch\"** - and of course we provide **\"\u003c$!\u003e\"** (map catch) operator (return value will be wrapped into Promise).\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module AnonymouModule): (module MathType)) =\u003e\r\n      3 |\u003e AnonymouModule.addOne |\u003e Js.log /* 4 */\r\n  )\r\n  \u003c$!\u003e (error =\u003e Js.log(error))\r\n);\r\n```\r\n\r\nNow, how can we dynamically import JavaScript library ? Write 1:1 binding like normal way and expose what you want.\r\n\r\n```sh\r\nnpm install ramda --save\r\n```\r\n\r\n```reason\r\n/* Ramda.re */\r\n[@bs.module \"ramda\"] external inc : int =\u003e int = \"inc\";\r\n\r\nlet inc = inc;\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type RamdaType = (module type of Ramda);\r\n\r\nDynamicImport.(\r\n  import(\"./Ramda.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Ramda): (module RamdaType)) =\u003e\r\n      3 |\u003e Ramda.inc |\u003e Js.log /* 4 */\r\n  )\r\n  \u003c$!\u003e (error =\u003e Js.log(error))\r\n);\r\n```\r\n\r\nWhat about default export compatibility ?\r\n\r\n```reason\r\n/* Math.re */\r\nlet addOne = x =\u003e x + 1;\r\n/* ... */\r\nlet default = () =\u003e \"Default export !\";\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      Ramda.default() |\u003e Js.log /* \"Default export !\" */\r\n  )\r\n  \u003c$!\u003e (error =\u003e Js.log(error))\r\n);\r\n```\r\n\r\n## Multiple module\r\n\r\nIf you want to import multiple module in parallel, there is multiple resolve function who work with tuple :\r\n\r\n- DynamicImport.resolve2\r\n- DynamicImport.resolve3\r\n- DynamicImport.resolve4\r\n- DynamicImport.resolve5\r\n- DynamicImport.resolve6\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\nmodule type CurrencyType = (module type of Currency);\r\n\r\nDynamicImport.(\r\n  resolve2((\r\n    import(\"./Math.bs.js\"),\r\n    import(\"./Currency.bs.js\")\r\n  ))\r\n  \u003c$\u003e (\r\n    (\r\n      (\r\n        (module Math): (module MathType),\r\n        (module Currency): (module CurrencyType)\r\n      )\r\n    ) =\u003e\r\n      3\r\n      |\u003e Math.addOne\r\n      |\u003e Currency.toDollar\r\n      |\u003e Js.log\r\n  )\r\n  \u003c$!\u003e (error =\u003e Js.log(error))\r\n);\r\n```\r\n\r\n# API\r\n\r\n## DynamicImport\r\n\r\n[![API](http://image.noelshack.com/fichiers/2018/12/2/1521519530-code.png)](http://image.noelshack.com/fichiers/2018/12/2/1521519530-code.png)\r\n\r\n#### type importable('a)\r\n\r\nDynamic module type.\r\n\r\n#### import: string =\u003e Js.Promise.t(importable('a))\r\n\r\nImport dynamic module.\r\n\r\n#### resolve: Js.Promise.t(importable('a)) =\u003e Js.Promise.t('a)\r\n\r\nResolve dynamic module.\r\n\r\nThere is resolve2, resolve3, resolve4, resolve5, resolve6 that do the same thing with tuple for parallel import.\r\n\r\n## Infix\r\n\r\nWe expose 6 infix operator for better experience :\r\n\r\n- \u003c$\u003e (Promise map).\r\n- \u003c$!\u003e (Promise map catch).\r\n- \\\u003e\u003e= (Promise then).\r\n- =\u003c\u003c (Reverse Promise then).\r\n- \\\u003e\u003e=! (Promise catch).\r\n- !=\u003c\u003c (Reverse Promise catch).\r\n\r\nUnderlying, these operator work with any **\"Js.Promise.t\"**.\r\n\r\n# Common errors\r\n\r\n#### \"The signature for this packaged module couldn't be inferred.\"\r\n\r\nThis error mean you forgot to provide module type on resolved module.\r\n\r\n❌ Wrong :\r\n\r\n```reason\r\n/* Main.re */\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math)) =\u003e /* Signature missing ! */\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n✔️ Good :\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math); /* Signature */\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e /* Provide signature */\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n#### \"The value \u003e\u003e= can't be found.\"\r\n\r\nYou should use local or global open to have **\"DynamicImport\"** module in scope.\r\n\r\n❌ Wrong :\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\n/* Where is DynamicImport ? */\r\nimport(\"./Math.bs.js\")\r\n|\u003e resolve\r\n\u003c$\u003e (\r\n  ((module Math): (module MathType)) =\u003e\r\n    3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n);\r\n```\r\n\r\n✔️ Good :\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\n/* Local open */\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\")\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n    3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\n/* Global open */\r\nopen DynamicImport;\r\n\r\nmodule type MathType = (module type of Math);\r\n\r\nimport(\"./Math.bs.js\")\r\n|\u003e resolve\r\n\u003c$\u003e (\r\n  ((module Math): (module MathType)) =\u003e\r\n    3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n);\r\n```\r\n\r\n#### \"JavaScript runtime type error (cannot find length, 0 is not a function)\" (nightmare !)\r\n\r\nCompiler can not verify that you have imported the right module or check if module path is correct.\r\n\r\nThat's your responsability and you should be cautious about this because it's very error prone.\r\n\r\nAlways import file that will be compiled by BuckleScript (\".bs.js\" file), never import Reason/Ocaml file.\r\n\r\nYou can catch any error with **\"\u003c$!\u003e\"** operator (map catch) and apply custom logic if something fail on runtime.\r\n\r\n❌ Wrong :\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Mat.bs.js\") /* Bad path, file is missing */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.re\") /* Can't, Reason file */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.js\") /* Can't, non-BuckleScript output */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.ml\") /* Can't, Ocaml file */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n);\r\n\r\n✔️ Good :\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type MathType = (module type of Math);\r\n\r\nDynamicImport.(\r\n  import(\"./Math.bs.js\") /* Can, BuckleScript output from Reason/Ocaml module */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module Math): (module MathType)) =\u003e\r\n      3 |\u003e Math.addOne |\u003e Js.log /* 4 */\r\n  )\r\n  \u003c$!\u003e ((_error) =\u003e Js.log(\"Something goes wrong, reloading ...\"))\r\n);\r\n```\r\n\r\n```reason\r\n/* Main.re */\r\nmodule type BsMathType = (module type of BsMath);\r\n\r\nDynamicImport.(\r\n  import(\"bs-math\") /* Can, BuckleScript output from Reason/Ocaml module */\r\n  |\u003e resolve\r\n  \u003c$\u003e (\r\n    ((module BsMath): (module BsMathType)) =\u003e\r\n      3 |\u003e BsMath.sqrt |\u003e Js.log /* 1.73 */\r\n  )\r\n  \u003c$!\u003e ((_error) =\u003e Js.log(\"Something goes wrong, reloading ...\"))\r\n);\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farialpew%2Fbs-dynamic-import","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farialpew%2Fbs-dynamic-import","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farialpew%2Fbs-dynamic-import/lists"}