From 85d3fa12c1c53bc64b81f94d9f250821187cbdad Mon Sep 17 00:00:00 2001 From: JMTK Date: Thu, 23 Mar 2023 18:39:54 -0400 Subject: [PATCH] - Update target to be to esnext from an outdated es version - Create contributing guide - Move duplicate functionality back to SpotifyUri class since almost all of it is copy pasted, and make special exceptions where a different property name was expected over 'id' - Make 'encode' less reliant on the escape function - Create new SpotifyTypes array to refer to them by string - Fix invalid jsdoc for parse --- CONTRIBUTING.md | 2 ++ src/album.ts | 19 +------------------ src/artist.ts | 21 ++------------------- src/episode.ts | 19 +------------------ src/local.ts | 12 +++++------- src/parse.ts | 44 ++++++++++++++++++++++---------------------- src/playlist.ts | 10 +++------- src/search.ts | 20 +++----------------- src/show.ts | 19 +------------------ src/spotify-uri.ts | 25 +++++++++++++++++++------ src/track.ts | 21 ++------------------- src/types-enum.ts | 12 ++++++++++++ src/user.ts | 22 ++++------------------ src/util.ts | 4 ++-- tsconfig.json | 2 +- 15 files changed, 80 insertions(+), 172 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 src/types-enum.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3a1b1b1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,2 @@ +# Running locally +To start contributing, install the packages using `npm install`. To ensure your changes are working, run `npm run build` followed by `npm run test` and all tests should pass. \ No newline at end of file diff --git a/src/album.ts b/src/album.ts index eefb02f..013fae7 100644 --- a/src/album.ts +++ b/src/album.ts @@ -1,24 +1,7 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Album extends SpotifyUri { - public type = 'album' - public id: string - - constructor (uri: string, id: string) { - super(uri) - this.id = id - } - public static is (v: any): v is Album { - return Boolean(typeof v === 'object' && v.type === 'album') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.id)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.id)}` + return typeof v === 'object' && v.type === 'album' } } diff --git a/src/artist.ts b/src/artist.ts index 8ac109d..1d9fa84 100644 --- a/src/artist.ts +++ b/src/artist.ts @@ -1,24 +1,7 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Artist extends SpotifyUri { - public type = 'artist' - public id: string - - constructor (uri: string, id: string) { - super(uri) - this.id = id - } - public static is (v: any): v is Artist { - return Boolean(typeof v === 'object' && v.type === 'artist') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.id)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.id)}` + return typeof v === 'object' && v.type === 'artist' } -} +} \ No newline at end of file diff --git a/src/episode.ts b/src/episode.ts index bde37e6..da35eee 100644 --- a/src/episode.ts +++ b/src/episode.ts @@ -1,24 +1,7 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Episode extends SpotifyUri { - public type = 'episode' - public id: string - - constructor (uri: string, id: string) { - super(uri) - this.id = id - } - public static is (v: any): v is Episode { - return Boolean(typeof v === 'object' && v.type === 'episode') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.id)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.id)}` + return typeof v === 'object' && v.type === 'episode' } } diff --git a/src/local.ts b/src/local.ts index 34793cd..f6b26be 100644 --- a/src/local.ts +++ b/src/local.ts @@ -1,8 +1,6 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' - +import { encode } from './util' export default class Local extends SpotifyUri { - public type = 'local' public artist: string public album: string public track: string @@ -15,7 +13,7 @@ export default class Local extends SpotifyUri { track: string, seconds: number ) { - super(uri) + super(uri, "") this.artist = artist this.album = album this.track = track @@ -23,14 +21,14 @@ export default class Local extends SpotifyUri { } public static is (v: any): v is Local { - return Boolean(typeof v === 'object' && v.type === 'local') + return typeof v === 'object' && v.type === 'local' } public toURI (): string { - return `spotify:local:${encode(this.artist)}:${encode(this.album)}:${encode(this.track)}:${this.seconds}` + return `spotify:${this.type}:${encode(this.artist)}:${encode(this.album)}:${encode(this.track)}:${this.seconds}` } public toURL (): string { - return `/local/${encode(this.artist)}/${encode(this.album)}/${encode(this.track)}/${this.seconds}` + return `/${this.type}/${encode(this.artist)}/${encode(this.album)}/${encode(this.track)}/${this.seconds}` } } diff --git a/src/parse.ts b/src/parse.ts index 5450ff4..d9c4ba8 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -11,11 +11,12 @@ import User from './user' import SpotifyUri from './spotify-uri' import { decode } from './util' import { ParsedSpotifyUri } from '.' +import { SpotifyTypes } from './types-enum' /** * Parses a "Spotify URI". * - * @param {String} uri + * @param {String} input * @return {Object} parsed Spotify uri object * @api public */ @@ -26,7 +27,7 @@ export default function parse (input: string | SpotifyUri): ParsedSpotifyUri { if (hostname === 'embed.spotify.com') { const parsedQs = Object.fromEntries(searchParams) if (typeof parsedQs.uri !== 'string') { - throw new Error('fo') + throw new Error('Parsed query string was not valid: ' + searchParams.toString()) } return parse(parsedQs.uri) } @@ -46,14 +47,16 @@ export default function parse (input: string | SpotifyUri): ParsedSpotifyUri { } function parseParts (uri: string, parts: string[]): ParsedSpotifyUri { - const len = parts.length - if (parts[1] === 'embed') { + let spotifyType = parts[1] + if (spotifyType === SpotifyTypes.Embed) { parts = parts.slice(1) + spotifyType = parts[1] } - if (parts[1] === 'search') { + const len = parts.length + if (spotifyType === SpotifyTypes.Search) { return new Search(uri, decode(parts.slice(2).join(':'))) } - if (len >= 3 && parts[1] === 'local') { + if (len >= 3 && spotifyType === SpotifyTypes.Local) { return new Local( uri, decode(parts[2]), @@ -62,35 +65,32 @@ function parseParts (uri: string, parts: string[]): ParsedSpotifyUri { +parts[5] ) } - if (len === 3 && parts[1] === 'playlist') { + if (len >= 4 || spotifyType === SpotifyTypes.Playlist) { + if (len >= 5) { + return new Playlist(uri, decode(parts[4]), decode(parts[2])) + } + if (parts[3] === 'starred') { + return new Playlist(uri, 'starred', decode(parts[2])) + } return new Playlist(uri, decode(parts[2])) } - if (len === 3 && parts[1] === 'user') { + if (len === 3 && spotifyType === SpotifyTypes.User) { return new User(uri, decode(parts[2])) } - if (len >= 5) { - return new Playlist(uri, decode(parts[4]), decode(parts[2])) - } - if (len >= 4 && parts[3] === 'starred') { - return new Playlist(uri, 'starred', decode(parts[2])) - } - if (parts[1] === 'artist') { + if (spotifyType === SpotifyTypes.Artist) { return new Artist(uri, parts[2]) } - if (parts[1] === 'album') { + if (spotifyType === SpotifyTypes.Album) { return new Album(uri, parts[2]) } - if (parts[1] === 'track') { + if (spotifyType === SpotifyTypes.Track) { return new Track(uri, parts[2]) } - if (parts[1] === 'episode') { + if (spotifyType === SpotifyTypes.Episode) { return new Episode(uri, parts[2]) } - if (parts[1] === 'show') { + if (spotifyType === SpotifyTypes.Show) { return new Show(uri, parts[2]) } - if (parts[1] === 'playlist') { - return new Playlist(uri, parts[2]) - } throw new TypeError(`Could not determine type for: ${uri}`) } diff --git a/src/playlist.ts b/src/playlist.ts index 1df07ec..4606a1f 100644 --- a/src/playlist.ts +++ b/src/playlist.ts @@ -1,21 +1,17 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' - +import { encode } from './util' export default class Playlist extends SpotifyUri { - public type = 'playlist' - public id: string public user?: string constructor (uri: string, id: string, user?: string) { - super(uri) - this.id = id + super(uri, id) if (typeof user === 'string') { this.user = user } } public static is (v: any): v is Playlist { - return Boolean(typeof v === 'object' && v.type === 'playlist') + return typeof v === 'object' && v.type === 'playlist' } public toURI (): string { diff --git a/src/search.ts b/src/search.ts index 3cba06b..f6e709b 100644 --- a/src/search.ts +++ b/src/search.ts @@ -1,24 +1,10 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Search extends SpotifyUri { - public type = 'search' - public query: string - - constructor (uri: string, query: string) { - super(uri) - this.query = query + get query() { + return this.id; } - public static is (v: any): v is Search { - return Boolean(typeof v === 'object' && v.type === 'search') - } - - public toURI (): string { - return `spotify:search:${encode(this.query)}` - } - - public toURL (): string { - return `/search/${encode(this.query)}` + return typeof v === 'object' && v.type === 'search' } } diff --git a/src/show.ts b/src/show.ts index ba847ee..4b4e526 100644 --- a/src/show.ts +++ b/src/show.ts @@ -1,24 +1,7 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Show extends SpotifyUri { - public type = 'show' - public id: string - - constructor (uri: string, id: string) { - super(uri) - this.id = id - } - public static is (v: any): v is Show { - return Boolean(typeof v === 'object' && v.type === 'show') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.id)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.id)}` + return typeof v === 'object' && v.type === 'show' } } diff --git a/src/spotify-uri.ts b/src/spotify-uri.ts index ae1c10e..f9943e9 100644 --- a/src/spotify-uri.ts +++ b/src/spotify-uri.ts @@ -1,14 +1,27 @@ +import { SpotifyTypes } from './types-enum'; +import { encode } from './util' + export default abstract class SpotifyUri { - public uri: string - public abstract toURL (): string - public abstract toURI (): string + public type: SpotifyTypes; + public id: string; + public uri: string; - constructor (uri: string) { - this.uri = uri + constructor (uri: string, id : string) { + this.uri = uri; + this.id = id; + this.type = this.constructor.name.toLowerCase() as SpotifyTypes; } public static is (v: any): v is SpotifyUri { - return Boolean(typeof v === 'object' && typeof v.uri === 'string') + return typeof v === 'object' && typeof v.uri === 'string' + } + + public toURI (): string { + return `spotify:${this.type}:${encode(this.id)}` + } + + public toURL (): string { + return `/${this.type}/${encode(this.id)}` } public toEmbedURL (): string { diff --git a/src/track.ts b/src/track.ts index 19b2047..8048516 100644 --- a/src/track.ts +++ b/src/track.ts @@ -1,24 +1,7 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class Track extends SpotifyUri { - public type = 'track' - public id: string - - constructor (uri: string, id: string) { - super(uri) - this.id = id - } - public static is (v: any): v is Track { - return Boolean(typeof v === 'object' && v.type === 'track') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.id)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.id)}` + return typeof v === 'object' && v.type === 'track' } -} +} \ No newline at end of file diff --git a/src/types-enum.ts b/src/types-enum.ts new file mode 100644 index 0000000..23e5b83 --- /dev/null +++ b/src/types-enum.ts @@ -0,0 +1,12 @@ +export enum SpotifyTypes { + Album = 'album', + Artist = 'artist', + Episode = 'episode', + Local = 'local', + Playlist = 'playlist', + Search = 'search', + Show = 'show', + Track = 'track', + User = 'user', + Embed = 'embed' +} \ No newline at end of file diff --git a/src/user.ts b/src/user.ts index 6f00dd9..c80ce58 100644 --- a/src/user.ts +++ b/src/user.ts @@ -1,24 +1,10 @@ -import { encode } from './util' import SpotifyUri from './spotify-uri' export default class User extends SpotifyUri { - public type = 'user' - public user: string - - constructor (uri: string, user: string) { - super(uri) - this.user = user + get user() { + return this.id; } - public static is (v: any): v is User { - return Boolean(typeof v === 'object' && v.type === 'user') - } - - public toURI (): string { - return `spotify:${this.type}:${encode(this.user)}` - } - - public toURL (): string { - return `/${this.type}/${encode(this.user)}` + return typeof v === 'object' && v.type === 'user' } -} +} \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index e62ca9f..cebbf89 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,7 +5,7 @@ * @api private */ export function decode (str: string): string { - return decodeURIComponent(str).replace(/\+/g, ' ') + return decodeURIComponent(str.replace(/\+/g, ' ')) } /** @@ -15,5 +15,5 @@ export function decode (str: string): string { * @api private */ export function encode (str: string): string { - return escape(str.replace(/ /g, '+')) + return encodeURIComponent(str).replace(/%20/g, "+").replace(/[!'()*]/g, escape) } diff --git a/tsconfig.json b/tsconfig.json index a038ec3..06993eb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "strict": true, "module": "CommonJS", - "target": "es2015", + "target": "esnext", "esModuleInterop": true, "lib": ["esnext"], "outDir": "dist",