Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support passing babel plugin configuration in JSON #88

Merged
merged 17 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import ExampleView from './ExampleView';
#### `experimentalBabelParserPluginsList`
A collection of parser names for babel parser. The plugin passes this list to babel parser so it can understand the syntaxes used in the file being formatted. The plugin uses prettier itself to figure out the parser it needs to use but if that fails, you can use this field to enforce the usage of the plugins babel needs.

Since prettier options are limited to strings, you can pass plugins with options as a JSON string of the plugin array: `"[\"plugin-name\", { \"pluginOption\": true }]"`.

```ecmascript 6
module.exports = {
"printWidth": 80,
Expand All @@ -85,7 +87,7 @@ module.exports = {
"importOrder": ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
"importOrderSeparation": true,
"importOrderCaseInsensitive": true,
"experimentalBabelParserPluginsList" : ["jsx", "typescript"]
"experimentalBabelParserPluginsList" : ["jsx", "typescript", "[\"decorators\", { \"decoratorsBeforeExport\": true }]"]
}
```

Expand Down
2 changes: 0 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { expressionStatement, stringLiteral } from '@babel/types';

export const flow: ParserPlugin = 'flow';
export const typescript: ParserPlugin = 'typescript';
export const decoratorsLegacy: ParserPlugin = 'decorators-legacy';
export const classProperties: ParserPlugin = 'classProperties';
export const jsx: ParserPlugin = 'jsx';

export const newLineCharacters = '\n\n';
Expand Down
4 changes: 3 additions & 1 deletion src/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getCodeFromAst } from './utils/get-code-from-ast';
import { getSortedNodes } from './utils/get-sorted-nodes';
import { getParserPlugins } from './utils/get-parser-plugins';
import { PrettierOptions } from './types';
import { getExperimentalParserPlugins } from './utils/get-experimental-parser-plugins';

export function preprocessor(code: string, options: PrettierOptions) {
const {
Expand All @@ -17,12 +18,13 @@ export function preprocessor(code: string, options: PrettierOptions) {
} = options;

const plugins = getParserPlugins(prettierParser);
const parsedExperimentalBabelParserPluginsList = getExperimentalParserPlugins(experimentalBabelParserPluginsList);

const importNodes: ImportDeclaration[] = [];

const parserOptions: ParserOptions = {
sourceType: 'module',
plugins: [...plugins, ...experimentalBabelParserPluginsList],
plugins: [...plugins, ...parsedExperimentalBabelParserPluginsList],
};

const ast = babelParser(code, parserOptions);
Expand Down
3 changes: 1 addition & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { RequiredOptions } from 'prettier';
import { ParserPlugin } from '@babel/parser';

export interface PrettierOptions extends RequiredOptions {
importOrder: string[];
importOrderSeparation: boolean;
importOrderCaseInsensitive: boolean;
experimentalBabelParserPluginsList: ParserPlugin[];
experimentalBabelParserPluginsList: string[]; // should be of type ParserPlugin from '@babel/parser' but prettier does not support nested arrays in options
}
24 changes: 24 additions & 0 deletions src/utils/__tests__/get-experimental-parser-plugins.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getExperimentalParserPlugins } from '../get-experimental-parser-plugins';

test('it should return empty list', () => {
expect(getExperimentalParserPlugins([])).toEqual([]);
});

test('it should return flow and decorators', () => {
expect(getExperimentalParserPlugins(['flow', 'decorators'])).toEqual(['flow', 'decorators']);
});

test('it should return decorators with parsed options', () => {
expect(getExperimentalParserPlugins(["[\"decorators\", { \"decoratorsBeforeExport\": true }]"]))
.toEqual([['decorators', { decoratorsBeforeExport: true }]]);
});

test('it should return decorators with parsed options', () => {
expect(getExperimentalParserPlugins(['flow', "[\"decorators\", { \"decoratorsBeforeExport\": true }]"]))
.toEqual(['flow', ['decorators', { decoratorsBeforeExport: true }]]);
});

test('it should throw an Error for invalid JSON', () => {
expect(() => getExperimentalParserPlugins(['flow', "[\"decorators\", { decoratorsBeforeExport: true }]"]))
.toThrowError("Invalid JSON in experimentalBabelParserPluginsList: ");
});
7 changes: 1 addition & 6 deletions src/utils/__tests__/get-parser-plugins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,5 @@ test('it should return flow', () => {
});

test('it should return ts related plugins', () => {
expect(getParserPlugins('typescript')).toEqual([
'typescript',
'jsx',
'decorators-legacy',
'classProperties',
]);
expect(getParserPlugins('typescript')).toEqual(['typescript', 'jsx']);
});
27 changes: 27 additions & 0 deletions src/utils/get-experimental-parser-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ParserPlugin, ParserPluginWithOptions } from '@babel/parser';

/**
* Returns a list of babel parser plugin names
* @param experimentalBabelParserPluginsList array of experimental babel parser plugins
* @returns list of parser plugins to be passed to babel parser
*/
export const getExperimentalParserPlugins = (experimentalBabelParserPluginsList: string[]): ParserPlugin[] => {
return experimentalBabelParserPluginsList.map(pluginNameOrJson => {
// ParserPlugin can be either a string or and array of [name: string, options: object]
// in prettier options the array will be sent in a JSON string
const isParserPluginWithOptions = pluginNameOrJson.startsWith("[");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change I suggested was to remove this line - Instead of heuristically check if it's a JSON (like you do here), always try to parse as JSON.
Or another option that is less heuristical - you can detect plugin names using a regex (maybe `/^@?[a-zA-Z_0-9-]+(/[a-zA-Z_0-9-]+)?$/), and if it doesn't match, assume it's a JSON and then try to parse it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, what about some short documentation? The JSON string solution isn't something that users would be able to guess, to you should update the README file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the options only support either strings or arrays of [string, object] I think this detection is correct and not just a heuristic.

Good catch on the documentation - done.


let plugin;
if (isParserPluginWithOptions) {
try {
plugin = JSON.parse(pluginNameOrJson) as ParserPluginWithOptions;
} catch (e) {
throw Error("Invalid JSON in experimentalBabelParserPluginsList: " + pluginNameOrJson);
}
} else {
plugin = pluginNameOrJson as ParserPlugin;
}

return plugin;
});
};
6 changes: 1 addition & 5 deletions src/utils/get-parser-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { RequiredOptions } from 'prettier';
import {
flow,
typescript,
decoratorsLegacy,
classProperties,
jsx,
} from '../constants';

Expand All @@ -20,9 +18,7 @@ export const getParserPlugins = (
const isTypescript = prettierParser === typescript;

// In case of typescript as prettier parser, we pass the following
// decoratorsLegacy, classProperties are passed in case of angular
// projects.
const tsPlugins = [typescript, jsx, decoratorsLegacy, classProperties];
const tsPlugins = [typescript, jsx];

return [...(isFlow ? [flow] : []), ...(isTypescript ? tsPlugins : [])];
};
1 change: 1 addition & 0 deletions tests/Typescript/ppsi.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
run_spec(__dirname, ["typescript"], {
importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'],
importOrderSeparation: true,
experimentalBabelParserPluginsList : ["classProperties", "[\"decorators\", { \"decoratorsBeforeExport\": true }]"]
});