diff --git a/package.json b/package.json index 271e13492..957909a67 100644 --- a/package.json +++ b/package.json @@ -241,6 +241,7 @@ "mustache": "4.2.0", "nyc": "15.1.0", "prettier": "2.3.2", + "reindent-template-literals": "^1.0.0", "semver": "7.3.5", "sinon": "11.1.1", "sinon-chai": "3.7.0", diff --git a/src/formatter/helpers/gherkin_document_parser_spec.ts b/src/formatter/helpers/gherkin_document_parser_spec.ts index fd4b54f3c..8bea18ee7 100644 --- a/src/formatter/helpers/gherkin_document_parser_spec.ts +++ b/src/formatter/helpers/gherkin_document_parser_spec.ts @@ -12,6 +12,7 @@ import { } from '../../../test/gherkin_helpers' import * as messages from '@cucumber/messages' import IGherkinDocument = messages.GherkinDocument +import { reindent } from 'reindent-template-literals' describe('GherkinDocumentParser', () => { describe('getGherkinStepMap', () => { @@ -315,64 +316,72 @@ async function parseGherkinDocument(data: string): Promise { } async function withBackgroundAndScenario(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Scenario: - When a regular step -`) + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step + + Scenario: + When a regular step + `) + ) } async function withBackgroundAndScenarioOutline(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Scenario Outline: - When a templated step with - Examples: - | word | - | foo | - | bar | -`) + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step + + Scenario Outline: + When a templated step with + Examples: + | word | + | foo | + | bar | + `) + ) } async function withBackgroundAndRuleWithExamples(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Rule: a rule - Example: an example - When a regular step - Then an assertion - - Example: another example - When a regular step - Then an assertion -`) + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step + + Rule: a rule + Example: an example + When a regular step + Then an assertion + + Example: another example + When a regular step + Then an assertion + `) + ) } async function withBackgroundAndRuleWithBackgroundAndExamples(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a feature-level setup step - - Rule: a rule - Background: - Given a rule-level setup step - - Example: an example - When a regular step - Then an assertion - - Example: another example - When a regular step - Then an assertion -`) + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a feature-level setup step + + Rule: a rule + Background: + Given a rule-level setup step + + Example: an example + When a regular step + Then an assertion + + Example: another example + When a regular step + Then an assertion + `) + ) } diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index 1a63a3f71..61c5dc4e3 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -4,6 +4,7 @@ import getColorFns from '../get_color_fns' import { formatIssue } from './issue_helpers' import figures from 'figures' import { getTestCaseAttempts } from '../../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' import { getBaseSupportCodeLibrary } from '../../../test/fixtures/steps' import FormatterBuilder from '../builder' @@ -37,211 +38,226 @@ describe('IssueHelpers', () => { describe('with a failed step', () => { it('prints the scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a failing step - Then a passing step -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When a failing step + Then a passing step + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When a failing step # steps.ts:9 - error - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When a failing step # steps.ts:9 + error + - Then a passing step # steps.ts:29 + + `) + ) }) }) describe('with an ambiguous step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When an ambiguous step - Then a passing step -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When an ambiguous step + Then a passing step + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 + - Then a passing step # steps.ts:29 + + `) + ) }) }) describe('with an undefined step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When an undefined step - Then a passing step -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When an undefined step + Then a passing step + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When an undefined step - Undefined. Implement with the following snippet: - - When('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); - - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When an undefined step + Undefined. Implement with the following snippet: + + When('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); + + - Then a passing step # steps.ts:29 + + `) + ) }) }) describe('with a pending step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + + `) + ) }) }) describe('step with data table', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step - |aaa|b|c| - |d|e|ff| - |gg|h|iii| -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + |aaa|b|c| + |d|e|ff| + |gg|h|iii| + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - | aaa | b | c | - | d | e | ff | - | gg | h | iii | - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + | aaa | b | c | + | d | e | ff | + | gg | h | iii | + + `) + ) }) }) describe('step with doc string', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step - """ - this is a multiline - doc string - - :-) - """ -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + """ + this is a multiline + doc string + + :-) + """ + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - """ - this is a multiline - doc string - - :-) - """ - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + """ + this is a multiline + doc string + + :-) + """ + + `) + ) }) }) describe('step with attachment text', () => { it('prints the scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given attachment step1 - When attachment step2 - Then a passing step -` + const sourceData = reindent(` + Feature: my feature + Scenario: my scenario + Given attachment step1 + When attachment step2 + Then a passing step + `) + // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given attachment step1 # steps.ts:35 - Attachment (text/plain): Some info - Attachment (application/json) - Attachment (image/png) - ${figures.cross} When attachment step2 # steps.ts:41 - Attachment (text/plain): Other info - error - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given attachment step1 # steps.ts:35 + Attachment (text/plain): Some info + Attachment (application/json) + Attachment (image/png) + ${figures.cross} When attachment step2 # steps.ts:41 + Attachment (text/plain): Other info + error + - Then a passing step # steps.ts:29 + + `) + ) }) }) }) diff --git a/src/formatter/progress_formatter_spec.ts b/src/formatter/progress_formatter_spec.ts index 6786f6c19..d7078e624 100644 --- a/src/formatter/progress_formatter_spec.ts +++ b/src/formatter/progress_formatter_spec.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, it } from 'mocha' import { expect } from 'chai' import { getBaseSupportCodeLibrary } from '../../test/fixtures/steps' import { testFormatter } from '../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' import figures from 'figures' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' @@ -21,21 +22,21 @@ describe('ProgressFormatter', () => { // Arrange const sources = [ { - data: `\ -Feature: a - Scenario: a1 - Given an ambiguous step - Scenario: a2 - Given a failing step - Scenario: a3 - Given a pending step - Scenario: a4 - Given a passing step - Scenario: a5 - Given a skipped step - Scenario: a6 - Given an undefined step -`, + data: reindent(` + Feature: a + Scenario: a1 + Given an ambiguous step + Scenario: a2 + Given a failing step + Scenario: a3 + Given a pending step + Scenario: a4 + Given a passing step + Scenario: a5 + Given a skipped step + Scenario: a6 + Given an undefined step + `), uri: 'a.feature', }, ] @@ -49,60 +50,62 @@ Feature: a }) // Assert - expect(output).to.eql(`\ -AFP.-U + expect(output).to.eql( + reindent(` + AFP.-U -Failures: + Failures: -1) Scenario: a1 # a.feature:2 - ${figures.cross} Given an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 + 1) Scenario: a1 # a.feature:2 + ${figures.cross} Given an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 -2) Scenario: a2 # a.feature:4 - ${figures.cross} Given a failing step # steps.ts:9 - error + 2) Scenario: a2 # a.feature:4 + ${figures.cross} Given a failing step # steps.ts:9 + error -3) Scenario: a6 # a.feature:12 - ? Given an undefined step - Undefined. Implement with the following snippet: + 3) Scenario: a6 # a.feature:12 + ? Given an undefined step + Undefined. Implement with the following snippet: - Given('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); + Given('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); -Warnings: + Warnings: -1) Scenario: a3 # a.feature:6 - ? Given a pending step # steps.ts:16 - Pending + 1) Scenario: a3 # a.feature:6 + ? Given a pending step # steps.ts:16 + Pending -6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) -6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) - -`) + 6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + 6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + + `) + ) }) it('handles rule/example results', async () => { // Arrange const sources = [ { - data: `\ -Feature: feature - Rule: rule1 - Example: example1 - Given a passing step - - Example: example2 - Given a passing step - - Rule: rule2 - Example: example1 - Given a passing step -`, + data: reindent(` + Feature: feature + Rule: rule1 + Example: example1 + Given a passing step + + Example: example2 + Given a passing step + + Rule: rule2 + Example: example1 + Given a passing step + `), uri: 'a.feature', }, ] @@ -116,12 +119,14 @@ Feature: feature }) // Assert - expect(output).to.eql(`\ -... - -3 scenarios (3 passed) -3 steps (3 passed) - -`) + expect(output).to.eql( + reindent(` + ... + + 3 scenarios (3 passed) + 3 steps (3 passed) + + `) + ) }) }) diff --git a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts index 17978db03..18fcf7781 100644 --- a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts +++ b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts @@ -8,6 +8,7 @@ import { CucumberExpressionGenerator, ParameterTypeRegistry, } from '@cucumber/cucumber-expressions' +import { reindent } from 'reindent-template-literals' function generateExpressions(text: string): readonly GeneratedExpression[] { const parameterTypeRegistry = new ParameterTypeRegistry() @@ -34,11 +35,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2, callback) { - // comment - callback(null, 'pending'); -});`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2, callback) { + // comment + callback(null, 'pending'); + });`) + ) }) }) @@ -57,11 +60,13 @@ functionName('{string} def {string}', function (string, string2, callback) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function *(string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function *(string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -80,11 +85,13 @@ functionName('{string} def {string}', function *(string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -103,11 +110,13 @@ functionName('{string} def {string}', function (string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -126,11 +135,13 @@ functionName('{string} def {string}', function (string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('pattern\\'', function () { - // comment - return 'pending'; -});`) + expect(result).to.eql( + reindent(` + functionName('pattern\\'', function () { + // comment + return 'pending'; + });`) + ) }) }) @@ -149,14 +160,16 @@ functionName('pattern\\'', function () { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{int} {int}', function (int, int2) { -// functionName('{int} {float}', function (int, float) { -// functionName('{float} {int}', function (float, int) { -// functionName('{float} {float}', function (float, float2) { - // comment - return 'pending'; -});`) + expect(result).to.eql( + reindent(` + functionName('{int} {int}', function (int, int2) { + // functionName('{int} {float}', function (int, float) { + // functionName('{float} {int}', function (float, int) { + // functionName('{float} {float}', function (float, float2) { + // comment + return 'pending'; + });`) + ) }) }) }) diff --git a/src/formatter/usage_formatter_spec.ts b/src/formatter/usage_formatter_spec.ts index b394f347f..752611bce 100644 --- a/src/formatter/usage_formatter_spec.ts +++ b/src/formatter/usage_formatter_spec.ts @@ -4,6 +4,7 @@ import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' import { getUsageSupportCodeLibrary } from '../../test/fixtures/usage_steps' import { testFormatter } from '../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' describe('UsageFormatter', () => { let clock: InstalledClock @@ -41,17 +42,19 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ UNUSED │ usage_steps.ts:16 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ UNUSED │ usage_steps.ts:16 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) + ) }) }) @@ -77,19 +80,21 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ - │ usage_steps.ts:16 │ -│ de │ - │ a.feature:4 │ -│ def │ - │ a.feature:3 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ - │ usage_steps.ts:16 │ + │ de │ - │ a.feature:4 │ + │ def │ - │ a.feature:3 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) + ) }) }) @@ -112,19 +117,21 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ 1.50ms │ usage_steps.ts:16 │ -│ def │ 2ms │ a.feature:3 │ -│ de │ 1ms │ a.feature:4 │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ 1.50ms │ usage_steps.ts:16 │ + │ def │ 2ms │ a.feature:3 │ + │ de │ 1ms │ a.feature:4 │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) + ) }) }) }) diff --git a/yarn.lock b/yarn.lock index ba0ebe021..26e5aa330 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3421,6 +3421,11 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +reindent-template-literals@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/reindent-template-literals/-/reindent-template-literals-1.0.0.tgz#7c6fa92f825f342515ea6e1c9e0cbe2d8671ce3c" + integrity sha512-q5ttxEAkS6dWL3oGyQS5wnvwZc2zzOpE679uZLAS0izTk6c1LkC7S4q6ca6gq/hL2Cokw1ozInee/mkPsXW4WA== + release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730"