Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wrap comments with words > 80 chars correctly #232

Merged
merged 2 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,21 @@ export const extendArray = <T>(arr1: T[], arr2: T[]): T[] => {
return arr1;
};

const earliest = (a: number, b: number) => {
if (a === -1 && b === -1) return 0;
erickzhao marked this conversation as resolved.
Show resolved Hide resolved
if (a === -1) return b;
if (b === -1) return a;
return Math.min(a, b);
};

export const wrapComment = (comment: string, additionalTags: DocumentationTag[] = []): string[] => {
if (!comment && !additionalTags.length) return [];
comment = comment.replace(/^\(optional\)(?: - )?/gi, '');
if (!comment && !additionalTags.length) return [];
const result = ['/**'];
while (comment.length > 0) {
let index = 0;
// Default the cut point to be the first "space" or "newline" character
let index = earliest(comment.indexOf(' '), comment.indexOf('\n'));
erickzhao marked this conversation as resolved.
Show resolved Hide resolved
for (let i = 0; i <= 80; i++) {
if (comment[i] === ' ') index = i;
if (comment[i] === '\n') {
Expand All @@ -44,6 +52,11 @@ export const wrapComment = (comment: string, additionalTags: DocumentationTag[]
if (comment.length <= 80 && !comment.includes('\n')) {
index = 80;
}
// If we didn't find a good cut point (i.e. there isn't a good cut point anywhere)
// then let's just take the whole thing it's probably one long word
if (index === 0) {
index = comment.length;
}
result.push(` * ${comment.substring(0, index)}`);
comment = comment.substring(index + 1);
}
Expand Down
150 changes: 81 additions & 69 deletions test/utils_spec.js
Original file line number Diff line number Diff line change
@@ -1,123 +1,135 @@
const expect = require('chai').expect
const utils = require('../dist/utils')
const expect = require('chai').expect;
const utils = require('../dist/utils');

describe('utils', () => {
describe('extendArray', () => {
it('should return an array with all elements added correctly', () => {
expect(utils.extendArray(['foo'], ['bar'])).to.deep.equal(['foo', 'bar'])
})
expect(utils.extendArray(['foo'], ['bar'])).to.deep.equal(['foo', 'bar']);
});

it('should return an array with all elements added in the correct oreder', () => {
expect(utils.extendArray([1, 2, 3, 4], [2, 3, 4, 5])).to.deep.equal([1, 2, 3, 4, 2, 3, 4, 5])
})
expect(utils.extendArray([1, 2, 3, 4], [2, 3, 4, 5])).to.deep.equal([1, 2, 3, 4, 2, 3, 4, 5]);
});

it('should mutate the original array', () => {
const primary = [1, 5, 9]
const secondary = [2, 6, 10]
utils.extendArray(primary, secondary)
expect(primary).to.deep.equal([1, 5, 9, 2, 6, 10])
})
})
const primary = [1, 5, 9];
const secondary = [2, 6, 10];
utils.extendArray(primary, secondary);
expect(primary).to.deep.equal([1, 5, 9, 2, 6, 10]);
});
});

describe('wrapComment', () => {
it('should return an array', () => {
expect(utils.wrapComment('Foo Bar')).to.be.a('array')
})
expect(utils.wrapComment('Foo Bar')).to.be.a('array');
});

it('should be a correctly formatted JS multi-line comment', () => {
const wrapped = utils.wrapComment('Foo bar')
expect(wrapped[0]).to.be.equal('/**')
expect(wrapped[wrapped.length - 1]).to.be.equal(' */')
})
const wrapped = utils.wrapComment('Foo bar');
expect(wrapped[0]).to.be.equal('/**');
expect(wrapped[wrapped.length - 1]).to.be.equal(' */');
});

it('should wrap each line to be a max of 80 chars', () => {
const reallyLongString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pulvinar nibh eu orci fringilla interdum. In mi arcu, accumsan nec justo eget, pharetra egestas mauris. Quisque nisl tellus, sagittis lobortis commodo nec, tincidunt a arcu. Donec congue lacus a lacus euismod, in hendrerit nunc faucibus. Praesent ac libero eros. Nunc lorem turpis, elementum vel pellentesque vitae, aliquet et erat. In tempus, nulla vitae cursus congue, massa dui pretium eros, eget ornare ipsum diam a velit. Aliquam ac iaculis dui. Phasellus mollis augue volutpat turpis posuere scelerisque. Donec a rhoncus nisl, eu viverra massa. Suspendisse rutrum fermentum diam, posuere tempus turpis accumsan in. Pellentesque commodo in leo vitae aliquet. Vestibulum id justo ac odio mollis fringilla ac a odio. Quisque rhoncus pretium risus, tristique convallis urna.'
const wrapped = utils.wrapComment(reallyLongString)
const reallyLongString =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pulvinar nibh eu orci fringilla interdum. In mi arcu, accumsan nec justo eget, pharetra egestas mauris. Quisque nisl tellus, sagittis lobortis commodo nec, tincidunt a arcu. Donec congue lacus a lacus euismod, in hendrerit nunc faucibus. Praesent ac libero eros. Nunc lorem turpis, elementum vel pellentesque vitae, aliquet et erat. In tempus, nulla vitae cursus congue, massa dui pretium eros, eget ornare ipsum diam a velit. Aliquam ac iaculis dui. Phasellus mollis augue volutpat turpis posuere scelerisque. Donec a rhoncus nisl, eu viverra massa. Suspendisse rutrum fermentum diam, posuere tempus turpis accumsan in. Pellentesque commodo in leo vitae aliquet. Vestibulum id justo ac odio mollis fringilla ac a odio. Quisque rhoncus pretium risus, tristique convallis urna.';
const wrapped = utils.wrapComment(reallyLongString);
wrapped.forEach(line => {
// Subtract 3 due to commend prefix " * "
expect(line.length - 3).to.be.lte(80)
})
})
expect(line.length - 3).to.be.lte(80);
});
});

