Skip to content

Commit a0555f7

Browse files
authored
Fetch live resolver urls in background (#529)
1 parent 1ab8cfd commit a0555f7

File tree

14 files changed

+257
-39
lines changed

14 files changed

+257
-39
lines changed

packages/core/click-handler.js

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ const LINK_SELECTOR = '.octolinker-link';
1111
const $body = $('body');
1212
let matches;
1313

14-
function openUrl(url, newWindow = false, newWindowActive = true) {
14+
function openUrl(event, url) {
1515
if (!url) {
1616
return;
1717
}
1818

19+
const newWindow =
20+
storage.get('newWindow') ||
21+
event.metaKey ||
22+
event.ctrlKey ||
23+
event.which === 2;
24+
const newWindowActive = storage.get('newWindowActive');
25+
1926
if (newWindow) {
2027
chrome.runtime.sendMessage({
2128
type: 'newTab',
@@ -29,39 +36,52 @@ function openUrl(url, newWindow = false, newWindowActive = true) {
2936
}
3037
}
3138

32-
function getResolverUrls(urls) {
33-
const BASE_URL = 'https://github.com';
34-
35-
return [].concat(urls).map(url => {
36-
if (!url) {
39+
function getResolverUrls(event, urls) {
40+
return [].concat(urls).map(item => {
41+
if (!item) {
3742
return null;
3843
}
3944

4045
// github-search resolver returns a function
41-
if (typeof url === 'function') {
46+
if (item.type === 'function') {
4247
return {
43-
func: url,
48+
func: item.handler,
4449
};
4550
}
4651

4752
// Live-query resolver results
48-
if (url.startsWith('https://githublinker.herokuapp.com')) {
53+
if (item.type === 'registry') {
54+
let cacheResult;
55+
56+
try {
57+
cacheResult = global.__ocotlinker_cache[item.registry][item.target];
58+
} catch (error) {
59+
//
60+
}
61+
62+
if (cacheResult) {
63+
openUrl(event, cacheResult);
64+
return [];
65+
}
66+
4967
return {
50-
url,
68+
url: `https://githublinker.herokuapp.com/q/${item.registry}/${
69+
item.target
70+
}`,
5171
method: 'GET',
5272
};
5373
}
5474

5575
// Relative file
56-
if (url.startsWith('{BASE_URL}') || url.startsWith(BASE_URL)) {
76+
if (item.type === 'internal-link') {
5777
return {
58-
url: url.replace('{BASE_URL}', BASE_URL),
78+
url: item.url,
5979
};
6080
}
6181

6282
// External urls
6383
return {
64-
url: `https://githublinker.herokuapp.com/ping?url=${url}`,
84+
url: `https://githublinker.herokuapp.com/ping?url=${item.url}`,
6585
method: 'GET',
6686
};
6787
});
@@ -83,7 +103,7 @@ async function onClick(event) {
83103

84104
showTooltip($tooltipTarget, PROCESS);
85105

86-
const urls = getResolverUrls(found.urls);
106+
const urls = getResolverUrls(event, found.urls);
87107

88108
if (!urls.length) {
89109
return;
@@ -93,14 +113,7 @@ async function onClick(event) {
93113
const { url, res } = await fetch(urls);
94114

95115
showTooltip($tooltipTarget, RESOLVED);
96-
97-
const newWindow =
98-
storage.get('newWindow') ||
99-
event.metaKey ||
100-
event.ctrlKey ||
101-
event.which === 2;
102-
const newWindowActive = storage.get('newWindowActive');
103-
openUrl((res || {}).url || url, newWindow, newWindowActive);
116+
openUrl(event, (res || {}).url || url);
104117
removeTooltip($tooltipTarget);
105118
} catch (err) {
106119
showTooltip($tooltipTarget, SORRY);

packages/core/loader.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
function createStore(json, payload) {
2+
const store = global.__ocotlinker_cache || {};
3+
4+
payload.forEach(({ registry, target }, index) => {
5+
store[registry] = store[registry] || {};
6+
store[registry][target] = json[index];
7+
});
8+
9+
global.__ocotlinker_cache = store;
10+
}
11+
12+
async function runLiveQuery(matches) {
13+
if (!matches.length) {
14+
return [];
15+
}
16+
17+
const payload = [].concat(...matches.map(match => match.urls));
18+
19+
const response = await fetch('https://githublinker.herokuapp.com/bulk', {
20+
method: 'POST',
21+
body: JSON.stringify(payload),
22+
headers: new Headers({
23+
'Content-Type': 'application/json',
24+
}),
25+
});
26+
const json = await response.json();
27+
28+
createStore(json, payload);
29+
}
30+
31+
function filterLiveResolver(matches) {
32+
return matches.reduce((memo, match) => {
33+
match.urls.forEach(url => {
34+
if (url.type !== 'registry') {
35+
return;
36+
}
37+
38+
memo.push(match);
39+
});
40+
41+
return memo;
42+
}, []);
43+
}
44+
45+
export default function(matches) {
46+
const registryMatch = filterLiveResolver(matches);
47+
runLiveQuery(registryMatch);
48+
}

packages/core/octo-linker.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import BlobReader from '@octolinker/blob-reader';
44
import insertLink from '@octolinker/helper-insert-link';
55
import * as storage from '@octolinker/helper-settings';
66
import helperSortUrls from '@octolinker/helper-sort-urls';
7+
import normaliseResolverResults from '@octolinker/helper-normalise-resolver-results';
78
import notification from './notification';
89
import clickHandler from './click-handler';
910
import Plugins from './plugin-manager.js';
1011
import debugMode from './debug-mode.js';
12+
import loader from './loader.js';
1113
import * as loadPlugins from './load-plugins';
1214

1315
function initialize(self) {
@@ -50,19 +52,19 @@ async function run(self) {
5052
matches = matches
5153
.filter(result => result !== undefined)
5254
.map(({ link, urls }) => {
53-
let finalUrls = urls;
54-
55-
// Some urls are single object e.g. live-resolver-query results
56-
if (Array.isArray(urls)) {
57-
finalUrls = helperSortUrls(urls, link.innerText);
58-
}
55+
const urlsSorted = helperSortUrls(urls, link.innerText);
5956

6057
return {
6158
link,
62-
urls: finalUrls,
59+
urls: normaliseResolverResults(urlsSorted),
6360
};
6461
});
6562

63+
// Prefetch live resolver results in background
64+
requestIdleCallback(() => {
65+
loader(matches);
66+
});
67+
6668
clickHandler(matches);
6769
}
6870

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@octolinker/helper-insert-link": "1.0.0",
1111
"@octolinker/helper-settings": "1.0.0",
1212
"@octolinker/helper-sort-urls": "1.0.0",
13+
"@octolinker/helper-normalise-resolver-results": "1.0.0",
1314
"@octolinker/plugin-bower-manifest": "1.0.0",
1415
"@octolinker/plugin-composer-manifest": "1.0.0",
1516
"@octolinker/plugin-css": "1.0.0",

packages/helper-insert-link/__tests__/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,12 @@ describe('insert-link', () => {
101101
expect(helper(input).matches.length).toBe(1);
102102
expect(helper(input).matches).toMatchSnapshot();
103103
});
104+
105+
it('returns an array with values', () => {
106+
const input = 'foo <span>"bar"</span>';
107+
108+
fakePlugin.resolve.mockReturnValue([undefined, null, '', 'bar']);
109+
expect(helper(input).matches[0].urls).toEqual(['bar']);
110+
});
104111
});
105112
});

packages/helper-insert-link/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,15 @@ export default function(blob, regex, plugin, meta = {}) {
175175
.slice(1)
176176
.map(item => item.replace(/['|"]/g, ''));
177177

178+
let urls = plugin.resolve(blob.path, values, meta);
179+
180+
if (Array.isArray(urls)) {
181+
urls = urls.filter(Boolean);
182+
}
183+
178184
matches.push({
179185
link,
180-
urls: plugin.resolve(blob.path, values, meta),
186+
urls,
181187
});
182188

183189
return node;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`normaliseResolverResults converts [object Object] 1`] = `
4+
Array [
5+
Object {
6+
"registry": "npm",
7+
"target": "foo",
8+
"type": "registry",
9+
},
10+
]
11+
`;
12+
13+
exports[`normaliseResolverResults converts {BASE_URL}/foo/bar/blob/1ab8cfd3b65d3b43335130d6cefbf8c62482680f/file.js 1`] = `
14+
Array [
15+
Object {
16+
"branch": "1ab8cfd3b65d3b43335130d6cefbf8c62482680f",
17+
"path": "file.js",
18+
"repo": "bar",
19+
"type": "internal-link",
20+
"url": "https://github.com/foo/bar/blob/1ab8cfd3b65d3b43335130d6cefbf8c62482680f/file.js",
21+
"user": "foo",
22+
},
23+
]
24+
`;
25+
26+
exports[`normaliseResolverResults converts {BASE_URL}/foo/bar/blob/master/file.js 1`] = `
27+
Array [
28+
Object {
29+
"branch": "master",
30+
"path": "file.js",
31+
"repo": "bar",
32+
"type": "internal-link",
33+
"url": "https://github.com/foo/bar/blob/master/file.js",
34+
"user": "foo",
35+
},
36+
]
37+
`;
38+
39+
exports[`normaliseResolverResults converts function () {} 1`] = `
40+
Array [
41+
Object {
42+
"handler": [Function],
43+
"type": "function",
44+
},
45+
]
46+
`;
47+
48+
exports[`normaliseResolverResults converts https://foosearch.org/ 1`] = `
49+
Array [
50+
Object {
51+
"type": "external-link",
52+
"url": "https://foosearch.org/",
53+
},
54+
]
55+
`;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import ghParse from 'github-url-parse';
2+
3+
const BASE_URL = 'https://github.com';
4+
5+
// Resource within this repositroy
6+
const internal = url => {
7+
const fullUrl = url.replace('{BASE_URL}', BASE_URL);
8+
const { user, repo, branch, path } = ghParse(fullUrl);
9+
10+
return {
11+
type: 'internal-link',
12+
url: fullUrl,
13+
user,
14+
repo,
15+
branch,
16+
path,
17+
};
18+
};
19+
20+
// An external url like a documenation page
21+
const external = url => ({
22+
type: 'external-link',
23+
url,
24+
});
25+
26+
// Async resolver
27+
const func = handler => ({
28+
type: 'function',
29+
handler,
30+
});
31+
32+
// Needs to be validated through https://githublinker.herokuapp.com/
33+
const registry = ({ registry: type, target }) => ({
34+
type: 'registry',
35+
registry: type,
36+
target,
37+
});
38+
39+
export default function(urls) {
40+
return [].concat(urls).map(url => {
41+
if (typeof url === 'string') {
42+
if (url.startsWith('{BASE_URL}')) {
43+
return internal(url);
44+
}
45+
46+
return external(url);
47+
}
48+
49+
if (url.registry) {
50+
return registry(url);
51+
}
52+
53+
if (typeof url === 'function') {
54+
return func(url);
55+
}
56+
});
57+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "@octolinker/helper-normalise-resolver-results",
3+
"version": "1.0.0",
4+
"description": "",
5+
"repository": "https://github.com/octolinker/octolinker/tree/master/packages/helper-normalise-resolver-results",
6+
"license": "MIT",
7+
"main": "./index.js",
8+
"dependencies": {
9+
"github-url-parse": "^0.1.0"
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import normaliseResolverResults from './index.js';
2+
3+
describe('normaliseResolverResults', () => {
4+
test.each([
5+
'{BASE_URL}/foo/bar/blob/master/file.js',
6+
'{BASE_URL}/foo/bar/blob/1ab8cfd3b65d3b43335130d6cefbf8c62482680f/file.js',
7+
'https://foosearch.org/',
8+
{ registry: 'npm', target: 'foo' },
9+
() => {},
10+
])('converts %s', url => {
11+
expect(normaliseResolverResults([url])).toMatchSnapshot();
12+
});
13+
});

packages/plugin-java/__tests__/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ describe('Java', () => {
1616
});
1717

1818
it('resolves community packages', () => {
19-
expect(Java.resolve(path, ['com.company.app'])).toBe(
20-
'https://githublinker.herokuapp.com/q/java/com.company.app',
21-
);
19+
expect(Java.resolve(path, ['com.company.app'])).toEqual({
20+
registry: 'java',
21+
target: 'com.company.app',
22+
});
2223
});
2324
});

0 commit comments

Comments
 (0)