@kanso-labs/unplugin-style-dictionary 0.6.2 → 0.8.0

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.
package/dist/index.js CHANGED
@@ -1,23 +1,24 @@
1
+ import { matchesWatchedFile } from "./watch-filter.js";
1
2
  import JSON5 from "json5";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import { pathToFileURL } from "node:url";
5
6
  import zlib from "node:zlib";
6
- import picomatch from "picomatch";
7
7
  import StyleDictionary from "style-dictionary";
8
8
  import { glob } from "tinyglobby";
9
9
  import { createUnplugin } from "unplugin";
10
10
  //#region src/index.ts
11
- function matchesWatchedFile(file, patterns) {
12
- const normalizedFile = file.replace(/\\/g, "/");
13
- return patterns.some((pattern) => {
14
- const normalizedPattern = pattern.replace(/\\/g, "/");
15
- return normalizedPattern === normalizedFile || picomatch.isMatch(normalizedFile, normalizedPattern);
16
- });
17
- }
18
11
  function asError(error) {
19
12
  return error instanceof Error ? error : new Error(errorMessage(error));
20
13
  }
14
+ function colourAllowed(stream) {
15
+ if (process.env.NO_COLOR) return false;
16
+ const forced = process.env.FORCE_COLOR;
17
+ if (forced === "0") return false;
18
+ if (forced !== void 0 && forced !== "") return true;
19
+ if (process.env.TERM === "dumb") return false;
20
+ return stream.isTTY === true;
21
+ }
21
22
  function discardTemporaryFile(temporary) {
22
23
  try {
23
24
  fs.rmSync(temporary, { force: true });
@@ -29,6 +30,15 @@ function errorMessage(error) {
29
30
  function isConfig(value) {
30
31
  return typeof value === "object" && value !== null;
31
32
  }
33
+ function isMessageChannel(value) {
34
+ return typeof value === "function";
35
+ }
36
+ function isThenable(value) {
37
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
38
+ }
39
+ function paint(code, value, allowed) {
40
+ return allowed ? `\u001B[${code}m${value}\u001B[0m` : value;
41
+ }
32
42
  function unwrapDefault(value) {
33
43
  return typeof value === "object" && value !== null && "default" in value ? value.default ?? value : value;
34
44
  }
@@ -130,11 +140,25 @@ function statOrNull(file) {
130
140
  }
131
141
  const unpluginFactory = (options = {}, meta) => {
132
142
  const isWebpack = meta.framework === "webpack";
133
- const { cache = true, failOnError = "build", logLevel, report = true, root: rootOption, silent = false } = options;
143
+ const { cache = true, errorOverlay = true, failOnError = "build", logLevel, onBuildEnd, onBuildError, onBuildStart, report = true, root: rootOption, silent = false } = options;
134
144
  const level = logLevel ?? (silent ? "silent" : void 0);
135
145
  const quiet = level === "silent" || level === "warn";
136
146
  const verbosity = level === void 0 ? void 0 : level === "verbose" ? "verbose" : level === "silent" ? "silent" : "default";
137
147
  const failsTheBuild = (context) => failOnError === true || (context === void 0 ? failOnError === "build" : failOnError === "serve");
148
+ let hostCommand = "build";
149
+ let hostMode;
150
+ let isWatching = false;
151
+ const configContext = () => ({
152
+ command: hostCommand,
153
+ mode: hostMode ?? (hostCommand === "serve" ? "development" : "production"),
154
+ watch: isWatching
155
+ });
156
+ const adoptWatchMode = (context) => {
157
+ const hookMeta = "meta" in context ? context.meta : void 0;
158
+ if (typeof hookMeta !== "object" || hookMeta === null) return;
159
+ const watching = "watchMode" in hookMeta ? hookMeta.watchMode : void 0;
160
+ if (typeof watching === "boolean") isWatching = watching;
161
+ };
138
162
  let root = rootOption ? path.resolve(process.cwd(), rootOption) : process.cwd();
139
163
  const generatedDestinations = /* @__PURE__ */ new Set();
140
164
  let cachedPatterns;
@@ -156,15 +180,52 @@ const unpluginFactory = (options = {}, meta) => {
156
180
  return Array.from(paths);
157
181
  };
158
182
  const isWatchedSource = (file, patterns) => !generatedDestinations.has(file.replace(/\\/g, "/")) && matchesWatchedFile(file, patterns);
183
+ const stdoutColour = colourAllowed(process.stdout);
184
+ const stderrColour = colourAllowed(process.stderr);
185
+ let host;
186
+ const adoptHost = (context) => {
187
+ if (host) return;
188
+ const warn = "warn" in context ? context.warn : void 0;
189
+ if (!isMessageChannel(warn)) return;
190
+ const info = "info" in context ? context.info : void 0;
191
+ host = {
192
+ error: (message) => {
193
+ warn.call(context, message);
194
+ },
195
+ info: isMessageChannel(info) ? (message) => {
196
+ info.call(context, message);
197
+ } : void 0
198
+ };
199
+ };
159
200
  const log = (message, type = "info") => {
160
201
  const prefix = "[unplugin-style-dictionary]";
161
202
  if (type === "error") {
162
- console.error(`\x1b[31m${prefix} ${message}\x1b[0m`);
203
+ if (host) {
204
+ host.error(`${prefix} ${message}`);
205
+ return;
206
+ }
207
+ console.error(paint("31", `${prefix} ${message}`, stderrColour));
163
208
  return;
164
209
  }
165
210
  if (quiet) return;
166
- if (type === "success") console.log(`\x1b[32m${prefix} ${message}\x1b[0m`);
167
- else console.log(`\x1b[36m${prefix} ${message}\x1b[0m`);
211
+ if (host?.info) {
212
+ host.info(`${prefix} ${message}`);
213
+ return;
214
+ }
215
+ console.log(paint(type === "success" ? "32" : "36", `${prefix} ${message}`, stdoutColour));
216
+ };
217
+ const callHook = (name, hook, ...args) => {
218
+ let result;
219
+ try {
220
+ result = hook(...args);
221
+ } catch (err) {
222
+ log(`The ${name} hook threw: ${errorMessage(err)}`, "error");
223
+ return;
224
+ }
225
+ if (!isThenable(result)) return;
226
+ Promise.resolve(result).catch((err) => {
227
+ log(`The ${name} hook rejected: ${errorMessage(err)}`, "error");
228
+ });
168
229
  };
169
230
  const resolveConfigs = async () => {
170
231
  let rawConfig = options.config;
@@ -184,7 +245,7 @@ const unpluginFactory = (options = {}, meta) => {
184
245
  log("No configuration specified and no default config file found. Style Dictionary will not compile.", "error");
185
246
  return [];
186
247
  }
187
- if (typeof rawConfig === "function") rawConfig = await rawConfig();
248
+ if (typeof rawConfig === "function") rawConfig = await rawConfig(configContext());
188
249
  return (Array.isArray(rawConfig) ? rawConfig : [rawConfig]).map((conf) => {
189
250
  if (typeof conf === "string") {
190
251
  const fullPath = path.resolve(root, conf);
@@ -314,7 +375,7 @@ const unpluginFactory = (options = {}, meta) => {
314
375
  const displayPath = path.relative(root, filePath).replace(/\\/g, "/");
315
376
  const dir = path.dirname(displayPath);
316
377
  const base = path.basename(displayPath);
317
- const coloredPath = dir === "." ? `\x1b[32m${base}\x1b[0m` : `\x1b[90m${dir}/\x1b[0m\x1b[32m${base}\x1b[0m`;
378
+ const coloredPath = dir === "." ? paint("32", base, stdoutColour) : paint("90", `${dir}/`, stdoutColour) + paint("32", base, stdoutColour);
318
379
  try {
319
380
  const sizeStr = `${(fs.statSync(filePath).size / 1024).toFixed(2)} kB`;
320
381
  const content = fs.readFileSync(filePath);
@@ -333,7 +394,7 @@ const unpluginFactory = (options = {}, meta) => {
333
394
  for (const info of fileInfos) {
334
395
  const pathPadding = " ".repeat(Math.max(2, longestPathLength - info.relativeDisplayPath.length + 2));
335
396
  const sizePadded = info.sizeStr.padStart(longestSizeLength);
336
- console.log(`${info.coloredPath}${pathPadding}\x1b[90m${sizePadded} │ gzip: ${info.gzipSizeStr}\x1b[0m`);
397
+ console.log(info.coloredPath + pathPadding + paint("90", `${sizePadded} │ gzip: ${info.gzipSizeStr}`, stdoutColour));
337
398
  }
338
399
  }
339
400
  };
@@ -343,6 +404,7 @@ const unpluginFactory = (options = {}, meta) => {
343
404
  let skipped = 0;
344
405
  try {
345
406
  if (!context) log("Compiling design tokens...", "info");
407
+ if (onBuildStart) callHook("onBuildStart", onBuildStart);
346
408
  for (const item of resolvedConfigs) {
347
409
  const declared = cache ? await readConfigObject(item, false) : null;
348
410
  if (declared && await isUpToDate(item, declared)) {
@@ -373,10 +435,17 @@ const unpluginFactory = (options = {}, meta) => {
373
435
  } catch (err) {
374
436
  const duration = Date.now() - startTime;
375
437
  log(`Compilation failed after ${duration}ms: ${errorMessage(err)}`, "error");
438
+ notifyBuildOutcome?.(asError(err));
439
+ if (onBuildError) callHook("onBuildError", onBuildError, err);
376
440
  if (failsTheBuild(context)) throw err;
377
441
  return;
378
442
  }
443
+ notifyBuildOutcome?.(null);
379
444
  const duration = Date.now() - startTime;
445
+ if (onBuildEnd) {
446
+ const files = Array.from(generatedFiles).sort((left, right) => left.localeCompare(right));
447
+ callHook("onBuildEnd", onBuildEnd, files, duration);
448
+ }
380
449
  const everythingSkipped = skipped === resolvedConfigs.length;
381
450
  if (context) {
382
451
  log(everythingSkipped ? `Design tokens already up to date after change in ${context} (${duration}ms)` : `Rebuilt design tokens due to change in ${context} (${duration}ms)`, "success");
@@ -418,6 +487,7 @@ const unpluginFactory = (options = {}, meta) => {
418
487
  let inFlight;
419
488
  let waiting = [];
420
489
  let refreshServerWatchList;
490
+ let notifyBuildOutcome;
421
491
  const drain = async () => {
422
492
  while (pendingReason !== void 0) {
423
493
  const reason = pendingReason;
@@ -437,7 +507,10 @@ const unpluginFactory = (options = {}, meta) => {
437
507
  }
438
508
  } catch (err) {
439
509
  failure = { error: err };
440
- if (!compiling) log(`Rebuild failed: ${errorMessage(err)}`, "error");
510
+ if (!compiling) {
511
+ log(`Rebuild failed: ${errorMessage(err)}`, "error");
512
+ notifyBuildOutcome?.(asError(err));
513
+ }
441
514
  }
442
515
  for (const settle of resolvers) settle(failure);
443
516
  }
@@ -460,6 +533,8 @@ const unpluginFactory = (options = {}, meta) => {
460
533
  };
461
534
  return {
462
535
  async buildStart() {
536
+ adoptHost(this);
537
+ adoptWatchMode(this);
463
538
  const resolved = await resolveConfigs();
464
539
  if (resolved.length === 0) return;
465
540
  const { paths } = await getWatchTargets(resolved);
@@ -476,8 +551,19 @@ const unpluginFactory = (options = {}, meta) => {
476
551
  vite: {
477
552
  configResolved(config) {
478
553
  if (rootOption === void 0) root = config.root || process.cwd();
554
+ hostCommand = config.command;
555
+ hostMode = config.mode;
556
+ host = {
557
+ error: (message) => {
558
+ config.logger.error(message);
559
+ },
560
+ info: (message) => {
561
+ config.logger.info(message);
562
+ }
563
+ };
479
564
  },
480
565
  async configureServer(server) {
566
+ isWatching = true;
481
567
  const resolved = await resolveConfigs();
482
568
  if (resolved.length === 0) return;
483
569
  let targets = await getWatchTargets(resolved);
@@ -486,6 +572,29 @@ const unpluginFactory = (options = {}, meta) => {
486
572
  targets = await getWatchTargets(rebuilt);
487
573
  server.watcher.add(targets.paths);
488
574
  };
575
+ if (errorOverlay) {
576
+ let overlayShowing = false;
577
+ notifyBuildOutcome = (error) => {
578
+ if (error) {
579
+ overlayShowing = true;
580
+ server.hot.send({
581
+ err: {
582
+ message: error.message,
583
+ plugin: "unplugin-style-dictionary",
584
+ stack: error.stack ?? ""
585
+ },
586
+ type: "error"
587
+ });
588
+ return;
589
+ }
590
+ if (!overlayShowing) return;
591
+ overlayShowing = false;
592
+ server.hot.send({
593
+ type: "update",
594
+ updates: []
595
+ });
596
+ };
597
+ }
489
598
  server.watcher.on("all", (_event, file) => {
490
599
  if (!isWatchedSource(file, targets.patterns)) return;
491
600
  schedule(path.basename(file)).catch(() => {});
@@ -493,6 +602,8 @@ const unpluginFactory = (options = {}, meta) => {
493
602
  }
494
603
  },
495
604
  async watchChange(id) {
605
+ adoptHost(this);
606
+ adoptWatchMode(this);
496
607
  watchRebuild = true;
497
608
  if (cachedPatterns && !isWatchedSource(id, cachedPatterns)) return;
498
609
  const resolved = await resolveConfigs();
@@ -504,7 +615,25 @@ const unpluginFactory = (options = {}, meta) => {
504
615
  },
505
616
  webpack(compiler) {
506
617
  if (rootOption === void 0) root = compiler.options.context ?? process.cwd();
618
+ hostMode = compiler.options.mode;
619
+ const pending = [];
620
+ host = { error: (message) => {
621
+ pending.push(message);
622
+ } };
623
+ compiler.hooks.compilation.tap("unplugin-style-dictionary", (compilation) => {
624
+ for (const message of pending.splice(0)) {
625
+ const reported = new Error(message);
626
+ reported.name = "UnpluginStyleDictionaryWarning";
627
+ compilation.warnings.push(reported);
628
+ }
629
+ });
630
+ const drainToConsole = () => {
631
+ for (const message of pending.splice(0)) console.error(paint("31", message, stderrColour));
632
+ };
633
+ compiler.hooks.failed.tap("unplugin-style-dictionary", drainToConsole);
634
+ compiler.hooks.done.tap("unplugin-style-dictionary", drainToConsole);
507
635
  compiler.hooks.beforeCompile.tapPromise("unplugin-style-dictionary", async () => {
636
+ isWatching = compiler.watchMode;
508
637
  const resolved = await resolveConfigs();
509
638
  if (resolved.length === 0) return;
510
639
  await compileOnceAcrossInstances(resolved);
@@ -515,6 +644,6 @@ const unpluginFactory = (options = {}, meta) => {
515
644
  };
516
645
  const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);
517
646
  //#endregion
518
- export { unplugin as default, unplugin, matchesWatchedFile, unpluginFactory };
647
+ export { unplugin as default, unplugin };
519
648
 
520
649
  //# sourceMappingURL=index.js.map