From c331137a43011c501120d8b5719f55a972153033 Mon Sep 17 00:00:00 2001 From: Steve King Date: Fri, 19 Jun 2020 09:20:36 +0100 Subject: [PATCH] Add necessary properties to the `module.exports` of `simple-git` to allow it to be used as an ES module with a default value. - moves the `@kwsites/file-exists` mock out to the helpers file --- CHANGELOG.md | 6 ++++ src/git-factory.js | 13 +++++++ src/index.js | 6 ++-- src/lib/responses/GetRemoteSummary.ts | 12 ++----- src/lib/utils/util.ts | 16 +++++++-- test/helpers.js | 18 +++++++++- test/unit/include/setup.js | 25 ++++---------- test/unit/utils.spec.js | 50 +++++++++++++++++++++++++-- 8 files changed, 111 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68ffffdb..4b9715e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ +## 2.8.0 - Support for `default` import in TS without use of `esModuleInterop` + +- Enables support for using the default export of `simple-git` as an es module, in TypeScript it is no + longer necessary to enable the `esModuleInterop` flag in the `tsconfig.json` to consume the default + export. + ## 2.7.2 - Bug Fix: Remove `promise.ts` source from `simple-git` published artifact - Closes #471, whereby the source for the promise wrapped runner would be included in the published artifact diff --git a/src/git-factory.js b/src/git-factory.js index 37a4a8e1..0d480ae8 100644 --- a/src/git-factory.js +++ b/src/git-factory.js @@ -9,6 +9,19 @@ for (let imported = require('./lib/api'), keys = Object.keys(imported), i = 0; i } } +/** + * Adds the necessary properties to the supplied object to enable it for use as + * the default export of a module. + * + * Eg: `module.exports = esModuleFactory({ something () {} })` + */ +module.exports.esModuleFactory = function esModuleFactory(defaultExport) { + return Object.defineProperties(defaultExport, { + __esModule: { value: true }, + default: { value: defaultExport }, + }); +} + module.exports.gitExportFactory = function gitExportFactory (factory, extra) { return Object.assign(function () { return factory.apply(null, arguments); diff --git a/src/index.js b/src/index.js index b0bc750f..2587f357 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,7 @@ const {gitP} = require('./lib/runners/promise-wrapped'); -const {gitInstanceFactory, gitExportFactory} = require('./git-factory'); +const {esModuleFactory, gitInstanceFactory, gitExportFactory} = require('./git-factory'); -module.exports = gitExportFactory(gitInstanceFactory, {gitP}); +module.exports = esModuleFactory( + gitExportFactory(gitInstanceFactory, {gitP}) +); diff --git a/src/lib/responses/GetRemoteSummary.ts b/src/lib/responses/GetRemoteSummary.ts index 2aaa8019..45b16574 100644 --- a/src/lib/responses/GetRemoteSummary.ts +++ b/src/lib/responses/GetRemoteSummary.ts @@ -1,3 +1,4 @@ +import { forEachLineWithContent } from '../utils'; export interface RemoteWithoutRefs { name: string; @@ -38,14 +39,5 @@ export function parseGetRemotesVerbose (text: string): RemoteWithRefs[] { } function forEach(text: string, handler: (line: string[]) => void) { - text.split('\n').forEach(line => { - const row = line.trim(); - if (!row) { - return; - } - - if (row) { - handler(row.split(/\s+/)); - } - }); + forEachLineWithContent(text, (line) => handler(line.split(/\s+/))); } diff --git a/src/lib/utils/util.ts b/src/lib/utils/util.ts index 526fb2df..044a03b5 100644 --- a/src/lib/utils/util.ts +++ b/src/lib/utils/util.ts @@ -35,7 +35,7 @@ export function last(input: T[]): T | undefined { return input && input.length ? input[input.length - 1] : undefined; } -export function toLinesWithContent(input: string, trimmed = false): string[] { +export function toLinesWithContent(input: string, trimmed = true): string[] { return input.split('\n') .reduce((output, line) => { const lineContent = trimmed ? line.trim() : line; @@ -46,13 +46,25 @@ export function toLinesWithContent(input: string, trimmed = false): string[] { }, [] as string[]); } +type LineWithContentCallback = (line: string) => T; +type LineWithContentTrailingArgs = [LineWithContentCallback] | [boolean, LineWithContentCallback]; +export function forEachLineWithContent(input: string, ...opt: LineWithContentTrailingArgs): T[] { + const [trimmed, callback] = opt.length === 1 ? [undefined, opt[0]] : opt; + return toLinesWithContent(input, trimmed).map(line => callback(line)); +} + export function folderExists(path: string): boolean { return exists(path, FOLDER); } +/** + * Adds `item` into the `target` `Array` or `Set` when it is not already present. + */ export function append(target: T[] | Set, item: T): typeof item { if (Array.isArray(target)) { - target.push(item); + if (!target.includes(item)) { + target.push(item); + } } else { target.add(item); diff --git a/test/helpers.js b/test/helpers.js index d8dfdbc0..aa0dac5d 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,4 +1,3 @@ -import { NOOP } from "../src/lib/utils"; module.exports.autoMergeFile = (fileName = 'pass.txt') => { return ` @@ -158,3 +157,20 @@ module.exports.mockDebugModule = (function mockDebugModule () { return debug; }()); + +module.exports.mockFileExistsModule = (function mockFileExistsModule () { + let next = true; + + return { + $fails () { + next = false; + }, + $reset () { + next = true; + }, + exists () { + return next; + }, + FOLDER: 2, + }; +}()); diff --git a/test/unit/include/setup.js b/test/unit/include/setup.js index 91c29387..080e39b3 100644 --- a/test/unit/include/setup.js +++ b/test/unit/include/setup.js @@ -1,7 +1,11 @@ (function () { 'use strict'; - const { mockDebugModule } = require('../../helpers'); + const { mockDebugModule, mockFileExistsModule } = require('../../helpers'); + const onRestore = [ + mockDebugModule.$reset, + mockFileExistsModule.$reset, + ]; jest.mock('child_process', () => { return new MockChildProcess(true); @@ -9,22 +13,7 @@ jest.mock('debug', () => mockDebugModule); - jest.mock('@kwsites/file-exists', () => { - let next = true; - - return { - $fails () { - next = false; - }, - $reset () { - next = true; - }, - exists () { - return next; - }, - FOLDER: 2, - }; - }); + jest.mock('@kwsites/file-exists', () => mockFileExistsModule); var mockChildProcess, mockChildProcesses = [], git; var sinon = require('sinon'); @@ -189,7 +178,7 @@ sandbox.restore(); } - tryCalling(require('debug').$reset); + onRestore.forEach(tryCalling); }, wait, diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js index 5eca666b..fe3791cd 100644 --- a/test/unit/utils.spec.js +++ b/test/unit/utils.spec.js @@ -1,14 +1,60 @@ import { + append, filterArray, filterFunction, filterHasLength, filterPlainObject, - filterPrimitives, - NOOP + filterPrimitives, forEachLineWithContent, + NOOP, toLinesWithContent } from "../../src/lib/utils"; describe('utils', () => { + describe('content', () => { + + it('filters lines with content', () => { + expect(toLinesWithContent(' \n content \n\n')).toEqual(['content']); + expect(toLinesWithContent(' \n content \n\n', false)).toEqual([' ', ' content ']); + }); + + it('maps lines with content', () => { + expect(forEachLineWithContent(' \n content \n\n', (line) => line.toUpperCase())) + .toEqual(['CONTENT']); + expect(forEachLineWithContent(' \n content \n\n', true, (line) => line.toUpperCase())) + .toEqual(['CONTENT']); + expect(forEachLineWithContent(' \n content \n\n', false, (line) => line.toUpperCase())) + .toEqual([' ', ' CONTENT ']); + }); + + }); + + describe('arrays', () => { + + function test (target, itemA, itemB) { + expect(append(target, itemA)).toBe(itemA); + expect(Array.from(target)).toEqual([itemA]); + + expect(append(target, itemB)).toBe(itemB); + expect(Array.from(target)).toEqual([itemA, itemB]); + + expect(append(target, itemA)).toBe(itemA); + expect(Array.from(target)).toEqual([itemA, itemB]); + } + + it('appends objects into an array', () => { + test([], {a: 1}, {b: 1}); + }); + it('appends primitives into an array', () => { + test([], 'A', 'B'); + }); + it('appends objects into a set', () => { + test(new Set(), {a: 1}, {b: 1}); + }); + it('appends primitives into a set', () => { + test(new Set(), 'A', 'B'); + }); + }); + describe('argument filtering', () => { it('recognises arrays', () => {