Repository: prettier/prettier-atom
Pull request #378 (2018-02-12 23:30:44)

fix: Fix asynchronous saves
Fix an issue where saving css files required another manual save action
  after autoformatting due to stylelint's async api.

- ensure the promise chain stays in tact starting from main.js
  'onWillSave' through executePrettierOnBufferRange.js.

olsonpm: If you guys prefer more in-depth testing for this then let me know what you're looking for and I'll implement it.
codecov-io: # [Codecov](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=h1) Report
> Merging [#378](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=desc) into [master](https://codecov.io/gh/prettier/prettier-atom/commit/3d05d959e48c06226b35fafeb12274d177a56871?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `66.66%`.

[![Impacted file tree graph](https://codecov.io/gh/prettier/prettier-atom/pull/378/graphs/tree.svg?token=Pdibkvk5Zo&src=pr&height=150&width=650)](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master     #378   +/-   ##
=======================================
  Coverage   77.63%   77.63%           
=======================================
  Files          27       27           
  Lines         483      483           
  Branches       50       52    +2     
=======================================
  Hits          375      375           
+ Misses         93       91    -2     
- Partials       15       17    +2
```


| [Impacted Files](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/main.js](https://codecov.io/gh/prettier/prettier-atom/pull/378/diff?src=pr&el=tree#diff-c3JjL21haW4uanM=) | `0% <0%> (ø)` | :arrow_up: |
| [src/atomInterface/index.js](https://codecov.io/gh/prettier/prettier-atom/pull/378/diff?src=pr&el=tree#diff-c3JjL2F0b21JbnRlcmZhY2UvaW5kZXguanM=) | `88.23% <100%> (ø)` | :arrow_up: |
| [...rc/executePrettier/executePrettierOnBufferRange.js](https://codecov.io/gh/prettier/prettier-atom/pull/378/diff?src=pr&el=tree#diff-c3JjL2V4ZWN1dGVQcmV0dGllci9leGVjdXRlUHJldHRpZXJPbkJ1ZmZlclJhbmdlLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [src/formatOnSave/index.js](https://codecov.io/gh/prettier/prettier-atom/pull/378/diff?src=pr&el=tree#diff-c3JjL2Zvcm1hdE9uU2F2ZS9pbmRleC5qcw==) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=footer). Last update [3d05d95...6550a65](https://codecov.io/gh/prettier/prettier-atom/pull/378?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/dist/atomInterface/index.js b/dist/atomInterface/index.js
index 59e102b..412646c 100644
--- a/dist/atomInterface/index.js
+++ b/dist/atomInterface/index.js
@@ -1,5 +1,15 @@
 'use strict';
 
+var _regenerator = require('babel-runtime/regenerator');
+
+var _regenerator2 = _interopRequireDefault(_regenerator);
+
+var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
+
+var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
 // constants
 var LINTER_LINT_COMMAND = 'linter:lint';
 
@@ -133,18 +143,43 @@ var addErrorNotification = function addErrorNotification(message, options) {
   return atom.notifications.addError(message, options);
 };
 
-var attemptWithErrorNotification = function attemptWithErrorNotification(func) {
-  for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
-    args[_key - 1] = arguments[_key];
-  }
-
-  try {
-    func.apply(undefined, args);
-  } catch (e) {
-    console.error(e); // eslint-disable-line no-console
-    addErrorNotification(e.message, { dismissable: true, stack: e.stack });
-  }
-};
+var attemptWithErrorNotification = function () {
+  var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(func) {
+    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+      args[_key - 1] = arguments[_key];
+    }
+
+    return _regenerator2.default.wrap(function _callee$(_context) {
+      while (1) {
+        switch (_context.prev = _context.next) {
+          case 0:
+            _context.prev = 0;
+            _context.next = 3;
+            return func.apply(undefined, args);
+
+          case 3:
+            _context.next = 9;
+            break;
+
+          case 5:
+            _context.prev = 5;
+            _context.t0 = _context['catch'](0);
+
+            console.error(_context.t0); // eslint-disable-line no-console
+            addErrorNotification(_context.t0.message, { dismissable: true, stack: _context.t0.stack });
+
+          case 9:
+          case 'end':
+            return _context.stop();
+        }
+      }
+    }, _callee, undefined, [[0, 5]]);
+  }));
+
+  return function attemptWithErrorNotification(_x) {
+    return _ref.apply(this, arguments);
+  };
+}();
 
 var runLinter = function runLinter(editor) {
   return isLinterLintCommandDefined(editor) && atom.commands.dispatch(atom.views.getView(editor), LINTER_LINT_COMMAND);
diff --git a/dist/main.js b/dist/main.js
index e12d6c0..62b8e92 100644
--- a/dist/main.js
+++ b/dist/main.js
@@ -1,5 +1,15 @@
 'use strict';
 
+var _regenerator = require('babel-runtime/regenerator');
+
+var _regenerator2 = _interopRequireDefault(_regenerator);
+
+var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
+
+var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
 var config = require('./config-schema.json');
 // eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
 
@@ -34,10 +44,34 @@ var lazyFormat = function lazyFormat() {
 };
 
 // HACK: lazy load most of the code we need for performance
-var lazyFormatOnSave = function lazyFormatOnSave(editor) {
-  if (!formatOnSave) formatOnSave = require('./formatOnSave'); // eslint-disable-line global-require
-  if (editor) formatOnSave(editor);
-};
+var lazyFormatOnSave = function () {
+  var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(editor) {
+    return _regenerator2.default.wrap(function _callee$(_context) {
+      while (1) {
+        switch (_context.prev = _context.next) {
+          case 0:
+            if (!formatOnSave) formatOnSave = require('./formatOnSave'); // eslint-disable-line global-require
+
+            if (!editor) {
+              _context.next = 4;
+              break;
+            }
+
+            _context.next = 4;
+            return formatOnSave(editor);
+
+          case 4:
+          case 'end':
+            return _context.stop();
+        }
+      }
+    }, _callee, undefined);
+  }));
+
+  return function lazyFormatOnSave(_x) {
+    return _ref.apply(this, arguments);
+  };
+}();
 
 // HACK: lazy load most of the code we need for performance
 var lazyWarnAboutLinterEslintFixOnSave = function lazyWarnAboutLinterEslintFixOnSave() {
diff --git a/src/atomInterface/index.js b/src/atomInterface/index.js
index 2e50e77..0c96da9 100644
--- a/src/atomInterface/index.js
+++ b/src/atomInterface/index.js
@@ -86,9 +86,9 @@ const addWarningNotification = (message: string, options?: Atom$Notifications$Op
 const addErrorNotification = (message: string, options?: Atom$Notifications$Options) =>
   atom.notifications.addError(message, options);
 
-const attemptWithErrorNotification = (func: Function, ...args: Array<any>) => {
+const attemptWithErrorNotification = async (func: Function, ...args: Array<any>) => {
   try {
-    func(...args);
+    await func(...args);
   } catch (e) {
     console.error(e); // eslint-disable-line no-console
     addErrorNotification(e.message, { dismissable: true, stack: e.stack });
diff --git a/src/executePrettier/executePrettierOnBufferRange.js b/src/executePrettier/executePrettierOnBufferRange.js
index bf4e207..0a48fd2 100644
--- a/src/executePrettier/executePrettierOnBufferRange.js
+++ b/src/executePrettier/executePrettierOnBufferRange.js
@@ -44,7 +44,7 @@ const buildPrettierStylelintOptions = (editor: TextEditor, text: string) => ({
   filePath: getCurrentFilePath(editor),
 });
 
-const executePrettierStylelint = (editor: TextEditor, text: string): string =>
+const executePrettierStylelint = (editor: TextEditor, text: string): Promise<string> =>
   prettierStylelint.format(buildPrettierStylelintOptions(editor, text));
 
 const executePrettierOrIntegration = async (
diff --git a/src/main.js b/src/main.js
index 99a5b55..bfd9877 100644
--- a/src/main.js
+++ b/src/main.js
@@ -24,9 +24,9 @@ const lazyFormat = () => {
 };
 
 // HACK: lazy load most of the code we need for performance
-const lazyFormatOnSave = editor => {
+const lazyFormatOnSave = async editor => {
   if (!formatOnSave) formatOnSave = require('./formatOnSave'); // eslint-disable-line global-require
-  if (editor) formatOnSave(editor);
+  if (editor) await formatOnSave(editor);
 };
 
 // HACK: lazy load most of the code we need for performance


Test patch:
diff --git a/src/formatOnSave/index.test.js b/src/formatOnSave/index.test.js
index d1d6262..78ba012 100644
--- a/src/formatOnSave/index.test.js
+++ b/src/formatOnSave/index.test.js
@@ -10,48 +10,56 @@ const createMockTextEditor = require('../../tests/mocks/textEditor');
 const shouldFormatOnSave = require('./shouldFormatOnSave');
 const formatOnSave = require('./index');
 
-it('clears linter errors before running', () => {
+it('returns a Promise', async () => {
   const editor = createMockTextEditor();
 
-  formatOnSave(editor);
+  const result = formatOnSave(editor);
+
+  expect(result).toBeInstanceOf(Promise);
+});
+
+it('clears linter errors before running', async () => {
+  const editor = createMockTextEditor();
+
+  await formatOnSave(editor);
 
   expect(clearLinterErrors).toHaveBeenCalledWith(editor);
 });
 
-it('executes prettier on the buffer range if appropriate and scope is not embedded', () => {
+it('executes prettier on the buffer range if appropriate and scope is not embedded', async () => {
   const editor = createMockTextEditor();
   const mockRange = { start: [0, 0], end: [0, 1] };
   getBufferRange.mockImplementation(() => mockRange);
   shouldFormatOnSave.mockImplementation(() => true);
   isCurrentScopeEmbeddedScope.mockImplementation(() => false);
 
-  formatOnSave(editor);
+  await formatOnSave(editor);
 
   expect(executePrettierOnBufferRange).toHaveBeenCalledWith(editor, mockRange, { setTextViaDiff: true });
 });
 
-it('executes prettier on the embedded scripts if appropriate and scope is embedded', () => {
+it('executes prettier on the embedded scripts if appropriate and scope is embedded', async () => {
   const editor = createMockTextEditor();
   shouldFormatOnSave.mockImplementation(() => true);
   isCurrentScopeEmbeddedScope.mockImplementation(() => true);
 
-  formatOnSave(editor);
+  await formatOnSave(editor);
 
   expect(executePrettierOnEmbeddedScripts).toHaveBeenCalledWith(editor);
 });
 
-it('does nothing if it should not format on save', () => {
+it('does nothing if it should not format on save', async () => {
   const editor = createMockTextEditor();
   shouldFormatOnSave.mockImplementation(() => false);
 
-  formatOnSave(editor);
+  await formatOnSave(editor);
 
   expect(shouldFormatOnSave).toHaveBeenCalledWith(editor);
   expect(executePrettierOnBufferRange).not.toHaveBeenCalled();
   expect(executePrettierOnEmbeddedScripts).not.toHaveBeenCalled();
 });
 
-it('catches uncaught errors so that the user is not prevented from saving', () => {
+it('catches uncaught errors so that the user is not prevented from saving', async () => {
   const originalConsoleError = console.error; // eslint-disable-line no-console
   console.error = jest.fn(); // eslint-disable-line no-console
   const editor = createMockTextEditor();
@@ -61,7 +69,7 @@ it('catches uncaught errors so that the user is not prevented from saving', () =
     throw fakeError;
   });
 
-  formatOnSave(editor);
+  await formatOnSave(editor);
 
   expect(atom.notifications.addError).toHaveBeenCalled();
   expect(console.error).toHaveBeenCalledWith(fakeError); // eslint-disable-line no-console