sprintf.js icon indicating copy to clipboard operation
sprintf.js copied to clipboard

Is there a ESM version?

Open Dmitri-Sintsov opened this issue 3 years ago • 17 comments

Can you provide a link to ESM version, please? Also perhaps adding it to the README.

Dmitri-Sintsov avatar Apr 22 '21 09:04 Dmitri-Sintsov

Oh, did they finally decide to fix Node?

Jokes aside, how would that work? I.e. supporting CommonJS and ESM at the same time.

alexei avatar Apr 23 '21 07:04 alexei

That works via providing separate sprintf.esm.js file. Which would use export sprintf, not module.exports.

I use it with modern browsers and with rollup.js, not in node.

Dmitri-Sintsov avatar Apr 23 '21 07:04 Dmitri-Sintsov

I run rollup.js in Deno.

Dmitri-Sintsov avatar Apr 23 '21 07:04 Dmitri-Sintsov

One may look how underscore.js provides their esm distribution, for example.

Dmitri-Sintsov avatar Apr 23 '21 07:04 Dmitri-Sintsov

I didn't know about that, and I'm even more confused today than I was yesterday: I thought the way to do it was .mjs?

alexei avatar Apr 23 '21 09:04 alexei

.mjs extension is optional one (for disambiguation), it's not required. Chrome / Firefox load ES modules finely from ordinary .js file ES modules.

https://underscorejs.org/underscore-esm.js

Deno works with ES modules natively, although it's typescript (.ts) by default.

https://deno.land/manual/examples/import_export

Dmitri-Sintsov avatar Apr 23 '21 09:04 Dmitri-Sintsov

So let me get this straight: first there was CJS, then AMD showed up for whatever reason, then that evolved into UMD. Now we have ESM. Browsers don't mind .js, but Node wants .mjs. And now this: .esm.js which I still don't understand where it came from and the Internet doesn't help either. This looks like a mess I don't want to deal atm. Sorry!

alexei avatar Apr 23 '21 09:04 alexei

As you wish. If one does not like esm.js, you may create sprintf.mjs though. It will work with modern browsers and with Deno. By the way, the author of Deno hopes that Deno will replace node.js at some point.

Dmitri-Sintsov avatar Apr 23 '21 09:04 Dmitri-Sintsov

For example, underscore.js is major library which provides different versions, including ESM one.

Dmitri-Sintsov avatar Apr 23 '21 09:04 Dmitri-Sintsov

Is there any documentation about this?

alexei avatar Apr 23 '21 09:04 alexei

See underscore.js build setup for example:

https://github.com/jashkenas/underscore/blob/97f4cb42a1125e160f8aac1bedcd4969745dffec/rollup.config.js

  // Monolithic ESM bundle for browsers and deno.
  {
    input: 'modules/index-all.js',
    treeshake: false,
    output: monolithConf({
      file: 'underscore-esm.js',
      format: 'esm',
    }),
  },

Dmitri-Sintsov avatar Apr 23 '21 09:04 Dmitri-Sintsov

It will be nice if we could support ESM version, I have to see this warning everyday ...

copyright.component.ts depends on 'sprintf-js'. CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies

rickynguyen4590 avatar Oct 28 '21 04:10 rickynguyen4590

Many are just doing a major release and ship it as esm-only ppl stuck with cjs can use dynamic import() or stay on the later version.

jimmywarting avatar Nov 21 '21 15:11 jimmywarting

As you wish. If one does not like esm.js, you may create sprintf.mjs though. It will work with modern browsers and with Deno. By the way, the author of Deno hopes that Deno will replace node.js at some point.

Alternative package with esm support: printj and same discussion about alternative way if esm will not be supported =). But I don't found both on cdnjs.

viT-1 avatar Jan 21 '22 07:01 viT-1

The approach used in printj is to build the ESM script with a .mjs extension and use the module field in package.json to point to the .mjs script. The main field points to the CJS version and we ship the minified dist scripts for standalone use in the browser. This approach does not break normal node require statements. Webpack will accept the extension. Node ESM is sated. Browser ESM (script type="module") accepts it. Older browsers can theoretically use the dist version without fear.

If you're comfortable with the idea of supporting ESM but don't want to be burdened with having to maintain two separate scripts, it's sensible to build the ESM script from the base script using gulp-replace. Strip the IIFE and replace the exports/window blocks with a single export statement:

  1. add the module line to package.json as well as the gulp-replace dev dep:
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
   "description": "JavaScript sprintf implementation",
   "author": "Alexandru Mărășteanu <[email protected]>",
   "main": "src/sprintf.js",
+  "module": "dist/sprintf.mjs",
   "scripts": {
     "test": "mocha test/*.js",
     "pretest": "npm run lint",
@@ -25,6 +26,7 @@
     "gulp-header": "^2.0.5",
     "gulp-mocha": "^6.0.0",
     "gulp-rename": "^1.4.0",
+    "gulp-replace": "^1.1.3",
     "gulp-sourcemaps": "^2.6.4",
     "gulp-uglify": "^3.0.1",
     "mocha": "^5.2.0"
  1. create a gulp task to create that file from your base file:
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -8,6 +8,7 @@ var pkg         = require('./package.json'),
     header      = require('gulp-header'),
     eslint      = require('gulp-eslint'),
     mocha       = require('gulp-mocha'),
+    replace     = require('gulp-replace'),
     benchmark   = require('gulp-benchmark'),
     banner      = '/*! <%= pkg.name %> v<%= pkg.version %> | Copyright (c) 2007-present, <%= pkg.author %> | <%= pkg.license %> */\n'
 
@@ -42,4 +43,15 @@ gulp.task('dist', ['test'], function() {
         .pipe(gulp.dest('dist'))
 })
 
-gulp.task('default', ['dist'])
+gulp.task('dist-esm', ['test'], function() {
+    return gulp.src([
+        'src/sprintf.js'
+    ])
+        .pipe(replace(/^[^]*?{/, ""))
+        .pipe(replace(/if \(typeof exports[^]*$/, "export { sprintf, vsprintf };"))
+        .pipe(rename({ extname: '.mjs' }))
+        .pipe(header(banner, {pkg: pkg}))
+        .pipe(gulp.dest('dist'))
+})
+
+gulp.task('default', ['dist', 'dist-esm'])

(you may also want to update dist/.gitattributes)

This approach works in a simple webpack import, in node 16 ESM, and in raw browser ESM.

@viT-1 unpkg and jsdelivr pull from the npm modules e.g. https://unpkg.com/[email protected]/dist/sprintf.min.js . The live demo linked in that thread imports from unpkg directly and it would roughly work the same way if the same approach is used here:

      import { sprintf } from 'https://unpkg.com/[email protected]/printj.mjs';

SheetJSDev avatar Jan 22 '22 07:01 SheetJSDev

+1 On this issue! I'm happy to help if need be. With newer build tools, it shouldn't be hard to produce this library in multiple formats.

Etienne-M avatar Jun 09 '22 21:06 Etienne-M

I managed to convert NPM package for sprinft.js to an ESM using esh.sh

From the deno docs: "esm.sh is a CDN that was specifically designed for Deno... uses esbuild to take an arbitrary npm package and ensure that it is consumable as an ES Module"

This worked in the browser with the bundle I create with esbuild_deno_loader, but VSCode (the Deno extension) complained with: "This expression is not callable... has no call signatures.deno-ts(2349)" wherever I used sprintf in my TypeScript source code. Maybe I didn't configure the IDE properly? 🤷

Anyway... I then saved the ESM that was generated by esm.sh to a file, and did a relative import

import { sprintf } from "./sprintf/sprintf.esm.js"

This works, both the browser and the IDE is happy

mozey avatar Jun 15 '23 14:06 mozey