Skip to content

Commit 8a74d77

Browse files
committed
feat: add support for parser/stringifier
1 parent 0a4fca7 commit 8a74d77

13 files changed

Lines changed: 147 additions & 27 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@
4545
"@types/glob": "^7.2.0",
4646
"@types/node": "^17.0.13",
4747
"dripip": "^0.10.0",
48+
"execa": "^5.1.1",
4849
"husky": "^7.0.4",
50+
"postcss-scss": "^4.0.3",
4951
"rimraf": "^3.0.2",
5052
"tsdx": "^0.14.1",
5153
"tslib": "^2.3.1",
5254
"tsup": "^5.11.11",
53-
"typescript": "^4.5.5",
54-
"execa": "^5.1.1"
55+
"typescript": "^4.5.5"
5556
},
5657
"dependencies": {
5758
"bundle-require": "^3.0.2",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@mixin reset-list {
2+
margin: 0;
3+
padding: 0;
4+
list-style: none;
5+
}
6+
7+
.a {
8+
// Inline comment
9+
color: green;
10+
margin: 20px;
11+
12+
&.b {
13+
border: 1px solid orange;
14+
color: $old-color-var;
15+
}
16+
}
17+
18+
ul {
19+
@include horizontal-list;
20+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.class {
2+
background: $old-color-var;
3+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@mixin reset-list {
2+
margin: 0;
3+
padding: 0;
4+
list-style: none;
5+
}
6+
7+
.a {
8+
// Inline comment
9+
color: green;
10+
margin: 20px;
11+
12+
&.b {
13+
border: 1px solid orange;
14+
color: $new-color-var;
15+
}
16+
}
17+
18+
ul {
19+
@include horizontal-list;
20+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.class {
2+
background: $new-color-var;
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Transform } from '../../src';
2+
import { parse, stringify } from 'postcss-scss';
3+
4+
export const transform: Transform = (fileInfo, api) => {
5+
const root = api.parse(fileInfo.source);
6+
7+
root.walk(node => {
8+
if (node.type === 'decl' && node.value === '$old-color-var') {
9+
node.value = '$new-color-var';
10+
}
11+
});
12+
13+
return root.toString(stringify);
14+
};
15+
16+
export const parser = parse;

src/api.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,49 @@
1-
import postcss, { Root } from 'postcss';
2-
1+
import postcss, { Root, Parser } from 'postcss';
32
export interface TransformAPI {
43
/**
54
* Parse a raw CSS string into an abstract syntax tree and return a PostCSS `Root` node.
65
*/
76
parse(source: string): Root;
87
}
98

10-
const parse: TransformAPI['parse'] = source => {
11-
return postcss.parse(source);
9+
const createAPIParse = ({
10+
parser,
11+
}: {
12+
parser?: Parser;
13+
}): TransformAPI['parse'] => {
14+
const parse: TransformAPI['parse'] = source => {
15+
const result = postcss().process(source, {
16+
// Silence a warning about sourcemaps. Not relevant to this use case.
17+
from: undefined,
18+
parser,
19+
});
20+
21+
// Explicitly destructure root, which lazy evaluates and populates an error
22+
// if one occurs. The error field can then be checked.
23+
const { root } = result;
24+
25+
// Re-surface an PostCSS parsing errors.
26+
// https://github.com/postcss/postcss/issues/1708
27+
if ((result as any).error) {
28+
throw (result as any).error;
29+
}
30+
31+
if (root?.type === 'root') {
32+
return root;
33+
} else {
34+
throw new Error(`Unexpected root node: ${root}`);
35+
}
36+
};
37+
38+
return parse;
1239
};
1340

14-
export const api: TransformAPI = { parse };
41+
export const createAPI = ({
42+
parser,
43+
}: { parser?: Parser } = {}): TransformAPI => {
44+
const api: TransformAPI = {
45+
parse: createAPIParse({ parser }),
46+
};
47+
48+
return api;
49+
};

src/perform.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { api } from './api';
1+
import { createAPI } from './api';
22
import { getAllFilesToTransform, getFileInfo, writeFile } from './files';
33
import { loadTransform } from './transform';
44

@@ -18,8 +18,9 @@ interface ProcessTransformOptions {
1818
* Perform a transformation across a set of files.
1919
*/
2020
export const perform = async (options: ProcessTransformOptions) => {
21-
const transform = await loadTransform(options.transform);
21+
const { transform, parser } = await loadTransform(options.transform);
2222
const files = getAllFilesToTransform(options.files);
23+
const api = createAPI({ parser });
2324

2425
files.map(file => {
2526
const fileInfo = getFileInfo(file);
@@ -33,7 +34,7 @@ export const perform = async (options: ProcessTransformOptions) => {
3334
} catch (err) {
3435
if (err instanceof Error) {
3536
console.error(
36-
`The following error occurred transforming "${file}":\n ${err.message}`
37+
`The following error occurred transforming "${file}":\n ${err.message}${err.stack}`
3738
);
3839
} else {
3940
console.error(

src/transform.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { bundleRequire } from 'bundle-require';
2+
import { Parser } from 'postcss';
23
import { TransformAPI } from './api';
34
import { TransformFileInfo } from './files';
45

@@ -34,14 +35,22 @@ export const validateTransform = (transform: unknown): Transform => {
3435
}
3536
};
3637

38+
interface LoadTransformResult {
39+
transform: Transform;
40+
parser?: Parser;
41+
}
42+
3743
/**
3844
* Load and validate the transform file given the filepath.
3945
*/
40-
export const loadTransform = async (filepath: string): Promise<Transform> => {
46+
export const loadTransform = async (
47+
filepath: string
48+
): Promise<LoadTransformResult> => {
4149
try {
4250
const { mod } = await bundleRequire({ filepath });
4351
const transform = validateTransform(mod.transform || mod.default);
44-
return transform;
52+
const parser = mod.parser;
53+
return { transform, parser };
4554
} catch (err) {
4655
console.error(
4756
`An error occurred loading the transform file. Verify "${filepath}" exists.`

test/api.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Declaration, Rule } from 'postcss';
2-
import { api } from '../src/api';
2+
import { createAPI } from '../src/api';
33

44
describe('api', () => {
5+
const api = createAPI();
6+
57
describe('#parse', () => {
68
it('should return the root node of an abstract syntax tree', () => {
79
const root = api.parse(`.class { color: orange; }`);

0 commit comments

Comments
 (0)