Skip to content

Commit 8cf2d35

Browse files
authored
feat(fmt): enable Yuku by default (#138)
1 parent 3f1d4ec commit 8cf2d35

12 files changed

Lines changed: 293 additions & 20 deletions

File tree

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ Rstack CLI brings the Rstack toolchain together for JavaScript development, with
1212

1313
It also covers local development needs outside Rstack's scope, with Prettier formatting and lint-staged commands.
1414

15-
| Command | Description | Powered by |
16-
| ------------ | -------------------------------- | --------------------------------------------------------- |
17-
| `rs dev` | Run the app dev server | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
18-
| `rs build` | Build the app for production | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
19-
| `rs preview` | Preview the app production build | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
20-
| `rs test` | Run tests | [Rstest](https://github.com/web-infra-dev/rstest) |
21-
| `rs lint` | Lint code | [Rslint](https://github.com/web-infra-dev/rslint) |
22-
| `rs lib` | Build library | [Rslib](https://github.com/web-infra-dev/rslib) |
23-
| `rs doc` | Serve or build docs | [Rspress](https://github.com/web-infra-dev/rspress) |
24-
| `rs fmt` | Format code | [Prettier](https://github.com/prettier/prettier) |
25-
| `rs setup` | Install Git hooks | - |
26-
| `rs staged` | Run tasks on staged Git files | [lint-staged](https://github.com/lint-staged/lint-staged) |
15+
| Command | Description | Powered by |
16+
| ------------ | -------------------------------- | ------------------------------------------------------------------------------------------------- |
17+
| `rs dev` | Run the app dev server | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
18+
| `rs build` | Build the app for production | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
19+
| `rs preview` | Preview the app production build | [Rsbuild](https://github.com/web-infra-dev/rsbuild) |
20+
| `rs test` | Run tests | [Rstest](https://github.com/web-infra-dev/rstest) |
21+
| `rs lint` | Lint code | [Rslint](https://github.com/web-infra-dev/rslint) |
22+
| `rs lib` | Build library | [Rslib](https://github.com/web-infra-dev/rslib) |
23+
| `rs doc` | Serve or build docs | [Rspress](https://github.com/web-infra-dev/rspress) |
24+
| `rs fmt` | Format code | [Prettier](https://github.com/prettier/prettier) + [Yuku](https://github.com/yuku-toolchain/yuku) |
25+
| `rs setup` | Install Git hooks | - |
26+
| `rs staged` | Run tasks on staged Git files | [lint-staged](https://github.com/lint-staged/lint-staged) |
2727

2828
Rstack CLI fits into your existing project workflow. It does not replace your runtime, package manager, or task runner, such as [pnpm](https://github.com/pnpm/pnpm), [Bun](https://github.com/oven-sh/bun), [Turborepo](https://github.com/vercel/turborepo), [Nx](https://github.com/nrwl/nx), and [Nub](https://github.com/nubjs/nub).
2929

packages/rstack/THIRD_PARTY_NOTICES.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,65 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2929
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3030
SOFTWARE.
3131

32+
## @prettier/plugin-yuku
33+
34+
This package includes bundled code from
35+
[@prettier/plugin-yuku](https://github.com/prettier/prettier/tree/main/packages/plugin-yuku).
36+
37+
License: MIT
38+
39+
Copyright © James Long and contributors
40+
41+
Permission is hereby granted, free of charge, to any person obtaining a copy of
42+
this software and associated documentation files (the "Software"), to deal in
43+
the Software without restriction, including without limitation the rights to
44+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
45+
of the Software, and to permit persons to whom the Software is furnished to do
46+
so, subject to the following conditions:
47+
48+
The above copyright notice and this permission notice shall be included in all
49+
copies or substantial portions of the Software.
50+
51+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
57+
SOFTWARE.
58+
59+
The bundled plugin also contains MIT-licensed code from:
60+
61+
- emoji-regex 10.6.0, copyright Mathias Bynens
62+
- escape-string-regexp 5.0.0, copyright Sindre Sorhus
63+
- get-east-asian-width 1.6.0, copyright Sindre Sorhus
64+
- index-to-position 1.2.0, copyright Sindre Sorhus
65+
- is-es5-identifier-name 1.0.1, copyright fisker Cheung
66+
- jest-docblock 30.4.0, copyright Meta Platforms, Inc. and Jest contributors
67+
- narrow-emojis 0.0.3, copyright fisker Cheung
68+
- Prettier 3.10.0-dev, copyright James Long and contributors
69+
- to-fast-properties 4.0.0, copyright Petka Antonov, Benjamin Gruenbaum,
70+
John-David Dalton, and Sindre Sorhus
71+
- trim-newlines 5.0.0, copyright Sindre Sorhus
72+
73+
Permission is hereby granted, free of charge, to any person obtaining a copy of
74+
this software and associated documentation files (the "Software"), to deal in
75+
the Software without restriction, including without limitation the rights to
76+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
77+
of the Software, and to permit persons to whom the Software is furnished to do
78+
so, subject to the following conditions:
79+
80+
The above copyright notices and this permission notice shall be included in all
81+
copies or substantial portions of the Software.
82+
83+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
84+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
85+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
86+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
87+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
88+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
89+
SOFTWARE.
90+
3291
## atomically
3392

3493
This package includes bundled code from

packages/rstack/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@
5757
"@rslib/core": "catalog:",
5858
"@rslint/core": "catalog:",
5959
"@rstest/core": "catalog:",
60-
"prettier": "catalog:"
60+
"prettier": "catalog:",
61+
"yuku-parser": "catalog:"
6162
},
6263
"devDependencies": {
64+
"@prettier/plugin-yuku": "catalog:",
6365
"@rspress/core": "catalog:",
6466
"@rstackjs/load-config": "catalog:",
6567
"@rstackjs/test-utils": "catalog:",

packages/rstack/src/fmt/format.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { format, formatWithCursor } from 'prettier';
22
import { resolveFmtOptions } from './config.ts';
33
import { resolveFmtParser } from './parser.ts';
44
import { createFmtPluginResolver } from './plugins.ts';
5+
import { getPrettierPlugins } from './prettierPlugins.ts';
56
import type { FormatTextOptions, FormatTextResult } from './types.ts';
67

78
/** Formats source text without reading formatter config or ignore files. */
@@ -23,6 +24,7 @@ const formatText = async (
2324
...options,
2425
filepath: filePath,
2526
parser,
27+
plugins: getPrettierPlugins(options.plugins),
2628
};
2729

2830
if (cursorOffset === undefined) {

packages/rstack/src/fmt/parser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getFileInfo, type FileInfoOptions, type Options as PrettierOptions } from 'prettier';
2+
import { getPrettierPlugins } from './prettierPlugins.ts';
23

34
const fileInfoOptions = {
45
ignorePath: [],
@@ -15,7 +16,7 @@ const resolveFmtParser = async (
1516
(
1617
await getFileInfo(filePath, {
1718
...fileInfoOptions,
18-
plugins: options.plugins,
19+
plugins: getPrettierPlugins(options.plugins),
1920
})
2021
).inferredParser;
2122

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as yukuPlugin from '@prettier/plugin-yuku';
2+
import type { Options as PrettierOptions } from 'prettier';
3+
4+
type PrettierPlugins = NonNullable<PrettierOptions['plugins']>;
5+
6+
const defaultFmtPlugins: PrettierPlugins = [yukuPlugin];
7+
8+
/** Prepends Yuku so project plugins can override the default parser. */
9+
const getPrettierPlugins = (plugins: PrettierOptions['plugins']): PrettierPlugins =>
10+
plugins?.length ? [...defaultFmtPlugins, ...plugins] : defaultFmtPlugins;
11+
12+
export { getPrettierPlugins };

packages/rstack/src/fmt/serial.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
import { readFile, writeFile } from 'atomically';
44
import { format } from 'prettier';
5+
import { getPrettierPlugins } from './prettierPlugins.ts';
56
import type { FmtFileRequest } from './types.ts';
67

78
const formatFileSerial = async (
89
{ path, options }: FmtFileRequest,
910
shouldWrite: boolean,
1011
): Promise<boolean> => {
1112
const source = await readFile(path, 'utf8');
12-
const formatted = await format(source, options);
13+
const formatted = await format(source, {
14+
...options,
15+
plugins: getPrettierPlugins(options.plugins),
16+
});
1317

1418
if (source === formatted) {
1519
return false;

packages/rstack/tests/cli/fmt/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ test('returns exit code 2 for formatting errors', () => {
312312

313313
expect(result.status).toBe(2);
314314
expect(result.stdout).toBe('');
315-
expect(result.stderr).toContain('error index.ts: SyntaxError: Expression expected.');
315+
expect(result.stderr).toContain('error index.ts: SyntaxError:');
316316
});
317317

318318
test('succeeds when no files can be formatted', () => {

packages/rstack/tests/fmt/discovery.test.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,18 @@ test('applies config ignore patterns outside the config root', async () => {
4747
});
4848
});
4949

50-
test('resolves parsers and accepts unknown extensions with an explicit parser', async () => {
50+
test('uses Yuku parsers by default and accepts an explicit parser', async () => {
5151
await withTempProject(async (rootPath) => {
52+
writeProjectFile(rootPath, 'index.js');
5253
writeProjectFile(rootPath, 'index.ts');
5354
writeProjectFile(rootPath, 'source.custom');
5455
writeProjectFile(rootPath, 'unknown.extension');
5556

5657
const inferredFiles = await discover(rootPath);
5758
const configuredFiles = await discover(rootPath, ['source.custom'], { parser: 'babel' });
5859

59-
expect(relativePaths(rootPath, inferredFiles)).toEqual(['index.ts']);
60-
expect(inferredFiles[0].options.parser).toBe('typescript');
60+
expect(relativePaths(rootPath, inferredFiles)).toEqual(['index.js', 'index.ts']);
61+
expect(inferredFiles.map((file) => file.options.parser)).toEqual(['yuku', 'yuku-ts']);
6162
expect(configuredFiles[0].options).toMatchObject({
6263
filepath: path.join(rootPath, 'source.custom'),
6364
parser: 'babel',
@@ -71,7 +72,10 @@ test('resolves plugins after applying matching overrides', async () => {
7172
rootPath,
7273
'node_modules/prettier-plugin-fixture/index.mjs',
7374
`export default {
74-
languages: [{ name: 'Fixture JSON', parsers: ['json'], extensions: ['.fixture'] }],
75+
languages: [
76+
{ name: 'Fixture JSON', parsers: ['json'], extensions: ['.fixture'] },
77+
{ name: 'Fixture TypeScript', parsers: ['babel'], extensions: ['.ts'] },
78+
],
7579
};
7680
`,
7781
);
@@ -88,6 +92,10 @@ test('resolves plugins after applying matching overrides', async () => {
8892
files: '*.fixture',
8993
options: { plugins: ['prettier-plugin-fixture'] },
9094
},
95+
{
96+
files: '*.ts',
97+
options: { plugins: ['prettier-plugin-fixture'] },
98+
},
9199
{
92100
files: '*.md',
93101
options: { plugins: ['missing-plugin'] },
@@ -104,5 +112,11 @@ test('resolves plugins after applying matching overrides', async () => {
104112
plugins: [pathToFileURL(pluginEntry).href],
105113
},
106114
});
115+
expect(files[1]).toMatchObject({
116+
options: {
117+
parser: 'babel',
118+
plugins: [pathToFileURL(pluginEntry).href],
119+
},
120+
});
107121
});
108122
});

0 commit comments

Comments
 (0)