2017-01-20 00:45:36 +03:00
|
|
|
"use strict";
|
2017-01-24 00:33:58 +03:00
|
|
|
|
2018-10-05 03:47:23 +03:00
|
|
|
const fs = require("fs");
|
2018-06-22 07:43:44 +03:00
|
|
|
const normalizePath = require("normalize-path");
|
2018-10-05 03:47:23 +03:00
|
|
|
const readlines = require("n-readlines");
|
2018-05-23 22:55:06 +03:00
|
|
|
const UndefinedParserError = require("../common/errors").UndefinedParserError;
|
2018-04-25 19:29:35 +03:00
|
|
|
const getSupportInfo = require("../main/support").getSupportInfo;
|
2018-01-18 10:26:27 +03:00
|
|
|
const normalizer = require("./options-normalizer");
|
2017-12-26 16:04:09 +03:00
|
|
|
const resolveParser = require("./parser").resolveParser;
|
2017-01-24 00:33:58 +03:00
|
|
|
|
2018-01-18 10:26:27 +03:00
|
|
|
const hiddenDefaults = {
|
2017-12-26 16:04:09 +03:00
|
|
|
astFormat: "estree",
|
2018-02-09 15:17:48 +03:00
|
|
|
printer: {},
|
2018-06-13 19:30:14 +03:00
|
|
|
originalText: undefined,
|
2018-02-09 15:17:48 +03:00
|
|
|
locStart: null,
|
|
|
|
locEnd: null
|
2017-01-05 04:45:28 +03:00
|
|
|
};
|
2016-12-23 21:38:10 +03:00
|
|
|
|
|
|
|
// Copy options and fill in default values.
|
2018-01-18 10:26:27 +03:00
|
|
|
function normalize(options, opts) {
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
|
const rawOptions = Object.assign({}, options);
|
2018-01-27 19:24:25 +03:00
|
|
|
|
|
|
|
const supportOptions = getSupportInfo(null, {
|
2018-04-25 19:29:35 +03:00
|
|
|
plugins: options.plugins,
|
2018-02-28 19:14:30 +03:00
|
|
|
showUnreleased: true,
|
|
|
|
showDeprecated: true
|
2018-01-27 19:24:25 +03:00
|
|
|
}).options;
|
|
|
|
const defaults = supportOptions.reduce(
|
|
|
|
(reduced, optionInfo) =>
|
2018-08-31 06:26:07 +03:00
|
|
|
optionInfo.default !== undefined
|
|
|
|
? Object.assign(reduced, { [optionInfo.name]: optionInfo.default })
|
|
|
|
: reduced,
|
2018-01-27 19:24:25 +03:00
|
|
|
Object.assign({}, hiddenDefaults)
|
|
|
|
);
|
2018-01-18 10:26:27 +03:00
|
|
|
|
2018-05-23 22:55:06 +03:00
|
|
|
if (!rawOptions.parser) {
|
|
|
|
if (!rawOptions.filepath) {
|
2018-05-29 08:43:44 +03:00
|
|
|
const logger = opts.logger || console;
|
|
|
|
logger.warn(
|
2018-12-27 16:05:19 +03:00
|
|
|
"No parser and no filepath given, using 'babel' the parser now " +
|
2018-05-29 08:43:44 +03:00
|
|
|
"but this will throw an error in the future. " +
|
|
|
|
"Please specify a parser or a filepath so one can be inferred."
|
2018-01-18 10:26:27 +03:00
|
|
|
);
|
2018-12-27 16:05:19 +03:00
|
|
|
rawOptions.parser = "babel";
|
2018-05-29 08:43:44 +03:00
|
|
|
} else {
|
|
|
|
rawOptions.parser = inferParser(rawOptions.filepath, rawOptions.plugins);
|
|
|
|
if (!rawOptions.parser) {
|
|
|
|
throw new UndefinedParserError(
|
|
|
|
`No parser could be inferred for file: ${rawOptions.filepath}`
|
|
|
|
);
|
|
|
|
}
|
2017-11-03 10:06:25 +03:00
|
|
|
}
|
2017-06-28 19:29:47 +03:00
|
|
|
}
|
|
|
|
|
2018-02-28 19:14:30 +03:00
|
|
|
const parser = resolveParser(
|
2018-05-23 22:55:06 +03:00
|
|
|
normalizer.normalizeApiOptions(
|
|
|
|
rawOptions,
|
|
|
|
[supportOptions.find(x => x.name === "parser")],
|
|
|
|
{ passThrough: true, logger: false }
|
|
|
|
)
|
2018-02-28 19:14:30 +03:00
|
|
|
);
|
2018-02-09 15:17:48 +03:00
|
|
|
rawOptions.astFormat = parser.astFormat;
|
|
|
|
rawOptions.locEnd = parser.locEnd;
|
|
|
|
rawOptions.locStart = parser.locStart;
|
2018-02-28 19:14:30 +03:00
|
|
|
|
2018-02-27 16:20:02 +03:00
|
|
|
const plugin = getPlugin(rawOptions);
|
|
|
|
rawOptions.printer = plugin.printers[rawOptions.astFormat];
|
2017-12-26 16:04:09 +03:00
|
|
|
|
2018-02-27 16:20:02 +03:00
|
|
|
const pluginDefaults = supportOptions
|
|
|
|
.filter(
|
|
|
|
optionInfo =>
|
2019-09-12 18:52:48 +03:00
|
|
|
optionInfo.pluginDefaults &&
|
|
|
|
optionInfo.pluginDefaults[plugin.name] !== undefined
|
2018-02-27 16:20:02 +03:00
|
|
|
)
|
|
|
|
.reduce(
|
|
|
|
(reduced, optionInfo) =>
|
|
|
|
Object.assign(reduced, {
|
|
|
|
[optionInfo.name]: optionInfo.pluginDefaults[plugin.name]
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
const mixedDefaults = Object.assign({}, defaults, pluginDefaults);
|
|
|
|
|
|
|
|
Object.keys(mixedDefaults).forEach(k => {
|
2018-01-18 10:26:27 +03:00
|
|
|
if (rawOptions[k] == null) {
|
2018-02-27 16:20:02 +03:00
|
|
|
rawOptions[k] = mixedDefaults[k];
|
2017-01-05 04:45:28 +03:00
|
|
|
}
|
|
|
|
});
|
2017-01-20 01:35:12 +03:00
|
|
|
|
2018-01-18 10:26:27 +03:00
|
|
|
if (rawOptions.parser === "json") {
|
|
|
|
rawOptions.trailingComma = "none";
|
|
|
|
}
|
|
|
|
|
|
|
|
return normalizer.normalizeApiOptions(
|
|
|
|
rawOptions,
|
2018-01-27 19:24:25 +03:00
|
|
|
supportOptions,
|
2018-01-18 10:26:27 +03:00
|
|
|
Object.assign({ passThrough: Object.keys(hiddenDefaults) }, opts)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-04-25 19:29:35 +03:00
|
|
|
function getPlugin(options) {
|
2018-07-18 00:01:44 +03:00
|
|
|
const { astFormat } = options;
|
2018-04-25 19:29:35 +03:00
|
|
|
|
|
|
|
if (!astFormat) {
|
|
|
|
throw new Error("getPlugin() requires astFormat to be set");
|
|
|
|
}
|
|
|
|
const printerPlugin = options.plugins.find(
|
2018-05-24 21:30:45 +03:00
|
|
|
plugin => plugin.printers && plugin.printers[astFormat]
|
2018-04-25 19:29:35 +03:00
|
|
|
);
|
|
|
|
if (!printerPlugin) {
|
|
|
|
throw new Error(`Couldn't find plugin for AST format "${astFormat}"`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return printerPlugin;
|
|
|
|
}
|
|
|
|
|
2018-10-05 03:47:23 +03:00
|
|
|
function getInterpreter(filepath) {
|
|
|
|
if (typeof filepath !== "string") {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
let fd;
|
|
|
|
try {
|
|
|
|
fd = fs.openSync(filepath, "r");
|
|
|
|
} catch (err) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const liner = new readlines(fd);
|
|
|
|
const firstLine = liner.next().toString("utf8");
|
|
|
|
|
|
|
|
// #!/bin/env node, #!/usr/bin/env node
|
|
|
|
const m1 = firstLine.match(/^#!\/(?:usr\/)?bin\/env\s+(\S+)/);
|
|
|
|
if (m1) {
|
|
|
|
return m1[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// #!/bin/node, #!/usr/bin/node, #!/usr/local/bin/node
|
|
|
|
const m2 = firstLine.match(/^#!\/(?:usr\/(?:local\/)?)?bin\/(\S+)/);
|
|
|
|
if (m2) {
|
|
|
|
return m2[1];
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
} catch (err) {
|
|
|
|
// There are some weird cases where paths are missing, causing Jest
|
|
|
|
// failures. It's unclear what these correspond to in the real world.
|
|
|
|
return "";
|
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
// There are some weird cases where paths are missing, causing Jest
|
|
|
|
// failures. It's unclear what these correspond to in the real world.
|
|
|
|
fs.closeSync(fd);
|
|
|
|
} catch (err) {
|
|
|
|
// nop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-18 10:26:27 +03:00
|
|
|
function inferParser(filepath, plugins) {
|
2018-06-22 07:43:44 +03:00
|
|
|
const filepathParts = normalizePath(filepath).split("/");
|
|
|
|
const filename = filepathParts[filepathParts.length - 1].toLowerCase();
|
2018-01-18 10:26:27 +03:00
|
|
|
|
2018-10-05 03:47:23 +03:00
|
|
|
// If the file has no extension, we can try to infer the language from the
|
|
|
|
// interpreter in the shebang line, if any; but since this requires FS access,
|
|
|
|
// do it last.
|
2018-01-18 10:26:27 +03:00
|
|
|
const language = getSupportInfo(null, {
|
2018-04-25 19:29:35 +03:00
|
|
|
plugins
|
2018-01-18 10:26:27 +03:00
|
|
|
}).languages.find(
|
|
|
|
language =>
|
2018-02-24 11:00:51 +03:00
|
|
|
language.since !== null &&
|
2018-07-01 11:18:08 +03:00
|
|
|
((language.extensions &&
|
|
|
|
language.extensions.some(extension => filename.endsWith(extension))) ||
|
2018-01-18 10:26:27 +03:00
|
|
|
(language.filenames &&
|
2018-10-05 03:47:23 +03:00
|
|
|
language.filenames.find(name => name.toLowerCase() === filename)) ||
|
|
|
|
(filename.indexOf(".") === -1 &&
|
|
|
|
language.interpreters &&
|
|
|
|
language.interpreters.indexOf(getInterpreter(filepath)) !== -1))
|
2018-01-18 10:26:27 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
return language && language.parsers[0];
|
2017-01-20 21:12:37 +03:00
|
|
|
}
|
2017-01-20 00:45:36 +03:00
|
|
|
|
2018-05-09 19:53:44 +03:00
|
|
|
module.exports = { normalize, hiddenDefaults, inferParser };
|