Skip to content

Commit f6f3f38

Browse files
1 parent 4c868d3 commit f6f3f38

File tree

2 files changed

+180
-3
lines changed

2 files changed

+180
-3
lines changed

packages/social-urls/lib/index.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55
module.exports.twitter = function twitter(username) {
66
// Creates the canonical twitter URL without the '@'
7-
return 'https://twitter.com/' + username.replace(/^@/, '');
7+
return 'https://x.com/' + username.replace(/^@/, '');
88
};
99

1010
/**
@@ -15,3 +15,68 @@ module.exports.facebook = function facebook(username) {
1515
// Handles a starting slash, this shouldn't happen, but just in case
1616
return 'https://www.facebook.com/' + username.replace(/^\//, '');
1717
};
18+
19+
/**
20+
* @param {string} username
21+
* @returns {string}
22+
*/
23+
module.exports.threads = function threads(username) {
24+
// a threads profile url is always prefixed with @
25+
username = username.replace(/^@/, '');
26+
return 'https://www.threads.net/@' + username;
27+
};
28+
29+
/**
30+
* @param {string} username
31+
* @returns {string}
32+
*/
33+
module.exports.bluesky = function bluesky(username) {
34+
// Bluesky URLs use profile path, no @ in stored handle
35+
return 'https://bsky.app/profile/' + username;
36+
};
37+
38+
/**
39+
* @param {string} username
40+
* @returns {string}
41+
*/
42+
module.exports.mastodon = function mastodon(username) {
43+
// Mastodon stores full URL without https://, just prepend protocol
44+
return 'https://' + username;
45+
};
46+
47+
/**
48+
* @param {string} username
49+
* @returns {string}
50+
*/
51+
module.exports.tiktok = function tiktok(username) {
52+
// TikTok URLs include @, handles stored with or without @
53+
username = username.startsWith('@') ? username : '@' + username;
54+
return 'https://www.tiktok.com/' + username;
55+
};
56+
57+
/**
58+
* @param {string} username
59+
* @returns {string}
60+
*/
61+
module.exports.youtube = function youtube(username) {
62+
// YouTube handles include @, user/, or channel/, stored as-is
63+
return 'https://www.youtube.com/' + username;
64+
};
65+
66+
/**
67+
* @param {string} username
68+
* @returns {string}
69+
*/
70+
module.exports.instagram = function instagram(username) {
71+
// Instagram URLs have no @, stored without @
72+
return 'https://www.instagram.com/' + username;
73+
};
74+
75+
/**
76+
* @param {string} username
77+
* @returns {string}
78+
*/
79+
module.exports.linkedin = function linkedin(username) {
80+
// LinkedIn URLs use /in/, stored without @
81+
return 'https://www.linkedin.com/in/' + username;
82+
};

packages/social-urls/test/urls.test.js

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// const testUtils = require('./utils');
33
require('./utils');
44

5+
const should = require('should');
56
const social = require('../lib/index');
67

