Skip to content

Commit 9c75869

Browse files
committed
Use a file system / network host instead of mocks, add failing integration test for xml
1 parent b558f75 commit 9c75869

25 files changed

+945
-498
lines changed

package-lock.json

Lines changed: 36 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"loglevel": "^1.6.3",
5959
"oniguruma": "^7.2.0",
6060
"plist": "^3.0.1",
61+
"rimraf": "^3.0.0",
6162
"unist-util-visit": "^1.4.0",
6263
"vscode-textmate": "^4.1.0"
6364
},

src/downloadExtension.js

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const fs = require('fs');
33
const path = require('path');
44
const zlib = require('zlib');
55
const util = require('util');
6-
const request = require('request');
7-
const decompress = require('decompress');
86
const processExtension = require('./processExtension');
97
const { highestBuiltinLanguageId } = require('./storeUtils');
108
const {
@@ -42,34 +40,23 @@ async function syncExtensionData({ identifier }, cache, extensionDir) {
4240
* @param {import('.').ExtensionDemand} extensionDemand
4341
* @param {*} cache
4442
* @param {string} extensionDir
43+
* @param {import('./host').Host} host
4544
*/
46-
async function downloadExtension(extensionDemand, cache, extensionDir) {
45+
async function downloadExtension(extensionDemand, cache, extensionDir, host) {
4746
const { identifier, version } = extensionDemand;
4847
const { publisher, name } = parseExtensionIdentifier(identifier);
4948
const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`;
50-
const archive = await new Promise((resolve, reject) => {
51-
request.get(url, { encoding: null }, (error, res, body) => {
52-
if (error) {
53-
return reject(error);
54-
}
55-
if (res.statusCode === 404) {
56-
return reject(
57-
new Error(`Could not find extension with publisher '${publisher}', name '${name}', and verion '${version}'.`)
58-
);
59-
}
60-
if (res.statusCode !== 200) {
61-
return reject(new Error(`Failed to download extension ${identifier} with status code ${res.statusCode}`));
62-
}
63-
if (res.headers['content-encoding'] === 'gzip') {
64-
return gunzip(body).then(resolve, reject);
65-
}
66-
67-
resolve(body);
68-
});
69-
});
49+
const response = await host.fetch(url, { encoding: null });
50+
if (response.statusCode === 404) {
51+
throw new Error(`Could not find extension with publisher '${publisher}', name '${name}', and verion '${version}'.`);
52+
}
53+
if (response.statusCode !== 200) {
54+
const details = response.body ? `. Response body:\n\n${response.body.toString('utf8')}` : '';
55+
throw new Error(`Failed to download extension ${identifier} with status code ${response.statusCode}${details}`);
56+
}
7057

7158
const extensionPath = getExtensionBasePath(identifier, extensionDir);
72-
await decompress(archive, extensionPath);
59+
await host.decompress(response.body, extensionPath);
7360
await syncExtensionData(extensionDemand, cache, extensionDir);
7461
return extensionPath;
7562
}
@@ -79,24 +66,25 @@ async function downloadExtension(extensionDemand, cache, extensionDir) {
7966
* @property {import('.').ExtensionDemand[]} extensions
8067
* @property {*} cache
8168
* @property {string} extensionDir
69+
* @property {import('./host').Host} host
8270
*/
8371

8472
/**
8573
* @param {DownloadExtensionOptions} options
8674
*/
87-
async function downloadExtensionsIfNeeded({ extensions, cache, extensionDir }) {
75+
async function downloadExtensionsIfNeeded({ extensions, cache, extensionDir, host }) {
8876
extensions = extensions.slice();
8977
while (extensions.length) {
9078
const extensionDemand = extensions.shift();
9179
const { identifier, version } = extensionDemand;
9280
const extensionPath = getExtensionBasePath(identifier, extensionDir);
9381
if (!fs.existsSync(extensionPath)) {
94-
await downloadExtension(extensionDemand, cache, extensionDir);
82+
await downloadExtension(extensionDemand, cache, extensionDir, host);
9583
continue;
9684
}
9785
const packageJson = getExtensionPackageJson(identifier, extensionDir);
9886
if (packageJson.version !== version) {
99-
await downloadExtension(extensionDemand, cache, extensionDir);
87+
await downloadExtension(extensionDemand, cache, extensionDir, host);
10088
continue;
10189
}
10290

src/host.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const request = require('request');
2+
const decompress = require('decompress');
3+
const zlib = require('zlib');
4+
const util = require('util');
5+
const gunzip = util.promisify(zlib.gunzip);
6+
7+
/**
8+
* @typedef {object} Response
9+
* @property {Buffer | undefined} body
10+
* @property {number} statusCode
11+
*/
12+
13+
/**
14+
* @typedef {object} Host
15+
* @property {(url: string, options: request.CoreOptions) => Promise<Response>} fetch
16+
* @property {(input: string | Buffer, output: string) => Promise<unknown>} decompress
17+
*/
18+
19+
/** @type {Host} */
20+
const host = {
21+
fetch: (url, options) =>
22+
new Promise((resolve, reject) => {
23+
request.get(url, options, async (error, res, body) => {
24+
if (error) {
25+
return reject(error);
26+
}
27+
if (res.statusCode !== 200) {
28+
return resolve({ body, statusCode: res.statusCode });
29+
}
30+
if (res.headers['content-encoding'] === 'gzip') {
31+
const unzipped = await gunzip(body);
32+
return resolve({ body: unzipped, statusCode: res.statusCode });
33+
}
34+
35+
resolve({ body, statusCode: res.statusCode });
36+
});
37+
}),
38+
39+
decompress
40+
};
41+
42+
module.exports = host;

src/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const fs = require('fs');
33
const path = require('path');
44
const logger = require('loglevel');
5+
const defaultHost = require('./host');
56
const visit = require('unist-util-visit');
67
const escapeHTML = require('lodash.escape');
78
const lineHighlighting = require('./lineHighlighting');
@@ -122,6 +123,7 @@ function getStylesFromSettings(settings) {
122123
* @property {(colorValue: string, theme: string) => string=} replaceColor
123124
* @property {string=} extensionDataDirectory
124125
* @property {'trace' | 'debug' | 'info' | 'warn' | 'error'=} logLevel
126+
* @property {import('./host').Host=} host
125127
*/
126128

127129
function createPlugin() {
@@ -143,7 +145,8 @@ function createPlugin() {
143145
injectStyles = true,
144146
replaceColor = x => x,
145147
extensionDataDirectory = path.resolve(__dirname, '../lib/extensions'),
146-
logLevel = 'error'
148+
logLevel = 'error',
149+
host = defaultHost
147150
} = {}
148151
) {
149152
logger.setLevel(logLevel);
@@ -162,7 +165,8 @@ function createPlugin() {
162165
await downloadExtensionsIfNeeded({
163166
extensions,
164167
cache,
165-
extensionDir: extensionDataDirectory
168+
extensionDir: extensionDataDirectory,
169+
host
166170
});
167171

168172
const grammarCache = await cache.get('grammars');

test/custom.tmLanguage.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)