@scalar/helpers
Advanced tools
| import { type SlugifyOptions } from './slugify.js'; | ||
| /** | ||
| * Creates a stateful slug generator that tracks previously seen slugs and | ||
| * appends an incrementing numeric suffix to avoid collisions, mirroring the | ||
| * behaviour of `github-slugger`. | ||
| * | ||
| * @example | ||
| * const { slug, reset } = createSlugger() | ||
| * slug('Hello World') // 'hello-world' | ||
| * slug('Hello World') // 'hello-world-1' | ||
| * slug('Hello World') // 'hello-world-2' | ||
| * reset() // Clears the seen slugs | ||
| * slug('Hello World') // 'hello-world' | ||
| */ | ||
| export declare const slugger: (options?: SlugifyOptions) => { | ||
| slug(v: string): string; | ||
| reset(): void; | ||
| }; | ||
| //# sourceMappingURL=slugger.d.ts.map |
| {"version":3,"file":"slugger.d.ts","sourceRoot":"","sources":["../../src/string/slugger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAW,MAAM,WAAW,CAAA;AAExD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,UAAS,cAAmB;YAIxC,MAAM,GAAG,MAAM;;CAkB1B,CAAA"} |
| import { slugify } from './slugify.js'; | ||
| /** | ||
| * Creates a stateful slug generator that tracks previously seen slugs and | ||
| * appends an incrementing numeric suffix to avoid collisions, mirroring the | ||
| * behaviour of `github-slugger`. | ||
| * | ||
| * @example | ||
| * const { slug, reset } = createSlugger() | ||
| * slug('Hello World') // 'hello-world' | ||
| * slug('Hello World') // 'hello-world-1' | ||
| * slug('Hello World') // 'hello-world-2' | ||
| * reset() // Clears the seen slugs | ||
| * slug('Hello World') // 'hello-world' | ||
| */ | ||
| export const slugger = (options = {}) => { | ||
| const seen = new Map(); | ||
| return { | ||
| slug(v) { | ||
| const base = slugify(v, options); | ||
| const count = seen.get(base); | ||
| if (count === undefined) { | ||
| seen.set(base, 0); | ||
| return base; | ||
| } | ||
| const next = count + 1; | ||
| seen.set(base, next); | ||
| return `${base}-${next}`; | ||
| }, | ||
| reset() { | ||
| seen.clear(); | ||
| }, | ||
| }; | ||
| }; |
| export type SlugifyOptions = { | ||
| /** | ||
| * A string of extra characters to allow through the non-word filter. | ||
| * Each character in the string is treated literally. | ||
| * @example '.' // keeps dots: "v1.2.3" → "v1.2.3" | ||
| * @example '.@' // keeps dots and at-signs | ||
| */ | ||
| allowedSpecialChars?: string; | ||
| /** | ||
| * When `true`, the result is preserved as-is (i.e. case is preserved). By default we lowercase the string. | ||
| * @default false | ||
| * @example slugify('MyAPI', { preserveCase: true }) // 'MyAPI' | ||
| */ | ||
| preserveCase?: boolean; | ||
| }; | ||
| /** | ||
| * Normalizes and slugifies a string. | ||
| * | ||
| * By default the result is lowercased, limited to 255 characters, and stripped | ||
| * of everything that is not a Unicode letter, mark, number, hyphen, or space | ||
| * (spaces and hyphens are then collapsed into a single hyphen). | ||
| * | ||
| * Pass {@link SlugifyOptions} to adjust this behaviour. | ||
| * | ||
| * | Option | Type | Default | Description | | ||
| * |----------------------|------------|---------|----------------------------------------------------------------------------------------------| | ||
| * | `allowedSpecialChars`| `string` | `""` | Extra characters that should survive the non-word filter (e.g. `"."` keeps dots so `"v1.2"` → `"v1.2"` instead of `"v12"`). | | ||
| * | `preserveCase` | `boolean` | `false` | When `true`, the case is preserved. By default we lowercase the string | | ||
| */ | ||
| export declare const slugify: (v: string, options?: SlugifyOptions) => string; | ||
| //# sourceMappingURL=slugify.d.ts.map |
| {"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../src/string/slugify.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,EAAE,UAAS,cAAmB,WA2B9D,CAAA"} |
| const RE_NON_WORD = /[^\p{L}\p{M}\p{N}\s_-]/gu; | ||
| const RE_SPACES = /[\s_-]+/g; | ||
| const RE_TRIM_HYPHENS = /^-+|-+$/g; | ||
| /** Cache of compiled non-word regexes keyed by their `allowedSpecialChars` string. */ | ||
| const reNonWordCache = new Map(); | ||
| /** | ||
| * Normalizes and slugifies a string. | ||
| * | ||
| * By default the result is lowercased, limited to 255 characters, and stripped | ||
| * of everything that is not a Unicode letter, mark, number, hyphen, or space | ||
| * (spaces and hyphens are then collapsed into a single hyphen). | ||
| * | ||
| * Pass {@link SlugifyOptions} to adjust this behaviour. | ||
| * | ||
| * | Option | Type | Default | Description | | ||
| * |----------------------|------------|---------|----------------------------------------------------------------------------------------------| | ||
| * | `allowedSpecialChars`| `string` | `""` | Extra characters that should survive the non-word filter (e.g. `"."` keeps dots so `"v1.2"` → `"v1.2"` instead of `"v12"`). | | ||
| * | `preserveCase` | `boolean` | `false` | When `true`, the case is preserved. By default we lowercase the string | | ||
| */ | ||
| export const slugify = (v, options = {}) => { | ||
| const { allowedSpecialChars = '', preserveCase = false } = options; | ||
| // Compile the non-word regex once and cache it for future use. | ||
| const reNonWord = (() => { | ||
| if (allowedSpecialChars.length === 0) { | ||
| return RE_NON_WORD; | ||
| } | ||
| const cached = reNonWordCache.get(allowedSpecialChars); | ||
| if (cached) { | ||
| return cached; | ||
| } | ||
| // Escape user-provided characters so they are treated literally inside the character class. | ||
| const escaped = allowedSpecialChars.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | ||
| const reNonWordWithAllowedSpecialChars = new RegExp(`[^\\p{L}\\p{M}\\p{N}\\s_\\-${escaped}]`, 'gu'); | ||
| reNonWordCache.set(allowedSpecialChars, reNonWordWithAllowedSpecialChars); | ||
| return reNonWordWithAllowedSpecialChars; | ||
| })(); | ||
| // Normalize before filtering so equivalent Unicode forms produce the same slug. | ||
| const normalized = v.slice(0, 255).trim().normalize('NFC'); | ||
| const result = preserveCase ? normalized : normalized.toLowerCase(); | ||
| return result.replace(reNonWord, '').replace(RE_SPACES, '-').replace(RE_TRIM_HYPHENS, ''); | ||
| }; |
+6
-0
| # @scalar/helpers | ||
| ## 0.5.5 | ||
| ### Patch Changes | ||
| - [#9023](https://github.com/scalar/scalar/pull/9023): chore: use homemade slugger | ||
| ## 0.5.4 | ||
@@ -4,0 +10,0 @@ |
+2
-1
@@ -17,3 +17,3 @@ { | ||
| ], | ||
| "version": "0.5.4", | ||
| "version": "0.5.5", | ||
| "engines": { | ||
@@ -130,4 +130,5 @@ "node": ">=22" | ||
| "test": "vitest --run", | ||
| "test:watch": "vitest", | ||
| "types:check": "tsc --noEmit" | ||
| } | ||
| } |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
220311
2.96%213
2.9%4854
2.58%