Skip to content
This repository was archived by the owner on Feb 16, 2021. It is now read-only.

Commit 6d658bd

Browse files
committed
feat(plugin): Get a typescript file by each CSS
Given a CSS file the plugin has to create a typescript file. closes #4
1 parent 3b9f62b commit 6d658bd

10 files changed

+168
-4
lines changed

src/buildFile.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const camelCase = require('camelcase');
2+
const path = require('path');
3+
4+
import { PostcssTypescriptCss } from './namespace/PostcssTypescriptCss';
5+
6+
const build = (file: PostcssTypescriptCss.Options) => {
7+
const filename = path.basename(file.cssFileName, '.postcss');
8+
return (
9+
`export const ${filename}Style = {
10+
${
11+
file.content.map((c: string) => {
12+
return ` ${camelCase(c)}: '${c}'\n`;
13+
})
14+
},
15+
};
16+
`
17+
);
18+
};
19+
20+
export default build;

src/namespace/PostcssTypescriptCss.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export namespace PostcssTypescriptCss {
2+
export interface Options {
3+
cssFileName: string;
4+
content: string[];
5+
}
6+
}

src/postcss-typescript-css.ts

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1-
const postcssTypescriptCss = () => {
2-
return 'Welcome to postcss-typescript-css';
3-
};
1+
import * as postcss from 'postcss';
2+
const parser = require('postcss-selector-parser');
3+
4+
import { PostcssTypescriptCss } from './namespace/PostcssTypescriptCss';
5+
import saveFile from './saveFile';
6+
7+
const postcssTypescriptCss = postcss.plugin<PostcssTypescriptCss.Options>('postcss-typescript-css', (opts) => {
8+
return (css, result) => {
9+
return new Promise((resolve, reject) => {
10+
const classes = new Set();
11+
css.walk((node: postcss.Rule) => {
12+
if (node.type === 'rule') {
13+
parser((selectors: postcss.Rule) => {
14+
selectors.walk((selector: any) => {
15+
if (selector.type === 'class') {
16+
classes.add(selector.value);
17+
}
18+
});
19+
}).process(node.selector).result;
20+
}
21+
});
22+
opts = {
23+
cssFileName: css.source.input.file,
24+
content: [...classes],
25+
};
26+
saveFile(opts)
27+
.then(() => {
28+
resolve();
29+
})
30+
.catch((err) => {
31+
reject(err);
32+
});
33+
});
34+
};
35+
});
436

537
export = postcssTypescriptCss;

src/saveFile.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { writeFileSync } from 'fs';
2+
const path = require('path');
3+
4+
import { PostcssTypescriptCss } from './namespace/PostcssTypescriptCss';
5+
import build from './buildFile';
6+
7+
const saveFile = (file: PostcssTypescriptCss.Options) => {
8+
return new Promise((resolve, reject) => {
9+
try {
10+
const dirname = path.dirname(file.cssFileName);
11+
const filename = path.basename(file.cssFileName, '.postcss');
12+
writeFileSync(`${dirname}/${filename}.ts`, build(file));
13+
resolve(true);
14+
} catch (err) {
15+
reject(err.toString());
16+
}
17+
});
18+
};
19+
20+
export default saveFile;

src/spec/buildFile.spec.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import test from 'ava';
2+
import build from './../buildFile';
3+
4+
test('should create a string with the classes as a properties', t => {
5+
const template = build({
6+
cssFileName: 'fakeComponent',
7+
content: ['.FakeComponent', '.FakeComponent-descendentName', '.FakeComponent--modifierName']
8+
});
9+
t.true(template.includes('fakeComponentStyle'));
10+
t.true(template.includes('fakeComponent:'));
11+
t.true(template.includes('fakeComponentDescendentName:'));
12+
t.true(template.includes('fakeComponentModifierName:'));
13+
});
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import test, { TestContext } from 'ava';
2+
import { readFileSync } from 'fs';
3+
const path = require('path');
4+
import * as postcss from 'postcss';
5+
6+
import { PostcssTypescriptCss } from '../namespace/PostcssTypescriptCss';
7+
import * as plugin from '../postcss-typescript-css';
8+
9+
function run(t: TestContext, input: {css: string, from: string}) {
10+
return postcss([ plugin ]).process(input.css, { from: input.from })
11+
.then((result) => {
12+
const fakeComponentTS = readFileSync(path.join(__dirname, 'postcss/fakeComponent.ts'), 'utf8');
13+
t.true(fakeComponentTS.includes('fakeComponentStyle'));
14+
t.true(fakeComponentTS.includes('fakeComponent:'));
15+
t.true(fakeComponentTS.includes('fakeComponentDescendentName:'));
16+
t.true(fakeComponentTS.includes('fakeComponentModifierName:'));
17+
})
18+
.catch((err: string) => {
19+
t.true(err.includes('TypeError: Path must be a string'));
20+
});
21+
}
22+
23+
test('should create a ts file', t => {
24+
const cssFile = path.join(__dirname, 'postcss/fakeComponent.postcss');
25+
const cssContent = readFileSync(cssFile, 'utf8');
26+
return run(t, { css: cssContent, from: cssFile });
27+
});
28+
29+
test('throws if cssFileName is null', t => {
30+
return run(t, { css: '', from: '' });
31+
});
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.FakeComponent {
2+
color: green;
3+
}
4+
.FakeComponent-descendentName {
5+
color: yellow;
6+
}
7+
.FakeComponent--modifierName {
8+
color: red;
9+
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.FakeComponent2 {
2+
color: green;
3+
}
4+
.FakeComponent2-descendentName {
5+
color: yellow;
6+
}
7+
.FakeComponent2--modifierName {
8+
color: red;
9+
}

src/spec/saveFile.spec.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import test from 'ava';
2+
import saveFile from './../saveFile';
3+
const path = require('path');
4+
5+
const cssFileName = path.join(__dirname, 'postcss/fakeComponent2.postcss');
6+
7+
test('should create a new ts file', async (t) => {
8+
t.is(await saveFile({
9+
cssFileName,
10+
content: ['.FakeComponent', '.FakeComponent2-descendentName', '.FakeComponent2--modifierName']
11+
}), true);
12+
const tsFile = path.join(__dirname, 'postcss/fakeComponent2.ts');
13+
const tsFileName = path.basename(tsFile, '.ts');
14+
t.is(tsFileName, 'fakeComponent2');
15+
});
16+
17+
test('throws if there is not a file name or content', async (t) => {
18+
saveFile({
19+
cssFileName: null,
20+
content: null
21+
}).catch((err: string) => {
22+
t.true(err.includes('TypeError: Path must be a string'));
23+
});
24+
});

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
"outDir": "dist",
44
"rootDir": "src",
5-
"sourceMap": false,
5+
"sourceMap": true,
66
"noImplicitAny": true,
77
"declaration": false,
88
"module": "commonjs",

0 commit comments

Comments
 (0)