Skip to content

Commit bd0d1d4

Browse files
authored
Merge pull request #26 from Igorbek/cleanup
Release 1.1
2 parents 6fd85e8 + 17a2286 commit bd0d1d4

File tree

9 files changed

+195
-113
lines changed

9 files changed

+195
-113
lines changed

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ It allows to optionally pass options that allow to tweak transformer's behavior.
147147
```ts
148148
interface Options {
149149
getDisplayName(filename: string, bindingName: string | undefined): string | undefined;
150+
identifiers: CustomStyledIdentifiers;
151+
ssr: boolean;
152+
displayName: boolean;
150153
}
151154
```
152155

@@ -167,6 +170,53 @@ function getStyledComponentDisplay(filename, bindingName) {
167170
}
168171
```
169172

173+
### `ssr`
174+
175+
By adding a unique identifier to every styled component, this plugin avoids checksum mismatches
176+
due to different class generation on the client and on the server.
177+
178+
This option allows to disable component id generation by setting it to `false`.
179+
180+
Default value is `true` which means that component id is being injected.
181+
182+
### `displayName`
183+
184+
This option enhances the attached CSS class name on each component with richer output
185+
to help identify your components in the DOM without React DevTools.
186+
187+
It also adds allows you to see the component's `displayName` in React DevTools.
188+
189+
To disable `displayName` generation set this option to `false`
190+
191+
Default value is `true` which means that display name is being injected.
192+
193+
### `identifiers`
194+
195+
This option allows to customize identifiers used by `styled-components` API functions.
196+
197+
> **Warning**. By providing custom identifiers, predefined ones are not added automatically.
198+
> Make sure you add standard APIs in case you meant to use them.
199+
200+
```ts
201+
interface CustomStyledIdentifiers {
202+
styled: string[];
203+
attrs: string[];
204+
}
205+
```
206+
207+
- `styled` - list of identifiers of `styled` API (default `['styled'])
208+
- `attrs` - list of identifiers of `attrs` API (default `['attrs'])
209+
210+
Example
211+
212+
```ts
213+
const styledComponentsTransformer = createStyledComponentsTransformer({
214+
identifiers: {
215+
styled: ['styled', 'typedStyled'] // typedStyled is an additional identifier of styled API
216+
}
217+
});
218+
```
219+
170220
# Notes
171221

172222
Technically, `typescript-plugin-styled-components` is not a TypeScript plugin, since it is only exposed as a TypeScript transformer.

example/webpack/transformer.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11

22
const createStyledComponentsTransformer = require('../../dist').default;
33

4-
const styledComponentsTransformer = createStyledComponentsTransformer({
5-
// getDisplayName: (name) => name
6-
ssr: true,
7-
displayName: false,
8-
});
4+
const styledComponentsTransformer = createStyledComponentsTransformer();
95

106
module.exports = () => ({ before: [styledComponentsTransformer] });
117

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typescript-plugin-styled-components",
3-
"version": "0.0.6",
3+
"version": "1.1.0",
44
"description": "TypeScript transformer for improving the debugging experience of styled-components",
55
"main": "dist/index.js",
66
"homepage": "https://github.com/Igorbek/typescript-plugin-styled-components",
@@ -26,8 +26,8 @@
2626
"@types/jest": "^19.2.4",
2727
"@types/node": "^7.0.31",
2828
"@types/react": "^16.3.13",
29-
"styled-components": "^2.1.2",
3029
"jest": "20",
30+
"styled-components": "^2.1.2",
3131
"ts-jest": "20",
3232
"ts-node": "^3.3.0",
3333
"typescript": "^2.8.3"

src/__tests__/__snapshots__/ssr-baselines.test.ts.snap

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ TypeScript after transform:
4141
4242
import styled from 'styled-components';
4343
export function createButtons() {
44-
const A = styled.button.withConfig({ displayName: "A", componentId: "ssr-12flnm5" }) \` color: red; \`;
45-
const B = styled(A).withConfig({ displayName: "B", componentId: "ssr-1nka2f9" }) \` color: blue; \`;
44+
const A = styled.button.withConfig({ displayName: "A", componentId: "sc-hana72" }) \` color: red; \`;
45+
const B = styled(A).withConfig({ displayName: "B", componentId: "sc-ke4nds" }) \` color: blue; \`;
4646
return { A, B };
4747
}
4848
export function createDivs() {
49-
const A = styled.div.withConfig({ displayName: "A", componentId: "ssr-12flnm5" }) \` color: green; \`;
50-
const B = styled(A).withConfig({ displayName: "B", componentId: "ssr-1nka2f9" }) \` color: yellow; \`;
49+
const A = styled.div.withConfig({ displayName: "A", componentId: "sc-1q7vmnt" }) \` color: green; \`;
50+
const B = styled(A).withConfig({ displayName: "B", componentId: "sc-7q8oop" }) \` color: yellow; \`;
5151
return { A, B };
5252
}
5353
@@ -109,26 +109,26 @@ TypeScript before transform:
109109
TypeScript after transform:
110110
111111
import styled from 'styled-components';
112-
const Button = styled.button.withConfig({ displayName: "Button", componentId: "ssr-mm3di7" }) \`
112+
const Button = styled.button.withConfig({ displayName: "Button", componentId: "sc-1okqsxw" }) \`
113113
color: red;
114114
\`;
115115
declare const nonStyled: any;
116116
const NonButton = nonStyled.button \`
117117
yo
118118
\`;
119-
const OtherButton = styled(Button).withConfig({ displayName: "OtherButton", componentId: "ssr-j426hc" }) \`
119+
const OtherButton = styled(Button).withConfig({ displayName: "OtherButton", componentId: "sc-ce0fkl" }) \`
120120
color: blue;
121121
\`;
122-
const SuperButton = Button.extend.withConfig({ displayName: "SuperButton", componentId: "ssr-w8sv2y" }) \`
122+
const SuperButton = Button.extend.withConfig({ displayName: "SuperButton", componentId: "sc-10xv1bi" }) \`
123123
color: super;
124124
\`;
125125
export default styled.link \`
126126
color: black;
127127
\`;
128-
export const SmallButton = Button.extend.withConfig({ displayName: "SmallButton", componentId: "ssr-f57us" }) \`
128+
export const SmallButton = Button.extend.withConfig({ displayName: "SmallButton", componentId: "sc-8sh5f7" }) \`
129129
font-size: .7em;
130130
\`;
131-
const MiniButton = styled(SmallButton).attrs({ size: "mini" }).withConfig({ displayName: "MiniButton", componentId: "ssr-feuq4j" }) \`
131+
const MiniButton = styled(SmallButton).attrs({ size: "mini" }).withConfig({ displayName: "MiniButton", componentId: "sc-v7haos" }) \`
132132
font-size: .1em;
133133
\`;
134134
@@ -164,7 +164,7 @@ TypeScript after transform:
164164
const styled2 = styled;
165165
const NonButton = styled.button;
166166
const NonStyled = styled \` color: red; \`;
167-
const Link = styled(NonStyled).withConfig({ displayName: "Link", componentId: "ssr-1bdnxv3" }) \` color: red; \`;
167+
const Link = styled(NonStyled).withConfig({ displayName: "Link", componentId: "sc-1xlc242" }) \` color: red; \`;
168168
169169
170170
@@ -213,7 +213,7 @@ TypeScript after transform:
213213
interface LabelProps {
214214
size: number;
215215
}
216-
const CustomLabel = styled.label.withConfig({ displayName: "CustomLabel", componentId: "ssr-vgngw" }) \`
216+
const CustomLabel = styled.label.withConfig({ displayName: "CustomLabel", componentId: "sc-1ydgk9x" }) \`
217217
font-size: \${(props: LabelProps) => props.size + 'px'}
218218
\`;
219219
const LabeledLink = () => <SmallButton />;

src/__tests__/baselines.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import createTransformer from '../';
22
import { expectBaselineTransforms } from './expectTransform';
33

4-
const transformer = createTransformer();
4+
const transformer = createTransformer({ ssr: false });
55

66
expectBaselineTransforms(transformer, __dirname + '/baselines');

src/createTransformer.ts

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import {
88
isTaggedTemplateExpression,
99
} from './ts-is-kind';
1010

11-
import {Options} from './models/Options';
11+
import { Options, CustomStyledIdentifiers } from './models/Options';
1212
import { hash } from './hash';
13-
import * as fs from 'fs';
1413
import * as path from 'path';
1514

1615
/** Detects that a node represents a styled function
@@ -21,9 +20,9 @@ import * as path from 'path';
2120
* styled(Component)
2221
* styledFunction.attrs(attributes)
2322
*/
24-
function isStyledFunction(node: ts.Node, styledIdentifiers: string[]): boolean {
23+
function isStyledFunction(node: ts.Node, identifiers: CustomStyledIdentifiers): boolean {
2524
if (isPropertyAccessExpression(node)) {
26-
if (isStyledObject(node.expression, styledIdentifiers)) {
25+
if (isStyledObject(node.expression, identifiers)) {
2726
return true;
2827
}
2928

@@ -38,20 +37,24 @@ function isStyledFunction(node: ts.Node, styledIdentifiers: string[]): boolean {
3837

3938
if (isCallExpression(node) && node.arguments.length === 1) {
4039

41-
if (isStyledObject(node.expression, styledIdentifiers)) {
40+
if (isStyledObject(node.expression, identifiers)) {
4241
return true;
4342
}
4443

45-
if (isStyledAttrs(node.expression, styledIdentifiers)) {
44+
if (isStyledAttrs(node.expression, identifiers)) {
4645
return true;
4746
}
4847
}
4948

5049
return false;
5150
}
5251

53-
function isStyledObject(node: ts.Node, styledIdentifiers: string[]) {
54-
return node && isIdentifier(node) && (node.text === 'styled' || styledIdentifiers.indexOf(node.text) !== -1);
52+
function isStyledObjectIdentifier(name: string, { styled: styledIdentifiers = ['styled'] }: CustomStyledIdentifiers) {
53+
return styledIdentifiers.indexOf(name) >= 0;
54+
}
55+
56+
function isStyledObject(node: ts.Node, identifiers: CustomStyledIdentifiers) {
57+
return node && isIdentifier(node) && isStyledObjectIdentifier(node.text, identifiers);
5558
}
5659

5760
function isValidComponent(node: ts.Node) {
@@ -66,18 +69,27 @@ function isValidComponentName(name: string) {
6669
return name[0] === name[0].toUpperCase();
6770
}
6871

69-
function isStyledAttrs(node: ts.Node, styledIdentifiers: string[]) {
72+
function isStyledAttrsIdentifier(name: string, { attrs: attrsIdentifiers = ['attrs'] }: CustomStyledIdentifiers) {
73+
return attrsIdentifiers.indexOf(name) >= 0;
74+
}
75+
76+
function isStyledAttrs(node: ts.Node, identifiers: CustomStyledIdentifiers) {
7077
return node && isPropertyAccessExpression(node)
71-
&& node.name.text === 'attrs'
72-
&& isStyledFunction((node as ts.PropertyAccessExpression).expression, styledIdentifiers);
78+
&& isStyledAttrsIdentifier(node.name.text, identifiers)
79+
&& isStyledFunction((node as ts.PropertyAccessExpression).expression, identifiers);
7380
}
7481

7582
function defaultGetDisplayName(filename: string, bindingName: string | undefined): string | undefined {
7683
return bindingName;
7784
}
7885

7986
export function createTransformer(options?: Partial<Options>): ts.TransformerFactory<ts.SourceFile>
80-
export function createTransformer({ getDisplayName = defaultGetDisplayName, styledIdentifiers = [], ssr, displayName = true, rootCheck = 'package.json' } : Partial<Options> = {}) {
87+
export function createTransformer({
88+
getDisplayName = defaultGetDisplayName,
89+
identifiers = {},
90+
ssr = true,
91+
displayName = true
92+
} : Partial<Options> = {}) {
8193

8294
/**
8395
* Infers display name of a styled component.
@@ -98,71 +110,63 @@ export function createTransformer({ getDisplayName = defaultGetDisplayName, styl
98110
return undefined;
99111
}
100112

101-
const separatorRegExp = new RegExp(`\\${path.sep}`, 'g');
102-
const findModuleRoot = (filename: string): string | null => {
103-
if (!filename) {
104-
return null
105-
}
106-
let dir = path.dirname(filename)
107-
if (fs.existsSync(path.join(dir, rootCheck))) {
108-
return dir
109-
} else if (dir !== filename) {
110-
return findModuleRoot(dir)
111-
} else {
112-
return null
113-
}
114-
}
115-
116-
function getIdFromNode(node: ts.Node): string | undefined {
113+
function getIdFromNode(node: ts.Node, sourceRoot: string | undefined, position: number): string | undefined {
117114
if ((isVariableDeclaration(node) && isIdentifier(node.name)) || isExportAssignment(node)) {
118115
const fileName = node.getSourceFile().fileName;
119-
const moduleRoot = findModuleRoot(fileName);
120-
const filePath = moduleRoot ? path.relative(moduleRoot, fileName).replace(separatorRegExp, '/') : '';
121-
return 'ssr-' + hash(`${getDisplayNameFromNode(node)}${filePath}`);
116+
const filePath = sourceRoot ? path.relative(sourceRoot, fileName).replace(path.sep, path.posix.sep) : fileName;
117+
return 'sc-' + hash(`${getDisplayNameFromNode(node)}${filePath}${position}`);
122118
}
123119
return undefined;
124120
}
125121

126122
const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
127-
const visitor: ts.Visitor = (node) => {
128-
if (node.parent
129-
130-
&& isTaggedTemplateExpression(node.parent)
131-
&& node.parent.tag === node
132-
&& node.parent.parent
133-
&& isVariableDeclaration(node.parent.parent)
134-
&& isStyledFunction(node, styledIdentifiers as string[])) {
135-
136-
const styledConfig = [];
137-
138-
if (displayName) {
139-
const displayNameValue = getDisplayNameFromNode(node.parent.parent);
140-
if(displayNameValue){
141-
styledConfig.push(ts.createPropertyAssignment('displayName', ts.createLiteral(displayNameValue)));
142-
}
143-
}
144-
if (ssr) {
145-
const componentId = getIdFromNode(node.parent.parent);
146-
if (componentId) {
147-
styledConfig.push(ts.createPropertyAssignment('componentId', ts.createLiteral(componentId)));
148-
}
123+
const { sourceRoot } = context.getCompilerOptions();
124+
125+
return (node) => {
126+
let lastComponentPosition = 0;
127+
128+
const visitor: ts.Visitor = (node) => {
129+
if (
130+
node.parent
131+
&& isTaggedTemplateExpression(node.parent)
132+
&& node.parent.tag === node
133+
&& node.parent.parent
134+
&& isVariableDeclaration(node.parent.parent)
135+
&& isStyledFunction(node, identifiers)
136+
) {
137+
138+
const styledConfig = [];
139+
140+
if (displayName) {
141+
const displayNameValue = getDisplayNameFromNode(node.parent.parent);
142+
if (displayNameValue) {
143+
styledConfig.push(ts.createPropertyAssignment('displayName', ts.createLiteral(displayNameValue)));
144+
}
145+
}
146+
147+
if (ssr) {
148+
const componentId = getIdFromNode(node.parent.parent, sourceRoot, ++lastComponentPosition);
149+
if (componentId) {
150+
styledConfig.push(ts.createPropertyAssignment('componentId', ts.createLiteral(componentId)));
151+
}
152+
}
153+
154+
return ts.createCall(
155+
ts.createPropertyAccess(node as ts.Expression, 'withConfig'),
156+
undefined,
157+
[ts.createObjectLiteral(styledConfig)]);
149158
}
150-
return ts.createCall(
151-
ts.createPropertyAccess(node as ts.Expression, 'withConfig'),
152-
undefined,
153-
[ts.createObjectLiteral(styledConfig),]);
154-
155-
}
156159

157-
ts.forEachChild(node, n => {
158-
if (!n.parent)
159-
n.parent = node;
160-
});
160+
ts.forEachChild(node, n => {
161+
if (!n.parent)
162+
n.parent = node;
163+
});
161164

162-
return ts.visitEachChild(node, visitor, context);
163-
}
165+
return ts.visitEachChild(node, visitor, context);
166+
}
164167

165-
return (node) => ts.visitNode(node, visitor);
168+
return ts.visitNode(node, visitor);
169+
};
166170
};
167171

168172
return transformer;

0 commit comments

Comments
 (0)