Skip to content

Commit dd5bcf6

Browse files
committed
WIP
1 parent 555dc1a commit dd5bcf6

File tree

2 files changed

+160
-137
lines changed

2 files changed

+160
-137
lines changed

index.js

Lines changed: 150 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,21 @@ function normalizeNodeArray(nodes) {
1919
}
2020
});
2121

22-
if (array.length > 0 && array[array.length - 1].type === 'spacing') {
22+
if (array.length > 0 && isSpacing(array[array.length - 1])) {
2323
array.pop();
2424
}
2525
return array;
2626
}
2727

2828
function checkForInconsistentRule(node, current, context) {
29-
if (context.isGlobal !== context.lastIsGlobal)
29+
if (context.global !== context.lastIsGlobal)
3030
throw new Error(
3131
'Inconsistent rule global/local result in rule "' +
3232
String(node) +
3333
'" (multiple selectors must result in the same mode for the rule)'
3434
);
3535
}
36+
const isSpacing = node => node.type === 'combinator' && node.value === ' ';
3637

3738
function trimSelectors(selector) {
3839
let last;
@@ -45,169 +46,191 @@ function trimSelectors(selector) {
4546
}
4647

4748
function localizeNodez(rule, mode, options) {
48-
// console.log(mode);
4949
const isScopePseudo = node =>
5050
node.value === ':local' || node.value === ':global';
5151

5252
const transform = (node, context) => {
53+
if (context.ignoreNextSpacing && !isSpacing(node)) {
54+
throw new Error('Missing whitespace after ' + context.ignoreNextSpacing);
55+
}
56+
if (context.enforceNoSpacing && isSpacing(node)) {
57+
throw new Error('Missing whitespace before ' + context.enforceNoSpacing);
58+
}
59+
60+
let newNodes;
5361
switch (node.type) {
5462
case 'root': {
55-
const childContext = { ...context };
56-
let overallIsGlobal;
57-
58-
node.each(childNode => {
59-
// isGlobal and hasLocals should not carry over across selectors:
60-
// `:global .foo, .bar -> .foo, :local(.bar)`
61-
childContext.isGlobal = context.isGlobal;
62-
childContext.hasLocals = false;
63-
64-
transform(childNode, childContext);
65-
66-
console.log(overallIsGlobal, childContext);
67-
68-
if (overallIsGlobal == null) {
69-
overallIsGlobal = childContext.isGlobal;
70-
} else {
71-
if (overallIsGlobal !== childContext.isGlobal)
72-
throw new Error(
73-
'Inconsistent rule global/local result in rule "' +
74-
String(node) +
75-
'" (multiple selectors must result in the same mode for the rule)'
76-
);
63+
let resultingGlobal;
64+
context.hasPureGlobals = false;
65+
newNodes = node.nodes.map(function(n) {
66+
const nContext = {
67+
global: context.global,
68+
lastWasSpacing: true,
69+
hasLocals: false,
70+
explicit: false,
71+
};
72+
n = transform(n, nContext);
73+
if (typeof resultingGlobal === 'undefined') {
74+
resultingGlobal = nContext.global;
75+
} else if (resultingGlobal !== nContext.global) {
76+
throw new Error(
77+
'Inconsistent rule global/local result in rule "' +
78+
node +
79+
'" (multiple selectors must result in the same mode for the rule)'
80+
);
7781
}
78-
79-
if (childContext.hasLocals) {
80-
context.hasPureGlobals = false;
82+
if (!nContext.hasLocals) {
83+
context.hasPureGlobals = true;
8184
}
85+
return n;
8286
});
87+
context.global = resultingGlobal;
8388

89+
node.nodes = normalizeNodeArray(newNodes);
90+
// console.log(node.nodes);
8491
break;
8592
}
8693
case 'selector': {
87-
node.each(childNode => transform(childNode, context));
88-
89-
trimSelectors(node);
94+
newNodes = node.map(childNode => transform(childNode, context));
9095

96+
node = node.clone();
97+
node.nodes = normalizeNodeArray(newNodes);
98+
console.log('SECLE', node.toString());
9199
break;
92100
}
93101
case 'combinator': {
94-
if (
95-
node.value === ' ' &&
96-
(context.shouldTrimTrainingWhitespace || !node.next())
97-
) {
98-
//console.log('COMBIN', node.spaces);
99-
context.shouldTrimTrainingWhitespace = false;
100-
node.remove();
102+
if (!isSpacing(node)) break;
103+
104+
if (context.ignoreNextSpacing) {
105+
context.ignoreNextSpacing = false;
106+
context.lastWasSpacing = false;
107+
context.enforceNoSpacing = false;
108+
return null;
101109
}
110+
context.lastWasSpacing = true;
102111
break;
103112
}
104113
case 'pseudo': {
105-
if (!isScopePseudo(node)) {
106-
// This needs to not update `isGlobal` for tests to pass
107-
// the behavior seems _wrong_ tho.
108-
const childContext = { ...context };
109-
console.log('PSEUEDO', node.value, context);
110-
node.each(childNode => transform(childNode, context));
111-
break;
112-
}
113-
114-
if (context.inside) {
115-
throw new Error(
116-
`A ${node.value} is not allowed inside of a ${context.inside}(...)`
117-
);
118-
}
119-
114+
let childContext;
120115
const isNested = !!node.length;
121-
const isGlobal = node.value === ':global';
122-
if (!isNested) {
123-
context.isGlobal = isGlobal;
116+
const isScoped = isScopePseudo(node);
124117

125-
context.shouldTrimTrainingWhitespace = !node.spaces.before;
126-
// console.log(node.spaces);
127-
node.remove();
128-
return null;
129-
}
130-
131-
const childContext = {
132-
...context,
133-
isGlobal,
134-
inside: node.value,
135-
hasLocals: false,
136-
};
137-
138-
// The nodes of a psuedo will be Selectors, which we want to flatten
139-
// into the parent
140-
const nodes = node
141-
.clone()
142-
.map(childNode => transform(childNode, childContext))
143-
.reduce(
144-
(acc, next) =>
145-
acc.concat(next.type === 'selector' ? next.nodes : next),
146-
[]
147-
);
148-
// console.log(context);
149-
if (childContext.hasLocals) {
150-
context.hasLocals = true;
151-
}
118+
// :local(.foo)
119+
if (isNested) {
120+
if (isScoped) {
121+
if (context.inside) {
122+
throw new Error(
123+
`A ${node.value} is not allowed inside of a ${
124+
context.inside
125+
}(...)`
126+
);
127+
}
128+
129+
childContext = {
130+
global: node.value === ':global',
131+
inside: node.value,
132+
hasLocals: false,
133+
explicit: true,
134+
};
135+
// console.log('PSUDI', node.nodes);
136+
137+
newNodes = node
138+
.map(childNode => transform(childNode, childContext))
139+
.reduce((acc, next) => acc.concat(next.nodes), []);
140+
141+
if (newNodes.length) {
142+
const { before, after } = node.spaces;
143+
144+
const first = newNodes[0];
145+
const last = newNodes[newNodes.length - 1];
146+
147+
first.spaces = { before, after: first.spaces.after };
148+
last.spaces = { before: last.spaces.before, after };
149+
}
150+
console.log('PSUDI', node);
151+
node = newNodes;
152+
153+
// // don't leak spacing
154+
// node[0].spaces.before = '';
155+
// node[node.length - 1].spaces.after = '';
156+
break;
157+
} else {
158+
childContext = {
159+
global: context.global,
160+
inside: context.inside,
161+
lastWasSpacing: true,
162+
hasLocals: false,
163+
explicit: context.explicit,
164+
};
165+
newNodes = node.map(childNode =>
166+
transform(childNode, childContext)
167+
);
152168

153-
// console.log('asfasfasf', nodes);
169+
node = node.clone();
170+
node.nodes = normalizeNodeArray(newNodes);
154171

155-
if (nodes.length) {
156-
const { before, after } = node.spaces;
172+
if (childContext.hasLocals) {
173+
context.hasLocals = true;
174+
}
175+
}
176+
break;
157177

158-
const first = nodes[0];
159-
const last = nodes[node.length - 1];
178+
//:local .foo .bar
179+
} else if (isScoped) {
180+
if (context.inside) {
181+
throw new Error(
182+
`A ${node.value} is not allowed inside of a ${
183+
context.inside
184+
}(...)`
185+
);
186+
}
160187

161-
first.spaces = { before, after: first.spaces.after };
162-
last.spaces = { before: last.spaces.before, after };
188+
const next = node.next();
189+
console.log('SPACESS', next, node.spaces);
190+
if (next) next.spaces = node.spaces;
191+
192+
context.ignoreNextSpacing = context.lastWasSpacing
193+
? node.value
194+
: false;
195+
context.enforceNoSpacing = context.lastWasSpacing
196+
? false
197+
: node.value;
198+
context.global = node.value === ':global';
199+
context.explicit = true;
200+
return null;
163201
}
164-
165-
nodes.forEach(childNode => {
166-
node.parent.insertBefore(node, childNode);
167-
});
168-
node.remove();
169-
// const parent = node.parent;
170-
// node.replaceWith(nodes[0].nodes);
171-
// console.log('asfasfasf', nodes, String(parent.parent));
172-
return nodes;
202+
break;
173203
}
174204
case 'id':
175205
case 'class': {
176-
const spaces = { ...node.spaces };
177-
178-
if (context.shouldTrimTrainingWhitespace) {
179-
// console.log('HEREEEEEE', spaces);
180-
spaces.before = '';
181-
context.shouldTrimTrainingWhitespace = false;
182-
}
183-
184-
if (!context.isGlobal) {
185-
// console.log('REPLCE', node.spaces);
206+
if (!context.global) {
207+
console.log('REPLCE', node.spaces);
186208
const innerNode = node.clone();
209+
console.log(innerNode);
187210
innerNode.spaces = { before: '', after: '' };
188211

189-
// console.log(node);
190-
node.replaceWith(
191-
selectorParser.pseudo({
192-
value: ':local',
193-
nodes: [innerNode],
194-
spaces,
195-
})
196-
);
212+
node = selectorParser.pseudo({
213+
value: ':local',
214+
nodes: [innerNode],
215+
spaces: node.spaces,
216+
});
197217
// console.log('HERE');
198218
context.hasLocals = true;
199219
}
200220

201221
break;
202222
}
203223
}
224+
225+
context.lastWasSpacing = false;
226+
context.ignoreNextSpacing = false;
227+
context.enforceNoSpacing = false;
204228
return node;
205229
};
206230

207-
const isGlobal = mode === 'global';
208231
const rootContext = {
209-
isGlobal,
210-
hasPureGlobals: true,
232+
global: mode === 'global',
233+
hasPureGlobals: false,
211234
};
212235

213236
const updatedRule = selectorParser(root => {
@@ -239,7 +262,7 @@ function localizeDeclNode(node, context) {
239262
}
240263

241264
let newUrl = context.options.rewriteUrl(
242-
context.isGlobal,
265+
context.global,
243266
nestedNode.value
244267
);
245268

@@ -349,8 +372,8 @@ function localizeAnimationShorthandDeclValues(decl, context) {
349372

350373
const subContext = {
351374
options: context.options,
352-
isGlobal: context.isGlobal,
353-
localizeNextItem: shouldParseAnimationName && !context.isGlobal,
375+
global: context.global,
376+
localizeNextItem: shouldParseAnimationName && !context.global,
354377
};
355378
return localizeDeclNode(node, subContext);
356379
});
@@ -363,8 +386,8 @@ function localizeDeclValues(localize, decl, context) {
363386
valueNodes.walk((node, index, nodes) => {
364387
const subContext = {
365388
options: context.options,
366-
isGlobal: context.isGlobal,
367-
localizeNextItem: localize && !context.isGlobal,
389+
global: context.global,
390+
localizeNextItem: localize && !context.global,
368391
};
369392
nodes[index] = localizeDeclNode(node, subContext);
370393
});
@@ -433,15 +456,15 @@ module.exports = postcss.plugin('postcss-modules-local-by-default', function(
433456
atrule.walkDecls(function(decl) {
434457
localizeDecl(decl, {
435458
options: options,
436-
isGlobal: globalKeyframes,
459+
global: globalKeyframes,
437460
});
438461
});
439462
} else if (atrule.nodes) {
440463
atrule.nodes.forEach(function(decl) {
441464
if (decl.type === 'decl') {
442465
localizeDecl(decl, {
443466
options: options,
444-
isGlobal: globalMode,
467+
global: globalMode,
445468
});
446469
}
447470
});

0 commit comments

Comments
 (0)