我正在尝试编写一个webpack插件(我的第一个!请耐心等待),该插件下载fontello图标字体,将它们放在dist文件夹中,然后生成一个icons.scss文件,其中包含每个图标的SASS变量。 / p>
我猜这有点不合常规,因为icons.scss文件不应该放在dist /文件夹中,而应该放在src /文件夹中。不过,将文件放在src /中似乎并不是不可能的,并且它必须存在以便我的app.scss导入它。
我遇到的问题是,生成的icons.scss文件(包含在我的SASS主入口点(app.scss)中)在编译SASS之前没有时间生成。
有什么办法告诉webpack等到我的插件完成后再继续构建吗?
要清楚一点,我的所有工作都相对良好,下载了字体,生成了icons.scss等,唯一的问题是SASS在icons.scss存在之前就进行了编译。我不知道哪个编译器/编译钩子最适合使用,所以也希望在此输入一些信息。
请参阅相关部分的代码摘录。我在感觉可以改善的地方留下了评论:
// Fontello API endpoint
let url = 'http://fontello.com';
// Read our fontello config JSON
let fontelloConfig = fs.createReadStream('icons.json', 'utf8');
// Send fontello config to API
request.post({url: url, formData: {config: fontelloConfig}}, (err, res, body) => {
if (err) {
return console.error(err);
}
// Fetch ZIP using the session we got back from the previous call
request.get(`http://fontello.com/${body}/get`)
// Unzip it
.pipe(unzipper.Parse())
// For each file
.on('entry', (entry) => {
// Get basename and extension
const basename = path.basename(entry.path);
const ext = path.extname(basename);
// Copy the fontello.css to the sass path
// NOTE: All of this is a mess, I'm writing the fontello.css file to src/icons.scss, then reading src/icons.scss
// only so that I can make my changes to its contents, finally I write the changed contents back to src/icons.scss
// please help me improve this part, I'm a node and webpack noob
if (basename === 'fontello.css') {
// Write fontello.css to icons.scss
entry.pipe(fs.createWriteStream('src/icons.scss')).on('finish', () => {
// Read the file...
fs.readFile('src/icons.scss', 'utf8', (err, data) => {
// NOTE: Not until this file is created should webpack continue with compilation,
// if this file doesn't exist when SASS is compiled it will fail because my app.scss is trying to import this file
fs.writeFile('src/icons.scss', makeMyChangesToTheFontelloCssFileContent(data), 'utf8', (err) => {});
});
});
}
// Copy fonts and config.json to dist
// NOTE: I'm a noob so I didn't know you're supposed to use compilation.assets[filename] = filecontent;
// I'm working on it, but please let me know if it's an easy change?
else if (entry.type === 'File' && (basename === 'config.json' || entry.path.indexOf('/font/') !== -1)) {
entry.pipe(fs.createWriteStream('dist/' + basename));
}
// Otherwise clean up(?): https://github.com/ZJONSSON/node-unzipper#parse-zip-file-contents
else {
entry.autodrain();
}
});
});
编辑:我已经研究了文档,但是很难知道没有示例该怎么做。我已经在几乎每个编译器和编译钩子上都设置了回调,以了解它们的运行时间和方式,但这并没有真正帮助我很多。
答案 0 :(得分:0)
我对这种解决方案不满意,但最终我只通过更改package.json来运行webpack之前的fontello脚本:
"dev": "node fontello.js && webpack --mode development",
"build": "node fontello.js && webpack --mode production",
最终的fontello.js如下所示:
// Utils
const path = require('path');
const fs = require('fs');
const request = require('request');
const unzipper = require('unzipper');
const css = require('css');
// Fontello Plugin
class FontelloSassWebpackPlugin {
constructor (config) {
this.config = Object.assign({
src: path.resolve(__dirname, 'src/icons.json'),
dest: path.resolve(__dirname, 'src/assets/fontello'),
sass: path.resolve(__dirname, 'src/sass/icons.scss'),
url: 'http://fontello.com'
}, config);
}
// Converts the fontello.css to what we need
convertFontelloCss (code) {
var obj = css.parse(code);
var newRules = [];
var sassVars = '';
var sassMixin = '';
obj.stylesheet.rules.forEach(rule => {
const selector = (rule.selectors && rule.selectors.length) ? rule.selectors[0] : null;
if (selector) {
// [class] rule
if (selector.indexOf('[class^="icon-"]:before') !== -1) {
rule.selectors.push(...['[class^="icon-"]:after', '[class*=" icon-"]:after']);
rule.declarations.forEach(d => {
if (d.type === 'declaration') {
sassMixin += `${d.property}: ${d.value};\n`;
}
});
sassMixin = `@mixin icon ($icon-code: "[NOICO]") {\n${sassMixin}\ncontent: $icon-code;\n}`;
}
// Icon rule
if (selector.indexOf('.icon-') !== -1) {
const iconName = selector.match(/\.icon-(.*?):before/)[1];
var iconVal = '[NO-ICON]';
rule.declarations.forEach(d => {
if (d.property === 'content') {
iconVal = d.value;
}
});
newRules.push({
type: 'rule',
selectors: [`.icon-${iconName}.icon--after:before`],
declarations: [{
type: 'declaration',
property: 'content',
value: 'normal'
}]
});
newRules.push({
type: 'rule',
selectors: [`.icon-${iconName}.icon--after:after`],
declarations: rule.declarations
});
sassVars += `$icon-${iconName}: ${iconVal};\n`;
}
}
});
obj.stylesheet.rules.push(...newRules);
return css.stringify(obj, {compress: false}).replace(/\.\.\/font\//g, 'assets/fontello/') + sassMixin + sassVars;
}
apply () {
const fontelloConfig = fs.createReadStream(this.config.src, 'utf8');
// Make sure folder exists
if (!fs.existsSync(this.config.dest)) {
fs.mkdirSync(this.config.dest, {recursive: true});
}
// Fetch session
request.post({url: this.config.url, formData: {config: fontelloConfig}}, (err, res, body) => {
if (err) {
return console.error(err);
}
// Fetch ZIP
request.get(`${this.config.url}/${body}/get`)
// Unzip it
.pipe(unzipper.Parse())
// For each file
.on('entry', (entry) => {
const basename = path.basename(entry.path);
const ext = path.extname(basename);
// Copy the fontello.css to the sass path
if (basename === 'fontello.css') {
entry.pipe(fs.createWriteStream(this.config.sass)).on('finish', () => {
fs.readFile(this.config.sass, 'utf8', (err, data) => {
fs.writeFile(this.config.sass, this.convertFontelloCss(data), 'utf8', (err) => {});
});
});
}
// Copy fonts and config.json to dist
else if (entry.type === 'File' && (basename === 'config.json' || entry.path.indexOf('/font/') !== -1)) {
entry.pipe(fs.createWriteStream(this.config.dest + '/' + basename));
}
// Otherwise clean up: https://github.com/ZJONSSON/node-unzipper#parse-zip-file-contents
else {
entry.autodrain();
}
});
});
}
}
const fswp = new FontelloSassWebpackPlugin();
fswp.apply();