From 7c35a842a6cba3f1344a09800626cce204120bf4 Mon Sep 17 00:00:00 2001 From: flouc001 Date: Sat, 11 Jul 2020 09:24:46 +0100 Subject: [PATCH 1/6] add dry run capabilities to default slugger --- src/Slugger.js | 42 ++++++++++++++++++++++++++++++---------- test/unit/marked-spec.js | 6 ++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/Slugger.js b/src/Slugger.js index 7095454ac3..75e16c85a3 100644 --- a/src/Slugger.js +++ b/src/Slugger.js @@ -6,11 +6,8 @@ module.exports = class Slugger { this.seen = {}; } - /** - * Convert string to unique id - */ - slug(value) { - let slug = value + serialize(value) { + return value .toLowerCase() .trim() // remove html tags @@ -18,16 +15,41 @@ module.exports = class Slugger { // remove unwanted chars .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') .replace(/\s/g, '-'); + } + /** + * Finds the next safe (unique) slug to use + */ + getNextSafeSlug(originalSlug, isDryRun) { + let slug = originalSlug; + let occurenceAccumulator = 0; if (this.seen.hasOwnProperty(slug)) { - const originalSlug = slug; + occurenceAccumulator = this.seen[originalSlug]; do { - this.seen[originalSlug]++; - slug = originalSlug + '-' + this.seen[originalSlug]; + occurenceAccumulator++; + slug = originalSlug + '-' + occurenceAccumulator; } while (this.seen.hasOwnProperty(slug)); } - this.seen[slug] = 0; - + if (!isDryRun) { + this.seen[originalSlug] = occurenceAccumulator; + this.seen[slug] = 0; + } return slug; } + + /** + * Convert string to unique id + */ + slug(value) { + const slug = this.serialize(value); + return this.getNextSafeSlug(slug); + } + + /** + * Get slug text without incrementing accumulator + */ + slugText(value) { + const slug = this.serialize(value); + return this.getNextSafeSlug(slug, true); + } }; diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 53b0d90818..c80868a832 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -63,6 +63,12 @@ describe('Test slugger functionality', () => { const slugger = new marked.Slugger(); expect(slugger.slug('html')).toBe('html'); }); + + it('should not increment seen when just getting text', () => { + const slugger = new marked.Slugger(); + slugger.slugText('

This Section

'); + expect(slugger.slug('

This Section

')).toBe('this-section'); + }); }); describe('Test paragraph token type', () => { From 2290f677c971c7e6bed2229fa8fef9ee5b340dcd Mon Sep 17 00:00:00 2001 From: flouc001 Date: Sat, 11 Jul 2020 09:36:24 +0100 Subject: [PATCH 2/6] update docs --- docs/USING_PRO.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md index 5912d2b5e6..418fd1e3ce 100644 --- a/docs/USING_PRO.md +++ b/docs/USING_PRO.md @@ -82,6 +82,16 @@ slugger.slug('foo-1') // foo-1-2 ... ``` +`slugger` also has a stateless method `slugText` which does not update the internal accumulator: +```js +slugger.slug('foo') // foo +slugger.slugText('foo') // foo-1 +slugger.slug('foo') // foo-1 +slugger.slugText('foo') // foo-2 +slugger.slug('foo') // foo-2 +... +``` + `flags` has the following properties: ```js From 710658cb61b92061eb16d05e9e6c313f46de541a Mon Sep 17 00:00:00 2001 From: flouc001 Date: Sun, 12 Jul 2020 19:38:37 +0100 Subject: [PATCH 3/6] use options param, update docs, update tests --- docs/USING_PRO.md | 12 ++++++------ src/Slugger.js | 14 ++++---------- test/unit/marked-spec.js | 10 ++++++++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md index 418fd1e3ce..fb70f46d69 100644 --- a/docs/USING_PRO.md +++ b/docs/USING_PRO.md @@ -82,13 +82,13 @@ slugger.slug('foo-1') // foo-1-2 ... ``` -`slugger` also has a stateless method `slugText` which does not update the internal accumulator: +`slugger.slug` can also be called with the `dryrun` option for stateless operation: ```js -slugger.slug('foo') // foo -slugger.slugText('foo') // foo-1 -slugger.slug('foo') // foo-1 -slugger.slugText('foo') // foo-2 -slugger.slug('foo') // foo-2 +slugger.slug('foo') // foo +slugger.slug('foo', { dryrun: true }) // foo-1 +slugger.slug('foo') // foo-1 +slugger.slug('foo', { dryrun: true }) // foo-2 +slugger.slug('foo') // foo-2 ... ``` diff --git a/src/Slugger.js b/src/Slugger.js index 75e16c85a3..db385f54dd 100644 --- a/src/Slugger.js +++ b/src/Slugger.js @@ -39,17 +39,11 @@ module.exports = class Slugger { /** * Convert string to unique id + * @param {object} options + * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. */ - slug(value) { + slug(value, options = {}) { const slug = this.serialize(value); - return this.getNextSafeSlug(slug); - } - - /** - * Get slug text without incrementing accumulator - */ - slugText(value) { - const slug = this.serialize(value); - return this.getNextSafeSlug(slug, true); + return this.getNextSafeSlug(slug, options.dryrun); } }; diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index c80868a832..93ce922e58 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -64,11 +64,17 @@ describe('Test slugger functionality', () => { expect(slugger.slug('html')).toBe('html'); }); - it('should not increment seen when just getting text', () => { + it('should not increment seen when using dryrun option', () => { const slugger = new marked.Slugger(); - slugger.slugText('

This Section

'); + slugger.slug('

This Section

', { dryrun: true }); expect(slugger.slug('

This Section

')).toBe('this-section'); }); + + it('should still return the next unique id when using dryrun', () => { + const slugger = new marked.Slugger(); + expect(slugger.slug('

This Section

')).toBe('this-section'); + expect(slugger.slug('

This Section

', { dryrun: true })).toBe('this-section-1'); + }); }); describe('Test paragraph token type', () => { From 0c415fd82e195bfadbcd910cdc3f8d539fd12e2a Mon Sep 17 00:00:00 2001 From: Callum Date: Wed, 15 Jul 2020 08:02:55 +0100 Subject: [PATCH 4/6] update docs example to be clearer Co-authored-by: Steven --- docs/USING_PRO.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md index fb70f46d69..ab684f662e 100644 --- a/docs/USING_PRO.md +++ b/docs/USING_PRO.md @@ -85,10 +85,12 @@ slugger.slug('foo-1') // foo-1-2 `slugger.slug` can also be called with the `dryrun` option for stateless operation: ```js slugger.slug('foo') // foo -slugger.slug('foo', { dryrun: true }) // foo-1 slugger.slug('foo') // foo-1 -slugger.slug('foo', { dryrun: true }) // foo-2 slugger.slug('foo') // foo-2 +slugger.slug('foo', { dryrun: true }) // foo-3 +slugger.slug('foo', { dryrun: true }) // foo-3 +slugger.slug('foo') // foo-3 +slugger.slug('foo') // foo-4 ... ``` From 014fbcf43f7bd805b79757e0b10154be51f911bc Mon Sep 17 00:00:00 2001 From: Callum Date: Wed, 15 Jul 2020 08:03:31 +0100 Subject: [PATCH 5/6] add assertion to existing test Co-authored-by: Tony Brix --- test/unit/marked-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 93ce922e58..2581a6d57f 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -66,7 +66,7 @@ describe('Test slugger functionality', () => { it('should not increment seen when using dryrun option', () => { const slugger = new marked.Slugger(); - slugger.slug('

This Section

', { dryrun: true }); + expect(slugger.slug('

This Section

', { dryrun: true })).toBe('this-section'); expect(slugger.slug('

This Section

')).toBe('this-section'); }); From 75b2646687d007b6bc18e40066a1395c1b26dd03 Mon Sep 17 00:00:00 2001 From: flouc001 Date: Wed, 15 Jul 2020 08:16:03 +0100 Subject: [PATCH 6/6] add sequence test --- test/unit/marked-spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 2581a6d57f..2f88258133 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -75,6 +75,17 @@ describe('Test slugger functionality', () => { expect(slugger.slug('

This Section

')).toBe('this-section'); expect(slugger.slug('

This Section

', { dryrun: true })).toBe('this-section-1'); }); + + it('should be repeatable in a sequence', () => { + const slugger = new marked.Slugger(); + expect(slugger.slug('foo')).toBe('foo'); + expect(slugger.slug('foo')).toBe('foo-1'); + expect(slugger.slug('foo')).toBe('foo-2'); + expect(slugger.slug('foo', { dryrun: true })).toBe('foo-3'); + expect(slugger.slug('foo', { dryrun: true })).toBe('foo-3'); + expect(slugger.slug('foo')).toBe('foo-3'); + expect(slugger.slug('foo')).toBe('foo-4'); + }); }); describe('Test paragraph token type', () => {