Create a distributive and an NPM package using one source code

Creating small open source tools is a passion of mine, and I strive to provide the best experience for my users. My packages usually consist of just one function, so here's what I aim to offer:

  1. A JavaScript file that users can easily add by including the src attribute in the script tag. This script will then allow them to call my function in their own scripts below. Ideal for those who prefer not to use a package manager:
<script src="https://amazingCDN.com/isEven.js"></script>
<script>
  isEven()
</script>
  1. A JavaScript file that can be packaged and published for NPM users to conveniently install with npm install isEven, followed by importing my package.

Both JS files should derive from the same source code. Let's assume the source code contains a named function that needs to be added to the window object and made importable when using Webpack. When packaging the files, I want only two JS output files generated. As for CDN support, let's say I utilize jsDelivr to fetch my GitHub-hosted JS file and handle minification without manual intervention.

I attempted writing my code as a module and utilizing Browserify with the standalone flag, which worked well with CommonJS modules. However, for ES modules compatibility, I had to resort to esmify, resulting in an object return with a default key. This forced me to refer to the function as foo.default() instead of foo(), which is less than ideal.

Another approach involved writing it as a standalone file and executing the following:

echo 'export default ' | paste -d'\0' - src.js > module.js

While somewhat successful, I am curious about more advanced and dependable solutions available.

Any suggestions on how to accomplish this effectively?

Answer №1

Are you considering packaging your project as a UMD?

Explore UMD on GitHub

If so, Rollup might be the right tool for outputting to UMD format, especially for smaller projects like single-function libraries.

isEven.mjs

function isEven(x) {
  return (x % 2) === 0 && x !== 0;
}

export {
  isEven
};

$ rollup isEven.mjs --format=umd --name=isEven

This command will generate:

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

  function isEven(x) {
    return (x % 2) === 0 && x !== 0;
  }

  exports.isEven = isEven;

  Object.defineProperty(exports, '__esModule', { value: true });

})));

This should meet your requirements perfectly.

Answer №2

After successfully completing the task, I went ahead and crafted a template repository specifically tailored for it.

Utilizing this template, you have the option to generate your own repository that converts your library into an NPM package using ES modules. Once you push the code changes, a dist folder will automatically be generated containing the distributive js file named after your library. Users can easily incorporate this file into their projects by adding it within a script tag, making your library accessible at the global namespace.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Achieving success with the "silent-scroll" technique

I've been struggling to implement the 'scroll-sneak' JavaScript code for quite some time now. This code is designed to prevent the page from jumping to the top when an anchor link is clicked, while still allowing the link to function as inte ...

How to upload a multipart form with JSON and file using Dojo's XHR functionality

I've been struggling to find a solution for this issue. The closest I found was a thread on Stack Overflow about composing multipart/form-data with different Content-Types, but it didn't fully address my problem. Here's the situation - I ha ...

Ongoing state configuration in a React hook

My custom hook: export function useToken2() { const { data: session, status } = useSession(); const [token, setToken] = useState<string | null>(null); useEffect(() => { if (status === 'authenticated' && session?.accessToken) { ...

Using the reduce method in JavaScript or TypeScript to manipulate arrays

Just exploring my culture. I have grasped the concept of the reduce principle var sumAll = function(...nums: number[]):void{ var sum = nums.reduce((a, b) => a + b , 0); document.write("sum: " + sum + "<br/>"); } sumAll(1,2,3,4,5); The r ...

Weird errors popping up when running npm build in ReactJS - lifecycle and export issues arise

Currently facing an issue while trying to build a React project using npm run build for creating a production build Running npm start works perfectly fine and compiles the react code without any issues npm run build - error https://i.sstatic.net/lMfbK.pn ...

convert a JSON object to an array using jQuery

Can anyone help me with converting an object into an array using jQuery? [["20"],["30"],["45"],["54"],["33"],["15"],["54"],["41"]] I am looking to achieve an array output like this: [20,30,45,54,33,15,54,41] Any suggestions on how to accomplish this? ...

Unable to retrieve a return value from an asynchronous waterfall function within a node module

A custom node module that utilizes async waterfall is working properly when run independently, but the AJAX callback does not receive the return value. //Node module var boilerplateFn = function(params){ async.waterfall([ function(callback){ ...

Node Express application experiences issues with Handlebars rendering additional empty objects from JSON arrays

Currently, I am attempting to retrieve data from a REST API call and display it as a list using Handlebars, which functions as my view engine in a Node Express App. Below is the route I am using: router.get('api/projects', function(req, res){ ...

Tips on sorting items in React

Hey there, I'm a beginner at react and currently working on my first ecommerce website. My main concern is about filtering products by size. I'm struggling with the logic behind it. Any help or guidance would be greatly appreciated. I also attem ...

Nuxt.js Development Script Fumbles

Recently, I've been working on a website using Nuxt.js and have been really enjoying the process. However, I'm encountering an error that's puzzling me whenever I attempt to run $npm run dev: 0 info it worked if it ends with ok 1 verbose cl ...

Bamboo Grunt task labeled 'dustc' is experiencing failures

After successfully transitioning my grunt job into Bamboo, everything seemed to be running smoothly except for the dust compiler. While all other tasks in my gruntfile can be targeted from the bamboo task and they work without issue, the dustc task produce ...

Ways to organize the information within the Ul and Li elements

I need help sorting the data within <ul> and <li> elements using jQuery. Below is a snippet of my code: <ul id="rootUl"> <li> <div id='title'>MSFT</div> <div id='price'>230</d ...

Selecting any of the bar chart labels will reveal just a two-day timeframe

My bar chart is behaving strangely - when I click on all labels, it only shows two days instead of updating as expected. I suspect it may be due to a bad implementation involving parsing. Can anyone provide assistance? I have created a minimum example on ...

What are the methods used in TypeScript to implement features that are not available in JavaScript, despite TypeScript ultimately being compiled to JavaScript?

After transitioning from JavaScript to TypeScript, I discovered that TypeScript offers many features not found in JS, such as types. However, TypeScript is ultimately compiled down to JavaScript. How is it possible for a language like TypeScript to achie ...

To enable communication between methods, you can easily add a property to a JavaScript class component

Is there a better way to communicate between methods in a class? class T extends React.Component { constructor(props) { super(props) this.a = false; } methodA { //will set 'a' in this method, maybe async. } ...

How to prevent the parent element from scrolling when changing the value of a number input by scrolling

Within a container with fixed dimensions and scroll bars that appear when the content size exceeds the container, there is a form. This form contains an input of type "number" which allows changing its value using the mouse wheel. The issue arises when at ...

Pause the execution of an AJAX callback function after numerous AJAX requests

I have two separate Ajax calls in my code. Both of them have different callbacks that need to execute upon successful call. $.ajax({ type: 'POST', url: "url1", success: foo1 }); $.ajax({ type: 'POST', url: "url2", ...

Using Vue to append elements that are not part of the loop

While looping over a table row, I realized that each of the table rows should be accompanied by another table row containing additional data from the loop. Unfortunately, I am struggling to insert <tr class="data-spacer"></tr> <tr& ...

Tips for switching a group of buttons within a userscript by clicking a single button?

Apologies if my wording is not clear, allow me to clarify. I am in the process of developing a userscript that will display a set of buttons below a main button when clicked. These additional buttons will serve different functions and should disappear whe ...

Transmitting OfficeJS 2D Array via an Ajax Call

Struggling with sending a "large" table using OfficeJS: functionfile.html loaded from manifest route <script> (function (){ "use strict"; Office.initialize = function (reason) { $(document).ready(function() { $("#send-data-button").cli ...