Skip to content

Commit 4dddfa1

Browse files
committed
Implement contracts
1 parent 6c38c38 commit 4dddfa1

File tree

2 files changed

+64
-22
lines changed

2 files changed

+64
-22
lines changed

src/index.js

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,42 @@ const addExports = (css, aliases) => {
117117
css.prepend(createICSSRules(icssImports, exports));
118118
};
119119

120-
module.exports = postcss.plugin(plugin, (options = {}) => css => {
120+
const getMessages = aliases =>
121+
Object.keys(aliases)
122+
.map(name => ({ plugin, type: "icss-scoped", name, value: aliases[name] }))
123+
.reduce((acc, msg) => [...acc, msg], []);
124+
125+
module.exports = postcss.plugin(plugin, (options = {}) => (css, result) => {
121126
const generateScopedName =
122127
options.generateScopedName ||
123128
genericNames("[name]__[local]---[hash:base64:5]");
124129
const input = (css && css.source && css.source.input) || {};
125130
const aliases = {};
126-
const getAlias = name => {
127-
const alias = generateScopedName(name, input.from, input.css);
128-
aliases[name] = alias;
129-
return alias;
130-
};
131131
walkRules(css, rule => {
132+
const getAlias = name => {
133+
if (aliases[name]) {
134+
return aliases[name];
135+
}
136+
// icss-value contract
137+
if (
138+
result.messages.find(
139+
msg => msg.type === "icss-value" && msg.value === name
140+
)
141+
) {
142+
return name;
143+
}
144+
const alias = generateScopedName(name, input.from, input.css);
145+
// icss-scoped contract
146+
if (
147+
result.messages.find(
148+
msg => msg.type === "icss-scoped" && msg.name === name
149+
)
150+
) {
151+
result.warn(`'${name}' already declared`, { node: rule });
152+
}
153+
aliases[name] = alias;
154+
return alias;
155+
};
132156
try {
133157
rule.selector = localizeSelectors(
134158
rule.selector,
@@ -139,5 +163,14 @@ module.exports = postcss.plugin(plugin, (options = {}) => css => {
139163
throw rule.error(e.message);
140164
}
141165
});
142-
addExports(css, aliases);
166+
// icss-composed contract
167+
const composedAliases = Object.keys(aliases).reduce((acc, name) => {
168+
const composedMsg = result.messages.find(
169+
msg => msg.type === "icss-composed" && msg.name === name
170+
);
171+
acc[name] = aliases[name] + (composedMsg ? ` ${composedMsg.value}` : "");
172+
return acc;
173+
}, {});
174+
result.messages.push(...getMessages(aliases));
175+
addExports(css, composedAliases);
143176
});

test/test.js

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import postcss from "postcss";
33
import stripIndent from "strip-indent";
44
import plugin from "../src";
55

6-
const strip = input =>
7-
stripIndent(input).replace(/^\n/, "").replace(/\s+$/, "");
6+
const strip = input => stripIndent(input).trim();
87
const compile = (input, options) =>
98
postcss([plugin(options)])
109
.process(input, options)
1110
.catch(e => Promise.reject(e.message));
1211
const generateScopedName = name => `__scope__${name}`;
13-
const messagesPlugin = messages => () => (css, result) => {
14-
result.push(...messages);
12+
const messagesPlugin = messages => (css, result) => {
13+
result.messages.push(...messages);
1514
};
1615

1716
const runCSS = ({ fixture, expected, options }) => {
@@ -29,14 +28,25 @@ const runError = ({ fixture, error, options }) => {
2928
);
3029
};
3130

32-
const runMessages = ({ fixture, inputMessages, outputMessages, expected }) => {
31+
const runMessages = ({
32+
fixture,
33+
inputMessages = [],
34+
outputMessages,
35+
warnings = [],
36+
expected
37+
}) => {
3338
const processor = postcss([
3439
messagesPlugin(inputMessages),
3540
plugin({ generateScopedName })
3641
]).process(strip(fixture));
3742
return processor.then(result => {
38-
expect(result.messages).toEqual(outputMessages);
39-
expect(result.css).toEqual(strip(expected));
43+
expect(result.messages.filter(msg => msg.type !== "warning")).toEqual(
44+
outputMessages
45+
);
46+
expect(result.warnings().map(msg => msg.text)).toEqual(warnings);
47+
if (expected) {
48+
expect(result.css).toEqual(strip(expected));
49+
}
4050
});
4151
};
4252

@@ -718,6 +728,7 @@ test("icss-scoped contract", () => {
718728
}
719729
.foo {}
720730
.bar {}
731+
.foo {}
721732
`,
722733
expected: `
723734
:export {
@@ -726,14 +737,11 @@ test("icss-scoped contract", () => {
726737
}
727738
.__scope__foo {}
728739
.__scope__bar {}
740+
.__scope__foo {}
729741
`,
742+
inputMessages,
730743
outputMessages: [
731744
...inputMessages,
732-
{
733-
plugin: "postcss-icss-selectors",
734-
type: "warning",
735-
text: `'foo' already declared by 'previous-plugin'`
736-
},
737745
{
738746
plugin: "postcss-icss-selectors",
739747
type: "icss-scoped",
@@ -746,7 +754,8 @@ test("icss-scoped contract", () => {
746754
name: "bar",
747755
value: "__scope__bar"
748756
}
749-
]
757+
],
758+
warnings: [`'foo' already declared`]
750759
});
751760
});
752761

@@ -778,8 +787,8 @@ test("icss-composed contract", () => {
778787
expected: `
779788
:export {
780789
foo: __scope__foo __compose__foo;
781-
bar: __scope__bar __compose__bar;
782-
baz: __scope__baz
790+
baz: __scope__baz;
791+
bar: __scope__bar __compose__bar
783792
}
784793
.__scope__foo {}
785794
.__scope__bar {}

0 commit comments

Comments
 (0)