stylelint可以用来扩展插件去实现各种规则,接下来带大家看看stylelint是如何执行插件的
首先遍历absoluteFilePaths路径(该路径是我们执行lint命令配置的文件类型eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix
,.{vue,ts,tsx}这个就是匹配的文件类型)。接下来执行lintSource方法。
// node_modules\stylelint\lib\standalone.js
const getStylelintResults = absoluteFilePaths.map(async (absoluteFilepath) => {
debug(`Processing ${absoluteFilepath}`);
try {
const postcssResult = await lintSource(stylelint, {
filePath: absoluteFilepath,
cache: useCache,
});
...
这里首先会获取针对当前文件类型的配置文件,该文件包括本地配置的.stylelintrc.js。
// node_modules\stylelint\lib\lintSource.js
try {
configForFile = await getConfigForFile(stylelint, configSearchPath, inputFilePath);
} catch (err) {
if (isCodeNotFile && isPathNotFoundError(err)) {
configForFile = await getConfigForFile(stylelint, cwd);
} else {
throw err;
}
}
配置文件包括当前文件要执行的plugins,我们此次要分析的是stylelint-order插件
// node_modules\stylelint\lib\lintSource.js
// 读取要处理的文件内容
const postcssResult =
existingPostcssResult ||
(await getPostcssResult(stylelint, {
code: options.code,
codeFilename: options.codeFilename,
filePath: inputFilePath,
codeProcessors: config.codeProcessors,
customSyntax: config.customSyntax,
}));
// 调用插件处理
await lintPostcssResult(stylelint._options, stylelintPostcssResult, config);
// 处理完得到root,root的nodes,root包括处理好的样式和codeBefore、codeAfter
return stylelintPostcssResult;
首先执行getPostcssResult方法获取要处理的文件内容,然后
// node_modules\stylelint\lib\getPostcssResult.js
if (options.code !== undefined) {
getCode = options.code;
} else if (options.filePath) {
getCode = await fs.readFile(options.filePath, 'utf8');
}
if (getCode === undefined) {
return Promise.reject(new Error('code or filePath required'));
}
if (options.codeProcessors && options.codeProcessors.length) {
if (stylelint._options.fix) {
console.warn(
'Autofix is incompatible with processors and will be disabled. Are you sure you need a processor?',
);
stylelint._options.fix = false;
}
const sourceName = options.code ? options.codeFilename : options.filePath;
for (const codeProcessor of options.codeProcessors) {
getCode = codeProcessor(getCode, sourceName);
}
}
// postcss-html插件处理读取的文件
const postcssResult = await new LazyResult(postcssProcessor, getCode, postcssOptions);
此时获取syntax去解析已读取的文件内容,我们此处用的是postcss-html。该插件可以读取vue模板中的style样式。
// node_modules\postcss\lib\lazy-result.js
let parser = parse
if (opts.syntax) parser = opts.syntax.parse
if (opts.parser) parser = opts.parser
if (parser.parse) parser = parser.parse
try {
root = parser(css, opts)
} catch (error) {
this.processed = true
this.error = error
}
在lintSource.js文件中处理完毕后得到postcssResult回到standalone.js文件
// node_modules\stylelint\lib\standalone.js
if (
postcssResult.root &&
postcssResult.opts &&
!postcssResult.stylelint.ignored &&
fix &&
!postcssResult.stylelint.disableWritingFix
) {// 将插件处理好的拼接起来
const fixedCss = postcssResult.root.toString(postcssResult.opts.syntax);
if (
postcssResult.root &&
postcssResult.root.source &&
postcssResult.root.source.input.css !== fixedCss
) {
await writeFileAtomic(absoluteFilepath, fixedCss);
}
}
此时调用postcssResult.root.toString方法拼接Root中的nodes和raws的codeBefore、codeAfter。
// node_modules\postcss-html\lib\stringify.js
if (node.nodes.length) {
node.nodes.forEach((root) => {
// 加上被处理样式之前
builder(root.raws.codeBefore, root, "codeBefore");
if (root.source.syntax) {
// 执行builder加上已处理样式
root.source.syntax.stringify(root, builder);
} else {
postcssStringify(root, builder);
}
// 加上被处理的样式后面
builder(root.raws.codeAfter || "", root, "codeAfter");
});
} else {
// If it do not have root, it will output the input.
builder(node.source.input.css);
}
此时拼接好的就是排序好的代码
此时回到standalone.js执行writeFileAtomic方法覆盖原文件
// node_modules\stylelint\lib\standalone.js
if (
postcssResult.root &&
postcssResult.root.source &&
postcssResult.root.source.input.css !== fixedCss
) {
await writeFileAtomic(absoluteFilepath, fixedCss);
}
总结
- stylelint会获取所有的需要lint的文件路径
- 根据路径的文件类型生成配置
- 使用node读取文件内容并调用配置的syntax解析文件
- 调用插件执行已解析的文件内容
- 拼接已处理的文件为完整字符串
- 重新写入文件覆盖原内容