Commit e265ba9 for mammothjs

commit e265ba9b08f300779a2ad45a5b5b3bb90beda6df
Author: Michael Williamson <mike@zwobble.org>
Date:   Thu Sep 17 22:54:54 2026 +0100

    Use native promises instead of bluebird

diff --git a/.eslintrc.json b/.eslintrc.json
index 75ad86c..83d0a73 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -68,7 +68,10 @@
     "console": true,
     "exports": true,
     "module": true,
+    "process": false,
+    "Promise": false,
     "require": false,
+    "setTimeout": false,
     "TextDecoder": false,
     "Uint8Array": false
   }
diff --git a/lib/index.js b/lib/index.js
index 0048cfe..600eaf7 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -6,6 +6,7 @@ var DocumentConverter = require("./document-to-html").DocumentConverter;
 var convertElementToRawText = require("./raw-text").convertElementToRawText;
 var readStyle = require("./style-reader").readStyle;
 var readOptions = require("./options-reader").readOptions;
+var promises = require("./promises");
 var unzip = require("./unzip");
 var Result = require("./results").Result;

@@ -32,7 +33,7 @@ function convertToMarkdown(input, options) {
 function convert(input, options) {
     options = readOptions(options);

-    return unzip.openZip(input)
+    var result = unzip.openZip(input)
         .then(function(docxFile) {
             return docxStyleMap.readStyleMap(docxFile).then(function(styleMap) {
                 options.embeddedStyleMap = styleMap;
@@ -48,11 +49,15 @@ function convert(input, options) {
                     return convertDocumentToHtml(documentResult, options);
                 });
         });
+
+    return promises.toExternalPromise(result);
 }

 function readEmbeddedStyleMap(input) {
-    return unzip.openZip(input)
+    var result = unzip.openZip(input)
         .then(docxStyleMap.readStyleMap);
+
+    return promises.toExternalPromise(result);
 }

 function convertDocumentToHtml(documentResult, options) {
@@ -80,15 +85,17 @@ function parseStyleMap(styleMap) {


 function extractRawText(input) {
-    return unzip.openZip(input)
+    var result = unzip.openZip(input)
         .then(docxReader.read)
         .then(function(documentResult) {
             return documentResult.map(convertElementToRawText);
         });
+
+    return promises.toExternalPromise(result);
 }

 function embedStyleMap(input, styleMap) {
-    return unzip.openZip(input)
+    var result = unzip.openZip(input)
         .then(function(docxFile) {
             return docxStyleMap.writeStyleMap(docxFile, styleMap)
                 .then(function() {
@@ -108,6 +115,8 @@ function embedStyleMap(input, styleMap) {
                 }
             };
         });
+
+    return promises.toExternalPromise(result);
 }

 exports.styleMapping = function() {
diff --git a/lib/main.js b/lib/main.js
index 9cc8196..8d5828d 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -14,7 +14,7 @@ function main(argv) {
     var outputFormat = argv.output_format;
     var styleMapPath = argv.style_map;

-    readStyleMap(styleMapPath).then(function(styleMap) {
+    var result = readStyleMap(styleMapPath).then(function(styleMap) {
         var options = {
             styleMap: styleMap,
             outputFormat: outputFormat
@@ -49,7 +49,9 @@ function main(argv) {

                 outputStream.write(result.value);
             });
-    }).done();
+    });
+
+    promises.toExternalPromise(result).done();
 }

 function readStyleMap(styleMapPath) {
diff --git a/lib/promises.js b/lib/promises.js
index 0da48bb..3620c96 100644
--- a/lib/promises.js
+++ b/lib/promises.js
@@ -1,10 +1,9 @@
 var _ = require("underscore");
-var bluebird = require("bluebird/js/release/promise")();

 exports.defer = defer;
-exports.Promise = bluebird.Promise;
-exports.resolve = bluebird.resolve;
-exports.reject = bluebird.reject;
+exports.Promise = Promise;
+exports.resolve = Promise.resolve.bind(Promise);
+exports.reject = Promise.reject.bind(Promise);

 exports.forEachSeries = function(array, func) {
     var arrayIndex = 0;
@@ -31,7 +30,7 @@ var props = exports.props = function(obj) {
     var keys = Object.keys(obj);
     var values = Object.values(obj);

-    var result = bluebird.all(values).then(function(resolvedValues) {
+    var result = Promise.all(values).then(function(resolvedValues) {
         var resolvedObj = {};
         for (var index = 0; index < keys.length; index++) {
             resolvedObj[keys[index]] = resolvedValues[index];
@@ -58,16 +57,16 @@ function addAlsoToPromise(promise) {

 exports.try = function(func) {
     try {
-        return bluebird.resolve(func());
+        return Promise.resolve(func());
     } catch (error) {
-        return bluebird.reject(error);
+        return Promise.reject(error);
     }
 };

 function defer() {
     var resolve;
     var reject;
-    var promise = new bluebird.Promise(function(resolveArg, rejectArg) {
+    var promise = new Promise(function(resolveArg, rejectArg) {
         resolve = resolveArg;
         reject = rejectArg;
     });
@@ -78,3 +77,59 @@ function defer() {
         promise: promise
     };
 }
+
+function ExternalPromise(executor) {
+    var promise = new Promise(executor);
+    promise.__proto__ = ExternalPromise.prototype;
+    return promise;
+}
+
+ExternalPromise.__proto__ = Promise;
+ExternalPromise.prototype.__proto__ = Promise.prototype;
+
+ExternalPromise.prototype.done = function(resolve, reject) {
+    return this.then(resolve, reject).catch(function(error) {
+        if (isNode()) {
+            var stack = error instanceof Error ? error.stack : error;
+            process.stderr.write("Fatal " + stack + "\n");
+            process.exit(2);
+        } else {
+            setTimeout(function() {
+                throw error;
+            }, 0);
+        }
+    });
+};
+
+/**
+ * Previous versions of Mammoth used bluebird instead of native promises. While
+ * internal usage can be replaced easily enough, external users of the library
+ * may have relied on values returned from function calls being a bluebird
+ * promise.
+ *
+ * However, since the return value was only documented as a promise, not as a
+ * bluebird promise, and it's relatively straightforward to wrap the return
+ * value in a bluebird promise if necessary, changing the API to return a native
+ * promise should be relatively safe.
+ *
+ * The only exception is that the docs previously used `.done()`: therefore,
+ * when returning promises from the API to external users, a `done` method is
+ * added.
+ */
+function toExternalPromise(promise) {
+    return ExternalPromise.resolve(promise);
+}
+
+exports.toExternalPromise = toExternalPromise;
+
+/**
+ * Detect whether we're running in node.js. This uses the same logic as in
+ * bluebird for consistency with previous versions of Mammoth.
+ */
+function isNode() {
+    if (typeof process === "undefined") {
+        return false;
+    }
+
+    return {}.toString.call(process).toLowerCase() === "[object process]";
+}
diff --git a/package-lock.json b/package-lock.json
index 1e34ae8..e7776cf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,17 @@
 {
   "name": "mammoth",
-  "version": "1.8.0",
+  "version": "1.12.3",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "mammoth",
-      "version": "1.8.0",
+      "version": "1.12.3",
       "license": "BSD-2-Clause",
       "dependencies": {
         "@xmldom/xmldom": "^0.8.6",
         "argparse": "~1.0.3",
         "base64-js": "^1.5.1",
-        "bluebird": "~3.4.0",
         "dingbat-to-unicode": "^1.0.1",
         "jszip": "^3.7.1",
         "lop": "^0.4.2",
@@ -296,7 +295,8 @@
     "node_modules/bluebird": {
       "version": "3.4.7",
       "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
-      "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+      "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=",
+      "dev": true
     },
     "node_modules/bn.js": {
       "version": "4.11.6",
@@ -3325,7 +3325,8 @@
     "bluebird": {
       "version": "3.4.7",
       "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
-      "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+      "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=",
+      "dev": true
     },
     "bn.js": {
       "version": "4.11.6",
diff --git a/package.json b/package.json
index 0751b5b..81123f0 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,6 @@
     "@xmldom/xmldom": "^0.8.6",
     "argparse": "~1.0.3",
     "base64-js": "^1.5.1",
-    "bluebird": "~3.4.0",
     "dingbat-to-unicode": "^1.0.1",
     "jszip": "^3.7.1",
     "lop": "^0.4.2",
diff --git a/test/promises.tests.js b/test/promises.tests.js
index 6497cd3..a80df0e 100644
--- a/test/promises.tests.js
+++ b/test/promises.tests.js
@@ -226,3 +226,13 @@ test("props", {
         });
     }
 });
+
+test("toExternalPromise() returns promise with .done()", function() {
+    var nativePromise = Promise.resolve("hello");
+
+    var externalPromise = promises.toExternalPromise(nativePromise);
+
+    externalPromise.done();
+
+    externalPromise.then().done();
+});