Skip to content

Commit 628cd1a

Browse files
authored
Merge pull request #18 from gitKrystan/template-single-quote
Add templateSingleQuote option (See #16) + Simplify test suite further
2 parents 7899500 + 3802ce0 commit 628cd1a

20 files changed

Lines changed: 268 additions & 140 deletions

README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,13 @@ With that said, I did have to make some decisions about when and where to includ
4848

4949
## Configuration
5050

51-
The current version offers no configuration options but the plan is to allow the relevant [standard
52-
config options for JavaScript files](https://prettier.io/docs/en/configuration.html) to be separately configured for the `<template>` portion of the file.
51+
These configuration options are available in addition to [Prettier's standard config for JavaScript and Handlebars files](https://prettier.io/docs/en/options.html).
5352

54-
<!-- TODO: These configuration options are available in addition to [Prettier's standard
55-
config for JavaScript files](https://prettier.io/docs/en/configuration.html).
53+
| Name | Default | Description |
54+
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
55+
| `templateSingleQuote` | `false` | [Same as in Prettier](https://prettier.io/docs/en/options.html#quotes) but affecting only template tags |
5656

57-
| Name | Default | Description |
58-
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
59-
| `templatePrintWidth` | `80` | [Same as in Prettier](https://prettier.io/docs/en/options.html#print-width) but affecting only template tags |
60-
| `templateSingleQuote` | `false` | Same as in Prettier](https://prettier.io/docs/en/options.html#print-width) but affecting only template tags | -->
57+
<!-- TODO: | `templatePrintWidth` | `80` | [Same as in Prettier](https://prettier.io/docs/en/options.html#print-width) but affecting only template tags | -->
6158

6259
<!-- TODO: ## Editor integration
6360

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Parser, Plugin, Printer, SupportLanguage } from 'prettier';
22

33
import { PARSER_NAME, PRINTER_NAME } from './config';
4+
import { options } from './options';
45
import { parser } from './parse';
56
import { printer } from './print/index';
67
import type { BaseNode } from './types/ast';
@@ -25,6 +26,7 @@ const plugin: Plugin<BaseNode> = {
2526
languages,
2627
parsers,
2728
printers,
29+
options,
2830
};
2931

3032
export default plugin;

src/options.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type {
2+
BooleanSupportOption,
3+
ParserOptions,
4+
SupportOptions,
5+
} from 'prettier';
6+
7+
import type { BaseNode } from './types/ast';
8+
9+
export interface Options extends ParserOptions<BaseNode> {
10+
templateSingleQuote?: boolean;
11+
}
12+
13+
/**
14+
* Extracts a valid `templateSingleQuote` option out of the provided options. If
15+
* `templateSingleQuote` is defined, it will be used, otherwise the value for
16+
* `singleQuote` will be inherited.
17+
*/
18+
export function getTemplateSingleQuote(options: Options): boolean {
19+
const { singleQuote, templateSingleQuote } = options;
20+
return typeof templateSingleQuote === 'boolean'
21+
? templateSingleQuote
22+
: singleQuote;
23+
}
24+
25+
const templateSingleQuote: BooleanSupportOption = {
26+
since: '0.0.3',
27+
category: 'Format',
28+
type: 'boolean',
29+
description:
30+
'Use single quotes instead of double quotes within template tags.',
31+
};
32+
33+
export const options: SupportOptions = {
34+
templateSingleQuote,
35+
};

src/parse.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Node } from '@babel/types';
33
// @ts-expect-error FIXME: TS7016 Is this a hack? IDK!
44
import { defineAliasedType } from '@babel/types/lib/definitions/utils';
55
import { preprocessEmbeddedTemplates } from 'ember-template-imports/lib/preprocess-embedded-templates';
6-
import type { Parser, ParserOptions } from 'prettier';
6+
import type { Parser } from 'prettier';
77
import { parsers as babelParsers } from 'prettier/parser-babel';
88

99
import {
@@ -12,6 +12,7 @@ import {
1212
TEMPLATE_TAG_NAME,
1313
TEMPLATE_TAG_PLACEHOLDER,
1414
} from './config';
15+
import type { Options } from './options';
1516
import { definePrinter } from './print/index';
1617
import type { BaseNode } from './types/ast';
1718
import { extractGlimmerExpression } from './types/glimmer';
@@ -54,7 +55,7 @@ export const parser: Parser<BaseNode> = {
5455
...typescript,
5556
astFormat: PRINTER_NAME,
5657

57-
preprocess(text: string, options: ParserOptions<BaseNode>): string {
58+
preprocess(text: string, options: Options): string {
5859
definePrinter(options);
5960
const js = preprocess(text, options);
6061
return typescript.preprocess?.(js, options) ?? js;
@@ -63,7 +64,7 @@ export const parser: Parser<BaseNode> = {
6364
parse(
6465
text: string,
6566
parsers: Record<string, Parser<unknown>>,
66-
options: ParserOptions<BaseNode>
67+
options: Options
6768
): BaseNode {
6869
const ast = typescript.parse(text, parsers, options);
6970
// FIXME: This is necessary for babel to not freak out with the custom type.

src/print/index.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import type { AstPath, ParserOptions, Plugin, Printer } from 'prettier';
1+
import type { AstPath, Plugin, Printer } from 'prettier';
22

33
import {
44
TEMPLATE_TAG_CLOSE,
55
TEMPLATE_TAG_OPEN,
66
TEMPLATE_TAG_PLACEHOLDER,
77
} from '../config';
8+
import type { Options } from '../options';
89
import type { BaseNode } from '../types/ast';
910
import {
1011
getGlimmerExpression,
@@ -24,15 +25,15 @@ import { printTemplateTag } from './template';
2425
// @ts-expect-error FIXME: HACK because estree printer isn't exported. See below.
2526
export const printer: Printer<BaseNode> = {};
2627

27-
let originalOptions: ParserOptions<BaseNode>;
28+
let originalOptions: Options;
2829

2930
/**
3031
* FIXME: HACK because estree printer isn't exported.
3132
*
3233
* @see https://github.com/prettier/prettier/issues/10259
3334
* @see https://github.com/prettier/prettier/issues/4424
3435
*/
35-
export function definePrinter(options: ParserOptions<BaseNode>): void {
36+
export function definePrinter(options: Options): void {
3637
originalOptions = { ...options };
3738
const estreePlugin = assertExists(options.plugins.find(isEstreePlugin));
3839
const estreePrinter = estreePlugin.printers.estree;
@@ -163,10 +164,7 @@ function isEstreePlugin(
163164
);
164165
}
165166

166-
function printRawText(
167-
path: AstPath<BaseNode>,
168-
options: ParserOptions<BaseNode>
169-
): string {
167+
function printRawText(path: AstPath<BaseNode>, options: Options): string {
170168
const node = path.getValue();
171169
assert('expected start', node.start);
172170
assert('expected end', node.end);

src/print/template.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { ParserOptions } from 'prettier';
33
import { doc } from 'prettier';
44

55
import { TEMPLATE_TAG_CLOSE, TEMPLATE_TAG_OPEN } from '../config';
6+
import type { Options } from '../options';
7+
import { getTemplateSingleQuote } from '../options';
68
import type { BaseNode } from '../types/ast';
79
import type { GlimmerExpression } from '../types/glimmer';
810

@@ -18,9 +20,12 @@ export function printTemplateTag(
1820
node: TemplateLiteral | GlimmerExpression,
1921
textToDoc: (
2022
text: string,
23+
// Don't use our `Options` here even though technically they are available
24+
// because we don't want to accidentally pass them into `textToDoc`. We
25+
// should normalize them into standard Prettier options at this point.
2126
options: ParserOptions<BaseNode>
2227
) => doc.builders.Doc,
23-
options: ParserOptions<BaseNode>,
28+
options: Options,
2429
hasPrettierIgnore: boolean
2530
): doc.builders.Doc {
2631
const text = node.quasis.map((quasi) => quasi.value.raw).join();
@@ -34,8 +39,7 @@ export function printTemplateTag(
3439
const contents = textToDoc(text.trim(), {
3540
...options,
3641
parser: 'glimmer',
37-
// TODO: Support templateSingleQuote option
38-
// singleQuote: options.templateSingleQuote,
42+
singleQuote: getTemplateSingleQuote(options),
3943
});
4044
return group([
4145
TEMPLATE_TAG_OPEN,

src/utils/ambiguity.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
isRegExpLiteral,
99
isUnaryExpression,
1010
} from '@babel/types';
11-
import type { ParserOptions } from 'prettier';
1211

12+
import type { Options } from '../options';
1313
import type { BaseNode } from '../types/ast';
1414
import { isGlimmerExpression, isGlimmerTSAsExpression } from '../types/glimmer';
1515

@@ -31,7 +31,7 @@ import { isGlimmerExpression, isGlimmerTSAsExpression } from '../types/glimmer';
3131
*/
3232
export function hasAmbiguousNextLine(
3333
path: NodePath<BaseNode>,
34-
options: ParserOptions<BaseNode>
34+
options: Options
3535
): boolean {
3636
// Note: getNextSibling().node will be undefined if there is no sibling
3737
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -63,7 +63,7 @@ function getNextLine(path: NodePath): Node | null {
6363

6464
function isAmbiguousExpression(
6565
expression: Expression,
66-
options: ParserOptions<BaseNode>
66+
options: Options
6767
): boolean {
6868
return (
6969
isGlimmerExpression(expression) ||
@@ -80,7 +80,7 @@ function isAmbiguousExpression(
8080

8181
function startsWithParenthesis(
8282
expression: Expression,
83-
options: ParserOptions<BaseNode>
83+
options: Options
8484
): boolean {
8585
return (
8686
isParenthesizedExpression(expression) ||

tests/helpers/ambiguous.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import type { Options } from 'prettier';
21
import { describe, expect, test } from 'vitest';
32

3+
import type { Options } from '../../src/options';
44
import type { TestCase } from '../helpers/cases';
5+
import { getAllCases } from '../helpers/cases';
56
import { format } from '../helpers/format';
6-
import type { Config } from './config';
7+
import type { Config } from './make-suite';
78

89
/**
910
* Add this comment to any test case and it will be replaced by the
@@ -23,9 +24,18 @@ const AMBIGUOUS_EXPRESSIONS = [
2324
];
2425

2526
/**
26-
* `testCallback` for `describeConfig` that tests the provided `config` and
27-
* `testCase` against each ambiguous expression from `AMBIGUOUS_EXPRESSIONS`,
28-
* with and without user-provided semicolons.
27+
* `caseFilter` for `describeConfig` that will return only cases that include
28+
* the `AMBIGUOUS_PLACEHOLDER`.
29+
*/
30+
export async function getAmbiguousCases(): Promise<TestCase[]> {
31+
return (await getAllCases()).filter((c) =>
32+
c.code.includes(AMBIGUOUS_PLACEHOLDER)
33+
);
34+
}
35+
36+
/**
37+
* Tests the provided `config` and `testCase` against each ambiguous expression
38+
* from `AMBIGUOUS_EXPRESSIONS`, with and without user-provided semicolons.
2939
*
3040
* @see https://github.com/gitKrystan/prettier-plugin-ember-template-tag/issues/1 for more details
3141
*/
@@ -58,7 +68,7 @@ export function ambiguousExpressionTest(
5868

5969
function behavesLikeFormattedAmbiguousCase(
6070
code: string,
61-
formatOptions: Options = {}
71+
formatOptions: Partial<Options> = {}
6272
): void {
6373
try {
6474
const result = format(code, formatOptions);
@@ -73,15 +83,3 @@ function behavesLikeFormattedAmbiguousCase(
7383
expect(isSyntaxError, 'Expected SyntaxError').toBeTruthy();
7484
}
7585
}
76-
77-
/**
78-
* `caseFilter` for `describeConfig` that will return only cases that include
79-
* the `AMBIGUOUS_PLACEHOLDER`.
80-
*/
81-
export function ambiguousCases(
82-
testCase: TestCase,
83-
_index: number,
84-
_cases: TestCase[]
85-
): boolean {
86-
return testCase.code.includes(AMBIGUOUS_PLACEHOLDER);
87-
}

tests/helpers/cases.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@ export async function getCases(
3434

3535
return ([] as TestCase[]).concat(...cases);
3636
}
37+
38+
/** Gets all of the Test Cases in the `cases` directory. */
39+
export async function getAllCases(): Promise<TestCase[]> {
40+
const caseDir = path.join(__dirname, '../cases');
41+
return await getCases(__dirname, caseDir);
42+
}

tests/helpers/config.ts

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)