@kanso-labs/unplugin-style-dictionary 0.7.0 → 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/README.md +265 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +144 -8
- package/dist/index.js.map +1 -1
- package/dist/rolldown.d.ts +2 -2
- package/dist/rollup.d.ts +2 -2
- package/dist/types.d.ts +133 -1
- package/dist/vite.d.ts +2 -2
- package/dist/webpack.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,9 @@ build on Rolldown/tsdown) that both need tokens compiled ahead of them.
|
|
|
27
27
|
- **Config flexibility**: Supports file paths (JSON, JSON5, JSONC, JS, MJS, TS),
|
|
28
28
|
configuration objects, or functions — including registering custom formats at
|
|
29
29
|
config-resolution time.
|
|
30
|
+
- **Error overlay**: A rebuild that fails under Vite's dev server is pushed to
|
|
31
|
+
the error overlay rather than only to the terminal, and cleared on the next
|
|
32
|
+
one that succeeds.
|
|
30
33
|
- **Atomic writes**: Every generated file is written to a temporary sibling and
|
|
31
34
|
renamed into place, so code importing a token file while it is being rebuilt
|
|
32
35
|
never reads a half-written file.
|
|
@@ -191,6 +194,38 @@ source and no config file does not reach it, so a dev server editing unrelated
|
|
|
191
194
|
project files leaves it alone — treat it as the place to prepare a build, not as
|
|
192
195
|
a general file-change hook.
|
|
193
196
|
|
|
197
|
+
**It is handed what the host is doing**, so the configuration it returns can
|
|
198
|
+
depend on it — build an expensive platform on `vite build` and skip it while the
|
|
199
|
+
dev server runs:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
styleDictionaryPlugin({
|
|
203
|
+
config: ({ command, mode, watch }) => ({
|
|
204
|
+
source: ['tokens/**/*.json'],
|
|
205
|
+
platforms: {
|
|
206
|
+
css: {
|
|
207
|
+
transformGroup: 'css',
|
|
208
|
+
buildPath: 'dist/',
|
|
209
|
+
files: [{ destination: 'vars.css', format: 'css/variables' }],
|
|
210
|
+
},
|
|
211
|
+
// Shells out to a native toolchain, so it is worth a minute of a real
|
|
212
|
+
// build and not worth a second of every rebuild.
|
|
213
|
+
...(command === 'build' && !watch ? { ios: iosPlatform(mode) } : {}),
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`command` is `'serve'` only under Vite's dev server; every other target builds.
|
|
220
|
+
`mode` is Vite's or webpack's own, and follows `command` on rollup and rolldown,
|
|
221
|
+
which have no such concept. `watch` is read from the host rather than inferred
|
|
222
|
+
from `command`, because `rollup --watch` both watches and builds. The full
|
|
223
|
+
contract is [`StyleDictionaryConfigContext`](#options-reference).
|
|
224
|
+
|
|
225
|
+
A function taking no arguments stays valid — TypeScript accepts one of fewer
|
|
226
|
+
parameters and JavaScript ignores the extra argument — so the example below
|
|
227
|
+
needs no change.
|
|
228
|
+
|
|
194
229
|
```typescript
|
|
195
230
|
// Named `styleDictionaryPlugin` here to avoid colliding with the `StyleDictionary`
|
|
196
231
|
// class imported from the `style-dictionary` package itself, below.
|
|
@@ -355,6 +390,41 @@ A compile that fails is reported at every level, including `'silent'`, which is
|
|
|
355
390
|
why there is no `'error'`. `log.warnings` is never touched: if your
|
|
356
391
|
configuration turns a warning into a thrown build, that stays your decision.
|
|
357
392
|
|
|
393
|
+
### Where the messages go
|
|
394
|
+
|
|
395
|
+
Through your bundler, not straight to the console, and each one takes them its
|
|
396
|
+
own way:
|
|
397
|
+
|
|
398
|
+
| Target | Progress lines | A failed compile |
|
|
399
|
+
| ------------------ | ------------------------ | ---------------------------------- |
|
|
400
|
+
| Vite | `config.logger.info` | `config.logger.error` |
|
|
401
|
+
| Rollup, Rolldown | the plugin context's log | the context's warning channel |
|
|
402
|
+
| Webpack | the console | `compilation.warnings`, so `stats` |
|
|
403
|
+
| No host (one-shot) | the console | the console |
|
|
404
|
+
|
|
405
|
+
That is what makes a `customLogger` and `clearScreen` work under Vite, and what
|
|
406
|
+
puts a failed compile into `stats.toJson()` under webpack — where it reaches CI
|
|
407
|
+
annotations and anything else reading the build's own output.
|
|
408
|
+
|
|
409
|
+
**A failure is reported as a warning, never on the host's error channel.**
|
|
410
|
+
Rollup's `this.error` aborts the bundle, so reporting a failure through it would
|
|
411
|
+
stop every build that reported one — taking the decision `failOnError` exists to
|
|
412
|
+
make. The same reasoning puts webpack's report in `compilation.warnings` rather
|
|
413
|
+
than `compilation.errors`, so `failOnError: false` really does leave the build
|
|
414
|
+
passing.
|
|
415
|
+
|
|
416
|
+
**Your bundler's own log level applies.** `vite --logLevel silent` silences
|
|
417
|
+
Vite's logger, and the plugin's lines are Vite's logger's now, so they go too.
|
|
418
|
+
Nothing is lost by it that matters: `failOnError` decides whether a broken token
|
|
419
|
+
set stops the build, and it decides that whether or not anything was printed.
|
|
420
|
+
|
|
421
|
+
Colour follows the usual conventions, which it previously ignored entirely: no
|
|
422
|
+
escapes when `NO_COLOR` is set, or when the stream is not a terminal, or under
|
|
423
|
+
`TERM=dumb`; escapes when `FORCE_COLOR` is set to anything but `0`, including on
|
|
424
|
+
a non-terminal, which is what that variable is for. `NO_COLOR` wins over
|
|
425
|
+
`FORCE_COLOR`. stdout and stderr are decided separately, because they are
|
|
426
|
+
redirected separately.
|
|
427
|
+
|
|
358
428
|
## Failing the Build
|
|
359
429
|
|
|
360
430
|
A token compile that fails stops the build. `vite build`, `rollup` and `webpack`
|
|
@@ -373,17 +443,68 @@ StyleDictionary({
|
|
|
373
443
|
})
|
|
374
444
|
```
|
|
375
445
|
|
|
376
|
-
A failure is always reported, whatever `failOnError` and `silent`
|
|
446
|
+
A failure is always reported by the plugin, whatever `failOnError` and `silent`
|
|
447
|
+
are set to — see [Where the messages go](#where-the-messages-go) for which
|
|
448
|
+
channel it arrives on, and for the one thing that can still suppress it.
|
|
449
|
+
|
|
450
|
+
Under Vite's dev server it is reported to the browser as well. A failed rebuild
|
|
451
|
+
is pushed to Vite's error overlay, naming this plugin and carrying Style
|
|
452
|
+
Dictionary's message, and the overlay is dismissed by the next rebuild that
|
|
453
|
+
succeeds — so a page left rendering the last good token file says so instead of
|
|
454
|
+
looking current. Set `errorOverlay: false` to keep the failure in the terminal
|
|
455
|
+
only.
|
|
456
|
+
|
|
457
|
+
`failOnError` and `errorOverlay` answer different questions and do not interact:
|
|
458
|
+
the first decides whether the host stops, the second whether the browser is
|
|
459
|
+
told. The dev server's default is not to stop, which is exactly when the overlay
|
|
460
|
+
is the only thing that can report the failure.
|
|
461
|
+
|
|
462
|
+
## Build Hooks
|
|
463
|
+
|
|
464
|
+
Three optional callbacks, for work that has to happen around a compile rather
|
|
465
|
+
than inside one — formatting the generated files, type-checking them, telling
|
|
466
|
+
something else they have landed.
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
StyleDictionary({
|
|
470
|
+
config: 'sd.config.json',
|
|
471
|
+
onBuildStart: () => console.log('compiling tokens'),
|
|
472
|
+
onBuildEnd: async (files, durationMs) => {
|
|
473
|
+
console.log(`wrote ${files.length} files in ${durationMs}ms`)
|
|
474
|
+
await formatGeneratedFiles(files)
|
|
475
|
+
},
|
|
476
|
+
onBuildError: (error) => notifySomething(error),
|
|
477
|
+
})
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
`files` holds the absolute, platform-native path of every file the build
|
|
481
|
+
declares, sorted. It is what the build declares rather than what it happened to
|
|
482
|
+
write this time: a configuration skipped because its output was already current
|
|
483
|
+
contributes its destinations too, so a post-processing step still sees the whole
|
|
484
|
+
set on a rebuild that changed one file.
|
|
485
|
+
|
|
486
|
+
A rebuild is a build, so all three fire again on every watch-triggered one.
|
|
487
|
+
|
|
488
|
+
**A hook cannot fail the build that called it.** The return value is not
|
|
489
|
+
awaited, so nothing waits for post-processing; a hook that throws is reported
|
|
490
|
+
and the build stands, and a promise that rejects is caught and reported rather
|
|
491
|
+
than reaching the host as an unhandled rejection — which would otherwise take a
|
|
492
|
+
dev server down from inside a step meant to reformat a file.
|
|
493
|
+
|
|
494
|
+
`onBuildError` fires whatever `failOnError` is set to, and before that option
|
|
495
|
+
decides whether to rethrow. The two answer different questions: one is whether
|
|
496
|
+
the host stops, the other is that a build went wrong.
|
|
377
497
|
|
|
378
498
|
## Public API
|
|
379
499
|
|
|
380
|
-
Small on purpose. Four bundler entry points, one root entry, and
|
|
500
|
+
Small on purpose. Four bundler entry points, one root entry, and two types.
|
|
381
501
|
|
|
382
502
|
| Import | What it is |
|
|
383
503
|
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
384
504
|
| `…/vite`, `…/rolldown`, `…/rollup`, `…/webpack` | Default export: the plugin for that bundler. Call it with the options below. |
|
|
385
505
|
| `…` (the root) | Default export, also named `unplugin`: the unplugin instance, carrying `.vite`, `.rolldown`, `.rollup` and `.webpack`. |
|
|
386
506
|
| `UnpluginStyleDictionaryOptions` | The options type, exported from every entry above. |
|
|
507
|
+
| `StyleDictionaryConfigContext` | What the function form of `config` is handed, exported from every entry above. |
|
|
387
508
|
|
|
388
509
|
Anything not in that table is internal, whatever a build output happens to
|
|
389
510
|
contain. In particular the watch filter and the raw unplugin factory are not
|
|
@@ -415,12 +536,72 @@ const plugin = styleDictionary.rollup({ config: 'sd.config.json' })
|
|
|
415
536
|
* build (e.g. `tsdown`/`rolldown build` without `--watch`) only builds once, in
|
|
416
537
|
* `buildStart`.
|
|
417
538
|
*
|
|
539
|
+
* Everything the plugin says goes through the host rather than to the console:
|
|
540
|
+
* Vite's `config.logger`, the plugin context under rollup and rolldown, and
|
|
541
|
+
* `compilation.warnings` under webpack, which is what puts a failed compile in
|
|
542
|
+
* `stats.toJson()`. A failure is reported on the warning channel and never the
|
|
543
|
+
* error one — rollup's `this.error` aborts the bundle, and that decision is
|
|
544
|
+
* `failOnError`'s alone. Where no host offers a channel the console is used,
|
|
545
|
+
* with colour gated on `NO_COLOR`, `FORCE_COLOR` and whether the stream is a
|
|
546
|
+
* terminal.
|
|
547
|
+
*
|
|
548
|
+
* The three `onBuild*` hooks are called synchronously and their return value
|
|
549
|
+
* is not awaited, so a build never waits for one. A hook may still be written
|
|
550
|
+
* `async`: a promise it returns is left to run on its own, and a rejection is
|
|
551
|
+
* caught and reported rather than reaching the host as an unhandled one. A
|
|
552
|
+
* hook that throws is reported and does not fail the build that called it.
|
|
553
|
+
*
|
|
554
|
+
* They return `Promise<void> | void` rather than `void` for that reason. Both
|
|
555
|
+
* accept an `async` hook as far as the compiler is concerned, but `void` alone
|
|
556
|
+
* makes one a `no-misused-promises` error under the type-aware lint rules a
|
|
557
|
+
* consumer is likely to be running — for a hook this documents as supported.
|
|
558
|
+
*
|
|
418
559
|
* Rolldown's watch mode is the exception, and it is not about glob patterns.
|
|
419
560
|
* `addWatchFile` is accepted either way, but what happens next differs by
|
|
420
561
|
* platform — on macOS a file registered through it is watched by nothing, while
|
|
421
562
|
* on a Linux runner the same edit reaches a rebuild. Do not rely on a token
|
|
422
563
|
* edit triggering a rebuild there.
|
|
423
564
|
*/
|
|
565
|
+
/**
|
|
566
|
+
* What the host is doing, handed to the function form of `config` so it can
|
|
567
|
+
* decide what to build.
|
|
568
|
+
*
|
|
569
|
+
* Only Vite reports all three. Where a host does not say, the value is
|
|
570
|
+
* derived rather than guessed at, and each field below says how.
|
|
571
|
+
*/
|
|
572
|
+
export interface StyleDictionaryConfigContext {
|
|
573
|
+
/**
|
|
574
|
+
* Whether the host is serving or building.
|
|
575
|
+
*
|
|
576
|
+
* `'serve'` comes from Vite's own `config.command` and is the dev server.
|
|
577
|
+
* Every other target builds, so it is `'build'` there — rollup, rolldown and
|
|
578
|
+
* webpack have no serving mode of their own to report.
|
|
579
|
+
*/
|
|
580
|
+
command: 'build' | 'serve'
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* The host's mode, as it names it.
|
|
584
|
+
*
|
|
585
|
+
* Vite reports its `config.mode` — `'development'` serving,
|
|
586
|
+
* `'production'` building, or whatever `--mode` named. webpack reports its
|
|
587
|
+
* `mode` option. rollup and rolldown have no such concept, so the value
|
|
588
|
+
* follows `command`: `'development'` when serving, `'production'` when
|
|
589
|
+
* building.
|
|
590
|
+
*/
|
|
591
|
+
mode: string
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Whether the host will keep rebuilding.
|
|
595
|
+
*
|
|
596
|
+
* `true` under Vite's dev server, `rollup --watch`, `rolldown.watch()` and
|
|
597
|
+
* `webpack --watch`; `false` for a one-shot build. It is read from the
|
|
598
|
+
* host — the plugin context's `meta.watchMode` on the three rollup-shaped
|
|
599
|
+
* targets, and `compiler.watchMode` on webpack — rather than inferred from
|
|
600
|
+
* `command`, because `rollup --watch` both watches and builds.
|
|
601
|
+
*/
|
|
602
|
+
watch: boolean
|
|
603
|
+
}
|
|
604
|
+
|
|
424
605
|
export interface UnpluginStyleDictionaryOptions {
|
|
425
606
|
/**
|
|
426
607
|
* Whether a configuration whose output is already up to date may skip its
|
|
@@ -462,18 +643,51 @@ export interface UnpluginStyleDictionaryOptions {
|
|
|
462
643
|
* - A function that returns a config or array of configs (or resolves to them).
|
|
463
644
|
* Useful for calling `StyleDictionary.registerFormat()` (or other `register*`
|
|
464
645
|
* methods) before returning a config that references the custom format by name.
|
|
646
|
+
* It is handed a `StyleDictionaryConfigContext` describing what the host is
|
|
647
|
+
* doing, so an expensive platform can be built only when it is wanted —
|
|
648
|
+
* skipped under the dev server, built by `vite build`. A function taking no
|
|
649
|
+
* arguments stays valid: TypeScript accepts one of fewer parameters, and
|
|
650
|
+
* JavaScript ignores the extra argument.
|
|
465
651
|
*
|
|
466
652
|
* If not provided, the root directory is searched for 'sd.config.json',
|
|
467
653
|
* 'config.json', 'sd.config.js' and 'sd.config.mjs', in that order. The
|
|
468
654
|
* first one that exists wins, and the rest are not looked at.
|
|
469
655
|
*/
|
|
470
656
|
config?:
|
|
471
|
-
| ((
|
|
657
|
+
| ((
|
|
658
|
+
context: StyleDictionaryConfigContext,
|
|
659
|
+
) => Config | Config[] | Promise<Config | Config[]>)
|
|
472
660
|
| Config
|
|
473
661
|
| Config[]
|
|
474
662
|
| string
|
|
475
663
|
| string[]
|
|
476
664
|
|
|
665
|
+
/**
|
|
666
|
+
* Whether a failed rebuild is pushed to Vite's error overlay.
|
|
667
|
+
*
|
|
668
|
+
* A rebuild that fails under the dev server used to reach the browser
|
|
669
|
+
* nowhere: the page went on rendering the last good generated file, and the
|
|
670
|
+
* only trace was one red terminal line the developer may not have been
|
|
671
|
+
* looking at. With this on, the failure is sent to the page as an error
|
|
672
|
+
* frame naming this plugin, and the overlay is dismissed on the next
|
|
673
|
+
* rebuild that succeeds.
|
|
674
|
+
*
|
|
675
|
+
* This is Vite's overlay, so it does nothing on the other three targets,
|
|
676
|
+
* and nothing under `vite build` — there is no page to draw on.
|
|
677
|
+
*
|
|
678
|
+
* It is not `failOnError`'s job, and the two are independent. `failOnError`
|
|
679
|
+
* decides whether the host stops; this decides whether the browser is told.
|
|
680
|
+
* A dev server deliberately keeps serving through a failed rebuild, which is
|
|
681
|
+
* precisely the case where the overlay is the only thing that can say so.
|
|
682
|
+
*
|
|
683
|
+
* A failure Style Dictionary raises before this plugin can catch it — a
|
|
684
|
+
* token file that is not valid JSON, which rejects out of band — reaches
|
|
685
|
+
* neither the overlay nor this option.
|
|
686
|
+
*
|
|
687
|
+
* @default true
|
|
688
|
+
*/
|
|
689
|
+
errorOverlay?: boolean
|
|
690
|
+
|
|
477
691
|
/**
|
|
478
692
|
* Whether a compile that fails should throw rather than only be reported.
|
|
479
693
|
*
|
|
@@ -513,11 +727,59 @@ export interface UnpluginStyleDictionaryOptions {
|
|
|
513
727
|
* A compile that fails is reported at every level, so there is no
|
|
514
728
|
* `'error'`: `'silent'` is the quietest and still reports a failure.
|
|
515
729
|
*
|
|
730
|
+
* This option governs what the plugin says, not where it goes. The messages
|
|
731
|
+
* are handed to the host — Vite's `config.logger`, the rollup and rolldown
|
|
732
|
+
* plugin context, webpack's `compilation` — so a host silenced by its own
|
|
733
|
+
* log level suppresses them after this option has let them through. A
|
|
734
|
+
* failure still stops the build whenever `failOnError` says it should,
|
|
735
|
+
* printed or not.
|
|
736
|
+
*
|
|
516
737
|
* @default undefined, which prints the plugin's own lines and leaves the
|
|
517
738
|
* configuration's `log.verbosity` alone
|
|
518
739
|
*/
|
|
519
740
|
logLevel?: 'info' | 'silent' | 'verbose' | 'warn'
|
|
520
741
|
|
|
742
|
+
/**
|
|
743
|
+
* Called once a build has finished, with every file it declares and how long
|
|
744
|
+
* it took in milliseconds.
|
|
745
|
+
*
|
|
746
|
+
* The paths are absolute and platform-native, sorted so two runs of the same
|
|
747
|
+
* configuration hand back the same order. They are what the build declares
|
|
748
|
+
* rather than what it wrote this time: a configuration skipped by `cache`
|
|
749
|
+
* contributes its destinations too, because they are on disk and current,
|
|
750
|
+
* and a post-processing step that ignored them would leave half the output
|
|
751
|
+
* untouched on a rebuild that changed one file.
|
|
752
|
+
*
|
|
753
|
+
* This is where formatting the generated files, type-checking them, or
|
|
754
|
+
* telling something else they have landed belongs.
|
|
755
|
+
*
|
|
756
|
+
* @default undefined
|
|
757
|
+
*/
|
|
758
|
+
onBuildEnd?: (files: string[], durationMs: number) => Promise<void> | void
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Called when a build fails, with whatever was thrown.
|
|
762
|
+
*
|
|
763
|
+
* It fires whatever `failOnError` is set to, and before that option decides
|
|
764
|
+
* whether to rethrow — the two answer different questions, and under a dev
|
|
765
|
+
* server the default is not to throw at all.
|
|
766
|
+
*
|
|
767
|
+
* The failure is reported to the console either way, so this is for reacting
|
|
768
|
+
* to one rather than for noticing it.
|
|
769
|
+
*
|
|
770
|
+
* @default undefined
|
|
771
|
+
*/
|
|
772
|
+
onBuildError?: (error: unknown) => Promise<void> | void
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Called before a build begins, once per build.
|
|
776
|
+
*
|
|
777
|
+
* A watch-triggered rebuild is a build, so this fires again for each one.
|
|
778
|
+
*
|
|
779
|
+
* @default undefined
|
|
780
|
+
*/
|
|
781
|
+
onBuildStart?: () => Promise<void> | void
|
|
782
|
+
|
|
521
783
|
/**
|
|
522
784
|
* Whether the table of generated files and their sizes is produced.
|
|
523
785
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { UnpluginStyleDictionaryOptions } from "./types.js";
|
|
1
|
+
import { StyleDictionaryConfigContext, UnpluginStyleDictionaryOptions } from "./types.js";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
export declare const unplugin: import("unplugin").UnpluginInstance<UnpluginStyleDictionaryOptions | undefined, false>;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { type UnpluginStyleDictionaryOptions, unplugin as default };
|
|
5
|
+
export { type StyleDictionaryConfigContext, type UnpluginStyleDictionaryOptions, unplugin as default };
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,14 @@ import { createUnplugin } from "unplugin";
|
|
|
11
11
|
function asError(error) {
|
|
12
12
|
return error instanceof Error ? error : new Error(errorMessage(error));
|
|
13
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
|
+
}
|
|
14
22
|
function discardTemporaryFile(temporary) {
|
|
15
23
|
try {
|
|
16
24
|
fs.rmSync(temporary, { force: true });
|
|
@@ -22,6 +30,15 @@ function errorMessage(error) {
|
|
|
22
30
|
function isConfig(value) {
|
|
23
31
|
return typeof value === "object" && value !== null;
|
|
24
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
|
+
}
|
|
25
42
|
function unwrapDefault(value) {
|
|
26
43
|
return typeof value === "object" && value !== null && "default" in value ? value.default ?? value : value;
|
|
27
44
|
}
|
|
@@ -123,11 +140,25 @@ function statOrNull(file) {
|
|
|
123
140
|
}
|
|
124
141
|
const unpluginFactory = (options = {}, meta) => {
|
|
125
142
|
const isWebpack = meta.framework === "webpack";
|
|
126
|
-
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;
|
|
127
144
|
const level = logLevel ?? (silent ? "silent" : void 0);
|
|
128
145
|
const quiet = level === "silent" || level === "warn";
|
|
129
146
|
const verbosity = level === void 0 ? void 0 : level === "verbose" ? "verbose" : level === "silent" ? "silent" : "default";
|
|
130
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
|
+
};
|
|
131
162
|
let root = rootOption ? path.resolve(process.cwd(), rootOption) : process.cwd();
|
|
132
163
|
const generatedDestinations = /* @__PURE__ */ new Set();
|
|
133
164
|
let cachedPatterns;
|
|
@@ -149,15 +180,52 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
149
180
|
return Array.from(paths);
|
|
150
181
|
};
|
|
151
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
|
+
};
|
|
152
200
|
const log = (message, type = "info") => {
|
|
153
201
|
const prefix = "[unplugin-style-dictionary]";
|
|
154
202
|
if (type === "error") {
|
|
155
|
-
|
|
203
|
+
if (host) {
|
|
204
|
+
host.error(`${prefix} ${message}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
console.error(paint("31", `${prefix} ${message}`, stderrColour));
|
|
156
208
|
return;
|
|
157
209
|
}
|
|
158
210
|
if (quiet) return;
|
|
159
|
-
if (
|
|
160
|
-
|
|
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
|
+
});
|
|
161
229
|
};
|
|
162
230
|
const resolveConfigs = async () => {
|
|
163
231
|
let rawConfig = options.config;
|
|
@@ -177,7 +245,7 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
177
245
|
log("No configuration specified and no default config file found. Style Dictionary will not compile.", "error");
|
|
178
246
|
return [];
|
|
179
247
|
}
|
|
180
|
-
if (typeof rawConfig === "function") rawConfig = await rawConfig();
|
|
248
|
+
if (typeof rawConfig === "function") rawConfig = await rawConfig(configContext());
|
|
181
249
|
return (Array.isArray(rawConfig) ? rawConfig : [rawConfig]).map((conf) => {
|
|
182
250
|
if (typeof conf === "string") {
|
|
183
251
|
const fullPath = path.resolve(root, conf);
|
|
@@ -307,7 +375,7 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
307
375
|
const displayPath = path.relative(root, filePath).replace(/\\/g, "/");
|
|
308
376
|
const dir = path.dirname(displayPath);
|
|
309
377
|
const base = path.basename(displayPath);
|
|
310
|
-
const coloredPath = dir === "." ?
|
|
378
|
+
const coloredPath = dir === "." ? paint("32", base, stdoutColour) : paint("90", `${dir}/`, stdoutColour) + paint("32", base, stdoutColour);
|
|
311
379
|
try {
|
|
312
380
|
const sizeStr = `${(fs.statSync(filePath).size / 1024).toFixed(2)} kB`;
|
|
313
381
|
const content = fs.readFileSync(filePath);
|
|
@@ -326,7 +394,7 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
326
394
|
for (const info of fileInfos) {
|
|
327
395
|
const pathPadding = " ".repeat(Math.max(2, longestPathLength - info.relativeDisplayPath.length + 2));
|
|
328
396
|
const sizePadded = info.sizeStr.padStart(longestSizeLength);
|
|
329
|
-
console.log(
|
|
397
|
+
console.log(info.coloredPath + pathPadding + paint("90", `${sizePadded} │ gzip: ${info.gzipSizeStr}`, stdoutColour));
|
|
330
398
|
}
|
|
331
399
|
}
|
|
332
400
|
};
|
|
@@ -336,6 +404,7 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
336
404
|
let skipped = 0;
|
|
337
405
|
try {
|
|
338
406
|
if (!context) log("Compiling design tokens...", "info");
|
|
407
|
+
if (onBuildStart) callHook("onBuildStart", onBuildStart);
|
|
339
408
|
for (const item of resolvedConfigs) {
|
|
340
409
|
const declared = cache ? await readConfigObject(item, false) : null;
|
|
341
410
|
if (declared && await isUpToDate(item, declared)) {
|
|
@@ -366,10 +435,17 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
366
435
|
} catch (err) {
|
|
367
436
|
const duration = Date.now() - startTime;
|
|
368
437
|
log(`Compilation failed after ${duration}ms: ${errorMessage(err)}`, "error");
|
|
438
|
+
notifyBuildOutcome?.(asError(err));
|
|
439
|
+
if (onBuildError) callHook("onBuildError", onBuildError, err);
|
|
369
440
|
if (failsTheBuild(context)) throw err;
|
|
370
441
|
return;
|
|
371
442
|
}
|
|
443
|
+
notifyBuildOutcome?.(null);
|
|
372
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
|
+
}
|
|
373
449
|
const everythingSkipped = skipped === resolvedConfigs.length;
|
|
374
450
|
if (context) {
|
|
375
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");
|
|
@@ -411,6 +487,7 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
411
487
|
let inFlight;
|
|
412
488
|
let waiting = [];
|
|
413
489
|
let refreshServerWatchList;
|
|
490
|
+
let notifyBuildOutcome;
|
|
414
491
|
const drain = async () => {
|
|
415
492
|
while (pendingReason !== void 0) {
|
|
416
493
|
const reason = pendingReason;
|
|
@@ -430,7 +507,10 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
430
507
|
}
|
|
431
508
|
} catch (err) {
|
|
432
509
|
failure = { error: err };
|
|
433
|
-
if (!compiling)
|
|
510
|
+
if (!compiling) {
|
|
511
|
+
log(`Rebuild failed: ${errorMessage(err)}`, "error");
|
|
512
|
+
notifyBuildOutcome?.(asError(err));
|
|
513
|
+
}
|
|
434
514
|
}
|
|
435
515
|
for (const settle of resolvers) settle(failure);
|
|
436
516
|
}
|
|
@@ -453,6 +533,8 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
453
533
|
};
|
|
454
534
|
return {
|
|
455
535
|
async buildStart() {
|
|
536
|
+
adoptHost(this);
|
|
537
|
+
adoptWatchMode(this);
|
|
456
538
|
const resolved = await resolveConfigs();
|
|
457
539
|
if (resolved.length === 0) return;
|
|
458
540
|
const { paths } = await getWatchTargets(resolved);
|
|
@@ -469,8 +551,19 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
469
551
|
vite: {
|
|
470
552
|
configResolved(config) {
|
|
471
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
|
+
};
|
|
472
564
|
},
|
|
473
565
|
async configureServer(server) {
|
|
566
|
+
isWatching = true;
|
|
474
567
|
const resolved = await resolveConfigs();
|
|
475
568
|
if (resolved.length === 0) return;
|
|
476
569
|
let targets = await getWatchTargets(resolved);
|
|
@@ -479,6 +572,29 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
479
572
|
targets = await getWatchTargets(rebuilt);
|
|
480
573
|
server.watcher.add(targets.paths);
|
|
481
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
|
+
}
|
|
482
598
|
server.watcher.on("all", (_event, file) => {
|
|
483
599
|
if (!isWatchedSource(file, targets.patterns)) return;
|
|
484
600
|
schedule(path.basename(file)).catch(() => {});
|
|
@@ -486,6 +602,8 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
486
602
|
}
|
|
487
603
|
},
|
|
488
604
|
async watchChange(id) {
|
|
605
|
+
adoptHost(this);
|
|
606
|
+
adoptWatchMode(this);
|
|
489
607
|
watchRebuild = true;
|
|
490
608
|
if (cachedPatterns && !isWatchedSource(id, cachedPatterns)) return;
|
|
491
609
|
const resolved = await resolveConfigs();
|
|
@@ -497,7 +615,25 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
497
615
|
},
|
|
498
616
|
webpack(compiler) {
|
|
499
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);
|
|
500
635
|
compiler.hooks.beforeCompile.tapPromise("unplugin-style-dictionary", async () => {
|
|
636
|
+
isWatching = compiler.watchMode;
|
|
501
637
|
const resolved = await resolveConfigs();
|
|
502
638
|
if (resolved.length === 0) return;
|
|
503
639
|
await compileOnceAcrossInstances(resolved);
|