Allow plugins to override default options (#3991)
* refactor(cli): defer default value applying * Allow plugins to override default options * Move "defaultOptions" to top level of plugin * Simplify implementation * Attach plugin name * Add pluginOptions to cli help * Update snapshots * Code review (immutable style) * Add test for help output * Use snapshot test, fix Object.assign * Refactor to immutable style * Add test case for automatic plugin resolution * Add tests for applying and overriding default opts * Remove "since" option * Only set defaults for CLI args when no pluginDefaults are present * Revert workaround, rebase to #4045 * Add basic documentation for `options` and `defaultOptions`master
parent
23f032f348
commit
d05a29da05
|
@ -46,7 +46,13 @@ If the plugin is unable to be found automatically, you can load them with:
|
|||
|
||||
## Developing Plugins
|
||||
|
||||
Prettier plugins are regular JavaScript modules with three exports, `languages`, `parsers` and `printers`.
|
||||
Prettier plugins are regular JavaScript modules with five exports:
|
||||
|
||||
* `languages`
|
||||
* `parsers`
|
||||
* `printers`
|
||||
* `options`
|
||||
* `defaultOptions`
|
||||
|
||||
### `languages`
|
||||
|
||||
|
@ -149,6 +155,33 @@ function embed(
|
|||
|
||||
If you don't want to switch to a different parser, simply return `null` or `undefined`.
|
||||
|
||||
### `options`
|
||||
|
||||
`options` is an object containing the custom options your plugin supports.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
options: {
|
||||
openingBraceNewLine: {
|
||||
type: "boolean",
|
||||
category: "Global",
|
||||
default: true,
|
||||
description: "Move open brace for code blocks onto new line."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `defaultOptions`
|
||||
|
||||
If your plugin requires different default values for some of prettier's core options, you can specify them in `defaultOptions`:
|
||||
|
||||
```
|
||||
defaultOptions: {
|
||||
tabWidth: 4
|
||||
}
|
||||
```
|
||||
|
||||
### Utility functions
|
||||
|
||||
A `util` module from prettier core is considered a private API and is not meant to be consumed by plugins. Instead, the `util-shared` module provides the following limited set of utility functions for plugins:
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
"use strict";
|
||||
|
||||
const minimist = require("minimist");
|
||||
|
||||
const PLACEHOLDER = null;
|
||||
|
||||
/**
|
||||
* unspecified boolean flag without default value is parsed as `undefined` instead of `false`
|
||||
*/
|
||||
module.exports = function(args, options) {
|
||||
const boolean = options.boolean || [];
|
||||
const defaults = options.default || {};
|
||||
|
||||
const booleanWithoutDefault = boolean.filter(key => !(key in defaults));
|
||||
const newDefaults = Object.assign(
|
||||
{},
|
||||
defaults,
|
||||
booleanWithoutDefault.reduce(
|
||||
(reduced, key) => Object.assign(reduced, { [key]: PLACEHOLDER }),
|
||||
{}
|
||||
)
|
||||
);
|
||||
|
||||
const parsed = minimist(
|
||||
args,
|
||||
Object.assign({}, options, { default: newDefaults })
|
||||
);
|
||||
|
||||
return Object.keys(parsed).reduce((reduced, key) => {
|
||||
if (parsed[key] !== PLACEHOLDER) {
|
||||
reduced[key] = parsed[key];
|
||||
}
|
||||
return reduced;
|
||||
}, {});
|
||||
};
|
|
@ -3,7 +3,6 @@
|
|||
const path = require("path");
|
||||
const camelCase = require("camelcase");
|
||||
const dashify = require("dashify");
|
||||
const minimist = require("minimist");
|
||||
const fs = require("fs");
|
||||
const globby = require("globby");
|
||||
const ignore = require("ignore");
|
||||
|
@ -11,6 +10,7 @@ const chalk = require("chalk");
|
|||
const readline = require("readline");
|
||||
const leven = require("leven");
|
||||
|
||||
const minimist = require("./minimist");
|
||||
const prettier = require("../../index");
|
||||
const cleanAST = require("../common/clean-ast").cleanAST;
|
||||
const errors = require("../common/errors");
|
||||
|
@ -228,11 +228,7 @@ function parseArgsToOptions(context, overrideDefaults) {
|
|||
Object.assign({
|
||||
string: minimistOptions.string,
|
||||
boolean: minimistOptions.boolean,
|
||||
default: Object.assign(
|
||||
{},
|
||||
cliifyOptions(context.apiDefaultOptions, apiDetailedOptionMap),
|
||||
cliifyOptions(overrideDefaults, apiDetailedOptionMap)
|
||||
)
|
||||
default: cliifyOptions(overrideDefaults, apiDetailedOptionMap)
|
||||
})
|
||||
),
|
||||
context.detailedOptions,
|
||||
|
@ -305,7 +301,7 @@ function createIgnorer(context) {
|
|||
}
|
||||
|
||||
function eachFilename(context, patterns, callback) {
|
||||
const ignoreNodeModules = context.argv["with-node-modules"] === false;
|
||||
const ignoreNodeModules = context.argv["with-node-modules"] !== true;
|
||||
if (ignoreNodeModules) {
|
||||
patterns = patterns.concat(["!**/node_modules/**", "!./node_modules/**"]);
|
||||
}
|
||||
|
@ -613,7 +609,16 @@ function createDetailedUsage(context, optionName) {
|
|||
? `\n\nDefault: ${createDefaultValueDisplay(optionDefaultValue)}`
|
||||
: "";
|
||||
|
||||
return `${header}${description}${choices}${defaults}`;
|
||||
const pluginDefaults =
|
||||
option.pluginDefaults && Object.keys(option.pluginDefaults).length
|
||||
? `\nPlugin defaults:${Object.keys(option.pluginDefaults).map(
|
||||
key =>
|
||||
`\n* ${key}: ${createDefaultValueDisplay(
|
||||
option.pluginDefaults[key]
|
||||
)}`
|
||||
)}`
|
||||
: "";
|
||||
return `${header}${description}${choices}${defaults}${pluginDefaults}`;
|
||||
}
|
||||
|
||||
function getOptionDefaultValue(context, optionName) {
|
||||
|
@ -743,6 +748,7 @@ function createMinimistOptions(detailedOptions) {
|
|||
.map(option => option.name),
|
||||
default: detailedOptions
|
||||
.filter(option => !option.deprecated)
|
||||
.filter(option => !option.forwardToApi || option.name === "plugin")
|
||||
.filter(option => option.default !== undefined)
|
||||
.reduce(
|
||||
(current, option) =>
|
||||
|
|
|
@ -30,7 +30,7 @@ function loadPlugins(plugins) {
|
|||
}
|
||||
|
||||
const pluginPath = resolve.sync(plugin, { basedir: process.cwd() });
|
||||
return eval("require")(pluginPath);
|
||||
return Object.assign({ name: plugin }, eval("require")(pluginPath));
|
||||
});
|
||||
|
||||
return deduplicate(internalPlugins.concat(externalPlugins));
|
||||
|
|
|
@ -249,6 +249,16 @@ function getSupportInfo(version, opts) {
|
|||
}
|
||||
|
||||
return newOption;
|
||||
})
|
||||
.map(option => {
|
||||
const filteredPlugins = plugins.filter(
|
||||
plugin => plugin.defaultOptions && plugin.defaultOptions[option.name]
|
||||
);
|
||||
const pluginDefaults = filteredPlugins.reduce((reduced, plugin) => {
|
||||
reduced[plugin.name] = plugin.defaultOptions[option.name];
|
||||
return reduced;
|
||||
}, {});
|
||||
return Object.assign(option, { pluginDefaults });
|
||||
});
|
||||
|
||||
const usePostCssParser = semver.lt(version, "1.7.1");
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
"use strict";
|
||||
|
||||
function getPlugin(options) {
|
||||
const astFormat = options.astFormat;
|
||||
|
||||
if (!astFormat) {
|
||||
throw new Error("getPlugin() requires astFormat to be set");
|
||||
}
|
||||
const printerPlugin = options.plugins.find(
|
||||
plugin => plugin.printers[astFormat]
|
||||
);
|
||||
if (!printerPlugin) {
|
||||
throw new Error(`Couldn't find plugin for AST format "${astFormat}"`);
|
||||
}
|
||||
|
||||
return printerPlugin;
|
||||
}
|
||||
|
||||
module.exports = getPlugin;
|
|
@ -1,21 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function getPrinter(options) {
|
||||
const astFormat = options.astFormat;
|
||||
|
||||
if (!astFormat) {
|
||||
throw new Error("getPrinter() requires astFormat to be set");
|
||||
}
|
||||
const printerPlugin = options.plugins.find(
|
||||
plugin => plugin.printers[astFormat]
|
||||
);
|
||||
if (!printerPlugin) {
|
||||
throw new Error(
|
||||
`Couldn't find printer plugin for AST format "${astFormat}"`
|
||||
);
|
||||
}
|
||||
|
||||
return printerPlugin.printers[astFormat];
|
||||
}
|
||||
|
||||
module.exports = getPrinter;
|
|
@ -5,7 +5,7 @@ const getSupportInfo = require("../common/support").getSupportInfo;
|
|||
const normalizer = require("./options-normalizer");
|
||||
const loadPlugins = require("../common/load-plugins");
|
||||
const resolveParser = require("./parser").resolveParser;
|
||||
const getPrinter = require("./get-printer");
|
||||
const getPlugin = require("./get-plugin");
|
||||
|
||||
const hiddenDefaults = {
|
||||
astFormat: "estree",
|
||||
|
@ -53,11 +53,27 @@ function normalize(options, opts) {
|
|||
rawOptions.astFormat = parser.astFormat;
|
||||
rawOptions.locEnd = parser.locEnd;
|
||||
rawOptions.locStart = parser.locStart;
|
||||
rawOptions.printer = getPrinter(rawOptions);
|
||||
const plugin = getPlugin(rawOptions);
|
||||
rawOptions.printer = plugin.printers[rawOptions.astFormat];
|
||||
|
||||
Object.keys(defaults).forEach(k => {
|
||||
const pluginDefaults = supportOptions
|
||||
.filter(
|
||||
optionInfo =>
|
||||
optionInfo.pluginDefaults && optionInfo.pluginDefaults[plugin.name]
|
||||
)
|
||||
.reduce(
|
||||
(reduced, optionInfo) =>
|
||||
Object.assign(reduced, {
|
||||
[optionInfo.name]: optionInfo.pluginDefaults[plugin.name]
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
const mixedDefaults = Object.assign({}, defaults, pluginDefaults);
|
||||
|
||||
Object.keys(mixedDefaults).forEach(k => {
|
||||
if (rawOptions[k] == null) {
|
||||
rawOptions[k] = defaults[k];
|
||||
rawOptions[k] = mixedDefaults[k];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -514,6 +514,36 @@ exports[`show detailed usage with --help write (stdout) 1`] = `
|
|||
|
||||
exports[`show detailed usage with --help write (write) 1`] = `Array []`;
|
||||
|
||||
exports[`show detailed usage with plugin options (automatic resolution) (stderr) 1`] = `""`;
|
||||
|
||||
exports[`show detailed usage with plugin options (automatic resolution) (stdout) 1`] = `
|
||||
"--tab-width <int>
|
||||
|
||||
Number of spaces per indentation level.
|
||||
|
||||
Default: 2
|
||||
Plugin defaults:
|
||||
* prettier-plugin-bar: 4
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`show detailed usage with plugin options (automatic resolution) (write) 1`] = `Array []`;
|
||||
|
||||
exports[`show detailed usage with plugin options (manual resolution) (stderr) 1`] = `""`;
|
||||
|
||||
exports[`show detailed usage with plugin options (manual resolution) (stdout) 1`] = `
|
||||
"--tab-width <int>
|
||||
|
||||
Number of spaces per indentation level.
|
||||
|
||||
Default: 2
|
||||
Plugin defaults:
|
||||
* ../plugins/automatic/node_modules/prettier-plugin-bar: 4
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`show detailed usage with plugin options (manual resolution) (write) 1`] = `Array []`;
|
||||
|
||||
exports[`show usage with --help (stderr) 1`] = `""`;
|
||||
|
||||
exports[`show usage with --help (stdout) 1`] = `
|
||||
|
|
|
@ -661,6 +661,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Include parentheses around a sole arrow function parameter.\\",
|
||||
\\"name\\": \\"arrowParens\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.9.0\\",
|
||||
\\"type\\": \\"choice\\"
|
||||
},
|
||||
|
@ -670,6 +671,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\": \\"Print spaces between brackets.\\",
|
||||
\\"name\\": \\"bracketSpacing\\",
|
||||
\\"oppositeDescription\\": \\"Do not print spaces between brackets.\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"0.0.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -679,6 +681,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Print (to stderr) where a cursor at the given position would move to after formatting.\\\\nThis option cannot be used with --range-start and --range-end.\\",
|
||||
\\"name\\": \\"cursorOffset\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"range\\": { \\"end\\": null, \\"start\\": -1, \\"step\\": 1 },
|
||||
\\"since\\": \\"1.4.0\\",
|
||||
\\"type\\": \\"int\\"
|
||||
|
@ -688,6 +691,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Specify the input filepath. This will be used to do parser inference.\\",
|
||||
\\"name\\": \\"filepath\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.4.0\\",
|
||||
\\"type\\": \\"path\\"
|
||||
},
|
||||
|
@ -697,6 +701,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Insert @format pragma into file's first docblock comment.\\",
|
||||
\\"name\\": \\"insertPragma\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.8.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -705,6 +710,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": false,
|
||||
\\"description\\": \\"Put > on the last line instead of at a new line.\\",
|
||||
\\"name\\": \\"jsxBracketSameLine\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"0.17.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -729,6 +735,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": \\"babylon\\",
|
||||
\\"description\\": \\"Which parser to use.\\",
|
||||
\\"name\\": \\"parser\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"0.0.10\\",
|
||||
\\"type\\": \\"choice\\"
|
||||
},
|
||||
|
@ -739,6 +746,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.\\",
|
||||
\\"name\\": \\"plugins\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.10.0\\",
|
||||
\\"type\\": \\"path\\"
|
||||
},
|
||||
|
@ -747,6 +755,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": 80,
|
||||
\\"description\\": \\"The line length where Prettier will try wrap.\\",
|
||||
\\"name\\": \\"printWidth\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"range\\": { \\"end\\": null, \\"start\\": 0, \\"step\\": 1 },
|
||||
\\"since\\": \\"0.0.0\\",
|
||||
\\"type\\": \\"int\\"
|
||||
|
@ -773,6 +782,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": \\"preserve\\",
|
||||
\\"description\\": \\"How to wrap prose. (markdown)\\",
|
||||
\\"name\\": \\"proseWrap\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.8.2\\",
|
||||
\\"type\\": \\"choice\\"
|
||||
},
|
||||
|
@ -782,6 +792,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Format code ending at a given character offset (exclusive).\\\\nThe range will extend forwards to the end of the selected statement.\\\\nThis option cannot be used with --cursor-offset.\\",
|
||||
\\"name\\": \\"rangeEnd\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"range\\": { \\"end\\": null, \\"start\\": 0, \\"step\\": 1 },
|
||||
\\"since\\": \\"1.4.0\\",
|
||||
\\"type\\": \\"int\\"
|
||||
|
@ -792,6 +803,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Format code starting at a given character offset.\\\\nThe range will extend backwards to the start of the first line containing the selected statement.\\\\nThis option cannot be used with --cursor-offset.\\",
|
||||
\\"name\\": \\"rangeStart\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"range\\": { \\"end\\": null, \\"start\\": 0, \\"step\\": 1 },
|
||||
\\"since\\": \\"1.4.0\\",
|
||||
\\"type\\": \\"int\\"
|
||||
|
@ -802,6 +814,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"description\\":
|
||||
\\"Require either '@prettier' or '@format' to be present in the file's first docblock comment\\\\nin order for it to be formatted.\\",
|
||||
\\"name\\": \\"requirePragma\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.7.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -812,6 +825,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"name\\": \\"semi\\",
|
||||
\\"oppositeDescription\\":
|
||||
\\"Do not print semicolons, except at the beginning of lines which may need them.\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.0.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -820,6 +834,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": false,
|
||||
\\"description\\": \\"Use single quotes instead of double quotes.\\",
|
||||
\\"name\\": \\"singleQuote\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"0.0.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
},
|
||||
|
@ -828,6 +843,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": 2,
|
||||
\\"description\\": \\"Number of spaces per indentation level.\\",
|
||||
\\"name\\": \\"tabWidth\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"range\\": { \\"end\\": null, \\"start\\": 0, \\"step\\": 1 },
|
||||
\\"type\\": \\"int\\"
|
||||
},
|
||||
|
@ -849,6 +865,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": \\"none\\",
|
||||
\\"description\\": \\"Print trailing commas wherever possible when multi-line.\\",
|
||||
\\"name\\": \\"trailingComma\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"0.0.0\\",
|
||||
\\"type\\": \\"choice\\"
|
||||
},
|
||||
|
@ -857,6 +874,7 @@ exports[`CLI --support-info (stdout) 1`] = `
|
|||
\\"default\\": false,
|
||||
\\"description\\": \\"Indent with tabs instead of spaces.\\",
|
||||
\\"name\\": \\"useTabs\\",
|
||||
\\"pluginDefaults\\": {},
|
||||
\\"since\\": \\"1.0.0\\",
|
||||
\\"type\\": \\"boolean\\"
|
||||
}
|
||||
|
|
|
@ -26,6 +26,27 @@ describe(`show detailed usage with --help l (alias)`, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe(`show detailed usage with plugin options (automatic resolution)`, () => {
|
||||
runPrettier("plugins/automatic", [
|
||||
"--help",
|
||||
"tab-width",
|
||||
"--parser=bar"
|
||||
]).test({
|
||||
status: 0
|
||||
});
|
||||
});
|
||||
|
||||
describe(`show detailed usage with plugin options (manual resolution)`, () => {
|
||||
runPrettier("cli", [
|
||||
"--help",
|
||||
"tab-width",
|
||||
"--plugin=../plugins/automatic/node_modules/prettier-plugin-bar",
|
||||
"--parser=bar"
|
||||
]).test({
|
||||
status: 0
|
||||
});
|
||||
});
|
||||
|
||||
commonUtil
|
||||
.arrayify(
|
||||
Object.assign(
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
"use strict";
|
||||
|
||||
const runPrettier = require("../runPrettier");
|
||||
|
||||
describe("plugin default options should work", () => {
|
||||
runPrettier(
|
||||
"plugins/defaultOptions",
|
||||
["--stdin-filepath", "example.foo", "--plugin=./plugin"],
|
||||
{ input: "hello-world" }
|
||||
).test({
|
||||
stdout: "tabWidth:8",
|
||||
stderr: "",
|
||||
status: 0,
|
||||
write: []
|
||||
});
|
||||
});
|
||||
|
||||
describe("overriding plugin default options should work", () => {
|
||||
runPrettier(
|
||||
"plugins/defaultOptions",
|
||||
["--stdin-filepath", "example.foo", "--plugin=./plugin", "--tab-width=4"],
|
||||
{ input: "hello-world" }
|
||||
).test({
|
||||
stdout: "tabWidth:4",
|
||||
stderr: "",
|
||||
status: 0,
|
||||
write: []
|
||||
});
|
||||
});
|
|
@ -19,5 +19,8 @@ module.exports = {
|
|||
bar: {
|
||||
print: path => concat(["bar+", path.getValue().text])
|
||||
}
|
||||
},
|
||||
defaultOptions: {
|
||||
tabWidth: 4
|
||||
}
|
||||
};
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"plugins": ["./plugin"]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
languages: [
|
||||
{
|
||||
name: "foo",
|
||||
parsers: ["foo-parser"],
|
||||
extensions: [".foo"]
|
||||
}
|
||||
],
|
||||
defaultOptions: {
|
||||
tabWidth: 8
|
||||
},
|
||||
parsers: {
|
||||
"foo-parser": {
|
||||
parse: text => ({ text }),
|
||||
astFormat: "foo-ast"
|
||||
}
|
||||
},
|
||||
printers: {
|
||||
"foo-ast": {
|
||||
print: (path, options) =>
|
||||
options.tabWidth ? `tabWidth:${options.tabWidth}` : path.getValue().text
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue