From bdce8b70d2fb2fc651d4621377579d3b8edd9c2c Mon Sep 17 00:00:00 2001 From: Kevin Gravier Date: Mon, 7 May 2018 16:46:00 -0400 Subject: [PATCH 1/2] Converted to use TypeScript Changed to named exports/imports Dropped NodeJS 0.12 support --- .coffeelint.json | 135 ------- .editorconfig | 2 +- .travis.yml | 2 +- README.md | 47 ++- example_async.js | 2 +- example_sync.js | 2 +- main/errors/CreateFileError.d.ts | 10 + main/errors/CreateFileError.js | 59 +-- main/errors/LaunchEditorError.d.ts | 10 + main/errors/LaunchEditorError.js | 59 +-- main/errors/ReadFileError.d.ts | 10 + main/errors/ReadFileError.js | 59 +-- main/errors/RemoveFileError.d.ts | 10 + main/errors/RemoveFileError.js | 59 +-- main/index.d.ts | 31 ++ main/index.js | 358 ++++++++---------- package-lock.json | 554 ++++++++++++++++++---------- package.json | 35 +- src/errors/CreateFileError.coffee | 11 - src/errors/CreateFileError.ts | 20 + src/errors/LaunchEditorError.coffee | 11 - src/errors/LaunchEditorError.ts | 20 + src/errors/ReadFileError.coffee | 11 - src/errors/ReadFileError.ts | 20 + src/errors/RemoveFileError.coffee | 11 - src/errors/RemoveFileError.ts | 20 + src/index.coffee | 118 ------ src/index.ts | 168 +++++++++ test/spec/main.coffee | 115 ------ test/spec/main.spec.ts | 170 +++++++++ tsconfig.json | 21 ++ tslint.json | 11 + 32 files changed, 1225 insertions(+), 946 deletions(-) delete mode 100644 .coffeelint.json create mode 100644 main/errors/CreateFileError.d.ts create mode 100644 main/errors/LaunchEditorError.d.ts create mode 100644 main/errors/ReadFileError.d.ts create mode 100644 main/errors/RemoveFileError.d.ts create mode 100644 main/index.d.ts delete mode 100644 src/errors/CreateFileError.coffee create mode 100644 src/errors/CreateFileError.ts delete mode 100644 src/errors/LaunchEditorError.coffee create mode 100644 src/errors/LaunchEditorError.ts delete mode 100644 src/errors/ReadFileError.coffee create mode 100644 src/errors/ReadFileError.ts delete mode 100644 src/errors/RemoveFileError.coffee create mode 100644 src/errors/RemoveFileError.ts delete mode 100644 src/index.coffee create mode 100644 src/index.ts delete mode 100644 test/spec/main.coffee create mode 100644 test/spec/main.spec.ts create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.coffeelint.json b/.coffeelint.json deleted file mode 100644 index 5aa4030..0000000 --- a/.coffeelint.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "arrow_spacing": { - "level": "error" - }, - "braces_spacing": { - "level": "error", - "spaces": 0, - "empty_object_spaces": 0 - }, - "camel_case_classes": { - "level": "error" - }, - "coffeescript_error": { - "level": "error" - }, - "colon_assignment_spacing": { - "level": "ignore", - "spacing": { - "left": 0, - "right": 0 - } - }, - "cyclomatic_complexity": { - "level": "ignore", - "value": 10 - }, - "duplicate_key": { - "level": "error" - }, - "empty_constructor_needs_parens": { - "level": "ignore" - }, - "ensure_comprehensions": { - "level": "warn" - }, - "eol_last": { - "level": "error" - }, - "indentation": { - "value": 2, - "level": "error" - }, - "line_endings": { - "level": "error", - "value": "unix" - }, - "max_line_length": { - "value": 80, - "level": "ignore", - "limitComments": true - }, - "missing_fat_arrows": { - "level": "ignore", - "is_strict": false - }, - "newlines_after_classes": { - "value": 3, - "level": "ignore" - }, - "no_backticks": { - "level": "error" - }, - "no_debugger": { - "level": "error", - "console": false - }, - "no_empty_functions": { - "level": "ignore" - }, - "no_empty_param_list": { - "level": "ignore" - }, - "no_implicit_braces": { - "level": "ignore", - "strict": true - }, - "no_implicit_parens": { - "level": "ignore", - "strict": true - }, - "no_interpolation_in_single_quotes": { - "level": "error" - }, - "no_nested_string_interpolation": { - "level": "warn" - }, - "no_plusplus": { - "level": "ignore" - }, - "no_private_function_fat_arrows": { - "level": "warn" - }, - "no_stand_alone_at": { - "level": "ignore" - }, - "no_tabs": { - "level": "error" - }, - "no_this": { - "level": "error" - }, - "no_throwing_strings": { - "level": "error" - }, - "no_trailing_semicolons": { - "level": "error" - }, - "no_trailing_whitespace": { - "level": "error", - "allowed_in_comments": false, - "allowed_in_empty_lines": true - }, - "no_unnecessary_double_quotes": { - "level": "error" - }, - "no_unnecessary_fat_arrows": { - "level": "warn" - }, - "non_empty_constructor_needs_parens": { - "level": "ignore" - }, - "prefer_english_operator": { - "level": "ignore", - "doubleNotLevel": "ignore" - }, - "space_operators": { - "level": "error" - }, - "spacing_after_comma": { - "level": "error" - }, - "transform_messes_up_line_numbers": { - "level": "warn" - } -} diff --git a/.editorconfig b/.editorconfig index 6aea837..aef4d06 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true charset = utf-8 end_of_line = lf indent_style = space -indent_size = 2 +indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true diff --git a/.travis.yml b/.travis.yml index 1ef3e2a..4668015 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ sudo: false node_js: + - 10 - 9 - 8 - 7 - 6 - 5 - 4 - - .12 language: node_js script: npm test diff --git a/README.md b/README.md index e052fc5..3bc38fb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A node module to edit a string with a users preferred text editor using $VISUAL Version: 2.2.0 -As of version 2.0.0, node 0.10 is no longer support. Minimum node version is now 0.12. +As of version 3.0.0, the minimum version of node supported is 4. ## Install @@ -19,27 +19,27 @@ As of version 2.0.0, node 0.10 is no longer support. Minimum node version is now A simple example using the `.edit` convenience method - var ExternalEditor = require('external-editor') - var data = ExternalEditor.edit('\n\n# Please write your text above'); + import {edit} from "external-editor"; + const data = edit('\n\n# Please write your text above'); console.log(data); A full featured example - var ExternalEditor = require('external-editor'); + import {ExternalEditor, CreateFileError, ReadFileError, RemoveFileError} from "external-editor" try { - var editor = new ExternalEditor(); - var text = editor.run() // the text is also available in editor.text + const editor = new ExternalEditor(); + const text = editor.run() // the text is also available in editor.text if (editor.last_exit_status !== 0) { console.log("The editor exited with a non-zero code"); } } catch (err) { - if (err instanceOf ExternalEditor.CreateFileError) { + if (err instanceOf CreateFileError) { console.log('Failed to create the temporary file'); - } else if (err instanceOf ExternalEditor.ReadFileError) { + } else if (err instanceOf ReadFileError) { console.log('Failed to read the temporary file'); - } else if (err instanceOf ExternalEditor.LaunchEditorError) { + } else if (err instanceOf LaunchEditorError) { console.log('Failed to launch your editor'); } else { throw err; @@ -52,7 +52,7 @@ A full featured example try { editor.cleanup(); } catch (err) { - if (err instanceOf ExternalEditor.RemoveFileError) { + if (err instanceOf RemoveFileError) { console.log('Failed to remove the temporary file'); } else { throw err @@ -61,7 +61,7 @@ A full featured example #### API -**Static Methods** +**Convenience Methods** - `edit(text)` - `text` (string) *Optional* Defaults to empty string @@ -74,14 +74,14 @@ A full featured example - `text`(string) The contents of the file -**Static Properties** +**Errors** - `CreateFileError` Error thrown if the temporary file could not be created. - `ReadFileError` Error thrown if the temporary file could not be read. - `RemoveFileError` Error thrown if the temporary file could not be removed during cleanup. - `LaunchEditorError` Error thrown if the editor could not be launched. -**Public Methods** +**External Editor Public Methods** - `new ExternalEditor(text)` - `text` (string) *Optional* Defaults to empty string @@ -96,18 +96,18 @@ A full featured example - `cleanup()` Removes the temporary file. - Could throw `RemoveFileError` -**Public Properties** +**External Editor Public Properties** - `text` (string) *readonly* The text in the temporary file. - `editor.bin` (string) The editor determined from the environment. - `editor.args` (array) Default arguments for the bin -- `temp_file` (string) Path to temporary file. Can be changed, but be careful as the temporary file probably already +- `tempFile` (string) Path to temporary file. Can be changed, but be careful as the temporary file probably already exists and would need be removed manually. -- `last_exit_status` (number) The last exit code emitted from the editor. +- `lastExitStatus` (number) The last exit code emitted from the editor. ## Errors -All errors have a simple message explaining what went wrong. They all also have an `original_error` property containing +All errors have a simple message explaining what went wrong. They all also have an `originalError` property containing the original error thrown for debugging purposes. ## Why Synchronous? @@ -120,17 +120,26 @@ please submit a PR. If async is really needed, you can use `editAsync` or `runAsync`. If you are using readline or have anything else listening to the stdin or you write to stdout, you will most likely have problem, so make sure to remove any other -listeners on stdin, stdout, or stdin. +listeners on stdin, stdout, or stderr. ## Demo [![asciicast](https://asciinema.org/a/a1qh9lypbe65mj0ivfuoslz2s.png)](https://asciinema.org/a/a1qh9lypbe65mj0ivfuoslz2s) + +## Breaking Changes from v2 to v3 + +- NodeJS 0.12 support dropped. +- Switched to named imports. +- All "snake_cased" variables and properties are now "camelCased". + - `ExternalEditor.temp_file` is now `ExternalEditor.tempFile`. + - `ExternalEditor.last_exit_status` is now `ExternalEditor.lastExitStatus`. + - `Error.original_error` is now `Error.originalError`. ## License The MIT License (MIT) -Copyright (c) 2016 Kevin Gravier +Copyright (c) 2016-2018 Kevin Gravier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example_async.js b/example_async.js index 025356c..0526166 100644 --- a/example_async.js +++ b/example_async.js @@ -1,4 +1,4 @@ -var ExternalEditor = require('./main'); +var ExternalEditor = require('./main').ExternalEditor; var readline = require('readline'); var rl = readline.createInterface({ diff --git a/example_sync.js b/example_sync.js index 5df4a85..c1879bb 100644 --- a/example_sync.js +++ b/example_sync.js @@ -1,4 +1,4 @@ -var ExternalEditor = require('./main'); +var ExternalEditor = require('./main').ExternalEditor; var readline = require('readline'); var rl = readline.createInterface({ diff --git a/main/errors/CreateFileError.d.ts b/main/errors/CreateFileError.d.ts new file mode 100644 index 0000000..0df1b3d --- /dev/null +++ b/main/errors/CreateFileError.d.ts @@ -0,0 +1,10 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ +export declare class CreateFileError extends Error { + originalError: Error; + constructor(originalError: Error); +} diff --git a/main/errors/CreateFileError.js b/main/errors/CreateFileError.js index e188155..3c9aed0 100644 --- a/main/errors/CreateFileError.js +++ b/main/errors/CreateFileError.js @@ -1,29 +1,36 @@ -// Generated by CoffeeScript 1.12.7 - -/* - ExternalEditor - Kevin Gravier - MIT +"use strict"; +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 */ - -(function() { - var CreateFileError, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - CreateFileError = (function(superClass) { - extend(CreateFileError, superClass); - - CreateFileError.prototype.message = 'Failed to create temporary file for editor'; - - function CreateFileError(original_error) { - this.original_error = original_error; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var CreateFileError = /** @class */ (function (_super) { + __extends(CreateFileError, _super); + function CreateFileError(originalError) { + var _newTarget = this.constructor; + var _this = _super.call(this, "Failed to create temporary file for editor") || this; + _this.originalError = originalError; + var proto = _newTarget.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(_this, proto); + } + else { + _this.__proto__ = _newTarget.prototype; + } + return _this; } - return CreateFileError; - - })(Error); - - module.exports = CreateFileError; - -}).call(this); +}(Error)); +exports.CreateFileError = CreateFileError; diff --git a/main/errors/LaunchEditorError.d.ts b/main/errors/LaunchEditorError.d.ts new file mode 100644 index 0000000..105077c --- /dev/null +++ b/main/errors/LaunchEditorError.d.ts @@ -0,0 +1,10 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ +export declare class LaunchEditorError extends Error { + originalError: Error; + constructor(originalError: Error); +} diff --git a/main/errors/LaunchEditorError.js b/main/errors/LaunchEditorError.js index d956a1b..a1cfef8 100644 --- a/main/errors/LaunchEditorError.js +++ b/main/errors/LaunchEditorError.js @@ -1,29 +1,36 @@ -// Generated by CoffeeScript 1.12.7 - -/* - ExternalEditor - Kevin Gravier - MIT +"use strict"; +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 */ - -(function() { - var LaunchEditorError, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - LaunchEditorError = (function(superClass) { - extend(LaunchEditorError, superClass); - - LaunchEditorError.prototype.message = 'Failed launch editor'; - - function LaunchEditorError(original_error) { - this.original_error = original_error; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var LaunchEditorError = /** @class */ (function (_super) { + __extends(LaunchEditorError, _super); + function LaunchEditorError(originalError) { + var _newTarget = this.constructor; + var _this = _super.call(this, "Failed launch editor") || this; + _this.originalError = originalError; + var proto = _newTarget.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(_this, proto); + } + else { + _this.__proto__ = _newTarget.prototype; + } + return _this; } - return LaunchEditorError; - - })(Error); - - module.exports = LaunchEditorError; - -}).call(this); +}(Error)); +exports.LaunchEditorError = LaunchEditorError; diff --git a/main/errors/ReadFileError.d.ts b/main/errors/ReadFileError.d.ts new file mode 100644 index 0000000..438eae1 --- /dev/null +++ b/main/errors/ReadFileError.d.ts @@ -0,0 +1,10 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ +export declare class ReadFileError extends Error { + originalError: Error; + constructor(originalError: Error); +} diff --git a/main/errors/ReadFileError.js b/main/errors/ReadFileError.js index 317be76..d7e1eb5 100644 --- a/main/errors/ReadFileError.js +++ b/main/errors/ReadFileError.js @@ -1,29 +1,36 @@ -// Generated by CoffeeScript 1.12.7 - -/* - ExternalEditor - Kevin Gravier - MIT +"use strict"; +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 */ - -(function() { - var ReadFileError, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - ReadFileError = (function(superClass) { - extend(ReadFileError, superClass); - - ReadFileError.prototype.message = 'Failed to read temporary file'; - - function ReadFileError(original_error) { - this.original_error = original_error; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ReadFileError = /** @class */ (function (_super) { + __extends(ReadFileError, _super); + function ReadFileError(originalError) { + var _newTarget = this.constructor; + var _this = _super.call(this, "Failed to read temporary file") || this; + _this.originalError = originalError; + var proto = _newTarget.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(_this, proto); + } + else { + _this.__proto__ = _newTarget.prototype; + } + return _this; } - return ReadFileError; - - })(Error); - - module.exports = ReadFileError; - -}).call(this); +}(Error)); +exports.ReadFileError = ReadFileError; diff --git a/main/errors/RemoveFileError.d.ts b/main/errors/RemoveFileError.d.ts new file mode 100644 index 0000000..a6402e4 --- /dev/null +++ b/main/errors/RemoveFileError.d.ts @@ -0,0 +1,10 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ +export declare class RemoveFileError extends Error { + originalError: Error; + constructor(originalError: Error); +} diff --git a/main/errors/RemoveFileError.js b/main/errors/RemoveFileError.js index 658c9b0..acf8d66 100644 --- a/main/errors/RemoveFileError.js +++ b/main/errors/RemoveFileError.js @@ -1,29 +1,36 @@ -// Generated by CoffeeScript 1.12.7 - -/* - ExternalEditor - Kevin Gravier - MIT +"use strict"; +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 */ - -(function() { - var RemoveFileError, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - RemoveFileError = (function(superClass) { - extend(RemoveFileError, superClass); - - RemoveFileError.prototype.message = 'Failed to cleanup temporary file'; - - function RemoveFileError(original_error) { - this.original_error = original_error; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var RemoveFileError = /** @class */ (function (_super) { + __extends(RemoveFileError, _super); + function RemoveFileError(originalError) { + var _newTarget = this.constructor; + var _this = _super.call(this, "Failed to cleanup temporary file") || this; + _this.originalError = originalError; + var proto = _newTarget.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(_this, proto); + } + else { + _this.__proto__ = _newTarget.prototype; + } + return _this; } - return RemoveFileError; - - })(Error); - - module.exports = RemoveFileError; - -}).call(this); +}(Error)); +exports.RemoveFileError = RemoveFileError; diff --git a/main/index.d.ts b/main/index.d.ts new file mode 100644 index 0000000..d2af486 --- /dev/null +++ b/main/index.d.ts @@ -0,0 +1,31 @@ +import { CreateFileError } from "./errors/CreateFileError"; +import { LaunchEditorError } from "./errors/LaunchEditorError"; +import { ReadFileError } from "./errors/ReadFileError"; +import { RemoveFileError } from "./errors/RemoveFileError"; +export interface IEditorParams { + args: string[]; + bin: string; +} +export declare type StringCallback = (err: Error, result: string) => void; +export declare type VoidCallback = () => void; +export { CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError }; +export declare function edit(text?: string): string; +export declare function editAsync(text: string, callback: StringCallback): void; +export declare class ExternalEditor { + text: string; + tempFile: string; + editor: IEditorParams; + lastExitStatus: number; + readonly temp_file: string; + readonly last_exit_status: number; + constructor(text?: string); + run(): string; + runAsync(callback: StringCallback): void; + cleanup(): void; + private determineEditor(); + private createTemporaryFile(); + private readTemporaryFile(); + private removeTemporaryFile(); + private launchEditor(); + private launchEditorAsync(callback); +} diff --git a/main/index.js b/main/index.js index 520c227..7b19a50 100644 --- a/main/index.js +++ b/main/index.js @@ -1,229 +1,163 @@ -// Generated by CoffeeScript 1.12.7 - -/* - ExternalEditor - Kevin Gravier - MIT +"use strict"; +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 */ - -(function() { - var ChatDet, CreateFileError, ExternalEditor, FS, IConvLite, LaunchEditorError, ReadFileError, RemoveFileError, Spawn, SpawnSync, Temp, - bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - FS = require('fs'); - - Temp = require('tmp'); - - SpawnSync = require('child_process').spawnSync; - - Spawn = require('child_process').spawn; - - IConvLite = require('iconv-lite'); - - ChatDet = require('chardet'); - - CreateFileError = require('./errors/CreateFileError'); - - ReadFileError = require('./errors/ReadFileError'); - - RemoveFileError = require('./errors/RemoveFileError'); - - LaunchEditorError = require('./errors/LaunchEditorError'); - - ExternalEditor = (function() { - ExternalEditor.edit = function(text) { - var editor; - if (text == null) { - text = ''; - } - editor = new ExternalEditor(text); - editor.run(); - editor.cleanup(); - return editor.text; - }; - - ExternalEditor.editAsync = function(text, callback) { - var editor; - if (text == null) { - text = ''; - } - editor = new ExternalEditor(text); - return editor.runAsync(function(error_run, text) { - var error_cleanup; - if (!error_run) { - try { - editor.cleanup(); - if (typeof callback === 'function') { - return setImmediate(callback, null, text); +Object.defineProperty(exports, "__esModule", { value: true }); +var chardet_1 = require("chardet"); +var child_process_1 = require("child_process"); +var fs_1 = require("fs"); +var iconv_lite_1 = require("iconv-lite"); +var tmp_1 = require("tmp"); +var CreateFileError_1 = require("./errors/CreateFileError"); +exports.CreateFileError = CreateFileError_1.CreateFileError; +var LaunchEditorError_1 = require("./errors/LaunchEditorError"); +exports.LaunchEditorError = LaunchEditorError_1.LaunchEditorError; +var ReadFileError_1 = require("./errors/ReadFileError"); +exports.ReadFileError = ReadFileError_1.ReadFileError; +var RemoveFileError_1 = require("./errors/RemoveFileError"); +exports.RemoveFileError = RemoveFileError_1.RemoveFileError; +function edit(text) { + if (text === void 0) { text = ""; } + var editor = new ExternalEditor(text); + editor.run(); + editor.cleanup(); + return editor.text; +} +exports.edit = edit; +function editAsync(text, callback) { + if (text === void 0) { text = ""; } + var editor = new ExternalEditor(text); + editor.runAsync(function (err, result) { + if (err) { + setImmediate(callback, err, null); + } + else { + try { + editor.cleanup(); + setImmediate(callback, null, result); } - } catch (error) { - error_cleanup = error; - if (typeof callback === 'function') { - return setImmediate(callback, error_cleanup, null); + catch (cleanupError) { + setImmediate(callback, cleanupError, null); } - } - } else { - if (typeof callback === 'function') { - return setImmediate(callback, error_run, null); - } } - }); - }; - - ExternalEditor.CreateFileError = CreateFileError; - - ExternalEditor.ReadFileError = ReadFileError; - - ExternalEditor.RemoveFileError = RemoveFileError; - - ExternalEditor.LaunchEditorError = LaunchEditorError; - - ExternalEditor.prototype.text = ''; - - ExternalEditor.prototype.temp_file = void 0; - - ExternalEditor.prototype.editor = { - bin: void 0, - args: [] - }; - - ExternalEditor.prototype.last_exit_status = void 0; - - function ExternalEditor(text1) { - this.text = text1 != null ? text1 : ''; - this.launchEditorAsync = bind(this.launchEditorAsync, this); - this.launchEditor = bind(this.launchEditor, this); - this.removeTemporaryFile = bind(this.removeTemporaryFile, this); - this.readTemporaryFile = bind(this.readTemporaryFile, this); - this.createTemporaryFile = bind(this.createTemporaryFile, this); - this.determineEditor = bind(this.determineEditor, this); - this.cleanup = bind(this.cleanup, this); - this.runAsync = bind(this.runAsync, this); - this.run = bind(this.run, this); - this.determineEditor(); - this.createTemporaryFile(); + }); +} +exports.editAsync = editAsync; +var ExternalEditor = /** @class */ (function () { + function ExternalEditor(text) { + if (text === void 0) { text = ""; } + this.text = ""; + this.text = text; + this.determineEditor(); + this.createTemporaryFile(); } - - ExternalEditor.prototype.run = function() { - this.launchEditor(); - return this.readTemporaryFile(); + Object.defineProperty(ExternalEditor.prototype, "temp_file", { + get: function () { + console.log("DEPRECATED: temp_file. Use tempFile moving forward."); + return this.tempFile; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ExternalEditor.prototype, "last_exit_status", { + get: function () { + console.log("DEPRECATED: last_exit_status. Use lastExitStatus moving forward."); + return this.lastExitStatus; + }, + enumerable: true, + configurable: true + }); + ExternalEditor.prototype.run = function () { + this.launchEditor(); + this.readTemporaryFile(); + return this.text; }; - - ExternalEditor.prototype.runAsync = function(callback) { - var error_launch; - try { - return this.launchEditorAsync((function(_this) { - return function() { - var error_read; - try { - _this.readTemporaryFile(); - if (typeof callback === 'function') { - return setImmediate(callback, null, _this.text); - } - } catch (error) { - error_read = error; - if (typeof callback === 'function') { - return setImmediate(callback, error_read, null); - } - } - }; - })(this)); - } catch (error) { - error_launch = error; - if (typeof callback === 'function') { - return setImmediate(callback, error_launch, null); + ExternalEditor.prototype.runAsync = function (callback) { + var _this = this; + try { + this.launchEditorAsync(function () { + try { + _this.readTemporaryFile(); + setImmediate(callback, null, _this.text); + } + catch (readError) { + setImmediate(callback, readError, null); + } + }); + } + catch (launchError) { + setImmediate(callback, launchError, null); } - } }; - - ExternalEditor.prototype.cleanup = function() { - return this.removeTemporaryFile(); + ExternalEditor.prototype.cleanup = function () { + this.removeTemporaryFile(); }; - - ExternalEditor.prototype.determineEditor = function() { - var args, ed, editor; - ed = /^win/.test(process.platform) ? 'notepad' : 'vim'; - editor = process.env.VISUAL || process.env.EDITOR || ed; - args = editor.split(/\s+/); - this.editor.bin = args.shift(); - return this.editor.args = args; + ExternalEditor.prototype.determineEditor = function () { + var editor = process.env.VISUAL ? process.env.VISUAL : + process.env.EDITOR ? process.env.EDITOR : + /^win/.test(process.platform) ? "notepad" : + "vim"; + var editorOpts = editor.split(/\s+/); + var bin = editorOpts.shift(); + this.editor = { args: editorOpts, bin: bin }; }; - - ExternalEditor.prototype.createTemporaryFile = function() { - var e; - try { - this.temp_file = Temp.tmpNameSync({}); - return FS.writeFileSync(this.temp_file, this.text, { - encoding: 'utf8' - }); - } catch (error) { - e = error; - throw new CreateFileError(e); - } + ExternalEditor.prototype.createTemporaryFile = function () { + try { + this.tempFile = tmp_1.tmpNameSync({}); + fs_1.writeFileSync(this.tempFile, this.text, { encoding: "utf8" }); + } + catch (createFileError) { + throw new CreateFileError_1.CreateFileError(createFileError); + } }; - - ExternalEditor.prototype.readTemporaryFile = function() { - var buffer, e, encoding; - try { - buffer = FS.readFileSync(this.temp_file); - if (!buffer.length) { - return this.text = ''; + ExternalEditor.prototype.readTemporaryFile = function () { + try { + var tempFileBuffer = fs_1.readFileSync(this.tempFile); + if (tempFileBuffer.length === 0) { + this.text = ""; + } + else { + var encoding = chardet_1.detect(tempFileBuffer).toString(); + this.text = iconv_lite_1.decode(tempFileBuffer, encoding); + } + } + catch (readFileError) { + throw new ReadFileError_1.ReadFileError(readFileError); } - encoding = ChatDet.detect(buffer); - return this.text = IConvLite.decode(buffer, encoding); - } catch (error) { - e = error; - throw new ReadFileError(e); - } }; - - ExternalEditor.prototype.removeTemporaryFile = function() { - var e; - try { - return FS.unlinkSync(this.temp_file); - } catch (error) { - e = error; - throw new RemoveFileError(e); - } + ExternalEditor.prototype.removeTemporaryFile = function () { + try { + fs_1.unlinkSync(this.tempFile); + } + catch (removeFileError) { + throw new RemoveFileError_1.RemoveFileError(removeFileError); + } }; - - ExternalEditor.prototype.launchEditor = function() { - var e, run; - try { - run = SpawnSync(this.editor.bin, this.editor.args.concat([this.temp_file]), { - stdio: 'inherit' - }); - return this.last_exit_status = run.status; - } catch (error) { - e = error; - throw new LaunchEditorError(e); - } + ExternalEditor.prototype.launchEditor = function () { + try { + var editorProcess = child_process_1.spawnSync(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" }); + this.lastExitStatus = editorProcess.status; + } + catch (launchError) { + throw new LaunchEditorError_1.LaunchEditorError(launchError); + } }; - - ExternalEditor.prototype.launchEditorAsync = function(callback) { - var child_process, e; - try { - child_process = Spawn(this.editor.bin, this.editor.args.concat([this.temp_file]), { - stdio: 'inherit' - }); - return child_process.on('exit', (function(_this) { - return function(code) { - _this.last_exit_status = code; - if (typeof callback === 'function') { - return callback(); - } - }; - })(this)); - } catch (error) { - e = error; - throw new LaunchEditorError(e); - } + ExternalEditor.prototype.launchEditorAsync = function (callback) { + var _this = this; + try { + var editorProcess = child_process_1.spawn(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" }); + editorProcess.on("exit", function (code) { + _this.lastExitStatus = code; + setImmediate(callback); + }); + } + catch (launchError) { + throw new LaunchEditorError_1.LaunchEditorError(launchError); + } }; - return ExternalEditor; - - })(); - - module.exports = ExternalEditor; - -}).call(this); +}()); +exports.ExternalEditor = ExternalEditor; diff --git a/package-lock.json b/package-lock.json index 270d709..5e416b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,116 @@ { "name": "external-editor", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/chai": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", + "integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==", + "dev": true + }, + "@types/chardet": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/chardet/-/chardet-0.5.0.tgz", + "integrity": "sha512-n5dB+Qtllpj36wOgWho8QtB09z0ad8KAihC+WJ6Jd+uQdmoLaBtacp5PlzB6eACwp+BioYI3R91W7FrQGIWMMA==", + "dev": true, + "requires": { + "@types/node": "4.0.42" + } + }, + "@types/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-YeDiSEzznwZwwp766SJ6QlrTyBYUGPSIwmREHVTmktUYiT/WADdWtpt9iH0KuUSf8lZLdI4lP0X6PBzPo5//JQ==", + "dev": true + }, + "@types/node": { + "version": "4.0.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-4.0.42.tgz", + "integrity": "sha512-6A7mraHS24hSAO0SihNWlPwJ51xR0uGpm2XrgNn+5239LH6KXifOCPs81QPBFoVBJG8HwhwKNa4nnOgUEte04w==", + "dev": true + }, + "@types/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "assertion-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", "dev": true }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -27,9 +128,21 @@ } }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "chai": { @@ -46,10 +159,38 @@ "type-detect": "4.0.3" } }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, "chardet": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.0.tgz", - "integrity": "sha1-C74TVaxE16PtSpJXB8TvcPgZD2w=" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", + "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==" }, "check-error": { "version": "1.0.2", @@ -57,42 +198,26 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, - "coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", - "dev": true - }, - "coffeelint": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/coffeelint/-/coffeelint-1.16.0.tgz", - "integrity": "sha1-g9jtHa/eOmd95E57ihi+YHdh5tg=", + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "coffee-script": "1.11.1", - "glob": "7.1.2", - "ignore": "3.3.5", - "optimist": "0.6.1", - "resolve": "0.6.3", - "strip-json-comments": "1.0.4" - }, - "dependencies": { - "coffee-script": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.11.1.tgz", - "integrity": "sha1-vxxHrWREOg2V0S3ysUfMCk2q1uk=", - "dev": true - } + "color-name": "1.1.3" } }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true }, "concat-map": { "version": "0.0.1", @@ -101,9 +226,9 @@ "dev": true }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -124,12 +249,30 @@ "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", "dev": true }, + "es6-shim": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.3.tgz", + "integrity": "sha1-m/tzY/7//4emzbbNk+QF7DxLbyY=", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -156,22 +299,25 @@ "path-is-absolute": "1.0.1" } }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, "he": { @@ -181,15 +327,12 @@ "dev": true }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "ignore": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", - "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==", - "dev": true + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.22.tgz", + "integrity": "sha512-1AinFBeDTnsvVEP+V1QBlHpM1UZZl7gWB6fcz7B1Ho+LI1dUh2sSrxoCfVt2PinRHzXAziSniEV3P7JbTDHcXA==", + "requires": { + "safer-buffer": "2.1.2" + } }, "inflight": { "version": "1.0.6", @@ -207,80 +350,28 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "3.0.1", - "lodash.keys": "3.1.2" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", "dev": true, "requires": { - "lodash._baseassign": "3.2.0", - "lodash._basecreate": "3.0.3", - "lodash._isiterateecall": "3.0.9" + "argparse": "1.0.10", + "esprima": "4.0.0" } }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -290,12 +381,6 @@ "brace-expansion": "1.1.8" } }, - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -314,38 +399,29 @@ } }, "mocha": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", - "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", "dev": true, "requires": { - "browser-stdout": "1.3.0", - "commander": "2.9.0", - "debug": "2.6.8", - "diff": "3.2.0", + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.1", - "growl": "1.9.2", + "glob": "7.1.2", + "growl": "1.10.3", "he": "1.1.1", - "json3": "3.3.2", - "lodash.create": "3.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "3.1.2" + "supports-color": "4.4.0" }, "dependencies": { - "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true } } }, @@ -364,16 +440,6 @@ "wrappy": "1.0.2" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.10", - "wordwrap": "0.0.3" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -385,31 +451,67 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "resolve": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", - "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=", + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "source-map-support": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.5.tgz", + "integrity": "sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "source-map": "0.6.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "2.0.0" } }, "tmp": { @@ -420,16 +522,92 @@ "os-tmpdir": "1.0.2" } }, + "ts-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.0.3.tgz", + "integrity": "sha512-ARaOMNFEPKg2ZuC1qJddFvHxHNFVckR0g9xLxMIoMqSSIkDc8iS4/LoV53EdDWWNq2FGwqcEf0bVVGJIWpsznw==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.4.1", + "diff": "3.2.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.5", + "yn": "2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tslint": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.1", + "commander": "2.15.1", + "diff": "3.2.0", + "glob": "7.1.2", + "js-yaml": "3.11.0", + "minimatch": "3.0.4", + "resolve": "1.7.1", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.26.2" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + } + } + }, + "tsutils": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.26.2.tgz", + "integrity": "sha512-uzwnhmrSbyinPCiwfzGsOY3IulBTwoky7r83HmZdz9QNCjhSCzavkh47KLWuU0zF2F2WbpmmzoJUIEiYyd+jEQ==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, "type-detect": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", "dev": true }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "typescript": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz", + "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", "dev": true }, "wrappy": { @@ -437,6 +615,12 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true } } } diff --git a/package.json b/package.json index 4edc95b..3168d78 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,12 @@ "version": "2.2.0", "description": "Edit a string with the users preferred text editor using $VISUAL or $ENVIRONMENT", "main": "main/index.js", + "types": "main/index.d.ts", "scripts": { "test": "npm run lint && npm run unit", - "unit": "mocha --recursive --compilers coffee:coffee-script/register --timeout 10000 ./test/spec", - "compile": "coffee --compile --output main/ src/", - "lint": "coffeelint -f .coffeelint.json src" + "unit": "mocha --recursive --require ts-node/register --timeout 10000 ./test/spec 'test/spec/**/*.ts'", + "compile": "tsc -p tsconfig.json", + "lint": "tslint './src/**/*.ts' './test/**/*.ts'" }, "repository": { "type": "git", @@ -26,22 +27,36 @@ }, "homepage": "https://github.com/mrkmg/node-external-editor#readme", "dependencies": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.5.0", + "iconv-lite": "^0.4.22", "tmp": "^0.0.33" }, "engines": { - "node": ">=0.12" + "node": ">=4" }, "devDependencies": { + "@types/chai": "^4.1.3", + "@types/chardet": "^0.5.0", + "@types/mocha": "^5.2.0", + "@types/node": "4.0.*", + "@types/tmp": "0.0.33", "chai": "^4.0.0", - "coffee-script": "^1.10.0", - "coffeelint": "^1.14.2", - "mocha": "^3.2.0" + "es6-shim": "^0.35.3", + "mocha": "^5.1.1", + "ts-node": "^6.0.3", + "tslint": "^5.10.0", + "typescript": "^2.8.3" }, "files": [ "main", "example_sync.js", "example_async.js" - ] + ], + "config": { + "ndt": { + "versions": [ + "major" + ] + } + } } diff --git a/src/errors/CreateFileError.coffee b/src/errors/CreateFileError.coffee deleted file mode 100644 index ea895cc..0000000 --- a/src/errors/CreateFileError.coffee +++ /dev/null @@ -1,11 +0,0 @@ -### - ExternalEditor - Kevin Gravier - MIT -### - -class CreateFileError extends Error - message: 'Failed to create temporary file for editor' - constructor: (@original_error) -> - -module.exports = CreateFileError diff --git a/src/errors/CreateFileError.ts b/src/errors/CreateFileError.ts new file mode 100644 index 0000000..f9bd403 --- /dev/null +++ b/src/errors/CreateFileError.ts @@ -0,0 +1,20 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +export class CreateFileError extends Error { + constructor(public originalError: Error) { + super("Failed to create temporary file for editor"); + + const proto = new.target.prototype; + if ((Object as any).setPrototypeOf) { + (Object as any).setPrototypeOf(this, proto); + } else { + (this as any).__proto__ = new.target.prototype; + + } + } +} diff --git a/src/errors/LaunchEditorError.coffee b/src/errors/LaunchEditorError.coffee deleted file mode 100644 index 5e75366..0000000 --- a/src/errors/LaunchEditorError.coffee +++ /dev/null @@ -1,11 +0,0 @@ -### - ExternalEditor - Kevin Gravier - MIT -### - -class LaunchEditorError extends Error - message: 'Failed launch editor' - constructor: (@original_error) -> - -module.exports = LaunchEditorError diff --git a/src/errors/LaunchEditorError.ts b/src/errors/LaunchEditorError.ts new file mode 100644 index 0000000..5bb305f --- /dev/null +++ b/src/errors/LaunchEditorError.ts @@ -0,0 +1,20 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +export class LaunchEditorError extends Error { + constructor(public originalError: Error) { + super("Failed launch editor"); + + const proto = new.target.prototype; + if ((Object as any).setPrototypeOf) { + (Object as any).setPrototypeOf(this, proto); + } else { + (this as any).__proto__ = new.target.prototype; + + } + } +} diff --git a/src/errors/ReadFileError.coffee b/src/errors/ReadFileError.coffee deleted file mode 100644 index d7646de..0000000 --- a/src/errors/ReadFileError.coffee +++ /dev/null @@ -1,11 +0,0 @@ -### - ExternalEditor - Kevin Gravier - MIT -### - -class ReadFileError extends Error - message: 'Failed to read temporary file' - constructor: (@original_error) -> - -module.exports = ReadFileError diff --git a/src/errors/ReadFileError.ts b/src/errors/ReadFileError.ts new file mode 100644 index 0000000..67ab177 --- /dev/null +++ b/src/errors/ReadFileError.ts @@ -0,0 +1,20 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +export class ReadFileError extends Error { + constructor(public originalError: Error) { + super("Failed to read temporary file"); + + const proto = new.target.prototype; + if ((Object as any).setPrototypeOf) { + (Object as any).setPrototypeOf(this, proto); + } else { + (this as any).__proto__ = new.target.prototype; + + } + } +} diff --git a/src/errors/RemoveFileError.coffee b/src/errors/RemoveFileError.coffee deleted file mode 100644 index 5527cf0..0000000 --- a/src/errors/RemoveFileError.coffee +++ /dev/null @@ -1,11 +0,0 @@ -### - ExternalEditor - Kevin Gravier - MIT -### - -class RemoveFileError extends Error - message: 'Failed to cleanup temporary file' - constructor: (@original_error) -> - -module.exports = RemoveFileError diff --git a/src/errors/RemoveFileError.ts b/src/errors/RemoveFileError.ts new file mode 100644 index 0000000..743f138 --- /dev/null +++ b/src/errors/RemoveFileError.ts @@ -0,0 +1,20 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +export class RemoveFileError extends Error { + constructor(public originalError: Error) { + super("Failed to cleanup temporary file"); + + const proto = new.target.prototype; + if ((Object as any).setPrototypeOf) { + (Object as any).setPrototypeOf(this, proto); + } else { + (this as any).__proto__ = new.target.prototype; + + } + } +} diff --git a/src/index.coffee b/src/index.coffee deleted file mode 100644 index b13974a..0000000 --- a/src/index.coffee +++ /dev/null @@ -1,118 +0,0 @@ -### - ExternalEditor - Kevin Gravier - MIT -### - -FS = require 'fs' -Temp = require 'tmp' -SpawnSync = require('child_process').spawnSync -Spawn = require('child_process').spawn -IConvLite = require 'iconv-lite' -ChatDet = require 'chardet' - -CreateFileError = require './errors/CreateFileError' -ReadFileError = require './errors/ReadFileError' -RemoveFileError = require './errors/RemoveFileError' -LaunchEditorError = require './errors/LaunchEditorError' - -class ExternalEditor - @edit: (text = '') -> - editor = new ExternalEditor(text) - editor.run() - editor.cleanup() - editor.text - - @editAsync: (text = '', callback) -> - editor = new ExternalEditor(text) - editor.runAsync (error_run, text) -> - if not error_run - try - editor.cleanup() - setImmediate(callback, null, text) if typeof callback is 'function' - catch error_cleanup - setImmediate(callback, error_cleanup, null) if typeof callback is 'function' - else - setImmediate(callback, error_run, null) if typeof callback is 'function' - - - @CreateFileError: CreateFileError - @ReadFileError: ReadFileError - @RemoveFileError: RemoveFileError - @LaunchEditorError: LaunchEditorError - - text: '' - temp_file: undefined - editor: - bin: undefined - args: [] - last_exit_status: undefined - - constructor: (@text = '') -> - @determineEditor() - @createTemporaryFile() - - run: => - @launchEditor() - @readTemporaryFile() - - runAsync: (callback) => - try - @launchEditorAsync => - try - @readTemporaryFile() - setImmediate(callback, null, @text) if typeof callback is 'function' - catch error_read - setImmediate(callback, error_read, null) if typeof callback is 'function' - catch error_launch - setImmediate(callback, error_launch, null) if typeof callback is 'function' - - cleanup: => - @removeTemporaryFile() - - determineEditor: => - ed = if /^win/.test process.platform then 'notepad' else 'vim' - editor = process.env.VISUAL or process.env.EDITOR or ed - args = editor.split /\s+/ - @editor.bin = args.shift() - @editor.args = args - - createTemporaryFile: => - try - @temp_file = Temp.tmpNameSync {} - FS.writeFileSync @temp_file, @text, encoding: 'utf8' - catch e - throw new CreateFileError e - - readTemporaryFile: => - try - buffer = FS.readFileSync(@temp_file) - return @text = '' unless buffer.length - encoding = ChatDet.detect buffer - @text = IConvLite.decode buffer, encoding - catch e - throw new ReadFileError e - - removeTemporaryFile: => - try - FS.unlinkSync(@temp_file) - catch e - throw new RemoveFileError e - - launchEditor: => - try - run = SpawnSync @editor.bin, @editor.args.concat([@temp_file]), stdio: 'inherit' - @last_exit_status = run.status - catch e - throw new LaunchEditorError e - - launchEditorAsync: (callback) => - try - child_process = Spawn @editor.bin, @editor.args.concat([@temp_file]), stdio: 'inherit' - child_process.on 'exit', (code) => - @last_exit_status = code - callback() if typeof callback is 'function' - catch e - throw new LaunchEditorError e - -module.exports = ExternalEditor diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..68ec0f0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,168 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +import {detect} from "chardet"; +import {spawn, spawnSync} from "child_process"; +import {readFileSync, unlinkSync, writeFileSync} from "fs"; +import {decode} from "iconv-lite"; +import {tmpNameSync} from "tmp"; +import {CreateFileError} from "./errors/CreateFileError"; +import {LaunchEditorError} from "./errors/LaunchEditorError"; +import {ReadFileError} from "./errors/ReadFileError"; +import {RemoveFileError} from "./errors/RemoveFileError"; + +export interface IEditorParams { + args: string[]; + bin: string; +} + +export type StringCallback = (err: Error, result: string) => void; +export type VoidCallback = () => void; +export {CreateFileError, LaunchEditorError, ReadFileError, RemoveFileError}; + +export function edit(text: string = "") { + const editor = new ExternalEditor(text); + editor.run(); + editor.cleanup(); + return editor.text; +} + +export function editAsync(text: string = "", callback: StringCallback) { + const editor = new ExternalEditor(text); + editor.runAsync((err: Error, result: string) => { + if (err) { + setImmediate(callback, err, null); + } else { + try { + editor.cleanup(); + setImmediate(callback, null, result); + } catch (cleanupError) { + setImmediate(callback, cleanupError, null); + } + } + }); + +} + +export class ExternalEditor { + public text: string = ""; + public tempFile: string; + public editor: IEditorParams; + public lastExitStatus: number; + + public get temp_file() { + console.log("DEPRECATED: temp_file. Use tempFile moving forward."); + return this.tempFile; + } + + public get last_exit_status() { + console.log("DEPRECATED: last_exit_status. Use lastExitStatus moving forward."); + return this.lastExitStatus; + } + + constructor(text: string = "") { + this.text = text; + this.determineEditor(); + this.createTemporaryFile(); + } + + public run() { + this.launchEditor(); + this.readTemporaryFile(); + return this.text; + } + + public runAsync(callback: StringCallback) { + try { + this.launchEditorAsync(() => { + try { + this.readTemporaryFile(); + setImmediate(callback, null, this.text); + } catch (readError) { + setImmediate(callback, readError, null); + } + }); + } catch (launchError) { + setImmediate(callback, launchError, null); + } + } + + public cleanup() { + this.removeTemporaryFile(); + } + + private determineEditor() { + const editor = + process.env.VISUAL ? process.env.VISUAL : + process.env.EDITOR ? process.env.EDITOR : + /^win/.test(process.platform) ? "notepad" : + "vim"; + + const editorOpts = editor.split(/\s+/); + const bin = editorOpts.shift(); + this.editor = {args: editorOpts, bin}; + } + + private createTemporaryFile() { + try { + this.tempFile = tmpNameSync({}); + writeFileSync(this.tempFile, this.text, {encoding: "utf8"}); + } catch (createFileError) { + throw new CreateFileError(createFileError); + } + } + + private readTemporaryFile() { + try { + const tempFileBuffer = readFileSync(this.tempFile); + if (tempFileBuffer.length === 0) { + this.text = ""; + } else { + const encoding = detect(tempFileBuffer).toString(); + this.text = decode(tempFileBuffer, encoding); + } + } catch (readFileError) { + throw new ReadFileError(readFileError); + } + + } + + private removeTemporaryFile() { + try { + unlinkSync(this.tempFile); + } catch (removeFileError) { + throw new RemoveFileError(removeFileError); + } + } + + private launchEditor() { + try { + const editorProcess = spawnSync( + this.editor.bin, + this.editor.args.concat([this.tempFile]), + {stdio: "inherit"}); + this.lastExitStatus = editorProcess.status; + } catch (launchError) { + throw new LaunchEditorError(launchError); + } + } + + private launchEditorAsync(callback: VoidCallback) { + try { + const editorProcess = spawn( + this.editor.bin, + this.editor.args.concat([this.tempFile]), + {stdio: "inherit"}); + editorProcess.on("exit", (code: number) => { + this.lastExitStatus = code; + setImmediate(callback); + }); + } catch (launchError) { + throw new LaunchEditorError(launchError); + } + } +} diff --git a/test/spec/main.coffee b/test/spec/main.coffee deleted file mode 100644 index 0bd07cd..0000000 --- a/test/spec/main.coffee +++ /dev/null @@ -1,115 +0,0 @@ -assert = require('chai').assert -readFileSync = require('fs').readFileSync -writeFileSync = require('fs').writeFileSync -IConvLite = require 'iconv-lite' -ExternalEditor = require('../../src') - -testingInput = 'aAbBcCdDeEfFgG' -expectedResult = 'aAbBcCdDeE' - -describe 'main', -> - before -> - @previous_visual = process.env.VISUAL - process.env.VISUAL = 'truncate --size 10' - - beforeEach -> - @editor = new ExternalEditor testingInput - - afterEach -> - @editor.cleanup() - - after -> - process.env.VISUAL = @previous_visual - - it 'convenience method ".edit"', -> - text = ExternalEditor.edit testingInput - assert.equal text, expectedResult - - it 'convenience method ".editAsync"', (cb) -> - ExternalEditor.editAsync testingInput, (e, text) -> - assert.equal text, expectedResult - cb() - - it 'writes original text to file', -> - contents = readFileSync @editor.temp_file - assert.equal contents, testingInput - - it 'run() returns correctly', -> - text = @editor.run() - assert.equal text, expectedResult - assert.equal @editor.last_exit_status, 0 - - it 'runAsync() callbacks correctly', (cb) -> - ed = @editor - @editor.runAsync (e, text) -> - assert.equal text, expectedResult - assert.equal ed.last_exit_status, 0 - cb() - - it 'run() returns text same as editor.text', -> - text = @editor.run() - assert.equal text, @editor.text - - it 'runAsync() callback text same as editor.text', (cb) -> - @editor.runAsync (e, text) => - assert.equal text, @editor.text - cb() - -describe 'invalid exit code', -> - - beforeEach -> - @editor = new ExternalEditor testingInput - @editor.editor.bin = "bash" - @editor.editor.args = ["-c", "exit 1"] - - afterEach -> - @editor.cleanup() - - it 'run()', -> - @editor.run() - assert.equal @editor.last_exit_status, 1 - - it 'runAsync()', (cb) -> - @editor.runAsync => - assert.equal @editor.last_exit_status, 1 - cb() - -describe 'charsets', -> - before -> - @previous_visual = process.env.VISUAL - process.env.VISUAL = 'true' - - beforeEach -> - @editor = new ExternalEditor 'XXX' - - afterEach -> - @editor.cleanup() - - after -> - process.env.VISUAL = @previous_visual - - it 'empty', -> - writeFileSync(@editor.temp_file, '') - text = @editor.run() - assert.equal text, '' - - it 'utf8', -> - writeFileSync(@editor.temp_file, IConvLite.encode('काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥', 'utf8'), encoding: 'binary') - text = @editor.run() - assert.equal text, 'काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥' - - it 'utf16', -> - writeFileSync(@editor.temp_file, IConvLite.encode('काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥', 'utf16'), encoding: 'binary') - text = @editor.run() - assert.equal text, 'काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥' - - it 'win1252', -> - writeFileSync(@editor.temp_file, IConvLite.encode('Testing 1 2 3 ! @ #', 'win1252'), encoding: 'binary') - text = @editor.run() - assert.equal text, 'Testing 1 2 3 ! @ #' - - it 'Big5', -> - writeFileSync(@editor.temp_file, IConvLite.encode('能 脊 胼 胯 臭 臬 舀 舐 航 舫 舨 般 芻 茫 荒 荔', 'Big5'), encoding: 'binary') - text = @editor.run() - assert.equal text, '能 脊 胼 胯 臭 臬 舀 舐 航 舫 舨 般 芻 茫 荒 荔' - diff --git a/test/spec/main.spec.ts b/test/spec/main.spec.ts new file mode 100644 index 0000000..e519ba5 --- /dev/null +++ b/test/spec/main.spec.ts @@ -0,0 +1,170 @@ +/*** + * Node External Editor + * + * Kevin Gravier + * MIT 2018 + */ + +// tslint:disable-next-line:no-var-requires +require("es6-shim"); + +import Chai = require("chai"); +import {readFileSync, writeFileSync} from "fs"; +import {encode} from "iconv-lite"; +import {edit, editAsync, ExternalEditor} from "../../src"; +const assert = Chai.assert; + +const testingInput = "aAbBcCdDeEfFgG"; +const expectedResult = "aAbBcCdDeE"; + +describe("main", () => { + let previousVisual: string; + let editor: ExternalEditor; + + before(() => { + previousVisual = process.env.VISUAL; + process.env.VISUAL = "truncate --size 10"; + }); + + beforeEach(() => { + editor = new ExternalEditor(testingInput); + }); + + afterEach(() => { + editor.cleanup(); + }); + + after(() => { + process.env.VISUAL = previousVisual; + }); + + it("convenience method \".edit\"", () => { + const text = edit(testingInput); + assert.equal(text, expectedResult); + }); + + it("convenience method \".editAsync\"", (cb) => { + editAsync(testingInput, (e, text) => { + assert.equal(text, expectedResult); + cb(); + }); + }); + + it("writes original text to file", () => { + const contents = readFileSync(editor.tempFile).toString(); + assert.equal(contents, testingInput); + }); + + it("run() returns correctly", () => { + const text = editor.run(); + assert.equal(text, expectedResult); + assert.equal(editor.lastExitStatus, 0); + }); + + it("runAsync() callbacks correctly", (cb) => { + editor.runAsync((e, text) => { + assert.equal(text, expectedResult); + assert.equal(editor.lastExitStatus, 0); + cb(); + }); + }); + + it("run() returns text same as editor.text", () => { + const text = editor.run(); + assert.equal(text, editor.text); + }); + + it("runAsync() callback text same as editor.text", (cb) => { + editor.runAsync((e, text) => { + assert.equal(text, editor.text); + cb(); + }); + }); +}); + +describe("invalid exit code", () => { + let editor: ExternalEditor; + + beforeEach(() => { + editor = new ExternalEditor(testingInput); + editor.editor.bin = "bash"; + editor.editor.args = ["-c", "exit 1"]; + }); + + afterEach(() => { + editor.cleanup(); + }); + + it("run()", () => { + editor.run(); + assert.equal(editor.lastExitStatus, 1); + }); + + it("runAsync()", (cb) => { + editor.runAsync((e, text) => { + assert.equal(editor.lastExitStatus, 1); + cb(); + }); + }); +}); + +describe("charsets", () => { + let previousVisual: string; + let editor: ExternalEditor; + + before(() => { + previousVisual = process.env.VISUAL; + process.env.VISUAL = "true"; + }); + + beforeEach(() => { + editor = new ExternalEditor("XXX"); + }); + + afterEach(() => { + editor.cleanup(); + }); + + after(() => { + process.env.VISUAL = previousVisual; + }); + + it("empty", () => { + writeFileSync(editor.tempFile, ""); + const text = editor.run(); + assert.equal(text, ""); + }); + + it("utf8", () => { + const testData = "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥"; + const textEncoding = "utf8"; + writeFileSync(editor.tempFile, encode(testData, textEncoding), {encoding: "binary"}); + const result = editor.run(); + assert.equal(testData, result); + }); + + it("utf16", () => { + const testData = "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥"; + const textEncoding = "utf16"; + writeFileSync(editor.tempFile, encode(testData, textEncoding), {encoding: "binary"}); + const result = editor.run(); + assert.equal(testData, result); + }); + + it("win1252", () => { + const testData = "Testing 1 2 3 ! @ #"; + const textEncoding = "win1252"; + writeFileSync(editor.tempFile, encode(testData, textEncoding), {encoding: "binary"}); + const result = editor.run(); + assert.equal(testData, result); + }); + + it("Big5", () => { + const testData = "能 脊 胼 胯 臭 臬 舀 舐 航 舫 舨 般 芻 茫 荒 荔"; + const textEncoding = "Big5"; + writeFileSync(editor.tempFile, encode(testData, textEncoding), {encoding: "binary"}); + const result = editor.run(); + assert.equal(testData, result); + }); + +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b6a40bf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "target": "es5", + "noImplicitAny": true, + "removeComments": false, + "preserveConstEnums": true, + "outDir": "main/", + "sourceMap": false, + "declaration": true + }, + "include": [ + "src/**/*", + "types/**/*" + ], + "exclude": [ + "node_modules", + "**/*.spec.ts" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..86bbb3a --- /dev/null +++ b/tslint.json @@ -0,0 +1,11 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "no-console": false + }, + "rulesDirectory": [] +} From 71a08863ecbf5170cf539f7510d8c783b918aa1e Mon Sep 17 00:00:00 2001 From: Kevin Gravier Date: Mon, 7 May 2018 16:49:37 -0400 Subject: [PATCH 2/2] Release 3.0.0 Converted to use TypeScript Changed to named exports/imports Dropped NodeJS 0.12 support --- README.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3bc38fb..f6a33d3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A node module to edit a string with a users preferred text editor using $VISUAL or $ENVIRONMENT. -Version: 2.2.0 +Version: 3.0.0 As of version 3.0.0, the minimum version of node supported is 4. diff --git a/package-lock.json b/package-lock.json index 5e416b2..9d797ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "external-editor", - "version": "2.2.0", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3168d78..045ee9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "external-editor", - "version": "2.2.0", + "version": "3.0.0", "description": "Edit a string with the users preferred text editor using $VISUAL or $ENVIRONMENT", "main": "main/index.js", "types": "main/index.d.ts",