Skip to content

Commit b7e21fc

Browse files
barak igalidoros
andauthored
feat(code-formatter): Implement new code formatter (#2268)
- new formatter activated with `experimental` flag in CLI and language service - support for the previous `tabSize`, `endWithNewLine`, and `wrapLineLength` options + new `wrapLineLength` option - no support for other options Co-authored-by: Ido Rosenthal <[email protected]>
1 parent ae460bf commit b7e21fc

File tree

13 files changed

+2319
-51
lines changed

13 files changed

+2319
-51
lines changed

package-lock.json

Lines changed: 22 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,12 @@ After installing `@stylable/cli`, the `stc-format` command will be available, ru
303303
| `--help` | `h` | Show help | `boolean` | |
304304
| `--version` | `v` | Show version number | `boolean` | |
305305

306+
### Experimental formatter
307+
308+
A new experimental formatter is available using the `--experimental` argument.
309+
310+
Currently not all configuration is accepted by the new formatter, the supported formatting options arguments are `--endWithNewline`, `--indentSize`, and a new `--wrapLineLength`.
311+
306312
### Formatting the source directory
307313

308314
```sh

packages/cli/src/code-format.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
#!/usr/bin/env node
22
import yargs from 'yargs';
33
import { nodeFs } from '@file-services/node';
4-
import { getDocumentFormatting } from '@stylable/code-formatter';
4+
import { getDocumentFormatting, formatCSS } from '@stylable/code-formatter';
55
import { createLogger } from './logger';
66
import { writeFileSync } from 'fs';
77

88
const { join } = nodeFs;
99

1010
const argv = yargs
1111
.usage('$0 [options]')
12+
.option('experimental', {
13+
type: 'boolean',
14+
description: 'use experimental code formatter',
15+
default: false,
16+
})
1217
.option('target', {
1318
type: 'string',
1419
description: 'file or directory to format',
@@ -83,6 +88,12 @@ const argv = yargs
8388
alias: 'r',
8489
default: [] as string[],
8590
})
91+
.option('wrapLineLength', {
92+
type: 'number',
93+
description: 'Length of line to be considered when wrapping',
94+
alias: 'W',
95+
default: 80,
96+
})
8697

8798
.alias('h', 'help')
8899
.alias('v', 'version')
@@ -104,6 +115,8 @@ const {
104115
selectorSeparatorNewline,
105116
target,
106117
silent,
118+
experimental,
119+
wrapLineLength,
107120
} = argv;
108121

109122
const log = createLogger(
@@ -120,18 +133,16 @@ for (const request of requires) {
120133
require(request);
121134
}
122135

123-
function readDirectoryDeep(dirPath: string, fileSuffixFilter = '.st.css') {
124-
const files = nodeFs.readdirSync(dirPath, 'utf-8');
125-
let res: string[] = [];
136+
function readDirectoryDeep(dirPath: string, fileSuffixFilter = '.st.css', res = new Set<string>()) {
137+
const items = nodeFs.readdirSync(dirPath, { withFileTypes: true });
126138

127-
for (const item of files) {
128-
const currentlFilePath = join(dirPath, item);
129-
const itemStat = nodeFs.statSync(join(currentlFilePath));
139+
for (const item of items) {
140+
const path = join(dirPath, item.name);
130141

131-
if (itemStat.isFile() && item.endsWith(fileSuffixFilter)) {
132-
res.push(currentlFilePath);
133-
} else if (itemStat.isDirectory()) {
134-
res = res.concat(readDirectoryDeep(currentlFilePath));
142+
if (item.isFile() && path.endsWith(fileSuffixFilter)) {
143+
res.add(path);
144+
} else if (item.isDirectory()) {
145+
readDirectoryDeep(path, fileSuffixFilter, res);
135146
}
136147
}
137148

@@ -141,20 +152,26 @@ function readDirectoryDeep(dirPath: string, fileSuffixFilter = '.st.css') {
141152
function formatStylesheet(filePath: string) {
142153
const fileContent = nodeFs.readFileSync(filePath, 'utf-8');
143154

144-
const newText = getDocumentFormatting(
145-
fileContent,
146-
{ start: 0, end: fileContent.length },
147-
{
148-
end_with_newline: endWithNewline,
149-
indent_empty_lines: indentEmptyLines,
150-
indent_size: indentSize,
151-
indent_with_tabs: indentWithTabs,
152-
max_preserve_newlines: maxPerserveNewlines,
153-
newline_between_rules: newlineBetweenRules,
154-
preserve_newlines: perserveNewlines,
155-
selector_separator_newline: selectorSeparatorNewline,
156-
}
157-
);
155+
const newText = experimental
156+
? formatCSS(fileContent, {
157+
indent: ' '.repeat(indentSize),
158+
endWithNewline,
159+
wrapLineLength,
160+
})
161+
: getDocumentFormatting(
162+
fileContent,
163+
{ start: 0, end: fileContent.length },
164+
{
165+
end_with_newline: endWithNewline,
166+
indent_empty_lines: indentEmptyLines,
167+
indent_size: indentSize,
168+
indent_with_tabs: indentWithTabs,
169+
max_preserve_newlines: maxPerserveNewlines,
170+
newline_between_rules: newlineBetweenRules,
171+
preserve_newlines: perserveNewlines,
172+
selector_separator_newline: selectorSeparatorNewline,
173+
}
174+
);
158175

159176
if (newText.length) {
160177
writeFileSync(filePath, newText);
@@ -182,7 +199,7 @@ if (formatPathStats.isFile()) {
182199
} else if (formatPathStats.isDirectory()) {
183200
const stylesheets = readDirectoryDeep(target);
184201

185-
if (stylesheets.length) {
202+
if (stylesheets.size) {
186203
for (const stylesheet of stylesheets) {
187204
formatStylesheet(stylesheet);
188205
}

packages/cli/test/code-format-cli.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
createTempDirectory,
88
ITempDirectory,
99
} from '@stylable/e2e-test-kit';
10+
import { deindent } from '@stylable/core-test-kit';
1011

1112
describe('Stylable Code Format Cli', function () {
1213
let tempDir: ITempDirectory;
@@ -135,6 +136,62 @@ describe('Stylable Code Format Cli', function () {
135136
});
136137
});
137138

139+
describe('experimental formatter', () => {
140+
it('should format a directory with a single stylesheet (check indent diff)', () => {
141+
const stylesheetPath = 'style.st.css';
142+
143+
populateDirectorySync(tempDir.path, {
144+
[stylesheetPath]: `.a{prop:\ngreen,\nblue}`,
145+
});
146+
147+
runFormatCliSync(['--target', tempDir.path, '--experimental']);
148+
149+
const dirContent = loadDirSync(tempDir.path);
150+
expect(dirContent[stylesheetPath]).to.equal(
151+
deindent(`
152+
.a {
153+
prop:
154+
green,
155+
blue;
156+
}
157+
`)
158+
);
159+
});
160+
it('should accept configuration', () => {
161+
const stylesheetPath = 'style.st.css';
162+
163+
populateDirectorySync(tempDir.path, {
164+
[stylesheetPath]: `.a{prop:\ngreen,\nblue;prop2:123456789 123456789}`,
165+
});
166+
167+
runFormatCliSync([
168+
'--target',
169+
tempDir.path,
170+
'--experimental',
171+
'--indentSize',
172+
'2',
173+
'--endWithNewline',
174+
'true',
175+
'--wrapLineLength',
176+
'25',
177+
]);
178+
179+
const dirContent = loadDirSync(tempDir.path);
180+
expect(dirContent[stylesheetPath]).to.equal(
181+
deindent(`
182+
.a {
183+
prop:
184+
green,
185+
blue;
186+
prop2: 123456789
187+
123456789;
188+
}
189+
`) + '\n'
190+
);
191+
//
192+
});
193+
});
194+
138195
describe('exceptions', () => {
139196
it('should throw an exception when no stylable stylesheets are found to format', () => {
140197
const filePath = 'file.txt';

packages/code-formatter/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
},
99
"dependencies": {
1010
"@stylable/core": "^5.6.1",
11-
"js-beautify": "^1.14.7"
11+
"@tokey/css-value-parser": "^0.1.2",
12+
"js-beautify": "^1.14.7",
13+
"postcss": "^8.4.18"
1214
},
1315
"files": [
1416
"dist",

0 commit comments

Comments
 (0)