整体思路
- 识别 - :local包裹的类名,将其进行hash化,保证不污染全局
 
- 将 - :local包裹的类名放在- :export中,这个是icss的规范,算是css规范的超集。这样就相当于- module.exports,外面使用时,可以通过- styles.xxx的方式来拿到hash后的类名。
 | 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | .guang {color: blue;
 }
 ._input_css_amSA5i__dong{
 color: green;
 }
 ._input_css_amSA5i__dongdong{
 color: green;
 }
 ._input_css_amSA5i__dongdongdong{
 color: red;
 }
 @keyframes _input_css_amSA5i__guangguang {
 from {
 width: 0;
 }
 to {
 width: 100px;
 }
 }
 
 :export {
 dong: _input_css_amSA5i__dong;
 dongdong: _input_css_amSA5i__dongdong;
 dongdongdong: _input_css_amSA5i__dongdongdong _input_css_amSA5i__dong _input_css_amSA5i__dongdong;
 guangguang: _input_css_amSA5i__guangguang;
 }
 
 |  
 
AST 解析
前面提高的,识别 :local()和 global() 标记,并且对其进行hash化,就是通过AST实现的。
这些可以通过 astexplorer.net 来可视化的查看转义后的AST:

整个转换的过程是:
具体代码这里 https://github.com/QuarkGluonPlasma/postcss-plugin-exercize ,或者展开下面:
代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 
 | const selectorParser = require("postcss-selector-parser");function generateScopedName(name) {
 const randomStr = Math.random().toString(16).slice(2);
 return `_${randomStr}__${name}`;
 };
 const plugin = (options = {}) => {
 return {
 postcssPlugin: "my-postcss-modules-scope",
 Once(root, helpers) {
 const exports = {};
 function exportScopedName(name) {
 const scopedName = generateScopedName(name);
 
 exports[name] = exports[name] || [];
 
 if (exports[name].indexOf(scopedName) < 0) {
 exports[name].push(scopedName);
 }
 return scopedName;
 }
 function localizeNode(node) {
 switch (node.type) {
 case "selector":
 node.nodes = node.map(localizeNode);
 return node;
 case "class":
 return selectorParser.className({
 value: exportScopedName(
 node.value,
 node.raws && node.raws.value ? node.raws.value : null
 ),
 });
 case "id": {
 return selectorParser.id({
 value: exportScopedName(
 node.value,
 node.raws && node.raws.value ? node.raws.value : null
 ),
 });
 }
 }
 }
 function traverseNode(node) {
 switch (node.type) {
 case "root":
 case "selector": {
 node.each(traverseNode);
 break;
 }
 case "id":
 case "class":
 exports[node.value] = [node.value];
 break;
 case "pseudo":
 if (node.value === ":local") {
 const selector = localizeNode(node.first, node.spaces);
 
 node.replaceWith(selector);
 
 return;
 }
 }
 return node;
 }
 
 root.walkRules((rule) => {
 const parsedSelector = selectorParser().astSync(rule);
 rule.selector = traverseNode(parsedSelector.clone()).toString();
 rule.walkDecls(/composes|compose-with/i, (decl) => {
 const localNames = parsedSelector.nodes.map((node) => {
 return node.nodes[0].first.first.value;
 })
 const classes = decl.value.split(/\s+/);
 classes.forEach((className) => {
 const global = /^global\(([^)]+)\)$/.exec(className);
 
 if (global) {
 localNames.forEach((exportedName) => {
 exports[exportedName].push(global[1]);
 });
 } else if (Object.prototype.hasOwnProperty.call(exports, className)) {
 localNames.forEach((exportedName) => {
 exports[className].forEach((item) => {
 exports[exportedName].push(item);
 });
 });
 } else {
 throw decl.error(
 `referenced class name "${className}" in ${decl.prop} not found`
 );
 }
 });
 
 decl.remove();
 });
 });
 
 root.walkAtRules(/keyframes$/i, (atRule) => {
 const localMatch = /^:local\((.*)\)$/.exec(atRule.params);
 
 if (localMatch) {
 atRule.params = exportScopedName(localMatch[1]);
 }
 });
 
 const exportedNames = Object.keys(exports);
 
 if (exportedNames.length > 0) {
 const exportRule = helpers.rule({ selector: ":export" });
 
 exportedNames.forEach((exportedName) =>
 exportRule.append({
 prop: exportedName,
 value: exports[exportedName].join(" "),
 raws: { before: "\n  " },
 })
 );
 root.append(exportRule);
 }
 },
 };
 };
 plugin.postcss = true;
 module.exports = plugin;
 
 | 
 
参考文档
bookmark
link_preview
bookmark