@tsrx/prettier-plugin 0.3.103 → 0.3.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +27 -6
- package/src/index.test.js +123 -7
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { parseModule } from '@tsrx/core';
|
|
22
22
|
import { doc } from 'prettier';
|
|
23
|
+
import postcssPlugin from 'prettier/parser-postcss.js';
|
|
23
24
|
|
|
24
25
|
const { builders, utils } = doc;
|
|
25
26
|
const {
|
|
@@ -51,6 +52,9 @@ export const languages = [
|
|
|
51
52
|
|
|
52
53
|
/** @type {import('prettier').Plugin['parsers']} */
|
|
53
54
|
export const parsers = {
|
|
55
|
+
// Carry the embedded stylesheet parsers with the TSRX plugin so browser
|
|
56
|
+
// consumers of prettier/standalone do not need to register PostCSS separately.
|
|
57
|
+
...postcssPlugin.parsers,
|
|
54
58
|
tsrx: {
|
|
55
59
|
astFormat: 'ripple-ast',
|
|
56
60
|
/**
|
|
@@ -82,6 +86,7 @@ export const parsers = {
|
|
|
82
86
|
|
|
83
87
|
/** @type {import('prettier').Plugin['printers']} */
|
|
84
88
|
export const printers = {
|
|
89
|
+
...postcssPlugin.printers,
|
|
85
90
|
'ripple-ast': {
|
|
86
91
|
/**
|
|
87
92
|
* @param {AstPath<AST.Node | AST.CSS.StyleSheet>} path
|
|
@@ -123,8 +128,8 @@ export const printers = {
|
|
|
123
128
|
return body;
|
|
124
129
|
} catch {
|
|
125
130
|
// A stylesheet that doesn't parse (e.g. mid-edit code) is an expected
|
|
126
|
-
// state, not an error: keep
|
|
127
|
-
return node.source;
|
|
131
|
+
// state, not an error: keep its authored lines and stay quiet.
|
|
132
|
+
return replaceEndOfLine(node.source.trim());
|
|
128
133
|
}
|
|
129
134
|
};
|
|
130
135
|
}
|
|
@@ -1664,7 +1669,8 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
1664
1669
|
if (!node.source || !node.source.trim()) {
|
|
1665
1670
|
nodeContent = '';
|
|
1666
1671
|
} else {
|
|
1667
|
-
|
|
1672
|
+
// Preserve authored lines when embedded-language formatting is disabled.
|
|
1673
|
+
nodeContent = replaceEndOfLine(node.source.trim());
|
|
1668
1674
|
}
|
|
1669
1675
|
break;
|
|
1670
1676
|
}
|
|
@@ -5820,10 +5826,10 @@ function isSimpleJSXExpressionChild(child) {
|
|
|
5820
5826
|
expression?.type === 'Identifier' ||
|
|
5821
5827
|
expression?.type === 'Literal' ||
|
|
5822
5828
|
expression?.type === 'TemplateLiteral' ||
|
|
5823
|
-
// Stock Prettier keeps a single `{expr}` child inline regardless of the
|
|
5824
|
-
// expression kind (member access, calls, etc.); only multiple children break.
|
|
5825
5829
|
expression?.type === 'MemberExpression' ||
|
|
5826
|
-
expression?.type === 'CallExpression'
|
|
5830
|
+
expression?.type === 'CallExpression' ||
|
|
5831
|
+
expression?.type === 'BinaryExpression' ||
|
|
5832
|
+
expression?.type === 'LogicalExpression'
|
|
5827
5833
|
);
|
|
5828
5834
|
}
|
|
5829
5835
|
|
|
@@ -6091,6 +6097,21 @@ function printJSXElement(node, path, options, print) {
|
|
|
6091
6097
|
(child) => child.type !== 'JSXText' || child.value.trim(),
|
|
6092
6098
|
);
|
|
6093
6099
|
const singleMeaningfulChild = meaningfulChildren.length === 1 ? meaningfulChildren[0] : null;
|
|
6100
|
+
const singleExpression =
|
|
6101
|
+
singleMeaningfulChild?.type === 'JSXExpressionContainer'
|
|
6102
|
+
? singleMeaningfulChild.expression
|
|
6103
|
+
: null;
|
|
6104
|
+
if (
|
|
6105
|
+
!forceMultiline &&
|
|
6106
|
+
childrenDocs.length === 1 &&
|
|
6107
|
+
(singleExpression?.type === 'BinaryExpression' ||
|
|
6108
|
+
singleExpression?.type === 'LogicalExpression')
|
|
6109
|
+
) {
|
|
6110
|
+
// Keep a short operation against its tags, but give a wrapping operation
|
|
6111
|
+
// an indented element body instead of aligning continuations after `{`.
|
|
6112
|
+
// Group the opening tag with that body so wrapped attributes break both.
|
|
6113
|
+
return group([openingTag, indent([softline, childrenDocs[0]]), softline, '</', tagName, '>']);
|
|
6114
|
+
}
|
|
6094
6115
|
if (
|
|
6095
6116
|
!forceMultiline &&
|
|
6096
6117
|
childrenDocs.length === 1 &&
|
package/src/index.test.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import prettier from 'prettier';
|
|
3
|
+
import standalonePrettier from 'prettier/standalone';
|
|
4
|
+
import estreePlugin from 'prettier/plugins/estree';
|
|
3
5
|
import { fileURLToPath } from 'url';
|
|
4
6
|
import { dirname, join } from 'path';
|
|
5
|
-
import { languages, parsers } from './index.js';
|
|
7
|
+
import { languages, parsers, printers } from './index.js';
|
|
6
8
|
|
|
7
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
10
|
const __dirname = dirname(__filename);
|
|
@@ -292,6 +294,79 @@ const items=[1,2,3];
|
|
|
292
294
|
expect(result).toBeWithNewline(expected);
|
|
293
295
|
});
|
|
294
296
|
|
|
297
|
+
it('formats style tags with standalone Prettier', async () => {
|
|
298
|
+
const input = `export default function App() @{
|
|
299
|
+
<style>
|
|
300
|
+
.demo { display: grid; gap: 0.5rem; justify-items: start; }
|
|
301
|
+
button { padding: 0.4rem 0.9rem; color: inherit; }
|
|
302
|
+
</style>
|
|
303
|
+
}`;
|
|
304
|
+
const expected = `export default function App() @{
|
|
305
|
+
<style>
|
|
306
|
+
.demo {
|
|
307
|
+
display: grid;
|
|
308
|
+
gap: 0.5rem;
|
|
309
|
+
justify-items: start;
|
|
310
|
+
}
|
|
311
|
+
button {
|
|
312
|
+
padding: 0.4rem 0.9rem;
|
|
313
|
+
color: inherit;
|
|
314
|
+
}
|
|
315
|
+
</style>
|
|
316
|
+
}`;
|
|
317
|
+
const options = {
|
|
318
|
+
parser: 'tsrx',
|
|
319
|
+
plugins: [{ languages, parsers, printers }, estreePlugin],
|
|
320
|
+
useTabs: true,
|
|
321
|
+
tabWidth: 2,
|
|
322
|
+
singleQuote: true,
|
|
323
|
+
printWidth: 100,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = await standalonePrettier.format(input, options);
|
|
327
|
+
expect(result).toBeWithNewline(expected);
|
|
328
|
+
expect(await standalonePrettier.format(result, options)).toBe(result);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('preserves style lines when embedded formatting is disabled', async () => {
|
|
332
|
+
const input = `export default function App() @{
|
|
333
|
+
<style>
|
|
334
|
+
.demo {
|
|
335
|
+
display: grid;
|
|
336
|
+
gap: 0.5rem;
|
|
337
|
+
}
|
|
338
|
+
button {
|
|
339
|
+
color: inherit;
|
|
340
|
+
}
|
|
341
|
+
</style>
|
|
342
|
+
}`;
|
|
343
|
+
|
|
344
|
+
const options = {
|
|
345
|
+
useTabs: true,
|
|
346
|
+
singleQuote: true,
|
|
347
|
+
embeddedLanguageFormatting: 'off',
|
|
348
|
+
};
|
|
349
|
+
const result = await format(input, options);
|
|
350
|
+
|
|
351
|
+
expect(result).toBeWithNewline(input);
|
|
352
|
+
expect(await format(result, options)).toBe(result);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('preserves style lines when embedded formatting fails', async () => {
|
|
356
|
+
const input = `export default function App() @{
|
|
357
|
+
<style>
|
|
358
|
+
.demo {
|
|
359
|
+
color red;
|
|
360
|
+
}
|
|
361
|
+
</style>
|
|
362
|
+
}`;
|
|
363
|
+
const options = { useTabs: true, singleQuote: true };
|
|
364
|
+
const result = await format(input, options);
|
|
365
|
+
|
|
366
|
+
expect(result).toBeWithNewline(input);
|
|
367
|
+
expect(await format(result, options)).toBe(result);
|
|
368
|
+
});
|
|
369
|
+
|
|
295
370
|
it('formats setup statements before the TSRX return', async () => {
|
|
296
371
|
const input = `function Counter(){let count=track(0);const increment=()=>count++;return <button onClick={increment}>{count}</button>}`;
|
|
297
372
|
const expected = `function Counter() {
|
|
@@ -326,6 +401,51 @@ const items=[1,2,3];
|
|
|
326
401
|
expect(result).toBeWithNewline(expected);
|
|
327
402
|
});
|
|
328
403
|
|
|
404
|
+
it('keeps a lone binary expression child inline when it fits', async () => {
|
|
405
|
+
const input = `export function App() @{
|
|
406
|
+
<h2>{'Count: ' + count}</h2>
|
|
407
|
+
}`;
|
|
408
|
+
const expected = `export function App() @{
|
|
409
|
+
<h2>{'Count: ' + count}</h2>
|
|
410
|
+
}`;
|
|
411
|
+
|
|
412
|
+
const result = await format(input, { singleQuote: true });
|
|
413
|
+
expect(result).toBeWithNewline(expected);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('indents a lone binary expression child when it wraps', async () => {
|
|
417
|
+
const input = `export function App() @{
|
|
418
|
+
<h2>{firstLongIdentifier + secondLongIdentifier + thirdLongIdentifier}</h2>
|
|
419
|
+
}`;
|
|
420
|
+
const expected = `export function App() @{
|
|
421
|
+
<h2>
|
|
422
|
+
{firstLongIdentifier +
|
|
423
|
+
secondLongIdentifier +
|
|
424
|
+
thirdLongIdentifier}
|
|
425
|
+
</h2>
|
|
426
|
+
}`;
|
|
427
|
+
|
|
428
|
+
const result = await format(input, { printWidth: 40 });
|
|
429
|
+
expect(result).toBeWithNewline(expected);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('indents a lone binary expression child after wrapped attributes', async () => {
|
|
433
|
+
const input = `export function App() @{
|
|
434
|
+
<h2 firstLongAttributeName={firstLongAttributeValue} secondLongAttributeName={secondLongAttributeValue}>{a + b}</h2>
|
|
435
|
+
}`;
|
|
436
|
+
const expected = `export function App() @{
|
|
437
|
+
<h2
|
|
438
|
+
firstLongAttributeName={firstLongAttributeValue}
|
|
439
|
+
secondLongAttributeName={secondLongAttributeValue}
|
|
440
|
+
>
|
|
441
|
+
{a + b}
|
|
442
|
+
</h2>
|
|
443
|
+
}`;
|
|
444
|
+
|
|
445
|
+
const result = await format(input, { printWidth: 50 });
|
|
446
|
+
expect(result).toBeWithNewline(expected);
|
|
447
|
+
});
|
|
448
|
+
|
|
329
449
|
it('preserves authored multiline whitespace around a single JSXText child', async () => {
|
|
330
450
|
const input = `function Foo() @{
|
|
331
451
|
@if (props.onRemove) {
|
|
@@ -5360,9 +5480,7 @@ const foo = <><Bar {...props} /></>;`;
|
|
|
5360
5480
|
const expected = `export function Test() {
|
|
5361
5481
|
const a = 1
|
|
5362
5482
|
const b = 2
|
|
5363
|
-
<div>
|
|
5364
|
-
{a + b}
|
|
5365
|
-
</div>
|
|
5483
|
+
<div>{a + b}</div>
|
|
5366
5484
|
}`;
|
|
5367
5485
|
const result = await format(input, { singleQuote: true, semi: false });
|
|
5368
5486
|
expect(result).toBeWithNewline(expected);
|
|
@@ -5377,9 +5495,7 @@ const foo = <><Bar {...props} /></>;`;
|
|
|
5377
5495
|
const expected = `export function Test() {
|
|
5378
5496
|
const a = 1;
|
|
5379
5497
|
const b = 2;
|
|
5380
|
-
<div>
|
|
5381
|
-
{a + b}
|
|
5382
|
-
</div>
|
|
5498
|
+
<div>{a + b}</div>
|
|
5383
5499
|
}`;
|
|
5384
5500
|
const result = await format(input, { singleQuote: true, semi: true });
|
|
5385
5501
|
expect(result).toBeWithNewline(expected);
|