Skip to content

Commit 7146d9c

Browse files
committed
Write options validator
1 parent 77deeb6 commit 7146d9c

File tree

6 files changed

+102
-66
lines changed

6 files changed

+102
-66
lines changed

scripts/scrapeBuiltinExtensions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const fs = require('fs');
33
const util = require('util');
44
const glob = require('glob');
55
const path = require('path');
6-
const processExtension = require('../src/processExtension');
6+
const { processExtension } = require('../src/processExtension');
77
const { requirePlistOrJson } = require('../src/utils');
88

99
const copyFile = util.promisify(fs.copyFile);

src/host.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,8 @@
11
// @ts-check
2-
const request = require('request');
32
const decompress = require('decompress');
4-
const { gunzip } = require('./utils');
53

64
/** @type {Host} */
75
const host = {
8-
fetch: (url, options) =>
9-
new Promise((resolve, reject) => {
10-
request.get(url, options, async (error, res, body) => {
11-
if (error) {
12-
return reject(error);
13-
}
14-
if (res.statusCode !== 200) {
15-
return resolve({ body, statusCode: res.statusCode });
16-
}
17-
if (res.headers['content-encoding'] === 'gzip') {
18-
const unzipped = await gunzip(body);
19-
return resolve({ body: unzipped, statusCode: res.statusCode });
20-
}
21-
22-
resolve({ body, statusCode: res.statusCode });
23-
});
24-
}),
25-
266
decompress
277
};
288

src/index.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const logger = require('loglevel');
55
const defaultHost = require('./host');
66
const visit = require('unist-util-visit');
77
const escapeHTML = require('lodash.escape');
8+
const validateOptions = require('./validateOptions');
89
const createGetRegistry = require('./createGetRegistry');
910
const tokenizeWithTheme = require('./tokenizeWithTheme');
1011
const getPossibleThemes = require('./getPossibleThemes');
@@ -16,19 +17,19 @@ const { getGrammar, getScope } = require('./storeUtils');
1617
const { renderHTML, span, code, pre, style, mergeAttributes, TriviaRenderFlags } = require('./renderers/html');
1718
const { joinClassNames, ruleset, media, declaration } = require('./renderers/css');
1819
const {
19-
once,
20-
deprecationNotice,
2120
getThemeClassName,
2221
getThemeClassNames,
2322
getStylesFromThemeSettings,
2423
flatMap,
2524
groupConditions,
26-
convertLegacyThemeOption
25+
convertLegacyThemeOption,
26+
createOnce
2727
} = require('./utils');
2828
const styles = fs.readFileSync(path.resolve(__dirname, '../styles.css'), 'utf8');
2929

