diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6142b0e..637c329 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+- namespace prefix to help multiple instances of library not clash (fix #381 in #395)
+
## [1.5.0]
- transform anonymous default exports (fix #371 in #367)
diff --git a/README.md b/README.md
index ec79060..431a5cd 100644
--- a/README.md
+++ b/README.md
@@ -225,6 +225,7 @@ interface Options {
ssr: boolean;
displayName: boolean;
minify: boolean;
+ componentIdPrefix: string;
}
```
@@ -275,6 +276,12 @@ The minification is not exactly the same and may produce slightly different resu
Default value is `false` which means the minification is not being performed.
+### `componentIdPrefix`
+
+To avoid colisions when running more than one insance of typescript-plugin-styled-components at a time, you can add a componentIdPrefix by providing an arbitrary string to this option.
+
+Default value is `''` which means that no namespacing will happen.
+
### `identifiers`
This option allows to customize identifiers used by `styled-components` API functions.
diff --git a/src/__tests__/baselines/componentIdPrefix/issue33.ts.baseline b/src/__tests__/baselines/componentIdPrefix/issue33.ts.baseline
new file mode 100644
index 0000000..8051f5b
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/issue33.ts.baseline
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`issue33.ts 1`] = `
+
+File: issue33.ts
+Source code:
+
+ declare const styled: any;
+ declare const $: any;
+ declare const jQuery: any;
+ declare const _: any;
+
+ declare const Button: any;
+
+ const Button1 = Button.extend\` color: red \`;
+
+ const Button2 = $.extend\` color: red \`;
+ const Button3 = jQuery.extend\` color: red \`;
+ const Button4 = _.extend\` color: red \`;
+
+
+TypeScript before transform:
+
+ declare const styled: any;
+ declare const $: any;
+ declare const jQuery: any;
+ declare const _: any;
+ declare const Button: any;
+ const Button1 = Button.extend \` color: red \`;
+ const Button2 = $.extend \` color: red \`;
+ const Button3 = jQuery.extend \` color: red \`;
+ const Button4 = _.extend \` color: red \`;
+
+
+TypeScript after transform:
+
+ declare const styled: any;
+ declare const $: any;
+ declare const jQuery: any;
+ declare const _: any;
+ declare const Button: any;
+ const Button1 = Button.extend \` color: red \`;
+ const Button2 = $.extend \` color: red \`;
+ const Button3 = jQuery.extend \` color: red \`;
+ const Button4 = _.extend \` color: red \`;
+
+
+
+`;
diff --git a/src/__tests__/baselines/componentIdPrefix/multiple-components.tsx.baseline b/src/__tests__/baselines/componentIdPrefix/multiple-components.tsx.baseline
new file mode 100644
index 0000000..a792748
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/multiple-components.tsx.baseline
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`multiple-components.tsx 1`] = `
+
+File: multiple-components.tsx
+Source code:
+
+ import styled from 'styled-components';
+
+ export function createButtons() {
+ const A = styled.button\` color: red; \`;
+ const B = styled(A)\` color: blue; \`;
+
+ return { A, B };
+ }
+
+ export function createDivs() {
+ const A = styled.div\` color: green; \`;
+ const B = styled(A)\` color: yellow; \`;
+
+ return { A, B };
+ }
+
+
+TypeScript before transform:
+
+ import styled from "styled-components";
+ export function createButtons() {
+ const A = styled.button \` color: red; \`;
+ const B = styled(A) \` color: blue; \`;
+ return { A, B };
+ }
+ export function createDivs() {
+ const A = styled.div \` color: green; \`;
+ const B = styled(A) \` color: yellow; \`;
+ return { A, B };
+ }
+
+
+TypeScript after transform:
+
+ import styled from 'styled-components';
+ export function createButtons() {
+ const A = styled.button.withConfig({ displayName: "test-A", componentId: "test-hana72" }) \` color: red; \`;
+ const B = styled(A).withConfig({ displayName: "test-B", componentId: "test-ke4nds" }) \` color: blue; \`;
+ return { A, B };
+ }
+ export function createDivs() {
+ const A = styled.div.withConfig({ displayName: "test-A", componentId: "test-1q7vmnt" }) \` color: green; \`;
+ const B = styled(A).withConfig({ displayName: "test-B", componentId: "test-7q8oop" }) \` color: yellow; \`;
+ return { A, B };
+ }
+
+
+
+`;
diff --git a/src/__tests__/baselines/componentIdPrefix/sample1.ts.baseline b/src/__tests__/baselines/componentIdPrefix/sample1.ts.baseline
new file mode 100644
index 0000000..5a7066a
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/sample1.ts.baseline
@@ -0,0 +1,96 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`sample1.ts 1`] = `
+
+File: sample1.ts
+Source code:
+
+ import styled from 'styled-components';
+
+ const Button = styled.button\`
+ color: red;
+ \`;
+
+ declare const nonStyled: any;
+
+ const NonButton = nonStyled.button\`
+ yo
+ \`;
+
+ const OtherButton = styled(Button)\`
+ color: blue;
+ \`;
+
+ const SuperButton = Button.extend\`
+ color: super;
+ \`;
+
+ export default styled.link\`
+ color: black;
+ \`;
+
+ export const SmallButton = Button.extend\`
+ font-size: .7em;
+ \`;
+
+ const MiniButton = styled(SmallButton).attrs({ size: 'mini' })\`
+ font-size: .1em;
+ \`;
+
+
+TypeScript before transform:
+
+ import styled from "styled-components";
+ const Button = styled.button \`
+ color: red;
+ \`;
+ declare const nonStyled: any;
+ const NonButton = nonStyled.button \`
+ yo
+ \`;
+ const OtherButton = styled(Button) \`
+ color: blue;
+ \`;
+ const SuperButton = Button.extend \`
+ color: super;
+ \`;
+ export default styled.link \`
+ color: black;
+ \`;
+ export const SmallButton = Button.extend \`
+ font-size: .7em;
+ \`;
+ const MiniButton = styled(SmallButton).attrs({ size: "mini" }) \`
+ font-size: .1em;
+ \`;
+
+
+TypeScript after transform:
+
+ import styled from 'styled-components';
+ const Button = styled.button.withConfig({ displayName: "test-Button", componentId: "test-1okqsxw" }) \`
+ color: red;
+ \`;
+ declare const nonStyled: any;
+ const NonButton = nonStyled.button \`
+ yo
+ \`;
+ const OtherButton = styled(Button).withConfig({ displayName: "test-OtherButton", componentId: "test-ce0fkl" }) \`
+ color: blue;
+ \`;
+ const SuperButton = Button.extend \`
+ color: super;
+ \`;
+ export default styled.link.withConfig({ componentId: "test-vba0dl" }) \`
+ color: black;
+ \`;
+ export const SmallButton = Button.extend \`
+ font-size: .7em;
+ \`;
+ const MiniButton = styled(SmallButton).attrs({ size: "mini" }).withConfig({ displayName: "test-MiniButton", componentId: "test-ndnumj" }) \`
+ font-size: .1em;
+ \`;
+
+
+
+`;
diff --git a/src/__tests__/baselines/componentIdPrefix/sample2.ts.baseline b/src/__tests__/baselines/componentIdPrefix/sample2.ts.baseline
new file mode 100644
index 0000000..7554e7e
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/sample2.ts.baseline
@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`sample2.ts 1`] = `
+
+File: sample2.ts
+Source code:
+
+ import styled from 'styled-components';
+
+ const styled2 = styled;
+ const NonButton = styled.button;
+ const NonStyled = styled\` color: red; \`;
+ const Link = styled(NonStyled)\` color: red; \`;
+
+
+TypeScript before transform:
+
+ import styled from "styled-components";
+ const styled2 = styled;
+ const NonButton = styled.button;
+ const NonStyled = styled \` color: red; \`;
+ const Link = styled(NonStyled) \` color: red; \`;
+
+
+TypeScript after transform:
+
+ import styled from 'styled-components';
+ const styled2 = styled;
+ const NonButton = styled.button;
+ const NonStyled = styled \` color: red; \`;
+ const Link = styled(NonStyled).withConfig({ displayName: "test-Link", componentId: "test-1xlc242" }) \` color: red; \`;
+
+
+
+`;
diff --git a/src/__tests__/baselines/componentIdPrefix/sample3.tsx.baseline b/src/__tests__/baselines/componentIdPrefix/sample3.tsx.baseline
new file mode 100644
index 0000000..cf12265
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/sample3.tsx.baseline
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`sample3.tsx 1`] = `
+
+File: sample3.tsx
+Source code:
+
+ import * as React from 'react';
+ import styled from '../themed-styled';
+ import { SmallButton } from './sample1';
+
+ interface LabelProps {
+ size: number;
+ }
+
+ const CustomLabel = styled.label\`
+ font-size: \${(props: LabelProps) => props.size + 'px'}
+ \`;
+
+ const LabeledLink = () => ;
+
+ export default CustomLabel;
+
+
+TypeScript before transform:
+
+ import * as React from "react";
+ import styled from "../themed-styled";
+ import { SmallButton } from "./sample1";
+ interface LabelProps {
+ size: number;
+ }
+ const CustomLabel = styled.label \`
+ font-size: \${(props: LabelProps) => props.size + "px"}
+ \`;
+ const LabeledLink = () => ;
+ export default CustomLabel;
+
+
+TypeScript after transform:
+
+ import * as React from 'react';
+ import styled from '../themed-styled';
+ import { SmallButton } from './sample1';
+ interface LabelProps {
+ size: number;
+ }
+ const CustomLabel = styled.label.withConfig({ displayName: "test-CustomLabel", componentId: "test-1ydgk9x" }) \`
+ font-size: \${(props: LabelProps) => props.size + 'px'}
+ \`;
+ const LabeledLink = () => ;
+ export default CustomLabel;
+
+
+
+`;
diff --git a/src/__tests__/baselines/componentIdPrefix/style-objects.ts.baseline b/src/__tests__/baselines/componentIdPrefix/style-objects.ts.baseline
new file mode 100644
index 0000000..32cae79
--- /dev/null
+++ b/src/__tests__/baselines/componentIdPrefix/style-objects.ts.baseline
@@ -0,0 +1,96 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`style-objects.ts 1`] = `
+
+File: style-objects.ts
+Source code:
+
+ declare const styled: any;
+
+ const Button = styled.button({
+ color: 'red'
+ });
+
+ declare const nonStyled: any;
+
+ const NonButton = nonStyled.button({
+ color: 'red'
+ });
+
+ const OtherButton = styled(Button)({
+ color: 'blue'
+ });
+
+ const SuperButton = Button.extend({
+ color: 'super'
+ });
+
+ export default styled.link({
+ color: 'black'
+ });
+
+ export const SmallButton = Button.extend({
+ fontSize: '.7em'
+ });
+
+ const MiniButton = styled(SmallButton).attrs({ size: 'mini' })({
+ fontSize: '.1em'
+ });
+
+
+TypeScript before transform:
+
+ declare const styled: any;
+ const Button = styled.button({
+ color: "red"
+ });
+ declare const nonStyled: any;
+ const NonButton = nonStyled.button({
+ color: "red"
+ });
+ const OtherButton = styled(Button)({
+ color: "blue"
+ });
+ const SuperButton = Button.extend({
+ color: "super"
+ });
+ export default styled.link({
+ color: "black"
+ });
+ export const SmallButton = Button.extend({
+ fontSize: ".7em"
+ });
+ const MiniButton = styled(SmallButton).attrs({ size: "mini" })({
+ fontSize: ".1em"
+ });
+
+
+TypeScript after transform:
+
+ declare const styled: any;
+ const Button = styled.button.withConfig({ displayName: "test-Button", componentId: "test-7b7p9e" })({
+ color: 'red'
+ });
+ declare const nonStyled: any;
+ const NonButton = nonStyled.button({
+ color: 'red'
+ });
+ const OtherButton = styled(Button).withConfig({ displayName: "test-OtherButton", componentId: "test-14ah7t" })({
+ color: 'blue'
+ });
+ const SuperButton = Button.extend({
+ color: 'super'
+ });
+ export default styled.link.withConfig({ componentId: "test-8xjslt" })({
+ color: 'black'
+ });
+ export const SmallButton = Button.extend({
+ fontSize: '.7em'
+ });
+ const MiniButton = styled(SmallButton).attrs({ size: "mini" }).withConfig({ displayName: "test-MiniButton", componentId: "test-ad4g7l" })({
+ fontSize: '.1em'
+ });
+
+
+
+`;
diff --git a/src/__tests__/componentIdPrefix-baselines.test.ts b/src/__tests__/componentIdPrefix-baselines.test.ts
new file mode 100644
index 0000000..f22ea4b
--- /dev/null
+++ b/src/__tests__/componentIdPrefix-baselines.test.ts
@@ -0,0 +1,9 @@
+import createTransformer from '..';
+import { expectBaselineTransforms } from './expectTransform';
+
+const transformer = createTransformer({
+ componentIdPrefix: 'test'
+});
+
+expectBaselineTransforms(transformer, __dirname + '/fixtures/base', 'baselines/componentIdPrefix');
+expectBaselineTransforms(transformer, __dirname + '/fixtures/componentIdPrefix', 'baselines/componentIdPrefix');
diff --git a/src/__tests__/fixtures/componentIdPrefix/multiple-components.tsx b/src/__tests__/fixtures/componentIdPrefix/multiple-components.tsx
new file mode 100644
index 0000000..5789b9b
--- /dev/null
+++ b/src/__tests__/fixtures/componentIdPrefix/multiple-components.tsx
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+export function createButtons() {
+ const A = styled.button` color: red; `;
+ const B = styled(A)` color: blue; `;
+
+ return { A, B };
+}
+
+export function createDivs() {
+ const A = styled.div` color: green; `;
+ const B = styled(A)` color: yellow; `;
+
+ return { A, B };
+}
diff --git a/src/createTransformer.ts b/src/createTransformer.ts
index ad02b56..8955b5b 100644
--- a/src/createTransformer.ts
+++ b/src/createTransformer.ts
@@ -122,7 +122,8 @@ export function createTransformer({
identifiers = {},
ssr = true,
displayName = true,
- minify = false
+ minify = false,
+ componentIdPrefix = '',
} : Partial = {}) {
/**
@@ -132,9 +133,9 @@ export function createTransformer({
* (const|var|let) ComponentName = styled...
* export default styled...
*/
- function getDisplayNameFromNode(node: ts.Node): string | undefined {
+ function getDisplayNameFromNode(node: ts.Node, componentIdPrefix?: string): string | undefined {
if (isVariableDeclaration(node) && isIdentifier(node.name)) {
- return getDisplayName(node.getSourceFile().fileName, node.name.text);
+ return (!!componentIdPrefix ? `${componentIdPrefix}-` : '') + getDisplayName(node.getSourceFile().fileName, node.name.text);
}
if (isExportAssignment(node)) {
@@ -144,11 +145,11 @@ export function createTransformer({
return undefined;
}
- function getIdFromNode(node: ts.Node, sourceRoot: string | undefined, position: number): string | undefined {
+ function getIdFromNode(node: ts.Node, sourceRoot: string | undefined, position: number, componentIdPrefix?: string): string | undefined {
if ((isVariableDeclaration(node) && isIdentifier(node.name)) || isExportAssignment(node)) {
const fileName = node.getSourceFile().fileName;
const filePath = sourceRoot ? path.relative(sourceRoot, fileName).replace(path.sep, path.posix.sep) : fileName;
- return 'sc-' + hash(`${getDisplayNameFromNode(node)}${filePath}${position}`);
+ return `${!!componentIdPrefix ? componentIdPrefix : 'sc'}-` + hash(`${getDisplayNameFromNode(node)}${filePath}${position}`);
}
return undefined;
}
@@ -187,17 +188,17 @@ export function createTransformer({
const styledConfig = [];
if (displayName) {
- const displayNameValue = getDisplayNameFromNode(node.parent.parent);
+ const displayNameValue = getDisplayNameFromNode(node.parent.parent, componentIdPrefix);
if (displayNameValue) {
styledConfig.push(ts.createPropertyAssignment('displayName', ts.createLiteral(displayNameValue)));
- }
+ }
}
if (ssr) {
- const componentId = getIdFromNode(node.parent.parent, sourceRoot, ++lastComponentPosition);
+ const componentId = getIdFromNode(node.parent.parent, sourceRoot, ++lastComponentPosition, componentIdPrefix);
if (componentId) {
- styledConfig.push(ts.createPropertyAssignment('componentId', ts.createLiteral(componentId)));
- }
+ styledConfig.push(ts.createPropertyAssignment('componentId', ts.createLiteral(componentId)));
+ }
}
if (styledConfig.length > 0) {
diff --git a/src/models/Options.ts b/src/models/Options.ts
index 116103b..3b0d462 100644
--- a/src/models/Options.ts
+++ b/src/models/Options.ts
@@ -5,17 +5,17 @@ export interface Options {
* Default strategy is to use `bindingName` if it's defined and use inference algorithm from `filename` otherwise.
*/
getDisplayName(filename: string, bindingName: string | undefined): string | undefined;
-
+
/**
* This option allows to customize identifiers used by `styled-components` API functions.
*/
identifiers: CustomStyledIdentifiers;
-
+
/**
* By adding a unique identifier to every styled component, this plugin avoids checksum mismatches
* due to different class generation on the client and on the server.
* This option allows to disable component id generation by setting it to `false`
- *
+ *
* @defaultValue `true`
*/
ssr: boolean;
@@ -24,9 +24,9 @@ export interface Options {
* This option enhances the attached CSS class name on each component with richer output
* to help identify your components in the DOM without React DevTools.
* It also adds allows you to see the component's `displayName` in React DevTools.
- *
+ *
* To disable `displayName` generation set this option to `false`
- *
+ *
* @defaultValue `true`
*/
displayName: boolean;
@@ -39,6 +39,14 @@ export interface Options {
* @experimental The minification feature is experimental.
*/
minify: boolean;
+
+ /**
+ * By adding a componentIdPrefix, running multiple instances of typescript-plugin-styled-components
+ * will not result in clashes caused by the class generation hash.
+ *
+ * @defaultValue `''`
+ */
+ componentIdPrefix: string;
}
export interface CustomStyledIdentifiers {
@@ -58,28 +66,28 @@ export interface CustomStyledIdentifiers {
/**
* Identifiers of `keyframes` function.
- *
+ *
* @defaultValue `['keyframes']`
*/
keyframes?: string[];
/**
* Identifiers of `css` function.
- *
+ *
* @defaultValue `['css']`
*/
css?: string[];
/**
* Identifiers of `createGlobalStyle` function.
- *
+ *
* @defaultValue `['createGlobalStyle']`
*/
createGlobalStyle?: string[];
/**
* Identifiers of `extend` function.
- *
+ *
* @defaultValue `[]`
*/
extend?: string[];