it('should not split words unless it needs to', () => {
const wrapped = utils.wrapComment('Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword')
const wrapped = utils.wrapComment(
'Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword Thisisalongword',
);
wrapped.forEach((line, index) => {
if (index === 0 || index === wrapped.length - 1) return
expect(line.endsWith('Thisisalongword')).to.eq(true)
})
})
})
if (index === 0 || index === wrapped.length - 1) return;
expect(line.endsWith('Thisisalongword')).to.eq(true);
});
});

it('should handle long urls and not create needless empty lines', () => {
const wrapped = utils.wrapComment(
'Unregisters the app from notifications received from APNS. See: https://developer.apple.com/documentation/appkit/nsapplication/1428747-unregisterforremotenotifications?language=objc',
);
expect(wrapped.length).equal(4);
});
});

describe('typify', () => {
it('should lower case known types', () => {
expect(utils.typify('String')).to.equal('string')
expect(utils.typify('Number')).to.equal('number')
})
expect(utils.typify('String')).to.equal('string');
expect(utils.typify('Number')).to.equal('number');
});

it('should convert specific number types to typescript types', () => {
expect(utils.typify('Integer')).to.equal('number')
expect(utils.typify('Float')).to.equal('number')
expect(utils.typify('Double')).to.equal('number')
expect(utils.typify('Number')).to.equal('number')
})
expect(utils.typify('Integer')).to.equal('number');
expect(utils.typify('Float')).to.equal('number');
expect(utils.typify('Double')).to.equal('number');
expect(utils.typify('Number')).to.equal('number');
});

it('should correctly convert a void function', () => {
expect(utils.typify('VoidFunction')).to.equal('(() => void)')
})
expect(utils.typify('VoidFunction')).to.equal('(() => void)');
});

it('should lower case known array types', () => {
expect(utils.typify('String[]')).to.equal('string[]')
expect(utils.typify('Number[]')).to.equal('number[]')
})
expect(utils.typify('String[]')).to.equal('string[]');
expect(utils.typify('Number[]')).to.equal('number[]');
});

it('should map an array of types through typify as well', () => {
expect(utils.typify(['String', 'Float', 'Boolean'])).to.deep.equal('(string) | (number) | (boolean)')
})
expect(utils.typify(['String', 'Float', 'Boolean'])).to.deep.equal(
'(string) | (number) | (boolean)',
);
});

it('should map an array of types through typify as well and remove duplicates', () => {
expect(utils.typify(['String', 'Float', 'Double'])).to.deep.equal('(string) | (number)')
})
expect(utils.typify(['String', 'Float', 'Double'])).to.deep.equal('(string) | (number)');
});

it('should map node objects to the correct type', () => {
expect(utils.typify('buffer')).to.equal('Buffer')
})
})
expect(utils.typify('buffer')).to.equal('Buffer');
});
});

describe('paramify', () => {
it('should pass through most param names', () => {
expect(utils.paramify('foo')).to.equal('foo')
})
expect(utils.paramify('foo')).to.equal('foo');
});

it('should clean reserved words', () => {
expect(utils.paramify('switch')).to.equal('the_switch')
})
})
expect(utils.paramify('switch')).to.equal('the_switch');
});
});

describe('isEmitter', () => {
it('should return true on most modules', () => {
expect(utils.isEmitter({ name: 'app' })).to.eq(true)
})
expect(utils.isEmitter({ name: 'app' })).to.eq(true);
});

it('should return false for specific non-emitter modules', () => {
expect(utils.isEmitter({ name: 'menuitem' })).to.eq(false)
})
})
expect(utils.isEmitter({ name: 'menuitem' })).to.eq(false);
});
});

describe('isOptional', () => {
it('should return true if param is not required', () => {
expect(utils.isOptional({})).to.eq(true)
})
expect(utils.isOptional({})).to.eq(true);
});

it('should return false if param is required', () => {
expect(utils.isOptional({ required: true })).to.eq(false)
})
expect(utils.isOptional({ required: true })).to.eq(false);
});

it('should default to true if param is a non-function', () => {
expect(utils.isOptional({ type: 'Foo' })).to.eq(true)
})
expect(utils.isOptional({ type: 'Foo' })).to.eq(true);
});

it('should default to false if param is a function', () => {
expect(utils.isOptional({ type: 'Function' })).to.eq(false)
})
})
})
expect(utils.isOptional({ type: 'Function' })).to.eq(false);
});
});
});