Offline usage examples
MichelML opened this issue · 5 comments
Add examples on how to use rdkit-js in a offline-first app
see context here #150 (comment)
I started experimenting with using the module in a browser-less / Node.js only setting. I've managed to load the module relatively easily and do some basic things with it.
I'm a little unsure as to what would count as an "offline-first" app. Just some bare .js / .ts files, without any browser interaction or HTML?
Edit:
Very basic example
// index.js
import initRDKitModule from '@rdkit/rdkit'
initRDKitModule()
.then((rdkit) => {
console.log(rdkit.version())
})
// console output
> 2022.03.5
Just being able to do server-side rendering would be very useful, both of depictions (particularly SVGs), and also (and likely easier) for molecule canonization.
Showcase here. I'm building a plugin for the note-taking app Obsidian, aiming to render SMILES strings as chemical structures. Repo link
Challenges
- Obsidian is built on Electron, so I can't access local assets directly via file paths
- The plugins are recommended to write in TypeScript
- I want to implement asynchronous loading to ensure that the structures are rendered correctly, rather than invoking an
undefined
window.RDKit when the notes are ready
Solutions
- Create an object URL from local
wasm
file (indirectly calling fs and conducted a binary read), then provide it to paramlocateFile
forinitRDKitModule()
- Follow the instructions in
TypeScript/README.md
and create a type definition - Asynchronously read
.wasm
file as binary and.js
script as text. The former is turned into an object URL and loaded. Create a script tag and set.js
text as itsinnerHTML
- I used
unpkg
distributions as a backup. Thewasm
URL works fine forlocateFile
, but I can't simply set the URL of.js
script as thesrc
attribute for a script tag. It's still necessary to conduct an async fetch and set theinnerHTML
as the response text.
- I used
@Acylation You may want to look at how I deal with this in rdkit-structure-renderer
. There are several examples of plain HTML pages that bootstrap themselves by loading the bundle through a script tag:
https://github.com/rdkit/rdkit-structure-renderer/blob/master/index.html
https://github.com/rdkit/rdkit-structure-renderer/blob/master/api_examples/svg_using_bundle.html
Structures are rendered as soon as the bundle has loaded and initialized.
@ptosco yes using onload
callback here is quite reasonable and elegant. As I want to get a return value from the async onload
(comparing to calling window.RDKit directly, this can ensure that I get a initialized reference), I need to do some "promisify". Following are the two versions for loading the package and they both work well.
Thanks a lot for providing these efficient examples!
Before
const loadRDKitUnpkg = async () => {
const rdkitBundler = document.createElement('script');
document.body.appendChild(rdkitBundler);
console.log('Fetching RDKit.js from unpkg...');
//requestUrl() is an API that fetching js scripts as text asynchoronously, based on `fetch()` API
rdkitBundler.innerHTML = await requestUrl( 'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js').text;
const RDKit = await window.initRDKitModule({
locateFile: () => 'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm',
});
console.log('RDKit.js has been successfully loaded.');
return RDKit;
};
After
const loadRDKitUnpkg = async () => {
const rdkitBundler = document.createElement('script');
document.body.appendChild(rdkitBundler);
console.log('Fetching RDKit.js from unpkg...');
rdkitBundler.src = 'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js';
return new Promise<RDKitModule>((resolve, reject) => {
rdkitBundler.onload = async () => {
const RDKit = await window.initRDKitModule({
locateFile: () => 'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm',
});
console.log('RDKit.js has been successfully loaded.');
resolve(RDKit);
rdkitBundler.onerror = (error) => {
reject(error);
};
};
});
};