78
describe('lib/social: urls', function () {
9+
// Verify each platform has a URL function
810
it('should have a twitter url function', function () {
911
should.exist(social.twitter);
1012
});
@@ -13,13 +15,41 @@ describe('lib/social: urls', function () {
1315
should.exist(social.facebook);
1416
});
1517

18+
it('should have a threads url function', function () {
19+
should.exist(social.threads);
20+
});
21+
22+
it('should have a bluesky url function', function () {
23+
should.exist(social.bluesky);
24+
});
25+
26+
it('should have a mastodon url function', function () {
27+
should.exist(social.mastodon);
28+
});
29+
30+
it('should have a tiktok url function', function () {
31+
should.exist(social.tiktok);
32+
});
33+
34+
it('should have a youtube url function', function () {
35+
should.exist(social.youtube);
36+
});
37+
38+
it('should have an instagram url function', function () {
39+
should.exist(social.instagram);
40+
});
41+
42+
it('should have a linkedin url function', function () {
43+
should.exist(social.linkedin);
44+
});
45+
1646
describe('twitter', function () {
1747
it('should return a correct concatenated URL', function () {
18-
social.twitter('myusername').should.eql('https://twitter.com/myusername');
48+
social.twitter('@myusername').should.eql('https://x.com/myusername');
1949
});
2050

2151
it('should return a url without an @ sign if one is provided', function () {
22-
social.twitter('@myusername').should.eql('https://twitter.com/myusername');
52+
social.twitter('myusername').should.eql('https://x.com/myusername');
2353
});
2454
});
2555

@@ -36,4 +66,86 @@ describe('lib/social: urls', function () {
3666
social.facebook('/page/xxx/123').should.eql('https://www.facebook.com/page/xxx/123');
3767
});
3868
});
69+
70+
describe('threads', function () {
71+
it('should return a correct concatenated URL', function () {
72+
social.threads('@myusername').should.eql('https://www.threads.net/@myusername');
73+
});
74+
75+
it('should return a correct URL for usernames without @', function () {
76+
social.threads('myusername').should.eql('https://www.threads.net/@myusername');
77+
});
78+
});
79+
80+
describe('bluesky', function () {
81+
it('should return a correct concatenated URL', function () {
82+
social.bluesky('myusername').should.eql('https://bsky.app/profile/myusername');
83+
});
84+
85+
it('should handle domain-based usernames', function () {
86+
social.bluesky('user.domain.com').should.eql('https://bsky.app/profile/user.domain.com');
87+
});
88+
});
89+
90+
describe('mastodon', function () {
91+
it('should return a correct concatenated URL for instance-specific handle', function () {
92+
social.mastodon('mastodon.social/@myusername').should.eql('https://mastodon.social/@myusername');
93+
});
94+
95+
it('should return a correct concatenated URL for federated handle', function () {
96+
social.mastodon('mastodon.social/@[email protected]').should.eql('https://mastodon.social/@[email protected]');
97+
});
98+
99+
it('should handle subdomains in instance URLs', function () {
100+
social.mastodon('eu.mastodon.green/@eco').should.eql('https://eu.mastodon.green/@eco');
101+
});
102+
});
103+
104+
describe('tiktok', function () {
105+
it('should return a correct concatenated URL', function () {
106+
social.tiktok('@myusername').should.eql('https://www.tiktok.com/@myusername');
107+
});
108+
109+
it('should return a correct URL for usernames without @', function () {
110+
social.tiktok('myusername').should.eql('https://www.tiktok.com/@myusername');
111+
});
112+
113+
it('should handle usernames with periods and underscores', function () {
114+
social.tiktok('@john.smith_123').should.eql('https://www.tiktok.com/@john.smith_123');
115+
});
116+
});
117+
118+
describe('youtube', function () {
119+
it('should return a correct concatenated URL for handle-based username', function () {
120+
social.youtube('@myusername').should.eql('https://www.youtube.com/@myusername');
121+
});
122+
123+
it('should return a correct concatenated URL for legacy user-based username', function () {
124+
social.youtube('user/myusername').should.eql('https://www.youtube.com/user/myusername');
125+
});
126+
127+
it('should return a correct concatenated URL for channel ID', function () {
128+
social.youtube('channel/UC4QobU6STFB0P71PMvOGN5A').should.eql('https://www.youtube.com/channel/UC4QobU6STFB0P71PMvOGN5A');
129+
});
130+
});
131+
132+
describe('instagram', function () {
133+
it('should return a correct concatenated URL', function () {
134+
social.instagram('myusername').should.eql('https://www.instagram.com/myusername');
135+
});
136+
137+
it('should handle usernames with periods and underscores', function () {
138+
social.instagram('john.smith_123').should.eql('https://www.instagram.com/john.smith_123');
139+
});
140+
});
141+
142+
describe('linkedin', function () {
143+
it('should return a correct concatenated URL', function () {
144+
social.linkedin('myusername').should.eql('https://www.linkedin.com/in/myusername');
145+
});
146+
147+
it('should handle usernames with periods, hyphens, and underscores', function () {
148+
social.linkedin('john.smith-123').should.eql('https://www.linkedin.com/in/john.smith-123');
149+
});
150+
});
39151
});

0 commit comments

Comments
 (0)