3030
function createPlugin() {
3131
const getRegistry = createGetRegistry();
32+
const once = createOnce();
3233

3334
/**
3435
* @param {{ markdownAST: MDASTNode, markdownNode: MarkdownNode, cache: any }} _
@@ -54,15 +55,24 @@ function createPlugin() {
5455
await once(async () => {
5556
logger.setLevel(logLevel);
5657
if (legacyTheme) {
57-
deprecationNotice(
58-
`The 'colorTheme' option has been replaced by 'theme' and will be removed in a future version. ` +
59-
`See https://github.com/andrewbranch/gatsby-remark-vscode/blob/master/MIGRATING.md for details.`,
60-
'colorThemeWarning'
61-
);
6258
theme = convertLegacyThemeOption(legacyTheme);
6359
}
6460

65-
await processExtensions(extensions, markdownNode.fileAbsolutePath, host, cache);
61+
validateOptions({
62+
theme,
63+
colorTheme: legacyTheme,
64+
wrapperClassName,
65+
languageAliases,
66+
extensions,
67+
getLineClassName,
68+
injectStyles,
69+
replaceColor,
70+
logLevel,
71+
host,
72+
getLineTransformers
73+
});
74+
75+
await processExtensions(extensions, host, cache);
6676
}, 'setup');
6777

6878
const lineTransformers = getLineTransformers({
@@ -235,6 +245,7 @@ function createPlugin() {
235245
});
236246
}
237247
}
248+
238249
return textmateHighlight;
239250
}
240251

src/processExtension.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// @ts-check
22
const path = require('path');
3-
const { getLanguageNames, requireJson, requirePlistOrJson, isRelativePath, readFile } = require('./utils');
3+
const { getLanguageNames, requireJson, requirePlistOrJson, readFile } = require('./utils');
44
const { highestBuiltinLanguageId } = require('./storeUtils');
55
const unzipDir = path.resolve(__dirname, '../lib/extensions');
66
let languageId = highestBuiltinLanguageId + 1;
77

88
/**
9-
* @param {string} packageJsonPath
9+
* @param {string} packageJsonPath
1010
*/
1111
async function processExtension(packageJsonPath) {
1212
const packageJson = requireJson(packageJsonPath);
@@ -83,14 +83,10 @@ async function mergeCache(cache, key, value) {
8383

8484
/**
8585
* @param {string} specifier
86-
* @param {string} contextDir
8786
* @param {Host} host
8887
*/
89-
async function getExtensionPath(specifier, contextDir, host) {
90-
const absolute = path.isAbsolute(specifier) ? specifier :
91-
isRelativePath(specifier) ? path.normalize(path.join(contextDir, specifier)) :
92-
require.resolve(specifier);
93-
88+
async function getExtensionPath(specifier, host) {
89+
const absolute = path.isAbsolute(specifier) ? specifier : require.resolve(specifier);
9490
const ext = path.extname(absolute);
9591
if (ext.toLowerCase() === '.vsix' || ext.toLowerCase() === '.zip') {
9692
const outDir = path.join(unzipDir, path.basename(absolute, ext));
@@ -103,18 +99,19 @@ async function getExtensionPath(specifier, contextDir, host) {
10399

104100
/**
105101
* @param {string[]} extensions
106-
* @param {string} contextDir
107102
* @param {Host} host
108103
* @param {*} cache
109104
*/
110-
function processExtensions(extensions, contextDir, host, cache) {
111-
return Promise.all(extensions.map(async extension => {
112-
const packageJsonPath = path.join(await getExtensionPath(extension, contextDir, host), 'package.json');
113-
const { grammars, themes } = await processExtension(packageJsonPath);
114-
Object.keys(grammars).forEach(scopeName => (grammars[scopeName].languageId = languageId++));
115-
await mergeCache(cache, 'grammars', grammars);
116-
await mergeCache(cache, 'themes', themes);
117-
}));
105+
function processExtensions(extensions, host, cache) {
106+
return Promise.all(
107+
extensions.map(async extension => {
108+
const packageJsonPath = path.join(await getExtensionPath(extension, host), 'package.json');
109+
const { grammars, themes } = await processExtension(packageJsonPath);
110+
Object.keys(grammars).forEach(scopeName => (grammars[scopeName].languageId = languageId++));
111+
await mergeCache(cache, 'grammars', grammars);
112+
await mergeCache(cache, 'themes', themes);
113+
})
114+
);
118115
}
119116

120117
module.exports = { processExtension, processExtensions };

src/utils.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -291,28 +291,28 @@ function convertLegacyThemeSettings(themeSettings) {
291291
};
292292
}
293293

294-
const fns = new Set();
295-
/**
296-
* @template {void | Promise<void>} T
297-
* @param {() => T} fn
298-
* @param {any=} key
299-
* @returns {T | undefined}
300-
*/
301-
function once(fn, key = fn) {
302-
if (!fns.has(key)) {
303-
fns.add(key);
304-
return fn();
305-
}
294+
function createOnce() {
295+
const onceFns = new Set();
296+
/**
297+
* @template {void | Promise<void>} T
298+
* @param {() => T} fn
299+
* @param {any=} key
300+
* @returns {T | undefined}
301+
*/
302+
return function once(fn, key = fn) {
303+
if (!onceFns.has(key)) {
304+
onceFns.add(key);
305+
return fn();
306+
}
307+
};
306308
}
307309

308-
function deprecationNotice(message, key = message) {
309-
once(() => {
310-
logger.warn(`Deprecation notice: ${message}`);
311-
}, key);
310+
function deprecationNotice(message) {
311+
logger.warn(`Deprecation notice: ${message}`);
312312
}
313313

314314
/**
315-
* @param {string} p
315+
* @param {string} p
316316
*/
317317
function isRelativePath(p) {
318318
return /^\.\.?[\\/]/.test(p);
@@ -342,7 +342,7 @@ module.exports = {
342342
groupConditions,
343343
getStylesFromThemeSettings,
344344
convertLegacyThemeOption,
345-
once,
346345
deprecationNotice,
347-
isRelativePath
346+
isRelativePath,
347+
createOnce
348348
};

src/validateOptions.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const { EOL } = require('os');
2+
const { deprecationNotice, isRelativePath } = require('./utils');
3+
4+
/**
5+
* @param {PluginOptions} options
6+
*/
7+
function validateOptions(options) {
8+
/** @type {string[]} */
9+
const errors = [];
10+
11+
if (options.colorTheme) {
12+
deprecationNotice(
13+
`The 'colorTheme' option has been replaced by 'theme' and will be removed in a future version. ` +
14+
`See https://github.com/andrewbranch/gatsby-remark-vscode/blob/master/MIGRATING.md for details.`
15+
);
16+
}
17+
18+
if (options.extensions) {
19+
if (options.extensions.some(ext => typeof ext !== 'string')) {
20+
addError(
21+
'extensions',
22+
'Each element must be a string. See ' +
23+
'https://github.com/andrewbranch/gatsby-remark-vscode/tree/master/MIGRATING.md ' +
24+
'for details.'
25+
);
26+
}
27+
28+
options.extensions.forEach(extension => {
29+
if (isRelativePath(extension)) {
30+
addError('extensions', `Extension paths must be absolute. Received '${extension}'.`);
31+
}
32+
});
33+
}
34+
35+
if (errors.length) {
36+
throw new Error(errors.join(EOL.repeat(2)));
37+
}
38+
39+
/**
40+
* @param {string} optionNme
41+
* @param {string} message
42+
*/
43+
function addError(optionNme, message) {
44+
errors.push(`Invalid option '${optionNme}': ${message}`);
45+
}
46+
}
47+
48+
module.exports = validateOptions;

0 commit comments

Comments
 (0)