-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
186 lines (159 loc) · 6.65 KB
/
index.js
1
2
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
const { hasNoneRootContainingBlockComment, hasRootContainingBlockComment, createContainingBlockWidthDecls, createContainingBlockHeightDecls, hasPrevIgnoreComment, createRegArrayChecker, createIncludeFunc, createExcludeFunc } = require("./src/logic-helper");
const { convert, genVarsRule, convertWidthContainingBlock, convertHeightContainingBlock, centreRoot } = require("./src/css-generator");
const { pxTestReg } = require("./src/regex");
const { ignorePrevComment, TYPE_REG, TYPE_ARY } = require("./src/constants");
const defaults = {
/** 设计图尺寸 */
viewport: {
/** 设计图宽度 */
width: 1920,
/** 设计图高度 */
height: 1080,
},
/** 根元素选择器,如果指定,则将指定选择器居中 */
rootSelector: null,
/** 精确到小数点后几位? */
unitPrecision: 3,
comment: {
/** 定义全局变量的注释名称,如果未指定,将判断是否设置根元素选择器,如果设置,全局变量定义在根选择器处,如果未设置,将定义在每个 css 文件开头 */
vars: null,
/** 定义注释名称,标记则不对当前行进行转换 */
ignorePrev: null,
},
/** 哪些文件需要转换? */
include: null,
/** 哪些文件不需要转换? */
exclude: null,
};
/** 检查是否是正则类型或包含正则的数组 */
const checkRegExpOrArray = createRegArrayChecker(TYPE_REG, TYPE_ARY);
/** 如果不包含,则返回 true,不转换 */
const hasNoIncludeFile = createIncludeFunc(TYPE_REG, TYPE_ARY);
/** 如果排除,则返回 true,不转换 */
const hasExcludeFile = createExcludeFunc(TYPE_REG, TYPE_ARY);
module.exports = (options = {}) => {
const opts = {
...defaults,
...options,
comment: {
...defaults.comment,
...options.comment,
}
};
const { viewport, rootSelector, unitPrecision, comment, include, exclude } = opts;
const { vars, ignorePrev } = comment;
const _ignorePrev = ignorePrev == null ? ignorePrevComment : ignorePrev;
const excludeType = checkRegExpOrArray(opts, "exclude");
const includeType = checkRegExpOrArray(opts, "include");
return {
postcssPlugin: "postcss-bud",
prepare(result) {
const file = result.root && result.root.source && result.root.source.input.file;
// 包含文件
if(hasNoIncludeFile(include, file, includeType)) return;
// 排除文件
if(hasExcludeFile(exclude, file, excludeType)) return;
/** 当前选择器 */
let selector = null;
/** 当前元素是否 fixed 定位? */
let hadFixed = null;
/** 依赖根包含块宽度的属性 */
let widthContainingBlockDeclsMap = null;
/** 依赖根包含块高度的属性 */
let heightContainingBlockDeclsMap = null;
// 是否动态视图宽度?
const isDynamicViewport = typeof viewport === "function";
const dynamicViewport = isDynamicViewport ? viewport(file) : viewport;
const {
width: viewWidth,
height: viewHeight,
} = dynamicViewport;
let prependedRootVars = false;
let walkedRule = false;
return {
Once(css, postcss) {
// 在每个文件定义变量
if (rootSelector == null && vars == null) {
const varsRule = genVarsRule(postcss.Rule, viewWidth, viewHeight, unitPrecision);
css.prepend(varsRule);
}
},
Comment(comment, postcss) {
const text = comment.text;
if (text === vars && text != null && prependedRootVars === false) {
prependedRootVars = true;
const varsRule = genVarsRule(postcss.Rule, viewWidth, viewHeight, unitPrecision);
result.root.prepend(varsRule);
comment.remove();
}
},
Rule(rule, postcss) {
if (rule.book) return ;
// console.log(postcss.Root)
hadFixed = false;
widthContainingBlockDeclsMap = createContainingBlockWidthDecls();
heightContainingBlockDeclsMap = createContainingBlockHeightDecls();
selector = rule.selector;
// 垂直水平居中根选择器
if (selector === rootSelector) {
// 设置了根选择器,没有设置变量标志
if (vars == null) {
const varsRule = genVarsRule(postcss.Rule, viewWidth, viewHeight, unitPrecision);
result.root.prepend(varsRule);
}
// 居中
centreRoot(rule);
}
/** 有标志*非根包含块*的注释吗? */
const notRootContainingBlock = hasNoneRootContainingBlockComment(rule);
if (notRootContainingBlock) widthContainingBlockDeclsMap = heightContainingBlockDeclsMap = new Map();
/** 有标志*根包含块*的注释吗? */
const hadRootContainingBlock = hasRootContainingBlockComment(rule);
if (hadRootContainingBlock) hadFixed = true;
walkedRule = true;
},
Declaration(decl, postcss) {
if (!walkedRule) return;
if (decl.book) return;
const prop = decl.prop;
const val = decl.value;
const important = decl.important;
if (prop === "position" && val === "fixed") return hadFixed = true;
if (hasPrevIgnoreComment(decl, _ignorePrev, result)) return;
// 涉及到包含块宽度的属性,推迟到 ruleExit 中计算
if (widthContainingBlockDeclsMap.has(prop)) {
const mapDecl = widthContainingBlockDeclsMap.get(prop);
if (mapDecl == null || important || !mapDecl.important)
widthContainingBlockDeclsMap.set(prop, decl);
return;
}
// 涉及到包含块高度的属性,推迟到 ruleExit 中计算
if (heightContainingBlockDeclsMap.has(prop)) {
const mapDecl = heightContainingBlockDeclsMap.get(prop);
if (mapDecl == null || important || !mapDecl.important)
heightContainingBlockDeclsMap.set(prop, decl);
return;
}
if (pxTestReg.test(val))
convert(decl, viewWidth, unitPrecision);
},
RuleExit(rule, postcss) {
if (walkedRule) {
walkedRule = false;
widthContainingBlockDeclsMap.forEach(decl => {
if (decl == null) return;
convertWidthContainingBlock(decl, viewWidth, unitPrecision, hadFixed);
});
heightContainingBlockDeclsMap.forEach(decl => {
if (decl == null) return;
convertHeightContainingBlock(decl, viewHeight, unitPrecision, hadFixed);
});
}
},
OnceExit(css) {
},
};
},
};
};
module.exports.postcss = true;