I am learning ES6 modules at this current time. It took me so long to finally understand and be proficient in closures, IIFEs and scope using one script that I was almost upset to find ES6 modules bring about a different, more manageable way to organise modular code into various different scripts and then for a bundler like Webpack to bundle it all back into one (or only a few) scripts.
I get the normal cross origin error when I put script type = ‘module’ and try to run modules on my local file system which is different to when I run a normal script just simply specifying a src!
Wherever I look the solution is to use a local host to get round this which I have done! But at what point does Webpack work its bundling? Is it when I run it in the command line or when it’s loaded into the browser?
If installing Webpack via npm in my project and setting up the configuration, does this mean I wouldn’t have to use a local host because my distribution code during the runtime is now in one script file, so it doesn’t have to import scripts not on the same URL?
I know it’s only on my local file system, but I cannot request scripts in the same folder when using ES6 modules due to the cross origin policy as I could if just specifying a script src without using modules.
Each time I run Webpack from the command line, it bundles together the latest code taken from the entry point (specified during my configuration).
This means I can use the ES6 module syntax on my entry file without having to use a live server as I never intend to load this JavaScript entry file into the browser. It’s simply to be the controller of all other script exports by importing what I need from them.
Then this script will be the target of Webpacks bundling (the entry point) meaning the only script that is loaded into the browser is the bundled script, which has no other imports. This avoids the need to run a live server when testing ES6 modules!!
If not using Webpack, I would have to run a server because my main script would be importing other scripts and unless they have the same URL (they don’t even have one yet in my file system) then I will get a cross origin error. As soon as I run my local server 127.0.0.1 then it would work. I prefer testing with Webpack.
Related
Recently I just picked up Typescript for a personal project. Since the project is designed to be ran locally (explicitly file://), I won't be able to use import/export features due to CORS restrictions. Aware of another similarly written question but lacking the specific context on my use case, I pose these questions:
How does one tell Typescript that all (or at least certain) scripts are imported to HTML via <script type="text/javascript" src="./source.js">?
Does Typescript's tsc build projects with this in mind? Also, does it edit existing HTML files to take this into consideration too? If not, are there tools to automate this process as well?
I don't want to bundle them like webpack or tsc-bundle does, since a secondary objective to this project is to keep all .js files human-readable just as much as the .ts files do.
Building Typescript using tsc -p tsconfig.json, configured to "target" : "ES2015" and "module" : "None", only outputs their respective .js files and doesn't update any of the HTML's <script> includes. Am currently maintaining the html file by manually inserting and juggling any new modules that emerges over the course of development.
My current load order in index.html is as follows:
index.js handles UI controls and loads first.
The remaining pseudo-modules .js files loads in-between, since these only define classes and doesn't perform any operations, so I figured it's safe to load them here.
main.js handles all the code from javascript "modules" and loads last.
My main concern is that my in-between modules might load out of order due to human error.
Edit: Running a local webserver is out of the question too, since the project is meant to target audience with limited technical knowledge, with the index.html file the only file they need to run in their browser.
I have a node module that I am building, and I want it to be able to execute on the server (in nextjs server side rendering) and the client (call additional lifecycle methods in the UI). This same module also needs to work when used purely as a js library that can be included in a <script> tag on the page. This module depends on the uuid module, which has logic in it to check if it is running in a browser or server context, and use the proper random number generators/crypto libraries that are available in that context.
If I don't specify a target in my webpack config, the bundle works great in a client browser. It includes the webpack browser logic just fine. But it doesn't work in the server case - webpack removed the server capable logic in the uuid module.
If I target: 'node' in my webpack config - it executes just fine as a node module on the server and the client. It seemingly included all of the logic this time. But now it doesn't work if included just as a script tag on the page. I get ReferenceError: require is not defined from the file that depends on the uuid module.
It seems like the uuid module should handle these different environments just fine, but webpack is messing with that. How can I let that module resolve the proper implementation at runtime?
I unfortunately do not have a minimally reproducible example, or additional code to share at this time. I figured someone might have run into this with webpack (or even webpack and the uuid module) and know the solution.
I was trying to do this by building a single version of the package, but I don't think that is possible.
What is possible is building multiple versions, and then hosting the web bundle via unpkg or jsdelivr via an entry in package.json. Those entries can point to the target: 'web' version of the package, while the npm package can point to the target: 'node' version.
I am trying to understand how does this work in case of javascript. Webpack basically minifies the javascript when it says compiling I suppose. So in this case, how can some untrusted javascript code can lead to execution of malicious code through webpack on the server? A kind person in the javascript IRC channel told me this could be achieved using inline loader syntax. But I still don't understand how this is possible.
For reference the warning displayed in the getting started page is this:
Do not compile untrusted code with webpack. It could lead to execution
of malicious code on your computer, remote servers, or in the Web
browsers of the end users of your application.
Ref: https://webpack.js.org/guides/getting-started/
More information:
There are 2 ways it can execute code, when being compiled (via loaders), and when being run. SSR would be the latter, if you compile code on your server the former. https://webpack.js.org/concepts/loaders/#inline allows you to specify a loader without it being in the webpack config, and loaders can run whatever they want.
A script that is being webpacked (and any script it includes) can run code when it is packed, and this is by design.
That warning was added after I sent an email into the npm security folks (request 86380) about webpack running code during the packing phase with this POC.
The trick is to abuse the "Magic Comments" feature documented here.
Just checked on current webpack 5.23.0 and this still works.
Apparently inline loaders are another abuse pathway. See: https://github.com/webpack/webpack/issues/10231
Once npm and nodejs installed, it is possible to easily use a module by running
npm install module
and then using the js require() function. I assume the source code stored somewhere on my computer will then be loaded. But what if I want to use javascript functions of this module within a client's browser ? With a bit of work, I can probably end up finding some file on my disk, or maybe on the web, with the source code I need inside it. My question is : is there a standard process that gathers all the dependencies of a given module, so I can serve them on a client website ? Or do I totally miss something and things are suppose to be done in an entirely different way ?
If the library is published as standard javascript modules, you can load it directly into the browser, as in this example:
<script type="module">
import { html, render } from '/node_modules/lit-html/lit-html.js';
render(html`<span>Hello, world</span>, document.body)
</script>
The unpkg service provides a useful tool to automatically transform module specifiers to absolute URLs, you just have to add the ?module query string:
import { html, render } from 'https://unpkg.com/lit-html/lit-html.js?module';
If, however, as is (unfortunately commonly) the case, the library is published as CJS modules, you'll need to use a module bundler. I'm partial to rollup. To roll cjs modules up in your bundle, make sure to install and use the rollup-plugin-commonjs plugin.
There are a variety of tools available to merge your dependencies into a single file that can be consumed by a web browser. Such tools are typically referred to as "bundlers".
A few popular examples include:
Webpack
Browserify
Parcel
This list is not meant to be comprehensive or even a recommendation of which tool to use.
I read through many articles on Browserify like http://javascriptplayground.com/blog/2013/11/backbone-browserify/ and there is always a step such as below:
$ browserify app/app.js | uglifyjs > app/bundle.js
This seems to be done before you run the script in the browser to see how it works. Is there a way NOT having to do build each time I change code? Something similar to define() function in requirejs...
It's 2015 now and there's a library for this, it's called drq. It uses internally sync xhr requests, so it's only well suited for development purposes. You just have to include it:
<script src="drq.js"></script>
And then, you can do your require calls in any script of the page:
<script>
var myModule = require('my-module'),
myClass = require('./classes/my-class.js');
// etc.
</script>
It will look for node modules up to your web root, so be sure to npm install them at a directory not upper than it. Also, please take a look at the GitHub page where you can find some tips to increase performance.
Again, please remember that bundles are the optimal solution for production.
I originally said you can't do this for the reasons below, but I want to add that where there is a will there is a way. I'm sure given enough time and effort, you (or someone) could (and probably will) come up with a way to accomplish this task - but as of right now (12/12/13), I don't know if there's any out of the box tools that will facilitate it.
browserify "modules" are written using the same concept as node.js modules. You write your code, and export any public methods/properties/etc via a module.exports object. Javascript in the browser doesn't support this sort of thing natively. There are some boilerplate templates (some info here) to help facilitate this in the browser, and they can be compatible with browserify, but...
When you browserify your code, the browserify script analyzes your syntax and finds the modules that it has to make available via the require method. This require method gets defined right in your bundle.js that you export, along with all the code for all the dependencies that your module needs. This allows the require method that browserify defines to work synchronously, returning a reference to the module that you requested immediately without waiting for any kind of web response (like, loading a js script).
Require.js works fundamentally differently than browserify. Require.js defines your packages using the define syntax you referenced, and exposes a require method which you use to tell Require.js which modules your code depends on. Require.js then, in turn, looks up the dependencies you require and if it hasn't loaded them for another module yet, generates a new script tag and forces your browser to load that module, waiting to execute your code until that is complete. This is an asynchronous process, which means, the javascript engine continues to process instructions while it waits for the new script to download, parse, and execute. Require.js wraps all of this up in some callbacks, so it can wait until all your dependencies are satisfied, before allowing your defined code to execute (which is why you pass functions to require and define, so require.js can execute them when it's ready).
The biggest reason to not want to bundle every time you make a change in development, is just for speed of iteration. Some things you can do (with browserify) to improve performance (that is, speed of bundling) are:
Don't uglify your code during development. You can just bundle it using browserify (make sure you use -d, for sourcemaps) without uglifying/minifying it, that should speed up the bundle performance a bit (for larger projects, anyway).
Split up your modules a little bit. Modules that don't have direct dependencies with one another don't have to be built at the same time. You can include different modules in your application using multiple script tags, or you can concatenate browserify bundles files together. You could absolutely set up some grunt tasks to watch your code for changes, and only compile the modules which contained the code change. This will cut out a lot of wasted cpu cycles, since browserify won't have to parse and transform multiple modules, just the ones that changed. From there you can re-concatenate into one big bundle, or just stick with the multiple bundle includes on the page.