@hibi_10000/grunt-webfont 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,609 @@
1
+ import { UNICODE_PUA_START, consolaLogger, cssFilePrefixes, fontFormats, fontSrcSeparators, fontsSrcsMap, generatedFontFiles, getFontPath } from "./util/util.js";
2
+ import fontforge_default from "./engines/fontforge.js";
3
+ import node_default from "./engines/node.js";
4
+ import package_default from "./package.js";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import crypto from "node:crypto";
8
+ import { globSync } from "glob";
9
+ import chalk from "chalk";
10
+ import _ from "lodash";
11
+ import ttf2woff2 from "ttf2woff2";
12
+
13
+ //#region tasks/webfont.ts
14
+ var webfont_default = (grunt) => {
15
+ grunt.registerMultiTask("webfont", "Compile separate SVG files to webfont", function() {
16
+ /**
17
+ * Consola to Grunt logger adapter.
18
+ */
19
+ const logger = {
20
+ log: {
21
+ warn: (...args) => {
22
+ grunt.log.warn.apply(null, args);
23
+ },
24
+ error: (...args) => {
25
+ grunt.warn.apply(null, args);
26
+ },
27
+ info: (...args) => {
28
+ grunt.log.writeln.apply(null, args);
29
+ },
30
+ verbose: (...args) => {
31
+ grunt.log.verbose.writeln.apply(null, args);
32
+ }
33
+ },
34
+ fail: { fatal: (...args) => {
35
+ grunt.fail.fatal.apply(null, args);
36
+ } }
37
+ };
38
+ const allDone = this.async();
39
+ const params = this.data;
40
+ const options = this.options(void 0);
41
+ this.requiresConfig([
42
+ this.name,
43
+ this.target,
44
+ "src"
45
+ ].join("."));
46
+ webfont(this.name, this.target, this.filesSrc, params, options, logger).finally(allDone);
47
+ });
48
+ };
49
+ const webfont = async (name, target, filesSrc, params, options, logger) => {
50
+ if (!logger) logger = consolaLogger;
51
+ const md5 = crypto.createHash("md5");
52
+ if (params.dest === void 0 && options.dest === void 0) logger.log.warn(`Required property ${name}.${target}.dest or ${name}.${target}.options.dest missing.`);
53
+ if (options.skip) {
54
+ completeTask();
55
+ return;
56
+ }
57
+ const files = filesSrc.filter(isSvgFile);
58
+ if (!files.length) {
59
+ logger.log.warn("Specified empty list of source SVG files.");
60
+ completeTask();
61
+ return;
62
+ }
63
+ if (typeof options.template !== "string") options.template = "";
64
+ let o = {
65
+ logger,
66
+ fontBaseName: options.font || "icons",
67
+ destCss: options.destCss || params.destCss || params.dest,
68
+ destScss: options.destScss || params.destScss || params.destCss || params.dest,
69
+ destSass: options.destSass || params.destSass || params.destCss || params.dest,
70
+ destLess: options.destLess || params.destLess || params.destCss || params.dest,
71
+ destStyl: options.destStyl || params.destStyl || params.destCss || params.dest,
72
+ dest: options.dest || params.dest,
73
+ relativeFontPath: options.relativeFontPath,
74
+ fontPathVariables: options.fontPathVariables || false,
75
+ addHashes: options.hashes !== false,
76
+ addLigatures: options.ligatures === true,
77
+ template: options.template,
78
+ syntax: options.syntax || "bem",
79
+ templateOptions: options.templateOptions || {},
80
+ stylesheets: options.stylesheets || [options.stylesheet || path.extname(options.template).replace(/^\./, "") || "css"],
81
+ htmlDemo: options.htmlDemo !== false,
82
+ htmlDemoTemplate: options.htmlDemoTemplate,
83
+ htmlDemoFilename: options.htmlDemoFilename,
84
+ styles: optionToArray(options.styles, "font,icon"),
85
+ types: optionToArray(options.types, "eot,woff,ttf"),
86
+ order: optionToArray(options.order, fontFormats),
87
+ embed: options.embed === true ? ["woff"] : optionToArray(options.embed, false),
88
+ rename: options.rename || path.basename,
89
+ engine: options.engine || "fontforge",
90
+ autoHint: options.autoHint !== false,
91
+ codepoints: options.codepoints,
92
+ codepointsFile: options.codepointsFile,
93
+ startCodepoint: options.startCodepoint || UNICODE_PUA_START,
94
+ ie7: options.ie7 === true,
95
+ normalize: options.normalize === true,
96
+ optimize: options.optimize === false ? false : true,
97
+ round: options.round !== void 0 ? options.round : 0x9184e72a000,
98
+ fontHeight: options.fontHeight !== void 0 ? options.fontHeight : 512,
99
+ descent: options.descent !== void 0 ? options.descent : 64,
100
+ version: options.version !== void 0 ? options.version : false,
101
+ cache: options.cache || path.join(import.meta.dirname, "..", ".cache"),
102
+ callback: options.callback,
103
+ customOutputs: options.customOutputs,
104
+ execMaxBuffer: options.execMaxBuffer || 1024 * 200
105
+ };
106
+ o = Object.assign(o, {
107
+ fontName: o.fontBaseName,
108
+ destCssPaths: {
109
+ css: o.destCss,
110
+ scss: o.destScss,
111
+ sass: o.destSass,
112
+ less: o.destLess,
113
+ styl: o.destStyl
114
+ },
115
+ relativeFontPath: o.relativeFontPath || path.relative(o.destCss, o.dest),
116
+ destHtml: options.destHtml || o.destCss,
117
+ fontfaceStyles: has(o.styles, "font"),
118
+ baseStyles: has(o.styles, "icon"),
119
+ extraStyles: has(o.styles, "extra"),
120
+ files,
121
+ glyphs: []
122
+ });
123
+ o.hash = getHash();
124
+ o.fontFilename = template(options.fontFilename || o.fontBaseName, o);
125
+ o.fontFamilyName = template(options.fontFamilyName || o.fontBaseName, o);
126
+ o.glyphs = o.files.map((file) => {
127
+ return o.rename(file).replace(path.extname(file), "");
128
+ });
129
+ let currentCodepoint = o.startCodepoint;
130
+ if (!o.codepoints) o.codepoints = {};
131
+ if (o.codepointsFile) o.codepoints = readCodepointsFromFile();
132
+ o.glyphs.forEach((name$1) => {
133
+ if (!o.codepoints[name$1]) o.codepoints[name$1] = getNextCodepoint();
134
+ });
135
+ if (o.codepointsFile) saveCodepointsToFile();
136
+ const previousHash = readHash(name, target);
137
+ logger.log.verbose("New hash:", o.hash, "- previous hash:", previousHash);
138
+ if (o.hash === previousHash) {
139
+ logger.log.verbose("Config and source files weren’t changed since last run, checking resulting files...");
140
+ let regenerationNeeded = false;
141
+ const generatedFiles = generatedFontFiles(o);
142
+ if (!generatedFiles.length) regenerationNeeded = true;
143
+ else {
144
+ generatedFiles.push(getDemoFilePath());
145
+ o.stylesheets.forEach((stylesheet) => {
146
+ generatedFiles.push(getCssFilePath(stylesheet));
147
+ });
148
+ regenerationNeeded = generatedFiles.some((filename) => {
149
+ if (!filename) return false;
150
+ if (!fs.existsSync(filename)) {
151
+ logger.log.verbose("File", filename, " is missed.");
152
+ return true;
153
+ }
154
+ return false;
155
+ });
156
+ }
157
+ if (!regenerationNeeded) {
158
+ logger.log.info(`Font ${chalk.cyan(o.fontName)} wasn’t changed since last run.`);
159
+ completeTask();
160
+ return;
161
+ }
162
+ }
163
+ saveHash(name, target, o.hash);
164
+ try {
165
+ createOutputDirs();
166
+ cleanOutputDir();
167
+ await generateFont();
168
+ generateWoff2Font();
169
+ generateStylesheets();
170
+ await generateDemoHtml();
171
+ generateCustomOutputs();
172
+ printDone();
173
+ } finally {
174
+ completeTask();
175
+ }
176
+ /**
177
+ * Call callback function if it was specified in the options.
178
+ */
179
+ function completeTask() {
180
+ if (o && typeof o.callback === "function") o.callback(o.fontName, o.types, o.glyphs, o.hash);
181
+ }
182
+ /**
183
+ * Calculate hash to flush browser cache.
184
+ * Hash is based on source SVG files contents, task options and grunt-webfont version.
185
+ */
186
+ function getHash() {
187
+ o.files.forEach((file) => {
188
+ md5.update(fs.readFileSync(file, "utf8"));
189
+ });
190
+ md5.update(JSON.stringify(o));
191
+ md5.update(package_default.version);
192
+ if (o.template) md5.update(fs.readFileSync(o.template, "utf8"));
193
+ if (o.htmlDemoTemplate) md5.update(fs.readFileSync(o.htmlDemoTemplate, "utf8"));
194
+ return md5.digest("hex");
195
+ }
196
+ /**
197
+ * Create output directory
198
+ */
199
+ function createOutputDirs() {
200
+ o.stylesheets.forEach((stylesheet) => {
201
+ fs.mkdirSync(option(o.destCssPaths, stylesheet), { recursive: true });
202
+ });
203
+ fs.mkdirSync(o.dest, { recursive: true });
204
+ }
205
+ /**
206
+ * Clean output directory
207
+ */
208
+ function cleanOutputDir() {
209
+ const htmlDemoFileMask = path.posix.join(o.destCss, `${o.fontBaseName}*.{css,html}`);
210
+ globSync(htmlDemoFileMask).concat(generatedFontFiles(o)).forEach((file) => {
211
+ fs.unlinkSync(file);
212
+ });
213
+ }
214
+ /**
215
+ * Generate font using selected engine
216
+ */
217
+ async function generateFont() {
218
+ const result = await (o.engine === "node" ? node_default : fontforge_default)(o);
219
+ if (result === false) {
220
+ completeTask();
221
+ return;
222
+ }
223
+ if (result) o = Object.assign(o, result);
224
+ }
225
+ /**
226
+ * Converts TTF font to WOFF2.
227
+ */
228
+ function generateWoff2Font() {
229
+ if (!has(o.types, "woff2")) return;
230
+ const ttfFontPath = getFontPath(o, "ttf");
231
+ const ttfFont = fs.readFileSync(ttfFontPath);
232
+ if (!has(o.types, "ttf")) fs.unlinkSync(ttfFontPath);
233
+ const woffFont = ttf2woff2(ttfFont);
234
+ const woff2FontPath = getFontPath(o, "woff2");
235
+ fs.writeFileSync(woff2FontPath, woffFont);
236
+ }
237
+ /**
238
+ * Generate CSS
239
+ */
240
+ function generateStylesheets() {
241
+ const codepoints = [];
242
+ o.glyphs.forEach((name$1) => {
243
+ codepoints.push(o.codepoints[name$1].toString(16));
244
+ });
245
+ o.codepoints = codepoints;
246
+ o.glyphs = o.glyphs.map(classnameize);
247
+ o.stylesheets.sort((a, b) => {
248
+ return a === "css" ? 1 : -1;
249
+ }).forEach(generateStylesheet);
250
+ }
251
+ /**
252
+ * Generate CSS
253
+ *
254
+ * @param stylesheet type: css, scss, ...
255
+ */
256
+ function generateStylesheet(stylesheet) {
257
+ o.relativeFontPath = normalizePath(o.relativeFontPath);
258
+ const fontSrcs = {
259
+ 0: [],
260
+ 1: []
261
+ };
262
+ o.order.forEach((type) => {
263
+ if (!has(o.types, type)) return;
264
+ const fontSrc1 = fontsSrcsMap[type][0];
265
+ if (fontSrc1) fontSrcs[0].push(generateFontSrc(type, fontSrc1, stylesheet));
266
+ fontSrcs[1].push(generateFontSrc(type, fontsSrcsMap[type][1], stylesheet));
267
+ });
268
+ const fontSrcSeparator = option(fontSrcSeparators, stylesheet);
269
+ o.fontSrc1 = fontSrcs[0].join(fontSrcSeparator);
270
+ o.fontSrc2 = fontSrcs[1].join(fontSrcSeparator);
271
+ o.fontRawSrcs = [fontSrcs[0], fontSrcs[1]];
272
+ const templateJson = readTemplate(o.template, o.syntax, ".json", true);
273
+ if (templateJson) o = Object.assign(o, JSON.parse(templateJson.template));
274
+ if (o.templateOptions) o = Object.assign(o, o.templateOptions);
275
+ const ext = path.extname(o.template) || ".css";
276
+ o.cssTemplate = readTemplate(o.template, o.syntax, ext);
277
+ const cssContext = Object.assign(o, {
278
+ iconsStyles: true,
279
+ stylesheet
280
+ });
281
+ let css = renderTemplate(o.cssTemplate, cssContext);
282
+ if (has([
283
+ "sass",
284
+ "scss",
285
+ "less",
286
+ "styl"
287
+ ], stylesheet)) css = css.replace(/\/\* *(.*?) *\*\//g, "// $1");
288
+ fs.writeFileSync(getCssFilePath(stylesheet), css);
289
+ }
290
+ /**
291
+ * Gets the codepoints from the set filepath in o.codepointsFile
292
+ */
293
+ function readCodepointsFromFile() {
294
+ if (!o.codepointsFile) return {};
295
+ if (!fs.existsSync(o.codepointsFile)) {
296
+ logger.log.verbose("Codepoints file not found");
297
+ return {};
298
+ }
299
+ const buffer = fs.readFileSync(o.codepointsFile);
300
+ return JSON.parse(buffer.toString());
301
+ }
302
+ /**
303
+ * Saves the codespoints to the set file
304
+ */
305
+ function saveCodepointsToFile() {
306
+ if (!o.codepointsFile) return;
307
+ const codepointsToString = JSON.stringify(o.codepoints, null, 4);
308
+ try {
309
+ fs.writeFileSync(o.codepointsFile, codepointsToString);
310
+ logger.log.verbose(`Codepoints saved to file "${o.codepointsFile}".`);
311
+ } catch (err) {
312
+ logger.log.error(err.message);
313
+ }
314
+ }
315
+ function prepareBaseTemplateContext() {
316
+ return Object.assign({}, o);
317
+ }
318
+ function prepareHtmlTemplateContext() {
319
+ let context = prepareBaseTemplateContext();
320
+ let htmlStyles;
321
+ const relativeRe = new RegExp(_.escapeRegExp(o.relativeFontPath).replace(/[=!:\/]/g, "\\$&"), "g");
322
+ const htmlRelativeFontPath = normalizePath(path.relative(o.destHtml, o.dest));
323
+ const _fontSrc1 = o.fontSrc1.replace(relativeRe, htmlRelativeFontPath);
324
+ const _fontSrc2 = o.fontSrc2.replace(relativeRe, htmlRelativeFontPath);
325
+ context = Object.assign(context, {
326
+ fontSrc1: _fontSrc1,
327
+ fontSrc2: _fontSrc2,
328
+ fontfaceStyles: true,
329
+ baseStyles: true,
330
+ extraStyles: false,
331
+ iconsStyles: true,
332
+ stylesheet: "css"
333
+ });
334
+ htmlStyles = renderTemplate(o.cssTemplate, context);
335
+ context = Object.assign(context, { styles: htmlStyles });
336
+ return context;
337
+ }
338
+ /**
339
+ * Iterator function used as callback by looping construct below to
340
+ * render "custom output" via mini configuration objects specified in
341
+ * the array `options.customOutputs`.
342
+ *
343
+ * @param outputConfig
344
+ */
345
+ function generateCustomOutput(outputConfig) {
346
+ let context = prepareBaseTemplateContext();
347
+ context = Object.assign(context, outputConfig.context);
348
+ const templatePath = outputConfig.template;
349
+ const extension = path.extname(templatePath);
350
+ const syntax = outputConfig.syntax || "";
351
+ const template$1 = readTemplate(templatePath, syntax, extension);
352
+ const output = renderTemplate(template$1, context);
353
+ const dest = outputConfig.dest || o.dest;
354
+ let filepath;
355
+ let destParent;
356
+ let destName;
357
+ if (path.extname(dest) === "") {
358
+ destParent = dest;
359
+ destName = path.basename(outputConfig.template);
360
+ filepath = path.join(dest, destName);
361
+ } else {
362
+ destParent = path.dirname(dest);
363
+ filepath = dest;
364
+ }
365
+ fs.mkdirSync(destParent, { recursive: true });
366
+ fs.writeFileSync(filepath, output);
367
+ }
368
+ function generateCustomOutputs() {
369
+ if (!o.customOutputs || o.customOutputs.length < 1) return;
370
+ o.customOutputs.forEach(generateCustomOutput);
371
+ }
372
+ /**
373
+ * Generate HTML demo page
374
+ */
375
+ async function generateDemoHtml() {
376
+ if (!o.htmlDemo) return;
377
+ const context = prepareHtmlTemplateContext();
378
+ const demoTemplate = readTemplate(o.htmlDemoTemplate, "demo", ".html");
379
+ const demo = renderTemplate(demoTemplate, context);
380
+ try {
381
+ await fs.promises.mkdir(getDemoPath(), { recursive: true });
382
+ } catch (err) {
383
+ if (err) {
384
+ logger.log.info(err);
385
+ return;
386
+ }
387
+ }
388
+ fs.writeFileSync(getDemoFilePath(), demo);
389
+ }
390
+ /**
391
+ * Print log
392
+ */
393
+ function printDone() {
394
+ logger.log.info(`Font ${chalk.cyan(o.fontName)} with ${o.glyphs.length} glyphs created.`);
395
+ }
396
+ /**
397
+ * Helpers
398
+ */
399
+ /**
400
+ * Convert a string of comma separated words into an array
401
+ *
402
+ * @param val Input string
403
+ * @param defVal Default value
404
+ */
405
+ function optionToArray(val, defVal) {
406
+ if (val === void 0) val = defVal;
407
+ if (!val) return [];
408
+ if (typeof val !== "string") return val;
409
+ return val.split(",").map((value) => value.trim());
410
+ }
411
+ /**
412
+ * Check if a value exists in an array
413
+ *
414
+ * @param haystack Array to find the needle in
415
+ * @param needle Value to find
416
+ * @return Needle was found
417
+ */
418
+ function has(haystack, needle) {
419
+ return haystack.indexOf(needle) !== -1;
420
+ }
421
+ /**
422
+ * Return a specified option if it exists in an object or `_default` otherwise
423
+ *
424
+ * @param map Options object
425
+ * @param key Option to find in the object
426
+ */
427
+ function option(map, key) {
428
+ if (key in map) return map[key];
429
+ else return map._default;
430
+ }
431
+ /**
432
+ * Find next unused codepoint.
433
+ */
434
+ function getNextCodepoint() {
435
+ while (Object.values(o.codepoints).includes(currentCodepoint)) currentCodepoint++;
436
+ return currentCodepoint;
437
+ }
438
+ /**
439
+ * Check whether file is SVG or not
440
+ *
441
+ * @param filepath File path
442
+ */
443
+ function isSvgFile(filepath) {
444
+ return path.extname(filepath).toLowerCase() === ".svg";
445
+ }
446
+ /**
447
+ * Convert font file to data:uri and remove source file
448
+ *
449
+ * @param fontFile Font file path
450
+ * @return Base64 encoded string
451
+ */
452
+ function embedFont(fontFile) {
453
+ const dataUri = fs.readFileSync(fontFile, "base64");
454
+ const fontUrl = `data:application/x-font-${path.extname(fontFile).substring(1)};charset=utf-8;base64,${dataUri}`;
455
+ fs.unlinkSync(fontFile);
456
+ return fontUrl;
457
+ }
458
+ /**
459
+ * Append a slash to end of a filepath if it not exists and make all slashes forward
460
+ *
461
+ * @param filepath File path
462
+ */
463
+ function normalizePath(filepath) {
464
+ if (!filepath.length) return filepath;
465
+ filepath = filepath.replace(/\\/g, "/");
466
+ if (!filepath.endsWith("/")) filepath += "/";
467
+ return filepath;
468
+ }
469
+ /**
470
+ * Generate URL for @font-face
471
+ *
472
+ * @param type Type of font
473
+ * @param font URL or Base64 string
474
+ * @param stylesheet type: css, scss, ...
475
+ */
476
+ function generateFontSrc(type, font, stylesheet) {
477
+ const filename = template(`${o.fontFilename}${font.ext}`, o);
478
+ let fontPathVariableName = `${o.fontFamilyName}-font-path`;
479
+ let url;
480
+ if (font.embeddable && has(o.embed, type)) url = embedFont(path.join(o.dest, filename));
481
+ else {
482
+ if (o.fontPathVariables && stylesheet !== "css") {
483
+ if (stylesheet === "less") {
484
+ fontPathVariableName = `@${fontPathVariableName}`;
485
+ o.fontPathVariable = `${fontPathVariableName} : "${o.relativeFontPath}";`;
486
+ } else {
487
+ fontPathVariableName = `$${fontPathVariableName}`;
488
+ o.fontPathVariable = `${fontPathVariableName} : "${o.relativeFontPath}" !default;`;
489
+ }
490
+ url = filename;
491
+ } else url = `${o.relativeFontPath}${filename}`;
492
+ if (o.addHashes) if (url.indexOf("#iefix") === -1) url = url.replace(/(#|$)/, `?${o.hash}$1`);
493
+ else url = url.replace(/(#|$)/, `${o.hash}$1`);
494
+ }
495
+ let src = `url("${url}")`;
496
+ if (o.fontPathVariables && stylesheet !== "css") if (stylesheet === "less") src = `url("@{${fontPathVariableName.replace("@", "")}}${url}")`;
497
+ else src = `url(${fontPathVariableName} + "${url}")`;
498
+ if (font.format) src += ` format("${font.format}")`;
499
+ return src;
500
+ }
501
+ /**
502
+ * Read the template file
503
+ *
504
+ * @param template Template file path
505
+ * @param syntax Syntax (bem, bootstrap, etc.)
506
+ * @param ext Extension of the template
507
+ * @return \{filename: 'Template filename', template: 'Template code'}
508
+ */
509
+ function readTemplate(template$1, syntax, ext, optional) {
510
+ const filename = template$1 ? path.resolve(template$1.replace(path.extname(template$1), ext)) : path.join(import.meta.dirname, `../templates/${syntax}${ext}`);
511
+ if (fs.existsSync(filename)) return {
512
+ filename,
513
+ template: fs.readFileSync(filename, "utf8")
514
+ };
515
+ else if (!optional) logger.fail.fatal(`Cannot find template at path: ${filename}`);
516
+ }
517
+ /**
518
+ * Render template with error reporting
519
+ *
520
+ * @param template {filename: 'Template filename', template: 'Template code'}
521
+ * @param context Template context
522
+ */
523
+ function renderTemplate(template$1, context) {
524
+ try {
525
+ return _.template(template$1.template)(context);
526
+ } catch (e) {
527
+ logger.fail.fatal(`Error while rendering template ${template$1.filename}: ${e.message}`);
528
+ }
529
+ }
530
+ /**
531
+ * Basic template function: replaces {variables}
532
+ *
533
+ * @param tmpl Template code
534
+ * @param context Values object
535
+ */
536
+ function template(tmpl, context) {
537
+ return tmpl.replace(/\{([^\}]+)\}/g, (m, key) => {
538
+ return context[key];
539
+ });
540
+ }
541
+ /**
542
+ * Prepare string to use as CSS class name
543
+ *
544
+ * @param str
545
+ */
546
+ function classnameize(str) {
547
+ return str.trim().replace(/\s+/g, "-");
548
+ }
549
+ /**
550
+ * Return path of CSS file.
551
+ *
552
+ * @param stylesheet (css, scss, ...)
553
+ */
554
+ function getCssFilePath(stylesheet) {
555
+ const cssFilePrefix = option(cssFilePrefixes, stylesheet);
556
+ return path.join(option(o.destCssPaths, stylesheet), `${cssFilePrefix}${o.fontBaseName}.${stylesheet}`);
557
+ }
558
+ /**
559
+ * Return path of HTML demo file or `null` if its generation was disabled.
560
+ */
561
+ function getDemoFilePath() {
562
+ if (!o.htmlDemo) return null;
563
+ const name$1 = o.htmlDemoFilename || o.fontBaseName;
564
+ return path.join(o.destHtml, `${name$1}.html`);
565
+ }
566
+ /**
567
+ * Return path of HTML demo file or `null` if feature was disabled
568
+ */
569
+ function getDemoPath() {
570
+ if (!o.htmlDemo) return null;
571
+ return o.destHtml;
572
+ }
573
+ /**
574
+ * Save hash to cache file.
575
+ *
576
+ * @param name Task name (webfont).
577
+ * @param target Task target name.
578
+ * @param hash Hash.
579
+ */
580
+ function saveHash(name$1, target$1, hash) {
581
+ const filepath = getHashPath(name$1, target$1);
582
+ fs.mkdirSync(path.dirname(filepath), { recursive: true });
583
+ fs.writeFileSync(filepath, hash);
584
+ }
585
+ /**
586
+ * Read hash from cache file or `null` if file don’t exist.
587
+ *
588
+ * @param name Task name (webfont).
589
+ * @param target Task target name.
590
+ */
591
+ function readHash(name$1, target$1) {
592
+ const filepath = getHashPath(name$1, target$1);
593
+ if (fs.existsSync(filepath)) return fs.readFileSync(filepath, "utf8");
594
+ return null;
595
+ }
596
+ /**
597
+ * Return path to cache file.
598
+ *
599
+ * @param name Task name (webfont).
600
+ * @param target Task target name.
601
+ */
602
+ function getHashPath(name$1, target$1) {
603
+ return path.join(o.cache, name$1, target$1, "hash");
604
+ }
605
+ };
606
+
607
+ //#endregion
608
+ export { webfont_default as default, webfont };
609
+ //# sourceMappingURL=webfont.js.map