284 lines
8.1 KiB
JavaScript
284 lines
8.1 KiB
JavaScript
"use strict";
|
|
|
|
const util = require("./util");
|
|
const dedent = require("dedent");
|
|
const semver = require("semver");
|
|
const currentVersion = require("../../package.json").version;
|
|
const loadPlugins = require("./load-plugins");
|
|
|
|
const CATEGORY_GLOBAL = "Global";
|
|
const CATEGORY_SPECIAL = "Special";
|
|
|
|
/**
|
|
* @typedef {Object} OptionInfo
|
|
* @property {string} since - available since version
|
|
* @property {string} category
|
|
* @property {'int' | 'boolean' | 'choice' | 'path'} type
|
|
* @property {boolean?} deprecated - deprecated since version
|
|
* @property {OptionRedirectInfo?} redirect - redirect deprecated option
|
|
* @property {string} description
|
|
* @property {string?} oppositeDescription - for `false` option
|
|
* @property {OptionValueInfo} default
|
|
* @property {OptionRangeInfo?} range - for type int
|
|
* @property {OptionChoiceInfo?} choices - for type choice
|
|
*
|
|
* @typedef {number | boolean | string} OptionValue
|
|
* @typedef {OptionValue | Array<{ since: string, value: OptionValue}>} OptionValueInfo
|
|
*
|
|
* @typedef {Object} OptionRedirectInfo
|
|
* @property {string} option
|
|
* @property {OptionValue} value
|
|
*
|
|
* @typedef {Object} OptionRangeInfo
|
|
* @property {number} start - recommended range start
|
|
* @property {number} end - recommended range end
|
|
* @property {number} step - recommended range step
|
|
*
|
|
* @typedef {Object} OptionChoiceInfo
|
|
* @property {boolean | string} value - boolean for the option that is originally boolean type
|
|
* @property {string?} description - undefined if redirect
|
|
* @property {string?} since - undefined if available since the first version of the option
|
|
* @property {string?} deprecated - deprecated since version
|
|
* @property {OptionValueInfo?} redirect - redirect deprecated value
|
|
*/
|
|
/** @type {{ [name: string]: OptionInfo } */
|
|
const supportOptions = {
|
|
cursorOffset: {
|
|
since: "1.4.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "int",
|
|
default: -1,
|
|
range: { start: -1, end: Infinity, step: 1 },
|
|
description: dedent`
|
|
Print (to stderr) where a cursor at the given position would move to after formatting.
|
|
This option cannot be used with --range-start and --range-end.
|
|
`
|
|
},
|
|
filepath: {
|
|
since: "1.4.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "path",
|
|
default: undefined,
|
|
description:
|
|
"Specify the input filepath. This will be used to do parser inference."
|
|
},
|
|
insertPragma: {
|
|
since: "1.8.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Insert @format pragma into file's first docblock comment."
|
|
},
|
|
parser: {
|
|
since: "0.0.10",
|
|
category: CATEGORY_GLOBAL,
|
|
type: "choice",
|
|
default: "babylon",
|
|
description: "Which parser to use.",
|
|
choices: [
|
|
{ value: "babylon", description: "JavaScript" },
|
|
{ value: "flow", description: "Flow" },
|
|
{ value: "typescript", since: "1.4.0", description: "TypeScript" },
|
|
{ value: "css", since: "1.7.1", description: "CSS" },
|
|
{
|
|
value: "postcss",
|
|
since: "1.4.0",
|
|
description: "CSS/Less/SCSS",
|
|
deprecated: "1.7.1",
|
|
redirect: "css"
|
|
},
|
|
{ value: "less", since: "1.7.1", description: "Less" },
|
|
{ value: "scss", since: "1.7.1", description: "SCSS" },
|
|
{ value: "json", since: "1.5.0", description: "JSON" },
|
|
{ value: "graphql", since: "1.5.0", description: "GraphQL" },
|
|
{ value: "markdown", since: "1.8.0", description: "Markdown" }
|
|
]
|
|
},
|
|
printWidth: {
|
|
since: "0.0.0",
|
|
category: CATEGORY_GLOBAL,
|
|
type: "int",
|
|
default: 80,
|
|
description: "The line length where Prettier will try wrap.",
|
|
range: { start: 0, end: Infinity, step: 1 }
|
|
},
|
|
rangeEnd: {
|
|
since: "1.4.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "int",
|
|
default: Infinity,
|
|
range: { start: 0, end: Infinity, step: 1 },
|
|
description: dedent`
|
|
Format code ending at a given character offset (exclusive).
|
|
The range will extend forwards to the end of the selected statement.
|
|
This option cannot be used with --cursor-offset.
|
|
`
|
|
},
|
|
rangeStart: {
|
|
since: "1.4.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "int",
|
|
default: 0,
|
|
range: { start: 0, end: Infinity, step: 1 },
|
|
description: dedent`
|
|
Format code starting at a given character offset.
|
|
The range will extend backwards to the start of the first line containing the selected statement.
|
|
This option cannot be used with --cursor-offset.
|
|
`
|
|
},
|
|
requirePragma: {
|
|
since: "1.7.0",
|
|
category: CATEGORY_SPECIAL,
|
|
type: "boolean",
|
|
default: false,
|
|
description: dedent`
|
|
Require either '@prettier' or '@format' to be present in the file's first docblock comment
|
|
in order for it to be formatted.
|
|
`
|
|
},
|
|
tabWidth: {
|
|
type: "int",
|
|
category: CATEGORY_GLOBAL,
|
|
default: 2,
|
|
description: "Number of spaces per indentation level.",
|
|
range: { start: 0, end: Infinity, step: 1 }
|
|
},
|
|
useFlowParser: {
|
|
since: "0.0.0",
|
|
category: CATEGORY_GLOBAL,
|
|
type: "boolean",
|
|
default: false,
|
|
deprecated: "0.0.10",
|
|
description: "Use flow parser.",
|
|
redirect: { option: "parser", value: "flow" }
|
|
},
|
|
useTabs: {
|
|
since: "1.0.0",
|
|
category: CATEGORY_GLOBAL,
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Indent with tabs instead of spaces."
|
|
}
|
|
};
|
|
|
|
function getSupportInfo(version, opts) {
|
|
opts = Object.assign(
|
|
{
|
|
plugins: [],
|
|
pluginsLoaded: false,
|
|
showUnreleased: false,
|
|
showDeprecated: false
|
|
},
|
|
opts
|
|
);
|
|
|
|
if (!version) {
|
|
version = currentVersion;
|
|
}
|
|
|
|
const plugins = opts.pluginsLoaded ? opts.plugins : loadPlugins(opts.plugins);
|
|
|
|
const options = util
|
|
.arrayify(
|
|
Object.assign(
|
|
plugins
|
|
.reduce(
|
|
(currentPrinters, plugin) =>
|
|
currentPrinters.concat(
|
|
Object.keys(plugin.printers).map(
|
|
printerName => plugin.printers[printerName]
|
|
)
|
|
),
|
|
[]
|
|
)
|
|
.reduce(
|
|
(currentOptions, printer) =>
|
|
Object.assign(currentOptions, printer.options),
|
|
{}
|
|
),
|
|
supportOptions
|
|
),
|
|
"name"
|
|
)
|
|
.sort((a, b) => (a.name === b.name ? 0 : a.name < b.name ? -1 : 1))
|
|
.filter(filterSince)
|
|
.filter(filterDeprecated)
|
|
.map(mapDeprecated)
|
|
.map(option => {
|
|
const newOption = Object.assign({}, option);
|
|
|
|
if (Array.isArray(newOption.default)) {
|
|
newOption.default = newOption.default
|
|
.filter(filterSince)
|
|
.sort((info1, info2) =>
|
|
semver.compare(info2.since, info1.since)
|
|
)[0].value;
|
|
}
|
|
|
|
if (Array.isArray(newOption.choices)) {
|
|
newOption.choices = newOption.choices
|
|
.filter(filterSince)
|
|
.filter(filterDeprecated)
|
|
.map(mapDeprecated);
|
|
}
|
|
|
|
return newOption;
|
|
});
|
|
|
|
const usePostCssParser = semver.lt(version, "1.7.1");
|
|
|
|
const languages = plugins
|
|
.reduce((all, plugin) => all.concat(plugin.languages), [])
|
|
.filter(language => language.since && semver.gte(version, language.since))
|
|
.map(language => {
|
|
// Prevent breaking changes
|
|
if (language.name === "Markdown") {
|
|
return Object.assign({}, language, {
|
|
parsers: ["markdown"]
|
|
});
|
|
}
|
|
if (language.name === "TypeScript") {
|
|
return Object.assign({}, language, {
|
|
parsers: ["typescript"]
|
|
});
|
|
}
|
|
|
|
if (usePostCssParser && language.group === "CSS") {
|
|
return Object.assign({}, language, {
|
|
parsers: ["postcss"]
|
|
});
|
|
}
|
|
return language;
|
|
});
|
|
|
|
return { languages, options };
|
|
|
|
function filterSince(object) {
|
|
return (
|
|
opts.showUnreleased ||
|
|
!("since" in object) ||
|
|
(object.since && semver.gte(version, object.since))
|
|
);
|
|
}
|
|
function filterDeprecated(object) {
|
|
return (
|
|
opts.showDeprecated ||
|
|
!("deprecated" in object) ||
|
|
(object.deprecated && semver.lt(version, object.deprecated))
|
|
);
|
|
}
|
|
function mapDeprecated(object) {
|
|
if (!object.deprecated || opts.showDeprecated) {
|
|
return object;
|
|
}
|
|
const newObject = Object.assign({}, object);
|
|
delete newObject.deprecated;
|
|
delete newObject.redirect;
|
|
return newObject;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
getSupportInfo
|
|
};
|