I am testing a React environment without a build process by loading React and Babel scripts directly from CDN.
When I try to add ES6 modules I run into a problem: babel converts import back down to require, which is not working in the browser.
Since there is no babel config file how can I tell babel not to do this? In other words, how can I load React with script tags from CDN and still use ES6 modules?
HTML
<head>
<script src="https://unpkg.com/react#18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<!-- babel for JSX and datatype to use modules -->
<script defer type="text/babel" data-type="module" src="main.js"></script>
</head>
<body>
<div id="main"></div>
</body>
MAIN.JS (works except for the import)
import { Drawing } from "./drawing"
const App = () => {
return <div>
<h1>Just a test</h1>
<Drawing />
</div>
}
const root = ReactDOM.createRoot(document.querySelector('#main'))
root.render(<App />)
PS I realise this setup is not optimised for production, but it is ideal for quickly sketching out ideas (no bundle process, no node modules folder with 30.000 packages).
Babel standalone added support for browser-native modules in v7.10.0 (including the data-type="module" attribute), while you're using v6. Removing the version number in the CDN or defining a specific one >= 7.10.0 fixes the transform-to-require issue.
However, you have two other issues.
The first one is trivial: ES6 native modules don't automatically resolve the file extension, so you need to do import { Drawing } from "./drawing.js" instead of import { Drawing } from "./drawing".
Secondly though, Babel won't transform the files that you import, so that JSX would be considered invalid. This leaves you with two options.
Create your own import function, and manually import like this (working example)
async function imp(path, requestOrigin) {
const { href } = new URL(path,
requestOrigin
? requestOrigin.slice(0, requestOrigin.lastIndexOf("/"))
: location.href
); // so the paths won't be messed up
const res = await fetch(href);
const text = await res.text();
const { code } = Babel.transform(text, { presets: ["react"] });
return await import(`data:application/javascript,${encodeURIComponent(code)}`);
}
Usage:
const { Drawing } = await imp("./drawing.js");
// if you're in a "nested" route, e.g. "/components/Button.js", you need to provide the file
const { OtherComponent } = await imp("./hello.js", import.meta.url);
okay, okay, WHY though wouldn't you use a bundler?? This is how the world works and always will (for the forseeable furture, i.e.)... I mean, just install parcel globally and you're like, done in two seconds with just a single command
Related
I am trying to use tone.js on ESM modules. (I could use it without problems in “commonjs” with a bundler)
In the html I have
<script src="tests.js" type="module"></script>
and tests.js:
import * as Tone from "./Tone.js"
gives -> Tone.Gain is not a constructor
If I try:
import * as Tone from "./node_modules/tone/build/esm/index.js";
then Chrome shows status 404 Global not found, and the same for classes, version, ToneAudioBuffer, AudioContext, ToneAudioBuffers andToneBufferSource
(Maybe I am wrong, just starting with ESM modules, but digging into that esm/index.js the imports are like
import { ToneAudioBuffer } from "./core/context/ToneAudioBuffer"; (without .js extension, shouldn’t have any ESM module explicitly add the extension?)
I’ve lost track of other combinations I have tried without success and I can not find a working example of a such project.
What would be the right way - if possible- to run Tone.js on js modules?
Without bundling, serving a HTML with a module script, try import "./node_modules/tone/build/esm/index.js";.
Or use a build of some kind, then use the recommended import import * as Tone from "tone";.
Optionally, use the CDN, with the plain global <script src="https://unpkg.com/tone#14.7.77/build/Tone.js"></script> or the module syntax above.
<script type="module">
import "https://unpkg.com/tone#14.7.77/build/Tone.js";
const btn = document.querySelector("button");
btn.addEventListener("click", async () => {
await Tone.start();
const synth = new Tone.Synth().toDestination();
const now = Tone.now();
synth.triggerAttack("C4", now);
synth.triggerRelease(now + 50);
});
</script>
<button>Play</button>
Whenever I import python modules in a pyodide, it gives this error.
pyodide.js:108 Invalid package name or URI
I am not sure how to properly import modules,
I have tried this which was mentioned in the docs.
pyodide.loadPackage('<module address>')
(this returns a promise on whoes resolution I run this method)
pyodide.runPython('
<python code here>
')
Upon execution, I get the error mentioned above.
Javascript Code:
<html>
<head>
<script type="text/javascript">
// set the pyodide files URL (packages.json, pyodide.asm.data etc)
window.languagePluginUrl = 'https://pyodide-cdn2.iodide.io/v0.15.0/full/';
</script>
<script src="https://pyodide-cdn2.iodide.io/v0.15.0/full/pyodide.js"></script>
</head>
<body>
Pyodide test page <br>
Open your browser console to see pyodide output
<script type="text/javascript">
languagePluginLoader.then(function () {
pyodide.loadPackage('<address>').then(() => {
console.log(pyodide.runPython('
import sys
from <my package> import *
sys.version
'));
console.log(pyodide.runPython('print(1 + 2)'));
});
});
</script>
</body>
</html>
There is a chance that this question might be unclear, but please let me know if you have trouble understanding something.
Also, the string passed in the runPython() method is the python code, just to avoid confusion.
I even tried uploading the module to a server as the docs mentioned a URL using the HTTP protocol, was pretty stupid trying this but I did.
Docs: https://pyodide.readthedocs.io/en/latest/using_pyodide_from_javascript.html#loading-packages
Update: Pyodide v0.21.0
Starting with Pyodide 0.18.0, runPythonAsync does not automatically load packages, so loadPackagesFromImports should be called beforehand.
So, to import a third-party package like numpy we have two options: we can either pre-load required packages manually and then import them in Python
// JS
await pyodide.loadPackage('numpy');
// numpy is now available
pyodide.runPython('import numpy as np')
console.log(pyodide.runPython('np.ones((3, 3)))').toJs())
or we can use the loadPackagesFromImports function that will automatically download all packages that the code snippet imports:
// JS
let python_code = `
import numpy as np
np.ones((3,3))
`
(async () => { // enable await
await pyodide.loadPackagesFromImports(python_code)
let result = await pyodide.runPythonAsync(python_code)
console.log(result.toJs())
})() // call the function immediately
More examples can be found here
(async () => { // enable await
let python_code = `
import numpy as np
np.ones((3,3))
`
let pyodide = await loadPyodide();
await pyodide.loadPackagesFromImports(python_code)
console.log(pyodide.runPython(python_code).toJs())
})() // call the function immediately
<script src="https://pyodide-cdn2.iodide.io/v0.21.0/full/pyodide.js"></script>
Note also, that starting with version 0.17, Pyodide uses JsProxy for non-JS datatypes. So, before printing the results, it has to be converted using toJs.
Old answer (related to Pyodide v0.15.0)
It is not clear what you are passing as <address> in pyodide.loadPackage('<address>'), but it should just be the package name (e.g. numpy).
Also, note that Pyodide currently supports a limited number of packages. Check out this tutorial on it for more details.
If you want to import a third-party package like numpy there are two options: you can either pre-load required packages manually and then import them in Python using pyodide.loadPackage and pyodide.runPython functions:
pyodide.loadPackage('numpy').then(() => {
// numpy is now available
pyodide.runPython('import numpy as np')
console.log(pyodide.runPython('np.ones((3, 3)))'))
})
Or you can use pyodide.runPythonAsync function that will automatically download all packages that the code snippet imports.
Here is the minimal example for pyodide.runPythonAsync
let python_code = `
import numpy as np
np.ones((3,3))
`
// init environment, then run python code
languagePluginLoader.then(() => {
pyodide.runPythonAsync(python_code).then(output => alert(output))
})
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.js"></script>
</head>
<body>
</body>
</html>
I need to import a library in my vue component, in the documentation I explain how to install it using npm (already do this step) but not how to import it into a vue component, this is the way in which it explains how to use the files:
<link href="node_modules/webdatarocks/webdatarocks.min.css" rel="stylesheet"/>
<script src="node_modules/webdatarocks/webdatarocks.toolbar.min.js"></script>
<script src="node_modules/webdatarocks/webdatarocks.js"></script>
and this is the way to instantiate the library:
<script>
var pivot = new WebDataRocks({
container: "#wdr-component",
toolbar: true,
report: {
dataSource: {
filename: "https://cdn.webdatarocks.com/data/data.csv"
}
}
});
</script>
So what is the best way to call this in my component?
This is a bit heavy.
The library is is not develop in module-like system, so the solution is make the js file imported as global.
A good library would be like const WebDataRocks = require("WebDataRocks"); or with imports, but the library is made only for end-client.
First Part - Add the JS file to the global web client
To use WebDataRocks you have to get the global variable, to get the global variable you have to inyect, as common javascript on html but with webpack.
Here are a solution for this
Webpack - How to load non module scripts into global scope | window
You have to do this for webdatarocks.toolbar.min.js and webdatarocks.js
Second Part - Add the CSS
You have some options, the easy way i found to do this is use require in your <script> zone:
require('../node_modules/webdatarocks/webdatarocks.js')
Good luck! 😁
If something fails check the paths and let us know more information about it
Alternative solution (But worse)
If you are going to use this script in a internet system, you could insert the script and CSS in the HTML. For this do:
Open index.html
Add this code on the head section:
<link href="https://cdn.webdatarocks.com/latest/webdatarocks.min.css" rel="stylesheet"/>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.toolbar.min.js"></script>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.js"></script>
Rebuild
Extracted from WebDataRocks React Example
Important! this is unsafe ☣ ⚠
Make this only if you are sure about what this mean
If the webdatarocks CDN's fails, your app will also fails.
Hope it helps :)
I did this and it works:
import WebDataRocks from 'webdatarocks'
import '#/../node_modules/webdatarocks/webdatarocks.min.css' // # is resolved to src/ folder
I didn't import the toolbar as I don't need it:
WebDataRocks({
container: '#pivot',
toolbar: false,
...
})
Is it not possible to access already 'built' components within the html file that the build is linked to?
I am trying the following -
In bundle.js
var React = require('react');
var ReactDOM = require('react-dom');
var Titles = React.createClass({
render() {
return (
<div>
<h1>{this.props.headerProp}</h1>
<h2>{this.props.contentProp}</h2>
</div>
);
}
});
In my html page -
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
<div id="con"></div>
<script type="text/javascript" src="/public/bundle.js'"></script>
<script type="text/jsx">
ReactDOM.render(<Titles headerProp = "Header from props..." contentProp = "Content
from props..."/>, document.getElementById('con'));
</script>
But console outputs React is not defined.
I have even tried to set react globally within the bundle -
window.React = React;
And calling it with window. prefixed but yields the same result.
Because you're mentiong a bundle.js file with a snippet containing commonjs style imports, I'm assuming you're using Webpack.
I have some considerations about your code.
bundle.js file will not expose any module as global. That includes React and any other module you might require inside the bundle. There isn't goint to be window.ModuleName. However, these module are accessible in the Browser via require.js because Webpack will export modules as UMD, that is, they will be accessible through either commonjs or AMD (Require.js).
I'm pretty sure that, if in the entry point of your webpack configuration file, you do something like var React = require("react"); window.React = React, that's actually going to work.
There's a Webpack module meant to expose modules globally (like in window.x) in a more ellegant way than (2) called expose-loader. You should take a look at it.
You should really try to avoid doing what you're trying to do. In your entry.js file (the entry point of your webpack configuration) should be responsible for doing something like ReactDOM.render(..., document.getElementById("#app")). So that, just by including your bundle, the app will render automatically. This is what everybody does.
JSXTransformer.js as well as the <script type="text/jsx"> have been deprecated a long time ago. Now you're supposed to use Babel to compile React.
I am currently using jspm and SystemJS to load ES6 modules. However, I would like to be able to
Scan the page for certain selectors (e.g., id, data-plugin)
Map these selectors to their module dependencies
Load only those modules
My thought was to handle these imports through the single entry point, System.import('src/main'), that has access to the document. I could then find the relevant selectors, map those selectors to modules, and then import those modules.
src/main would look something like this:
['d3', 'jquery'].forEach(function(dependency) {
import dependency;
});
This is not a viable solution as it is invalid syntax. Is there a better way to achieve dynamic module loading in this sense?
Normal import syntax can't be used to conditionally load modules, as you've already seen. To get around this, we can use the programmatic API provided by System. You're already familiar with this API, since you use it to System.import('src/main');.
To conditionally load modules, instead of using the import keyword, you just have to continue using the System.import method.
An example of this:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
</head>
<body>
<div id='one'>One</div>
<div>Two</div>
<script>
System.import('main');
</script>
</body>
</html>
main.js
const map = {
'#one': 'one',
'#two': 'two'
};
for (let selector in map) {
if (document.querySelector(selector)) {
System.import(map[selector]);
}
}
one.js
window.HAS_ONE = true;
two.js
window.HAS_TWO = true;
In this example, window.HAS_ONE would be defined, but window.HAS_TWO would remain undefined.